支持四则运算中的变量

栏目: 编程语言 · 发布时间: 5年前

内容简介:在中的嵌套的表达式PS:上面的结果中的

上一篇文章 中, jjcc2 函数实现了对 setq 这个语句的编译。这么一来,便可以将加减乘除运算中的嵌套表达式都替换为变量了。比如,将

(+ (+ 1 2) 3)

中的嵌套的表达式 (+ 1 2) 用一个变量 G564 代替,变成

(PROGN
  (SETQ #:G564 (+ 1 2))
  (+ #:G564 3))

PS:上面的结果中的 #:G564 只是打印出来的时候长这个样子而已,实际地输入这段代码的话,两个 #:G564 其实是不同的符号,会导致未绑定的变量的错误的。

言归正传。既然如此,现在就要来支持编译 (+ #:G564 3) 这样的表达式了。其实这个真的是太简单了,只需要将这个符号塞入到 jjcc2 的第二个参数的 globals 中,然后在生成的“汇编指令”的S表达式中,嵌入这个符号即可。

我刚开始的时候也是这么想的,后来发现这样出来的代码编译不过,哭

折腾了一小段时间后,才知道原来有一种叫做“RIP-relative”的东西——好吧,我的X64的汇编语言知识也是赶鸭子上架的,遇到什么问题就放狗搜,所以完全不成体系——总之,我找到了解决办法,就是将原本放入一个符号的操作数,替换为类似于下面这样的内容

G564(%RIP)

所以对于操作数,实际上还需要先判断一下其类型。如果是整数,就按照原来的方式原样输出;如果是符号,就生成像上面这样的RIP-relative的结构。这部分太经常出现了,于是提炼出了一个专门处理四则运算的操作数的辅助函数 get-operand

(defun get-operand (expr n)
  "从EXPR中提取出第N个操作数,操作数的下标从0开始计算"
  (check-type expr list)
  (check-type n integer)
  (let ((e (nth (1+ n) expr)))
    (etypecase e
      (integer e)
      (symbol (format nil "~A(%RIP)" e)))))

借助它重写 jjcc2 ,结果如下

(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) '*)
         ;; 将两个数字相乘的结果放到第二个操作数所在的寄存器中
         ;; 因为约定了用EAX寄存器作为存放最终结果给continuation用的寄存器,所以第二个操作数应当为EAX
         `((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)
         ;; 编译赋值语句的方式比较简单,就是将被赋值的符号视为一个全局变量,然后将eax寄存器中的内容移动到这里面去
         ;; TODO: 这里expr的second的结果必须是一个符号才行
         ;; FIXME: 不知道应该赋值什么比较好,先随便写个0吧
         (setf (gethash (second expr) globals) 0)
         (values (append (jjcc2 (third expr) globals)
                         ;; 为了方便stringify函数的实现,这里直接构造出RIP-relative形式的字符串
                         `((movl %eax ,(get-operand expr 0))))
                 globals))))

现在,如果运行下面的这个 example1 函数

(defun example1 ()
  "验证jjcc2确实可以处理含有变量的加减乘除运算"
  (let ((ht (make-hash-table)))
    (setf (gethash 'a ht) 1)
    (let ((asm (jjcc2 '(+ a a) ht)))
      (stringify asm ht))))

便可以得到下面这段汇编代码了

        .data
A: .long 1
        .section __TEXT,__text,regular,pure_instructions
        .globl _main
_main:
        MOVL A(%RIP), %EAX
        MOVL A(%RIP), %EBX
        ADDL %EBX, %EAX
        movl %eax, %edi
        movl $0x2000001, %eax
        syscall

全文完。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

七周七并发模型

七周七并发模型

Paul Butcher / 黄炎 / 人民邮电出版社 / 2015-3 / 49.00元

借助Java、Go等多种语言的特长,深度剖析所有主流并发编程模型 基于锁和线程的并发模型是目前最常用的一种并发模型,但是并发编程模型不仅仅只有这一种,本书几乎涵盖了目前所有的并发编程模型。了解和熟悉各种并发编程模型,在解决并发问题时会有更多思路。 ——方腾飞,并发编程网站长 当看到这本书的目录时,我就为之一振。它涉及了当今所有的主流并发编程模型(当然也包括Go语言及其实现的CSP......一起来看看 《七周七并发模型》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换