1 //===- CoroEarly.cpp - Coroutine Early Function Pass ----------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 // This pass lowers coroutine intrinsics that hide the details of the exact
10 // calling convention for coroutine resume and destroy functions and details of
11 // the structure of the coroutine frame.
12 //===----------------------------------------------------------------------===//
13 
14 #include "CoroInternal.h"
15 #include "llvm/IR/CallSite.h"
16 #include "llvm/IR/InstIterator.h"
17 #include "llvm/IR/Module.h"
18 #include "llvm/Pass.h"
19 
20 using namespace llvm;
21 
22 #define DEBUG_TYPE "coro-early"
23 
24 namespace {
25 // Created on demand if CoroEarly pass has work to do.
26 class Lowerer : public coro::LowererBase {
27   void lowerResumeOrDestroy(CallSite CS, CoroSubFnInst::ResumeKind);
28 
29 public:
30   Lowerer(Module &M) : LowererBase(M) {}
31   bool lowerEarlyIntrinsics(Function &F);
32 };
33 }
34 
35 // Replace a direct call to coro.resume or coro.destroy with an indirect call to
36 // an address returned by coro.subfn.addr intrinsic. This is done so that
37 // CGPassManager recognizes devirtualization when CoroElide pass replaces a call
38 // to coro.subfn.addr with an appropriate function address.
39 void Lowerer::lowerResumeOrDestroy(CallSite CS,
40                                    CoroSubFnInst::ResumeKind Index) {
41   Value *ResumeAddr =
42       makeSubFnCall(CS.getArgOperand(0), Index, CS.getInstruction());
43   CS.setCalledFunction(ResumeAddr);
44   CS.setCallingConv(CallingConv::Fast);
45 }
46 
47 bool Lowerer::lowerEarlyIntrinsics(Function &F) {
48   bool Changed = false;
49   for (auto IB = inst_begin(F), IE = inst_end(F); IB != IE;) {
50     Instruction &I = *IB++;
51     if (auto CS = CallSite(&I)) {
52       switch (CS.getIntrinsicID()) {
53       default:
54         continue;
55       case Intrinsic::coro_id:
56         // Mark a function that comes out of the frontend that has a coro.begin
57         // with a coroutine attribute.
58         if (auto *CII = cast<CoroIdInst>(&I)) {
59           if (CII->getInfo().isPreSplit())
60             F.addFnAttr(CORO_PRESPLIT_ATTR, UNPREPARED_FOR_SPLIT);
61         }
62         break;
63       case Intrinsic::coro_resume:
64         lowerResumeOrDestroy(CS, CoroSubFnInst::ResumeIndex);
65         break;
66       case Intrinsic::coro_destroy:
67         lowerResumeOrDestroy(CS, CoroSubFnInst::DestroyIndex);
68         break;
69       }
70       Changed = true;
71     }
72   }
73   return Changed;
74 }
75 
76 //===----------------------------------------------------------------------===//
77 //                              Top Level Driver
78 //===----------------------------------------------------------------------===//
79 
80 namespace {
81 
82 struct CoroEarly : public FunctionPass {
83   static char ID; // Pass identification, replacement for typeid.
84   CoroEarly() : FunctionPass(ID) {}
85 
86   std::unique_ptr<Lowerer> L;
87 
88   // This pass has work to do only if we find intrinsics we are going to lower
89   // in the module.
90   bool doInitialization(Module &M) override {
91     if (coro::declaresIntrinsics(
92             M, {"llvm.coro.begin", "llvm.coro.resume", "llvm.coro.destroy"}))
93       L = llvm::make_unique<Lowerer>(M);
94     return false;
95   }
96 
97   bool runOnFunction(Function &F) override {
98     if (!L)
99       return false;
100 
101     return L->lowerEarlyIntrinsics(F);
102   }
103 
104   void getAnalysisUsage(AnalysisUsage &AU) const override {
105     AU.setPreservesCFG();
106   }
107 };
108 }
109 
110 char CoroEarly::ID = 0;
111 INITIALIZE_PASS(CoroEarly, "coro-early", "Lower early coroutine intrinsics",
112                 false, false)
113 
114 Pass *llvm::createCoroEarlyPass() { return new CoroEarly(); }
115