Update SIL devirtualization to handle begin_apply instructions.
In order to make this reasonable, I needed to shift responsibilities
around a little; the devirtualization operation is now responsible for
replacing uses of the original apply. I wanted to remove the
phase-separation completely, but there was optimization-remark code
relying on the old apply site not having been deleted yet.
The begin_apply aspects of this aren't testable independently of
replacing materializeForSet because coroutines are currently never
called indirectly.
diff --git a/include/swift/SILOptimizer/Utils/Devirtualize.h b/include/swift/SILOptimizer/Utils/Devirtualize.h
index 9119be7..ee08fd8 100644
--- a/include/swift/SILOptimizer/Utils/Devirtualize.h
+++ b/include/swift/SILOptimizer/Utils/Devirtualize.h
@@ -35,22 +35,6 @@
class Emitter;
}
-/// A pair representing results of devirtualization.
-/// - The first element is the value representing the result of the
-/// devirtualized call.
-/// - The second element is the new apply/try_apply instruction.
-/// If no devirtualization was possible, the pair:
-/// <nullptr, FullApplySite()> is returned.
-///
-/// Two elements are required, because a result of the new devirtualized
-/// apply/try_apply instruction (second element) eventually needs to be
-/// casted to produce a properly typed value (first element).
-///
-/// *NOTE* The reason why we use a ValueBase here instead of a SILInstruction is
-/// that a devirtualization result may be a BB arg if there was a cast in
-/// between optional types.
-typedef std::pair<ValueBase *, ApplySite> DevirtualizationResult;
-
/// Compute all subclasses of a given class.
///
/// \p CHA class hierarchy analysis
@@ -64,9 +48,14 @@
SILModule &M,
ClassHierarchyAnalysis::ClassList &Subs);
-DevirtualizationResult tryDevirtualizeApply(ApplySite AI,
- ClassHierarchyAnalysis *CHA,
- OptRemark::Emitter *ORE = nullptr);
+/// Attempt to devirtualize the given apply site. If this fails,
+/// the returned ApplySite will be null.
+///
+/// If this succeeds, the caller must call deleteDevirtualizedApply on
+/// the original apply site.
+ApplySite tryDevirtualizeApply(ApplySite AI,
+ ClassHierarchyAnalysis *CHA,
+ OptRemark::Emitter *ORE = nullptr);
bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);
bool isNominalTypeWithUnboundGenericParameters(SILType Ty, SILModule &M);
bool canDevirtualizeClassMethod(FullApplySite AI, SILType ClassInstanceType,
@@ -74,15 +63,45 @@
bool isEffectivelyFinalMethod = false);
SILFunction *getTargetClassMethod(SILModule &M, SILType ClassOrMetatypeType,
MethodInst *MI);
-DevirtualizationResult devirtualizeClassMethod(FullApplySite AI,
- SILValue ClassInstance,
- OptRemark::Emitter *ORE);
-DevirtualizationResult
+
+/// Devirtualize the given apply site, which is known to be devirtualizable.
+///
+/// The caller must call deleteDevirtualizedApply on the original apply site.
+FullApplySite devirtualizeClassMethod(FullApplySite AI,
+ SILValue ClassInstance,
+ OptRemark::Emitter *ORE);
+
+/// Attempt to devirtualize the given apply site, which is known to be
+/// of a class method. If this fails, the returned FullApplySite will be null.
+///
+/// If this succeeds, the caller must call deleteDevirtualizedApply on
+/// the original apply site.
+FullApplySite
tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
OptRemark::Emitter *ORE,
bool isEffectivelyFinalMethod = false);
-DevirtualizationResult
-tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
-}
+
+/// Attempt to devirtualize the given apply site, which is known to be
+/// of a witness method. If this fails, the returned FullApplySite
+/// will be null.
+///
+/// If this succeeds, the caller must call deleteDevirtualizedApply on
+/// the original apply site.
+ApplySite tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
+
+/// Delete a successfully-devirtualized apply site. This must always be
+/// called after devirtualizing an apply; not only is it not semantically
+/// equivalent to leave the old apply in-place, but the SIL isn't necessary
+/// well-formed.
+///
+/// Devirtualization is responsible for replacing uses of the original
+/// apply site with uses of the new one. The only thing this does is delete
+/// the instruction and any now-trivially-dead operands; it is separated
+/// from the actual devirtualization step only to apply the caller to log
+/// information about the original apply site. This is probably not a
+/// good enough reason to complicate the API.
+void deleteDevirtualizedApply(ApplySite AI);
+
+} // end namespace swift
#endif
diff --git a/include/swift/SILOptimizer/Utils/Local.h b/include/swift/SILOptimizer/Utils/Local.h
index 0d0835f..0037b6a 100644
--- a/include/swift/SILOptimizer/Utils/Local.h
+++ b/include/swift/SILOptimizer/Utils/Local.h
@@ -119,11 +119,6 @@
/// after \p ABI and returns it.
ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index);
-/// Replace an apply with an instruction that produces the same value,
-/// then delete the apply and the instructions that produce its callee
-/// if possible.
-void replaceDeadApply(ApplySite Old, ValueBase *New);
-
/// \brief Return true if any call inside the given function may bind dynamic
/// 'Self' to a generic argument of the callee.
bool mayBindDynamicSelf(SILFunction *F);
diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp
index 9918dbc..abcf59a 100644
--- a/lib/SIL/SILVerifier.cpp
+++ b/lib/SIL/SILVerifier.cpp
@@ -4527,6 +4527,7 @@
SILModule &M = F->getModule();
for (auto &BB : *F) {
TermInst *TI = BB.getTerminator();
+ CurInstruction = TI;
// Check for non-cond_br critical edges in canonical SIL.
if (!isa<CondBranchInst>(TI) && M.getStage() == SILStage::Canonical) {
diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp
index c24852b..5a00f2e 100644
--- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp
+++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp
@@ -434,14 +434,14 @@
static std::tuple<FullApplySite, SILBasicBlock::iterator>
tryDevirtualizeApplyHelper(FullApplySite InnerAI, SILBasicBlock::iterator I,
ClassHierarchyAnalysis *CHA) {
- auto NewInstPair = tryDevirtualizeApply(InnerAI, CHA);
- if (!NewInstPair.second) {
+ auto NewInst = tryDevirtualizeApply(InnerAI, CHA);
+ if (!NewInst) {
return std::make_tuple(InnerAI, I);
}
- replaceDeadApply(InnerAI, NewInstPair.first);
+ deleteDevirtualizedApply(InnerAI);
- auto newApplyAI = NewInstPair.second.getInstruction();
+ auto newApplyAI = NewInst.getInstruction();
assert(newApplyAI && "devirtualized but removed apply site?");
return std::make_tuple(FullApplySite::isa(newApplyAI),
diff --git a/lib/SILOptimizer/Transforms/Devirtualizer.cpp b/lib/SILOptimizer/Transforms/Devirtualizer.cpp
index f2fd84f..36ccbb8 100644
--- a/lib/SILOptimizer/Transforms/Devirtualizer.cpp
+++ b/lib/SILOptimizer/Transforms/Devirtualizer.cpp
@@ -69,14 +69,14 @@
}
}
for (auto Apply : Applies) {
- auto NewInstPair = tryDevirtualizeApply(Apply, CHA, &ORE);
- if (!NewInstPair.second)
+ auto NewInst = tryDevirtualizeApply(Apply, CHA, &ORE);
+ if (!NewInst)
continue;
Changed = true;
- replaceDeadApply(Apply, NewInstPair.first);
- NewApplies.push_back(NewInstPair.second);
+ deleteDevirtualizedApply(Apply);
+ NewApplies.push_back(NewInst);
}
// For each new apply, attempt to link in function bodies if we do
diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
index 88fadb2..6205f1b 100644
--- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
+++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
@@ -29,6 +29,7 @@
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/PassManager.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
+#include "swift/SILOptimizer/Utils/CFG.h"
#include "swift/SILOptimizer/Utils/Devirtualize.h"
#include "swift/SILOptimizer/Utils/SILInliner.h"
#include "swift/AST/ASTContext.h"
@@ -47,6 +48,38 @@
STATISTIC(NumTargetsPredicted, "Number of monomorphic functions predicted");
+/// We want to form a second edge to the given block, but we know
+/// that'll form a critical edge. Return a basic block to which we can
+/// create an edge essentially like the original edge.
+static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) {
+#ifndef NDEBUG
+ auto origDestBB = TI->getSuccessors()[SuccIndex].getBB();
+#endif
+
+ // Split the edge twice. The first split will become our cloned
+ // and temporarily-unused edge. The second split will remain in place
+ // as the original edge.
+ auto clonedEdgeBB = splitEdge(TI, SuccIndex);
+ auto replacementEdgeBB = splitEdge(TI, SuccIndex);
+
+ // Extract the terminators.
+ auto clonedEdgeBranch =
+ cast<BranchInst>(clonedEdgeBB->getTerminator());
+ auto replacementEdgeBranch =
+ cast<BranchInst>(replacementEdgeBB->getTerminator());
+
+ assert(TI->getSuccessors()[SuccIndex].getBB() == replacementEdgeBB);
+ assert(replacementEdgeBranch->getDestBB() == clonedEdgeBB);
+ assert(clonedEdgeBranch->getDestBB() == origDestBB);
+
+ // Change the replacement branch to point to the original destination.
+ // This will leave the cloned edge unused, which is how we wanted it.
+ replacementEdgeBranch->getSuccessors()[0] = clonedEdgeBranch->getDestBB();
+ assert(clonedEdgeBB->pred_empty());
+
+ return clonedEdgeBB;
+}
+
// A utility function for cloning the apply instruction.
static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) {
// Clone the Apply.
@@ -68,18 +101,17 @@
break;
case SILInstructionKind::TryApplyInst: {
auto *TryApplyI = cast<TryApplyInst>(AI.getInstruction());
+ auto NormalBB = cloneEdge(TryApplyI, TryApplyInst::NormalIdx);
+ auto ErrorBB = cloneEdge(TryApplyI, TryApplyInst::ErrorIdx);
NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(),
AI.getSubstitutionMap(),
- Ret,
- TryApplyI->getNormalBB(),
- TryApplyI->getErrorBB());
- }
+ Ret, NormalBB, ErrorBB);
break;
+ }
default:
llvm_unreachable("Trying to clone an unsupported apply instruction");
}
- NAI.getInstruction();
return NAI;
}
@@ -96,6 +128,10 @@
if (SubType.getASTType()->hasDynamicSelfType())
return FullApplySite();
+ // Can't speculate begin_apply yet.
+ if (isa<BeginApplyInst>(AI))
+ 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
@@ -182,10 +218,10 @@
NumTargetsPredicted++;
// Devirtualize the apply instruction on the identical path.
- auto NewInstPair =
+ auto NewInst =
devirtualizeClassMethod(IdenAI, DownCastedClassInstance, nullptr);
- assert(NewInstPair.first && "Expected to be able to devirtualize apply!");
- replaceDeadApply(IdenAI, NewInstPair.first);
+ assert(NewInst && "Expected to be able to devirtualize apply!");
+ deleteDevirtualizedApply(IdenAI);
// Split critical edges resulting from VirtAI.
if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) {
@@ -366,10 +402,10 @@
// try to devirtualize it completely.
ClassHierarchyAnalysis::ClassList Subs;
if (isDefaultCaseKnown(CHA, AI, CD, Subs)) {
- auto NewInstPair = tryDevirtualizeClassMethod(AI, SubTypeValue, &ORE);
- if (NewInstPair.first)
- replaceDeadApply(AI, NewInstPair.first);
- return NewInstPair.second.getInstruction() != nullptr;
+ auto NewInst = tryDevirtualizeClassMethod(AI, SubTypeValue, &ORE);
+ if (NewInst)
+ deleteDevirtualizedApply(AI);
+ return bool(NewInst);
}
LLVM_DEBUG(llvm::dbgs() << "Inserting monomorphic speculative call for "
@@ -529,10 +565,10 @@
ORE.emit(RB);
return true;
}
- auto NewInstPair = tryDevirtualizeClassMethod(AI, SubTypeValue, nullptr);
- if (NewInstPair.first) {
+ auto NewInst = tryDevirtualizeClassMethod(AI, SubTypeValue, nullptr);
+ if (NewInst) {
ORE.emit(RB);
- replaceDeadApply(AI, NewInstPair.first);
+ deleteDevirtualizedApply(AI);
return true;
}
diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp
index 4410b59..5683fd5 100644
--- a/lib/SILOptimizer/Utils/Devirtualize.cpp
+++ b/lib/SILOptimizer/Utils/Devirtualize.cpp
@@ -466,6 +466,140 @@
baseCalleeSig);
}
+static ApplyInst *replaceApplyInst(SILBuilder &B, SILLocation Loc,
+ ApplyInst *OldAI,
+ SILValue NewFn,
+ SubstitutionMap NewSubs,
+ ArrayRef<SILValue> NewArgs) {
+ auto *NewAI = B.createApply(Loc, NewFn, NewSubs, NewArgs,
+ OldAI->isNonThrowing());
+
+ // Check if any casting is required for the return value.
+ SILValue ResultValue =
+ castValueToABICompatibleType(&B, Loc, NewAI, NewAI->getType(),
+ OldAI->getType());
+
+ OldAI->replaceAllUsesWith(ResultValue);
+ return NewAI;
+}
+
+static TryApplyInst *replaceTryApplyInst(SILBuilder &B, SILLocation Loc,
+ TryApplyInst *OldTAI,
+ SILValue NewFn,
+ SubstitutionMap NewSubs,
+ ArrayRef<SILValue> NewArgs,
+ SILFunctionConventions Conv) {
+ SILBasicBlock *NormalBB = OldTAI->getNormalBB();
+ SILBasicBlock *ResultBB = nullptr;
+
+ SILType NewResultTy = Conv.getSILResultType();
+
+ // Does the result value need to be casted?
+ auto OldResultTy = NormalBB->getArgument(0)->getType();
+ bool ResultCastRequired = NewResultTy != OldResultTy;
+
+ // Create a new normal BB only if the result of the new apply differs
+ // in type from the argument of the original normal BB.
+ if (!ResultCastRequired) {
+ ResultBB = NormalBB;
+ } else {
+ ResultBB = B.getFunction().createBasicBlockBefore(NormalBB);
+ ResultBB->createPHIArgument(NewResultTy, ValueOwnershipKind::Owned);
+ }
+
+ // We can always just use the original error BB because we'll be
+ // deleting the edge to it from the old TAI.
+ SILBasicBlock *ErrorBB = OldTAI->getErrorBB();
+
+ // Insert a try_apply here.
+ // Note that this makes this block temporarily double-terminated!
+ // We won't fix that until deleteDevirtualizedApply.
+ auto NewTAI = B.createTryApply(Loc, NewFn, NewSubs, NewArgs,
+ ResultBB, ErrorBB);
+
+ if (ResultCastRequired) {
+ B.setInsertionPoint(ResultBB);
+
+ SILValue ResultValue = ResultBB->getArgument(0);
+ ResultValue = castValueToABICompatibleType(&B, Loc, ResultValue,
+ NewResultTy, OldResultTy);
+
+ B.createBranch(Loc, NormalBB, { ResultValue });
+ }
+
+ B.setInsertionPoint(NormalBB->begin());
+ return NewTAI;
+}
+
+static BeginApplyInst *replaceBeginApplyInst(SILBuilder &B, SILLocation Loc,
+ BeginApplyInst *OldBAI,
+ SILValue NewFn,
+ SubstitutionMap NewSubs,
+ ArrayRef<SILValue> NewArgs) {
+ auto NewBAI = B.createBeginApply(Loc, NewFn, NewSubs, NewArgs,
+ OldBAI->isNonThrowing());
+
+ // Forward the token.
+ OldBAI->getTokenResult()->replaceAllUsesWith(NewBAI->getTokenResult());
+
+ auto OldYields = OldBAI->getYieldedValues();
+ auto NewYields = NewBAI->getYieldedValues();
+ assert(OldYields.size() == NewYields.size());
+
+ for (auto i : indices(OldYields)) {
+ auto OldYield = OldYields[i];
+ auto NewYield = NewYields[i];
+ NewYield = castValueToABICompatibleType(&B, Loc, NewYield,
+ NewYield->getType(),
+ OldYield->getType());
+ OldYield->replaceAllUsesWith(NewYield);
+ }
+
+ return NewBAI;
+}
+
+static PartialApplyInst *replacePartialApplyInst(SILBuilder &B, SILLocation Loc,
+ PartialApplyInst *OldPAI,
+ SILValue NewFn,
+ SubstitutionMap NewSubs,
+ ArrayRef<SILValue> NewArgs) {
+ auto Convention =
+ OldPAI->getType().getAs<SILFunctionType>()->getCalleeConvention();
+ auto *NewPAI = B.createPartialApply(Loc, NewFn, NewSubs, NewArgs,
+ Convention);
+
+ // Check if any casting is required for the partially-applied function.
+ SILValue ResultValue = castValueToABICompatibleType(
+ &B, Loc, NewPAI, NewPAI->getType(), OldPAI->getType());
+ OldPAI->replaceAllUsesWith(ResultValue);
+
+ return NewPAI;
+}
+
+static ApplySite replaceApplySite(SILBuilder &B, SILLocation Loc,
+ ApplySite OldAS,
+ SILValue NewFn,
+ SubstitutionMap NewSubs,
+ ArrayRef<SILValue> NewArgs,
+ SILFunctionConventions Conv) {
+ if (auto *OldAI = dyn_cast<ApplyInst>(OldAS)) {
+ return replaceApplyInst(B, Loc, OldAI, NewFn, NewSubs, NewArgs);
+ } else if (auto *OldTAI = dyn_cast<TryApplyInst>(OldAS)) {
+ return replaceTryApplyInst(B, Loc, OldTAI, NewFn, NewSubs, NewArgs, Conv);
+ } else if (auto *OldBAI = dyn_cast<BeginApplyInst>(OldAS)) {
+ return replaceBeginApplyInst(B, Loc, OldBAI, NewFn, NewSubs, NewArgs);
+ } else {
+ auto *OldPAI = cast<PartialApplyInst>(OldAS);
+ return replacePartialApplyInst(B, Loc, OldPAI, NewFn, NewSubs, NewArgs);
+ }
+}
+
+/// Delete an apply site that's been successfully devirtualized.
+void swift::deleteDevirtualizedApply(ApplySite Old) {
+ auto *OldApply = Old.getInstruction();
+ recursivelyDeleteTriviallyDeadInstructions(OldApply, true);
+}
+
SILFunction *swift::getTargetClassMethod(SILModule &M,
SILType ClassOrMetatypeType,
MethodInst *MI) {
@@ -551,9 +685,9 @@
/// \p ClassOrMetatype is a class value or metatype value that is the
/// self argument of the apply we will devirtualize.
/// return the result value of the new ApplyInst if created one or null.
-DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
- SILValue ClassOrMetatype,
- OptRemark::Emitter *ORE) {
+FullApplySite swift::devirtualizeClassMethod(FullApplySite AI,
+ SILValue ClassOrMetatype,
+ OptRemark::Emitter *ORE) {
LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize : "
<< *AI.getInstruction());
@@ -574,7 +708,8 @@
SILFunctionConventions substConv(SubstCalleeType, Mod);
SILBuilderWithScope B(AI.getInstruction());
- FunctionRefInst *FRI = B.createFunctionRef(AI.getLoc(), F);
+ SILLocation Loc = AI.getLoc();
+ FunctionRefInst *FRI = B.createFunctionRef(Loc, F);
// Create the argument list for the new apply, casting when needed
// in order to handle covariant indirect return types and
@@ -584,7 +719,7 @@
auto IndirectResultArgIter = AI.getIndirectSILResults().begin();
for (auto ResultTy : substConv.getIndirectSILResultTypes()) {
NewArgs.push_back(
- castValueToABICompatibleType(&B, AI.getLoc(), *IndirectResultArgIter,
+ castValueToABICompatibleType(&B, Loc, *IndirectResultArgIter,
IndirectResultArgIter->getType(), ResultTy));
++IndirectResultArgIter;
}
@@ -594,7 +729,7 @@
for (auto param : substConv.getParameters().drop_back()) {
auto paramType = substConv.getSILType(param);
NewArgs.push_back(
- castValueToABICompatibleType(&B, AI.getLoc(), *ParamArgIter,
+ castValueToABICompatibleType(&B, Loc, *ParamArgIter,
ParamArgIter->getType(), paramType));
++ParamArgIter;
}
@@ -602,80 +737,14 @@
// Add the self argument, upcasting if required because we're
// calling a base class's method.
auto SelfParamTy = substConv.getSILType(SubstCalleeType->getSelfParameter());
- NewArgs.push_back(castValueToABICompatibleType(&B, AI.getLoc(),
+ NewArgs.push_back(castValueToABICompatibleType(&B, Loc,
ClassOrMetatype,
ClassOrMetatypeType,
SelfParamTy));
- SILType ResultTy = substConv.getSILResultType();
-
- FullApplySite NewAI;
-
- SILBasicBlock *ResultBB = nullptr;
- SILBasicBlock *NormalBB = nullptr;
- SILValue ResultValue;
- bool ResultCastRequired = false;
- SmallVector<Operand *, 4> OriginalResultUses;
-
- if (!isa<TryApplyInst>(AI)) {
- auto apply = B.createApply(AI.getLoc(), FRI, Subs, NewArgs,
- cast<ApplyInst>(AI)->isNonThrowing());
- NewAI = apply;
- ResultValue = apply;
- } else {
- auto *TAI = cast<TryApplyInst>(AI);
- // Create new normal and error BBs only if:
- // - re-using a BB would create a critical edge
- // - or, the result of the new apply would be of different
- // type than the argument of the original normal BB.
- if (TAI->getNormalBB()->getSinglePredecessorBlock())
- ResultBB = TAI->getNormalBB();
- else {
- ResultBB = B.getFunction().createBasicBlock();
- ResultBB->createPHIArgument(ResultTy, ValueOwnershipKind::Owned);
- }
-
- NormalBB = TAI->getNormalBB();
-
- SILBasicBlock *ErrorBB = nullptr;
- if (TAI->getErrorBB()->getSinglePredecessorBlock())
- ErrorBB = TAI->getErrorBB();
- else {
- ErrorBB = B.getFunction().createBasicBlock();
- ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(),
- ValueOwnershipKind::Owned);
- }
-
- NewAI = B.createTryApply(AI.getLoc(), FRI, Subs, NewArgs, ResultBB, ErrorBB);
- if (ErrorBB != TAI->getErrorBB()) {
- B.setInsertionPoint(ErrorBB);
- B.createBranch(TAI->getLoc(), TAI->getErrorBB(),
- {ErrorBB->getArgument(0)});
- }
-
- // Does the result value need to be casted?
- ResultCastRequired = ResultTy != NormalBB->getArgument(0)->getType();
-
- if (ResultBB != NormalBB)
- B.setInsertionPoint(ResultBB);
- else if (ResultCastRequired) {
- B.setInsertionPoint(NormalBB->begin());
- // Collect all uses, before casting.
- for (auto *Use : NormalBB->getArgument(0)->getUses()) {
- OriginalResultUses.push_back(Use);
- }
- NormalBB->getArgument(0)->replaceAllUsesWith(
- SILUndef::get(AI.getType(), Mod));
- NormalBB->replacePHIArgument(0, ResultTy, ValueOwnershipKind::Owned);
- }
-
- // The result value is passed as a parameter to the normal block.
- ResultValue = ResultBB->getArgument(0);
- }
-
- // Check if any casting is required for the return value.
- ResultValue = castValueToABICompatibleType(&B, NewAI.getLoc(), ResultValue,
- ResultTy, AI.getType());
+ ApplySite NewAS = replaceApplySite(B, Loc, AI, FRI, Subs, NewArgs, substConv);
+ FullApplySite NewAI = FullApplySite::isa(NewAS.getInstruction());
+ assert(NewAI);
LLVM_DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n");
if (ORE)
@@ -686,34 +755,16 @@
});
NumClassDevirt++;
- if (NormalBB) {
- if (NormalBB != ResultBB) {
- // If artificial normal BB was introduced, branch
- // to the original normal BB.
- B.createBranch(NewAI.getLoc(), NormalBB, { ResultValue });
- } else if (ResultCastRequired) {
- // Update all original uses by the new value.
- for (auto *Use: OriginalResultUses) {
- Use->set(ResultValue);
- }
- }
- }
-
- // We need to return a pair of values here:
- // - the first one is the actual result of the devirtualized call, possibly
- // casted into an appropriate type. This SILValue may be a BB arg, if it
- // was a cast between optional types.
- // - the second one is the new apply site.
- return std::make_pair(ResultValue, NewAI);
+ return NewAI;
}
-DevirtualizationResult
+FullApplySite
swift::tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
OptRemark::Emitter *ORE,
bool isEffectivelyFinalMethod) {
if (!canDevirtualizeClassMethod(AI, ClassInstance->getType(), ORE,
isEffectivelyFinalMethod))
- return std::make_pair(nullptr, FullApplySite());
+ return FullApplySite();
return devirtualizeClassMethod(AI, ClassInstance, ORE);
}
@@ -855,7 +906,7 @@
/// 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 DevirtualizationResult
+static ApplySite
devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
ProtocolConformanceRef C, OptRemark::Emitter *ORE) {
// We know the witness thunk and the corresponding set of substitutions
@@ -897,30 +948,8 @@
SILLocation Loc = AI.getLoc();
FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F);
- ApplySite SAI;
-
- SILValue ResultValue;
- if (auto *A = dyn_cast<ApplyInst>(AI)) {
- auto *NewAI = Builder.createApply(Loc, FRI, SubMap, Arguments,
- A->isNonThrowing());
- // Check if any casting is required for the return value.
- ResultValue = castValueToABICompatibleType(&Builder, Loc, NewAI,
- NewAI->getType(), AI.getType());
- SAI = NewAI;
- }
- if (auto *TAI = dyn_cast<TryApplyInst>(AI))
- SAI = Builder.createTryApply(Loc, FRI, SubMap, Arguments,
- TAI->getNormalBB(), TAI->getErrorBB());
- if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) {
- auto PartialApplyConvention = PAI->getType().getAs<SILFunctionType>()
- ->getCalleeConvention();
- auto *NewPAI = Builder.createPartialApply(
- Loc, FRI, SubMap, Arguments, PartialApplyConvention);
- // Check if any casting is required for the return value.
- ResultValue = castValueToABICompatibleType(
- &Builder, Loc, NewPAI, NewPAI->getType(), PAI->getType());
- SAI = NewPAI;
- }
+ ApplySite SAI = replaceApplySite(Builder, Loc, AI, FRI, SubMap, Arguments,
+ substConv);
if (ORE)
ORE->emit([&]() {
@@ -929,7 +958,7 @@
<< "Devirtualized call to " << NV("Method", F);
});
NumWitnessDevirt++;
- return std::make_pair(ResultValue, SAI);
+ return SAI;
}
static bool canDevirtualizeWitnessMethod(ApplySite AI) {
@@ -958,10 +987,10 @@
/// 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
+ApplySite
swift::tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE) {
if (!canDevirtualizeWitnessMethod(AI))
- return std::make_pair(nullptr, FullApplySite());
+ return ApplySite();
SILFunction *F;
SILWitnessTable *WT;
@@ -981,9 +1010,9 @@
/// Attempt to devirtualize the given apply if possible, and return a
/// new instruction in that case, or nullptr otherwise.
-DevirtualizationResult swift::tryDevirtualizeApply(ApplySite AI,
- ClassHierarchyAnalysis *CHA,
- OptRemark::Emitter *ORE) {
+ApplySite swift::tryDevirtualizeApply(ApplySite AI,
+ ClassHierarchyAnalysis *CHA,
+ OptRemark::Emitter *ORE) {
LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize: "
<< *AI.getInstruction());
@@ -998,7 +1027,7 @@
// TODO: check if we can also de-virtualize partial applies of class methods.
FullApplySite FAS = FullApplySite::isa(AI.getInstruction());
if (!FAS)
- return std::make_pair(nullptr, ApplySite());
+ return ApplySite();
/// Optimize a class_method and alloc_ref pair into a direct function
/// reference:
@@ -1053,7 +1082,7 @@
return tryDevirtualizeClassMethod(FAS, FAS.getArguments().back(), ORE);
}
- return std::make_pair(nullptr, ApplySite());
+ return ApplySite();
}
bool swift::canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA) {
diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp
index b01b514..cfda0a9 100644
--- a/lib/SILOptimizer/Utils/Local.cpp
+++ b/lib/SILOptimizer/Utils/Local.cpp
@@ -313,15 +313,6 @@
return FullApplySite();
}
-// Replace a dead apply with a new instruction that computes the same
-// value, and delete the old apply.
-void swift::replaceDeadApply(ApplySite Old, ValueBase *New) {
- auto *OldApply = Old.getInstruction();
- if (!isa<TryApplyInst>(OldApply))
- cast<SingleValueInstruction>(OldApply)->replaceAllUsesWith(New);
- recursivelyDeleteTriviallyDeadInstructions(OldApply, true);
-}
-
bool swift::mayBindDynamicSelf(SILFunction *F) {
if (!F->hasSelfMetadataParam())
return false;