Skip to content

Commit 7208cf5

Browse files
authored
Merge pull request #65269 from tshortli/unavailable-decl-optimization-stub-5.9
[5.9] Introduce `-unavailable-decl-optimization=stub`
2 parents f8cc3d4 + 8c45bb2 commit 7208cf5

39 files changed

+477
-9
lines changed

docs/ABI/Mangling.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ are always non-polymorphic ``<impl-function-type>`` types.
301301
VALUE-WITNESS-KIND ::= 'ug' // getEnumTag
302302
VALUE-WITNESS-KIND ::= 'up' // destructiveProjectEnumData
303303
VALUE-WITNESS-KIND ::= 'ui' // destructiveInjectEnumTag
304+
VALUE-WITNESS-KIND ::= 'et' // getEnumTagSinglePayload
305+
VALUE-WITNESS-KIND ::= 'st' // storeEnumTagSinglePayload
304306

305307
``<VALUE-WITNESS-KIND>`` differentiates the kinds of value
306308
witness functions for a type.

include/swift/AST/KnownDecls.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ FUNC_DECL(DiagnoseUnexpectedError, "_unexpectedError")
6868
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
6969
FUNC_DECL(DiagnoseUnexpectedEnumCase, "_diagnoseUnexpectedEnumCase")
7070
FUNC_DECL(DiagnoseUnexpectedEnumCaseValue, "_diagnoseUnexpectedEnumCaseValue")
71+
FUNC_DECL(DiagnoseUnavailableCodeReached, "_diagnoseUnavailableCodeReached")
7172

7273
FUNC_DECL(GetErrorEmbeddedNSError, "_getErrorEmbeddedNSError")
7374

