implements PR-O3.2
This commit is contained in:
parent
a38fedc591
commit
0df936e36f
@ -6,6 +6,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -23,6 +24,8 @@ public class BytecodeEmitter {
|
||||
public BytecodeModule emit(
|
||||
final EmissionPlan plan) {
|
||||
final var inputPlan = plan == null ? EmissionPlan.empty() : plan;
|
||||
final var canonicalConstPool = dedupeConstPool(inputPlan.constPool());
|
||||
final var canonicalExports = dedupeExports(inputPlan.exports());
|
||||
final var functionFragments = new ArrayList<BytecodeFunctionLayoutBuilder.FunctionFragment>(inputPlan.functions().size());
|
||||
final var orderedSyscalls = new LinkedHashMap<SyscallIdentity, BytecodeModule.SyscallDecl>();
|
||||
final var syscallIndexByIdentity = new LinkedHashMap<SyscallIdentity, Integer>();
|
||||
@ -101,11 +104,11 @@ public class BytecodeEmitter {
|
||||
final var layout = BytecodeFunctionLayoutBuilder.build(ReadOnlyList.wrap(functionFragments));
|
||||
return new BytecodeModule(
|
||||
inputPlan.version(),
|
||||
inputPlan.constPool(),
|
||||
canonicalConstPool,
|
||||
layout.functions(),
|
||||
layout.code(),
|
||||
layout.debugInfo(),
|
||||
inputPlan.exports(),
|
||||
canonicalExports,
|
||||
ReadOnlyList.wrap(orderedSyscalls.values()));
|
||||
}
|
||||
|
||||
@ -125,6 +128,34 @@ public class BytecodeEmitter {
|
||||
return span;
|
||||
}
|
||||
|
||||
private ReadOnlyList<BytecodeModule.ConstantPoolEntry> dedupeConstPool(
|
||||
final ReadOnlyList<BytecodeModule.ConstantPoolEntry> input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return ReadOnlyList.empty();
|
||||
}
|
||||
final var ordered = new LinkedHashSet<BytecodeModule.ConstantPoolEntry>(input.size());
|
||||
for (final var constant : input) {
|
||||
if (constant != null) {
|
||||
ordered.add(constant);
|
||||
}
|
||||
}
|
||||
return ReadOnlyList.wrap(ordered.stream().toList());
|
||||
}
|
||||
|
||||
private ReadOnlyList<BytecodeModule.Export> dedupeExports(
|
||||
final ReadOnlyList<BytecodeModule.Export> input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return ReadOnlyList.empty();
|
||||
}
|
||||
final var ordered = new LinkedHashSet<BytecodeModule.Export>(input.size());
|
||||
for (final var export : input) {
|
||||
if (export != null) {
|
||||
ordered.add(export);
|
||||
}
|
||||
}
|
||||
return ReadOnlyList.wrap(ordered.stream().toList());
|
||||
}
|
||||
|
||||
private record SyscallIdentity(
|
||||
String module,
|
||||
String name,
|
||||
|
||||
@ -6,6 +6,7 @@ import p.studio.utilities.structures.ReadOnlyList;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -167,6 +168,60 @@ class BytecodeEmitterTest {
|
||||
assertEquals(2, readU32(module.code(), 8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emitMustDeduplicateConstPoolAndExportsByFirstOccurrence() {
|
||||
final var emitter = new BytecodeEmitter();
|
||||
final var module = emitter.emit(new BytecodeEmitter.EmissionPlan(
|
||||
0,
|
||||
ReadOnlyList.from(
|
||||
new BytecodeModule.Int32Constant(7),
|
||||
new BytecodeModule.Int32Constant(7),
|
||||
new BytecodeModule.StringConstant("abc"),
|
||||
new BytecodeModule.StringConstant("abc")),
|
||||
ReadOnlyList.from(
|
||||
new BytecodeModule.Export("main", 0),
|
||||
new BytecodeModule.Export("main", 0),
|
||||
new BytecodeModule.Export("aux", 1)),
|
||||
ReadOnlyList.from(
|
||||
new BytecodeEmitter.FunctionPlan(
|
||||
"main",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(BytecodeEmitter.Operation.ret())))));
|
||||
|
||||
assertEquals(2, module.constPool().size());
|
||||
assertEquals(2, module.exports().size());
|
||||
assertEquals("main", module.exports().get(0).symbol());
|
||||
assertEquals("aux", module.exports().get(1).symbol());
|
||||
}
|
||||
|
||||
@Test
|
||||
void emitMustRemainDeterministicAfterInterning() {
|
||||
final var emitter = new BytecodeEmitter();
|
||||
final var plan = new BytecodeEmitter.EmissionPlan(
|
||||
0,
|
||||
ReadOnlyList.from(
|
||||
new BytecodeModule.Int32Constant(1),
|
||||
new BytecodeModule.Int32Constant(1)),
|
||||
ReadOnlyList.from(
|
||||
new BytecodeModule.Export("main", 0),
|
||||
new BytecodeModule.Export("main", 0)),
|
||||
ReadOnlyList.from(
|
||||
new BytecodeEmitter.FunctionPlan(
|
||||
"main",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(BytecodeEmitter.Operation.halt()))));
|
||||
|
||||
final var first = emitter.emit(plan).serialize();
|
||||
final var second = emitter.emit(plan).serialize();
|
||||
assertArrayEquals(first, second);
|
||||
}
|
||||
|
||||
private static int readU16(final byte[] bytes, final int offset) {
|
||||
return ByteBuffer.wrap(bytes, offset, 2).order(ByteOrder.LITTLE_ENDIAN).getShort() & 0xFFFF;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user