//===-- CoroInstr.h - Coroutine Intrinsics Instruction Wrappers -*- C++ -*-===// // // 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 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. //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/raw_ostream.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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_subfn_addr; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_alloc; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents a common base class for llvm.coro.id instructions. class LLVM_LIBRARY_VISIBILITY AnyCoroIdInst : public IntrinsicInst { 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"); } // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { auto ID = I->getIntrinsicID(); return ID == Intrinsic::coro_id || ID == Intrinsic::coro_id_retcon || ID == Intrinsic::coro_id_retcon_once; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.id instruction. class LLVM_LIBRARY_VISIBILITY CoroIdInst : public AnyCoroIdInst { enum { AlignArg, PromiseArg, CoroutineArg, InfoArg }; public: 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_id; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents either the llvm.coro.id.retcon or /// llvm.coro.id.retcon.once instruction. class LLVM_LIBRARY_VISIBILITY AnyCoroIdRetconInst : public AnyCoroIdInst { enum { SizeArg, AlignArg, StorageArg, PrototypeArg, AllocArg, DeallocArg }; public: void checkWellFormed() const; uint64_t getStorageSize() const { return cast(getArgOperand(SizeArg))->getZExtValue(); } uint64_t getStorageAlignment() const { return cast(getArgOperand(AlignArg))->getZExtValue(); } Value *getStorage() const { return getArgOperand(StorageArg); } /// Return the prototype for the continuation function. The type, /// attributes, and calling convention of the continuation function(s) /// are taken from this declaration. Function *getPrototype() const { return cast(getArgOperand(PrototypeArg)->stripPointerCasts()); } /// Return the function to use for allocating memory. Function *getAllocFunction() const { return cast(getArgOperand(AllocArg)->stripPointerCasts()); } /// Return the function to use for deallocating memory. Function *getDeallocFunction() const { return cast(getArgOperand(DeallocArg)->stripPointerCasts()); } // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { auto ID = I->getIntrinsicID(); return ID == Intrinsic::coro_id_retcon || ID == Intrinsic::coro_id_retcon_once; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.id.retcon instruction. class LLVM_LIBRARY_VISIBILITY CoroIdRetconInst : public AnyCoroIdRetconInst { public: // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_id_retcon; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.id.retcon.once instruction. class LLVM_LIBRARY_VISIBILITY CoroIdRetconOnceInst : public AnyCoroIdRetconInst { public: // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_id_retcon_once; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_frame; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_free; } static 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: AnyCoroIdInst *getId() const { return cast(getArgOperand(IdArg)); } Value *getMem() const { return getArgOperand(MemArg); } // Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_begin; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_save; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_promise; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; class LLVM_LIBRARY_VISIBILITY AnyCoroSuspendInst : public IntrinsicInst { public: CoroSaveInst *getCoroSave() const; // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_suspend || I->getIntrinsicID() == Intrinsic::coro_suspend_retcon; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.suspend instruction. class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public AnyCoroSuspendInst { 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_suspend; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; inline CoroSaveInst *AnyCoroSuspendInst::getCoroSave() const { if (auto Suspend = dyn_cast(this)) return Suspend->getCoroSave(); return nullptr; } /// This represents the llvm.coro.suspend.retcon instruction. class LLVM_LIBRARY_VISIBILITY CoroSuspendRetconInst : public AnyCoroSuspendInst { public: op_iterator value_begin() { return arg_begin(); } const_op_iterator value_begin() const { return arg_begin(); } op_iterator value_end() { return arg_end(); } const_op_iterator value_end() const { return arg_end(); } iterator_range value_operands() { return make_range(value_begin(), value_end()); } iterator_range value_operands() const { return make_range(value_begin(), value_end()); } // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_suspend_retcon; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_size; } static 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 bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_end; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.alloca.alloc instruction. class LLVM_LIBRARY_VISIBILITY CoroAllocaAllocInst : public IntrinsicInst { enum { SizeArg, AlignArg }; public: Value *getSize() const { return getArgOperand(SizeArg); } unsigned getAlignment() const { return cast(getArgOperand(AlignArg))->getZExtValue(); } // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_alloca_alloc; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.alloca.get instruction. class LLVM_LIBRARY_VISIBILITY CoroAllocaGetInst : public IntrinsicInst { enum { AllocArg }; public: CoroAllocaAllocInst *getAlloc() const { return cast(getArgOperand(AllocArg)); } // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_alloca_get; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; /// This represents the llvm.coro.alloca.free instruction. class LLVM_LIBRARY_VISIBILITY CoroAllocaFreeInst : public IntrinsicInst { enum { AllocArg }; public: CoroAllocaAllocInst *getAlloc() const { return cast(getArgOperand(AllocArg)); } // Methods to support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::coro_alloca_free; } static bool classof(const Value *V) { return isa(V) && classof(cast(V)); } }; } // End namespace llvm. #endif