include/swift/Basic/LangOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ namespace swift {
112112
/// resulting binary by default in this mode.
113113
None,
114114

115+
/// Stub out code associated with unavailable declarations.
116+
///
117+
/// For example, the bodies of unavailable functions should be compiled as
118+
/// if they just contained a call to fatalError().
119+
Stub,
120+
115121
/// Avoid generating any code for unavailable declarations.
116122
///
117123
/// NOTE: This optimization can be ABI breaking for a library evolution

include/swift/SIL/ApplySite.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,12 @@ class ApplySite {
241241
FOREACH_IMPL_RETURN(isCalleeKnownProgramTerminationPoint());
242242
}
243243

244+
/// Returns true if the callee function is annotated with
245+
/// @_semantics("unavailable_code_reached")
246+
bool isCalleeUnavailableCodeReached() const {
247+
FOREACH_IMPL_RETURN(isCalleeUnavailableCodeReached());
248+
}
249+
244250
/// Check if this is a call of a never-returning function.
245251
bool isCalleeNoReturn() const { FOREACH_IMPL_RETURN(isCalleeNoReturn()); }
246252

include/swift/SIL/SILInstruction.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2777,6 +2777,14 @@ class ApplyInstBase<Impl, Base, false> : public Base {
27772777
return calleeFn->hasSemanticsAttr(SEMANTICS_PROGRAMTERMINATION_POINT);
27782778
}
27792779

2780+
/// Returns true if the callee function is annotated with
2781+
/// @_semantics("unavailable_code_reached")
2782+
bool isCalleeUnavailableCodeReached() const {
2783+
auto calleeFn = getCalleeFunction();
2784+
if (!calleeFn) return false;
2785+
return calleeFn->hasSemanticsAttr(SEMANTICS_UNAVAILABLE_CODE_REACHED);
2786+
}
2787+
27802788
/// True if this application has generic substitutions.
27812789
bool hasSubstitutions() const {
27822790
return Substitutions.hasAnySubstitutableParams();

include/swift/SIL/SILModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,10 @@ LLVM_LIBRARY_VISIBILITY bool usesObjCAllocator(ClassDecl *theClass);
10831083
/// A declaration may not require lowering if, for example, it is annotated as
10841084
/// unavailable and optimization settings allow it to be omitted.
10851085
LLVM_LIBRARY_VISIBILITY bool shouldSkipLowering(Decl *D);
1086+
1087+
/// Returns true if SIL/IR lowering for the given declaration should produce
1088+
/// a stub that traps at runtime because the code ought to be unreachable.
1089+
LLVM_LIBRARY_VISIBILITY bool shouldLowerToUnavailableCodeStub(Decl *D);
10861090
} // namespace Lowering
10871091

10881092
/// Apply the given function to each ABI member of \c D skipping the members

include/swift/Strings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ constexpr static const StringLiteral SEMANTICS_PROGRAMTERMINATION_POINT =
6060
constexpr static const StringLiteral SEMANTICS_DEFAULT_ACTOR =
6161
"defaultActor";
6262

63+
constexpr static const StringLiteral SEMANTICS_UNAVAILABLE_CODE_REACHED =
64+
"unavailable_code_reached";
65+
6366
constexpr static const StringLiteral DEFAULT_ACTOR_STORAGE_FIELD_NAME =
6467
"$defaultActor";
6568

lib/ClangImporter/ClangImporter.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6566,7 +6566,6 @@ CxxRecordSemanticsKind
65666566
CxxRecordSemantics::evaluate(Evaluator &evaluator,
65676567
CxxRecordSemanticsDescriptor desc) const {
65686568
const auto *decl = desc.decl;
6569-
auto &clangSema = desc.ctx.getClangModuleLoader()->getClangSema();
65706569

65716570
if (hasImportAsRefAttr(decl)) {
65726571
return CxxRecordSemanticsKind::Reference;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
568568
auto value =
569569
llvm::StringSwitch<Optional<UnavailableDeclOptimization>>(A->getValue())
570570
.Case("none", UnavailableDeclOptimization::None)
571+
.Case("stub", UnavailableDeclOptimization::Stub)
571572
.Case("complete", UnavailableDeclOptimization::Complete)
572573
.Default(None);
573574

lib/SIL/IR/SILModule.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,3 +965,13 @@ bool Lowering::shouldSkipLowering(Decl *D) {
965965
// -unavailable-decl-optimization=complete is specified.
966966
return D->getSemanticUnavailableAttr() != None;
967967
}
968+
969+
bool Lowering::shouldLowerToUnavailableCodeStub(Decl *D) {
970+
if (D->getASTContext().LangOpts.UnavailableDeclOptimizationMode !=
971+
UnavailableDeclOptimization::Stub)
972+
return false;
973+
974+
// Unavailable declarations should trap at runtime if
975+
// -unavailable-decl-optimization=stub is specified.
976+
return D->getSemanticUnavailableAttr() != None;
977+
}

lib/SILGen/SILGenApply.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5829,7 +5829,15 @@ SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc,
58295829
SubstitutionMap subMap,
58305830
ArrayRef<ManagedValue> args,
58315831
SGFContext ctx) {
5832-
auto callee = Callee::forDirect(*this, SILDeclRef(fn), subMap, loc);
5832+
return emitApplyOfLibraryIntrinsic(loc, SILDeclRef(fn), subMap, args, ctx);
5833+
}
5834+
5835+
RValue SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc,
5836+
SILDeclRef declRef,
5837+
SubstitutionMap subMap,
5838+
ArrayRef<ManagedValue> args,
5839+
SGFContext ctx) {
5840+
auto callee = Callee::forDirect(*this, declRef, subMap, loc);
58335841

58345842
auto origFormalType = callee.getOrigFormalType();
58355843
auto substFormalType = callee.getSubstFormalType();
@@ -5863,6 +5871,29 @@ SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc,
58635871
finalArgs, calleeTypeInfo, ApplyOptions(), ctx, None);
58645872
}
58655873

