it('should invoke assembly code via wrapper', function() { var wrapperCode = jit.compile(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.spill([ 'rbx', 'r12', 'r13', 'r14', 'r15' ], function() { // Shift args this.mov('rax', 'rdi'); this.mov('rdi', 'rsi'); this.mov('rsi', 'rdx'); this.mov('rdx', 'rcx'); this.mov('rcx', 'r8'); this.call('rax'); }); this.Return(); }); })._buffer; // NOTE: Old wrapper code function wrapper(fn, state, self, args) { var argv = args.map(function(arg) { return arg.deref(); }); argv.unshift(state.deref()); argv.unshift(self.deref()); argv.unshift(fn.code().code()); return heap.binding.call(wrapperCode, argv); } var h = heap.create({ callWrapper: wrapper }); var code = jit.generate(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.mov('rax', 'rcx'); this.Return(); }); }); var c = h.allocCode(code.buffer, []); code.resolve(c.deref()); var fn = h.allocFunction(c); var r = fn.call(h.allocObject(32), [ h.allocString('hello'), h.allocString('ohai') ]); assert.equal(r.cast().value(), 'ohai'); });
it('should invoke assembly code via wrapper', function() { var wrapper = jit.compile(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.spill([ 'rbx', 'r12', 'r13', 'r14', 'r15' ], function() { // Shift args this.mov('rax', 'rdi'); this.mov('rdi', 'rsi'); this.mov('rsi', 'rdx'); this.mov('rdx', 'rcx'); this.mov('rcx', 'r8'); this.call('rax'); }); this.Return(); }); })._buffer; var h = heap.create({ callWrapper: wrapper }); var code = jit.generate(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.mov('rax', 'rcx'); this.Return(); }); }); var c = h.allocCode(code.buffer, []); code.resolve(c.deref()); var fn = h.allocFunction(c); var r = fn.call(h.allocObject(32), [ h.allocString('hello'), h.allocString('ohai') ]); assert.equal(r.cast().toString(), 'ohai'); });
function Context(compiler, options) { this.compiler = compiler; this.heap = this.compiler.heap; this.block = null; this.masm = jit.create(util._extend({ stubs: this.compiler.platform.stubs, helpers: this.compiler.platform.helpers }, options || {})); this.masm.ctx = this; this.references = null; this.offsets = []; this.blocks = {}; }
it('should invoke assembly code', function() { var code = jit.generate(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.mov('rax', 'rcx'); this.Return(); }); }); var c = h.allocCode(code.buffer, []); code.resolve(c.deref()); var fn = h.allocFunction(c); var r = fn.call(null, [ h.allocString('hello'), h.allocString('ohai') ]); assert.equal(r.cast().toString(), 'ohai'); });
function Base(runtime) { this.runtime = runtime; this.ctx = null; this._baseState = new BaseState(); // NOTE: will be set by runtime this.heap = null; this.compiler = null; this.helpers = util._extend({}, Base.helpers); this.helpers.platform = this; this.ptrSize = 0; this.Stub = stub.Stub; this.IC = ic.IC; // Get helpers, args, ret this.init(); this.registers = null; this.instructions = Base.instructions.get.call(this); this.instructionMethods = null; var self = this; this.stubs = jit.stubs({ helpers: this.helpers, compile: function(body, options) { return self.ctx.compiler.compileStubs(body, options); } }); this.cfgStubs = {}; this.initStubs(); }
X64.prototype.initCallWrapper = function initCallWrapper() { var self = this; var proc = jit.compile(function() { this.Proc(function() { // Callee-save this.spill([ 'rbx', 'r12', 'r13', 'r14', 'r15' ], function() { // rdi = code // rsi = state // rdx = buf.length // rcx = buf // Copy frame from buffer to stack var loop = this.label(); this.shr('rdx', heap.tagShift); this.lea('rcx', [ 'rcx', -heap.tagPointer ]); this.lea('rdx', [ 'rcx', 'rdx', -heap.ptrSize]); // NOTE: assumes that the buffer is never empty this.bind(loop); this.push([ 'rdx' ]); this.sub('rdx', heap.ptrSize); this.cmp('rdx', 'rcx'); this.j('ge', loop); // Untag code this.lea('rax', [ 'rdi', -heap.tagPointer ]); // Load state (note: 'rdi' === self.state, so previous line should // be previous) this.mov(self.state, 'rsi'); // Untag code pointer this.alignedCall('rax'); }); this.Return(); }); }, { helpers: X64.helpers })._buffer; this.callWrapper = function callWrapper(fn, ctx, self, argv) { // Aligned frame length var len = heap.ptrSize * (3 + argv.length); if (len % (2 * heap.ptrSize) !== 0) len += heap.ptrSize; var frame = new Buffer(len); var off = 0; // This heap.binding.writeTagged(frame, self.deref(), off); off += heap.ptrSize; // Self heap.binding.writeTagged(frame, fn.deref(), off); off += heap.ptrSize; // Argc heap.binding.writeTagged(frame, argv.length, off); off += heap.ptrSize; // Copy all args for (var i = 0; i < argv.length; i++, off += heap.ptrSize) heap.binding.writeTagged(frame, argv[i].deref(), off); // Fill rest with zeroes for (; off !== frame.length; off += heap.ptrSize) heap.binding.writeTagged(frame, 0, off); var code = fn.code().code(); return heap.binding.call(proc, [ code, ctx.deref(), frame.length, frame ]); }; };
#!/usr/bin/env node var jit = require('jit.js'), esprima = require('esprima'), assert = require('assert'); var ast = esprima.parse(process.argv[2]); // Compile var fn = jit.compile(function() { // This will generate default entry boilerplate this.Proc(function() { visit.call(this, ast); // The result should be in 'rax' at this point // // This will generate default exit boilerplate this.Return(); }); }); // Execute console.log(fn()); function visit(ast) { if (ast.type === 'Program') visitProgram.call(this, ast); else if (ast.type === 'Literal') visitLiteral.call(this, ast); else if (ast.type === 'UnaryExpression') visitUnary.call(this, ast); else if (ast.type === 'BinaryExpression')
function Base(runtime) { this.runtime = runtime; this.ctx = null; this._baseState = new BaseState(); // NOTE: will be set by runtime this.heap = null; this.helpers = util._extend({}, Base.helpers); this.helpers.platform = this; this.ptrSize = 0; // Get helpers, args, ret this.init(); var out = { type: 'register', id: this.ret }; var reg = { type: 'register' }; this.registers = null; this.instructions = { literal: { inputs: [ { type: 'js' } ], output: reg }, binary: { inputs: [ { type: 'js' }, reg, reg ], output: out, shallow: true }, ret: { inputs: [ out ], output: null }, global: { inputs: [], output: reg }, 'this': { inputs: [], output: reg }, self: { inputs: [], output: reg }, allocHashMap: { inputs: [ { type: 'js' } ], output: out }, allocObject: { inputs: [ reg ], output: out }, loadProperty: { inputs: [ reg, reg ], output: out, shallow: true }, storeProperty: { inputs: [ reg, reg, reg ], output: out, shallow: true }, toBoolean: { inputs: [ reg ], output: out, shallow: true }, branch: { inputs: [ reg ], output: null }, nop: { inputs: [ { type: 'any' } ], output: [ reg ] }, loadArg: { inputs: [ { type: 'js' } ], output: reg }, pushArg: { inputs: [ reg ], output: null }, alignStack: { inputs: [ { type: 'js' } ], output: null }, fn: { inputs: [ { type: 'js' } ], output: out }, checkMap: { inputs: [ reg, { type: 'js' } ], output: null, shallow: true }, call: { inputs: [ reg, reg, { type: 'js' } ], output: out, call: true, shallow: true } }; var self = this; this.stubs = jit.stubs({ helpers: this.helpers, compile: function(body, options) { return self.ctx.compiler.compileStubs(body, options); } }); this.initStubs(); }
describe('placing in object', function() { var acc = new Buffer(heap.ptrSize); var getCode = jit.generate(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.mov('rax', acc.ref()); this.add([ 'rax' ], 1 << heap.tagShift); this.mov('rax', [ 'rax' ]); this.Return(); }); }); var setCode = jit.generate(function() { this.Proc(function() { assert.equal(this.arch, 'x64'); this.mov('rax', acc.ref()); this.mov([ 'rax' ], 'rdx'); this.Return(); }); }); it('should invoke getter', function() { acc.fill(0); var getter = h.allocFunction(h.allocCode(getCode.buffer)); var pair = h.allocAccessPair({ getter: getter }); var o = h.allocObject(); var key = h.allocString('key'); o.set(key, pair); assert.equal(o.get(key).cast().value(), 1); assert.equal(o.get(key).cast().value(), 2); }); it('should invoke setter', function() { acc.fill(0); var getter = h.allocFunction(h.allocCode(getCode.buffer)); var setter = h.allocFunction(h.allocCode(setCode.buffer)); var pair = h.allocAccessPair({ getter: getter, setter: setter }); var o = h.allocObject(); var key = h.allocString('key'); o.set(key, pair); assert.equal(o.get(key).cast().value(), 1); assert.equal(o.get(key).cast().value(), 2); o.set(key, h.smi(42)); assert.equal(o.get(key).cast().value(), 43); assert.equal(o.get(key).cast().value(), 44); }); it('should work with no getter/setter', function() { var pair = h.allocAccessPair(); var o = h.allocObject(); var key = h.allocString('key'); o.set(key, pair); assert(o.get(key).isUndef()); o.set(key, h.smi(42)); assert(o.get(key).isUndef()); }); it('should be enumerable by default', function() { var pair = h.allocAccessPair(); var o = h.allocObject(); var key = h.allocString('key'); o.set(key, pair); assert.deepEqual(o.toJSON(), { key: undefined }); }); it('should support custom attributes', function() { var pair = h.allocAccessPair({ enumerable: false }); var o = h.allocObject(); var key = h.allocString('key'); o.set(key, pair); assert.deepEqual(o.toJSON(), {}); }); });