關於 JIT 基礎知識的幾個連接

Basic JIT

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

Brainfuck JIT

使用了一個 GNU Lightning 用於動態編譯代碼.
代碼略長, 是將 BF 代碼, 轉到了彙編寫入文件/html

BF-JIT

看不懂..java

Hello, JIT World: The Joy of Simple JITs

文章使用了 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

A libjit wrapper for Golang

libjit GNU C 寫的 JIT 模塊, 教程幾乎無法看, 太難懂了.express

但看看 Go 的封裝以爲仍是不錯. 另外能夠參考下面例子大體瞭解 JIT 怎樣調用.app

Samples https://github.com/eliben/libjit-sampleside

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

相關文章
相關標籤/搜索