Skip to content

Commit 476f634

Browse files
authored
Merge pull request #40147 from compnerd/5.5-async-function-protocol
[5.5] async function pointer support for Windows
2 parents 6fa7650 + 814f8b8 commit 476f634

File tree

9 files changed

+138
-12
lines changed

9 files changed

+138
-12
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ class IRGenOptions {
311311
/// Used on Windows to avoid cross-module references.
312312
unsigned LazyInitializeClassMetadata : 1;
313313
unsigned LazyInitializeProtocolConformances : 1;
314+
unsigned IndirectAsyncFunctionPointer : 1;
314315

315316
/// Normally if the -read-legacy-type-info flag is not specified, we look for
316317
/// a file named "legacy-<arch>.yaml" in SearchPathOpts.RuntimeLibraryPath.
@@ -406,7 +407,8 @@ class IRGenOptions {
406407
ValueNames(false), EnableReflectionMetadata(true),
407408
EnableReflectionNames(true), EnableAnonymousContextMangledNames(false),
408409
ForcePublicLinkage(false), LazyInitializeClassMetadata(false),
409-
LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false),
410+
LazyInitializeProtocolConformances(false),
411+
IndirectAsyncFunctionPointer(false), DisableLegacyTypeInfo(false),
410412
PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true),
411413
UseSwiftCall(false), UseTypeLayoutValueHandling(true),
412414
GenerateProfile(false), EnableDynamicReplacementChaining(false),

lib/Frontend/CompilerInvocation.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,11 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
17001700
// witness.
17011701
Opts.LazyInitializeProtocolConformances = Triple.isOSBinFormatCOFF();
17021702

1703+
// PE/COFF cannot deal with the cross-module reference to the
1704+
// AsyncFunctionPointer data block. Force the use of indirect
1705+
// AsyncFunctionPointer access.
1706+
Opts.IndirectAsyncFunctionPointer = Triple.isOSBinFormatCOFF();
1707+
17031708
if (Args.hasArg(OPT_disable_legacy_type_info)) {
17041709
Opts.DisableLegacyTypeInfo = true;
17051710
}

lib/IRGen/GenCall.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,6 +1977,46 @@ void IRGenFunction::emitAllExtractValues(llvm::Value *value,
19771977
out.add(Builder.CreateExtractValue(value, i));
19781978
}
19791979

