随笔-341  评论-2670  文章-0  trackbacks-0
    经过一个多星期的推敲,终于将中间语言定稿。为了屏蔽寄存器、堆栈、数值比较逻辑、跳转、变量参数存放位置等,设计了以下中间语言。

    首先语言由常数块、变量块以及代码块组成。代码由函数组成。函数有参数、返回值(以及标记返回值是否浮点数)、调用约定组成:
1 FUNCTION BITS NAME [RETURN_FLOAT] (STDCALL|FASTCALL|CDECL)

    BITS代表的是返回值的大小。接下来由一系列的PARAM BITS NAME规定每一个参数的大小。再接下来就是代码了。代码由语句块或者指令组成,其中语句块可以声明变量以及包含更多的代码。语句块有一个名称,跳转的时候可以条件或强制跳转到语句块的开头或结尾,但是只能跳转到与指令所在的语句块内的语句块或者副语句块内:
 1 FUNCTION BITS NAME [RETURN_FLOAT] (STDCALL|FASTCALL|CDECL)
 2   PARAM BITS NAME
 3   
 4 BEGIN
 5   CODE
 6 END FUNCTION
 7 
 8 BLOCK [@NAME]
 9   VAR BITS NAME
10   
11 BEGIN
12 {INSTRUCTION | BLOCK}
13 END BLOCK
14 
15 INSTRUCTION=
16   NAME [PARAMETER{,PARAMETER}]
17 PARAMETER=TYPE VALUE|POSITION
18 PARAMETER=TYPE PTR POSITION (treate POSITION as ptr=int32u)
19 PARAMETER=NAME
20 PARAMETER=@NAME
21 POSITION=NAME (constant pointer except block name)
22 POSITION=#NAME (constant pointer)
23 POSITION=$NAME[+offset] (external linking value)
24 
25 PREDEFINED_POSITION
26   #RETURN_VALUE

    通过上面的设计可以看出,声明变量的时候只需要给出大小就好了,在实际使用的时候给定类型。于是我们可以声明一个10个字节长的变量,然后使用的时候将头4个字节当成整数进行运算:
1BLOCK
2  VAR 10 number
3BEGIN
4  ADD int32s number, int32s 1, int32s 2
5END BLOCK

    这有什么好处呢?
    1、可以不用管变量的存放
    2、自由使用空间

    由于语句块的结构跟高级语言类似,所以我们可以更加容易的编译。

    每一个指令,譬如ADD,都可以接受很多类型,譬如将一个byte+short放进一个float里面。这个时候类型转换由x86代码生成器搞定,用户不必操心。中间语言还提供了4个指令供复制对象使用:
1   MOV vif, if
2   COPY vif, vif, i
3   LDA vi, vif
4   LDAC vi, vif, vi, ci, ci (&vif+i*c1+c2)

    最后就是看跳转了。跳转有两种,第一种是函数内跳转:
1   JB @BLOCK (jump begin, can not jump past variable declarations)
2   JE @BLOCK (jump end, can not jump past variable declarations)
3   JBT vi, @BLOCK (jump begin if true, can not jump past variable declarations)
4   JET vi, @BLOCK (jump end if true, can not jump past variable declarations)
5   JBF vi, @BLOCK (jump begin if false, can not jump past variable declarations)
6   JEF vi, @BLOCK (jump end if false, can not jump past variable declarations)

    第二种是函数跳转,也就是调用函数了:
1   CALLF vifp, (NAME|vi) [{,if}]
2   CALLP (NAME|vi) [{,if}]

    我们可以看到所有的操作数都放在了一起,因为如果调用约定改了就要到处改call的代码显然是不合适的,所以这里设计成将所有参数放在同一条指令里面,最后由x86代码生成器处理。这也符合文章开头要求的“屏蔽堆栈”。

    定稿了之后,我写了一个判断中间语言的一个程序是否合理的函数,不过这个不重要,我们看看定稿之后上一篇文章两个菲薄纳契数列函数的写法:
 1 FUNCTION 4 fab STDCALL
 2   PARAM 4 number
 3 BEGIN
 4 BLOCK
 5   VAR 4 compare_result
 6 BEGIN
 7   LT int32s compare_result, int32s number, int32s 2
 8   JBF int32s compare_result, @COMBINE
 9   MOV int32s #RETURN_VALUE, int32s 1
10   JE @COMBINE
11   BLOCK @COMBINE
12     VAR 4 a
13     VAR 4 b
14     VAR 4 c
15   BEGIN
16     MOV int32s a, int32s 1
17     MOV int32s b, int32s 1
18     SUB int32s number, int32s number, int32s 1
19     BLOCK @LOOP
20     BEGIN
21       LT int32s compare_result, int32s number, int32s 2
22       JEF int32s compare_result, @LOOP
23       ADD int32s c, int32s a, int32s b
24       MOV int32s a, int32s b
25       MOV int32s b, int32s c
26       SUB int32s number, int32s number, int32s 1
27       JB @LOOP
28     END BLOCK
29     MOV int32s #RETURN_VALUE, int32s b
30   END BLOCK
31 END BLOCK
32 END FUNCTION
33 
34 FUNCTION 4 fab2 STDCALL
35   PARAM 4 number
36 BEGIN
37 BLOCK
38   VAR 4 compare_result
39 BEGIN
40   LT int32s compare_result, int32s number, int32s 2
41   JBF int32s compare_result, @COMBINE
42   MOV int32s #RETURN_VALUE, int32s 1
43   JE @COMBINE
44   BLOCK @COMBINE
45     VAR 4 difference
46     VAR 4 n_1
47     VAR 4 n_2
48   BEGIN
49     SUB int32s difference, int32s number, int32s 1
50     CALLF int32s n_1, func fab2, int32s difference
51     SUB int32s difference, int32s number, int32s 2
52     CALLF int32s n_2, func fab2, int32s difference
53     ADD int32s #RETURN_VALUE, int32s n_1, int32s n_2
54   END BLOCK
55 END BLOCK
56 END FUNCTION

    接下来就可以做x86代码生成器了。
posted on 2009-03-19 20:49 陈梓瀚(vczh) 阅读(2191) 评论(1)  编辑 收藏 引用 所属分类: JIT

评论:
# re: JIT脚本引擎:中间语言定稿并完成验证工作 2009-03-19 22:49 | 空明流转
很好,我的语义检查和编译期常量计算也差不多完工了,烦的一毛啊。  回复  更多评论
  

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