在 Part 10 中,你的编译器需要支持 while
循环。
保证测试用例在正确的控制流下不会出现死循环。
你需要支持的语法规则如下(以 CompUnit
为开始符号):
CompUnit -> Decl* FuncDef
Decl -> ConstDecl | VarDecl
ConstDecl -> 'const' BType ConstDef { ',' ConstDef } ';'
BType -> 'int'
ConstDef -> Ident '=' ConstInitVal
ConstInitVal -> ConstExp
ConstExp -> AddExp
VarDecl -> BType VarDef { ',' VarDef } ';'
VarDef -> Ident
| Ident '=' InitVal
InitVal -> Exp
FuncDef -> FuncType Ident '(' ')' Block // 保证当前 Ident 只为 "main"
FuncType -> 'int'
Block -> '{' { BlockItem } '}'
BlockItem -> Decl | Stmt
Stmt -> LVal '=' Exp ';'
| Block
| [Exp] ';'
| 'if' '(' Cond ')' Stmt [ 'else' Stmt ]
| 'while' '(' Cond ')' Stmt
| 'return' Exp ';' // [changed]
Exp -> AddExp
Cond -> LOrExp
LVal -> Ident
PrimaryExp -> '(' Exp ')' | LVal | Number
UnaryExp -> PrimaryExp
| Ident '(' [FuncRParams] ')'
| UnaryOp UnaryExp
UnaryOp -> '+' | '-' | '!' // 保证 '!' 只出现在 Cond 中
FuncRParams -> Exp { ',' Exp }
MulExp -> UnaryExp
| MulExp ('*' | '/' | '%') UnaryExp
AddExp -> MulExp
| AddExp ('+' | '-') MulExp
RelExp -> AddExp
| RelExp ('<' | '>' | '<=' | '>=') AddExp
EqExp -> RelExp
| EqExp ('==' | '!=') RelExp
LAndExp -> EqExp
| LAndExp '&&' EqExp
LOrExp -> LAndExp
| LOrExp '||' LAndExp
示例 IR 中的基本块跳转有点乱,是历史遗留问题
样例程序 1:
int main() {
int n = getint();
int i = 0, sum = 0;
while (i < n) {
i = i + 1;
sum = sum + i;
putint(sum);
putch(10);
}
return 0;
}
示例 IR 1:
declare i32 @getint()
declare void @putint(i32)
declare void @putch(i32)
define dso_local i32 @main() {
%1 = alloca i32
%2 = alloca i32
%3 = alloca i32
%4 = call i32 @getint()
store i32 %4, i32* %3
store i32 0, i32* %2
store i32 0, i32* %1
br label %5
5:
%6 = load i32, i32* %2
%7 = load i32, i32* %3
%8 = icmp slt i32 %6, %7
br i1 %8, label %9, label %16
9:
%10 = load i32, i32* %2
%11 = add i32 %10, 1
store i32 %11, i32* %2
%12 = load i32, i32* %1
%13 = load i32, i32* %2
%14 = add i32 %12, %13
store i32 %14, i32* %1
%15 = load i32, i32* %1
call void @putint(i32 %15)
call void @putch(i32 10)
br label %5
16:
ret i32 0
}
输入样例 1:
5
输出样例 1:
1
3
6
10
15
样例程序 2:
int main() {
const int ch = 48;
int i = 1;
while (i < 12) {
int j = 0;
while (j < 2 * i - 1) {
if (j % 3 == 1) {
putch(ch + 1);
} else {
putch(ch);
}
j = j + 1;
}
putch(10);
i = i + 1;
}
return 0;
}
示例 IR 2:
declare void @putch(i32 )
define dso_local i32 @main() {
%1 = alloca i32
%2 = alloca i32
store i32 1, i32* %2
br label %3
3:
%4 = load i32, i32* %2
%5 = icmp slt i32 %4, 12
br i1 %5, label %8, label %7
6:
store i32 0, i32* %1
br label %9
7:
ret i32 0
8:
br label %6
9:
%10 = load i32, i32* %1
%11 = load i32, i32* %2
%12 = mul i32 2, %11
%13 = sub i32 %12, 1
%14 = icmp slt i32 %10, %13
br i1 %14, label %24, label %21
15:
%16 = load i32, i32* %1
%17 = sdiv i32 %16, 3
%18 = mul i32 %17, 3
%19 = sub i32 %16, %18
%20 = icmp eq i32 %19, 1
br i1 %20, label %31, label %30
21:
call void @putch(i32 10)
%22 = load i32, i32* %2
%23 = add i32 %22, 1
store i32 %23, i32* %2
br label %3
24:
br label %15
25:
%26 = add i32 48, 1
call void @putch(i32 %26)
br label %27
27:
%28 = load i32, i32* %1
%29 = add i32 %28, 1
store i32 %29, i32* %1
br label %9
30:
call void @putch(i32 48)
br label %27
31:
br label %25
}
输出样例 2:
0
010
01001
0100100
010010010
01001001001
0100100100100
010010010010010
01001001001001001
0100100100100100100
010010010010010010010