1980+
namespace {
1981+
// TODO(compnerd) analyze if this should be out-lined via a runtime call rather
1982+
// than be open-coded. This needs to account for the fact that we are able to
1983+
// statically optimize this often times due to CVP changing the select to a
1984+
// `select i1 true, ...`.
1985+
llvm::Value *emitIndirectAsyncFunctionPointer(IRGenFunction &IGF,
1986+
llvm::Value *pointer) {
1987+
llvm::IntegerType *IntPtrTy = IGF.IGM.IntPtrTy;
1988+
llvm::Type *AsyncFunctionPointerPtrTy = IGF.IGM.AsyncFunctionPointerPtrTy;
1989+
llvm::Constant *Zero =
1990+
llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(),
1991+
0));
1992+
llvm::Constant *One =
1993+
llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(),
1994+
1));
1995+
llvm::Constant *NegativeOne =
1996+
llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(),
1997+
-2));
1998+
swift::irgen::Alignment PointerAlignment = IGF.IGM.getPointerAlignment();
1999+
2000+
llvm::Value *PtrToInt = IGF.Builder.CreatePtrToInt(pointer, IntPtrTy);
2001+
llvm::Value *And = IGF.Builder.CreateAnd(PtrToInt, One);
2002+
llvm::Value *ICmp = IGF.Builder.CreateICmpEQ(And, Zero);
2003+
2004+
llvm::Value *BitCast =
2005+
IGF.Builder.CreateBitCast(pointer, AsyncFunctionPointerPtrTy);
2006+
2007+
llvm::Value *UntaggedPointer = IGF.Builder.CreateAnd(PtrToInt, NegativeOne);
2008+
llvm::Value *IntToPtr =
2009+
IGF.Builder.CreateIntToPtr(UntaggedPointer,
2010+
AsyncFunctionPointerPtrTy->getPointerTo());
2011+
llvm::Value *Load = IGF.Builder.CreateLoad(IntToPtr, PointerAlignment);
2012+
2013+
// (select (icmp eq, (and (ptrtoint %AsyncFunctionPointer), 1), 0),
2014+
// (%AsyncFunctionPointer),
2015+
// (inttoptr (and (ptrtoint %AsyncFunctionPointer), -2)))
2016+
return IGF.Builder.CreateSelect(ICmp, BitCast, Load);
2017+
}
2018+
}
2019+
19802020
std::pair<llvm::Value *, llvm::Value *> irgen::getAsyncFunctionAndSize(
19812021
IRGenFunction &IGF, SILFunctionTypeRepresentation representation,
19822022
FunctionPointer functionPointer, llvm::Value *thickContext,
@@ -1998,9 +2038,11 @@ std::pair<llvm::Value *, llvm::Value *> irgen::getAsyncFunctionAndSize(
19982038
if (auto authInfo = functionPointer.getAuthInfo()) {
19992039
ptr = emitPointerAuthAuth(IGF, ptr, authInfo);
20002040
}
2001-
auto *afpPtr =
2002-
IGF.Builder.CreateBitCast(ptr, IGF.IGM.AsyncFunctionPointerPtrTy);
2003-
afpPtrValue = afpPtr;
2041+
afpPtrValue =
2042+
(IGF.IGM.getOptions().IndirectAsyncFunctionPointer)
2043+
? emitIndirectAsyncFunctionPointer(IGF, ptr)
2044+
: IGF.Builder.CreateBitCast(ptr,
2045+
IGF.IGM.AsyncFunctionPointerPtrTy);
20042046
}
20052047
return *afpPtrValue;
20062048
};
@@ -4800,6 +4842,8 @@ llvm::Value *FunctionPointer::getPointer(IRGenFunction &IGF) const {
48004842
auto *fnPtr = Value;
48014843
if (auto authInfo = AuthInfo) {
48024844
fnPtr = emitPointerAuthAuth(IGF, fnPtr, authInfo);
4845+
if (IGF.IGM.getOptions().IndirectAsyncFunctionPointer)
4846+
fnPtr = emitIndirectAsyncFunctionPointer(IGF, fnPtr);
48034847
}
48044848
auto *descriptorPtr =
48054849
IGF.Builder.CreateBitCast(fnPtr, IGF.IGM.AsyncFunctionPointerPtrTy);

lib/IRGen/GenThunk.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,34 @@ void IRGenModule::emitDispatchThunk(SILDeclRef declRef) {
345345

346346
llvm::Constant *
347347
IRGenModule::getAddrOfAsyncFunctionPointer(LinkEntity entity) {
348-
return getAddrOfLLVMVariable(
349-
LinkEntity::forAsyncFunctionPointer(entity),
350-
NotForDefinition, DebugTypeInfo());
348+
llvm::Constant *Pointer =
349+
getAddrOfLLVMVariable(LinkEntity::forAsyncFunctionPointer(entity),
350+
NotForDefinition, DebugTypeInfo());
351+
if (!getOptions().IndirectAsyncFunctionPointer)
352+
return Pointer;
353+
354+
// When the symbol does not have DLL Import storage, we must directly address
355+
// it. Otherwise, we will form an invalid reference.
356+
if (!Pointer->isDLLImportDependent())
357+
return Pointer;
358+
359+
llvm::Constant *PointerPointer =
360+
getOrCreateGOTEquivalent(Pointer,
361+
LinkEntity::forAsyncFunctionPointer(entity));
362+
llvm::Constant *PointerPointerConstant =
363+
llvm::ConstantExpr::getPtrToInt(PointerPointer, IntPtrTy);
364+
llvm::Constant *Marker =
365+
llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(),
366+
1));
367+
// TODO(compnerd) ensure that the pointer alignment guarantees that bit-0 is
368+
// cleared. We cannot use an `getOr` here as it does not form a relocatable
369+
// expression.
370+
llvm::Constant *Address =
371+
llvm::ConstantExpr::getAdd(PointerPointerConstant, Marker);
372+
373+
IndirectAsyncFunctionPointers[entity] = Address;
374+
return llvm::ConstantExpr::getIntToPtr(Address,
375+
AsyncFunctionPointerTy->getPointerTo());
351376
}
352377

353378
llvm::Constant *
@@ -374,6 +399,15 @@ IRGenModule::getSILFunctionForAsyncFunctionPointer(llvm::Constant *afp) {
374399
return entity.getSILFunction();
375400
}
376401
}
402+
for (auto &entry : IndirectAsyncFunctionPointers) {
403+
if (entry.getSecond() == afp) {
404+
auto entity = entry.getFirst();
405+
assert(getOptions().IndirectAsyncFunctionPointer &&
406+
"indirect async function found for non-indirect async function"
407+
" target?");
408+
return entity.getSILFunction();
409+
}
410+
}
377411
return nullptr;
378412
}
379413

lib/IRGen/IRGenModule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,7 @@ class IRGenModule {
10971097
LinkEntity entity);
10981098

