Radicalc  Artifact [6b29d342fb]

Artifact 6b29d342fb12b68312d40335a42153af270955c819e750698f33aff3a532bfe7:

  • File src/llvm.js — part of check-in [863a252f75] at 2017-08-01 14:18:39 on branch trunk — added intrinsincs (user: athaudia size: 6772)

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};