static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F, ArrayRef<Substitution> Subs, SmallVectorImpl<Substitution> &NewSubs) { auto &Module = AI.getModule(); auto CalleeCanType = F->getLoweredFunctionType(); ProtocolDecl *proto = nullptr; if (CalleeCanType->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod) { proto = CalleeCanType->getDefaultWitnessMethodProtocol( *Module.getSwiftModule()); } ArrayRef<Substitution> origSubs = AI.getSubstitutions(); if (proto != nullptr) { // If the callee is a default witness method thunk, preserve substitutions // from the call site. NewSubs.append(origSubs.begin(), origSubs.end()); return; } // If the callee is a concrete witness method thunk, apply substitutions // from the conformance, and drop any substitutions derived from the Self // type. NewSubs.append(Subs.begin(), Subs.end()); if (auto generics = AI.getOrigCalleeType()->getGenericSignature()) { for (auto genericParam : generics->getAllDependentTypes()) { auto origSub = origSubs.front(); origSubs = origSubs.slice(1); // If the callee is a concrete witness method thunk, we ignore // generic parameters derived from 'self', the generic parameter at // depth 0, index 0. auto type = genericParam->getCanonicalType(); while (auto memberType = dyn_cast<DependentMemberType>(type)) { type = memberType.getBase(); } auto paramType = cast<GenericTypeParamType>(type); if (paramType->getDepth() == 0) { // There shouldn't be any other parameters at this depth. assert(paramType->getIndex() == 0); continue; } // Okay, remember this substitution. NewSubs.push_back(origSub); } } assert(origSubs.empty() && "subs not parallel to dependent types"); }
/// Generate a new apply of a function_ref to replace an apply of a /// witness_method when we've determined the actual function we'll end /// up calling. static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ProtocolConformanceRef C, OptRemark::Emitter *ORE) { // We know the witness thunk and the corresponding set of substitutions // required to invoke the protocol method at this point. auto &Module = AI.getModule(); // Collect all the required substitutions. // // The complete set of substitutions may be different, e.g. because the found // witness thunk F may have been created by a specialization pass and have // additional generic parameters. auto SubMap = getWitnessMethodSubstitutions(Module, AI, F, C); // Figure out the exact bound type of the function to be called by // applying all substitutions. auto CalleeCanType = F->getLoweredFunctionType(); auto SubstCalleeCanType = CalleeCanType->substGenericArgs(Module, SubMap); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); SILFunctionConventions substConv(SubstCalleeCanType, Module); unsigned substArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg(); for (auto arg : AI.getArguments()) { auto paramType = substConv.getSILArgumentType(substArgIdx++); if (arg->getType() != paramType) arg = castValueToABICompatibleType(&B, AI.getLoc(), arg, arg->getType(), paramType); Arguments.push_back(arg); } assert(substArgIdx == substConv.getNumSILArguments()); // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); ApplySite SAI = replaceApplySite(Builder, Loc, AI, FRI, SubMap, Arguments, substConv); if (ORE) ORE->emit([&]() { using namespace OptRemark; return RemarkPassed("WitnessMethodDevirtualized", *AI.getInstruction()) << "Devirtualized call to " << NV("Method", F); }); NumWitnessDevirt++; return SAI; }
/// In the cases where we can statically determine the function that /// we'll call to, replace an apply of a witness_method with an apply /// of a function_ref, returning the new apply. ApplySite swift::tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE) { if (!canDevirtualizeWitnessMethod(AI)) return ApplySite(); SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); return devirtualizeWitnessMethod(AI, F, WMI->getConformance(), ORE); }
/// In the cases where we can statically determine the function that /// we'll call to, replace an apply of a witness_method with an apply /// of a function_ref, returning the new apply. DevirtualizationResult swift::tryDevirtualizeWitnessMethod(ApplySite AI) { if (!canDevirtualizeWitnessMethod(AI)) return std::make_pair(nullptr, FullApplySite()); SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); auto Result = devirtualizeWitnessMethod(AI, F, WMI->getConformance()); return std::make_pair(Result.getInstruction(), Result); }
static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F, ProtocolConformanceRef CRef, SmallVectorImpl<Substitution> &NewSubs) { auto &Module = AI.getModule(); auto requirementSig = AI.getOrigCalleeType()->getGenericSignature(); auto witnessThunkSig = F->getLoweredFunctionType()->getGenericSignature(); SubstitutionList origSubs = AI.getSubstitutions(); bool isDefaultWitness = F->getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod && F->getLoweredFunctionType()->getDefaultWitnessMethodProtocol( *Module.getSwiftModule()) == CRef.getRequirement(); getWitnessMethodSubstitutions(Module, CRef, requirementSig, witnessThunkSig, origSubs, isDefaultWitness, NewSubs); }
static bool canDevirtualizeWitnessMethod(ApplySite AI) { SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); if (!F) return false; if (AI.getFunction()->isSerialized()) { // function_ref inside fragile function cannot reference a private or // hidden symbol. if (!F->hasValidLinkageForFragileRef()) return false; } return true; }
/// In the cases where we can statically determine the function that /// we'll call to, replace an apply of a witness_method with an apply /// of a function_ref, returning the new apply. DevirtualizationResult swift::tryDevirtualizeWitnessMethod(ApplySite AI) { SILFunction *F; SILWitnessTable *WT; auto *WMI = cast<WitnessMethodInst>(AI.getCallee()); std::tie(F, WT) = AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(), WMI->getMember()); if (!F) return std::make_pair(nullptr, FullApplySite()); if (AI.getFunction()->isFragile()) { // function_ref inside fragile function cannot reference a private or // hidden symbol. if (!F->hasValidLinkageForFragileRef()) return std::make_pair(nullptr, FullApplySite()); } auto Result = devirtualizeWitnessMethod(AI, F, WMI->getConformance()); return std::make_pair(Result.getInstruction(), Result); }
/// Generate a new apply of a function_ref to replace an apply of a /// witness_method when we've determined the actual function we'll end /// up calling. static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ArrayRef<Substitution> Subs) { // We know the witness thunk and the corresponding set of substitutions // required to invoke the protocol method at this point. auto &Module = AI.getModule(); // Collect all the required substitutions. // // The complete set of substitutions may be different, e.g. because the found // witness thunk F may have been created by a specialization pass and have // additional generic parameters. SmallVector<Substitution, 16> NewSubstList(Subs.begin(), Subs.end()); // Add the non-self-derived substitutions from the original application. ArrayRef<Substitution> SubstList; SubstList = AI.getSubstitutionsWithoutSelfSubstitution(); for (auto &origSub : SubstList) if (!origSub.getArchetype()->isSelfDerived()) NewSubstList.push_back(origSub); // Figure out the exact bound type of the function to be called by // applying all substitutions. auto CalleeCanType = F->getLoweredFunctionType(); auto SubstCalleeCanType = CalleeCanType->substGenericArgs( Module, Module.getSwiftModule(), NewSubstList); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); auto ParamTypes = SubstCalleeCanType->getParameterSILTypes(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = ParamTypes[ParamTypes.size() - AI.getNumArguments() + ArgN]; if (A.getType() != ParamType) A = B.createUpcast(AI.getLoc(), A, ParamType); Arguments.push_back(A); } // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = SubstCalleeCanType->getSILResult(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubstList, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }
/// Generate a new apply of a function_ref to replace an apply of a /// witness_method when we've determined the actual function we'll end /// up calling. static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ArrayRef<Substitution> Subs) { // We know the witness thunk and the corresponding set of substitutions // required to invoke the protocol method at this point. auto &Module = AI.getModule(); // Collect all the required substitutions. // // The complete set of substitutions may be different, e.g. because the found // witness thunk F may have been created by a specialization pass and have // additional generic parameters. SmallVector<Substitution, 16> NewSubstList(Subs.begin(), Subs.end()); if (auto generics = AI.getOrigCalleeType()->getGenericSignature()) { ArrayRef<Substitution> origSubs = AI.getSubstitutions(); for (auto genericParam : generics->getAllDependentTypes()) { auto origSub = origSubs.front(); origSubs = origSubs.slice(1); // Ignore generic parameters derived from 'self', the generic // parameter at depth 0, index 0. auto type = genericParam->getCanonicalType(); while (auto memberType = dyn_cast<DependentMemberType>(type)) { type = memberType.getBase(); } auto paramType = cast<GenericTypeParamType>(type); if (paramType->getDepth() == 0) { // There shouldn't be any other parameters at this depth. assert(paramType->getIndex() == 0); continue; } // Okay, remember this substitution. NewSubstList.push_back(origSub); } assert(origSubs.empty() && "subs not parallel to dependent types"); } // Figure out the exact bound type of the function to be called by // applying all substitutions. auto CalleeCanType = F->getLoweredFunctionType(); auto SubstCalleeCanType = CalleeCanType->substGenericArgs( Module, Module.getSwiftModule(), NewSubstList); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = SubstCalleeCanType->getSILArgumentType( SubstCalleeCanType->getNumSILArguments() - AI.getNumArguments() + ArgN); if (A->getType() != ParamType) A = B.createUpcast(AI.getLoc(), A, ParamType); Arguments.push_back(A); } // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = SubstCalleeCanType->getSILResult(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubstList, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubstList, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }
/// Generate a new apply of a function_ref to replace an apply of a /// witness_method when we've determined the actual function we'll end /// up calling. static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ProtocolConformanceRef C) { // We know the witness thunk and the corresponding set of substitutions // required to invoke the protocol method at this point. auto &Module = AI.getModule(); // Collect all the required substitutions. // // The complete set of substitutions may be different, e.g. because the found // witness thunk F may have been created by a specialization pass and have // additional generic parameters. SmallVector<Substitution, 4> NewSubs; getWitnessMethodSubstitutions(AI, F, C, NewSubs); // Figure out the exact bound type of the function to be called by // applying all substitutions. auto CalleeCanType = F->getLoweredFunctionType(); auto SubstCalleeCanType = CalleeCanType->substGenericArgs(Module, NewSubs); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); SILFunctionConventions substConv(SubstCalleeCanType, Module); unsigned substArgIdx = AI.getCalleeArgIndexOfFirstAppliedArg(); for (auto arg : AI.getArguments()) { auto paramType = substConv.getSILArgumentType(substArgIdx++); if (arg->getType() != paramType) arg = castValueToABICompatibleType(&B, AI.getLoc(), arg, arg->getType(), paramType); Arguments.push_back(arg); } assert(substArgIdx == substConv.getNumSILArguments()); // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = substConv.getSILResultType(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubs, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }
/// Generate a new apply of a function_ref to replace an apply of a /// witness_method when we've determined the actual function we'll end /// up calling. static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, ProtocolConformanceRef C) { // We know the witness thunk and the corresponding set of substitutions // required to invoke the protocol method at this point. auto &Module = AI.getModule(); // Collect all the required substitutions. // // The complete set of substitutions may be different, e.g. because the found // witness thunk F may have been created by a specialization pass and have // additional generic parameters. SmallVector<Substitution, 4> NewSubs; getWitnessMethodSubstitutions(AI, F, C, NewSubs); // Figure out the exact bound type of the function to be called by // applying all substitutions. auto CalleeCanType = F->getLoweredFunctionType(); auto SubstCalleeCanType = CalleeCanType->substGenericArgs(Module, NewSubs); // Bail if some of the arguments cannot be converted into // types required by the found devirtualized method. if (!canPassOrConvertAllArguments(AI, SubstCalleeCanType)) return ApplySite(); // Collect arguments from the apply instruction. auto Arguments = SmallVector<SILValue, 4>(); // Iterate over the non self arguments and add them to the // new argument list, upcasting when required. SILBuilderWithScope B(AI.getInstruction()); for (unsigned ArgN = 0, ArgE = AI.getNumArguments(); ArgN != ArgE; ++ArgN) { SILValue A = AI.getArgument(ArgN); auto ParamType = SubstCalleeCanType->getSILArgumentType( SubstCalleeCanType->getNumSILArguments() - AI.getNumArguments() + ArgN); if (A->getType() != ParamType) A = B.createUpcast(AI.getLoc(), A, ParamType); Arguments.push_back(A); } // Replace old apply instruction by a new apply instruction that invokes // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType); auto ResultSILType = SubstCalleeCanType->getSILResult(); ApplySite SAI; if (auto *A = dyn_cast<ApplyInst>(AI)) SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType, NewSubs, Arguments, A->isNonThrowing()); if (auto *TAI = dyn_cast<TryApplyInst>(AI)) SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, TAI->getNormalBB(), TAI->getErrorBB()); if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, PAI->getType()); NumWitnessDevirt++; return SAI; }