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  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专门用来存放实例化后的模板函数。

    接下去就可以去开始做模板全局存储区了。

你可能感兴趣的:(Vczh Library++3.0实现二进制模板函数)