课程回顾

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

Egger鈥檛est eater egg_List

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

Egger鈥檛est eater egg_元组_02

编程作业

本次的大作业是实现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