Skip to content

Commit deb6a63

Browse files
Implement LLVM bits
1 parent 56ee6a1 commit deb6a63

19 files changed

+364
-9
lines changed

llvm/include/llvm/IR/Instruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,10 @@ class Instruction : public User,
445445
/// !annotation metadata, append the tuple to
446446
/// the existing node.
447447
void addAnnotationMetadata(SmallVector<StringRef> Annotations);
448+
449+
/// Returns true if an !annotation metadata is set to this instruction.
450+
bool hasAnnotationMetadata(StringRef Name) const;
451+
448452
/// Returns the AA metadata for this instruction.
449453
AAMDNodes getAAMetadata() const;
450454

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===- CoroAnnotationElide.h - Optimizing a coro_must_elide call ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// \file
10+
// This pass transforms all Call or Invoke instructions that are annotated
11+
// "coro_must_elide" to call the `.noalloc` variant of coroutine instead.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_TRANSFORMS_COROUTINES_COROANNOTATIONELIDE_H
16+
#define LLVM_TRANSFORMS_COROUTINES_COROANNOTATIONELIDE_H
17+
18+
#include "llvm/Analysis/CGSCCPassManager.h"
19+
#include "llvm/Analysis/LazyCallGraph.h"
20+
#include "llvm/IR/PassManager.h"
21+
22+
namespace llvm {
23+
24+
struct CoroAnnotationElidePass : PassInfoMixin<CoroAnnotationElidePass> {
25+
CoroAnnotationElidePass() {}
26+
27+
PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
28+
LazyCallGraph &CG, CGSCCUpdateResult &UR);
29+
30+
static bool isRequired() { return false; }
31+
};
32+
} // end namespace llvm
33+
34+
#endif // LLVM_TRANSFORMS_COROUTINES_COROANNOTATIONELIDE_H

llvm/lib/IR/Metadata.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,22 @@ void Instruction::addAnnotationMetadata(StringRef Name) {
17031703
setMetadata(LLVMContext::MD_annotation, MD);
17041704
}
17051705

1706+
bool Instruction::hasAnnotationMetadata(StringRef Name) const {
1707+
auto *Metadata = getMetadata(LLVMContext::MD_annotation);
1708+
if (!Metadata)
1709+
return false;
1710+
1711+
auto *Tuple = cast<MDTuple>(Metadata);
1712+
for (auto &N : Tuple->operands()) {
1713+
if (auto *S = dyn_cast<MDString>(N.get())) {
1714+
if (S->getString() == Name) {
1715+
return true;
1716+
}
1717+
}
1718+
}
1719+
return false;
1720+
}
1721+
17061722
AAMDNodes Instruction::getAAMetadata() const {
17071723
AAMDNodes Result;
17081724
// Not using Instruction::hasMetadata() because we're not interested in

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
#include "llvm/Target/TargetMachine.h"
135135
#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h"
136136
#include "llvm/Transforms/CFGuard.h"
137+
#include "llvm/Transforms/Coroutines/CoroAnnotationElide.h"
137138
#include "llvm/Transforms/Coroutines/CoroCleanup.h"
138139
#include "llvm/Transforms/Coroutines/CoroConditionalWrapper.h"
139140
#include "llvm/Transforms/Coroutines/CoroEarly.h"

llvm/lib/Passes/PassBuilderPipelines.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "llvm/Support/VirtualFileSystem.h"
3333
#include "llvm/Target/TargetMachine.h"
3434
#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h"
35+
#include "llvm/Transforms/Coroutines/CoroAnnotationElide.h"
3536
#include "llvm/Transforms/Coroutines/CoroCleanup.h"
3637
#include "llvm/Transforms/Coroutines/CoroConditionalWrapper.h"
3738
#include "llvm/Transforms/Coroutines/CoroEarly.h"
@@ -968,8 +969,8 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
968969
// it's been modified since.
969970
MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(
970971
RequireAnalysisPass<ShouldNotRunFunctionPassesAnalysis, Function>()));
971-
972972
MainCGPipeline.addPass(CoroSplitPass(Level != OptimizationLevel::O0));
973+
MainCGPipeline.addPass(CoroAnnotationElidePass());
973974

