在上一篇文章中,初步搭建了一個輸入Common Lisp代碼,輸出彙編代碼的編譯器的骨架,實現了二元整數的加法運算。在這個基礎上,要想實現減法、乘法,以及除法就是手到擒來的事情了。只需依葫蘆畫瓢,補充更多的分支狀況便可。html
我本身模仿着x64的調用約定,規定四則運算的結果始終放在EAX
這個寄存器中。在稍後給出的代碼中,對於減法和除法運算,都是把運算符的左操做數放到EAX
寄存器中,再從EAX
中減去或者除掉右操做數。git
在摸索除法的彙編代碼怎麼生成時,遇到了個費解的問題,最後才知道,原來須要把EAX
寄存器的符號擴展到高位的EDX
寄存器中去。對於as
這個彙編器來講,須要用到CLTD
指令。github
最後,jjcc2
和stringify
兩個函數被修改成以下的樣子segmentfault
(defun jjcc2 (expr) "支持兩個數的四則運算的編譯器" (cond ((eq (first expr) '+) `((movl ,(second expr) %eax) (movl ,(third expr) %ebx) (addl %ebx %eax))) ((eq (first expr) '-) `((movl ,(second expr) %eax) (movl ,(third expr) %ebx) (subl %ebx %eax))) ((eq (first expr) '*) ;; 將兩個數字相乘的結果放到第二個操做數所在的寄存器中 ;; 由於約定了用EAX寄存器做爲存放最終結果給continuation用的寄存器,因此第二個操做數應當爲EAX `((movl ,(second expr) %eax) (movl ,(third expr) %ebx) (imull %ebx %eax))) ((eq (first expr) '/) `((movl ,(second expr) %eax) (cltd) (movl ,(third expr) %ebx) (idivl %ebx))))) (defun stringify (asm) "根據jjcc2產生的S表達式生成彙編代碼字符串" (format t " .section __TEXT,__text,regular,pure_instructions~%") (format t " .globl _main~%") (format t "_main:~%") (dolist (ins asm) (cond ((= (length ins) 3) (format t " ~A ~A, ~A~%" (first ins) (if (numberp (second ins)) (format nil "$~A" (second ins)) (second ins)) (if (numberp (third ins)) (format nil "$~A" (third ins)) (third ins)))) ((= (length ins) 2) (format t " ~A ~A~%" (first ins) (if (numberp (second ins)) (format nil "$~A" (second ins)) (second ins)))) ((= (length ins) 1) (format t " ~A~%" (first ins))))) (format t " movl %eax, %edi~%") (format t " movl $0x2000001, %eax~%") (format t " syscall~%"))
全文完。oracle
閱讀原文函數