Skip to content

Commit 1e8aae2

Browse files
Merge pull request #15793 from aschwaighofer/irgen_relative_pointers_in_resilient_witness_table
IRGen: Ensure collocation of relative pointers in resilient witness tables
2 parents 16e39ff + ae0f98d commit 1e8aae2

File tree

6 files changed

+80
-8
lines changed

6 files changed

+80
-8
lines changed

lib/IRGen/GenDecl.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) {
10481048
return f.getProfiler() && m.getOptions().EmitProfileCoverageMapping;
10491049
}
10501050

1051-
void IRGenerator::emitGlobalTopLevel() {
1051+
void IRGenerator::emitGlobalTopLevel(bool emitForParallelEmission) {
10521052
// Generate order numbers for the functions in the SIL module that
10531053
// correspond to definitions in the LLVM module.
10541054
unsigned nextOrderNumber = 0;
@@ -1058,6 +1058,13 @@ void IRGenerator::emitGlobalTopLevel() {
10581058
FunctionOrder.insert(std::make_pair(&silFn, nextOrderNumber++));
10591059
}
10601060

1061+
// Ensure that relative symbols are collocated in the same LLVM module.
1062+
for (SILWitnessTable &wt : PrimaryIGM->getSILModule().getWitnessTableList()) {
1063+
CurrentIGMPtr IGM = getGenModule(wt.getConformance()->getDeclContext());
1064+
if (emitForParallelEmission)
1065+
IGM->ensureRelativeSymbolCollocation(wt);
1066+
}
1067+
10611068
for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) {
10621069
Decl *decl = v.getDecl();
10631070
CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr);

lib/IRGen/GenProto.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,10 +1832,6 @@ static llvm::Constant *emitResilientWitnessTable(IRGenModule &IGM,
18321832
SILFunction *Func = entry.getMethodWitness().Witness;
18331833
llvm::Constant *witness;
18341834
if (Func) {
1835-
// Force the thunk to be emitted in the current translation unit
1836-
// when in multi-threaded mode.
1837-
IGM.IRGen.forceLocalEmitOfLazyFunction(Func);
1838-
18391835
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
18401836
} else {
18411837
// The method is removed by dead method elimination.
@@ -2046,6 +2042,21 @@ llvm::Constant *WitnessTableBuilder::buildInstantiationFunction() {
20462042
return fn;
20472043
}
20482044

2045+
void IRGenModule::ensureRelativeSymbolCollocation(SILWitnessTable &wt) {
2046+
// Only resilient conformances use relative pointers for witness methods.
2047+
if (wt.isDeclaration() || isAvailableExternally(wt.getLinkage()) ||
2048+
!isResilientConformance(wt.getConformance()))
2049+
return;
2050+
2051+
for (auto &entry : wt.getEntries()) {
2052+
if (entry.getKind() != SILWitnessTable::Method)
2053+
continue;
2054+
auto *witness = entry.getMethodWitness().Witness;
2055+
if (witness)
2056+
IRGen.forceLocalEmitOfLazyFunction(witness);
2057+
}
2058+
}
2059+
20492060
/// Do a memoized witness-table layout for a protocol.
20502061
const ProtocolInfo &IRGenModule::getProtocolInfo(ProtocolDecl *protocol) {
20512062
return Types.getProtocolInfo(protocol);

lib/IRGen/IRGen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -933,8 +933,8 @@ static void performParallelIRGeneration(
933933
}
934934

935935
// Emit the module contents.
936-
irgen.emitGlobalTopLevel();
937-
936+
irgen.emitGlobalTopLevel(true /*emitForParallelEmission*/);
937+
938938
for (auto *File : M->getFiles()) {
939939
if (auto *SF = dyn_cast<SourceFile>(File)) {
940940
IRGenModule *IGM = irgen.getGenModule(SF);

lib/IRGen/IRGenModule.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,11 @@ class IRGenerator {
309309

310310
/// Emit functions, variables and tables which are needed anyway, e.g. because
311311
/// they are externally visible.
312-
void emitGlobalTopLevel();
312+
/// If \p emitForParallelEmission is true ensures that symbols that are
313+
/// expressed as relative pointers are collocated in the same output module
314+
/// with their base symbol. For example, witness methods need to be collocated
315+
/// with the witness table in the same LLVM module.
316+
void emitGlobalTopLevel(bool emitForParallelEmission = false);
313317

314318
/// Emit references to each of the protocol descriptors defined in this
315319
/// IR module.
@@ -1257,6 +1261,8 @@ private: \
12571261

12581262
void emitSharedContextDescriptor(DeclContext *dc);
12591263

1264+
void ensureRelativeSymbolCollocation(SILWitnessTable &wt);
1265+
12601266
private:
12611267
llvm::Constant *
12621268
getAddrOfSharedContextDescriptor(LinkEntity entity,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public func test() {
2+
inlineThis(Character("1"))
3+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -module-name test -num-threads 1 -O -c -g %S/Inputs/resilient-witness-2.swift %s -o %t/resilient-witness-2.o -o %t/resilient-witness.o
3+
// RUN: %target-swift-frontend -module-name test -num-threads 1 -O -emit-ir -g %S/Inputs/resilient-witness-2.swift %s -o %t/resilient-witness-2.ll -o %t/resilient-witness.ll
4+
// RUN: %FileCheck %s < %t/resilient-witness.ll
5+
6+
// We used to crash on this test case during code generation because the
7+
// resilient witness table and the witness of the equatable conformance was
8+
// emitted into different files. Because the witness is expressed as a relative
9+
// pointer this was problematic.
10+
11+
class Context {
12+
enum SomeEnum: Int {
13+
case One = 1
14+
case MinusOne = -1
15+
16+
init?(_ delimiter: Character) {
17+
switch delimiter {
18+
case "1":
19+
self = .One
20+
case "-":
21+
self = .MinusOne
22+
default:
23+
return nil
24+
}
25+
}
26+
}
27+
private struct SomeEnumStruct {
28+
var e: SomeEnum?
29+
}
30+
31+
private var arr: [SomeEnumStruct?] = []
32+
}
33+
34+
public func inlineThis(_ char: Character) {
35+
if Context.SomeEnum(char) == .MinusOne {
36+
print("foobar")
37+
}
38+
}
39+
40+
// The witness table and witness definition need to be in the same file.
41+
42+
// CHECK: @"$S4test7ContextC8SomeEnumOs9EquatableAAWr" = {{.*}}sub {{.*}} @"$S4test7ContextC8SomeEnumOs9EquatableAAsAFP2eeoiySbx_xtFZTW" {{.*}} @"$S4test7ContextC8SomeEnumOs9EquatableAAWr"
43+
44+
// CHECK: define{{.*}}@"$S4test7ContextC8SomeEnumOs9EquatableAAsAFP2eeoiySbx_xtFZTW"({{.*}}){{.*}} {
45+
// CHECK-NEXT: entry:

0 commit comments

Comments
 (0)