编译过程概述

这次我们使用的编译目标是 C0,一种简化了 C 语言语义和编译过程、同时魔改了语法的玩具编程语言。

编译过程

整个 C0 工具链分为两个部分,分别是你接下来需要写的 编译器 和我们提供的 虚拟机。整个编译流程如下:

  • 编译器(你要写的)
    • 读入 c0 源代码
    • 输出 o0 二进制代码
  • 虚拟机
    • 读入 o0 二进制代码
    • 解释执行代码
    • 输出运行结果

你的编译器至少能通过某种命令行接口读入一个 c0 文件,并将编译出的 o0 文件输出到另一个文件中。接口的实际特征我们不做规定,需要你自己定义并写在评测配置中,具体见 评测要求

o0 二进制代码的定义见 这里

我们不限制你编写编译器使用的语言,且允许复用 miniplc0 实验中的代码。

如果你决定编译到其他目标(自己设计的指令集、LLVM IR、Java Bitcode、物理 CPU 指令集等),请联系助教进行单独手动评测。由于需要单独评测,你不太可能因此获得额外的加分。

编译目标

我们编译的目标是本课程自行设计的 navm 虚拟机使用的 o0 代码。有关 navm 虚拟机的设计、结构参见 对 navm 虚拟机的介绍

比如如果你编译这个文件:

fn foo(i: int) -> int {
    return -i;
}

fn main() -> void {
    putint(foo(-123456));
}

你得到的结果应该类似于这个(o0 格式没有规定相应的文字形式,以下是官方参考实现 natriumnavm 使用的输出格式):

static: 66 6F 6F (`foo`)

static: 70 75 74 69 6E 74 (`putint`)

static: 6D 61 69 6E (`main`)

static: 5F 73 74 61 72 74 (`_start`)


fn [3] 0 0 -> 0 {
    0: StackAlloc(0)
    1: Call(2)
}

fn [0] 0 1 -> 1 {
    0: ArgA(0)
    1: ArgA(1)
    2: Load64
    3: NegI
    4: Store64
    5: Ret
}

fn [2] 0 0 -> 0 {
    0: StackAlloc(0)
    1: StackAlloc(1)
    2: Push(123456)
    3: NegI
    4: Call(1)
    5: CallName(1)
    6: Ret
}