From 48b7418b03305b96091ff916fd2595f9caf59a50 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 11 May 2025 22:58:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snow/compiler/backend/IROpCodeMapper.java | 74 +++++----- .../compiler/backend/RegisterAllocator.java | 47 +++--- .../compiler/backend/VMCodeGenerator.java | 134 ++++++++++++++---- 3 files changed, 170 insertions(+), 85 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/IROpCodeMapper.java b/src/main/java/org/jcnc/snow/compiler/backend/IROpCodeMapper.java index c6cb9e4..c1a21b8 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/IROpCodeMapper.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/IROpCodeMapper.java @@ -6,65 +6,65 @@ import java.util.EnumMap; import java.util.Map; /** - * IROpCodeMapper —— IR 操作码(IROpCode)到 VM 虚拟机操作码(字符串形式)的映射器。 + * 提供 IR 操作码({@link IROpCode})到虚拟机指令名({@code String})的一一映射工具类。 *

- * 本类用于编译器后端阶段,将中间表示中的操作类型(如 ADD_I32)转换为 - * 虚拟机指令集中的具体指令(如 "I_ADD"),用于代码生成输出。 + * 本类在编译器后端负责将中间表示中的操作码转换为对应的 VM 指令,用于最终的代码生成。 + * 不负责操作数或参数传递的逻辑,仅做纯映射处理。 *

*

- * 注意:该类只做一一映射,不处理操作数或参数传递逻辑。 - * 若未来有更多数据类型或扩展语义,可在此处集中管理。 + * 如需扩展更多操作码或新增指令语义,可在此集中管理映射关系。 *

*/ public final class IROpCodeMapper { - /** 存储映射关系的枚举映射表(使用 EnumMap 提高性能) */ + /** + * IR 操作码到 VM 指令名的映射表,基于 {@code EnumMap} 实现以提升性能。 + */ private static final Map opcodeMap = new EnumMap<>(IROpCode.class); - // 初始化静态映射表 static { - // ───── 算术运算 ───── - opcodeMap.put(IROpCode.ADD_I32, "I_ADD"); - opcodeMap.put(IROpCode.SUB_I32, "I_SUB"); - opcodeMap.put(IROpCode.MUL_I32, "I_MUL"); - opcodeMap.put(IROpCode.DIV_I32, "I_DIV"); - opcodeMap.put(IROpCode.NEG_I32, "I_NEG"); + opcodeMap.put(IROpCode.ADD_I32, "I_ADD"); + opcodeMap.put(IROpCode.SUB_I32, "I_SUB"); + opcodeMap.put(IROpCode.MUL_I32, "I_MUL"); + opcodeMap.put(IROpCode.DIV_I32, "I_DIV"); + opcodeMap.put(IROpCode.NEG_I32, "I_NEG"); - // ───── 逻辑比较运算 ───── - opcodeMap.put(IROpCode.CMP_EQ, "IC_EQ"); - opcodeMap.put(IROpCode.CMP_NE, "IC_NE"); - opcodeMap.put(IROpCode.CMP_LT, "IC_L"); - opcodeMap.put(IROpCode.CMP_GT, "IC_G"); - opcodeMap.put(IROpCode.CMP_LE, "IC_LE"); - opcodeMap.put(IROpCode.CMP_GE, "IC_GE"); + opcodeMap.put(IROpCode.CMP_EQ, "IC_EQ"); + opcodeMap.put(IROpCode.CMP_NE, "IC_NE"); + opcodeMap.put(IROpCode.CMP_LT, "IC_L"); + opcodeMap.put(IROpCode.CMP_GT, "IC_G"); + opcodeMap.put(IROpCode.CMP_LE, "IC_LE"); + opcodeMap.put(IROpCode.CMP_GE, "IC_GE"); - // ───── 数据操作 ───── - opcodeMap.put(IROpCode.LOAD, "I_LOAD"); - opcodeMap.put(IROpCode.STORE, "I_STORE"); - opcodeMap.put(IROpCode.CONST, "I_PUSH"); + opcodeMap.put(IROpCode.LOAD, "I_LOAD"); + opcodeMap.put(IROpCode.STORE, "I_STORE"); + opcodeMap.put(IROpCode.CONST, "I_PUSH"); - // ───── 控制流 ───── - opcodeMap.put(IROpCode.JUMP, "JMP"); + opcodeMap.put(IROpCode.JUMP, "JMP"); opcodeMap.put(IROpCode.JUMP_IF_ZERO, "JZ"); - opcodeMap.put(IROpCode.LABEL, "LABEL"); + opcodeMap.put(IROpCode.LABEL, "LABEL"); - // ───── 函数调用 ───── - opcodeMap.put(IROpCode.CALL, "CALL"); - opcodeMap.put(IROpCode.RET, "RET"); + opcodeMap.put(IROpCode.CALL, "CALL"); + opcodeMap.put(IROpCode.RET, "RET"); } /** - * 将给定的 IR 操作码转换为对应的 VM 指令名(字符串)。 + * 将给定的 IR 操作码转换为对应的 VM 指令名。 * - * @param irOp IROpCode 中的操作码 - * @return 对应的 VM opcode 字符串 - * @throws IllegalArgumentException 若操作码未被映射 + * @param irOp 需转换的 IR 操作码 + * @return 与 IR 操作码对应的 VM 指令名称 + * @throws IllegalArgumentException 若 {@code irOp} 未在映射表中定义 */ public static String toVMOp(IROpCode irOp) { - String code = opcodeMap.get(irOp); - if (code == null) { + String vmCode = opcodeMap.get(irOp); + if (vmCode == null) { throw new IllegalArgumentException("未映射的 IR 操作码: " + irOp); } - return code; + return vmCode; + } + + // 私有构造函数,防止实例化 + private IROpCodeMapper() { + // 不允许实例化 } } diff --git a/src/main/java/org/jcnc/snow/compiler/backend/RegisterAllocator.java b/src/main/java/org/jcnc/snow/compiler/backend/RegisterAllocator.java index 738dea3..d02c0eb 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/RegisterAllocator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/RegisterAllocator.java @@ -1,52 +1,55 @@ package org.jcnc.snow.compiler.backend; -import org.jcnc.snow.compiler.ir.core.*; +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IRValue; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import java.util.HashMap; import java.util.Map; /** - * A very simple linear-scan register allocator. - * - *

Strategy:

+ * 线性扫描寄存器分配器。 + *

+ * 分配策略: *

    - *
  1. Assign formal parameters to slots - * 0 .. n-1 (in declaration order).
  2. - *
  3. Scan the instruction list; every unseen virtual register - * gets the next free slot number.
  4. + *
  5. 将函数的形式参数按照声明顺序分配到槽编号 0 到 n-1。
  6. + *
  7. 随后扫描函数体中的指令列表,每遇到尚未分配槽号的虚拟寄存器, + * 即为其分配下一个可用的槽编号。
  8. *
- *

No liveness analysis, no spilling – demo-grade only.

*/ public final class RegisterAllocator { - private final Map map = new HashMap<>(); + /** + * 存储虚拟寄存器到物理槽的映射表。 + * 键为虚拟寄存器,值为分配的槽编号。 + */ + private final Map map = new HashMap<>(); /** - * Computes a register→slot mapping for the given function. + * 为指定的 IR 函数计算寄存器到槽的映射关系。 + *

+ * 先为函数的形式参数分配槽号,再为函数体中出现的其他虚拟寄存器依次分配。 + *

* - * @param fn IRFunction to allocate - * @return mapping table (immutable) + * @param fn 需要进行寄存器分配的中间表示函数 + * @return 一个不可变的映射表,表示每个虚拟寄存器对应的槽编号 */ - public Map allocate(IRFunction fn) { - + public Map allocate(IRFunction fn) { int next = 0; - /* ---------- 1) parameters first ---------- */ + // 1. 为形式参数分配槽编号 for (IRVirtualRegister param : fn.parameters()) { map.put(param, next++); } - /* ---------- 2) then all remaining VRegs ---------- */ + // 2. 扫描函数体,分配其他虚拟寄存器的槽编号 for (IRInstruction inst : fn.body()) { - if (inst.dest() != null && !map.containsKey(inst.dest())) { map.put(inst.dest(), next++); } - - for (IRValue v : inst.operands()) { - if (v instanceof IRVirtualRegister vr && - !map.containsKey(vr)) { + for (IRValue operand : inst.operands()) { + if (operand instanceof IRVirtualRegister vr && !map.containsKey(vr)) { map.put(vr, next++); } } diff --git a/src/main/java/org/jcnc/snow/compiler/backend/VMCodeGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/VMCodeGenerator.java index 0fb8c7f..d03ba84 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/VMCodeGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/VMCodeGenerator.java @@ -1,50 +1,95 @@ package org.jcnc.snow.compiler.backend; -import org.jcnc.snow.compiler.ir.core.*; -import org.jcnc.snow.compiler.ir.instruction.*; -import org.jcnc.snow.compiler.ir.value.*; +import org.jcnc.snow.compiler.ir.core.IRFunction; +import org.jcnc.snow.compiler.ir.core.IRInstruction; +import org.jcnc.snow.compiler.ir.core.IRValue; +import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction; +import org.jcnc.snow.compiler.ir.instruction.CallInstruction; +import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction; +import org.jcnc.snow.compiler.ir.instruction.ReturnInstruction; +import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction; +import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; +import org.jcnc.snow.compiler.ir.value.IRConstant; import org.jcnc.snow.vm.engine.VMOpCode; import java.lang.reflect.Field; import java.util.Map; /** - * Generates VM code for a single IRFunction. + * 为单个 {@link IRFunction} 生成对应的虚拟机指令序列。 + *

+ * 本类依据中间表示函数的指令列表,调用 {@link VMProgramBuilder} 输出相应的 VM 操作码, + * 包括常量加载、算术运算、函数调用和返回等指令生成逻辑。 + *

*/ public final class VMCodeGenerator { - private final Map slotMap; + /** + * 虚拟寄存器到槽编号的映射表,用于确定加载和存储的目标槽。 + */ + private final Map slotMap; + + /** + * VM 指令输出器,用于构建最终的指令流。 + */ private final VMProgramBuilder out; + + /** + * 当前正在生成代码的函数名称,用于在返回指令处区分主函数与其他函数。 + */ private String currentFn; - public VMCodeGenerator(Map slotMap, + /** + * 构造 VMCodeGenerator 实例。 + * + * @param slotMap 虚拟寄存器与槽编号的映射表 + * @param out 用于输出 VM 指令的构建器 + */ + public VMCodeGenerator(Map slotMap, VMProgramBuilder out) { this.slotMap = slotMap; - this.out = out; + this.out = out; } + /** + * 为指定的 IR 函数生成完整的 VM 指令流。 + * + * @param fn 目标 IRFunction 实例 + * @throws IllegalStateException 若遇到不支持的 IR 指令类型 + */ public void generate(IRFunction fn) { currentFn = fn.name(); out.beginFunction(currentFn); - for (IRInstruction inst : fn.body()) switch (inst) { - case LoadConstInstruction c -> genLoadConst(c); - case BinaryOperationInstruction b -> genBinOp(b); - case UnaryOperationInstruction u -> genUnary(u); - case CallInstruction c -> genCall(c); - case ReturnInstruction r -> genRet(r); - default -> throw new IllegalStateException("Unsupported IR: "+inst); + for (IRInstruction inst : fn.body()) { + switch (inst) { + case LoadConstInstruction c -> genLoadConst(c); + case BinaryOperationInstruction b -> genBinOp(b); + case UnaryOperationInstruction u -> genUnary(u); + case CallInstruction c -> genCall(c); + case ReturnInstruction r -> genRet(r); + default -> throw new IllegalStateException("Unsupported IR: " + inst); + } } } - /* ---------- helpers ---------- */ - + /** + * 生成常量加载指令序列:将常量推入栈并存入目标寄存器槽。 + * + * @param c 常量加载指令 + */ private void genLoadConst(LoadConstInstruction c) { - IRConstant k = (IRConstant) c.operands().getFirst(); - emit(op("I_PUSH"), k.value().toString()); + IRConstant constant = (IRConstant) c.operands().getFirst(); + emit(op("I_PUSH"), constant.value().toString()); emit(op("I_STORE"), slot(c.dest())); } + /** + * 生成二元算术或比较运算指令序列: + * 依次加载两个操作数,执行运算,并存回目标寄存器槽。 + * + * @param b 二元操作指令 + */ private void genBinOp(BinaryOperationInstruction b) { emit(op("I_LOAD"), slot((IRVirtualRegister) b.operands().get(0))); emit(op("I_LOAD"), slot((IRVirtualRegister) b.operands().get(1))); @@ -52,15 +97,24 @@ public final class VMCodeGenerator { emit(op("I_STORE"), slot(b.dest())); } + /** + * 生成一元运算指令序列:加载操作数,执行运算,并存回目标寄存器槽。 + * + * @param u 一元操作指令 + */ private void genUnary(UnaryOperationInstruction u) { emit(op("I_LOAD"), slot((IRVirtualRegister) u.operands().getFirst())); emit(op(IROpCodeMapper.toVMOp(u.op()))); emit(op("I_STORE"), slot(u.dest())); } - /** Generates CALL addr nArgs */ + /** + * 生成函数调用指令序列: + * 按参数顺序加载每个参数,执行调用,并将返回值存入目标寄存器槽。 + * + * @param c 调用指令 + */ private void genCall(CallInstruction c) { - /* push arguments in IR order (already left-to-right) */ for (IRValue arg : c.getArguments()) { emit(op("I_LOAD"), slot((IRVirtualRegister) arg)); } @@ -69,28 +123,56 @@ public final class VMCodeGenerator { emit(op("I_STORE"), slot(c.getDest())); } + /** + * 生成返回指令序列:可选地加载返回值,然后根据是否为主函数选择 HALT 或 RET。 + * + * @param r 返回指令 + */ private void genRet(ReturnInstruction r) { - if (r.value() != null) + if (r.value() != null) { emit(op("I_LOAD"), slot(r.value())); - emit("main".equals(currentFn) ? op("HALT") : op("RET")); + } + String opcode = "main".equals(currentFn) ? op("HALT") : op("RET"); + out.emit(opcode); } + /** + * 构造并输出一条 VM 指令。 + * + * @param opcode 操作码字符串 + * @param args 可选的操作数字符串列表 + */ private void emit(String opcode, String... args) { StringBuilder sb = new StringBuilder(opcode); - for (String a : args) sb.append(' ').append(a); + for (String a : args) { + sb.append(' ').append(a); + } out.emit(sb.toString()); } + /** + * 将虚拟寄存器映射为对应的槽编号字符串。 + * + * @param r 虚拟寄存器 + * @return 目标槽编号的字符串形式 + */ private String slot(IRVirtualRegister r) { return slotMap.get(r).toString(); } + /** + * 根据 VMOpCode 常量名反射获取对应的操作码字符串。 + * + * @param name VMOpCode 常量字段名 + * @return 对应的操作码字符串 + * @throws RuntimeException 若未找到指定操作码字段 + */ private String op(String name) { try { - Field f = VMOpCode.class.getField(name); - return f.get(null).toString(); + Field field = VMOpCode.class.getField(name); + return field.get(null).toString(); } catch (Exception e) { - throw new RuntimeException("Unknown opcode: "+name, e); + throw new RuntimeException("Unknown opcode: " + name, e); } } }