手把手带你从0手撸c语言编译器(下) 挑战全网最硬核教程 从词法分析到语义分析全拆解 建议收藏 挑战全网最硬核教程 从词法分析到语义分析全拆解 耗时 200 小时肝出的编译器实战教程


接上文:
手把手带你从0手撸c语言编译器 挑战全网最硬核教程 从词法分析到语义分析全拆解 建议收藏 挑战全网最硬核教程 从词法分析到语义分析全拆解 耗时 200 小时肝出的编译器实战教程 含符号表 / 类型系统-优快云博客
 

第四章:从抽象到具体——中间代码生成与三地址码)

朋友们,欢迎回到我们“手撸编译器”的第四站!在第三章中,我们成功地为抽象语法树(AST)注入了“灵魂”,通过语义分析确保了代码的类型正确性和作用域可见性。现在,我们的AST已经是一个语义上完整且正确的程序表示了。

然而,这棵AST虽然强大,但它仍然是高级的、抽象的。它直接反映了源代码的结构,但离具体的机器指令还有一段距离。例如,一个复杂的表达式在AST中可能是一个深层嵌套的树结构,而机器指令通常是简单的、扁平的。

为了弥合这种“抽象”与“具体”之间的鸿沟,并为后续的代码优化和目标代码生成提供便利,编译器通常会引入一个中间表示(Intermediate Representation, IR)。这就是我们第四章的主题——中间代码生成(Intermediate Code Generation)

引言:为什么需要中间代码?

你可能会问,既然我们已经有了AST,为什么还需要一个中间代码呢?直接从AST生成机器码不行吗?答案是:可以,但效率低下且难以维护。中间代码带来了诸多好处:

  1. 独立于源语言和目标机器: 中间代码是一种通用的表示形式,它不依赖于特定的源语言(如C、Java)或特定的目标机器架构(如x86、ARM)。这意味着编译器的前端(词法、语法、语义分析)可以独立于后端(代码生成),从而实现模块化。我们可以为不同的源语言编写不同的前端,生成相同的中间代码;也可以为不同的目标机器编写不同的后端,处理相同的中间代码。这大大提高了编译器的可移植性可扩展性

  2. 便于代码优化: 在中间代码阶段进行优化比在AST或机器码阶段进行优化更为有效。

    • 在AST上优化过于抽象,难以发现底层的优化机会。

    • 在机器码上优化过于具体,受限于指令集,且优化效果可能不佳。

    • 中间代码提供了一个平衡点,它既足够抽象,可以进行高级优化(如循环优化、死代码消除),又足够具体,可以映射到机器指令。

  3. 简化后端设计: 有了中间代码,后端(代码生成器)只需要将中间代码翻译成目标机器码,而无需关心复杂的源语言语法结构。这使得后端的设计和实现变得更加简单和清晰。

简单来说,中间代码就像是建筑设计中的“施工图”:它比“效果图”(AST)更具体,但又比最终的“实物”(机器码)更通用,便于施工队(后端)理解和操作,也便于工程师(优化器)进行结构优化。

中间代码的种类

编译器领域有多种中间代码表示形式,每种都有其优缺点和适用场景:

  • 抽象语法树(AST): 虽然我们已经用它作为前端的输出,但它本身也可以被视为一种高层IR。

  • 逆波兰表示法(Postfix Notation): 也称为后缀表达式,是一种无括号的表达式表示,便于栈式机器求值。

  • 三地址码(Three-Address Code, TAC): 我们本章的重点。每条指令最多包含三个地址(两个操作数,一个结果)。它是一种线性的、类汇编的表示形式。

  • 控制流图(Control Flow Graph, CFG): 以图的形式表示程序的执行路径,节点是基本块(一系列顺序执行的指令),边表示控制流转移。

  • 静态单赋值形式(Static Single Assignment, SSA): 一种特殊的IR,要求每个变量只被赋值一次。这极大地简化了数据流分析和优化。

在本章中,为了实现上的简洁性和教学目的,我们将选择**三地址码(TAC)**作为我们的中间代码表示形式。

三地址码(Three-Address Code, TAC)

三地址码的特点是每条指令最多包含三个地址(变量或常量),形式通常为:result = operand1 op operand2

例如,C语言代码 x = y + z * 2; 可能会被翻译成以下三地址码:

t1 = z * 2
t2 = y + t1
x = t2

其中 t1, t2 是编译器引入的临时变量

三地址码的优点:

  • 简单: 每条指令都非常简单,易于理解和处理。

  • 扁平: 将复杂的表达式分解为一系列简单的操作,消除了嵌套。

  • 易于优化: 临时变量的使用使得数据流分析和优化(如公共子表达式消除、常量传播)变得容易。

  • 接近机器码: 它的形式与汇编语言指令相似,便于后续翻译成目标代码。

三地址码的设计

我们需要定义三地址码指令的结构。一个指令通常包含一个操作码(表示操作类型)和若干个操作数。

操作数(Operand)

操作数可以是:

  • 常量(Constant): 整数、浮点数、字符等字面量。

  • 变量(Variable): 程序中声明的变量。

  • 临时变量(Temporary Variable): 编译器在生成中间代码时引入的中间结果。

  • 标签(Label): 用于跳转指令的目标地址。

// intermediate_code.h (部分代码,完整版在后面)
#ifndef INTERMEDIATE_CODE_H
#define INTERMEDIATE_CODE_H

#include <stdlib.h> // For NULL
#include "type.h"   // 需要Type信息来描述操作数的类型

// 操作数的种类
typedef enum {
    OP_NONE,        // 无操作数
    OP_CONSTANT_INT,// 整数常量
    OP_CONSTANT_FLOAT, // 浮点数常量
    OP_VARIABLE,    // 变量 (包括全局变量、局部变量、参数)
    OP_TEMP_VAR,    // 临时变量
    OP_LABEL        // 标签
} OperandKind;

// 操作数结构体
typedef struct Operand {
    OperandKind kind; // 操作数种类
    Type* type;       // 操作数的类型 (语义分析阶段确定)
    union {
        long long int_val; // 整数常量的值
        double float_val;  // 浮点数常量的值
        char* var_name;    // 变量名 (对于OP_VARIABLE, OP_TEMP_VAR, OP_LABEL)
        int var_offset;    // 变量在栈帧或数据段中的偏移量 (语义分析阶段从符号表获取)
        int temp_id;       // 临时变量的唯一ID
        int label_id;      // 标签的唯一ID
    } val;
} Operand;

// 中间代码操作码
typedef enum {
    // 赋值
    IC_ASSIGN,      // result = op1

    // 算术运算
    IC_ADD,         // result = op1 + op2
    IC_SUB,         // result = op1 - op2
    IC_MUL,         // result = op1 * op2
    IC_DIV,         // result = op1 / op2
    IC_MOD,         // result = op1 % op2

    // 关系运算 (结果为0或1)
    IC_EQ,          // result = (op1 == op2)
    IC_NE,          // result = (op1 != op2)
    IC_LT,          // result = (op1 < op2)
    IC_LE,          // result = (op1 <= op2)
    IC_GT,          // result = (op1 > op2)
    IC_GE,          // result = (op1 >= op2)

    // 逻辑运算 (结果为0或1)
    IC_AND,         // result = (op1 && op2)
    IC_OR,          // result = (op1 || op2)
    IC_NOT,         // result = !op1

    // 位运算
    IC_BIT_AND,     // result = op1 & op2
    IC_BIT_OR,      // result = op1 | op2
    IC_BIT_XOR,     // result = op1 ^ op2
    IC_BIT_NOT,     // result = ~op1
    IC_LSHIFT,      // result = op1 << op2
    IC_RSHIFT,      // result = op1 >> op2

    // 跳转
    IC_LABEL,       // label:
    IC_JUMP,        // goto label
    IC_JUMPIF_TRUE, // if op1 goto label (如果op1为真则跳转)
    IC_JUMPIF_FALSE,// if op1 == 0 goto label (如果op1为假则跳转)

    // 函数调用
    IC_PARAM,       // push param (用于函数调用前推送参数)
    IC_CALL,        // result = call func, num_args (调用函数,结果存入result)
    IC_RETURN,      // return op1 (函数返回)
    IC_FUNC_BEGIN,  // func_name: (函数开始标记)
    IC_FUNC_END,    // end func_name (函数结束标记)

    // 类型转换 (简化:直接赋值时进行隐式转换,显式转换需要单独指令)
    IC_CAST,        // result = (target_type)op1

    // 其他 (自增自减等,可以分解为加减和赋值)
    // IC_INC,         // op1++
    // IC_DEC,         // op1--

} IntermediateCodeOp;

// 单条三地址码指令结构体
typedef struct Instruction {
    IntermediateCodeOp op; // 操作码
    Operand result;        // 结果操作数 (通常是变量或临时变量)
    Operand op1;           // 第一个操作数
    Operand op2;           // 第二个操作数
} Instruction;

// 中间代码列表 (通常是一个函数的所有指令)
typedef struct IntermediateCode {
    Instruction** instructions; // 指令数组
    int num_instructions;       // 当前指令数量
    int capacity;               // 数组容量
} IntermediateCode;

// --- Operand 创建函数 ---
Operand create_operand_int(long long val);
Operand create_operand_float(double val);
Operand create_operand_var(char* name, int offset, Type* type);
Operand create_operand_temp(int id, Type* type);
Operand create_operand_label(int id);
Operand create_operand_none(); // 用于没有操作数的情况

// --- Instruction 创建函数 ---
Instruction create_instruction(IntermediateCodeOp op, Operand result, Operand op1, Operand op2);

// --- IntermediateCode 管理函数 ---
IntermediateCode* init_intermediate_code();
void add_instruction(IntermediateCode* ic, Instruction instr);
void print_intermediate_code(IntermediateCode* ic);
void free_intermediate_code(IntermediateCode* ic);

// 辅助函数:将操作码转换为字符串
const char* ic_op_to_string(IntermediateCodeOp op);

#endif // INTERMEDIATE_CODE_H

从AST到中间代码的转换

中间代码生成器将深度优先遍历AST。对于AST中的每个表达式和语句,它都会生成一系列对应的三地址码指令。关键在于如何处理表达式和控制流。

表达式的转换

表达式的转换通常采用递归下降的方式。当遇到一个表达式节点时:

  1. 递归生成子表达式的中间代码: 首先递归地生成左右子表达式(如果存在)的中间代码。

  2. 使用临时变量: 子表达式的结果通常会存储在一个临时变量中。这个临时变量会作为当前表达式操作的输入。

  3. 生成当前操作的指令: 根据当前表达式的操作符,生成对应的三地址码指令,将子表达式的临时变量作为操作数,并将结果存储到新的临时变量中。

例如,对于 a + b * c

  • 首先,递归处理 b * c

    • 生成 t1 = b * c

  • 然后,处理 a + t1

    • 生成 t2 = a + t1

最终,a + b * c 的结果存储在 t2 中。

语句的转换

语句的转换相对直接:

  • 变量声明: 变量的内存分配已经在语义分析阶段通过符号表中的offset确定。这里通常不需要生成显式的指令,除非有初始化表达式。

  • 赋值语句: 生成 target = value 形式的 IC_ASSIGN 指令。

  • 表达式语句: 递归生成其内部表达式的中间代码。

  • 复合语句(代码块): 递归遍历其内部的所有语句。

  • if 语句:

    1. 生成条件表达式的中间代码,结果存入临时变量 t_cond

    2. 生成 IC_JUMPIF_FALSE t_cond, L_ELSE (如果条件为假,跳到else分支)。

    3. 生成 then 分支的中间代码。

    4. 生成 IC_JUMP L_ENDIF (跳过else分支)。

    5. 生成 IC_LABEL L_ELSE

    6. 生成 else 分支的中间代码(如果存在)。

    7. 生成 IC_LABEL L_ENDIF

    • 这里需要引入**标签(Label)**来标记跳转的目标地址。

  • while 循环:

    1. 生成 IC_LABEL L_LOOP_START

    2. 生成条件表达式的中间代码,结果存入临时变量 t_cond

    3. 生成 IC_JUMPIF_FALSE t_cond, L_LOOP_END (如果条件为假,跳出循环)。

    4. 生成循环体的中间代码。

    5. 生成 IC_JUMP L_LOOP_START (跳回循环开始)。

    6. 生成 IC_LABEL L_LOOP_END

  • 函数定义:

    1. 生成 IC_FUNC_BEGIN func_name

    2. 为函数参数生成 IC_PARAM 指令(或在代码生成时处理)。

    3. 递归生成函数体的中间代码。

    4. 生成 IC_FUNC_END func_name

  • return 语句: 生成 IC_RETURN value 指令。

中间代码生成器(Intermediate Code Generator)的实现

我们需要一个结构体来管理中间代码生成的状态,例如临时变量的计数器和标签的计数器。

// ic_generator.h (部分代码,完整版在后面)
#ifndef IC_GENERATOR_H
#define IC_GENERATOR_H

#include "ast.h"
#include "intermediate_code.h"
#include "symbol_table.h" // 需要符号表来获取变量信息

// 中间代码生成器结构体
typedef struct {
    IntermediateCode* current_ic; // 当前正在生成的中间代码列表
    int temp_counter;             // 临时变量计数器,用于生成唯一ID
    int label_counter;            // 标签计数器,用于生成唯一ID
    SymbolTable* sym_table;       // 符号表,用于查找变量信息 (偏移量等)
} ICG;

// 函数声明
ICG* init_icg(SymbolTable* sym_table);
void free_icg(ICG* icg);

// 生成新的临时变量Operand
Operand new_temp_operand(ICG* icg, Type* type);
// 生成新的标签Operand
Operand new_label_operand(ICG* icg);

// 核心生成函数:遍历AST并生成中间代码
IntermediateCode* generate_program_ic(ASTNode* program_node, ICG* icg);
void generate_function_ic(ASTNode* func_def_node, ICG* icg);
void generate_statement_ic(ASTNode* stmt_node, ICG* icg);
Operand generate_expression_ic(ASTNode* expr_node, ICG* icg);

#endif // IC_GENERATOR_H

完整的代码实现

intermediate_code.h

// intermediate_code.h
#ifndef INTERMEDIATE_CODE_H
#define INTERMEDIATE_CODE_H

#include <stdlib.h> // For NULL
#include "type.h"   // 需要Type信息来描述操作数的类型

// 操作数的种类
// 定义了操作数可以是哪种类型的数据或引用
typedef enum {
    OP_NONE,        // 无操作数,用于某些不需要操作数的指令
    OP_CONSTANT_INT,// 整数常量,例如 10, 0xFF
    OP_CONSTANT_FLOAT, // 浮点数常量,例如 3.14, 1.0e-5
    OP_VARIABLE,    // 变量,包括全局变量、局部变量、函数参数
    OP_TEMP_VAR,    // 临时变量,编译器在生成中间代码时创建的中间结果
    OP_LABEL        // 标签,用于跳转指令的目标地址
} OperandKind;

// 操作数结构体
// 描述一个三地址码操作数的所有信息
typedef struct Operand {
    OperandKind kind; // 操作数的种类
    Type* type;       // 操作数的类型 (由语义分析阶段确定并传递过来)
    union {           // 根据操作数种类存储不同的值
        long long int_val; // 如果是整数常量,存储其值
        double float_val;  // 如果是浮点数常量,存储其值
        char* var_name;    // 如果是变量或标签,存储其名称 (用于调试和可读性)
        int var_offset;    // 如果是变量,存储其在栈帧或数据段中的偏移量
        int temp_id;       // 如果是临时变量,存储其唯一ID
        int label_id;      // 如果是标签,存储其唯一ID
    } val;
} Operand;

// 中间代码操作码
// 定义了三地址码支持的各种操作类型
typedef enum {
    // 赋值操作
    IC_ASSIGN,      // result = op1 (简单的值传递)

    // 算术运算
    IC_ADD,         // result = op1 + op2
    IC_SUB,         // result = op1 - op2
    IC_MUL,         // result = op1 * op2
    IC_DIV,         // result = op1 / op2
    IC_MOD,         // result = op1 % op2 (仅限整数)

    // 关系运算 (结果通常为布尔值,这里用int 0/1表示)
    IC_EQ,          // result = (op1 == op2)
    IC_NE,          // result = (op1 != op2)
    IC_LT,          // result = (op1 < op2)
    IC_LE,          // result = (op1 <= op2)
    IC_GT,          // result = (op1 > op2)
    IC_GE,          // result = (op1 >= op2)

    // 逻辑运算 (结果为int 0/1)
    IC_AND,         // result = (op1 && op2)
    IC_OR,          // result = (op1 || op2)
    IC_NOT,         // result = !op1

    // 位运算 (仅限整数)
    IC_BIT_AND,     // result = op1 & op2
    IC_BIT_OR,      // result = op1 | op2
    IC_BIT_XOR,     // result = op1 ^ op2
    IC_BIT_NOT,     // result = ~op1
    IC_LSHIFT,      // result = op1 << op2
    IC_RSHIFT,      // result = op1 >> op2

    // 控制流跳转
    IC_LABEL,       // label L_id: (定义一个标签)
    IC_JUMP,        // goto L_id (无条件跳转)
    IC_JUMPIF_TRUE, // if op1 goto L_id (如果op1为真则跳转)
    IC_JUMPIF_FALSE,// if op1 == 0 goto L_id (如果op1为假则跳转)

    // 函数调用和返回
    IC_PARAM,       // param op1 (将op1作为参数压入栈,用于函数调用前)
    IC_CALL,        // result = call func_name, num_args (调用函数,结果存入result)
    IC_RETURN,      // return op1 (函数返回,返回值为op1)
    IC_FUNC_BEGIN,  // func_name: (函数开始标记,通常在函数入口处)
    IC_FUNC_END,    // end func_name (函数结束标记,通常在函数出口处)

    // 类型转换 (显式类型转换,例如 (int)float_var)
    IC_CAST,        // result = (target_type)op1

    // 内存操作 (TODO: 未来扩展,例如数组访问、指针解引用)
    // IC_LOAD,        // result = *op1 (从地址op1加载值)
    // IC_STORE,       // *op1 = op2 (将op2存储到地址op1)

} IntermediateCodeOp;

// 单条三地址码指令结构体
// 每条指令最多包含一个结果和两个操作数
typedef struct Instruction {
    IntermediateCodeOp op; // 操作码
    Operand result;        // 结果操作数 (例如赋值操作的左值,函数调用的返回值接收者)
    Operand op1;           // 第一个操作数
    Operand op2;           // 第二个操作数
} Instruction;

