Skip to content

Commit 7f20380

Browse files
committed
IRGen: Generate #_hasSymbol query functions.
For each decl that needs a `#_hasSymbol()` query function, emit the corresponding helper function body during IRGen. Use `IRSymbolVisitor` to collect linkable symbols associated with the decl and return true from the helper function if the address of every associated symbol is non-null. Resolves rdar://101884587
1 parent 49e0015 commit 7f20380

File tree

9 files changed

+334
-14
lines changed

9 files changed

+334
-14
lines changed

include/swift/IRGen/Linking.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ class LinkEntity {
685685
isa<ProtocolDecl>(decl->getDeclContext()));
686686
}
687687

688+
SILDeclRef::Kind getSILDeclRefKind() const;
689+
688690
public:
689691
static LinkEntity forDispatchThunk(SILDeclRef declRef) {
690692
assert(isValidResilientMethodRef(declRef));
@@ -1538,6 +1540,15 @@ class LinkEntity {
15381540
bool isDynamicallyReplaceableFunctionKey() const {
15391541
return getKind() == Kind::DynamicallyReplaceableFunctionKey;
15401542
}
1543+
bool isTypeMetadataAccessFunction() const {
1544+
return getKind() == Kind::TypeMetadataAccessFunction;
1545+
}
1546+
bool isDispatchThunk() const {
1547+
return getKind() == Kind::DispatchThunk ||
1548+
getKind() == Kind::DispatchThunkInitializer ||
1549+
getKind() == Kind::DispatchThunkAllocator ||
1550+
getKind() == Kind::DispatchThunkDerivative;
1551+
}
15411552

15421553
/// Determine whether this entity will be weak-imported.
15431554
bool isWeakImported(ModuleDecl *module) const;

lib/IRGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_swift_host_library(swiftIRGen STATIC
2525
GenEnum.cpp
2626
GenExistential.cpp
2727
GenFunc.cpp
28+
GenHasSymbol.cpp
2829
GenHeap.cpp
2930
GenInit.cpp
3031
GenIntegerLiteral.cpp

lib/IRGen/GenHasSymbol.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//===--- GenHasSymbol.cpp - IR Generation for #_hasSymbol queries ---------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements IR generation for `if #_hasSymbol` condition queries.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/AST/ASTMangler.h"
18+
#include "swift/AST/PrettyStackTrace.h"
19+
#include "swift/IRGen/IRSymbolVisitor.h"
20+
#include "swift/IRGen/Linking.h"
21+
#include "swift/SIL/SILFunctionBuilder.h"
22+
#include "swift/SIL/SILModule.h"
23+
#include "swift/SIL/SILSymbolVisitor.h"
24+
25+
#include "GenDecl.h"
26+
#include "IRGenFunction.h"
27+
#include "IRGenModule.h"
28+
29+
using namespace swift;
30+
using namespace irgen;
31+
32+
/// Wrapper for IRGenModule::getAddrOfLLVMVariable() that also handles a few
33+
/// additional types of entities that the main utility cannot.
34+
static llvm::Constant *getAddrOfLLVMVariable(IRGenModule &IGM,
35+
LinkEntity entity) {
36+
if (entity.isTypeMetadataAccessFunction())
37+
return IGM.getAddrOfTypeMetadataAccessFunction(entity.getType(),
38+
NotForDefinition);
39+
if (entity.isDispatchThunk())
40+
return IGM.getAddrOfDispatchThunk(entity.getSILDeclRef(), NotForDefinition);
41+
42+
return IGM.getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo());
43+
}
44+
45+
class HasSymbolIRGenVisitor : public IRSymbolVisitor {
46+
IRGenModule &IGM;
47+
llvm::SmallVector<llvm::Constant *, 4> &Addrs;
48+
49+
void addFunction(StringRef name) {
50+
SILFunction *silFn = IGM.getSILModule().lookUpFunction(name);
51+
// Definitions for each SIL function should have been emitted by SILGen.
52+
assert(silFn && "missing SIL function?");
53+
if (silFn) {
54+
Addrs.emplace_back(IGM.getAddrOfSILFunction(silFn, NotForDefinition));
55+
}
56+
}
57+
58+
public:
59+
HasSymbolIRGenVisitor(IRGenModule &IGM,
60+
llvm::SmallVector<llvm::Constant *, 4> &Addrs)
61+
: IGM{IGM}, Addrs{Addrs} {};
62+
63+
void addFunction(SILDeclRef declRef) override {
64+
addFunction(declRef.mangle());
65+
}
66+
67+
void addFunction(StringRef name, SILDeclRef declRef) override {
68+
addFunction(name);
69+
}
70+
71+
void addGlobalVar(VarDecl *VD) override {
72+
// FIXME: Handle global vars
73+
llvm::report_fatal_error("unhandled global var");
74+
}
75+
76+
void addLinkEntity(LinkEntity entity) override {
77+
auto constant = getAddrOfLLVMVariable(IGM, entity);
78+
auto global = cast<llvm::GlobalValue>(constant);
79+
Addrs.emplace_back(global);
80+
}
81+
82+
void addProtocolWitnessThunk(RootProtocolConformance *C,
83+
ValueDecl *requirementDecl) override {
84+
// FIXME: Handle protocol witness thunks
85+
llvm::report_fatal_error("unhandled protocol witness thunk");
86+
}
87+
};
88+
89+
void IRGenModule::emitHasSymbolFunctions() {
90+
SILSymbolVisitorOptions opts;
91+
opts.VisitMembers = false;
92+
auto silCtx = SILSymbolVisitorContext(getSwiftModule(), opts);
93+
auto linkInfo = UniversalLinkageInfo(*this);
94+
auto symbolVisitorCtx = IRSymbolVisitorContext(linkInfo, silCtx);
95+
96+
for (ValueDecl *decl : getSILModule().getHasSymbolDecls()) {
97+
PrettyStackTraceDecl trace("emitting #_hasSymbol query for", decl);
98+
Mangle::ASTMangler mangler;
99+
100+
auto func = cast<llvm::Function>(getOrCreateHelperFunction(
101+
mangler.mangleHasSymbolQuery(decl), Int1Ty, {},
102+
[decl, this, symbolVisitorCtx](IRGenFunction &IGF) {
103+
auto &Builder = IGF.Builder;
104+
llvm::SmallVector<llvm::Constant *, 4> addrs;
105+
HasSymbolIRGenVisitor(*this, addrs).visit(decl, symbolVisitorCtx);
106+
107+
llvm::Value *ret = nullptr;
108+
for (llvm::Constant *addr : addrs) {
109+
assert(cast<llvm::GlobalValue>(addr)->hasExternalWeakLinkage());
110+
111+
auto isNonNull = IGF.Builder.CreateIsNotNull(addr);
112+
ret = (ret) ? IGF.Builder.CreateAnd(ret, isNonNull) : isNonNull;
113+
}
114+
115+
if (ret) {
116+
Builder.CreateRet(ret);
117+
} else {
118+
// There were no addresses produced by the visitor, return true.
119+
Builder.CreateRet(llvm::ConstantInt::get(Int1Ty, 1));
120+
}
121+
},
122+
/*IsNoInline*/ false));
123+
124+
func->setDoesNotThrow();
125+
func->setCallingConv(SwiftCC);
126+
func->addFnAttr(llvm::Attribute::ReadOnly);
127+
}
128+
}
129+
130+
void IRGenerator::emitHasSymbolFunctions() {
131+
for (auto &IGM : *this)
132+
IGM.second->emitHasSymbolFunctions();
133+
}

lib/IRGen/IRGen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,9 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
13481348
// Okay, emit any definitions that we suddenly need.
13491349
irgen.emitLazyDefinitions();
13501350

1351+
// Emit functions supporting `if #_hasSymbol(...)` conditions.
1352+
IGM.emitHasSymbolFunctions();
1353+
13511354
// Register our info with the runtime if needed.
13521355
if (Opts.UseJIT) {
13531356
IGM.emitBuiltinReflectionMetadata();

lib/IRGen/IRGenModule.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,9 @@ class IRGenerator {
432432
/// Emit coverage mapping info.
433433
void emitCoverageMapping();
434434

435+
/// Emit helper functions for `if #_hasSymbol(...)` conditions.
436+
void emitHasSymbolFunctions();
437+
435438
/// Checks if metadata for this type can be emitted lazily. This is true for
436439
/// non-public types as well as imported types, except for classes and
437440
/// protocols which are always emitted eagerly.
@@ -1738,6 +1741,14 @@ private: \
17381741
ConstantReference
17391742
getAddrOfLLVMVariableOrGOTEquivalent(LinkEntity entity);
17401743

1744+
llvm::Constant *getAddrOfLLVMVariable(LinkEntity entity,
1745+
ConstantInit definition,
1746+
DebugTypeInfo debugType,
1747+
llvm::Type *overrideDeclType = nullptr);
1748+
llvm::Constant *getAddrOfLLVMVariable(LinkEntity entity,
1749+
ForDefinition_t forDefinition,
1750+
DebugTypeInfo debugType);
1751+
17411752
llvm::Constant *emitRelativeReference(ConstantReference target,
17421753
llvm::GlobalValue *base,
17431754
ArrayRef<unsigned> baseIndices);
@@ -1790,14 +1801,7 @@ private: \
17901801
getAddrOfSharedContextDescriptor(LinkEntity entity,
17911802
ConstantInit definition,
17921803
llvm::function_ref<void()> emit);
1793-
1794-
llvm::Constant *getAddrOfLLVMVariable(LinkEntity entity,
1795-
ConstantInit definition,
1796-
DebugTypeInfo debugType,
1797-
llvm::Type *overrideDeclType = nullptr);
1798-
llvm::Constant *getAddrOfLLVMVariable(LinkEntity entity,
1799-
ForDefinition_t forDefinition,
1800-
DebugTypeInfo debugType);
1804+
18011805
ConstantReference getAddrOfLLVMVariable(LinkEntity entity,
18021806
ConstantInit definition,
18031807
DebugTypeInfo debugType,
@@ -1826,6 +1830,7 @@ private: \
18261830
void emitRuntimeRegistration();
18271831
void emitVTableStubs();
18281832
void emitTypeVerifier();
1833+
void emitHasSymbolFunctions();
18291834

18301835
/// Create llvm metadata which encodes the branch weights given by
18311836
/// \p TrueCount and \p FalseCount.

lib/IRGen/Linking.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -540,13 +540,28 @@ std::string LinkEntity::mangleAsString() const {
540540
llvm_unreachable("bad entity kind!");
541541
}
542542

543-
SILDeclRef LinkEntity::getSILDeclRef() const {
544-
assert(getKind() == Kind::DistributedThunkAsyncFunctionPointer ||
545-
getKind() == Kind::AsyncFunctionPointerAST);
543+
SILDeclRef::Kind LinkEntity::getSILDeclRefKind() const {
544+
switch (getKind()) {
545+
case Kind::DispatchThunk:
546+
case Kind::MethodDescriptor:
547+
return SILDeclRef::Kind::Func;
548+
case Kind::DispatchThunkInitializer:
549+
case Kind::MethodDescriptorInitializer:
550+
return SILDeclRef::Kind::Initializer;
551+
case Kind::MethodDescriptorAllocator:
552+
case Kind::DispatchThunkAllocator:
553+
return SILDeclRef::Kind::Allocator;
554+
case Kind::DistributedThunkAsyncFunctionPointer:
555+
case Kind::AsyncFunctionPointerAST:
556+
return static_cast<SILDeclRef::Kind>(
557+
reinterpret_cast<uintptr_t>(SecondaryPointer));
558+
default:
559+
llvm_unreachable("unhandled kind");
560+
}
561+
}
546562

547-
return SILDeclRef(const_cast<ValueDecl *>(getDecl()),
548-
static_cast<SILDeclRef::Kind>(
549-
reinterpret_cast<uintptr_t>(SecondaryPointer)));
563+
SILDeclRef LinkEntity::getSILDeclRef() const {
564+
return SILDeclRef(const_cast<ValueDecl *>(getDecl()), getSILDeclRefKind());
550565
}
551566

552567
SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
public let global: Int = 0
2+
3+
public func function(with argument: Int) {}
4+
public func throwingFunc() throws {}
5+
public func genericFunc<T: P>(_ t: T) {}
6+
public func funcWithOpaqueResult() -> some P { return S(member: 0) }
7+
@_cdecl("cdecl_func") public func cdeclFunc() {}
8+
@_silgen_name("forward_declared_func") public func forwardDeclaredFunc()
9+
10+
public protocol P {
11+
func requirement()
12+
}
13+
14+
public struct S {
15+
public var member: Int
16+
17+
public init(member: Int) {
18+
self.member = member
19+
}
20+
public func method(with argument: Int) {}
21+
}
22+
23+
extension S: P {
24+
public func requirement() {}
25+
}
26+
27+
public class C {
28+
public init() {}
29+
public func method(with argument: Int) {}
30+
}
31+
32+
public enum E {
33+
case basicCase
34+
case payloadCase(_: S)
35+
}

test/IRGen/has_symbol.swift

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol_helper.swift -enable-library-evolution -disable-availability-checking
3+
// RUN: %target-swift-frontend -emit-irgen %s -I %t -module-name test | %FileCheck %s
4+
5+
// REQUIRES: VENDOR=apple
6+
7+
@_weakLinked import has_symbol_helper
8+
9+
func testGlobalFunctions() {
10+
if #_hasSymbol(function(with:)) {}
11+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper8function4withySi_tFTwS"()
12+
// CHECK: ret i1 icmp ne (void (i64)* @"$s17has_symbol_helper8function4withySi_tF", void (i64)* null)
13+
14+
if #_hasSymbol(throwingFunc) {}
15+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper12throwingFuncyyKFTwS"()
16+
// CHECK: ret i1 icmp ne (void (%swift.refcounted*, %swift.error**)* @"$s17has_symbol_helper12throwingFuncyyKF", void (%swift.refcounted*, %swift.error**)* null)
17+
18+
if #_hasSymbol(genericFunc(_:) as (S) -> Void) {}
19+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper11genericFuncyyxAA1PRzlFTwS"()
20+
// CHECK: ret i1 icmp ne (void (%swift.opaque*, %swift.type*, i8**)* @"$s17has_symbol_helper11genericFuncyyxAA1PRzlF", void (%swift.opaque*, %swift.type*, i8**)* null)
21+
22+
if #_hasSymbol(funcWithOpaqueResult) {}
23+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper20funcWithOpaqueResultQryFTwS"()
24+
// CHECK: ret i1 and (i1 icmp ne (%swift.type_descriptor* @"$s17has_symbol_helper20funcWithOpaqueResultQryFQOMQ", %swift.type_descriptor* null), i1 icmp ne (void (%swift.opaque*)* @"$s17has_symbol_helper20funcWithOpaqueResultQryF", void (%swift.opaque*)* null))
25+
26+
if #_hasSymbol(cdeclFunc) {}
27+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper9cdeclFuncyyFTwS"()
28+
// CHECK: ret i1 and (i1 icmp ne (void ()* @"$s17has_symbol_helper9cdeclFuncyyF", void ()* null), i1 icmp ne (void ()* @cdecl_func, void ()* null))
29+
30+
if #_hasSymbol(forwardDeclaredFunc) {}
31+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper19forwardDeclaredFuncyyFTwS"()
32+
// CHECK: ret i1 icmp ne (void ()* @forward_declared_func, void ()* null)
33+
34+
// FIXME: Test `dynamic` functions
35+
// FIXME: Test `dynamic` functions with opaque return types
36+
}
37+
38+
func testVars() {
39+
if #_hasSymbol(global) {}
40+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper6globalSivpTwS"()
41+
// CHECK: ret i1 icmp ne (i64 ()* @"$s17has_symbol_helper6globalSivg", i64 ()* null)
42+
}
43+
44+
func testClass(_ c: C) {
45+
if #_hasSymbol(C.init) {}
46+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1CCACycfcTwS"()
47+
// CHECK: ret i1 and (i1 icmp ne (%T17has_symbol_helper1CC* (%T17has_symbol_helper1CC*)* @"$s17has_symbol_helper1CCACycfc", %T17has_symbol_helper1CC* (%T17has_symbol_helper1CC*)* null), i1 icmp ne (%T17has_symbol_helper1CC* (%swift.type*)* @"$s17has_symbol_helper1CCACycfC", %T17has_symbol_helper1CC* (%swift.type*)* null))
48+
49+
if #_hasSymbol(c.method(with:)) {}
50+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1CC6method4withySi_tFTwS"()
51+
// CHECK: ret i1 and (i1 icmp ne (void (i64, %T17has_symbol_helper1CC*)* @"$s17has_symbol_helper1CC6method4withySi_tFTj", void (i64, %T17has_symbol_helper1CC*)* null), i1 icmp ne (%swift.method_descriptor* @"$s17has_symbol_helper1CC6method4withySi_tFTq", %swift.method_descriptor* null))
52+
}
53+
54+
func testStruct(_ s: S) {
55+
if #_hasSymbol(s.member) {}
56+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1SV6memberSivpTwS"()
57+
// CHECK: ret i1 and (i1 and (i1 and (i1 icmp ne (%swift.type_descriptor* @"$s17has_symbol_helper1SV6memberSivpMV", %swift.type_descriptor* null), i1 icmp ne (i64 (%swift.opaque*)* @"$s17has_symbol_helper1SV6memberSivg", i64 (%swift.opaque*)* null)), i1 icmp ne (void (i64, %swift.opaque*)* @"$s17has_symbol_helper1SV6memberSivs", void (i64, %swift.opaque*)* null)), i1 icmp ne ({ i8*, %TSi* } (i8*, %swift.opaque*)* @"$s17has_symbol_helper1SV6memberSivM", { i8*, %TSi* } (i8*, %swift.opaque*)* null))
58+
59+
if #_hasSymbol(s.method(with:)) {}
60+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1SV6method4withySi_tFTwS"()
61+
// CHECK: ret i1 icmp ne (void (i64, %swift.opaque*)* @"$s17has_symbol_helper1SV6method4withySi_tF", void (i64, %swift.opaque*)* null)
62+
}
63+
64+
func testEnum(_ e: E) {
65+
if #_hasSymbol(E.basicCase) {}
66+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1EO9basicCaseyA2CmFTwS"()
67+
// CHECK: ret i1 icmp ne (i32* @"$s17has_symbol_helper1EO9basicCaseyA2CmFWC", i32* null)
68+
69+
if #_hasSymbol(E.payloadCase) {}
70+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1EO11payloadCaseyAcA1SVcACmFTwS"()
71+
// CHECK: ret i1 icmp ne (i32* @"$s17has_symbol_helper1EO11payloadCaseyAcA1SVcACmFWC", i32* null)
72+
}
73+
74+
func testMetatypes() {
75+
if #_hasSymbol(S.self) {}
76+
// CHECK: define linkonce_odr hidden swiftcc i1 @"$s17has_symbol_helper1SVTwS"()
77+
// CHECK: ret i1 and (i1 and (i1 icmp ne (%swift.type_descriptor* @"$s17has_symbol_helper1SVMn", %swift.type_descriptor* null), i1 icmp ne (%swift.type* @"$s17has_symbol_helper1SVN", %swift.type* null)), i1 icmp ne (%swift.metadata_response (i64)* @"$s17has_symbol_helper1SVMa", %swift.metadata_response (i64)* null))
78+
}

0 commit comments

Comments
 (0)