Skip to content

IRGen: Use indirect relative func-ptr for harvard archs #41995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ class IRGenOptions {
unsigned LazyInitializeProtocolConformances : 1;
unsigned IndirectAsyncFunctionPointer : 1;

/// Force relative function pointer to be indirect
/// Used on harvard architectures like WebAssembly
unsigned IndirectRelativeFunctionPointer : 1;

/// Normally if the -read-legacy-type-info flag is not specified, we look for
/// a file named "legacy-<arch>.yaml" in SearchPathOpts.RuntimeLibraryPath.
/// Passing this flag completely disables this behavior.
Expand Down
6 changes: 6 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,12 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
// AsyncFunctionPointer access.
Opts.IndirectAsyncFunctionPointer = Triple.isOSBinFormatCOFF();

// Harvard architectures cannot make direct relative function pointer
// because the referent function and the metadata are in different
// address spaces, and the relative offset between them is not representable.
// So we always use indirect relative function pointer for those.
Opts.IndirectRelativeFunctionPointer = Triple.isOSBinFormatWasm();

if (Args.hasArg(OPT_disable_legacy_type_info)) {
Opts.DisableLegacyTypeInfo = true;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/ConformanceDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class ConformanceDescription {

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

/// The resilient witnesses, if any.
SmallVector<llvm::Constant *, 4> resilientWitnesses;
Expand Down
25 changes: 25 additions & 0 deletions lib/IRGen/ConstantBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//===----------------------------------------------------------------------===//

#include "swift/ABI/MetadataValues.h"
#include "swift/AST/IRGenOptions.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
Expand Down Expand Up @@ -81,6 +82,27 @@ class ConstantAggregateBuilderBase

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

void addRelativeFunctionAddressOrNull(llvm::Function *function) {
if (function) {
addRelativeFunctionAddress(function);
} else {
addInt(IGM().RelativeAddressTy, 0);
}
}

/// Add a relative function reference to the given function. The relative
/// address is direct when the address spaces of the code and the building
/// constant are the same. Otherwise, on harvard architectures, the relative
/// offset between the field and the referent function cannot be computed,
/// so the relative address is indirect.
void addRelativeFunctionAddress(llvm::Function *function) {
if (IGM().getOptions().IndirectRelativeFunctionPointer) {
addRelativeAddress(IGM().getAddrOfIndirectFunctionPointer(function));
} else {
addRelativeOffset(IGM().RelativeAddressTy, function);
}
}

void addRelativeAddressOrNull(llvm::Constant *target) {
if (target) {
addRelativeAddress(target);
Expand All @@ -91,6 +113,9 @@ class ConstantAggregateBuilderBase

void addRelativeAddress(llvm::Constant *target) {
assert(!isa<llvm::ConstantPointerNull>(target));
assert((!IGM().getOptions().IndirectRelativeFunctionPointer ||
!isa<llvm::Function>(target)) &&
"use addRelativeFunctionAddress");
addRelativeOffset(IGM().RelativeAddressTy, target);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/IRGen/GenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,7 @@ std::pair<llvm::Value *, llvm::Value *> irgen::getAsyncFunctionAndSize(
llvm::Value *addrPtr = IGF.Builder.CreateStructGEP(
getAFPPtr()->getType()->getScalarType()->getPointerElementType(),
getAFPPtr(), 0);
fn = IGF.emitLoadOfRelativePointer(
fn = IGF.emitLoadOfRelativeFunctionPointer(
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
/*expectedType*/ functionPointer.getFunctionType()->getPointerTo());
}
Expand Down Expand Up @@ -4952,7 +4952,7 @@ llvm::Value *FunctionPointer::getPointer(IRGenFunction &IGF) const {
auto *addrPtr = IGF.Builder.CreateStructGEP(
descriptorPtr->getType()->getScalarType()->getPointerElementType(),
descriptorPtr, 0);
auto *result = IGF.emitLoadOfRelativePointer(
auto *result = IGF.emitLoadOfRelativeFunctionPointer(
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
/*expectedType*/ getFunctionType()->getPointerTo());
if (auto codeAuthInfo = AuthInfo.getCorrespondingCodeAuthInfo()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2063,7 +2063,7 @@ void IRGenerator::emitEntryPointInfo() {
auto &IGM = *getGenModule(entrypoint);
ConstantInitBuilder builder(IGM);
auto entrypointInfo = builder.beginStruct();
entrypointInfo.addRelativeAddress(
entrypointInfo.addRelativeFunctionAddress(
IGM.getAddrOfSILFunction(entrypoint, NotForDefinition));
auto var = entrypointInfo.finishAndCreateGlobal(
"\x01l_entry_point", Alignment(4),
Expand Down
35 changes: 22 additions & 13 deletions lib/IRGen/GenKeyPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ getAccessorForComputedComponent(IRGenModule &IGM,
return accessorThunk;
}

static llvm::Constant *
static llvm::Function *
getLayoutFunctionForComputedComponent(IRGenModule &IGM,
const KeyPathPatternComponent &component,
GenericEnvironment *genericEnv,
Expand Down Expand Up @@ -548,7 +548,7 @@ struct KeyPathIndexOperand {
const KeyPathPatternComponent *LastUser;
};

static llvm::Constant *
static llvm::Function *
getInitializerForComputedComponent(IRGenModule &IGM,
const KeyPathPatternComponent &component,
ArrayRef<KeyPathIndexOperand> operands,
Expand Down Expand Up @@ -948,13 +948,22 @@ emitKeyPathComponent(IRGenModule &IGM,
// FIXME: Does this need to be signed?
auto idRef = IGM.getAddrOfLLVMVariableOrGOTEquivalent(
LinkEntity::forSILFunction(id.getFunction()));

idValue = idRef.getValue();
// If we got an indirect reference, we'll need to resolve it at
// instantiation time.
idResolution = idRef.isIndirect()
? KeyPathComponentHeader::IndirectPointer
: KeyPathComponentHeader::Resolved;

if (IGM.getOptions().IndirectRelativeFunctionPointer &&
!idRef.isIndirect()) {
// Force the function pointer to be indirect for harvard architecture
// even though it can be referenceable directly.
idValue = IGM.getAddrOfIndirectFunctionPointer(
IGM.getAddrOfSILFunction(id.getFunction(), NotForDefinition));
idResolution = KeyPathComponentHeader::IndirectPointer;
} else {
idValue = idRef.getValue();
// If we got an indirect reference, we'll need to resolve it at
// instantiation time.
idResolution = idRef.isIndirect()
? KeyPathComponentHeader::IndirectPointer
: KeyPathComponentHeader::Resolved;
}
break;
}
case KeyPathPatternComponent::ComputedPropertyId::DeclRef: {
Expand Down Expand Up @@ -1101,12 +1110,12 @@ emitKeyPathComponent(IRGenModule &IGM,
}

// Push the accessors, possibly thunked to marshal generic environment.
fields.addRelativeAddress(
fields.addRelativeFunctionAddress(
getAccessorForComputedComponent(IGM, component, Getter,
genericEnv, requirements,
hasSubscriptIndices));
if (settable)
fields.addRelativeAddress(
fields.addRelativeFunctionAddress(
getAccessorForComputedComponent(IGM, component, Setter,
genericEnv, requirements,
hasSubscriptIndices));
Expand All @@ -1116,7 +1125,7 @@ emitKeyPathComponent(IRGenModule &IGM,
// arguments in the component. Thunk the SIL-level accessors to give the
// runtime implementation a polymorphically-callable interface.

fields.addRelativeAddress(
fields.addRelativeFunctionAddress(
getLayoutFunctionForComputedComponent(IGM, component,
genericEnv, requirements));

Expand All @@ -1136,7 +1145,7 @@ emitKeyPathComponent(IRGenModule &IGM,

// Add an initializer function that copies generic arguments out of the
// pattern argument buffer into the instantiated object.
fields.addRelativeAddress(
fields.addRelativeFunctionAddress(
getInitializerForComputedComponent(IGM, component, operands,
genericEnv, requirements));
}
Expand Down
89 changes: 60 additions & 29 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,13 @@ static void buildMethodDescriptorFields(IRGenModule &IGM,
assert(entry->getKind() == SILVTable::Entry::Kind::Normal);

auto *impl = entry->getImplementation();
llvm::Constant *implFn;
if (impl->isAsync())
implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
else
implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);

descriptor.addRelativeAddress(implFn);
if (impl->isAsync()) {
llvm::Constant *implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
descriptor.addRelativeAddress(implFn);
} else {
llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
descriptor.addRelativeFunctionAddress(implFn);
}
} else {
// The method is removed by dead method elimination.
// It should be never called. We add a pointer to an error function.
Expand Down Expand Up @@ -962,7 +962,15 @@ namespace {
reqt.addInt32(info.Flags.getIntValue());

// Default implementation.
reqt.addRelativeAddressOrNull(info.DefaultImpl);
if (info.DefaultImpl) {
if (auto *fn = llvm::dyn_cast<llvm::Function>(info.DefaultImpl)) {
reqt.addRelativeFunctionAddress(fn);
} else {
reqt.addRelativeAddress(info.DefaultImpl);
}
} else {
reqt.addRelativeAddressOrNull(nullptr);
}

reqt.finishAndAddTo(B);
}
Expand Down Expand Up @@ -1226,7 +1234,7 @@ namespace {
}

void addAccessFunction() {
llvm::Constant *accessor;
llvm::Function *accessor;

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

B.addRelativeAddressOrNull(accessor);
B.addRelativeFunctionAddressOrNull(accessor);
}

ConstantReference getParent() {
Expand Down Expand Up @@ -1376,12 +1384,12 @@ namespace {

/// Add a ForeignMetadataInitialization structure to the descriptor.
void addForeignMetadataInitialization() {
llvm::Constant *completionFunction = nullptr;
llvm::Function *completionFunction = nullptr;
if (asImpl().needsForeignMetadataCompletionFunction()) {
completionFunction =
IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition);
}
B.addRelativeAddressOrNull(completionFunction);
B.addRelativeFunctionAddressOrNull(completionFunction);
}

bool needsForeignMetadataCompletionFunction() {
Expand All @@ -1402,7 +1410,7 @@ namespace {
// Completion function.
auto completionFunction =
IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition);
B.addRelativeAddress(completionFunction);
B.addRelativeFunctionAddress(completionFunction);
}

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

auto *impl = entry->getImplementation();
llvm::Constant *implFn;
if (impl->isAsync())
implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
else
implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);

descriptor.addRelativeAddress(implFn);
if (impl->isAsync()) {
llvm::Constant *implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
descriptor.addRelativeAddress(implFn);
} else {
llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
descriptor.addRelativeFunctionAddress(implFn);
}
} else {
// The method is removed by dead method elimination.
// It should be never called. We add a pointer to an error function.
Expand Down Expand Up @@ -2005,7 +2013,7 @@ namespace {
}
auto specialization = pair.first;
auto *function = IGM.getAddrOfCanonicalSpecializedGenericTypeMetadataAccessFunction(specialization, NotForDefinition);
B.addRelativeAddress(function);
B.addRelativeFunctionAddress(function);
}
}
};
Expand Down Expand Up @@ -2723,7 +2731,7 @@ namespace {
void addInstantiationFunction() {
auto function = IGM.getAddrOfTypeMetadataInstantiationFunction(Target,
NotForDefinition);
B.addRelativeAddress(function);
B.addRelativeFunctionAddress(function);
}

void addCompletionFunction() {
Expand All @@ -2734,7 +2742,7 @@ namespace {

auto function = IGM.getAddrOfTypeMetadataCompletionFunction(Target,
NotForDefinition);
B.addRelativeAddress(function);
B.addRelativeFunctionAddress(function);
}

void addPatternFlags() {
Expand Down Expand Up @@ -2935,7 +2943,7 @@ static void emitClassMetadataBaseOffset(IRGenModule &IGM,
offsetVar->setConstant(true);
}

static Optional<llvm::Constant *>
static Optional<llvm::Function *>
getAddrOfDestructorFunction(IRGenModule &IGM, ClassDecl *classDecl) {
auto dtorRef = SILDeclRef(classDecl->getDestructor(),
SILDeclRef::Kind::Deallocator);
Expand Down Expand Up @@ -3539,15 +3547,15 @@ namespace {

void addDestructorFunction() {
auto function = getAddrOfDestructorFunction(IGM, Target);
B.addRelativeAddressOrNull(function ? *function : nullptr);
B.addRelativeFunctionAddressOrNull(function ? *function : nullptr);
}

void addIVarDestroyer() {
auto function = IGM.getAddrOfIVarInitDestroy(Target,
/*isDestroyer=*/ true,
/*isForeign=*/ false,
NotForDefinition);
B.addRelativeAddressOrNull(function ? *function : nullptr);
B.addRelativeFunctionAddressOrNull(function ? *function : nullptr);
}

void addClassFlags() {
Expand Down Expand Up @@ -3663,15 +3671,15 @@ namespace {

void addDestructorFunction() {
auto function = getAddrOfDestructorFunction(IGM, Target);
B.addRelativeAddressOrNull(function ? *function : nullptr);
B.addRelativeFunctionAddressOrNull(function ? *function : nullptr);
}

void addIVarDestroyer() {
auto function = IGM.getAddrOfIVarInitDestroy(Target,
/*isDestroyer=*/ true,
/*isForeign=*/ false,
NotForDefinition);
B.addRelativeAddressOrNull(function ? *function : nullptr);
B.addRelativeFunctionAddressOrNull(function ? *function : nullptr);
}

bool hasExtraDataPattern() {
Expand Down Expand Up @@ -5614,8 +5622,31 @@ llvm::GlobalValue *irgen::emitAsyncFunctionPointer(IRGenModule &IGM,
ConstantInitBuilder initBuilder(IGM);
ConstantStructBuilder builder(
initBuilder.beginStruct(IGM.AsyncFunctionPointerTy));
builder.addRelativeAddress(function);
builder.addRelativeFunctionAddress(function);
builder.addInt32(size.getValue());
return cast<llvm::GlobalValue>(IGM.defineAsyncFunctionPointer(
entity, builder.finishAndCreateFuture()));
}

/// Get or create an indirect function pointer that references the given
/// function.
///
/// Similar to \ref IRGenModule::getAddrOfLLVMVariableOrGOTEquivalent, but this
/// one always creates a new trampoline constant pointing to the function even
/// though it can be referenced directly. The indirect function pointer is used
/// to create a relative indirect function pointer for harvard architecture
/// targets.
llvm::Constant *
IRGenModule::getAddrOfIndirectFunctionPointer(llvm::Function *fn) {
auto &entry = IndirectRelativeFunctionPointers[fn];
if (entry) {
return IndirectRelativeFunctionPointers[fn];
}
auto *pointer = new llvm::GlobalVariable(
Module, fn->getType(),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, fn,
Twine("indirect.") + fn->getName());
pointer->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
entry = pointer;
return pointer;
}
Loading