diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 0a433d5ab5ff8..f9a544ff642ac 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -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-.yaml" in SearchPathOpts.RuntimeLibraryPath. /// Passing this flag completely disables this behavior. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 0dc7a34f64d00..19178f191f4fb 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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; } 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..b20861595f5a4 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,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); @@ -91,6 +113,9 @@ class ConstantAggregateBuilderBase void addRelativeAddress(llvm::Constant *target) { assert(!isa(target)); + assert((!IGM().getOptions().IndirectRelativeFunctionPointer || + !isa(target)) && + "use addRelativeFunctionAddress"); addRelativeOffset(IGM().RelativeAddressTy, target); } diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index f25f5fe2b6299..fa711b1f665f4 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.emitLoadOfRelativeFunctionPointer( Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false, /*expectedType*/ functionPointer.getFunctionType()->getPointerTo()); } @@ -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()) { diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 09db8318c6827..2ab4f9667f7bf 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.addRelativeFunctionAddress( 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..d61d7c9f8b89d 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, @@ -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: { @@ -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)); @@ -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)); @@ -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)); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index bd0769132214d..223c3e15f373e 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.addRelativeFunctionAddress(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.addRelativeFunctionAddress(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.addRelativeFunctionAddressOrNull(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.addRelativeFunctionAddressOrNull(completionFunction); } bool needsForeignMetadataCompletionFunction() { @@ -1402,7 +1410,7 @@ namespace { // Completion function. auto completionFunction = IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition); - B.addRelativeAddress(completionFunction); + B.addRelativeFunctionAddress(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.addRelativeFunctionAddress(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.addRelativeFunctionAddress(function); } } }; @@ -2723,7 +2731,7 @@ namespace { void addInstantiationFunction() { auto function = IGM.getAddrOfTypeMetadataInstantiationFunction(Target, NotForDefinition); - B.addRelativeAddress(function); + B.addRelativeFunctionAddress(function); } void addCompletionFunction() { @@ -2734,7 +2742,7 @@ namespace { auto function = IGM.getAddrOfTypeMetadataCompletionFunction(Target, NotForDefinition); - B.addRelativeAddress(function); + B.addRelativeFunctionAddress(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.addRelativeFunctionAddressOrNull(function ? *function : nullptr); } void addIVarDestroyer() { @@ -3547,7 +3555,7 @@ namespace { /*isDestroyer=*/ true, /*isForeign=*/ false, NotForDefinition); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addRelativeFunctionAddressOrNull(function ? *function : nullptr); } void addClassFlags() { @@ -3663,7 +3671,7 @@ namespace { void addDestructorFunction() { auto function = getAddrOfDestructorFunction(IGM, Target); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addRelativeFunctionAddressOrNull(function ? *function : nullptr); } void addIVarDestroyer() { @@ -3671,7 +3679,7 @@ namespace { /*isDestroyer=*/ true, /*isForeign=*/ false, NotForDefinition); - B.addRelativeAddressOrNull(function ? *function : nullptr); + B.addRelativeFunctionAddressOrNull(function ? *function : nullptr); } bool hasExtraDataPattern() { @@ -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(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; +} diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 7a47eb1df41e0..7d562aba77244 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.addRelativeFunctionAddress(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.addRelativeFunctionAddressOrNull(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..b944b8e43d77b 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.addRelativeFunctionAddress(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.addRelativeFunctionAddress(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.addRelativeFunctionAddress(accessor); // And a null terminator! S.addInt(Int8Ty, 0); diff --git a/lib/IRGen/IRGenFunction.cpp b/lib/IRGen/IRGenFunction.cpp index 0e5ce215103c0..2e133e645833f 100644 --- a/lib/IRGen/IRGenFunction.cpp +++ b/lib/IRGen/IRGenFunction.cpp @@ -337,6 +337,19 @@ IRGenFunction::emitLoadOfRelativePointer(Address addr, bool isFar, return pointer.getAddress(); } +llvm::Value *IRGenFunction::emitLoadOfRelativeFunctionPointer( + Address addr, bool isFar, llvm::PointerType *expectedType, + const llvm::Twine &name) { + if (IGM.getOptions().IndirectRelativeFunctionPointer) { + llvm::Value *value = emitLoadOfRelativePointer( + Address(addr.getAddress(), IGM.getPointerAlignment()), isFar, + expectedType->getPointerTo(), name); + return Builder.CreateLoad(value, addr.getAlignment()); + } 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..fa401047d541f 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 * + emitLoadOfRelativeFunctionPointer(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/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index f0047cc1c050f..36645b2173439 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1118,6 +1118,7 @@ class IRGenModule { llvm::DenseMap IndirectAsyncFunctionPointers; llvm::DenseMap GlobalGOTEquivalents; llvm::DenseMap GlobalFuncs; + llvm::DenseMap IndirectRelativeFunctionPointers; llvm::DenseSet GlobalClangDecls; llvm::StringMap> GlobalStrings; @@ -1294,6 +1295,8 @@ class IRGenModule { llvm::Constant *getAssociatedTypeWitness(Type type, GenericSignature sig, bool inProtocolContext); + llvm::Constant *getAddrOfIndirectFunctionPointer(llvm::Function *fn); + void emitAssociatedTypeMetadataRecord(const RootProtocolConformance *C); void emitFieldDescriptor(const NominalTypeDecl *Decl); diff --git a/test/IRGen/harvard-arch-relative-func-ptr.swift b/test/IRGen/harvard-arch-relative-func-ptr.swift new file mode 100644 index 0000000000000..11ef2db462a55 --- /dev/null +++ b/test/IRGen/harvard-arch-relative-func-ptr.swift @@ -0,0 +1,13 @@ +// RUN: %swift -target wasm32-unknown-wasi -parse-stdlib -emit-ir -o - %s | %FileCheck %s + +// REQUIRES: CODEGENERATOR=WebAssembly + +// Ensure that relative function pointer in entry_point is indirect on harvard archs +// +// CHECK: @indirect.main = private unnamed_addr constant i32 (i32, i8*)* @main +// CHECK: @"\01l_entry_point" = private constant { i32 } { +// CHECK-SAME: i32 sub ( +// CHECK-SAME: i32 ptrtoint (i32 (i32, i8*)** @indirect.main to i32), +// CHECK-SAME: i32 ptrtoint ({ i32 }* @"\01l_entry_point" to i32) +// CHECK-SAME: ) +// CHECK-SAME: }, section "swift5_entry", align 4