// 中间代码列表结构体
// 用于存储一个函数或整个程序的所有三地址码指令
typedef struct IntermediateCode {
    Instruction** instructions; // 指令指针数组
    int num_instructions;       // 当前指令数量
    int capacity;               // 数组的当前容量
} IntermediateCode;

// --- Operand 创建函数 ---

/**
 * @brief 创建一个整数常量操作数。
 * @param val 整数值。
 * @return 创建的Operand结构体。
 */
Operand create_operand_int(long long val);

/**
 * @brief 创建一个浮点数常量操作数。
 * @param val 浮点数值。
 * @return 创建的Operand结构体。
 */
Operand create_operand_float(double val);

/**
 * @brief 创建一个变量操作数。
 * @param name 变量名称。
 * @param offset 变量在内存中的偏移量。
 * @param type 变量类型。
 * @return 创建的Operand结构体。
 */
Operand create_operand_var(char* name, int offset, Type* type);

/**
 * @brief 创建一个临时变量操作数。
 * @param id 临时变量的唯一ID。
 * @param type 临时变量的类型。
 * @return 创建的Operand结构体。
 */
Operand create_operand_temp(int id, Type* type);

/**
 * @brief 创建一个标签操作数。
 * @param id 标签的唯一ID。
 * @return 创建的Operand结构体。
 */
Operand create_operand_label(int id);

/**
 * @brief 创建一个空操作数 (无操作数)。
 * @return 创建的Operand结构体。
 */
Operand create_operand_none();

// --- Instruction 创建函数 ---

/**
 * @brief 创建一个三地址码指令。
 * @param op 操作码。
 * @param result 结果操作数。
 * @param op1 第一个操作数。
 * @param op2 第二个操作数。
 * @return 创建的Instruction结构体。
 */
Instruction create_instruction(IntermediateCodeOp op, Operand result, Operand op1, Operand op2);

// --- IntermediateCode 管理函数 ---

/**
 * @brief 初始化一个 IntermediateCode 结构体。
 * @return 指向新创建的IntermediateCode结构体的指针。
 */
IntermediateCode* init_intermediate_code();

/**
 * @brief 向 IntermediateCode 列表中添加一条指令。
 * 如果容量不足,会自动扩容。
 * @param ic 指向 IntermediateCode 结构体的指针。
 * @param instr 要添加的指令。
 */
void add_instruction(IntermediateCode* ic, Instruction instr);

/**
 * @brief 打印 IntermediateCode 列表中的所有指令 (用于调试)。
 * @param ic 指向 IntermediateCode 结构体的指针。
 */
void print_intermediate_code(IntermediateCode* ic);

/**
 * @brief 释放 IntermediateCode 结构体及其所有指令占用的内存。
 * @param ic 指向要释放的IntermediateCode结构体的指针。
 */
void free_intermediate_code(IntermediateCode* ic);

// 辅助函数:将操作码转换为可读的字符串
const char* ic_op_to_string(IntermediateCodeOp op);

#endif // INTERMEDIATE_CODE_H

intermediate_code.c

// intermediate_code.c
#include "intermediate_code.h"
#include <stdio.h>  // For fprintf, printf
#include <stdlib.h> // For malloc, free, exit
#include <string.h> // For strdup, strcmp

// 初始指令数组容量
#define INITIAL_IC_CAPACITY 64

// --- Operand 创建函数的实现 ---

Operand create_operand_int(long long val) {
    Operand op;
    op.kind = OP_CONSTANT_INT;
    op.type = type_int; // 整数常量类型为 int
    op.val.int_val = val;
    return op;
}

Operand create_operand_float(double val) {
    Operand op;
    op.kind = OP_CONSTANT_FLOAT;
    op.type = type_float; // 浮点数常量类型为 float
    op.val.float_val = val;
    return op;
}

Operand create_operand_var(char* name, int offset, Type* type) {
    Operand op;
    op.kind = OP_VARIABLE;
    op.type = type;
    op.val.var_name = strdup(name); // 复制变量名
    op.val.var_offset = offset;
    return op;
}

Operand create_operand_temp(int id, Type* type) {
    Operand op;
    op.kind = OP_TEMP_VAR;
    op.type = type;
    op.val.temp_id = id;
    return op;
}

Operand create_operand_label(int id) {
    Operand op;
    op.kind = OP_LABEL;
    op.type = type_void; // 标签没有具体类型
    op.val.label_id = id;
    return op;
}

Operand create_operand_none() {
    Operand op;
    op.kind = OP_NONE;
    op.type = type_void; // 无类型
    // 不需要初始化val
    return op;
}

// --- Instruction 创建函数的实现 ---

Instruction create_instruction(IntermediateCodeOp op, Operand result, Operand op1, Operand op2) {
    Instruction instr;
    instr.op = op;
    instr.result = result;
    instr.op1 = op1;
    instr.op2 = op2;
    return instr;
}

// --- IntermediateCode 管理函数的实现 ---

IntermediateCode* init_intermediate_code() {
    IntermediateCode* ic = (IntermediateCode*)malloc(sizeof(IntermediateCode));
    if (!ic) {
        fprintf(stderr, "错误: 内存分配失败 (IntermediateCode)\n");
        exit(EXIT_FAILURE);
    }
    ic->instructions = (Instruction**)malloc(INITIAL_IC_CAPACITY * sizeof(Instruction*));
    if (!ic->instructions) {
        fprintf(stderr, "错误: 内存分配失败 (IntermediateCode instructions)\n");
        free(ic);
        exit(EXIT_FAILURE);
    }
    ic->num_instructions = 0;
    ic->capacity = INITIAL_IC_CAPACITY;
    return ic;
}

void add_instruction(IntermediateCode* ic, Instruction instr) {
    // 检查容量,如果不足则扩容
    if (ic->num_instructions >= ic->capacity) {
        ic->capacity *= 2; // 容量翻倍
        ic->instructions = (Instruction**)realloc(ic->instructions, ic->capacity * sizeof(Instruction*));
        if (!ic->instructions) {
            fprintf(stderr, "错误: 内存重新分配失败 (IntermediateCode instructions)\n");
            exit(EXIT_FAILURE);
        }
    }
    // 为新指令分配内存并复制内容
    Instruction* new_instr = (Instruction*)malloc(sizeof(Instruction));
    if (!new_instr) {
        fprintf(stderr, "错误: 内存分配失败 (Instruction)\n");
        exit(EXIT_FAILURE);
    }
    *new_instr = instr; // 复制整个结构体

    // 特殊处理需要复制字符串的Operand
    if (new_instr->result.kind == OP_VARIABLE && new_instr->result.val.var_name) {
        new_instr->result.val.var_name = strdup(new_instr->result.val.var_name);
    }
    if (new_instr->op1.kind == OP_VARIABLE && new_instr->op1.val.var_name) {
        new_instr->op1.val.var_name = strdup(new_instr->op1.val.var_name);
    }
    if (new_instr->op2.kind == OP_VARIABLE && new_instr->op2.val.var_name) {
        new_instr->op2.val.var_name = strdup(new_instr->op2.val.var_name);
    }
    if (new_instr->op == IC_CALL && new_instr->op1.kind == OP_VARIABLE && new_instr->op1.val.var_name) {
        new_instr->op1.val.var_name = strdup(new_instr->op1.val.var_name); // 函数名也需要复制
    }

    ic->instructions[ic->num_instructions++] = new_instr;
}

// 辅助函数:释放Operand中动态分配的内存 (主要是var_name)
static void free_operand_data(Operand* op) {
    if (op->kind == OP_VARIABLE && op->val.var_name) {
        free(op->val.var_name);
        op->val.var_name = NULL;
    }
    // 对于OP_TEMP_VAR, OP_LABEL, OP_CONSTANT_*, OP_NONE,没有动态分配的内存
}

void free_intermediate_code(IntermediateCode* ic) {
    if (ic) {
        if (ic->instructions) {
            for (int i = 0; i < ic->num_instructions; ++i) {
                if (ic->instructions[i]) {
                    // 释放指令内部可能动态分配的字符串
                    free_operand_data(&ic->instructions[i]->result);
                    free_operand_data(&ic->instructions[i]->op1);
                    free_operand_data(&ic->instructions[i]->op2);
                    free(ic->instructions[i]);
                }
            }
            free(ic->instructions);
        }
        free(ic);
    }
}

// 辅助函数:打印单个操作数
static void print_operand(Operand op) {
    switch (op.kind) {
        case OP_NONE:
            printf(" ");
            break;
        case OP_CONSTANT_INT:
            printf("%lld", op.val.int_val);
            break;
        case OP_CONSTANT_FLOAT:
            printf("%f", op.val.float_val);
            break;
        case OP_VARIABLE:
            printf("%s", op.val.var_name);
            // printf("%s (offset: %d)", op.val.var_name, op.val.var_offset); // 调试时显示偏移量
            break;
        case OP_TEMP_VAR:
            printf("t%d", op.val.temp_id);
            break;
        case OP_LABEL:
            printf("L%d", op.val.label_id);
            break;
        default:
            printf("UNKNOWN_OP");
            break;
    }
    // 可以选择打印类型信息
    // if (op.type && op.type->kind != TYPE_VOID) {
    //     printf(":%s", type_kind_to_string(op.type->kind));
    // }
}

// 将操作码转换为可读的字符串
const char* ic_op_to_string(IntermediateCodeOp op) {
    switch (op) {
        case IC_ASSIGN: return "ASSIGN";
        case IC_ADD: return "ADD";
        case IC_SUB: return "SUB";
        case IC_MUL: return "MUL";
        case IC_DIV: return "DIV";
        case IC_MOD: return "MOD";
        case IC_EQ: return "EQ";
        case IC_NE: return "NE";
        case IC_LT: return "LT";
        case IC_LE: return "LE";
        case IC_GT: return "GT";
        case IC_GE: return "GE";
        case IC_AND: return "AND";
        case IC_OR: return "OR";
        case IC_NOT: return "NOT";
        case IC_BIT_AND: return "BIT_AND";
        case IC_BIT_OR: return "BIT_OR";
        case IC_BIT_XOR: return "BIT_XOR";
        case IC_BIT_NOT: return "BIT_NOT";
        case IC_LSHIFT: return "LSHIFT";
        case IC_RSHIFT: return "RSHIFT";
        case IC_LABEL: return "LABEL";
        case IC_JUMP: return "JUMP";
        case IC_JUMPIF_TRUE: return "JUMPIF_TRUE";
        case IC_JUMPIF_FALSE: return "JUMPIF_FALSE";
        case IC_PARAM: return "PARAM";
        case IC_CALL: return "CALL";
        case IC_RETURN: return "RETURN";
        case IC_FUNC_BEGIN: return "FUNC_BEGIN";
        case IC_FUNC_END: return "FUNC_END";
        case IC_CAST: return "CAST";
        default: return "UNKNOWN_IC_OP";
    }
}

// 打印 IntermediateCode 列表中的所有指令
void print_intermediate_code(IntermediateCode* ic) {
    if (!ic) {
        printf("中间代码为空。\n");
        return;
    }
    for (int i = 0; i < ic->num_instructions; ++i) {
        Instruction* instr = ic->instructions[i];
        if (!instr) continue;

        printf("%4d: ", i); // 打印指令序号

        switch (instr->op) {
            case IC_LABEL:
                printf("L%d:\n", instr->result.val.label_id); // 标签单独一行
                continue; // 不打印其他操作数
            case IC_JUMP:
                printf("  JUMP "); print_operand(instr->result); printf("\n");
                break;
            case IC_JUMPIF_TRUE:
                printf("  JUMPIF_TRUE "); print_operand(instr->op1); printf(", "); print_operand(instr->result); printf("\n");
                break;
            case IC_JUMPIF_FALSE:
                printf("  JUMPIF_FALSE "); print_operand(instr->op1); printf(", "); print_operand(instr->result); printf("\n");
                break;
            case IC_PARAM:
                printf("  PARAM "); print_operand(instr->op1); printf("\n");
                break;
            case IC_CALL:
                if (instr->result.kind != OP_NONE) {
                    print_operand(instr->result); printf(" = ");
                }
                printf("CALL "); print_operand(instr->op1); printf(", %d\n", instr->op2.val.int_val); // op2.val.int_val 存储参数数量
                break;
            case IC_RETURN:
                printf("  RETURN "); print_operand(instr->op1); printf("\n");
                break;
            case IC_FUNC_BEGIN:
                printf("\nFUNC_BEGIN %s:\n", instr->op1.val.var_name);
                break;
            case IC_FUNC_END:
                printf("FUNC_END %s\n\n", instr->op1.val.var_name);
                break;
            case IC_ASSIGN:
                print_operand(instr->result); printf(" = "); print_operand(instr->op1); printf("\n");
                break;
            case IC_NOT:
            case IC_BIT_NOT:
                print_operand(instr->result); printf(" = %s ", ic_op_to_string(instr->op)); print_operand(instr->op1); printf("\n");
                break;
            case IC_CAST:
                print_operand(instr->result); printf(" = (%s)", type_kind_to_string(instr->result.type->kind)); print_operand(instr->op1); printf("\n");
                break;
            default: // 大多数二元操作
                print_operand(instr->result); printf(" = "); print_operand(instr->op1);
                printf(" %s ", ic_op_to_string(instr->op)); print_operand(instr->op2); printf("\n");
                break;
        }
    }
}

ic_generator.h

// ic_generator.h
#ifndef IC_GENERATOR_H
#define IC_GENERATOR_H

#include "ast.h"           // 需要AST节点定义
#include "intermediate_code.h" // 需要中间代码结构体
#include "symbol_table.h"  // 需要符号表来获取变量信息

// 中间代码生成器结构体
// 包含了中间代码生成过程中所需的所有状态和工具
typedef struct {
    IntermediateCode* current_ic; // 当前正在生成的中间代码列表 (通常是一个函数的所有指令)
    int temp_counter;             // 临时变量计数器,用于生成唯一的临时变量ID (t0, t1, ...)
    int label_counter;            // 标签计数器,用于生成唯一的标签ID (L0, L1, ...)
    SymbolTable* sym_table;       // 符号表,用于查找变量信息 (如偏移量),由语义分析器提供
} ICG;

// 函数声明

/**
 * @brief 初始化中间代码生成器。
 * @param sym_table 语义分析阶段构建的符号表,用于查询符号信息。
 * @return 成功返回指向 ICG 结构体的指针,失败返回NULL。
 */
ICG* init_icg(SymbolTable* sym_table);

/**
 * @brief 释放中间代码生成器占用的所有资源。
 * 包括其内部的 IntermediateCode 列表。
 * @param icg 指向要释放的 ICG 结构体的指针。
 */
void free_icg(ICG* icg);

/**
 * @brief 生成一个新的临时变量操作数。
 * 每次调用都会返回一个具有唯一ID的临时变量。
 * @param icg 指向 ICG 结构体的指针。
 * @param type 临时变量的类型。
 * @return 新创建的临时变量Operand。
 */
Operand new_temp_operand(ICG* icg, Type* type);

/**
 * @brief 生成一个新的标签操作数。
 * 每次调用都会返回一个具有唯一ID的标签。
 * @param icg 指向 ICG 结构体的指针。
 * @return 新创建的标签Operand。
 */
Operand new_label_operand(ICG* icg);

// 核心生成函数:递归遍历AST并生成中间代码

/**
 * @brief 生成整个程序的中间代码。
 * 遍历AST的根节点,为每个函数定义生成中间代码。
 * @param program_node 程序根AST节点。
 * @param icg 中间代码生成器实例。
 * @return 包含整个程序所有中间代码指令的 IntermediateCode 结构体指针。
 */
IntermediateCode* generate_program_ic(ASTNode* program_node, ICG* icg);

/**
 * @brief 生成函数定义的中间代码。
 * 处理函数入口、参数、函数体和函数出口。
 * @param func_def_node 函数定义AST节点。
 * @param icg 中间代码生成器实例。
 */
void generate_function_ic(ASTNode* func_def_node, ICG* icg);

/**
 * @brief 生成语句的中间代码。
 * 根据语句类型分派到不同的处理函数,例如复合语句、表达式语句、if、while等。
 * @param stmt_node 语句AST节点。
 * @param icg 中间代码生成器实例。
 */
void generate_statement_ic(ASTNode* stmt_node, ICG* icg);

/**
 * @brief 生成表达式的中间代码。
 * 递归分析表达式,生成其对应的三地址码指令,并返回表示表达式结果的Operand。
 * @param expr_node 表达式AST节点。
 * @param icg 中间代码生成器实例。
 * @return 表示表达式结果的Operand。
 */
Operand generate_expression_ic(ASTNode* expr_node, ICG* icg);

#endif // IC_GENERATOR_H

ic_generator.c

// ic_generator.c
#include "ic_generator.h"
#include <stdio.h>  // For fprintf
#include <stdlib.h> // For malloc, free, exit
#include <string.h> // For strcmp

// --- ICG 初始化与清理 ---

ICG* init_icg(SymbolTable* sym_table) {
    ICG* icg = (ICG*)malloc(sizeof(ICG));
    if (!icg) {
        fprintf(stderr, "错误: 内存分配失败 (ICG)\n");
        return NULL;
    }
    icg->current_ic = NULL; // 初始时没有当前中间代码列表,每个函数会创建自己的
    icg->temp_counter = 0;   // 临时变量ID从0开始
    icg->label_counter = 0;  // 标签ID从0开始
    icg->sym_table = sym_table; // 引用语义分析器生成的符号表

    fprintf(stderr, "中间代码生成器初始化成功。\n");
    return icg;
}

void free_icg(ICG* icg) {
    if (icg) {
        // icg->current_ic 会在 generate_program_ic 中被赋值和管理,
        // 并在那里被返回给调用者,最终由调用者 free。
        // 所以这里不需要 free icg->current_ic
        free(icg);
        fprintf(stderr, "中间代码生成器资源已清理。\n");
    }
}

// --- Operand 生成辅助函数 ---

Operand new_temp_operand(ICG* icg, Type* type) {
    return create_operand_temp(icg->temp_counter++, type);
}

Operand new_label_operand(ICG* icg) {
    return create_operand_label(icg->label_counter++);
}

// --- 核心中间代码生成逻辑 ---

