compiler-go/doc/汇编生成说明.md

5.2 KiB
Raw Blame History

结合之前的main代码:

步骤 1: 定义三地址代码结构

TCG/Translator.h中,你可能已经定义了一个TACFile类来存储三地址代码块,其中每个块包含一系列的三地址代码指令。

步骤 2: 实现翻译器

TCG/Translator.hTCG/Translator.cpp中,你实现了一个Translator类,它负责将三地址代码翻译为汇编代码。这个类可能包含如下方法:

  • Translate():遍历TACFile中的所有三地址代码指令,并为每个指令生成对应的汇编代码。
  • OutputFile():将生成的汇编代码输出到文件。

步骤 3: 遍历三地址代码

main函数中,你已经创建了一个TACFile对象,并将其传递给Translator对象。Translator对象的Translate()方法将遍历所有的三地址代码指令,并生成汇编代码。

步骤 4: 生成汇编代码

Translator类的Translate()方法中,你需要为每种三地址代码指令编写一个翻译规则,将它们转换为对应的汇编指令。例如:

  • 对于MOV指令,生成一个将值移动到寄存器的汇编指令。
  • 对于ADD指令,生成一个加法汇编指令。
  • 对于SUB指令,生成一个减法汇编指令。

步骤 5: 输出汇编代码

Translator类的OutputFile()方法中,你需要将生成的汇编代码写入到文件中。这通常涉及到打开文件、格式化汇编代码和写入文件。

示例

假设你有以下三地址代码:

MOV R1, 5
MOV R2, 3
ADD R3, R1, R2

对应的汇编代码可能是:

mov R1, #5
mov R2, #3
add R3, R1, R2

在你的Translator类中,你需要为MOVADD等操作编写翻译规则,并将它们输出为上述汇编代码。

总结来说,将三地址代码翻译为汇编代码是一个涉及指令选择、寄存器分配和代码生成的过程。你需要在编译器后端实现这些步骤,并将它们集成到你的main函数中,以便将三地址代码转换为汇编代码。

从提供的源代码文件中,我们可以总结出以下翻译规则、寄存器分配和指令调度的实现方法:

翻译规则

  1. 基本块翻译BlockTranslator.cpp

    • 每个基本块TACBlock被翻译成一系列的汇编代码行ASMLines
    • 翻译过程涉及到对每个TACLine的遍历并根据操作码TACOP选择相应的翻译器Translator进行翻译。
  2. 语句翻译SentenceTranslator

    • 对于每种类型的TACLine如赋值ASSIGN、函数调用CALL、条件跳转IF都有专门的翻译器来处理。
    • 翻译器根据操作数的类型(立即数、变量、内存位置等)生成相应的汇编指令。
  3. 寄存器和内存操作

    • 对于变量的存储位置,需要判断是存储在寄存器还是内存中,这由SymbolManager中的encode_varposition函数决定。

寄存器分配

  1. 获取寄存器SymbolManager.cpp

    • get_reg函数尝试为一个变量分配一个寄存器,如果失败,则调用get_replaced_reg来找到一个可以替换的寄存器。
    • get_replaced_reg寻找不再使用的变量或者内存中有位置的变量,以决定哪个寄存器可以被替换。
  2. 寄存器备份

    • 在进行操作之前如果需要使用到特定的寄存器如EAX、EBX而这些寄存器中存储的值后续还需要使用则需要将这些值备份到内存中。
  3. 更新寄存器状态

    • 操作完成后,更新SymbolManager中的寄存器状态,如set_avalue_reg用于设置一个变量的寄存器值。

指令调度

  1. 指令顺序

    • 指令的顺序通常由TACLine的顺序决定翻译器按照TACLine的顺序生成汇编指令。
  2. 栈操作

    • 在函数调用CALL和参数传递PARA需要对栈进行操作pushpop指令的使用。
    • SymbolManager中的set_esp_bias用于调整栈指针ESP的偏移。
  3. 条件跳转和标签

    • 条件跳转IF和标签LABEL的翻译涉及到对跳转目标的识别和指令的生成。

具体实现方法

  1. 寄存器分配策略

    • 使用SymbolManager中的get_free_reg来获取一个空闲的寄存器。
    • 如果没有空闲寄存器,使用get_replaced_reg来替换一个当前不活跃的寄存器。
  2. 指令生成

    • 对于每个操作数,根据其存储位置(寄存器、内存或全局变量),生成相应的mov指令来移动值。
    • 对于计算操作,如ADDSUBMUL等,生成相应的汇编指令。
  3. 内存管理

    • 使用pushpop指令来管理栈上的内存。
    • 对于局部变量使用基于EBP的偏移来访问内存位置。
  4. 函数调用和返回

    • 在函数调用前,将参数压入栈中。
    • 在函数返回时,从栈中弹出返回值。

这些源代码文件展示了一个编译器后端如何将三地址代码TAC翻译成汇编代码ASM涉及到寄存器分配、指令调度和内存管理等多个编译器优化阶段。每个翻译器都负责特定的操作码SymbolManager则负责跟踪和更新符号表信息,包括变量的存储位置和寄存器的使用情况。