implements PR-08.5

This commit is contained in:
bQUARKz 2026-03-09 14:29:35 +00:00
parent 505a4f72ce
commit 12a6602b0a
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 262 additions and 29 deletions

View File

@ -1,11 +1,16 @@
package p.studio.compiler.backend.irvm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.OptionalInt;
final class IRVMIntrinsicRegistry {
private static final Map<IntrinsicKey, Integer> FINAL_ID_BY_INTRINSIC = createRegistry();
private static final String REGISTRY_RESOURCE = "/intrinsics/registry-v1.csv";
private static final Map<IntrinsicKey, Integer> FINAL_ID_BY_INTRINSIC = loadRegistry();
private IRVMIntrinsicRegistry() {
}
@ -23,38 +28,79 @@ final class IRVMIntrinsicRegistry {
return OptionalInt.of(resolved);
}
private static Map<IntrinsicKey, Integer> createRegistry() {
static Map<String, Integer> snapshotByCanonicalIdentity() {
final var snapshot = new HashMap<String, Integer>(FINAL_ID_BY_INTRINSIC.size());
for (final var entry : FINAL_ID_BY_INTRINSIC.entrySet()) {
snapshot.put(canonicalIdentity(entry.getKey().canonicalName(), entry.getKey().canonicalVersion()), entry.getValue());
}
return Map.copyOf(snapshot);
}
private static Map<IntrinsicKey, Integer> loadRegistry() {
final var stream = IRVMIntrinsicRegistry.class.getResourceAsStream(REGISTRY_RESOURCE);
if (stream == null) {
throw new IllegalStateException("missing intrinsic registry resource: " + REGISTRY_RESOURCE);
}
final var registry = new HashMap<IntrinsicKey, Integer>();
register(registry, "vec2.dot", 1, 0x1000);
register(registry, "vec2.length", 1, 0x1001);
register(registry, "input.pad", 1, 0x2000);
register(registry, "input.touch", 1, 0x2001);
register(registry, "input.pad.up", 1, 0x2010);
register(registry, "input.pad.down", 1, 0x2011);
register(registry, "input.pad.left", 1, 0x2012);
register(registry, "input.pad.right", 1, 0x2013);
register(registry, "input.pad.a", 1, 0x2014);
register(registry, "input.pad.b", 1, 0x2015);
register(registry, "input.pad.x", 1, 0x2016);
register(registry, "input.pad.y", 1, 0x2017);
register(registry, "input.pad.l", 1, 0x2018);
register(registry, "input.pad.r", 1, 0x2019);
register(registry, "input.pad.start", 1, 0x201A);
register(registry, "input.pad.select", 1, 0x201B);
register(registry, "input.touch.button", 1, 0x2020);
register(registry, "input.touch.x", 1, 0x2021);
register(registry, "input.touch.y", 1, 0x2022);
register(registry, "input.button.pressed", 1, 0x2030);
register(registry, "input.button.released", 1, 0x2031);
register(registry, "input.button.down", 1, 0x2032);
register(registry, "input.button.hold", 1, 0x2033);
final var identityByFinalId = new HashMap<Integer, String>();
try (final var reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
String rawLine;
int lineNumber = 0;
while ((rawLine = reader.readLine()) != null) {
lineNumber++;
final var line = rawLine.trim();
if (line.isBlank() || line.startsWith("#")) {
continue;
}
final var columns = line.split(",", -1);
if (columns.length != 3) {
throw new IllegalStateException("invalid intrinsic registry row at line " + lineNumber + ": " + line);
}
final var canonicalName = columns[0].trim();
final long canonicalVersion;
try {
canonicalVersion = Long.parseLong(columns[1].trim());
} catch (NumberFormatException ex) {
throw new IllegalStateException("invalid intrinsic version at line " + lineNumber + ": " + line, ex);
}
final int finalId = parseFinalId(columns[2].trim(), lineNumber, line);
register(registry, canonicalName, canonicalVersion, finalId);
final var identity = canonicalIdentity(canonicalName, canonicalVersion);
final var previous = identityByFinalId.putIfAbsent(finalId, identity);
if (previous != null && !previous.equals(identity)) {
throw new IllegalStateException(
"duplicate intrinsic final id 0x%08X for %s and %s".formatted(finalId, previous, identity));
}
}
} catch (IOException ex) {
throw new IllegalStateException("failed to read intrinsic registry resource: " + REGISTRY_RESOURCE, ex);
}
return Map.copyOf(registry);
}
private static int parseFinalId(
final String rawId,
final int lineNumber,
final String line) {
try {
if (rawId.startsWith("0x") || rawId.startsWith("0X")) {
return Integer.parseUnsignedInt(rawId.substring(2), 16);
}
return Integer.parseUnsignedInt(rawId, 10);
} catch (NumberFormatException ex) {
throw new IllegalStateException(
"invalid intrinsic final id at line %d: %s".formatted(lineNumber, line),
ex);
}
}
private static String canonicalIdentity(
final String canonicalName,
final long canonicalVersion) {
return canonicalName + "@" + canonicalVersion;
}
private static void register(
final Map<IntrinsicKey, Integer> registry,
final String canonicalName,

View File

@ -0,0 +1,24 @@
# canonicalName,canonicalVersion,finalId
vec2.dot,1,0x1000
vec2.length,1,0x1001
input.pad,1,0x2000
input.touch,1,0x2001
input.pad.up,1,0x2010
input.pad.down,1,0x2011
input.pad.left,1,0x2012
input.pad.right,1,0x2013
input.pad.a,1,0x2014
input.pad.b,1,0x2015
input.pad.x,1,0x2016
input.pad.y,1,0x2017
input.pad.l,1,0x2018
input.pad.r,1,0x2019
input.pad.start,1,0x201A
input.pad.select,1,0x201B
input.touch.button,1,0x2020
input.touch.x,1,0x2021
input.touch.y,1,0x2022
input.button.pressed,1,0x2030
input.button.released,1,0x2031
input.button.down,1,0x2032
input.button.hold,1,0x2033
1 # canonicalName canonicalVersion finalId
2 vec2.dot 1 0x1000
3 vec2.length 1 0x1001
4 input.pad 1 0x2000
5 input.touch 1 0x2001
6 input.pad.up 1 0x2010
7 input.pad.down 1 0x2011
8 input.pad.left 1 0x2012
9 input.pad.right 1 0x2013
10 input.pad.a 1 0x2014
11 input.pad.b 1 0x2015
12 input.pad.x 1 0x2016
13 input.pad.y 1 0x2017
14 input.pad.l 1 0x2018
15 input.pad.r 1 0x2019
16 input.pad.start 1 0x201A
17 input.pad.select 1 0x201B
18 input.touch.button 1 0x2020
19 input.touch.x 1 0x2021
20 input.touch.y 1 0x2022
21 input.button.pressed 1 0x2030
22 input.button.released 1 0x2031
23 input.button.down 1 0x2032
24 input.button.hold 1 0x2033

View File

@ -0,0 +1,163 @@
package p.studio.compiler.backend.irvm;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.fail;
class IRVMIntrinsicRegistryParityTest {
private static final Path RUNTIME_INTRINSICS_FILE = Path.of("../runtime/crates/console/prometeu-vm/src/builtins.rs");
private static final Pattern OWNER_PATTERN = Pattern.compile("owner:\\s*\"([^\"]+)\",");
private static final Pattern NAME_PATTERN = Pattern.compile("name:\\s*\"([^\"]+)\",");
private static final Pattern VERSION_PATTERN = Pattern.compile("version:\\s*(\\d+),");
private static final Pattern ID_PATTERN = Pattern.compile("id:\\s*(0x[0-9A-Fa-f]+|\\d+),");
private static final String STRICT_PARITY_ENV = "PROMETEU_INTRINSIC_PARITY_STRICT";
@Test
void registryResourceMustExposeExpectedCanonicalEntries() {
final var snapshot = IRVMIntrinsicRegistry.snapshotByCanonicalIdentity();
assertFalse(snapshot.isEmpty());
assertEquals(23, snapshot.size());
assertEquals(0x2000, snapshot.get("input.pad@1"));
assertEquals(0x2033, snapshot.get("input.button.hold@1"));
}
@Test
void registryMustStayInSyncWithRuntimeBuiltinsTable() throws IOException {
final var compilerSnapshot = IRVMIntrinsicRegistry.snapshotByCanonicalIdentity();
if (!Files.exists(RUNTIME_INTRINSICS_FILE)) {
if (strictParityEnabled()) {
fail("runtime intrinsic table not found: " + RUNTIME_INTRINSICS_FILE.toAbsolutePath());
}
return;
}
final var runtimeSnapshot = parseRuntimeIntrinsicSnapshot(RUNTIME_INTRINSICS_FILE);
assertEquals(runtimeSnapshot, compilerSnapshot, parityDiff(runtimeSnapshot, compilerSnapshot));
}
private Map<String, Integer> parseRuntimeIntrinsicSnapshot(final Path runtimeFile) throws IOException {
final var runtimeSource = Files.readAllLines(runtimeFile, StandardCharsets.UTF_8);
final var mapped = new LinkedHashMap<String, Integer>();
var insideIntrinsicsArray = false;
var insideEntry = false;
String owner = null;
String name = null;
Long version = null;
Integer id = null;
for (final var rawLine : runtimeSource) {
final var line = rawLine.trim();
if (!insideIntrinsicsArray) {
if (line.startsWith("const INTRINSICS:")) {
insideIntrinsicsArray = true;
}
continue;
}
if (line.equals("];")) {
break;
}
if (line.startsWith("IntrinsicMeta {")) {
insideEntry = true;
owner = null;
name = null;
version = null;
id = null;
continue;
}
if (!insideEntry) {
continue;
}
final var ownerMatcher = OWNER_PATTERN.matcher(line);
if (ownerMatcher.find()) {
owner = ownerMatcher.group(1);
}
final var nameMatcher = NAME_PATTERN.matcher(line);
if (nameMatcher.find()) {
name = nameMatcher.group(1);
}
final var versionMatcher = VERSION_PATTERN.matcher(line);
if (versionMatcher.find()) {
version = Long.parseLong(versionMatcher.group(1));
}
final var idMatcher = ID_PATTERN.matcher(line);
if (idMatcher.find()) {
id = parseRuntimeId(idMatcher.group(1));
}
if (line.equals("},")) {
if (owner == null || name == null || version == null || id == null) {
throw new IllegalStateException("invalid runtime intrinsic entry near line: " + rawLine);
}
final var identity = owner + "." + name + "@" + version;
final var previous = mapped.putIfAbsent(identity, id);
if (previous != null && previous != id) {
throw new IllegalStateException(
"runtime intrinsic identity collision for " + identity + ": " + previous + " vs " + id);
}
insideEntry = false;
}
}
return Map.copyOf(mapped);
}
private int parseRuntimeId(final String rawId) {
if (rawId.startsWith("0x") || rawId.startsWith("0X")) {
return Integer.parseUnsignedInt(rawId.substring(2), 16);
}
return Integer.parseUnsignedInt(rawId, 10);
}
private boolean strictParityEnabled() {
final var strict = System.getenv(STRICT_PARITY_ENV);
if (isTruthy(strict)) {
return true;
}
return isTruthy(System.getenv("CI"));
}
private boolean isTruthy(final String value) {
if (value == null) {
return false;
}
return switch (value.trim().toLowerCase()) {
case "1", "true", "yes", "on" -> true;
default -> false;
};
}
private String parityDiff(
final Map<String, Integer> runtimeSnapshot,
final Map<String, Integer> compilerSnapshot) {
final var missingInCompiler = new ArrayList<String>();
final var missingInRuntime = new ArrayList<String>();
final var mismatchedIds = new ArrayList<String>();
for (final var runtimeEntry : runtimeSnapshot.entrySet()) {
final var compilerId = compilerSnapshot.get(runtimeEntry.getKey());
if (compilerId == null) {
missingInCompiler.add(runtimeEntry.getKey());
continue;
}
if (!runtimeEntry.getValue().equals(compilerId)) {
mismatchedIds.add(runtimeEntry.getKey() + " runtime=" + runtimeEntry.getValue() + " compiler=" + compilerId);
}
}
for (final var compilerEntry : compilerSnapshot.entrySet()) {
if (!runtimeSnapshot.containsKey(compilerEntry.getKey())) {
missingInRuntime.add(compilerEntry.getKey());
}
}
return "intrinsic registry parity mismatch"
+ "\nmissingInCompiler=" + List.copyOf(missingInCompiler)
+ "\nmissingInRuntime=" + List.copyOf(missingInRuntime)
+ "\nidMismatches=" + List.copyOf(mismatchedIds);
}
}