From 542a378436c6609479b1afbdcd48f5c389cd7cf1 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 5 Feb 2021 11:15:10 +0100 Subject: [PATCH 1/7] SIL: add FunctionRefInst::getReferencedFunction() If we know that we have a FunctionRefInst (and not another variant of FunctionRefBaseInst), we know that getting the referenced function will not be null (in contrast to FunctionRefBaseInst::getReferencedFunctionOrNull). NFC --- include/swift/SIL/SILCloner.h | 2 +- include/swift/SIL/SILInstruction.h | 5 ++++- include/swift/SIL/TypeSubstCloner.h | 2 +- lib/SIL/IR/Linker.cpp | 2 +- lib/SIL/IR/SILGlobalVariable.cpp | 2 +- lib/SIL/IR/SILInstruction.cpp | 3 +-- lib/SIL/IR/SILPrinter.cpp | 4 ++-- lib/SILOptimizer/Analysis/ArraySemantic.cpp | 6 +++--- lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp | 2 +- .../Analysis/DifferentiableActivityAnalysis.cpp | 2 +- lib/SILOptimizer/Differentiation/Thunk.cpp | 4 ++-- lib/SILOptimizer/IPO/ClosureSpecializer.cpp | 12 ++++-------- lib/SILOptimizer/IPO/DeadFunctionElimination.cpp | 2 +- lib/SILOptimizer/Mandatory/CapturePromotion.cpp | 2 +- lib/SILOptimizer/Mandatory/Differentiation.cpp | 7 +++---- lib/SILOptimizer/Mandatory/MandatoryInlining.cpp | 2 +- .../SILCombiner/SILCombinerApplyVisitors.cpp | 4 ++-- lib/SILOptimizer/Transforms/AllocBoxToStack.cpp | 2 +- lib/SILOptimizer/Transforms/CSE.cpp | 3 +-- lib/SILOptimizer/Transforms/Outliner.cpp | 4 ++-- lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp | 2 +- lib/SILOptimizer/Utils/ConstExpr.cpp | 2 +- lib/SILOptimizer/Utils/Generics.cpp | 2 +- lib/SILOptimizer/Utils/InstOptUtils.cpp | 11 +---------- lib/SILOptimizer/Utils/SpecializationMangler.cpp | 4 ++-- lib/Serialization/SerializeSIL.cpp | 2 +- 26 files changed, 41 insertions(+), 54 deletions(-) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 80078f9e504b2..7423100091ca4 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -967,7 +967,7 @@ template void SILCloner::visitFunctionRefInst(FunctionRefInst *Inst) { SILFunction *OpFunction = - getOpFunction(Inst->getInitiallyReferencedFunction()); + getOpFunction(Inst->getReferencedFunction()); getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction(Inst, getBuilder().createFunctionRef( getOpLocation(Inst->getLoc()), OpFunction)); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 1b9a4e8b4ee92..a7c955b74fa23 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -2756,9 +2756,9 @@ class LiteralInst : public SingleValueInstruction { }; class FunctionRefBaseInst : public LiteralInst { +protected: SILFunction *f; -protected: FunctionRefBaseInst(SILInstructionKind Kind, SILDebugLocation DebugLoc, SILFunction *F, TypeExpansionContext context); @@ -2819,6 +2819,9 @@ class FunctionRefInst : public FunctionRefBaseInst { TypeExpansionContext context); public: + /// Return the referenced function. + SILFunction *getReferencedFunction() const { return f; } + static bool classof(SILNodePointer node) { return node->getKind() == SILNodeKind::FunctionRefInst; } diff --git a/include/swift/SIL/TypeSubstCloner.h b/include/swift/SIL/TypeSubstCloner.h index 827dc93394c29..3998e6be85850 100644 --- a/include/swift/SIL/TypeSubstCloner.h +++ b/include/swift/SIL/TypeSubstCloner.h @@ -69,7 +69,7 @@ class TypeSubstCloner : public SILClonerWithScopes { if (!Cloner.Inlining) { FunctionRefInst *FRI = dyn_cast(AI.getCallee()); - if (FRI && FRI->getInitiallyReferencedFunction() == AI.getFunction() && + if (FRI && FRI->getReferencedFunction() == AI.getFunction() && Subs == Cloner.SubsMap) { // Handle recursions by replacing the apply to the callee with an // apply to the newly specialized function, but only if substitutions diff --git a/lib/SIL/IR/Linker.cpp b/lib/SIL/IR/Linker.cpp index 358bc257d9c71..26f99ec0e531d 100644 --- a/lib/SIL/IR/Linker.cpp +++ b/lib/SIL/IR/Linker.cpp @@ -175,7 +175,7 @@ void SILLinkerVisitor::visitPartialApplyInst(PartialApplyInst *PAI) { } void SILLinkerVisitor::visitFunctionRefInst(FunctionRefInst *FRI) { - maybeAddFunctionToWorklist(FRI->getInitiallyReferencedFunction()); + maybeAddFunctionToWorklist(FRI->getReferencedFunction()); } void SILLinkerVisitor::visitDynamicFunctionRefInst( diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index 729b53a815fad..a5981542e9b3f 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -241,7 +241,7 @@ SILFunction *swift::getCalleeOfOnceCall(BuiltinInst *BI) { "Expected C function representation!"); if (auto *FR = dyn_cast(Callee)) - return FR->getReferencedFunctionOrNull(); + return FR->getReferencedFunction(); return nullptr; } diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index f2d8e9c6ddf38..88113ed0cb819 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -475,8 +475,7 @@ namespace { bool visitFunctionRefInst(const FunctionRefInst *RHS) { auto *X = cast(LHS); - return X->getInitiallyReferencedFunction() == - RHS->getInitiallyReferencedFunction(); + return X->getReferencedFunction() == RHS->getReferencedFunction(); } bool visitDynamicFunctionRefInst(const DynamicFunctionRefInst *RHS) { auto *X = cast(LHS); diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 4822bb60b668e..4e84e0bb00f99 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -957,7 +957,7 @@ class SILPrinter : public SILInstructionVisitor { void print(const SILInstruction *I) { if (auto *FRI = dyn_cast(I)) *this << " // function_ref " - << demangleSymbol(FRI->getInitiallyReferencedFunction()->getName()) + << demangleSymbol(FRI->getReferencedFunction()->getName()) << "\n"; else if (auto *FRI = dyn_cast(I)) *this << " // dynamic_function_ref " @@ -1270,7 +1270,7 @@ class SILPrinter : public SILInstructionVisitor { } void visitFunctionRefInst(FunctionRefInst *FRI) { - FRI->getInitiallyReferencedFunction()->printName(PrintState.OS); + FRI->getReferencedFunction()->printName(PrintState.OS); *this << " : " << FRI->getType(); } void visitDynamicFunctionRefInst(DynamicFunctionRefInst *FRI) { diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index 669c6e7d5cbbc..7e9e1a99de2c8 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -66,7 +66,7 @@ ArrayCallKind swift::getArraySemanticsKind(SILFunction *f) { static ParameterConvention getSelfParameterConvention(ApplyInst *SemanticsCall) { FunctionRefInst *FRI = cast(SemanticsCall->getCallee()); - SILFunction *F = FRI->getInitiallyReferencedFunction(); + SILFunction *F = FRI->getReferencedFunction(); auto FnTy = F->getLoweredFunctionType(); return FnTy->getSelfParameter().getConvention(); @@ -78,7 +78,7 @@ bool swift::ArraySemanticsCall::isValidSignature() { assert(SemanticsCall && getKind() != ArrayCallKind::kNone && "Need an array semantic call"); FunctionRefInst *FRI = cast(SemanticsCall->getCallee()); - SILFunction *F = FRI->getInitiallyReferencedFunction(); + SILFunction *F = FRI->getReferencedFunction(); auto FnTy = F->getLoweredFunctionType(); auto &Mod = F->getModule(); @@ -203,7 +203,7 @@ ArrayCallKind swift::ArraySemanticsCall::getKind() const { return ArrayCallKind::kNone; auto F = cast(SemanticsCall->getCallee()) - ->getInitiallyReferencedFunction(); + ->getReferencedFunction(); return getArraySemanticsKind(F); } diff --git a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp index f0ef090a029c0..6d92d617ccebe 100644 --- a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp @@ -256,7 +256,7 @@ CalleeList CalleeCache::getCalleeListForCalleeKind(SILValue Callee) const { case ValueKind::FunctionRefInst: return CalleeList( - cast(Callee)->getInitiallyReferencedFunction()); + cast(Callee)->getReferencedFunction()); case ValueKind::DynamicFunctionRefInst: case ValueKind::PreviousDynamicFunctionRefInst: diff --git a/lib/SILOptimizer/Analysis/DifferentiableActivityAnalysis.cpp b/lib/SILOptimizer/Analysis/DifferentiableActivityAnalysis.cpp index b7e78ef6fdce7..1c377adb1a5ca 100644 --- a/lib/SILOptimizer/Analysis/DifferentiableActivityAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/DifferentiableActivityAnalysis.cpp @@ -29,7 +29,7 @@ using namespace swift::autodiff; static bool isWithoutDerivative(SILValue v) { if (auto *fnRef = dyn_cast(v)) - return fnRef->getReferencedFunctionOrNull()->hasSemanticsAttr( + return fnRef->getReferencedFunction()->hasSemanticsAttr( "autodiff.nonvarying"); return false; } diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index 84399833cbd5b..cea9b660525be 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -852,7 +852,7 @@ getOrCreateSubsetParametersThunkForDerivativeFunction( StringRef origName; if (auto *origFnRef = peerThroughFunctionConversions(origFnOperand)) { - origName = origFnRef->getInitiallyReferencedFunction()->getName(); + origName = origFnRef->getReferencedFunction()->getName(); } else if (auto *origMethodInst = peerThroughFunctionConversions(origFnOperand)) { origName = origMethodInst->getMember() @@ -905,7 +905,7 @@ getOrCreateSubsetParametersThunkForDerivativeFunction( SILValue assocRef; if (auto *derivativeFnRef = peerThroughFunctionConversions(derivativeFn)) { - auto *assoc = derivativeFnRef->getReferencedFunctionOrNull(); + auto *assoc = derivativeFnRef->getReferencedFunction(); assocRef = builder.createFunctionRef(loc, assoc); } else if (auto *assocMethodInst = peerThroughFunctionConversions( diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index fd3e9c54021bd..a857b1e27b3e2 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -184,18 +184,15 @@ class CallSiteDescriptor { CallSiteDescriptor &operator=(CallSiteDescriptor &&) =default; SILFunction *getApplyCallee() const { - return cast(AI.getCallee()) - ->getInitiallyReferencedFunction(); + return cast(AI.getCallee())->getReferencedFunction(); } SILFunction *getClosureCallee() const { if (auto *PAI = dyn_cast(getClosure())) - return cast(PAI->getCallee()) - ->getInitiallyReferencedFunction(); + return cast(PAI->getCallee())->getReferencedFunction(); auto *TTTFI = cast(getClosure()); - return cast(TTTFI->getCallee()) - ->getInitiallyReferencedFunction(); + return cast(TTTFI->getCallee())->getReferencedFunction(); } bool closureHasRefSemanticContext() const { @@ -565,8 +562,7 @@ static bool isSupportedClosure(const SILInstruction *Closure) { // Bail if any of the arguments are passed by address and // are not @inout. // This is a temporary limitation. - auto ClosureCallee = FRI->getReferencedFunctionOrNull(); - assert(ClosureCallee); + auto ClosureCallee = FRI->getReferencedFunction(); auto ClosureCalleeConv = ClosureCallee->getConventions(); unsigned ClosureArgIdx = ClosureCalleeConv.getNumSILArguments() - PAI->getNumArguments(); diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 4908107793fe4..3f9cdac5676fb 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -334,7 +334,7 @@ class FunctionLivenessComputation { MethodInfo *mi = getMethodInfo(funcDecl, /*isWitnessTable*/ false); ensureAliveClassMethod(mi, dyn_cast(funcDecl), MethodCl); } else if (auto *FRI = dyn_cast(&I)) { - ensureAlive(FRI->getInitiallyReferencedFunction()); + ensureAlive(FRI->getReferencedFunction()); } else if (auto *FRI = dyn_cast(&I)) { ensureAlive(FRI->getInitiallyReferencedFunction()); } else if (auto *FRI = dyn_cast(&I)) { diff --git a/lib/SILOptimizer/Mandatory/CapturePromotion.cpp b/lib/SILOptimizer/Mandatory/CapturePromotion.cpp index 0d77661d66230..454e800a81593 100644 --- a/lib/SILOptimizer/Mandatory/CapturePromotion.cpp +++ b/lib/SILOptimizer/Mandatory/CapturePromotion.cpp @@ -539,7 +539,7 @@ SILFunction *ClosureCloner::constructClonedFunction( SILFunction *f = pai->getFunction(); // Create the Cloned Name for the function. - SILFunction *origF = fri->getReferencedFunctionOrNull(); + SILFunction *origF = fri->getReferencedFunction(); IsSerialized_t isSerialized = IsNotSerialized; if (f->isSerialized() && origF->isSerialized()) diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index 36ae62eda41dc..6a6dcbb167152 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -416,7 +416,7 @@ static SILValue reapplyFunctionConversion( // `differentiable_function` of the function-typed thunk argument. auto isReabstractionThunkCallee = [&]() -> bool { auto *fri = dyn_cast(oldFunc); - return fri && fri->getReferencedFunctionOrNull()->isThunk() == + return fri && fri->getReferencedFunction()->isThunk() == IsReabstractionThunk; }; if (isReabstractionThunkCallee()) { @@ -513,8 +513,7 @@ emitDerivativeFunctionReference( if (auto *originalFRI = peerThroughFunctionConversions(original)) { auto loc = originalFRI->getLoc(); - auto *originalFn = originalFRI->getReferencedFunctionOrNull(); - assert(originalFn); + auto *originalFn = originalFRI->getReferencedFunction(); auto originalFnTy = originalFn->getLoweredFunctionType(); auto *desiredParameterIndices = desiredConfig.parameterIndices; auto *desiredResultIndices = desiredConfig.resultIndices; @@ -1005,7 +1004,7 @@ static SILValue promoteCurryThunkApplicationToDifferentiableFunction( auto *thunkRef = dyn_cast(ai->getCallee()); if (!thunkRef) return nullptr; - auto *thunk = thunkRef->getReferencedFunctionOrNull(); + auto *thunk = thunkRef->getReferencedFunction(); auto thunkTy = thunk->getLoweredFunctionType(); auto thunkResult = thunkTy->getSingleResult(); auto resultFnTy = thunkResult.getInterfaceType()->getAs(); diff --git a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp index 00eebd898be92..b3c1683b384da 100644 --- a/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp +++ b/lib/SILOptimizer/Mandatory/MandatoryInlining.cpp @@ -741,7 +741,7 @@ getCalleeFunction(SILFunction *F, FullApplySite AI, bool &IsThick, if (!FRI) return nullptr; - SILFunction *CalleeFunction = FRI->getReferencedFunctionOrNull(); + SILFunction *CalleeFunction = FRI->getReferencedFunction(); switch (CalleeFunction->getRepresentation()) { case SILFunctionTypeRepresentation::Thick: diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp index c78e8a08c058e..0413a90a713b1 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp @@ -1422,7 +1422,7 @@ static bool knowHowToEmitReferenceCountInsts(ApplyInst *Call) { FunctionRefInst *FRI = dyn_cast(Call->getCallee()); if (!FRI) return false; - SILFunction *F = FRI->getReferencedFunctionOrNull(); + SILFunction *F = FRI->getReferencedFunction(); auto FnTy = F->getLoweredFunctionType(); // Look at the result type. @@ -1445,7 +1445,7 @@ static bool knowHowToEmitReferenceCountInsts(ApplyInst *Call) { /// Add reference counting operations equal to the effect of the call. static void emitMatchingRCAdjustmentsForCall(ApplyInst *Call, SILValue OnX) { FunctionRefInst *FRI = cast(Call->getCallee()); - SILFunction *F = FRI->getReferencedFunctionOrNull(); + SILFunction *F = FRI->getReferencedFunction(); auto FnTy = F->getLoweredFunctionType(); assert(FnTy->getNumResults() == 1); auto ResultInfo = FnTy->getResults()[0]; diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 4fc6ccdd67ef6..af974628cc8d1 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -888,7 +888,7 @@ specializeApplySite(SILOptFunctionBuilder &FuncBuilder, ApplySite Apply, AllocBoxToStackState &pass) { auto *FRI = cast(Apply.getCallee()); assert(FRI && "Expected a direct ApplySite"); - auto *F = FRI->getReferencedFunctionOrNull(); + auto *F = FRI->getReferencedFunction(); assert(F && "Expected a referenced function!"); IsSerialized_t Serialized = IsNotSerialized; diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index c8c05312e62c1..4e0e2aadf8db4 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -144,8 +144,7 @@ class HashVisitor : public SILInstructionVisitor { } hash_code visitFunctionRefInst(FunctionRefInst *X) { - return llvm::hash_combine(X->getKind(), - X->getInitiallyReferencedFunction()); + return llvm::hash_combine(X->getKind(), X->getReferencedFunction()); } hash_code visitGlobalAddrInst(GlobalAddrInst *X) { diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 0447c19baadfb..7044bac3fe8fd 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -485,7 +485,7 @@ static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst, // Check that we call the _unconditionallyBridgeFromObjectiveC witness. auto NativeType = Apply->getType().getASTType(); - auto *BridgeFun = FunRef->getInitiallyReferencedFunction(); + auto *BridgeFun = FunRef->getReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); // Not every type conforms to the ObjectiveCBridgeable protocol in such a case // getBridgeFromObjectiveC returns SILDeclRef(). @@ -821,7 +821,7 @@ BridgedArgument BridgedArgument::match(unsigned ArgIdx, SILValue Arg, // Make sure we are calling the actual bridge witness. auto NativeType = BridgedValue->getType().getASTType(); - auto *BridgeFun = FunRef->getInitiallyReferencedFunction(); + auto *BridgeFun = FunRef->getReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); // Not every type conforms to the ObjectiveCBridgeable protocol in such a case // getBridgeToObjectiveC returns SILDeclRef(). diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index adae7464eb0f6..e616d6c9b75fc 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -127,7 +127,7 @@ class BugReducerTester : public SILFunctionTransform { } auto *FRI = dyn_cast(FAS.getCallee()); - if (!FRI || !FRI->getInitiallyReferencedFunction()->getName().equals( + if (!FRI || !FRI->getReferencedFunction()->getName().equals( FunctionTarget)) { ++II; continue; diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index c5ba918ea011d..caa070ebc6ae1 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -287,7 +287,7 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { return SymbolicValue::getString(sli->getValue(), evaluator.getAllocator()); if (auto *fri = dyn_cast(value)) - return SymbolicValue::getFunction(fri->getInitiallyReferencedFunction()); + return SymbolicValue::getFunction(fri->getReferencedFunction()); // If we have a reference to a metatype, constant fold any substitutable // types. diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 5fd68c52df78a..54483d9896f13 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2521,7 +2521,7 @@ void swift::trySpecializeApplyOfGeneric( assert(Apply.hasSubstitutions() && "Expected an apply with substitutions!"); auto *F = Apply.getFunction(); auto *RefF = - cast(Apply.getCallee())->getReferencedFunctionOrNull(); + cast(Apply.getCallee())->getReferencedFunction(); LLVM_DEBUG(llvm::dbgs() << "\n\n*** ApplyInst in function " << F->getName() << ":\n"; diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 67f985ee8e46d..23af53febd267 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -603,17 +603,8 @@ void swift::recursivelyDeleteTriviallyDeadInstructions( // If we have a function ref inst, we need to especially drop its function // argument so that it gets a proper ref decrement. - auto *fri = dyn_cast(inst); - if (fri && fri->getInitiallyReferencedFunction()) + if (auto *fri = dyn_cast(inst)) fri->dropReferencedFunction(); - - auto *dfri = dyn_cast(inst); - if (dfri && dfri->getInitiallyReferencedFunction()) - dfri->dropReferencedFunction(); - - auto *pfri = dyn_cast(inst); - if (pfri && pfri->getInitiallyReferencedFunction()) - pfri->dropReferencedFunction(); } for (auto inst : deadInsts) { diff --git a/lib/SILOptimizer/Utils/SpecializationMangler.cpp b/lib/SILOptimizer/Utils/SpecializationMangler.cpp index a5076f0309d25..2d67dbdd49a70 100644 --- a/lib/SILOptimizer/Utils/SpecializationMangler.cpp +++ b/lib/SILOptimizer/Utils/SpecializationMangler.cpp @@ -182,12 +182,12 @@ FunctionSignatureSpecializationMangler::mangleClosureProp(SILInstruction *Inst) // restriction is removed, the assert here will fire. if (auto *TTTFI = dyn_cast(Inst)) { auto *FRI = cast(TTTFI->getCallee()); - appendIdentifier(FRI->getInitiallyReferencedFunction()->getName()); + appendIdentifier(FRI->getReferencedFunction()->getName()); return; } auto *PAI = cast(Inst); auto *FRI = cast(PAI->getCallee()); - appendIdentifier(FRI->getInitiallyReferencedFunction()->getName()); + appendIdentifier(FRI->getReferencedFunction()->getName()); // Then we mangle the types of the arguments that the partial apply is // specializing. diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index f6d2ae4131f4b..becc98f6db9ca 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1416,7 +1416,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { // Use SILOneOperandLayout to specify the function type and the function // name (IdentifierID). const FunctionRefInst *FRI = cast(&SI); - SILFunction *ReferencedFunction = FRI->getInitiallyReferencedFunction(); + SILFunction *ReferencedFunction = FRI->getReferencedFunction(); unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; SILOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, (unsigned)SI.getKind(), 0, From ae313feb3ab766e26ef196f7614e0416113a5261 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 5 Feb 2021 11:48:08 +0100 Subject: [PATCH 2/7] SIL: allow creating convert_function and thin_to_thick_function instructions in an initializer of a SILGlobalVariable. This means: don't require to have a SILFunction in the SILBuilder. --- include/swift/SIL/SILBuilder.h | 6 ++++-- include/swift/SIL/SILInstruction.h | 6 +++--- lib/SIL/IR/SILInstructions.cpp | 25 +++++++++++++++---------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index e55775df35cb8..c759dd60aef8d 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -201,6 +201,8 @@ class SILBuilder { bool isInsertingIntoGlobal() const { return F == nullptr; } TypeExpansionContext getTypeExpansionContext() const { + if (!F) + return TypeExpansionContext::minimal(); return TypeExpansionContext(getFunction()); } @@ -1027,7 +1029,7 @@ class SILBuilder { SILType Ty, bool WithoutActuallyEscaping) { return insert(ConvertFunctionInst::create(getSILDebugLocation(Loc), Op, Ty, - getFunction(), C.OpenedArchetypes, + getModule(), F, C.OpenedArchetypes, WithoutActuallyEscaping)); } @@ -1157,7 +1159,7 @@ class SILBuilder { ThinToThickFunctionInst *createThinToThickFunction(SILLocation Loc, SILValue Op, SILType Ty) { return insert(ThinToThickFunctionInst::create( - getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes)); + getSILDebugLocation(Loc), Op, Ty, getModule(), F, C.OpenedArchetypes)); } ThickToObjCMetatypeInst *createThickToObjCMetatype(SILLocation Loc, diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index a7c955b74fa23..061e85d164e66 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -4815,7 +4815,7 @@ class ConvertFunctionInst final static ConvertFunctionInst *create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, - SILFunction &F, + SILModule &Mod, SILFunction *F, SILOpenedArchetypesState &OpenedArchetypes, bool WithoutActuallyEscaping); @@ -5165,8 +5165,8 @@ class ThinToThickFunctionInst final Operand.getOwnershipKind()) {} static ThinToThickFunctionInst * - create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, - SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes); + create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, SILModule &Mod, + SILFunction *F, SILOpenedArchetypesState &OpenedArchetypes); public: /// Return the callee of the thin_to_thick_function. diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 4e022a512b6da..87941dea65668 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2317,12 +2317,14 @@ UpcastInst *UpcastInst::create(SILDebugLocation DebugLoc, SILValue Operand, ThinToThickFunctionInst * ThinToThickFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand, - SILType Ty, SILFunction &F, + SILType Ty, SILModule &Mod, SILFunction *F, SILOpenedArchetypesState &OpenedArchetypes) { - SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; - collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getASTType()); + if (F) { + assert(&F->getModule() == &Mod); + collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, *F, + Ty.getASTType()); + } unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(ThinToThickFunctionInst)); @@ -2346,12 +2348,15 @@ PointerToThinFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand, } ConvertFunctionInst *ConvertFunctionInst::create( - SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, SILFunction &F, + SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, SILModule &Mod, + SILFunction *F, SILOpenedArchetypesState &OpenedArchetypes, bool WithoutActuallyEscaping) { - SILModule &Mod = F.getModule(); SmallVector TypeDependentOperands; - collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F, - Ty.getASTType()); + if (F) { + assert(&F->getModule() == &Mod); + collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, *F, + Ty.getASTType()); + } unsigned size = totalSizeToAlloc(1 + TypeDependentOperands.size()); void *Buffer = Mod.allocateInst(size, alignof(ConvertFunctionInst)); @@ -2362,14 +2367,14 @@ ConvertFunctionInst *ConvertFunctionInst::create( // // *NOTE* We purposely do not use an early return here to ensure that in // builds without assertions this whole if statement is optimized out. - if (F.getModule().getStage() != SILStage::Lowered) { + if (Mod.getStage() != SILStage::Lowered) { // Make sure we are not performing ABI-incompatible conversions. CanSILFunctionType opTI = CFI->getOperand()->getType().castTo(); (void)opTI; CanSILFunctionType resTI = CFI->getType().castTo(); (void)resTI; - assert(opTI->isABICompatibleWith(resTI, F).isCompatible() && + assert((!F || opTI->isABICompatibleWith(resTI, *F).isCompatible()) && "Can not convert in between ABI incompatible function types"); } return CFI; From ee8cdac501e4e9201033da8328dd5723043ed04d Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 8 Feb 2021 11:48:47 +0100 Subject: [PATCH 3/7] DeadFunctionElimination: Also eliminate dead global variables. It makes sense to to this in a single pass, because there might be dead cycles for globals and functions, e.g. if a global references a function in its static initializer and the function references that global. Another improvement: eliminate dead global-initializers. Before we had an explicit SIL representation of statically initialized globals, we had to keep them alive. rdar://32956923 --- include/swift/SIL/SILGlobalVariable.h | 4 ++ lib/SIL/IR/SILGlobalVariable.cpp | 5 ++ .../IPO/DeadFunctionElimination.cpp | 51 ++++++++++++++++--- .../dead_function_elimination.swift | 20 ++++++++ test/SILOptimizer/remove_unused_func.sil | 32 +++++++++++- .../sil_combine_protocol_conf.swift | 20 ++++---- 6 files changed, 115 insertions(+), 17 deletions(-) diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index 1c5090bee8f25..096247af2d3b4 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -128,6 +128,10 @@ class SILGlobalVariable SILLinkage getLinkage() const { return SILLinkage(Linkage); } void setLinkage(SILLinkage linkage) { Linkage = unsigned(linkage); } + /// Returns true if the linkage of the SILFunction indicates that the global + /// might be referenced from outside the current compilation unit. + bool isPossiblyUsedExternally() const; + /// Get this global variable's serialized attribute. IsSerialized_t isSerialized() const; void setSerialized(IsSerialized_t isSerialized); diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index a5981542e9b3f..9b91eddbb31ac 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -62,6 +62,11 @@ SILGlobalVariable::~SILGlobalVariable() { StaticInitializerBlock.clearStaticInitializerBlock(Module); } +bool SILGlobalVariable::isPossiblyUsedExternally() const { + SILLinkage linkage = getLinkage(); + return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule()); +} + /// Get this global variable's fragile attribute. IsSerialized_t SILGlobalVariable::isSerialized() const { return Serialized ? IsSerialized : IsNotSerialized; diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 3f9cdac5676fb..4f050e83441a8 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -27,6 +27,7 @@ using namespace swift; STATISTIC(NumDeadFunc, "Number of dead functions eliminated"); +STATISTIC(NumDeadGlobals, "Number of dead global variables eliminated"); namespace { @@ -115,11 +116,6 @@ class FunctionLivenessComputation { if (F->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) return true; - // Global initializers are always emitted into the defining module and - // their bodies are never SIL serialized. - if (F->isGlobalInit()) - return true; - return false; } @@ -145,6 +141,11 @@ class FunctionLivenessComputation { return AliveFunctionsAndTables.count(WT) != 0; } + /// Returns true if a global variable is marked as alive. + bool isAlive(SILGlobalVariable *global) { + return AliveFunctionsAndTables.count(global) != 0; + } + /// Marks a function as alive. void makeAlive(SILFunction *F) { AliveFunctionsAndTables.insert(F); @@ -204,6 +205,16 @@ class FunctionLivenessComputation { } } + /// Marks the \p global and all functions, which are referenced from its + /// initializer as alive. + void makeAlive(SILGlobalVariable *global) { + AliveFunctionsAndTables.insert(global); + for (const SILInstruction &initInst : *global) { + if (auto *fRef = dyn_cast(&initInst)) + ensureAlive(fRef->getReferencedFunction()); + } + } + /// Marks the declarations referenced by a key path pattern as alive if they /// aren't yet. void @@ -239,6 +250,12 @@ class FunctionLivenessComputation { makeAlive(F); } + /// Marks a global variable as alive if it is not alive yet. + void ensureAlive(SILGlobalVariable *global) { + if (!isAlive(global)) + makeAlive(global); + } + /// Marks a witness table as alive if it is not alive yet. void ensureAliveConformance(const ProtocolConformance *C) { SILWitnessTable *WT = Module->lookUpWitnessTable(C, @@ -342,6 +359,10 @@ class FunctionLivenessComputation { } else if (auto *KPI = dyn_cast(&I)) { for (auto &component : KPI->getPattern()->getComponents()) ensureKeyPathComponentIsAlive(component); + } else if (auto *GA = dyn_cast(&I)) { + ensureAlive(GA->getReferencedGlobal()); + } else if (auto *GV = dyn_cast(&I)) { + ensureAlive(GV->getReferencedGlobal()); } } } @@ -407,6 +428,11 @@ class FunctionLivenessComputation { ensureAlive(&F); } } + + for (SILGlobalVariable &global : Module->getSILGlobals()) { + if (global.isPossiblyUsedExternally()) + ensureAlive(&global); + } } public: @@ -675,13 +701,21 @@ class DeadFunctionElimination : FunctionLivenessComputation { // First drop all references so that we don't get problems with non-zero // reference counts of dead functions. - std::vector DeadFunctions; + llvm::SmallVector DeadFunctions; + llvm::SmallVector DeadGlobals; for (SILFunction &F : *Module) { if (!isAlive(&F)) { F.dropAllReferences(); DeadFunctions.push_back(&F); } } + + for (SILGlobalVariable &global : Module->getSILGlobals()) { + if (!isAlive(&global)) { + global.dropAllReferences(); + DeadGlobals.push_back(&global); + } + } // Next step: delete dead witness tables. SILModule::WitnessTableListType &WTables = Module->getWitnessTableList(); @@ -706,6 +740,11 @@ class DeadFunctionElimination : FunctionLivenessComputation { DFEPass->notifyWillDeleteFunction(F); Module->eraseFunction(F); } + for (SILGlobalVariable *deadGlobal : DeadGlobals) { + ++NumDeadGlobals; + Module->eraseGlobalVariable(deadGlobal); + } + if (changedTables) DFEPass->invalidateFunctionTables(); } diff --git a/test/SILOptimizer/dead_function_elimination.swift b/test/SILOptimizer/dead_function_elimination.swift index 47afd12d9b6d1..9de527cc988bf 100644 --- a/test/SILOptimizer/dead_function_elimination.swift +++ b/test/SILOptimizer/dead_function_elimination.swift @@ -168,11 +168,29 @@ internal func donotEliminate() { return } +func callingGlobalFuncPtr() { + GFStr.globalFuncPtr() +} + +func aliveReferencedFunc() { +} + +struct GFStr { + static var globalFuncPtr: () -> () = callingGlobalFuncPtr + static var aliveFuncPtr: () -> () = aliveReferencedFunc +} + +public func keepPtrAlive() { + GFStr.aliveFuncPtr() +} + // CHECK-NOT: sil {{.*}}inCycleA // CHECK-NOT: sil {{.*}}inCycleB // CHECK-NOT: sil {{.*}}DeadMethod // CHECK-NOT: sil {{.*}}DeadWitness // CHECK-NOT: sil {{.*}}publicClassMethod +// CHECK-NOT: sil {{.*}}callingGlobalFuncPtr +// CHECK-NOT: sil_global {{.*}}globalFuncPtr // CHECK-TESTING: sil {{.*}}inCycleA // CHECK-TESTING: sil {{.*}}inCycleB @@ -180,7 +198,9 @@ internal func donotEliminate() { // CHECK-TESTING: sil {{.*}}publicClassMethod // CHECK-TESTING: sil {{.*}}DeadWitness +// CHECK-LABEL: sil_global hidden @$s25dead_function_elimination5GFStrV12aliveFuncPtryycvpZ // CHECK-LABEL: @$s25dead_function_elimination14donotEliminateyyF +// CHECK-LABEL: sil @$s25dead_function_elimination12keepPtrAliveyyF // CHECK-LABEL: sil_vtable Base // CHECK: aliveMethod diff --git a/test/SILOptimizer/remove_unused_func.sil b/test/SILOptimizer/remove_unused_func.sil index e3f5507e29a1e..77b516205ce19 100644 --- a/test/SILOptimizer/remove_unused_func.sil +++ b/test/SILOptimizer/remove_unused_func.sil @@ -1,3 +1,4 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-deadfuncelim | %FileCheck --check-prefix=KEEP %s // RUN: %target-sil-opt -enable-sil-verify-all %s -sil-deadfuncelim | %FileCheck %s sil_stage canonical @@ -6,7 +7,7 @@ import Builtin import Swift // This function needs to be removed. -// CHECK-NOT: @remove_me +// KEEP-NOT: @remove_me sil private @remove_me : $@convention(thin) (Builtin.Int64) -> Builtin.Int64 { bb0(%0 : $Builtin.Int64): @@ -15,3 +16,32 @@ bb0(%0 : $Builtin.Int64): %4 = tuple_extract %3 : $(Builtin.Int64, Builtin.Int1), 0 return %4: $Builtin.Int64 } + +sil_global @globalFunctionPointer : $@callee_guaranteed () -> () = { + %0 = function_ref @alivePrivateFunc : $@convention(thin) () -> () + %initval = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_guaranteed () -> () +} + +// CHECK-LABEL: sil private @alivePrivateFunc +sil private @alivePrivateFunc : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// KEEP-NOT: @privateFunctionPointer +sil_global private @privateFunctionPointer : $@callee_guaranteed () -> () = { + %0 = function_ref @deadPrivateFunc : $@convention(thin) () -> () + %initval = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_guaranteed () -> () +} + +// KEEP-NOT: @deadPrivateFunc +sil private @deadPrivateFunc : $@convention(thin) () -> () { +bb0: + %0 = global_addr @privateFunctionPointer : $*@callee_guaranteed () -> () + %1 = load %0 : $*@callee_guaranteed () -> () + %2 = apply %1() : $@callee_guaranteed () -> () + %3 = tuple () + return %3 : $() +} + diff --git a/test/SILOptimizer/sil_combine_protocol_conf.swift b/test/SILOptimizer/sil_combine_protocol_conf.swift index 99832b32d766a..afb6510d3aabd 100644 --- a/test/SILOptimizer/sil_combine_protocol_conf.swift +++ b/test/SILOptimizer/sil_combine_protocol_conf.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend %s -O -wmo -emit-sil -Xllvm -sil-disable-pass=DeadFunctionElimination | %FileCheck %s +// RUN: %target-swift-frontend %s -O -wmo -emit-sil | %FileCheck %s // case 1: class protocol -- should optimize internal protocol SomeProtocol : class { @@ -95,7 +95,7 @@ internal class Klass2: MultipleConformanceProtocol { } -internal class Other { +public class Other { let x:SomeProtocol let y:SomeNonClassProtocol let z:DerivedProtocol @@ -113,7 +113,7 @@ internal class Other { self.s = s; } -// CHECK-LABEL: sil hidden [noinline] @$s25sil_combine_protocol_conf5OtherC11doWorkClassSiyF : $@convention(method) (@guaranteed Other) -> Int { +// CHECK-LABEL: sil [noinline] @$s25sil_combine_protocol_conf5OtherC11doWorkClassSiyF : $@convention(method) (@guaranteed Other) -> Int { // CHECK: bb0 // CHECK: debug_value // CHECK: integer_literal @@ -162,7 +162,7 @@ internal class Other { // CHECK: struct // CHECK: return // CHECK: } // end sil function '$s25sil_combine_protocol_conf5OtherC11doWorkClassSiyF' - @inline(never) func doWorkClass () ->Int { + @inline(never) public func doWorkClass () ->Int { return self.x.foo(x:self.x) // optimize + self.y.bar(x:self.y) // optimize + self.z.foo() // do not optimize @@ -221,7 +221,7 @@ struct GenericOuter { } } -internal class OtherClass { +public class OtherClass { var arg1: PropProtocol var arg2: GenericPropProtocol var arg3: NestedPropProtocol @@ -234,7 +234,7 @@ internal class OtherClass { self.arg4 = arg4 } -// CHECK-LABEL: sil hidden [noinline] @$s25sil_combine_protocol_conf10OtherClassC12doWorkStructSiyF : $@convention(method) (@guaranteed OtherClass) -> Int { +// CHECK-LABEL: sil [noinline] @$s25sil_combine_protocol_conf10OtherClassC12doWorkStructSiyF : $@convention(method) (@guaranteed OtherClass) -> Int { // CHECK: bb0([[ARG:%.*]] : // CHECK: debug_value // CHECK: [[R1:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg1 @@ -279,7 +279,7 @@ internal class OtherClass { // CHECK: struct // CHECK: return // CHECK: } // end sil function '$s25sil_combine_protocol_conf10OtherClassC12doWorkStructSiyF' - @inline(never) func doWorkStruct () -> Int{ + @inline(never) public func doWorkStruct () -> Int{ return self.arg1.val // optimize + self.arg2.val // do not optimize + self.arg3.val // optimize @@ -322,7 +322,7 @@ internal enum AGenericEnum : AGenericProtocol { } } -internal class OtherKlass { +public class OtherKlass { var arg1: AProtocol var arg2: AGenericProtocol @@ -331,7 +331,7 @@ internal class OtherKlass { self.arg2 = arg2 } -// CHECK-LABEL: sil hidden [noinline] @$s25sil_combine_protocol_conf10OtherKlassC10doWorkEnumSiyF : $@convention(method) (@guaranteed OtherKlass) -> Int { +// CHECK-LABEL: sil [noinline] @$s25sil_combine_protocol_conf10OtherKlassC10doWorkEnumSiyF : $@convention(method) (@guaranteed OtherKlass) -> Int { // CHECK: bb0([[ARG:%.*]] : // CHECK: debug_value // CHECK: integer_literal @@ -350,7 +350,7 @@ internal class OtherKlass { // CHECK: struct // CHECK: return // CHECK: } // end sil function '$s25sil_combine_protocol_conf10OtherKlassC10doWorkEnumSiyF' - @inline(never) func doWorkEnum() -> Int { + @inline(never) public func doWorkEnum() -> Int { return self.arg1.val // optimize + self.arg2.val // do not optimize } From 9f616e5e37d9f914d507ef49182ded81e2c4faf8 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 8 Feb 2021 11:49:31 +0100 Subject: [PATCH 4/7] DeadFunctionElimination: rename the pass and some other refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main change is to rename DeadFunctionElimination -> DeadFunctionAndGlobalElimination, because the pass is now also doing dead-global elimination. A second change is to remove the FunctionLivenessComputation base class. It’s not used anywhere else. --- .../swift/SILOptimizer/PassManager/Passes.def | 8 +- .../IPO/DeadFunctionElimination.cpp | 74 ++++++------------- lib/SILOptimizer/PassManager/PassPipeline.cpp | 8 +- ...existential_specializer_indirect_class.sil | 2 +- 4 files changed, 32 insertions(+), 60 deletions(-) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 186bc1cd1039f..873b49ae2c7e8 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -140,8 +140,8 @@ PASS(DCE, "dce", "Dead Code Elimination") PASS(DeadArgSignatureOpt, "dead-arg-signature-opt", "Dead Argument Elimination via Function Specialization") -PASS(DeadFunctionElimination, "sil-deadfuncelim", - "Dead Function Elimination") +PASS(DeadFunctionAndGlobalElimination, "sil-deadfuncelim", + "Dead Function and Global Variable Elimination") PASS(DeadObjectElimination, "deadobject-elim", "Dead Object Elimination for Classes with Trivial Destruction") PASS(DefiniteInitialization, "definite-init", @@ -220,8 +220,8 @@ PASS(LICM, "licm", "Loop Invariant Code Motion") PASS(LateCodeMotion, "late-codemotion", "Late Code Motion with Release Hoisting") -PASS(LateDeadFunctionElimination, "late-deadfuncelim", - "Late Dead Function Elimination") +PASS(LateDeadFunctionAndGlobalElimination, "late-deadfuncelim", + "Late Dead Function and Global Elimination") PASS(LateInliner, "late-inline", "Late Function Inlining") PASS(LoopCanonicalizer, "loop-canonicalizer", diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 4f050e83441a8..7edf204c4eeef 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -1,4 +1,4 @@ -//===--- DeadFunctionElimination.cpp - Eliminate dead functions -----------===// +//===- DeadFunctionElimination.cpp - Eliminate dead functions and globals -===// // // This source file is part of the Swift.org open source project // @@ -31,12 +31,7 @@ STATISTIC(NumDeadGlobals, "Number of dead global variables eliminated"); namespace { -/// This is a base class for passes that are based on function liveness -/// computations like e.g. dead function elimination. -/// It provides a common logic for computing live (i.e. reachable) functions. -class FunctionLivenessComputation { -protected: - +class DeadFunctionAndGlobalElimination { /// Represents a function which is implementing a vtable or witness table /// method. struct FuncImpl { @@ -402,9 +397,6 @@ class FunctionLivenessComputation { return false; } - /// Find anchors in vtables and witness tables, if required. - virtual void findAnchorsInTables() = 0; - /// Find all functions which are alive from the beginning. /// For example, functions which may be referenced externally. void findAnchors() { @@ -435,12 +427,6 @@ class FunctionLivenessComputation { } } -public: - FunctionLivenessComputation(SILModule *module, - bool keepExternalWitnessTablesAlive) : - Module(module), - keepExternalWitnessTablesAlive(keepExternalWitnessTablesAlive) {} - /// The main entry point of the optimization. bool findAliveFunctions() { @@ -461,19 +447,6 @@ class FunctionLivenessComputation { return false; } - virtual ~FunctionLivenessComputation() {} -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// DeadFunctionElimination -//===----------------------------------------------------------------------===// - -namespace { - -class DeadFunctionElimination : FunctionLivenessComputation { - void collectMethodImplementations() { // Collect vtable method implementations. for (auto &vTable : Module->getVTables()) { @@ -528,10 +501,9 @@ class DeadFunctionElimination : FunctionLivenessComputation { } } - /// DeadFunctionElimination pass takes functions - /// reachable via vtables and witness_tables into account + /// Take functions reachable via vtables and witness_tables into account /// when computing a function liveness information. - void findAnchorsInTables() override { + void findAnchorsInTables() { collectMethodImplementations(); @@ -688,11 +660,13 @@ class DeadFunctionElimination : FunctionLivenessComputation { } public: - DeadFunctionElimination(SILModule *module, bool keepExternalWitnessTablesAlive) - : FunctionLivenessComputation(module, keepExternalWitnessTablesAlive) {} + DeadFunctionAndGlobalElimination(SILModule *module, + bool keepExternalWitnessTablesAlive) : + Module(module), + keepExternalWitnessTablesAlive(keepExternalWitnessTablesAlive) {} /// The main entry point of the optimization. - void eliminateFunctions(SILModuleTransform *DFEPass) { + void eliminateFunctionsAndGlobals(SILModuleTransform *DFEPass) { LLVM_DEBUG(llvm::dbgs() << "running dead function elimination\n"); findAliveFunctions(); @@ -730,15 +704,12 @@ class DeadFunctionElimination : FunctionLivenessComputation { } // Last step: delete all dead functions. - while (!DeadFunctions.empty()) { - SILFunction *F = DeadFunctions.back(); - DeadFunctions.pop_back(); - - LLVM_DEBUG(llvm::dbgs() << " erase dead function " << F->getName() + for (SILFunction *deadFunc : DeadFunctions) { + LLVM_DEBUG(llvm::dbgs() << " erase dead function " << deadFunc->getName() << "\n"); ++NumDeadFunc; - DFEPass->notifyWillDeleteFunction(F); - Module->eraseFunction(F); + DFEPass->notifyWillDeleteFunction(deadFunc); + Module->eraseFunction(deadFunc); } for (SILGlobalVariable *deadGlobal : DeadGlobals) { ++NumDeadGlobals; @@ -758,13 +729,13 @@ class DeadFunctionElimination : FunctionLivenessComputation { namespace { -class SILDeadFuncElimination : public SILModuleTransform { +class DeadFunctionAndGlobalEliminationPass : public SILModuleTransform { private: bool isLateDFE; public: - SILDeadFuncElimination(bool isLateDFE) : isLateDFE(isLateDFE) { } + DeadFunctionAndGlobalEliminationPass(bool isLateDFE) : isLateDFE(isLateDFE) {} void run() override { LLVM_DEBUG(llvm::dbgs() << "Running DeadFuncElimination\n"); @@ -776,24 +747,25 @@ class SILDeadFuncElimination : public SILModuleTransform { // can eliminate such functions. getModule()->invalidateSILLoaderCaches(); - DeadFunctionElimination deadFunctionElimination(getModule(), + DeadFunctionAndGlobalElimination deadFunctionElimination(getModule(), /*keepExternalWitnessTablesAlive*/ !isLateDFE); - deadFunctionElimination.eliminateFunctions(this); + deadFunctionElimination.eliminateFunctionsAndGlobals(this); } }; } // end anonymous namespace -SILTransform *swift::createDeadFunctionElimination() { - return new SILDeadFuncElimination(/*isLateDFE*/ false); +SILTransform *swift::createDeadFunctionAndGlobalElimination() { + return new DeadFunctionAndGlobalEliminationPass(/*isLateDFE*/ false); } -SILTransform *swift::createLateDeadFunctionElimination() { - return new SILDeadFuncElimination(/*isLateDFE*/ true); +SILTransform *swift::createLateDeadFunctionAndGlobalElimination() { + return new DeadFunctionAndGlobalEliminationPass(/*isLateDFE*/ true); } void swift::performSILDeadFunctionElimination(SILModule *M) { - llvm::SmallVector Pass = {PassKind::DeadFunctionElimination}; + llvm::SmallVector Pass = + {PassKind::DeadFunctionAndGlobalElimination}; auto &opts = M->getOptions(); auto plan = SILPassPipelinePlan::getPassPipelineForKinds(opts, Pass); executePassPipelinePlan(M, plan); diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 959216a5b454e..bc81982c26bf6 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -451,7 +451,7 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { // Get rid of apparently dead functions as soon as possible so that // we do not spend time optimizing them. - P.addDeadFunctionElimination(); + P.addDeadFunctionAndGlobalElimination(); // Cleanup after SILGen: remove trivial copies to temporaries. P.addTempRValueOpt(); @@ -525,7 +525,7 @@ static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { // one round of module passes. static void addHighLevelModulePipeline(SILPassPipelinePlan &P) { P.startPipeline("HighLevel,Module+StackPromote"); - P.addDeadFunctionElimination(); + P.addDeadFunctionAndGlobalElimination(); P.addPerformanceSILLinker(); P.addDeadObjectElimination(); P.addGlobalPropertyOpt(); @@ -572,7 +572,7 @@ static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) { static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { P.startPipeline("ClosureSpecialize"); - P.addDeadFunctionElimination(); + P.addDeadFunctionAndGlobalElimination(); P.addDeadStoreElimination(); P.addDeadObjectElimination(); @@ -639,7 +639,7 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) { // Delete dead code and drop the bodies of shared functions. // Also, remove externally available witness tables. They are not needed // anymore after the last devirtualizer run. - P.addLateDeadFunctionElimination(); + P.addLateDeadFunctionAndGlobalElimination(); // Perform the final lowering transformations. P.addCodeSinking(); diff --git a/test/SILOptimizer/existential_specializer_indirect_class.sil b/test/SILOptimizer/existential_specializer_indirect_class.sil index bec432a1021d3..416779e23fd0e 100644 --- a/test/SILOptimizer/existential_specializer_indirect_class.sil +++ b/test/SILOptimizer/existential_specializer_indirect_class.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all -sil-disable-pass=DeadFunctionElimination %s | %FileCheck %s +// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all -sil-disable-pass=DeadFunctionAndGlobalElimination %s | %FileCheck %s sil_stage canonical From 65b03f9815eb30d97125e283e1c2c94b0cb6e332 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 9 Feb 2021 17:11:19 +0100 Subject: [PATCH 5/7] SILOptimizer: prevent illegal load forwarding of function references in global variables. We cannot replace a load from a global let-variable with a function_ref, if the referenced function would violate the resilience rules. That means if a non-public function_ref would be inlined into a function which is serialized. --- .../SILOptimizer/Utils/BasicBlockOptUtils.h | 8 ++++-- lib/SILOptimizer/IPO/GlobalOpt.cpp | 6 +++-- .../SILCombiner/SILCombinerMiscVisitors.cpp | 5 ++-- lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp | 17 ++++++++++--- test/SILOptimizer/sil_combine_ossa.sil | 25 +++++++++++++++++++ 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h index c66784c473988..9422f976a9196 100644 --- a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h +++ b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h @@ -320,7 +320,9 @@ class StaticInitCloner : public SILCloner { /// Add \p InitVal and all its operands (transitively) for cloning. /// /// Note: all init values must are added, before calling clone(). - void add(SILInstruction *initVal); + /// Returns false if cloning is not possible, e.g. if we would end up cloning + /// a reference to a private function into a function which is serialized. + bool add(SILInstruction *initVal); /// Clone \p InitVal and all its operands into the initializer of the /// SILGlobalVariable. @@ -332,7 +334,9 @@ class StaticInitCloner : public SILCloner { static void appendToInitializer(SILGlobalVariable *gVar, SingleValueInstruction *initVal) { StaticInitCloner cloner(gVar); - cloner.add(initVal); + bool success = cloner.add(initVal); + (void)success; + assert(success && "adding initVal cannot fail for a global variable"); cloner.clone(initVal); } diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 02dd722985e57..f9eadde1c203c 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -388,7 +388,8 @@ replaceLoadsByKnownValue(SILFunction *InitF, SILGlobalVariable *SILG, StaticInitCloner cloner(initCall); SmallVector insertedInsts; cloner.setTrackingList(&insertedInsts); - cloner.add(initVal); + if (!cloner.add(initVal)) + continue; // Replace all loads from the addressor with the initial value of the global. replaceLoadsFromGlobal(initCall, initVal, cloner); @@ -693,7 +694,8 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG, continue; StaticInitCloner cloner(globalAddr); - cloner.add(initVal); + if (!cloner.add(initVal)) + continue; // Replace all loads from the addressor with the initial value of the global. replaceLoadsFromGlobal(globalAddr, initVal, cloner); diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 059d2f558717f..b8d8472a5b1c6 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -959,8 +959,9 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) { // globals, which can occur with cross-module optimization. if (SingleValueInstruction *initVal = getValueFromStaticLet(LI->getOperand())) { StaticInitCloner cloner(LI); - cloner.add(initVal); - return cloner.clone(initVal); + if (cloner.add(initVal)) { + return cloner.clone(initVal); + } } // If we have a load [copy] whose only non-debug users are destroy_value, just diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index d369e10042a19..3311e39003c3b 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -247,10 +247,19 @@ bool SinkAddressProjections::cloneProjections() { return true; } -void StaticInitCloner::add(SILInstruction *initVal) { +bool StaticInitCloner::add(SILInstruction *initVal) { // Don't schedule an instruction twice for cloning. if (numOpsToClone.count(initVal) != 0) - return; + return true; + + if (auto *funcRef = dyn_cast(initVal)) { + // We cannot inline non-public functions into functions which are serialized. + if (!getBuilder().isInsertingIntoGlobal() && + getBuilder().getFunction().isSerialized() && + !funcRef->getReferencedFunction()->hasValidLinkageForFragileRef()) { + return false; + } + } ArrayRef operands = initVal->getAllOperands(); numOpsToClone[initVal] = operands.size(); @@ -261,9 +270,11 @@ void StaticInitCloner::add(SILInstruction *initVal) { } else { // Recursively add all operands. for (const Operand &operand : operands) { - add(cast(operand.get())); + if (!add(cast(operand.get()))) + return false; } } + return true; } SingleValueInstruction * diff --git a/test/SILOptimizer/sil_combine_ossa.sil b/test/SILOptimizer/sil_combine_ossa.sil index c276dc1383333..f61966018fcd1 100644 --- a/test/SILOptimizer/sil_combine_ossa.sil +++ b/test/SILOptimizer/sil_combine_ossa.sil @@ -4497,6 +4497,31 @@ bb0: return %1 : $Int64 } +sil private [ossa] @somePrivateFunction : $@convention(thin) () -> Int64 { +bb0: + %0 = integer_literal $Builtin.Int64, 27 + %1 = struct $Int64 (%0 : $Builtin.Int64) + return %1 : $Int64 +} + +sil_global [let] @pointerToSomePrivateFunction : $@callee_guaranteed () -> Int64 = { + %0 = function_ref @somePrivateFunction: $@convention(thin) () -> Int64 + %initval = thin_to_thick_function %0 : $@convention(thin) () -> Int64 to $@callee_guaranteed () -> Int64 +} + +// CHECK-LABEL: sil [serialized] [ossa] @dontInlinePrivateFunctionRefIntoSerializedFunction +// CHECK: [[GA:%[0-9]+]] = global_addr +// CHECK: [[L:%[0-9]+]] = load [copy] [[GA]] +// CHECK: return [[L]] +// CHECK: } // end sil function 'dontInlinePrivateFunctionRefIntoSerializedFunction' +sil [serialized] [ossa] @dontInlinePrivateFunctionRefIntoSerializedFunction : $@convention(thin) () -> @owned @callee_guaranteed () -> Int64 { +bb0: + %0 = global_addr @pointerToSomePrivateFunction : $*@callee_guaranteed () -> Int64 + %1 = load [copy] %0 : $*@callee_guaranteed () -> Int64 + return %1 : $@callee_guaranteed () -> Int64 +} + + // Check for disabled optimization of unchecked_bitwise_cast to unchecked_ref_cast in ossa // This test can be optimized when ossa is supported in the SILCombine for unchecked_bitwise_cast // CHECK-LABEL: sil [ossa] @refcast : From 44f07aea2bc16f98b22f782cb6cf08361fb82675 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 5 Feb 2021 11:56:23 +0100 Subject: [PATCH 6/7] IRGen: a small refactoring in GenConstant Just moving a few functions around and make emitConstantValue the main entry point for creating constants. NFC --- lib/IRGen/GenConstant.cpp | 127 ++++++++++++++++++-------------------- lib/IRGen/GenConstant.h | 7 +-- lib/IRGen/IRGenSIL.cpp | 13 +--- 3 files changed, 64 insertions(+), 83 deletions(-) diff --git a/lib/IRGen/GenConstant.cpp b/lib/IRGen/GenConstant.cpp index f9a6eae9d39bb..928c491c05479 100644 --- a/lib/IRGen/GenConstant.cpp +++ b/lib/IRGen/GenConstant.cpp @@ -22,6 +22,7 @@ #include "GenTuple.h" #include "TypeInfo.h" #include "StructLayout.h" +#include "Callee.h" #include "swift/Basic/Range.h" #include "swift/SIL/SILModule.h" @@ -109,11 +110,67 @@ llvm::Constant *irgen::emitAddrOfConstantString(IRGenModule &IGM, llvm_unreachable("bad string encoding"); } -static llvm::Constant *emitConstantValue(IRGenModule &IGM, SILValue operand) { +namespace { + +/// Fill in the missing values for padding. +void insertPadding(SmallVectorImpl &Elements, + llvm::StructType *sTy) { + // fill in any gaps, which are the explicit padding that swiftc inserts. + for (unsigned i = 0, e = Elements.size(); i != e; ++i) { + auto &elt = Elements[i]; + if (elt == nullptr) { + auto *eltTy = sTy->getElementType(i); + assert(eltTy->isArrayTy() && + eltTy->getArrayElementType()->isIntegerTy(8) && + "Unexpected non-byte-array type for constant struct padding"); + elt = llvm::UndefValue::get(eltTy); + } + } +} + +template +llvm::Constant *emitConstantStructOrTuple(IRGenModule &IGM, InstTy inst, + NextIndexFunc nextIndex) { + auto type = inst->getType(); + auto *sTy = cast(IGM.getTypeInfo(type).getStorageType()); + + SmallVector elts(sTy->getNumElements(), nullptr); + + // run over the Swift initializers, putting them into the struct as + // appropriate. + for (unsigned i = 0, e = inst->getElements().size(); i != e; ++i) { + auto operand = inst->getOperand(i); + Optional index = nextIndex(IGM, type, i); + if (index.hasValue()) { + assert(elts[index.getValue()] == nullptr && + "Unexpected constant struct field overlap"); + + elts[index.getValue()] = emitConstantValue(IGM, operand); + } + } + insertPadding(elts, sTy); + return llvm::ConstantStruct::get(sTy, elts); +} +} // end anonymous namespace + +llvm::Constant *irgen::emitConstantValue(IRGenModule &IGM, SILValue operand) { if (auto *SI = dyn_cast(operand)) { - return emitConstantStruct(IGM, SI); + // The only way to get a struct's stored properties (which we need to map to + // their physical/LLVM index) is to iterate over the properties + // progressively. Fortunately the iteration order matches the order of + // operands in a StructInst. + auto StoredProperties = SI->getStructDecl()->getStoredProperties(); + auto Iter = StoredProperties.begin(); + + return emitConstantStructOrTuple( + IGM, SI, [&Iter](IRGenModule &IGM, SILType Type, unsigned _i) mutable { + (void)_i; + auto *FD = *Iter++; + return irgen::getPhysicalStructFieldIndex(IGM, Type, FD); + }); } else if (auto *TI = dyn_cast(operand)) { - return emitConstantTuple(IGM, TI); + return emitConstantStructOrTuple(IGM, TI, + irgen::getPhysicalTupleElementStructIndex); } else if (auto *ILI = dyn_cast(operand)) { return emitConstantInt(IGM, ILI); } else if (auto *FLI = dyn_cast(operand)) { @@ -164,70 +221,6 @@ static llvm::Constant *emitConstantValue(IRGenModule &IGM, SILValue operand) { } } -namespace { - -/// Fill in the missing values for padding. -void insertPadding(SmallVectorImpl &Elements, - llvm::StructType *sTy) { - // fill in any gaps, which are the explicit padding that swiftc inserts. - for (unsigned i = 0, e = Elements.size(); i != e; ++i) { - auto &elt = Elements[i]; - if (elt == nullptr) { - auto *eltTy = sTy->getElementType(i); - assert(eltTy->isArrayTy() && - eltTy->getArrayElementType()->isIntegerTy(8) && - "Unexpected non-byte-array type for constant struct padding"); - elt = llvm::UndefValue::get(eltTy); - } - } -} - -template -llvm::Constant *emitConstantStructOrTuple(IRGenModule &IGM, InstTy inst, - NextIndexFunc nextIndex) { - auto type = inst->getType(); - auto *sTy = cast(IGM.getTypeInfo(type).getStorageType()); - - SmallVector elts(sTy->getNumElements(), nullptr); - - // run over the Swift initializers, putting them into the struct as - // appropriate. - for (unsigned i = 0, e = inst->getElements().size(); i != e; ++i) { - auto operand = inst->getOperand(i); - Optional index = nextIndex(IGM, type, i); - if (index.hasValue()) { - assert(elts[index.getValue()] == nullptr && - "Unexpected constant struct field overlap"); - - elts[index.getValue()] = emitConstantValue(IGM, operand); - } - } - insertPadding(elts, sTy); - return llvm::ConstantStruct::get(sTy, elts); -} -} // end anonymous namespace - -llvm::Constant *irgen::emitConstantStruct(IRGenModule &IGM, StructInst *SI) { - // The only way to get a struct's stored properties (which we need to map to - // their physical/LLVM index) is to iterate over the properties - // progressively. Fortunately the iteration order matches the order of - // operands in a StructInst. - auto StoredProperties = SI->getStructDecl()->getStoredProperties(); - auto Iter = StoredProperties.begin(); - - return emitConstantStructOrTuple( - IGM, SI, [&Iter](IRGenModule &IGM, SILType Type, unsigned _i) mutable { - (void)_i; - auto *FD = *Iter++; - return irgen::getPhysicalStructFieldIndex(IGM, Type, FD); - }); -} - -llvm::Constant *irgen::emitConstantTuple(IRGenModule &IGM, TupleInst *TI) { - return emitConstantStructOrTuple(IGM, TI, - irgen::getPhysicalTupleElementStructIndex); -} - llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI, StructLayout *ClassLayout) { auto *sTy = cast(ClassLayout->getType()); diff --git a/lib/IRGen/GenConstant.h b/lib/IRGen/GenConstant.h index 6416893726003..a0dac169c3795 100644 --- a/lib/IRGen/GenConstant.h +++ b/lib/IRGen/GenConstant.h @@ -37,11 +37,8 @@ llvm::Constant *emitConstantFP(IRGenModule &IGM, FloatLiteralInst *FLI); llvm::Constant *emitAddrOfConstantString(IRGenModule &IGM, StringLiteralInst *SLI); -/// Construct a struct literal from a StructInst containing constant values. -llvm::Constant *emitConstantStruct(IRGenModule &IGM, StructInst *SI); - -/// Construct a struct literal from a TupleInst containing constant values. -llvm::Constant *emitConstantTuple(IRGenModule &IGM, TupleInst *TI); +/// Construct a constant from a SILValue containing constant values. +llvm::Constant *emitConstantValue(IRGenModule &IGM, SILValue value); /// Construct an object (with a HeapObject header) from an ObjectInst /// containing constant values. diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 1a4caa4477881..69614be893236 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -6414,17 +6414,8 @@ void IRGenModule::emitSILStaticInitializers() { continue; } - // Set the IR global's initializer to the constant for this SIL - // struct. - if (auto *SI = dyn_cast(InitValue)) { - IRGlobal->setInitializer(emitConstantStruct(*this, SI)); - continue; - } - - // Set the IR global's initializer to the constant for this SIL - // tuple. - auto *TI = cast(InitValue); - IRGlobal->setInitializer(emitConstantTuple(*this, TI)); + IRGlobal->setInitializer( + emitConstantValue(*this, cast(InitValue))); } } From 0a483a6b6b48add920fe84fa4f3a9adc887f1978 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 5 Feb 2021 14:08:35 +0100 Subject: [PATCH 7/7] SILOptimizer: allow function pointers in static globals and static global arrays. For example, generate a static global for the array literal in: func foo(_ i: Int) -> Int { ... } func bar(_ i: Int) -> Int { ... } func returnFunctionArray() -> [(Int) -> Int] { return [foo, bar] } rdar://73570149 https://bugs.swift.org/browse/SR-14101 --- lib/IRGen/GenConstant.cpp | 34 +++++++++++++++++++++++ lib/SIL/IR/SILGlobalVariable.cpp | 15 ++++++----- test/SILOptimizer/static_arrays.swift | 39 +++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/lib/IRGen/GenConstant.cpp b/lib/IRGen/GenConstant.cpp index 928c491c05479..4116589e48665 100644 --- a/lib/IRGen/GenConstant.cpp +++ b/lib/IRGen/GenConstant.cpp @@ -216,6 +216,40 @@ llvm::Constant *irgen::emitConstantValue(IRGenModule &IGM, SILValue operand) { auto *val = emitConstantValue(IGM, VTBI->getOperand()); auto *sTy = IGM.getTypeInfo(VTBI->getType()).getStorageType(); return llvm::ConstantExpr::getIntToPtr(val, sTy); + + } else if (auto *CFI = dyn_cast(operand)) { + return emitConstantValue(IGM, CFI->getOperand()); + + } else if (auto *T2TFI = dyn_cast(operand)) { + SILType type = operand->getType(); + auto *sTy = cast(IGM.getTypeInfo(type).getStorageType()); + + auto *function = llvm::ConstantExpr::getBitCast( + emitConstantValue(IGM, T2TFI->getCallee()), + sTy->getTypeAtIndex((unsigned)0)); + + auto *context = llvm::ConstantExpr::getBitCast( + llvm::ConstantPointerNull::get(IGM.OpaquePtrTy), + sTy->getTypeAtIndex((unsigned)1)); + + return llvm::ConstantStruct::get(sTy, {function, context}); + + } else if (auto *FRI = dyn_cast(operand)) { + SILFunction *fn = FRI->getReferencedFunction(); + + llvm::Constant *fnPtr = IGM.getAddrOfSILFunction(fn, NotForDefinition); + assert(!fn->isAsync() && "TODO: support async functions"); + + CanSILFunctionType fnType = FRI->getType().getAs(); + auto authInfo = PointerAuthInfo::forFunctionPointer(IGM, fnType); + if (authInfo.isSigned()) { + auto constantDiscriminator = + cast(authInfo.getDiscriminator()); + assert(!constantDiscriminator->getType()->isPointerTy()); + fnPtr = IGM.getConstantSignedPointer(fnPtr, authInfo.getKey(), nullptr, + constantDiscriminator); + } + return fnPtr; } else { llvm_unreachable("Unsupported SILInstruction in static initializer!"); } diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index 9b91eddbb31ac..32aeb1470a493 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -170,12 +170,19 @@ bool SILGlobalVariable::isValidStaticInitializerInst(const SILInstruction *I, return false; } return false; + case SILInstructionKind::FunctionRefInst: + // TODO: support async function pointers in static globals. + if (cast(I)->getReferencedFunction()->isAsync()) + return false; + return true; case SILInstructionKind::StructInst: case SILInstructionKind::TupleInst: case SILInstructionKind::IntegerLiteralInst: case SILInstructionKind::FloatLiteralInst: case SILInstructionKind::ObjectInst: case SILInstructionKind::ValueToBridgeObjectInst: + case SILInstructionKind::ConvertFunctionInst: + case SILInstructionKind::ThinToThickFunctionInst: return true; default: return false; @@ -310,13 +317,7 @@ swift::getVariableOfStaticInitializer(SILFunction *InitFunc, if (HasStore || SI->getDest() != SGA) return nullptr; HasStore = true; - SILValue value = SI->getSrc(); - - // We only handle StructInst and TupleInst being stored to a - // global variable for now. - if (!isa(value) && !isa(value)) - return nullptr; - InitVal = cast(value); + InitVal = cast(SI->getSrc()); } else if (!SILGlobalVariable::isValidStaticInitializerInst(&I, I.getModule())) { return nullptr; diff --git a/test/SILOptimizer/static_arrays.swift b/test/SILOptimizer/static_arrays.swift index d94540b319aad..a83d75a71bf0b 100644 --- a/test/SILOptimizer/static_arrays.swift +++ b/test/SILOptimizer/static_arrays.swift @@ -9,6 +9,11 @@ // Check if the optimizer is able to convert array literals to statically initialized arrays. +// CHECK-LABEL: sil_global @$s4test4FStrV10globalFuncyS2icvpZ : $@callee_guaranteed (Int) -> Int = { +// CHECK: %0 = function_ref @$s4test3fooyS2iF : $@convention(thin) (Int) -> Int +// CHECK-NEXT: %initval = thin_to_thick_function %0 +// CHECK-NEXT: } + // CHECK-LABEL: outlined variable #0 of arrayLookup(_:) // CHECK-NEXT: sil_global private @{{.*}}arrayLookup{{.*}} = { // CHECK-DAG: integer_literal $Builtin.Int{{[0-9]+}}, 10 @@ -51,6 +56,20 @@ // CHECK: object {{.*}} ({{[^,]*}}, [tail_elems] {{[^,]*}}, {{[^,]*}}) // CHECK-NEXT: } +// CHECK-LABEL: outlined variable #0 of functionArray() +// CHECK-NEXT: sil_global private @{{.*functionArray.*}} = { +// CHECK: function_ref +// CHECK: thin_to_thick_function +// CHECK: convert_function +// CHECK: function_ref +// CHECK: thin_to_thick_function +// CHECK: convert_function +// CHECK: function_ref +// CHECK: thin_to_thick_function +// CHECK: convert_function +// CHECK: object {{.*}} ({{[^,]*}}, [tail_elems] +// CHECK-NEXT: } + // CHECK-LABEL: outlined variable #0 of returnDictionary() // CHECK-NEXT: sil_global private @{{.*}}returnDictionary{{.*}} = { // CHECK-DAG: integer_literal $Builtin.Int{{[0-9]+}}, 5 @@ -154,6 +173,22 @@ public func returnStringDictionary() -> [String:String] { return ["1":"2", "3":"4", "5":"6"] } +func foo(_ i: Int) -> Int { return i } + +// CHECK-LABEL: sil {{.*functionArray.*}} : $@convention(thin) () -> @owned Array<(Int) -> Int> { +// CHECK: global_value @{{.*functionArray.*}} +// CHECK: } // end sil function '{{.*functionArray.*}}' +@inline(never) +func functionArray() -> [(Int) -> Int] { + func bar(_ i: Int) -> Int { return i + 1 } + return [foo, bar, { $0 + 10 }] +} + +public struct FStr { + // Not an array, but also tested here. + public static var globalFunc = foo +} + // CHECK-OUTPUT: [100, 101, 102] print(globalVariable) // CHECK-OUTPUT-NEXT: 11 @@ -168,6 +203,10 @@ print(gg!) storeArray() // CHECK-OUTPUT-NEXT: [227, 228] print(gg!) +// CHECK-OUTPUT-NEXT: 311 +print(functionArray()[0](100) + functionArray()[1](100) + functionArray()[2](100)) +// CHECK-OUTPUT-NEXT: 27 +print(FStr.globalFunc(27)) let dict = returnDictionary() // CHECK-OUTPUT-NEXT: dict 3: 2, 4, 6