/// In this function we create the actual cloned function and its proper cloned /// type. But we do not create any body. This implies that the creation of the /// actual arguments in the function is in populateCloned. /// /// \arg PAUser The function that is being passed the partial apply. /// \arg PAI The partial apply that is being passed to PAUser. /// \arg ClosureIndex The index of the partial apply in PAUser's function /// signature. /// \arg ClonedName The name of the cloned function that we will create. SILFunction * ClosureSpecCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, const CallSiteDescriptor &CallSiteDesc, StringRef ClonedName) { SILFunction *ClosureUser = CallSiteDesc.getApplyCallee(); // This is the list of new interface parameters of the cloned function. llvm::SmallVector<SILParameterInfo, 4> NewParameterInfoList; // First add to NewParameterInfoList all of the SILParameterInfo in the // original function except for the closure. CanSILFunctionType ClosureUserFunTy = ClosureUser->getLoweredFunctionType(); auto ClosureUserConv = ClosureUser->getConventions(); unsigned Index = ClosureUserConv.getSILArgIndexOfFirstParam(); for (auto ¶m : ClosureUserConv.getParameters()) { if (Index != CallSiteDesc.getClosureIndex()) NewParameterInfoList.push_back(param); ++Index; } // Then add any arguments that are captured in the closure to the function's // argument type. Since they are captured, we need to pass them directly into // the new specialized function. SILFunction *ClosedOverFun = CallSiteDesc.getClosureCallee(); auto ClosedOverFunConv = ClosedOverFun->getConventions(); SILModule &M = ClosureUser->getModule(); // Captured parameters are always appended to the function signature. If the // type of the captured argument is: // - direct and trivial, pass the argument as Direct_Unowned. // - direct and non-trivial, pass the argument as Direct_Owned. // - indirect, pass the argument using the same parameter convention as in the // original closure. // // We use the type of the closure here since we allow for the closure to be an // external declaration. unsigned NumTotalParams = ClosedOverFunConv.getNumParameters(); unsigned NumNotCaptured = NumTotalParams - CallSiteDesc.getNumArguments(); for (auto &PInfo : ClosedOverFunConv.getParameters().slice(NumNotCaptured)) { ParameterConvention ParamConv; if (PInfo.isFormalIndirect()) { ParamConv = PInfo.getConvention(); assert(!SILModuleConventions(M).useLoweredAddresses() || ParamConv == ParameterConvention::Indirect_Inout || ParamConv == ParameterConvention::Indirect_InoutAliasable); } else { ParamConv = ClosedOverFunConv.getSILType(PInfo).isTrivial(M) ? ParameterConvention::Direct_Unowned : ParameterConvention::Direct_Owned; } SILParameterInfo NewPInfo(PInfo.getType(), ParamConv); NewParameterInfoList.push_back(NewPInfo); } // The specialized function is always a thin function. This is important // because we may add additional parameters after the Self parameter of // witness methods. In this case the new function is not a method anymore. auto ExtInfo = ClosureUserFunTy->getExtInfo(); ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); auto ClonedTy = SILFunctionType::get( ClosureUserFunTy->getGenericSignature(), ExtInfo, ClosureUserFunTy->getCoroutineKind(), ClosureUserFunTy->getCalleeConvention(), NewParameterInfoList, ClosureUserFunTy->getYields(), ClosureUserFunTy->getResults(), ClosureUserFunTy->getOptionalErrorResult(), M.getASTContext()); // We make this function bare so we don't have to worry about decls in the // SILArgument. auto *Fn = FunctionBuilder.createFunction( // It's important to use a shared linkage for the specialized function // and not the original linkage. // Otherwise the new function could have an external linkage (in case the // original function was de-serialized) and would not be code-gen'd. // It's also important to disconnect this specialized function from any // classes (the classSubclassScope), because that may incorrectly // influence the linkage. getSpecializedLinkage(ClosureUser, ClosureUser->getLinkage()), ClonedName, ClonedTy, ClosureUser->getGenericEnvironment(), ClosureUser->getLocation(), IsBare, ClosureUser->isTransparent(), CallSiteDesc.isSerialized(), IsNotDynamic, ClosureUser->getEntryCount(), ClosureUser->isThunk(), /*classSubclassScope=*/SubclassScope::NotApplicable, ClosureUser->getInlineStrategy(), ClosureUser->getEffectsKind(), ClosureUser, ClosureUser->getDebugScope()); if (!ClosureUser->hasQualifiedOwnership()) { Fn->setUnqualifiedOwnership(); } for (auto &Attr : ClosureUser->getSemanticsAttrs()) Fn->addSemanticsAttr(Attr); return Fn; }
SILInstruction *SILCombiner::visitTryApplyInst(TryApplyInst *AI) { // apply{partial_apply(x,y)}(z) -> apply(z,x,y) is triggered // from visitPartialApplyInst(), so bail here. if (isa<PartialApplyInst>(AI->getCallee())) return nullptr; if (auto *CFI = dyn_cast<ConvertFunctionInst>(AI->getCallee())) { return optimizeApplyOfConvertFunctionInst(AI, CFI); } // Optimize readonly functions with no meaningful users. SILFunction *Fn = AI->getReferencedFunction(); if (Fn && Fn->getEffectsKind() < EffectsKind::ReadWrite) { UserListTy Users; if (isTryApplyResultNotUsed(Users, AI)) { SILBasicBlock *BB = AI->getParent(); SILBasicBlock *NormalBB = AI->getNormalBB(); SILBasicBlock *ErrorBB = AI->getErrorBB(); SILLocation Loc = AI->getLoc(); Builder.setInsertionPoint(BB); Builder.setCurrentDebugScope(AI->getDebugScope()); eraseApply(AI, Users); // Replace the try_apply with a cond_br false, which will be removed by // SimplifyCFG. We don't want to modify the CFG in SILCombine. auto *TrueLit = Builder.createIntegerLiteral(Loc, SILType::getBuiltinIntegerType(1, Builder.getASTContext()), 0); Builder.createCondBranch(Loc, TrueLit, NormalBB, ErrorBB); NormalBB->eraseBBArg(0); ErrorBB->eraseBBArg(0); return nullptr; } // We found a user that we can't handle. } // (try_apply (thin_to_thick_function f)) to (try_apply f) if (auto *TTTFI = dyn_cast<ThinToThickFunctionInst>(AI->getCallee())) { // TODO: Handle substitutions and indirect results if (AI->hasSubstitutions() || AI->hasIndirectResults()) return nullptr; SmallVector<SILValue, 4> Arguments; for (auto &Op : AI->getArgumentOperands()) { Arguments.push_back(Op.get()); } // The type of the substitution is the source type of the thin to thick // instruction. SILType substTy = TTTFI->getOperand()->getType(); auto *NewAI = Builder.createTryApply(AI->getLoc(), TTTFI->getOperand(), substTy, AI->getSubstitutions(), Arguments, AI->getNormalBB(), AI->getErrorBB()); return NewAI; } // (apply (witness_method)) -> propagate information about // a concrete type from init_existential_addr or init_existential_ref. if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) { propagateConcreteTypeOfInitExistential(AI, WMI); return nullptr; } // (apply (function_ref method_from_protocol_extension)) -> // propagate information about a concrete type from init_existential_addr or // init_existential_ref. if (isa<FunctionRefInst>(AI->getCallee())) { if (propagateConcreteTypeOfInitExistential(AI)) { return nullptr; } } return nullptr; }
/// In this function we create the actual cloned function and its proper cloned /// type. But we do not create any body. This implies that the creation of the /// actual arguments in the function is in populateCloned. /// /// \arg PAUser The function that is being passed the partial apply. /// \arg PAI The partial apply that is being passed to PAUser. /// \arg ClosureIndex The index of the partial apply in PAUser's function /// signature. /// \arg ClonedName The name of the cloned function that we will create. SILFunction * ClosureSpecCloner::initCloned(const CallSiteDescriptor &CallSiteDesc, StringRef ClonedName) { SILFunction *ClosureUser = CallSiteDesc.getApplyCallee(); // This is the list of new interface parameters of the cloned function. llvm::SmallVector<SILParameterInfo, 4> NewParameterInfoList; // First add to NewParameterInfoList all of the SILParameterInfo in the // original function except for the closure. CanSILFunctionType ClosureUserFunTy = ClosureUser->getLoweredFunctionType(); unsigned Index = ClosureUserFunTy->getNumIndirectResults(); for (auto ¶m : ClosureUserFunTy->getParameters()) { if (Index != CallSiteDesc.getClosureIndex()) NewParameterInfoList.push_back(param); ++Index; } // Then add any arguments that are captured in the closure to the function's // argument type. Since they are captured, we need to pass them directly into // the new specialized function. SILFunction *ClosedOverFun = CallSiteDesc.getClosureCallee(); CanSILFunctionType ClosedOverFunTy = ClosedOverFun->getLoweredFunctionType(); SILModule &M = ClosureUser->getModule(); // Captured parameters are always appended to the function signature. If the // type of the captured argument is trivial, pass the argument as // Direct_Unowned. Otherwise pass it as Direct_Owned. // // We use the type of the closure here since we allow for the closure to be an // external declaration. unsigned NumTotalParams = ClosedOverFunTy->getParameters().size(); unsigned NumNotCaptured = NumTotalParams - CallSiteDesc.getNumArguments(); for (auto &PInfo : ClosedOverFunTy->getParameters().slice(NumNotCaptured)) { if (PInfo.getSILType().isTrivial(M)) { SILParameterInfo NewPInfo(PInfo.getType(), ParameterConvention::Direct_Unowned); NewParameterInfoList.push_back(NewPInfo); continue; } SILParameterInfo NewPInfo(PInfo.getType(), ParameterConvention::Direct_Owned); NewParameterInfoList.push_back(NewPInfo); } // The specialized function is always a thin function. This is important // because we may add additional parameters after the Self parameter of // witness methods. In this case the new function is not a method anymore. auto ExtInfo = ClosureUserFunTy->getExtInfo(); ExtInfo = ExtInfo.withRepresentation(SILFunctionTypeRepresentation::Thin); auto ClonedTy = SILFunctionType::get( ClosureUserFunTy->getGenericSignature(), ExtInfo, ClosureUserFunTy->getCalleeConvention(), NewParameterInfoList, ClosureUserFunTy->getAllResults(), ClosureUserFunTy->getOptionalErrorResult(), M.getASTContext()); // We make this function bare so we don't have to worry about decls in the // SILArgument. auto *Fn = M.createFunction( // It's important to use a shared linkage for the specialized function // and not the original linkage. // Otherwise the new function could have an external linkage (in case the // original function was de-serialized) and would not be code-gen'd. getSpecializedLinkage(ClosureUser, ClosureUser->getLinkage()), ClonedName, ClonedTy, ClosureUser->getGenericEnvironment(), ClosureUser->getLocation(), IsBare, ClosureUser->isTransparent(), CallSiteDesc.isFragile(), ClosureUser->isThunk(), ClosureUser->getClassVisibility(), ClosureUser->getInlineStrategy(), ClosureUser->getEffectsKind(), ClosureUser, ClosureUser->getDebugScope()); Fn->setDeclCtx(ClosureUser->getDeclContext()); if (ClosureUser->hasUnqualifiedOwnership()) { Fn->setUnqualifiedOwnership(); } for (auto &Attr : ClosureUser->getSemanticsAttrs()) Fn->addSemanticsAttr(Attr); return Fn; }
SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) { Builder.setCurrentDebugScope(AI->getDebugScope()); // apply{partial_apply(x,y)}(z) -> apply(z,x,y) is triggered // from visitPartialApplyInst(), so bail here. if (isa<PartialApplyInst>(AI->getCallee())) return nullptr; if (auto *CFI = dyn_cast<ConvertFunctionInst>(AI->getCallee())) return optimizeApplyOfConvertFunctionInst(AI, CFI); // Optimize readonly functions with no meaningful users. SILFunction *SF = AI->getReferencedFunction(); if (SF && SF->getEffectsKind() < EffectsKind::ReadWrite) { UserListTy Users; if (recursivelyCollectARCUsers(Users, AI)) { eraseApply(AI, Users); return nullptr; } // We found a user that we can't handle. } if (SF) { if (SF->getEffectsKind() < EffectsKind::ReadWrite) { // Try to optimize string concatenation. if (auto I = optimizeConcatenationOfStringLiterals(AI)) { return I; } } if (SF->hasSemanticsAttr("array.uninitialized")) { UserListTy Users; // If the uninitialized array is only written into then it can be removed. if (recursivelyCollectARCUsers(Users, AI)) { eraseApply(AI, Users); return nullptr; } } } // (apply (thin_to_thick_function f)) to (apply f) if (auto *TTTFI = dyn_cast<ThinToThickFunctionInst>(AI->getCallee())) { // TODO: Handle substitutions and indirect results if (AI->hasSubstitutions() || AI->hasIndirectResults()) return nullptr; SmallVector<SILValue, 4> Arguments; for (auto &Op : AI->getArgumentOperands()) { Arguments.push_back(Op.get()); } // The type of the substitution is the source type of the thin to thick // instruction. SILType substTy = TTTFI->getOperand()->getType(); auto *NewAI = Builder.createApply(AI->getLoc(), TTTFI->getOperand(), substTy, AI->getType(), AI->getSubstitutions(), Arguments, AI->isNonThrowing()); return NewAI; } // (apply (witness_method)) -> propagate information about // a concrete type from init_existential_addr or init_existential_ref. if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) { propagateConcreteTypeOfInitExistential(AI, WMI); return nullptr; } // (apply (function_ref method_from_protocol_extension)) -> // propagate information about a concrete type from init_existential_addr or // init_existential_ref. if (isa<FunctionRefInst>(AI->getCallee())) { if (propagateConcreteTypeOfInitExistential(AI)) { return nullptr; } } // Optimize f_inverse(f(x)) -> x. if (optimizeIdentityCastComposition(AI, "convertFromObjectiveC", "convertToObjectiveC")) return nullptr; if (optimizeIdentityCastComposition(AI, "convertToObjectiveC", "convertFromObjectiveC")) return nullptr; return nullptr; }
void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Create the optimized function ! SILModule &M = F->getModule(); std::string Name = createOptimizedSILFunctionName(); SILLinkage linkage = F->getLinkage(); if (isAvailableExternally(linkage)) linkage = SILLinkage::Shared; DEBUG(llvm::dbgs() << " -> create specialized function " << Name << "\n"); NewF = M.createFunction(linkage, Name, createOptimizedSILFunctionType(), F->getGenericEnvironment(), F->getLocation(), F->isBare(), F->isTransparent(), F->isSerialized(), F->isThunk(), F->getClassVisibility(), F->getInlineStrategy(), F->getEffectsKind(), nullptr, F->getDebugScope()); if (F->hasUnqualifiedOwnership()) { NewF->setUnqualifiedOwnership(); } // Then we transfer the body of F to NewF. NewF->spliceBody(F); // Array semantic clients rely on the signature being as in the original // version. for (auto &Attr : F->getSemanticsAttrs()) { if (!StringRef(Attr).startswith("array.")) NewF->addSemanticsAttr(Attr); } // Do the last bit of work to the newly created optimized function. ArgumentExplosionFinalizeOptimizedFunction(); DeadArgumentFinalizeOptimizedFunction(); // Create the thunk body ! F->setThunk(IsThunk); // The thunk now carries the information on how the signature is // optimized. If we inline the thunk, we will get the benefit of calling // the signature optimized function without additional setup on the // caller side. F->setInlineStrategy(AlwaysInline); SILBasicBlock *ThunkBody = F->createBasicBlock(); for (auto &ArgDesc : ArgumentDescList) { ThunkBody->createFunctionArgument(ArgDesc.Arg->getType(), ArgDesc.Decl); } SILLocation Loc = ThunkBody->getParent()->getLocation(); SILBuilder Builder(ThunkBody); Builder.setCurrentDebugScope(ThunkBody->getParent()->getDebugScope()); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, NewF); // Create the args for the thunk's apply, ignoring any dead arguments. llvm::SmallVector<SILValue, 8> ThunkArgs; for (auto &ArgDesc : ArgumentDescList) { addThunkArgument(ArgDesc, Builder, ThunkBody, ThunkArgs); } // We are ignoring generic functions and functions with out parameters for // now. SILValue ReturnValue; SILType LoweredType = NewF->getLoweredType(); SILType ResultType = NewF->getConventions().getSILResultType(); auto FunctionTy = LoweredType.castTo<SILFunctionType>(); if (FunctionTy->hasErrorResult()) { // We need a try_apply to call a function with an error result. SILFunction *Thunk = ThunkBody->getParent(); SILBasicBlock *NormalBlock = Thunk->createBasicBlock(); ReturnValue = NormalBlock->createPHIArgument(ResultType, ValueOwnershipKind::Owned); SILBasicBlock *ErrorBlock = Thunk->createBasicBlock(); SILType Error = SILType::getPrimitiveObjectType(FunctionTy->getErrorResult().getType()); auto *ErrorArg = ErrorBlock->createPHIArgument(Error, ValueOwnershipKind::Owned); Builder.createTryApply(Loc, FRI, LoweredType, SubstitutionList(), ThunkArgs, NormalBlock, ErrorBlock); Builder.setInsertionPoint(ErrorBlock); Builder.createThrow(Loc, ErrorArg); Builder.setInsertionPoint(NormalBlock); } else { ReturnValue = Builder.createApply(Loc, FRI, LoweredType, ResultType, SubstitutionList(), ThunkArgs, false); } // Set up the return results. if (NewF->isNoReturnFunction()) { Builder.createUnreachable(Loc); } else { Builder.createReturn(Loc, ReturnValue); } // Do the last bit work to finalize the thunk. OwnedToGuaranteedFinalizeThunkFunction(Builder, F); assert(F->getDebugScope()->Parent != NewF->getDebugScope()->Parent); }