implements PR-O1.5

This commit is contained in:
bQUARKz 2026-03-07 18:04:20 +00:00
parent e36c59fe26
commit b5622bd9df
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 110 additions and 20 deletions

View File

@ -30,14 +30,21 @@ public class BytecodeEmitter {
for (final var op : function.operations()) {
final var pc = code.size();
switch (op.kind()) {
case HALT -> writeOpNoImm(code, OP_HALT);
case RET -> writeOpNoImm(code, OP_RET);
case CALL_FUNC -> writeOpU32(code, OP_CALL, op.immediate());
case HALT -> {
writeOpNoImm(code, OP_HALT);
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
}
case RET -> {
writeOpNoImm(code, OP_RET);
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
}
case CALL_FUNC -> {
writeOpU32(code, OP_CALL, op.immediate());
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
}
case INTRINSIC -> {
writeOpU32(code, OP_INTRINSIC, op.immediate());
if (op.span() != null) {
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, op.span()));
}
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
}
case HOSTCALL -> {
final var decl = Objects.requireNonNull(op.syscallDecl(), "syscallDecl");
@ -59,9 +66,7 @@ public class BytecodeEmitter {
}
final var index = syscallIndexByIdentity.get(identity);
writeOpU32(code, OP_HOSTCALL, index);
if (op.span() != null) {
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, op.span()));
}
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
}
case RAW_SYSCALL -> throw new BytecodeMarshalingException(
BytecodeMarshalingErrorCode.MARSHAL_LINKAGE_RAW_SYSCALL_IN_PRELOAD,
@ -98,6 +103,13 @@ public class BytecodeEmitter {
out.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(immediate).array());
}
private static BytecodeModule.SourceSpan spanOrUnknown(final BytecodeModule.SourceSpan span) {
if (span == null) {
return new BytecodeModule.SourceSpan(-1, 0, 0);
}
return span;
}
private record SyscallIdentity(
String module,
String name,
@ -154,26 +166,50 @@ public class BytecodeEmitter {
}
public static Operation halt() {
return new Operation(OperationKind.HALT, 0, null, null, null, null);
return halt(null);
}
public static Operation halt(final BytecodeModule.SourceSpan span) {
return new Operation(OperationKind.HALT, 0, null, null, null, span);
}
public static Operation ret() {
return new Operation(OperationKind.RET, 0, null, null, null, null);
return ret(null);
}
public static Operation ret(final BytecodeModule.SourceSpan span) {
return new Operation(OperationKind.RET, 0, null, null, null, span);
}
public static Operation intrinsic(final int intrinsicId) {
return new Operation(OperationKind.INTRINSIC, intrinsicId, null, null, null, null);
return intrinsic(intrinsicId, null);
}
public static Operation intrinsic(final int intrinsicId, final BytecodeModule.SourceSpan span) {
return new Operation(OperationKind.INTRINSIC, intrinsicId, null, null, null, span);
}
public static Operation callFunc(final int funcId) {
return new Operation(OperationKind.CALL_FUNC, funcId, null, null, null, null);
return callFunc(funcId, null);
}
public static Operation callFunc(final int funcId, final BytecodeModule.SourceSpan span) {
return new Operation(OperationKind.CALL_FUNC, funcId, null, null, null, span);
}
public static Operation hostcall(
final BytecodeModule.SyscallDecl decl,
final Integer expectedArgSlots,
final Integer expectedRetSlots) {
return new Operation(OperationKind.HOSTCALL, 0, decl, expectedArgSlots, expectedRetSlots, null);
return hostcall(decl, expectedArgSlots, expectedRetSlots, null);
}
public static Operation hostcall(
final BytecodeModule.SyscallDecl decl,
final Integer expectedArgSlots,
final Integer expectedRetSlots,
final BytecodeModule.SourceSpan span) {
return new Operation(OperationKind.HOSTCALL, 0, decl, expectedArgSlots, expectedRetSlots, span);
}
public static Operation rawSyscall(final int syscallId) {

View File

@ -1,8 +1,10 @@
package p.studio.compiler.backend.irvm;
import p.studio.compiler.backend.bytecode.BytecodeEmitter;
import p.studio.compiler.backend.bytecode.BytecodeModule;
import p.studio.compiler.models.IRBackend;
import p.studio.compiler.models.IRBackendExecutableFunction;
import p.studio.compiler.source.Span;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
@ -48,14 +50,15 @@ public class LowerToIRVMService {
final var instructions = new ArrayList<IRVMInstruction>(fn.instructions().size());
final var operations = new ArrayList<BytecodeEmitter.Operation>(fn.instructions().size());
for (final var instr : fn.instructions()) {
final var sourceSpan = toBytecodeSpan(fn.fileId().getId(), instr.span());
switch (instr.kind()) {
case HALT -> {
instructions.add(new IRVMInstruction(IRVMOp.HALT, null));
operations.add(BytecodeEmitter.Operation.halt());
operations.add(BytecodeEmitter.Operation.halt(sourceSpan));
}
case RET -> {
instructions.add(new IRVMInstruction(IRVMOp.RET, null));
operations.add(BytecodeEmitter.Operation.ret());
operations.add(BytecodeEmitter.Operation.ret(sourceSpan));
}
case CALL_FUNC -> {
final var key = callableKey(instr.calleeModuleKey(), instr.calleeCallableName());
@ -85,7 +88,7 @@ public class LowerToIRVMService {
calleeFunction.returnSlots()));
}
instructions.add(new IRVMInstruction(IRVMOp.CALL, calleeId));
operations.add(BytecodeEmitter.Operation.callFunc(calleeId));
operations.add(BytecodeEmitter.Operation.callFunc(calleeId, sourceSpan));
}
case CALL_HOST -> {
final var host = instr.hostCall();
@ -98,12 +101,13 @@ public class LowerToIRVMService {
host.argSlots(),
host.retSlots()),
host.argSlots(),
host.retSlots()));
host.retSlots(),
sourceSpan));
}
case CALL_INTRINSIC -> {
final var intrinsic = instr.intrinsicCall();
instructions.add(new IRVMInstruction(IRVMOp.INTRINSIC, intrinsic.intrinsicId()));
operations.add(BytecodeEmitter.Operation.intrinsic(intrinsic.intrinsicId()));
operations.add(BytecodeEmitter.Operation.intrinsic(intrinsic.intrinsicId(), sourceSpan));
}
}
}
@ -160,4 +164,27 @@ public class LowerToIRVMService {
final String callableName) {
return (moduleKey == null ? "" : moduleKey) + "::" + callableName;
}
private BytecodeModule.SourceSpan toBytecodeSpan(
final int fallbackFileId,
final Span span) {
final var sourceSpan = span == null ? Span.none() : span;
final var fileId = sourceSpan.getFileId() == null || sourceSpan.getFileId().isNone()
? fallbackFileId
: sourceSpan.getFileId().getId();
return new BytecodeModule.SourceSpan(
fileId,
safeToInt(sourceSpan.getStart()),
safeToInt(sourceSpan.getEnd()));
}
private int safeToInt(final long value) {
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
if (value < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
return (int) value;
}
}

