/// Set up epilogue work for the thunk arguments based in the given argument. /// Default implementation simply passes it through. void FunctionSignatureTransform:: OwnedToGuaranteedAddArgumentRelease(ArgumentDescriptor &AD, SILBuilder &Builder, SILFunction *F) { // If we have any arguments that were consumed but are now guaranteed, // insert a release_value. if (!AD.OwnedToGuaranteed) { return; } SILInstruction *Call = findOnlyApply(F); if (isa<ApplyInst>(Call)) { Builder.setInsertionPoint(&*std::next(SILBasicBlock::iterator(Call))); Builder.createReleaseValue(RegularLocation(SourceLoc()), F->getArguments()[AD.Index], Builder.getDefaultAtomicity()); } else { SILBasicBlock *NormalBB = dyn_cast<TryApplyInst>(Call)->getNormalBB(); Builder.setInsertionPoint(&*NormalBB->begin()); Builder.createReleaseValue(RegularLocation(SourceLoc()), F->getArguments()[AD.Index], Builder.getDefaultAtomicity()); SILBasicBlock *ErrorBB = dyn_cast<TryApplyInst>(Call)->getErrorBB(); Builder.setInsertionPoint(&*ErrorBB->begin()); Builder.createReleaseValue(RegularLocation(SourceLoc()), F->getArguments()[AD.Index], Builder.getDefaultAtomicity()); } }
static SILFunction * moveFunctionBodyToNewFunctionWithName(SILFunction *F, const std::string &NewFName, SignatureOptimizer &Optimizer) { // First we create an empty function (i.e. no BB) whose function signature has // had its arity modified. // // We only do this to remove dead arguments. All other function signature // optimization is done later by modifying the function signature elements // themselves. SILFunction *NewF = Optimizer.createEmptyFunctionWithOptimizedSig(NewFName); // Then we transfer the body of F to NewF. At this point, the arguments of the // first BB will not match. NewF->spliceBody(F); // Do the same with the call graph. // Then perform any updates to the arguments of NewF. SILBasicBlock *NewFEntryBB = &*NewF->begin(); MutableArrayRef<ArgumentDescriptor> ArgDescs = Optimizer.getArgDescList(); unsigned ArgOffset = 0; SILBuilder Builder(NewFEntryBB->begin()); Builder.setCurrentDebugScope(NewFEntryBB->getParent()->getDebugScope()); for (auto &ArgDesc : ArgDescs) { // We always need to reset the insertion point in case we delete the first // instruction. Builder.setInsertionPoint(NewFEntryBB->begin()); DEBUG(llvm::dbgs() << "Updating arguments at ArgOffset: " << ArgOffset << " for: " << *ArgDesc.Arg); ArgOffset = ArgDesc.updateOptimizedBBArgs(Builder, NewFEntryBB, ArgOffset); } // Otherwise generate the thunk body just in case. SILBasicBlock *ThunkBody = F->createBasicBlock(); for (auto &ArgDesc : ArgDescs) { ThunkBody->createBBArg(ArgDesc.Arg->getType(), ArgDesc.Decl); } createThunkBody(ThunkBody, NewF, Optimizer); F->setThunk(IsThunk); assert(F->getDebugScope()->Parent != NewF->getDebugScope()->Parent); return NewF; }
/// Optimize placement of initializer calls given a list of calls to the /// same initializer. All original initialization points must be dominated by /// the final initialization calls. /// /// The current heuristic hoists all initialization points within a function to /// a single dominating call in the outer loop preheader. void SILGlobalOpt::placeInitializers(SILFunction *InitF, ArrayRef<ApplyInst *> Calls) { LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: calls to " << Demangle::demangleSymbolAsString(InitF->getName()) << " : " << Calls.size() << "\n"); // Map each initializer-containing function to its final initializer call. llvm::DenseMap<SILFunction *, ApplyInst *> ParentFuncs; for (auto *AI : Calls) { assert(AI->getNumArguments() == 0 && "ill-formed global init call"); assert(cast<FunctionRefInst>(AI->getCallee())->getReferencedFunction() == InitF && "wrong init call"); SILFunction *ParentF = AI->getFunction(); DominanceInfo *DT = DA->get(ParentF); ApplyInst *HoistAI = getHoistedApplyForInitializer(AI, DT, InitF, ParentF, ParentFuncs); // If we were unable to find anything, just go onto the next apply. if (!HoistAI) { continue; } // Otherwise, move this call to the outermost loop preheader. SILBasicBlock *BB = HoistAI->getParent(); typedef llvm::DomTreeNodeBase<SILBasicBlock> DomTreeNode; DomTreeNode *Node = DT->getNode(BB); while (Node) { SILBasicBlock *DomParentBB = Node->getBlock(); if (isAvailabilityCheck(DomParentBB)) { LLVM_DEBUG(llvm::dbgs() << " don't hoist above availability check " "at bb" << DomParentBB->getDebugID() << "\n"); break; } BB = DomParentBB; if (!isInLoop(BB)) break; Node = Node->getIDom(); } if (BB == HoistAI->getParent()) { // BB is either unreachable or not in a loop. LLVM_DEBUG(llvm::dbgs() << " skipping (not in a loop): " << *HoistAI << " in " << HoistAI->getFunction()->getName() << "\n"); continue; } LLVM_DEBUG(llvm::dbgs() << " hoisting: " << *HoistAI << " in " << HoistAI->getFunction()->getName() << "\n"); HoistAI->moveBefore(&*BB->begin()); placeFuncRef(HoistAI, DT); HasChanged = true; } }
/// Walk backwards from an unsafeGuaranteedEnd builtin instruction looking for a /// release on the reference returned by the matching unsafeGuaranteed builtin /// ignoring releases on the way. /// /// %4 = builtin "unsafeGuaranteed"<Foo>(%0 : $Foo) : $(Foo, Builtin.Int8) /// %5 = tuple_extract %4 : $(Foo, Builtin.Int8), 0 /// %6 = tuple_extract %4 : $(Foo, Builtin.Int8), 1 /// strong_release %5 : $Foo // <-- Matching release. /// strong_release %6 : $Foo // Ignore. /// %12 = builtin "unsafeGuaranteedEnd"(%6 : $Builtin.Int8) : $() /// static SILBasicBlock::iterator findReleaseToMatchUnsafeGuaranteedValue(SILInstruction *UnsafeGuaranteedEndI, SILValue UnsafeGuaranteedValue, SILBasicBlock &BB) { auto UnsafeGuaranteedEndIIt = SILBasicBlock::iterator(UnsafeGuaranteedEndI); if (UnsafeGuaranteedEndIIt == BB.begin()) return BB.end(); auto LastReleaseIt = std::prev(UnsafeGuaranteedEndIIt); while (LastReleaseIt != BB.begin() && (isa<StrongReleaseInst>(*LastReleaseIt) || isa<ReleaseValueInst>(*LastReleaseIt)) && LastReleaseIt->getOperand(0) != UnsafeGuaranteedValue) --LastReleaseIt; if ((!isa<StrongReleaseInst>(*LastReleaseIt) && !isa<ReleaseValueInst>(*LastReleaseIt)) || LastReleaseIt->getOperand(0) != UnsafeGuaranteedValue) { return BB.end(); } return LastReleaseIt; }
void FunctionSignatureTransform::ArgumentExplosionFinalizeOptimizedFunction() { SILBasicBlock *BB = &*NewF->begin(); SILBuilder Builder(BB->begin()); Builder.setCurrentDebugScope(BB->getParent()->getDebugScope()); unsigned TotalArgIndex = 0; for (ArgumentDescriptor &AD : ArgumentDescList) { // Simply continue if do not explode. if (!AD.Explode) { AIM[TotalArgIndex] = AD.Index; TotalArgIndex ++; continue; } // OK, we need to explode this argument. unsigned ArgOffset = ++TotalArgIndex; unsigned OldArgIndex = ArgOffset - 1; llvm::SmallVector<SILValue, 8> LeafValues; // We do this in the same order as leaf types since ProjTree expects that the // order of leaf values matches the order of leaf types. llvm::SmallVector<const ProjectionTreeNode*, 8> LeafNodes; AD.ProjTree.getLeafNodes(LeafNodes); for (auto *Node : LeafNodes) { auto OwnershipKind = *AD.getTransformedOwnershipKind(Node->getType()); LeafValues.push_back(BB->insertFunctionArgument( ArgOffset++, Node->getType(), OwnershipKind, BB->getArgument(OldArgIndex)->getDecl())); AIM[TotalArgIndex - 1] = AD.Index; TotalArgIndex ++; } // Then go through the projection tree constructing aggregates and replacing // uses. AD.ProjTree.replaceValueUsesWithLeafUses(Builder, BB->getParent()->getLocation(), LeafValues); // We ignored debugvalue uses when we constructed the new arguments, in order // to preserve as much information as possible, we construct a new value for // OrigArg from the leaf values and use that in place of the OrigArg. SILValue NewOrigArgValue = AD.ProjTree.computeExplodedArgumentValue(Builder, BB->getParent()->getLocation(), LeafValues); // Replace all uses of the original arg with the new value. SILArgument *OrigArg = BB->getArgument(OldArgIndex); OrigArg->replaceAllUsesWith(NewOrigArgValue); // Now erase the old argument since it does not have any uses. We also // decrement ArgOffset since we have one less argument now. BB->eraseArgument(OldArgIndex); TotalArgIndex --; } }
/// At least one value feeding the specified SILArgument is a Struct. Attempt to /// replace the Argument with a new Struct in the same block. /// /// When we handle more types of casts, this can become a template. /// /// ArgValues are the values feeding the specified Argument from each /// predecessor. They must be listed in order of Arg->getParent()->getPreds(). static StructInst * replaceBBArgWithStruct(SILPhiArgument *Arg, SmallVectorImpl<SILValue> &ArgValues) { SILBasicBlock *PhiBB = Arg->getParent(); auto *FirstSI = dyn_cast<StructInst>(ArgValues[0]); if (!FirstSI) return nullptr; // Collect the BBArg index of each struct oper. // e.g. // struct(A, B) // br (B, A) // : ArgIdxForOper => {1, 0} SmallVector<unsigned, 4> ArgIdxForOper; for (unsigned OperIdx : indices(FirstSI->getElements())) { bool FoundMatchingArgIdx = false; for (unsigned ArgIdx : indices(PhiBB->getArguments())) { SmallVectorImpl<SILValue>::const_iterator AVIter = ArgValues.begin(); bool TryNextArgIdx = false; for (SILBasicBlock *PredBB : PhiBB->getPredecessorBlocks()) { // All argument values must be StructInst. auto *PredSI = dyn_cast<StructInst>(*AVIter++); if (!PredSI) return nullptr; OperandValueArrayRef EdgeValues = getEdgeValuesForTerminator(PredBB->getTerminator(), PhiBB); if (EdgeValues[ArgIdx] != PredSI->getElements()[OperIdx]) { TryNextArgIdx = true; break; } } if (!TryNextArgIdx) { assert(AVIter == ArgValues.end() && "# ArgValues does not match # BB preds"); FoundMatchingArgIdx = true; ArgIdxForOper.push_back(ArgIdx); break; } } if (!FoundMatchingArgIdx) return nullptr; } SmallVector<SILValue, 4> StructArgs; for (auto ArgIdx : ArgIdxForOper) StructArgs.push_back(PhiBB->getArgument(ArgIdx)); SILBuilder Builder(PhiBB, PhiBB->begin()); return Builder.createStruct(cast<StructInst>(ArgValues[0])->getLoc(), Arg->getType(), StructArgs); }
static TryApplyInst *replaceTryApplyInst(SILBuilder &B, SILLocation Loc, TryApplyInst *OldTAI, SILValue NewFn, SubstitutionMap NewSubs, ArrayRef<SILValue> NewArgs, SILFunctionConventions Conv) { SILBasicBlock *NormalBB = OldTAI->getNormalBB(); SILBasicBlock *ResultBB = nullptr; SILType NewResultTy = Conv.getSILResultType(); // Does the result value need to be casted? auto OldResultTy = NormalBB->getArgument(0)->getType(); bool ResultCastRequired = NewResultTy != OldResultTy; // Create a new normal BB only if the result of the new apply differs // in type from the argument of the original normal BB. if (!ResultCastRequired) { ResultBB = NormalBB; } else { ResultBB = B.getFunction().createBasicBlockBefore(NormalBB); ResultBB->createPHIArgument(NewResultTy, ValueOwnershipKind::Owned); } // We can always just use the original error BB because we'll be // deleting the edge to it from the old TAI. SILBasicBlock *ErrorBB = OldTAI->getErrorBB(); // Insert a try_apply here. // Note that this makes this block temporarily double-terminated! // We won't fix that until deleteDevirtualizedApply. auto NewTAI = B.createTryApply(Loc, NewFn, NewSubs, NewArgs, ResultBB, ErrorBB); if (ResultCastRequired) { B.setInsertionPoint(ResultBB); SILValue ResultValue = ResultBB->getArgument(0); ResultValue = castValueToABICompatibleType(&B, Loc, ResultValue, NewResultTy, OldResultTy); B.createBranch(Loc, NormalBB, { ResultValue }); } B.setInsertionPoint(NormalBB->begin()); return NewTAI; }
/// Attempt to hoist a destroy point up to the last use. If the last use is a /// copy, eliminate both the copy and the destroy. /// /// The copy will be eliminated if the original is not accessed between the /// point of copy and the original's destruction. /// /// Def = <uniquely identified> // no aliases /// ... /// Copy = copy_addr [init] Def /// ... // no access to Def /// destroy_addr Def /// /// Return true if a destroy was inserted, forwarded from a copy, or the /// block was marked dead-in. bool CopyForwarding::hoistDestroy(SILInstruction *DestroyPoint, SILLocation DestroyLoc) { if (!EnableDestroyHoisting) return false; assert(!SrcUserInsts.count(DestroyPoint) && "caller should check terminator"); SILBasicBlock *BB = DestroyPoint->getParent(); // If DestroyPoint is a block terminator, we must hoist. bool MustHoist = (DestroyPoint == BB->getTerminator()); bool IsWorthHoisting = MustHoist; auto SI = DestroyPoint->getIterator(), SE = BB->begin(); while (SI != SE) { --SI; SILInstruction *Inst = &*SI; if (!SrcUserInsts.count(Inst)) { if (!IsWorthHoisting && isa<ApplyInst>(Inst)) IsWorthHoisting = true; continue; } if (auto *CopyInst = dyn_cast<CopyAddrInst>(Inst)) { if (!CopyInst->isTakeOfSrc() && CopyInst->getSrc() == CurrentDef) { // This use is a copy of CurrentDef. Attempt to forward CurrentDef to // all uses of the copy's value. if (propagateCopy(CopyInst)) return true; } } // We reached a user of CurrentDef. If we haven't seen anything significant, // avoid useless hoisting. if (!IsWorthHoisting) return false; DEBUG(llvm::dbgs() << " Hoisting to Use:" << *Inst); SILBuilderWithScope(std::next(SI), Inst) .createDestroyAddr(DestroyLoc, CurrentDef); HasChanged = true; return true; } if (!DoGlobalHoisting) return false; DeadInBlocks.insert(BB); return true; }
bool StackPromoter::promote() { llvm::SetVector<SILBasicBlock *> ReachableBlocks; // First step: find blocks which end up in a no-return block (terminated by // an unreachable instruction). // Search for function-exiting blocks, i.e. return and throw. for (SILBasicBlock &BB : *F) { TermInst *TI = BB.getTerminator(); if (TI->isFunctionExiting()) ReachableBlocks.insert(&BB); } // Propagate the reachability up the control flow graph. unsigned Idx = 0; while (Idx < ReachableBlocks.size()) { SILBasicBlock *BB = ReachableBlocks[Idx++]; for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) ReachableBlocks.insert(Pred); } bool Changed = false; // Search the whole function for stack promotable allocations. for (SILBasicBlock &BB : *F) { // Don't stack promote any allocation inside a code region which ends up in // a no-return block. Such allocations may missing their final release. // We would insert the deallocation too early, which may result in a // use-after-free problem. if (ReachableBlocks.count(&BB) == 0) continue; for (auto Iter = BB.begin(); Iter != BB.end();) { // The allocation instruction may be moved, so increment Iter prior to // doing the optimization. SILInstruction *I = &*Iter++; if (auto *ARI = dyn_cast<AllocRefInst>(I)) { Changed |= tryPromoteAlloc(ARI); } } } return Changed; }
void ARCRegionState::addInterestingInst(SILInstruction *TargetInst) { // Insert I into its location in the interesting instruction list. SILBasicBlock *BB = getRegion()->getBlock(); assert(TargetInst->getParent() == BB); auto II = BB->begin(); auto IE = BB->end(); assert(II != IE && "I can not be an element of an empty block"); auto SI = SummarizedInterestingInsts.begin(); auto SE = SummarizedInterestingInsts.end(); while (II != IE) { if (SI == SE) { // Ok, TargetInst is after all of the interesting insts. Append it to the // list. SummarizedInterestingInsts.push_back(TargetInst); return; } // Move II down the block until it hits TargetInst or the first // SummarizedInterestingInst. while (&*II != *SI && &*II != TargetInst) { ++II; } // If II == SI and TargetInst == II then there is nothing further to do. if (&*II == TargetInst) { assert(&*II != *SI); SummarizedInterestingInsts.insert(SI, TargetInst); return; } // If we reach this point, then we know that II == SI and we have not found // TargetInst yet. So we move to the next II, SI. ++II; ++SI; } llvm_unreachable("Could not find Inst in the block?!"); }
ApplyInst *SILGlobalOpt::getHoistedApplyForInitializer( ApplyInst *AI, DominanceInfo *DT, SILFunction *InitF, SILFunction *ParentF, llvm::DenseMap<SILFunction *, ApplyInst *> &ParentFuncs) { auto PFI = ParentFuncs.find(ParentF); if (PFI == ParentFuncs.end()) { ParentFuncs[ParentF] = AI; // It's the first time we found a call to InitF in this function, so we // try to hoist it out of any loop. return AI; } // Found a replacement for this init call. Ensure the replacement dominates // the original call site. ApplyInst *CommonAI = PFI->second; assert( cast<FunctionRefInst>(CommonAI->getCallee())->getReferencedFunction() == InitF && "ill-formed global init call"); SILBasicBlock *DomBB = DT->findNearestCommonDominator(AI->getParent(), CommonAI->getParent()); // We must not move initializers around availability-checks. if (isAvailabilityCheckOnDomPath(DomBB, CommonAI->getParent(), DT)) return nullptr; ApplyInst *Result = nullptr; if (DomBB != CommonAI->getParent()) { CommonAI->moveBefore(&*DomBB->begin()); placeFuncRef(CommonAI, DT); // Try to hoist the existing AI again if we move it to another block, // e.g. from a loop exit into the loop. Result = CommonAI; } AI->replaceAllUsesWith(CommonAI); AI->eraseFromParent(); HasChanged = true; return Result; }
void FunctionSignatureTransform:: OwnedToGuaranteedAddResultRelease(ResultDescriptor &RD, SILBuilder &Builder, SILFunction *F) { // If we have any result that were consumed but are now guaranteed, // insert a release_value. if (!RD.OwnedToGuaranteed) { return; } SILInstruction *Call = findOnlyApply(F); if (isa<ApplyInst>(Call)) { Builder.setInsertionPoint(&*std::next(SILBasicBlock::iterator(Call))); Builder.createRetainValue(RegularLocation(SourceLoc()), Call, Atomicity::Atomic); } else { SILBasicBlock *NormalBB = dyn_cast<TryApplyInst>(Call)->getNormalBB(); Builder.setInsertionPoint(&*NormalBB->begin()); Builder.createRetainValue(RegularLocation(SourceLoc()), NormalBB->getArgument(0), Atomicity::Atomic); } }
/// Perform outlining on the function and return any newly created outlined /// functions. bool tryOutline(SILOptFunctionBuilder &FuncBuilder, SILFunction *Fun, SmallVectorImpl<SILFunction *> &FunctionsAdded) { SmallPtrSet<SILBasicBlock *, 32> Visited; SmallVector<SILBasicBlock *, 128> Worklist; OutlinePatterns patterns(FuncBuilder); // Traverse the function. Worklist.push_back(&*Fun->begin()); while (!Worklist.empty()) { SILBasicBlock *CurBlock = Worklist.pop_back_val(); if (!Visited.insert(CurBlock).second) continue; SILBasicBlock::iterator CurInst = CurBlock->begin(); // Go over the instructions trying to match and replace patterns. while (CurInst != CurBlock->end()) { if (OutlinePattern *match = patterns.tryToMatch(CurInst)) { SILFunction *F; SILBasicBlock::iterator LastInst; std::tie(F, LastInst) = match->outline(Fun->getModule()); if (F) FunctionsAdded.push_back(F); CurInst = LastInst; assert(LastInst->getParent() == CurBlock); } else if (isa<TermInst>(CurInst)) { std::copy(CurBlock->succ_begin(), CurBlock->succ_end(), std::back_inserter(Worklist)); ++CurInst; } else { ++CurInst; } } } return false; }
/// \brief Devirtualize an apply of a class method. /// /// \p AI is the apply to devirtualize. /// \p ClassOrMetatype is a class value or metatype value that is the /// self argument of the apply we will devirtualize. /// return the result value of the new ApplyInst if created one or null. DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI, SILValue ClassOrMetatype) { DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction()); SILModule &Mod = AI.getModule(); auto *CMI = cast<ClassMethodInst>(AI.getCallee()); auto ClassOrMetatypeType = ClassOrMetatype.getType(); auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, CMI->getMember()); CanSILFunctionType GenCalleeType = F->getLoweredFunctionType(); auto Subs = getSubstitutionsForCallee(Mod, GenCalleeType, ClassOrMetatypeType, AI); CanSILFunctionType SubstCalleeType = GenCalleeType; if (GenCalleeType->isPolymorphic()) SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Mod.getSwiftModule(), Subs); SILBuilderWithScope B(AI.getInstruction()); FunctionRefInst *FRI = B.createFunctionRef(AI.getLoc(), F); // Create the argument list for the new apply, casting when needed // in order to handle covariant indirect return types and // contravariant argument types. llvm::SmallVector<SILValue, 8> NewArgs; auto Args = AI.getArguments(); auto ParamTypes = SubstCalleeType->getParameterSILTypes(); for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) NewArgs.push_back(castValueToABICompatibleType(&B, AI.getLoc(), Args[i], Args[i].getType(), ParamTypes[i]).getValue()); // Add the self argument, upcasting if required because we're // calling a base class's method. auto SelfParamTy = SubstCalleeType->getSelfParameter().getSILType(); NewArgs.push_back(castValueToABICompatibleType(&B, AI.getLoc(), ClassOrMetatype, ClassOrMetatypeType, SelfParamTy).getValue()); // If we have a direct return type, make sure we use the subst callee return // type. If we have an indirect return type, AI's return type of the empty // tuple should be ok. SILType ResultTy = AI.getType(); if (!SubstCalleeType->hasIndirectResult()) { ResultTy = SubstCalleeType->getSILResult(); } SILType SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); FullApplySite NewAI; SILBasicBlock *ResultBB = nullptr; SILBasicBlock *NormalBB = nullptr; SILValue ResultValue; bool ResultCastRequired = false; SmallVector<Operand *, 4> OriginalResultUses; if (!isa<TryApplyInst>(AI)) { NewAI = B.createApply(AI.getLoc(), FRI, SubstCalleeSILType, ResultTy, Subs, NewArgs, cast<ApplyInst>(AI)->isNonThrowing()); ResultValue = SILValue(NewAI.getInstruction(), 0); } else { auto *TAI = cast<TryApplyInst>(AI); // Create new normal and error BBs only if: // - re-using a BB would create a critical edge // - or, the result of the new apply would be of different // type than the argument of the original normal BB. if (TAI->getNormalBB()->getSinglePredecessor()) ResultBB = TAI->getNormalBB(); else { ResultBB = B.getFunction().createBasicBlock(); ResultBB->createBBArg(ResultTy); } NormalBB = TAI->getNormalBB(); SILBasicBlock *ErrorBB = nullptr; if (TAI->getErrorBB()->getSinglePredecessor()) ErrorBB = TAI->getErrorBB(); else { ErrorBB = B.getFunction().createBasicBlock(); ErrorBB->createBBArg(TAI->getErrorBB()->getBBArg(0)->getType()); } NewAI = B.createTryApply(AI.getLoc(), FRI, SubstCalleeSILType, Subs, NewArgs, ResultBB, ErrorBB); if (ErrorBB != TAI->getErrorBB()) { B.setInsertionPoint(ErrorBB); B.createBranch(TAI->getLoc(), TAI->getErrorBB(), {ErrorBB->getBBArg(0)}); } // Does the result value need to be casted? ResultCastRequired = ResultTy != NormalBB->getBBArg(0)->getType(); if (ResultBB != NormalBB) B.setInsertionPoint(ResultBB); else if (ResultCastRequired) { B.setInsertionPoint(NormalBB->begin()); // Collect all uses, before casting. for (auto *Use : NormalBB->getBBArg(0)->getUses()) { OriginalResultUses.push_back(Use); } NormalBB->getBBArg(0)->replaceAllUsesWith(SILUndef::get(AI.getType(), Mod)); NormalBB->replaceBBArg(0, ResultTy, nullptr); } // The result value is passed as a parameter to the normal block. ResultValue = ResultBB->getBBArg(0); } // Check if any casting is required for the return value. ResultValue = castValueToABICompatibleType(&B, NewAI.getLoc(), ResultValue, ResultTy, AI.getType()).getValue(); DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; if (NormalBB) { if (NormalBB != ResultBB) { // If artificial normal BB was introduced, branch // to the original normal BB. B.createBranch(NewAI.getLoc(), NormalBB, { ResultValue }); } else if (ResultCastRequired) { // Update all original uses by the new value. for(auto *Use: OriginalResultUses) { Use->set(ResultValue); } } return std::make_pair(NewAI.getInstruction(), NewAI); } // We need to return a pair of values here: // - the first one is the actual result of the devirtualized call, possibly // casted into an appropriate type. This SILValue may be a BB arg, if it // was a cast between optional types. // - the second one is the new apply site. return std::make_pair(ResultValue.getDef(), NewAI); }
/// Optimize placement of initializer calls given a list of calls to the /// same initializer. All original initialization points must be dominated by /// the final initialization calls. /// /// The current heuristic hoists all initialization points within a function to /// a single dominating call in the outer loop preheader. void SILGlobalOpt::placeInitializers(SILFunction *InitF, ArrayRef<ApplyInst*> Calls) { DEBUG(llvm::dbgs() << "GlobalOpt: calls to " << demangle_wrappers::demangleSymbolAsString(InitF->getName()) << " : " << Calls.size() << "\n"); // Map each initializer-containing function to its final initializer call. llvm::DenseMap<SILFunction*, ApplyInst*> ParentFuncs; for (auto *AI : Calls) { assert(AI->getNumArguments() == 0 && "ill-formed global init call"); assert(cast<FunctionRefInst>(AI->getCallee())->getReferencedFunction() == InitF && "wrong init call"); SILFunction *ParentF = AI->getFunction(); DominanceInfo *DT = DA->get(ParentF); auto PFI = ParentFuncs.find(ParentF); ApplyInst *HoistAI = nullptr; if (PFI != ParentFuncs.end()) { // Found a replacement for this init call. // Ensure the replacement dominates the original call site. ApplyInst *CommonAI = PFI->second; assert(cast<FunctionRefInst>(CommonAI->getCallee()) ->getReferencedFunction() == InitF && "ill-formed global init call"); SILBasicBlock *DomBB = DT->findNearestCommonDominator(AI->getParent(), CommonAI->getParent()); // We must not move initializers around availability-checks. if (!isAvailabilityCheckOnDomPath(DomBB, CommonAI->getParent(), DT)) { if (DomBB != CommonAI->getParent()) { CommonAI->moveBefore(&*DomBB->begin()); placeFuncRef(CommonAI, DT); // Try to hoist the existing AI again if we move it to another block, // e.g. from a loop exit into the loop. HoistAI = CommonAI; } AI->replaceAllUsesWith(CommonAI); AI->eraseFromParent(); HasChanged = true; } } else { ParentFuncs[ParentF] = AI; // It's the first time we found a call to InitF in this function, so we // try to hoist it out of any loop. HoistAI = AI; } if (HoistAI) { // Move this call to the outermost loop preheader. SILBasicBlock *BB = HoistAI->getParent(); typedef llvm::DomTreeNodeBase<SILBasicBlock> DomTreeNode; DomTreeNode *Node = DT->getNode(BB); while (Node) { SILBasicBlock *DomParentBB = Node->getBlock(); if (isAvailabilityCheck(DomParentBB)) { DEBUG(llvm::dbgs() << " don't hoist above availability check at bb" << DomParentBB->getDebugID() << "\n"); break; } BB = DomParentBB; if (!isInLoop(BB)) break; Node = Node->getIDom(); } if (BB == HoistAI->getParent()) { // BB is either unreachable or not in a loop. DEBUG(llvm::dbgs() << " skipping (not in a loop): " << *HoistAI << " in " << HoistAI->getFunction()->getName() << "\n"); } else { DEBUG(llvm::dbgs() << " hoisting: " << *HoistAI << " in " << HoistAI->getFunction()->getName() << "\n"); HoistAI->moveBefore(&*BB->begin()); placeFuncRef(HoistAI, DT); HasChanged = true; } } } }
/// Perform CopyForwarding on the current Def. void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { reset(F); CurrentDef = Def; DEBUG(llvm::dbgs() << "Analyzing copies of Def: " << Def); if (!collectUsers()) return; // First forward any copies that implicitly destroy CurrentDef. There is no // need to hoist Destroy for these. for (auto *CopyInst : TakePoints) propagateCopy(CopyInst); // If the copied address is also loaded from, then destroy hoisting is unsafe. // // TODO: Record all loads during collectUsers. Implement findRetainPoints to // peek though projections of the load, like unchecked_enum_data to find the // true extent of the lifetime including transitively referenced objects. if (IsLoadedFrom) return; bool HoistedDestroyFound = false; SILLocation HoistedDestroyLoc = F->getLocation(); const SILDebugScope *HoistedDebugScope = nullptr; for (auto *Destroy : DestroyPoints) { // If hoistDestroy returns false, it was not worth hoisting. if (hoistDestroy(Destroy, Destroy->getLoc())) { // Propagate DestroyLoc for any destroy hoisted above a block. if (DeadInBlocks.count(Destroy->getParent())) { HoistedDestroyLoc = Destroy->getLoc(); HoistedDebugScope = Destroy->getDebugScope(); HoistedDestroyFound = true; } // We either just created a new destroy, forwarded a copy, or will // continue propagating from this dead-in block. In any case, erase the // original Destroy. Destroy->eraseFromParent(); assert(HasChanged || !DeadInBlocks.empty() && "HasChanged should be set"); } } // Any blocks containing a DestroyPoints where hoistDestroy did not find a use // are now marked in DeadInBlocks. if (DeadInBlocks.empty()) return; assert(HoistedDestroyFound && "Hoisted destroy should have been found"); DestroyPoints.clear(); // Propagate dead-in blocks upward via PostOrder traversal. // TODO: We could easily handle hoisting above loops if LoopInfo is available. // for (auto *BB : PostOrder->get(F)->getPostOrder()) { SmallVector<unsigned, 4> DeadInSuccs; ArrayRef<SILSuccessor> Succs = BB->getSuccessors(); if (Succs.size() == 0) continue; for (unsigned EdgeIdx = 0, End = Succs.size(); EdgeIdx != End; ++EdgeIdx) { if (DeadInBlocks.count(Succs[EdgeIdx].getBB())) DeadInSuccs.push_back(EdgeIdx); } if (DeadInSuccs.size() == Succs.size() && !SrcUserInsts.count(BB->getTerminator())) { // All successors are dead, so continue hoisting. bool WasHoisted = hoistDestroy(BB->getTerminator(), HoistedDestroyLoc); (void)WasHoisted; assert(WasHoisted && "should always hoist above a terminator"); continue; } // Emit a destroy on each CFG edge leading to a dead-in block. This requires // splitting critical edges and will naturally handle redundant branch // targets. for (unsigned EdgeIdx : DeadInSuccs) { SILBasicBlock *SuccBB = splitCriticalEdge(BB->getTerminator(), EdgeIdx); if (SuccBB) HasChangedCFG = true; else SuccBB = BB->getSuccessors()[EdgeIdx]; // We make no attempt to use the best DebugLoc, because in all known // cases, we only have one. SILBuilder B(SuccBB->begin()); B.setCurrentDebugScope(HoistedDebugScope); B.createDestroyAddr(HoistedDestroyLoc, CurrentDef); HasChanged = true; } } }
static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst, SILValue SwitchOperand) { auto *SwitchEnum = dyn_cast<SwitchEnumInst>(Inst); if (!SwitchEnum || SwitchEnum->getNumCases() != 2 || SwitchEnum->getOperand() != SwitchOperand) return false; auto *SwitchBB = SwitchEnum->getParent(); SILBasicBlock *SomeBB = SwitchEnum->getCase(0).second; SILBasicBlock *NoneBB = SwitchEnum->getCase(1).second; if (NoneBB->getSinglePredecessorBlock() != SwitchBB) return false; if (SomeBB->getSinglePredecessorBlock() != SwitchBB) return false; if (NoneBB->args_size() == 1) std::swap(NoneBB, SomeBB); if (SomeBB->args_size() != 1 || NoneBB->args_size() != 0) return false; // bb9: // %43 = enum $Optional<String>, #Optional.none!enumelt auto It = NoneBB->begin(); auto *NoneEnum = dyn_cast<EnumInst>(It); if (!NoneEnum || NoneEnum->hasOperand() || !NoneEnum->hasOneUse()) return false; // br bb10(%43 : $Optional<String>) ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Br1 = dyn_cast<BranchInst>(It); if (!Br1 || Br1->getNumArgs() != 1 || Br1->getArg(0) != NoneEnum) return false; auto *MergeBB = Br1->getDestBB(); // bb8(%36 : $NSString): It = SomeBB->begin(); auto *SomeBBArg = SomeBB->getArgument(0); if (!SomeBBArg->hasOneUse()) return false; // %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String auto *FunRef = dyn_cast<FunctionRefInst>(It); if (!FunRef || !FunRef->hasOneUse()) return false; // %38 = enum $Optional<NSString>, #Optional.some!enumelt.1, %36 : $NSString ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *SomeEnum = dyn_cast<EnumInst>(It); if (!SomeEnum || !SomeEnum->hasOperand() || SomeEnum->getOperand() != SomeBBArg) return false; size_t numSomeEnumUses = std::distance(SomeEnum->use_begin(), SomeEnum->use_end()); if (numSomeEnumUses > 2) return false; // %39 = metatype $@thin String.Type ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Metatype = dyn_cast<MetatypeInst>(It); if (!Metatype || !Metatype->hasOneUse()) return false; // %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Apply = dyn_cast<ApplyInst>(It); if (!Apply || !Apply->hasOneUse() || Apply->getCallee() != FunRef || Apply->getNumArguments() != 2 || Apply->getArgument(0) != SomeEnum || Apply->getArgument(1) != Metatype || Apply->getSubstCalleeType()->getNumResults() != 1) return false; if (Apply->getSubstCalleeType()->getSingleResult().getConvention() != ResultConvention::Owned) return false; // Check that we call the _unconditionallyBridgeFromObjectiveC witness. auto NativeType = Apply->getType().getASTType(); auto *BridgeFun = FunRef->getReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); auto bridgeWitness = getBridgeFromObjectiveC(NativeType, SwiftModule); if (BridgeFun->getName() != bridgeWitness.mangle()) return false; // %41 = enum $Optional<String>, #Optional.some!enumelt.1, %40 : $String ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Enum3 = dyn_cast<EnumInst>(It); if (!Enum3 || !Enum3->hasOneUse() || !Enum3->hasOperand() || Enum3->getOperand() != Apply) return false; if (numSomeEnumUses == 2) { // release_value %38 : $Optional<NSString> ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *RVI = dyn_cast<ReleaseValueInst>(It); if (!RVI || RVI->getOperand() != SomeEnum) return false; } // br bb10(%41 : $Optional<String>) ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Br = dyn_cast<BranchInst>(It); if (!Br || Br->getDestBB() != MergeBB || Br->getNumArgs() != 1 || Br->getArg(0) != Enum3) return false; SI.SwitchEnum = SwitchEnum; SI.SomeBB = SomeBB; SI.NoneBB = NoneBB; SI.Br = Br; return true; }
/// \brief Issue an "unreachable code" diagnostic if the blocks contains or /// leads to another block that contains user code. /// /// Note, we rely on SILLocation information to determine if SILInstructions /// correspond to user code. static bool diagnoseUnreachableBlock(const SILBasicBlock &B, SILModule &M, const SILBasicBlockSet &Reachable, UnreachableUserCodeReportingState *State, const SILBasicBlock *TopLevelB, llvm::SmallPtrSetImpl<const SILBasicBlock*> &Visited){ if (Visited.count(&B)) return false; Visited.insert(&B); assert(State); for (auto I = B.begin(), E = B.end(); I != E; ++I) { SILLocation Loc = I->getLoc(); // If we've reached an implicit return, we have not found any user code and // can stop searching for it. if (Loc.is<ImplicitReturnLocation>() || Loc.isAutoGenerated()) return false; // Check if the instruction corresponds to user-written code, also make // sure we don't report an error twice for the same instruction. if (isUserCode(&*I) && !State->BlocksWithErrors.count(&B)) { // Emit the diagnostic. auto BrInfoIter = State->MetaMap.find(TopLevelB); assert(BrInfoIter != State->MetaMap.end()); auto BrInfo = BrInfoIter->second; switch (BrInfo.Kind) { case (UnreachableKind::FoldedBranch): // Emit the diagnostic on the unreachable block and emit the // note on the branch responsible for the unreachable code. diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::unreachable_code_branch, BrInfo.CondIsAlwaysTrue); break; case (UnreachableKind::FoldedSwitchEnum): { // If we are warning about a switch condition being a constant, the main // emphasis should be on the condition (to ensure we have a single // message per switch). const SwitchStmt *SS = BrInfo.Loc.getAsASTNode<SwitchStmt>(); if (!SS) break; assert(SS); const Expr *SE = SS->getSubjectExpr(); diagnose(M.getASTContext(), SE->getLoc(), diag::switch_on_a_constant); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code_note); break; } case (UnreachableKind::NoreturnCall): { // Specialcase when we are warning about unreachable code after a call // to a noreturn function. if (!BrInfo.Loc.isASTNode<ExplicitCastExpr>()) { assert(BrInfo.Loc.isASTNode<ApplyExpr>()); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::call_to_noreturn_note); } break; } } // Record that we've reported this unreachable block to avoid duplicates // in the future. State->BlocksWithErrors.insert(&B); return true; } } // This block could be empty if it's terminator has been folded. if (B.empty()) return false; // If we have not found user code in this block, inspect it's successors. // Check if at least one of the successors contains user code. for (auto I = B.succ_begin(), E = B.succ_end(); I != E; ++I) { SILBasicBlock *SB = *I; bool HasReachablePred = false; for (auto PI = SB->pred_begin(), PE = SB->pred_end(); PI != PE; ++PI) { if (Reachable.count(*PI)) HasReachablePred = true; } // If all of the predecessors of this successor are unreachable, check if // it contains user code. if (!HasReachablePred && diagnoseUnreachableBlock(*SB, M, Reachable, State, TopLevelB, Visited)) return true; } return false; }
static bool simplifyBlocksWithCallsToNoReturn(SILBasicBlock &BB, UnreachableUserCodeReportingState *State) { auto I = BB.begin(), E = BB.end(); bool DiagnosedUnreachableCode = false; SILInstruction *NoReturnCall = nullptr; // Collection of all instructions that should be deleted. llvm::SmallVector<SILInstruction*, 32> ToBeDeleted; // If all of the predecessor blocks end in a try_apply to a noreturn // function, the entire block is dead. NoReturnCall = getPrecedingCallToNoReturn(BB); // Does this block contain a call to a noreturn function? while (I != E) { auto *CurrentInst = &*I; // Move the iterator before we remove instructions to avoid iterator // invalidation issues. ++I; // Remove all instructions following the noreturn call. if (NoReturnCall) { // We will need to delete the instruction later on. ToBeDeleted.push_back(CurrentInst); // Diagnose the unreachable code within the same block as the call to // noreturn. if (isUserCode(CurrentInst) && !DiagnosedUnreachableCode) { if (NoReturnCall->getLoc().is<RegularLocation>()) { if (!NoReturnCall->getLoc().isASTNode<ExplicitCastExpr>()) { diagnose(BB.getModule().getASTContext(), CurrentInst->getLoc().getSourceLoc(), diag::unreachable_code); diagnose(BB.getModule().getASTContext(), NoReturnCall->getLoc().getSourceLoc(), diag::call_to_noreturn_note); DiagnosedUnreachableCode = true; } } } // We are going to bluntly remove these instructions. Change uses in // different basic blocks to undef. This is safe because all control flow // created by transparent inlining of functions applications after a call // to a 'noreturn' function is control dependent on the call to the // noreturn function and therefore dead. setOutsideBlockUsesToUndef(CurrentInst); NumInstructionsRemoved++; continue; } // Check if this instruction is the first call to noreturn in this block. if (!NoReturnCall) { NoReturnCall = getAsCallToNoReturn(CurrentInst); } } if (!NoReturnCall) return false; // If the call is to the 'unreachable' builtin, then remove the call, // as it is redundant with the actual unreachable terminator. if (auto Builtin = dyn_cast<BuiltinInst>(NoReturnCall)) { if (Builtin->getName().str() == "unreachable") ToBeDeleted.push_back(NoReturnCall); } // Record the diagnostic info. if (!DiagnosedUnreachableCode && NoReturnCall->getLoc().is<RegularLocation>() && State){ for (auto SI = BB.succ_begin(), SE = BB.succ_end(); SI != SE; ++SI) { SILBasicBlock *UnreachableBlock = *SI; if (!State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { // If this is the first time we see this unreachable block, store it // along with the noreturn call info. State->PossiblyUnreachableBlocks.insert(UnreachableBlock); State->MetaMap.insert( std::pair<const SILBasicBlock*, UnreachableInfo>( UnreachableBlock, UnreachableInfo{UnreachableKind::NoreturnCall, NoReturnCall->getLoc(), true })); } } } recursivelyDeleteTriviallyDeadInstructions(ToBeDeleted, true); NumInstructionsRemoved += ToBeDeleted.size(); // Add an unreachable terminator. The terminator has an invalid source // location to signal to the DataflowDiagnostic pass that this code does // not correspond to user code. SILBuilder B(&BB); B.createUnreachable(ArtificialUnreachableLocation()); return true; }
SILInstruction *StackPromoter::findDeallocPoint(SILInstruction *StartInst, SILInstruction *&RestartPoint, EscapeAnalysis::CGNode *Node, int NumUsePointsToFind) { // In the following we check two requirements for stack promotion: // 1) Are all uses in the same control region as the alloc? E.g. if the // allocation is in a loop then there may not be any uses of the object // outside the loop. // 2) We need to find an insertion place for the deallocation so that it // preserves a properly nested stack allocation-deallocation structure. SILBasicBlock *StartBlock = StartInst->getParent(); // The block where we assume we can insert the deallocation. SILBasicBlock *EndBlock = StartBlock; // We visit all instructions starting at the allocation instruction. WorkListType WorkList; // It's important that the EndBlock is at the head of the WorkList so that // we handle it after all other blocks. WorkList.insert(EndBlock, -1); WorkList.insert(StartBlock, 0); for (;;) { SILBasicBlock *BB = WorkList.pop_back_val(); int StackDepth = 0; SILBasicBlock::iterator Iter; if (BB == StartBlock) { // In the first block we start at the allocation instruction and not at // the begin of the block. Iter = StartInst->getIterator(); } else { // Track all uses in the block arguments. for (SILArgument *BBArg : BB->getArguments()) { if (ConGraph->isUsePoint(BBArg, Node)) NumUsePointsToFind--; } // Make sure that the EndBlock is not inside a loop (which does not // contain the StartBlock). // E.g.: // %obj = alloc_ref // the allocation // br loop // loop: // the_only_use_of_obj(%obj) // cond_br ..., loop, exit // exit: // ... // this is the new EndBlock EndBlock = updateEndBlock(BB, EndBlock, WorkList); if (!EndBlock) return nullptr; Iter = BB->begin(); StackDepth = WorkList.getStackDepth(BB); } // Visit all instructions of the current block. while (Iter != BB->end()) { SILInstruction &I = *Iter++; if (BB == EndBlock && StackDepth == 0 && NumUsePointsToFind == 0) { // We found a place to insert the stack deallocation. return &I; } if (I.isAllocatingStack()) { StackDepth++; } else if (I.isDeallocatingStack()) { if (StackDepth == 0) { // The allocation is inside a stack alloc-dealloc region and we are // now leaving this region without having found a place for the // deallocation. E.g. // E.g.: // %1 = alloc_stack // %obj = alloc_ref // the allocation // dealloc_stack %1 // use_of_obj(%obj) // // In this case we can move the alloc_ref before the alloc_stack // to fix the nesting. auto *Alloc = dyn_cast<SILInstruction>(I.getOperand(0)); if (!Alloc) return nullptr; // This should always be the case, but let's be on the safe side. if (!postDominates(StartBlock, Alloc->getParent())) return nullptr; // Trigger another iteration with a new start point; RestartPoint = Alloc; return nullptr; } StackDepth--; } // Track a use. if (ConGraph->isUsePoint(&I, Node) != 0) NumUsePointsToFind--; } if (WorkList.empty()) { if (EndBlock == BB) { // We reached the EndBlock but didn't find a place for the deallocation // so far (because we didn't find all uses yet or we entered another // stack alloc-dealloc region). Let's extend our lifetime region. // E.g.: // %obj = alloc_ref // the allocation // %1 = alloc_stack // use_of_obj(%obj) // can't insert the deallocation in this block // cond_br ..., bb1, bb2 // bb1: // ... // br bb2 // bb2: // dealloc_stack %1 // this is the new EndBlock EndBlock = getImmediatePostDom(EndBlock); if (!EndBlock) return nullptr; } // Again, it's important that the EndBlock is the first in the WorkList. WorkList.insert(EndBlock, -1); } // Push the successor blocks to the WorkList. for (SILBasicBlock *Succ : BB->getSuccessors()) { if (!strictlyDominates(StartBlock, Succ)) { // The StartBlock is inside a loop but we couldn't find a deallocation // place in this loop, e.g. because there are uses outside the loop. // E.g.: // %container = alloc_ref // br loop // loop: // %obj = alloc_ref // the allocation // store %obj to %some_field_in_container // cond_br ..., loop, exit // exit: // use(%container) return nullptr; } WorkList.insert(Succ, StackDepth); } } }
bool CSE::processNode(DominanceInfoNode *Node) { SILBasicBlock *BB = Node->getBlock(); bool Changed = false; // See if any instructions in the block can be eliminated. If so, do it. If // not, add them to AvailableValues. for (SILBasicBlock::iterator I = BB->begin(), E = BB->end(); I != E;) { SILInstruction *Inst = &*I; ++I; DEBUG(llvm::dbgs() << "SILCSE VISITING: " << *Inst << "\n"); // Dead instructions should just be removed. if (isInstructionTriviallyDead(Inst)) { DEBUG(llvm::dbgs() << "SILCSE DCE: " << *Inst << '\n'); eraseFromParentWithDebugInsts(Inst, I); Changed = true; ++NumSimplify; continue; } // If the instruction can be simplified (e.g. X+0 = X) then replace it with // its simpler value. if (SILValue V = simplifyInstruction(Inst)) { DEBUG(llvm::dbgs() << "SILCSE SIMPLIFY: " << *Inst << " to: " << *V << '\n'); Inst->replaceAllUsesWith(V); Inst->eraseFromParent(); Changed = true; ++NumSimplify; continue; } // If this is not a simple instruction that we can value number, skip it. if (!canHandle(Inst)) continue; // If an instruction can be handled here, then it must also be handled // in isIdenticalTo, otherwise looking up a key in the map with fail to // match itself. assert(Inst->isIdenticalTo(Inst) && "Inst must match itself for map to work"); // Now that we know we have an instruction we understand see if the // instruction has an available value. If so, use it. if (ValueBase *V = AvailableValues->lookup(Inst)) { DEBUG(llvm::dbgs() << "SILCSE CSE: " << *Inst << " to: " << *V << '\n'); // Instructions producing a new opened archetype need a special handling, // because replacing these instructions may require a replacement // of the opened archetype type operands in some of the uses. if (!isa<OpenExistentialRefInst>(Inst) || processOpenExistentialRef(Inst, V, I)) { Inst->replaceAllUsesWith(V); Inst->eraseFromParent(); Changed = true; ++NumCSE; continue; } } // Otherwise, just remember that this value is available. AvailableValues->insert(Inst, Inst); DEBUG(llvm::dbgs() << "SILCSE Adding to value table: " << *Inst << " -> " << *Inst << "\n"); } return Changed; }
/// Insert monomorphic inline caches for a specific class or metatype /// type \p SubClassTy. static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILType SubType, CheckedCastBranchInst *&CCBI) { CCBI = nullptr; // Bail if this class_method cannot be devirtualized. if (!canDevirtualizeClassMethod(AI, SubType)) return FullApplySite(); // Create a diamond shaped control flow and a checked_cast_branch // instruction that checks the exact type of the object. // This cast selects between two paths: one that calls the slow dynamic // dispatch and one that calls the specific method. auto It = AI.getInstruction()->getIterator(); SILFunction *F = AI.getFunction(); SILBasicBlock *Entry = AI.getParent(); // Iden is the basic block containing the direct call. SILBasicBlock *Iden = F->createBasicBlock(); // Virt is the block containing the slow virtual call. SILBasicBlock *Virt = F->createBasicBlock(); Iden->createBBArg(SubType); SILBasicBlock *Continue = Entry->splitBasicBlock(It); SILBuilderWithScope Builder(Entry, AI.getInstruction()); // Create the checked_cast_branch instruction that checks at runtime if the // class instance is identical to the SILType. ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee()); CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, CMI->getOperand(), SubType, Iden, Virt); It = CCBI->getIterator(); SILBuilderWithScope VirtBuilder(Virt, AI.getInstruction()); SILBuilderWithScope IdenBuilder(Iden, AI.getInstruction()); // This is the class reference downcasted into subclass SubType. SILValue DownCastedClassInstance = Iden->getBBArg(0); // Copy the two apply instructions into the two blocks. FullApplySite IdenAI = CloneApply(AI, IdenBuilder); FullApplySite VirtAI = CloneApply(AI, VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. if (auto *Release = dyn_cast<StrongReleaseInst>(std::next(Continue->begin()))) { if (Release->getOperand() == CMI->getOperand()) { VirtBuilder.createStrongRelease(Release->getLoc(), CMI->getOperand()); IdenBuilder.createStrongRelease(Release->getLoc(), DownCastedClassInstance); Release->eraseFromParent(); } } // Create a PHInode for returning the return value from both apply // instructions. SILArgument *Arg = Continue->createBBArg(AI.getType()); if (!isa<TryApplyInst>(AI)) { IdenBuilder.createBranch(AI.getLoc(), Continue, ArrayRef<SILValue>(IdenAI.getInstruction())); VirtBuilder.createBranch(AI.getLoc(), Continue, ArrayRef<SILValue>(VirtAI.getInstruction())); } // Remove the old Apply instruction. if (!isa<TryApplyInst>(AI)) AI.getInstruction()->replaceAllUsesWith(Arg); auto *OriginalBB = AI.getParent(); AI.getInstruction()->eraseFromParent(); if (OriginalBB->empty()) OriginalBB->removeFromParent(); // Update the stats. NumTargetsPredicted++; // Devirtualize the apply instruction on the identical path. auto NewInstPair = devirtualizeClassMethod(IdenAI, DownCastedClassInstance); assert(NewInstPair.first && "Expected to be able to devirtualize apply!"); replaceDeadApply(IdenAI, NewInstPair.first); // Split critical edges resulting from VirtAI. if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) { auto *ErrorBB = TAI->getFunction()->createBasicBlock(); ErrorBB->createBBArg(TAI->getErrorBB()->getBBArg(0)->getType()); Builder.setInsertionPoint(ErrorBB); Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), {ErrorBB->getBBArg(0)}); auto *NormalBB = TAI->getFunction()->createBasicBlock(); NormalBB->createBBArg(TAI->getNormalBB()->getBBArg(0)->getType()); Builder.setInsertionPoint(NormalBB); Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(), {NormalBB->getBBArg(0) }); Builder.setInsertionPoint(VirtAI.getInstruction()); SmallVector<SILValue, 4> Args; for (auto Arg : VirtAI.getArguments()) { Args.push_back(Arg); } FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(), VirtAI.getSubstCalleeSILType(), VirtAI.getSubstitutions(), Args, NormalBB, ErrorBB); VirtAI.getInstruction()->eraseFromParent(); VirtAI = NewVirtAI; } return VirtAI; }
bool StackPromoter::canPromoteAlloc(SILInstruction *AI, SILInstruction *&AllocInsertionPoint, SILInstruction *&DeallocInsertionPoint) { AllocInsertionPoint = nullptr; DeallocInsertionPoint = nullptr; auto *Node = ConGraph->getNodeOrNull(AI, EA); if (!Node) return false; // The most important check: does the object escape the current function? if (Node->escapes()) return false; // Now we have to determine the lifetime of the allocated object in its // function. // Get all interesting uses of the object (e.g. release instructions). This // includes uses of objects where the allocation is stored to. int NumUsePointsToFind = ConGraph->getNumUsePoints(Node); if (NumUsePointsToFind == 0) { // There should always be at least one release for an allocated object. // But in case all paths from this block end in unreachable then the // final release of the object may be optimized away. We bail out in this // case. return false; } // In the following we check two requirements for stack promotion: // 1) Are all uses in the same control region as the alloc? E.g. if the // allocation is in a loop then there may not be any uses of the object // outside the loop. // 2) We need to find an insertion place for the deallocation so that it // preserves a properly nested stack allocation-deallocation structure. SILBasicBlock *StartBlock = AI->getParent(); // The block where we assume we can insert the deallocation. SILBasicBlock *EndBlock = StartBlock; // We visit all instructions starting at the allocation instruction. WorkListType WorkList; // It's important that the EndBlock is at the head of the WorkList so that // we handle it after all other blocks. WorkList.insert(EndBlock, -1); WorkList.insert(StartBlock, 0); for (;;) { SILBasicBlock *BB = WorkList.pop_back_val(); int StackDepth = 0; SILBasicBlock::iterator Iter; if (BB == StartBlock) { // In the first block we start at the allocation instruction and not at // the begin of the block. Iter = AI->getIterator(); } else { // Track all uses in the block arguments. for (SILArgument *BBArg : BB->getBBArgs()) { if (ConGraph->isUsePoint(BBArg, Node)) NumUsePointsToFind--; } // Make sure that the EndBlock is not inside a loop (which does not // contain the StartBlock). // E.g.: // %obj = alloc_ref // the allocation // br loop // loop: // the_only_use_of_obj(%obj) // cond_br ..., loop, exit // exit: // ... // this is the new EndBlock for (SILBasicBlock *Pred : BB->getPreds()) { // Extend the lifetime region until the EndBlock post dominates the // StartBlock. while (!strictlyPostDominates(EndBlock, Pred)) { EndBlock = getImmediatePostDom(EndBlock); if (!EndBlock) return false; } } Iter = BB->begin(); StackDepth = WorkList.getStackDepth(BB); } // Visit all instructions of the current block. while (Iter != BB->end()) { SILInstruction &I = *Iter++; if (BB == EndBlock && StackDepth == 0 && NumUsePointsToFind == 0) { // We found a place to insert the stack deallocation. DeallocInsertionPoint = &I; return true; } if (I.isAllocatingStack()) { StackDepth++; } else if (I.isDeallocatingStack()) { if (StackDepth == 0) { // The allocation is inside a stack alloc-dealloc region and we are // now leaving this region without having found a place for the // deallocation. E.g. // E.g.: // %1 = alloc_stack // %obj = alloc_ref // the allocation // dealloc_stack %1 // use_of_obj(%obj) // // In this case we can move the alloc_ref before the alloc_stack // to fix the nesting. if (!isa<AllocRefInst>(AI)) return false; auto *Alloc = dyn_cast<SILInstruction>(I.getOperand(0)); if (!Alloc) return false; // This should always be the case, but let's be on the safe side. if (!PDT->dominates(StartBlock, Alloc->getParent())) return false; AllocInsertionPoint = Alloc; StackDepth++; } StackDepth--; } // Track a use. if (ConGraph->isUsePoint(&I, Node) != 0) NumUsePointsToFind--; } if (WorkList.empty()) { if (EndBlock == BB) { // We reached the EndBlock but didn't find a place for the deallocation // so far (because we didn't find all uses yet or we entered another // stack alloc-dealloc region). Let's extend our lifetime region. // E.g.: // %obj = alloc_ref // the allocation // %1 = alloc_stack // use_of_obj(%obj) // can't insert the deallocation in this block // cond_br ..., bb1, bb2 // bb1: // ... // br bb2 // bb2: // dealloc_stack %1 // this is the new EndBlock EndBlock = getImmediatePostDom(EndBlock); if (!EndBlock) return false; } // Again, it's important that the EndBlock is the first in the WorkList. WorkList.insert(EndBlock, -1); } // Push the successor blocks to the WorkList. for (SILBasicBlock *Succ : BB->getSuccessors()) { if (!strictlyDominates(StartBlock, Succ)) { // The StartBlock is inside a loop but we couldn't find a deallocation // place in this loop, e.g. because there are uses outside the loop. // E.g.: // %container = alloc_ref // br loop // loop: // %obj = alloc_ref // the allocation // store %obj to %some_field_in_container // cond_br ..., loop, exit // exit: // use(%container) return false; } WorkList.insert(Succ, StackDepth); } } }
void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) { DEBUG(llvm::dbgs() << "*** Promoting in-block: " << *ASI); SILBasicBlock *BB = ASI->getParent(); // The default value of the AllocStack is NULL because we don't have // uninitialized variables in Swift. SILValue RunningVal = SILValue(); // For all instructions in the block. for (auto BBI = BB->begin(), E = BB->end(); BBI != E;) { SILInstruction *Inst = &*BBI; ++BBI; // Remove instructions that we are loading from. Replace the loaded value // with our running value. if (isLoadFromStack(Inst, ASI)) { if (!RunningVal) { assert(ASI->getElementType().isVoid() && "Expected initialization of non-void type!"); RunningVal = SILUndef::get(ASI->getElementType(), ASI->getModule()); } replaceLoad(cast<LoadInst>(Inst), RunningVal, ASI); NumInstRemoved++; continue; } // Remove stores and record the value that we are saving as the running // value. if (auto *SI = dyn_cast<StoreInst>(Inst)) { if (SI->getDest() == ASI) { RunningVal = SI->getSrc(); Inst->eraseFromParent(); NumInstRemoved++; continue; } } // Replace debug_value_addr with debug_value of the promoted value. if (auto *DVAI = dyn_cast<DebugValueAddrInst>(Inst)) { if (DVAI->getOperand() == ASI) { if (RunningVal) { promoteDebugValueAddr(DVAI, RunningVal, B); } else { // Drop debug_value_addr of uninitialized void values. assert(ASI->getElementType().isVoid() && "Expected initialization of non-void type!"); DVAI->eraseFromParent(); } } continue; } // Replace destroys with a release of the value. if (auto *DAI = dyn_cast<DestroyAddrInst>(Inst)) { if (DAI->getOperand() == ASI) { replaceDestroy(DAI, RunningVal); } continue; } // Remove deallocation. if (auto *DSI = dyn_cast<DeallocStackInst>(Inst)) { if (DSI->getOperand() == ASI) { Inst->eraseFromParent(); NumInstRemoved++; // No need to continue scanning after deallocation. break; } } SILValue InstVal = Inst; // Remove dead address instructions that may be uses of the allocation. while (InstVal->use_empty() && (isa<StructElementAddrInst>(InstVal) || isa<TupleElementAddrInst>(InstVal))) { SILInstruction *I = cast<SILInstruction>(InstVal); InstVal = I->getOperand(0); I->eraseFromParent(); NumInstRemoved++; } } }
/// Remove redundant checks in a basic block. This pass will reset the state /// after an instruction that may modify any array allowing removal of redundant /// checks up to that point and after that point. static bool removeRedundantChecksInBlock(SILBasicBlock &BB, ArraySet &Arrays, RCIdentityFunctionInfo *RCIA) { ABCAnalysis ABC(false, Arrays, RCIA); IndexedArraySet RedundantChecks; bool Changed = false; DEBUG(llvm::dbgs() << "Removing in BB\n"); DEBUG(BB.dump()); // Process all instructions in the current block. for (auto Iter = BB.begin(); Iter != BB.end();) { auto Inst = &*Iter; ++Iter; ABC.analyse(Inst); if (ABC.clearArraysUnsafeFlag()) { // Any array may be modified -> forget everything. This is just a // shortcut to the isUnsafe test for a specific array below. RedundantChecks.clear(); continue; } // Is this a check_bounds. ArraySemanticsCall ArrayCall(Inst); auto Kind = ArrayCall.getKind(); if (Kind != ArrayCallKind::kCheckSubscript && Kind != ArrayCallKind::kCheckIndex) { DEBUG(llvm::dbgs() << " not a check_bounds call " << *Inst); continue; } auto Array = ArrayCall.getSelf(); // Get the underlying array pointer. Array = getArrayStructPointer(Kind, Array); // Is this an unsafe array whose size could have been changed? if (ABC.isUnsafe(Array)) { DEBUG(llvm::dbgs() << " not a safe array argument " << *Array); continue; } // Get the array index. auto ArrayIndex = ArrayCall.getIndex(); if (!ArrayIndex) continue; auto IndexedArray = getArrayIndexPair(Array, ArrayIndex, Kind); DEBUG(llvm::dbgs() << " IndexedArray: " << *Array << " and " << *ArrayIndex); // Saw a check for the first time. if (!RedundantChecks.count(IndexedArray)) { DEBUG(llvm::dbgs() << " first time: " << *Inst << " with array argument: " << *Array); RedundantChecks.insert(IndexedArray); continue; } // Remove the bounds check. ArrayCall.removeCall(); Changed = true; } return Changed; }