随笔-341  评论-2670  文章-0  trackbacks-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专门用来存放实例化后的模板函数。

    接下去就可以去开始做模板全局存储区了。
posted on 2010-07-12 03:12 陈梓瀚(vczh) 阅读(3056) 评论(8)  编辑 收藏 引用 所属分类: VL++3.0开发纪事

评论:
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 04:47 | zoyi
听vczh的话,造轮子去  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 05:05 | 陈梓瀚(vczh)
@zoyi
你想造啥  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 05:45 | 唐风
造3D图形引擎吧!  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 06:07 | 陈梓瀚(vczh)
@唐风
造过,没持续兴趣……  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 07:17 | johndragon
挺好  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 07:23 | 空明流转
@唐风
跟哥哥造Renderer吧。  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-12 16:32 | 路青飞
@zoyi
好样的!
Never give up!  回复  更多评论
  
# re: Vczh Library++3.0实现二进制模板函数 2010-07-14 04:11 | 唐风
@空明流转
兴趣是有,图形知识几乎为0.
好吧,我申请去给你们打打杂~!
看你们收不?  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理