zoukankan      html  css  js  c++  java
  • 用Scheme写Scheme编译器(三):一元运算

    对于一元运算,我们现在指的还不是函数(完成函数会在以后讲到),而是一般语言中内置的一元运算,比如Scheme中的add1,sub1,integer->char, char->integer, fixnum? ,boolean? ,null? ,char? 等的运算.



    实现这种一元运算的原理和关键就在于用汇编语言完成这些函数的运算后,我们可以使用%eax寄存器将运算的结果进行返回.



    1 修改Scheme编译主程序

    我们要将编译程序调整为适应一元运算

    (define (compile-program expr)
    
    (emit-head)
    
    (emit-expr expr)
    
    (emit-return))



    从我们之前的经验大概可以看出来,汇编语句中只有一些是于我们所要编写的程序相关的,所以我们可以把编译主程序改写为由head,expr,return三部分组成的程序.



    Head:

    (define (emit-head)
    
    (emit " .text")
    
    (emit " .global _scheme_entry")
    
    (emit " .def _scheme_entry; .scl 2; .type 32; .endef")
    
    (emit "_scheme_entry:")
    
    (emit "LFB0:")
    
    (emit " .cfi_startproc")
    
    (emit " pushl %ebp")
    
    (emit " .cfi_def_cfa_offset 8")
    
    (emit " .cfi_offset 5, -8")
    
    (emit " movl %esp, %ebp")
    
    (emit " .cfi_def_cfa_register 5"))

    expr:

    (define (emit-expr expr)
    
    (cond ((immediate-value? expr)
    
    (emit-immediate expr))
    
    ((primcall? expr)
    
    (emit-primcall expr))
    
    (else (error 'emit-expr))))


    return:

    (define (emit-return)
    
    (emit " popl %ebp")
    
    (emit " .cfi_restore 5")
    
    (emit " .cfi_def_cfa 4, 4")
    
    (emit " ret")
    
    (emit " .cfi_endproc")
    
    (emit "LFE0:"))


    2 定义用于编写一元运算的宏

    为了方便我们编写一元运算的汇编代码,我们可以写一个宏:

    (define-syntax define-primitive
    
    (syntax-rules ()
    
    ((_ (op arg* ...) b b* ...)
    
    (begin (putprop 'op '*is-prim* #t)
    
    (putprop 'op '*arg-length* (length '(arg* ...)))
    
    (putprop 'op '*emitter* (lambda (arg* ...) b b* …))))))



    3 完成一元运算的汇编代码



    (define-primitive (add1 arg)
    
    (emit-expr arg)
    
    (emit " addl $~a, %eax" (immediate-value-rep 1)))


    (define-primitive (sub1 arg)
    
    (emit-expr arg)
    
    (emit " subl $~a, %eax" (immediate-value-rep 1)))
    (define-primitive (char->integer arg)
    
    (emit-expr arg)
    
    (emit " shll $~s, %eax" (- charshift fxshift)))

    字符转换为数字只需要向右移动6位即可,

    因为字符的低8位为00001111, 数字 的低8位为xxxxxx00

    (define-primitive (integer->char arg)
    
    (emit-expr arg)
    
    (emit " shll $~s, %eax" (- charshift fxshift)))



    (define-primitive (fixnum? arg)
    
    (emit-expr arg)
    
    (emit " and $~s, %al" fxmask)
    
    (emit-cmp fxtag))
    (define-primitive (null? arg)
    
    (emit-expr arg)
    
    (emit-cmp list_nil))
    (define-primitive (boolean? arg)
    
    (emit-expr arg)
    
    (emit " and $~s, %al" bool_mask)
    
    (emit-cmp bool_f))

    判断是否是布尔值是利用bool_mask(0xbf),将#t转变为#f,然后比较是否等于#f



    (define-primitive (char? arg)
    
    (emit-expr arg)
    
    (emit " and $~s, %al" charmask)
    
    (emit-cmp chartag))


    最后是判断是否相等的汇编代码:

    (define (emit-cmp tag)
    
    (emit " cmp $~s, %al" tag)
    
    (emit " sete %al")
    
    (emit " movzbl %al, %eax")
    
    (emit " sal $~s, %al" bool_bit)
    
    (emit " or $~s, %al" bool_f))

    通过sete我们可以将比较结果放置在%eax中,如果相等,为1,否则为0

    1左移6位后,与bool_f或运算为bool_t,若为0,或运算后仍为bool_t.

  • 相关阅读:
    对于指定区块div,如何区分区块内的点击 和 区块外的点击?
    broadcom代码中httpd进程启动流程介绍
    一个简单的搜索布局样式
    一种在视频OBJECT标签上放置均分四个区域的框选方法
    JQuery执行DOM批量克隆并插入的提效方法
    DevOps技术路线图
    后端开发技术路线图
    Angular route传参
    Angular使用echarts
    TypeScript Array Remove
  • 原文地址:https://www.cnblogs.com/zhangfann/p/4350353.html
Copyright © 2011-2022 走看看