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/Function.h" |
12 | #include "llvm/IR/IRBuilder.h" |
13 | #include "llvm/IR/InstIterator.h" |
14 | #include "llvm/IR/Module.h" |
15 | #include "llvm/IR/PassManager.h" |
16 | #include "llvm/Transforms/Scalar/SimplifyCFG.h" |
17 | |
18 | using namespace llvm; |
19 | |
20 | #define DEBUG_TYPE "coro-cleanup" |
21 | |
22 | namespace { |
23 | // Created on demand if CoroCleanup pass has work to do. |
24 | struct Lowerer : coro::LowererBase { |
25 | IRBuilder<> Builder; |
26 | Lowerer(Module &M) : LowererBase(M), Builder(Context) {} |
27 | bool lower(Function &F); |
28 | }; |
29 | } |
30 | |
31 | static void lowerSubFn(IRBuilder<> &Builder, CoroSubFnInst *SubFn) { |
32 | Builder.SetInsertPoint(SubFn); |
33 | Value *FramePtr = SubFn->getFrame(); |
34 | int Index = SubFn->getIndex(); |
35 | |
36 | auto *FrameTy = StructType::get(Context&: SubFn->getContext(), |
37 | Elements: {Builder.getPtrTy(), Builder.getPtrTy()}); |
38 | |
39 | Builder.SetInsertPoint(SubFn); |
40 | auto *Gep = Builder.CreateConstInBoundsGEP2_32(Ty: FrameTy, Ptr: FramePtr, Idx0: 0, Idx1: Index); |
41 | auto *Load = Builder.CreateLoad(Ty: FrameTy->getElementType(N: Index), Ptr: Gep); |
42 | |
43 | SubFn->replaceAllUsesWith(V: Load); |
44 | } |
45 | |
46 | bool Lowerer::lower(Function &F) { |
47 | bool IsPrivateAndUnprocessed = F.isPresplitCoroutine() && F.hasLocalLinkage(); |
48 | bool Changed = false; |
49 | |
50 | for (Instruction &I : llvm::make_early_inc_range(Range: instructions(F))) { |
51 | if (auto *II = dyn_cast<IntrinsicInst>(Val: &I)) { |
52 | switch (II->getIntrinsicID()) { |
53 | default: |
54 | continue; |
55 | case Intrinsic::coro_begin: |
56 | II->replaceAllUsesWith(V: II->getArgOperand(i: 1)); |
57 | break; |
58 | case Intrinsic::coro_free: |
59 | II->replaceAllUsesWith(V: II->getArgOperand(i: 1)); |
60 | break; |
61 | case Intrinsic::coro_alloc: |
62 | II->replaceAllUsesWith(V: ConstantInt::getTrue(Context)); |
63 | break; |
64 | case Intrinsic::coro_async_resume: |
65 | II->replaceAllUsesWith( |
66 | V: ConstantPointerNull::get(T: cast<PointerType>(Val: I.getType()))); |
67 | break; |
68 | case Intrinsic::coro_id: |
69 | case Intrinsic::coro_id_retcon: |
70 | case Intrinsic::coro_id_retcon_once: |
71 | case Intrinsic::coro_id_async: |
72 | II->replaceAllUsesWith(V: ConstantTokenNone::get(Context)); |
73 | break; |
74 | case Intrinsic::coro_subfn_addr: |
75 | lowerSubFn(Builder, SubFn: cast<CoroSubFnInst>(Val: II)); |
76 | break; |
77 | case Intrinsic::coro_end: |
78 | case Intrinsic::coro_suspend_retcon: |
79 | if (IsPrivateAndUnprocessed) { |
80 | II->replaceAllUsesWith(V: UndefValue::get(T: II->getType())); |
81 | } else |
82 | continue; |
83 | break; |
84 | case Intrinsic::coro_async_size_replace: |
85 | auto *Target = cast<ConstantStruct>( |
86 | Val: cast<GlobalVariable>(Val: II->getArgOperand(i: 0)->stripPointerCasts()) |
87 | ->getInitializer()); |
88 | auto *Source = cast<ConstantStruct>( |
89 | Val: cast<GlobalVariable>(Val: II->getArgOperand(i: 1)->stripPointerCasts()) |
90 | ->getInitializer()); |
91 | auto *TargetSize = Target->getOperand(i_nocapture: 1); |
92 | auto *SourceSize = Source->getOperand(i_nocapture: 1); |
93 | if (TargetSize->isElementWiseEqual(Y: SourceSize)) { |
94 | break; |
95 | } |
96 | auto *TargetRelativeFunOffset = Target->getOperand(i_nocapture: 0); |
97 | auto *NewFuncPtrStruct = ConstantStruct::get( |
98 | T: Target->getType(), Vs: TargetRelativeFunOffset, Vs: SourceSize); |
99 | Target->replaceAllUsesWith(V: NewFuncPtrStruct); |
100 | break; |
101 | } |
102 | II->eraseFromParent(); |
103 | Changed = true; |
104 | } |
105 | } |
106 | |
107 | return Changed; |
108 | } |
109 | |
110 | static bool declaresCoroCleanupIntrinsics(const Module &M) { |
111 | return coro::declaresIntrinsics( |
112 | M, {"llvm.coro.alloc" , "llvm.coro.begin" , "llvm.coro.subfn.addr" , |
113 | "llvm.coro.free" , "llvm.coro.id" , "llvm.coro.id.retcon" , |
114 | "llvm.coro.id.async" , "llvm.coro.id.retcon.once" , |
115 | "llvm.coro.async.size.replace" , "llvm.coro.async.resume" }); |
116 | } |
117 | |
118 | PreservedAnalyses CoroCleanupPass::run(Module &M, |
119 | ModuleAnalysisManager &MAM) { |
120 | if (!declaresCoroCleanupIntrinsics(M)) |
121 | return PreservedAnalyses::all(); |
122 | |
123 | FunctionAnalysisManager &FAM = |
124 | MAM.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager(); |
125 | |
126 | FunctionPassManager FPM; |
127 | FPM.addPass(Pass: SimplifyCFGPass()); |
128 | |
129 | PreservedAnalyses FuncPA; |
130 | FuncPA.preserveSet<CFGAnalyses>(); |
131 | |
132 | Lowerer L(M); |
133 | for (auto &F : M) { |
134 | if (L.lower(F)) { |
135 | FAM.invalidate(IR&: F, PA: FuncPA); |
136 | FPM.run(IR&: F, AM&: FAM); |
137 | } |
138 | } |
139 | |
140 | return PreservedAnalyses::none(); |
141 | } |
142 | |