diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index f4e6e09fd3d49..10e4d6b7012de 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -47,77 +47,29 @@ using InstSet = llvm::SmallPtrSet; using InstVector = llvm::SmallVector; -/// Returns true if the \p SideEffectInsts set contains any memory writes which -/// may alias with the memory addressed by \a LI. +/// A subset of instruction which may have side effects. +/// Doesn't contain ones that have special handling (e.g. fix_lifetime) +using WriteSet = SmallPtrSet; + +/// Returns true if the \p MayWrites set contains any memory writes which may +/// alias with the memory addressed by \a LI. template -static bool mayWriteTo(AliasAnalysis *AA, InstSet &SideEffectInsts, +static bool mayWriteTo(AliasAnalysis *AA, WriteSet &MayWrites, UnaryInstructionBase *Inst) { - for (auto *I : SideEffectInsts) - if (AA->mayWriteToMemory(I, Inst->getOperand())) { - LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *I << " to " + for (auto *W : MayWrites) + if (AA->mayWriteToMemory(W, Inst->getOperand())) { + LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *W << " to " << *Inst << "\n"); return true; } return false; } -/// Returns true if \p I is a store to \p addr. -static StoreInst *isStoreToAddr(SILInstruction *I, SILValue addr) { - auto *SI = dyn_cast(I); - if (!SI) - return nullptr; - - // TODO: handle StoreOwnershipQualifier::Init - if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Init) - return nullptr; - - if (SI->getDest() != addr) - return nullptr; - - return SI; -} - -/// Returns true if \p I is a load from \p addr or a projected address from -/// \p addr. -static LoadInst *isLoadFromAddr(SILInstruction *I, SILValue addr) { - auto *LI = dyn_cast(I); - if (!LI) - return nullptr; - - // TODO: handle StoreOwnershipQualifier::Take - if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Take) - return nullptr; - - SILValue v = LI->getOperand(); - for (;;) { - if (v == addr) { - return LI; - } else if (isa(v) || isa(v)) { - v = cast(v)->getOperand(0); - } else { - return nullptr; - } - } -} - -/// Returns true if all instructions in \p SideEffectInsts which may alias with -/// \p addr are either loads or stores from \p addr. -static bool isOnlyLoadedAndStored(AliasAnalysis *AA, InstSet &SideEffectInsts, - SILValue addr) { - for (auto *I : SideEffectInsts) { - if (AA->mayReadOrWriteMemory(I, addr) && - !isStoreToAddr(I, addr) && !isLoadFromAddr(I, addr)) { - return false; - } - } - return true; -} - -/// Returns true if the \p SideEffectInsts set contains any memory writes which -/// may alias with any memory which is read by \p AI. +/// Returns true if the \p MayWrites set contains any memory writes which may +/// alias with any memory which is read by \p AI. /// Note: This function should only be called on a read-only apply! static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, - InstSet &SideEffectInsts, ApplyInst *AI) { + WriteSet &MayWrites, ApplyInst *AI) { FunctionSideEffects E; SEA->getCalleeEffects(E, AI); assert(E.getMemBehavior(RetainObserveKind::IgnoreRetains) <= @@ -135,9 +87,9 @@ static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, SILValue Arg = AI->getArgument(Idx); // Check if the memory addressed by the argument may alias any writes. - for (auto *I : SideEffectInsts) { - if (AA->mayWriteToMemory(I, Arg)) { - LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *I << " to " + for (auto *W : MayWrites) { + if (AA->mayWriteToMemory(W, Arg)) { + LLVM_DEBUG(llvm::dbgs() << " mayWriteTo\n" << *W << " to " << *AI << "\n"); return true; } @@ -146,6 +98,23 @@ static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, return false; } +static bool hasLoopInvariantOperands(SILInstruction *I, SILLoop *L) { + auto Opds = I->getAllOperands(); + + return std::all_of(Opds.begin(), Opds.end(), [=](Operand &Op) { + + ValueBase *Def = Op.get(); + + // Operand is defined outside the loop. + if (auto *Inst = Def->getDefiningInstruction()) + return !L->contains(Inst->getParent()); + if (auto *Arg = dyn_cast(Def)) + return !L->contains(Arg->getParent()); + + return false; + }); +} + // When Hoisting / Sinking, // Don't descend into control-dependent code. // Only traverse into basic blocks that dominate all exits. @@ -177,19 +146,9 @@ static void getDominatingBlocks(SmallVectorImpl &domBlocks, } } -/// Returns true if \p v is loop invariant in \p L. -static bool isLoopInvariant(SILValue v, SILLoop *L) { - if (SILBasicBlock *parent = v->getParentBlock()) - return !L->contains(parent); - return false; -} - static bool hoistInstruction(DominanceInfo *DT, SILInstruction *Inst, SILLoop *Loop, SILBasicBlock *&Preheader) { - auto Operands = Inst->getAllOperands(); - if (!std::all_of(Operands.begin(), Operands.end(), [=](Operand &Op) { - return isLoopInvariant(Op.get(), Loop); - })) { + if (!hasLoopInvariantOperands(Inst, Loop)) { LLVM_DEBUG(llvm::dbgs() << " loop variant operands\n"); return false; } @@ -233,17 +192,17 @@ static bool hoistInstructions(SILLoop *Loop, DominanceInfo *DT, return Changed; } -/// Summary of side effect instructions occurring in the loop tree rooted at \p +/// Summary of may writes occurring in the loop tree rooted at \p /// Loop. This includes all writes of the sub loops and the loop itself. struct LoopNestSummary { SILLoop *Loop; - InstSet SideEffectInsts; + WriteSet MayWrites; LoopNestSummary(SILLoop *Curr) : Loop(Curr) {} void copySummary(LoopNestSummary &Other) { - SideEffectInsts.insert(Other.SideEffectInsts.begin(), Other.SideEffectInsts.end()); + MayWrites.insert(Other.MayWrites.begin(), Other.MayWrites.end()); } LoopNestSummary(const LoopNestSummary &) = delete; @@ -325,8 +284,8 @@ static bool sinkInstruction(DominanceInfo *DT, } if (Changed && !ExitBB) { // Created clones of instruction - // Remove it from the side-effect set - dangling pointer - LoopSummary->SideEffectInsts.erase(Inst); + // Remove it from the may write set - dangling pointer + LoopSummary->MayWrites.erase(Inst); Inst->getParent()->erase(Inst); } return Changed; @@ -422,12 +381,6 @@ class LoopTreeOptimization { /// Instructions that we may be able to sink down InstVector SinkDown; - /// Load and store instructions that we may be able to move out of the loop. - InstVector LoadsAndStores; - - /// All addresses of the \p LoadsAndStores instructions. - llvm::SetVector LoadAndStoreAddrs; - /// Hoistable Instructions that need special treatment /// e.g. begin_access InstVector SpecialHoist; @@ -460,36 +413,6 @@ class LoopTreeOptimization { /// Optimize the current loop nest. bool optimizeLoop(std::unique_ptr &CurrSummary); - - /// Move all loads and stores from/to \p addr out of the \p loop. - void hoistLoadsAndStores(SILValue addr, SILLoop *loop, InstVector &toDelete); - - /// Move all loads and stores from all addresses in LoadAndStoreAddrs out of - /// the \p loop. - /// - /// This is a combination of load hoisting and store sinking, e.g. - /// \code - /// preheader: - /// br header_block - /// header_block: - /// %x = load %not_aliased_addr - /// // use %x and define %y - /// store %y to %not_aliased_addr - /// ... - /// exit_block: - /// \endcode - /// is transformed to: - /// \code - /// preheader: - /// %x = load %not_aliased_addr - /// br header_block - /// header_block: - /// // use %x and define %y - /// ... - /// exit_block: - /// store %y to %not_aliased_addr - /// \endcode - bool hoistAllLoadsAndStores(SILLoop *loop); }; } // end anonymous namespace @@ -510,14 +433,12 @@ bool LoopTreeOptimization::optimize() { // Might allow us to sink the instruction out of the loop bool currChanged = false; do { + currChanged = false; + // Analyze the current loop for instructions that can be hoisted. analyzeCurrentLoop(CurrLoopSummary); currChanged = optimizeLoop(CurrLoopSummary); - if (currChanged) { - CurrLoopSummary->SideEffectInsts.clear(); - Changed = true; - } // Reset the data structures for next loop in the list HoistUp.clear(); @@ -555,10 +476,9 @@ static bool isSafeReadOnlyApply(SideEffectAnalysis *SEA, ApplyInst *AI) { return (MB <= SILInstruction::MemoryBehavior::MayRead); } -static void checkSideEffects(swift::SILInstruction &Inst, - InstSet &SideEffectInsts) { +static void checkSideEffects(swift::SILInstruction &Inst, WriteSet &MayWrites) { if (Inst.mayHaveSideEffects()) { - SideEffectInsts.insert(&Inst); + MayWrites.insert(&Inst); } } @@ -631,7 +551,7 @@ static bool isCoveredByScope(BeginAccessInst *BI, DominanceInfo *DT, static bool analyzeBeginAccess(BeginAccessInst *BI, SmallVector &BeginAccesses, SmallVector &fullApplies, - InstSet &SideEffectInsts, + WriteSet &MayWrites, AccessedStorageAnalysis *ASA, DominanceInfo *DT) { const AccessedStorage &storage = @@ -675,12 +595,12 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, // TODO Introduce "Pure Swift" deinitializers // We can then make use of alias information for instr's operands // If they don't alias - we might get away with not recording a conflict - for (SILInstruction *I : SideEffectInsts) { - // we actually compute all SideEffectInsts in analyzeCurrentLoop - if (!I->mayRelease()) { + for (auto mayWrite : MayWrites) { + // we actually compute all MayWrites in analyzeCurrentLoop + if (!mayWrite->mayRelease()) { continue; } - if (!isCoveredByScope(BI, DT, I)) + if (!isCoveredByScope(BI, DT, mayWrite)) return false; } @@ -691,112 +611,110 @@ static bool analyzeBeginAccess(BeginAccessInst *BI, // Computes set of instructions we may be able to move out of the loop // Important Note: // We can't bail out of this method! we have to run it on all loops. -// We *need* to discover all SideEffectInsts - +// We *need* to discover all MayWrites - // even if the loop is otherwise skipped! // This is because outer loops will depend on the inner loop's writes. void LoopTreeOptimization::analyzeCurrentLoop( std::unique_ptr &CurrSummary) { - InstSet &sideEffects = CurrSummary->SideEffectInsts; + WriteSet &MayWrites = CurrSummary->MayWrites; SILLoop *Loop = CurrSummary->Loop; LLVM_DEBUG(llvm::dbgs() << " Analyzing accesses.\n"); - auto *Preheader = Loop->getLoopPreheader(); - if (!Preheader) { - // Can't hoist/sink instructions - return; - } - - // Interesting instructions in the loop: + // Contains function calls in the loop, which only read from memory. SmallVector ReadOnlyApplies; + // Contains Loads inside the loop. SmallVector Loads; - SmallVector Stores; + // Contains fix_lifetime, we might be able to sink them. SmallVector FixLifetimes; + // Contains begin_access, we might be able to hoist them. SmallVector BeginAccesses; + // Contains all applies - used for begin_access SmallVector fullApplies; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { switch (Inst.getKind()) { case SILInstructionKind::FixLifetimeInst: { - auto *FL = cast(&Inst); - if (DomTree->dominates(FL->getOperand()->getParentBlock(), Preheader)) - FixLifetimes.push_back(FL); + auto *FL = dyn_cast(&Inst); + assert(FL && "Expected a FixLifetime instruction"); + FixLifetimes.push_back(FL); // We can ignore the side effects of FixLifetimes break; } - case SILInstructionKind::LoadInst: - Loads.push_back(cast(&Inst)); - LoadsAndStores.push_back(&Inst); - break; - case SILInstructionKind::StoreInst: { - Stores.push_back(cast(&Inst)); - LoadsAndStores.push_back(&Inst); - checkSideEffects(Inst, sideEffects); + case SILInstructionKind::LoadInst: { + auto *LI = dyn_cast(&Inst); + assert(LI && "Expected a Load instruction"); + Loads.push_back(LI); break; } - case SILInstructionKind::BeginAccessInst: - BeginAccesses.push_back(cast(&Inst)); - checkSideEffects(Inst, sideEffects); + case SILInstructionKind::BeginAccessInst: { + auto *BI = dyn_cast(&Inst); + assert(BI && "Expected a Begin Access"); + BeginAccesses.push_back(BI); + checkSideEffects(Inst, MayWrites); break; - case SILInstructionKind::RefElementAddrInst: - SpecialHoist.push_back(cast(&Inst)); + } + case SILInstructionKind::RefElementAddrInst: { + auto *REA = static_cast(&Inst); + SpecialHoist.push_back(REA); break; - case swift::SILInstructionKind::CondFailInst: + } + case swift::SILInstructionKind::CondFailInst: { // We can (and must) hoist cond_fail instructions if the operand is // invariant. We must hoist them so that we preserve memory safety. A // cond_fail that would have protected (executed before) a memory access // must - after hoisting - also be executed before said access. HoistUp.insert(&Inst); - checkSideEffects(Inst, sideEffects); + checkSideEffects(Inst, MayWrites); break; + } case SILInstructionKind::ApplyInst: { - auto *AI = cast(&Inst); + auto *AI = dyn_cast(&Inst); + assert(AI && "Expected an Apply Instruction"); if (isSafeReadOnlyApply(SEA, AI)) { ReadOnlyApplies.push_back(AI); } // check for array semantics and side effects - same as default LLVM_FALLTHROUGH; } - default: + default: { if (auto fullApply = FullApplySite::isa(&Inst)) { fullApplies.push_back(fullApply); } - checkSideEffects(Inst, sideEffects); + checkSideEffects(Inst, MayWrites); if (canHoistUpDefault(&Inst, Loop, DomTree, RunsOnHighLevelSIL)) { HoistUp.insert(&Inst); } break; } + } } } + auto *Preheader = Loop->getLoopPreheader(); + if (!Preheader) { + // Can't hoist/sink instructions + return; + } for (auto *AI : ReadOnlyApplies) { - if (!mayWriteTo(AA, SEA, sideEffects, AI)) { + if (!mayWriteTo(AA, SEA, MayWrites, AI)) { HoistUp.insert(AI); } } for (auto *LI : Loads) { - if (!mayWriteTo(AA, sideEffects, LI)) { + if (!mayWriteTo(AA, MayWrites, LI)) { HoistUp.insert(LI); } } - // Collect memory locations for which we can move all loads and stores out - // of the loop. - for (StoreInst *SI : Stores) { - SILValue addr = SI->getDest(); - if (isLoopInvariant(addr, Loop) && - isOnlyLoadedAndStored(AA, sideEffects, addr)) { - LoadAndStoreAddrs.insert(addr); + bool mayWritesMayRelease = + std::any_of(MayWrites.begin(), MayWrites.end(), + [&](SILInstruction *W) { return W->mayRelease(); }); + for (auto *FL : FixLifetimes) { + if (!DomTree->dominates(FL->getOperand()->getParentBlock(), Preheader)) { + continue; } - } - if (!FixLifetimes.empty()) { - bool sideEffectsMayRelease = - std::any_of(sideEffects.begin(), sideEffects.end(), - [&](SILInstruction *W) { return W->mayRelease(); }); - for (auto *FL : FixLifetimes) { - if (!sideEffectsMayRelease || !mayWriteTo(AA, sideEffects, FL)) { - SinkDown.push_back(FL); - } + if (!mayWriteTo(AA, MayWrites, FL) || !mayWritesMayRelease) { + SinkDown.push_back(FL); } } for (auto *BI : BeginAccesses) { @@ -805,7 +723,7 @@ void LoopTreeOptimization::analyzeCurrentLoop( LLVM_DEBUG(llvm::dbgs() << "Some end accesses can't be handled\n"); continue; } - if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, sideEffects, ASA, + if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, MayWrites, ASA, DomTree)) { SpecialHoist.push_back(BI); } @@ -819,185 +737,14 @@ bool LoopTreeOptimization::optimizeLoop( if (!CurrentLoop->getLoopPreheader()) return false; bool currChanged = false; - if (hoistAllLoadsAndStores(CurrentLoop)) - return true; - currChanged |= hoistInstructions(CurrentLoop, DomTree, HoistUp); currChanged |= sinkInstructions(CurrSummary, DomTree, LoopInfo, SinkDown); currChanged |= hoistSpecialInstruction(CurrSummary, DomTree, LoopInfo, SpecialHoist); + Changed |= currChanged; return currChanged; } -/// Creates a value projection from \p rootVal based on the address projection -/// from \a rootAddr to \a addr. -static SILValue projectLoadValue(SILValue addr, SILValue rootAddr, - SILValue rootVal, SILInstruction *beforeInst) { - if (addr == rootAddr) - return rootVal; - - if (auto *SEI = dyn_cast(addr)) { - SILValue val = projectLoadValue(SEI->getOperand(), rootAddr, rootVal, - beforeInst); - SILBuilder B(beforeInst); - return B.createStructExtract(beforeInst->getLoc(), val, SEI->getField(), - SEI->getType().getObjectType()); - } - if (auto *TEI = dyn_cast(addr)) { - SILValue val = projectLoadValue(TEI->getOperand(), rootAddr, rootVal, - beforeInst); - SILBuilder B(beforeInst); - return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldNo(), - TEI->getType().getObjectType()); - } - llvm_unreachable("unknown projection"); -} - -/// Returns true if all stores to \p addr commonly dominate the loop exitst of -/// \p loop. -static bool storesCommonlyDominateLoopExits(SILValue addr, SILLoop *loop, - ArrayRef exitingBlocks) { - SmallPtrSet stores; - for (Operand *use : addr->getUses()) { - SILInstruction *user = use->getUser(); - if (isa(user)) - stores.insert(user->getParent()); - } - SILBasicBlock *header = loop->getHeader(); - // If a store is in the loop header, we already know that it's dominating all - // loop exits. - if (stores.count(header) != 0) - return true; - - // Propagate the store-is-not-alive flag through the control flow in the loop, - // starting at the header. - SmallPtrSet storesNotAlive; - storesNotAlive.insert(header); - bool changed = false; - do { - changed = false; - for (SILBasicBlock *block : loop->blocks()) { - bool storeAlive = (storesNotAlive.count(block) == 0); - if (storeAlive && stores.count(block) == 0 && - std::any_of(block->pred_begin(), block->pred_end(), - [&](SILBasicBlock *b) { return storesNotAlive.count(b) != 0; })) { - storesNotAlive.insert(block); - changed = true; - } - } - } while (changed); - - auto isUnreachableBlock = [](SILBasicBlock *succ) { - return isa(succ->getTerminator()); - }; - - // Check if the store-is-not-alive flag reaches any of the exits. - for (SILBasicBlock *eb : exitingBlocks) { - // Ignore loop exits to blocks which end in an unreachable. - if (!std::any_of(eb->succ_begin(), eb->succ_end(), isUnreachableBlock) && - storesNotAlive.count(eb) != 0) { - return false; - } - } - return true; -} - -void LoopTreeOptimization::hoistLoadsAndStores(SILValue addr, SILLoop *loop, InstVector &toDelete) { - - SmallVector exitingBlocks; - loop->getExitingBlocks(exitingBlocks); - - // This is not a requirement for functional correctness, but we don't want to - // _speculatively_ load and store the value (outside of the loop). - if (!storesCommonlyDominateLoopExits(addr, loop, exitingBlocks)) - return; - - // Inserting the stores requires the exit edges to be not critical. - for (SILBasicBlock *exitingBlock : exitingBlocks) { - auto successors = exitingBlock->getSuccessors(); - for (unsigned idx = 0, e = successors.size(); idx != e; ++idx) { - SILBasicBlock *succ = successors[idx]; - if (!loop->contains(succ)) - splitCriticalEdge(exitingBlock->getTerminator(), idx, DomTree, LoopInfo); - } - } - - SILBasicBlock *preheader = loop->getLoopPreheader(); - assert(preheader && "Expected a preheader"); - - // Initially load the value in the loop pre header. - SILBuilder B(preheader->getTerminator()); - auto *initialLoad = B.createLoad(preheader->getTerminator()->getLoc(), addr, - LoadOwnershipQualifier::Unqualified); - - SILSSAUpdater ssaUpdater; - ssaUpdater.Initialize(initialLoad->getType()); - ssaUpdater.AddAvailableValue(preheader, initialLoad); - - Optional loc; - for (SILInstruction *I : LoadsAndStores) { - if (auto *SI = isStoreToAddr(I, addr)) { - // If there are multiple stores in a block, only the last one counts. - loc = SI->getLoc(); - ssaUpdater.AddAvailableValue(SI->getParent(), SI->getSrc()); - } - } - - // Remove all stores and replace the loads with the current value. - SILBasicBlock *currentBlock = nullptr; - SILValue currentVal; - for (SILInstruction *I : LoadsAndStores) { - SILBasicBlock *block = I->getParent(); - if (block != currentBlock) { - currentBlock = block; - currentVal = SILValue(); - } - if (auto *SI = isStoreToAddr(I, addr)) { - currentVal = SI->getSrc(); - toDelete.push_back(SI); - } else if (auto *LI = isLoadFromAddr(I, addr)) { - // If we didn't see a store in this block yet, get the current value from - // the ssaUpdater. - if (!currentVal) - currentVal = ssaUpdater.GetValueInMiddleOfBlock(block); - SILValue projectedValue = projectLoadValue(LI->getOperand(), addr, - currentVal, LI); - LI->replaceAllUsesWith(projectedValue); - toDelete.push_back(LI); - } - } - - // Store back the value at all loop exits. - for (SILBasicBlock *exitingBlock : exitingBlocks) { - for (SILBasicBlock *succ : exitingBlock->getSuccessors()) { - if (!loop->contains(succ)) { - assert(succ->getSinglePredecessorBlock() && - "should have split critical edges"); - SILBuilder B(succ->begin()); - B.createStore(loc.getValue(), ssaUpdater.GetValueInMiddleOfBlock(succ), - addr, StoreOwnershipQualifier::Unqualified); - } - } - } - - // In case the value is only stored but never loaded in the loop. - recursivelyDeleteTriviallyDeadInstructions(initialLoad); -} - -bool LoopTreeOptimization::hoistAllLoadsAndStores(SILLoop *loop) { - InstVector toDelete; - for (SILValue addr : LoadAndStoreAddrs) { - hoistLoadsAndStores(addr, loop, toDelete); - } - LoadsAndStores.clear(); - LoadAndStoreAddrs.clear(); - - for (SILInstruction *I : toDelete) { - I->eraseFromParent(); - } - return !toDelete.empty(); -} - namespace { /// Hoist loop invariant code out of innermost loops. /// diff --git a/test/SILOptimizer/licm.sil b/test/SILOptimizer/licm.sil index 8262e77d7ce6d..f1deec36d3c0b 100644 --- a/test/SILOptimizer/licm.sil +++ b/test/SILOptimizer/licm.sil @@ -184,14 +184,12 @@ bb2: return %r1 : $() } -sil @use_addr : $@convention(thin) (@inout Int32) -> () - // CHECK-LABEL: sil @dont_hoist_aliased_stack_location // CHECK: {{^}}bb0 // CHECK-NOT: load // CHECK: {{^}}bb1: // CHECK: store -// CHECK: apply +// CHECK: load // CHECK: {{^}}bb2: // CHECK: return sil @dont_hoist_aliased_stack_location : $@convention(thin) (Int32) -> () { @@ -201,8 +199,7 @@ bb0(%0 : $Int32): bb1: store %0 to %313 : $*Int32 - %f = function_ref @use_addr : $@convention(thin) (@inout Int32) -> () - %a = apply %f(%313) : $@convention(thin) (@inout Int32) -> () + %l1 = load %313 : $*Int32 cond_br undef, bb1, bb2 bb2: @@ -354,191 +351,3 @@ bb4: %10 = tuple () return %10 : $() } - -// CHECK-LABEL sil @hoist_load_and_store -// CHECK: [[V1:%[0-9]+]] = load %0 -// CHECK: br bb1([[V1]] : $Int32) -// CHECK: bb1([[V2:%[0-9]+]] : $Int32): -// CHECK-NOT: load -// CHECK: [[E:%[0-9]+]] = struct_extract [[V2]] -// CHECK: "sadd_with_overflow_Int64"([[E]] -// CHECK: [[V3:%[0-9]+]] = struct $Int32 -// CHECK-NOT: store -// CHECK: bb2: -// CHECK: br bb1([[V3]] : $Int32) -// CHECK: bb3: -// CHECK: store [[V3]] to %0 -// CHECK: } // end sil function 'hoist_load_and_store' -sil @hoist_load_and_store : $@convention(thin) (@inout Int32, Int32) -> () { -bb0(%0 : $*Int32, %1 : $Int32): - %8 = struct_element_addr %0 : $*Int32, #Int32._value - %9 = struct_extract %1 : $Int32, #Int32._value - %10 = integer_literal $Builtin.Int1, 0 - br bb1 - -bb1: - %17 = load %8 : $*Builtin.Int32 - %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) - %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 - %20 = struct $Int32 (%19 : $Builtin.Int32) - store %20 to %0 : $*Int32 - cond_br undef, bb2, bb3 - -bb2: - br bb1 - -bb3: - %12 = tuple () - return %12 : $() -} - -// CHECK-LABEL sil @hoist_load_and_two_stores -// CHECK: [[V1:%[0-9]+]] = load %0 -// CHECK: br bb1([[V1]] : $Int32) -// CHECK: bb1([[V2:%[0-9]+]] : $Int32): -// CHECK-NOT: load -// CHECK: [[E:%[0-9]+]] = struct_extract [[V2]] -// CHECK: "sadd_with_overflow_Int64"([[E]] -// CHECK: [[V3:%[0-9]+]] = struct $Int32 -// CHECK: bb2: -// CHECK-NOT: store -// CHECK: br bb4([[V3]] : $Int32) -// CHECK: bb3: -// CHECK-NOT: store -// CHECK: br bb4([[V3]] : $Int32) -// CHECK: bb4([[V4:%[0-9]+]] : $Int32): -// CHECK: cond_br -// CHECK: bb5: -// CHECK: br bb1([[V4]] : $Int32) -// CHECK: bb6: -// CHECK: store [[V4]] to %0 -// CHECK: } // end sil function 'hoist_load_and_two_stores' -sil @hoist_load_and_two_stores : $@convention(thin) (@inout Int32, Int32) -> () { -bb0(%0 : $*Int32, %1 : $Int32): - %8 = struct_element_addr %0 : $*Int32, #Int32._value - %9 = struct_extract %1 : $Int32, #Int32._value - %10 = integer_literal $Builtin.Int1, 0 - br bb1 - -bb1: - %17 = load %8 : $*Builtin.Int32 - %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) - %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 - %20 = struct $Int32 (%19 : $Builtin.Int32) - cond_br undef, bb2, bb3 -bb2: - store %20 to %0 : $*Int32 - br bb4 -bb3: - store %20 to %0 : $*Int32 - br bb4 -bb4: - cond_br undef, bb5, bb6 - -bb5: - br bb1 - -bb6: - %12 = tuple () - return %12 : $() -} - -// CHECK-LABEL sil @dont_hoist_stores_not_dominating_exit -// CHECK: bb0(%0 : $*Int32, %1 : $Int32): -// CHECK-NOT: load -// CHECK: bb1: -// CHECK: load -// CHECK: bb3: -// CHECK: store -// CHECK: bb4: -// CHECK: bb6: -// CHECK-NOT: store -// CHECK: } // end sil function 'dont_hoist_stores_not_dominating_exit' -sil @dont_hoist_stores_not_dominating_exit : $@convention(thin) (@inout Int32, Int32) -> () { -bb0(%0 : $*Int32, %1 : $Int32): - %8 = struct_element_addr %0 : $*Int32, #Int32._value - %9 = struct_extract %1 : $Int32, #Int32._value - %10 = integer_literal $Builtin.Int1, 0 - br bb1 - -bb1: - %17 = load %8 : $*Builtin.Int32 - %18 = builtin "sadd_with_overflow_Int64"(%17 : $Builtin.Int32, %9 : $Builtin.Int32, %10 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) - %19 = tuple_extract %18 : $(Builtin.Int32, Builtin.Int1), 0 - %20 = struct $Int32 (%19 : $Builtin.Int32) - cond_br undef, bb2, bb3 -bb2: - br bb4 -bb3: - store %20 to %0 : $*Int32 - br bb4 -bb4: - cond_br undef, bb5, bb6 - -bb5: - br bb1 - -bb6: - %12 = tuple () - return %12 : $() -} - -// CHECK-LABEL sil @hoist_loads_and_stores_multiple_exits -// CHECK: [[V1:%[0-9]+]] = load %0 -// CHECK: br bb1([[V1]] : $Int32) -// CHECK: bb1([[V2:%[0-9]+]] : $Int32): -// CHECK-NOT: load -// CHECK: [[E:%[0-9]+]] = struct_extract [[V2]] -// CHECK: "sadd_with_overflow_Int64"([[E]] -// CHECK: [[V3:%[0-9]+]] = struct $Int32 -// CHECK: bb2: -// CHECK-NOT: store -// CHECK: cond_br undef, bb1([[V3]] : $Int32), bb3 -// CHECK: bb3: -// CHECK: store [[V3]] to %0 -// CHECK: br bb6 -// CHECK: bb4: -// CHECK-NOT: store -// CHECK: cond_br undef, bb1([[V3]] : $Int32), bb5 -// CHECK: bb5: -// CHECK: store [[V3]] to %0 -// CHECK: br bb6 -// CHECK: bb6: -// CHECK: } // end sil function 'hoist_loads_and_stores_multiple_exits' -sil @hoist_loads_and_stores_multiple_exits : $@convention(thin) (@inout Int32, Int32) -> () { -// %0 // users: %14, %17, %5, %2 -// %1 // user: %3 -bb0(%0 : $*Int32, %1 : $Int32): - %2 = struct_element_addr %0 : $*Int32, #Int32._value - %3 = struct_extract %1 : $Int32, #Int32._value // user: %9 - %4 = integer_literal $Builtin.Int1, 0 // user: %9 - %5 = load %0 : $*Int32 // user: %6 - br bb1(%5 : $Int32) // id: %6 - -// %7 // user: %8 -bb1(%7 : $Int32): // Preds: bb0 bb2 bb4 - %8 = struct_extract %7 : $Int32, #Int32._value // user: %9 - %9 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int32, %3 : $Builtin.Int32, %4 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) // user: %10 - %10 = tuple_extract %9 : $(Builtin.Int32, Builtin.Int1), 0 // user: %11 - %11 = struct $Int32 (%10 : $Builtin.Int32) // users: %14, %17, %13, %16 - cond_br undef, bb2, bb4 // id: %12 - -bb2: // Preds: bb1 - cond_br undef, bb1(%11 : $Int32), bb3 // id: %13 - -bb3: // Preds: bb2 - store %11 to %0 : $*Int32 // id: %14 - br bb6 // id: %15 - -bb4: // Preds: bb1 - cond_br undef, bb1(%11 : $Int32), bb5 // id: %16 - -bb5: // Preds: bb4 - store %11 to %0 : $*Int32 // id: %17 - br bb6 // id: %18 - -bb6: // Preds: bb3 bb5 - %19 = tuple () // user: %20 - return %19 : $() // id: %20 -} // end sil function 'hoist_loads_and_stores' - diff --git a/test/SILOptimizer/licm_apply.sil b/test/SILOptimizer/licm_apply.sil index 0f6a3ae31b78a..376e695e3e017 100644 --- a/test/SILOptimizer/licm_apply.sil +++ b/test/SILOptimizer/licm_apply.sil @@ -14,16 +14,10 @@ bb0(%0 : $*Int64, %1 : $Int64): } //CHECK-LABEL: sil @licm_readonly_apply -//CHECK: [[V1:%[0-9]+]] = load -//CHECK: %{{[0-9]+}} = apply -//CHECK: br bb1([[V1]] : $Int64) -//CHECK: bb1({{.*}} : $Int64): +//CHECK: %{{[0-9]+}} = apply +//CHECK: bb1: //CHECK-NOT: {{ apply}} -//CHECK-NOT: load -//CHECK-NOT: store -//CHECK: bb2: -//CHECK: store -//CHECK: return +//CHECK: return sil @licm_readonly_apply : $@convention(thin) (Int64, @inout Int64) -> () { bb0(%0 : $Int64, %1 : $*Int64): %2 = alloc_stack $Int64 diff --git a/test/SILOptimizer/licm_exclusivity.sil b/test/SILOptimizer/licm_exclusivity.sil index 6e7a5fd821cb7..ebe67b8a8d49b 100644 --- a/test/SILOptimizer/licm_exclusivity.sil +++ b/test/SILOptimizer/licm_exclusivity.sil @@ -186,9 +186,9 @@ bb2: // CHECK: [[GLOBALY:%.*]] = global_addr @globalY : $*X // CHECK: [[BEGIN:%.*]] = begin_access [read] [static] [[GLOBAL]] : $*X // CHECK: [[BEGINY:%.*]] = begin_access [read] [dynamic] [[GLOBALY]] : $*X -// CHECK-NEXT: load [[BEGIN]] -// CHECK-NEXT: load [[BEGINY]] // CHECK-NEXT: br bb1 +// CHECK: load +// CHECK: load // CHECK: cond_br // CHECK: bb2 // CHECK: end_access [[BEGINY]] diff --git a/test/SILOptimizer/licm_multiend.sil b/test/SILOptimizer/licm_multiend.sil index 260b63061f05e..d1c4c7f888292 100644 --- a/test/SILOptimizer/licm_multiend.sil +++ b/test/SILOptimizer/licm_multiend.sil @@ -26,20 +26,20 @@ sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage // CHECK: bb2: // CHECK: [[GLOBALVAR:%.*]] = global_addr @$s3tmp1xSivp : $*Int // CHECK: [[BEGINA:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]] : $*Int -// CHECK: br [[LOOPH:bb[0-9]+]]({{.*}} : $Builtin.Int64) +// CHECK-NEXT: br [[LOOPH:bb[0-9]+]]({{.*}} : $Builtin.Int64) // CHECK: [[LOOPH]]({{.*}} : $Builtin.Int64) // CHECK: cond_br {{.*}}, [[LOOPCOND1:bb[0-9]+]], [[LOOPCOND2:bb[0-9]+]] // CHECK: [[LOOPCOND1]]: +// CHECK-NEXT: store // CHECK-NEXT: cond_br {{.*}}, [[LOOPEXIT1:bb[0-9]+]], [[LOOPCONT1:bb[0-9]+]] // CHECK: [[LOOPEXIT1]]: -// CHECK-NEXT: store // CHECK-NEXT: end_access [[BEGINA]] : $*Int // CHECK-NEXT: br [[LOOPAFTEREXIT:bb[0-9]+]] // CHECK: [[LOOPCOND2]]: // CHECK-NEXT: struct $Int +// CHECK-NEXT: store // CHECK-NEXT: cond_br {{.*}}, [[LOOPEXIT2:bb[0-9]+]], [[LOOPCONT1]] // CHECK: [[LOOPEXIT2]]: -// CHECK-NEXT: store // CHECK-NEXT: end_access [[BEGINA]] : $*Int // CHECK-NEXT: br [[LOOPAFTEREXIT]] // CHECK: [[LOOPCONT1]]: @@ -71,7 +71,7 @@ bb0: cond_br %16, bb1, bb2 bb1: - br bb12 + br bbRet bb2: %19 = global_addr @$s3tmp1xSivp : $*Int @@ -101,14 +101,14 @@ bb4(%27 : $Builtin.Int64): %global = begin_access [modify] [dynamic] [no_nested_conflict] %19 : $*Int %46 = builtin "cmp_eq_Int64"(%29 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1 %47 = builtin "int_expect_Int1"(%46 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1 - cond_br %47, bb10, bb11 - -bb10: + cond_br %47, bbend1, bbend2 + +bbend1: store %43 to %global : $*Int end_access %global : $*Int cond_br %47, bb6, bb5 - -bb11: + +bbend2: %otherInt = struct $Int (%27 : $Builtin.Int64) store %otherInt to %global : $*Int end_access %global : $*Int @@ -118,9 +118,9 @@ bb5: br bb4(%29 : $Builtin.Int64) bb6: - br bb12 - -bb12: + br bbRet + +bbRet: %25 = tuple () return %25 : $() } // end sil function 'multi_end_licm'