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

目前我能找到的好用的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 
#include 
#include 
#include 
#include 

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 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 结束

你可能感兴趣的:(Flex&Bison教程)