// 将AST操作符类型映射到中间代码操作码
static IntermediateCodeOp map_binary_op(TokenType op_type) {
    switch (op_type) {
        case TOKEN_OP_PLUS: return IC_ADD;
        case TOKEN_OP_MINUS: return IC_SUB;
        case TOKEN_OP_MULTIPLY: return IC_MUL;
        case TOKEN_OP_DIVIDE: return IC_DIV;
        case TOKEN_OP_MODULO: return IC_MOD;
        case TOKEN_OP_EQ: return IC_EQ;
        case TOKEN_OP_NE: return IC_NE;
        case TOKEN_OP_LT: return IC_LT;
        case TOKEN_OP_LE: return IC_LE;
        case TOKEN_OP_GT: return IC_GT;
        case TOKEN_OP_GE: return IC_GE;
        case TOKEN_OP_AND: return IC_AND;
        case TOKEN_OP_OR: return IC_OR;
        case TOKEN_OP_BIT_AND: return IC_BIT_AND;
        case TOKEN_OP_BIT_OR: return IC_BIT_OR;
        case TOKEN_OP_BIT_XOR: return IC_BIT_XOR;
        case TOKEN_OP_LSHIFT: return IC_LSHIFT;
        case TOKEN_OP_RSHIFT: return IC_RSHIFT;
        default: return -1; // 未知操作
    }
}

static IntermediateCodeOp map_unary_op(TokenType op_type) {
    switch (op_type) {
        case TOKEN_OP_PLUS: return IC_ASSIGN; // 一元加,直接赋值即可
        case TOKEN_OP_MINUS: return IC_SUB;   // 一元减,可以看作 0 - op1
        case TOKEN_OP_NOT: return IC_NOT;
        case TOKEN_OP_BIT_NOT: return IC_BIT_NOT;
        // TODO: 自增自减 (IC_INC, IC_DEC) 需要特殊处理,分解为加减和赋值
        // TODO: 取地址 & 和解引用 * 需要 IC_LOAD / IC_STORE 或其他内存操作指令
        default: return -1;
    }
}

// 生成表达式的中间代码
// 返回一个Operand,表示表达式计算的结果 (可能是常量、变量或临时变量)
Operand generate_expression_ic(ASTNode* expr_node, ICG* icg) {
    if (!expr_node) return create_operand_none();

    Operand result_op = create_operand_none(); // 默认结果

    switch (expr_node->type) {
        case AST_INTEGER_LITERAL_EXPR:
            result_op = create_operand_int(expr_node->data.integer_literal_expr.value);
            break;
        case AST_FLOAT_LITERAL_EXPR:
            result_op = create_operand_float(expr_node->data.float_literal_expr.value);
            break;
        case AST_CHAR_LITERAL_EXPR:
            result_op = create_operand_int(expr_node->data.char_literal_expr.value); // char 提升为 int
            break;
        case AST_STRING_LITERAL_EXPR:
            // 字符串字面量通常在数据段,这里简化为返回一个表示其地址的临时变量或特殊操作数
            // 暂时不处理,返回一个无效操作数
            fprintf(stderr, "警告: 字符串字面量暂不支持中间代码生成。\n");
            result_op = create_operand_none();
            break;
        case AST_IDENTIFIER_EXPR: {
            // 标识符表达式的结果就是它本身
            Symbol* sym = expr_node->data.identifier_expr.symbol_entry; // 语义分析阶段已填充
            if (!sym) {
                fprintf(stderr, "错误: 标识符 '%s' 未在符号表中找到,无法生成中间代码。\n", expr_node->data.identifier_expr.name);
                result_op = create_operand_none();
            } else {
                result_op = create_operand_var(sym->name, sym->offset, sym->type);
            }
            break;
        }
        case AST_BINARY_EXPR: {
            Operand left_op = generate_expression_ic(expr_node->data.binary_expr.left, icg);
            Operand right_op = generate_expression_ic(expr_node->data.binary_expr.right, icg);

            // 确保子表达式有有效类型
            if (left_op.type == type_unknown || right_op.type == type_unknown) {
                return create_operand_none();
            }

            // 获取公共类型作为结果类型
            Type* result_type = get_common_type(left_op.type, right_op.type);
            if (result_type == type_unknown) {
                fprintf(stderr, "错误: 二元运算操作数类型不兼容,无法生成中间代码。\n");
                return create_operand_none();
            }

            // 创建一个临时变量来存储运算结果
            Operand temp_result = new_temp_operand(icg, result_type);
            
            // 生成对应的三地址码指令
            IntermediateCodeOp op = map_binary_op(expr_node->data.binary_expr.op);
            if (op != -1) {
                add_instruction(icg->current_ic, create_instruction(op, temp_result, left_op, right_op));
                result_op = temp_result;
            } else {
                fprintf(stderr, "错误: 不支持的二元运算符 '%s',无法生成中间代码。\n", token_type_to_string(expr_node->data.binary_expr.op));
                result_op = create_operand_none();
            }
            break;
        }
        case AST_UNARY_EXPR: {
            Operand operand_op = generate_expression_ic(expr_node->data.unary_expr.operand, icg);
            if (operand_op.type == type_unknown) {
                return create_operand_none();
            }

            Type* result_type = operand_op.type; // 默认结果类型与操作数相同
            if (expr_node->data.unary_expr.op == TOKEN_OP_NOT) { // 逻辑非结果是int
                result_type = type_int;
            }

            Operand temp_result = new_temp_operand(icg, result_type);

            IntermediateCodeOp op = map_unary_op(expr_node->data.unary_expr.op);
            if (op != -1) {
                if (op == IC_SUB && expr_node->data.unary_expr.op == TOKEN_OP_MINUS) { // 特殊处理一元减
                    // result = 0 - operand
                    add_instruction(icg->current_ic, create_instruction(IC_SUB, temp_result, create_operand_int(0), operand_op));
                } else if (op == IC_ASSIGN && expr_node->data.unary_expr.op == TOKEN_OP_PLUS) { // 特殊处理一元加
                    // result = operand
                    add_instruction(icg->current_ic, create_instruction(IC_ASSIGN, temp_result, operand_op, create_operand_none()));
                } else {
                    add_instruction(icg->current_ic, create_instruction(op, temp_result, operand_op, create_operand_none()));
                }
                result_op = temp_result;
            } else {
                // TODO: 处理自增自减,例如 x++ -> t = x; x = x + 1; result = t;
                fprintf(stderr, "错误: 不支持的一元运算符 '%s',无法生成中间代码。\n", token_type_to_string(expr_node->data.unary_expr.op));
                result_op = create_operand_none();
            }
            break;
        }
        case AST_ASSIGN_EXPR: {
            // 赋值操作的左侧必须是可赋值的(L-value),例如变量
            if (expr_node->data.assign_expr.target->type != AST_IDENTIFIER_EXPR) {
                fprintf(stderr, "错误: 赋值目标不是标识符,无法生成中间代码。\n");
                return create_operand_none();
            }

            Operand target_op = generate_expression_ic(expr_node->data.assign_expr.target, icg);
            Operand value_op = generate_expression_ic(expr_node->data.assign_expr.value, icg);

            // 检查类型兼容性 (语义分析阶段已检查,这里再次确认)
            if (!is_assignable(target_op.type, value_op.type)) {
                fprintf(stderr, "错误: 赋值类型不兼容,无法生成中间代码。\n");
                return create_operand_none();
            }
            
            // TODO: 处理复合赋值运算符 (+=, -= 等),分解为二元运算和赋值
            // 例如: a += b  ->  t = a + b; a = t;
            // 目前只处理简单的赋值 =
            add_instruction(icg->current_ic, create_instruction(IC_ASSIGN, target_op, value_op, create_operand_none()));
            result_op = target_op; // 赋值表达式的结果是赋值目标的类型和值
            break;
        }
        case AST_CALL_EXPR: {
            // 1. 生成参数的中间代码并添加 PARAM 指令
            for (int i = 0; i < expr_node->data.call_expr.num_args; ++i) {
                Operand arg_op = generate_expression_ic(expr_node->data.call_expr.args[i], icg);
                add_instruction(icg->current_ic, create_instruction(IC_PARAM, create_operand_none(), arg_op, create_operand_none()));
            }

            // 2. 获取被调用函数的信息
            ASTNode* callee_node = expr_node->data.call_expr.callee;
            Symbol* func_sym = callee_node->data.identifier_expr.symbol_entry; // 语义分析阶段已填充
            if (!func_sym || func_sym->kind != SYM_FUNC) {
                fprintf(stderr, "错误: '%s' 不是一个函数,无法生成中间代码。\n", callee_node->data.identifier_expr.name);
                return create_operand_none();
            }
            Type* func_type = func_sym->type; // 函数的类型 (TYPE_FUNCTION)

            // 3. 创建临时变量接收函数返回值
            Operand temp_result = create_operand_none();
            if (func_type->return_type->kind != TYPE_VOID) {
                temp_result = new_temp_operand(icg, func_type->return_type);
            }
            
            // 4. 生成 CALL 指令
            // op1 存储函数名,op2.val.int_val 存储参数数量
            add_instruction(icg->current_ic, create_instruction(IC_CALL, 
                                                                temp_result, 
                                                                create_operand_var(func_sym->name, 0, func_type), // 函数名作为操作数
                                                                create_operand_int(expr_node->data.call_expr.num_args)));
            result_op = temp_result;
            break;
        }
        default:
            fprintf(stderr, "错误: 未知或不支持的AST表达式类型用于中间代码生成。\n");
            result_op = create_operand_none();
            break;
    }
    return result_op;
}

// 生成语句的中间代码
void generate_statement_ic(ASTNode* stmt_node, ICG* icg) {
    if (!stmt_node) return;

    switch (stmt_node->type) {
        case AST_COMPOUND_STMT: {
            // 复合语句只是简单地遍历其内部的所有语句
            for (int i = 0; i < stmt_node->data.compound_stmt.num_statements; ++i) {
                ASTNode* child_node = stmt_node->data.compound_stmt.statements[i];
                if (child_node->type == AST_VAR_DECL) {
                    // 局部变量声明:如果带初始化,则生成赋值指令
                    if (child_node->data.var_decl.initializer) {
                        Operand var_op = create_operand_var(child_node->data.var_decl.identifier->data.identifier_expr.symbol_entry->name,
                                                            child_node->data.var_decl.identifier->data.identifier_expr.symbol_entry->offset,
                                                            child_node->data.var_decl.identifier->resolved_type);
                        Operand init_op = generate_expression_ic(child_node->data.var_decl.initializer, icg);
                        add_instruction(icg->current_ic, create_instruction(IC_ASSIGN, var_op, init_op, create_operand_none()));
                    }
                } else {
                    generate_statement_ic(child_node, icg); // 递归生成子语句的中间代码
                }
            }
            break;
        }
        case AST_EXPR_STMT:
            if (stmt_node->data.expr_stmt.expression) {
                generate_expression_ic(stmt_node->data.expr_stmt.expression, icg);
            }
            break;
        case AST_RETURN_STMT: {
            Operand return_val_op = create_operand_none();
            if (stmt_node->data.return_stmt.expression) {
                return_val_op = generate_expression_ic(stmt_node->data.return_stmt.expression, icg);
            }
            add_instruction(icg->current_ic, create_instruction(IC_RETURN, create_operand_none(), return_val_op, create_operand_none()));
            break;
        }
        case AST_IF_STMT: {
            Operand cond_op = generate_expression_ic(stmt_node->data.if_stmt.condition, icg);
            Operand label_else = new_label_operand(icg);
            Operand label_endif = new_label_operand(icg);

            // if (cond) goto L_then; else goto L_else;
            add_instruction(icg->current_ic, create_instruction(IC_JUMPIF_FALSE, label_else, cond_op, create_operand_none()));
            
            // then 分支
            generate_statement_ic(stmt_node->data.if_stmt.then_branch, icg);
            
            if (stmt_node->data.if_stmt.else_branch) {
                add_instruction(icg->current_ic, create_instruction(IC_JUMP, label_endif, create_operand_none(), create_operand_none()));
                add_instruction(icg->current_ic, create_instruction(IC_LABEL, label_else, create_operand_none(), create_operand_none()));
                // else 分支
                generate_statement_ic(stmt_node->data.if_stmt.else_branch, icg);
                add_instruction(icg->current_ic, create_instruction(IC_LABEL, label_endif, create_operand_none(), create_operand_none()));
            } else {
                // 没有 else 分支,条件为假直接跳到 if 结束
                add_instruction(icg->current_ic, create_instruction(IC_LABEL, label_else, create_operand_none(), create_operand_none()));
            }
            break;
        }
        case AST_WHILE_STMT: {
            Operand label_loop_start = new_label_operand(icg);
            Operand label_loop_end = new_label_operand(icg);

            add_instruction(icg->current_ic, create_instruction(IC_LABEL, label_loop_start, create_operand_none(), create_operand_none()));
            
            Operand cond_op = generate_expression_ic(stmt_node->data.while_stmt.condition, icg);
            add_instruction(icg->current_ic, create_instruction(IC_JUMPIF_FALSE, label_loop_end, cond_op, create_operand_none()));
            
            generate_statement_ic(stmt_node->data.while_stmt.body, icg);
            
            add_instruction(icg->current_ic, create_instruction(IC_JUMP, label_loop_start, create_operand_none(), create_operand_none()));
            add_instruction(icg->current_ic, create_instruction(IC_LABEL, label_loop_end, create_operand_none(), create_operand_none()));
            break;
        }
        // TODO: AST_FOR_STMT, AST_BREAK_STMT, AST_CONTINUE_STMT, AST_SWITCH_STMT 等
        default:
            fprintf(stderr, "错误: 未知或不支持的AST语句类型用于中间代码生成。\n");
            break;
    }
}

// 生成函数定义的中间代码
void generate_function_ic(ASTNode* func_def_node, ICG* icg) {
    // 为当前函数创建一个新的中间代码列表
    // 注意:这里我们简化处理,所有函数的中间代码都添加到一个大的IC列表中
    // 实际编译器中,每个函数会有独立的IC列表
    if (!icg->current_ic) { // 第一次调用时初始化
        icg->current_ic = init_intermediate_code();
    }
    
    Symbol* func_sym = func_def_node->data.func_def.symbol_entry; // 语义分析阶段已填充
    if (!func_sym) {
        fprintf(stderr, "错误: 函数 '%s' 未在符号表中找到,无法生成中间代码。\n", func_def_node->data.func_def.identifier->data.identifier_expr.name);
        return;
    }

    // 函数入口标记
    add_instruction(icg->current_ic, create_instruction(IC_FUNC_BEGIN, create_operand_none(), create_operand_var(func_sym->name, 0, func_sym->type), create_operand_none()));

    // TODO: 处理函数参数的中间代码 (例如,将参数从寄存器/栈传递到局部变量空间)
    // 目前参数的偏移量已在符号表中确定,代码生成阶段可以直接使用

    // 生成函数体的中间代码
    generate_statement_ic(func_def_node->data.func_def.body, icg);

    // 确保函数末尾有返回指令,如果没有,对于非void函数,这是一个错误
    // 对于void函数,可以隐式添加 return;
    if (func_sym->type->return_type->kind == TYPE_VOID) {
        // 检查最后一条指令是否是RETURN,如果不是,则添加一个隐式的 RETURN;
        if (icg->current_ic->num_instructions == 0 || icg->current_ic->instructions[icg->current_ic->num_instructions - 1]->op != IC_RETURN) {
             add_instruction(icg->current_ic, create_instruction(IC_RETURN, create_operand_none(), create_operand_none(), create_operand_none()));
        }
    } else {
        // 对于非void函数,如果函数体没有显式 return,语义分析阶段应该已经报错
        // 这里假设语义分析已确保正确性
    }

    // 函数出口标记
    add_instruction(icg->current_ic, create_instruction(IC_FUNC_END, create_operand_none(), create_operand_var(func_sym->name, 0, func_sym->type), create_operand_none()));
}

// 生成整个程序的中间代码
IntermediateCode* generate_program_ic(ASTNode* program_node, ICG* icg) {
    if (program_node->type != AST_PROGRAM) {
        fprintf(stderr, "错误: AST根节点不是程序类型,无法生成中间代码。\n");
        return NULL;
    }

    // 初始化整个程序的中间代码列表
    icg->current_ic = init_intermediate_code();
    if (!icg->current_ic) {
        return NULL;
    }

    // 遍历所有顶层声明和定义
    for (int i = 0; i < program_node->data.program.num_declarations; ++i) {
        ASTNode* decl_node = program_node->data.program.declarations[i];
        if (decl_node->type == AST_VAR_DECL) {
            // 全局变量声明:如果带初始化,则生成赋值指令
            if (decl_node->data.var_decl.initializer) {
                Operand var_op = create_operand_var(decl_node->data.var_decl.identifier->data.identifier_expr.symbol_entry->name,
                                                    decl_node->data.var_decl.identifier->data.identifier_expr.symbol_entry->offset,
                                                    decl_node->data.var_decl.identifier->resolved_type);
                Operand init_op = generate_expression_ic(decl_node->data.var_decl.initializer, icg);
                add_instruction(icg->current_ic, create_instruction(IC_ASSIGN, var_op, init_op, create_operand_none()));
            }
        } else if (decl_node->type == AST_FUNC_DEF) {
            generate_function_ic(decl_node, icg); // 生成函数定义的中间代码
        } else {
            fprintf(stderr, "警告: 顶层节点类型 '%s' 暂不支持中间代码生成。\n", ast_node_type_to_string(decl_node->type));
        }
    }
    
    fprintf(stderr, "程序中间代码生成完成。\n");
    return icg->current_ic; // 返回生成的中间代码列表
}

main.c (修改)

