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