implements PR-O2.4
This commit is contained in:
parent
7679c5eeeb
commit
ea27561e65
@ -20,7 +20,6 @@ import p.studio.utilities.structures.ReadOnlyList;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -31,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
class BackendGateIIntegrationTest {
|
class BackendGateIIntegrationTest {
|
||||||
|
private final RuntimeCompatibilityAdapter compatibilityAdapter = new LocalRuntimeCompatibilityAdapter();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void gateI_syscSectionPresentAndEmpty() {
|
void gateI_syscSectionPresentAndEmpty() {
|
||||||
@ -38,7 +38,7 @@ class BackendGateIIntegrationTest {
|
|||||||
fn("main", ReadOnlyList.from(ret()))));
|
fn("main", ReadOnlyList.from(ret()))));
|
||||||
|
|
||||||
assertTrue(module.syscalls().isEmpty());
|
assertTrue(module.syscalls().isEmpty());
|
||||||
assertEquals(CompatibilityError.NONE, new RuntimePreloadCompatibilityChecker().check(module).error());
|
assertEquals(CompatibilityError.NONE, compatibilityAdapter.check(module).error());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -48,9 +48,11 @@ class BackendGateIIntegrationTest {
|
|||||||
callHost("gfx", "draw_pixel", 1, 2, 0),
|
callHost("gfx", "draw_pixel", 1, 2, 0),
|
||||||
ret()))));
|
ret()))));
|
||||||
|
|
||||||
final var check = new RuntimePreloadCompatibilityChecker().check(module);
|
final var check = compatibilityAdapter.check(module);
|
||||||
assertEquals(CompatibilityError.NONE, check.error());
|
assertEquals(CompatibilityError.NONE, check.error());
|
||||||
assertTrue(check.hostcallCount() > 0);
|
assertTrue(check.hostcallCount() > 0);
|
||||||
|
assertEquals("local", check.adapterMode());
|
||||||
|
assertTrue(!check.runtimeLine().isBlank());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -64,7 +66,7 @@ class BackendGateIIntegrationTest {
|
|||||||
ReadOnlyList.empty(),
|
ReadOnlyList.empty(),
|
||||||
ReadOnlyList.from(new BytecodeModule.SyscallDecl("gfx", "draw_pixel", 1, 1, 0)));
|
ReadOnlyList.from(new BytecodeModule.SyscallDecl("gfx", "draw_pixel", 1, 1, 0)));
|
||||||
|
|
||||||
final var check = new RuntimePreloadCompatibilityChecker().check(module);
|
final var check = compatibilityAdapter.check(module);
|
||||||
assertEquals(CompatibilityError.HOSTCALL_INDEX_OUT_OF_BOUNDS, check.error());
|
assertEquals(CompatibilityError.HOSTCALL_INDEX_OUT_OF_BOUNDS, check.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ class BackendGateIIntegrationTest {
|
|||||||
ReadOnlyList.empty(),
|
ReadOnlyList.empty(),
|
||||||
ReadOnlyList.from(new BytecodeModule.SyscallDecl("gfx", "draw_pixel", 1, 1, 0)));
|
ReadOnlyList.from(new BytecodeModule.SyscallDecl("gfx", "draw_pixel", 1, 1, 0)));
|
||||||
|
|
||||||
final var check = new RuntimePreloadCompatibilityChecker().check(module);
|
final var check = compatibilityAdapter.check(module);
|
||||||
assertEquals(CompatibilityError.UNUSED_SYSC_DECL, check.error());
|
assertEquals(CompatibilityError.UNUSED_SYSC_DECL, check.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +96,7 @@ class BackendGateIIntegrationTest {
|
|||||||
ReadOnlyList.empty(),
|
ReadOnlyList.empty(),
|
||||||
ReadOnlyList.empty());
|
ReadOnlyList.empty());
|
||||||
|
|
||||||
final var check = new RuntimePreloadCompatibilityChecker().check(module);
|
final var check = compatibilityAdapter.check(module);
|
||||||
assertEquals(CompatibilityError.RAW_SYSCALL_IN_PRELOAD, check.error());
|
assertEquals(CompatibilityError.RAW_SYSCALL_IN_PRELOAD, check.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +133,7 @@ class BackendGateIIntegrationTest {
|
|||||||
final var caps = Set.of("input");
|
final var caps = Set.of("input");
|
||||||
final var required = new HashMap<String, String>();
|
final var required = new HashMap<String, String>();
|
||||||
required.put("gfx::draw_pixel::1", "gfx");
|
required.put("gfx::draw_pixel::1", "gfx");
|
||||||
final var check = new RuntimePreloadCompatibilityChecker(caps, required).check(module);
|
final var check = new LocalRuntimeCompatibilityAdapter(caps, required).check(module);
|
||||||
assertEquals(CompatibilityError.MISSING_CAPABILITY, check.error());
|
assertEquals(CompatibilityError.MISSING_CAPABILITY, check.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ class BackendGateIIntegrationTest {
|
|||||||
callIntrinsic("core.color.pack", 1, 0x2000),
|
callIntrinsic("core.color.pack", 1, 0x2000),
|
||||||
ret()))));
|
ret()))));
|
||||||
|
|
||||||
final var check = new RuntimePreloadCompatibilityChecker().check(module);
|
final var check = compatibilityAdapter.check(module);
|
||||||
assertEquals(CompatibilityError.NONE, check.error());
|
assertEquals(CompatibilityError.NONE, check.error());
|
||||||
assertTrue(module.syscalls().isEmpty());
|
assertTrue(module.syscalls().isEmpty());
|
||||||
}
|
}
|
||||||
@ -244,83 +246,4 @@ class BackendGateIIntegrationTest {
|
|||||||
out.putShort((short) 0x51);
|
out.putShort((short) 0x51);
|
||||||
return out.array();
|
return out.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CompatibilityError {
|
|
||||||
NONE,
|
|
||||||
RAW_SYSCALL_IN_PRELOAD,
|
|
||||||
HOSTCALL_INDEX_OUT_OF_BOUNDS,
|
|
||||||
UNUSED_SYSC_DECL,
|
|
||||||
MISSING_CAPABILITY,
|
|
||||||
}
|
|
||||||
|
|
||||||
private record CompatibilityResult(
|
|
||||||
CompatibilityError error,
|
|
||||||
int hostcallCount) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class RuntimePreloadCompatibilityChecker {
|
|
||||||
private final Set<String> capabilities;
|
|
||||||
private final Map<String, String> requiredCapabilityBySyscallIdentity;
|
|
||||||
|
|
||||||
private RuntimePreloadCompatibilityChecker() {
|
|
||||||
this(Set.of("gfx", "input", "audio", "system"), Map.of());
|
|
||||||
}
|
|
||||||
|
|
||||||
private RuntimePreloadCompatibilityChecker(
|
|
||||||
final Set<String> capabilities,
|
|
||||||
final Map<String, String> requiredCapabilityBySyscallIdentity) {
|
|
||||||
this.capabilities = capabilities == null ? Set.of() : new HashSet<>(capabilities);
|
|
||||||
this.requiredCapabilityBySyscallIdentity = requiredCapabilityBySyscallIdentity == null
|
|
||||||
? Map.of()
|
|
||||||
: new HashMap<>(requiredCapabilityBySyscallIdentity);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompatibilityResult check(final BytecodeModule module) {
|
|
||||||
final var used = new boolean[module.syscalls().size()];
|
|
||||||
var hostcallCount = 0;
|
|
||||||
var pc = 0;
|
|
||||||
while (pc < module.code().length) {
|
|
||||||
final var opcode = readU16(module.code(), pc);
|
|
||||||
switch (opcode) {
|
|
||||||
case 0x70 -> {
|
|
||||||
return new CompatibilityResult(CompatibilityError.RAW_SYSCALL_IN_PRELOAD, hostcallCount);
|
|
||||||
}
|
|
||||||
case 0x71 -> {
|
|
||||||
final var syscIndex = readU32(module.code(), pc + 2);
|
|
||||||
if (syscIndex < 0 || syscIndex >= module.syscalls().size()) {
|
|
||||||
return new CompatibilityResult(CompatibilityError.HOSTCALL_INDEX_OUT_OF_BOUNDS, hostcallCount);
|
|
||||||
}
|
|
||||||
used[syscIndex] = true;
|
|
||||||
hostcallCount++;
|
|
||||||
final var decl = module.syscalls().get(syscIndex);
|
|
||||||
final var identity = decl.module() + "::" + decl.name() + "::" + decl.version();
|
|
||||||
final var requiredCap = requiredCapabilityBySyscallIdentity.get(identity);
|
|
||||||
if (requiredCap != null && !capabilities.contains(requiredCap)) {
|
|
||||||
return new CompatibilityResult(CompatibilityError.MISSING_CAPABILITY, hostcallCount);
|
|
||||||
}
|
|
||||||
pc += 6;
|
|
||||||
}
|
|
||||||
case 0x50, 0x72, 0x02, 0x03, 0x04, 0x10, 0x18, 0x40, 0x41, 0x42, 0x43, 0x56 -> pc += 6;
|
|
||||||
case 0x14, 0x15, 0x52, 0x54 -> pc += 10;
|
|
||||||
case 0x16 -> pc += 3;
|
|
||||||
default -> pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var i = 0; i < used.length; i++) {
|
|
||||||
if (!used[i]) {
|
|
||||||
return new CompatibilityResult(CompatibilityError.UNUSED_SYSC_DECL, hostcallCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new CompatibilityResult(CompatibilityError.NONE, hostcallCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int readU16(final byte[] code, final int offset) {
|
|
||||||
return ByteBuffer.wrap(code, offset, 2).order(ByteOrder.LITTLE_ENDIAN).getShort() & 0xFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int readU32(final byte[] code, final int offset) {
|
|
||||||
return ByteBuffer.wrap(code, offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
package p.studio.compiler.integration;
|
||||||
|
|
||||||
|
enum CompatibilityError {
|
||||||
|
NONE,
|
||||||
|
RAW_SYSCALL_IN_PRELOAD,
|
||||||
|
HOSTCALL_INDEX_OUT_OF_BOUNDS,
|
||||||
|
UNUSED_SYSC_DECL,
|
||||||
|
MISSING_CAPABILITY,
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package p.studio.compiler.integration;
|
||||||
|
|
||||||
|
record CompatibilityResult(
|
||||||
|
CompatibilityError error,
|
||||||
|
int hostcallCount,
|
||||||
|
String runtimeLine,
|
||||||
|
String adapterMode) {
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package p.studio.compiler.integration;
|
||||||
|
|
||||||
|
import p.studio.compiler.backend.bytecode.BytecodeModule;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class LocalRuntimeCompatibilityAdapter implements RuntimeCompatibilityAdapter {
|
||||||
|
private final Set<String> capabilities;
|
||||||
|
private final Map<String, String> requiredCapabilityBySyscallIdentity;
|
||||||
|
private final String runtimeLine;
|
||||||
|
|
||||||
|
LocalRuntimeCompatibilityAdapter() {
|
||||||
|
this(Set.of("gfx", "input", "audio", "system"), Map.of(), "runtime-local-sim-v1");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalRuntimeCompatibilityAdapter(
|
||||||
|
final Set<String> capabilities,
|
||||||
|
final Map<String, String> requiredCapabilityBySyscallIdentity) {
|
||||||
|
this(capabilities, requiredCapabilityBySyscallIdentity, "runtime-local-sim-v1");
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalRuntimeCompatibilityAdapter(
|
||||||
|
final Set<String> capabilities,
|
||||||
|
final Map<String, String> requiredCapabilityBySyscallIdentity,
|
||||||
|
final String runtimeLine) {
|
||||||
|
this.capabilities = capabilities == null ? Set.of() : new HashSet<>(capabilities);
|
||||||
|
this.requiredCapabilityBySyscallIdentity = requiredCapabilityBySyscallIdentity == null
|
||||||
|
? Map.of()
|
||||||
|
: new HashMap<>(requiredCapabilityBySyscallIdentity);
|
||||||
|
this.runtimeLine = runtimeLine == null || runtimeLine.isBlank() ? "runtime-local-sim-v1" : runtimeLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompatibilityResult check(final BytecodeModule module) {
|
||||||
|
final var used = new boolean[module.syscalls().size()];
|
||||||
|
var hostcallCount = 0;
|
||||||
|
var pc = 0;
|
||||||
|
while (pc < module.code().length) {
|
||||||
|
final var opcode = readU16(module.code(), pc);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0x70 -> {
|
||||||
|
return new CompatibilityResult(
|
||||||
|
CompatibilityError.RAW_SYSCALL_IN_PRELOAD,
|
||||||
|
hostcallCount,
|
||||||
|
runtimeLine,
|
||||||
|
"local");
|
||||||
|
}
|
||||||
|
case 0x71 -> {
|
||||||
|
final var syscIndex = readU32(module.code(), pc + 2);
|
||||||
|
if (syscIndex < 0 || syscIndex >= module.syscalls().size()) {
|
||||||
|
return new CompatibilityResult(
|
||||||
|
CompatibilityError.HOSTCALL_INDEX_OUT_OF_BOUNDS,
|
||||||
|
hostcallCount,
|
||||||
|
runtimeLine,
|
||||||
|
"local");
|
||||||
|
}
|
||||||
|
used[syscIndex] = true;
|
||||||
|
hostcallCount++;
|
||||||
|
final var decl = module.syscalls().get(syscIndex);
|
||||||
|
final var identity = decl.module() + "::" + decl.name() + "::" + decl.version();
|
||||||
|
final var requiredCap = requiredCapabilityBySyscallIdentity.get(identity);
|
||||||
|
if (requiredCap != null && !capabilities.contains(requiredCap)) {
|
||||||
|
return new CompatibilityResult(
|
||||||
|
CompatibilityError.MISSING_CAPABILITY,
|
||||||
|
hostcallCount,
|
||||||
|
runtimeLine,
|
||||||
|
"local");
|
||||||
|
}
|
||||||
|
pc += 6;
|
||||||
|
}
|
||||||
|
case 0x50, 0x72, 0x02, 0x03, 0x04, 0x10, 0x18, 0x40, 0x41, 0x42, 0x43, 0x56 -> pc += 6;
|
||||||
|
case 0x14, 0x15, 0x52, 0x54 -> pc += 10;
|
||||||
|
case 0x16 -> pc += 3;
|
||||||
|
default -> pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = 0; i < used.length; i++) {
|
||||||
|
if (!used[i]) {
|
||||||
|
return new CompatibilityResult(
|
||||||
|
CompatibilityError.UNUSED_SYSC_DECL,
|
||||||
|
hostcallCount,
|
||||||
|
runtimeLine,
|
||||||
|
"local");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new CompatibilityResult(CompatibilityError.NONE, hostcallCount, runtimeLine, "local");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readU16(final byte[] code, final int offset) {
|
||||||
|
return ByteBuffer.wrap(code, offset, 2).order(ByteOrder.LITTLE_ENDIAN).getShort() & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readU32(final byte[] code, final int offset) {
|
||||||
|
return ByteBuffer.wrap(code, offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package p.studio.compiler.integration;
|
||||||
|
|
||||||
|
import p.studio.compiler.backend.bytecode.BytecodeModule;
|
||||||
|
|
||||||
|
interface RuntimeCompatibilityAdapter {
|
||||||
|
CompatibilityResult check(BytecodeModule module);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user