text: //===- CoroCleanup.cpp - Coroutine Cleanup Pass ---------------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  // This pass lowers all remaining coroutine intrinsics.
  //===----------------------------------------------------------------------===//
  
  #include "CoroInternal.h"
  #include "llvm/IR/IRBuilder.h"
  #include "llvm/IR/InstIterator.h"
  #include "llvm/IR/LegacyPassManager.h"
  #include "llvm/Pass.h"
  #include "llvm/Transforms/Scalar.h"
  
  using namespace llvm;
  
  #define DEBUG_TYPE "coro-cleanup"
  
  namespace {
  // Created on demand if CoroCleanup pass has work to do.
  struct Lowerer : coro::LowererBase {
    IRBuilder<> Builder;
    Lowerer(Module &M) : LowererBase(M), Builder(Context) {}
    bool lowerRemainingCoroIntrinsics(Function &F);
  };
  }
  
  static void simplifyCFG(Function &F) {
    llvm::legacy::FunctionPassManager FPM(F.getParent());
    FPM.add(createCFGSimplificationPass());
  
    FPM.doInitialization();
    FPM.run(F);
    FPM.doFinalization();
  }
  
  static void lowerSubFn(IRBuilder<> &Builder, CoroSubFnInst *SubFn) {
    Builder.SetInsertPoint(SubFn);
    Value *FrameRaw = SubFn->getFrame();
    int Index = SubFn->getIndex();
  
    auto *FrameTy = StructType::get(
        SubFn->getContext(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()});
    PointerType *FramePtrTy = FrameTy->getPointerTo();
  
    Builder.SetInsertPoint(SubFn);
    auto *FramePtr = Builder.CreateBitCast(FrameRaw, FramePtrTy);
    auto *Gep = Builder.CreateConstInBoundsGEP2_32(FrameTy, FramePtr, 0, Index);
    auto *Load = Builder.CreateLoad(FrameTy->getElementType(Index), Gep);
  
    SubFn->replaceAllUsesWith(Load);
  }
  
  bool Lowerer::lowerRemainingCoroIntrinsics(Function &F) {
    bool Changed = false;
  
    for (auto IB = inst_begin(F), E = inst_end(F); IB != E;) {
      Instruction &I = *IB++;
      if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
        switch (II->getIntrinsicID()) {
        default:
          continue;
        case Intrinsic::coro_begin:
          II->replaceAllUsesWith(II->getArgOperand(1));
          break;
        case Intrinsic::coro_free:
          II->replaceAllUsesWith(II->getArgOperand(1));
          break;
        case Intrinsic::coro_alloc:
          II->replaceAllUsesWith(ConstantInt::getTrue(Context));
          break;
        case Intrinsic::coro_id:
          II->replaceAllUsesWith(ConstantTokenNone::get(Context));
          break;
        case Intrinsic::coro_subfn_addr:
          lowerSubFn(Builder, cast<CoroSubFnInst>(II));
          break;
        }
        II->eraseFromParent();
        Changed = true;
      }
    }
  
    if (Changed) {
      // After replacement were made we can cleanup the function body a little.
      simplifyCFG(F);
    }
    return Changed;
  }
  
  //===----------------------------------------------------------------------===//
  //                              Top Level Driver
  //===----------------------------------------------------------------------===//
  
  namespace {
  
  struct CoroCleanup : FunctionPass {
    static char ID; // Pass identification, replacement for typeid
  
    CoroCleanup() : FunctionPass(ID) {
      initializeCoroCleanupPass(*PassRegistry::getPassRegistry());
    }
  
    std::unique_ptr<Lowerer> L;
  
    // This pass has work to do only if we find intrinsics we are going to lower
    // in the module.
    bool doInitialization(Module &M) override {
      if (coro::declaresIntrinsics(M, {"llvm.coro.alloc", "llvm.coro.begin",
                                       "llvm.coro.subfn.addr", "llvm.coro.free",
                                       "llvm.coro.id"}))
        L = llvm::make_unique<Lowerer>(M);
      return false;
    }
  
    bool runOnFunction(Function &F) override {
      if (L)
        return L->lowerRemainingCoroIntrinsics(F);
      return false;
    }
    void getAnalysisUsage(AnalysisUsage &AU) const override {
      if (!L)
        AU.setPreservesAll();
    }
    StringRef getPassName() const override { return "Coroutine Cleanup"; }
  };
  }
  
  char CoroCleanup::ID = 0;
  INITIALIZE_PASS(CoroCleanup, "coro-cleanup",
                  "Lower all coroutine related intrinsics", false, false)
  
  Pass *llvm::createCoroCleanupPass() { return new CoroCleanup(); }
  