Vczh Library++ 3.0终于实现跨Assembly调用函数了。其实在设计之初架构就已经允许了这个东西,只是一直都留着没做。现在先看两段代码,然后逐一解释指令的内容。
首先是第一个Assembly(可以认为是dll,概念是一样的),实现了一个全局变量,然后有一个单参数的函数,返回参数跟全局变量的和(代码是从语法树生成出来的,主要是为了实现指令集里面的自动加注释功能):
1 /*NativeX Code*/
2 unit nativex_program_generated;
3 variable int32 leftOperand = 0;
4
5 function int32 add(int32 rightOperand)
6 (result=(leftOperand+rightOperand));
7
8
9 /*Assembly*/
10 .data
11 0x00000000: 00 00 00 00
12 .label
13 0: instruction 7
14 .code
15 // unit nativex_program_generated;
16 0: stack_reserve 0
17 // variable int32 leftOperand = 0;
18 1: push s8 0
19 2: convert s32 s8
20 3: link_pushdata 0
21 4: write s32
22 // unit nativex_program_generated;
23 5: stack_reserve 0
24 6: ret 0
25 // function int32 add(int32 rightOperand)
26 7: stack_reserve 0
27 // (result=(leftOperand+rightOperand));
28 8: stack_offset 16
29 9: read s32
30 10: link_pushdata 0
31 11: read s32
32 12: add s32
33 13: resptr
34 14: write s32
35 // function int32 add(int32 rightOperand)
36 15: stack_reserve 0
37 16: ret 4
38
这段简单的加法代码没什么好解释的。窥孔优化还没做,因此会有一些垃圾在里面。在这里可以看到全局变量的访问跟参数访问的不同。全局变量使用link_pushdata,而参数使用stack_offset。link_开头的都是链接时指令,链接器会把这些东西给转换成真正的指令。因为在编译的时候并不知道全局空间的实际指针,因此只好链接的时候再做,这个时候全局空间已经生成出来了。最终link_pushdata会被转换成一个push ptr x指令,x是一个常数。
下面是调用这个Assembly里面的另一个Assembly的main函数:
1 /*NativeX Code*/
2 unit nativex_program_generated;
3 variable int32 adder alias programAdd.leftOperand;
4
5 function int32 add(int32 offset) alias programAdd.add;
6
7 function int32 main()
8 {
9 (adder=1);
10 (result=add(2));
11 }
12
13
14 /*Assembly*/
15 .data
16 .label
17 0: instruction 3
18 .code
19 // unit nativex_program_generated;
20 0: stack_reserve 0
21 1: stack_reserve 0
22 2: ret 0
23 // function int32 main()
24 3: stack_reserve 0
25 // (adder=1);
26 4: push s8 1
27 5: convert s32 s8
28 6: link_pushforeigndata 0
29 7: write s32
30 // (result=add(2));
31 8: push s8 2
32 9: convert s32 s8
33 10: resptr
34 11: link_callforeignfunc 1
35 // function int32 main()
36 12: stack_reserve 0
37 13: ret 0
38
这里主要是看看一个Assembly里面的代码是如何操作另外一个Assembly的东西的。首先定义链接符号,譬如说variable int32 adder alias programAdd.leftOperator。programAdd是第一个Assembly的名字(没有反应在代码里),然后leftOperator明显就是变量名了。因为Assembly的数据里面还保留了所有变量、函数、结构类型的声明的全部内容,因此不会出现“Dll地狱”。链接的时候可以比较一下被链接的符号的声明以及定义的连接符号的声明是否吻合,不吻合则代表要么Assembly版本有问题,要么Assembly就是错的,因此直接抛出异常不允许加载。
在这个代码里面我们有两个符号:programAdd.leftOperator和programAdd.add。他们按照顺序分别被套上ID:0和1。因此在对adder,也就是programAdd.leftOperator赋值的时候,这里使用了链接时指令link_pushforeigndata 0,用来读入该变量的地址。调用add的时候,先push一个参数2,然后将存放结果的变量的指针也push进去,最后调用函数programAdd.add,也就是ID为1的符号了:link_callforeignfunc 1。
链接器会把所有link_开头的指令全部通过已经加载的信息重新替换成运行是指令。显然link_pushforeigndata 0和link_callforeignfunc 1都是缺少加载时才有的信息才写成这样子的,最后会被翻译成push ptr x和call assembly_id instruction_address。
既然可以调用外部Assembly的函数,那么把外部Assembly的函数的函数指针存放起来供以后调用也是完全可能的:
1 /*NativeX Code*/
2 unit nativex_program_generated;
3 function int32 add(int32 a, int32 b)
4 (result=(a+b));
5
6
7 /*Assembly*/
8 .data
9 .label
10 0: instruction 3
11 .code
12 // unit nativex_program_generated;
13 0: stack_reserve 0
14 1: stack_reserve 0
15 2: ret 0
16 // function int32 add(int32 a, int32 b)
17 3: stack_reserve 0
18 // (result=(a+b));
19 4: stack_offset 20
20 5: read s32
21 6: stack_offset 16
22 7: read s32
23 8: add s32
24 9: resptr
25 10: write s32
26 // function int32 add(int32 a, int32 b)
27 11: stack_reserve 0
28 12: ret 8
29
这个我就不废话了,更加简单,连全局变量都没有了,就一个加法函数。接下来的main函数会把这个加法函数和自己的加法函数的函数指针存下来,然后调用:
1 /*NativeX Code*/
2 unit nativex_program_generated;
3 function int32 main()
4 {
5 variable function int32(int32, int32) padd1 = add1;
6 variable function int32(int32, int32) padd2 = add2;
7 variable int32 a = padd1(1, 2);
8 variable int32 b = padd2(3, 4);
9 (result=((a*10)+b));
10 }
11
12 function int32 add1(int32 a, int32 b) alias programAdd.add;
13
14 function int32 add2(int32 a, int32 b)
15 (result=(a+b));
16
17
18 /*Assembly*/
19 .data
20 .label
21 0: instruction 3
22 1: instruction 40
23 .code
24 // unit nativex_program_generated;
25 0: stack_reserve 0
26 1: stack_reserve 0
27 2: ret 0
28 // function int32 main()
29 3: stack_reserve 16
30 // variable function int32(int32, int32) padd1 = add1;
31 4: link_pushforeignfunc 0
32 5: stack_offset -4
33 6: write u32
34 // variable function int32(int32, int32) padd2 = add2;
35 7: link_pushfunc 1
36 8: stack_offset -8
37 9: write u32
38 // variable int32 a = padd1(1, 2);
39 10: push s8 2
40 11: convert s32 s8
41 12: push s8 1
42 13: convert s32 s8
43 14: stack_offset -12
44 15: stack_offset -4
45 16: read u32
46 17: label
47 18: call_indirect
48 // variable int32 b = padd2(3, 4);
49 19: push s8 4
50 20: convert s32 s8
51 21: push s8 3
52 22: convert s32 s8
53 23: stack_offset -16
54 24: stack_offset -8
55 25: read u32
56 26: label
57 27: call_indirect
58 // (result=((a*10)+b));
59 28: stack_offset -16
60 29: read s32
61 30: push s8 10
62 31: convert s32 s8
63 32: stack_offset -12
64 33: read s32
65 34: mul s32
66 35: add s32
67 36: resptr
68 37: write s32
69 // function int32 main()
70 38: stack_reserve -16
71 39: ret 0
72 // function int32 add2(int32 a, int32 b)
73 40: stack_reserve 0
74 // (result=(a+b));
75 41: stack_offset 20
76 42: read s32
77 43: stack_offset 16
78 44: read s32
79 45: add s32
80 46: resptr
81 47: write s32
82 // function int32 add2(int32 a, int32 b)
83 48: stack_reserve 0
84 49: ret 8
85
哇哈哈。
最新代码可以在
这里获得。
posted on 2010-06-11 07:13
陈梓瀚(vczh) 阅读(2531)
评论(3) 编辑 收藏 引用 所属分类:
VL++3.0开发纪事