编译实践Lv1.main函数

Posted by farmer3-c on June 10, 2026

目的

实现一个能处理 main 函数和 return 语句的编译器.编译器可以将如下的 SysY 程序:

1
2
3
4
int main() {
  // 注释也应该被删掉哦
  return 0;
}

编译为对应的 Koopa IR:

1
2
3
4
fun @main(): i32 {
%entry:
  ret 0
}

实现

Lv1.2. 词法/语法分析初见

我使用项目提供的c++模板,直接执行:

1
2
make
build/compiler -koopa hello.c -o hello.koopa

输出:

1
int main() { return 0; }

这是因为默认的模板使用string存放ast,所以输出的ast也是string形式:

1
2
3
4
5
CompUnit
  : FuncDef {
    ast = unique_ptr<string>($1);
  }
  ;

Lv1.3. 解析 main 函数

这里设计一个ast用于输出程序的语法结构,教程写的已经很详细了:写一个头文件来定义AST,对于头文件重复声明的问题,加上#pragma once就可以解决。

我是这样定义基类和子类进行构造的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 所有 AST 的基类
class BaseAST {
 public:
  virtual ~BaseAST() = default;
  virtual void Dump() const = 0;
};

// CompUnit 是 BaseAST
class CompUnitAST : public BaseAST {
 public:
  // 用智能指针管理对象
  std::unique_ptr<BaseAST> func_def;

    void Dump() const override {
    std::cout << "CompUnitAST { ";
    func_def->Dump();
    std::cout << " }";
  }
};
……

同时,修改参数类型的相关声明:比如%parse-param { std::unique_ptr<string> &ast } 改为%parse-param { std::unique_ptr<BaseAST> &ast }

make之后就会输出CompUnitAST { FuncDefAST { FuncTypeAST { int }, main, BlockAST { StmtAST { 0 } } } }

Lv1.4. IR 生成

我采用定义 AST 一样定义表示 Koopa IR 的数据结构, 然后遍历 AST 输出这种结构, 再遍历这种结构输出字符串。

类似的,写一个KoopaIR头文件,定义它的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Value 是所有指令和常量的基类
class Value {
 public:
  virtual ~Value() = default;
  virtual void Dump() const = 0;
};

// 整数常量
class Integer : public Value {
 public:
  int value;
  
  Integer(int v) : value(v) {}
  
  void Dump() const override {
    std::cout << value;
  }
};

接着完成GenerateIR的工作,完成遍历 AST 生成 IR,由于只需要识别main函数就可以了,复杂一些的递归之类的暂时不需要写。

1
2
3
4
5
6
7
8
9
10
11
12
13
main.cpp
  │
  ├─ yyparse(ast)          // 调用 Bison/Flex 解析源码 → 得到 AST
  │
  ├─ GenerateIR(*ast)      // ← 遍历 AST,生成 Koopa IR (Program)
  │     └─ IRGenerator.cpp
  │          ├─ dynamic_cast<CompUnitAST*> 取出 func_def
  │          ├─ dynamic_cast<FuncDefAST*>  取出 ident + block
  │          ├─ dynamic_cast<BlockAST*>    取出 stmt
  │          ├─ dynamic_cast<StmtAST*>     取出 num
  │          └─ 构建 Return(Integer(num)) → BasicBlock → Function → Program
  │
  └─ program->Dump()       // 以文本格式输出 Koopa IR

Lv1.5. 测试

有一个小问题,模板给出的sysy.l没有给出注释块的定义,需要我们自己加上BlockComment "/*"(.|\n)*?"*/",否则测试的时候会有一个测试点不过。

测试结果: 1