-
Notifications
You must be signed in to change notification settings - Fork 13.7k
[SandboxIR] Implement BranchInst #100063
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SandboxIR] Implement BranchInst #100063
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,7 @@ class Context; | |
class Function; | ||
class Instruction; | ||
class SelectInst; | ||
class BranchInst; | ||
class LoadInst; | ||
class ReturnInst; | ||
class StoreInst; | ||
|
@@ -179,6 +180,7 @@ class Value { | |
friend class User; // For getting `Val`. | ||
friend class Use; // For getting `Val`. | ||
friend class SelectInst; // For getting `Val`. | ||
friend class BranchInst; // For getting `Val`. | ||
friend class LoadInst; // For getting `Val`. | ||
friend class StoreInst; // For getting `Val`. | ||
friend class ReturnInst; // For getting `Val`. | ||
|
@@ -343,6 +345,14 @@ class User : public Value { | |
virtual unsigned getUseOperandNo(const Use &Use) const = 0; | ||
friend unsigned Use::getOperandNo() const; // For getUseOperandNo() | ||
|
||
void swapOperandsInternal(unsigned OpIdxA, unsigned OpIdxB) { | ||
assert(OpIdxA < getNumOperands() && "OpIdxA out of bounds!"); | ||
assert(OpIdxB < getNumOperands() && "OpIdxB out of bounds!"); | ||
auto UseA = getOperandUse(OpIdxA); | ||
auto UseB = getOperandUse(OpIdxB); | ||
UseA.swap(UseB); | ||
} | ||
|
||
#ifndef NDEBUG | ||
void verifyUserOfLLVMUse(const llvm::Use &Use) const; | ||
#endif // NDEBUG | ||
|
@@ -504,6 +514,7 @@ class Instruction : public sandboxir::User { | |
/// returns its topmost LLVM IR instruction. | ||
llvm::Instruction *getTopmostLLVMInstruction() const; | ||
friend class SelectInst; // For getTopmostLLVMInstruction(). | ||
friend class BranchInst; // For getTopmostLLVMInstruction(). | ||
friend class LoadInst; // For getTopmostLLVMInstruction(). | ||
friend class StoreInst; // For getTopmostLLVMInstruction(). | ||
friend class ReturnInst; // For getTopmostLLVMInstruction(). | ||
|
@@ -617,6 +628,100 @@ class SelectInst : public Instruction { | |
#endif | ||
}; | ||
|
||
class BranchInst : public Instruction { | ||
/// Use Context::createBranchInst(). Don't call the constructor directly. | ||
BranchInst(llvm::BranchInst *BI, Context &Ctx) | ||
: Instruction(ClassID::Br, Opcode::Br, BI, Ctx) {} | ||
friend Context; // for BranchInst() | ||
Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final { | ||
return getOperandUseDefault(OpIdx, Verify); | ||
} | ||
SmallVector<llvm::Instruction *, 1> getLLVMInstrs() const final { | ||
return {cast<llvm::Instruction>(Val)}; | ||
} | ||
|
||
public: | ||
unsigned getUseOperandNo(const Use &Use) const final { | ||
return getUseOperandNoDefault(Use); | ||
} | ||
unsigned getNumOfIRInstrs() const final { return 1u; } | ||
static BranchInst *create(BasicBlock *IfTrue, Instruction *InsertBefore, | ||
Context &Ctx); | ||
static BranchInst *create(BasicBlock *IfTrue, BasicBlock *InsertAtEnd, | ||
Context &Ctx); | ||
static BranchInst *create(BasicBlock *IfTrue, BasicBlock *IfFalse, | ||
Value *Cond, Instruction *InsertBefore, | ||
Context &Ctx); | ||
static BranchInst *create(BasicBlock *IfTrue, BasicBlock *IfFalse, | ||
Value *Cond, BasicBlock *InsertAtEnd, Context &Ctx); | ||
/// For isa/dyn_cast. | ||
static bool classof(const Value *From); | ||
bool isUnconditional() const { | ||
return cast<llvm::BranchInst>(Val)->isUnconditional(); | ||
} | ||
bool isConditional() const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copy-paste? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do I need both? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both of these function exist in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignore that, I was too fast. |
||
return cast<llvm::BranchInst>(Val)->isConditional(); | ||
} | ||
Value *getCondition() const; | ||
void setCondition(Value *V) { setOperand(0, V); } | ||
unsigned getNumSuccessors() const { return 1 + isConditional(); } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. number + bool? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return isConditional() ? 2 : 1; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I could have casted it to unsigned first, but this is how it's done in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am just afraid some compiler, including future Clang will complain. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't worry too much about it, it's legal c++. Casting bool to int gives us 0 or 1. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignore me again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's always good to point out things that look strange or potentially wrong, so thanks for the comments. |
||
BasicBlock *getSuccessor(unsigned SuccIdx) const; | ||
void setSuccessor(unsigned Idx, BasicBlock *NewSucc); | ||
void swapSuccessors() { swapOperandsInternal(1, 2); } | ||
|
||
private: | ||
struct LLVMBBToSBBB { | ||
Context &Ctx; | ||
LLVMBBToSBBB(Context &Ctx) : Ctx(Ctx) {} | ||
BasicBlock *operator()(llvm::BasicBlock *BB) const; | ||
}; | ||
|
||
struct ConstLLVMBBToSBBB { | ||
Context &Ctx; | ||
ConstLLVMBBToSBBB(Context &Ctx) : Ctx(Ctx) {} | ||
const BasicBlock *operator()(const llvm::BasicBlock *BB) const; | ||
}; | ||
|
||
public: | ||
using sb_succ_op_iterator = | ||
mapped_iterator<llvm::BranchInst::succ_op_iterator, LLVMBBToSBBB>; | ||
iterator_range<sb_succ_op_iterator> successors() { | ||
iterator_range<llvm::BranchInst::succ_op_iterator> LLVMRange = | ||
cast<llvm::BranchInst>(Val)->successors(); | ||
LLVMBBToSBBB BBMap(Ctx); | ||
sb_succ_op_iterator MappedBegin = map_iterator(LLVMRange.begin(), BBMap); | ||
sb_succ_op_iterator MappedEnd = map_iterator(LLVMRange.end(), BBMap); | ||
return make_range(MappedBegin, MappedEnd); | ||
} | ||
|
||
using const_sb_succ_op_iterator = | ||
mapped_iterator<llvm::BranchInst::const_succ_op_iterator, | ||
ConstLLVMBBToSBBB>; | ||
iterator_range<const_sb_succ_op_iterator> successors() const { | ||
iterator_range<llvm::BranchInst::const_succ_op_iterator> ConstLLVMRange = | ||
static_cast<const llvm::BranchInst *>(cast<llvm::BranchInst>(Val)) | ||
->successors(); | ||
ConstLLVMBBToSBBB ConstBBMap(Ctx); | ||
const_sb_succ_op_iterator ConstMappedBegin = | ||
map_iterator(ConstLLVMRange.begin(), ConstBBMap); | ||
const_sb_succ_op_iterator ConstMappedEnd = | ||
map_iterator(ConstLLVMRange.end(), ConstBBMap); | ||
return make_range(ConstMappedBegin, ConstMappedEnd); | ||
} | ||
|
||
#ifndef NDEBUG | ||
void verify() const final { | ||
assert(isa<llvm::BranchInst>(Val) && "Expected BranchInst!"); | ||
} | ||
friend raw_ostream &operator<<(raw_ostream &OS, const BranchInst &BI) { | ||
BI.dump(OS); | ||
return OS; | ||
} | ||
void dump(raw_ostream &OS) const override; | ||
LLVM_DUMP_METHOD void dump() const override; | ||
#endif | ||
}; | ||
|
||
class LoadInst final : public Instruction { | ||
/// Use LoadInst::create() instead of calling the constructor. | ||
LoadInst(llvm::LoadInst *LI, Context &Ctx) | ||
|
@@ -870,6 +975,8 @@ class Context { | |
|
||
SelectInst *createSelectInst(llvm::SelectInst *SI); | ||
friend SelectInst; // For createSelectInst() | ||
BranchInst *createBranchInst(llvm::BranchInst *I); | ||
friend BranchInst; // For createBranchInst() | ||
LoadInst *createLoadInst(llvm::LoadInst *LI); | ||
friend LoadInst; // For createLoadInst() | ||
StoreInst *createStoreInst(llvm::StoreInst *SI); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't IfFalse == nullptr be the one that creates the instruction with only the true branch? That way you save creating two functions or put it as a default parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that would make sense, but I guess it is not as explicit as having a separate function. This is how it's done in
llvm::BranchInst
so I just created a similar API.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!