您的位置:首页 > 编程语言

创建一门新的编程语言-Flex&Bison&libjit-(5)-流行jit工具之一-libjit

2013-12-28 23:41 435 查看
目前我能找到的好用的jit有两个:libjit和llvm。其中对于现在最出名的要数llvm,libjit可能有的人连听都没听说过。

这后面有一个悲伤的故事:

libjit是dotgnu的子项目。在2004年前后的几年,libjit力压llvm,是最流行的jit工具,几乎没人对oop过度设计的llvm感兴趣(这一点现在也一直在恶心我)。但是,dotgnu最终解体(后面大名鼎鼎的mono项目和dotgnu有很大的关系),libjit逐渐没落,众多开发人员如今只剩下一个人,其还在默默维护着libjit,但是已经没有新增特性了。所以,llvm便逐渐成了现在的样子。

但是我还是要先介绍libjit。对比libjit和llvm,很多api与设计理念是差不多的,先了解学习libjit有助于了解这些jit工具的通用套路。

libjit目前的主页在http://www.gnu.org/software/libjit/,最新的released版本还停留在一个2008年发布的0.1.2(git上的版本不推荐用),虽然“古老”,但是支持的平台还是较多的,但能生成机器码(libjit和llvm都有两种模式:解释和编译,解释就是所谓的“一句一句”执行,编译则是转换成机器码)的平台只有x86和x86-64。

libjit不是我们教程的重点,这里就简单说说。

先看代码(看之前最好看看libjit的document):

#include <stdio.h>
#include <jit/jit.h>
#include <jit/jit-dump.h>
#include <jit/jit-elf.h>
#include <iostream>

using namespace std;
#define prt(x) cout << x << endl;
int tfunc(int a, int b) //我们的目标函数样本,我们将尝试转换成jit
{
int x = 0;
struct ts
{
int c;
int d;
};
ts ats;
ats.c = 10;
ats.d = 1;
while (a < b)
{
x += a;
a++;
}
prt(x);
prt(ats.c);
return x;
}

void prt_int(int a)
{
prt("Sound from jit:" << a);
}

inline jit_nuint getoff(jit_value_t val, const char *name)
{
jit_type_t vt = jit_value_get_type(val);
return jit_type_get_offset(vt, jit_type_find_name(vt, name));
}

jit_function_t gen_t_func(jit_context_t context)
{
jit_context_build_start(context); //开始构建context。这个和最后的jit_context_build_end(context); 配套使用,作用是多线程时保证只有 一个进程在操作context
jit_type_t parms_t[] = { jit_type_int, jit_type_int }; //声明两个type,分别是tfunc的a和b的type
jit_type_t sign =
jit_type_create_signature(jit_abi_cdecl, jit_type_int, parms_t, 2, 1); //类似java里builder的东西,具体看document
jit_function_t f = jit_function_create(context, sign); //在context里新建这个函数

jit_value_t a = jit_value_get_param(f, 0); //把参数提取出来,转换成value
jit_value_t b = jit_value_get_param(f, 1);

jit_value_t x = jit_value_create(f, jit_type_int); //新建变量。非常需要注意的是:这里与llvm不同,这里得到的value就是相应类型的实现,但是在llvm进行类似操作,得到的是相应类型的指针
#define CONST_INT(f,x) jit_value_create_nint_constant(f,jit_type_nint,x)
jit_insn_store(f, x, CONST_INT(f, 0)); //赋值。中间有insn的函数,都是在创建操作(instruction)

jit_type_t ts_fs[] = { jit_type_int, jit_type_int };
jit_type_t ts = jit_type_create_struct(ts_fs, 2, 1);
char *ts_names[] = { "c", "d" };
jit_type_set_names(ts, ts_names, 2); //只是起个名字

jit_value_t ats = jit_value_create(f, ts);
jit_value_set_addressable(ats); //我们要用他的指针,所以要设置成addressable
jit_insn_store_relative(f, jit_insn_address_of(f, ats), getoff(ats, "c"), //根据offset来赋值
CONST_INT(f, 10));
jit_insn_store_relative(f, jit_insn_address_of(f, ats), getoff(ats, "d"),
CONST_INT(f, 1));

jit_label_t while_l = jit_label_undefined, after_while_l =
jit_label_undefined; //建立两个label,while里与while后的程序段。在llvm不叫label,叫block(可能更好理解吧)
jit_insn_label(f, &while_l); //转换成while_l模式,今后的操作会反应在while_l里
jit_value_t cmpx0 = jit_insn_lt(f, a, b); //比较
jit_insn_branch_if_not(f, cmpx0, &after_while_l); //branch操作。branch用来跳转label,使程序有目的地跳转到不同的代码段
jit_value_t tmp1 = jit_insn_add(f, x, a);
jit_insn_store(f, x, tmp1);
jit_value_t tmp2 = jit_insn_add(f, a, CONST_INT(f, 1));
jit_insn_store(f, a, tmp2);
jit_insn_branch(f, &while_l);
jit_insn_label(f, &after_while_l);
jit_type_t params_p[] = { jit_type_int };
jit_type_t sign_prt_int =
jit_type_create_signature(jit_abi_cdecl, jit_type_void, params_p, 1,
1);
jit_value_t tmp_args[] = { x };

jit_insn_call_native(f, "prt_int", (void *)prt_int, sign_prt_int, tmp_args,
1, 0); //调用我们的pri_int函数,其在我们的C程序中定义
jit_value_t tmp_args2[] =
{ jit_insn_load_relative(f, jit_insn_address_of(f, ats),
getoff(ats, "c"), jit_type_int) };
jit_insn_call_native(f, "prt_int", (void *)prt_int, sign_prt_int,
tmp_args2, 1, 0);

jit_insn_return(f, x); //return

jit_context_build_start(context);
return f;
}