View File

@ -116,6 +116,33 @@ class BytecodeEmitterTest {
assertEquals(0x2000, readU32(module.code(), 2));
}
@Test
void emitMustProducePcToSpanForEveryInstructionStartWhenSpansAreProvided() {
final var emitter = new BytecodeEmitter();
final var module = emitter.emit(new BytecodeEmitter.EmissionPlan(
0,
ReadOnlyList.empty(),
ReadOnlyList.empty(),
ReadOnlyList.from(
new BytecodeEmitter.FunctionPlan(
"main",
0,
0,
0,
4,
ReadOnlyList.from(
BytecodeEmitter.Operation.halt(new BytecodeModule.SourceSpan(1, 0, 1)),
BytecodeEmitter.Operation.callFunc(0, new BytecodeModule.SourceSpan(1, 2, 3)),
BytecodeEmitter.Operation.intrinsic(0x2000, new BytecodeModule.SourceSpan(1, 4, 5)),
BytecodeEmitter.Operation.ret(new BytecodeModule.SourceSpan(1, 6, 7)))))));
assertEquals(4, module.debugInfo().pcToSpan().size());
assertEquals(0, module.debugInfo().pcToSpan().get(0).pc());
assertEquals(2, module.debugInfo().pcToSpan().get(1).pc());
assertEquals(8, module.debugInfo().pcToSpan().get(2).pc());
assertEquals(14, module.debugInfo().pcToSpan().get(3).pc());
}
private static int readU16(final byte[] bytes, final int offset) {
return ByteBuffer.wrap(bytes, offset, 2).order(ByteOrder.LITTLE_ENDIAN).getShort() & 0xFFFF;
}
@ -124,4 +151,3 @@ class BytecodeEmitterTest {
return ByteBuffer.wrap(bytes, offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
}

View File

@ -52,6 +52,7 @@ class LowerToIRVMServiceTest {
final var emissionOps = lowered.emissionPlan().functions().getFirst().operations();
assertEquals(p.studio.compiler.backend.bytecode.BytecodeEmitter.OperationKind.HOSTCALL, emissionOps.get(0).kind());
assertEquals(p.studio.compiler.backend.bytecode.BytecodeEmitter.OperationKind.INTRINSIC, emissionOps.get(1).kind());
assertTrue(emissionOps.stream().allMatch(op -> op.span() != null));
}
@Test