Skip to content

Commit bdd7b42

Browse files
committed
[copy-propagation] Since this runs on OSSA and we have formalized consuming there, use that instead of our own handrolled consuming use impl.
This is in preparation for adding a run of this around ownership lowering in order to eliminate extra copies that passes may introduce as they transform IR. The tests for the pass all still pass in the exact same way so no updates were needed.
1 parent 303d88c commit bdd7b42

File tree

1 file changed

+36
-222
lines changed

1 file changed

+36
-222
lines changed

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 36 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -145,200 +145,12 @@ STATISTIC(NumCopiesGenerated, "number of copy_value instructions created");
145145
STATISTIC(NumDestroysGenerated, "number of destroy_value instructions created");
146146
STATISTIC(NumUnknownUsers, "number of functions with unknown users");
147147

148-
//===----------------------------------------------------------------------===//
149-
// Ownership Abstraction.
150-
//
151-
// FIXME: These helpers are only defined in this pass for prototyping. After
152-
// bootstrapping, they should be moved to a central ownership API (shared by
153-
// SILOwnershipVerifier, etc.).
154-
//
155-
// Categories of owned value users. (U1-U2 apply to any value, O3-O5 only apply
156-
// to owned values).
157-
//
158-
// U1. Use the value instantaneously (copy_value, @guaranteed).
159-
//
160-
// U2. Escape the nontrivial contents of the value (ref_to_unowned,
161-
// unchecked_trivial_bitcast).
162-
//
163-
// O3. Propagate the value without consuming it (mark_dependence, begin_borrow).
164-
//
165-
// O4. Consume the value immediately (store, destroy, @owned, destructure).
166-
//
167-
// O5. Consume the value indirectly via a move (tuple, struct).
168-
// ===---------------------------------------------------------------------===//
169-
170-
// TODO: Figure out how to handle these cases if possible.
171-
static bool isUnknownUse(Operand *use) {
172-
switch (use->getUser()->getKind()) {
173-
default:
174-
return false;
175-
// FIXME: (Category O3) mark_dependence requires recursion to find all
176-
// uses. It should be replaced by begin/end dependence.
177-
case SILInstructionKind::MarkDependenceInst: // Dependent
178-
// FIXME: (Category O3) ref_tail_addr should require a borrow because it
179-
// doesn't rely on fix_lifetime like other escaping instructions.
180-
case SILInstructionKind::RefTailAddrInst:
181-
// FIXME: (Category O3) dynamic_method_br seems to capture self, presumably
182-
// propagating lifetime. This should probably borrow self, then be treated
183-
// like mark_dependence.
184-
case SILInstructionKind::DynamicMethodBranchInst:
185-
// FIXME: (Category O3) The ownership verifier says project_box can accept an
186-
// owned value as a normal use, but it projects the address. That's either an
187-
// ownership bug or a special case.
188-
case SILInstructionKind::ProjectBoxInst:
189-
case SILInstructionKind::ProjectExistentialBoxInst:
190-
// FIXME: (Category O3) The ownership verifier says open_existential_box can
191-
// accept an owned value as a normal use, but it projects an address.
192-
case SILInstructionKind::OpenExistentialBoxInst:
193-
// Unmanaged operations hopefully don't apply to the same value as CopyValue?
194-
case SILInstructionKind::UnmanagedRetainValueInst:
195-
case SILInstructionKind::UnmanagedReleaseValueInst:
196-
case SILInstructionKind::UnmanagedAutoreleaseValueInst:
197-
return true;
198-
}
199-
}
200-
201-
/// Return true if the given owned operand is consumed by the given call.
202-
static bool isAppliedArgConsumed(ApplySite apply, Operand *oper) {
203-
ParameterConvention paramConv;
204-
if (oper->get() == apply.getCallee()) {
205-
assert(oper->getOperandNumber() == 0
206-
&& "function can't be passed to itself");
207-
paramConv = apply.getSubstCalleeType()->getCalleeConvention();
208-
} else {
209-
unsigned argIndex = apply.getCalleeArgIndex(*oper);
210-
paramConv = apply.getSubstCalleeConv()
211-
.getParamInfoForSILArg(argIndex)
212-
.getConvention();
213-
}
214-
return isConsumedParameter(paramConv);
215-
}
216-
217-
/// Return true if the given builtin consumes its operand.
218-
static bool isBuiltinArgConsumed(BuiltinInst *BI) {
219-
const BuiltinInfo &Builtin = BI->getBuiltinInfo();
220-
switch (Builtin.ID) {
221-
default:
222-
llvm_unreachable("Unexpected Builtin with owned value operand.");
223-
// Extend lifetime without consuming.
224-
case BuiltinValueKind::ErrorInMain:
225-
case BuiltinValueKind::UnexpectedError:
226-
case BuiltinValueKind::WillThrow:
227-
return false;
228-
// UnsafeGuaranteed moves the value, which will later be destroyed.
229-
case BuiltinValueKind::UnsafeGuaranteed:
230-
return true;
231-
}
232-
}
233-
234-
/// Return true if the given operand is consumed by its user.
235-
///
236-
/// TODO: Review the semantics of operations that extend the lifetime *without*
237-
/// propagating the value. Ideally, that never happens without borrowing first.
238-
static bool isConsuming(Operand *use) {
239-
auto *user = use->getUser();
240-
if (isa<ApplySite>(user))
241-
return isAppliedArgConsumed(ApplySite(user), use);
242-
243-
if (auto *BI = dyn_cast<BuiltinInst>(user))
244-
return isBuiltinArgConsumed(BI);
245-
246-
switch (user->getKind()) {
247-
default:
248-
llvm::dbgs() << *user;
249-
llvm_unreachable("Unexpected use of a loadable owned value.");
250-
251-
// Consume the value.
252-
case SILInstructionKind::AutoreleaseValueInst:
253-
case SILInstructionKind::DeallocBoxInst:
254-
case SILInstructionKind::DeallocExistentialBoxInst:
255-
case SILInstructionKind::DeallocRefInst:
256-
case SILInstructionKind::DeinitExistentialValueInst:
257-
case SILInstructionKind::DestroyValueInst:
258-
case SILInstructionKind::EndLifetimeInst:
259-
case SILInstructionKind::InitExistentialRefInst:
260-
case SILInstructionKind::InitExistentialValueInst:
261-
case SILInstructionKind::KeyPathInst:
262-
case SILInstructionKind::ReleaseValueInst:
263-
case SILInstructionKind::ReleaseValueAddrInst:
264-
case SILInstructionKind::StoreInst:
265-
case SILInstructionKind::StrongReleaseInst:
266-
case SILInstructionKind::UnownedReleaseInst:
267-
case SILInstructionKind::UnconditionalCheckedCastValueInst:
268-
return true;
269-
270-
// Terminators must consume their owned values.
271-
case SILInstructionKind::BranchInst:
272-
case SILInstructionKind::CheckedCastBranchInst:
273-
case SILInstructionKind::CheckedCastValueBranchInst:
274-
case SILInstructionKind::CondBranchInst:
275-
case SILInstructionKind::ReturnInst:
276-
case SILInstructionKind::ThrowInst:
277-
return true;
278-
279-
case SILInstructionKind::DeallocPartialRefInst:
280-
return cast<DeallocPartialRefInst>(user)->getInstance() == use->get();
281-
282-
// Move the value.
283-
case SILInstructionKind::TupleInst:
284-
case SILInstructionKind::StructInst:
285-
case SILInstructionKind::ObjectInst:
286-
case SILInstructionKind::EnumInst:
287-
case SILInstructionKind::OpenExistentialRefInst:
288-
case SILInstructionKind::UpcastInst:
289-
case SILInstructionKind::UncheckedRefCastInst:
290-
case SILInstructionKind::ConvertFunctionInst:
291-
case SILInstructionKind::RefToBridgeObjectInst:
292-
case SILInstructionKind::BridgeObjectToRefInst:
293-
case SILInstructionKind::UnconditionalCheckedCastInst:
294-
case SILInstructionKind::MarkUninitializedInst:
295-
case SILInstructionKind::UncheckedEnumDataInst:
296-
case SILInstructionKind::DestructureStructInst:
297-
case SILInstructionKind::DestructureTupleInst:
298-
return true;
299-
300-
// BeginBorrow should already be skipped.
301-
// EndBorrow extends the lifetime like a normal use.
302-
case SILInstructionKind::EndBorrowInst:
303-
return false;
304-
305-
// Extend the lifetime without borrowing, propagating, or destroying it.
306-
case SILInstructionKind::BridgeObjectToWordInst:
307-
case SILInstructionKind::ClassMethodInst:
308-
case SILInstructionKind::CopyBlockInst:
309-
case SILInstructionKind::CopyValueInst:
310-
case SILInstructionKind::DebugValueInst:
311-
case SILInstructionKind::ExistentialMetatypeInst:
312-
case SILInstructionKind::FixLifetimeInst:
313-
case SILInstructionKind::SelectEnumInst:
314-
case SILInstructionKind::SetDeallocatingInst:
315-
case SILInstructionKind::StoreWeakInst:
316-
case SILInstructionKind::ValueMetatypeInst:
317-
return false;
318-
319-
// Escape the value. The lifetime must already be enforced via something like
320-
// fix_lifetime.
321-
case SILInstructionKind::RefToRawPointerInst:
322-
case SILInstructionKind::RefToUnmanagedInst:
323-
case SILInstructionKind::RefToUnownedInst:
324-
case SILInstructionKind::UncheckedBitwiseCastInst:
325-
case SILInstructionKind::UncheckedTrivialBitCastInst:
326-
return false;
327-
328-
// Dynamic dispatch without capturing self.
329-
case SILInstructionKind::ObjCMethodInst:
330-
case SILInstructionKind::ObjCSuperMethodInst:
331-
case SILInstructionKind::SuperMethodInst:
332-
case SILInstructionKind::WitnessMethodInst:
333-
return false;
334-
}
335-
}
336-
337148
//===----------------------------------------------------------------------===//
338149
// CopyPropagationState: shared state for the pass's analysis and transforms.
339150
//===----------------------------------------------------------------------===//
340151

