From 43a25e89b796acdfd906dad054385703f80bd614 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 30 Mar 2022 05:13:31 +0000 Subject: [PATCH] [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. --- include/swift/AST/IRGenOptions.h | 5 ++ lib/Frontend/CompilerInvocation.cpp | 8 +++ lib/IRGen/ConformanceDescription.h | 2 +- lib/IRGen/ConstantBuilder.h | 23 +++++++++ lib/IRGen/GenCall.cpp | 4 +- lib/IRGen/GenDecl.cpp | 2 +- lib/IRGen/GenKeyPath.cpp | 12 ++--- lib/IRGen/GenMeta.cpp | 66 ++++++++++++++----------- lib/IRGen/GenProto.cpp | 15 ++++-- lib/IRGen/GenReflection.cpp | 6 +-- lib/IRGen/IRGenFunction.cpp | 14 ++++++ lib/IRGen/IRGenFunction.h | 4 ++ test/IRGen/wasm-absolute-func-ptr.swift | 6 +++ 13 files changed, 120 insertions(+), 47 deletions(-) create mode 100644 test/IRGen/wasm-absolute-func-ptr.swift diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 0a433d5ab5ff8..2fa5e98a1e41b 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -320,6 +320,11 @@ class IRGenOptions { unsigned LazyInitializeProtocolConformances : 1; unsigned IndirectAsyncFunctionPointer : 1; + /// Use absolute function references instead of relative ones. + /// Mainly used on WebAssembly, that doesn't support relative references + /// to code from data. + unsigned CompactAbsoluteFunctionPointer : 1; + /// Normally if the -read-legacy-type-info flag is not specified, we look for /// a file named "legacy-.yaml" in SearchPathOpts.RuntimeLibraryPath. /// Passing this flag completely disables this behavior. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 75a7f4a01e36c..c133d42bb615e 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2153,6 +2153,14 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, // AsyncFunctionPointer access. Opts.IndirectAsyncFunctionPointer = Triple.isOSBinFormatCOFF(); + // On some Harvard architectures 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. + // TODO(katei): This is a short-term solution until the WebAssembly target stabilizes + // PIC and 64-bit specifications and toolchain support. + Opts.CompactAbsoluteFunctionPointer = Triple.isOSBinFormatWasm(); + if (Args.hasArg(OPT_disable_legacy_type_info)) { Opts.DisableLegacyTypeInfo = true; } diff --git a/lib/IRGen/ConformanceDescription.h b/lib/IRGen/ConformanceDescription.h index 028429d455303..4a0406b09a131 100644 --- a/lib/IRGen/ConformanceDescription.h +++ b/lib/IRGen/ConformanceDescription.h @@ -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 resilientWitnesses; diff --git a/lib/IRGen/ConstantBuilder.h b/lib/IRGen/ConstantBuilder.h index ffca625d8236d..8708520df90f0 100644 --- a/lib/IRGen/ConstantBuilder.h +++ b/lib/IRGen/ConstantBuilder.h @@ -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" @@ -81,6 +82,26 @@ class ConstantAggregateBuilderBase void addSize(Size size) { addInt(IGM().SizeTy, size.getValue()); } + void addCompactFunctionReferenceOrNull(llvm::Function *function) { + if (function) { + addCompactFunctionReference(function); + } else { + addInt(IGM().RelativeAddressTy, 0); + } + } + + /// Add a 32-bit function reference to the given function. The reference + /// is direct relative pointer whenever possible. Otherwise, it is a + /// absolute pointer assuming the function address is 32-bit. + void addCompactFunctionReference(llvm::Function *function) { + if (IGM().getOptions().CompactAbsoluteFunctionPointer) { + // Assume that the function address is 32-bit. + add(llvm::ConstantExpr::getPtrToInt(function, IGM().RelativeAddressTy)); + } else { + addRelativeOffset(IGM().RelativeAddressTy, function); + } + } + void addRelativeAddressOrNull(llvm::Constant *target) { if (target) { addRelativeAddress(target); @@ -91,6 +112,8 @@ class ConstantAggregateBuilderBase void addRelativeAddress(llvm::Constant *target) { assert(!isa(target)); + assert((!IGM().getOptions().CompactAbsoluteFunctionPointer || + !isa(target)) && "use addCompactFunctionReference"); addRelativeOffset(IGM().RelativeAddressTy, target); } diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 7eef31841a60b..5f22f4c2ac849 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -2086,7 +2086,7 @@ std::pair irgen::getAsyncFunctionAndSize( llvm::Value *addrPtr = IGF.Builder.CreateStructGEP( getAFPPtr()->getType()->getScalarType()->getPointerElementType(), getAFPPtr(), 0); - fn = IGF.emitLoadOfRelativePointer( + fn = IGF.emitLoadOfCompactFunctionPointer( Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false, /*expectedType*/ functionPointer.getFunctionType()->getPointerTo()); } @@ -4955,7 +4955,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.emitLoadOfCompactFunctionPointer( Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false, /*expectedType*/ getFunctionType()->getPointerTo()); if (auto codeAuthInfo = AuthInfo.getCorrespondingCodeAuthInfo()) { diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 09db8318c6827..6cc624fab3bad 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2063,7 +2063,7 @@ void IRGenerator::emitEntryPointInfo() { auto &IGM = *getGenModule(entrypoint); ConstantInitBuilder builder(IGM); auto entrypointInfo = builder.beginStruct(); - entrypointInfo.addRelativeAddress( + entrypointInfo.addCompactFunctionReference( IGM.getAddrOfSILFunction(entrypoint, NotForDefinition)); auto var = entrypointInfo.finishAndCreateGlobal( "\x01l_entry_point", Alignment(4), diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 03a66fdf7bcb9..af1a22ab8db98 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -268,7 +268,7 @@ getAccessorForComputedComponent(IRGenModule &IGM, return accessorThunk; } -static llvm::Constant * +static llvm::Function * getLayoutFunctionForComputedComponent(IRGenModule &IGM, const KeyPathPatternComponent &component, GenericEnvironment *genericEnv, @@ -548,7 +548,7 @@ struct KeyPathIndexOperand { const KeyPathPatternComponent *LastUser; }; -static llvm::Constant * +static llvm::Function * getInitializerForComputedComponent(IRGenModule &IGM, const KeyPathPatternComponent &component, ArrayRef operands, @@ -1101,12 +1101,12 @@ emitKeyPathComponent(IRGenModule &IGM, } // Push the accessors, possibly thunked to marshal generic environment. - fields.addRelativeAddress( + fields.addCompactFunctionReference( getAccessorForComputedComponent(IGM, component, Getter, genericEnv, requirements, hasSubscriptIndices)); if (settable) - fields.addRelativeAddress( + fields.addCompactFunctionReference( getAccessorForComputedComponent(IGM, component, Setter, genericEnv, requirements, hasSubscriptIndices)); @@ -1116,7 +1116,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.addCompactFunctionReference( getLayoutFunctionForComputedComponent(IGM, component, genericEnv, requirements)); @@ -1136,7 +1136,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.addCompactFunctionReference( getInitializerForComputedComponent(IGM, component, operands, genericEnv, requirements)); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index bd0769132214d..1c080833ff517 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -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.addCompactFunctionReference(implFn); + } } else { // The method is removed by dead method elimination. // It should be never called. We add a pointer to an error function. @@ -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(info.DefaultImpl)) { + reqt.addCompactFunctionReference(fn); + } else { + reqt.addRelativeAddress(info.DefaultImpl); + } + } else { + reqt.addRelativeAddressOrNull(nullptr); + } reqt.finishAndAddTo(B); } @@ -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. @@ -1247,7 +1255,7 @@ namespace { accessor = getOtherwiseDefinedTypeMetadataAccessFunction(IGM, type); } - B.addRelativeAddressOrNull(accessor); + B.addCompactFunctionReferenceOrNull(accessor); } ConstantReference getParent() { @@ -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.addCompactFunctionReferenceOrNull(completionFunction); } bool needsForeignMetadataCompletionFunction() { @@ -1402,7 +1410,7 @@ namespace { // Completion function. auto completionFunction = IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition); - B.addRelativeAddress(completionFunction); + B.addCompactFunctionReference(completionFunction); } void addIncompleteMetadata() { @@ -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.addCompactFunctionReference(implFn); + } } else { // The method is removed by dead method elimination. // It should be never called. We add a pointer to an error function. @@ -2005,7 +2013,7 @@ namespace { } auto specialization = pair.first; auto *function = IGM.getAddrOfCanonicalSpecializedGenericTypeMetadataAccessFunction(specialization, NotForDefinition); - B.addRelativeAddress(function); + B.addCompactFunctionReference(function); } } }; @@ -2723,7 +2731,7 @@ namespace { void addInstantiationFunction() { auto function = IGM.getAddrOfTypeMetadataInstantiationFunction(Target, NotForDefinition); - B.addRelativeAddress(function); + B.addCompactFunctionReference(function); } void addCompletionFunction() { @@ -2734,7 +2742,7 @@ namespace { auto function = IGM.getAddrOfTypeMetadataCompletionFunction(Target, NotForDefinition); - B.addRelativeAddress(function); + B.addCompactFunctionReference(function); } void addPatternFlags() { @@ -2935,7 +2943,7 @@ static void emitClassMetadataBaseOffset(IRGenModule &IGM, offsetVar->setConstant(true); } -static Optional +static Optional getAddrOfDestructorFunction(IRGenModule &IGM, ClassDecl *classDecl) { auto dtorRef = SILDeclRef(classDecl->getDestructor(), SILDeclRef::Kind::Deallocator); @@ -3539,7 +3547,7 @@ namespace { void addDestructorFunction() { auto function = getAddrOfDestructorFunction(IGM, Target); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addCompactFunctionReferenceOrNull(function ? *function : nullptr); } void addIVarDestroyer() { @@ -3547,7 +3555,7 @@ namespace { /*isDestroyer=*/ true, /*isForeign=*/ false, NotForDefinition); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addCompactFunctionReferenceOrNull(function ? *function : nullptr); } void addClassFlags() { @@ -3663,7 +3671,7 @@ namespace { void addDestructorFunction() { auto function = getAddrOfDestructorFunction(IGM, Target); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addCompactFunctionReferenceOrNull(function ? *function : nullptr); } void addIVarDestroyer() { @@ -3671,7 +3679,7 @@ namespace { /*isDestroyer=*/ true, /*isForeign=*/ false, NotForDefinition); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addCompactFunctionReferenceOrNull(function ? *function : nullptr); } bool hasExtraDataPattern() { @@ -5614,7 +5622,7 @@ llvm::GlobalValue *irgen::emitAsyncFunctionPointer(IRGenModule &IGM, ConstantInitBuilder initBuilder(IGM); ConstantStructBuilder builder( initBuilder.beginStruct(IGM.AsyncFunctionPointerTy)); - builder.addRelativeAddress(function); + builder.addCompactFunctionReference(function); builder.addInt32(size.getValue()); return cast(IGM.defineAsyncFunctionPointer( entity, builder.finishAndCreateFuture())); diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 7a47eb1df41e0..c66719c12b7e7 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1472,7 +1472,7 @@ class AccessorConformanceInfo : public ConformanceInfo { /// Build the instantiation function that runs at the end of witness /// table specialization. - llvm::Constant *buildInstantiationFunction(); + llvm::Function *buildInstantiationFunction(); }; /// A resilient witness table consists of a list of descriptor/witness pairs, @@ -1741,7 +1741,7 @@ void ResilientWitnessTableBuilder::collectResilientWitnesses( } } -llvm::Constant *FragileWitnessTableBuilder::buildInstantiationFunction() { +llvm::Function *FragileWitnessTableBuilder::buildInstantiationFunction() { // We need an instantiation function if any base conformance // is non-dependent. if (SpecializedBaseConformances.empty()) @@ -1962,7 +1962,12 @@ namespace { } // Add the witness. - B.addRelativeAddress(witnesses.front()); + llvm::Constant *witness = witnesses.front(); + if (auto *fn = llvm::dyn_cast(witness)) { + B.addCompactFunctionReference(fn); + } else { + B.addRelativeAddress(witness); + } witnesses = witnesses.drop_front(); } assert(witnesses.empty() && "Wrong # of resilient witnesses"); @@ -1979,7 +1984,7 @@ namespace { (Description.witnessTablePrivateSize << 1) | Description.requiresSpecialization); // Instantiation function - B.addRelativeAddressOrNull(Description.instantiationFn); + B.addCompactFunctionReferenceOrNull(Description.instantiationFn); // Private data if (IGM.IRGen.Opts.NoPreallocatedInstantiationCaches) { @@ -2255,7 +2260,7 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) { unsigned tableSize = 0; llvm::GlobalVariable *global = nullptr; - llvm::Constant *instantiationFunction = nullptr; + llvm::Function *instantiationFunction = nullptr; bool isDependent = isDependentConformance(conf); SmallVector resilientWitnesses; diff --git a/lib/IRGen/GenReflection.cpp b/lib/IRGen/GenReflection.cpp index 7343f75f28bac..b05cea28867e6 100644 --- a/lib/IRGen/GenReflection.cpp +++ b/lib/IRGen/GenReflection.cpp @@ -281,7 +281,7 @@ getTypeRefByFunction(IRGenModule &IGM, S.setPacked(true); S.add(llvm::ConstantInt::get(IGM.Int8Ty, 255)); S.add(llvm::ConstantInt::get(IGM.Int8Ty, 9)); - S.addRelativeAddress(accessor); + S.addCompactFunctionReference(accessor); // And a null terminator! S.addInt(IGM.Int8Ty, 0); @@ -438,7 +438,7 @@ IRGenModule::emitWitnessTableRefString(CanType type, S.setPacked(true); S.add(llvm::ConstantInt::get(Int8Ty, 255)); S.add(llvm::ConstantInt::get(Int8Ty, 9)); - S.addRelativeAddress(accessorThunk); + S.addCompactFunctionReference(accessorThunk); // And a null terminator! S.addInt(Int8Ty, 0); @@ -482,7 +482,7 @@ llvm::Constant *IRGenModule::getMangledAssociatedConformance( S.setPacked(true); S.add(llvm::ConstantInt::get(Int8Ty, 255)); S.add(llvm::ConstantInt::get(Int8Ty, kind)); - S.addRelativeAddress(accessor); + S.addCompactFunctionReference(accessor); // And a null terminator! S.addInt(Int8Ty, 0); diff --git a/lib/IRGen/IRGenFunction.cpp b/lib/IRGen/IRGenFunction.cpp index 0e5ce215103c0..460283ddee362 100644 --- a/lib/IRGen/IRGenFunction.cpp +++ b/lib/IRGen/IRGenFunction.cpp @@ -337,6 +337,20 @@ IRGenFunction::emitLoadOfRelativePointer(Address addr, bool isFar, return pointer.getAddress(); } +llvm::Value * +IRGenFunction::emitLoadOfCompactFunctionPointer(Address addr, bool isFar, + llvm::PointerType *expectedType, + const llvm::Twine &name) { + if (IGM.getOptions().CompactAbsoluteFunctionPointer) { + llvm::Value *value = Builder.CreateLoad(addr); + auto *uncastPointer = Builder.CreateIntToPtr(value, IGM.Int8PtrTy); + auto pointer = Builder.CreateBitCast(Address(uncastPointer, IGM.getPointerAlignment()), expectedType); + return pointer.getAddress(); + } else { + return emitLoadOfRelativePointer(addr, isFar, expectedType, name); + } +} + llvm::Value * IRGenFunction::emitLoadOfRelativeIndirectablePointer(Address addr, bool isFar, diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 0cda72ba7c7ed..af7d74392348c 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -266,6 +266,10 @@ class IRGenFunction { llvm::Value *emitLoadOfRelativePointer(Address addr, bool isFar, llvm::PointerType *expectedType, const llvm::Twine &name = ""); + llvm::Value * + emitLoadOfCompactFunctionPointer(Address addr, bool isFar, + llvm::PointerType *expectedType, + const llvm::Twine &name = ""); llvm::Value *emitAllocObjectCall(llvm::Value *metadata, llvm::Value *size, llvm::Value *alignMask, diff --git a/test/IRGen/wasm-absolute-func-ptr.swift b/test/IRGen/wasm-absolute-func-ptr.swift new file mode 100644 index 0000000000000..52767ea78acaf --- /dev/null +++ b/test/IRGen/wasm-absolute-func-ptr.swift @@ -0,0 +1,6 @@ +// RUN: %swift -target wasm32-unknown-wasi -parse-stdlib -emit-ir -o - %s | %FileCheck %s + +// REQUIRES: CODEGENERATOR=WebAssembly + +// CHECK: @"\01l_entry_point" = private constant { i32 } { i32 ptrtoint (i32 (i32, i8*)* @main to i32) }, section "swift5_entry", align 4 +