fix: 优化函数调用指令生成逻辑

- 新增 GlobalFunctionTable 工具类,用于记录函数返回类型
- 修改 CallGenerator 以利用 GlobalFunctionTable 判断是否需要保存返回值
- 更新 FunctionBuilder,在构建函数 IR 时注册函数返回类型- 调整测试用例,将 foo 函数返回类型改为 void
This commit is contained in:
Luke 2025-07-10 16:28:45 +08:00
parent 565cc79329
commit 3e8b3f7629
3 changed files with 101 additions and 38 deletions

View File

@ -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.builder.VMProgramBuilder;
import org.jcnc.snow.compiler.backend.core.InstructionGenerator; import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
import org.jcnc.snow.compiler.backend.utils.OpHelper; 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.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.Map; import java.util.Map;
/** /**
* 函数调用指令生成器 * IR CallInstruction 生成 VM 指令
* <p>
* 该类实现了函数调用CallInstruction的指令翻译逻辑
* 负责将 IR 层的函数调用转换为虚拟机可执行的低级指令
*/ */
public class CallGenerator implements InstructionGenerator<CallInstruction> { public class CallGenerator implements InstructionGenerator<CallInstruction> {
/**
* 返回本指令生成器支持的 IR 指令类型CallInstruction
*
* @return 指令类型的 Class 对象
*/
@Override @Override
public Class<CallInstruction> supportedClass() { public Class<CallInstruction> supportedClass() {
return CallInstruction.class; return CallInstruction.class;
} }
/**
* 生成函数调用相关的虚拟机指令
* <p>
* 步骤如下
* <ol>
* <li>预测返回值类型采用首个实参槽的类型作为近似</li>
* <li>为每个参数根据实际类型发出加载指令</li>
* <li>生成 CALL 调用指令</li>
* <li>将返回值存储到目标槽并记录类型信息</li>
* </ol>
*
* @param ins 待翻译的 CallInstruction 指令对象
* @param out 指令输出与类型槽管理器
* @param slotMap IR 寄存器到槽号的映射
* @param currentFn 当前函数名未用可用于递归/闭包等复杂场景
*/
@Override @Override
public void generate(CallInstruction ins, public void generate(CallInstruction ins,
VMProgramBuilder out, VMProgramBuilder out,
Map<IRVirtualRegister, Integer> slotMap, Map<IRVirtualRegister, Integer> slotMap,
String currentFn) { String currentFn) {
// 1. 预测返回值类型用首个实参槽类型作为近似推断 /* 1. 推断返回值类型(用于非 void 情况下的 I/F/D/L_STORE */
char retType = 'I'; // 默认整型 char retType = 'I';
if (!ins.getArguments().isEmpty()) { if (!ins.getArguments().isEmpty()) {
int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst()); int firstSlot = slotMap.get((IRVirtualRegister) ins.getArguments().getFirst());
retType = out.getSlotType(firstSlot); // 获取槽位实际类型 retType = out.getSlotType(firstSlot);
if (retType == '\0') retType = 'I'; // 默认整型 if (retType == '\0') retType = 'I';
} }
// 2. 按真实类型加载每个参数到虚拟机操作栈 /* 2. 依次加载实参 */
for (var arg : ins.getArguments()) { for (var arg : ins.getArguments()) {
int slotId = slotMap.get((IRVirtualRegister) arg); // 获取参数槽号 int slotId = slotMap.get((IRVirtualRegister) arg);
char t = out.getSlotType(slotId); // 获取参数类型 char t = out.getSlotType(slotId);
if (t == '\0') t = 'I'; // 类型未知时默认整型 if (t == '\0') t = 'I';
// 生成类型相关的加载指令 I_LOADF_LOAD
out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId); out.emit(OpHelper.opcode(t + "_LOAD") + " " + slotId);
} }
// 3. 生成 CALL 调用指令 /* 3. 发出 CALL 指令 */
out.emitCall(ins.getFunctionName(), ins.getArguments().size()); out.emitCall(ins.getFunctionName(), ins.getArguments().size());
// 4. 将返回值存入目标槽并记录槽的类型 /* -- 3.5 若被调用函数返回 void则无需保存返回值 -- */
int destSlot = slotMap.get(ins.getDest()); // 目标寄存器槽 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.emit(OpHelper.opcode(retType + "_STORE") + " " + destSlot);
out.setSlotType(destSlot, retType); // 标记返回值类型 out.setSlotType(destSlot, retType);
} }
} }

View File

@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.ir.builder; 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.core.IRFunction;
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils; import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
@ -32,6 +33,10 @@ public class FunctionBuilder {
*/ */
public IRFunction build(FunctionNode functionNode) { public IRFunction build(FunctionNode functionNode) {
// 在全局函数表中注册函数信息
GlobalFunctionTable.register(functionNode.name(), functionNode.returnType());
// 0) 基本初始化创建 IRFunction 实例与对应上下文 // 0) 基本初始化创建 IRFunction 实例与对应上下文
IRFunction irFunction = new IRFunction(functionNode.name()); IRFunction irFunction = new IRFunction(functionNode.name());
IRContext irContext = new IRContext(irFunction); IRContext irContext = new IRContext(irFunction);

View File

@ -0,0 +1,76 @@
package org.jcnc.snow.compiler.ir.common;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 全局函数返回类型表
* <p>
* 此工具类用于在编译前端构建每个函数 IR中间表示时登记函数的返回类型
* 以便后端在生成 {@code CALL} 指令时判断是否需要保存返回值
* </p>
*
* 使用说明
* <ul>
* <li>在函数 IR 构建阶段调用 {@link #register(String, String)} 方法登记函数名与返回类型</li>
* <li>在生成调用指令阶段通过 {@link #getReturnType(String)} 查询函数的返回类型</li>
* <li>返回类型统一为小写若调用 {@code register} 时传入的返回类型为 {@code null}则登记为 {@code "void"}</li>
* </ul>
*/
public final class GlobalFunctionTable {
/**
* 存储全局函数返回类型映射表
* <ul>
* <li>Key函数名不含模块限定</li>
* <li>Value返回类型统一转换为小写字符串若无返回值则为 {@code "void"}</li>
* </ul>
*/
private static final Map<String, String> RETURN_TYPES = new ConcurrentHashMap<>();
/**
* 私有构造函数防止实例化
*/
private GlobalFunctionTable() {
// 工具类禁止实例化
}
/**
* 登记或更新指定函数的返回类型
*
* <p>若传入的 {@code returnType} {@code null}则登记为 {@code "void"}
* 否则将去除前后空白并转换为小写后登记</p>
*
* @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()
);
}
/**
* 查询指定函数的返回类型
*
* <p>返回类型为登记时的值小写如果函数未登记过则返回 {@code null}</p>
*
* @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);
}
}