dm1299

[swarthmore cs75] Compiler 5 – Egg-eater

课程回顾

Swarthmore学院16年开的编译系统课,总共10次大作业。本随笔记录了相关的课堂笔记以及第7次大作业。

  • 抽象语法:
  • 存储方式:
    • 栈中的数据如果最后三位(tag bits)是001表示元组。
    • 堆中元组的起始地址最后三位都是000。
    • 通过引入ESI寄存器可以实现堆区数据的存取。

编程作业

本次的大作业是实现Egg-Eater语言:支持函数,数字,布尔值以及元组;元组的语法(egg)非常像一个🥚,故得其名。

  • 元组的实现细节

    • 抽象语法:

      type expr =
        ...
        | ETuple of expr list
        | EGetItem of expr * expr
      
      type prim1 =
        | IsTuple
      
      type cexpr = 
        ...
        | CTuple of immexpr list
        | CGetItem of immexpr * immexpr
      

      具体实现:

      let rec anf_list (es : expr list) (k : immexpr list -> aexpr) : aexpr =
        match es with
          | [] -> k []
          | e::rest ->
            anf e (ImmHole(fun imm ->
              anf_list rest (fun imms -> k (imm::imms))))
      
      | ETuple(elts) -> 
        anf_list elts (fun imms -> fill_c h (CTuple(imms)))
      | EGetItem(coll, index) ->
        anf coll (ImmHole(fun limm ->
          anf index (ImmHole(fun rimm ->
            (fill_c h (CGetItem(limm, rimm)))))))
      
    • 堆布局(Heap Layout)

       (4 bytes)    (4 bytes)  (4 bytes)          (4 bytes)
      --------------------------------------------------------
      | # elements | element_0 | element_1 | ... | element_n |
      --------------------------------------------------------
      

      首先,需要明确一点:ESI的起始地址的最后三位必须是000,为了达到目的,可以将ESI指向:(堆区起始地址+8)&0xFFFFFFF8。

      ESI = 0xada0
      
      
                          0xada0
                          |
      --------------------|
      | *** used space ***|
      ---------------------
      

      其次,如果存储一个元组后,ESI指向的地址的最后三位不是000,就需要加上padding(dead space),使ESI指向地址的最后三位满足条件。

      ESI = 0xadac
      
      
                          0xada0                                0xadac
                          |                                      |
      --------------------|--------------------------------------|
      | *** used space ***| 0x00000002 | 0x00000004 | 0xFFFFFFFF |
      -----------------------------------------------------------|
      

    计算方式为:ESI = (堆区尾地址+4)&0xFFFFFFF8。

    ESI = 0xadb0
    
    
                      0xada0                                            0xadb0
                        |  (size)       (value 4)  (value true)           |
    --------------------|-------------------------------------------------|
    | *** used space ***| 0x00000002 | 0x00000008 | 0xFFFFFFFF | padding  |
    ----------------------------------------------------------------------|
    
    • 语义分析
      (6, 7, 8, 9)[1 + 2]
      
      首先对()中的表达式求值,然后对[]中的表达式求值。
      检查表达式(6, 7, 8, 9)的tag bits,即是否是元组,如果不是跳转到错误处理label。
      检查index是否是数字,如果不是跳转到错误处理label。
      检查index是否在0~size-1这个范围内,否则跳转到错误处理的label。
      进行求值并返回index位置的元素。计算逻辑为:mov eax, [eax + ecx * 4 + offset],eax是元组首地址+1 (包括tag bits), ecx存储了元素下标;当然还需要加上第一个元素(size)的偏移量。
      具体实现:
      let check_tuple arg =
        let label_end = gen_temp "end" in
        [
          IMov(Reg(EAX), arg);
          IAnd(Reg(EAX), Const(0x00000007));
          ICmp(Reg(EAX), Const(0x00000001));
          IJne(error_non_tuple)
        ]
      
      | CTuple(elts) ->
            let size = List.length elts in
            [
              IMov(RegOffset(0, ESI), Sized(DWORD_PTR, Const(size)));
            ] @
            List.flatten 
              (List.mapi (fun i elt ->  
                let arg = acompile_imm_arg elt si env in
                [
                  IMov(Reg(EAX), Sized(DWORD_PTR, arg));
                  IMov(RegOffset(4 * (i + 1), ESI), Reg(EAX));
                ]
              ) elts)
            @ 
            [
              IMov(Reg(EAX), Reg(ESI));
              IAdd(Reg(EAX), Const(1));
              IAdd(Reg(ESI), Const((size + 1) * 4));
              IAdd(Reg(ESI), Const(4)); 
              IAnd(Reg(ESI), HexConst(0xFFFFFFF8));
            ]	
      | CGetItem(coll, index) ->
            let coll_as_arg = acompile_imm_arg coll si env in
            let index_as_arg = acompile_imm_arg index si env in
            let checked = check_tuple coll_as_arg in
            checked @ [
              IMov(Reg(ECX), index_as_arg);
              (* Check that the index value is a number *)
              ITest(Reg(ECX), Const(0x00000001));
              IJnz(error_non_int);
              ISar(Reg(ECX), Const(1));
              ICmp(Reg(ECX), Const(0));
              IJl(error_too_small);
              IMov(Reg(EAX), coll_as_arg);
              ICmp(Reg(ECX), RegOffset(-1, EAX));
              IJge(error_too_large);
              IMov(Reg(EAX), RegOffsetReg(EAX, ECX, 4, 3))
            ]
      
      打印方法:调用c函数处理。
      print((4, (true, 3)))   (* 需要处理嵌套的情况 *)
      
      具体实现:
      const int TRUE = 0xFFFFFFFF;
      const int FALSE = 0x7FFFFFFF;
      
      int ispair(int val)
      {
        return (val & 0x00000007) == 0x00000001;
      }
      
      void print_rec(int val)
      {
        if ((val & 0x00000001) ^ 0x00000001) 
        {
          printf("%d", val >> 1);
        }
        else if (val == TRUE) 
        {
          printf("true");
        }
        else if (val == FALSE) 
        {
          printf("false");
        }
        else if (ispair(val)) 
        {
          int *p = (int *) (val - 1); // size
          printf("(");
          for (size_t i = 1; ;i++)
          {
            print_rec(*(p + i));
            if (i < *p) 
              printf(", ");
            else 
              break;
          }
          printf(")");
        }
        else 
        {
          printf("Unknown value: %#010x", val);
        }
      }
      
      int print(int val) 
      {
        print_rec(val);
        printf("\n");
        return val;
      }
      
      比较方法:调用c函数处理。
      let t = (4, 5) in t == t  (* 地址相等,返回true *)
      (4,5) == (4,5)            (* 会返回false,因为在Diamondback中比较的是地址 *)            
      
      具体实现:
      | Equal ->
        let leave_false = gen_temp "equals" in
        [
          IPush(Sized(DWORD_PTR ,left_as_arg));
          IPush(Sized(DWORD_PTR ,right_as_arg));
          ICall("equal");
          IAdd(Reg(ESP), Const(8));
        ]
      
      int equal(int val1, int val2) 
      {
        if (val1 == val2) { return TRUE; }
        else if (ispair(val1) && ispair(val2)) 
        {
          int *p1 = (int *)(val1 - 1); // size of pair1
          int *p2 = (int *)(val2 - 1); // size of pair2
          if (*p1 != *p2) { return FALSE; }
          else
          {
            for (size_t i = 1; i <= *p1; i++)
                if (equal(*(p1 + i), *(p2 + i)) == FALSE)
                  return FALSE;
            return TRUE;
          }
        }
        else { return FALSE; }
      }
      
  • 测试代码

    • 为了测试能够正确编译新的语法,实现了下面几个方法:
      def link(first, rest): 
        (first, rest)
      
      def length(l):
        if l == false: 0
        else:
          1 + length(l[1])
      
      def sum(l):
        if l[1] == false: l[0]
        else:
          l[0] + sum(l[1])
      
      def append(l1, l2):
        let data = l1[0], next = l1[1] in
        if (next == false): 
          link(data, l2)
        else:
          link(data, append(next, l2))
      
      def reverse(l, next):
        if l[1] == false: 
          link(l[0], next)
        else:
          reverse(l[1], link(l[0], next))
      
    • 接下来,可以用下面的方法来调用定义的函数:
      let mylist1 = link(1, link(2, link(3, false))) in
      let mylist2 = link(4, link(5, link(6, false))) in
      let newlist = append(mylist1, mylist2) in
      reverse(newlist, false) 
      
    • 下面展示了调用reverse这个函数(反转一个链表)的规约过程:
      reverse(link(1, link(2, link(3, link(4, link(5, link(6, false)))))), false)
      reverse(link(2, link(3, link(4, link(5, link(6, false))))), link(1, false))
      reverse(link(3, link(4, link(5, link(6, false)))), link(2, link(1, false)))
      reverse(link(4, link(5, link(6, false))), link(3, link(2, link(1, false))))
      reverse(link(5, link(6, false)), link(4, link(3, link(2, link(1, false)))))
      reverse(link(6, false), link(5, link(4, link(3, link(2, link(1, false))))))
      (6, link(5, link(4, link(3, link(2, link(1, false))))))
      

遗留问题

在实现Diamondback中,我发现'>', '<'的实现逻辑有误;现做修改,例如:1 < 1 和 1 > 1 都应该返回false。

| Less ->
  [
    IMov(Reg(EAX), left_as_arg);
    ISub(Reg(EAX), right_as_arg);
    IAnd(Reg(EAX), HexConst(0x80000000));
    IOr(Reg(EAX), HexConst(0x7FFFFFFF));
  ]
| Greater ->
[
  IMov(Reg(EAX), right_as_arg); 
  ISub(Reg(EAX), left_as_arg);
  IAnd(Reg(EAX), HexConst(0x80000000));
  IOr(Reg(EAX), HexConst(0x7FFFFFFF)); 
]

参考资料

starter-egg-eater

posted on 2019-03-17 23:43  dm1299  阅读(197)  评论(0编辑  收藏  举报

导航