// Analyzing the body of this class destructor is valid because the object is // dead. This means that the object is never passed to objc_setAssociatedObject, // so its destructor cannot be extended at runtime. static SILFunction *getDestructor(AllocRefInst *ARI) { // We only support classes. ClassDecl *ClsDecl = ARI->getType().getClassOrBoundGenericClass(); if (!ClsDecl) return nullptr; // Look up the destructor of ClsDecl. DestructorDecl *Destructor = ClsDecl->getDestructor(); assert(Destructor && "getDestructor() should never return a nullptr."); // Find the destructor name via SILDeclRef. // FIXME: When destructors get moved into vtables, update this to use the // vtable for the class. SILDeclRef Ref(Destructor); SILFunction *Fn = ARI->getModule().lookUpFunction(Ref); if (!Fn || Fn->empty()) { DEBUG(llvm::dbgs() << " Could not find destructor.\n"); return nullptr; } DEBUG(llvm::dbgs() << " Found destructor!\n"); // If the destructor has an objc_method calling convention, we cannot // analyze it since it could be swapped out from under us at runtime. if (Fn->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) { DEBUG(llvm::dbgs() << " Found objective-c destructor. Can't " "analyze!\n"); return nullptr; } return Fn; }
/// Run the optimization. bool run(bool hasCaller) { bool Changed = false; if (!hasCaller && canBeCalledIndirectly(F->getRepresentation())) { DEBUG(llvm::dbgs() << " function has no caller -> abort\n"); return false; } // Run OwnedToGuaranteed optimization. if (OwnedToGuaranteedAnalyze()) { Changed = true; DEBUG(llvm::dbgs() << " transform owned-to-guaranteed\n"); OwnedToGuaranteedTransform(); } // Run DeadArgument elimination transformation. We only specialize // if this function has a caller inside the current module or we have // already created a thunk. if ((hasCaller || Changed) && DeadArgumentAnalyzeParameters()) { Changed = true; DEBUG(llvm::dbgs() << " remove dead arguments\n"); DeadArgumentTransformFunction(); } // Run ArgumentExplosion transformation. We only specialize // if this function has a caller inside the current module or we have // already created a thunk. // // NOTE: we run argument explosion last because we've already initialized // the ArgumentDescList to have unexploded number of arguments. Exploding // it without changing the argument count is not going to help with // owned-to-guaranteed transformation. // // In order to not miss any opportunity, we send the optimized function // to the passmanager to optimize any opportunities exposed by argument // explosion. if ((hasCaller || Changed) && ArgumentExplosionAnalyzeParameters()) { Changed = true; } // Check if generic signature of the function could be changed by // removed some unused generic arguments. if (F->getLoweredFunctionType()->isPolymorphic() && createOptimizedSILFunctionType() != F->getLoweredFunctionType()) { Changed = true; } // Create the specialized function and invalidate the old function. if (Changed) { createFunctionSignatureOptimizedFunction(); } return Changed; }
/// Returns the callee SILFunction called at a call site, in the case /// that the call is transparent (as in, both that the call is marked /// with the transparent flag and that callee function is actually transparently /// determinable from the SIL) or nullptr otherwise. This assumes that the SIL /// is already in SSA form. /// /// In the case that a non-null value is returned, FullArgs contains effective /// argument operands for the callee function. static SILFunction *getCalleeFunction( SILFunction *F, FullApplySite AI, bool &IsThick, SmallVectorImpl<std::pair<SILValue, ParameterConvention>> &CaptureArgs, SmallVectorImpl<SILValue> &FullArgs, PartialApplyInst *&PartialApply) { IsThick = false; PartialApply = nullptr; CaptureArgs.clear(); FullArgs.clear(); for (const auto &Arg : AI.getArguments()) FullArgs.push_back(Arg); SILValue CalleeValue = AI.getCallee(); if (auto *LI = dyn_cast<LoadInst>(CalleeValue)) { // Conservatively only see through alloc_box; we assume this pass is run // immediately after SILGen auto *PBI = dyn_cast<ProjectBoxInst>(LI->getOperand()); if (!PBI) return nullptr; auto *ABI = dyn_cast<AllocBoxInst>(PBI->getOperand()); if (!ABI) return nullptr; // Ensure there are no other uses of alloc_box than the project_box and // retains, releases. for (Operand *ABIUse : ABI->getUses()) if (ABIUse->getUser() != PBI && !isa<StrongRetainInst>(ABIUse->getUser()) && !isa<StrongReleaseInst>(ABIUse->getUser())) return nullptr; // Scan forward from the alloc box to find the first store, which // (conservatively) must be in the same basic block as the alloc box StoreInst *SI = nullptr; for (auto I = SILBasicBlock::iterator(ABI), E = I->getParent()->end(); I != E; ++I) { // If we find the load instruction first, then the load is loading from // a non-initialized alloc; this shouldn't really happen but I'm not // making any assumptions if (&*I == LI) return nullptr; if ((SI = dyn_cast<StoreInst>(I)) && SI->getDest() == PBI) { // We found a store that we know dominates the load; now ensure there // are no other uses of the project_box except loads. for (Operand *PBIUse : PBI->getUses()) if (PBIUse->getUser() != SI && !isa<LoadInst>(PBIUse->getUser())) return nullptr; // We can conservatively see through the store break; } } if (!SI) return nullptr; CalleeValue = SI->getSrc(); } // PartialApply/ThinToThick -> ConvertFunction patterns are generated // by @noescape closures. // // FIXME: We don't currently handle mismatched return types, however, this // would be a good optimization to handle and would be as simple as inserting // a cast. auto skipFuncConvert = [](SILValue CalleeValue) { // We can also allow a thin @escape to noescape conversion as such: // %1 = function_ref @thin_closure_impl : $@convention(thin) () -> () // %2 = convert_function %1 : // $@convention(thin) () -> () to $@convention(thin) @noescape () -> () // %3 = thin_to_thick_function %2 : // $@convention(thin) @noescape () -> () to // $@noescape @callee_guaranteed () -> () // %4 = apply %3() : $@noescape @callee_guaranteed () -> () if (auto *ThinToNoescapeCast = dyn_cast<ConvertFunctionInst>(CalleeValue)) { auto FromCalleeTy = ThinToNoescapeCast->getOperand()->getType().castTo<SILFunctionType>(); if (FromCalleeTy->getExtInfo().hasContext()) return CalleeValue; auto ToCalleeTy = ThinToNoescapeCast->getType().castTo<SILFunctionType>(); auto EscapingCalleeTy = ToCalleeTy->getWithExtInfo( ToCalleeTy->getExtInfo().withNoEscape(false)); if (FromCalleeTy != EscapingCalleeTy) return CalleeValue; return ThinToNoescapeCast->getOperand(); } auto *CFI = dyn_cast<ConvertEscapeToNoEscapeInst>(CalleeValue); if (!CFI) return CalleeValue; // TODO: Handle argument conversion. All the code in this file needs to be // cleaned up and generalized. The argument conversion handling in // optimizeApplyOfConvertFunctionInst should apply to any combine // involving an apply, not just a specific pattern. // // For now, just handle conversion that doesn't affect argument types, // return types, or throws. We could trivially handle any other // representation change, but the only one that doesn't affect the ABI and // matters here is @noescape, so just check for that. auto FromCalleeTy = CFI->getOperand()->getType().castTo<SILFunctionType>(); auto ToCalleeTy = CFI->getType().castTo<SILFunctionType>(); auto EscapingCalleeTy = ToCalleeTy->getWithExtInfo(ToCalleeTy->getExtInfo().withNoEscape(false)); if (FromCalleeTy != EscapingCalleeTy) return CalleeValue; return CFI->getOperand(); }; // Look through a escape to @noescape conversion. CalleeValue = skipFuncConvert(CalleeValue); // We are allowed to see through exactly one "partial apply" instruction or // one "thin to thick function" instructions, since those are the patterns // generated when using auto closures. if (auto *PAI = dyn_cast<PartialApplyInst>(CalleeValue)) { // Collect the applied arguments and their convention. collectPartiallyAppliedArguments(PAI, CaptureArgs, FullArgs); CalleeValue = PAI->getCallee(); IsThick = true; PartialApply = PAI; } else if (auto *TTTFI = dyn_cast<ThinToThickFunctionInst>(CalleeValue)) { CalleeValue = TTTFI->getOperand(); IsThick = true; } CalleeValue = skipFuncConvert(CalleeValue); auto *FRI = dyn_cast<FunctionRefInst>(CalleeValue); if (!FRI) return nullptr; SILFunction *CalleeFunction = FRI->getReferencedFunction(); switch (CalleeFunction->getRepresentation()) { case SILFunctionTypeRepresentation::Thick: case SILFunctionTypeRepresentation::Thin: case SILFunctionTypeRepresentation::Method: case SILFunctionTypeRepresentation::Closure: case SILFunctionTypeRepresentation::WitnessMethod: break; case SILFunctionTypeRepresentation::CFunctionPointer: case SILFunctionTypeRepresentation::ObjCMethod: case SILFunctionTypeRepresentation::Block: return nullptr; } // If the CalleeFunction is a not-transparent definition, we can not process // it. if (CalleeFunction->isTransparent() == IsNotTransparent) return nullptr; // If CalleeFunction is a declaration, see if we can load it. if (CalleeFunction->empty()) AI.getModule().loadFunction(CalleeFunction); // If we fail to load it, bail. if (CalleeFunction->empty()) return nullptr; if (F->isSerialized() && !CalleeFunction->hasValidLinkageForFragileInline()) { if (!CalleeFunction->hasValidLinkageForFragileRef()) { llvm::errs() << "caller: " << F->getName() << "\n"; llvm::errs() << "callee: " << CalleeFunction->getName() << "\n"; llvm_unreachable("Should never be inlining a resilient function into " "a fragile function"); } return nullptr; } return CalleeFunction; }
/// \brief Returns the callee SILFunction called at a call site, in the case /// that the call is transparent (as in, both that the call is marked /// with the transparent flag and that callee function is actually transparently /// determinable from the SIL) or nullptr otherwise. This assumes that the SIL /// is already in SSA form. /// /// In the case that a non-null value is returned, FullArgs contains effective /// argument operands for the callee function. static SILFunction * getCalleeFunction(FullApplySite AI, bool &IsThick, SmallVectorImpl<SILValue>& CaptureArgs, SmallVectorImpl<SILValue>& FullArgs, PartialApplyInst *&PartialApply, SILModule::LinkingMode Mode) { IsThick = false; PartialApply = nullptr; CaptureArgs.clear(); FullArgs.clear(); for (const auto &Arg : AI.getArguments()) FullArgs.push_back(Arg); SILValue CalleeValue = AI.getCallee(); if (LoadInst *LI = dyn_cast<LoadInst>(CalleeValue)) { assert(CalleeValue.getResultNumber() == 0); // Conservatively only see through alloc_box; we assume this pass is run // immediately after SILGen SILInstruction *ABI = dyn_cast<AllocBoxInst>(LI->getOperand()); if (!ABI) return nullptr; assert(LI->getOperand().getResultNumber() == 1); // Scan forward from the alloc box to find the first store, which // (conservatively) must be in the same basic block as the alloc box StoreInst *SI = nullptr; for (auto I = SILBasicBlock::iterator(ABI), E = I->getParent()->end(); I != E; ++I) { // If we find the load instruction first, then the load is loading from // a non-initialized alloc; this shouldn't really happen but I'm not // making any assumptions if (static_cast<SILInstruction*>(I) == LI) return nullptr; if ((SI = dyn_cast<StoreInst>(I)) && SI->getDest().getDef() == ABI) { // We found a store that we know dominates the load; now ensure there // are no other uses of the alloc other than loads, retains, releases // and dealloc stacks for (auto UI = ABI->use_begin(), UE = ABI->use_end(); UI != UE; ++UI) if (UI.getUser() != SI && !isa<LoadInst>(UI.getUser()) && !isa<StrongRetainInst>(UI.getUser()) && !isa<StrongReleaseInst>(UI.getUser())) return nullptr; // We can conservatively see through the store break; } } if (!SI) return nullptr; CalleeValue = SI->getSrc(); } // We are allowed to see through exactly one "partial apply" instruction or // one "thin to thick function" instructions, since those are the patterns // generated when using auto closures. if (PartialApplyInst *PAI = dyn_cast<PartialApplyInst>(CalleeValue)) { assert(CalleeValue.getResultNumber() == 0); for (const auto &Arg : PAI->getArguments()) { CaptureArgs.push_back(Arg); FullArgs.push_back(Arg); } CalleeValue = PAI->getCallee(); IsThick = true; PartialApply = PAI; } else if (ThinToThickFunctionInst *TTTFI = dyn_cast<ThinToThickFunctionInst>(CalleeValue)) { assert(CalleeValue.getResultNumber() == 0); CalleeValue = TTTFI->getOperand(); IsThick = true; } FunctionRefInst *FRI = dyn_cast<FunctionRefInst>(CalleeValue); if (!FRI) return nullptr; SILFunction *CalleeFunction = FRI->getReferencedFunction(); switch (CalleeFunction->getRepresentation()) { case SILFunctionTypeRepresentation::Thick: case SILFunctionTypeRepresentation::Thin: case SILFunctionTypeRepresentation::Method: case SILFunctionTypeRepresentation::WitnessMethod: break; case SILFunctionTypeRepresentation::CFunctionPointer: case SILFunctionTypeRepresentation::ObjCMethod: case SILFunctionTypeRepresentation::Block: return nullptr; } // If CalleeFunction is a declaration, see if we can load it. If we fail to // load it, bail. if (CalleeFunction->empty() && !AI.getModule().linkFunction(CalleeFunction, Mode)) return nullptr; return CalleeFunction; }
/// \brief Inlines the callee of a given ApplyInst (which must be the value of a /// FunctionRefInst referencing a function with a known body), into the caller /// containing the ApplyInst, which must be the same function as provided to the /// constructor of SILInliner. It only performs one step of inlining: it does /// not recursively inline functions called by the callee. /// /// It is the responsibility of the caller of this function to delete /// the given ApplyInst when inlining is successful. /// /// \returns true on success or false if it is unable to inline the function /// (for any reason). bool SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) { SILFunction *CalleeFunction = &Original; this->CalleeFunction = CalleeFunction; // Do not attempt to inline an apply into its parent function. if (AI.getFunction() == CalleeFunction) return false; SILFunction &F = getBuilder().getFunction(); if (CalleeFunction->getName() == "_TTSg5Vs4Int8___TFVs12_ArrayBufferg9_isNativeSb" && F.getName() == "_TTSg5Vs4Int8___TFVs12_ArrayBufferg8endIndexSi") llvm::errs(); assert(AI.getFunction() && AI.getFunction() == &F && "Inliner called on apply instruction in wrong function?"); assert(((CalleeFunction->getRepresentation() != SILFunctionTypeRepresentation::ObjCMethod && CalleeFunction->getRepresentation() != SILFunctionTypeRepresentation::CFunctionPointer) || IKind == InlineKind::PerformanceInline) && "Cannot inline Objective-C methods or C functions in mandatory " "inlining"); CalleeEntryBB = &*CalleeFunction->begin(); // Compute the SILLocation which should be used by all the inlined // instructions. if (IKind == InlineKind::PerformanceInline) { Loc = InlinedLocation::getInlinedLocation(AI.getLoc()); } else { assert(IKind == InlineKind::MandatoryInline && "Unknown InlineKind."); Loc = MandatoryInlinedLocation::getMandatoryInlinedLocation(AI.getLoc()); } auto AIScope = AI.getDebugScope(); // FIXME: Turn this into an assertion instead. if (!AIScope) AIScope = AI.getFunction()->getDebugScope(); if (IKind == InlineKind::MandatoryInline) { // Mandatory inlining: every instruction inherits scope/location // from the call site. CallSiteScope = AIScope; } else { // Performance inlining. Construct a proper inline scope pointing // back to the call site. CallSiteScope = new (F.getModule()) SILDebugScope(AI.getLoc(), &F, AIScope); assert(CallSiteScope->getParentFunction() == &F); } assert(CallSiteScope && "call site has no scope"); // Increment the ref count for the inlined function, so it doesn't // get deleted before we can emit abstract debug info for it. CalleeFunction->setInlined(); // If the caller's BB is not the last BB in the calling function, then keep // track of the next BB so we always insert new BBs before it; otherwise, // we just leave the new BBs at the end as they are by default. auto IBI = std::next(SILFunction::iterator(AI.getParent())); InsertBeforeBB = IBI != F.end() ? &*IBI : nullptr; // Clear argument map and map ApplyInst arguments to the arguments of the // callee's entry block. ValueMap.clear(); assert(CalleeEntryBB->bbarg_size() == Args.size() && "Unexpected number of arguments to entry block of function?"); auto BAI = CalleeEntryBB->bbarg_begin(); for (auto AI = Args.begin(), AE = Args.end(); AI != AE; ++AI, ++BAI) ValueMap.insert(std::make_pair(*BAI, *AI)); InstructionMap.clear(); BBMap.clear(); // Do not allow the entry block to be cloned again SILBasicBlock::iterator InsertPoint = SILBasicBlock::iterator(AI.getInstruction()); BBMap.insert(std::make_pair(CalleeEntryBB, AI.getParent())); getBuilder().setInsertionPoint(InsertPoint); // Recursively visit callee's BB in depth-first preorder, starting with the // entry block, cloning all instructions other than terminators. visitSILBasicBlock(CalleeEntryBB); // If we're inlining into a normal apply and the callee's entry // block ends in a return, then we can avoid a split. if (auto nonTryAI = dyn_cast<ApplyInst>(AI)) { if (ReturnInst *RI = dyn_cast<ReturnInst>(CalleeEntryBB->getTerminator())) { // Replace all uses of the apply instruction with the operands of the // return instruction, appropriately mapped. nonTryAI->replaceAllUsesWith(remapValue(RI->getOperand())); return true; } } // If we're inlining into a try_apply, we already have a return-to BB. SILBasicBlock *ReturnToBB; if (auto tryAI = dyn_cast<TryApplyInst>(AI)) { ReturnToBB = tryAI->getNormalBB(); // Otherwise, split the caller's basic block to create a return-to BB. } else { SILBasicBlock *CallerBB = AI.getParent(); // Split the BB and do NOT create a branch between the old and new // BBs; we will create the appropriate terminator manually later. ReturnToBB = CallerBB->splitBasicBlock(InsertPoint); // Place the return-to BB after all the other mapped BBs. if (InsertBeforeBB) F.getBlocks().splice(SILFunction::iterator(InsertBeforeBB), F.getBlocks(), SILFunction::iterator(ReturnToBB)); else F.getBlocks().splice(F.getBlocks().end(), F.getBlocks(), SILFunction::iterator(ReturnToBB)); // Create an argument on the return-to BB representing the returned value. auto *RetArg = new (F.getModule()) SILArgument(ReturnToBB, AI.getInstruction()->getType()); // Replace all uses of the ApplyInst with the new argument. AI.getInstruction()->replaceAllUsesWith(RetArg); } // Now iterate over the callee BBs and fix up the terminators. for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) { getBuilder().setInsertionPoint(BI->second); // Modify return terminators to branch to the return-to BB, rather than // trying to clone the ReturnInst. if (ReturnInst *RI = dyn_cast<ReturnInst>(BI->first->getTerminator())) { auto thrownValue = remapValue(RI->getOperand()); getBuilder().createBranch(Loc.getValue(), ReturnToBB, thrownValue); continue; } // Modify throw terminators to branch to the error-return BB, rather than // trying to clone the ThrowInst. if (ThrowInst *TI = dyn_cast<ThrowInst>(BI->first->getTerminator())) { if (auto *A = dyn_cast<ApplyInst>(AI)) { (void)A; assert(A->isNonThrowing() && "apply of a function with error result must be non-throwing"); getBuilder().createUnreachable(Loc.getValue()); continue; } auto tryAI = cast<TryApplyInst>(AI); auto returnedValue = remapValue(TI->getOperand()); getBuilder().createBranch(Loc.getValue(), tryAI->getErrorBB(), returnedValue); continue; } // Otherwise use normal visitor, which clones the existing instruction // but remaps basic blocks and values. visit(BI->first->getTerminator()); } return true; }