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 +