mirror of
https://github.com/RPCS3/llvm.git
synced 2025-05-13 17:06:15 +00:00

Summary: If heap allocation of a coroutine is elided, we need to make sure that we will update an address stored in the coroutine frame from f.destroy to f.cleanup. Before this change, CoroSplit synthesized these stores after coro.begin: ``` store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr ``` In those cases where we did heap elision, but were not able to devirtualize all indirect calls, destroy call will attempt to "free" the coroutine frame stored on the stack. Oops. Now we use select to put an appropriate coroutine subfunction in the destroy slot. As bellow: ``` store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr %0 = select i1 %need.alloc, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup store void (%f.Frame*)* %0, void (%f.Frame*)** %destroy.addr ``` Reviewers: majnemer Subscribers: mehdi_amini, llvm-commits Differential Revision: https://reviews.llvm.org/D25377 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283625 91177308-0d34-0410-b5e6-96231b3b80d8
319 lines
11 KiB
C++
319 lines
11 KiB
C++
//===-- 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<CoroSubFnInst>(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<ResumeKind>(Index);
|
|
}
|
|
|
|
ConstantInt *getRawIndex() const {
|
|
return cast<ConstantInt>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<CoroAllocInst>(U))
|
|
return CA;
|
|
return nullptr;
|
|
}
|
|
|
|
IntrinsicInst *getCoroBegin() {
|
|
for (User *U : users())
|
|
if (auto *II = dyn_cast<IntrinsicInst>(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<ConstantPointerNull>(Arg)
|
|
? nullptr
|
|
: cast<AllocaInst>(Arg->stripPointerCasts());
|
|
}
|
|
|
|
void clearPromise() {
|
|
Value *Arg = getArgOperand(PromiseArg);
|
|
setArgOperand(PromiseArg,
|
|
ConstantPointerNull::get(Type::getInt8PtrTy(getContext())));
|
|
if (isa<AllocaInst>(Arg))
|
|
return;
|
|
assert((isa<BitCastInst>(Arg) || isa<GetElementPtrInst>(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<Instruction>(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<GlobalVariable>(getRawInfo());
|
|
if (!GV)
|
|
return Result;
|
|
|
|
assert(GV->isConstant() && GV->hasDefinitiveInitializer());
|
|
Constant *Initializer = GV->getInitializer();
|
|
if ((Result.OutlinedParts = dyn_cast<ConstantStruct>(Initializer)))
|
|
return Result;
|
|
|
|
Result.Resumers = cast<ConstantArray>(Initializer);
|
|
return Result;
|
|
}
|
|
Constant *getRawInfo() const {
|
|
return cast<Constant>(getArgOperand(InfoArg)->stripPointerCasts());
|
|
}
|
|
|
|
void setInfo(Constant *C) { setArgOperand(InfoArg, C); }
|
|
|
|
Function *getCoroutine() const {
|
|
return cast<Function>(getArgOperand(CoroutineArg)->stripPointerCasts());
|
|
}
|
|
void setCoroutineSelf() {
|
|
assert(isa<ConstantPointerNull>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<CoroIdInst>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<Constant>(getArgOperand(FromArg))->isOneValue();
|
|
}
|
|
unsigned getAlignment() const {
|
|
return cast<ConstantInt>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<CoroSaveInst>(Arg))
|
|
return SI;
|
|
assert(isa<ConstantTokenNone>(Arg));
|
|
return nullptr;
|
|
}
|
|
bool isFinal() const {
|
|
return cast<Constant>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(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<Constant>(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<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
|
|
}
|
|
};
|
|
|
|
} // End namespace llvm.
|