function dataTypeStr(dataType) {
return {
i8:'i8',i16:'i16',i32:'i32',i64:'i64',
u8:'i8',u16:'i16',u32:'i32',u64:'i64',
bool:'i1',void:'void'
}[dataType.name];
}
function outDataType(out, dataType) {
out.write(dataTypeStr(dataType));
}
function outFunName(out, fun) {
out.write("@");
out.write(fun.name);
out.write("__");
out.write(fun.dataType.name);
fun.params.forEach(function(param){
out.write("__");
out.write(param.dataType.name);
});
}
// 1 and not 0 because 0 parses as false
var llvmVar = 1;
var llvmTmp = 1;
var llvmLabel = 1;
function varStr(v) {
if(!v.llvmId)
v.llvmId = llvmVar++;
return "%v" + String(v.llvmId);
}
function outVar(out, v) {
out.write(varStr(v));
}
function newTmp() {
return "%tmp"+String(llvmTmp++);
}
function newLabel() {
return "Label"+String(llvmLabel++);
}
var llvmIntrinsics = [];
function addIntrinsic(name, dataTypes, func) {
llvmIntrinsics.push({
name: name,
dataTypes: dataTypes,
func: func
});
}
function getIntrinsic(name, dataTypes) {
for(var i = 0; i < llvmIntrinsics.length; ++i) {
var int = llvmIntrinsics[i];
if(name == int.name && dataTypes.length == int.dataTypes.length) {
var pe = true;
for(var j = 0; j < dataTypes.length; ++j)
pe = pe && (dataTypes[j] == int.dataTypes[j]);
if(pe)
return int.func;
}
}
return false;
}
const icmpOp = {
"is_less": "slt",
"is_less_or_eq": "sle",
"is_greater": "sgt",
"is_greater_or_eq": "sge",
"is_eq": "eq",
"is_not_eq": "ne"
};
function initIntrinsics(dataTypes) {
["add", "sub", "mul", "div"].forEach((op) => {
[dataTypes.i8, dataTypes.i16,
dataTypes.i32, dataTypes.i64].forEach((dt) => {
addIntrinsic(op, [dt,dt], (out, params) => {
var tmp = newTmp();
out.write(" " + tmp + " = " + op + " ");
outDataType(out, dt);
out.write(" " + params[0] + ", " + params[1] + "\n")
return tmp;
});
});
});
["is_less", "is_less_or_eq",
"is_greater", "is_greater_or_eq"].forEach((op) => {
[dataTypes.i8, dataTypes.i16,
dataTypes.i32, dataTypes.i64].forEach((dt) => {
addIntrinsic(op, [dt, dt], (out, params) => {
var tmp = newTmp();
out.write(" " + tmp + " = icmp " + icmpOp[op] + " ");
outDataType(out, dt);
out.write(" " + params[0] + ", " + params[1] + "\n");
return tmp;
});
});
});
["is_eq", "is_not_eq"].forEach((op) => {
[dataTypes.i8, dataTypes.i16, dataTypes.i32, dataTypes.i64,
dataTypes.bool].forEach((dt) => {
addIntrinsic(op, [dt, dt], (out, params) => {
var tmp = newTmp();
out.write(" " + tmp + " = icmp "+ icmpOp[op] + " ");
outDataType(out, dt);
out.write(" " + params[0] + ", " + params[1] + "\n");
return tmp;
});
});
});
}
function outNode(out, node) {
switch(node.type) {
case 'funCall':
var dts = node.params.map((param) => { return param.dataType });
var intrinsicFunc = getIntrinsic(node.name, dts);
if(intrinsicFunc) {
var params = node.params.map((param) => {
return outNode(out, param);
});
return intrinsicFunc(out, params);
} else {
var params = node.params.map(function(param) {
return [outNode(out, param), param.dataType];
});
var tmp = newTmp();
out.write(" ");
if(node.dataType.name != "void") {
out.write(tmp);
out.write(" = ");
}
out.write("call ");
outDataType(out, node.dataType);
out.write(" ");
outFunName(out, node.funCall);
out.write("(");
out.write(params.map(function(param) {
return dataTypeStr(param[1]) + " " + param[0];
}).join(", "));
out.write(")\n");
}
return tmp;
case 'litInt':
//return llvmDataTypeStr(node.dataType) + " " + node.val;
return node.val;
case 'varUse':
if(node.var.isParam) {
return varStr(node.var);
}
var tmp = newTmp();
out.write(" " + tmp + " = load ");
var dt = dataTypeStr(node.dataType);
out.write(dt + ", " + dt + "* ");
outVar(out, node.var);
out.write("\n");
return tmp;
default:
out.write(" ; ")
out.write(node.type);
out.write(" node not yet implemented\n");
return "errrrrrrrorrrrr";
}
}
function outStatement(out, stmt) {
switch(stmt.type) {
case 'assign':
var tmp = outNode(out, stmt.expr);
out.write(" store ");
outDataType(out, stmt.dataType);
out.write(" ");
out.write(String(tmp));
out.write(", ");
outDataType(out, stmt.dataType);
out.write("* ");
outVar(out, stmt.var);
out.write("\n");
break;
case 'return':
if(stmt.expr) {
var ret = outNode(out, stmt.expr);
out.write(" ret ");
outDataType(out, stmt.dataType);
out.write(" ");
out.write(ret);
out.write("\n");
} else {
out.write(" ret void\n");
}
break;
case 'stmtExpr':
outNode(out, stmt.expr);
break;
case 'varDef':
if(stmt.expr) {
var tmp = outNode(out, stmt.expr);
out.write(" store ");
outDataType(out, stmt.dataType);
out.write(" ");
out.write(String(tmp));
out.write(", ");
outDataType(out, stmt.dataType);
out.write("* ");
outVar(out, stmt);
out.write("\n");
}
break;
case 'while':
var beginLabel = newLabel();
var innerBeginLabel = newLabel();
var endLabel = newLabel();
out.write(" br label %"+beginLabel+"\n");
out.write(beginLabel+":\n");
var cond = outNode(out, stmt.expr);
out.write(" br i1 " + cond + ", label %");
out.write(innerBeginLabel);
out.write(", label %");
out.write(endLabel);
out.write("\n");
out.write(innerBeginLabel + ":\n");
outBlock(out, stmt.block);
out.write(" br label %");
out.write(beginLabel);
out.write("\n");
out.write(endLabel);
out.write(":\n");
break;
default:
out.write(" ; ");
out.write(stmt.type);
out.write(" statement not yet implemented\n");
}
}
function outBlock(out, block) {
for(var key in block.scope.vars) {
var v = block.scope.vars[key];
if(!v.isParam) {
out.write(" ");
outVar(out, v);
out.write(" = alloca ");
outDataType(out, v.dataType);
out.write("\n");
}
}
block.statements.forEach(function(stmt) {
outStatement(out, stmt);
});
}
function outFunction(out, fun) {
out.write("define ");
outDataType(out, fun.dataType);
out.write(" ");
outFunName(out, fun);
out.write("(");
for(var i = 0; i < fun.params.length; ++i) {
var p = fun.params[i];
if(i > 0)
out.write(", ");
outDataType(out, p.dataType);
out.write(" ");
out.write(varStr(p));
}
out.write(") {\n");
outBlock(out, fun.block);
out.write("}\n\n");
}
function outFunctions(out, funs) {
funs.forEach(function(fun){
fun.label = newLabel();
});
funs.forEach(function(fun){
if(!fun.isStub)
outFunction(out, fun);
});
}
module.exports = {outFunctions: outFunctions, initIntrinsics: initIntrinsics};