!37 fix: 优化函数调用指令以支持 void 类型
Merge pull request !37 from Luke/bugfix/fix-error-empty-function
This commit is contained in:
commit
7310884047
@ -9,13 +9,9 @@ module: Main
|
|||||||
end function
|
end function
|
||||||
|
|
||||||
function: foo
|
function: foo
|
||||||
return_type: int
|
return_type: void
|
||||||
body:
|
body:
|
||||||
if false then
|
|
||||||
return 1
|
|
||||||
end if
|
|
||||||
|
|
||||||
return 0
|
|
||||||
end body
|
end body
|
||||||
end function
|
end function
|
||||||
end module
|
end module
|
||||||
@ -1,12 +0,0 @@
|
|||||||
## 编译器输出
|
|
||||||
### Snow 源代码
|
|
||||||
#### Main.snow
|
|
||||||
```snow
|
|
||||||
function: main
|
|
||||||
return_type: int
|
|
||||||
body:
|
|
||||||
3 L
|
|
||||||
return 65537
|
|
||||||
end body
|
|
||||||
end function
|
|
||||||
```
|
|
||||||
@ -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_LOAD、F_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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("函数名不能为空或 null");
|
||||||
|
}
|
||||||
|
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("函数名不能为空或 null");
|
||||||
|
}
|
||||||
|
return RETURN_TYPES.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,41 +1,63 @@
|
|||||||
package org.jcnc.snow.compiler.ir.instruction;
|
package org.jcnc.snow.compiler.ir.instruction;
|
||||||
|
|
||||||
|
import org.jcnc.snow.compiler.ir.common.GlobalFunctionTable;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
import org.jcnc.snow.compiler.ir.core.IRInstruction;
|
||||||
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||||
|
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
import org.jcnc.snow.compiler.ir.core.IRVisitor;
|
||||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CallInstruction —— 表示一次函数调用,格式:dest = CALL functionName, arg1, arg2, ...
|
* CallInstruction —— 表示一次函数调用的中间代码指令
|
||||||
|
* <pre>
|
||||||
|
* dest = CALL foo, arg1, arg2, ...
|
||||||
|
* </pre>
|
||||||
|
* 若被调函数返回 void,则 {@code dest} 可以为 {@code null},
|
||||||
|
* 并且不会参与寄存器分配。
|
||||||
*/
|
*/
|
||||||
public class CallInstruction extends IRInstruction {
|
public class CallInstruction extends IRInstruction {
|
||||||
|
/** 调用结果目标寄存器;void 返回时可为 null */
|
||||||
private final IRVirtualRegister dest;
|
private final IRVirtualRegister dest;
|
||||||
|
/** 被调用的函数名(含模块限定) */
|
||||||
private final String functionName;
|
private final String functionName;
|
||||||
|
/** 实参列表 */
|
||||||
private final List<IRValue> arguments;
|
private final List<IRValue> arguments;
|
||||||
|
|
||||||
public CallInstruction(IRVirtualRegister dest, String functionName, List<IRValue> args) {
|
public CallInstruction(IRVirtualRegister dest,
|
||||||
|
String functionName,
|
||||||
|
List<IRValue> args) {
|
||||||
this.dest = dest;
|
this.dest = dest;
|
||||||
this.functionName = functionName;
|
this.functionName = functionName;
|
||||||
this.arguments = List.copyOf(args);
|
this.arguments = List.copyOf(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === 基本信息 ===
|
||||||
@Override
|
@Override
|
||||||
public IROpCode op() {
|
public IROpCode op() {
|
||||||
return IROpCode.CALL;
|
return IROpCode.CALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 仅在函数有返回值时才暴露目标寄存器 */
|
||||||
|
@Override
|
||||||
|
public IRVirtualRegister dest() {
|
||||||
|
return isVoidReturn() ? null : dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 操作数列表:void 调用不包含 dest */
|
||||||
@Override
|
@Override
|
||||||
public List<IRValue> operands() {
|
public List<IRValue> operands() {
|
||||||
List<IRValue> ops = new ArrayList<>();
|
List<IRValue> ops = new ArrayList<>();
|
||||||
|
if (!isVoidReturn() && dest != null) {
|
||||||
ops.add(dest);
|
ops.add(dest);
|
||||||
|
}
|
||||||
ops.addAll(arguments);
|
ops.addAll(arguments);
|
||||||
return ops;
|
return ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Getter ===
|
||||||
public IRVirtualRegister getDest() {
|
public IRVirtualRegister getDest() {
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
@ -48,14 +70,26 @@ public class CallInstruction extends IRInstruction {
|
|||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === 帮助方法 ===
|
||||||
|
/** 判断被调函数是否返回 void */
|
||||||
|
private boolean isVoidReturn() {
|
||||||
|
return "void".equals(GlobalFunctionTable.getReturnType(functionName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 访客模式 ===
|
||||||
@Override
|
@Override
|
||||||
public void accept(IRVisitor visitor) {
|
public void accept(IRVisitor visitor) {
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === 字符串表示 ===
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder(dest + " = CALL " + functionName);
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (!isVoidReturn() && dest != null) {
|
||||||
|
sb.append(dest).append(" = ");
|
||||||
|
}
|
||||||
|
sb.append("CALL ").append(functionName);
|
||||||
for (IRValue arg : arguments) {
|
for (IRValue arg : arguments) {
|
||||||
sb.append(", ").append(arg);
|
sb.append(", ").append(arg);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user