Common Lisp中的setq
类似于其它语言中的赋值语句,它可以给一个符号对象设定一个值,类似于将一个值赋值给一个变量一样。简单起见,在jjcc2
中,我会将所有的符号都作为全局的一个label来实现。也就是说,如果代码中出现了
这样的代码,那么在最后生成的代码中,就会相应的在.data
段中有一个同名的label,其中存放着数值1。
既然都是全局变量,那么只需要准备一个容器来盛这些变量名即可。现阶段,暂时认为所有的变量都是数值类型即可。简单起见,这个容器直接用Common Lisp内置的HASH-TABLE
来表示。
当在jjcc2
函数中遭遇到setq
这个符号时,整个表的形态是这样的
这时候,首先要将var
放入到记录全局变量的哈希表中。然后,递归地调用jjcc2
函数,先编译form
,得到一系列的汇编代码。然后,生成一条mov
语句,将eax
寄存器中的内容放到var
所指的内存位置中。最终的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
| (defun jjcc2 (expr globals) "支持两个数的四则运算的编译器" (check-type globals hash-table) (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) '*) `((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))) ((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 ,(format nil "~A(%RIP)" (second expr))))) globals))))
|
然后还需要修改stringify
函数,现在它需要处理传给jjcc2
的全局变量的哈希表,将其转化为对应的.data
段的声明。代码如下
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
| (defun stringify (asm globals) "根据jjcc2产生的S表达式生成汇编代码字符串" (check-type globals hash-table) (format t " .data~%") (maphash (lambda (k v) (format t "~A: .long ~D~%" k v)) globals)
(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~%"))
|
弄了一个辅助的函数来方便将jjcc2
和stringify
串起来
1 2 3 4 5
| (defun test (expr) (let ((ht (make-hash-table))) (multiple-value-bind (asm globals) (jjcc2 expr ht) (stringify asm globals))))
|
尝试在SLIME中运行
1
| (test '(setq a (+ 1 2)))
|
最后得到如下的汇编代码
1 2 3 4 5 6 7 8 9 10 11 12
| .data A: .long 0 .section __TEXT,__text,regular,pure_instructions .globl _main _main: MOVL $1, %EAX MOVL $2, %EBX ADDL %EBX, %EAX MOVL %EAX, A(%RIP) movl %eax, %edi movl $0x2000001, %eax syscall
|
全文完