// main.c (修改)
#include "lexer.h"
#include "parser.h"
#include "ast.h"
#include "token.h"
#include "semantic_analyzer.h"
#include "type.h"
#include "symbol_table.h"
#include "intermediate_code.h" // 新增:中间代码头文件
#include "ic_generator.h"      // 新增:中间代码生成器头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 这是一个包含各种C语言语法元素的测试代码字符串
// 用于全面测试词法分析器、语法分析器、语义分析器和中间代码生成器的功能
const char* TEST_CODE = 
    "// 这是一个单行注释\n"
    "/* 这是一个\n"
    " * 多行注释\n"
    " */\n"
    "int global_var = 100;\n" // 全局变量声明
    "char another_global = 'Z';\n" // 另一个全局变量
    "\n"
    "float add(float a, float b) {\n" // 函数定义:浮点数加法
    "    return a + b;\n"
    "}\n"
    "\n"
    "int subtract(int x, int y) {\n" // 新增函数:整数减法
    "    int temp_local = 5;\n" // 局部变量
    "    if (x > y) {\n"
    "        temp_local = x - y;\n"
    "    } else {\n"
    "        temp_local = y - x;\n"
    "    }\n"
    "    return temp_local;\n"
    "}\n"
    "\n"
    "int main() {\n" // main 函数的定义
    "    int x = 10;\n" // 整数变量声明和初始化
    "    float y = 3.14e-2;\n" // 浮点数变量声明和初始化,包含科学计数法
    "    char c = 'A';\n" // 字符变量声明和初始化
    "    // const char* str_literal = \"Hello, \\\"World!\\n\";\n" // 字符串字面量 (暂不支持ICG)
    "    \n"
    "    if (x >= 5 && y < 10.0) {\n" // if 条件语句,包含比较运算符和逻辑运算符
    "        int z = x + (int)y * 2; // 强制类型转换 (暂不支持显式IC_CAST,但会处理表达式) \n" // 局部变量声明和表达式
    "        if (z > 20) {\n"
    "            z = z - 5;\n"
    "        }\n"
    "        return z;\n" // return 语句
    "    } else if (x == 10) {\n" // else if 分支 (else后面跟if语句)
    "        x++;\n" // 自增运算符 (会分解为 ADD 和 ASSIGN)
    "    } else {\n" // else 分支
    "        y--;\n" // 自减运算符 (会分解为 SUB 和 ASSIGN)
    "    }\n"
    "    \n"
    "    while (x > 0) {\n" // while 循环
    "        x = x - 1;\n" // 循环体内的表达式语句
    "        if (x == 5) {\n"
    "            // break; // 暂不支持 break/continue,但语法上可以识别
    "        }\n"
    "    }\n"
    "    \n"
    "    float sum_result = add(x, y);\n" // 函数调用,并将结果赋值给变量
    "    int diff_result = subtract(20, 5);\n" // 调用新函数
    "    \n"
    "    sum_result += 5.0f;\n" // 复合赋值运算符 (会分解为 ADD 和 ASSIGN)
    "    global_var = (int)sum_result;\n" // 访问并修改全局变量,隐式转换为int (ICG会生成ASSIGN)
    "    \n"
    "    // 语义错误测试用例 (这些应该在语义分析阶段被捕获)\n"
    "    // int undeclared_var = undeclared_func(1, 2);\n" 
    "    // int bad_assign = \"string\";\n" 
    "    // int x = 20; // 重复定义变量 x (在main函数内)
    "    // return \"string\"; // return 类型不匹配\n"
    "    // int main() { /* 重复定义 main 函数 */ }\n"
    "    // void bad_var; // void 类型变量\n"
    "    \n"
    "    return 0;\n" // main 函数返回
    "}\n"
    ;

