Skip to content

Commit 43a25e8

Browse files
[Wasm][IRGen] Add initial support for absolute function pointer
On some Harvard architectures like WebAssembly that allow sliding code and data address space offsets independently, it's impossible to make direct relative reference to code from data because the relative offset between them is not representable. Use absolute function references instead of relative ones on such targets.
1 parent 66999ce commit 43a25e8

13 files changed

+120
-47
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ class IRGenOptions {
320320
unsigned LazyInitializeProtocolConformances : 1;
321321
unsigned IndirectAsyncFunctionPointer : 1;
322322

323+
/// Use absolute function references instead of relative ones.
324+
/// Mainly used on WebAssembly, that doesn't support relative references
325+
/// to code from data.
326+
unsigned CompactAbsoluteFunctionPointer : 1;
327+
323328
/// Normally if the -read-legacy-type-info flag is not specified, we look for
324329
/// a file named "legacy-<arch>.yaml" in SearchPathOpts.RuntimeLibraryPath.
325330
/// Passing this flag completely disables this behavior.

lib/Frontend/CompilerInvocation.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,6 +2153,14 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
21532153
// AsyncFunctionPointer access.
21542154
Opts.IndirectAsyncFunctionPointer = Triple.isOSBinFormatCOFF();
21552155

2156+
// On some Harvard architectures that allow sliding code and data address space
2157+
// offsets independently, it's impossible to make direct relative reference to
2158+
// code from data because the relative offset between them is not representable.
2159+
// Use absolute function references instead of relative ones on such targets.
2160+
// TODO(katei): This is a short-term solution until the WebAssembly target stabilizes
2161+
// PIC and 64-bit specifications and toolchain support.
2162+
Opts.CompactAbsoluteFunctionPointer = Triple.isOSBinFormatWasm();
2163+
21562164
if (Args.hasArg(OPT_disable_legacy_type_info)) {
21572165
Opts.DisableLegacyTypeInfo = true;
21582166
}

lib/IRGen/ConformanceDescription.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class ConformanceDescription {
5353

5454
/// The instantiation function, to be run at the end of witness table
5555
/// instantiation.
56-
llvm::Constant *instantiationFn = nullptr;
56+
llvm::Function *instantiationFn = nullptr;
5757

5858
/// The resilient witnesses, if any.
5959
SmallVector<llvm::Constant *, 4> resilientWitnesses;

lib/IRGen/ConstantBuilder.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//===----------------------------------------------------------------------===//
1515

1616
#include "swift/ABI/MetadataValues.h"
17+
#include "swift/AST/IRGenOptions.h"
1718
#include "llvm/IR/Constants.h"
1819
#include "llvm/IR/DerivedTypes.h"
1920
#include "llvm/IR/GlobalVariable.h"
@@ -81,6 +82,26 @@ class ConstantAggregateBuilderBase
8182

8283
void addSize(Size size) { addInt(IGM().SizeTy, size.getValue()); }
8384

85+
void addCompactFunctionReferenceOrNull(llvm::Function *function) {
86+
if (function) {
87+
addCompactFunctionReference(function);
88+
} else {
89+
addInt(IGM().RelativeAddressTy, 0);
90+
}
91+
}
92+
93+
/// Add a 32-bit function reference to the given function. The reference
94+
/// is direct relative pointer whenever possible. Otherwise, it is a
95+
/// absolute pointer assuming the function address is 32-bit.
96+
void addCompactFunctionReference(llvm::Function *function) {
97+
if (IGM().getOptions().CompactAbsoluteFunctionPointer) {
98+
// Assume that the function address is 32-bit.
99+
add(llvm::ConstantExpr::getPtrToInt(function, IGM().RelativeAddressTy));
100+
} else {
101+
addRelativeOffset(IGM().RelativeAddressTy, function);
102+
}
103+
}
104+
84105
void addRelativeAddressOrNull(llvm::Constant *target) {
85106
if (target) {
86107
addRelativeAddress(target);
@@ -91,6 +112,8 @@ class ConstantAggregateBuilderBase
91112

92113
void addRelativeAddress(llvm::Constant *target) {
93114
assert(!isa<llvm::ConstantPointerNull>(target));
115+
assert((!IGM().getOptions().CompactAbsoluteFunctionPointer ||
116+
!isa<llvm::Function>(target)) && "use addCompactFunctionReference");
94117
addRelativeOffset(IGM().RelativeAddressTy, target);
95118
}
96119

lib/IRGen/GenCall.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,7 +2086,7 @@ std::pair<llvm::Value *, llvm::Value *> irgen::getAsyncFunctionAndSize(
20862086
llvm::Value *addrPtr = IGF.Builder.CreateStructGEP(
20872087
getAFPPtr()->getType()->getScalarType()->getPointerElementType(),
20882088
getAFPPtr(), 0);
2089-
fn = IGF.emitLoadOfRelativePointer(
2089+
fn = IGF.emitLoadOfCompactFunctionPointer(
20902090
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
20912091
/*expectedType*/ functionPointer.getFunctionType()->getPointerTo());
20922092
}
@@ -4955,7 +4955,7 @@ llvm::Value *FunctionPointer::getPointer(IRGenFunction &IGF) const {
49554955
auto *addrPtr = IGF.Builder.CreateStructGEP(
49564956
descriptorPtr->getType()->getScalarType()->getPointerElementType(),
49574957
descriptorPtr, 0);
4958-
auto *result = IGF.emitLoadOfRelativePointer(
4958+
auto *result = IGF.emitLoadOfCompactFunctionPointer(
49594959
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
49604960
/*expectedType*/ getFunctionType()->getPointerTo());
49614961
if (auto codeAuthInfo = AuthInfo.getCorrespondingCodeAuthInfo()) {

lib/IRGen/GenDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2063,7 +2063,7 @@ void IRGenerator::emitEntryPointInfo() {
20632063
auto &IGM = *getGenModule(entrypoint);
20642064
ConstantInitBuilder builder(IGM);
20652065
auto entrypointInfo = builder.beginStruct();
2066-
entrypointInfo.addRelativeAddress(
2066+
entrypointInfo.addCompactFunctionReference(
20672067
IGM.getAddrOfSILFunction(entrypoint, NotForDefinition));
20682068
auto var = entrypointInfo.finishAndCreateGlobal(
20692069
"\x01l_entry_point", Alignment(4),

lib/IRGen/GenKeyPath.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ getAccessorForComputedComponent(IRGenModule &IGM,
268268
return accessorThunk;
269269
}
270270

271-
static llvm::Constant *
271+
static llvm::Function *
272272
getLayoutFunctionForComputedComponent(IRGenModule &IGM,
273273
const KeyPathPatternComponent &component,
274274
GenericEnvironment *genericEnv,
@@ -548,7 +548,7 @@ struct KeyPathIndexOperand {
548548
const KeyPathPatternComponent *LastUser;
549549
};
550550

551-
static llvm::Constant *
551+
static llvm::Function *
552552
getInitializerForComputedComponent(IRGenModule &IGM,
553553
const KeyPathPatternComponent &component,
554554
ArrayRef<KeyPathIndexOperand> operands,
@@ -1101,12 +1101,12 @@ emitKeyPathComponent(IRGenModule &IGM,
11011101
}
11021102

11031103
// Push the accessors, possibly thunked to marshal generic environment.
1104-
fields.addRelativeAddress(
1104+
fields.addCompactFunctionReference(
11051105
getAccessorForComputedComponent(IGM, component, Getter,
11061106
genericEnv, requirements,
11071107
hasSubscriptIndices));
11081108
if (settable)
1109-
fields.addRelativeAddress(
1109+
fields.addCompactFunctionReference(
11101110
getAccessorForComputedComponent(IGM, component, Setter,
11111111
genericEnv, requirements,
11121112
hasSubscriptIndices));
@@ -1116,7 +1116,7 @@ emitKeyPathComponent(IRGenModule &IGM,
11161116
// arguments in the component. Thunk the SIL-level accessors to give the
11171117
// runtime implementation a polymorphically-callable interface.
11181118

1119-
fields.addRelativeAddress(
1119+
fields.addCompactFunctionReference(
11201120
getLayoutFunctionForComputedComponent(IGM, component,
11211121
genericEnv, requirements));
11221122

@@ -1136,7 +1136,7 @@ emitKeyPathComponent(IRGenModule &IGM,
11361136

11371137
// Add an initializer function that copies generic arguments out of the
11381138
// pattern argument buffer into the instantiated object.
1139-
fields.addRelativeAddress(
1139+
fields.addCompactFunctionReference(
11401140
getInitializerForComputedComponent(IGM, component, operands,
11411141
genericEnv, requirements));
11421142
}

lib/IRGen/GenMeta.cpp

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -302,13 +302,13 @@ static void buildMethodDescriptorFields(IRGenModule &IGM,
302302
assert(entry->getKind() == SILVTable::Entry::Kind::Normal);
303303

304304
auto *impl = entry->getImplementation();
305-
llvm::Constant *implFn;
306-
if (impl->isAsync())
307-
implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
308-
else
309-
implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
310-
311-
descriptor.addRelativeAddress(implFn);
305+
if (impl->isAsync()) {
306+
llvm::Constant *implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
307+
descriptor.addRelativeAddress(implFn);
308+
} else {
309+
llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
310+
descriptor.addCompactFunctionReference(implFn);
311+
}
312312
} else {
313313
// The method is removed by dead method elimination.
314314
// It should be never called. We add a pointer to an error function.
@@ -962,7 +962,15 @@ namespace {
962962
reqt.addInt32(info.Flags.getIntValue());
963963

964964
// Default implementation.
965-
reqt.addRelativeAddressOrNull(info.DefaultImpl);
965+
if (info.DefaultImpl) {
966+
if (auto *fn = llvm::dyn_cast<llvm::Function>(info.DefaultImpl)) {
967+
reqt.addCompactFunctionReference(fn);
968+
} else {
969+
reqt.addRelativeAddress(info.DefaultImpl);
970+
}
971+
} else {
972+
reqt.addRelativeAddressOrNull(nullptr);
973+
}
966974

967975
reqt.finishAndAddTo(B);
968976
}
@@ -1226,7 +1234,7 @@ namespace {
12261234
}
12271235

12281236
void addAccessFunction() {
1229-
llvm::Constant *accessor;
1237+
llvm::Function *accessor;
12301238

12311239
// Don't include an access function if we're emitting the context
12321240
// descriptor without metadata.
@@ -1247,7 +1255,7 @@ namespace {
12471255
accessor = getOtherwiseDefinedTypeMetadataAccessFunction(IGM, type);
12481256
}
12491257

1250-
B.addRelativeAddressOrNull(accessor);
1258+
B.addCompactFunctionReferenceOrNull(accessor);
12511259
}
12521260

12531261
ConstantReference getParent() {
@@ -1376,12 +1384,12 @@ namespace {
13761384

13771385
/// Add a ForeignMetadataInitialization structure to the descriptor.
13781386
void addForeignMetadataInitialization() {
1379-
llvm::Constant *completionFunction = nullptr;
1387+
llvm::Function *completionFunction = nullptr;
13801388
if (asImpl().needsForeignMetadataCompletionFunction()) {
13811389
completionFunction =
13821390
IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition);
13831391
}
1384-
B.addRelativeAddressOrNull(completionFunction);
1392+
B.addCompactFunctionReferenceOrNull(completionFunction);
13851393
}
13861394

13871395
bool needsForeignMetadataCompletionFunction() {
@@ -1402,7 +1410,7 @@ namespace {
14021410
// Completion function.
14031411
auto completionFunction =
14041412
IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition);
1405-
B.addRelativeAddress(completionFunction);
1413+
B.addCompactFunctionReference(completionFunction);
14061414
}
14071415

14081416
void addIncompleteMetadata() {
@@ -1905,13 +1913,13 @@ namespace {
19051913
assert(entry->getKind() == SILVTable::Entry::Kind::Override);
19061914

19071915
auto *impl = entry->getImplementation();
1908-
llvm::Constant *implFn;
1909-
if (impl->isAsync())
1910-
implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
1911-
else
1912-
implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
1913-
1914-
descriptor.addRelativeAddress(implFn);
1916+
if (impl->isAsync()) {
1917+
llvm::Constant *implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
1918+
descriptor.addRelativeAddress(implFn);
1919+
} else {
1920+
llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
1921+
descriptor.addCompactFunctionReference(implFn);
1922+
}
19151923
} else {
19161924
// The method is removed by dead method elimination.
19171925
// It should be never called. We add a pointer to an error function.
@@ -2005,7 +2013,7 @@ namespace {
20052013
}
20062014
auto specialization = pair.first;
20072015
auto *function = IGM.getAddrOfCanonicalSpecializedGenericTypeMetadataAccessFunction(specialization, NotForDefinition);
2008-
B.addRelativeAddress(function);
2016+
B.addCompactFunctionReference(function);
20092017
}
20102018
}
20112019
};
@@ -2723,7 +2731,7 @@ namespace {
27232731
void addInstantiationFunction() {
27242732
auto function = IGM.getAddrOfTypeMetadataInstantiationFunction(Target,
27252733
NotForDefinition);
2726-
B.addRelativeAddress(function);
2734+
B.addCompactFunctionReference(function);
27272735
}
27282736

27292737
void addCompletionFunction() {
@@ -2734,7 +2742,7 @@ namespace {
27342742

27352743
auto function = IGM.getAddrOfTypeMetadataCompletionFunction(Target,
27362744
NotForDefinition);
2737-
B.addRelativeAddress(function);
2745+
B.addCompactFunctionReference(function);
27382746
}
27392747

27402748
void addPatternFlags() {
@@ -2935,7 +2943,7 @@ static void emitClassMetadataBaseOffset(IRGenModule &IGM,
29352943
offsetVar->setConstant(true);
29362944
}
29372945

2938-
static Optional<llvm::Constant *>
2946+
static Optional<llvm::Function *>
29392947
getAddrOfDestructorFunction(IRGenModule &IGM, ClassDecl *classDecl) {
29402948
auto dtorRef = SILDeclRef(classDecl->getDestructor(),
29412949
SILDeclRef::Kind::Deallocator);
@@ -3539,15 +3547,15 @@ namespace {
35393547

35403548
void addDestructorFunction() {
35413549
auto function = getAddrOfDestructorFunction(IGM, Target);
3542-
B.addRelativeAddressOrNull(function ? *function : nullptr);
3550+
B.addCompactFunctionReferenceOrNull(function ? *function : nullptr);
35433551
}
35443552

35453553
void addIVarDestroyer() {
35463554
auto function = IGM.getAddrOfIVarInitDestroy(Target,
35473555
/*isDestroyer=*/ true,
35483556
/*isForeign=*/ false,
35493557
NotForDefinition);
3550-
B.addRelativeAddressOrNull(function ? *function : nullptr);
3558+
B.addCompactFunctionReferenceOrNull(function ? *function : nullptr);
35513559
}
35523560

35533561
void addClassFlags() {
@@ -3663,15 +3671,15 @@ namespace {
36633671

36643672
void addDestructorFunction() {
36653673
auto function = getAddrOfDestructorFunction(IGM, Target);
3666-
B.addRelativeAddressOrNull(function ? *function : nullptr);
3674+
B.addCompactFunctionReferenceOrNull(function ? *function : nullptr);
36673675
}
36683676

36693677
void addIVarDestroyer() {
36703678
auto function = IGM.getAddrOfIVarInitDestroy(Target,
36713679
/*isDestroyer=*/ true,
36723680
/*isForeign=*/ false,
36733681
NotForDefinition);
3674-
B.addRelativeAddressOrNull(function ? *function : nullptr);
3682+
B.addCompactFunctionReferenceOrNull(function ? *function : nullptr);
36753683
}
36763684

36773685
bool hasExtraDataPattern() {
@@ -5614,7 +5622,7 @@ llvm::GlobalValue *irgen::emitAsyncFunctionPointer(IRGenModule &IGM,
56145622
ConstantInitBuilder initBuilder(IGM);
56155623
ConstantStructBuilder builder(
56165624
initBuilder.beginStruct(IGM.AsyncFunctionPointerTy));
5617-
builder.addRelativeAddress(function);
5625+
builder.addCompactFunctionReference(function);
56185626
builder.addInt32(size.getValue());
56195627
return cast<llvm::GlobalValue>(IGM.defineAsyncFunctionPointer(
56205628
entity, builder.finishAndCreateFuture()));

lib/IRGen/GenProto.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ class AccessorConformanceInfo : public ConformanceInfo {
14721472

14731473
/// Build the instantiation function that runs at the end of witness
14741474
/// table specialization.
1475-
llvm::Constant *buildInstantiationFunction();
1475+
llvm::Function *buildInstantiationFunction();
14761476
};
14771477

14781478
/// A resilient witness table consists of a list of descriptor/witness pairs,
@@ -1741,7 +1741,7 @@ void ResilientWitnessTableBuilder::collectResilientWitnesses(
17411741
}
17421742
}
17431743

1744-
llvm::Constant *FragileWitnessTableBuilder::buildInstantiationFunction() {
1744+
llvm::Function *FragileWitnessTableBuilder::buildInstantiationFunction() {
17451745
// We need an instantiation function if any base conformance
17461746
// is non-dependent.
17471747
if (SpecializedBaseConformances.empty())
@@ -1962,7 +1962,12 @@ namespace {
19621962
}
19631963

19641964
// Add the witness.
1965-
B.addRelativeAddress(witnesses.front());
1965+
llvm::Constant *witness = witnesses.front();
1966+
if (auto *fn = llvm::dyn_cast<llvm::Function>(witness)) {
1967+
B.addCompactFunctionReference(fn);
1968+
} else {
1969+
B.addRelativeAddress(witness);
1970+
}
19661971
witnesses = witnesses.drop_front();
19671972
}
19681973
assert(witnesses.empty() && "Wrong # of resilient witnesses");
@@ -1979,7 +1984,7 @@ namespace {
19791984
(Description.witnessTablePrivateSize << 1) |
19801985
Description.requiresSpecialization);
19811986
// Instantiation function
1982-
B.addRelativeAddressOrNull(Description.instantiationFn);
1987+
B.addCompactFunctionReferenceOrNull(Description.instantiationFn);
19831988

19841989
// Private data
19851990
if (IGM.IRGen.Opts.NoPreallocatedInstantiationCaches) {
@@ -2255,7 +2260,7 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) {
22552260

22562261
unsigned tableSize = 0;
22572262
llvm::GlobalVariable *global = nullptr;
2258-
llvm::Constant *instantiationFunction = nullptr;
2263+
llvm::Function *instantiationFunction = nullptr;
22592264
bool isDependent = isDependentConformance(conf);
22602265
SmallVector<llvm::Constant *, 4> resilientWitnesses;
22612266

0 commit comments

Comments
 (0)