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) 閱讀(2540)
評論(3) 編輯 收藏 引用 所屬分類:
VL++3.0開發紀事