引言
TinyC(TCC)是一个轻量级、高效的C语言编译器,由Fabrice Bellard开发。它以极小的体积(约100KB)和快速的编译速度著称,同时支持完整的C语言标准。本文将深入剖析TinyC的源码,从编译原理的角度出发,详细解析其代码实现,帮助读者理解一个完整编译器的构建过程。
1. TinyC概述
1.1 TinyC的特点
TinyC具有以下显著特点:
- 轻量级:整个编译器仅约100KB,可嵌入到各种应用中
- 快速编译:编译速度比GCC快10倍以上
- 完整C语言支持:支持C99标准,包括内联汇编
- 跨平台:支持x86、x86-64、ARM等多种架构
- 自包含:无需外部依赖,可独立运行
1.2 TinyC的架构
TinyC采用经典的编译器架构,主要包含以下组件:
- 词法分析器(Lexer)
- 语法分析器(Parser)
- 语义分析器(Semantic Analyzer)
- 中间代码生成器(IR Generator)
- 代码优化器(Optimizer)
- 目标代码生成器(Code Generator)
2. 编译原理基础
在深入源码之前,我们需要回顾编译原理的核心概念:
2.1 编译过程
编译过程通常分为以下几个阶段:
- 词法分析:将源代码分解为词法单元(Token)
- 语法分析:根据语法规则构建抽象语法树(AST)
- 语义分析:检查语义正确性,进行类型检查
- 中间代码生成:生成与机器无关的中间表示
- 代码优化:对中间代码进行优化
- 目标代码生成:生成特定平台的机器码
2.2 TinyC的编译流程
TinyC的编译流程如下:
源代码 → 词法分析 → 语法分析 → 语义分析 → 代码生成 → 目标代码
3. 源码结构分析
3.1 目录结构
TinyC的源码结构相对简单:
tcc/
├── tcc.c # 主程序入口
├── tccgen.c # 代码生成器
├── tccpp.c # 预处理器
├── tccelf.c # ELF文件处理
├── libtcc.c # 库函数
├── i386-gen.c # x86架构代码生成
├── x86_64-gen.c # x86-64架构代码生成
└── include/ # 标准头文件
3.2 核心文件解析
3.2.1 tcc.c - 主程序
tcc.c是TinyC的主程序入口,负责命令行参数解析和编译流程控制:
// tcc.c 简化版
int main(int argc, char **argv) {
TCCState *s;
int ret = 0;
// 初始化编译器状态
s = tcc_new();
if (!s) {
fprintf(stderr, "tcc: could not create tcc state\n");
return 1;
}
// 解析命令行参数
if (tcc_parse_args(s, argc, argv) < 0) {
ret = 1;
goto cleanup;
}
// 编译源文件
if (s->output_type == TCC_OUTPUT_MEMORY) {
// 内存模式编译
ret = tcc_compile(s);
if (ret == 0 && s->run) {
ret = tcc_run(s, argc, argv);
}
} else {
// 输出文件模式
ret = tcc_compile(s);
if (ret == 0) {
tcc_output_file(s, s->outfile);
}
}
cleanup:
tcc_delete(s);
return ret;
}
3.2.2 tccgen.c - 代码生成器
tccgen.c是TinyC的核心,负责将AST转换为机器码:
// tccgen.c 简化版 - 函数定义处理
void gen_function(FunctionDef *fdef) {
// 生成函数序言
gen_prologue(fdef);
// 生成函数体
gen_stmt(fdef->body);
// 生成函数尾声
gen_epilogue(fdef);
}
// 生成函数序言
void gen_prologue(FunctionDef *fdef) {
// 保存寄存器
emit_push(EBP);
emit_mov(ESP, EBP);
// 分配局部变量空间
int stack_size = fdef->local_vars * 4;
if (stack_size > 0) {
emit_sub(stack_size, ESP);
}
}
// 生成函数尾声
void gen_epilogue(FunctionDef *fdef) {
emit_mov(EBP, ESP);
emit_pop(EBP);
emit_ret();
}
3.2.3 tccpp.c - 预处理器
tccpp.c负责预处理指令的处理:
// tccpp.c 简化版 - 宏展开
void expand_macro(Token *macro) {
// 查找宏定义
MacroDef *def = find_macro(macro->str);
if (!def) return;
// 参数替换
if (def->params) {
// 处理带参数的宏
Token *arg = get_macro_args();
for (int i = 0; i < def->param_count; i++) {
replace_param(def->body, def->params[i], arg[i]);
}
}
// 展开宏体
Token *expanded = expand_tokens(def->body);
insert_tokens(expanded);
}
4. 词法分析器实现
4.1 词法分析器的工作原理
词法分析器将源代码字符流转换为Token序列。TinyC的词法分析器在tccpp.c中实现:
// tccpp.c - 词法分析核心函数
Token *get_token(void) {
Token *tok;
int c;
// 跳过空白字符
while ((c = get_char()) == ' ' || c == '\t' || c == '\n') {
if (c == '\n') {
line_num++;
if (in_include) {
// 处理行号信息
update_line_info();
}
}
}
// 分配Token内存
tok = tcc_mallocz(sizeof(Token));
tok->line_num = line_num;
tok->file_name = current_file;
// 识别Token类型
if (c == EOF) {
tok->tok = TOK_EOF;
return tok;
}
// 处理标识符和关键字
if (is_alpha(c) || c == '_') {
return get_identifier(c);
}
// 处理数字常量
if (is_digit(c)) {
return get_number(c);
}
// 处理字符串常量
if (c == '"') {
return get_string();
}
// 处理字符常量
if (c == '\'') {
return get_char_constant();
}
// 处理运算符和标点
return get_operator(c);
}
4.2 标识符识别
// 识别标识符
Token *get_identifier(int first_char) {
Token *tok = tcc_mallocz(sizeof(Token));
char buf[256];
int len = 0;
buf[len++] = first_char;
// 读取标识符的剩余部分
while (1) {
int c = peek_char();
if (!is_alnum(c) && c != '_') break;
buf[len++] = get_char();
}
buf[len] = '\0';
// 检查是否为关键字
tok->tok = check_keyword(buf);
if (tok->tok == TOK_IDENTIFIER) {
// 普通标识符
tok->str = tcc_strdup(buf);
}
return tok;
}
4.3 数字常量处理
// 处理数字常量
Token *get_number(int first_char) {
Token *tok = tcc_mallocz(sizeof(Token));
int base = 10;
unsigned long long value = 0;
// 检查进制前缀
if (first_char == '0') {
int c = peek_char();
if (c == 'x' || c == 'X') {
base = 16;
get_char(); // 跳过'x'
} else if (c >= '0' && c <= '7') {
base = 8;
}
}
// 解析数字
while (1) {
int c = peek_char();
int digit;
if (c >= '0' && c <= '9') {
digit = c - '0';
} else if (base == 16 && c >= 'a' && c <= 'f') {
digit = c - 'a' + 10;
} else if (base == 16 && c >= 'A' && c <= 'F') {
digit = c - 'A' + 10;
} else {
break;
}
if (digit >= base) {
error("Invalid digit in number");
break;
}
get_char();
value = value * base + digit;
}
// 检查后缀
int is_float = 0;
int is_long = 0;
int is_unsigned = 0;
while (1) {
int c = peek_char();
if (c == 'f' || c == 'F') {
is_float = 1;
get_char();
} else if (c == 'l' || c == 'L') {
is_long = 1;
get_char();
} else if (c == 'u' || c == 'U') {
is_unsigned = 1;
get_char();
} else {
break;
}
}
// 设置Token类型
if (is_float) {
tok->tok = TOK_FLOAT;
tok->fval = (double)value;
} else {
tok->tok = TOK_INTEGER;
tok->ival = value;
tok->is_unsigned = is_unsigned;
tok->is_long = is_long;
}
return tok;
}
5. 语法分析器实现
5.1 语法分析器架构
TinyC的语法分析器采用递归下降分析法,在tccgen.c中实现:
// tccgen.c - 语法分析入口
void parse_program(void) {
while (1) {
Token *tok = get_token();
if (tok->tok == TOK_EOF) {
break;
}
// 处理外部声明
if (tok->tok == TOK_KEYWORD_EXTERN) {
parse_extern_declaration();
} else if (tok->tok == TOK_KEYWORD_STATIC) {
parse_static_declaration();
} else if (tok->tok == TOK_KEYWORD_TYPEDEF) {
parse_typedef();
} else {
// 函数或变量定义
parse_declaration(tok);
}
}
}
5.2 表达式解析
表达式解析是语法分析的核心部分:
// 解析表达式
Expr *parse_expr(int min_precedence) {
Expr *left = parse_primary();
while (1) {
Token *op = get_token();
// 检查运算符优先级
int prec = get_precedence(op->tok);
if (prec < min_precedence) {
unget_token(op);
break;
}
// 右结合运算符处理
int next_prec = prec;
if (is_right_assoc(op->tok)) {
next_prec++;
}
// 递归解析右操作数
Expr *right = parse_expr(next_prec);
// 构建二元表达式
left = new_binary_expr(op->tok, left, right);
}
return left;
}
// 解析主要表达式
Expr *parse_primary(void) {
Token *tok = get_token();
switch (tok->tok) {
case TOK_INTEGER:
return new_int_expr(tok->ival);
case TOK_FLOAT:
return new_float_expr(tok->fval);
case TOK_STRING:
return new_string_expr(tok->str);
case TOK_IDENTIFIER:
// 变量或函数调用
return parse_identifier_expr(tok);
case '(':
// 括号表达式
Expr *expr = parse_expr(0);
expect(')');
return expr;
case TOK_KEYWORD_SIZEOF:
return parse_sizeof_expr();
default:
error("Unexpected token in expression");
return NULL;
}
}
5.3 语句解析
// 解析语句
Stmt *parse_stmt(void) {
Token *tok = get_token();
switch (tok->tok) {
case '{':
return parse_block_stmt();
case TOK_KEYWORD_IF:
return parse_if_stmt();
case TOK_KEYWORD_FOR:
return parse_for_stmt();
case TOK_KEYWORD_WHILE:
return parse_while_stmt();
case TOK_KEYWORD_DO:
return parse_do_stmt();
case TOK_KEYWORD_SWITCH:
return parse_switch_stmt();
case TOK_KEYWORD_BREAK:
return parse_break_stmt();
case TOK_KEYWORD_CONTINUE:
return parse_continue_stmt();
case TOK_KEYWORD_RETURN:
return parse_return_stmt();
case ';':
return new_empty_stmt();
default:
// 表达式语句
unget_token(tok);
Expr *expr = parse_expr(0);
expect(';');
return new_expr_stmt(expr);
}
}
// 解析if语句
Stmt *parse_if_stmt(void) {
Stmt *stmt = tcc_mallocz(sizeof(Stmt));
stmt->type = STMT_IF;
expect('(');
stmt->if_stmt.condition = parse_expr(0);
expect(')');
stmt->if_stmt.then_branch = parse_stmt();
// 检查else分支
Token *tok = get_token();
if (tok->tok == TOK_KEYWORD_ELSE) {
stmt->if_stmt.else_branch = parse_stmt();
} else {
unget_token(tok);
stmt->if_stmt.else_branch = NULL;
}
return stmt;
}
6. 语义分析器实现
6.1 符号表管理
TinyC使用符号表来管理变量和函数:
// 符号表结构
typedef struct Symbol {
char *name;
Type *type;
int storage_class; // STATIC, EXTERN, AUTO, REGISTER
int offset; // 栈偏移或全局地址
int is_function:1;
int is_prototype:1;
struct Symbol *next;
} Symbol;
// 符号表管理
typedef struct SymbolTable {
Symbol **table;
int size;
int count;
struct SymbolTable *parent;
} SymbolTable;
// 查找符号
Symbol *find_symbol(SymbolTable *table, const char *name) {
while (table) {
for (int i = 0; i < table->count; i++) {
Symbol *sym = table->table[i];
if (strcmp(sym->name, name) == 0) {
return sym;
}
}
table = table->parent;
}
return NULL;
}
// 添加符号
Symbol *add_symbol(SymbolTable *table, const char *name, Type *type, int storage) {
// 检查重复定义
Symbol *existing = find_symbol(table, name);
if (existing && existing->storage_class != EXTERN) {
error("Redefinition of '%s'", name);
return NULL;
}
// 扩展符号表
if (table->count >= table->size) {
table->size *= 2;
table->table = tcc_realloc(table->table, table->size * sizeof(Symbol*));
}
// 创建新符号
Symbol *sym = tcc_mallocz(sizeof(Symbol));
sym->name = tcc_strdup(name);
sym->type = type;
sym->storage_class = storage;
table->table[table->count++] = sym;
return sym;
}
6.2 类型系统
TinyC的类型系统支持基本类型、指针、数组和结构体:
// 类型定义
typedef struct Type {
enum {
TYPE_VOID,
TYPE_CHAR,
TYPE_SHORT,
TYPE_INT,
TYPE_LONG,
TYPE_FLOAT,
TYPE_DOUBLE,
TYPE_POINTER,
TYPE_ARRAY,
TYPE_STRUCT,
TYPE_UNION,
TYPE_ENUM
} kind;
int size; // 类型大小(字节)
int align; // 对齐要求
int is_signed:1; // 是否有符号
int is_const:1; // 是否为常量
int is_volatile:1; // 是否为volatile
// 复合类型信息
union {
// 指针类型
struct {
Type *base;
} pointer;
// 数组类型
struct {
Type *base;
int length;
} array;
// 结构体/联合体
struct {
char *name;
Field *fields;
int field_count;
int is_union:1;
} struct_union;
} u;
} Type;
// 创建基本类型
Type *type_int = &(Type){.kind = TYPE_INT, .size = 4, .align = 4, .is_signed = 1};
Type *type_char = &(Type){.kind = TYPE_CHAR, .size = 1, .align = 1, .is_signed = 1};
Type *type_float = &(Type){.kind = TYPE_FLOAT, .size = 4, .align = 4};
// 创建指针类型
Type *make_pointer_type(Type *base) {
Type *ptr = tcc_mallocz(sizeof(Type));
ptr->kind = TYPE_POINTER;
ptr->size = 4; // 32位系统
ptr->align = 4;
ptr->u.pointer.base = base;
return ptr;
}
// 创建数组类型
Type *make_array_type(Type *base, int length) {
Type *arr = tcc_mallocz(sizeof(Type));
arr->kind = TYPE_ARRAY;
arr->size = base->size * length;
arr->align = base->align;
arr->u.array.base = base;
arr->u.array.length = length;
return arr;
}
6.3 类型检查
// 类型检查表达式
void check_expr_type(Expr *expr) {
switch (expr->type) {
case EXPR_BINARY:
check_binary_expr(expr);
break;
case EXPR_UNARY:
check_unary_expr(expr);
break;
case EXPR_CALL:
check_call_expr(expr);
break;
case EXPR_IDENTIFIER:
check_identifier_expr(expr);
break;
default:
// 基本表达式类型已确定
break;
}
}
// 检查二元表达式
void check_binary_expr(Expr *expr) {
Expr *left = expr->u.binary.left;
Expr *right = expr->u.binary.right;
// 检查左右操作数类型
check_expr_type(left);
check_expr_type(right);
// 根据运算符进行类型转换
switch (expr->u.binary.op) {
case '+':
case '-':
case '*':
case '/':
case '%':
// 算术运算
if (!is_arithmetic_type(left->expr_type) ||
!is_arithmetic_type(right->expr_type)) {
error("Operands must be arithmetic types");
}
// 类型提升
expr->expr_type = binary_type_promotion(left->expr_type, right->expr_type);
break;
case TOK_EQ:
case TOK_NE:
case '<':
case '>':
case TOK_LE:
case TOK_GE:
// 比较运算
if (!is_compatible_type(left->expr_type, right->expr_type)) {
error("Incompatible types in comparison");
}
expr->expr_type = type_int;
break;
case '=':
// 赋值运算
if (!is_assignable_type(left->expr_type, right->expr_type)) {
error("Invalid assignment");
}
expr->expr_type = left->expr_type;
break;
default:
error("Unknown binary operator");
}
}
7. 代码生成器实现
7.1 中间代码生成
TinyC直接生成目标代码,但我们可以理解其代码生成逻辑:
// 代码生成上下文
typedef struct CodeGenCtx {
uint8_t *code; // 生成的机器码缓冲区
int code_size; // 当前代码大小
int code_capacity; // 缓冲区容量
SymbolTable *symtab; // 符号表
int stack_offset; // 栈偏移
// 寄存器分配
int reg_used[8]; // 寄存器使用状态
int reg_alloc[8]; // 寄存器分配状态
} CodeGenCtx;
// 生成函数代码
void gen_function_code(CodeGenCtx *ctx, FunctionDef *fdef) {
// 保存当前上下文
int old_stack_offset = ctx->stack_offset;
// 分配局部变量空间
int local_size = calculate_local_size(fdef);
ctx->stack_offset += local_size;
// 生成函数序言
gen_prologue(ctx, fdef);
// 生成函数体
gen_stmt_code(ctx, fdef->body);
// 生成函数尾声
gen_epilogue(ctx, fdef);
// 恢复上下文
ctx->stack_offset = old_stack_offset;
}
7.2 x86架构代码生成
TinyC针对不同架构有专门的代码生成器,以x86为例:
// i386-gen.c - x86代码生成
void gen_x86_prologue(CodeGenCtx *ctx, FunctionDef *fdef) {
// push ebp
emit_byte(ctx, 0x55);
// mov ebp, esp
emit_byte(ctx, 0x89);
emit_byte(ctx, 0xE5);
// sub esp, N
if (fdef->local_vars > 0) {
int stack_size = fdef->local_vars * 4;
emit_byte(ctx, 0x83);
emit_byte(ctx, 0xEC);
emit_byte(ctx, stack_size & 0xFF);
}
}
void gen_x86_epilogue(CodeGenCtx *ctx) {
// mov esp, ebp
emit_byte(ctx, 0x89);
emit_byte(ctx, 0xE5);
// pop ebp
emit_byte(ctx, 0x5D);
// ret
emit_byte(ctx, 0xC3);
}
// 生成算术运算
void gen_x86_arithmetic(CodeGenCtx *ctx, int op, Expr *left, Expr *right) {
// 生成左操作数代码
gen_expr_code(ctx, left);
// 保存结果到寄存器
int reg = allocate_register(ctx);
mov_reg_to_reg(ctx, EAX, reg);
// 生成右操作数代码
gen_expr_code(ctx, right);
// 执行运算
switch (op) {
case '+':
// add reg, eax
emit_byte(ctx, 0x01);
emit_byte(ctx, 0xC0 | (reg << 3) | 0x00);
break;
case '-':
// sub reg, eax
emit_byte(ctx, 0x29);
emit_byte(ctx, 0xC0 | (reg << 3) | 0x00);
break;
case '*':
// imul reg, eax
emit_byte(ctx, 0x0F);
emit_byte(ctx, 0xAF);
emit_byte(ctx, 0xC0 | (reg << 3) | 0x00);
break;
case '/':
// idiv reg
emit_byte(ctx, 0xF7);
emit_byte(ctx, 0xF8 | reg);
break;
}
free_register(ctx, reg);
}
7.3 寄存器分配
TinyC使用简单的寄存器分配策略:
// 寄存器分配
int allocate_register(CodeGenCtx *ctx) {
// 优先使用空闲寄存器
for (int i = 0; i < 8; i++) {
if (ctx->reg_used[i] == 0) {
ctx->reg_used[i] = 1;
return i;
}
}
// 如果没有空闲寄存器,溢出到栈
return spill_register(ctx);
}
// 寄存器溢出
int spill_register(CodeGenCtx *ctx) {
// 选择一个寄存器溢出到栈
int reg = select_register_to_spill(ctx);
// 保存寄存器内容到栈
emit_push_reg(ctx, reg);
// 更新栈偏移
ctx->stack_offset += 4;
return reg;
}
// 恢复寄存器
void restore_register(CodeGenCtx *ctx, int reg) {
// 从栈恢复寄存器内容
emit_pop_reg(ctx, reg);
// 更新栈偏移
ctx->stack_offset -= 4;
}
8. 预处理器实现
8.1 宏处理
TinyC的预处理器支持宏定义和展开:
// 宏定义结构
typedef struct MacroDef {
char *name;
int is_function_like:1; // 是否为函数式宏
int is_variadic:1; // 是否为可变参数宏
int param_count; // 参数数量
char **params; // 参数名列表
Token *body; // 宏体
struct MacroDef *next;
} MacroDef;
// 宏展开
void expand_macro(Token *macro) {
MacroDef *def = find_macro(macro->str);
if (!def) return;
if (def->is_function_like) {
// 函数式宏
Token *args = get_macro_args();
expand_function_macro(def, args);
} else {
// 对象式宏
expand_object_macro(def);
}
}
// 函数式宏展开
void expand_function_macro(MacroDef *def, Token *args) {
Token *expanded = NULL;
Token *current = def->body;
while (current) {
if (current->tok == TOK_IDENTIFIER) {
// 查找参数
int param_idx = find_param_index(def, current->str);
if (param_idx >= 0) {
// 替换为实际参数
Token *arg = get_nth_arg(args, param_idx);
expanded = append_tokens(expanded, copy_tokens(arg));
} else {
// 不是参数,直接复制
expanded = append_tokens(expanded, copy_token(current));
}
} else if (current->tok == TOK_STRINGIFY) {
// 字符串化操作符
Token *next = current->next;
if (next && next->tok == TOK_IDENTIFIER) {
int param_idx = find_param_index(def, next->str);
if (param_idx >= 0) {
Token *arg = get_nth_arg(args, param_idx);
Token *stringified = stringify_token(arg);
expanded = append_tokens(expanded, stringified);
current = next; // 跳过参数
}
}
} else {
expanded = append_tokens(expanded, copy_token(current));
}
current = current->next;
}
// 插入展开后的token
insert_tokens(expanded);
}
8.2 条件编译
// 条件编译处理
void process_conditionals(void) {
Token *tok = get_token();
if (tok->tok == TOK_PP_IF) {
process_if();
} else if (tok->tok == TOK_PP_IFDEF) {
process_ifdef();
} else if (tok->tok == TOK_PP_IFNDEF) {
process_ifndef();
} else if (tok->tok == TOK_PP_ELIF) {
process_elif();
} else if (tok->tok == TOK_PP_ELSE) {
process_else();
} else if (tok->tok == TOK_PP_ENDIF) {
process_endif();
}
}
// #ifdef处理
void process_ifdef(void) {
Token *macro = get_token();
if (macro->tok != TOK_IDENTIFIER) {
error("Expected identifier after #ifdef");
return;
}
// 检查宏是否定义
MacroDef *def = find_macro(macro->str);
if (def) {
// 宏已定义,处理真分支
process_true_branch();
} else {
// 宏未定义,跳过真分支
skip_false_branch();
}
}
9. 高级特性实现
9.1 内联汇编
TinyC支持内联汇编,这是其特色功能之一:
// 内联汇编处理
void process_inline_asm(void) {
expect('(');
// 解析汇编字符串
Token *asm_str = get_token();
if (asm_str->tok != TOK_STRING) {
error("Expected string literal after __asm__");
return;
}
// 解析约束和操作数
Token *constraints = NULL;
Token *operands = NULL;
if (peek_token()->tok == ':') {
get_token(); // 跳过':'
constraints = parse_asm_constraints();
if (peek_token()->tok == ':') {
get_token(); // 跳过第二个':'
operands = parse_asm_operands();
}
}
expect(')');
// 生成汇编代码
generate_inline_asm(asm_str->str, constraints, operands);
}
// 生成内联汇编
void generate_inline_asm(const char *asm_str, Token *constraints, Token *operands) {
// 解析汇编字符串中的占位符
char *parsed = parse_asm_template(asm_str, operands);
// 生成机器码
emit_asm_code(parsed);
// 处理约束
process_asm_constraints(constraints, operands);
}
9.2 可变参数函数
TinyC支持C99的可变参数函数:
// 可变参数函数处理
void process_variadic_function(FunctionDef *fdef) {
// 检查是否为可变参数函数
if (fdef->param_count > 0 &&
strcmp(fdef->params[fdef->param_count-1]->name, "...") == 0) {
fdef->is_variadic = 1;
// 移除"..."参数
fdef->param_count--;
// 设置可变参数处理
setup_variadic_prologue(fdef);
}
}
// 可变参数函数序言
void setup_variadic_prologue(FunctionDef *fdef) {
// 保存寄存器
emit_push_reg(EAX);
emit_push_reg(ECX);
emit_push_reg(EDX);
// 设置va_list
// va_start宏的实现
// ...
}
10. 性能优化
10.1 编译速度优化
TinyC的快速编译得益于以下设计:
- 单遍编译:TinyC采用单遍编译,无需多次遍历AST
- 内存管理:使用简单的内存池,减少malloc/free调用
- 缓存机制:预处理器和词法分析器有缓存
// 内存池实现
typedef struct MemoryPool {
void **blocks;
int block_count;
int block_size;
int current_offset;
} MemoryPool;
void *pool_alloc(MemoryPool *pool, int size) {
// 对齐大小
size = (size + 15) & ~15;
// 检查当前块是否有足够空间
if (pool->current_offset + size > pool->block_size) {
// 分配新块
pool->blocks = tcc_realloc(pool->blocks,
(pool->block_count + 1) * sizeof(void*));
pool->blocks[pool->block_count] = tcc_malloc(pool->block_size);
pool->block_count++;
pool->current_offset = 0;
}
// 从当前块分配
void *ptr = (char*)pool->blocks[pool->block_count-1] + pool->current_offset;
pool->current_offset += size;
return ptr;
}
10.2 代码优化
虽然TinyC的优化能力有限,但仍有一些基本优化:
// 常量折叠
Expr *fold_constants(Expr *expr) {
if (expr->type == EXPR_BINARY) {
Expr *left = fold_constants(expr->u.binary.left);
Expr *right = fold_constants(expr->u.binary.right);
// 如果左右都是常量
if (left->type == EXPR_CONSTANT && right->type == EXPR_CONSTANT) {
// 执行常量计算
Constant result = compute_constant(left->u.constant,
right->u.constant,
expr->u.binary.op);
return new_constant_expr(result);
}
}
return expr;
}
// 死代码消除
void eliminate_dead_code(Stmt *stmt) {
if (stmt->type == STMT_IF) {
// 检查条件是否为常量
if (stmt->if_stmt.condition->type == EXPR_CONSTANT) {
int cond_value = stmt->if_stmt.condition->u.constant.ival;
if (cond_value) {
// 条件为真,保留then分支
eliminate_dead_code(stmt->if_stmt.then_branch);
stmt->if_stmt.else_branch = NULL;
} else {
// 条件为假,保留else分支
eliminate_dead_code(stmt->if_stmt.else_branch);
stmt->if_stmt.then_branch = NULL;
}
}
}
}
11. 实际应用示例
11.1 编译一个简单程序
让我们通过一个例子来理解TinyC的工作流程:
// example.c
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 4);
return result;
}
11.2 编译过程分析
词法分析:将源代码分解为Token序列
[int] [add] [(] [int] [a] [,] [int] [b] [)] [{] [return] [a] [+] [b] [;] [}]语法分析:构建AST
FunctionDef: add Parameters: a(int), b(int) Body: ReturnStmt BinaryExpr: + Identifier: a Identifier: b语义分析:类型检查
- 检查
a + b的类型兼容性 - 确定返回类型为int
- 检查
代码生成:生成x86机器码 “`assembly add: push ebp mov ebp, esp mov eax, [ebp+8] ; a add eax, [ebp+12] ; b pop ebp ret
main:
push ebp
mov ebp, esp
sub esp, 4
push 4
push 3
call add
add esp, 8
mov [ebp-4], eax
mov eax, [ebp-4]
mov esp, ebp
pop ebp
ret
”`
12. 总结
TinyC作为一个轻量级编译器,展示了编译器设计的精髓。通过深入分析其源码,我们可以学到:
- 编译器架构:如何组织词法分析、语法分析、语义分析和代码生成
- 代码生成技术:如何将高级语言转换为机器码
- 性能优化:如何在保持代码简洁的同时实现高效编译
- 跨平台支持:如何设计可移植的编译器
TinyC的成功证明了编译器不必复杂才能高效。其简洁的设计和高效的实现为编译器开发提供了宝贵的参考。
通过学习TinyC源码,开发者不仅可以深入理解编译原理,还能获得实际的编译器开发经验,为构建自己的编译器或语言工具链打下坚实基础。
