diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java index d3a7b80..0611295 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/CallGenerator.java @@ -3,74 +3,56 @@ package org.jcnc.snow.compiler.backend.generator; import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; import org.jcnc.snow.compiler.backend.utils.OpHelper; +import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable; import org.jcnc.snow.compiler.ir.instruction.CallInstruction; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import java.util.Map; /** - * 函数调用指令生成器。 - *

- * 该类实现了函数调用(CallInstruction)的指令翻译逻辑, - * 负责将 IR 层的函数调用转换为虚拟机可执行的低级指令。 + * 将 IR CallInstruction 生成 VM 指令 */ public class CallGenerator implements InstructionGenerator { - /** - * 返回本指令生成器支持的 IR 指令类型(CallInstruction)。 - * - * @return 指令类型的 Class 对象 - */ @Override public Class supportedClass() { return CallInstruction.class; } - /** - * 生成函数调用相关的虚拟机指令。 - *

- * 步骤如下: - *

    - *
  1. 预测返回值类型(采用首个实参槽的类型作为近似)
  2. - *
  3. 为每个参数根据实际类型发出加载指令
  4. - *
  5. 生成 CALL 调用指令
  6. - *
  7. 将返回值存储到目标槽,并记录类型信息
  8. - *
- * - * @param ins 待翻译的 CallInstruction 指令对象 - * @param out 指令输出与类型槽管理器 - * @param slotMap IR 寄存器到槽号的映射 - * @param currentFn 当前函数名(未用,可用于递归/闭包等复杂场景) - */ @Override public void generate(CallInstruction ins, VMProgramBuilder out, Map slotMap, String currentFn) { - // —— 1. 预测返回值类型(用首个实参槽类型作为近似推断) —— - char retType = 'I'; // 默认整型 + /* 1. 推断返回值类型(用于非 void 情况下的 I/F/D/L_STORE) */ + char retType = 'I'; if (!ins.getArguments().isEmpty()) { int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst()); - retType = out.getSlotType(firstSlot); // 获取槽位实际类型 - if (retType == '\0') retType = 'I'; // 默认整型 + retType = out.getSlotType(firstSlot); + if (retType == '\0') retType = 'I'; } - // —— 2. 按真实类型加载每个参数到虚拟机操作栈 —— + /* 2. 依次加载实参 */ for (var arg : ins.getArguments()) { - int slotId = slotMap.get((IRVirtualRegister) arg); // 获取参数槽号 - char t = out.getSlotType(slotId); // 获取参数类型 - if (t == '\0') t = 'I'; // 类型未知时默认整型 - // 生成类型相关的加载指令,如 I_LOAD、F_LOAD 等 + int slotId = slotMap.get((IRVirtualRegister) arg); + char t = out.getSlotType(slotId); + if (t == '\0') t = 'I'; out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId); } - // —— 3. 生成 CALL 调用指令 —— + /* 3. 发出 CALL 指令 */ out.emitCall(ins.getFunctionName(), ins.getArguments().size()); - // —— 4. 将返回值存入目标槽,并记录槽的类型 —— - int destSlot = slotMap.get(ins.getDest()); // 目标寄存器槽 + /* -- 3.5 若被调用函数返回 void,则无需保存返回值 -- */ + String rt = GlobalFunctionTable.getReturnType(ins.getFunctionName()); + if ("void".equals(rt)) { + return; // 直接结束,无 _STORE + } + + /* 4. 保存返回值到目标槽 */ + int destSlot = slotMap.get(ins.getDest()); out.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot); - out.setSlotType(destSlot, retType); // 标记返回值类型 + out.setSlotType(destSlot, retType); } } diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java index 48b10e7..c9f4fd5 100644 --- a/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java +++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/FunctionBuilder.java @@ -1,5 +1,6 @@ package org.jcnc.snow.compiler.ir.builder; +import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable; import org.jcnc.snow.compiler.ir.core.IRFunction; import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; @@ -32,6 +33,10 @@ public class FunctionBuilder { */ public IRFunction build(FunctionNode functionNode) { + // 在全局函数表中注册函数信息 + GlobalFunctionTable.register(functionNode.name(), functionNode.returnType()); + + // 0) 基本初始化:创建 IRFunction 实例与对应上下文 IRFunction irFunction = new IRFunction(functionNode.name()); IRContext irContext = new IRContext(irFunction); diff --git a/src/main/java/org/jcnc/snow/compiler/ir/common/GlobalFunctionTable.java b/src/main/java/org/jcnc/snow/compiler/ir/common/GlobalFunctionTable.java new file mode 100644 index 0000000..762cfc0 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/ir/common/GlobalFunctionTable.java @@ -0,0 +1,76 @@ +package org.jcnc.snow.compiler.ir.common; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 全局函数返回类型表。 + *

+ * 此工具类用于在编译前端构建每个函数 IR(中间表示)时登记函数的返回类型, + * 以便后端在生成 {@code CALL} 指令时判断是否需要保存返回值。 + *

+ * + * 使用说明 + * + */ +public final class GlobalFunctionTable { + + /** + * 存储全局函数返回类型映射表。 + * + */ + private static final Map RETURN_TYPES = new ConcurrentHashMap<>(); + + /** + * 私有构造函数,防止实例化。 + */ + private GlobalFunctionTable() { + // 工具类,禁止实例化 + } + + /** + * 登记或更新指定函数的返回类型。 + * + *

若传入的 {@code returnType} 为 {@code null},则登记为 {@code "void"}。 + * 否则将去除前后空白并转换为小写后登记。

+ * + * @param name 函数名(不含模块限定) + * @param returnType 函数返回类型字符串,如 {@code "int"}、{@code "String"}。 + * 如果为 {@code null},则登记类型为 {@code "void"}。 + * @throws IllegalArgumentException 如果 {@code name} 为空或 {@code null} + */ + public static void register(String name, String returnType) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Function name must not be null or empty"); + } + RETURN_TYPES.put( + name, + returnType == null + ? "void" + : returnType.trim().toLowerCase() + ); + } + + /** + * 查询指定函数的返回类型。 + * + *

返回类型为登记时的值(小写),如果函数未登记过,则返回 {@code null}。

+ * + * @param name 函数名(不含模块限定) + * @return 已登记的返回类型(小写),或 {@code null} 表示未知 + * @throws IllegalArgumentException 如果 {@code name} 为空或 {@code null} + */ + public static String getReturnType(String name) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Function name must not be null or empty"); + } + return RETURN_TYPES.get(name); + } +}