UNPKG

jaycc

Version:

### the Javascript C Compiler

349 lines (348 loc) 11.2 kB
/* codegen.jsplus */ import { ASTfunction, expr, numberconstant, Returnstm, Program } from './ast.js'; var architecture; (function (architecture) { architecture[architecture["ia16"] = 0] = "ia16"; architecture[architecture["ia32"] = 1] = "ia32"; architecture[architecture["ia64"] = 2] = "ia64"; })(architecture || (architecture = {})); let salloc; let sfree; let state; let test; let label; salloc = (s) => { let r; r = s.freelist.shift() || null; if (!r) throw new Error('Out of scratches'); s.alloclist.push(r); return r; }; sfree = (s, r) => { let rs; let n; n = s.alloclist.length; rs = s.alloclist.filter((x) => (x !== r)); if (n !== rs.length) s.freelist.push(r); return; }; state = () => ({ alloclist: [], freelist: ['bx', 'cx', 'di', 'si'], nextlabel: 1, labellist: new Set() }); label = (s, l, global) => { let x; x = (l === null) ? { name: "lbl" + (s.nextlabel++).toString(), global: (global) ? true : false } : (l == 'main') ? { name: '_main', global: false } : { name: l, global: (global) ? true : false }; if (s.labellist.has(x.name)) throw new Error('Duplicate symbols: ' + x.name); else return (s.labellist.add(x.name) && x); }; class Arch { arch; regset; constructor(a) { this.regset = [ { idx: 0, r16: 'ax', r32: 'eax', r64: 'rax' }, { idx: 1, r16: 'bx', r32: 'ebx', r64: 'rbx' }, { idx: 2, r16: 'cx', r32: 'ecx', r64: 'rcx' }, { idx: 3, r16: 'dx', r32: 'edx', r64: 'rdx' }, { idx: 4, r16: 'di', r32: 'edi', r64: 'rid' }, { idx: 5, r16: 'si', r32: 'esi', r64: 'rsi' }, { idx: 6, r16: 'sp', r32: 'esp', r64: 'rsp' }, { idx: 7, r16: 'bp', r32: 'ebp', r64: 'rbp' }, { idx: 8, r16: 'ip', r32: 'eip', r64: 'rip' }, { idx: 9, r16: 'ss', r32: 'ss', r64: 'ss' }, { idx: 10, r16: 'ds', r32: 'ds', r64: 'ds' }, { idx: 11, r16: 'es', r32: 'es', r64: 'es' }, { idx: 12, r16: 'cs', r32: 'cs', r64: 'cs' }, { idx: 13, r16: null, r32: 'gs', r64: 'gs' }, { idx: 14, r16: null, r32: 'fs', r64: 'fs' } ]; switch (a) { case architecture.ia16: case architecture.ia32: case architecture.ia64: this.arch = a; break; default: throw new Error('Unknown architecture'); return false; break; } } convert(r) { let ret; let reg; reg = this.regset.find((x) => ((x.r16 == r) || (x.r32 == r))) || null; if (!reg) { throw new Error('Unknown register'); return false; } switch (this.arch) { case architecture.ia16: if (reg.r16 === null) return reg.r32; else return reg.r16; case architecture.ia32: return reg.r32; case architecture.ia64: return reg.r64; default: throw new Error('Bad archtecture'); return false; } } } class Assembly { header; body; regs; constructor(s, arch, node, header, body, register) { let reg; let dstreg; let srcreg; this.regs = []; this.header = this.body = ''; if (!node) { if (!header && !body) { throw new Error('Argument error'); return false; } else { (header) ? (this.header = header) : void 0; (body) ? (this.body = body) : void 0; } } else switch (true) { case (node instanceof Program): let bits; let sbits; let lbl1; let lbl2; let r; let r2; let exitnr; let sexitnr; bits = (arch.arch == architecture.ia16) ? 16 : (arch.arch == architecture.ia32) ? 32 : (arch.arch == architecture.ia64) ? 64 : this.fail('Bad architecture'); exitnr = (arch.arch < architecture.ia64) ? 1 : (arch.arch == architecture.ia64) ? 60 : this.fail('Bad architecture'); sexitnr = exitnr.toString(); sbits = bits.toString(); lbl1 = label(s, '_start', true); lbl2 = label(s, '_exit', false); r = arch.convert('ax'); r2 = arch.convert('bx'); this.header = `bits ${sbits}\n` + `global ${lbl1.name}`; this.body = "\n" + "_exit:\n" + `mov ${r2},${r}\n` + `mov ${r},${sexitnr}\n` + "int 0x80\n" + "ret\n" + "\n" + "_start:\n" + "call _main\n" + "call _exit\n" + "ret\n"; break; case (node instanceof ASTfunction): { let name; let lbl; let bp; let sp; name = node.name; lbl = label(s, name, true); bp = arch.convert('bp'); sp = arch.convert('sp'); if (lbl.global) this.header = `global ${lbl.name}`; this.body = `\n\n${lbl.name}:\n` + `push ${bp}\n` + `mov ${bp},${sp}`; } break; case (node instanceof Returnstm): { let bp; let sp; if (!register) { throw new Error('No scratch register provided'); return false; } sp = arch.convert('sp'); bp = arch.convert('bp'); dstreg = arch.convert('ax'); srcreg = arch.convert(register); sfree(s, register); this.header = ''; this.body = `mov ${dstreg},${srcreg}\n` + `mov ${sp},${bp}\n` + `pop ${bp}\n` + "ret"; } break; default: switch (node.tag) { // case 'Expr': case 'Numberconstant': reg = salloc(s); this.regs.push(reg); dstreg = arch.convert(reg); this.header = ''; this.body = `mov ${dstreg},${new Number(node)}\n`; break; default: throw new Error('Unknown AST node'); return false; break; } } } fail(msg = 'Error in code generation') { throw new Error(msg); return false; } get emit() { return [this.header, this.body, this.regs]; } } class CodeGen { header; text; data; bss; constructor(s, ast, arch) { let p; let c; let r; let e; let n; let regs; let reg; let hdr; let body; let a; let asm; // Initialization this.header = ''; this.text = ''; this.data = ''; this.bss = ''; // Program a = new Arch(arch); p = ast; asm = new Assembly(s, a, p); [hdr, body] = asm.emit; this.header += hdr + "\n"; this.text += body + "\n"; // ASTfunction p.fns.forEach((x) => { asm = new Assembly(s, a, x); [hdr, body] = asm.emit; this.header += hdr + "\n"; this.text += body + "\n"; // Codeblock c = x.body; // Stm c.stms.forEach((y) => { switch (y.tag) { case 'Returnstm': r = y; // || this.fail('Bad statement'); // Expr e = r.expr; n = e; asm = new Assembly(s, a, e); [hdr, body, regs] = asm.emit; reg = regs[0]; this.header += hdr; this.text += body; // Returnstm asm = new Assembly(s, a, r, undefined, undefined, reg); [hdr, body] = asm.emit; this.header += hdr; this.text += body; break; default: throw new Error('Bad statement'); return false; break; } }); }); } fail(msg = 'Error in code generation') { throw new Error(msg); return false; } get emit() { return this.header + "\n" + ((this.data.length) ? this.data + "\n" : '') + ((this.bss.length) ? this.bss + "\n" : '') + ((this.text.length) ? this.text + "\n" : ''); } } test = () => { let e; let r; let n; let output; let output2; let arch; let asm; let asm2; let s; let _, __, sc; n = numberconstant(5); e = expr(n); r = new Returnstm(e); s = state(); arch = new Arch(architecture.ia16); asm = new Assembly(s, arch, e); [_, output, sc] = asm.emit; asm2 = new Assembly(s, arch, r, undefined, undefined); [_, output2, __] = asm2.emit; console.log(output); console.log(output2); // console.log(asm); return void 0; }; export { test, state, Arch, CodeGen, architecture };