Skip to content

Commit 3470888

Browse files
committed
[IRGen] Fix ABI for thick async functions.
Previously, thick async functions were represented sometimes as a pair of (AsyncFunctionPointer, nullptr)--when the thick function was produced via a thin_to_thick_function, e.g.--and sometimes as a pair of (FunctionPointer, ThickContext)--when the thick function was produced by a partial_apply--with the size stored in the slot of the ThickContext. That optimized for the wrong case: partial applies of dynamic async functions; in that case, there is no appropriate AsyncFunctionPointer to form when lowering the partial_apply instruction. The far more common case is to know exactly which function is being partially applied. In that case, we can form the appropriate AsyncFunctionPointer. Furthermore, the previous representation made calling a thick function more complex: it was always necessary to check whether the context was in fact null and then proceed along two different paths depending. Here, that behavior is corrected by creating a thunk in a mandatory IRGen SIL pass in the case that the function that is being partially applied is dynamic. That new thunk is then partially applied in place of the original partial_apply of the dynamic function.
1 parent 96a1e07 commit 3470888

File tree

12 files changed

+586
-299
lines changed

12 files changed

+586
-299
lines changed

docs/ABI/Mangling.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ types where the metadata itself has unknown layout.)
210210
global ::= global 'Td' // direct method reference thunk
211211
global ::= global 'TI' // implementation of a dynamic_replaceable function
212212
global ::= global 'Tu' // async function pointer of a function
213+
global ::= global 'Tw' // async partial apply thunk for a non-constant function
213214
global ::= global 'TX' // function pointer of a dynamic_replaceable function
214215
global ::= entity entity 'TV' // vtable override thunk, derived followed by base
215216
global ::= type label-list? 'D' // type mangling for the debugger with label list for function types.

