15.5 翻译 Pcode 中的自定义函数命令和变量声明命令
上一节我们已经实现了将简单 TinyC 语句手工翻译成 Pcode ,然后编写了 NASM 宏将这些 Pcode 翻译成 x86 指令,最后汇编、链接成可执行程序。我们已经编写了大部分 Pcode 命令所对应的宏,本节将编写 NASM 宏来翻译 Pcode 中最复杂、也是最难翻译的命令–自定义函数命令和变量声明命令(FUNC / ENDFUNC / arg / ret / $func_name / var)。
具体来说,我们需要将以下 Pcode 翻译成 x86 指令:
- ; int main() {
- FUNC @main:
- ; int a;
- var a
- ; a = 3;
- push 3
- pop a
- ; print("sum = %d", sum(4, a));
- push 4
- push a
- $sum
- print "sum = %d"
- ; return 0;
- ret 0
- ; }
- ENDFUNC
- ; int sum(int a, int b) {
- FUNC @sum:
- arg a, b
- ; int c;
- var c
- ; c = a + b;
- push a
- push b
- add
- pop c
- ; return c;
- ret c
- ; }
- ENDFUNC
最难的部分在于如何避免不同函数中的同名变量的冲突。 NASM 的宏虽然强大,但无法满足如此复杂的需要。为降低翻译的难度,将以上 Pcode 稍微改写一下,保存为 test.pcode :
- ; int main() {
- FUNC @main:
- ; int a;
- main.var a
- ; a = 3;
- push 3
- pop a
- ; print("sum = %d", sum(4, a));
- push 4
- push a
- $sum
- print "sum = %d"
- ; return 0;
- ret 0
- ; }
- ENDFUNC@main
- ; int sum(int a, int b) {
- FUNC @sum:
- sum.arg a, b
- ; int c;
- sum.var c
- ; c = a + b;
- push a
- push b
- add
- pop c
- ; return c;
- ret c
- ; }
- ENDFUNC@sum
首先编写 FUNC 和 ret 宏,放到 macro.inc 文件中,同时在该文件的最后增加调用 @main 函数及退出的指令:
- %MACRO FUNC 1
- %1
- PUSH EBP
- MOV EBP, ESP
- %ENDMACRO
- %MACRO ret 0-1
- %IFIDN %0, 1
- %IFIDN %1, ~
- MOV EAX, [ESP]
- %ELSE
- MOV EAX, %1
- %ENDIF
- %ENDIF
- LEAVE
- RET
- %ENDMACRO
- EXTERN PRINT, READINT
- GLOBAL _start
- [SECTION .TEXT]
- _start:
- CALL @main
- PUSH EAX
- exit [ESP]
然后,编写 main.var, ENDFUNC@main, $sum, sum.arg, sum.var 和 ENDFUNC@sum 宏,保存为 test.funcmacro :
- ; ==== begin function `main` ====
- %define main.varc 1
- %MACRO main.var main.varc
- %define a [EBP - 4*1]
- SUB ESP, 4*main.varc
- %ENDMACRO
- %MACRO ENDFUNC@main 0
- LEAVE
- RET
- %undef a
- %ENDMACRO
- ; ==== end function `main` ====
- ; ==== begin function `sum` ====
- %define sum.argc 2
- %define sum.varc 1
- %MACRO $sum 0
- CALL @sum
- ADD ESP, 4*sum.argc
- PUSH EAX
- %ENDMACRO
- %MACRO sum.arg sum.argc
- %define a [EBP + 8 + 4*sum.argc - 4*1]
- %define b [EBP + 8 + 4*sum.argc - 4*2]
- %ENDMACRO
- %MACRO sum.var sum.varc
- %define c [EBP - 4*1]
- SUB ESP, 4*sum.varc
- %ENDMACRO
- %MACRO ENDFUNC@sum 0
- LEAVE
- RET
- %undef a
- %undef b
- %undef c
- %ENDMACRO
- ; ==== end function `sum` ====
这些宏会将 @sum 函数展开为如下形式:
- @sum: PUSH EBP MOV EBP, ESP SUB ESP, 4*1
PUSH DWORD [EBP + 12] ; push a
PUSH DWORD [EBP + 8] ; push b
add
POP DWORD [EBP - 4*1] ; pop c
MOV EAX, [EBP - 4*1] ; MOV EAX, c
LEAVE
RET
最后,改写 makefile 文件中的以下两行:
- test.o: test.pcode test.funcmacro macro.inc
- nasm -f elf32 -P"macro.inc" -P"test.funcmacro" -o test.o test.pcode
将以上四个文件以及库函数文件 tio.c 放到用一个目录,输入 make run 即可编译并运行测试代码。
至此所有 Pcode 命令对应的 NASM 宏编写完毕。
第 15 章完