974975
// Make sure we don't affect potential future NoRerun CGSCC adaptors.
975976
MIWP.addLateModulePass(createModuleToFunctionPassAdaptor(

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ CGSCC_PASS("attributor-light-cgscc", AttributorLightCGSCCPass())
234234
CGSCC_PASS("invalidate<all>", InvalidateAllAnalysesPass())
235235
CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass())
236236
CGSCC_PASS("openmp-opt-cgscc", OpenMPOptCGSCCPass())
237+
CGSCC_PASS("coro-annotation-elide", CoroAnnotationElidePass())
237238
#undef CGSCC_PASS
238239

239240
#ifndef CGSCC_PASS_WITH_PARAMS

llvm/lib/Transforms/Coroutines/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_llvm_component_library(LLVMCoroutines
22
Coroutines.cpp
3+
CoroAnnotationElide.cpp
34
CoroCleanup.cpp
45
CoroConditionalWrapper.cpp
56
CoroEarly.cpp
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//===- CoroSplit.cpp - Converts a coroutine into a state machine ----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
//===----------------------------------------------------------------------===//
10+
11+
#include "llvm/Transforms/Coroutines/CoroAnnotationElide.h"
12+
13+
#include "llvm/Analysis/LazyCallGraph.h"
14+
#include "llvm/IR/Analysis.h"
15+
#include "llvm/IR/IRBuilder.h"
16+
#include "llvm/IR/InstIterator.h"
17+
#include "llvm/IR/Instruction.h"
18+
#include "llvm/IR/Module.h"
19+
20+
#include <cassert>
21+
22+
using namespace llvm;
23+
24+
#define DEBUG_TYPE "coro-annotation-elide"
25+
26+
#define CORO_MUST_ELIDE_ANNOTATION "coro_must_elide"
27+
28+
static Instruction *getFirstNonAllocaInTheEntryBlock(Function *F) {
29+
for (Instruction &I : F->getEntryBlock())
30+
if (!isa<AllocaInst>(&I))
31+
return &I;
32+
llvm_unreachable("no terminator in the entry block");
33+
}
34+
35+
static Value *allocateFrameInCaller(Function *Caller, uint64_t FrameSize,
36+
Align FrameAlign) {
37+
LLVMContext &C = Caller->getContext();
38+
BasicBlock::iterator InsertPt =
39+
getFirstNonAllocaInTheEntryBlock(Caller)->getIterator();
40+
const DataLayout &DL = Caller->getDataLayout();
41+
auto FrameTy = ArrayType::get(Type::getInt8Ty(C), FrameSize);
42+
auto *Frame = new AllocaInst(FrameTy, DL.getAllocaAddrSpace(), "", InsertPt);
43+
Frame->setAlignment(FrameAlign);
44+
return new BitCastInst(Frame, PointerType::getUnqual(C), "vFrame", InsertPt);
45+
}
46+
47+
static void processCall(CallBase *CB, Function *Caller, Function *NewCallee,
48+
uint64_t FrameSize, Align FrameAlign) {
49+
auto *FramePtr = allocateFrameInCaller(Caller, FrameSize, FrameAlign);
50+
CB->setCalledFunction(NewCallee->getFunctionType(), NewCallee);
51+
auto NewCBInsertPt = CB->getIterator();
52+
llvm::CallBase *NewCB = nullptr;
53+
SmallVector<Value *, 4> NewArgs;
54+
NewArgs.append(CB->arg_begin(), CB->arg_end());
55+
NewArgs.push_back(FramePtr);
56+
57+
// TODO: See CallBase::Create(CallBase*, ...)
58+
if (auto *CI = dyn_cast<CallInst>(CB)) {
59+
auto *NewCI = CallInst::Create(NewCallee->getFunctionType(), NewCallee,
60+
NewArgs, "", NewCBInsertPt);
61+
NewCI->setTailCallKind(CI->getTailCallKind());
62+
NewCB = NewCI;
63+
} else if (auto *II = dyn_cast<InvokeInst>(CB)) {
64+
NewCB = InvokeInst::Create(NewCallee->getFunctionType(), NewCallee,
65+
II->getNormalDest(), II->getUnwindDest(),
66+
NewArgs, std::nullopt, "", NewCBInsertPt);
67+
} else {
68+
llvm_unreachable("CallBase should either be Call or Invoke!");
69+
}
70+
71+
NewCB->setCallingConv(CB->getCallingConv());
72+
NewCB->setAttributes(CB->getAttributes());
73+
NewCB->setDebugLoc(CB->getDebugLoc());
74+
std::copy(CB->bundle_op_info_begin(), CB->bundle_op_info_end(),
75+
NewCB->bundle_op_info_begin());
76+
77+
CB->replaceAllUsesWith(NewCB);
78+
CB->eraseFromParent();
79+
}
80+
81+
PreservedAnalyses CoroAnnotationElidePass::run(LazyCallGraph::SCC &C,
82+
CGSCCAnalysisManager &AM,
83+
LazyCallGraph &CG,
84+
CGSCCUpdateResult &UR) {
85+
bool Changed = false;
86+
87+
for (LazyCallGraph::Node &N : C) {
88+
Function *Callee = &N.getFunction();
89+
Function *NewCallee = Callee->getParent()->getFunction(
90+
(Callee->getName() + ".noalloc").str());
91+
if (!NewCallee) {
92+
continue;
93+
}
94+
95+
auto FrameSize = NewCallee->getParamDereferenceableBytes(0);
96+
auto FrameAlign = NewCallee->getParamAlign(0).valueOrOne();
97+
llvm::dbgs() << "Callee: ";
98+
Callee->dump();
99+
100+
SmallVector<CallBase *, 4> Users;
101+
for (auto *U : Callee->users()) {
102+
if (auto *CB = dyn_cast<CallBase>(U)) {
103+
Users.push_back(CB);
104+
}
105+
}
106+
107+
for (auto *CB : Users) {
108+
auto *Caller = CB->getFunction();
109+
if (Caller && Caller->isPresplitCoroutine() &&
110+
CB->hasAnnotationMetadata(CORO_MUST_ELIDE_ANNOTATION)) {
111+
processCall(CB, Caller, NewCallee, FrameSize, FrameAlign);
112+
Changed = true;
113+
}
114+
}
115+
}
116+
return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
117+
}

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ bool declaresIntrinsics(const Module &M,
2626
const std::initializer_list<StringRef>);
2727
void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
2828

29+
void suppressCoroAllocs(CoroIdInst *CoroId);
30+
void suppressCoroAllocs(LLVMContext &Context,
31+
ArrayRef<CoroAllocInst *> CoroAllocs);
32+
2933
/// Attempts to rewrite the location operand of debug intrinsics in terms of
3034
/// the coroutine frame pointer, folding pointer offsets into the DIExpression
3135
/// of the intrinsic.

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/ADT/PriorityWorklist.h"
2626
#include "llvm/ADT/SmallPtrSet.h"
2727
#include "llvm/ADT/SmallVector.h"
28+
#include "llvm/ADT/StringExtras.h"
2829
#include "llvm/ADT/StringRef.h"
2930
#include "llvm/ADT/Twine.h"
3031
#include "llvm/Analysis/CFG.h"
@@ -1179,6 +1180,14 @@ static void updateAsyncFuncPointerContextSize(coro::Shape &Shape) {
11791180
Shape.AsyncLowering.AsyncFuncPointer->setInitializer(NewFuncPtrStruct);
11801181
}
11811182

1183+
static TypeSize getFrameSizeForShape(coro::Shape &Shape) {
1184+
// In the same function all coro.sizes should have the same result type.
1185+
auto *SizeIntrin = Shape.CoroSizes.back();
1186+
Module *M = SizeIntrin->getModule();
1187+
const DataLayout &DL = M->getDataLayout();
1188+
return DL.getTypeAllocSize(Shape.FrameTy);
1189+
}
1190+
11821191
static void replaceFrameSizeAndAlignment(coro::Shape &Shape) {
11831192
if (Shape.ABI == coro::ABI::Async)
11841193
updateAsyncFuncPointerContextSize(Shape);
@@ -1194,10 +1203,8 @@ static void replaceFrameSizeAndAlignment(coro::Shape &Shape) {
11941203

11951204
// In the same function all coro.sizes should have the same result type.
11961205
auto *SizeIntrin = Shape.CoroSizes.back();
1197-
Module *M = SizeIntrin->getModule();
1198-
const DataLayout &DL = M->getDataLayout();
1199-
auto Size = DL.getTypeAllocSize(Shape.FrameTy);
1200-
auto *SizeConstant = ConstantInt::get(SizeIntrin->getType(), Size);
1206+
auto *SizeConstant =
1207+
ConstantInt::get(SizeIntrin->getType(), getFrameSizeForShape(Shape));
12011208

12021209
for (CoroSizeInst *CS : Shape.CoroSizes) {
12031210
CS->replaceAllUsesWith(SizeConstant);
@@ -2077,6 +2084,46 @@ static void addPrepareFunction(const Module &M,
20772084
Fns.push_back(PrepareFn);
20782085
}
20792086

2087+
static Function *createNoAllocVariant(Function &F, ValueToValueMapTy &VMap,
2088+
coro::Shape &Shape) {
2089+
auto *OrigFnTy = F.getFunctionType();
2090+
auto OldParams = OrigFnTy->params();
2091+
2092+
SmallVector<Type *> NewParams;
2093+
NewParams.reserve(OldParams.size() + 1);
2094+
for (Type *T : OldParams) {
2095+
NewParams.push_back(T);
2096+
}
2097+
NewParams.push_back(PointerType::getUnqual(Shape.FrameTy));
2098+
2099+
auto *NewFnTy = FunctionType::get(OrigFnTy->getReturnType(), NewParams,
2100+
OrigFnTy->isVarArg());
2101+
Function *NoAllocF =
2102+
Function::Create(NewFnTy, F.getLinkage(), F.getName() + ".noalloc");
2103+
unsigned int Idx = 0;
2104+
for (const auto &I : F.args()) {
2105+
VMap[&I] = NoAllocF->getArg(Idx++);
2106+
}
2107+
SmallVector<ReturnInst *, 4> Returns;
2108+
CloneFunctionInto(NoAllocF, &F, VMap,
2109+
CloneFunctionChangeType::LocalChangesOnly, Returns);
2110+
2111+
if (Shape.CoroBegin) {
2112+
auto *NewCoroBegin = cast_if_present<CoroBeginInst>(VMap[Shape.CoroBegin]);
2113+
auto *NewCoroId = cast<CoroIdInst>(NewCoroBegin->getId());
2114+
coro::replaceCoroFree(NewCoroId, /*Elide=*/true);
2115+
coro::suppressCoroAllocs(NewCoroId);
2116+
NewCoroBegin->replaceAllUsesWith(NoAllocF->getArg(Idx));
2117+
NewCoroBegin->eraseFromParent();
2118+
}
2119+
2120+
Module *M = F.getParent();
2121+
M->getFunctionList().insert(M->end(), NoAllocF);
2122+
2123+
removeUnreachableBlocks(*NoAllocF);
2124+
return NoAllocF;
2125+
}
2126+
20802127
CoroSplitPass::CoroSplitPass(bool OptimizeFrame)
20812128
: MaterializableCallback(coro::defaultMaterializable),
20822129
OptimizeFrame(OptimizeFrame) {}
@@ -2108,13 +2155,14 @@ PreservedAnalyses CoroSplitPass::run(LazyCallGraph::SCC &C,
21082155
// Split all the coroutines.
21092156
for (LazyCallGraph::Node *N : Coroutines) {
21102157
Function &F = N->getFunction();
2158+
21112159
LLVM_DEBUG(dbgs() << "CoroSplit: Processing coroutine '" << F.getName()
21122160
<< "\n");
21132161
F.setSplittedCoroutine();
21142162

21152163
SmallVector<Function *, 4> Clones;
21162164
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
2117-
const coro::Shape Shape =
2165+
coro::Shape Shape =
21182166
splitCoroutine(F, Clones, FAM.getResult<TargetIRAnalysis>(F),
21192167
OptimizeFrame, MaterializableCallback);
21202168
removeCoroEndsFromRampFunction(Shape);
@@ -2133,11 +2181,28 @@ PreservedAnalyses CoroSplitPass::run(LazyCallGraph::SCC &C,
21332181
for (Function *Clone : Clones)
21342182
UR.CWorklist.insert(CG.lookupSCC(CG.get(*Clone)));
21352183
}
2136-
}
21372184

2138-
for (auto *PrepareFn : PrepareFns) {
2139-
replaceAllPrepares(PrepareFn, CG, C);
2185+
if (Shape.ABI == coro::ABI::Switch) {
2186+
ValueToValueMapTy VMap;
2187+
auto *NoAllocF = createNoAllocVariant(F, VMap, Shape);
2188+
NoAllocF->addFnAttr("elided-coro");
2189+
auto NewAttrs = NoAllocF->getAttributes();
2190+
// We just appended the frame pointer as the last argument of the new
2191+
// function.
2192+
auto FrameIdx = NoAllocF->arg_size() - 1;
2193+
// When we elide allocation, we read these attributes to determine the
2194+
// frame size and alignment.
2195+
addFramePointerAttrs(NewAttrs, NoAllocF->getContext(), FrameIdx,
2196+
Shape.FrameSize, Shape.FrameAlign,
2197+
/*NoAlias=*/false);
2198+
2199+
NoAllocF->setAttributes(NewAttrs);
21402200
}
2201+
}
2202+
2203+
for (auto *PrepareFn : PrepareFns) {
2204+
replaceAllPrepares(PrepareFn, CG, C);
2205+
}
21412206

21422207
return PreservedAnalyses::none();
21432208
}

0 commit comments

Comments
 (0)