diff --git a/README.md b/README.md index 86a2024..91db7ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RVBTCC -- 约 1800 行的轻量级自举编译器。 +- 约 1900 行的轻量级自举编译器。 - 编译器和自举编译器行为一致。 - 语法类似 C,输出 RISC-V 汇编。 - 依赖几个 libc 函数用于输入输出。 @@ -12,7 +12,6 @@ ### 真机运行 - 编译运行程序,src 为本语言源代码。可以编译 demo 文件夹下的实例。 ```sh @@ -25,19 +24,6 @@ $ sh run-native.sh $ sh boot-native.sh ``` -输出六个文件: - -| 源代码 | 编译器 | 汇编 | 可执行 | 命名 | -| ----------------- | --------- | ------- | --------- | ---------------------- | -| boot.c boot-lib.c | gcc | | gcc.out | 自制编译器 | -| boot.c boot-lib.h | gcc.out | boot1.s | boot1.out | 自举自制编译器 | -| boot.c boot-lib.h | boot1.out | boot2.s | boot2.out | 自举自举自制编译器 | -| boot.c boot-lib.h | boot2.out | boot3.s | | 验证自举自举自制编译器 | - -后三次编译时,boot-lib.h 的内容被手动导入 boot.c 开头进行编译,boot-lib.c 提供的库通过链接引入。 - -自举的目标为 boot1.s == boot2.s == boot3.s - ### 模拟运行 安装以下依赖 @@ -58,27 +44,31 @@ $ sh run.sh $ sh boot.sh ``` +### 自举过程 + + +自举会输出六个文件,三个汇编文件和三个可执行文件: + +| 源代码 | 编译器 | 汇编 | 可执行 | 代号 | 命名 | +| ----------------- | --------- | ------- | --------- | ---- | ---------------------- | +| boot.c boot-lib.c | gcc | | gcc.out | G | 自制编译器 | +| boot.c boot-lib.h | gcc.out | boot1.s | boot1.out | B1 | 自举自制编译器 | +| boot.c boot-lib.h | boot1.out | boot2.s | boot2.out | B2 | 自举自举自制编译器 | +| boot.c boot-lib.h | boot2.out | boot3.s | | B3 | 验证自举自举自制编译器 | + +后三次编译时,boot-lib.h 的内容被手动导入 boot.c 开头进行编译,boot-lib.c 提供的库通过链接引入。 + +整个自举及其验证的过程如下图所示: + +![](bootstrapping.png) + +自举的目标为 G、B1、B2 的可执行文件行为一致,也就是说 B1、B2、B3 的汇编代码一致。 + ## 语言文档 -### 关键字 +### 注释 -本语言包含的关键字即为支持的标量类型的关键字和流程控制的关键字,还有 `const`。 - -### `const` 关键字 - -`const` 关键字可以在类型中使用,在大部分情况下会被直接忽略。支持它是为了更好兼容 C 程序。 - -但是当在出现 - -- 全局,标量(即不是数组) -- 类型为 `const int` 或 `const int const` -- 带有初始化 - -的声明时,将会被解析为整数常量。 - -整数常量在使用的时候会被直接替换为对应的右值,失去作为全局变量左值的性质。 - -使用 `int const` 或 `int` 可以避免这样的特殊处理。 +支持多行 `/* ... */` 和单行 `//` 两种注释 ### 支持六个基本类型 @@ -103,6 +93,25 @@ $ sh boot.sh - `break` `continue` - `return` +### 关键字 + +本语言包含的关键字即为支持的标量类型的关键字和流程控制的关键字,还有 `const`。 + +#### `const` 关键字 + +`const` 关键字可以在类型中使用,在大部分情况下会被直接忽略。支持它是为了更好兼容 C 程序。 + +但是当在出现 + +- 全局,标量(即不是数组) +- 类型为 `const int` 或 `const int const` +- 带有初始化 + +的声明时,将会被解析为整数常量。 + +整数常量在使用的时候会被直接替换为对应的右值,失去作为全局变量左值的性质。 + +使用 `int const` 或 `int` 可以避免这样的特殊处理。 ### 支持以下运算符 @@ -121,7 +130,8 @@ $ sh boot.sh | | | 按位或 | 从左到右 | | `&&` | 逻辑与 | 从左到右 | | || | 逻辑或 | 从左到右 | -| `=` | 赋值 | 从右到左 | +| `?:` | 条件 | 从右到左 | +| `=` `+=` `-=` `*=` `/=` `%=` `<<=` `>>=` `&=` `^=` |= | 赋值 | 从右到左 | | `,` | 逗号 | 从左到右 | - 同级表达式的求值顺序与结合性一致。 diff --git a/boot.c b/boot.c index a56a1ac..f5a33e7 100644 --- a/boot.c +++ b/boot.c @@ -21,7 +21,7 @@ int token_data; const int TOKEN_EOF = 0; const int TOKEN_SEMICOLON = 1; const int TOKEN_ADD = 2; -const int TOKEN_MINUS = 3; +const int TOKEN_SUB = 3; const int TOKEN_MUL = 4; const int TOKEN_DIV = 5; const int TOKEN_REM = 6; @@ -40,6 +40,18 @@ const int TOKEN_LOR = 18; const int TOKEN_ELLIPSIS = 19; const int TOKEN_INC = 20; const int TOKEN_DEC = 21; +const int TOKEN_ADD_ASSIGN = 22; +const int TOKEN_SUB_ASSIGN = 23; +const int TOKEN_MUL_ASSIGN = 24; +const int TOKEN_DIV_ASSIGN = 25; +const int TOKEN_REM_ASSIGN = 26; +const int TOKEN_AND_ASSIGN = 27; +const int TOKEN_OR_ASSIGN = 28; +const int TOKEN_XOR_ASSIGN = 29; +const int TOKEN_LSHIFT_ASSIGN = 30; +const int TOKEN_RSHIFT_ASSIGN = 31; +const int TOKEN_QUESTION = 32; +const int TOKEN_COLON = 33; const int TOKEN_EQ = 40; const int TOKEN_NE = 41; @@ -85,8 +97,8 @@ const int TYPE_TOKEN_MASK = 128; int parse_int(int ch) { int num = ch - '0'; while (is_digit(ch = getchar())) { - num = num * 10; - num = num + ch - '0'; + num *= 10; + num += ch - '0'; } ungetchar(ch); return num; @@ -264,6 +276,8 @@ void next_token() { int ch2 = getchar(); if (ch2 == '+') { token_type = TOKEN_INC; + } else if (ch2 == '=') { + token_type = TOKEN_ADD_ASSIGN; } else { ungetchar(ch2); token_type = TOKEN_ADD; @@ -272,15 +286,25 @@ void next_token() { int ch2 = getchar(); if (ch2 == '-') { token_type = TOKEN_DEC; + } else if (ch2 == '=') { + token_type = TOKEN_SUB_ASSIGN; } else { ungetchar(ch2); - token_type = TOKEN_MINUS; + token_type = TOKEN_SUB; } } else if (ch == '*') { - token_type = TOKEN_MUL; + int ch2 = getchar(); + if (ch2 == '=') { + token_type = TOKEN_MUL_ASSIGN; + } else { + ungetchar(ch2); + token_type = TOKEN_MUL; + } } else if (ch == '/') { int ch2 = getchar(); - if (ch2 == '/') { + if (ch2 == '=') { + token_type = TOKEN_DIV_ASSIGN; + } if (ch2 == '/') { do ch = getchar(); while (ch != -1 && ch != '\n'); next_token(); return; @@ -305,9 +329,19 @@ void next_token() { token_type = TOKEN_DIV; } } else if (ch == '%') { - token_type = TOKEN_REM; + int ch2 = getchar(); + if (ch2 == '=') { + token_type = TOKEN_REM_ASSIGN; + } else { + ungetchar(ch2); + token_type = TOKEN_REM; + } } else if (ch == ';') { token_type = TOKEN_SEMICOLON; + } else if (ch == '?') { + token_type = TOKEN_QUESTION; + } else if (ch == ':') { + token_type = TOKEN_COLON; } else if (ch == ',') { token_type = TOKEN_COMMA; } else if (ch == '<') { @@ -315,7 +349,13 @@ void next_token() { if (ch2 == '=') { token_type = TOKEN_LE; } else if (ch2 == '<') { - token_type = TOKEN_LSHIFT; + int ch3 = getchar(); + if (ch3 == '=') { + token_type = TOKEN_LSHIFT_ASSIGN; + } else { + ungetchar(ch3); + token_type = TOKEN_LSHIFT; + } } else { ungetchar(ch2); token_type = TOKEN_LT; @@ -325,7 +365,13 @@ void next_token() { if (ch2 == '=') { token_type = TOKEN_GE; } else if (ch2 == '>') { - token_type = TOKEN_RSHIFT; + int ch3 = getchar(); + if (ch3 == '=') { + token_type = TOKEN_RSHIFT_ASSIGN; + } else { + ungetchar(ch3); + token_type = TOKEN_RSHIFT; + } } else { ungetchar(ch2); token_type = TOKEN_GT; @@ -348,7 +394,9 @@ void next_token() { } } else if (ch == '&') { int ch2 = getchar(); - if (ch2 == '&') { + if (ch2 == '=') { + token_type = TOKEN_AND_ASSIGN; + } else if (ch2 == '&') { token_type = TOKEN_LAND; } else { ungetchar(ch2); @@ -356,14 +404,22 @@ void next_token() { } } else if (ch == '|') { int ch2 = getchar(); - if (ch2 == '|') { + if (ch2 == '=') { + token_type = TOKEN_OR_ASSIGN; + } else if (ch2 == '|') { token_type = TOKEN_LOR; } else { ungetchar(ch2); token_type = TOKEN_OR; } } else if (ch == '^') { - token_type = TOKEN_XOR; + int ch2 = getchar(); + if (ch2 == '=') { + token_type = TOKEN_XOR_ASSIGN; + } else { + ungetchar(ch2); + token_type = TOKEN_XOR; + } } else if (ch == '~') { token_type = TOKEN_COMPL; } else if (ch == '\'') { @@ -432,7 +488,7 @@ int parse_type() { if (token_type == TOKEN_MUL) { next_token(); ignore_const(); - type = type | TYPE_PTR_MASK; + type |= TYPE_PTR_MASK; } unget_token(); return type; @@ -799,7 +855,7 @@ void store_into_local(int rs1, int slot) { int materialize_address(int rd, int type, int marker) { if (marker == MARKER_ARRAY) { - type = type | TYPE_PTR_MASK; + type |= TYPE_PTR_MASK; } reg_type[rd] = type; indirection[rd] = marker == MARKER_SCALAR; @@ -926,7 +982,7 @@ void asm_return() { } int log_step_of(int type) { - return 2 * (type == TYPE_INT_PTR); + return type == TYPE_INT_PTR ? 2 : 0; } int step_of(int type) { @@ -976,9 +1032,9 @@ int asm_sub(int lhs, int rhs) { eprintf("void pointer cannot be arithmetically operated\n"); exit(1); } - int difference = asm_rr(TYPE_INT, "sub", lhs, rhs); - _asm_ri("slli", difference, difference, log_step_of(lhs_type)); - return difference; + int diff = asm_rr(TYPE_INT, "sub", lhs, rhs); + _asm_ri("slli", diff, diff, log_step_of(lhs_type)); + return diff; } if (type1) { int neg = asm_r_arith("neg", rhs); @@ -1142,7 +1198,7 @@ int parse_prefix_expr() { exit(1); } return dereference(reg); - } else if (token_type == TOKEN_MINUS) { + } else if (token_type == TOKEN_SUB) { int reg = parse_postfix_expr(); return asm_r_arith("neg", reg); } else if (token_type == TOKEN_COMPL) { @@ -1193,7 +1249,7 @@ int parse_add_expr() { if (token_type == TOKEN_ADD) { int rhs = parse_mul_expr(); lhs = asm_add(lhs, rhs); - } else if (token_type == TOKEN_MINUS) { + } else if (token_type == TOKEN_SUB) { int rhs = parse_mul_expr(); lhs = asm_sub(lhs, rhs); } else { @@ -1254,12 +1310,12 @@ int parse_eq_expr() { next_token(); if (token_type == TOKEN_EQ) { int rhs = parse_cmp_expr(); - int xor0 = asm_rr_cmp("xor", lhs, rhs); - lhs = asm_r(TYPE_INT, "seqz", xor0); + int xor = asm_rr_cmp("xor", lhs, rhs); + lhs = asm_r(TYPE_INT, "seqz", xor); } else if (token_type == TOKEN_NE) { int rhs = parse_cmp_expr(); - int xor0 = asm_rr_cmp("xor", lhs, rhs); - lhs = asm_r(TYPE_INT, "snez", xor0); + int xor = asm_rr_cmp("xor", lhs, rhs); + lhs = asm_r(TYPE_INT, "snez", xor); } else { unget_token(); break; @@ -1372,13 +1428,90 @@ int parse_logical_or_expr() { return lhs; } +int parse_conditional_expr() { + int cond = parse_logical_or_expr(); + next_token(); + if (token_type == TOKEN_QUESTION) { + int label1 = next_label(); + int label2 = next_label(); + asm_beqz(cond, label1); + int lhs = parse_expr(); + int result = next_reg(reg_type[lhs]); + asm_mv(result, lhs); + asm_j(label2); + expect_token(TOKEN_COLON); + asm_label(label1); + int rhs = parse_conditional_expr(); + if (reg_type[lhs] != reg_type[rhs]) { + eprintf("type mismatch in conditional expression\n"); + exit(1); + } + asm_mv(result, rhs); + asm_label(label2); + return result; + } else { + unget_token(); + return cond; + } +} + int parse_assign_expr() { - int lhs = parse_logical_or_expr(); + int lhs = parse_conditional_expr(); next_token(); if (token_type == TOKEN_ASSIGN) { int rhs = parse_assign_expr(); asm_mv(lhs, rhs); return lhs; + } else if (token_type == TOKEN_ADD_ASSIGN) { + int rhs = parse_assign_expr(); + int sum = asm_add(lhs, rhs); + asm_mv(lhs, sum); + return lhs; + } else if (token_type == TOKEN_SUB_ASSIGN) { + int rhs = parse_assign_expr(); + int diff = asm_sub(lhs, rhs); + asm_mv(lhs, diff); + return lhs; + } else if (token_type == TOKEN_MUL_ASSIGN) { + int rhs = parse_assign_expr(); + int prod = asm_rr_arith("mul", lhs, rhs); + asm_mv(lhs, prod); + return lhs; + } else if (token_type == TOKEN_DIV_ASSIGN) { + int rhs = parse_assign_expr(); + int quot = asm_rr_arith("div", lhs, rhs); + asm_mv(lhs, quot); + return lhs; + } else if (token_type == TOKEN_REM_ASSIGN) { + int rhs = parse_assign_expr(); + int rem = asm_rr_arith("rem", lhs, rhs); + asm_mv(lhs, rem); + return lhs; + } else if (token_type == TOKEN_LSHIFT_ASSIGN) { + int rhs = parse_assign_expr(); + int lshift = asm_rr_arith("sll", lhs, rhs); + asm_mv(lhs, lshift); + return lhs; + } else if (token_type == TOKEN_RSHIFT_ASSIGN) { + int rhs = parse_assign_expr(); + int rshift = asm_rr_arith("sra", lhs, rhs); + asm_mv(lhs, rshift); + return lhs; + } else if (token_type == TOKEN_AND_ASSIGN) { + int rhs = parse_assign_expr(); + int and = asm_rr_arith("and", lhs, rhs); + asm_mv(lhs, and); + return lhs; + } else if (token_type == TOKEN_XOR_ASSIGN) { + int rhs = parse_assign_expr(); + int xor = asm_rr_arith("xor", lhs, rhs); + asm_mv(lhs, xor); + return lhs; + } else if (token_type == TOKEN_OR_ASSIGN) { + int rhs = parse_assign_expr(); + int or = asm_rr_arith("or", lhs, rhs); + asm_mv(lhs, or); + return lhs; } else { unget_token(); return lhs; @@ -1599,7 +1732,7 @@ void parse_function(const char* name) { eprintf("array of pointers is not supported\n"); exit(1); } - arg_type = arg_type | TYPE_PTR_MASK; + arg_type |= TYPE_PTR_MASK; } if (arg >= 8) { eprintf("too many arguments\n"); @@ -1646,7 +1779,7 @@ void parse_function(const char* name) { int frame_size = (max_local_id - 1 + reg_used + 2) * 8; if (reg_used > 10) reg_used = 10; if (frame_size % 16 != 0) { - frame_size = frame_size + 8; + frame_size += 8; } // prolog asm_label(prolog_label); diff --git a/bootstrapping.png b/bootstrapping.png new file mode 100644 index 0000000..221b03a Binary files /dev/null and b/bootstrapping.png differ