Vczh Library++ 3.0終于實(shí)現(xiàn)跨Assembly調(diào)用函數(shù)了。其實(shí)在設(shè)計(jì)之初架構(gòu)就已經(jīng)允許了這個(gè)東西,只是一直都留著沒(méi)做?,F(xiàn)在先看兩段代碼,然后逐一解釋指令的內(nèi)容。
首先是第一個(gè)Assembly(可以認(rèn)為是dll,概念是一樣的),實(shí)現(xiàn)了一個(gè)全局變量,然后有一個(gè)單參數(shù)的函數(shù),返回參數(shù)跟全局變量的和(代碼是從語(yǔ)法樹(shù)生成出來(lái)的,主要是為了實(shí)現(xiàn)指令集里面的自動(dòng)加注釋功能):
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
這段簡(jiǎn)單的加法代碼沒(méi)什么好解釋的。窺孔優(yōu)化還沒(méi)做,因此會(huì)有一些垃圾在里面。在這里可以看到全局變量的訪問(wèn)跟參數(shù)訪問(wèn)的不同。全局變量使用link_pushdata,而參數(shù)使用stack_offset。link_開(kāi)頭的都是鏈接時(shí)指令,鏈接器會(huì)把這些東西給轉(zhuǎn)換成真正的指令。因?yàn)樵诰幾g的時(shí)候并不知道全局空間的實(shí)際指針,因此只好鏈接的時(shí)候再做,這個(gè)時(shí)候全局空間已經(jīng)生成出來(lái)了。最終link_pushdata會(huì)被轉(zhuǎn)換成一個(gè)push ptr x指令,x是一個(gè)常數(shù)。
下面是調(diào)用這個(gè)Assembly里面的另一個(gè)Assembly的main函數(shù):
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
這里主要是看看一個(gè)Assembly里面的代碼是如何操作另外一個(gè)Assembly的東西的。首先定義鏈接符號(hào),譬如說(shuō)variable int32 adder alias programAdd.leftOperator。programAdd是第一個(gè)Assembly的名字(沒(méi)有反應(yīng)在代碼里),然后leftOperator明顯就是變量名了。因?yàn)锳ssembly的數(shù)據(jù)里面還保留了所有變量、函數(shù)、結(jié)構(gòu)類型的聲明的全部?jī)?nèi)容,因此不會(huì)出現(xiàn)“Dll地獄”。鏈接的時(shí)候可以比較一下被鏈接的符號(hào)的聲明以及定義的連接符號(hào)的聲明是否吻合,不吻合則代表要么Assembly版本有問(wèn)題,要么Assembly就是錯(cuò)的,因此直接拋出異常不允許加載。
在這個(gè)代碼里面我們有兩個(gè)符號(hào):programAdd.leftOperator和programAdd.add。他們按照順序分別被套上ID:0和1。因此在對(duì)adder,也就是programAdd.leftOperator賦值的時(shí)候,這里使用了鏈接時(shí)指令link_pushforeigndata 0,用來(lái)讀入該變量的地址。調(diào)用add的時(shí)候,先push一個(gè)參數(shù)2,然后將存放結(jié)果的變量的指針也push進(jìn)去,最后調(diào)用函數(shù)programAdd.add,也就是ID為1的符號(hào)了:link_callforeignfunc 1。
鏈接器會(huì)把所有l(wèi)ink_開(kāi)頭的指令全部通過(guò)已經(jīng)加載的信息重新替換成運(yùn)行是指令。顯然link_pushforeigndata 0和link_callforeignfunc 1都是缺少加載時(shí)才有的信息才寫成這樣子的,最后會(huì)被翻譯成push ptr x和call assembly_id instruction_address。
既然可以調(diào)用外部Assembly的函數(shù),那么把外部Assembly的函數(shù)的函數(shù)指針存放起來(lái)供以后調(diào)用也是完全可能的:
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
這個(gè)我就不廢話了,更加簡(jiǎn)單,連全局變量都沒(méi)有了,就一個(gè)加法函數(shù)。接下來(lái)的main函數(shù)會(huì)把這個(gè)加法函數(shù)和自己的加法函數(shù)的函數(shù)指針存下來(lái),然后調(diào)用:
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
哇哈哈。
最新代碼可以在
這里獲得。