5874+
void SILGenFunction::emitApplyOfUnavailableCodeReached() {
5875+
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
5876+
FuncDecl *fd = getASTContext().getDiagnoseUnavailableCodeReached();
5877+
5878+
if (!fd) {
5879+
// Broken stdlib?
5880+
B.createUnconditionalFail(loc, "unavailable code reached");
5881+
return;
5882+
}
5883+
5884+
auto declRef = SILDeclRef(fd);
5885+
if (fd->isBackDeployed(getASTContext())) {
5886+
// The standard library entry point for the diagnostic function was
5887+
// introduced in Swift 5.9 so we call the back deployment thunk in case this
5888+
// code will execute on an older runtime.
5889+
declRef =
5890+
declRef.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk);
5891+
}
5892+
5893+
emitApplyOfLibraryIntrinsic(loc, declRef, SubstitutionMap(), {},
5894+
SGFContext());
5895+
}
5896+
58665897
StringRef SILGenFunction::getMagicFunctionString() {
58675898
assert(MagicFunctionName
58685899
&& "asking for #function but we don't have a function name?!");

lib/SILGen/SILGenBridging.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,11 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
15561556
assert(thunk.isForeign);
15571557
SILDeclRef native = thunk.asForeign(false);
15581558

1559+
if (thunk.hasDecl()) {
1560+
if (shouldLowerToUnavailableCodeStub(thunk.getDecl()))
1561+
emitApplyOfUnavailableCodeReached();
1562+
}
1563+
15591564
// If we're calling a native non-designated class initializer, we have to
15601565
// discard the `self` object we were given, since
15611566
// Swift convenience initializers only have allocating entry points that
@@ -2067,6 +2072,9 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
20672072
auto nativeFnTy = F.getLoweredFunctionType();
20682073
assert(nativeFnTy == nativeCI.SILFnType);
20692074

2075+
if (shouldLowerToUnavailableCodeStub(fd))
2076+
emitApplyOfUnavailableCodeReached();
2077+
20702078
// Use the same generic environment as the native entry point.
20712079
F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(thunk));
20722080

lib/SILGen/SILGenConstructor.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
306306
RegularLocation Loc(ctor);
307307
Loc.markAutoGenerated();
308308

309+
if (shouldLowerToUnavailableCodeStub(ctor))
310+
SGF.emitApplyOfUnavailableCodeReached();
311+
309312
AssertingManualScope functionLevelScope(SGF.Cleanups,
310313
CleanupLocation(Loc));
311314

@@ -532,6 +535,9 @@ void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
532535
bool isDelegating = ctor->getDelegatingOrChainedInitKind().initKind ==
533536
BodyInitKind::Delegating;
534537

