mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[sil-devirtualizer] Support devirtualization of try_apply instructions.
rdar://21909405 Swift SVN r30710
This commit is contained in:
@@ -3902,6 +3902,11 @@ public:
|
||||
FOREACH_IMPL_RETURN(getCallee());
|
||||
}
|
||||
|
||||
/// Return the type.
|
||||
SILType getType() const {
|
||||
FOREACH_IMPL_RETURN(getSubstCalleeType()->getResult().getSILType());
|
||||
}
|
||||
|
||||
/// Get the type of the callee without the applied substitutions.
|
||||
CanSILFunctionType getOrigCalleeType() const {
|
||||
return getCallee().getType().castTo<SILFunctionType>();
|
||||
@@ -3939,6 +3944,17 @@ public:
|
||||
FOREACH_IMPL_RETURN(getSubstitutions());
|
||||
}
|
||||
|
||||
ArrayRef<Substitution> getSubstitutionsWithoutSelfSubstitution() const {
|
||||
switch (Inst->getKind()) {
|
||||
case ValueKind::ApplyInst:
|
||||
return cast<ApplyInst>(Inst)->getSubstitutionsWithoutSelfSubstitution();
|
||||
case ValueKind::TryApplyInst:
|
||||
return cast<TryApplyInst>(Inst)->getSubstitutionsWithoutSelfSubstitution();
|
||||
default:
|
||||
llvm_unreachable("not implemented for this instruction!");
|
||||
}
|
||||
}
|
||||
|
||||
/// The arguments passed to this instruction.
|
||||
MutableArrayRef<Operand> getArgumentOperands() const {
|
||||
FOREACH_IMPL_RETURN(getArgumentOperands());
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
|
||||
namespace swift {
|
||||
|
||||
SILInstruction *tryDevirtualizeApply(ApplyInst *AI);
|
||||
SILInstruction *tryDevirtualizeApply(FullApplySite AI);
|
||||
bool isClassWithUnboundGenericParameters(SILType C, SILModule &M);
|
||||
bool canDevirtualizeClassMethod(ApplyInst *AI, SILType ClassInstanceType);
|
||||
SILInstruction *devirtualizeClassMethod(ApplyInst *AI, SILValue ClassInstance);
|
||||
SILInstruction *tryDevirtualizeClassMethod(ApplyInst *AI,
|
||||
bool canDevirtualizeClassMethod(FullApplySite AI, SILType ClassInstanceType);
|
||||
SILInstruction *devirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance);
|
||||
SILInstruction *tryDevirtualizeClassMethod(FullApplySite AI,
|
||||
SILValue ClassInstance);
|
||||
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
<< "\n");
|
||||
for (auto &BB : F) {
|
||||
for (auto II = BB.begin(), IE = BB.end(); II != IE;) {
|
||||
ApplyInst *AI = dyn_cast<ApplyInst>(&*II);
|
||||
FullApplySite AI = FullApplySite::isa(&*II);
|
||||
++II;
|
||||
|
||||
if (!AI)
|
||||
@@ -109,37 +109,56 @@ SILTransform *swift::createDevirtualizer() {
|
||||
}
|
||||
|
||||
// A utility function for cloning the apply instruction.
|
||||
static ApplyInst *CloneApply(ApplyInst *AI, SILBuilder &Builder) {
|
||||
static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) {
|
||||
// Clone the Apply.
|
||||
auto Args = AI->getArguments();
|
||||
auto Args = AI.getArguments();
|
||||
SmallVector<SILValue, 8> Ret(Args.size());
|
||||
for (unsigned i = 0, e = Args.size(); i != e; ++i)
|
||||
Ret[i] = Args[i];
|
||||
|
||||
auto NAI = Builder.createApply(AI->getLoc(), AI->getCallee(),
|
||||
AI->getSubstCalleeSILType(),
|
||||
AI->getType(),
|
||||
AI->getSubstitutions(),
|
||||
FullApplySite NAI;
|
||||
|
||||
switch (AI.getInstruction()->getKind()) {
|
||||
case ValueKind::ApplyInst:
|
||||
NAI = Builder.createApply(AI.getLoc(), AI.getCallee(),
|
||||
AI.getSubstCalleeSILType(),
|
||||
AI.getType(),
|
||||
AI.getSubstitutions(),
|
||||
Ret);
|
||||
NAI->setDebugScope(AI->getDebugScope());
|
||||
break;
|
||||
case ValueKind::TryApplyInst: {
|
||||
auto *TryApplyI = cast<TryApplyInst>(AI.getInstruction());
|
||||
NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(),
|
||||
AI.getSubstCalleeSILType(),
|
||||
AI.getSubstitutions(),
|
||||
Ret,
|
||||
TryApplyI->getNormalBB(),
|
||||
TryApplyI->getErrorBB());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Trying to clone an unsupported apply instruction");
|
||||
}
|
||||
|
||||
NAI.getInstruction()->setDebugScope(AI.getDebugScope());
|
||||
return NAI;
|
||||
}
|
||||
|
||||
/// Insert monomorphic inline caches for a specific class or metatype
|
||||
/// type \p SubClassTy.
|
||||
static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI,
|
||||
static FullApplySite speculateMonomorphicTarget(FullApplySite AI,
|
||||
SILType SubType) {
|
||||
// Bail if this class_method cannot be devirtualized.
|
||||
if (!canDevirtualizeClassMethod(AI, SubType))
|
||||
return nullptr;
|
||||
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.
|
||||
SILBasicBlock::iterator It = AI;
|
||||
SILFunction *F = AI->getFunction();
|
||||
SILBasicBlock *Entry = AI->getParent();
|
||||
SILBasicBlock::iterator It = AI.getInstruction();
|
||||
SILFunction *F = AI.getFunction();
|
||||
SILBasicBlock *Entry = AI.getParent();
|
||||
|
||||
// Iden is the basic block containing the direct call.
|
||||
SILBasicBlock *Iden = F->createBasicBlock();
|
||||
@@ -149,13 +168,13 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI,
|
||||
|
||||
SILBasicBlock *Continue = Entry->splitBasicBlock(It);
|
||||
|
||||
SILBuilderWithScope<> Builder(Entry, AI->getDebugScope());
|
||||
SILBuilderWithScope<> Builder(Entry, AI.getDebugScope());
|
||||
// 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());
|
||||
ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee());
|
||||
|
||||
It = Builder.createCheckedCastBranch(AI->getLoc(), /*exact*/ true,
|
||||
It = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true,
|
||||
CMI->getOperand(), SubType, Iden,
|
||||
Virt);
|
||||
|
||||
@@ -181,8 +200,8 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI,
|
||||
}
|
||||
|
||||
// Copy the two apply instructions into the two blocks.
|
||||
ApplyInst *IdenAI = CloneApply(AI, IdenBuilder);
|
||||
ApplyInst *VirtAI = CloneApply(AI, VirtBuilder);
|
||||
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.
|
||||
@@ -200,15 +219,23 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI,
|
||||
|
||||
// Create a PHInode for returning the return value from both apply
|
||||
// instructions.
|
||||
SILArgument *Arg = Continue->createBBArg(AI->getType());
|
||||
IdenBuilder.createBranch(AI->getLoc(), Continue, ArrayRef<SILValue>(IdenAI))
|
||||
->setDebugScope(AI->getDebugScope());
|
||||
VirtBuilder.createBranch(AI->getLoc(), Continue, ArrayRef<SILValue>(VirtAI))
|
||||
->setDebugScope(AI->getDebugScope());
|
||||
SILArgument *Arg = Continue->createBBArg(AI.getType());
|
||||
if (!isa<TryApplyInst>(AI)) {
|
||||
IdenBuilder.createBranch(AI.getLoc(), Continue,
|
||||
ArrayRef<SILValue>(IdenAI.getInstruction()))->setDebugScope(
|
||||
AI.getDebugScope());
|
||||
VirtBuilder.createBranch(AI.getLoc(), Continue,
|
||||
ArrayRef<SILValue>(VirtAI.getInstruction()))->setDebugScope(
|
||||
AI.getDebugScope());
|
||||
}
|
||||
|
||||
// Remove the old Apply instruction.
|
||||
AI->replaceAllUsesWith(Arg);
|
||||
AI->eraseFromParent();
|
||||
if (!isa<TryApplyInst>(AI))
|
||||
AI.getInstruction()->replaceAllUsesWith(Arg);
|
||||
auto *OriginalBB = AI.getParent();
|
||||
AI.getInstruction()->eraseFromParent();
|
||||
if (OriginalBB->empty())
|
||||
OriginalBB->removeFromParent();
|
||||
|
||||
// Update the stats.
|
||||
NumTargetsPredicted++;
|
||||
@@ -222,6 +249,32 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI,
|
||||
if (CMI->hasOneUse())
|
||||
CMI->moveBefore(CMI->use_begin()->getUser());
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -235,12 +288,12 @@ static ApplyInst* speculateMonomorphicTarget(ApplyInst *AI,
|
||||
/// \p CD static class of the instance whose method is being invoked
|
||||
/// \p Subs set of direct subclasses of this class
|
||||
static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA,
|
||||
ApplyInst *AI,
|
||||
FullApplySite AI,
|
||||
ClassDecl *CD,
|
||||
ClassHierarchyAnalysis::ClassList &Subs) {
|
||||
ClassMethodInst *CMI = cast<ClassMethodInst>(AI->getCallee());
|
||||
ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee());
|
||||
auto *Method = CMI->getMember().getFuncDecl();
|
||||
const DeclContext *DC = AI->getModule().getAssociatedContext();
|
||||
const DeclContext *DC = AI.getModule().getAssociatedContext();
|
||||
|
||||
if (CD->isFinal())
|
||||
return true;
|
||||
@@ -262,7 +315,7 @@ static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA,
|
||||
case Accessibility::Public:
|
||||
return false;
|
||||
case Accessibility::Internal:
|
||||
if (!AI->getModule().isWholeModule())
|
||||
if (!AI.getModule().isWholeModule())
|
||||
return false;
|
||||
break;
|
||||
case Accessibility::Private:
|
||||
@@ -323,9 +376,9 @@ static bool isDefaultCaseKnown(ClassHierarchyAnalysis *CHA,
|
||||
|
||||
/// \brief Try to speculate the call target for the call \p AI. This function
|
||||
/// returns true if a change was made.
|
||||
static bool tryToSpeculateTarget(ApplyInst *AI,
|
||||
static bool tryToSpeculateTarget(FullApplySite AI,
|
||||
ClassHierarchyAnalysis *CHA) {
|
||||
ClassMethodInst *CMI = cast<ClassMethodInst>(AI->getCallee());
|
||||
ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee());
|
||||
|
||||
// We cannot devirtualize in cases where dynamic calls are
|
||||
// semantically required.
|
||||
@@ -341,7 +394,7 @@ static bool tryToSpeculateTarget(ApplyInst *AI,
|
||||
// Bail if any generic types parameters of the class instance type are
|
||||
// unbound.
|
||||
// We cannot devirtualize unbound generic calls yet.
|
||||
if (isClassWithUnboundGenericParameters(SubType, AI->getModule()))
|
||||
if (isClassWithUnboundGenericParameters(SubType, AI.getModule()))
|
||||
return false;
|
||||
|
||||
auto &M = CMI->getModule();
|
||||
@@ -365,7 +418,7 @@ static bool tryToSpeculateTarget(ApplyInst *AI,
|
||||
|
||||
DEBUG(llvm::dbgs() << "Inserting monomorphic speculative call for class " <<
|
||||
CD->getName() << "\n");
|
||||
return speculateMonomorphicTarget(AI, SubType);
|
||||
return !!speculateMonomorphicTarget(AI, SubType);
|
||||
}
|
||||
|
||||
// Collect the direct subclasses for the class.
|
||||
@@ -511,11 +564,11 @@ namespace {
|
||||
bool Changed = false;
|
||||
|
||||
// Collect virtual calls that may be specialized.
|
||||
SmallVector<ApplyInst *, 16> ToSpecialize;
|
||||
SmallVector<FullApplySite, 16> ToSpecialize;
|
||||
for (auto &BB : *getFunction()) {
|
||||
for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) {
|
||||
auto *AI = dyn_cast<ApplyInst>(&*II);
|
||||
if (AI && isa<ClassMethodInst>(AI->getCallee()))
|
||||
FullApplySite AI = FullApplySite::isa(&*II);
|
||||
if (AI && isa<ClassMethodInst>(AI.getCallee()))
|
||||
ToSpecialize.push_back(AI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ bool swift::isClassWithUnboundGenericParameters(SILType C, SILModule &M) {
|
||||
// to invoke the method.
|
||||
static ArrayRef<Substitution>
|
||||
getSubstitutionsForCallee(SILModule &M, CanSILFunctionType GenCalleeType,
|
||||
SILType ClassInstanceType, ApplyInst *AI) {
|
||||
SILType ClassInstanceType, FullApplySite AI) {
|
||||
// *NOTE*:
|
||||
// Apply instruction substitutions are for the Member from a protocol or
|
||||
// class B, where this member was first defined, before it got overridden by
|
||||
@@ -163,12 +163,12 @@ getSubstitutionsForCallee(SILModule &M, CanSILFunctionType GenCalleeType,
|
||||
}
|
||||
|
||||
if (ClassSubs.empty())
|
||||
return AI->getSubstitutions();
|
||||
return AI.getSubstitutions();
|
||||
|
||||
auto AISubs = AI->getSubstitutions();
|
||||
auto AISubs = AI.getSubstitutions();
|
||||
|
||||
CanSILFunctionType AIGenCalleeType =
|
||||
AI->getCallee().getType().castTo<SILFunctionType>();
|
||||
AI.getCallee().getType().castTo<SILFunctionType>();
|
||||
|
||||
CanType AISelfClass = AIGenCalleeType->getSelfParameter().getType();
|
||||
|
||||
@@ -228,11 +228,11 @@ static SILFunction *getTargetClassMethod(SILModule &M,
|
||||
/// \p ClassOrMetatypeType is the class type or metatype type we are
|
||||
/// devirtualizing for.
|
||||
/// return true if it is possible to devirtualize, false - otherwise.
|
||||
bool swift::canDevirtualizeClassMethod(ApplyInst *AI,
|
||||
bool swift::canDevirtualizeClassMethod(FullApplySite AI,
|
||||
SILType ClassOrMetatypeType) {
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI);
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction());
|
||||
|
||||
SILModule &Mod = AI->getModule();
|
||||
SILModule &Mod = AI.getModule();
|
||||
|
||||
// Bail if any generic types parameters of the class instance type are
|
||||
// unbound.
|
||||
@@ -244,7 +244,7 @@ bool swift::canDevirtualizeClassMethod(ApplyInst *AI,
|
||||
// either be a metatype or an alloc_ref.
|
||||
DEBUG(llvm::dbgs() << " Origin Type: " << ClassOrMetatypeType);
|
||||
|
||||
auto *CMI = cast<ClassMethodInst>(AI->getCallee());
|
||||
auto *CMI = cast<ClassMethodInst>(AI.getCallee());
|
||||
|
||||
// Find the implementation of the member which should be invoked.
|
||||
auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, CMI->getMember());
|
||||
@@ -257,7 +257,7 @@ bool swift::canDevirtualizeClassMethod(ApplyInst *AI,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AI->getFunction()->isFragile()) {
|
||||
if (AI.getFunction()->isFragile()) {
|
||||
// function_ref inside fragile function cannot reference a private or
|
||||
// hidden symbol.
|
||||
if (!(F->isFragile() || isValidLinkageForFragileRef(F->getLinkage()) ||
|
||||
@@ -313,15 +313,15 @@ static SILValue conditionallyCastAddr(SILBuilderWithScope<16> &B,
|
||||
|
||||
/// Insert instructions to cast the tuple return type into appropiate
|
||||
/// tuple type expected by the original apply_inst.
|
||||
static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI,
|
||||
static SILInstruction *castTupleReturnType(FullApplySite AI, SILValue Value,
|
||||
CanTypeWrapper<TupleType> ResultTupleTy, SILFunction *F, SILBuilder& B) {
|
||||
auto AITupleTy = cast<TupleType>(AI->getType().getSwiftRValueType());
|
||||
auto AITupleTy = cast<TupleType>(AI.getType().getSwiftRValueType());
|
||||
SmallVector<SILValue, 4> TupleElements;
|
||||
auto TupleElementTypes = ResultTupleTy.getElementTypes();
|
||||
unsigned NumElements = ResultTupleTy->getElements().size();
|
||||
for (unsigned i = 0; i < NumElements; ++i) {
|
||||
auto EltTy = TupleElementTypes[i];
|
||||
auto ExtractedElt = B.createTupleExtract(AI->getLoc(), NewAI, i);
|
||||
auto ExtractedElt = B.createTupleExtract(AI.getLoc(), Value, i);
|
||||
OptionalTypeKind OTK;
|
||||
auto OptionalEltTy =
|
||||
EltTy.getCanonicalTypeOrNull()->getAnyOptionalObjectType(OTK);
|
||||
@@ -334,25 +334,25 @@ static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI,
|
||||
|
||||
// Dereference the optional value
|
||||
auto *SomeDecl = B.getASTContext().getOptionalSomeDecl(OTK);
|
||||
auto FuncPtr = B.createUncheckedEnumData(AI->getLoc(), ExtractedElt,
|
||||
auto FuncPtr = B.createUncheckedEnumData(AI.getLoc(), ExtractedElt,
|
||||
SomeDecl);
|
||||
|
||||
auto AIOptionalEltTy =
|
||||
AITupleTy.getElementType(i).getCanonicalTypeOrNull()->getAnyOptionalObjectType();
|
||||
auto SILAIOptionalEltTy = AI->getModule().Types.getLoweredType(
|
||||
auto SILAIOptionalEltTy = AI.getModule().Types.getLoweredType(
|
||||
AIOptionalEltTy);
|
||||
auto ConvertedFuncPtr = B.createConvertFunction(AI->getLoc(), FuncPtr,
|
||||
auto ConvertedFuncPtr = B.createConvertFunction(AI.getLoc(), FuncPtr,
|
||||
SILAIOptionalEltTy);
|
||||
|
||||
TupleElements.push_back(
|
||||
B.createOptionalSome(AI->getLoc(), ConvertedFuncPtr, OTK,
|
||||
B.createOptionalSome(AI.getLoc(), ConvertedFuncPtr, OTK,
|
||||
SILType::getPrimitiveObjectType(AITupleTy.getElementType(i))));
|
||||
}
|
||||
|
||||
// Now create a new tuple
|
||||
DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
|
||||
NumClassDevirt++;
|
||||
return B.createTuple(AI->getLoc(), AI->getType(), TupleElements);
|
||||
return B.createTuple(AI.getLoc(), AI.getType(), TupleElements);
|
||||
}
|
||||
|
||||
/// \brief Devirtualize an apply of a class method.
|
||||
@@ -361,12 +361,12 @@ static SILInstruction *castTupleReturnType(ApplyInst *AI, ApplyInst *NewAI,
|
||||
/// \p ClassOrMetatype is a class value or metatype value that is the
|
||||
/// self argument of the apply we will devirtualize.
|
||||
/// return the new ApplyInst if created one or null.
|
||||
SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
SILInstruction *swift::devirtualizeClassMethod(FullApplySite AI,
|
||||
SILValue ClassOrMetatype) {
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI);
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction());
|
||||
|
||||
SILModule &Mod = AI->getModule();
|
||||
auto *CMI = cast<ClassMethodInst>(AI->getCallee());
|
||||
SILModule &Mod = AI.getModule();
|
||||
auto *CMI = cast<ClassMethodInst>(AI.getCallee());
|
||||
auto ClassOrMetatypeType = ClassOrMetatype.getType();
|
||||
auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, CMI->getMember());
|
||||
|
||||
@@ -378,18 +378,18 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
if (GenCalleeType->isPolymorphic())
|
||||
SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Mod.getSwiftModule(), Subs);
|
||||
|
||||
SILBuilderWithScope<16> B(AI);
|
||||
FunctionRefInst *FRI = B.createFunctionRef(AI->getLoc(), F);
|
||||
SILBuilderWithScope<16> 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 Args = AI.getArguments();
|
||||
auto ParamTypes = SubstCalleeType->getParameterSILTypes();
|
||||
|
||||
for (unsigned i = 0, e = Args.size() - 1; i != e; ++i)
|
||||
NewArgs.push_back(conditionallyCastAddr(B, AI->getLoc(), Args[i],
|
||||
NewArgs.push_back(conditionallyCastAddr(B, AI.getLoc(), Args[i],
|
||||
ParamTypes[i]));
|
||||
|
||||
// Add the self argument, upcasting if required because we're
|
||||
@@ -398,32 +398,66 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
if (ClassOrMetatypeType == SelfParamTy)
|
||||
NewArgs.push_back(ClassOrMetatype);
|
||||
else
|
||||
NewArgs.push_back(B.createUpcast(AI->getLoc(), ClassOrMetatype,
|
||||
NewArgs.push_back(B.createUpcast(AI.getLoc(), ClassOrMetatype,
|
||||
SelfParamTy));
|
||||
|
||||
// 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 ReturnType = AI->getType();
|
||||
SILType ReturnType = AI.getType();
|
||||
if (!SubstCalleeType->hasIndirectResult()) {
|
||||
ReturnType = SubstCalleeType->getSILResult();
|
||||
}
|
||||
|
||||
SILType SubstCalleeSILType =
|
||||
SILType::getPrimitiveObjectType(SubstCalleeType);
|
||||
SILInstruction *NewAI =
|
||||
B.createApply(AI->getLoc(), FRI, SubstCalleeSILType, ReturnType,
|
||||
Subs, NewArgs);
|
||||
FullApplySite NewAI;
|
||||
|
||||
if (ReturnType == AI->getType()) {
|
||||
SILBasicBlock *ResultBB = nullptr;
|
||||
SILBasicBlock *NormalBB = nullptr;
|
||||
SILValue ResultValue;
|
||||
|
||||
if (!isa<TryApplyInst>(AI)) {
|
||||
NewAI = B.createApply(AI.getLoc(), FRI, SubstCalleeSILType, ReturnType,
|
||||
Subs, NewArgs);
|
||||
ResultValue = SILValue(NewAI.getInstruction(), 0);
|
||||
} else {
|
||||
auto *TAI = cast<TryApplyInst>(AI);
|
||||
// Always create a new BB for normal and error BBs.
|
||||
// This avoids creation of critical edges.
|
||||
ResultBB = B.getFunction().createBasicBlock();
|
||||
ResultBB->createBBArg(ReturnType);
|
||||
NormalBB = TAI->getNormalBB();
|
||||
|
||||
auto *ErrorBB = B.getFunction().createBasicBlock();
|
||||
ErrorBB->createBBArg(TAI->getErrorBB()->getBBArg(0)->getType());
|
||||
|
||||
NewAI = B.createTryApply(AI.getLoc(), FRI, SubstCalleeSILType,
|
||||
Subs, NewArgs,
|
||||
ResultBB, ErrorBB);
|
||||
// The result value is passed as a parameter to the normal block.
|
||||
ResultValue = ResultBB->getBBArg(0);
|
||||
B.setInsertionPoint(ErrorBB);
|
||||
B.createBranch(TAI->getLoc(), TAI->getErrorBB(),
|
||||
{ErrorBB->getBBArg(0)});
|
||||
B.setInsertionPoint(ResultBB);
|
||||
}
|
||||
|
||||
SILInstruction *CastedReturnValue = NewAI.getInstruction();
|
||||
|
||||
if (ReturnType == AI.getType()) {
|
||||
DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
|
||||
NumClassDevirt++;
|
||||
return NewAI;
|
||||
if (NormalBB) {
|
||||
B.createBranch(NewAI.getLoc(), NormalBB, { ResultBB->getBBArg(0) });
|
||||
}
|
||||
return CastedReturnValue;
|
||||
}
|
||||
|
||||
// If our return type differs from AI's return type, then we know that we have
|
||||
// a covariant return type. Cast it before we RAUW. This can not happen
|
||||
|
||||
|
||||
// Accessors could return a tuple where one of the elements is of a function
|
||||
// type, which may refer to a subclass instead of a superclass in its
|
||||
// signature.
|
||||
@@ -432,9 +466,14 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
// the function types and reconstruct the tuple.
|
||||
if (auto *FD = dyn_cast<FuncDecl>(CMI->getMember().getDecl())) {
|
||||
if (FD->isAccessor()) {
|
||||
if (auto ResultTupleTy = dyn_cast<TupleType>(ReturnType.getSwiftRValueType()))
|
||||
return castTupleReturnType(AI, dyn_cast<ApplyInst>(NewAI),
|
||||
ResultTupleTy, F, B);
|
||||
if (auto ResultTupleTy = dyn_cast<TupleType>(ReturnType.getSwiftRValueType())) {
|
||||
assert(isa<ApplyInst>(NewAI) && "should be an apply_inst");
|
||||
CastedReturnValue = castTupleReturnType(AI, ResultValue, ResultTupleTy, F, B);
|
||||
if (NormalBB) {
|
||||
B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue });
|
||||
}
|
||||
return CastedReturnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +488,7 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
auto OptionalReturnType = ReturnType.getSwiftRValueType()
|
||||
.getAnyOptionalObjectType(OTK);
|
||||
|
||||
auto OptionalAIType = AI->getType().getSwiftRValueType()
|
||||
auto OptionalAIType = AI.getType().getSwiftRValueType()
|
||||
.getAnyOptionalObjectType(AI_OTK);
|
||||
|
||||
// Return type if not an optional, but the expected type is an optional
|
||||
@@ -462,7 +501,7 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
auto OptType = OptionalType::get(AI_OTK,
|
||||
ReturnType.getSwiftRValueType()).
|
||||
getCanonicalTypeOrNull();
|
||||
NewAI = B.createOptionalSome(AI->getLoc(), NewAI,
|
||||
ResultValue = B.createOptionalSome(AI.getLoc(), ResultValue,
|
||||
AI_OTK,
|
||||
SILType::getPrimitiveObjectType(OptType));
|
||||
OptionalReturnType = ReturnType.getSwiftRValueType();
|
||||
@@ -470,7 +509,11 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
if (OptionalAIType == OptionalReturnType) {
|
||||
DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
|
||||
NumClassDevirt++;
|
||||
return NewAI;
|
||||
CastedReturnValue = dyn_cast<SILInstruction>(ResultValue.getDef());
|
||||
if (NormalBB) {
|
||||
B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue });
|
||||
}
|
||||
return CastedReturnValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,10 +524,14 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
// Both types are optional and one of them is the superclass of the other.
|
||||
DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
|
||||
NumClassDevirt++;
|
||||
return B.createUpcast(AI->getLoc(), SILValue(NewAI, 0), AI->getType());
|
||||
CastedReturnValue = B.createUpcast(AI.getLoc(), ResultValue, AI.getType());
|
||||
if (NormalBB) {
|
||||
B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue });
|
||||
}
|
||||
return CastedReturnValue;
|
||||
}
|
||||
|
||||
if (OptionalReturnType == AI->getType().getSwiftRValueType()) {
|
||||
if (OptionalReturnType == AI.getType().getSwiftRValueType()) {
|
||||
UnwrapOptionalResult = true;
|
||||
}
|
||||
|
||||
@@ -498,28 +545,34 @@ SILInstruction *swift::devirtualizeClassMethod(ApplyInst *AI,
|
||||
"Only addresses and refs can have their types changed due to "
|
||||
"covariant return types or contravariant argument types.");
|
||||
|
||||
SILInstruction *CastedAI = NewAI;
|
||||
if (UnwrapOptionalResult) {
|
||||
// The devirtualized method returns an optional result.
|
||||
// We need to extract the actual result from the optional.
|
||||
auto *SomeDecl = B.getASTContext().getOptionalSomeDecl(OTK);
|
||||
CastedAI = B.createUncheckedEnumData(AI->getLoc(), NewAI, SomeDecl);
|
||||
CastedReturnValue = B.createUncheckedEnumData(AI.getLoc(),
|
||||
ResultValue, SomeDecl);
|
||||
} else if (WrapOptionalResult) {
|
||||
// The devirtualized method returns a non-optional result.
|
||||
// We need to wrap it into an optional.
|
||||
CastedAI = B.createOptionalSome(AI->getLoc(), NewAI, AI_OTK, AI->getType());
|
||||
CastedReturnValue = B.createOptionalSome(AI.getLoc(), ResultValue,
|
||||
AI_OTK, AI.getType());
|
||||
} else if (ReturnType.isAddress()) {
|
||||
CastedAI = B.createUncheckedAddrCast(AI->getLoc(), NewAI, AI->getType());
|
||||
CastedReturnValue = B.createUncheckedAddrCast(AI.getLoc(),
|
||||
ResultValue, AI.getType());
|
||||
} else {
|
||||
CastedAI = B.createUncheckedRefCast(AI->getLoc(), NewAI, AI->getType());
|
||||
CastedReturnValue = B.createUncheckedRefCast(AI.getLoc(),
|
||||
ResultValue, AI.getType());
|
||||
}
|
||||
|
||||
DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
|
||||
NumClassDevirt++;
|
||||
return CastedAI;
|
||||
if (NormalBB) {
|
||||
B.createBranch(NewAI.getLoc(), NormalBB, { CastedReturnValue });
|
||||
}
|
||||
return CastedReturnValue;
|
||||
}
|
||||
|
||||
SILInstruction *swift::tryDevirtualizeClassMethod(ApplyInst *AI,
|
||||
SILInstruction *swift::tryDevirtualizeClassMethod(FullApplySite AI,
|
||||
SILValue ClassInstance) {
|
||||
if (!canDevirtualizeClassMethod(AI, ClassInstance.getType()))
|
||||
return nullptr;
|
||||
@@ -534,11 +587,11 @@ SILInstruction *swift::tryDevirtualizeClassMethod(ApplyInst *AI,
|
||||
/// 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 ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F,
|
||||
static ApplyInst *devirtualizeWitnessMethod(FullApplySite 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();
|
||||
auto &Module = AI.getModule();
|
||||
|
||||
// Collect all the required substitutions.
|
||||
//
|
||||
@@ -548,7 +601,7 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F,
|
||||
SmallVector<Substitution, 16> NewSubstList(Subs.begin(), Subs.end());
|
||||
|
||||
// Add the non-self-derived substitutions from the original application.
|
||||
for (auto &origSub : AI->getSubstitutionsWithoutSelfSubstitution())
|
||||
for (auto &origSub : AI.getSubstitutionsWithoutSelfSubstitution())
|
||||
if (!origSub.getArchetype()->isSelfDerived())
|
||||
NewSubstList.push_back(origSub);
|
||||
|
||||
@@ -567,10 +620,10 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F,
|
||||
|
||||
// Iterate over the non self arguments and add them to the
|
||||
// new argument list, upcasting when required.
|
||||
SILBuilderWithScope<8> B(AI);
|
||||
for (SILValue A : AI->getArguments()) {
|
||||
SILBuilderWithScope<8> B(AI.getInstruction());
|
||||
for (SILValue A : AI.getArguments()) {
|
||||
if (A.getType() != *ParamType)
|
||||
A = B.createUpcast(AI->getLoc(), A, *ParamType);
|
||||
A = B.createUpcast(AI.getLoc(), A, *ParamType);
|
||||
|
||||
Arguments.push_back(A);
|
||||
++ParamType;
|
||||
@@ -578,8 +631,8 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F,
|
||||
|
||||
// Replace old apply instruction by a new apply instruction that invokes
|
||||
// the witness thunk.
|
||||
SILBuilderWithScope<2> Builder(AI);
|
||||
SILLocation Loc = AI->getLoc();
|
||||
SILBuilderWithScope<2> Builder(AI.getInstruction());
|
||||
SILLocation Loc = AI.getLoc();
|
||||
FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F);
|
||||
|
||||
auto SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeCanType);
|
||||
@@ -594,15 +647,15 @@ static ApplyInst *devirtualizeWitnessMethod(ApplyInst *AI, SILFunction *F,
|
||||
/// 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.
|
||||
static ApplyInst *tryDevirtualizeWitnessMethod(ApplyInst *AI) {
|
||||
static ApplyInst *tryDevirtualizeWitnessMethod(FullApplySite AI) {
|
||||
SILFunction *F;
|
||||
ArrayRef<Substitution> Subs;
|
||||
SILWitnessTable *WT;
|
||||
|
||||
auto *WMI = cast<WitnessMethodInst>(AI->getCallee());
|
||||
auto *WMI = cast<WitnessMethodInst>(AI.getCallee());
|
||||
|
||||
std::tie(F, WT, Subs) =
|
||||
AI->getModule().lookUpFunctionInWitnessTable(WMI->getConformance(),
|
||||
AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(),
|
||||
WMI->getMember());
|
||||
|
||||
if (!F)
|
||||
@@ -652,15 +705,15 @@ static bool isKnownFinal(SILModule &M, SILDeclRef Member) {
|
||||
|
||||
/// Attempt to devirtualize the given apply if possible, and return a
|
||||
/// new instruction in that case, or nullptr otherwise.
|
||||
SILInstruction *swift::tryDevirtualizeApply(ApplyInst *AI) {
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize: " << *AI);
|
||||
SILInstruction *swift::tryDevirtualizeApply(FullApplySite AI) {
|
||||
DEBUG(llvm::dbgs() << " Trying to devirtualize: " << *AI.getInstruction());
|
||||
|
||||
// Devirtualize apply instructions that call witness_method instructions:
|
||||
//
|
||||
// %8 = witness_method $Optional<UInt16>, #LogicValue.boolValue!getter.1
|
||||
// %9 = apply %8<Self = CodeUnit?>(%6#1) : ...
|
||||
//
|
||||
if (isa<WitnessMethodInst>(AI->getCallee()))
|
||||
if (isa<WitnessMethodInst>(AI.getCallee()))
|
||||
return tryDevirtualizeWitnessMethod(AI);
|
||||
|
||||
/// Optimize a class_method and alloc_ref pair into a direct function
|
||||
@@ -679,7 +732,7 @@ SILInstruction *swift::tryDevirtualizeApply(ApplyInst *AI) {
|
||||
/// into
|
||||
///
|
||||
/// %YY = function_ref @...
|
||||
if (auto *CMI = dyn_cast<ClassMethodInst>(AI->getCallee())) {
|
||||
if (auto *CMI = dyn_cast<ClassMethodInst>(AI.getCallee())) {
|
||||
// Check if the class member is known to be final.
|
||||
if (isKnownFinal(CMI->getModule(), CMI->getMember()))
|
||||
return tryDevirtualizeClassMethod(AI, CMI->getOperand());
|
||||
|
||||
@@ -220,6 +220,7 @@ ApplyInst *swift::findApplyFromDevirtualizedResult(SILInstruction *I) {
|
||||
// value, and delete the old apply.
|
||||
void swift::replaceDeadApply(FullApplySite Old, SILInstruction *New) {
|
||||
auto *OldApply = Old.getInstruction();
|
||||
if (!isa<TryApplyInst>(OldApply))
|
||||
OldApply->replaceAllUsesWith(New);
|
||||
recursivelyDeleteTriviallyDeadInstructions(OldApply, true);
|
||||
}
|
||||
|
||||
144
test/SILPasses/devirt_try_apply.swift
Normal file
144
test/SILPasses/devirt_try_apply.swift
Normal file
@@ -0,0 +1,144 @@
|
||||
// RUN: %target-swift-frontend -O -primary-file %s -emit-sil -sil-inline-threshold 1000 -sil-verify-all | FileCheck %s
|
||||
|
||||
private class Base {
|
||||
@inline(never)
|
||||
func foo() throws -> Int32? {
|
||||
print("Base")
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
func boo1() throws -> Base {
|
||||
return self
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
func boo2() throws -> Base? {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Derived1: Base {
|
||||
@inline(never)
|
||||
override func foo() throws -> Int32? {
|
||||
print("Derived1")
|
||||
return 1
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
override func boo1() throws -> Derived1 {
|
||||
return self
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
override func boo2() throws -> Derived1? {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
private class Derived2: Base {
|
||||
@inline(never)
|
||||
override func foo() throws -> Int32 {
|
||||
print("Derived2")
|
||||
return 2
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
override func boo1() throws -> Derived2 {
|
||||
return self
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
override func boo2() throws -> Derived2 {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil private [noinline] @_TTSf4g___TF16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A219testTryApplyDevirt1
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived13foofS0_FzT_GSqVSs5Int32_ : $@convention(method) (@guaranteed Derived1) -> (Optional<Int32>, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived23foofS0_FzT_VSs5Int32 : $@convention(method) (@guaranteed Derived2) -> (Int32, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A24Base3foofS0_FzT_GSqVSs5Int32_ : $@convention(method) (@guaranteed Base) -> (Optional<Int32>, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
private func testTryApplyDevirt1(b: Base) -> Int32? {
|
||||
var result: Int32? = nil
|
||||
do {
|
||||
result = try b.foo()
|
||||
} catch _ {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil private [noinline] @_TTSf4g___TF16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A219testTryApplyDevirt2
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived14boo1fS0_FzT_S0_ : $@convention(method) (@guaranteed Derived1) -> (@owned Derived1, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived24boo1fS0_FzT_S0_ : $@convention(method) (@guaranteed Derived2) -> (@owned Derived2, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A24Base4boo1fS0_FzT_S0_ : $@convention(method) (@guaranteed Base) -> (@owned Base, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
private func testTryApplyDevirt2(b: Base) -> Base? {
|
||||
var result: Base? = nil
|
||||
do {
|
||||
result = try b.boo1()
|
||||
} catch _ {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil private [noinline] @_TTSf4g___TF16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A219testTryApplyDevirt3
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived14boo2fS0_FzT_GSqS0__ : $@convention(method) (@guaranteed Derived1) -> (@owned Optional<Derived1>, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A28Derived24boo2fS0_FzT_S0_ : $@convention(method) (@guaranteed Derived2) -> (@owned Derived2, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK-NOT: }
|
||||
// CHECK: function_ref @_TFC16devirt_try_applyP33_E45F5529CC31A51875E58096B25575A24Base4boo2fS0_FzT_GSqS0__ : $@convention(method) (@guaranteed Base) -> (@owned Optional<Base>, @error ErrorType)
|
||||
// CHECK: try_apply
|
||||
// CHECK-NOT: class_method
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
private func testTryApplyDevirt3(b: Base) -> Base? {
|
||||
var result: Base? = nil
|
||||
do {
|
||||
result = try b.boo2()
|
||||
} catch _ {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public func test1() {
|
||||
testTryApplyDevirt1(Base())
|
||||
}
|
||||
|
||||
public func test2() {
|
||||
testTryApplyDevirt2(Base())
|
||||
}
|
||||
|
||||
public func test3() {
|
||||
testTryApplyDevirt3(Base())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user