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