int main(int argc, char **argv)
{
jit_context_t context = jit_context_create();
jit_function_t f = gen_t_func(context);
jit_dump_function(stderr, f, "func [Uncompiled]");
int a = 1;
int b = 1000000;
void *values[] = { &a, &b };
jit_function_compile(f);
jit_dump_function(stderr, f, "func [Compiled]");
jit_function_apply(f, values, 0);
using ft = int(*)(int,int);
ft c_f = (ft)jit_function_to_closure(f);
c_f(a,b);
tfunc(a, b);
jit_context_destroy(context);
cin.get();
return 0;
}
输出:
function func [Uncompiled](i1 : int, i2 : int) : int
incoming_frame_posn(i1, 8)
incoming_frame_posn(i2, 12)
i5 = 0
i9 = &s7
store_relative_int(i9, 10, 0)
i11 = &s7
store_relative_int(i11, 1, 4)
.L0:
if i1 >= i2 then goto .L1
.L2:
i14 = i5 + i1
i5 = i14
i16 = i1 + 1
i1 = i16
goto .L0
ends_in_dead
.L1:
push_int(i5)
.L3:
call_external prt_int (0x00401420)
i18 = &s7
i19 = load_relative_int(i18, 0)
push_int(i19)
.L4:
call_external prt_int (0x00401420)
return_int(i5)
ends_in_dead
end

function func [Compiled](int, int) : int

V:\Users\wxdao\AppData\Local\Temp/libjit-dump.o: file format pe-i386

Disassembly of section .text:

00020018 <.text>:
20018: 55 push %ebp
20019: 8b ec mov %esp,%ebp
2001b: 83 ec 08 sub $0x8,%esp
2001e: 56 push %esi
2001f: 57 push %edi
20020: 8b 7d 08 mov 0x8(%ebp),%edi
20023: 33
4000
f6 xor %esi,%esi
20025: 8d 45 f8 lea -0x8(%ebp),%eax
20028: c7 00 0a 00 00 00 movl $0xa,(%eax)
2002e: 8d 45 f8 lea -0x8(%ebp),%eax
20031: c7 40 04 01 00 00 00 movl $0x1,0x4(%eax)
20038: 3b 7d 0c cmp 0xc(%ebp),%edi
2003b: 0f 8d 07 00 00 00 jge 20048 <.text+0x30>
20041: 03 f7 add %edi,%esi
20043: 83 c7 01 add $0x1,%edi
20046: eb f0 jmp 20038 <.text+0x20>
20048: 56 push %esi
20049: e8 d2 13 3e 00 call 401420 <.text+0x3e1408>
2004e: 8d 45 f8 lea -0x8(%ebp),%eax
20051: 8b 00 mov (%eax),%eax
20053: 50 push %eax
20054: e8 c7 13 3e 00 call 401420 <.text+0x3e1408>
20059: 8b c6 mov %esi,%eax
2005b: 8b 75 f4 mov -0xc(%ebp),%esi
2005e: 8b 7d f0 mov -0x10(%ebp),%edi
20061: 8b e5 mov %ebp,%esp
20063: 5d pop %ebp
20064: c3 ret
20065: 90 nop
20066: 90 nop
20067: 90 nop

end

Sound from jit:1783293664
Sound from jit:10
Sound from jit:1783293664
Sound from jit:10
1783293664
10下面的三个输出分别来自:jit,jit(closure)模式,C程序里的tfunc函数。
可以看到,context,type,value,insn组成了整个jit,这在llvm里也是类似的。

草草说完了libjit,希望你对jit工具的设计有了一定的认识,下一节将重点学习llvm。

(5)-流行jit工具之一-libjit 结束
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息