538+
if (shouldLowerToUnavailableCodeStub(ctor))
539+
emitApplyOfUnavailableCodeReached();
540+
535541
// Get the 'self' decl and type.
536542
VarDecl *selfDecl = ctor->getImplicitSelfDecl();
537543
auto &lowering = getTypeLowering(selfDecl->getType());
@@ -744,6 +750,9 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) {
744750
auto &enumTI =
745751
SGM.Types.getTypeLowering(enumTy, TypeExpansionContext::minimal());
746752

753+
if (shouldLowerToUnavailableCodeStub(element))
754+
emitApplyOfUnavailableCodeReached();
755+
747756
RegularLocation Loc(element);
748757
CleanupLocation CleanupLoc(element);
749758
Loc.markAutoGenerated();
@@ -820,6 +829,9 @@ void SILGenFunction::emitClassConstructorAllocator(ConstructorDecl *ctor) {
820829
SmallVector<SILValue, 8> args;
821830
bindParametersForForwarding(ctor->getParameters(), args);
822831

832+
if (shouldLowerToUnavailableCodeStub(ctor))
833+
emitApplyOfUnavailableCodeReached();
834+
823835
SILValue selfMetaValue = emitConstructorMetatypeArg(*this, ctor);
824836

825837
// Allocate the "self" value.
@@ -936,6 +948,9 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
936948

937949
assert(ctor->getTypecheckedBody() && "Class constructor without a body?");
938950

951+
if (shouldLowerToUnavailableCodeStub(ctor))
952+
emitApplyOfUnavailableCodeReached();
953+
939954
// True if this constructor delegates to a peer constructor with self.init().
940955
bool isDelegating = false;
941956
if (!ctor->hasStubImplementation()) {

lib/SILGen/SILGenDestructor.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) {
3535
if (dd->isImplicit())
3636
Loc.markAutoGenerated();
3737

38+
if (shouldLowerToUnavailableCodeStub(dd))
39+
emitApplyOfUnavailableCodeReached();
40+
3841
auto cd = cast<ClassDecl>(dd->getDeclContext()->getSelfNominalTypeDecl());
3942
auto &C = cd->getASTContext();
4043
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
@@ -178,6 +181,9 @@ void SILGenFunction::emitDeallocatingClassDestructor(DestructorDecl *dd) {
178181
RegularLocation loc(dd);
179182
loc.markAutoGenerated();
180183

184+
if (shouldLowerToUnavailableCodeStub(dd))
185+
emitApplyOfUnavailableCodeReached();
186+
181187
// Emit the prolog.
182188
SILValue initialSelfValue =
183189
emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
@@ -233,6 +239,9 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) {
233239
if (dd->isImplicit())
234240
loc.markAutoGenerated();
235241

242+
if (shouldLowerToUnavailableCodeStub(dd))
243+
emitApplyOfUnavailableCodeReached();
244+
236245
// Emit the prolog.
237246
auto selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
238247

@@ -603,6 +612,9 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) {
603612
if (dd->isImplicit())
604613
loc.markAutoGenerated();
605614

615+
if (shouldLowerToUnavailableCodeStub(dd))
616+
emitApplyOfUnavailableCodeReached();
617+
606618
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
607619

608620
// Create a basic block to jump to for the implicit destruction behavior

lib/SILGen/SILGenFunction.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,9 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
882882
prepareEpilog(fd->getResultInterfaceType(),
883883
fd->hasThrows(), CleanupLocation(fd));
884884

885+
if (shouldLowerToUnavailableCodeStub(fd))
886+
emitApplyOfUnavailableCodeReached();
887+
885888
emitProfilerIncrement(fd->getTypecheckedBody());
886889

887890
// Emit the actual function body as usual

lib/SILGen/SILGenFunction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
19011901
ArrayRef<ManagedValue> args,
19021902
SGFContext ctx);
19031903

1904+
RValue emitApplyOfLibraryIntrinsic(SILLocation loc, SILDeclRef declRef,
1905+
SubstitutionMap subMap,
1906+
ArrayRef<ManagedValue> args,
1907+
SGFContext ctx);
1908+
1909+
/// Emits a call to the `_diagnoseUnavailableCodeReached()` function in the
1910+
/// standard library.
1911+
void emitApplyOfUnavailableCodeReached();
1912+
19041913
RValue emitApplyAllocatingInitializer(SILLocation loc, ConcreteDeclRef init,
19051914
PreparedArguments &&args, Type overriddenSelfType,
19061915
SGFContext ctx);

lib/SILGen/SILGenGlobalVariable.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ void SILGenFunction::emitLazyGlobalInitializer(PatternBindingDecl *binding,
240240

241241
// Add unused context pointer argument required to pass to `Builtin.once`
242242
SILBasicBlock &entry = *F.begin();
243+
244+
if (shouldLowerToUnavailableCodeStub(binding))
245+
emitApplyOfUnavailableCodeReached();
246+
243247
SILType rawPointerSILTy =
244248
getLoweredLoadableType(getASTContext().TheRawPointerType);
245249
entry.createFunctionArgument(rawPointerSILTy);
@@ -279,6 +283,9 @@ static void emitOnceCall(SILGenFunction &SGF, VarDecl *global,
279283
void SILGenFunction::emitGlobalAccessor(VarDecl *global,
280284
SILGlobalVariable *onceToken,
281285
SILFunction *onceFunc) {
286+
if (shouldLowerToUnavailableCodeStub(global))
287+
emitApplyOfUnavailableCodeReached();
288+
282289
emitOnceCall(*this, global, onceToken, onceFunc);
283290

284291
// Return the address of the global variable.

lib/SILGen/SILGenPoly.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4966,6 +4966,9 @@ void SILGenFunction::emitProtocolWitness(
49664966
SmallVector<ManagedValue, 8> origParams;
49674967
collectThunkParams(loc, origParams);
49684968

4969+
if (shouldLowerToUnavailableCodeStub(witness.getDecl()))
4970+
emitApplyOfUnavailableCodeReached();
4971+
49694972
if (enterIsolation) {
49704973
// If we are supposed to enter the actor, do so now by hopping to the
49714974
// actor.

lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,14 @@ static SILInstruction *getPrecedingCallToNoReturn(SILBasicBlock &BB) {
724724
return first;
725725
}
726726

727+
static bool isUnavailableCodeReachedCall(SILInstruction *I) {
728+
if (auto *AI = dyn_cast<ApplyInst>(I))
729+
if (AI->hasSemantics(SEMANTICS_UNAVAILABLE_CODE_REACHED))
730+
return true;
731+
732+
return false;
733+
}
734+
727735
static bool simplifyBlocksWithCallsToNoReturn(SILBasicBlock &BB,
728736
UnreachableUserCodeReportingState *State) {
729737
auto I = BB.begin(), E = BB.end();
@@ -766,6 +774,13 @@ static bool simplifyBlocksWithCallsToNoReturn(SILBasicBlock &BB,
766774
noReturnCall->getLoc().isASTNode<ExplicitCastExpr>())
767775
return false;
768776

777+
// If the no-return instruction is a call to the unavailable code reached
778+
// diagnostic function then we assume that the call was inserted by the
779+
// compiler because the function is semantically unavailable. Diagnosing the
780+
// user written body of the function as unreachable would be redundant.
781+
if (isUnavailableCodeReachedCall(noReturnCall))
782+
return false;
783+
769784
diagnose(BB.getModule().getASTContext(), currInst->getLoc().getSourceLoc(),
770785
diag::unreachable_code);
771786
diagnose(BB.getModule().getASTContext(),

lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ namespace {
5050
class LowerHopToActor {
5151
SILFunction *F;
5252
DominanceInfo *Dominance;
53-
SILOptFunctionBuilder &functionBuilder;
5453

5554
/// A map from an actor value to the executor we've derived for it.
5655
llvm::ScopedHashTable<SILValue, SILValue> ExecutorForActor;
@@ -64,11 +63,9 @@ class LowerHopToActor {
6463

6564
public:
6665
LowerHopToActor(SILFunction *f,
67-
SILOptFunctionBuilder &FunctionBuilder,
6866
DominanceInfo *dominance)
6967
: F(f),
70-
Dominance(dominance),
71-
functionBuilder(FunctionBuilder)
68+
Dominance(dominance)
7269
{ }
7370

7471
/// The entry point to the transformation.
@@ -250,8 +247,7 @@ class LowerHopToActorPass : public SILFunctionTransform {
250247
void run() override {
251248
auto fn = getFunction();
252249
auto domTree = getAnalysis<DominanceAnalysis>()->get(fn);
253-
auto functionBuilder = SILOptFunctionBuilder(*this);
254-
LowerHopToActor pass(getFunction(), functionBuilder, domTree);
250+
LowerHopToActor pass(getFunction(), domTree);
255251
if (pass.run())
256252
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
257253
}

0 commit comments

Comments
 (0)