經(jīng)過一個多星期的推敲,終于將中間語言定稿。為了屏蔽寄存器、堆棧、數(shù)值比較邏輯、跳轉(zhuǎn)、變量參數(shù)存放位置等,設(shè)計了以下中間語言。
首先語言由常數(shù)塊、變量塊以及代碼塊組成。代碼由函數(shù)組成。函數(shù)有參數(shù)、返回值(以及標(biāo)記返回值是否浮點數(shù))、調(diào)用約定組成:
1 FUNCTION BITS NAME [RETURN_FLOAT] (STDCALL|FASTCALL|CDECL)
BITS代表的是返回值的大小。接下來由一系列的PARAM BITS NAME規(guī)定每一個參數(shù)的大小。再接下來就是代碼了。代碼由語句塊或者指令組成,其中語句塊可以聲明變量以及包含更多的代碼。語句塊有一個名稱,跳轉(zhuǎn)的時候可以條件或強(qiáng)制跳轉(zhuǎn)到語句塊的開頭或結(jié)尾,但是只能跳轉(zhuǎn)到與指令所在的語句塊內(nèi)的語句塊或者副語句塊內(nèi):
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
通過上面的設(shè)計可以看出,聲明變量的時候只需要給出大小就好了,在實際使用的時候給定類型。于是我們可以聲明一個10個字節(jié)長的變量,然后使用的時候?qū)㈩^4個字節(jié)當(dāng)成整數(shù)進(jìn)行運(yùn)算:
1
BLOCK
2
VAR 10 number
3
BEGIN
4
ADD int32s number, int32s 1, int32s 2
5
END BLOCK
這有什么好處呢?
1、可以不用管變量的存放
2、自由使用空間
由于語句塊的結(jié)構(gòu)跟高級語言類似,所以我們可以更加容易的編譯。
每一個指令,譬如ADD,都可以接受很多類型,譬如將一個byte+short放進(jìn)一個float里面。這個時候類型轉(zhuǎn)換由x86代碼生成器搞定,用戶不必操心。中間語言還提供了4個指令供復(fù)制對象使用:
1 MOV vif, if
2 COPY vif, vif, i
3 LDA vi, vif
4 LDAC vi, vif, vi, ci, ci (&vif+i*c1+c2)
最后就是看跳轉(zhuǎn)了。跳轉(zhuǎn)有兩種,第一種是函數(shù)內(nèi)跳轉(zhuǎn):
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)
第二種是函數(shù)跳轉(zhuǎn),也就是調(diào)用函數(shù)了:
1 CALLF vifp, (NAME|vi) [{,if}]
2 CALLP (NAME|vi) [{,if}]
我們可以看到所有的操作數(shù)都放在了一起,因為如果調(diào)用約定改了就要到處改call的代碼顯然是不合適的,所以這里設(shè)計成將所有參數(shù)放在同一條指令里面,最后由x86代碼生成器處理。這也符合文章開頭要求的“屏蔽堆棧”。
定稿了之后,我寫了一個判斷中間語言的一個程序是否合理的函數(shù),不過這個不重要,我們看看定稿之后
上一篇文章兩個菲薄納契數(shù)列函數(shù)的寫法:
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) 閱讀(2201)
評論(1) 編輯 收藏 引用 所屬分類:
JIT