include/swift/IRGen/IRGenSILPasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace irgen {
1919
/// Create a pass to hoist alloc_stack instructions with non-fixed size.
2020
SILTransform *createAllocStackHoisting();
2121
SILTransform *createLoadableByAddress();
22+
SILTransform *createPartialApplyLowering();
2223

2324
} // end namespace irgen
2425
} // end namespace swift

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ IRGEN_PASS(LoadableByAddress, "loadable-address",
296296
"SIL Large Loadable type by-address lowering.")
297297
PASS(MandatorySILLinker, "mandatory-linker",
298298
"Deserialize all referenced SIL functions that are shared or transparent")
299+
IRGEN_PASS(PartialApplyLowering, "partial-apply-lowering",
300+
"Partial Apply Lowering")
299301
PASS(PerformanceSILLinker, "performance-linker",
300302
"Deserialize all referenced SIL functions")
301303
PASS(RawSILInstLowering, "raw-sil-inst-lowering",

lib/IRGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ add_swift_host_library(swiftIRGen STATIC
5252
MetadataLayout.cpp
5353
MetadataRequest.cpp
5454
Outlining.cpp
55+
PartialApplyLowering.cpp
5556
StructLayout.cpp
5657
SwiftTargetInfo.cpp
5758
TypeLayout.cpp

lib/IRGen/GenCall.cpp

Lines changed: 27 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,208 +1926,38 @@ std::pair<llvm::Value *, llvm::Value *> irgen::getAsyncFunctionAndSize(
19261926
assert(functionPointer.getKind() != FunctionPointer::Kind::Function);
19271927
bool emitFunction = values.first;
19281928
bool emitSize = values.second;
1929-
// TODO: This calculation should be extracted out into standalone functions
1930-
// emitted on-demand per-module to improve codesize.
1931-
switch (representation) {
1932-
case SILFunctionTypeRepresentation::Thick: {
1933-
assert(!functionPointer.useStaticContextSize());
1934-
// If the called function is thick, the size of the called function's
1935-
// async context is not statically knowable.
1936-
//
1937-
// Specifically, if the thick function was produced by a partial_apply,
1938-
// the function which was originally partially applied determines the
1939-
// size of the needed async context. That original function isn't known
1940-
// statically. The dynamic size is available within the context as an
1941-
// i32 at the first index: <{ %swift.refcounted*, /*size*/ i32, ... }>.
1942-
// In this case, the function pointer is actually a pointer to an llvm
1943-
// function.
1944-
//
1945-
// On the other hand, if the thick function was produced by a
1946-
// thin_to_thick_function, then the context will be nullptr. In that
1947-
// case, the dynamic size of the needed async context is available within
1948-
// the struct, an AsyncFunctionPointer pointed to by the "function" pointer
1949-
// as an i32 at the second index: <{ /*fn rel addr*/ i32, /*size*/ i32 }>.
1950-
//
1951-
// We are currently emitting into some basic block. To handle these two
1952-
// cases, we need to branch based on whether the context is nullptr; each
1953-
// branch must then determine the size and function pointer in the manner
1954-
// appropriate to it. Finally, both blocks must join back together to make
1955-
// the call:
1956-
//
1957-
// +-------------------------+
1958-
// |%cond = %ctx == nullptr |
1959-
// +----------------|br %cond, thin, thick |----------------------+
1960-
// | +-------------------------+ |
1961-
// | |
1962-
// V |
1963-
// +-thin-------------------------------------------+ |
1964-
// |%afp = bitcast %fp to %swift.async_func_pointer*| |
1965-
// |%size_ptr = getelementptr %afp, i32 0, i32 1 | |
1966-
// |%size = load %size_ptr | |
1967-
// |%offset_ptr = getelementptr %afp, i32 0, i32 1 | |
1968-
// |%offset = load i32 %offset_ptr | |
1969-
// |%offset64 = sext %offset to i64 | |
1970-
// |%raw_fp = add %offset64, %offset_ptr | |
1971-
// |br join(%raw_fp, %size) | |
1972-
// +------------------------------------------------+ |
1973-
// | |
1974-
// | V
1975-
// | +-thick--------------------------------------------+
1976-
// | |%layout = bitcast %ctx to <{%swift.context*, i32}>|
1977-
// | |%size_addr = getelementptr %layout, i32 0, i32 1 |
1978-
// | |%size = load %size_addr |
1979-
// | |br join(%fp, %size) |
1980-
// | +---/----------------------------------------------+
1981-
// | /
1982-
// | /
1983-
// V V
1984-
// +-join(%fn, %size)------------------------------------------------------+
1985-
// |%dataAddr = swift_taskAlloc(%task, %size) |
1986-
// |%async_context = bitcast %dataAddr to ASYNC_CONTEXT(static_callee_type)|
1987-
// |... // populate the fields %ctx with arguments |
1988-
// |call %fn(%async_context, %ctx) |
1989-
// +-----------------------------------------------------------------------+
1990-
auto *thinBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
1991-
auto *thickBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
1992-
auto *joinBlock = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
1993-
1994-
auto hasThickContext =
1995-
IGF.Builder.CreateICmpNE(thickContext, IGF.IGM.RefCountedNull);
1996-
IGF.Builder.CreateCondBr(hasThickContext, thickBlock, thinBlock);
1997-
1998-
SmallVector<std::pair<llvm::BasicBlock *, llvm::Value *>, 2> fnPhiValues;
1999-
SmallVector<std::pair<llvm::BasicBlock *, llvm::Value *>, 2> sizePhiValues;
2000-
{ // thin
2001-
IGF.Builder.emitBlock(thinBlock);
2002-
auto *ptr = functionPointer.getRawPointer();
2003-
if (auto authInfo = functionPointer.getAuthInfo()) {
2004-
ptr = emitPointerAuthAuth(IGF, ptr, authInfo);
2005-
}
2006-
auto *afpPtr =
2007-
IGF.Builder.CreateBitCast(ptr, IGF.IGM.AsyncFunctionPointerPtrTy);
2008-
if (emitFunction) {
2009-
llvm::Value *addrPtr = IGF.Builder.CreateStructGEP(afpPtr, 0);
2010-
auto *uncastFnPtr = IGF.emitLoadOfRelativePointer(
2011-
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
2012-
/*expectedType*/ functionPointer.getFunctionType()->getPointerTo());
2013-
auto *fnPtr = IGF.Builder.CreateBitCast(uncastFnPtr, IGF.IGM.Int8PtrTy);
2014-
if (auto authInfo = functionPointer.getAuthInfo()) {
2015-
fnPtr = emitPointerAuthSign(IGF, fnPtr, authInfo);
2016-
}
2017-
fnPhiValues.push_back({thinBlock, fnPtr});
2018-
}
2019-
if (emitSize) {
2020-
auto *sizePtr = IGF.Builder.CreateStructGEP(afpPtr, 1);
2021-
auto *size =
2022-
IGF.Builder.CreateLoad(sizePtr, IGF.IGM.getPointerAlignment());
2023-
sizePhiValues.push_back({thinBlock, size});
2024-
}
2025-
IGF.Builder.CreateBr(joinBlock);
2026-
}
2027-
2028-
{ // thick
2029-
IGF.Builder.emitBlock(thickBlock);
2030-
if (emitFunction) {
2031-
auto *uncastFnPtr = functionPointer.getRawPointer();
2032-
auto *fnPtr = IGF.Builder.CreateBitCast(uncastFnPtr, IGF.IGM.Int8PtrTy);
2033-
fnPhiValues.push_back({thickBlock, fnPtr});
2034-
}
2035-
if (emitSize) {
2036-
SmallVector<const TypeInfo *, 4> argTypeInfos;
2037-
SmallVector<SILType, 4> argValTypes;
2038-
auto int32ASTType =
2039-
BuiltinIntegerType::get(32, IGF.IGM.IRGen.SIL.getASTContext())
2040-
->getCanonicalType();
2041-
auto int32SILType = SILType::getPrimitiveObjectType(int32ASTType);
2042-
const TypeInfo &int32TI = IGF.IGM.getTypeInfo(int32SILType);
2043-
argValTypes.push_back(int32SILType);
2044-
argTypeInfos.push_back(&int32TI);
2045-
HeapLayout layout(IGF.IGM, LayoutStrategy::Optimal, argValTypes,
2046-
argTypeInfos,
2047-
/*typeToFill*/ nullptr, NecessaryBindings());
2048-
auto castThickContext =
2049-
layout.emitCastTo(IGF, thickContext, "context.prefix");
2050-
auto sizeLayout = layout.getElement(0);
2051-
auto sizeAddr = sizeLayout.project(IGF, castThickContext,
2052-
/*NonFixedOffsets*/ llvm::None);
2053-
auto *sizeValue = IGF.Builder.CreateLoad(sizeAddr);
2054-
sizePhiValues.push_back({thickBlock, sizeValue});
2055-
}
2056-
IGF.Builder.CreateBr(joinBlock);
2057-
}
2058-
2059-
{ // join
2060-
IGF.Builder.emitBlock(joinBlock);
2061-
llvm::Value *fn = nullptr;
2062-
llvm::PHINode *fnPhi = nullptr;
2063-
llvm::PHINode *sizePhi = nullptr;
2064-
if (emitFunction) {
2065-
fnPhi = IGF.Builder.CreatePHI(IGF.IGM.Int8PtrTy, fnPhiValues.size());
2066-
}
2067-
if (emitSize) {
2068-
sizePhi = IGF.Builder.CreatePHI(IGF.IGM.Int32Ty, sizePhiValues.size());
2069-
}
2070-
if (emitFunction) {
2071-
assert(fnPhi);
2072-
for (auto &entry : fnPhiValues) {
2073-
fnPhi->addIncoming(entry.second, entry.first);
2074-
}
2075-
fn = IGF.Builder.CreateBitCast(
2076-
fnPhi, functionPointer.getFunctionType()->getPointerTo());
2077-
}
2078-
llvm::Value *size = nullptr;
2079-
if (emitSize) {
2080-
assert(sizePhi);
2081-
for (auto &entry : sizePhiValues) {
2082-
sizePhi->addIncoming(entry.second, entry.first);
2083-
}
2084-
size = sizePhi;
2085-
}
2086-
return {fn, size};
1929+
auto *ptr = functionPointer.getRawPointer();
1930+
if (auto authInfo = functionPointer.getAuthInfo()) {
1931+
ptr = emitPointerAuthAuth(IGF, ptr, authInfo);
1932+
}
1933+
auto *afpPtr =
1934+
IGF.Builder.CreateBitCast(ptr, IGF.IGM.AsyncFunctionPointerPtrTy);
1935+
llvm::Value *fn = nullptr;
1936+
if (emitFunction) {
1937+
if (functionPointer.useStaticContextSize()) {
1938+
fn = functionPointer.getRawPointer();
1939+
} else {
1940+
llvm::Value *addrPtr = IGF.Builder.CreateStructGEP(afpPtr, 0);
1941+
fn = IGF.emitLoadOfRelativePointer(
1942+
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
1943+
/*expectedType*/ functionPointer.getFunctionType()->getPointerTo());
20871944
}
2088-
}
2089-
case SILFunctionTypeRepresentation::Thin:
2090-
case SILFunctionTypeRepresentation::CFunctionPointer:
2091-
case SILFunctionTypeRepresentation::Method:
2092-
case SILFunctionTypeRepresentation::ObjCMethod:
2093-
case SILFunctionTypeRepresentation::WitnessMethod:
2094-
case SILFunctionTypeRepresentation::Closure:
2095-
case SILFunctionTypeRepresentation::Block: {
2096-
auto *ptr = functionPointer.getRawPointer();
20971945
if (auto authInfo = functionPointer.getAuthInfo()) {
2098-
ptr = emitPointerAuthAuth(IGF, ptr, authInfo);
1946+
fn = emitPointerAuthSign(IGF, fn, authInfo);
20991947
}
2100-
auto *afpPtr =
2101-
IGF.Builder.CreateBitCast(ptr, IGF.IGM.AsyncFunctionPointerPtrTy);
2102-
llvm::Value *fn = nullptr;
2103-
if (emitFunction) {
2104-
if (functionPointer.useStaticContextSize()) {
2105-
fn = functionPointer.getRawPointer();
2106-
} else {
2107-
llvm::Value *addrPtr = IGF.Builder.CreateStructGEP(afpPtr, 0);
2108-
fn = IGF.emitLoadOfRelativePointer(
2109-
Address(addrPtr, IGF.IGM.getPointerAlignment()), /*isFar*/ false,
2110-
/*expectedType*/ functionPointer.getFunctionType()->getPointerTo());
2111-
}
2112-
if (auto authInfo = functionPointer.getAuthInfo()) {
2113-
fn = emitPointerAuthSign(IGF, fn, authInfo);
2114-
}
2115-
}
2116-
llvm::Value *size = nullptr;
2117-
if (emitSize) {
2118-
if (functionPointer.useStaticContextSize()) {
2119-
size = llvm::ConstantInt::get(IGF.IGM.Int32Ty,
2120-
initialContextSize.getValue());
2121-
} else {
2122-
assert(!functionPointer.useStaticContextSize());
2123-
auto *sizePtr = IGF.Builder.CreateStructGEP(afpPtr, 1);
2124-
size = IGF.Builder.CreateLoad(sizePtr, IGF.IGM.getPointerAlignment());
2125-
}
2126-
}
2127-
return {fn, size};
21281948
}
1949+
llvm::Value *size = nullptr;
1950+
if (emitSize) {
1951+
if (functionPointer.useStaticContextSize()) {
1952+
size = llvm::ConstantInt::get(IGF.IGM.Int32Ty,
1953+
initialContextSize.getValue());
1954+
} else {
1955+
assert(!functionPointer.useStaticContextSize());
1956+
auto *sizePtr = IGF.Builder.CreateStructGEP(afpPtr, 1);
1957+
size = IGF.Builder.CreateLoad(sizePtr, IGF.IGM.getPointerAlignment());
1958+
}
21291959
}
2130-
llvm_unreachable("unhandled case");
1960+
return {fn, size};
21311961
}
21321962

21331963
static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,

0 commit comments

Comments
 (0)