1 //===- CoroCleanup.cpp - Coroutine Cleanup Pass ---------------------------===//
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 #include "llvm/Transforms/Coroutines/CoroCleanup.h"
10 #include "CoroInternal.h"
11 #include "llvm/IR/IRBuilder.h"
12 #include "llvm/IR/InstIterator.h"
13 #include "llvm/IR/LegacyPassManager.h"
14 #include "llvm/Pass.h"
15 #include "llvm/Transforms/Scalar.h"
16
17 using namespace llvm;
18
19 #define DEBUG_TYPE "coro-cleanup"
20
21 namespace {
22 // Created on demand if CoroCleanup pass has work to do.
23 struct Lowerer : coro::LowererBase {
24 IRBuilder<> Builder;
Lowerer__anon5127c88e0111::Lowerer25 Lowerer(Module &M) : LowererBase(M), Builder(Context) {}
26 bool lowerRemainingCoroIntrinsics(Function &F);
27 };
28 }
29
simplifyCFG(Function & F)30 static void simplifyCFG(Function &F) {
31 llvm::legacy::FunctionPassManager FPM(F.getParent());
32 FPM.add(createCFGSimplificationPass());
33
34 FPM.doInitialization();
35 FPM.run(F);
36 FPM.doFinalization();
37 }
38
lowerSubFn(IRBuilder<> & Builder,CoroSubFnInst * SubFn)39 static void lowerSubFn(IRBuilder<> &Builder, CoroSubFnInst *SubFn) {
40 Builder.SetInsertPoint(SubFn);
41 Value *FrameRaw = SubFn->getFrame();
42 int Index = SubFn->getIndex();
43
44 auto *FrameTy = StructType::get(
45 SubFn->getContext(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()});
46 PointerType *FramePtrTy = FrameTy->getPointerTo();
47
48 Builder.SetInsertPoint(SubFn);
49 auto *FramePtr = Builder.CreateBitCast(FrameRaw, FramePtrTy);
50 auto *Gep = Builder.CreateConstInBoundsGEP2_32(FrameTy, FramePtr, 0, Index);
51 auto *Load = Builder.CreateLoad(FrameTy->getElementType(Index), Gep);
52
53 SubFn->replaceAllUsesWith(Load);
54 }
55
lowerRemainingCoroIntrinsics(Function & F)56 bool Lowerer::lowerRemainingCoroIntrinsics(Function &F) {
57 bool Changed = false;
58
59 for (auto IB = inst_begin(F), E = inst_end(F); IB != E;) {
60 Instruction &I = *IB++;
61 if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
62 switch (II->getIntrinsicID()) {
63 default:
64 continue;
65 case Intrinsic::coro_begin:
66 II->replaceAllUsesWith(II->getArgOperand(1));
67 break;
68 case Intrinsic::coro_free:
69 II->replaceAllUsesWith(II->getArgOperand(1));
70 break;
71 case Intrinsic::coro_alloc:
72 II->replaceAllUsesWith(ConstantInt::getTrue(Context));
73 break;
74 case Intrinsic::coro_id:
75 case Intrinsic::coro_id_retcon:
76 case Intrinsic::coro_id_retcon_once:
77 case Intrinsic::coro_id_async:
78 II->replaceAllUsesWith(ConstantTokenNone::get(Context));
79 break;
80 case Intrinsic::coro_subfn_addr:
81 lowerSubFn(Builder, cast<CoroSubFnInst>(II));
82 break;
83 case Intrinsic::coro_async_size_replace:
84 auto *Target = cast<ConstantStruct>(
85 cast<GlobalVariable>(II->getArgOperand(0)->stripPointerCasts())
86 ->getInitializer());
87 auto *Source = cast<ConstantStruct>(
88 cast<GlobalVariable>(II->getArgOperand(1)->stripPointerCasts())
89 ->getInitializer());
90 auto *TargetSize = Target->getOperand(1);
91 auto *SourceSize = Source->getOperand(1);
92 if (TargetSize->isElementWiseEqual(SourceSize)) {
93 break;
94 }
95 auto *TargetRelativeFunOffset = Target->getOperand(0);
96 auto *NewFuncPtrStruct = ConstantStruct::get(
97 Target->getType(), TargetRelativeFunOffset, SourceSize);
98 Target->replaceAllUsesWith(NewFuncPtrStruct);
99 break;
100 }
101 II->eraseFromParent();
102 Changed = true;
103 }
104 }
105
106 if (Changed) {
107 // After replacement were made we can cleanup the function body a little.
108 simplifyCFG(F);
109 }
110
111 return Changed;
112 }
113
declaresCoroCleanupIntrinsics(const Module & M)114 static bool declaresCoroCleanupIntrinsics(const Module &M) {
115 return coro::declaresIntrinsics(
116 M, {"llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.subfn.addr",
117 "llvm.coro.free", "llvm.coro.id", "llvm.coro.id.retcon",
118 "llvm.coro.id.retcon.once", "llvm.coro.async.size.replace"});
119 }
120
run(Function & F,FunctionAnalysisManager & AM)121 PreservedAnalyses CoroCleanupPass::run(Function &F,
122 FunctionAnalysisManager &AM) {
123 auto &M = *F.getParent();
124 if (!declaresCoroCleanupIntrinsics(M) ||
125 !Lowerer(M).lowerRemainingCoroIntrinsics(F))
126 return PreservedAnalyses::all();
127
128 return PreservedAnalyses::none();
129 }
130
131 namespace {
132
133 struct CoroCleanupLegacy : FunctionPass {
134 static char ID; // Pass identification, replacement for typeid
135
CoroCleanupLegacy__anon5127c88e0211::CoroCleanupLegacy136 CoroCleanupLegacy() : FunctionPass(ID) {
137 initializeCoroCleanupLegacyPass(*PassRegistry::getPassRegistry());
138 }
139
140 std::unique_ptr<Lowerer> L;
141
142 // This pass has work to do only if we find intrinsics we are going to lower
143 // in the module.
doInitialization__anon5127c88e0211::CoroCleanupLegacy144 bool doInitialization(Module &M) override {
145 if (declaresCoroCleanupIntrinsics(M))
146 L = std::make_unique<Lowerer>(M);
147 return false;
148 }
149
runOnFunction__anon5127c88e0211::CoroCleanupLegacy150 bool runOnFunction(Function &F) override {
151 if (L)
152 return L->lowerRemainingCoroIntrinsics(F);
153 return false;
154 }
getAnalysisUsage__anon5127c88e0211::CoroCleanupLegacy155 void getAnalysisUsage(AnalysisUsage &AU) const override {
156 if (!L)
157 AU.setPreservesAll();
158 }
getPassName__anon5127c88e0211::CoroCleanupLegacy159 StringRef getPassName() const override { return "Coroutine Cleanup"; }
160 };
161 }
162
163 char CoroCleanupLegacy::ID = 0;
164 INITIALIZE_PASS(CoroCleanupLegacy, "coro-cleanup",
165 "Lower all coroutine related intrinsics", false, false)
166
createCoroCleanupLegacyPass()167 Pass *llvm::createCoroCleanupLegacyPass() { return new CoroCleanupLegacy(); }
168