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
18using namespace llvm;
19
20#define DEBUG_TYPE "coro-cleanup"
21
22namespace {
23// Created on demand if CoroCleanup pass has work to do.
24struct Lowerer : coro::LowererBase {
25 IRBuilder<> Builder;
26 Lowerer(Module &M) : LowererBase(M), Builder(Context) {}
27 bool lower(Function &F);
28};
29}
30
31static 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
46bool 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 case Intrinsic::coro_begin_custom_abi:
57 II->replaceAllUsesWith(V: II->getArgOperand(i: 1));
58 break;
59 case Intrinsic::coro_free:
60 II->replaceAllUsesWith(V: II->getArgOperand(i: 1));
61 break;
62 case Intrinsic::coro_alloc:
63 II->replaceAllUsesWith(V: ConstantInt::getTrue(Context));
64 break;
65 case Intrinsic::coro_async_resume:
66 II->replaceAllUsesWith(
67 V: ConstantPointerNull::get(T: cast<PointerType>(Val: I.getType())));
68 break;
69 case Intrinsic::coro_id:
70 case Intrinsic::coro_id_retcon:
71 case Intrinsic::coro_id_retcon_once:
72 case Intrinsic::coro_id_async:
73 II->replaceAllUsesWith(V: ConstantTokenNone::get(Context));
74 break;
75 case Intrinsic::coro_subfn_addr:
76 lowerSubFn(Builder, SubFn: cast<CoroSubFnInst>(Val: II));
77 break;
78 case Intrinsic::coro_end:
79 case Intrinsic::coro_suspend_retcon:
80 if (IsPrivateAndUnprocessed) {
81 II->replaceAllUsesWith(V: PoisonValue::get(T: II->getType()));
82 } else
83 continue;
84 break;
85 case Intrinsic::coro_async_size_replace:
86 auto *Target = cast<ConstantStruct>(
87 Val: cast<GlobalVariable>(Val: II->getArgOperand(i: 0)->stripPointerCasts())
88 ->getInitializer());
89 auto *Source = cast<ConstantStruct>(
90 Val: cast<GlobalVariable>(Val: II->getArgOperand(i: 1)->stripPointerCasts())
91 ->getInitializer());
92 auto *TargetSize = Target->getOperand(i_nocapture: 1);
93 auto *SourceSize = Source->getOperand(i_nocapture: 1);
94 if (TargetSize->isElementWiseEqual(Y: SourceSize)) {
95 break;
96 }
97 auto *TargetRelativeFunOffset = Target->getOperand(i_nocapture: 0);
98 auto *NewFuncPtrStruct = ConstantStruct::get(
99 T: Target->getType(), Vs: TargetRelativeFunOffset, Vs: SourceSize);
100 Target->replaceAllUsesWith(V: NewFuncPtrStruct);
101 break;
102 }
103 II->eraseFromParent();
104 Changed = true;
105 }
106 }
107
108 return Changed;
109}
110
111static bool declaresCoroCleanupIntrinsics(const Module &M) {
112 return coro::declaresIntrinsics(
113 M, List: {Intrinsic::coro_alloc, Intrinsic::coro_begin,
114 Intrinsic::coro_subfn_addr, Intrinsic::coro_free, Intrinsic::coro_id,
115 Intrinsic::coro_id_retcon, Intrinsic::coro_id_async,
116 Intrinsic::coro_id_retcon_once, Intrinsic::coro_async_size_replace,
117 Intrinsic::coro_async_resume, Intrinsic::coro_begin_custom_abi});
118}
119
120PreservedAnalyses CoroCleanupPass::run(Module &M,
121 ModuleAnalysisManager &MAM) {
122 if (!declaresCoroCleanupIntrinsics(M))
123 return PreservedAnalyses::all();
124
125 FunctionAnalysisManager &FAM =
126 MAM.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager();
127
128 FunctionPassManager FPM;
129 FPM.addPass(Pass: SimplifyCFGPass());
130
131 PreservedAnalyses FuncPA;
132 FuncPA.preserveSet<CFGAnalyses>();
133
134 Lowerer L(M);
135 for (auto &F : M) {
136 if (L.lower(F)) {
137 FAM.invalidate(IR&: F, PA: FuncPA);
138 FPM.run(IR&: F, AM&: FAM);
139 }
140 }
141
142 return PreservedAnalyses::none();
143}
144