Skip to content

Commit ae0f98d

Browse files
committed
IRGen: Ensure collocation of relative pointers in resilient witness tables
Ensure collocation by recording the dependence between witness table and witness before functions are processed. Debug info of inlined function scopes can reference the witness and will cause the wrong IRGenModule to be associated before lazy witness tables are processed. No, I am not sure that this is the only instance of this but the same solution can apply to other instances if we find them. rdar://39116991
1 parent 369f19d commit ae0f98d

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)