1. 程式人生 > >Vczh Library++3.0實現二進位制模板函式

Vczh Library++3.0實現二進位制模板函式

    經過一個星期的奮鬥,二進位制模板函式終於實現了,當然這還是沒有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 0101 74: stack_offset 20102 75: read s32
103 76: gt s32
104 77: jumpfalse 1001105 // (result = Apply2<T>(f, result, ( * items)));106 78: stack_offset 16107 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 1115 // (count -- );116 87: push s32 1117 88: stack_offset 20118 89: read s32
119 90: sub s32
120 91: stack_offset 20121 92: write s32
122 // (items ++ );123 93: push s32 1[Linear]
124 94: stack_offset 16125 95: read u32
126 96: add u32
127 97: stack_offset 16128 98: write u32
129 // while((count > 0s32))130 99: jump 731131 // function T Sum(T* items, int32 count, T init, function T(T, T) f)132 100: stack_reserve 0133 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 =1141   Instruction =57142   Lengtht =12143   UniqueName = [assembly_generated]::[Apply2]<{0}>144 }
145 Entries[1= {
146   Name = Sum
147   Arguments =1148   Instruction =69149   Lengtht =33150   UniqueName = [assembly_generated]::[Sum]<{0}>151 }
152 Targets[0= {
153   AssemblyName = assembly_generated
154   SymbolName = Sum
155   ArgumentSizes[0=4156   ArgumentNames[0= s32
157 }
158 Targets[1= {
159   AssemblyName = assembly_generated
160   SymbolName = Apply2
161   ArgumentSizes[0=1*T0 +0162   ArgumentNames[0= {0}
163 }
164 Linears[0=1*T0 +20165 Linears[1=1*T0 +0166 Linears[2=2*T0 +4167 Linears[3=1*T0 +24168 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 19:12 陳梓瀚(vczh) 閱讀(2787) 評論(8)  編輯 收藏 引用 所屬分類: VL++3.0開發紀事