mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
IRGen: Add metadata for async funclets denoting frame entry and frame exists
Adds sections `__TEXT,__swift_as_entry`, and `__TEXT,__swift_as_ret` that contain relative pointers to async functlets modelling async function entries, and function returns, respectively. Emission of the sections can be trigger with the frontend option `-Xfrontend -enable-async-frame-push-pop-metadata`. This is done by: * IRGen adding a `async_entry` function attribute to async functions. * LLVM's coroutine splitting identifying continuation funclets that model the return from an async function call by adding the function attribute `async_ret`. (see #llvm-project/pull/9204) * An LLVM pass that keys off these two function attribute and emits the metadata into the above mention sections. rdar://134460666
This commit is contained in:
@@ -478,6 +478,8 @@ public:
|
|||||||
// Whether to run the HotColdSplitting pass when optimizing.
|
// Whether to run the HotColdSplitting pass when optimizing.
|
||||||
unsigned EnableHotColdSplit : 1;
|
unsigned EnableHotColdSplit : 1;
|
||||||
|
|
||||||
|
unsigned EmitAsyncFramePushPopMetadata : 1;
|
||||||
|
|
||||||
/// The number of threads for multi-threaded code generation.
|
/// The number of threads for multi-threaded code generation.
|
||||||
unsigned NumThreads = 0;
|
unsigned NumThreads = 0;
|
||||||
|
|
||||||
@@ -567,7 +569,7 @@ public:
|
|||||||
DisableReadonlyStaticObjects(false), CollocatedMetadataFunctions(false),
|
DisableReadonlyStaticObjects(false), CollocatedMetadataFunctions(false),
|
||||||
ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false),
|
ColocateTypeDescriptors(true), UseRelativeProtocolWitnessTables(false),
|
||||||
UseFragileResilientProtocolWitnesses(false),
|
UseFragileResilientProtocolWitnesses(false),
|
||||||
EnableHotColdSplit(false),
|
EnableHotColdSplit(false), EmitAsyncFramePushPopMetadata(false),
|
||||||
CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()),
|
CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()),
|
||||||
TypeInfoFilter(TypeInfoDumpFilter::All),
|
TypeInfoFilter(TypeInfoDumpFilter::All),
|
||||||
PlatformCCallingConvention(llvm::CallingConv::C), UseCASBackend(false),
|
PlatformCCallingConvention(llvm::CallingConv::C), UseCASBackend(false),
|
||||||
|
|||||||
@@ -149,6 +149,12 @@ namespace swift {
|
|||||||
llvm::PreservedAnalyses run(llvm::Module &M,
|
llvm::PreservedAnalyses run(llvm::Module &M,
|
||||||
llvm::ModuleAnalysisManager &AM);
|
llvm::ModuleAnalysisManager &AM);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AsyncEntryReturnMetadataPass
|
||||||
|
: public llvm::PassInfoMixin<AsyncEntryReturnMetadataPass> {
|
||||||
|
llvm::PreservedAnalyses run(llvm::Module &M,
|
||||||
|
llvm::ModuleAnalysisManager &AM);
|
||||||
|
};
|
||||||
} // end namespace swift
|
} // end namespace swift
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1310,6 +1310,13 @@ def disable_fragile_resilient_protocol_witnesses :
|
|||||||
Flag<["-"], "disable-fragile-relative-protocol-tables">,
|
Flag<["-"], "disable-fragile-relative-protocol-tables">,
|
||||||
HelpText<"Disable relative protocol witness tables">;
|
HelpText<"Disable relative protocol witness tables">;
|
||||||
|
|
||||||
|
def enable_async_frame_push_pop_metadata :
|
||||||
|
Flag<["-"], "enable-async-frame-push-pop-metadata">,
|
||||||
|
HelpText<"Enable async frame push pop metadata">;
|
||||||
|
def disable_async_frame_push_pop_metadata :
|
||||||
|
Flag<["-"], "disable-async-frame-push-pop-metadata">,
|
||||||
|
HelpText<"Disable async frame push pop metadata">;
|
||||||
|
|
||||||
def enable_split_cold_code :
|
def enable_split_cold_code :
|
||||||
Flag<["-"], "enable-split-cold-code">,
|
Flag<["-"], "enable-split-cold-code">,
|
||||||
HelpText<"Enable splitting of cold code when optimizing">;
|
HelpText<"Enable splitting of cold code when optimizing">;
|
||||||
|
|||||||
@@ -3435,6 +3435,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
|
|||||||
Args.hasFlag(OPT_enable_split_cold_code,
|
Args.hasFlag(OPT_enable_split_cold_code,
|
||||||
OPT_disable_split_cold_code,
|
OPT_disable_split_cold_code,
|
||||||
Opts.EnableHotColdSplit);
|
Opts.EnableHotColdSplit);
|
||||||
|
Opts.EmitAsyncFramePushPopMetadata =
|
||||||
|
Args.hasFlag(OPT_enable_async_frame_push_pop_metadata,
|
||||||
|
OPT_disable_async_frame_push_pop_metadata,
|
||||||
|
Opts.EmitAsyncFramePushPopMetadata);
|
||||||
Opts.EnableLargeLoadableTypesReg2Mem =
|
Opts.EnableLargeLoadableTypesReg2Mem =
|
||||||
Args.hasFlag(OPT_enable_large_loadable_types_reg2mem,
|
Args.hasFlag(OPT_enable_large_loadable_types_reg2mem,
|
||||||
OPT_disable_large_loadable_types_reg2mem,
|
OPT_disable_large_loadable_types_reg2mem,
|
||||||
|
|||||||
@@ -6150,6 +6150,7 @@ void irgen::emitAsyncReturn(
|
|||||||
// Setup the coro.end.async intrinsic call.
|
// Setup the coro.end.async intrinsic call.
|
||||||
auto &Builder = IGF.Builder;
|
auto &Builder = IGF.Builder;
|
||||||
auto mustTailCallFn = IGF.createAsyncDispatchFn(fnPtr,Args);
|
auto mustTailCallFn = IGF.createAsyncDispatchFn(fnPtr,Args);
|
||||||
|
|
||||||
auto handle = IGF.getCoroutineHandle();
|
auto handle = IGF.getCoroutineHandle();
|
||||||
auto rawFnPtr =
|
auto rawFnPtr =
|
||||||
Builder.CreateBitOrPointerCast(fnPtr.getRawPointer(), IGF.IGM.Int8PtrTy);
|
Builder.CreateBitOrPointerCast(fnPtr.getRawPointer(), IGF.IGM.Int8PtrTy);
|
||||||
|
|||||||
@@ -270,12 +270,14 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts,
|
|||||||
if (Level != OptimizationLevel::O0)
|
if (Level != OptimizationLevel::O0)
|
||||||
FPM.addPass(SwiftARCOptPass());
|
FPM.addPass(SwiftARCOptPass());
|
||||||
});
|
});
|
||||||
PB.registerOptimizerLastEPCallback([](ModulePassManager &MPM,
|
PB.registerOptimizerLastEPCallback([&](ModulePassManager &MPM,
|
||||||
OptimizationLevel Level) {
|
OptimizationLevel Level) {
|
||||||
if (Level != OptimizationLevel::O0)
|
if (Level != OptimizationLevel::O0)
|
||||||
MPM.addPass(createModuleToFunctionPassAdaptor(SwiftARCContractPass()));
|
MPM.addPass(createModuleToFunctionPassAdaptor(SwiftARCContractPass()));
|
||||||
if (Level == OptimizationLevel::O0)
|
if (Level == OptimizationLevel::O0)
|
||||||
MPM.addPass(AlwaysInlinerPass());
|
MPM.addPass(AlwaysInlinerPass());
|
||||||
|
if (Opts.EmitAsyncFramePushPopMetadata)
|
||||||
|
MPM.addPass(AsyncEntryReturnMetadataPass());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2522,6 +2522,10 @@ void IRGenSILFunction::emitSILFunction() {
|
|||||||
CurFn,
|
CurFn,
|
||||||
LinkEntity::forSILFunction(CurSILFn),
|
LinkEntity::forSILFunction(CurSILFn),
|
||||||
getAsyncContextLayout(*this).getSize());
|
getAsyncContextLayout(*this).getSize());
|
||||||
|
|
||||||
|
if (IGM.getOptions().EmitAsyncFramePushPopMetadata) {
|
||||||
|
CurFn->addFnAttr("async_entry");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isAsyncFn) {
|
if (isAsyncFn) {
|
||||||
IGM.noteSwiftAsyncFunctionDef();
|
IGM.noteSwiftAsyncFunctionDef();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ add_swift_host_library(swiftLLVMPasses STATIC
|
|||||||
LLVMARCContract.cpp
|
LLVMARCContract.cpp
|
||||||
LLVMInlineTree.cpp
|
LLVMInlineTree.cpp
|
||||||
LLVMMergeFunctions.cpp
|
LLVMMergeFunctions.cpp
|
||||||
|
LLVMEmitAsyncEntryReturnMetadata.cpp
|
||||||
|
|
||||||
LLVM_LINK_COMPONENTS
|
LLVM_LINK_COMPONENTS
|
||||||
analysis
|
analysis
|
||||||
|
|||||||
90
lib/LLVMPasses/LLVMEmitAsyncEntryReturnMetadata.cpp
Normal file
90
lib/LLVMPasses/LLVMEmitAsyncEntryReturnMetadata.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
//===--- LLVMEmitAsyncEntryReturnMetadata.cpp - Async function metadata ---===//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "swift/LLVMPasses/Passes.h"
|
||||||
|
#include "llvm/Pass.h"
|
||||||
|
#include "llvm/IR/Constants.h"
|
||||||
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace swift;
|
||||||
|
|
||||||
|
#define DEBUG_TYPE "swift-async-return"
|
||||||
|
|
||||||
|
PreservedAnalyses AsyncEntryReturnMetadataPass::run(Module &M,
|
||||||
|
ModuleAnalysisManager &AM) {
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
SmallVector<llvm::Function *, 16> asyncEntries;
|
||||||
|
SmallVector<llvm::Function *, 16> asyncReturns;
|
||||||
|
for (auto &F : M) {
|
||||||
|
if (F.isDeclaration())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (F.hasFnAttribute("async_entry"))
|
||||||
|
asyncEntries.push_back(&F);
|
||||||
|
if (F.hasFnAttribute("async_ret"))
|
||||||
|
asyncReturns.push_back(&F);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &ctxt = M.getContext();
|
||||||
|
auto int32Ty = llvm::Type::getInt32Ty(ctxt);
|
||||||
|
auto sizeTy = M.getDataLayout().getIntPtrType(ctxt, /*addrspace*/ 0);
|
||||||
|
|
||||||
|
auto addSection = [&] (const char * sectionName, const char *globalName,
|
||||||
|
SmallVectorImpl<llvm::Function *> & entries) {
|
||||||
|
if (entries.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto intArrayTy = llvm::ArrayType::get(int32Ty, entries.size());
|
||||||
|
auto global =
|
||||||
|
new llvm::GlobalVariable(M, intArrayTy, true,
|
||||||
|
llvm::GlobalValue::InternalLinkage,
|
||||||
|
nullptr, /*init*/ globalName,
|
||||||
|
nullptr, /*insertBefore*/
|
||||||
|
llvm::GlobalValue::NotThreadLocal,
|
||||||
|
0/*address space*/);
|
||||||
|
global->setAlignment(Align(4));
|
||||||
|
global->setSection(sectionName);
|
||||||
|
size_t index = 0;
|
||||||
|
SmallVector<llvm::Constant*, 16> offsets;
|
||||||
|
for (auto *fn : entries) {
|
||||||
|
llvm::Constant *indices[] = { llvm::ConstantInt::get(int32Ty, 0),
|
||||||
|
llvm::ConstantInt::get(int32Ty, index)};
|
||||||
|
++index;
|
||||||
|
|
||||||
|
llvm::Constant *base = llvm::ConstantExpr::getInBoundsGetElementPtr(
|
||||||
|
intArrayTy, global, indices);
|
||||||
|
base = llvm::ConstantExpr::getPtrToInt(base, sizeTy);
|
||||||
|
auto *target = llvm::ConstantExpr::getPtrToInt(fn, sizeTy);
|
||||||
|
llvm::Constant *offset = llvm::ConstantExpr::getSub(target, base);
|
||||||
|
|
||||||
|
if (sizeTy != int32Ty) {
|
||||||
|
offset = llvm::ConstantExpr::getTrunc(offset, int32Ty);
|
||||||
|
}
|
||||||
|
offsets.push_back(offset);
|
||||||
|
}
|
||||||
|
auto constant = llvm::ConstantArray::get(intArrayTy, offsets);
|
||||||
|
global->setInitializer(constant);
|
||||||
|
appendToUsed(M, global);
|
||||||
|
|
||||||
|
llvm::GlobalVariable::SanitizerMetadata Meta;
|
||||||
|
Meta.IsDynInit = false;
|
||||||
|
Meta.NoAddress = true;
|
||||||
|
global->setSanitizerMetadata(Meta);
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
addSection("__TEXT,__swift_as_entry, coalesced, no_dead_strip",
|
||||||
|
"__swift_async_entry_functlets",
|
||||||
|
asyncEntries);
|
||||||
|
addSection("__TEXT,__swift_as_ret, coalesced, no_dead_strip",
|
||||||
|
"__swift_async_ret_functlets",
|
||||||
|
asyncReturns);
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
|
return PreservedAnalyses::all();
|
||||||
|
|
||||||
|
return PreservedAnalyses::none();
|
||||||
|
}
|
||||||
40
test/IRGen/async_frame_entry_return_metadata.swift
Normal file
40
test/IRGen/async_frame_entry_return_metadata.swift
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// RUN: %target-swift-frontend -primary-file %s -emit-ir -module-name async -disable-availability-checking -enable-async-frame-push-pop-metadata | %FileCheck %s --check-prefix=ENABLED
|
||||||
|
// RUN: %target-swift-frontend -primary-file %s -emit-ir -module-name async -disable-availability-checking -O -enable-async-frame-push-pop-metadata | %FileCheck %s --check-prefix=ENABLED
|
||||||
|
// RUN: %target-swift-frontend -primary-file %s -emit-ir -module-name async -disable-availability-checking -disable-async-frame-push-pop-metadata | %FileCheck %s --check-prefix=DISABLED
|
||||||
|
// RUN: %target-swift-frontend -primary-file %s -emit-ir -module-name async -disable-availability-checking | %FileCheck %s --check-prefix=DISABLED
|
||||||
|
|
||||||
|
// REQUIRES: OS=macosx || OS=iphoneos
|
||||||
|
// REQUIRES: PTRSIZE=64
|
||||||
|
|
||||||
|
// ENABLED: @__swift_async_entry_functlets = internal constant [2 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @"$s5async6calleeyyYaF" to i64), i64 ptrtoint (ptr @__swift_async_entry_functlets to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @"$s5async6callerySiSbYaF" to i64), i64 ptrtoint (ptr getelementptr inbounds ([2 x i32], ptr @__swift_async_entry_functlets, i32 0, i32 1) to i64)) to i32)], section "__TEXT,__swift_as_entry, coalesced, no_dead_strip", no_sanitize_address, align 4
|
||||||
|
// ENABLED: @__swift_async_ret_functlets = internal constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @"$s5async6callerySiSbYaFTQ1_" to i64), i64 ptrtoint (ptr @__swift_async_ret_functlets to i64)) to i32)], section "__TEXT,__swift_as_ret, coalesced, no_dead_strip", no_sanitize_address, align 4
|
||||||
|
|
||||||
|
// DISABLED-NOT: @__swift_async_entry_functlets
|
||||||
|
// DISABLED-NOT: @__swift_async_ret_functlets
|
||||||
|
// DISABLED-NOT: s5async6calleeyyYaF.0
|
||||||
|
// DISABLED-NOT: s5async6callerySiSbYaF.0
|
||||||
|
|
||||||
|
@inline(never)
|
||||||
|
public func plusOne() {
|
||||||
|
print("+1")
|
||||||
|
}
|
||||||
|
|
||||||
|
@inline(never)
|
||||||
|
public func minusOne() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@inline(never)
|
||||||
|
public func callee() async {
|
||||||
|
print("callee")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func caller(_ b: Bool) async -> Int {
|
||||||
|
plusOne()
|
||||||
|
|
||||||
|
if b {
|
||||||
|
await callee()
|
||||||
|
}
|
||||||
|
|
||||||
|
minusOne()
|
||||||
|
return 1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user