341152
namespace {
153+
342154
/// LiveWithin blocks have at least one use and/or def within the block, but are
343155
/// not LiveOut.
344156
///
@@ -357,9 +169,10 @@ class LivenessInfo {
357169
// used value is consumed. (Non-consuming uses within a block that is already
358170
// known to be live are uninteresting.)
359171
DenseMap<SILInstruction *, bool> users;
172+
360173
// Original points in the CFG where the current value was consumed or
361174
// destroyed.
362-
typedef SmallSetVector<SILBasicBlock *, 8> BlockSetVec;
175+
using BlockSetVec = SmallSetVector<SILBasicBlock *, 8>;
363176
BlockSetVec originalDestroyBlocks;
364177

365178
public:
@@ -409,7 +222,7 @@ class LivenessInfo {
409222
//
410223
// This call cannot be allowed to destroy %val.
411224
void recordUser(Operand *use) {
412-
bool consume = isConsuming(use);
225+
bool consume = use->isConsumingUse();
413226
auto iterAndSuccess = users.try_emplace(use->getUser(), consume);
414227
if (!iterAndSuccess.second)
415228
iterAndSuccess.first->second &= consume;
@@ -468,7 +281,7 @@ class DestroyInfo {
468281

469282
/// This pass' shared state.
470283
struct CopyPropagationState {
471-
SILFunction *F;
284+
SILFunction *func;
472285

473286
// Per-function invalidation state.
474287
unsigned invalidation;
@@ -483,7 +296,7 @@ struct CopyPropagationState {
483296
DestroyInfo destroys;
484297

485298
CopyPropagationState(SILFunction *F)
486-
: F(F), invalidation(SILAnalysis::InvalidationKind::Nothing) {}
299+
: func(F), invalidation(SILAnalysis::InvalidationKind::Nothing) {}
487300

488301
bool isValueOwned() const {
489302
return currDef.getOwnershipKind() == ValueOwnershipKind::Owned;
@@ -600,27 +413,23 @@ static bool computeLiveness(CopyPropagationState &pass) {
600413
for (Operand *use : value->getUses()) {
601414
auto *user = use->getUser();
602415

603-
// Bailout if we cannot yet determine the ownership of a use.
604-
if (isUnknownUse(use)) {
605-
LLVM_DEBUG(llvm::dbgs() << "Unknown owned value user: "; user->dump());
606-
++NumUnknownUsers;
607-
return false;
608-
}
609416
// Recurse through copies.
610417
if (auto *copy = dyn_cast<CopyValueInst>(user)) {
611418
defUseWorkList.insert(copy);
612419
continue;
613420
}
421+
614422
// An entire borrow scope is considered a single use that occurs at the
615423
// point of the end_borrow.
616-
if (auto *BBI = dyn_cast<BeginBorrowInst>(user)) {
617-
for (Operand *use : BBI->getUses()) {
424+
if (auto *bbi = dyn_cast<BeginBorrowInst>(user)) {
425+
for (Operand *use : bbi->getUses()) {
618426
if (isa<EndBorrowInst>(use->getUser()))
619427
computeUseLiveness(use, pass);
620428
}
621429
continue;
622430
}
623-
if (isConsuming(use)) {
431+
432+
if (use->isConsumingUse()) {
624433
pass.liveness.recordOriginalDestroy(use);
625434
// Destroying a values does not force liveness.
626435
if (isa<DestroyValueInst>(user))
@@ -649,13 +458,14 @@ static void insertDestroyOnCFGEdge(SILBasicBlock *predBB, SILBasicBlock *succBB,
649458
if (destroyBB != succBB)
650459
pass.markInvalid(SILAnalysis::InvalidationKind::Branches);
651460

652-
SILBuilderWithScope B(destroyBB->begin());
653-
auto *DI = B.createDestroyValue(succBB->begin()->getLoc(), pass.currDef);
461+
SILBuilderWithScope builder(destroyBB->begin());
462+
auto *di =
463+
builder.createDestroyValue(succBB->begin()->getLoc(), pass.currDef);
654464

655-
pass.destroys.recordFinalDestroy(DI);
465+
pass.destroys.recordFinalDestroy(di);
656466

657467
++NumDestroysGenerated;
658-
LLVM_DEBUG(llvm::dbgs() << " Destroy on edge "; DI->dump());
468+
LLVM_DEBUG(llvm::dbgs() << " Destroy on edge "; di->dump());
659469

660470
pass.markInvalid(SILAnalysis::InvalidationKind::Instructions);
661471
}
@@ -665,11 +475,11 @@ static void insertDestroyOnCFGEdge(SILBasicBlock *predBB, SILBasicBlock *succBB,
665475
/// Create a final destroy, immediately after `pos`.
666476
static void insertDestroyAtInst(SILBasicBlock::iterator pos,
667477
CopyPropagationState &pass) {
668-
SILBuilderWithScope B(pos);
669-
auto *DI = B.createDestroyValue((*pos).getLoc(), pass.currDef);
670-
pass.destroys.recordFinalDestroy(DI);
478+
SILBuilderWithScope builder(pos);
479+
auto *di = builder.createDestroyValue((*pos).getLoc(), pass.currDef);
480+
pass.destroys.recordFinalDestroy(di);
671481
++NumDestroysGenerated;
672-
LLVM_DEBUG(llvm::dbgs() << " Destroy at last use "; DI->dump());
482+
LLVM_DEBUG(llvm::dbgs() << " Destroy at last use "; di->dump());
673483
pass.markInvalid(SILAnalysis::InvalidationKind::Instructions);
674484
}
675485

@@ -679,9 +489,9 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos,
679489
static void findOrInsertDestroyInBlock(SILBasicBlock *bb,
680490
CopyPropagationState &pass) {
681491
auto *defInst = pass.currDef->getDefiningInstruction();
682-
auto I = bb->getTerminator()->getIterator();
492+
auto instIter = bb->getTerminator()->getIterator();
683493
while (true) {
684-
auto *inst = &*I;
494+
auto *inst = &*instIter;
685495
Optional<bool> isConsumingResult = pass.liveness.isConsumingUser(inst);
686496
if (isConsumingResult.hasValue()) {
687497
if (isConsumingResult.getValue()) {
@@ -691,20 +501,20 @@ static void findOrInsertDestroyInBlock(SILBasicBlock *bb,
691501
}
692502
// Insert a destroy after this non-consuming use.
693503
assert(inst != bb->getTerminator() && "Terminator must consume operand.");
694-
insertDestroyAtInst(std::next(I), pass);
504+
insertDestroyAtInst(std::next(instIter), pass);
695505
break;
696506
}
697507
// This is not a potential last user. Keep scanning.
698508
// If the original destroy is reached, this is a dead live range. Insert a
699509
// destroy immediately after the def.
700-
if (I == bb->begin()) {
510+
if (instIter == bb->begin()) {
701511
assert(cast<SILArgument>(pass.currDef)->getParent() == bb);
702-
insertDestroyAtInst(I, pass);
512+
insertDestroyAtInst(instIter, pass);
703513
break;
704514
}
705-
--I;
706-
if (&*I == defInst) {
707-
insertDestroyAtInst(std::next(I), pass);
515+
--instIter;
516+
if (&*instIter == defInst) {
517+
insertDestroyAtInst(std::next(instIter), pass);
708518
break;
709519
}
710520
}
@@ -799,6 +609,7 @@ static void rewriteCopies(CopyPropagationState &pass) {
799609
defUseWorklist.insert(copy);
800610
return;
801611
}
612+
802613
if (auto *destroy = dyn_cast<DestroyValueInst>(user)) {
803614
// If this destroy was marked as a final destroy, ignore it; otherwise,
804615
// delete it.
@@ -809,8 +620,9 @@ static void rewriteCopies(CopyPropagationState &pass) {
809620
}
810621
return;
811622
}
623+
812624
// Nonconsuming uses do not need copies and cannot be marked as destroys.
813-
if (!isConsuming(use))
625+
if (!use->isConsumingUse())
814626
return;
815627

816628
// If this use was marked as a final destroy *and* this is the first
@@ -862,6 +674,7 @@ static SILValue stripCopies(SILValue v) {
862674
v = srcCopy->getOperand();
863675
continue;
864676
}
677+
865678
return v;
866679
}
867680
}
@@ -885,12 +698,13 @@ void CopyPropagation::run() {
885698
// Step 1. Find all copied defs.
886699
CopyPropagationState pass(getFunction());
887700
SmallSetVector<SILValue, 16> copiedDefs;
888-
for (auto &BB : *pass.F) {
889-
for (auto &I : BB) {
890-
if (auto *copy = dyn_cast<CopyValueInst>(&I))
701+
for (auto &bb : *pass.func) {
702+
for (auto &i : bb) {
703+
if (auto *copy = dyn_cast<CopyValueInst>(&i))
891704
copiedDefs.insert(stripCopies(copy));
892705
}
893706
}
707+
894708
for (auto &def : copiedDefs) {
895709
pass.resetDef(def);
896710
// Step 2: computeLiveness

0 commit comments

Comments
 (0)