Clean code

toy-compiler 编译器目前的类图是这样的:

/images/arch_v0.png

分支

为了实现分支语句,我们创建一个新的 NBranchStatement 类,这个类继承自 NStatement 类.每一条分支语句( NBranchStatement )对象应该包含若干个 if 语句块( NIFBlock )和至多一个 else 语句块( NBlock ).而每个 NIFBlock 中应包含一个条件表达式( NExpression )和一个基本语句块( NBlock , .

NBranchStatement 的结构如下:

  class NIFBlock : public NBlock {
  public:
    NExpression &condExpr;

    NIFBlock(NExpression &condition, NBlock &block) : condExpr(condition) {
      // 这里可以优化
      statements = block.statements;
    }
  };

  class NIFBlocks : public NBlock {
  public:
    IFBlockList IFBlocks;

    NIFBlocks() {}
    IFBlockList &getIFBlocks() { return IFBlocks; };
  };

  class NBranchStatement : public NStatement {
  public:
    IFBlockList &IFBlocks;
    NBlock *ElseBlock;

    NBranchStatement(IFBlockList &ifBlocks, NBlock *elseBlock): IFBlocks(ifBlocks), ElseBlock(elseBlock) {};

    void setIFBlocks(IFBlockList &ifBlocks);
    void setElseBlock(NBlock *elseBlock);
    llvm::Value *codeGen(CodeGenContext &context) override;
  };

因为 NIFBlock 继承自 NBlock,本身就含有一个语句列表 StatementList,所以不需要再添加新的语句列表; NIFBlock::codeGen 函数 不重写 父类的 codeGen,这样 NIFBlock::codeGen 能直接生成 if 分支的代码.

接下来需要实现 NBranchStatement::codeGen 函数,主要参考了手册第5章的内容:

考虑只有 if-then-else 的情况,需要一段比较条件表达式值的代码和三个基本块(then 基本块和 else基本块 , merge基本块 ), merge基本块else基本块then基本块 的交汇点,也就是说,这两个基本块的最后一条语句都是跳转到 merge 基本块执行.如果条件表达式的值不为0,也就是 true,就跳转到 then基本块 执行,如果条件表达式的值为0,也就是 false,就跳转到 else基本块 执行.

如果加入 elseif 的情况,其实也非常直观:如果前一个 if 语句或者 elseif 语句条件成立,则跳转到对应的 then基本块 执行,否则跳转到下一个 elseif 条件表达式处(实际上代码中将条件表达式单独放到一个基本块中)或者最后的 else基本块 处执行(如果没有 else 基本块,就直接跳到最后的 merge基本块 处执行).

举个例子来说,对于下面的分支语句:

int a = 1
int b = 0
if (a) {
    echo(1) {
else if (b) {
    echo(2)
}
else {
    echo(3)
}

生成的 IR 为:

    %0 = load i64, ptr %a, align 4
    %ifcond = icmp eq i64 %0, 0
    br i1 %ifcond, label %if, label %then

  then:                                             ; preds = %entry
    call void @echo(i64 1)
    br label %merge

  if:                                               ; preds = %entry
    %1 = load i64, ptr %b, align 4
    %ifcond1 = icmp eq i64 %1, 0
    br i1 %ifcond1, label %else, label %then2

  then2:                                            ; preds = %if
    call void @echo(i64 2)
    br label %merge

  else:                                             ; preds = %if
    call void @echo(i64 3)
    br label %merge

  merge:                                            ; preds = %else, %then2, %then
    ret i32 0

分支语句的类图如下:

/images/branch-statement.png

循环

之后想实现类似 Python 中的 for 循环,这就需要先了解一下 Python 的实现,然后再做设计.

本节只实现普通的 while 循环.

为了实现 while 语句,我们创建一个新的 NWhileStatement 类,这个类继承自 NStatement 类.一个 while 语句的结构和只有一个if分支语句的结构是类似的,包含一个条件表达式和两个基本块( then 基本块和 merge 基本块).

while 循环的实现和 if 语句类似,先生成条件表达式,如果条件表达式的值为 true,则执行 while 语句块的代码,否则,跳转到 merge基本块 执行.

举个例子来说,对于下面的分支语句:

  int a = 2
  while (a) {
      echo(1)
      a = a - 1
  }

生成的 IR 为:

    br label %whilecond

  whilecond:                                        ; preds = %then, %entry
    %0 = load i64, ptr %a, align 4
    %whilecond1 = icmp eq i64 %0, 0
    br i1 %whilecond1, label %merge, label %then

  then:                                             ; preds = %whilecond
    call void @echo(i64 1)
    %1 = load i64, ptr %a, align 4
    %subtmp = sub i64 %1, 1
    store i64 %subtmp, ptr %a, align 4
    br label %whilecond

  merge:                                            ; preds = %whilecond
    ret i32 0

循环语句的类图如下:

/images/while-statement.png

本文涉及的代码详见github.