#pragma once
/**
@file
@brief LLVM IR generator
@author MITSUNARI Shigeo(@herumi)
@license modified new BSD license
http://opensource.org/licenses/BSD-3-Clause
*/
//#define CYBOZU_EXCEPTION_WITH_STACKTRACE
#include <string>
#include <vector>
#include <cybozu/exception.hpp>
#include <cybozu/itoa.hpp>
#include <stdio.h>
#ifdef _MSC_VER
// #pragma warning(push)
#pragma warning(disable : 4458)
#endif
namespace mcl {
namespace impl {
struct File {
FILE *fp;
File() : fp(stdout) {}
~File() { if (fp != stdout) fclose(fp); }
void open(const std::string& file)
{
#ifdef _MSC_VER
bool isOK = fopen_s(&fp, file.c_str(), "wb") != 0;
#else
fp = fopen(file.c_str(), "wb");
bool isOK = fp != NULL;
#endif
if (!isOK) throw cybozu::Exception("File:open") << file;
}
void write(const std::string& str)
{
int ret = fprintf(fp, "%s\n", str.c_str());
if (ret < 0) {
throw cybozu::Exception("File:write") << str;
}
}
};
template<size_t dummy=0>
struct Param {
static File f;
};
template<size_t dummy>
File Param<dummy>::f;
bool isOldLLVM = false;
} // mcl::impl
inline bool isOldLLVM() { return impl::isOldLLVM; }
struct Generator {
static const uint8_t None = 0;
static const uint8_t Int = 1;
static const uint8_t Imm = 2;
static const uint8_t Ptr = 1 << 7;
static const uint8_t IntPtr = Int | Ptr;
void setOldLLVM() { impl::isOldLLVM = true; }
struct Type {
uint8_t type;
bool isPtr;
Type(int type = 0)
: type(static_cast<uint8_t>(type & ~Ptr))
, isPtr((type & Ptr) != 0)
{
}
inline friend std::ostream& operator<<(std::ostream& os, const Type& self)
{
return os << (self.type | (self.isPtr ? Ptr : 0));
}
};
enum CondType {
eq = 1,
neq = 2,
ugt = 3,
uge = 4,
ult = 5,
ule = 6,
sgt = 7,
sge = 8,
slt = 9,
sle = 10
};
static inline const std::string& toStr(CondType type)
{
static const std::string tbl[] = {
"eq", "neq", "ugt", "uge", "ult", "ule", "sgt", "sge", "slt", "sle"
};
return tbl[type - 1];
}
void open(const std::string& file)
{
impl::Param<>::f.open(file);
}
struct Operand;
struct Function;
struct Eval;
struct Label {
std::string name;
explicit Label(const std::string& name = "") : name(name) {}
std::string toStr() const { return std::string("label %") + name; }
};
void putLabel(const Label& label)
{
put(label.name + ":");
}
static inline int& getGlobalIdx()
{
static int globalIdx = 0;
return ++globalIdx;
}
static inline void resetGlobalIdx()
{
getGlobalIdx() = 0;
}
static inline void put(const std::string& str)
{
impl::Param<>::f.write(str);
}
void beginFunc(const Function& f);
void endFunc()
{
put("}");
}
Eval zext(const Operand& x, uint32_t size);
Eval mul(const Operand& x, const Operand& y);
Eval add(const Operand& x, const Operand& y);
Eval sub(const Operand& x, const Operand& y);
Eval _and(const Operand& x, const Operand& y);
Eval _or(const Operand& x, const Operand& y);
void ret(const Operand& r);
Eval lshr(const Operand& x, uint32_t size);
Eval ashr(const Operand& x, uint32_t size);
Eval shl(const Operand& x, uint32_t size);
Eval trunc(const Operand& x, uint32_t size);
Eval getelementptr(const Operand& p, const Operand& i);
Eval getelementptr(const Operand& p, int n);
Eval load(const Operand& p);
void store(const Operand& r, const Operand& p);
Eval select(const Operand& c, const Operand& r1, const Operand& r2);
Eval _alloca(uint32_t bit, uint32_t n);
// QQQ : type of type must be Type
Eval bitcast(const Operand& r, const Operand& type);
Eval icmp(CondType type, const Operand& r1, const Operand& r2);
void br(const Operand& op, const Label& ifTrue, const Label& ifFalse);
Eval call(const Function& f);
Eval call(const Function& f, const Operand& op1);
Eval call(const Function& f, const Operand& op1, const Operand& op2);
Eval call(const Function& f, const Operand& op1, const Operand& op2, const Operand& op3);
Eval call(const Function& f, const Operand& op1, const Operand& op2, const Operand& op3, const Operand& op4);
Eval call(const Function& f, const Operand& op1, const Operand& op2, const Operand& op3, const Operand& op4, const Operand& op5);
Operand makeImm(uint32_t bit, int64_t imm);
};
struct Generator::Operand {
Type type;
uint32_t bit;
int64_t imm;
uint32_t idx;
Operand() : type(None), bit(0), imm(0), idx(0) {}
Operand(Type type, uint32_t bit)
: type(type), bit(bit), imm(0), idx(getGlobalIdx())
{
}
Operand(const Operand& rhs)
: type(rhs.type), bit(rhs.bit), imm(rhs.imm), idx(rhs.idx)
{
}
void operator=(const Operand& rhs)
{
type = rhs.type;
bit = rhs.bit;
imm = rhs.imm;
idx = rhs.idx;
}
void update()
{
idx = getGlobalIdx();
}
Operand(const Eval& e);
void operator=(const Eval& e);
std::string toStr(bool isAlias = true) const
{
if (type.isPtr) {
return getType(isAlias) + " " + getName();
}
switch (type.type) {
default:
return getType();
case Int:
case Imm:
return getType() + " " + getName();
}
}
std::string getType(bool isAlias = true) const
{
std::string s;
switch (type.type) {
default:
return "void";
case Int:
case Imm:
s = std::string("i") + cybozu::itoa(bit);
break;
}
if (type.isPtr) {
s += "*";
if (!isAlias) {
s += " noalias ";
}
}
return s;
}
std::string getName() const
{
switch (type.type) {
default:
return "";
case Int:
return std::string("%r") + cybozu::itoa(idx);
case Imm:
return cybozu::itoa(imm);
}
}
};
inline Generator::Operand Generator::makeImm(uint32_t bit, int64_t imm)
{
Generator::Operand v(Generator::Imm, bit);
v.imm = imm;
return v;
}
struct Generator::Eval {
std::string s;
Generator::Operand op;
mutable bool used;
Eval() : used(false) {}
~Eval()
{
if (used) return;
put(s);
}
};
inline Generator::Operand::Operand(const Generator::Eval& e)
{
*this = e.op;
update();
put(getName() + " = " + e.s);
e.used = true;
}
inline void Generator::Operand::operator=(const Generator::Eval& e)
{
*this = e.op;
update();
put(getName() + " = " + e.s);
e.used = true;
}
struct Generator::Function {
typedef std::vector<Generator::Operand> OperandVec;
std::string name;
Generator::Operand ret;
OperandVec opv;
bool isPrivate;
bool isAlias;
void clear()
{
isPrivate = false;
isAlias = false;
}
explicit Function(const std::string& name = "") : name(name) { clear(); }
Function(const std::string& name, const Operand& ret)
: name(name), ret(ret)
{
clear();
}
Function(const std::string& name, const Operand& ret, const Operand& op1)
: name(name), ret(ret)
{
clear();
opv.push_back(op1);
}
Function(const std::string& name, const Operand& ret, const Operand& op1, const Operand& op2)
: name(name), ret(ret)
{
clear();
opv.push_back(op1);
opv.push_back(op2);
}
Function(const std::string& name, const Operand& ret, const Operand& op1, const Operand& op2, const Operand& op3)
: name(name), ret(ret)
{
clear();
opv.push_back(op1);
opv.push_back(op2);
opv.push_back(op3);
}
Function(const std::string& name, const Operand& ret, const Operand& op1, const Operand& op2, const Operand& op3, const Operand& op4)
: name(name), ret(ret)
{
clear();
opv.push_back(op1);
opv.push_back(op2);
opv.push_back(op3);
opv.push_back(op4);
}
Function(const std::string& name, const Operand& ret, const Operand& op1, const Operand& op2, const Operand& op3, const Operand& op4, const Operand& op5)
: name(name), ret(ret)
{
clear();
opv.push_back(op1);
opv.push_back(op2);
opv.push_back(op3);
opv.push_back(op4);
opv.push_back(op5);
}
void setPrivate()
{
isPrivate = true;
}
void setAlias()
{
isAlias = true;
}
std::string toStr() const
{
std::string str = std::string("define ");
if (isPrivate) {
str += "private ";
}
str += ret.getType();
str += " @" + name + "(";
for (size_t i = 0; i < opv.size(); i++) {
if (i > 0) str += ", ";
str += opv[i].toStr(isAlias);
}
str += ")";
return str;
}
};
namespace impl {
inline Generator::Eval callSub(const Generator::Function& f, const Generator::Operand **opTbl, size_t opNum)
{
if (f.opv.size() != opNum) throw cybozu::Exception("impl:callSub:bad num of arg") << f.opv.size() << opNum;
if (f.name.empty()) throw cybozu::Exception("impl:callSub:no name");
Generator::Eval e;
e.op = f.ret;
e.s = "call ";
e.s += f.ret.getType();
e.s += " @" + f.name + "(";
for (size_t i = 0; i < opNum; i++) {
if (i > 0) {
e.s += ", ";
}
e.s += opTbl[i]->toStr();
}
e.s += ")";
return e;
}
inline Generator::Eval aluSub(const char *name, const Generator::Operand& x, const Generator::Operand& y)
{
if (x.bit != y.bit) throw cybozu::Exception("Generator:aluSub:bad size") << name << x.bit << y.bit;
Generator::Eval e;
e.op.type = Generator::Int;
e.op.bit = x.bit;
e.s = name;
e.s += " ";
e.s += x.toStr() + ", " + y.getName();
return e;
}
inline Generator::Eval shiftSub(const char *name, const Generator::Operand& x, uint32_t size)
{
Generator::Eval e;
e.op = x;
e.s = name;
e.s += " ";
e.s += x.toStr() + ", " + cybozu::itoa(size);
return e;
}
} // mcl::impl
inline void Generator::beginFunc(const Generator::Function& f)
{
put(f.toStr() + "\n{");
}
inline Generator::Eval Generator::zext(const Generator::Operand& x, uint32_t size)
{
if (x.bit >= size) throw cybozu::Exception("Generator:zext:bad size") << x.bit << size;
Eval e;
e.op = x;
e.op.bit = size;
e.s = "zext ";
e.s += x.toStr() + " to i" + cybozu::itoa(size);
return e;
}
inline Generator::Eval Generator::mul(const Generator::Operand& x, const Generator::Operand& y)
{
return impl::aluSub("mul", x, y);
}
inline Generator::Eval Generator::add(const Generator::Operand& x, const Generator::Operand& y)
{
return impl::aluSub("add", x, y);
}
inline Generator::Eval Generator::sub(const Generator::Operand& x, const Generator::Operand& y)
{
return impl::aluSub("sub", x, y);
}
inline Generator::Eval Generator::_and(const Generator::Operand& x, const Generator::Operand& y)
{
return impl::aluSub("and", x, y);
}
inline Generator::Eval Generator::_or(const Generator::Operand& x, const Generator::Operand& y)
{
return impl::aluSub("or", x, y);
}
inline void Generator::ret(const Generator::Operand& x)
{
std::string s = "ret " + x.toStr();
put(s);
}
inline Generator::Eval Generator::lshr(const Generator::Operand& x, uint32_t size)
{
return impl::shiftSub("lshr", x, size);
}
inline Generator::Eval Generator::ashr(const Generator::Operand& x, uint32_t size)
{
return impl::shiftSub("ashr", x, size);
}
inline Generator::Eval Generator::shl(const Generator::Operand& x, uint32_t size)
{
return impl::shiftSub("shl", x, size);
}
inline Generator::Eval Generator::trunc(const Generator::Operand& x, uint32_t size)
{
Eval e;
e.op = x;
e.op.bit = size;
e.s = "trunc ";
e.s += x.toStr() + " to i" + cybozu::itoa(size);
return e;
}
inline Generator::Eval Generator::getelementptr(const Generator::Operand& p, const Generator::Operand& i)
{
Eval e;
e.op = p;
e.s = "getelementptr ";
if (!isOldLLVM()) {
e.s += "i" + cybozu::itoa(p.bit) + ", ";
}
e.s += p.toStr() + ", " + i.toStr();
return e;
}
inline Generator::Eval Generator::getelementptr(const Generator::Operand& p, int n)
{
return Generator::getelementptr(p, makeImm(32, n));
}
inline Generator::Eval Generator::load(const Generator::Operand& p)
{
if (!p.type.isPtr) throw cybozu::Exception("Generator:load:not pointer") << p.type;
Eval e;
e.op = p;
e.op.type.isPtr = false;
e.s = "load ";
if (!isOldLLVM()) {
e.s += "i" + cybozu::itoa(p.bit) + ", ";
}
e.s += p.toStr();
return e;
}
inline void Generator::store(const Generator::Operand& r, const Generator::Operand& p)
{
if (!p.type.isPtr) throw cybozu::Exception("Generator:store:not pointer") << p.type;
std::string s = "store ";
s += r.toStr();
s += ", ";
s += p.toStr();
put(s);
}
inline Generator::Eval Generator::select(const Generator::Operand& c, const Generator::Operand& r1, const Generator::Operand& r2)
{
if (c.bit != 1) throw cybozu::Exception("Generator:select:bad bit") << c.bit;
Eval e;
e.op = r1;
e.s = "select ";
e.s += c.toStr();
e.s += ", ";
e.s += r1.toStr();
e.s += ", ";
e.s += r2.toStr();
return e;
}
inline Generator::Eval Generator::_alloca(uint32_t bit, uint32_t n)
{
Eval e;
e.op = Operand(IntPtr, bit);
e.s = "alloca i";
e.s += cybozu::itoa(bit);
e.s += ", i32 ";
e.s += cybozu::itoa(n);
return e;
}
inline Generator::Eval Generator::bitcast(const Generator::Operand& r, const Generator::Operand& type)
{
Eval e;
e.op = type;
e.s = "bitcast ";
e.s += r.toStr();
e.s += " to ";
e.s += type.getType();
return e;
}
inline Generator::Eval Generator::icmp(Generator::CondType type, const Generator::Operand& r1, const Generator::Operand& r2)
{
Eval e;
e.op.type = Int;
e.op.bit = 1;
e.s = "icmp ";
e.s += toStr(type);
e.s += " ";
e.s += r1.toStr();
e.s += ", ";
e.s += r2.getName();
return e;
}
inline void Generator::br(const Generator::Operand& op, const Generator::Label& ifTrue, const Generator::Label& ifFalse)
{
if (op.bit != 1) throw cybozu::Exception("Generator:br:bad reg size") << op.bit;
std::string s = "br i1";
s += op.getName();
s += ", ";
s += ifTrue.toStr();
s += ", ";
s += ifFalse.toStr();
put(s);
}
inline Generator::Eval Generator::call(const Generator::Function& f)
{
return impl::callSub(f, 0, 0);
}
inline Generator::Eval Generator::call(const Generator::Function& f, const Generator::Operand& op1)
{
const Operand *tbl[] = { &op1 };
return impl::callSub(f, tbl, CYBOZU_NUM_OF_ARRAY(tbl));
}
inline Generator::Eval Generator::call(const Generator::Function& f, const Generator::Operand& op1, const Generator::Operand& op2)
{
const Operand *tbl[] = { &op1, &op2 };
return impl::callSub(f, tbl, CYBOZU_NUM_OF_ARRAY(tbl));
}
inline Generator::Eval Generator::call(const Generator::Function& f, const Generator::Operand& op1, const Generator::Operand& op2, const Generator::Operand& op3)
{
const Operand *tbl[] = { &op1, &op2, &op3 };
return impl::callSub(f, tbl, CYBOZU_NUM_OF_ARRAY(tbl));
}
inline Generator::Eval Generator::call(const Generator::Function& f, const Generator::Operand& op1, const Generator::Operand& op2, const Generator::Operand& op3, const Generator::Operand& op4)
{
const Operand *tbl[] = { &op1, &op2, &op3, &op4 };
return impl::callSub(f, tbl, CYBOZU_NUM_OF_ARRAY(tbl));
}
inline Generator::Eval Generator::call(const Generator::Function& f, const Generator::Operand& op1, const Generator::Operand& op2, const Generator::Operand& op3, const Generator::Operand& op4, const Generator::Operand& opt5)
{
const Operand *tbl[] = { &op1, &op2, &op3, &op4, &opt5 };
return impl::callSub(f, tbl, CYBOZU_NUM_OF_ARRAY(tbl));
}
#define MCL_GEN_FUNCTION(name, ...) Function name(#name, __VA_ARGS__)
} // mcl
#ifdef _MSC_VER
// #pragma warning(pop)
#endif