创建一门新的编程语言-Flex&Bison&libjit-(5)-流行jit
目前我能找到的好用的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,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,"d"),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,a); jit_insn_store(f,tmp1); jit_value_t tmp2 = jit_insn_add(f,1)); jit_insn_store(f,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,0); //调用我们的pri_int函数,其在我们的C程序中定义 jit_value_t tmp_args2[] = { jit_insn_load_relative(f,jit_type_int) }; jit_insn_call_native(f,tmp_args2,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,"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,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:UserswxdaoAppDataLocalTemp/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 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 结束 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |