这篇文章描述了 JIT 一个基本的原理, 将其余地方获得的二进制码直接写入内存,
随后经过强制类型转化变成函数指针.. 这样 JIT 代码就能够跑了.javascript
#include <stdio.h> // printf #include <string.h> // memcpy #include <sys/mman.h> // mmap, munmap int main () { // Hexadecimal x86_64 machine code for: int mul (int a, int b) { return a * b; } unsigned char code [] = { 0x55, // push rbp 0x48, 0x89, 0xe5, // mov rbp, rsp 0x89, 0x7d, 0xfc, // mov DWORD PTR [rbp-0x4],edi 0x89, 0x75, 0xf8, // mov DWORD PTR [rbp-0x8],esi 0x8b, 0x75, 0xfc, // mov esi,DWORD PTR [rbp-04x] 0x0f, 0xaf, 0x75, 0xf8, // imul esi,DWORD PTR [rbp-0x8] 0x89, 0xf0, // mov eax,esi 0x5d, // pop rbp 0xc3 // ret }; // allocate executable memory via sys call void* mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); // copy runtime code into allocated memory memcpy(mem, code, sizeof(code)); // typecast allocated memory to a function pointer int (*func) () = mem; // call function pointer printf("%d * %d = %d\n", 5, 11, func(5, 11)); // Free up allocated memory munmap(mem, sizeof(code)); }
使用了一个 GNU Lightning 用于动态编译代码.
代码略长, 是将 BF 代码, 转到了汇编写入文件/html
看不懂..java
文章使用了 Mike Poll 的 DynASM 来对汇编进行转化, 适应各个平台.node
首先是一个很是简单的例子(- -! 我没以为简单啊), 这个例子没有 BynASM,
只是用了 mmap
方法, 临时容许内存有可执行权限:git
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> int main(int argc, char *argv[]) { // Machine code for: // mov eax, 0 // ret unsigned char code[] = {0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3}; if (argc < 2) { fprintf(stderr, "Usage: jit1 <integer>\n"); return 1; } // Overwrite immediate value "0" in the instruction // with the user's value. This will make our code: // mov eax, <user's value> // ret int num = atoi(argv[1]); memcpy(&code[1], &num, 4); // Allocate writable/executable memory. // Note: real programs should not map memory both writable // and executable because it is a security risk. void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); memcpy(mem, code, sizeof(code)); // The function will return the user's value. int (*func)() = mem; return func(); }
后边给出了一个 BF 的 JIT 例子, 表示看不懂...
对应的完整代码在 GitHub 上 https://github.com/haberman/jitdemogithub
libjit GNU C 写的 JIT 模块, 教程几乎无法看, 太难懂了.express
但看看 Go 的封装以为仍是不错. 另外能够参考下面例子大体了解 JIT 怎样调用.app
Samples https://github.com/eliben/libjit-sampleside
是个 C++ 的 JS 封装, 里边定义很多的指令, 目前看不懂.函数
文章大体介绍了一些关于汇编的基础, 而且给出了下面代码的例子.
这里使用 esprima 解析 API 再用上边的 jit.js 生成 JIT 代码的, 难度不算过高.
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(functon() { 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') visitBinary.call(this, ast); else throw new Error('Unknown ast node: ' + ast.type); } function visitProgram(ast) { assert.equal(ast.body.length, 1, 'Only one statement programs are supported'); assert.equal(ast.body[0].type, 'ExpressionStatement'); visit.call(this, ast.body[0].expression); } function visitLiteral(ast) { assert.equal(typeof ast.value, 'number'); assert.equal(ast.value | 0, ast.value, 'Only integer numbers are supported'); this.mov('rax', ast.value); } function visitBinary(ast) { // Preserve 'rbx' after leaving the AST node this.push('rbx'); // Visit right side of expresion visit.call(this, ast.right); // Move it to 'rbx' this.mov('rbx', 'rax'); // Visit left side of expression (the result is in 'rax') visit.call(this, ast.left); // // So, to conclude, we've left side in 'rax' and right in 'rbx' // // Execute binary operation if (ast.operator === '+') { this.add('rax', 'rbx'); } else if (ast.operator === '-') { this.sub('rax', 'rbx'); } else if (ast.operator === '*') { // Signed multiplication // rax = rax * rbx this.imul('rbx'); } else if (ast.operator === '/') { // Preserve 'rdx' this.push('rdx'); // idiv is diving rdx:rax by rbx, therefore we need to clear rdx // before running it this.xor('rdx', 'rdx'); // Signed division, rax = rax / rbx this.idiv('rbx'); // Restore 'rdx' this.pop('rdx'); } else if (ast.operator === '%') { // Preserve 'rdx' this.push('rdx'); // Prepare to execute idiv this.xor('rdx', 'rdx'); this.idiv('rbx'); // imul puts remainedr in 'rdx' this.mov('rax', 'rdx'); // Restore 'rdx' this.pop('rdx'); } else { throw new Error('Unsupported binary operator: ' + ast.operator); } // Restore 'rbx' this.pop('rbx'); // The result is in 'rax' } function visitUnary(ast) { // Visit argument and put result into 'rax' visit.call(this, ast.argument); if (ast.operator === '-') { // Negate argument this.neg('rax'); } else { throw new Error('Unsupported unary operator: ' + ast.operator); } }
文章做者是 Node.js 核心成员之一, 他有另外一篇博客也讲了些 jit.js 的用例:
https://blog.indutny.com/5.allocating-numbers
一个简单的加法经过 LLVM 动态编译的例子.. 不简单, 没看懂
返回博客首页: http://blog.tiye.me