原定的计划中这一篇应当是要讲如何编译if表达式的,但是我发现没什么东西可以作为if的test-form的部分的表达式,所以觉得,要不还是先实现一下比较两个数字这样子的功能吧。说干就干,我决定用大于运算符来作为例子——大于运算符就是指>
啦。所以,我的目标是要编译下面这样的代码
并且比较之后的结果要放在EAX
寄存器中。鉴于现在这门语言还非常地简陋,没有布尔类型这样子的东西,所以在此仿照C语言的处置方式,以数值0表示逻辑假,其它的值表示逻辑真。所以上面的表达式在编译成汇编代码并最终运行后,应当可以看到EAX
寄存器中的值为0。
为了编译大于运算符,并且将结果放入到EAX
寄存器中,需要用到新的指令CMP
、JG
,以及JMP
了。我的想法是,先将第一个操作数放入到EAX
寄存器,将第二个操作数放入到EBX
寄存器。然后,使用CMP
指令比较这两个寄存器。如果EAX
中的数值大于EBX
,那么就使用JG
指令跳到一个MOV
指令上,这道MOV
会将寄存器EAX
的值修改为1;否则,JG
不被执行,执行后续的一道MOV
指令,将数值0写入到EAX
寄存器,然后使用JMP
跳走,避免又执行到了刚才的第一道MOV
指令。思路还是挺简单的。
在修改jjcc2
之前,还需要在inside-out/aux
中对>
予以支持,但没什么特别的,就是往member
的参数中加入>
这个符号而已。之后,将jjcc2
改为如下的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| (defun jjcc2 (expr globals) "支持两个数的四则运算的编译器" (check-type globals hash-table) (cond ((eq (first expr) '+) `((movl ,(get-operand expr 0) %eax) (movl ,(get-operand expr 1) %ebx) (addl %ebx %eax))) ((eq (first expr) '-) `((movl ,(get-operand expr 0) %eax) (movl ,(get-operand expr 1) %ebx) (subl %ebx %eax))) ((eq (first expr) '*) `((movl ,(get-operand expr 0) %eax) (movl ,(get-operand expr 1) %ebx) (imull %ebx %eax))) ((eq (first expr) '/) `((movl ,(get-operand expr 0) %eax) (cltd) (movl ,(get-operand expr 1) %ebx) (idivl %ebx))) ((eq (first expr) 'progn) (let ((result '())) (dolist (expr (rest expr)) (setf result (append result (jjcc2 expr globals)))) result)) ((eq (first expr) 'setq) (setf (gethash (second expr) globals) 0) (values (append (jjcc2 (third expr) globals) `((movl %eax ,(get-operand expr 0)))) globals)) ((eq (first expr) '_exit) `((movl ,(get-operand expr 0) %edi) (movl #x2000001 %eax) (syscall))) ((eq (first expr) '>) (let ((label-greater-than (intern (symbol-name (gensym)) :keyword)) (label-end (intern (symbol-name (gensym)) :keyword))) `((movl ,(get-operand expr 0) %eax) (movl ,(get-operand expr 1) %ebx) (cmpl %ebx %eax) (jg ,label-greater-than) (movl $0 %eax) (jmp ,label-end) ,label-greater-than (movl $1 %eax) ,label-end)))))
|
然后便可以在REPL中运行下列代码了
1 2 3
| (let* ((ht (make-hash-table)) (asm (jjcc2 (inside-out '(_exit (> 1 2))) ht))) (stringify asm ht))
|
输出的汇编代码为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| .data G809: .long 0 .section __TEXT,__text,regular,pure_instructions .globl _main _main: MOVL $1, %EAX MOVL $2, %EBX CMPL %EBX, %EAX JG G810 MOVL $0, %EAX JMP G811 G810: MOVL $1, %EAX G811: MOVL %EAX, G809(%RIP) MOVL G809(%RIP), %EDI MOVL $33554433, %EAX SYSCALL
|
编译链接运行后,就可以得到预期的结果了。
全文完。