//===-- CoroInstr.h - Coroutine Intrinsics Instruction Wrappers -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // This file defines classes that make it really easy to deal with intrinsic // functions with the isa/dyncast family of functions. In particular, this // allows you to do things like: // // if (auto *SF = dyn_cast(Inst)) // ... SF->getFrame() ... // // All intrinsic function calls are instances of the call instruction, so these // are all subclasses of the CallInst class. Note that none of these classes // has state or virtual methods, which is an important part of this gross/neat // hack working. // // The helpful comment above is borrowed from llvm/IntrinsicInst.h, we keep // coroutine intrinsic wrappers here since they are only used by the passes in // the Coroutine library. //===----------------------------------------------------------------------===// #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IntrinsicInst.h" namespace llvm { /// This class represents the llvm.coro.subfn.addr instruction. class LLVM_LIBRARY_VISIBILITY CoroSubFnInst : public IntrinsicInst { enum { FrameArg, IndexArg }; public: enum ResumeKind { RestartTrigger = -1, ResumeIndex, DestroyIndex, CleanupIndex, IndexLast, IndexFirst = RestartTrigger }; Value *getFrame() const { return getArgOperand(FrameArg); } ResumeKind getIndex() const { int64_t Index = getRawIndex()->getValue().getSExtValue(); assert(Index >= IndexFirst && Index < IndexLast && "unexpected CoroSubFnInst index argument"); return static_cast(Index); } ConstantInt *getRawIndex() const { return cast(getArgOperand(IndexArg)); } // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_subfn_addr; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.alloc instruction. class LLVM_LIBRARY_VISIBILITY CoroAllocInst : public IntrinsicInst { public: // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_alloc; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.alloc instruction. class LLVM_LIBRARY_VISIBILITY CoroIdInst : public IntrinsicInst { enum { AlignArg, PromiseArg, CoroutineArg, InfoArg }; public: CoroAllocInst *getCoroAlloc() { for (User *U : users()) if (auto *CA = dyn_cast(U)) return CA; return nullptr; } IntrinsicInst *getCoroBegin() { for (User *U : users()) if (auto *II = dyn_cast(U)) if (II->getIntrinsicID() == Intrinsic::coro_begin) return II; llvm_unreachable("no coro.begin associated with coro.id"); } AllocaInst *getPromise() const { Value *Arg = getArgOperand(PromiseArg); return isa(Arg) ? nullptr : cast(Arg->stripPointerCasts()); } void clearPromise() { Value *Arg = getArgOperand(PromiseArg); setArgOperand(PromiseArg, ConstantPointerNull::get(Type::getInt8PtrTy(getContext()))); if (isa(Arg)) return; assert((isa(Arg) || isa(Arg)) && "unexpected instruction designating the promise"); // TODO: Add a check that any remaining users of Inst are after coro.begin // or add code to move the users after coro.begin. auto *Inst = cast(Arg); if (Inst->use_empty()) { Inst->eraseFromParent(); return; } Inst->moveBefore(getCoroBegin()->getNextNode()); } // Info argument of coro.id is // fresh out of the frontend: null ; // outlined : {Init, Return, Susp1, Susp2, ...} ; // postsplit : [resume, destroy, cleanup] ; // // If parts of the coroutine were outlined to protect against undesirable // code motion, these functions will be stored in a struct literal referred to // by the Info parameter. Note: this is only needed before coroutine is split. // // After coroutine is split, resume functions are stored in an array // referred to by this parameter. struct Info { ConstantStruct *OutlinedParts = nullptr; ConstantArray *Resumers = nullptr; bool hasOutlinedParts() const { return OutlinedParts != nullptr; } bool isPostSplit() const { return Resumers != nullptr; } bool isPreSplit() const { return !isPostSplit(); } }; Info getInfo() const { Info Result; auto *GV = dyn_cast(getRawInfo()); if (!GV) return Result; assert(GV->isConstant() && GV->hasDefinitiveInitializer()); Constant *Initializer = GV->getInitializer(); if ((Result.OutlinedParts = dyn_cast(Initializer))) return Result; Result.Resumers = cast(Initializer); return Result; } Constant *getRawInfo() const { return cast(getArgOperand(InfoArg)->stripPointerCasts()); } void setInfo(Constant *C) { setArgOperand(InfoArg, C); } Function *getCoroutine() const { return cast(getArgOperand(CoroutineArg)->stripPointerCasts()); } void setCoroutineSelf() { assert(isa(getArgOperand(CoroutineArg)) && "Coroutine argument is already assigned"); auto *const Int8PtrTy = Type::getInt8PtrTy(getContext()); setArgOperand(CoroutineArg, ConstantExpr::getBitCast(getFunction(), Int8PtrTy)); } // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_id; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.frame instruction. class LLVM_LIBRARY_VISIBILITY CoroFrameInst : public IntrinsicInst { public: // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_frame; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.free instruction. class LLVM_LIBRARY_VISIBILITY CoroFreeInst : public IntrinsicInst { enum { IdArg, FrameArg }; public: Value *getFrame() const { return getArgOperand(FrameArg); } // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_free; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This class represents the llvm.coro.begin instruction. class LLVM_LIBRARY_VISIBILITY CoroBeginInst : public IntrinsicInst { enum { IdArg, MemArg }; public: CoroIdInst *getId() const { return cast(getArgOperand(IdArg)); } Value *getMem() const { return getArgOperand(MemArg); } // Methods for support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_begin; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.save instruction. class LLVM_LIBRARY_VISIBILITY CoroSaveInst : public IntrinsicInst { public: // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_save; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.promise instruction. class LLVM_LIBRARY_VISIBILITY CoroPromiseInst : public IntrinsicInst { enum { FrameArg, AlignArg, FromArg }; public: bool isFromPromise() const { return cast(getArgOperand(FromArg))->isOneValue(); } unsigned getAlignment() const { return cast(getArgOperand(AlignArg))->getZExtValue(); } // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_promise; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.suspend instruction. class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public IntrinsicInst { enum { SaveArg, FinalArg }; public: CoroSaveInst *getCoroSave() const { Value *Arg = getArgOperand(SaveArg); if (auto *SI = dyn_cast(Arg)) return SI; assert(isa(Arg)); return nullptr; } bool isFinal() const { return cast(getArgOperand(FinalArg))->isOneValue(); } // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_suspend; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.size instruction. class LLVM_LIBRARY_VISIBILITY CoroSizeInst : public IntrinsicInst { public: // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_size; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.end instruction. class LLVM_LIBRARY_VISIBILITY CoroEndInst : public IntrinsicInst { enum { FrameArg, UnwindArg }; public: bool isFallthrough() const { return !isUnwind(); } bool isUnwind() const { return cast(getArgOperand(UnwindArg))->isOneValue(); } // Methods to support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_end; } static inline bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; } // End namespace llvm.