Basic JIT
这篇文章描述了 JIT 一个基本的原理, 将其他地方得到的二进制码直接写入内存,
随后通过强制类型转化变成函数指针.. 这样 JIT 代码就可以跑了.
#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));
}
Brainfuck JIT
使用了一个 GNU Lightning 用于动态编译代码.
代码略长, 是将 BF 代码, 转到了汇编写入文件/
BF-JIT
看不懂..
Hello, JIT World: The Joy of Simple JITs
文章使用了 Mike Poll 的 DynASM 来对汇编进行转化, 适应各个平台.
首先是一个非常简单的例子(- -! 我没觉得简单啊), 这个例子没有 BynASM,
只是用了 mmap
方法, 临时允许内存有可执行权限:
#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/jitdemo
A libjit wrapper for Golang
libjit GNU C 写的 JIT 模块, 教程几乎没法看, 太难懂了.
但看看 Go 的封装觉得还是不错. 另外可以参考下面例子大致了解 JIT 怎样调用.
Samples https://github.com/eliben/libjit-samples
jit.js - JIT Assembler in javascript
是个 C++ 的 JS 封装, 里边定义不少的指令, 目前看不懂.
How to start JIT-ting
文章大致介绍了一些关于汇编的基础, 并且给出了下面代码的例子.
这里使用 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
Simple VM JIT with LLVM
一个简单的加法通过 LLVM 动态编译的例子.. 不简单, 没看懂
A Basic Just-In-Time Compiler
How to JIT - an introduction
Building a simple JIT in Rust
返回博客首页: http://blog.tiye.me
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。