changeset 205:6fe9343b1400

Some minor work on creating an IL backend based on the C backend
author Mike Pavone <pavone@retrodev.com>
date Sun, 13 Oct 2013 20:23:08 -0700
parents a8dffa4d4b54
children b4a9d4e405c5
files ilbackend.js tpc.js
diffstat 2 files changed, 1401 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ilbackend.js	Sun Oct 13 20:23:08 2013 -0700
@@ -0,0 +1,1397 @@
+var mainModule;
+var modules = {};
+
+var methodIds = {};
+var methodNames = [];
+var assignNames;
+
+function register(num)
+{
+	this.num = num;
+}
+
+register.prototype = {
+	valueOf: function() {
+		return 'r' + this.num;
+	},
+	get isRegister() { return true; },
+	get isArgument() { return false; }
+};
+
+function argument(num)
+{
+	this.num = num;
+}
+
+argument.prototype = {
+	valueOf: function() {
+		return 'a' + this.num;
+	},
+	get isRegister() { return true; },
+	get isArgument() { return true; }
+};
+
+function retreg(size)
+{
+	this.size = size;
+	this.reg = NULL;
+}
+
+retreg.prototype = {
+	valueOf: function() {
+		if (!this.reg) {
+			return 'retr';
+		}
+		return this.reg.valueOf();
+	},
+	clobber: function(code) {
+		this.reg = code.getReg();
+		code.addInst('mov', 'retr', this.reg, this.size);
+	},
+	get isRegister() { return true; },
+	get isArgument() { return false; }
+}
+
+function indexed(base, offset, index, size)
+{
+	this.base = base;
+	this.offset = offset;
+	this.index = index;
+	this.size = size;
+}
+
+indexed.prototype = {
+	valueOf: function() {
+		return this.offset + '[' + this.base + ' ' + this.index + '*' + this.size + ']';
+	},
+	get isRegister() { return false; },
+	get isArgument() { return false; }
+};
+
+function offset(base, offset)
+{
+	this.base = base;
+	this.offset = offset;
+}
+
+offset.prototype = {
+	valueOf: function() {
+		var out = '';
+		if (this.offset) {
+			out += this.offset;
+		}
+		return out + '[' + this.base + ']';
+	},
+	get isRegister() { return false; },
+	get isArgument() { return false; }
+};
+
+function funCode(name)
+{
+	this.name = name;
+	this.instructions = [];
+	this.nextReg = 0;
+	this.toclobber = [];
+	this.envreg = null;
+}
+
+funCode.prototype = {
+	addInst: function() {
+		var inst = '';
+		for (var i = 0; i < arguments.length; i++) {
+			if (arguments[0] == 'call') {
+				for (var i = 0; i < this.toclobber.length; i++) {
+					this.toclobber[i].clobber();
+				}
+				this.toclobber = [];
+			}
+			if (inst) {
+				inst += ' ';
+			}
+			inst += arguments[i];
+		}
+		this.instructions.push(inst);
+	},
+	getReg: function() {
+		return new register(this.nextReg++);
+	},
+	getRetReg: function(size) {
+		var reg = new retreg(size);
+		this.toclobber.push(reg);
+		return reg;
+	},
+	getEnvReg: function() {
+		if (!this.envreg) {
+			this.envreg = this.getReg();
+		}
+		return this.envreg;
+	},
+	valueOf: function() {
+		return this.name + ':\n\t' + this.instructions.join('\n\t') + '\n';
+	}
+};
+function getMethodId(methodName)
+{
+	if (!(methodName in methodIds)) {
+		methodIds[methodName] = methodNames.length;
+		methodNames.push(methodName);
+	}
+	return methodIds[methodName];
+}
+
+function getOpMethodName(opname)
+{
+	var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_',
+	                '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_',
+					'.': 'CAT_', '&&':'if', '||':'ifnot', '|': 'CONS_'};
+	if (opname in optoMeth) {
+		return optoMeth[opname];
+	} else {
+		return opname;
+	}
+}
+
+var slot_arr_offset = 8;
+function getMethodSlot(code, base, methodid)
+{
+	var reg;
+	if (!base.isRegister()) {
+		reg = code.getReg();
+		code.addInst('mov', base, reg, 'q');
+		base = reg;
+	}
+	reg = code.getReg();
+	code.addInst('mov', new offset(base, 0), reg, 'q');
+	return new offset(reg, slot_arr_offset + (methodid & 0xF)*8);
+}
+
+op.prototype.toIL = function(code, isReceiver) {
+	var method = getOpMethodName(this.op);
+	var left = this.left.toIL(code);
+	var right = this.right.toIL(code);
+	var methId = getMethodId(method);
+	if (this.op == '|') {
+		code.addInst('call', getMethodSlot(code, right, methId), methId, right, left);
+	} else {
+		code.addInst('call', getMethodSlot(code, left, methId), methId, left, right);
+	}
+	return code.getRetReg();
+};
+op.prototype.toILLLExpr = function(vars) {
+	var opmap = {'=': '==', 'xor': '^', 'or': '|', 'and': '&'};
+	return this.left.toILLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toILLLExpr(vars);
+};
+op.prototype.toILLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';'];
+};
+
+
+function escapeCName(name)
+{
+	if (name == 'self') {
+		return name;
+	}
+	name = name.replace(/_/g, "UN_").replace(/:/g, "CN_").replace(/!/g, "EX_").replace(/\?/g, 'QS_').replace(/@/g, 'AT_');
+	name = 'tp_' + name;
+	return name;
+}
+
+function getSymbolPrefix(info, symbols)
+{
+	var pre = '';
+	switch(info.type) {
+	case 'self':
+
+		pre = (new symbol('self', symbols)).toIL() + '->';
+		break;
+	case 'parent':
+		pre = (new symbol('self', symbols)).toIL() + '->header.';
+		for (var i = 0; i < info.depth; ++i) {
+			pre += (i ? '->' : '') + 'parent';
+		}
+		pre = '((' + info.selftype + ' *)' + pre + ')->';
+		break;
+	case 'upvar':
+		pre = 'env->';
+		for (var i = info.startdepth; i < info.depth; ++i) {
+			pre += 'parent->';
+		}
+		break;
+	case 'recupvar':
+		if (info.subtype == 'object') {
+			pre = 'self->env->';
+		} else {
+			//TODO: fill this case in if necessary
+		}
+		pre += getSymbolPrefix(info.parent);
+	case 'closedover':
+		pre = 'myenv->';
+	}
+	return pre;
+}
+
+symbol.prototype.toIL = function(code) {
+	var name = this.cleanName();
+	var info = this.symbols.find(name);
+	if (!info) {
+		throw new Error('symbol ' + name + ' not found in ' + assignNames.join('<-'));
+	}
+	switch (info.type)
+	{
+	case 'toplevel':
+		return toplevel.moduleVar(name);
+	case 'self':
+		if (info.def instanceof lambda) {
+			var self = (new symbol('self', this.symbols)).toIL(code);
+			var methId = getMethodId(name);
+			code.addInst('call', getMethodSlot(code, self, methId), methId, self);
+			return code.getRetReg();
+		} else {
+			throw new Error('implement me');
+		}
+	case 'parent':
+		if (info.def instanceof lambda) {
+			throw new Error('implement me');
+			var obj = (new symbol('self', this.symbols)).toIL() + '->header.';
+			for (var i = 0; i < info.depth; ++i) {
+				obj += (i ? '->' : '') + 'parent';
+			}
+			return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, ' + obj + ')';
+		} else {
+			throw new Error('implement me');
+		}
+	case 'closedover':
+		var env = code.getEnvReg();
+		return new offset(env, info.offset)
+	default:
+		throw new Error('implement ' + info.type);
+	}
+};
+symbol.prototype.toILTypeName = function() {
+	return this.cleanName();
+};
+symbol.prototype.toILLLExpr = function(vars) {
+	var name = this.cleanName();
+	if (name in vars) {
+		return name;
+	}
+	if (name == 'self') {
+		return 'self';
+	}
+	var info = this.symbols.find(name, false, true);
+	var symbols = this.symbols;
+	while (info && info.type == 'local') {
+		symbols = symbols.parent;
+		info = symbols.find(name, false, true);
+	}
+	if (!info) {
+		return name;
+	}
+	if (info.type == 'toplevel') {
+		return toplevel.moduleVar(name);
+	} else if (info.type == 'self') {
+		if (info.isll || !(info.def instanceof lambda)) {
+			return 'self->' + name;
+		} else {
+			return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, self)';
+		}
+	}
+	throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name);
+};
+symbol.prototype.toILLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ];
+};
+
+var declaredInts = {};
+
+intlit.prototype.toIL = function() {
+	var intType = (this.unsigned ? 'u' : '') + 'int' + this.bits;
+	var str =  intType + '_' + (this.val < 0 ? 'neg_' + (0-this.val).toString() : this.val.toString());
+	if (!(str in declaredInts)) {
+		toplevelcode += 'obj_' + intType + ' ' + str + ' = {{&obj_' + intType + '_meta, NULL}, ' + this.val.toString() + '};\n';
+		declaredInts[str] = true;
+	}
+	return '((object *)&' + str + ')';
+}
+intlit.prototype.toILLLExpr = function(vars) {
+	return this.val.toString();
+};
+intlit.prototype.toILLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ];
+};
+
+floatlit.prototype.toIL = function() {
+	return 'make_float(' + this.val.toString() + ')';
+}
+floatlit.prototype.toILLLExpr = function(vars) {
+	return this.val.toString();
+};
+floatlit.prototype.toILLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) + ';' ];
+};
+
+var declaredStrings = {};
+var nextStringId = 0;
+
+strlit.prototype.toIL = function() {
+	if (!(this.val in declaredStrings)) {
+		//TODO: get the proper byte length
+		toplevelcode += 'string str_' + nextStringId + ' = {{&string_meta, NULL}, ' + this.val.length + ', ' + this.val.length + ', "' + this.val.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"};\n';
+		declaredStrings[this.val] = nextStringId++;
+	}
+	return '((object *)&str_' + declaredStrings[this.val] + ')';
+};
+strlit.prototype.toILLLExpr = function(vars) {
+	return '"' + this.val.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '"';
+};
+strlit.prototype.toILLines = function(vars, needsreturn) {
+	return [ (needsreturn ? 'return (object *)' : '' ) + this.toILLLExpr(vars) +';' ];
+};
+
+listlit.prototype.toIL = function() {
+	var ret = 'make_list(' + this.val.length;
+	for (var i = this.val.length-1; i >= 0; i--) {
+		ret += ', ' + this.val[i].toIL();
+	}
+	return ret + ')';
+}
+
+arraylit.prototype.toIL = function() {
+	var ret = 'make_array(' + this.val.length;
+	for (var i = 0; i < this.val.length; i++) {
+		ret += ', ' + this.val[i].toIL();
+	}
+	return ret + ')';
+}
+
+funcall.prototype.toIL = function() {
+	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
+	if (name == 'foreign') {
+		if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object)) {
+			return null;
+		} else if(this.args[0] instanceof symbol) {
+			return this.args[0].name;
+		} else {
+			throw new Error("Unexpected AST type for foreign:");
+		}
+	} else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') {
+		return null;
+	}
+	var args = this.args.slice(0, this.args.length);
+	if (this.receiver) {
+		args.splice(0, 0, this.receiver);
+	}
+	var method = false;
+	var funinfo = this.symbols.find(name);
+	var start = 0;
+	if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') {
+		method = true;
+	} else {
+		switch(funinfo.type)
+		{
+		case 'self':
+		case 'parent':
+			var defargs = funinfo.def.args.length;
+			if (!defargs || funinfo.def.args[0].name != 'self') {
+				defargs ++
+			}
+			if (args.length < defargs) {
+				if (funinfo.type == 'self') {
+					args.splice(0, 0, new symbol('self', this.symbols));
+				} else {
+					var obj = (new symbol('self', this.symbols)).toIL() + '->header.';
+					for (var i = 0; i < funinfo.depth; ++i) {
+						obj += (i ? '->' : '') + 'parent';
+					}
+					args.splice(0, 0, ', ' + obj);
+					start = 1;
+				}
+			} else if(!(args[0] instanceof symbol) || args[0].name != 'self') {
+				funinfo = null;
+			}
+			method = true;
+			break;
+		}
+	}
+	for (var i = start; i < args.length; i++) {
+		args[i] = ', ' + (!i && method ? '(object *)(' : '') + args[i].toIL() + (!i && method ? ')' : '');
+	}
+	var callpart;
+	if (method) {
+		if (funinfo && funinfo.type == 'self' && funinfo.def.name) {
+			callpart =  funinfo.def.name + '(' + (funinfo.def.symbols.parent.needsenv ? (new symbol('self', this.symbols)).toIL() + '->env' : 'NULL' );
+		} else {
+			callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */';
+		}
+	} else {
+		var closVar = (new symbol(name, this.symbols)).toIL();
+		callpart = '((lambda *)' + closVar + ')->func(((lambda *)' + closVar + ')->env';
+	}
+	return callpart + ', ' + args.length + args.join('') + ')';
+};
+funcall.prototype.toILTypeName = function() {
+	switch(this.name)
+	{
+	case 'ptr:':
+	case 'ptr':
+		var receiver = this.receiver ? this.receiver : this.args[0];
+		return receiver.toILTypeName() + ' *';
+		break;
+	case 'struct:':
+	case 'struct':
+		var receiver = this.receiver ? this.receiver : this.args[0];
+		return 'struct ' + receiver.toILTypeName();
+		break;
+	case 'funptr:':
+	case 'funptr':
+		var rettype;
+		var firstArg;
+		if (this.receiver) {
+			rettype = this.receiver;
+			firstArg = 0;
+		} else {
+			rettype = this.args[0];
+			firstArg = 1;
+		}
+		var argtypes = '';
+		for (var i = firstArg; i < this.args.length; i++) {
+			if (argtypes) {
+				argtypes += ', '
+			}
+			argtypes += this.args[i].toILTypeName();
+		}
+		return [rettype.toILTypeName() + '(*', ')(' + argtypes + ')'];
+		break;
+	default:
+		throw new Error('invalid use of funcall expression where a C type name is expected');
+	}
+};
+funcall.prototype.toILLines = function(vars, needsreturn) {
+	var lines = [];
+	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
+	var args = this.args.slice(0, this.args.length);
+	if (this.receiver) {
+		args.splice(0, 0, [this.receiver]);
+	}
+	switch(name)
+	{
+	case 'if':
+		lines.push('if (' + this.args[0].toILLLExpr(vars) + ') {');
+		var blines = this.args[1].toILLines(vars, needsreturn);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		if (needsreturn) {
+			lines.push('} else {');
+			lines.push('\t return module_false;');
+			lines.push('}');
+		} else {
+			lines.push('}');
+		}
+		break;
+	case 'if:else':
+		lines.push('if (' + this.args[0].toILLLExpr(vars) + ') {');
+		var blines = this.args[1].toILLines(vars, needsreturn);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		lines.push('} else {');
+		blines = this.args[2].toILLines(vars, needsreturn);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		lines.push('}');
+		break;
+	case 'while:do':
+		if (needsreturn) {
+			throw new Error("while:do can't be last statement in llMessage code block");
+		}
+		lines.push('while (' + this.args[0].toILLLExpr(vars) + ') {');
+		var blines = this.args[1].toILLines(vars);
+		for (var i in blines) {
+			lines.push('\t' + blines[i]);
+		}
+		lines.push('}');
+		break;
+	default:
+		lines.push( (needsreturn ? 'return (object *)' : '') + this.toILLLExpr(vars) + ';');
+	}
+	return lines;
+};
+
+funcall.prototype.toILLLExpr = function(vars) {
+	var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name;
+	var args = this.args.slice(0, this.args.length);
+	if (this.receiver) {
+		if(this.args.length == 0) {
+			if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") {
+				return this.receiver.toILLLExpr(vars) + '.' + this.name;
+			} else {
+				return this.receiver.toILLLExpr(vars) + '->' + this.name;
+			}
+		} else if (this.args.length == 1 && name[name.length-1] == '!') {
+			if (this.receiver instanceof symbol && this.receiver.name in vars && vars[this.receiver.name].substr(-1) != "*") {
+				return this.receiver.toILLLExpr(vars) + '.' + this.name.substr(0, name.length-1) + ' = ' + args[0].toILLLExpr(vars);
+			} else {
+				return this.receiver.toILLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toILLLExpr(vars);
+			}
+		} else {
+			args.splice(0, 0, this.receiver);
+		}
+	}
+	switch(name)
+	{
+	case 'if':
+		return '((' + args[0].toILLLExpr(vars) + ') ? (' + args[1].toILLLExpr(vars) + ') : 0)';
+	case 'if:else':
+		return '((' + args[0].toILLLExpr(vars) + ') ? (' + args[1].toILLLExpr(vars) + ') : (' + args[2].toILLLExpr(vars) + '))';
+	case 'while:do':
+		throw new Error('while:do not allowed in expression context in llMessage block');
+	case 'addr_of':
+		return '&(' + args[0].toILLLExpr(vars) + ')';
+	case 'sizeof':
+		return 'sizeof(' + args[0].toILTypeName() + ')';
+	case 'get':
+		return args[0].toILLLExpr(vars) + '[' + args[1].toILLLExpr(vars) + ']';
+	case 'set':
+		return args[0].toILLLExpr(vars) + '[' + args[1].toILLLExpr(vars) + '] = ' + args[2].toILLLExpr(vars);
+	case 'not':
+		return '!(' + args[0].toILLLExpr(vars) + ')';
+	case 'castTo':
+		return '((' + args[1].toILTypeName() + ')(' + args[0].toILLLExpr(vars) + '))';
+	case 'mcall':
+		if (args[0] instanceof symbol) {
+			args[0] = new intlit(getMethodId(args[0].name));
+		}
+	default:
+		for (var i in args) {
+			args[i] = args[i].toILLLExpr(vars);
+		}
+		return name + '(' + args.join(', ') + ')';
+	}
+};
+
+function cObject(name) {
+	this.name = name;
+	this.slots = {};
+	this.properties = [];
+	this.values = [];
+	this.slotvars = {};
+	this.includes = {};
+	this.parent = 'NULL';
+	this.init = [];
+	this.initmsgadded = false;
+}
+
+cObject.prototype.addInclude = function(includefile) {
+	this.includes[includefile] = true;
+}
+
+cObject.prototype.addMessage = function(msgname, implementation) {
+	var methodid = getMethodId(msgname);
+	var trunc = methodid & 0xF;
+	if (!(trunc in this.slots)) {
+		this.slots[trunc] = [];
+	}
+	this.slots[trunc].push([methodid, '\t\t' + implementation.lines.join('\n\t\t') + '\n', msgname]);
+	if (!(trunc in this.slotvars)) {
+		this.slotvars[trunc] = {};
+	}
+	for (var varname in implementation.vars) {
+		this.slotvars[trunc][varname] = implementation.vars[varname];
+	}
+}
+
+cObject.prototype.addProperty = function(propname, value, type) {
+	if (type != undefined) {
+		this.properties.push([propname, type]);
+		if (value !== null) {
+			this.values.push(value);
+		}
+	} else {
+		var escaped = escapeCName(propname);
+		this.addMessage(propname, {
+			vars: {},
+			lines: [
+				'return self->' + escaped + ';'
+		]});
+		this.addMessage(propname + '!', {
+			vars: {},
+			lines: [
+				'self->' + escaped + ' = va_arg(args, object *);',
+				'return (object *)self;'
+		]});
+		this.properties.push(escaped);
+		this.values.push(value);
+	}
+}
+
+cObject.prototype.addInit = function(statement) {
+	this.init.push(statement);
+};
+
+cObject.prototype.addImport = function(symbols, source) {
+	this.imported.push(source);
+	var importNum = imported.length - 1;
+	if (symbols) {
+		each(symbols, function(i, sym) {
+			this.addMessage(sym.name, {
+				vars: {},
+				lines: [
+					'return self->import_' + importNum + '->meta->meth_lookup[method_id & 0xF](method_id, num_args, self->import_' + importNum + ', args);'
+				]
+			});
+		});
+	} else {
+		//TODO: handle proxying for unimplemented methods
+	}
+};
+
+cObject.prototype.checkInitMsg = function() {
+	if (!this.initmsgadded && this.init.length) {
+		var init = this.init.slice(0, this.init.length);
+		init.push('return (object *)self;');
+		this.addMessage('_init', {
+			vars: {},
+			lines: init
+		});
+		this.initmsgadded = true;
+	}
+}
+
+cObject.prototype.populateSymbols = function() {};
+
+cObject.prototype.toEarlyCDef = function() {
+	this.checkInitMsg();
+	var includes = '';
+	for (var file in this.includes) {
+		includes += '#include ' + file + '\n';
+	}
+	var objdef =  'typedef struct ' + this.name + ' {\n\tobject header;\n';
+	for (var i in this.properties) {
+		if (this.properties[i] instanceof Array) {
+			if (this.properties[i][1] instanceof Array) {
+				objdef += '\t' + this.properties[i][1][0] + this.properties[i][0] + this.properties[i][1][1] + ';\n';
+			} else {
+				objdef += '\t' + this.properties[i][1] + ' ' + this.properties[i][0] + ';\n';
+			}
+		} else {
+			objdef += '\tobject * ' + this.properties[i] + ';\n'
+		}
+	}
+	objdef += '} ' + this.name + ';\nobj_meta ' + this.name + '_meta;\n';
+	return includes + objdef;
+}
+
+cObject.prototype.toILDef = function() {
+	this.checkInitMsg();
+	var slotdefs = '';
+	var metadef = 'obj_meta ' + this.name + '_meta = {sizeof(' + this.name +'), {';
+	for (var i = 0; i < 16; i++) {
+		if (i) {
+			metadef += ', ';
+		}
+		if (i in this.slots) {
+			slotdefs += 'object * ' + this.name + '_slot_' + i + '(uint32_t method_id, uint32_t num_params, object * oself, va_list args) {\n\t' +
+				this.name + ' *self = (' + this.name + ' *)oself;\n';
+			for (var varname in this.slotvars[i]) {
+				if (this.slotvars[i][varname] instanceof Array) {
+					slotdefs += '/*foo*/\t' + this.slotvars[i][varname][0] + ' ' + varname + this.slotvars[i][varname][1] + ';\n';
+				} else {
+					slotdefs += '/*bar*/\t' + this.slotvars[i][varname] + ' ' + varname + ';\n';
+				}
+			}
+			if (this.slots[i].length == 1) {
+				slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' +
+					'\t\t' + this.slots[i][0][1] + '\n' +
+					'\t}\n' +
+					'\treturn no_impl(method_id, num_params, (object *)self, args);\n}\n';
+			} else {
+				slotdefs += '\tswitch(method_id) {\n';
+				for (j in this.slots[i]) {
+					slotdefs += '\t\tcase ' + this.slots[i][j][0] + ': /* ' + this.slots[i][j][2] + '*/\n' +
+						'\t\t\t' + this.slots[i][j][1] + '\n';
+				}
+				slotdefs += '\t\tdefault:\n' +
+					'\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n';
+
+			}
+			metadef += this.name + '_slot_' + i;
+		} else {
+			metadef += 'no_impl';
+		}
+	}
+	metadef += '}};\n';
+	return slotdefs + metadef;
+}
+
+cObject.prototype.toILInstance = function() {
+	this.checkInitMsg();
+	var ret = 'make_object(&' + this.name + '_meta, ' + this.parent + ', ' + this.values.length + (this.values.length ? ', ' : '') + this.values.join(', ') + ')';
+	if (this.initmsgadded) {
+		ret = 'mcall(' + getMethodId('_init') + ', 1, ' + ret + ')'
+	}
+	return ret;
+}
+
+cObject.prototype.toIL = function() {
+	forwarddec += this.toEarlyCDef();
+	toplevelcode += this.toILDef();
+	return this.toILInstance();
+}
+
+var nextobject = 0;
+
+
+object.prototype.toILObject = function() {
+	var messages = this.messages;
+	if (!this.name) {
+		this.name = 'object_' + nextobject++;
+	}
+	var me = new cObject(this.name);
+	this.symbols.typename = me.name;
+	if (this.symbols.needsenv) {
+		me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * ');
+		me.hasenv = true;
+	}
+	if (this.symbols.needsparent && !(this.symbols.parent instanceof osymbols)) {
+		me.parent = '(object *)(' + (new symbol('self', this.symbols.parent)).toIL() + ')';
+	}
+	for (var i in messages) {
+		if (messages[i] instanceof funcall) {
+			if (messages[i].name == 'import:' && messages[i].args.length == 1) {
+				me.addImport(false, messages[i].args[0]);
+			} else if(messages[i].name == 'import:from:' && messages[i].args.length == 2) {
+				var importsyms = [];
+				each(messages[i].args[0].val, function(i, el) {
+					if (!(el instanceof symbol)) {
+						throw new Error('Names in import:from statement must be symbols');
+					}
+					importsyms.push(el);
+				});
+				me.addImport(importsyms, messages[i].args[1]);
+			} else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) {
+				me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toILTypeName());
+			} else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) {
+				var msgname = messages[i].args[0].name
+				var rawvars = messages[i].args[1].expressions;
+				var vars = {};
+				for(var v in rawvars) {
+					vars[rawvars[v].symbol.name] = rawvars[v].expression.toILTypeName();
+				}
+				me.addMessage(msgname, {
+					vars: vars,
+					lines: messages[i].args[2].toILLines(vars, true)
+				});
+			} else if(messages[i].name == 'includeSystemHeader:' && messages[i].args.length == 1) {
+				me.addInclude("<" + messages[i].args[0].val + ">");
+			} else {
+
+				throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.');
+			}
+		} else {
+			messages[i].toILObject(me);
+		}
+	}
+
+	return me;
+};
+
+object.prototype.toIL = function() {
+	return this.toILObject().toIL();
+};
+
+var toplevelcode;
+var forwarddec;
+
+function addBinaryOp(cobject, opname, cop, objtype)
+{
+	cobject.addMessage(opname, {
+		vars: {ret: objtype + ' *', argb: objtype +' *'},
+		lines: [
+			'argb = va_arg(args, ' + objtype + ' *);',
+			'ret = (' + objtype + ' *)make_object(&' + objtype + '_meta, NULL, 0);',
+			'ret->num = self->num ' + cop + ' argb->num;',
+			'return &(ret->header);'
+		]
+	});
+}
+
+function addCompOp(cobject, opname, cop, objtype)
+{
+	cobject.addMessage(opname, {
+		vars: {argb: objtype + ' *'},
+		lines: [
+			'argb = va_arg(args, ' + objtype + ' *);',
+			'if (self->num ' + cop + ' argb->num) {',
+			'	return ' + toplevel.moduleVar('true') + ';',
+			'}',
+			'return ' + toplevel.moduleVar('false') + ';',
+		]
+	});
+}
+
+function makeInt(bits, unsigned)
+{
+	var typename = 'obj_' + (unsigned ? 'u' : '') + 'int' + bits;
+	var intObj = new cObject(typename);
+	intObj.addProperty('num', null, (unsigned ? 'u' : '') + 'int' + bits +'_t');
+	addBinaryOp(intObj, 'ADD_', '+', typename);
+	addBinaryOp(intObj, 'SUB_', '-', typename);
+	addBinaryOp(intObj, 'MUL_', '*', typename);
+	addBinaryOp(intObj, 'DIV_', '/', typename);
+	addBinaryOp(intObj, 'MOD_', '%', typename);
+	addBinaryOp(intObj, 'or', '|', typename);
+	addBinaryOp(intObj, 'xor', '^', typename);
+	addBinaryOp(intObj, 'and', '&', typename);
+	addBinaryOp(intObj, 'lshift:by', '<<', typename);
+	addBinaryOp(intObj, 'rshift:by', '>>', typename);
+	addCompOp(intObj, 'LT_', '<', typename);
+	addCompOp(intObj, 'GT_', '>', typename);
+	addCompOp(intObj, 'EQ_', '==', typename);
+	addCompOp(intObj, 'NEQ_', '!=', typename);
+	addCompOp(intObj, 'GEQ_', '>=', typename);
+	addCompOp(intObj, 'LEQ_', '<=', typename);
+	intObj.addInclude('<string.h>');
+	//-9223372036854775808
+	//01234567890123456789
+	intObj.addMessage('string', {
+		vars: {str: 'string *'},
+		lines: [
+			'str = (string *)make_object(&string_meta, NULL, 0);',
+			'str->data = GC_MALLOC(' + (bits == 64 ? 21 : 12) + ');',
+			'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') + (unsigned ? 'u' : 'd') + '", self->num);',
+			'str->len = str->bytes = strlen(str->data);',
+			'return &(str->header);'
+		]
+	});
+	//7FFFFFFFFFFFFFFF
+	//01234567890123456789
+	intObj.addMessage('hex', {
+		vars: {str: 'string *'},
+		lines: [
+			'str = (string *)make_object(&string_meta, NULL, 0);',
+			'str->data = GC_MALLOC(' + (bits == 64 ? 17 : 9) + ');',
+			'sprintf(str->data, "%' + (bits == 64 ? 'l' : '') +'X", self->num);',
+			'str->len = str->bytes = strlen(str->data);',
+			'return &(str->header);'
+		]
+	});
+	intObj.addMessage('isInteger?', {
+		vars: {},
+		lines: [
+			'return ' + toplevel.moduleVar('true') + ';'
+		]
+	});
+	intObj.addMessage('hash', {
+		vars: {},
+		lines: [
+			'return &(self->header);'
+		]
+	});
+	intObj.addMessage('signed?', {
+		vars: {},
+		lines: [
+			'return ' + toplevel.moduleVar(unsigned ? 'false' : 'true') + ';'
+		]
+	});
+	var sizes = [8, 16, 32, 64];
+	var destunsigned = [false, true];
+	for (var i = 0; i < sizes.length; i++) {
+		size = sizes[i];
+		for (var j = 0; j < destunsigned.length; j++) {
+			uns = destunsigned[j];
+			if (uns == unsigned && size == bits) {
+				intObj.addMessage((uns ? 'u' : '') + 'int' + size, {
+					vars: {},
+					lines: [
+						'return &(self->header);'
+					]
+				});
+			} else {
+				var retType = 'obj_' + (uns ? 'u' : '') + 'int' + size;
+				var retName = 'ret' + (uns ? 'u' : '') + size;
+				var vars = {};
+				vars[retName] = retType + ' *';
+				intObj.addMessage((uns ? 'u' : '') + 'int' + size, {
+
+					vars: vars,
+					lines: [
+						retName + ' = ('+retType+' *)make_object(&' + retType +'_meta, NULL, 0);',
+						retName + '->num = self->num;',
+						'return &(' + retName + '->header);'
+					]
+				});
+			}
+		}
+	}
+
+	return intObj;
+}
+
+function makeArray()
+{
+	var arrayfile = toplevel.names['array'];
+	var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
+	ast.name = 'array';
+	ast.populateSymbols(toplevel);
+	return ast.toILObject();
+}
+
+function makeString()
+{
+	var arrayfile = toplevel.names['string'];
+	var ast = parseFile(arrayfile.path + '/' + arrayfile.file);
+	ast.name = 'string';
+	ast.populateSymbols(toplevel);
+	return ast.toILObject();
+}
+
+function makelambda()
+{
+	var clos = new cObject('lambda');
+	clos.addProperty('env', null, 'void *');
+	clos.addProperty('func', null, 'closure_func');
+	clos.addMessage('while:do', {
+		vars: {action: 'lambda *', ret: 'object *'},
+		lines: [
+			'action = va_arg(args, lambda *);',
+			'ret = ' + toplevel.moduleVar('true') + ';',
+			'while(' + toplevel.moduleVar('true') + ' == ccall(self, 0)) {',
+			'	ccall(action, 0);',
+			'}',
+			'return ret;'
+		]
+	});
+	return clos;
+}
+
+function builtinTypes()
+{
+	return [makeInt(64, false), makeInt(32, false), makeInt(16, false), makeInt(8, false),
+	        makeInt(64, true) , makeInt(32, true),  makeInt(16, true),  makeInt(8, true),
+	        makeArray(), makeString(), makelambda()];
+}
+
+function addBuiltinModules(toplevel)
+{
+	var os = new cObject('mod_obj_os');
+	os.addInclude('<sys/stat.h>');
+	os.addInclude('<fcntl.h>');
+	os.addInclude('<stdlib.h>');
+	os.addInclude('<time.h>');
+	os.addInclude('<unistd.h>');
+	os.addMessage('write', {
+		vars: {str: 'string *', intret: 'obj_int32 *', filedes: 'obj_int32 *'},
+		lines: [
+			'filedes = va_arg(args, obj_int32 *);',
+			'str = va_arg(args, string *);',
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = write(filedes->num, str->data, str->bytes);',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('read', {
+		vars: {str: 'string *', size: 'obj_int32 *', filedes: 'obj_int32 *'},
+		lines: [
+			'filedes = va_arg(args, obj_int32 *);',
+			'size = va_arg(args, obj_int32 *);',
+			'str = (string *)make_object(&string_meta, NULL, 0);',
+			'str->data = GC_MALLOC_ATOMIC(size->num + 1);',
+			'str->len = str->bytes = read(filedes->num, str->data, size->num);',
+			'if (str->bytes < 0) { str->bytes = str->len = 0; }',
+			'str->data[str->bytes] = 0;',
+			'return &(str->header);'
+		]
+	});
+	os.addMessage('open', {
+		vars: {str: 'string *', flags: 'obj_int32 *', filedes: 'obj_int32 *', perm: 'obj_int32 *'},
+		lines: [
+			'str = va_arg(args, string *);',
+			'flags = va_arg(args, obj_int32 *);',
+			'filedes = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'if (num_params == 3) {',
+			'	filedes->num = open(str->data, flags->num);',
+			'} else if (num_params == 4) {',
+			'	perm = va_arg(args, obj_int32 *);',
+			'	filedes->num = open(str->data, flags->num, perm->num);',
+			'} else {',
+			'	filedes->num = -1;',
+			'}',
+			'return &(filedes->header);'
+		]
+	});
+	os.addMessage('close', {
+		vars: {filedes: 'obj_int32 *', intret: 'obj_int32 *'},
+		lines: [
+			'filedes = va_arg(args, obj_int32 *);',
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = close(filedes->num);',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('O_RDONLY', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = O_RDONLY;',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('O_WRONLY', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = O_WRONLY;',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('O_RDWR', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = O_RDWR;',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('O_CREAT', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = O_CREAT;',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('O_APPEND', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = O_APPEND;',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('O_TRUNC', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = O_TRUNC;',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('rand', {
+		vars: {intret: 'obj_int32 *'},
+		lines: [
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = rand();',
+			'return &(intret->header);'
+		]
+	});
+	os.addMessage('rand64', {
+		vars: {intret64: 'obj_int64 *'},
+		lines: [
+			'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);',
+			'intret64->num = (((int64_t)rand()) << 32 ) | rand();',
+			'return &(intret64->header);'
+		]
+	});
+	os.addMessage('srand', {
+		vars: {oseed: 'object *', seed: 'obj_int32 *'},
+		lines: [
+			'oseed = va_arg(args, object *);',
+			'seed = mcall(' + getMethodId("int32") + ', 1, oseed);',
+			'srand(seed->num);',
+			'return &(seed->header);'
+		]
+	});
+	os.addMessage('time', {
+		vars: {intret64: 'obj_int64 *'},
+		lines: [
+			'intret64 = (obj_int64 *)make_object(&obj_int64_meta, NULL, 0);',
+			'intret64->num = time(NULL);',
+			'return &(intret64->header);'
+		]
+	});
+	os.addMessage('sleep', {
+		vars: {osecs: 'object *', secs: 'obj_int32 *', intret: 'obj_int32 *'},
+		lines: [
+			'osecs = va_arg(args, object *);',
+			'secs = mcall(' + getMethodId("int32") + ', 1, osecs);',
+			'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);',
+			'intret->num = sleep(secs->num);',
+			'return &(intret->header);'
+		]
+	});
+	toplevel.names['os'] = os;
+}
+
+modulefile.prototype.toIL = function(){
+	return this.ast.toILModuleInstance();
+};
+
+function processUsedToplevel(toplevel)
+{
+	var alwaysused = ['true', 'false', 'list'];
+	var ret = '';
+	var modulenum = 0;
+	var visited = {};
+	for (var i in alwaysused) {
+		toplevel.used[alwaysused[i]] = true;
+	}
+	var newused = Object.keys(toplevel.used);
+	var allused = newused;
+	while (newused.length) {
+		for (var i in newused) {
+			debugprint('//---module', newused[i], '--- populate symbols');
+			forwarddec += 'object * ' + toplevel.moduleVar(newused[i]) + ';\n';
+			toplevel.names[newused[i]].populateSymbols(toplevel);
+			visited[newused[i]] = true;
+		}
+		newused = [];
+		for (var symbol in toplevel.used) {
+			if (!(symbol in visited)) {
+				debugprint('//found new usage of module', symbol);
+				newused.push(symbol);
+				allused.push(symbol);
+			}
+		}
+	}
+
+	for (var i = allused.length-1; i >= 0; i--) {
+		var symbol = allused[i];
+		debugprint('//---module', symbol, '(' + i +')--- compile');
+		assignNames.push(symbol);
+		ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toIL() + ';\n';
+		assignNames.pop();
+	}
+	return ret;
+}
+
+function makeCProg(obj)
+{
+	forwarddec = toplevelcode = '';
+	assignNames = [];
+	var builtins = builtinTypes();
+	for (var i in builtins) {
+		forwarddec += builtins[i].toEarlyCDef();
+		toplevelcode += builtins[i].toILDef();
+	}
+	addBuiltinModules(toplevel);
+	debugprint('//------POPULATING SYMBOLS-----');
+	obj.populateSymbols(toplevel);
+	var moduleinit = processUsedToplevel(toplevel);
+	debugprint('//------COMPILING AST-----');
+	var rest = 'object * mainModule() {\n' + moduleinit + '\tmain_module = ' + obj.toILModuleInstance() + ';\n\treturn main_module;\n}\n';
+	var mnames = 'char * methodNames[] = {\n';
+	for (var i = 0; i < methodNames.length; i++) {
+		mnames += '\t"' + methodNames[i].replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '",\n';
+	}
+	mnames += '};\n';
+	return '#include "runtime/proghead.inc"\n' +
+		'#define METHOD_ID_MAIN ' + getMethodId('main') + '\n' +
+		'#define METHOD_ID_EMPTY ' + getMethodId('empty') + '\n' +
+		'#define METHOD_ID_CONS ' + getMethodId(getOpMethodName('|')) + '\n' +
+		mnames + forwarddec + toplevelcode + rest + '#include "runtime/progfoot.inc"\n';
+}
+
+object.prototype.toILModule = function() {
+	return makeCProg(this);
+}
+
+object.prototype.toILModuleInstance = function() {
+	return this.toIL();
+}
+
+lambda.prototype.toIL = function(parentcode) {
+	var code = new funCode(this.name);
+	var args = this.args ? this.args.slice(0, this.args.length) : [];
+	var exprs = this.expressions;
+	var assignPath = assignNames.join('<-');
+	debugprint('//', this.name, assignPath);
+	var addedTypeDef = false;
+	if (Object.keys(this.symbols.closedover).length) {
+		this.symbols.envtype = this.name + '_env';
+		forwarddec += 'typedef struct ' + this.symbols.envtype + '  ' + this.symbols.envtype + ';\n'
+		var addedTypeDef = true;
+	}
+	if (this.selftype) {
+		this.symbols.defineVar('self', this.selftype);
+		if (args[0] && args[0].cleanName() == 'self') {
+			args.splice(0, 1);
+		}
+		var offset = 1;
+	} else {
+		var offset = 0;
+	}
+	for (var i = 0; i < args.length; ++i) {
+		var argname = args[i].toIL(code);
+
+		args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n';
+	}
+	var compiled = []
+	for (var i in exprs) {
+		var js = exprs[i].toIL(code);
+		if (js) {
+			compiled.push(indent(js));
+		}
+	}
+	if (compiled.length) {
+		if (exprs[exprs.length - 1] instanceof assignment) {
+			compiled.push('return ' + exprs[exprs.length - 1].symbol.toIL() + ';');
+		} else {
+			compiled[compiled.length-1] = 'return (object *)(' + compiled[compiled.length-1] + ');';
+		}
+	}
+	exprs = compiled;
+
+	if (Object.keys(this.symbols.closedover).length) {
+		if (!addedTypeDef) {
+			for (var key in this.symbols.closedover) {
+				print(key, ": ", this.symbols.closedover[key]);
+			}
+			throw new Error('this.symbols.closedover is not empty, but it was when compilation of ' + this.name + ' "' + assignPath + '" started');
+		}
+		forwarddec += 'struct ' + this.name + '_env {\n';
+		if (this.symbols.needsParentEnv) {
+			forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n';
+		}
+		for (var varname in this.symbols.closedover) {
+			if (varname == 'self' && this.selftype) {
+				forwarddec += '\tstruct ' + this.selftype + ' * self;\n';
+			} else {
+				forwarddec += '\tobject * ' + escapeCName(varname) + ';\n';
+			}
+		}
+		forwarddec += '};\n'
+
+		var myenvinit = '\t' + this.name + '_env * myenv = GC_MALLOC(sizeof(' + this.name + '_env));\n';
+		if (this.symbols.needsParentEnv) {
+			myenvinit += '\tmyenv->parent = env;\n';
+		}
+		this.symbols.envtype = this.name + '_env';
+	} else {
+		var myenvinit = '';
+	}
+	forwarddec += 'object *' + this.name + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...);\n';
+
+	toplevelcode += '//' + assignPath + "\n";
+	toplevelcode +=  'object * ' + this.name + ' ( ' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n';
+	if (this.selftype) {
+		var selfvar = (new symbol('self', this.symbols)).toIL();
+		if (selfvar == 'self') {
+			toplevelcode += '\t' + this.selftype + ' * self = va_arg(args, ' + this.selftype + ' *);\n';
+		} else {
+			toplevelcode += '\t' + selfvar  + ' = va_arg(args, ' + this.selftype + ' *);\n';
+		}
+
+	}
+	toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n';
+
+	if (this.selftype) {
+		return this.name;
+	} else {
+		if (this.symbols.parentEnvType() != 'void') {
+			if (this.symbols.parent.passthruenv) {
+				var envvar = 'env';
+			} else {
+				var envvar = 'myenv';
+			}
+			debugprint('//' + this.name, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length);
+			return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')';
+		} else {
+			toplevelcode += 'lambda ' + this.name + '_obj = {{&lambda_meta, NULL}, NULL, ' + this.name + '};\n';
+			return '((object *)&' + this.name + '_obj)';
+		}
+	}
+};
+lambda.prototype.toILModuleInstance = function() {
+	this.toIL();
+	return this.name + '(NULL, 0)';
+};
+lambda.prototype.toILObject = function(typename) {
+	this.selftype = typename;
+	return this.toIL();
+};
+lambda.prototype.toILModule = function() {
+	return makeCProg(this);
+};
+lambda.prototype.toILLines = function(vars, needsreturn) {
+	var lines = [];
+	for (var i in this.args) {
+		var name = this.args[i].name;
+		if (name[0] == ':') {
+			name = name.substr(1);
+		}
+		if(name != 'self') {
+			lines.push(name + ' = va_arg(args, ' + vars[name] + ');');
+		}
+	}
+	for (var i in this.expressions) {
+		var exprlines = this.expressions[i].toILLines(vars, needsreturn && i == this.expressions.length - 1);
+		for (var j in exprlines) {
+			lines.push('\t' + exprlines[j]);
+		}
+	}
+	return lines;
+}
+lambda.prototype.toILLLExpr = function(vars) {
+	if (this.expressions.length != 1) {
+		throw new Error('lambda in expression context must have a single statement in llMessage block');
+	}
+	return this.expressions[0].toILLLExpr(vars);
+}
+
+assignment.prototype.toIL = function() {
+	debugprint('//assignment', this.symbol.name);
+	var existing = this.symbols.find(this.symbol.name);
+	var prefix = '';
+	assignNames.push(this.symbol.name);
+	var val = this.expression.toIL();
+	assignNames.pop(this.symbol.name);
+	if (val === null) {
+		return null;
+	}
+	if (existing.type == 'local' && !existing.isdeclared) {
+		prefix = 'object *';
+		this.symbols.declareVar(this.symbol.name);
+		debugprint('//declared var', this.symbol.name);
+	}
+	return prefix + this.symbol.toIL() + ' = ' + val;
+};
+assignment.prototype.toILObject = function(cobj) {
+	debugprint('//message definition', this.symbol.name);
+	assignNames.push('#' + this.symbol.name);
+	if (this.expression.toILObject) {
+		var val = this.expression.toILObject(cobj.name);
+	} else {
+		var val = this.expression.toIL();
+	}
+	assignNames.pop();
+	if (val === null) {
+		return;
+	}
+	if (this.expression instanceof lambda) {
+		var params = ['((object *)self)'];
+		var paramget = '';
+		var messagevars = {};
+		for (var i in this.expression.args) {
+			var escaped = escapeCName(this.expression.args[i].cleanName());
+			if (escaped != 'self') {
+				messagevars[escaped] = 'object *';
+				params.push(escaped);
+				paramget += escaped + ' =  va_arg(args, object *); ';
+			}
+		}
+		cobj.addMessage(getOpMethodName(this.symbol.name), {
+			vars: messagevars,
+			lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');']
+		});
+	} else {
+		cobj.addProperty(this.symbol.name, val);
+		if (this.expression instanceof object && this.expression.symbols.needsparent) {
+			cobj.addInit('self->' + escapeCName(this.symbol.name) + '->parent = (object *)self;');
+		}
+	}
+};
+assignment.prototype.toILLines = function(vars, needsreturn) {
+	return [(needsreturn ? 'return ' : '') + this.symbol.toILLLExpr(vars) + ' = ' + this.expression.toILLLExpr(vars) + ';']
+};
--- a/tpc.js	Wed Aug 28 21:50:22 2013 -0700
+++ b/tpc.js	Sun Oct 13 20:23:08 2013 -0700
@@ -94,6 +94,8 @@
 	load(basedir + 'compiler.js');
 	if (backend == 'C') {
 		load(basedir + 'cbackend.js');
+	} else if (backend == 'IL') {
+		load(basedir + 'ilbackend.js');
 	} else {
 		load(basedir + 'jsbackend.js');
 	}
@@ -108,6 +110,8 @@
 	case 'C':
 		var c = parsed.toCModule();
 		break;
+	case 'IL':
+		var c = parsed.toILModule();
 	case 'JS':
 		var c = makeJSProg(parsed);
 		break;