這篇文章描述了 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