diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java index 4e5bd83..91a4d91 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java @@ -4,7 +4,9 @@ import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; import org.jcnc.snow.compiler.backend.util.IROpCodeMapper; import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.ir.core.IRValue; import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction; +import org.jcnc.snow.compiler.ir.value.IRConstant; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import java.util.Map; @@ -13,15 +15,18 @@ import java.util.concurrent.atomic.AtomicInteger; /** * 二元运算指令生成器。 *

- * 负责将中间表示的二元运算指令(算术运算及比较运算)生成对应的虚拟机指令序列。 - * 支持对操作数进行自动类型提升,以保证运算结果的正确性。 + * 负责将中间表示的二元运算指令(算术、位运算及比较运算)生成对应的虚拟机指令序列, + * 并自动进行类型提升。 + * 同时实现 "+0 → MOV" 的 Peephole 优化,避免多余的 PUSH/ADD 序列。 *

*

类型提升优先级:D > F > L > I > S > B

*/ public class BinaryOpGenerator implements InstructionGenerator { + /* -------------------- 常量与工具 -------------------- */ + /** - * 用于生成唯一标签的计数器。 + * 用于生成唯一标签的计数器 */ private static final AtomicInteger COUNTER = new AtomicInteger(0); @@ -30,17 +35,14 @@ public class BinaryOpGenerator implements InstructionGenerator 3; case 'S' -> 2; case 'B' -> 1; - default -> 0; + default -> 0; }; } /** - * 比较两个类型字符,返回优先级更高的那个。 - * - * @param a 左操作数类型 - * @param b 右操作数类型 - * @return 优先级更高者的类型字符 + * 返回优先级更高的类型字符 */ private static char promote(char a, char b) { return rank(a) >= rank(b) ? a : b; } /** - * 将类型字符转换为字符串形式。 - * - * @param p 类型字符 - * @return 长度为1的字符串 + * 单字符转字符串 */ private static String str(char p) { return String.valueOf(p); } + /** + * 判断常量值是否等于 0。 + * 仅支持 Java 原生数值类型。 + */ + private static boolean isZero(Object v) { + if (v == null) return false; + return switch (v) { + case Integer i -> i == 0; + case Long l -> l == 0L; + case Short s -> s == (short) 0; + case Byte b -> b == (byte) 0; + case Float f -> f == 0.0f; + case Double d -> d == 0.0; + default -> false; + }; + } /** - * 获取从类型 from 到类型 to 的转换指令名称。 - * - * @param from 源类型字符 - * @param to 目标类型字符 - * @return 对应的指令名称,如 "I2L";若两类型相同或不可转换则返回 null + * 获取从类型 {@code from} 到 {@code to} 的转换指令名。 + * 相同类型或无显式转换需求返回 {@code null}。 */ private static String convert(char from, char to) { if (from == to) return null; @@ -100,55 +108,60 @@ public class BinaryOpGenerator implements InstructionGenerator "D2F"; case "SI" -> "S2I"; case "BI" -> "B2I"; - default -> null; + default -> null; }; } - /** - * 返回该生成器支持的指令类型。 - * - * @return BinaryOperationInstruction 的 Class 对象 - */ + /* -------------------- 接口实现 -------------------- */ + @Override public Class supportedClass() { return BinaryOperationInstruction.class; } - /** - * 根据中间表示的二元运算指令生成对应的虚拟机指令序列。 - * - *

步骤:

- *
    - *
  1. 获取操作数与目标操作数寄存槽位及其类型。
  2. - *
  3. 将左右操作数加载到栈并根据需要进行类型转换。
  4. - *
  5. 区分算术/位运算与比较运算,分别生成不同指令序列:
  6. - *
      - *
    • 算术/位运算:直接调用对应的运算指令并保存结果。
    • - *
    • 比较运算:使用条件跳转生成布尔结果。
    • - *
    - *
  7. 将结果存回目标槽位,并更新槽位类型。
  8. - *
- * - * @param ins 中间表示的二元运算指令实例 - * @param out 字节码生成器,用于输出虚拟机指令 - * @param slotMap 虚拟寄存器到槽位编号的映射 - * @param currentFn 当前函数名,用于生成唯一标签 - */ @Override public void generate(BinaryOperationInstruction ins, VMProgramBuilder out, Map slotMap, String currentFn) { + /* ---------- 0. +0 → MOV Peephole 优化 ---------- */ + String irName = ins.op().name(); + if (irName.startsWith("ADD_")) { + IRValue lhs = ins.operands().getFirst(); + IRValue rhs = ins.operands().get(1); + + boolean lhsZero = lhs instanceof IRConstant && isZero(((IRConstant) lhs).value()); + boolean rhsZero = rhs instanceof IRConstant && isZero(((IRConstant) rhs).value()); + + // 仅当一侧为常量 0 时可替换为 MOV + if (lhsZero ^ rhsZero) { + IRVirtualRegister srcVr = null; + if ((lhsZero ? rhs : lhs) instanceof IRVirtualRegister) { + srcVr = (IRVirtualRegister) (lhsZero ? rhs : lhs); + } + int srcSlot = slotMap.get(srcVr); + int destSlot = slotMap.get(ins.dest()); + + // 源与目标槽位不同才需要发 MOV + if (srcSlot != destSlot) { + out.emit(OpHelper.opcode("MOV") + " " + srcSlot + " " + destSlot); + } + // 复制槽位类型信息 + out.setSlotType(destSlot, out.getSlotType(srcSlot)); + return; // 优化路径结束 + } + } + /* ---------- 1. 槽位与类型 ---------- */ int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); int dSlot = slotMap.get(ins.dest()); - char lType = out.getSlotType(lSlot); // 如未登记默认 'I' + char lType = out.getSlotType(lSlot); // 未登记默认 'I' char rType = out.getSlotType(rSlot); - char tType = promote(lType, rType); // 类型提升结果 + char tType = promote(lType, rType); // 类型提升结果 String tPre = str(tType); /* ---------- 2. 加载并做类型转换 ---------- */ @@ -161,13 +174,12 @@ public class BinaryOpGenerator implements InstructionGenerator slotMap, String currentFn) { - // 获取操作数所在槽号 - int slotId = slotMap.get((IRVirtualRegister) ins.operands().getFirst()); - // 加载操作数到虚拟机栈顶 - out.emit(OpHelper.opcode("I_LOAD") + " " + slotId); - // 生成对应的一元运算操作码(如取负等) - out.emit(OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op()))); - // 将结果存储到目标寄存器槽 - out.emit(OpHelper.opcode("I_STORE") + " " + slotMap.get(ins.dest())); + + /* -------- 1. 源槽位与类型 -------- */ + int srcSlot = slotMap.get((IRVirtualRegister) ins.operands().getFirst()); + char prefix = out.getSlotType(srcSlot); // 未登记则返回默认 'I' + + String loadOp = prefix + "_LOAD"; + String storeOp = prefix + "_STORE"; + + /* -------- 2. 指令序列 -------- */ + // 2-A. 加载操作数 + out.emit(OpHelper.opcode(loadOp) + + " " + srcSlot); + + // 2-B. 执行具体一元运算(NEG、NOT…) + out.emit(OpHelper.opcode( + IROpCodeMapper.toVMOp(ins.op()))); + + // 2-C. 存结果到目标槽 + int destSlot = slotMap.get(ins.dest()); + out.emit(OpHelper.opcode(storeOp) + + " " + destSlot); + + /* -------- 3. 更新目标槽类型 -------- */ + out.setSlotType(destSlot, prefix); } } diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java index 2d2e75e..11288ba 100644 --- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java @@ -74,15 +74,16 @@ public record ExpressionBuilder(IRContext ctx) { }; } + /** 处理一元表达式 */ private IRVirtualRegister buildUnary(UnaryExpressionNode un) { - String op = un.operator(); - IRVirtualRegister val = build(un.operand()); + String op = un.operator(); + IRVirtualRegister val = build(un.operand()); - // -x → NEG_I32 + // -x → NEG_*(根据类型自动选择位宽) if (op.equals("-")) { IRVirtualRegister dest = ctx.newRegister(); - ctx.addInstruction(new UnaryOperationInstruction( - IROpCode.NEG_I32, dest, val)); + IROpCode code = ExpressionUtils.negOp(un.operand()); + ctx.addInstruction(new UnaryOperationInstruction(code, dest, val)); return dest; } diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java index 18f750b..8a95ca5 100644 --- a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java @@ -8,6 +8,8 @@ import org.jcnc.snow.compiler.parser.ast.*; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import java.util.Locale; + /** * StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。 *

@@ -177,4 +179,16 @@ public class StatementBuilder { InstructionFactory.cmpJump(ctx, IROpCode.CMP_EQ, condReg, zero, falseLabel); } } + + private static char typeSuffixFromType(String type) { + if (type == null) return '\0'; + return switch (type.toLowerCase(Locale.ROOT)) { + case "byte" -> 'b'; + case "short" -> 's'; + case "long" -> 'l'; + case "float" -> 'f'; + case "double" -> 'd'; + default -> '\0'; // 其余默认按 32-bit 整型处理 + }; + } } diff --git a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java index cc1e426..91155df 100644 --- a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java +++ b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java @@ -78,6 +78,30 @@ public class ExpressionUtils { }; } + /** + * 根据表达式节点推断一元取负(-)运算应使用的操作码。 + * + *

优先级与 {@link #resolveOpCode} 使用的类型提升规则保持一致:

+ *
    + *
  • 字面量或标识符带显式后缀时,直接以后缀决定位宽;
  • + *
  • 未显式指定时,默认使用 32 位整型 {@link IROpCode#NEG_I32}。
  • + *
+ * + * @param operand 一元取负运算的操作数 + * @return 匹配的 {@link IROpCode} + */ + public static IROpCode negOp(ExpressionNode operand) { + char t = typeChar(operand); + return switch (t) { + case 'b' -> IROpCode.NEG_B8; + case 's' -> IROpCode.NEG_S16; + case 'l' -> IROpCode.NEG_L64; + case 'f' -> IROpCode.NEG_F32; + case 'd' -> IROpCode.NEG_D64; + default -> IROpCode.NEG_I32; + }; + } + /* =================== 类型推断与操作符匹配 =================== */ /**