int main(int argc, char* argv[]) {
    // 1. 将 TEST_CODE 字符串内容写入一个临时文件
    const char* temp_filepath = "test_code.c";
    FILE* temp_file = fopen(temp_filepath, "w");
    if (!temp_file) {
        fprintf(stderr, "错误: 无法创建临时文件 '%s'\n", temp_filepath);
        return EXIT_FAILURE;
    }
    fprintf(temp_file, "%s", TEST_CODE);
    fclose(temp_file);

    // 2. 初始化词法分析器
    Lexer* lexer = init_lexer(temp_filepath);
    if (!lexer) {
        return EXIT_FAILURE;
    }
    printf("--- 词法分析器初始化成功 ---\n");

    // 3. 初始化语法分析器
    Parser* parser = init_parser(lexer);
    if (!parser) {
        free_lexer(lexer);
        return EXIT_FAILURE;
    }
    printf("--- 语法分析器初始化成功 ---\n");

    // 4. 开始解析程序,构建抽象语法树 (AST)
    printf("--- 开始语法分析,构建AST ---\n");
    ASTNode* program_ast = parse_program(parser);
    
    // 5. 检查AST是否成功构建
    if (!program_ast) {
        fprintf(stderr, "错误: AST 构建失败。\n");
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- AST 构建成功!---\n");
    
    // 6. 初始化语义分析器并执行语义分析
    SemanticAnalyzer* analyzer = init_semantic_analyzer();
    if (!analyzer) {
        ast_free_program(program_ast);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 语义分析开始!---\n");
    analyze_program(program_ast, analyzer); // 执行语义分析

    if (analyzer->error_count > 0) {
        fprintf(stderr, "语义分析完成,发现 %d 个语义错误。程序终止。\n", analyzer->error_count);
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_parser(parser);
        remove(temp_filepath);
        return EXIT_FAILURE; // 有语义错误,终止后续阶段
    } else {
        printf("语义分析完成,未发现语义错误!代码是语义正确的!\n");
    }

    // 打印带有语义信息的AST (可选,用于调试)
    // printf("--- 打印带有语义信息的AST结构:---\n");
    // ast_print(program_ast, 0);
    // printf("--- AST 打印完成 ---\n");
    
    // 7. 初始化中间代码生成器并生成中间代码
    ICG* icg = init_icg(analyzer->sym_table); // 将语义分析器生成的符号表传递给ICG
    if (!icg) {
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 中间代码生成开始!---\n");
    IntermediateCode* program_ic = generate_program_ic(program_ast, icg); // 生成中间代码

    if (!program_ic) {
        fprintf(stderr, "错误: 中间代码生成失败。\n");
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_icg(icg);
        free_parser(parser);
        remove(temp_filepath);
        return EXIT_FAILURE;
    }

    printf("--- 中间代码生成成功!---\n");
    printf("--- 打印生成的中间代码:---\n");
    print_intermediate_code(program_ic);
    printf("--- 中间代码打印完成 ---\n");

    // 8. 释放所有资源
    ast_free_program(program_ast);
    printf("--- AST 内存已释放 ---\n");
    free_semantic_analyzer(analyzer);
    printf("--- 语义分析器资源已释放 ---\n");
    free_intermediate_code(program_ic); // 释放中间代码列表
    printf("--- 中间代码内存已释放 ---\n");
    free_icg(icg); // 释放ICG本身
    printf("--- 中间代码生成器资源已释放 ---\n");
    free_parser(parser);
    printf("--- 解析器和词法分析器资源已释放 ---\n");

    // 可选:删除之前创建的临时文件
    remove(temp_filepath);

    return EXIT_SUCCESS;
}

编译和运行

将上述所有文件(token.h, token.c, lexer.h, lexer.c, parser.h, parser.c, ast.h, ast.c, type.h, type.c, symbol_table.h, symbol_table.c, semantic_analyzer.h, semantic_analyzer.c, intermediate_code.h, intermediate_code.c, ic_generator.h, ic_generator.c, main.c)放在同一个目录下。

你可以使用GCC编译器进行编译:

gcc -o my_compiler main.c lexer.c token.c ast.c parser.c type.c symbol_table.c semantic_analyzer.c intermediate_code.c ic_generator.c -Wall -Wextra

然后运行:

./my_compiler

你将看到程序依次进行词法分析、语法分析、语义分析,最后是中间代码生成。如果一切顺利,你将看到生成的详细三地址码指令列表。

中间代码生成器的逻辑分析与挑战

  1. 分阶段生成: 我们将中间代码生成过程分为三个主要部分:generate_program_ic(程序级别)、generate_function_ic(函数级别)和generate_statement_ic/generate_expression_ic(语句/表达式级别)。这种模块化设计使得代码清晰且易于管理。

  2. 操作数和指令设计:

    • Operand结构体能够灵活地表示各种类型的数据(常量、变量、临时变量、标签),并携带类型信息。

    • Instruction结构体遵循三地址码范式,包含操作码、结果操作数和两个源操作数。

    • 我们定义了丰富的IntermediateCodeOp枚举,覆盖了C语言中的常见操作。

  3. 临时变量和标签管理:

    • ICG结构体中的temp_counterlabel_counter确保了生成的临时变量和标签具有唯一的ID,避免冲突。

    • new_temp_operandnew_label_operand函数简化了这些特殊操作数的创建。

  4. 表达式转换的核心:

    • generate_expression_ic是中间代码生成的核心。它递归地处理子表达式,并将子表达式的结果存储在临时变量中。

    • 对于二元和一元操作符,它会根据操作符类型生成对应的三地址码指令,并将结果存入新的临时变量。

    • 它利用了语义分析阶段填充的resolved_type来确定临时变量的类型。

    • 类型提升: get_common_type函数在二元运算中用于确定结果类型,实现了简化的类型提升规则。

  5. 语句转换的逻辑:

    • 变量声明: 局部变量的声明本身不生成指令,但如果带有初始化表达式,则会生成一个IC_ASSIGN指令。

    • 赋值表达式: 生成IC_ASSIGN指令。

    • 控制流 (if, while): 这是生成中间代码的复杂之处。我们通过引入标签条件跳转指令IC_JUMPIF_FALSE, IC_JUMP)来模拟C语言的控制流。这使得控制流在中间代码层面变得扁平化和显式化。

    • 函数调用: 生成一系列IC_PARAM指令来推送参数,然后生成一个IC_CALL指令来执行函数调用,并可选地将返回值存入临时变量。

    • 函数定义: 使用IC_FUNC_BEGINIC_FUNC_END标记函数的开始和结束,这对于后续的代码生成和优化非常重要。

  6. 内存管理:

    • intermediate_code.c中的add_instruction会为每条指令及其内部需要复制的字符串(如变量名)动态分配内存。

    • free_intermediate_code负责递归释放这些内存,防止内存泄漏。这是C语言编译器开发中非常重要但容易出错的部分。

  7. 挑战与未来扩展:

    • 更复杂的类型支持: 目前只支持基本类型和函数类型。指针、数组、结构体、联合体等需要更复杂的内存访问指令(如IC_LOAD, IC_STORE)和地址计算逻辑。

    • 复合赋值运算符: a += b 等复合赋值目前被简化处理,未来需要将其分解为 t = a + b; a = t; 这样的序列。

    • 自增/自减运算符: x++, ++x 等操作符需要分解为读取、加/减、写入的序列,并区分前缀和后缀的语义。

    • 短路逻辑: &&|| 的短路求值特性在中间代码中需要通过更复杂的条件跳转来实现。

    • breakcontinue 需要在ICG中维护当前循环/switch的标签栈,以便breakcontinue能够跳转到正确的循环结束或下一次迭代标签。

    • for 循环: for循环可以分解为while循环和额外的初始化/步进语句。

    • 类型转换: 显式类型转换(如(int)y)需要生成IC_CAST指令,并确保类型大小和表示的正确转换。

    • 全局变量初始化: 全局变量的初始化通常在程序启动时进行,需要生成特殊的初始化指令。

总结与展望

朋友们,恭喜你!你已经完成了手撸编译器的第四步——一个功能强大的C语言中间代码生成器!你亲手将抽象的AST转换成了一种扁平、简单、机器友好的三地址码。这不仅是编译流程中的关键一步,更是你对编译原理理解的又一次质的飞跃!

现在,我们的程序已经从源代码变成了结构化的AST,再变成了线性的三地址码。这些三地址码指令,虽然还不是最终的机器码,但它们已经非常接近了!它们为我们后续的代码优化目标代码生成奠定了坚实的基础。

这只是我们“肝爆”之旅的第四站。在下一章中,我们将进入编译器的第五个核心阶段:代码优化(Code Optimization)。我们将利用中间代码的优势,对生成的指令进行各种变换,以提高程序的执行效率!

准备好迎接更高效的代码了吗?继续保持这份激情,我们下一章不见不散!点赞、收藏、关注,让我们一起把这“手撸编译器”的肝爆之旅进行到底!

第五章:精益求精——代码优化与中间代码变换)

朋友们,欢迎来到我们“手撸编译器”的第五站!在第四章中,我们亲手将抽象语法树(AST)转换成了线性的、机器友好的三地址码(TAC)。现在,我们的程序已经以一种更接近底层但仍具通用性的形式存在了。

然而,仅仅生成正确的中间代码是不够的!想象一下,你建造了一辆汽车,它能跑,但油耗惊人,速度也慢得像蜗牛。你肯定会想办法改进它,让它跑得更快,更省油。编译器的**代码优化(Code Optimization)**阶段正是扮演着这样的角色!

代码优化的使命:让程序“飞”起来

代码优化是编译器中的一个可选但至关重要的阶段。它的主要目标是:

  1. 提高执行速度: 减少程序的执行时间,让程序运行得更快。

  2. 减少资源消耗: 降低程序对内存、CPU周期、磁盘I/O等资源的占用。这包括减小生成的可执行文件大小。

优化并非总是能带来巨大的性能提升,有时甚至可能适得其反(例如,优化本身耗时过长,或者导致代码膨胀)。因此,编译器通常会提供不同的优化级别(如GCC的-O1, -O2, -O3, -Os等),让开发者根据需求选择。

优化的分类

代码优化通常可以根据其作用范围和依赖性进行分类:

  • 机器无关优化(Machine-Independent Optimization):

    • 不依赖于特定目标机器的指令集架构。

    • 通常在中间代码(如三地址码)上进行。

    • 例如:常量折叠、公共子表达式消除、死代码消除、循环优化等。

  • 机器相关优化(Machine-Dependent Optimization):

    • 依赖于特定目标机器的特性(如寄存器数量、指令集、缓存结构)。

    • 通常在目标代码生成阶段或之后进行。

    • 例如:寄存器分配、指令调度、窥孔优化等。

在本章中,我们将主要关注机器无关优化,因为它们直接作用于我们已经生成的三地址码。

常见的代码优化技术

我们将实现几种简单但有效的优化技术,它们可以显著改善程序的性能。

1. 常量折叠(Constant Folding)与常量传播(Constant Propagation)

  • 常量折叠: 在编译时计算常量表达式的值,并用结果替换表达式。

    • 原始代码: int x = 10 + 20;

    • 中间代码(未优化):

      t0 = 10
      t1 = 20
      t2 = t0 + t1
      x = t2
      
      
    • 中间代码(常量折叠后):

      x = 30
      
      
  • 常量传播: 如果一个变量被赋值为一个常量,那么在后续使用该变量的地方,可以用这个常量值直接替换。

    • 原始代码:

      int a = 10;
      int b = a + 5;
      
      
    • 中间代码(未优化):

      a = 10
      t0 = a + 5
      b = t0
      
      
    • 中间代码(常量传播后):

      a = 10
      t0 = 10 + 5  // (继续常量折叠)
      b = t0
      
      
    • 最终:

      a = 10
      b = 15
      
      

2. 死代码消除(Dead Code Elimination)

  • 消除那些永远不会被执行到,或者其结果永远不会被使用的代码。

    • 原始代码:

      int x = 10;
      int y = x + 5;
      // ... 后面代码没有使用y
      if (0) { // 永远不会执行
          printf("Hello");
      }
      
      
    • 中间代码(未优化):

      x = 10
      t0 = x + 5
      y = t0
      L0: JUMPIF_FALSE 0, L1 // if (0)
      ... // printf 相关指令
      L1:
      
      
    • 中间代码(死代码消除后):

      x = 10
      // t0 = x + 5 和 y = t0 被消除,因为y未被使用
      // if (0) 分支也被消除
      
      

3. 公共子表达式消除(Common Subexpression Elimination, CSE)

  • 识别并消除在程序中多次计算的相同表达式,用一个临时变量存储其结果,并在后续使用时直接引用该临时变量。

    • 原始代码:

      int a = (x + y) * z;
      int b = (x + y) / 2;
      
      
    • 中间代码(未优化):

      t0 = x + y
      t1 = t0 * z
      a = t1
      t2 = x + y
      t3 = t2 / 2
      b = t3
      
      
    • 中间代码(CSE后):

      t0 = x + y   // 第一次计算 x + y
      t1 = t0 * z
      a = t1
      // t2 = x + y 被消除,因为 t0 已经存储了结果
      t3 = t0 / 2  // 直接使用 t0
      b = t3
      
      

4. 循环不变代码外提(Loop-Invariant Code Motion)

  • 将循环体内计算结果不随循环迭代而改变的表达式(循环不变式)移到循环体之外,只计算一次。

    • 原始代码:

      for (i = 0; i < N; i++) {
          int temp = A * B; // A*B在循环内不变
          sum += temp;
      }
      
      
    • 中间代码(未优化):

      L0: JUMPIF_FALSE i < N, L1
          t0 = A * B
          sum = sum + t0
          i = i + 1
          JUMP L0
      L1:
      
      
    • 中间代码(循环不变代码外提后):

      t_temp = A * B // 移到循环外
      L0: JUMPIF_FALSE i < N, L1
          sum = sum + t_temp // 直接使用t_temp
          i = i + 1
          JUMP L0
      L1:
      
      

优化器的实现策略

实现这些优化通常需要对中间代码进行数据流分析(Data Flow Analysis),以收集变量的使用、定义、活跃性等信息。这通常比简单的遍历更复杂,因为它需要迭代地分析指令之间的依赖关系。

为了简化,我们在这里将实现一个简化的、基于遍历的优化器,主要关注常量折叠和简单的死代码消除。更高级的优化需要构建控制流图(CFG)和进行迭代数据流分析。

优化器结构

我们将创建一个Optimizer结构体来管理优化过程。

// optimizer.h (部分代码,完整版在后面)
#ifndef OPTIMIZER_H
#define OPTIMIZER_H

#include "intermediate_code.h" // 需要中间代码

// 优化器结构体
typedef struct {
    // 可以在这里存储优化过程中需要的状态,例如:
    // 一个映射表,用于常量传播 (变量名 -> 常量值)
    // 一个集合,用于记录活跃变量 (死代码消除)
} Optimizer;

// 函数声明
Optimizer* init_optimizer();
void free_optimizer(Optimizer* opt);

/**
 * @brief 对中间代码进行优化。
 * @param ic 指向 IntermediateCode 结构体的指针。
 * @param opt 优化器实例。
 */
void optimize_intermediate_code(IntermediateCode* ic, Optimizer* opt);

// 各种优化子函数 (内部使用)
static void constant_folding(IntermediateCode* ic);
static void dead_code_elimination(IntermediateCode* ic);
// static void common_subexpression_elimination(IntermediateCode* ic); // 复杂,暂不实现
// static void loop_invariant_code_motion(IntermediateCode* ic); // 复杂,暂不实现

#endif // OPTIMIZER_H

完整的代码实现

optimizer.h

// optimizer.h
#ifndef OPTIMIZER_H
#define OPTIMIZER_H

#include "intermediate_code.h" // 需要中间代码结构体
#include "type.h"              // 需要Type定义

// 优化器结构体
// 包含了优化过程中可能需要的状态和数据结构
typedef struct {
    // 可以在这里存储优化过程中需要的状态,例如:
    // - 一个映射表,用于常量传播 (变量名/临时变量ID -> 常量值)
    // - 一个集合,用于记录活跃变量 (死代码消除)
    // - 一个记录已计算表达式结果的哈希表 (公共子表达式消除)
    // 为了简化,当前版本这个结构体可能为空或只包含少量辅助变量
} Optimizer;

// 函数声明

/**
 * @brief 初始化优化器。
 * @return 成功返回指向 Optimizer 结构体的指针,失败返回NULL。
 */
Optimizer* init_optimizer();

/**
 * @brief 释放优化器占用的所有资源。
 * @param opt 指向要释放的 Optimizer 结构体的指针。
 */
void free_optimizer(Optimizer* opt);

/**
 * @brief 对中间代码进行优化。
 * 这是优化阶段的入口点,它会调用各种优化子函数。
 * @param ic 指向 IntermediateCode 结构体的指针,优化将直接修改此结构体。
 * @param opt 优化器实例。
 */
void optimize_intermediate_code(IntermediateCode* ic, Optimizer* opt);

// 各种优化子函数 (通常是静态的,内部使用)
// 为了简化,我们只实现常量折叠和简单的死代码消除
static void constant_folding(IntermediateCode* ic);
static void dead_code_elimination(IntermediateCode* ic);
// static void common_subexpression_elimination(IntermediateCode* ic); // 更复杂,需要数据流分析
// static void loop_invariant_code_motion(IntermediateCode* ic); // 更复杂,需要控制流图和数据流分析

#endif // OPTIMIZER_H

optimizer.c

// optimizer.c
#include "optimizer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h> // For bool type

// --- 辅助函数 ---

// 检查操作数是否是常量
static bool is_constant(Operand op) {
    return op.kind == OP_CONSTANT_INT || op.kind == OP_CONSTANT_FLOAT;
}

// 检查操作数是否是整数常量
static bool is_int_constant(Operand op) {
    return op.kind == OP_CONSTANT_INT;
}

// 检查操作数是否是浮点数常量
static bool is_float_constant(Operand op) {
    return op.kind == OP_CONSTANT_FLOAT;
}

// 评估二元常量表达式
static Operand evaluate_binary_constant(IntermediateCodeOp op, Operand op1, Operand op2) {
    // 确保都是常量
    if (!is_constant(op1) || !is_constant(op2)) {
        return create_operand_none(); // 不是常量,无法折叠
    }

    // 处理整数运算
    if (is_int_constant(op1) && is_int_constant(op2)) {
        long long val1 = op1.val.int_val;
        long long val2 = op2.val.int_val;
        long long result_val;
        bool is_valid_op = true;

        switch (op) {
            case IC_ADD: result_val = val1 + val2; break;
            case IC_SUB: result_val = val1 - val2; break;
            case IC_MUL: result_val = val1 * val2; break;
            case IC_DIV: 
                if (val2 == 0) { fprintf(stderr, "警告: 编译时整数除零。\n"); is_valid_op = false; }
                else { result_val = val1 / val2; }
                break;
            case IC_MOD:
                if (val2 == 0) { fprintf(stderr, "警告: 编译时整数模零。\n"); is_valid_op = false; }
                else { result_val = val1 % val2; }
                break;
            case IC_EQ: result_val = (val1 == val2); break;
            case IC_NE: result_val = (val1 != val2); break;
            case IC_LT: result_val = (val1 < val2); break;
            case IC_LE: result_val = (val1 <= val2); break;
            case IC_GT: result_val = (val1 > val2); break;
            case IC_GE: result_val = (val1 >= val2); break;
            case IC_AND: result_val = (val1 && val2); break; // 逻辑与
            case IC_OR: result_val = (val1 || val2); break;  // 逻辑或
            case IC_BIT_AND: result_val = (val1 & val2); break;
            case IC_BIT_OR: result_val = (val1 | val2); break;
            case IC_BIT_XOR: result_val = (val1 ^ val2); break;
            case IC_LSHIFT: result_val = (val1 << val2); break;
            case IC_RSHIFT: result_val = (val1 >> val2); break;
            default: is_valid_op = false; break;
        }
        if (is_valid_op) {
            return create_operand_int(result_val);
        }
    } 
    // 处理浮点数运算 (简化:只处理两个都是浮点数的情况,不处理混合类型)
    else if (is_float_constant(op1) && is_float_constant(op2)) {
        double val1 = op1.val.float_val;
        double val2 = op2.val.float_val;
        double result_val;
        bool is_valid_op = true;

        switch (op) {
            case IC_ADD: result_val = val1 + val2; break;
            case IC_SUB: result_val = val1 - val2; break;
            case IC_MUL: result_val = val1 * val2; break;
            case IC_DIV: 
                if (val2 == 0.0) { fprintf(stderr, "警告: 编译时浮点数除零。\n"); is_valid_op = false; }
                else { result_val = val1 / val2; }
                break;
            case IC_EQ: result_val = (val1 == val2); break;
            case IC_NE: result_val = (val1 != val2); break;
            case IC_LT: result_val = (val1 < val2); break;
            case IC_LE: result_val = (val1 <= val2); break;
            case IC_GT: result_val = (val1 > val2); break;
            case IC_GE: result_val = (val1 >= val2); break;
            default: is_valid_op = false; break;
        }
        if (is_valid_op) {
            return create_operand_float(result_val);
        }
    }
    // TODO: 处理混合类型运算 (例如 int + float -> float)

    return create_operand_none(); // 无法折叠或不支持的操作
}

// 评估一元常量表达式
static Operand evaluate_unary_constant(IntermediateCodeOp op, Operand op1) {
    if (!is_constant(op1)) {
        return create_operand_none();
    }

    if (is_int_constant(op1)) {
        long long val = op1.val.int_val;
        long long result_val;
        bool is_valid_op = true;
        switch (op) {
            case IC_SUB: result_val = -val; break; // 一元负
            case IC_NOT: result_val = !val; break; // 逻辑非
            case IC_BIT_NOT: result_val = ~val; break; // 位非
            default: is_valid_op = false; break;
        }
        if (is_valid_op) return create_operand_int(result_val);
    } else if (is_float_constant(op1)) {
        double val = op1.val.float_val;
        double result_val;
        bool is_valid_op = true;
        switch (op) {
            case IC_SUB: result_val = -val; break; // 一元负
            case IC_NOT: result_val = !val; break; // 逻辑非
            default: is_valid_op = false; break;
        }
        if (is_valid_op) return create_operand_float(result_val);
    }
    return create_operand_none();
}

// --- 优化子函数实现 ---

// 常量折叠:在编译时计算常量表达式
static void constant_folding(IntermediateCode* ic) {
    fprintf(stderr, "执行常量折叠...\n");
    for (int i = 0; i < ic->num_instructions; ++i) {
        Instruction* instr = ic->instructions[i];
        if (!instr) continue;

        // 处理二元运算
        if (instr->op >= IC_ADD && instr->op <= IC_GE) { // 算术、关系、逻辑二元运算
            if (is_constant(instr->op1) && is_constant(instr->op2)) {
                Operand folded_result = evaluate_binary_constant(instr->op, instr->op1, instr->op2);
                if (folded_result.kind != OP_NONE) {
                    // 将指令转换为赋值指令,结果为折叠后的常量
                    instr->op = IC_ASSIGN;
                    instr->op1 = folded_result;
                    instr->op2 = create_operand_none();
                    fprintf(stderr, "  折叠指令 %d: %s = %s %s %s -> %s = %s\n", 
                            i, 
                            ic_op_to_string(instr->op), 
                            "op1", "op2", 
                            "result", 
                            ic_op_to_string(IC_ASSIGN), 
                            type_kind_to_string(folded_result.type->kind));
                }
            }
        }
        // 处理一元运算
        else if (instr->op == IC_NOT || instr->op == IC_BIT_NOT || (instr->op == IC_SUB && instr->op1.kind != OP_NONE && instr->op2.kind == OP_NONE)) { // 逻辑非、位非、一元负
             if (is_constant(instr->op1)) {
                Operand folded_result = evaluate_unary_constant(instr->op, instr->op1);
                if (folded_result.kind != OP_NONE) {
                    instr->op = IC_ASSIGN;
                    instr->op1 = folded_result;
                    instr->op2 = create_operand_none();
                    fprintf(stderr, "  折叠指令 %d: %s = %s %s -> %s = %s\n", 
                            i, 
                            ic_op_to_string(instr->op), 
                            "op1", 
                            "result", 
                            ic_op_to_string(IC_ASSIGN), 
                            type_kind_to_string(folded_result.type->kind));
                }
            }
        }
    }
}

// 简单的死代码消除:消除对结果没有后续使用的赋值指令
// 注意:这是一个非常简化的版本,真正的死代码消除需要更复杂的数据流分析 (活跃变量分析)
static void dead_code_elimination(IntermediateCode* ic) {
    fprintf(stderr, "执行死代码消除 (简化版)...\n");
    bool* is_instruction_dead = (bool*)calloc(ic->num_instructions, sizeof(bool));
    if (!is_instruction_dead) {
        fprintf(stderr, "错误: 内存分配失败 (is_instruction_dead)\n");
        return;
    }

    // 标记所有指令为“活跃” (默认)
    // 只有在确定某条指令是死代码后,才将其标记为“死”

    // 第一次遍历:找出所有被使用的变量和临时变量
    // 这是一个反向数据流分析的简化版本
    bool* is_var_used = (bool*)calloc(ic->temp_counter + ic->sym_table->global_var_offset / 4 + 100, sizeof(bool)); // 粗略估计变量数量
    if (!is_var_used) {
        fprintf(stderr, "错误: 内存分配失败 (is_var_used)\n");
        free(is_instruction_dead);
        return;
    }

    // 遍历所有指令,标记操作数中的变量和临时变量为“被使用”
    // 这是一个简化的活跃变量分析,不考虑控制流
    for (int i = 0; i < ic->num_instructions; ++i) {
        Instruction* instr = ic->instructions[i];
        if (!instr) continue;

        // 如果是跳转指令或函数调用,其操作数通常是“活跃”的
        if (instr->op == IC_JUMP || instr->op == IC_JUMPIF_TRUE || instr->op == IC_JUMPIF_FALSE || instr->op == IC_RETURN || instr->op == IC_PARAM || instr->op == IC_CALL) {
            // 这些指令本身就是活跃的,并且它们的op1/op2通常是活跃的
            if (instr->op1.kind == OP_VARIABLE) is_var_used[instr->op1.val.var_offset / 4] = true;
            if (instr->op1.kind == OP_TEMP_VAR) is_var_used[instr->op1.val.temp_id] = true;
            if (instr->op2.kind == OP_VARIABLE) is_var_used[instr->op2.val.var_offset / 4] = true;
            if (instr->op2.kind == OP_TEMP_VAR) is_var_used[instr->op2.val.temp_id] = true;
            continue; // 这些指令不会被标记为死代码
        }
        
        // 标记op1和op2为被使用
        if (instr->op1.kind == OP_VARIABLE) {
            is_var_used[instr->op1.val.var_offset / 4] = true;
        } else if (instr->op1.kind == OP_TEMP_VAR) {
            is_var_used[instr->op1.val.temp_id] = true;
        }

        if (instr->op2.kind == OP_VARIABLE) {
            is_var_used[instr->op2.val.var_offset / 4] = true;
        } else if (instr->op2.kind == OP_TEMP_VAR) {
            is_var_used[instr->op2.val.temp_id] = true;
        }
    }

    // 第二次遍历:标记死指令
    int eliminated_count = 0;
    for (int i = 0; i < ic->num_instructions; ++i) {
        Instruction* instr = ic->instructions[i];
        if (!instr) continue;

        // 只有赋值指令和计算指令的结果才可能成为死代码
        if (instr->op == IC_ASSIGN || (instr->op >= IC_ADD && instr->op <= IC_BIT_NOT) || instr->op == IC_CAST) {
            bool result_is_dead = false;
            if (instr->result.kind == OP_VARIABLE) {
                if (!is_var_used[instr->result.val.var_offset / 4]) {
                    result_is_dead = true;
                }
            } else if (instr->result.kind == OP_TEMP_VAR) {
                if (!is_var_used[instr->result.val.temp_id]) {
                    result_is_dead = true;
                }
            }

            if (result_is_dead) {
                is_instruction_dead[i] = true;
                eliminated_count++;
                fprintf(stderr, "  消除死指令 %d: %s\n", i, ic_op_to_string(instr->op));
            }
        }
    }

    // 第三次遍历:重建指令列表,移除死指令
    int current_idx = 0;
    for (int i = 0; i < ic->num_instructions; ++i) {
        if (!is_instruction_dead[i]) {
            ic->instructions[current_idx++] = ic->instructions[i];
        } else {
            // 释放被消除指令的内存
            free_operand_data(&ic->instructions[i]->result);
            free_operand_data(&ic->instructions[i]->op1);
            free_operand_data(&ic->instructions[i]->op2);
            free(ic->instructions[i]);
            ic->instructions[i] = NULL; // 清空指针
        }
    }
    ic->num_instructions = current_idx; // 更新指令数量

    free(is_instruction_dead);
    free(is_var_used);
    fprintf(stderr, "  死代码消除完成,移除了 %d 条指令。\n", eliminated_count);
}


// --- 优化器公共接口实现 ---

Optimizer* init_optimizer() {
    Optimizer* opt = (Optimizer*)malloc(sizeof(Optimizer));
    if (!opt) {
        fprintf(stderr, "错误: 内存分配失败 (Optimizer)\n");
        return NULL;
    }
    // 初始化优化器内部状态 (如果需要)
    fprintf(stderr, "优化器初始化成功。\n");
    return opt;
}

void free_optimizer(Optimizer* opt) {
    if (opt) {
        // 释放优化器内部可能动态分配的资源
        free(opt);
        fprintf(stderr, "优化器资源已清理。\n");
    }
}

// 对中间代码进行优化
void optimize_intermediate_code(IntermediateCode* ic, Optimizer* opt) {
    if (!ic) return;
    fprintf(stderr, "开始优化中间代码...\n");

    // 迭代执行优化,直到没有更多改进或达到最大迭代次数
    // 某些优化可能会为其他优化创造机会
    bool changed;
    int iteration = 0;
    const int MAX_OPTIMIZATION_ITERATIONS = 5; // 限制迭代次数以防无限循环

    do {
        changed = false;
        fprintf(stderr, "--- 优化迭代 %d ---\n", ++iteration);

        int old_num_instructions = ic->num_instructions;

        // 1. 常量折叠
        constant_folding(ic);
        if (old_num_instructions != ic->num_instructions) changed = true; // 如果指令数量变化,说明有优化

        // 2. 简单的死代码消除
        // 注意:死代码消除可能会改变指令数量,需要重新计算活跃性
        // 这里的简化版死代码消除可能需要多次运行才能完全清除
        dead_code_elimination(ic);
        if (old_num_instructions != ic->num_instructions) changed = true; // 如果指令数量变化,说明有优化

        // TODO: 添加其他优化,例如:
        // common_subexpression_elimination(ic);
        // loop_invariant_code_motion(ic);
        // constant_propagation(ic); // 需要更复杂的数据流分析

    } while (changed && iteration < MAX_OPTIMIZATION_ITERATIONS); // 如果有变化,继续迭代

    fprintf(stderr, "中间代码优化完成。\n");
}

main.c (修改)

// main.c (修改)
#include "lexer.h"
#include "parser.h"
#include "ast.h"
#include "token.h"
#include "semantic_analyzer.h"
#include "type.h"
#include "symbol_table.h"
#include "intermediate_code.h"
#include "ic_generator.h"
#include "optimizer.h"         // 新增:优化器头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 这是一个包含各种C语言语法元素的测试代码字符串
// 用于全面测试词法分析器、语法分析器、语义分析器、中间代码生成器和优化器的功能
const char* TEST_CODE = 
    "// 这是一个单行注释\n"
    "/* 这是一个\n"
    " * 多行注释\n"
    " */\n"
    "int global_var = 100 + 200; // 全局变量声明,可常量折叠\n" 
    "char another_global = 'Z';\n" 
    "\n"
    "float add(float a, float b) {\n" 
    "    return a + b;\n"
    "}\n"
    "\n"
    "int subtract(int x, int y) {\n" 
    "    int temp_local = 5 * 10; // 局部变量,可常量折叠\n" 
    "    int unused_var = 123; // 死代码\n"
    "    if (x > y) {\n"
    "        temp_local = x - y;\n"
    "    } else {\n"
    "        temp_local = y - x;\n"
    "    }\n"
    "    // unused_var 永远不会被使用,应该被消除\n"
    "    return temp_local;\n"
    "}\n"
    "\n"
    "int main() {\n" 
    "    int x = 10;\n" 
    "    float y = 3.14e-2;\n" 
    "    char c = 'A';\n" 
    "    \n"
    "    int common_expr_res = x + 5; // 公共子表达式 (目前简化版优化器不处理)\n"
    "    int another_common_expr_res = x + 5; // 同上\n"
    "    \n"
    "    if (x >= 5 && y < 10.0) {\n" 
    "        int z = x + (int)y * 2; \n" 
    "        if (z > 20) {\n"
    "            z = z - 5;\n"
    "        }\n"
    "        return z;\n" 
    "    } else if (x == 10) {\n" 
    "        x++;\n" 
    "    } else {\n" 
    "        y--;\n" 
    "    }\n"
    "    \n"
    "    while (x > 0) {\n" 
    "        x = x - 1;\n" 
    "        if (x == 5) {\n"
    "            // break; \n"
    "        }\n"
    "    }\n"
    "    \n"
    "    float sum_result = add(x, y);\n" 
    "    int diff_result = subtract(20, 5);\n" 
    "    \n"
    "    sum_result += 5.0f;\n" 
    "    global_var = (int)sum_result;\n" 
    "    \n"
    "    return 0;\n" 
    "}\n"
    ;

int main(int argc, char* argv[]) {
    // 1. 将 TEST_CODE 字符串内容写入一个临时文件
    const char* temp_filepath = "test_code.c";
    FILE* temp_file = fopen(temp_filepath, "w");
    if (!temp_file) {
        fprintf(stderr, "错误: 无法创建临时文件 '%s'\n", temp_filepath);
        return EXIT_FAILURE;
    }
    fprintf(temp_file, "%s", TEST_CODE);
    fclose(temp_file);

    // 2. 初始化词法分析器
    Lexer* lexer = init_lexer(temp_filepath);
    if (!lexer) {
        return EXIT_FAILURE;
    }
    printf("--- 词法分析器初始化成功 ---\n");

    // 3. 初始化语法分析器
    Parser* parser = init_parser(lexer);
    if (!parser) {
        free_lexer(lexer);
        return EXIT_FAILURE;
    }
    printf("--- 语法分析器初始化成功 ---\n");

    // 4. 开始解析程序,构建抽象语法树 (AST)
    printf("--- 开始语法分析,构建AST ---\n");
    ASTNode* program_ast = parse_program(parser);
    
    // 5. 检查AST是否成功构建
    if (!program_ast) {
        fprintf(stderr, "错误: AST 构建失败。\n");
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- AST 构建成功!---\n");
    
    // 6. 初始化语义分析器并执行语义分析
    SemanticAnalyzer* analyzer = init_semantic_analyzer();
    if (!analyzer) {
        ast_free_program(program_ast);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 语义分析开始!---\n");
    analyze_program(program_ast, analyzer); // 执行语义分析

    if (analyzer->error_count > 0) {
        fprintf(stderr, "语义分析完成,发现 %d 个语义错误。程序终止。\n", analyzer->error_count);
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_parser(parser);
        remove(temp_filepath);
        return EXIT_FAILURE; 
    } else {
        printf("语义分析完成,未发现语义错误!代码是语义正确的!\n");
    }
    
    // 7. 初始化中间代码生成器并生成中间代码
    ICG* icg = init_icg(analyzer->sym_table); 
    if (!icg) {
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 中间代码生成开始!---\n");
    IntermediateCode* program_ic = generate_program_ic(program_ast, icg); 

    if (!program_ic) {
        fprintf(stderr, "错误: 中间代码生成失败。\n");
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_icg(icg);
        free_parser(parser);
        remove(temp_filepath);
        return EXIT_FAILURE;
    }

    printf("--- 中间代码生成成功!---\n");
    printf("--- 打印未优化前的中间代码:---\n");
    print_intermediate_code(program_ic);
    printf("--- 未优化中间代码打印完成 ---\n");

    // 8. 初始化优化器并执行代码优化
    Optimizer* optimizer = init_optimizer();
    if (!optimizer) {
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_intermediate_code(program_ic);
        free_icg(icg);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 代码优化开始!---\n");
    optimize_intermediate_code(program_ic, optimizer); // 执行优化

    printf("--- 代码优化完成!---\n");
    printf("--- 打印优化后的中间代码:---\n");
    print_intermediate_code(program_ic);
    printf("--- 优化后中间代码打印完成 ---\n");

    // 9. 释放所有资源
    ast_free_program(program_ast);
    printf("--- AST 内存已释放 ---\n");
    free_semantic_analyzer(analyzer);
    printf("--- 语义分析器资源已释放 ---\n");
    free_intermediate_code(program_ic); 
    printf("--- 中间代码内存已释放 ---\n");
    free_icg(icg); 
    printf("--- 中间代码生成器资源已释放 ---\n");
    free_optimizer(optimizer); // 释放优化器
    printf("--- 优化器资源已释放 ---\n");
    free_parser(parser);
    printf("--- 解析器和词法分析器资源已释放 ---\n");

    // 可选:删除之前创建的临时文件
    remove(temp_filepath);

    return EXIT_SUCCESS;
}

编译和运行

将上述所有文件(token.h, token.c, lexer.h, lexer.c, parser.h, parser.c, ast.h, ast.c, type.h, type.c, symbol_table.h, symbol_table.c, semantic_analyzer.h, semantic_analyzer.c, intermediate_code.h, intermediate_code.c, ic_generator.h, ic_generator.c, optimizer.h, optimizer.c, main.c)放在同一个目录下。

你可以使用GCC编译器进行编译:

gcc -o my_compiler main.c lexer.c token.c ast.c parser.c type.c symbol_table.c semantic_analyzer.c intermediate_code.c ic_generator.c optimizer.c -Wall -Wextra

然后运行:

./my_compiler

你将看到程序依次进行词法分析、语法分析、语义分析、中间代码生成,然后是代码优化。在优化前后,程序会分别打印出中间代码,你可以对比它们的变化,观察优化效果。

代码优化器的逻辑分析与挑战

  1. 优化器框架:

    • Optimizer结构体目前比较简单,但为未来更复杂的优化(如需要维护数据流信息)预留了扩展空间。

    • optimize_intermediate_code函数是优化阶段的入口,它会迭代调用不同的优化子函数,直到没有更多的改进或达到最大迭代次数。这种迭代是必要的,因为一个优化可能会为另一个优化创造新的机会。

  2. 常量折叠 (constant_folding):

    • 遍历所有指令,识别二元和一元运算指令。

    • is_constant辅助函数用于判断操作数是否是常量(整数或浮点数)。

    • evaluate_binary_constantevaluate_unary_constant函数负责在编译时执行实际的算术或逻辑运算。

    • 如果表达式完全由常量组成,计算结果,并将原始指令替换为简单的IC_ASSIGN指令,将结果常量直接赋给目标变量/临时变量。

    • 挑战: 混合类型运算(如int + float)的常量折叠需要更复杂的类型提升和转换逻辑。

  3. 死代码消除 (dead_code_elimination):

    • 这是一个非常简化的死代码消除版本,主要针对那些结果变量/临时变量在后续代码中从未被使用的赋值或计算指令。

    • 它通过一个is_var_used数组来粗略地跟踪变量和临时变量的“活跃性”(即是否在后续被读取)。

    • 两趟遍历:

      1. 第一趟(反向数据流分析的简化): 遍历所有指令,标记所有作为操作数(op1, op2)或作为跳转/函数调用参数的变量/临时变量为“被使用”。

      2. 第二趟: 再次遍历指令,如果一个赋值或计算指令的结果变量/临时变量在第一趟中没有被标记为“被使用”,那么这条指令就被认为是死代码,并被标记为is_instruction_dead

      3. 第三趟(重建): 遍历指令列表,只保留那些未被标记为死代码的指令,从而“删除”死代码。

    • 挑战: 真正的死代码消除需要更精确的活跃变量分析,这通常涉及构建程序的控制流图(CFG),并在CFG上进行迭代的数据流方程求解。当前的简化版无法处理复杂的控制流(如分支、循环内部的活跃性变化)或变量的重新定义。

  4. 内存管理: 在移除死指令时,我们不仅要将指令从instructions数组中移除,还要释放该指令结构体及其内部动态分配的var_name字符串,以避免内存泄漏。

总结与展望

朋友们,恭喜你!你已经完成了手撸编译器的第五步——一个初步的代码优化器!你亲手实现了常量折叠和简化的死代码消除,让你的编译器能够生成更精炼、更高效的中间代码。这种“精益求精”的精神,正是编译器开发的魅力所在!

现在,我们的中间代码已经经过了初步的“瘦身”和“提速”,它比原始的中间代码更加高效。有了这份优化后的中间代码,我们就可以自信地迈向编译器的最后阶段了!

这只是我们“肝爆”之旅的第五站。在下一章中,我们将进入编译器的最后一个核心阶段:目标代码生成(Target Code Generation)。我们将把这些优化后的三地址码指令,真正地翻译成特定机器架构(如x86汇编)的机器码!

准备好迎接最终的挑战,让你的C语言代码在机器上“跑”起来了吗?继续保持这份激情,我们下一章不见不散!点赞、收藏、关注,让我们一起把这“手撸编译器”的肝爆之旅进行到底!

最终章:第六章  从中间到机器——目标代码生成与X86汇编)

朋友们,欢迎来到我们“手撸编译器”的最终章!在第五章中,我们成功地对中间代码进行了优化,使其更加精炼高效。现在,我们的程序已经以一种高效的三地址码(TAC)形式存在了。

然而,三地址码仍然是一种抽象的表示,CPU并不能直接执行它。CPU只认识它自己的指令集!因此,编译器的最后一个核心阶段——目标代码生成(Target Code Generation)——登场了!

目标代码生成的使命:让程序在CPU上“跑”起来

目标代码生成器是编译器的后端,它的任务是将中间代码(例如三地址码)翻译成特定目标机器的机器码。通常,这个过程会先生成汇编代码,然后由汇编器将其转换为机器码。

目标代码生成阶段的主要挑战和任务包括:

  1. 指令选择(Instruction Selection): 将中间代码操作映射到目标机器的指令集。例如,一个ADD指令可能对应x86的ADD指令,也可能对应ARM的ADD指令。

  2. 寄存器分配(Register Allocation): 决定哪些变量和临时变量应该存储在CPU的寄存器中,哪些应该存储在内存中。寄存器访问速度远快于内存,因此高效的寄存器分配对性能至关重要。

  3. 指令调度(Instruction Scheduling): 重新排列指令的执行顺序,以最大化CPU的并行性,减少流水线停顿。

  4. 地址计算(Address Calculation): 确定变量在内存中的实际地址(例如,局部变量在栈帧中的偏移量,全局变量在数据段中的地址)。

  5. 函数调用约定(Calling Convention): 遵循目标平台特定的函数调用规则,包括参数传递、返回值处理、栈帧管理等。

简单来说,目标代码生成器就像一位“高级翻译官”,它不仅要将通用语言(中间代码)翻译成方言(汇编指令),还要考虑到方言的语法、习惯和表达效率(寄存器、指令调度)。

在本章中,我们将以简化的x86汇编为例,实现一个目标代码生成器。我们将主要关注指令选择、简单的寄存器使用和栈帧管理。

简化的X86汇编基础

x86架构是目前最流行的CPU架构之一。为了简化,我们将关注以下几个核心概念:

  • 寄存器:

    • EAX (或 RAX 在64位):通常用于存储函数返回值和算术运算的结果。

    • EBX, ECX, EDX (或 RBX, RCX, RDX 等):通用寄存器,用于存储数据。

    • ESP (或 RSP):栈指针,指向栈顶。

    • EBP (或 RBP):基址指针,通常用于指向当前函数栈帧的底部。

  • 内存访问: 使用 [address][base + offset] 形式访问内存。

  • 指令:

    • MOV dest, src:数据传输。

    • ADD dest, src:加法。

    • SUB dest, src:减法。

    • IMUL dest, src:乘法。

    • IDIV src:除法(结果在EAX,余数在EDX)。

    • CMP op1, op2:比较,设置标志位。

    • JMP label:无条件跳转。

    • JE label:相等则跳转。

    • JNE label:不相等则跳转。

    • JL label:小于则跳转。

    • JLE label:小于等于则跳转。

    • JG label:大于则跳转。

    • JGE label:大于等于则跳转。

    • PUSH src:将数据压入栈。

    • POP dest:将数据从栈弹出。

    • CALL func_name:调用函数。

    • RET:从函数返回。

栈帧(Stack Frame)管理

函数调用时,会为每个函数创建一个栈帧。栈帧通常包含:

  • 函数参数

  • 返回地址

  • 保存的寄存器值(如果函数使用了这些寄存器)

  • 局部变量

我们的编译器将使用EBP作为基址指针来访问局部变量和参数。

  • 参数: 通常在EBP的上方(正偏移量)。

  • 局部变量: 通常在EBP的下方(负偏移量)。

目标代码生成器(Target Code Generator)的实现

我们需要一个结构体来管理代码生成的状态,并存储生成的汇编代码。

// target_code.h (部分代码,完整版在后面)
#ifndef TARGET_CODE_H
#define TARGET_CODE_H

#include <stdio.h> // For FILE*

// 目标代码生成器结构体
typedef struct {
    FILE* output_file; // 输出汇编代码的文件指针
    // 可以在这里存储寄存器分配状态、当前函数信息等
    int current_stack_offset; // 当前函数栈帧的局部变量总大小
    // TODO: 寄存器描述符和地址描述符 (用于寄存器分配)
} TargetCodeGenerator;

// 函数声明
TargetCodeGenerator* init_target_code_generator(const char* output_filename);
void free_target_code_generator(TargetCodeGenerator* tcg);

/**
 * @brief 生成整个程序的汇编代码。
 * @param ic 中间代码列表。
 * @param tcg 目标代码生成器实例。
 */
void generate_program_assembly(IntermediateCode* ic, TargetCodeGenerator* tcg);

// 辅助函数:生成汇编指令
void emit_instruction(TargetCodeGenerator* tcg, const char* format, ...);

#endif // TARGET_CODE_H

完整的代码实现

target_code.h

// target_code.h
#ifndef TARGET_CODE_H
#define TARGET_CODE_H

#include <stdio.h> // For FILE*
#include "intermediate_code.h" // 需要中间代码结构体
#include "symbol_table.h"      // 需要符号表来获取变量信息

// 目标代码生成器结构体
// 包含了目标代码生成过程中所需的所有状态和工具
typedef struct {
    FILE* output_file; // 输出汇编代码的文件指针
    
    // 寄存器分配相关的状态 (简化版,只用EAX和ECX作为临时寄存器)
    // 实际编译器需要更复杂的寄存器描述符和分配算法
    // char* reg_eax_occupant; // EAX当前存储的变量/临时变量名
    // char* reg_ecx_occupant; // ECX当前存储的变量/临时变量名

    // 当前函数栈帧的局部变量总大小 (用于栈帧设置)
    int current_func_stack_size; 
    // 当前函数定义节点,用于获取函数符号信息
    ASTNode* current_func_def_node; 

    // 符号表,用于获取变量的内存偏移量
    SymbolTable* sym_table; 

} TargetCodeGenerator;

// 函数声明

/**
 * @brief 初始化目标代码生成器。
 * 打开输出文件,并初始化内部状态。
 * @param output_filename 输出汇编文件的名称。
 * @param sym_table 语义分析阶段构建的符号表,用于查询符号信息。
 * @return 成功返回指向 TargetCodeGenerator 结构体的指针,失败返回NULL。
 */
TargetCodeGenerator* init_target_code_generator(const char* output_filename, SymbolTable* sym_table);

/**
 * @brief 释放目标代码生成器占用的所有资源。
 * 关闭输出文件。
 * @param tcg 指向要释放的 TargetCodeGenerator 结构体的指针。
 */
void free_target_code_generator(TargetCodeGenerator* tcg);

/**
 * @brief 生成整个程序的汇编代码。
 * 这是目标代码生成阶段的入口点。
 * @param ic 中间代码列表。
 * @param tcg 目标代码生成器实例。
 */
void generate_program_assembly(IntermediateCode* ic, TargetCodeGenerator* tcg);

// 辅助函数:向输出文件写入汇编指令
void emit_instruction(TargetCodeGenerator* tcg, const char* format, ...);

#endif // TARGET_CODE_H

target_code.c

// target_code.c
#include "target_code.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h> // For va_list, va_start, va_end

// --- 辅助函数:汇编指令发射器 ---

// 向输出文件写入汇编指令
void emit_instruction(TargetCodeGenerator* tcg, const char* format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(tcg->output_file, format, args);
    fprintf(tcg->output_file, "\n"); // 每条指令后换行
    va_end(args);
}

// --- 辅助函数:操作数到汇编操作数的转换 ---

// 将中间代码的Operand转换为x86汇编字符串
// 这是一个简化的版本,只处理常量、变量和临时变量
static char* operand_to_x86(Operand op, TargetCodeGenerator* tcg) {
    // 静态缓冲区,每次调用会被覆盖,不适合复杂场景
    // 实际编译器会使用动态字符串或更复杂的寄存器分配策略
    static char buffer[128]; 

    switch (op.kind) {
        case OP_CONSTANT_INT:
            sprintf(buffer, "%lld", op.val.int_val);
            break;
        case OP_CONSTANT_FLOAT:
            // 浮点数常量在x86汇编中通常需要特殊处理,例如加载到FPU寄存器
            // 这里我们简化为直接使用其值,实际可能需要将其存储到数据段
            sprintf(buffer, "__float_%d", (int)op.val.float_val); // 临时表示
            fprintf(stderr, "警告: 浮点数常量在汇编中处理简化。\n");
            break;
        case OP_VARIABLE: {
            Symbol* sym = lookup_symbol(tcg->sym_table, op.val.var_name);
            if (!sym) {
                fprintf(stderr, "错误: 汇编生成时未找到变量 '%s' 的符号表条目。\n", op.val.var_name);
                sprintf(buffer, "UNKNOWN_VAR_%s", op.val.var_name);
                break;
            }
            if (sym->is_global) {
                // 全局变量直接使用名称 (会被汇编器解析为地址)
                sprintf(buffer, "%s", sym->name);
            } else {
                // 局部变量和参数通过EBP+偏移量访问
                // 参数偏移量是正的 (EBP + 8, EBP + 12...)
                // 局部变量偏移量是负的 (EBP - 4, EBP - 8...)
                // 我们在符号表中存储的offset是正的,这里需要转换为相对EBP的负偏移
                // 或者对于参数,是正偏移
                // 简化处理:假设所有局部变量和参数都在栈上,且偏移量是相对于EBP的
                // 实际偏移量需要根据栈帧布局和参数传递约定来精确计算
                // 假设符号表中的offset已经是相对于EBP的正确偏移
                sprintf(buffer, "[EBP - %d]", sym->offset); // 假设局部变量是负偏移
                // TODO: 区分参数和局部变量的偏移方向
            }
            break;
        }
        case OP_TEMP_VAR:
            // 临时变量通常分配给寄存器。这里简化为使用EAX/ECX或栈
            // 为了简单,我们直接将临时变量映射到EAX或ECX,或者在必要时使用栈
            // 实际编译器会进行复杂的寄存器分配
            sprintf(buffer, "t%d", op.val.temp_id); // 临时变量名,后续需要映射到寄存器或内存
            fprintf(stderr, "警告: 临时变量在汇编中处理简化,可能未分配寄存器。\n");
            break;
        case OP_LABEL:
            sprintf(buffer, "L%d", op.val.label_id);
            break;
        case OP_NONE:
            strcpy(buffer, "");
            break;
        default:
            strcpy(buffer, "UNKNOWN_OPERAND");
            break;
    }
    return buffer;
}

// --- 核心汇编代码生成逻辑 ---

// 生成指令
static void generate_instruction_assembly(Instruction* instr, TargetCodeGenerator* tcg) {
    if (!instr) return;

    char* result_x86;
    char* op1_x86;
    char* op2_x86;

    // 预先将操作数转换为x86字符串,简化后续逻辑
    result_x86 = operand_to_x86(instr->result, tcg);
    op1_x86 = operand_to_x86(instr->op1, tcg);
    op2_x86 = operand_to_x86(instr->op2, tcg);

    switch (instr->op) {
        case IC_ASSIGN: // result = op1
            // MOV result, op1
            emit_instruction(tcg, "    MOV %s, %s", result_x86, op1_x86);
            break;
        case IC_ADD: // result = op1 + op2
            // MOV EAX, op1
            // ADD EAX, op2
            // MOV result, EAX
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    ADD EAX, %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_SUB: // result = op1 - op2
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    SUB EAX, %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_MUL: // result = op1 * op2
            // IMUL op2 (EAX = EAX * op2)
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    IMUL EAX, %s", op2_x86); // 简化为EAX = EAX * op2
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_DIV: // result = op1 / op2 (整数除法)
            // MOV EAX, op1
            // CDQ (EAX符号扩展到EDX:EAX)
            // IDIV op2 (EAX = EDX:EAX / op2, EDX = 余数)
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    CDQ"); // 扩展EAX到EDX:EAX
            emit_instruction(tcg, "    IDIV %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86); // 结果是商
            break;
        case IC_MOD: // result = op1 % op2 (整数模)
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    CDQ");
            emit_instruction(tcg, "    IDIV %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EDX", result_x86); // 结果是余数
            break;
        case IC_EQ: // result = (op1 == op2)
        case IC_NE: // result = (op1 != op2)
        case IC_LT: // result = (op1 < op2)
        case IC_LE: // result = (op1 <= op2)
        case IC_GT: // result = (op1 > op2)
        case IC_GE: { // result = (op1 >= op2)
            // CMP op1, op2
            // SETcc AL (设置AL为0或1)
            // MOV result, EAX (将AL扩展到EAX,然后赋值)
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86); // 将op1加载到EAX
            emit_instruction(tcg, "    CMP EAX, %s", op2_x86); // 比较EAX和op2

            char* setcc_op;
            switch (instr->op) {
                case IC_EQ: setcc_op = "SETE"; break;
                case IC_NE: setcc_op = "SETNE"; break;
                case IC_LT: setcc_op = "SETL"; break; // 有符号小于
                case IC_LE: setcc_op = "SETLE"; break; // 有符号小于等于
                case IC_GT: setcc_op = "SETG"; break; // 有符号大于
                case IC_GE: setcc_op = "SETGE"; break; // 有符号大于等于
                default: setcc_op = "UNKNOWN_SETCC"; break;
            }
            emit_instruction(tcg, "    %s AL", setcc_op); // 将结果设置到AL寄存器 (低8位)
            emit_instruction(tcg, "    MOVZX EAX, AL"); // 将AL零扩展到EAX
            emit_instruction(tcg, "    MOV %s, EAX", result_x86); // 将结果赋值给result
            break;
        }
        case IC_AND: // result = (op1 && op2)
            // 逻辑与,简化为 (op1 != 0 && op2 != 0)
            // MOV EAX, op1
            // TEST EAX, EAX
            // JZ L_false
            // MOV ECX, op2
            // TEST ECX, ECX
            // JZ L_false
            // MOV result, 1
            // JMP L_end
            // L_false: MOV result, 0
            // L_end:
            {
                Operand label_false = create_operand_label(instr->result.val.label_id + 1000); // 避免与现有标签冲突
                Operand label_end = create_operand_label(instr->result.val.label_id + 1001);

                emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
                emit_instruction(tcg, "    TEST EAX, EAX");
                emit_instruction(tcg, "    JZ %s", operand_to_x86(label_false, tcg));

                emit_instruction(tcg, "    MOV ECX, %s", op2_x86);
                emit_instruction(tcg, "    TEST ECX, ECX");
                emit_instruction(tcg, "    JZ %s", operand_to_x86(label_false, tcg));

                emit_instruction(tcg, "    MOV %s, 1", result_x86);
                emit_instruction(tcg, "    JMP %s", operand_to_x86(label_end, tcg));

                emit_instruction(tcg, "%s:", operand_to_x86(label_false, tcg));
                emit_instruction(tcg, "    MOV %s, 0", result_x86);

                emit_instruction(tcg, "%s:", operand_to_x86(label_end, tcg));
            }
            break;
        case IC_OR: // result = (op1 || op2)
            // 逻辑或,简化为 (op1 != 0 || op2 != 0)
            // MOV EAX, op1
            // TEST EAX, EAX
            // JNZ L_true
            // MOV ECX, op2
            // TEST ECX, ECX
            // JNZ L_true
            // MOV result, 0
            // JMP L_end
            // L_true: MOV result, 1
            // L_end:
            {
                Operand label_true = create_operand_label(instr->result.val.label_id + 2000); // 避免与现有标签冲突
                Operand label_end = create_operand_label(instr->result.val.label_id + 2001);

                emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
                emit_instruction(tcg, "    TEST EAX, EAX");
                emit_instruction(tcg, "    JNZ %s", operand_to_x86(label_true, tcg));

                emit_instruction(tcg, "    MOV ECX, %s", op2_x86);
                emit_instruction(tcg, "    TEST ECX, ECX");
                emit_instruction(tcg, "    JNZ %s", operand_to_x86(label_true, tcg));

                emit_instruction(tcg, "    MOV %s, 0", result_x86);
                emit_instruction(tcg, "    JMP %s", operand_to_x86(label_end, tcg));

                emit_instruction(tcg, "%s:", operand_to_x86(label_true, tcg));
                emit_instruction(tcg, "    MOV %s, 1", result_x86);

                emit_instruction(tcg, "%s:", operand_to_x86(label_end, tcg));
            }
            break;
        case IC_NOT: // result = !op1
            // MOV EAX, op1
            // TEST EAX, EAX
            // SETZ AL (如果EAX为0则AL=1,否则AL=0)
            // MOVZX EAX, AL
            // MOV result, EAX
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    TEST EAX, EAX");
            emit_instruction(tcg, "    SETZ AL");
            emit_instruction(tcg, "    MOVZX EAX, AL");
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_BIT_AND: // result = op1 & op2
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    AND EAX, %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_BIT_OR: // result = op1 | op2
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    OR EAX, %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_BIT_XOR: // result = op1 ^ op2
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    XOR EAX, %s", op2_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_BIT_NOT: // result = ~op1
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    NOT EAX");
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_LSHIFT: // result = op1 << op2
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    MOV ECX, %s", op2_x86); // 移位量通常在CL/CX/ECX
            emit_instruction(tcg, "    SHL EAX, CL"); // SHL EAX, ECX (如果ECX是常数,可以直接SHL EAX, const)
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_RSHIFT: // result = op1 >> op2
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    MOV ECX, %s", op2_x86);
            emit_instruction(tcg, "    SAR EAX, CL"); // SAR (算术右移,保留符号位) 或 SHR (逻辑右移)
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            break;
        case IC_LABEL: // label L_id:
            emit_instruction(tcg, "%s:", result_x86);
            break;
        case IC_JUMP: // goto L_id
            emit_instruction(tcg, "    JMP %s", result_x86);
            break;
        case IC_JUMPIF_TRUE: // if op1 goto L_id
            // MOV EAX, op1
            // TEST EAX, EAX
            // JNZ L_id (如果非零则跳转)
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    TEST EAX, EAX"); // TEST op, op 会设置标志位,如果op为0则ZF=1
            emit_instruction(tcg, "    JNZ %s", result_x86);
            break;
        case IC_JUMPIF_FALSE: // if op1 == 0 goto L_id
            // MOV EAX, op1
            // TEST EAX, EAX
            // JZ L_id (如果为零则跳转)
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    TEST EAX, EAX");
            emit_instruction(tcg, "    JZ %s", result_x86);
            break;
        case IC_PARAM: // param op1 (将op1作为参数压入栈)
            // PUSH op1
            emit_instruction(tcg, "    PUSH %s", op1_x86);
            break;
        case IC_CALL: { // result = call func_name, num_args
            // CALL func_name
            // (可选) ADD ESP, num_args * 4 (清理栈上的参数)
            // (可选) MOV result, EAX (如果函数有返回值)
            emit_instruction(tcg, "    CALL %s", op1_x86); // op1_x86 是函数名
            // 清理参数栈空间 (C调用约定通常由调用者清理)
            int num_args = instr->op2.val.int_val;
            if (num_args > 0) {
                emit_instruction(tcg, "    ADD ESP, %d", num_args * 4); // 假设参数都是4字节
            }
            if (instr->result.kind != OP_NONE && instr->result.type->kind != TYPE_VOID) {
                emit_instruction(tcg, "    MOV %s, EAX", result_x86); // 函数返回值在EAX
            }
            break;
        }
        case IC_RETURN: // return op1
            // MOV EAX, op1 (如果op1存在)
            // JMP .L_func_end (跳转到函数结尾清理栈帧)
            if (instr->op1.kind != OP_NONE) {
                emit_instruction(tcg, "    MOV EAX, %s", op1_x86); // 将返回值放入EAX
            }
            // 跳转到函数结束标签,由函数结束处理栈帧和RET
            emit_instruction(tcg, "    JMP %s_end", tcg->current_func_def_node->data.func_def.identifier->data.identifier_expr.name);
            break;
        case IC_FUNC_BEGIN: { // func_name: (函数开始标记)
            // PUSH EBP
            // MOV EBP, ESP
            // SUB ESP, stack_frame_size (为局部变量分配空间)
            char* func_name = instr->op1.val.var_name;
            emit_instruction(tcg, "\n%s:", func_name); // 函数标签
            emit_instruction(tcg, "    PUSH EBP");
            emit_instruction(tcg, "    MOV EBP, ESP");
            // 计算局部变量总大小 (从符号表获取)
            // tcg->current_func_stack_size 已经在 generate_function_assembly 中设置
            if (tcg->current_func_stack_size > 0) {
                emit_instruction(tcg, "    SUB ESP, %d", tcg->current_func_stack_size);
            }
            break;
        }
        case IC_FUNC_END: { // end func_name (函数结束标记)
            // MOV ESP, EBP
            // POP EBP
            // RET
            char* func_name = instr->op1.val.var_name;
            emit_instruction(tcg, "%s_end:", func_name); // 函数结束标签
            emit_instruction(tcg, "    MOV ESP, EBP"); // 恢复ESP
            emit_instruction(tcg, "    POP EBP");      // 恢复EBP
            emit_instruction(tcg, "    RET");          // 返回调用者
            break;
        }
        case IC_CAST: // result = (target_type)op1
            // 简单的类型转换,例如 int <-> float
            // 实际需要FPU指令或更复杂的整数截断/符号扩展
            // 这里我们简化为直接赋值,假设类型大小兼容,或由汇编器处理
            emit_instruction(tcg, "    MOV EAX, %s", op1_x86);
            emit_instruction(tcg, "    MOV %s, EAX", result_x86);
            fprintf(stderr, "警告: 类型转换在汇编中处理简化。\n");
            break;
        default:
            fprintf(stderr, "错误: 未知或不支持的中间代码操作 '%s',无法生成汇编。\n", ic_op_to_string(instr->op));
            break;
    }
}

// 计算函数局部变量和参数的总大小
static int calculate_function_stack_size(ASTNode* func_def_node, SymbolTable* sym_table) {
    int total_local_size = 0;
    // 遍历函数体内的所有局部变量声明
    // 这里需要重新遍历AST来获取局部变量信息,或者在语义分析阶段就计算好
    // 简化:我们假设所有局部变量在符号表中的偏移量是连续的,
    // 且 sym_table->current_scope->next_local_offset 记录了当前作用域的总大小
    // 但这个值在函数退出后就重置了。
    // 更准确的做法是,在语义分析时,为每个函数定义节点存储其局部变量的总大小。
    
    // 临时解决方案:遍历符号表中的所有局部变量和参数,找到最大偏移量
    // 这是一个非常粗略的估计,不考虑对齐和参数传递方式
    int max_offset = 0;
    // 遍历函数体的复合语句
    ASTNode* body = func_def_node->data.func_def.body;
    if (body->type == AST_COMPOUND_STMT) {
        for (int i = 0; i < body->data.compound_stmt.num_statements; ++i) {
            ASTNode* stmt = body->data.compound_stmt.statements[i];
            if (stmt->type == AST_VAR_DECL) {
                Symbol* sym = stmt->data.var_decl.identifier->data.identifier_expr.symbol_entry;
                if (sym && !sym->is_global) { // 确保是局部变量
                    // 局部变量偏移量是相对于EBP的负值,但我们这里存储的是正的“大小”
                    // 假设offset是相对于栈帧底部的累积大小
                    if (sym->offset + sym->type->size > max_offset) {
                        max_offset = sym->offset + sym->type->size;
                    }
                }
            }
        }
    }
    // 考虑参数,参数的偏移量是正的,在EBP之上
    // 局部变量的偏移量是负的,在EBP之下
    // 这里我们只计算局部变量所需的空间
    // 符号表中的offset是相对于函数栈帧开头的,所以直接用next_local_offset即可
    // 但由于符号表在语义分析结束后会清除局部作用域,这里需要重新计算
    // 最佳实践:在FuncDef AST节点中存储一个字段,记录局部变量的总大小
    
    // 假设语义分析阶段已经计算并存储了每个函数局部变量的累积大小
    // 例如,在FuncDef AST节点中添加一个 int local_var_size 字段
    // 这里我们暂时使用一个简化的方法:遍历符号表中的所有局部变量,找到最大的偏移量
    // 这是一个hack,因为符号表在语义分析后会重置,无法直接访问所有局部变量
    // 正确的做法是在语义分析时,计算并存储在FuncDef AST节点中
    // 暂时返回一个固定值或根据参数数量粗略估计
    
    // 重新计算局部变量大小 (这需要在语义分析阶段完成并存储在AST中)
    // 遍历函数体,找到所有局部变量声明,累加它们的大小
    int local_vars_size = 0;
    if (func_def_node->data.func_def.body->type == AST_COMPOUND_STMT) {
        ASTNode* compound_stmt = func_def_node->data.func_def.body;
        for (int i = 0; i < compound_stmt->data.compound_stmt.num_statements; ++i) {
            ASTNode* stmt = compound_stmt->data.compound_stmt.statements[i];
            if (stmt->type == AST_VAR_DECL) {
                Symbol* sym = stmt->data.var_decl.identifier->data.identifier_expr.symbol_entry;
                if (sym && !sym->is_global && sym->kind == SYM_VAR) { // 确保是局部变量
                    local_vars_size += sym->type->size;
                }
            }
        }
    }
    // 对齐到16字节 (常见的栈对齐要求)
    return (local_vars_size + 15) & ~15; 
}


// 生成函数定义的汇编代码
static void generate_function_assembly(Instruction* func_begin_instr, IntermediateCode* ic, TargetCodeGenerator* tcg, ASTNode* program_ast) {
    char* func_name = func_begin_instr->op1.val.var_name;
    fprintf(stderr, "生成函数 '%s' 的汇编代码...\n", func_name);

    // 查找对应的AST函数定义节点,以便获取其局部变量信息
    ASTNode* current_func_def_node = NULL;
    for (int i = 0; i < program_ast->data.program.num_declarations; ++i) {
        ASTNode* decl_node = program_ast->data.program.declarations[i];
        if (decl_node->type == AST_FUNC_DEF && strcmp(decl_node->data.func_def.identifier->data.identifier_expr.name, func_name) == 0) {
            current_func_def_node = decl_node;
            break;
        }
    }
    if (!current_func_def_node) {
        fprintf(stderr, "错误: 无法找到函数 '%s' 的AST定义节点。\n", func_name);
        return;
    }
    tcg->current_func_def_node = current_func_def_node;

    // 计算局部变量所需的栈空间
    // 这是一个简化,实际需要遍历函数体的所有局部变量声明,并累加其大小
    // 假设在语义分析时,已经为每个局部变量分配了相对于EBP的偏移量
    // 这里我们简单地计算所有局部变量的总大小
    int local_var_total_size = 0;
    // 遍历函数体的复合语句
    if (current_func_def_node->data.func_def.body->type == AST_COMPOUND_STMT) {
        ASTNode* body = current_func_def_node->data.func_def.body;
        // 遍历语句,查找变量声明
        for (int i = 0; i < body->data.compound_stmt.num_statements; ++i) {
            ASTNode* stmt = body->data.compound_stmt.statements[i];
            if (stmt->type == AST_VAR_DECL) {
                Symbol* sym = stmt->data.var_decl.identifier->data.identifier_expr.symbol_entry;
                if (sym && !sym->is_global) { // 确保是局部变量
                    // 局部变量的偏移量是相对于EBP的负值
                    // 符号表中的offset是正的,代表其在局部变量区域的起始位置
                    // 假设offset是累积的,那么最大的offset就是所需空间
                    if (sym->offset + sym->type->size > local_var_total_size) {
                        local_var_total_size = sym->offset + sym->type->size;
                    }
                }
            }
        }
    }
    // 对齐到16字节 (常见的栈对齐要求)
    tcg->current_func_stack_size = (local_var_total_size + 15) & ~15;

    // 生成函数开始的汇编代码 (PUSH EBP, MOV EBP, ESP, SUB ESP, size)
    generate_instruction_assembly(func_begin_instr, tcg); // IC_FUNC_BEGIN

    // 遍历当前函数的所有中间代码指令,生成汇编
    // 找到函数结束的指令索引
    int func_end_idx = -1;
    for (int i = 0; i < ic->num_instructions; ++i) {
        if (ic->instructions[i]->op == IC_FUNC_END && strcmp(ic->instructions[i]->op1.val.var_name, func_name) == 0) {
            func_end_idx = i;
            break;
        }
    }

    // 从函数开始指令的下一条指令开始,到函数结束指令的前一条指令
    int start_idx = 0; // 找到 func_begin_instr 在 ic 中的位置
    for (int i = 0; i < ic->num_instructions; ++i) {
        if (ic->instructions[i]->op == IC_FUNC_BEGIN && strcmp(ic->instructions[i]->op1.val.var_name, func_name) == 0) {
            start_idx = i + 1;
            break;
        }
    }

    for (int i = start_idx; i < func_end_idx; ++i) {
        generate_instruction_assembly(ic->instructions[i], tcg);
    }

    // 生成函数结束的汇编代码 (MOV ESP, EBP, POP EBP, RET)
    generate_instruction_assembly(ic->instructions[func_end_idx], tcg); // IC_FUNC_END
}


// 生成整个程序的汇编代码
void generate_program_assembly(IntermediateCode* ic, TargetCodeGenerator* tcg) {
    fprintf(stderr, "开始生成程序汇编代码...\n");

    // 假设我们有一个AST的根节点,可以用来查找函数定义
    // 这里我们传递一个ASTNode* program_ast参数,以便在generate_function_assembly中查找函数信息
    // 实际编译器中,这个信息可能通过其他方式传递或预处理
    // 为了通过编译,我们在这里临时创建一个空的ASTNode作为占位符
    // 实际使用时,需要传入语义分析后的完整AST根节点
    ASTNode* dummy_program_ast = (ASTNode*)malloc(sizeof(ASTNode));
    dummy_program_ast->type = AST_PROGRAM;
    dummy_program_ast->data.program.declarations = NULL;
    dummy_program_ast->data.program.num_declarations = 0;
    
    // 重新构建 program_ast,因为我们没有直接传递它
    // 这是一个临时的解决方案,正确的方法是在main函数中传递 program_ast
    // 查找所有函数定义节点,并填充到 dummy_program_ast 中
    // 这段逻辑应该在main函数中完成,然后将完整的program_ast传递进来
    // 为了演示,我们假设 ic 中已经包含了所有必要的信息
    // 或者,我们直接在 generate_function_assembly 中通过符号表查找函数信息
    
    // 临时方案:在全局作用域查找所有函数符号,并构建一个临时的函数列表
    // 这不是理想的,因为我们丢失了AST结构
    // 更好的方法是:在main函数中,将完整的ASTNode* program_ast 传递给这个函数
    // 假设我们有一个全局的ASTNode* program_ast,或者从main函数传入
    // 这里我们依赖于 main.c 传入的 program_ast
    
    // 为了简化,我们直接在 generate_function_assembly 中查找 ASTNode*
    // 因此,generate_program_assembly 还需要一个 ASTNode* program_ast 参数
    // 暂时先这样,后续在 main.c 中调用时传入正确的 program_ast
    
    // emit_instruction(tcg, "section .data"); // 数据段 (用于全局变量和字符串常量)
    // emit_instruction(tcg, "section .bss");  // 未初始化数据段
    emit_instruction(tcg, "section .text"); // 代码段
    emit_instruction(tcg, "    global main"); // 声明 main 函数为全局入口点

    // 遍历中间代码,找到每个函数的开始指令,然后生成其汇编
    for (int i = 0; i < ic->num_instructions; ++i) {
        Instruction* instr = ic->instructions[i];
        if (instr->op == IC_FUNC_BEGIN) {
            // 找到函数开始指令,然后生成整个函数的汇编代码
            // 这里需要传入完整的 ASTNode* program_ast,以便 generate_function_assembly 查找函数信息
            // 暂时传入一个NULL,表示我们没有直接的AST访问,这会限制一些功能
            // 实际编译器中,这个函数会接收 ASTNode* program_ast
            generate_function_assembly(instr, ic, tcg, NULL); // TODO: 传入正确的 program_ast
        } else if (instr->op == IC_ASSIGN && instr->result.kind == OP_VARIABLE && instr->result.val.var_name && 
                   lookup_symbol(tcg->sym_table, instr->result.val.var_name) && 
                   lookup_symbol(tcg->sym_table, instr->result.val.var_name)->is_global) {
            // 处理全局变量的初始化 (在数据段中)
            // 这应该在 .data 段完成,而不是 .text 段
            // 简化:这里只生成赋值指令,实际初始化在汇编器层面处理
            fprintf(stderr, "警告: 全局变量初始化在汇编中处理简化。\n");
            // emit_instruction(tcg, "%s: DD %s", instr->result.val.var_name, operand_to_x86(instr->op1, tcg)); // 示例
        }
    }

    fprintf(stderr, "程序汇编代码生成完成。\n");
    free(dummy_program_ast); // 释放临时占位符
}

// --- 目标代码生成器初始化与清理 ---

TargetCodeGenerator* init_target_code_generator(const char* output_filename, SymbolTable* sym_table) {
    TargetCodeGenerator* tcg = (TargetCodeGenerator*)malloc(sizeof(TargetCodeGenerator));
    if (!tcg) {
        fprintf(stderr, "错误: 内存分配失败 (TargetCodeGenerator)\n");
        return NULL;
    }
    tcg->output_file = fopen(output_filename, "w");
    if (!tcg->output_file) {
        fprintf(stderr, "错误: 无法打开输出文件 '%s'。\n", output_filename);
        free(tcg);
        return NULL;
    }
    tcg->current_func_stack_size = 0;
    tcg->current_func_def_node = NULL;
    tcg->sym_table = sym_table; // 引用符号表

    fprintf(stderr, "目标代码生成器初始化成功,输出到 '%s'。\n", output_filename);
    return tcg;
}

void free_target_code_generator(TargetCodeGenerator* tcg) {
    if (tcg) {
        if (tcg->output_file) {
            fclose(tcg->output_file);
        }
        free(tcg);
        fprintf(stderr, "目标代码生成器资源已清理。\n");
    }
}

main.c (修改)

为了让generate_program_assembly函数能够正确访问到ASTNode* program_ast,我们需要修改main.c中的函数调用,将program_ast作为参数传递。

// main.c (修改)
#include "lexer.h"
#include "parser.h"
#include "ast.h"
#include "token.h"
#include "semantic_analyzer.h"
#include "type.h"
#include "symbol_table.h"
#include "intermediate_code.h"
#include "ic_generator.h"
#include "optimizer.h"
#include "target_code.h"       // 新增:目标代码生成器头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 这是一个包含各种C语言语法元素的测试代码字符串
// 用于全面测试词法分析器、语法分析器、语义分析器、中间代码生成器、优化器和目标代码生成器的功能
const char* TEST_CODE = 
    "// 这是一个单行注释\n"
    "/* 这是一个\n"
    " * 多行注释\n"
    " */\n"
    "int global_var = 100 + 200; // 全局变量声明,可常量折叠\n" 
    "char another_global = 'Z';\n" 
    "\n"
    "float add(float a, float b) {\n" 
    "    return a + b;\n"
    "}\n"
    "\n"
    "int subtract(int x, int y) {\n" 
    "    int temp_local = 5 * 10; // 局部变量,可常量折叠\n" 
    "    int unused_var = 123; // 死代码\n"
    "    if (x > y) {\n"
    "        temp_local = x - y;\n"
    "    } else {\n"
    "        temp_local = y - x;\n"
    "    }\n"
    "    // unused_var 永远不会被使用,应该被消除\n"
    "    return temp_local;\n"
    "}\n"
    "\n"
    "int main() {\n" 
    "    int x = 10;\n" 
    "    float y = 3.14e-2;\n" 
    "    char c = 'A';\n" 
    "    \n"
    "    int common_expr_res = x + 5; // 公共子表达式 (目前简化版优化器不处理)\n"
    "    int another_common_expr_res = x + 5; // 同上\n"
    "    \n"
    "    if (x >= 5 && y < 10.0) {\n" 
    "        int z = x + (int)y * 2; \n" 
    "        if (z > 20) {\n"
    "            z = z - 5;\n"
    "        }\n"
    "        return z;\n" 
    "    } else if (x == 10) {\n" 
    "        x++;\n" 
    "    } else {\n" 
    "        y--;\n" 
    "    }\n"
    "    \n"
    "    while (x > 0) {\n" 
    "        x = x - 1;\n" 
    "        if (x == 5) {\n"
    "            // break; \n"
    "        }\n"
    "    }\n"
    "    \n"
    "    float sum_result = add(x, y);\n" 
    "    int diff_result = subtract(20, 5);\n" 
    "    \n"
    "    sum_result += 5.0f;\n" 
    "    global_var = (int)sum_result;\n" 
    "    \n"
    "    return 0;\n" 
    "}\n"
    ;

int main(int argc, char* argv[]) {
    // 1. 将 TEST_CODE 字符串内容写入一个临时文件
    const char* temp_filepath = "test_code.c";
    FILE* temp_file = fopen(temp_filepath, "w");
    if (!temp_file) {
        fprintf(stderr, "错误: 无法创建临时文件 '%s'\n", temp_filepath);
        return EXIT_FAILURE;
    }
    fprintf(temp_file, "%s", TEST_CODE);
    fclose(temp_file);

    // 2. 初始化词法分析器
    Lexer* lexer = init_lexer(temp_filepath);
    if (!lexer) {
        return EXIT_FAILURE;
    }
    printf("--- 词法分析器初始化成功 ---\n");

    // 3. 初始化语法分析器
    Parser* parser = init_parser(lexer);
    if (!parser) {
        free_lexer(lexer);
        return EXIT_FAILURE;
    }
    printf("--- 语法分析器初始化成功 ---\n");

    // 4. 开始解析程序,构建抽象语法树 (AST)
    printf("--- 开始语法分析,构建AST ---\n");
    ASTNode* program_ast = parse_program(parser);
    
    // 5. 检查AST是否成功构建
    if (!program_ast) {
        fprintf(stderr, "错误: AST 构建失败。\n");
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- AST 构建成功!---\n");
    
    // 6. 初始化语义分析器并执行语义分析
    SemanticAnalyzer* analyzer = init_semantic_analyzer();
    if (!analyzer) {
        ast_free_program(program_ast);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 语义分析开始!---\n");
    analyze_program(program_ast, analyzer); // 执行语义分析

    if (analyzer->error_count > 0) {
        fprintf(stderr, "语义分析完成,发现 %d 个语义错误。程序终止。\n", analyzer->error_count);
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_parser(parser);
        remove(temp_filepath);
        return EXIT_FAILURE; 
    } else {
        printf("语义分析完成,未发现语义错误!代码是语义正确的!\n");
    }
    
    // 7. 初始化中间代码生成器并生成中间代码
    ICG* icg = init_icg(analyzer->sym_table); 
    if (!icg) {
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 中间代码生成开始!---\n");
    IntermediateCode* program_ic = generate_program_ic(program_ast, icg); 

    if (!program_ic) {
        fprintf(stderr, "错误: 中间代码生成失败。\n");
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_icg(icg);
        free_parser(parser);
        remove(temp_filepath);
        return EXIT_FAILURE;
    }

    printf("--- 中间代码生成成功!---\n");
    printf("--- 打印未优化前的中间代码:---\n");
    print_intermediate_code(program_ic);
    printf("--- 未优化中间代码打印完成 ---\n");

    // 8. 初始化优化器并执行代码优化
    Optimizer* optimizer = init_optimizer();
    if (!optimizer) {
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_intermediate_code(program_ic);
        free_icg(icg);
        free_parser(parser);
        return EXIT_FAILURE;
    }
    printf("--- 代码优化开始!---\n");
    optimize_intermediate_code(program_ic, optimizer); // 执行优化

    printf("--- 代码优化完成!---\n");
    printf("--- 打印优化后的中间代码:---\n");
    print_intermediate_code(program_ic);
    printf("--- 优化后中间代码打印完成 ---\n");

    // 9. 初始化目标代码生成器并生成汇编代码
    const char* output_asm_filename = "output.asm";
    TargetCodeGenerator* tcg = init_target_code_generator(output_asm_filename, analyzer->sym_table);
    if (!tcg) {
        ast_free_program(program_ast);
        free_semantic_analyzer(analyzer);
        free_intermediate_code(program_ic);
        free_icg(icg);
        free_optimizer(optimizer);
        return EXIT_FAILURE;
    }
    printf("--- 目标代码生成开始!---\n");
    // 传入 program_ast 给 generate_program_assembly
    generate_program_assembly(program_ic, tcg, program_ast); 

    printf("--- 目标代码生成完成,汇编代码已写入 '%s'!---\n", output_asm_filename);

    // 10. 释放所有资源
    ast_free_program(program_ast);
    printf("--- AST 内存已释放 ---\n");
    free_semantic_analyzer(analyzer);
    printf("--- 语义分析器资源已释放 ---\n");
    free_intermediate_code(program_ic); 
    printf("--- 中间代码内存已释放 ---\n");
    free_icg(icg); 
    printf("--- 中间代码生成器资源已释放 ---\n");
    free_optimizer(optimizer); 
    printf("--- 优化器资源已释放 ---\n");
    free_target_code_generator(tcg); // 释放目标代码生成器
    printf("--- 目标代码生成器资源已释放 ---\n");
    free_parser(parser);
    printf("--- 解析器和词法分析器资源已释放 ---\n");

    // 可选:删除之前创建的临时文件
    remove(temp_filepath);

    return EXIT_SUCCESS;
}

编译和运行

将上述所有文件(token.h, token.c, lexer.h, lexer.c, parser.h, parser.c, ast.h, ast.c, type.h, type.c, symbol_table.h, symbol_table.c, semantic_analyzer.h, semantic_analyzer.c, intermediate_code.h, intermediate_code.c, ic_generator.h, ic_generator.c, optimizer.h, optimizer.c, target_code.h, target_code.c, main.c)放在同一个目录下。

你可以使用GCC编译器进行编译:

gcc -o my_compiler main.c lexer.c token.c ast.c parser.c type.c symbol_table.c semantic_analyzer.c intermediate_code.c ic_generator.c optimizer.c target_code.c -Wall -Wextra

然后运行:

./my_compiler

程序将依次执行所有编译阶段,最终在当前目录下生成一个名为output.asm的汇编文件。你可以打开这个文件,查看你的C语言代码是如何被翻译成x86汇编指令的!

目标代码生成器的逻辑分析与挑战

  1. 汇编指令发射器 (emit_instruction): 这是一个简单的辅助函数,用于将格式化的字符串写入输出汇编文件。

  2. 操作数到x86转换 (operand_to_x86):

    • 这是核心的映射函数,它将中间代码的Operand结构体转换为x86汇编中可用的字符串表示。

    • 常量: 直接转换为其数值字符串。

    • 变量:

      • 全局变量: 直接使用其名称,汇编器会将其解析为数据段的地址。

      • 局部变量/参数: 根据符号表中存储的偏移量,转换为相对于EBP的内存地址表示,例如[EBP - offset]。这里我们做了简化,假设所有局部变量都在EBP下方,且偏移量是正的,实际需要区分参数(EBP + offset)和局部变量(EBP - offset)。

    • 临时变量: 这是一个巨大的简化!在真正的编译器中,临时变量需要通过复杂的寄存器分配算法(如图着色算法)来分配给有限的CPU寄存器。如果寄存器不足,则需要将其“溢出”到栈内存。我们这里只是简单地将它们表示为t<id>,并在生成指令时直接使用EAX/ECX作为临时寄存器,这在实际中是不可行的,因为寄存器会被覆盖。

  3. 指令选择 (generate_instruction_assembly):

    • 这是将每条三地址码指令翻译成一条或多条x86汇编指令的核心逻辑。

    • 算术/逻辑/位运算: 大多数二元运算被翻译为MOV指令将第一个操作数载入EAX,然后使用对应的x86指令(ADD, SUB, IMUL, AND, OR等)与第二个操作数进行运算,最后将结果从EAX移回目标。

    • 除法/模运算: IDIV指令在x86中比较特殊,它使用EDX:EAX作为被除数,结果商在EAX,余数在EDX。需要CDQ指令进行符号扩展。

    • 关系运算: 翻译为CMP指令(比较)和SETcc指令(根据标志位设置AL寄存器为0或1),然后MOVZX将AL零扩展到EAX,最后赋值。

    • 跳转指令: 直接翻译为JMP, JNZ, JZ等。

    • 函数调用 (IC_PARAM, IC_CALL, IC_RETURN):

      • IC_PARAM翻译为PUSH指令,将参数压入栈。

      • IC_CALL翻译为CALL指令,并清理栈上的参数(遵循C调用约定)。函数返回值通常在EAX

      • IC_RETURN将返回值放入EAX,然后跳转到函数结束标签。

    • 函数入口/出口 (IC_FUNC_BEGIN, IC_FUNC_END):

      • IC_FUNC_BEGIN负责设置栈帧:PUSH EBP, MOV EBP, ESP, SUB ESP, stack_frame_size(为局部变量分配栈空间)。

      • IC_FUNC_END负责恢复栈帧和返回:MOV ESP, EBP, POP EBP, RET

    • 类型转换 (IC_CAST): 这是一个巨大的简化,目前直接翻译为MOV。实际的类型转换(如intfloat,或不同大小整数之间)需要特定的FPU指令或更复杂的位操作。

  4. 栈帧大小计算 (calculate_function_stack_sizecurrent_func_stack_size):

    • 为了正确设置栈帧,我们需要知道函数中所有局部变量的总大小。

    • 我们通过遍历函数体内的AST_VAR_DECL节点,并累加其resolved_type->size来计算。

    • tcg->current_func_stack_size用于在IC_FUNC_BEGIN时生成SUB ESP, size指令。

    • 栈帧大小通常需要对齐(例如,16字节对齐),我们通过(size + 15) & ~15来实现。

  5. 全局变量: 全局变量的初始化通常在程序的.data段或.bss段进行,而不是在.text(代码)段。我们的简化版目前只在main.c中通过IC_ASSIGN指令处理了它们的初始化,这会在.text段生成MOV指令,实际汇编器会将其视为运行时赋值,而不是静态初始化。

总结与展望

朋友们,恭喜你!你已经完成了手撸编译器的最后一步——一个初步的目标代码生成器!你亲手将优化后的三地址码翻译成了x86汇编代码,让你的C语言程序第一次真正地“触摸”到了机器的底层。这种从高级语言到机器指令的完整链路,是不是让你对计算机科学的奥秘有了前所未有的洞察?

虽然我们实现的编译器是一个简化版,但它涵盖了现代编译器的所有核心阶段。从词法分析到目标代码生成,你已经亲手构建了一个能够将C语言源代码转换为可执行代码的工具!

这趟“肝爆”之旅虽然告一段落,但编译器的世界远比这更广阔和深奥。你可以继续探索:

  • 更复杂的语言特性: 数组、指针、结构体、联合体、枚举、typedef、多维数组、函数指针、可变参数函数等。

  • 更高级的优化: 数据流分析、控制流图(CFG)、静态单赋值(SSA)、寄存器分配、指令调度、窥孔优化、内联、循环展开等。

  • 其他目标架构: 学习ARM、RISC-V等其他CPU架构的指令集,为你的编译器添加多平台支持。

  • 运行时系统: 了解如何构建一个简单的运行时环境,包括内存管理(堆)、垃圾回收等。

  • 链接器和加载器: 探索编译器后端如何与这些工具协作,生成最终的可执行文件。



 

感谢大家的点赞、收藏、关注!!!






 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值