經過一個星期的奮斗,
二進制模板函數終于實現了,當然這還是沒有generic concept的版本。現在NativeX已經支持跟C#一樣的模板函數了:可以被編譯進獨立的二進制文件,然后另外一個代碼引用該二進制文件,還能實例化新的模板函數。現在先來看debug log輸出的二進制結構。首先是被編譯的代碼。下面的代碼因為是直接從語法樹生成的,所以括號什么的會比較多,而且因為NativeX支持s8、s16等的數值類型后綴,代碼生成的時候也使用了。一般來說沒有使用的話則默認為跟VC++的ptrdiff_t一樣的類型:
1 /*NativeX Code*/
2 unit nativex_program_generated;
3 function int32 main()
4 {
5 variable int32[5] numbers;
6 (numbers[0s32] = 1s32);
7 (numbers[1s32] = 3s32);
8 (numbers[2s32] = 5s32);
9 (numbers[3s32] = 7s32);
10 (numbers[4s32] = 9s32);
11 (result = Sum<int32>(cast<int32*>( & numbers), 5s32, 0s32, Add));
12 }
13
14 function int32 Add(int32 a, int32 b)
15 (result = (a + b));
16
17 generic<T>
18 function T Apply2(function T(T, T) f, T a, T b)
19 (result = f(a, b));
20
21 generic<T>
22 function T Sum(T* items, int32 count, T init, function T(T, T) f)
23 {
24 (result = init);
25 while((count > 0s32))
26 {
27 (result = Apply2<T>(f, result, ( * items)));
28 (count -- );
29 (items ++ );
30 }
31 }
這里的main函數聲明了一個數組,然后調用Sum<int32>計算結果,計算的時候要傳入一個加法函數Add。Sum里面調用了Apply2去執行加法函數(純粹是為了在模板函數里面調用另一個模板函數,沒有什么特別意義)。于是用一個循環就可以把數組的和算出來了。當然結果是25。讓我們來看看編譯后的代碼:
1 /*Assembly*/
2 .data
3 .label
4 0: instruction 3
5 1: instruction 47
6 2: instruction 57
7 3: instruction 69
8 .code
9 // unit nativex_program_generated;
10 0: stack_reserve 0
11 1: stack_reserve 0
12 2: ret 0
13 // function int32 main()
14 3: stack_reserve 20
15 // (numbers[0s32] = 1s32);
16 4: push s32 1
17 5: stack_offset -20
18 6: push s32 0
19 7: push s32 4
20 8: mul s32
21 9: add s32
22 10: write s32
23 // (numbers[1s32] = 3s32);
24 11: push s32 3
25 12: stack_offset -20
26 13: push s32 1
27 14: push s32 4
28 15: mul s32
29 16: add s32
30 17: write s32
31 // (numbers[2s32] = 5s32);
32 18: push s32 5
33 19: stack_offset -20
34 20: push s32 2
35 21: push s32 4
36 22: mul s32
37 23: add s32
38 24: write s32
39 // (numbers[3s32] = 7s32);
40 25: push s32 7
41 26: stack_offset -20
42 27: push s32 3
43 28: push s32 4
44 29: mul s32
45 30: add s32
46 31: write s32
47 // (numbers[4s32] = 9s32);
48 32: push s32 9
49 33: stack_offset -20
50 34: push s32 4
51 35: push s32 4
52 36: mul s32
53 37: add s32
54 38: write s32
55 // (result = Sum<int32>(cast<int32*>( & numbers), 5s32, 0s32, Add));
56 39: pushlabel 2
57 40: push s32 0
58 41: push s32 5
59 42: stack_offset -20
60 43: resptr
61 44: generic_callfunc 0
62 // function int32 main()
63 45: stack_reserve -20
64 46: ret 0
65 // function int32 Add(int32 a, int32 b)
66 47: stack_reserve 0
67 // (result = (a + b));
68 48: stack_offset 20
69 49: read s32
70 50: stack_offset 16
71 51: read s32
72 52: add s32
73 53: resptr
74 54: write s32
75 // function int32 Add(int32 a, int32 b)
76 55: stack_reserve 0
77 56: ret 8
78 // function T Apply2(function T(T, T) f, T a, T b)
79 57: stack_reserve 0
80 // (result = f(a, b));
81 58: stack_offset 0[Linear]
82 59: readmem 1[Linear]
83 60: stack_offset 20
84 61: readmem 1[Linear]
85 62: resptr
86 63: stack_offset 16
87 64: read u32
88 65: label
89 66: call_indirect
90 // function T Apply2(function T(T, T) f, T a, T b)
91 67: stack_reserve 0
92 68: ret 2[Linear]
93 // function T Sum(T* items, int32 count, T init, function T(T, T) f)
94 69: stack_reserve 0
95 // (result = init);
96 70: stack_offset 24
97 71: resptr
98 72: copymem 1[Linear]
99 // while((count > 0s32))
100 73: push s32 0
101 74: stack_offset 20
102 75: read s32
103 76: gt s32
104 77: jumpfalse 100 1
105 // (result = Apply2<T>(f, result, ( * items)));
106 78: stack_offset 16
107 79: read u32
108 80: readmem 1[Linear]
109 81: resptr
110 82: readmem 1[Linear]
111 83: stack_offset 3[Linear]
112 84: read u32
113 85: resptr
114 86: generic_callfunc 1
115 // (count -- );
116 87: push s32 1
117 88: stack_offset 20
118 89: read s32
119 90: sub s32
120 91: stack_offset 20
121 92: write s32
122 // (items ++ );
123 93: push s32 1[Linear]
124 94: stack_offset 16
125 95: read u32
126 96: add u32
127 97: stack_offset 16
128 98: write u32
129 // while((count > 0s32))
130 99: jump 73 1
131 // function T Sum(T* items, int32 count, T init, function T(T, T) f)
132 100: stack_reserve 0
133 101: ret 4[Linear]
134 .exports
135 Assembly Name: assembly_generated
136 Exports[0] = (3, main)
137 Exports[1] = (47, Add)
138 Entries[0] = {
139 Name = Apply2
140 Arguments = 1
141 Instruction = 57
142 Lengtht = 12
143 UniqueName = [assembly_generated]::[Apply2]<{0}>
144 }
145 Entries[1] = {
146 Name = Sum
147 Arguments = 1
148 Instruction = 69
149 Lengtht = 33
150 UniqueName = [assembly_generated]::[Sum]<{0}>
151 }
152 Targets[0] = {
153 AssemblyName = assembly_generated
154 SymbolName = Sum
155 ArgumentSizes[0] = 4
156 ArgumentNames[0] = s32
157 }
158 Targets[1] = {
159 AssemblyName = assembly_generated
160 SymbolName = Apply2
161 ArgumentSizes[0] = 1*T0 + 0
162 ArgumentNames[0] = {0}
163 }
164 Linears[0] = 1*T0 + 20
165 Linears[1] = 1*T0 + 0
166 Linears[2] = 2*T0 + 4
167 Linears[3] = 1*T0 + 24
168 Linears[4] = 1*T0 + 12
二進制模板函數的思想是,類型在編譯到二進制代碼后,只需要留下名字和尺寸兩種信息就夠了。因此模板函數除了編譯成指令,還要在一個“二進制資源”里面留下一些信息,譬如說有多少個參數啦,有了參數之后將會如何組合成一個全局唯一符號(以區別尺寸相同而實際上類型不同的類型參數,有其他意義),等等。而且指令里面引用了參數尺寸的地方還要有個標記,在上面的log里就是后面帶了[Linear]的東西了。Linear會變成一張表,在log的最后部分看到,其實就是一個多項式。所有跟尺寸相關的東西,最終都可以用一個多項式的方法來表達,因此我就采用了這種結構了。
譬如Apply2<T>函數在一開始push兩個參數的時候,因為T的尺寸還不知道,因此參數b在堆棧中的位置就只好用一個多像是來表達了,而參數a因為a的前面只有一個固定大小的參數,因此其位置是固定的。push了之后,因為T類型不知道,所以只能用readmem指令加上一個多項式 1 [Linear]來表達其長度了。1是Linear表的索引,Linear可以在log的最后一個部分看到。
因為模板函數需要被編譯到二進制文件里面,而且在被不同的二進制文件引用到的時候,相同的實例不能被特化多次(因為函數指針可以用來判斷是否相等),因此特化的工作就落在了虛擬機上面了。虛擬機會根據“二進制資源”的信息去閱讀一個模板函數的二進制代碼,然后復制并修改,最終保存在一個內部的二進制Assembly里面,這個Assembly專門用來存放實例化后的模板函數。
接下去就可以去開始做模板全局存儲區了。
posted on 2010-07-12 03:12
陳梓瀚(vczh) 閱讀(3111)
評論(8) 編輯 收藏 引用 所屬分類:
VL++3.0開發紀事