10991099
llvm::DenseMap<LinkEntity, llvm::Constant*> GlobalVars;
1100+
llvm::DenseMap<LinkEntity, llvm::Constant*> IndirectAsyncFunctionPointers;
11001101
llvm::DenseMap<LinkEntity, llvm::Constant*> GlobalGOTEquivalents;
11011102
llvm::DenseMap<LinkEntity, llvm::Function*> GlobalFuncs;
11021103
llvm::DenseSet<const clang::Decl *> GlobalClangDecls;

test/IRGen/async-inheritance.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %empty-directory(%t)
2+
// 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
3+
// 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)
4+
// RUN: %target-codesign %t/E
5+
// RUN: %target-run %t/E %t/%target-library-name(L) | %FileCheck %s
6+
7+
// REQUIRES: concurrency
8+
// REQUIRES: concurrency_runtime
9+
// REQUIRES: executable_test
10+
11+
// UNSUPPORTED: back_deployment_runtime
12+
13+
#if L
14+
open class C {
15+
public init() {}
16+
open func f() async {
17+
print("\(#function)")
18+
}
19+
}
20+
#else
21+
import L
22+
class D: C {
23+
}
24+
25+
@main
26+
struct S {
27+
public static func main() async {
28+
await D().f()
29+
}
30+
}
31+
#endif
32+
33+
// CHECK: f()

test/IRGen/async/class_resilience.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -enable-experimental-concurrency -disable-availability-checking -enable-library-evolution -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class %S/Inputs/resilient_class.swift
3-
// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -disable-availability-checking -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s
3+
// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -disable-availability-checking -enable-library-evolution %s | %FileCheck -check-prefix CHECK -check-prefix CHECK-%target-cpu -check-prefix CHECK-%target-import-type %s
44
// REQUIRES: concurrency
55

66
import resilient_class
@@ -42,7 +42,11 @@ open class MyBaseClass<T> {
4242
// CHECK-SAME: %swift.async_func_pointer* @"$s16class_resilience9MyDerivedC4waitSiyYaF010resilient_A09BaseClassCADxyYaFTVTu"
4343

4444
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swift{{(tail)?}}cc void @"$s16class_resilience14callsAwaitableyx010resilient_A09BaseClassCyxGYalF"(%swift.opaque* noalias nocapture %0, %swift.context* swiftasync %1{{.*}})
45-
// CHECK: %swift.async_func_pointer* @"$s15resilient_class9BaseClassC4waitxyYaFTjTu"
45+
// CHECK-DIRECT: %swift.async_func_pointer* @"$s15resilient_class9BaseClassC4waitxyYaFTjTu"
46+
// 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}}
47+
// CHECK-INDIRECT-NEXT: 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),
48+
// 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*),
49+
// CHECK-INDIRECT-SAME: %swift.async_func_pointer* [[LOAD]]
4650
// CHECK: ret void
4751
public func callsAwaitable<T>(_ c: BaseClass<T>) async -> T {
4852
return await c.wait()

test/IRGen/async/protocol_resilience.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -enable-experimental-concurrency -disable-availability-checking -g -enable-library-evolution -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/Inputs/resilient_protocol.swift
3-
// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -disable-availability-checking -g -enable-library-evolution %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s
3+
// RUN: %target-swift-frontend -I %t -emit-ir -enable-experimental-concurrency -disable-availability-checking -g -enable-library-evolution %s | %FileCheck -check-prefix CHECK -check-prefix CHECK-%target-cpu -check-prefix CHECK-%target-import-type %s
44
// REQUIRES: concurrency
55

66
import resilient_protocol
@@ -27,7 +27,11 @@ public protocol MyAwaitable {
2727
// CHECK-SAME: %swift.async_func_pointer* @"$s19protocol_resilience19ConformsToAwaitableVyxG010resilient_A00E0AaeFP4wait6ResultQzyYaFTWTu"
2828

2929
// 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)
30-
// CHECK: %swift.async_func_pointer* @"$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu"
30+
// CHECK-DIRECT: %swift.async_func_pointer* @"$s18resilient_protocol9AwaitableP4wait6ResultQzyYaFTjTu"
31+
// 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}}
32+
// 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),
33+
// 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*),
34+
// CHECK-INDIRECT-SAME: %swift.async_func_pointer* [[LOAD]]
3135
// CHECK: ret void
3236
public func callsAwaitable<T : Awaitable>(_ t: T) async -> T.Result {
3337
return await t.wait()

test/IRGen/async/run-call-nonresilient-classinstance-void-to-void.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
// REQUIRES: concurrency
1212
// REQUIRES: concurrency_runtime
1313
// UNSUPPORTED: back_deployment_runtime
14-
// XFAIL: windows
1514

1615
import _Concurrency
1716
import NonresilientClass

0 commit comments

Comments
 (0)