This is a toy project, it has lot of unimplemented code and strict assertions.
Created
April 18, 2022 06:07
-
-
Save rainyx/ad778d2c6dda5112f0a5dd6fa832e667 to your computer and use it in GitHub Desktop.
Create IR code easily
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by rainyx on 2022/4/16. | |
// | |
#include "IRMaker.h" | |
using namespace llvm; | |
using namespace std; | |
static bool hasTerminator(BasicBlock *BB) { | |
return BB->getTerminator() != nullptr; | |
} | |
static vector<Value *>unwrap(const ArrayRef<Val> &Arr) { | |
vector<Value *> Unwrapped; | |
for (auto V : Arr) | |
Unwrapped.push_back(V.unwrap()); | |
return Unwrapped; | |
} | |
void IRMaker::when(const Val &Cond, const std::function<void (IRMaker &)> &IfBlk) { | |
BasicBlock *IfBB = BasicBlock::Create(Ctx, "If.body", F); | |
BasicBlock *EndBB = BasicBlock::Create(Ctx, "If.end", F); | |
BranchInst::Create(IfBB, EndBB, Cond.unwrap(), BB); | |
auto IfMaker = IRMaker(IfBB); | |
SubMaker = &IfMaker; | |
IfBlk(IfMaker); | |
SubMaker = nullptr; | |
if (!hasTerminator(IfMaker.BB)) | |
BranchInst::Create(EndBB, IfMaker.BB); | |
BB = EndBB; | |
} | |
void IRMaker::when(const Val &Cond, const std::function<void (IRMaker &)> &IfBlk, | |
const std::function<void (IRMaker &)> &ElseBlk) { | |
BasicBlock *IfBB = BasicBlock::Create(Ctx, "If.True", F); | |
BasicBlock *ElseBB = BasicBlock::Create(Ctx, "If.False", F); | |
BasicBlock *EndBB = BasicBlock::Create(Ctx, "If.End", F); | |
BranchInst::Create(IfBB, ElseBB, Cond.unwrap(), BB); | |
auto IfMaker = IRMaker(IfBB); | |
SubMaker = &IfMaker; | |
IfBlk(IfMaker); | |
SubMaker = nullptr; | |
if (!hasTerminator(IfMaker.BB)) | |
BranchInst::Create(EndBB, IfMaker.BB); | |
auto ElseMaker = IRMaker(ElseBB); | |
SubMaker = &ElseMaker; | |
ElseBlk(ElseMaker); | |
SubMaker = nullptr; | |
if (!hasTerminator(ElseMaker.BB)) | |
BranchInst::Create(EndBB, ElseMaker.BB); | |
BB = EndBB; | |
} | |
void IRMaker::loopWhen(const std::function<Val (IRMaker &)> &CondBlk, | |
const std::function<void (IRMaker &)> &BodyBlk) { | |
BasicBlock *CondBB = BasicBlock::Create(Ctx, "Loop.Cond", F); | |
BasicBlock *BodyBB = BasicBlock::Create(Ctx, "Loop.Body", F); | |
BasicBlock *EndBB = BasicBlock::Create(Ctx, "Loop.End", F); | |
IRMaker CondMaker(CondBB); | |
SubMaker = &CondMaker; | |
Val Cond = CondBlk(CondMaker); | |
SubMaker = nullptr; | |
BranchInst::Create(CondBB, BB); | |
BranchInst::Create(BodyBB, EndBB, Cond.unwrap(), CondBB); | |
auto BodyMaker = IRMaker(BodyBB); | |
SubMaker = &BodyMaker; | |
BodyBlk(BodyMaker); | |
SubMaker = nullptr; | |
BranchInst::Create(CondBB, BodyMaker.BB); | |
BB = EndBB; | |
} | |
void IRMaker::loopUntil(const std::function<void (IRMaker &)> &BodyBlk, | |
const std::function<Val (IRMaker &)> &UntilBlk) { | |
BasicBlock *CondBB = BasicBlock::Create(Ctx, "Loop.Cond", F); | |
BasicBlock *BodyBB = BasicBlock::Create(Ctx, "Loop.Body", F); | |
BasicBlock *EndBB = BasicBlock::Create(Ctx, "Loop.End", F); | |
IRMaker CondMaker(CondBB); | |
SubMaker = &CondMaker; | |
Val Cond = UntilBlk(CondMaker); | |
SubMaker = nullptr; | |
BranchInst::Create(BodyBB, BB); | |
BranchInst::Create(EndBB, BodyBB, Cond.unwrap(), CondBB); | |
auto BodyMaker = IRMaker(BodyBB); | |
SubMaker = &BodyMaker; | |
BodyBlk(BodyMaker); | |
SubMaker = nullptr; | |
BranchInst::Create(CondBB, BodyMaker.BB); | |
BB = EndBB; | |
} | |
Val IRMaker::isNull(const Val &V, const Twine &Name) { | |
Val Null = null(V.unwrap()->getType()); | |
return icmp(ICmpInst::ICMP_EQ, V, Null, Name); | |
} | |
Val IRMaker::isNotNull(const Val &V, const Twine &Name) { | |
Val Null = null(V.unwrap()->getType()); | |
return icmp(ICmpInst::ICMP_NE, V, Null, Name); | |
} | |
Val IRMaker::call(Function *F, const ArrayRef<Val> &Args, const Twine &Name) { | |
return wrap(CallInst::Create(F, unwrap(Args), Name, getInsertionPoint())); | |
} | |
void IRMaker::ret(const Val &V) { | |
ReturnInst::Create(Ctx, V.unwrap(), getInsertionPoint()); | |
} | |
void IRMaker::retNull() { | |
ReturnInst::Create(Ctx, null(F->getReturnType()).unwrap(), getInsertionPoint()); | |
} | |
void IRMaker::ret() { | |
ReturnInst::Create(Ctx, getInsertionPoint()); | |
} | |
Val IRMaker::alloc(const Val &InitialValue, const Twine &Name) { | |
Type *Ty = InitialValue.getType(); | |
Val Ptr = alloc(Ty, nullptr, Name); | |
store(InitialValue, Ptr); | |
return Ptr; | |
} | |
Val IRMaker::alloc(Type *Ty, const Val *ArraySize, const Twine &Name) { | |
Value *ArrSz = ArraySize ? ArraySize->unwrap() : nullptr; | |
return wrap(new AllocaInst(Ty, 0, ArrSz, Name, getInsertionPoint())); | |
} | |
Val IRMaker::malloc(Type *Ty, const Twine &Name) { | |
return malloc(Ty, nullptr, Name); | |
} | |
Val IRMaker::malloc(Type *Ty, const Val *ArraySize, const Twine &Name) { | |
Type *ITy = i32Ty(); | |
Constant *AllocSize = ConstantExpr::getSizeOf(Ty); | |
AllocSize = ConstantExpr::getTruncOrBitCast(AllocSize, ITy); | |
Value *ArrSz = ArraySize ? ArraySize->unwrap() : nullptr; | |
Instruction *Malloc = CallInst::CreateMalloc(getInsertionPoint(), ITy, Ty, AllocSize, | |
ArrSz, nullptr, ""); | |
insertAtEnd(Malloc); | |
return wrap(Malloc); | |
} | |
void IRMaker::free(const Val &Ptr) { | |
Instruction *Free = CallInst::CreateFree(Ptr.unwrap(), getInsertionPoint()); | |
insertAtEnd(Free); | |
} | |
Val IRMaker::add(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return wrap(BinaryOperator::Create(Instruction::Add, Lhs.unwrap(), Rhs.unwrap(), Name, getInsertionPoint())); | |
} | |
Val IRMaker::sub(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::Sub, Lhs, Rhs, Name); | |
} | |
Val IRMaker::mul(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::Mul, Lhs, Rhs, Name); | |
} | |
Val IRMaker::sdiv(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::SDiv, Lhs, Rhs, Name); | |
} | |
Val IRMaker::udiv(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::UDiv, Lhs, Rhs, Name); | |
} | |
Val IRMaker::bitXor(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::Xor, Lhs, Rhs, Name); | |
} | |
Val IRMaker::aAnd(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::And, Lhs, Rhs, Name); | |
} | |
Val IRMaker::lAnd(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return wrap(SelectInst::Create(Lhs.unwrap(), Rhs.unwrap(), ConstantInt::getNullValue(Rhs.unwrap()->getType()), Name, getInsertionPoint())); | |
} | |
Val IRMaker::aOr(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::Or, Lhs, Rhs, Name); | |
} | |
Val IRMaker::lOr(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return wrap(SelectInst::Create(Lhs.unwrap(), ConstantInt::getAllOnesValue(Rhs.unwrap()->getType()), Rhs.unwrap(), Name, getInsertionPoint())); | |
} | |
Val IRMaker::shl(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::Shl, Lhs, Rhs, Name); | |
} | |
Val IRMaker::aShr(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::AShr, Lhs, Rhs, Name); | |
} | |
Val IRMaker::lShr(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return bo(Instruction::LShr, Lhs, Rhs, Name); | |
} | |
Val IRMaker::bo(Instruction::BinaryOps op, const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return wrap(BinaryOperator::Create(op, Lhs.unwrap(), Rhs.unwrap(), Name, getInsertionPoint())); | |
} | |
Val IRMaker::isGE(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name) { | |
return icmp(IsSigned ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE, Lhs, Rhs, Name); | |
} | |
Val IRMaker::isGT(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name) { | |
return icmp(IsSigned ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT, Lhs, Rhs, Name); | |
} | |
Val IRMaker::isLE(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name) { | |
return icmp(IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE, Lhs, Rhs, Name); | |
} | |
Val IRMaker::isLT(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name) { | |
return icmp(IsSigned ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT, Lhs, Rhs, Name); | |
} | |
Val IRMaker::isEQ(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return icmp(llvm::CmpInst::ICMP_EQ, Lhs, Rhs, Name); | |
} | |
Val IRMaker::isNE(const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
return icmp(llvm::CmpInst::ICMP_NE, Lhs, Rhs, Name); | |
} | |
Val IRMaker::icmp(ICmpInst::Predicate P, const Val &Lhs, const Val &Rhs, const Twine &Name) { | |
auto ICMP = new ICmpInst(P, Lhs.unwrap(), Rhs.unwrap(), Name); | |
insertAtEnd(ICMP); | |
return wrap(ICMP); | |
} | |
Val IRMaker::gep(const Val &Ptr, const ArrayRef<Val> &IdxList, const Twine &Name) { | |
Value *RawPtr = Ptr.unwrap(); | |
Type *PointeeType = RawPtr->getType()->getPointerElementType(); | |
Value *GEP = GetElementPtrInst::Create(PointeeType, RawPtr, unwrap(IdxList), Name + "_GEP", getInsertionPoint()); | |
return wrap(GEP); | |
} | |
Val IRMaker::gep(const Val &Ptr, const Val &Idx, const Twine &Name) { | |
Value *RawPtr = Ptr.unwrap(); | |
Type *PointeeType = RawPtr->getType()->getPointerElementType(); | |
Value *GEP = GetElementPtrInst::Create(PointeeType, RawPtr, Idx.unwrap(), Name + "_GEP", getInsertionPoint()); | |
return wrap(GEP); | |
} | |
Val IRMaker::load(const Val &Ptr, const Twine &Name) { | |
Value *RawPtr = Ptr.unwrap(); | |
Value *Value = new LoadInst(RawPtr->getType()->getPointerElementType(), RawPtr, Name, getInsertionPoint()); | |
return wrap(Value); | |
} | |
Val IRMaker::load(const Val &Ptr, const ArrayRef<Val> &IdxList, const Twine &Name) { | |
Val GEP = gep(Ptr, IdxList, Name); | |
return load(GEP, Name); | |
} | |
Val IRMaker::load(const Val &Ptr, const Val &Idx, const Twine &Name) { | |
Val GEP = gep(Ptr, Idx, Name); | |
return load(GEP, Name); | |
} | |
void IRMaker::store(const Val &Ptr, const ArrayRef<Val> &IdxList, const Val &Ele) { | |
Val GEP = gep(Ptr, IdxList); | |
store(Ele, GEP); | |
} | |
void IRMaker::store(const Val &Ptr, const Val &Idx, const Val &Ele) { | |
Val GEP = gep(Ptr, Idx); | |
store(Ele, GEP); | |
} | |
void IRMaker::store(const Val &V, const Val &Ptr) { | |
new StoreInst(V.unwrap(), Ptr.unwrap(), getInsertionPoint()); | |
} | |
Val IRMaker::null(Type *Ty) { | |
return wrap(Constant::getNullValue(Ty)); | |
} | |
Val IRMaker::bitCast(const Val &Ptr, Type *DestTy, const Twine &Name) { | |
return cast(Instruction::BitCast, Ptr, DestTy, Name); | |
} | |
Val IRMaker::ptrToInt(const Val &Ptr, Type *DestTy, const Twine &Name) { | |
return cast(Instruction::PtrToInt, Ptr, DestTy, Name); | |
} | |
Val IRMaker::intToPtr(const Val &V, Type *DestTy, const Twine &Name) { | |
return cast(Instruction::IntToPtr, V, DestTy, Name); | |
} | |
Val IRMaker::cast(CastInst::CastOps Op, const Val &Ptr, Type *DestTy, const Twine &Name) { | |
auto CI = CastInst::Create(Op, Ptr.unwrap(), DestTy, Name); | |
insertAtEnd(CI); | |
return wrap(CI); | |
} | |
BasicBlock *IRMaker::getInsertionPoint() const { | |
return SubMaker ? SubMaker->getInsertionPoint() : BB; | |
} | |
void IRMaker::insertAtEnd(Instruction *Inst) { | |
BasicBlock *InsertionPoint = getInsertionPoint(); | |
InsertionPoint->getInstList().insert(InsertionPoint->end(), Inst); | |
} | |
void Val::store(const Val &Idx, const Val &Ele) { | |
M.store(*this, Idx, Ele); | |
} | |
void Val::store(const ArrayRef<Val> &IdxList, const Val &Ele) { | |
M.store(*this, IdxList, Ele); | |
} | |
void Val::store(const ArrayRef<Value *> &IdxList, const Val &Ele) { | |
store(M.wrap(IdxList), Ele); | |
} | |
Val Val::load(uint32_t Idx) const { | |
return load(M.i32(Idx)); | |
} | |
Val Val::load(const Val &Idx) const { | |
return M.load(*this, Idx); | |
} | |
Val Val::load(const ArrayRef<Val> &IdxList) const { | |
return M.load(*this, IdxList); | |
} | |
Val Val::load(const ArrayRef<Value *> &IdxList) const { | |
return load(M.wrap(IdxList)); | |
} | |
Val Val::gep(const ArrayRef<Val> &IdxList) const { | |
return M.gep(*this, IdxList); | |
} | |
Val Val::gep(const ArrayRef<Value *> &IdxList) const { | |
return gep(M.wrap(IdxList)); | |
} | |
Val Val::gep(const Val &Idx) const { | |
return M.gep(*this, Idx); | |
} | |
Val Val::load() const { | |
return M.load(*this); | |
} | |
void Val::store(const Val &V) { | |
M.store(V, *this); | |
} | |
void Val::store(uint32_t Idx, const Val &Ele) { | |
return store(M.i32(Idx), Ele); | |
} | |
Val Val::bitCast(Type *DestTy) { | |
return M.bitCast(*this, DestTy); | |
} | |
Val Val::ptrToInt(Type *DestTy) { | |
return M.ptrToInt(*this, DestTy); | |
} | |
Val Val::intToPtr(Type *DestTy) { | |
return M.intToPtr(*this, DestTy); | |
} | |
Val Val::isNull() { | |
return M.isNull(*this); | |
} | |
Val Val::isNotNull() { | |
return M.isNotNull(*this); | |
} | |
Val Val::operator==(const Val &Rhs) { | |
return M.isEQ(*this, Rhs); | |
} | |
Val Val::operator==(uint64_t Rhs) { | |
return operator==(wrapInt(Rhs)); | |
} | |
Val Val::operator!=(const Val &Rhs) { | |
return M.isNE(*this, Rhs); | |
} | |
Val Val::operator!=(uint64_t Rhs) { | |
return operator!=(wrapInt(Rhs)); | |
} | |
Val Val::operator<(const Val &Rhs) { | |
return M.isLT(*this, Rhs, Signed); | |
} | |
Val Val::operator<=(const Val &Rhs) { | |
return M.isLE(*this, Rhs, Signed); | |
} | |
Val Val::operator>(const Val &Rhs) { | |
return M.isGT(*this, Rhs, Signed); | |
} | |
Val Val::operator>=(const Val &Rhs) { | |
return M.isGE(*this, Rhs, Signed); | |
} | |
Val Val::operator>=(uint64_t Rhs) { | |
return operator>=(wrapInt(Rhs)); | |
} | |
Val Val::operator+(const Val &Rhs) { | |
return M.add(*this, Rhs); | |
} | |
Val Val::operator+(uint64_t Rhs) { | |
return operator+(wrapInt(Rhs)); | |
} | |
Val Val::operator-(const Val &Rhs) { | |
return M.sub(*this, Rhs); | |
} | |
Val Val::operator-(uint64_t Rhs) { | |
return operator-(wrapInt(Rhs)); | |
} | |
Val Val::operator*(const Val &Rhs) { | |
return M.mul(*this, Rhs); | |
} | |
Val Val::operator*(uint64_t Rhs) { | |
return operator*(wrapInt(Rhs)); | |
} | |
Val Val::operator/(const Val &Rhs) { | |
if (Signed) { | |
return M.sdiv(*this, Rhs); | |
} else { | |
return M.udiv(*this, Rhs); | |
} | |
} | |
Val Val::operator/(uint64_t Rhs) { | |
return operator/(wrapInt(Rhs)); | |
} | |
Val Val::operator&(const Val &Rhs) { | |
return M.aAnd(*this, Rhs); | |
} | |
Val Val::operator&&(const Val &Rhs) { | |
return M.lAnd(*this, Rhs); | |
} | |
Val Val::operator|(const Val &Rhs) { | |
return M.aOr(*this, Rhs); | |
} | |
Val Val::operator||(const Val &Rhs) { | |
return M.lOr(*this, Rhs); | |
} | |
Val Val::operator^(const Val &Rhs) { | |
return M.bitXor(*this, Rhs); | |
} | |
Val Val::operator<<(const Val &Rhs) { | |
return M.shl(*this, Rhs); | |
} | |
Val Val::operator>>(const Val &Rhs) { | |
if (getType()->getIntegerBitWidth() == 1) { | |
return M.lShr(*this, Rhs); | |
} else { | |
return M.aShr(*this, Rhs); | |
} | |
} | |
Val Val::operator*() { | |
return load(); | |
} | |
Val Val::wrapInt(uint64_t V) { | |
return M.i(cast<IntegerType>(getType()), V); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by rainyx on 2022/4/16. | |
// | |
#ifndef MYTRANS_IRMAKER_H | |
#define MYTRANS_IRMAKER_H | |
#include "llvm/IR/Instructions.h" | |
#include "llvm/IR/Constants.h" | |
using namespace llvm; | |
using namespace std; | |
/** | |
* IRMaker creates highly readable IR code in LLVM project. | |
* It includes widely used functions, such as variable allocation, memory access and flow control, etc. | |
* With IRMaker, you don't need to care about when you need to create BB(BasicBlock) and complicated relationships between BBs. | |
* Nested lambda statements help you to organize your IR logic, just like high level programming language. | |
* // Demo: Generate Fibonacci sequence. | |
* // C version: | |
* int fib(int n) { | |
* if (n==1||n==2) | |
* return 1; | |
* return fib(n-1) + fib(n-2); | |
* } | |
* | |
* // IR version: | |
* Function *F = createFn("fib"); | |
* IRMaker M(F); | |
* | |
* Val N = M.arg(0); | |
* M.when(N == 1 || N == 2, [&](IRMaker &IfM) { | |
* IfM.ret(IfM.i32(1)) | |
* }); | |
* M.ret(M.call(F, {N-1}) + M.call(F, {N-2}); | |
* | |
* // Demo: Binary tree search with stack. | |
* // C Version: | |
* Node *BTS(Node *node, int v) { | |
* Node *stack[100]; | |
* int stackIdx = 0; | |
* stack[0] = node; | |
* while (stackIdx >=0 ) { | |
* Node *top = stack[stackIdx--]; | |
* if (top->v == v) | |
* return top; | |
* if (top->right) | |
* stack[++stackIdx] = top->right; | |
* if (top->left) | |
* stack[++stackIdx] = top->left; | |
* } | |
* | |
* return NULL; | |
* } | |
* | |
* // IR version: | |
* Function *F = createFn("BTS"); | |
* IRMaker M(F); | |
* Val Node = M.arg(0); | |
* Val V = M.arg(1); | |
* | |
* Val StackSize = M.i32(100); | |
* Val StackPtr = M.alloc(NodePtrTy, &StackSize); // Alloc array with 100 elements. | |
* Val StackIdxPtr = M.alloc(M.i32(0)); // Alloc i32 variable with initial value 0. | |
* StackPtr.store(0, Node); // stack[0] = node | |
* | |
* // while (stackIdx >=0 ) | |
* M.loopWhen([&](IRMaker &CondM) { | |
* return *StackIdxPtr >= 0; | |
* }, [&](IRMaker &LoopM) { | |
* Val TopPtr = StackPtr.load(*StackIdxPtr); // top = stack[stackIdx] | |
* StackIdxPtr.store(*StackIdxPtr - 1); // stackIdx -- | |
* M.when(TopPtr.load(Idx_Node_Value) == V, [&](IRMaker &IfM) { | |
* IfM.ret(TopPtr); | |
* }); | |
* | |
* // Push right if needed. | |
* M.when(TopPtr.load(Idx_Node_Right).isNotNull(), [&](IRMaker &IfM) { | |
* StackIdxPtr.store(*StackIdxPtr + 1); // stackIdx ++ | |
* StackPtr.store(*StackIdxPtr, TopPtr.load(Idx_Node_Right)); // stack[stackIdx] = top->right; | |
* }); | |
* | |
* // Push left if needed. | |
* M.when(TopPtr.load(Idx_Node_Left).isNotNull(), [&](IRMaker &IfM) { | |
* StackIdxPtr.store(*StackIdxPtr + 1); // stackIdx ++ | |
* StackPtr.store(*StackIdxPtr, TopPtr.load(Idx_Node_Left)); // stack[stackIdx] = top->left; | |
* }); | |
* }); | |
* | |
* M.retNull(); | |
*/ | |
class IRMaker; | |
class Val { | |
public: | |
Val(Value *Raw, IRMaker &M): Raw(Raw), M(M) {} | |
Val &s() { Signed = true; return *this; } | |
Val &u() { Signed = false; return *this; } | |
Value *unwrap() const { return Raw; } | |
Type *getType() const { return Raw->getType(); } | |
Val gep(const Val &Idx) const; | |
Val gep(const ArrayRef<Val> &IdxList) const; | |
Val gep(const ArrayRef<Value *> &IdxList) const; | |
Val load() const; | |
Val load(uint32_t Idx) const; | |
Val load(const Val &Idx) const; | |
Val load(const ArrayRef<Val> &IdxList) const; | |
Val load(const ArrayRef<Value *> &IdxList) const; | |
void store(const Val &V); | |
void store(uint32_t Idx, const Val &Ele); | |
void store(const Val &Idx, const Val &Ele); | |
void store(const ArrayRef<Val> &IdxList, const Val &Ele); | |
void store(const ArrayRef<Value *> &IdxList, const Val &Ele); | |
Val bitCast(Type *DestTy); | |
Val ptrToInt(Type *DestTy); | |
Val intToPtr(Type *DestTy); | |
Val isNull(); | |
Val isNotNull(); | |
Val operator==(const Val &Rhs); | |
Val operator==(uint64_t Rhs); | |
Val operator!=(const Val &Rhs); | |
Val operator!=(uint64_t Rhs); | |
Val operator<(const Val &Rhs); | |
Val operator<=(const Val &Rhs); | |
Val operator>(const Val &Rhs); | |
Val operator>=(const Val &Rhs); | |
Val operator>=(uint64_t Rhs); | |
Val operator+(const Val &Rhs); | |
Val operator+(uint64_t Rhs); | |
Val operator-(const Val &Rhs); | |
Val operator-(uint64_t Rhs); | |
Val operator*(const Val &Rhs); | |
Val operator*(uint64_t Rhs); | |
Val operator/(const Val &Rhs); | |
Val operator/(uint64_t Rhs); | |
Val operator&&(const Val &Rhs); | |
Val operator&(const Val &Rhs); | |
Val operator||(const Val &Rhs); | |
Val operator|(const Val &Rhs); | |
Val operator^(const Val &Rhs); | |
Val operator>>(const Val &Rhs); | |
Val operator<<(const Val &Rhs); | |
Val operator*(); | |
private: | |
Val wrapInt(uint64_t V); | |
private: | |
Value *Raw; | |
IRMaker &M; | |
bool Signed {true}; // Default value is signed | |
}; | |
class IRMaker { | |
public: | |
explicit IRMaker(BasicBlock *BB): BB(BB), F(BB->getParent()), Ctx(F->getContext()) {} | |
IRMaker(Function *F): F(F), Ctx(F->getContext()) { | |
if (!F->getBasicBlockList().empty()) | |
report_fatal_error("Function BBs is not empty!"); | |
BasicBlock *EntryBB = BasicBlock::Create(Ctx, "Entry", F); | |
BB = EntryBB; | |
} | |
Val wrap(Value *V) { | |
return Val(V, *this); | |
} | |
vector<Val> wrap(const ArrayRef<Value *> &Arr) { | |
vector<Val> Wrapped; | |
for (auto V : Arr) | |
Wrapped.push_back(wrap(V)); | |
return Wrapped; | |
} | |
// if (Cond) { ... } else { ... } | |
void when(const Val &Cond, const std::function<void (IRMaker &)> &IfBlk, | |
const std::function<void (IRMaker &)> &ElseBlk); | |
// if (Cond) { ... } | |
void when(const Val &Cond, const std::function<void (IRMaker &)> &IfBlk); | |
// while (Cond) { ... } | |
void loopWhen(const std::function<Val (IRMaker &)> &CondBlk, | |
const std::function<void (IRMaker &)> &BodyBlk); | |
// do { ... } while (!Cond) | |
void loopUntil(const std::function<void (IRMaker &)> &BodyBlk, | |
const std::function<Val (IRMaker &)> &UntilBlk); | |
Val arg(unsigned Idx) { return wrap(F->getArg(Idx)); } | |
Val isNull(const Val &V, const Twine &Name = ""); | |
Val isNotNull(const Val &V, const Twine &Name = ""); | |
Val isGE(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name = ""); | |
Val isGT(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name = ""); | |
Val isLE(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name = ""); | |
Val isLT(const Val &Lhs, const Val &Rhs, bool IsSigned, const Twine &Name = ""); | |
Val isEQ(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val isNE(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val call(Function *F, const ArrayRef<Val> &Args, const Twine &Name = ""); | |
void ret(const Val &V); | |
void retNull(); | |
void ret(); | |
Val gep(const Val &Ptr, const Val &Idx, const Twine &Name = ""); | |
Val gep(const Val &Ptr, const ArrayRef<Val> &IdxList, const Twine &Name = ""); | |
Val load(const Val &Ptr, const Twine &Name = ""); | |
Val load(const Val &Ptr, const Val &Idx, const Twine &Name = ""); | |
Val load(const Val &Ptr, const ArrayRef<Val> &IdxList, const Twine &Name = ""); | |
void store(const Val &V, const Val &Ptr); | |
void store(const Val &Ptr, const Val &Idx, const Val &Ele); | |
void store(const Val &Ptr, const ArrayRef<Val> &IdxList, const Val &Ele); | |
Val alloc(const Val &InitialValue, const Twine &Name = ""); | |
Val alloc(Type *Ty, const Val *ArraySize, const Twine &Name = ""); | |
Val malloc(Type *Ty, const Twine &Name = ""); | |
Val malloc(Type *Ty, const Val *ArraySize, const Twine &Name = ""); | |
void free(const Val &Ptr); | |
Val add(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val sub(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val mul(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val sdiv(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val udiv(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val bitXor(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val aAnd(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val lAnd(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val aOr(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val lOr(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val shl(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val aShr(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val lShr(const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val bo(Instruction::BinaryOps op, const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val icmp(ICmpInst::Predicate P, const Val &Lhs, const Val &Rhs, const Twine &Name = ""); | |
Val bitCast(const Val &Ptr, Type *DestTy, const Twine &Name = ""); | |
Val ptrToInt(const Val &Ptr, Type *DestTy, const Twine &Name = ""); | |
Val intToPtr(const Val &V, Type *DestTy, const Twine &Name = ""); | |
Val cast(CastInst::CastOps Op, const Val &V, Type *DestTy, const Twine &Name = ""); | |
Val null(Type *Ty); | |
Val i8(uint8_t V) { return i(i8Ty(), V); } | |
Val i16(uint16_t V) { return i(i16Ty(), V); } | |
Val i32(uint32_t V) { return i(i32Ty(), V); } | |
Val i64(uint64_t V) { return i(i64Ty(), V); } | |
Val i(IntegerType *Ty, uint64_t V) { return wrap(ConstantInt::get(Ty, V)); } | |
IntegerType *i8Ty() { return IntegerType::getInt8Ty(Ctx); } | |
IntegerType *i16Ty() { return IntegerType::getInt16Ty(Ctx); } | |
IntegerType *i32Ty() { return IntegerType::getInt32Ty(Ctx); } | |
IntegerType *i64Ty() { return IntegerType::getInt64Ty(Ctx); } | |
PointerType *i8PtrTy() { return IntegerType::getInt8PtrTy(Ctx); } | |
PointerType *i16PtrTy() { return IntegerType::getInt16PtrTy(Ctx); } | |
PointerType *i32PtrTy() { return IntegerType::getInt32PtrTy(Ctx); } | |
PointerType *i64PtrTy() { return IntegerType::getInt64PtrTy(Ctx); } | |
private: | |
BasicBlock *getInsertionPoint() const; | |
void insertAtEnd(Instruction *I); | |
private: | |
BasicBlock *BB; | |
Function *F; | |
LLVMContext &Ctx; | |
IRMaker *SubMaker {nullptr}; | |
}; | |
#endif //MYTRANS_IRMAKER_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment