diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index d3721c2f9d3fb..561435b6a4f11 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -311,6 +311,7 @@ class IRGenOptions { /// Used on Windows to avoid cross-module references. unsigned LazyInitializeClassMetadata : 1; unsigned LazyInitializeProtocolConformances : 1; + unsigned IndirectAsyncFunctionPointer : 1; /// Normally if the -read-legacy-type-info flag is not specified, we look for /// a file named "legacy-.yaml" in SearchPathOpts.RuntimeLibraryPath. @@ -417,7 +418,8 @@ class IRGenOptions { ValueNames(false), EnableReflectionMetadata(true), EnableReflectionNames(true), EnableAnonymousContextMangledNames(false), ForcePublicLinkage(false), LazyInitializeClassMetadata(false), - LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false), + LazyInitializeProtocolConformances(false), + IndirectAsyncFunctionPointer(false), DisableLegacyTypeInfo(false), PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true), UseTypeLayoutValueHandling(true), ForceStructTypeLayouts(false), GenerateProfile(false), EnableDynamicReplacementChaining(false), diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 64a85a7d4e9ff..f72898773f3d7 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1876,6 +1876,11 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, // witness. Opts.LazyInitializeProtocolConformances = Triple.isOSBinFormatCOFF(); + // PE/COFF cannot deal with the cross-module reference to the + // AsyncFunctionPointer data block. Force the use of indirect + // AsyncFunctionPointer access. + Opts.IndirectAsyncFunctionPointer = Triple.isOSBinFormatCOFF(); + if (Args.hasArg(OPT_disable_legacy_type_info)) { Opts.DisableLegacyTypeInfo = true; } diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index 89ac45849607a..4dbcdeb177aee 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1986,6 +1986,46 @@ void IRGenFunction::emitAllExtractValues(llvm::Value *value, out.add(Builder.CreateExtractValue(value, i)); } +namespace { +// TODO(compnerd) analyze if this should be out-lined via a runtime call rather +// than be open-coded. This needs to account for the fact that we are able to +// statically optimize this often times due to CVP changing the select to a +// `select i1 true, ...`. +llvm::Value *emitIndirectAsyncFunctionPointer(IRGenFunction &IGF, + llvm::Value *pointer) { + llvm::IntegerType *IntPtrTy = IGF.IGM.IntPtrTy; + llvm::Type *AsyncFunctionPointerPtrTy = IGF.IGM.AsyncFunctionPointerPtrTy; + llvm::Constant *Zero = + llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(), + 0)); + llvm::Constant *One = + llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(), + 1)); + llvm::Constant *NegativeOne = + llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(), + -2)); + swift::irgen::Alignment PointerAlignment = IGF.IGM.getPointerAlignment(); + + llvm::Value *PtrToInt = IGF.Builder.CreatePtrToInt(pointer, IntPtrTy); + llvm::Value *And = IGF.Builder.CreateAnd(PtrToInt, One); + llvm::Value *ICmp = IGF.Builder.CreateICmpEQ(And, Zero); + + llvm::Value *BitCast = + IGF.Builder.CreateBitCast(pointer, AsyncFunctionPointerPtrTy); + + llvm::Value *UntaggedPointer = IGF.Builder.CreateAnd(PtrToInt, NegativeOne); + llvm::Value *IntToPtr = + IGF.Builder.CreateIntToPtr(UntaggedPointer, + AsyncFunctionPointerPtrTy->getPointerTo()); + llvm::Value *Load = IGF.Builder.CreateLoad(IntToPtr, PointerAlignment); + + // (select (icmp eq, (and (ptrtoint %AsyncFunctionPointer), 1), 0), + // (%AsyncFunctionPointer), + // (inttoptr (and (ptrtoint %AsyncFunctionPointer), -2))) + return IGF.Builder.CreateSelect(ICmp, BitCast, Load); +} +} + std::pair irgen::getAsyncFunctionAndSize( IRGenFunction &IGF, SILFunctionTypeRepresentation representation, FunctionPointer functionPointer, llvm::Value *thickContext, @@ -2007,9 +2047,11 @@ std::pair irgen::getAsyncFunctionAndSize( if (auto authInfo = functionPointer.getAuthInfo()) { ptr = emitPointerAuthAuth(IGF, ptr, authInfo); } - auto *afpPtr = - IGF.Builder.CreateBitCast(ptr, IGF.IGM.AsyncFunctionPointerPtrTy); - afpPtrValue = afpPtr; + afpPtrValue = + (IGF.IGM.getOptions().IndirectAsyncFunctionPointer) + ? emitIndirectAsyncFunctionPointer(IGF, ptr) + : IGF.Builder.CreateBitCast(ptr, + IGF.IGM.AsyncFunctionPointerPtrTy); } return *afpPtrValue; }; @@ -4831,6 +4873,8 @@ llvm::Value *FunctionPointer::getPointer(IRGenFunction &IGF) const { auto *fnPtr = Value; if (auto authInfo = AuthInfo) { fnPtr = emitPointerAuthAuth(IGF, fnPtr, authInfo); + if (IGF.IGM.getOptions().IndirectAsyncFunctionPointer) + fnPtr = emitIndirectAsyncFunctionPointer(IGF, fnPtr); } auto *descriptorPtr = IGF.Builder.CreateBitCast(fnPtr, IGF.IGM.AsyncFunctionPointerPtrTy); diff --git a/lib/IRGen/GenThunk.cpp b/lib/IRGen/GenThunk.cpp index 75ceb0cbffede..726d7d03a8fd2 100644 --- a/lib/IRGen/GenThunk.cpp +++ b/lib/IRGen/GenThunk.cpp @@ -344,9 +344,34 @@ void IRGenModule::emitDispatchThunk(SILDeclRef declRef) { llvm::Constant * IRGenModule::getAddrOfAsyncFunctionPointer(LinkEntity entity) { - return getAddrOfLLVMVariable( - LinkEntity::forAsyncFunctionPointer(entity), - NotForDefinition, DebugTypeInfo()); + llvm::Constant *Pointer = + getAddrOfLLVMVariable(LinkEntity::forAsyncFunctionPointer(entity), + NotForDefinition, DebugTypeInfo()); + if (!getOptions().IndirectAsyncFunctionPointer) + return Pointer; + + // When the symbol does not have DLL Import storage, we must directly address + // it. Otherwise, we will form an invalid reference. + if (!Pointer->isDLLImportDependent()) + return Pointer; + + llvm::Constant *PointerPointer = + getOrCreateGOTEquivalent(Pointer, + LinkEntity::forAsyncFunctionPointer(entity)); + llvm::Constant *PointerPointerConstant = + llvm::ConstantExpr::getPtrToInt(PointerPointer, IntPtrTy); + llvm::Constant *Marker = + llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(), + 1)); + // TODO(compnerd) ensure that the pointer alignment guarantees that bit-0 is + // cleared. We cannot use an `getOr` here as it does not form a relocatable + // expression. + llvm::Constant *Address = + llvm::ConstantExpr::getAdd(PointerPointerConstant, Marker); + + IndirectAsyncFunctionPointers[entity] = Address; + return llvm::ConstantExpr::getIntToPtr(Address, + AsyncFunctionPointerTy->getPointerTo()); } llvm::Constant * @@ -373,6 +398,15 @@ IRGenModule::getSILFunctionForAsyncFunctionPointer(llvm::Constant *afp) { return entity.getSILFunction(); } } + for (auto &entry : IndirectAsyncFunctionPointers) { + if (entry.getSecond() == afp) { + auto entity = entry.getFirst(); + assert(getOptions().IndirectAsyncFunctionPointer && + "indirect async function found for non-indirect async function" + " target?"); + return entity.getSILFunction(); + } + } return nullptr; } diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index b7713a8a62bf8..6d7f6357a79fe 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1105,6 +1105,7 @@ class IRGenModule { LinkEntity entity); llvm::DenseMap GlobalVars; + llvm::DenseMap IndirectAsyncFunctionPointers; llvm::DenseMap GlobalGOTEquivalents; llvm::DenseMap GlobalFuncs; llvm::DenseSet GlobalClangDecls; diff --git a/test/IRGen/async-inheritance.swift b/test/IRGen/async-inheritance.swift new file mode 100644 index 0000000000000..61e43077cc5c8 --- /dev/null +++ b/test/IRGen/async-inheritance.swift @@ -0,0 +1,33 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/%target-library-name(L)) -Xfrontend -disable-availability-checking -module-name L -emit-module -emit-module-path %t/L.swiftmodule %s -DL +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -I%t -L%t -lL -parse-as-library %s -module-name E -o %t/E %target-rpath(%t) +// RUN: %target-codesign %t/E +// RUN: %target-run %t/E %t/%target-library-name(L) | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: concurrency_runtime +// REQUIRES: executable_test + +// UNSUPPORTED: back_deployment_runtime + +#if L +open class C { + public init() {} + open func f() async { + print("\(#function)") + } +} +#else +import L +class D: C { +} + +@main +struct S { + public static func main() async { + await D().f() + } +} +#endif + +// CHECK: f() diff --git a/test/IRGen/async/class_resilience.swift b/test/IRGen/async/class_resilience.swift index 3a4c65646e13a..6e6e8df509024 100644 --- a/test/IRGen/async/class_resilience.swift +++ b/test/IRGen/async/class_resilience.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -disable-availability-checking -enable-library-evolution -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class %S/Inputs/resilient_class.swift -// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -enable-library-evolution %s | %FileCheck -check-prefix CHECK -check-prefix CHECK-%target-cpu -check-prefix CHECK-%target-import-type %s // REQUIRES: concurrency import resilient_class @@ -42,7 +42,11 @@ open class MyBaseClass { // CHECK-SAME: %swift.async_func_pointer* @"$s16class_resilience9MyDerivedC4waitSiyYaF010resilient_A09BaseClassCADxyYaFTVTu" // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swift{{(tail)?}}cc void @"$s16class_resilience14callsAwaitableyx010resilient_A09BaseClassCyxGYalF"(%swift.opaque* noalias nocapture %0, %swift.context* swiftasync %1{{.*}}) -// CHECK: %swift.async_func_pointer* @"$s15resilient_class9BaseClassC4waitxyYaFTjTu" +// CHECK-DIRECT: %swift.async_func_pointer* @"$s15resilient_class9BaseClassC4waitxyYaFTjTu" +// CHECK-INDIRECT: [[LOAD:%[0-9]+]] = load %swift.async_func_pointer*, %swift.async_func_pointer** inttoptr (i64 and (i64 add (i64 ptrtoint (%swift.async_func_pointer** @"\01__imp_$s15resilient_class9BaseClassC4waitxyYaFTjTu" to i64), i64 1), i64 -2) to %swift.async_func_pointer**), align {{4|8}} +// CHECK-INDIRECT-NEXT: %14 = select i1 icmp eq (i64 and (i64 add (i64 ptrtoint (%swift.async_func_pointer** @"\01__imp_$s15resilient_class9BaseClassC4waitxyYaFTjTu" to i64), i64 1), i64 1), i64 0), +// CHECK-INDIRECT-SAME: %swift.async_func_pointer* inttoptr (i64 add (i64 ptrtoint (%swift.async_func_pointer** @"\01__imp_$s15resilient_class9BaseClassC4waitxyYaFTjTu" to i64), i64 1) to %swift.async_func_pointer*), +// CHECK-INDIRECT-SAME: %swift.async_func_pointer* [[LOAD]] // CHECK: ret void public func callsAwaitable(_ c: BaseClass) async -> T { return await c.wait() diff --git a/test/IRGen/async/protocol_resilience.swift b/test/IRGen/async/protocol_resilience.swift index 95fd7f36d3d4d..d44e2780ed337 100644 --- a/test/IRGen/async/protocol_resilience.swift +++ b/test/IRGen/async/protocol_resilience.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -disable-availability-checking -g -enable-library-evolution -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/Inputs/resilient_protocol.swift -// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -g -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// RUN: %target-swift-frontend -I %t -emit-ir -disable-availability-checking -g -enable-library-evolution %s | %FileCheck -check-prefix CHECK -check-prefix CHECK-%target-cpu -check-prefix CHECK-%target-import-type %s // REQUIRES: concurrency import resilient_protocol @@ -27,7 +27,11 @@ public protocol MyAwaitable { // CHECK-SAME: %swift.async_func_pointer* @"$s19protocol_resilience19ConformsToAwaitableVyxG010resilient_A00E0AaeFP4wait6ResultQzyYaFTWTu" // CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swift{{(tail)?}}cc void @"$s19protocol_resilience14callsAwaitabley6ResultQzxYa010resilient_A00D0RzlF"(%swift.opaque* noalias nocapture %0, %swift.context* swiftasync %1, %swift.opaque* noalias nocapture %2, %swift.type* %T, i8** %T.Awaitable) -// CHECK: %swift.async_func_pointer* @"$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu" +// CHECK-DIRECT: %swift.async_func_pointer* @"$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu" +// CHECK-INDIRECT: [[LOAD:%[0-9]+]] = load %swift.async_func_pointer*, %swift.async_func_pointer** inttoptr (i64 and (i64 add (i64 ptrtoint (%swift.async_func_pointer** @"\01__imp_$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu" to i64), i64 1), i64 -2) to %swift.async_func_pointer**), align {{4|8}} +// CHECK-INDIRECT: select i1 icmp eq (i64 and (i64 add (i64 ptrtoint (%swift.async_func_pointer** @"\01__imp_$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu" to i64), i64 1), i64 1), i64 0), +// CHECK-INDIRECT-SAME: %swift.async_func_pointer* inttoptr (i64 add (i64 ptrtoint (%swift.async_func_pointer** @"\01__imp_$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu" to i64), i64 1) to %swift.async_func_pointer*), +// CHECK-INDIRECT-SAME: %swift.async_func_pointer* [[LOAD]] // CHECK: ret void public func callsAwaitable(_ t: T) async -> T.Result { return await t.wait() diff --git a/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift b/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift index 230b7f5d06fba..3f27329a311f3 100644 --- a/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift +++ b/test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift @@ -11,7 +11,6 @@ // REQUIRES: concurrency // REQUIRES: concurrency_runtime // UNSUPPORTED: back_deployment_runtime -// XFAIL: windows import _Concurrency import NonresilientClass