Skip to content

Commit

Permalink
Simplify V3Interpreter with MemoryAccessor<T>
Browse files Browse the repository at this point in the history
  • Loading branch information
titzer committed Dec 16, 2024
1 parent 06f8611 commit f36a13b
Showing 1 changed file with 116 additions and 97 deletions.
213 changes: 116 additions & 97 deletions src/engine/v3/V3Interpreter.v3
Original file line number Diff line number Diff line change
Expand Up @@ -441,29 +441,29 @@ class V3Interpreter extends WasmStack {
Runtime.TABLE_SET(this, frame.func.instance, table_index);
}

I32_LOAD => doLoad(4, DataReaders.read_range_u32, pushu);
I64_LOAD => doLoad(8, DataReaders.read_range_u64, pushw);
F32_LOAD => doLoad(4, DataReaders.read_range_float, pushf);
F64_LOAD => doLoad(8, DataReaders.read_range_double, pushd);
I32_LOAD8_S => doLoad(1, DataReaders.read_range_u32_i8, pushu);
I32_LOAD8_U => doLoad(1, DataReaders.read_range_u32_u8, pushu);
I32_LOAD16_S => doLoad(2, DataReaders.read_range_u32_i16, pushu);
I32_LOAD16_U => doLoad(2, DataReaders.read_range_u32_u16, pushu);
I64_LOAD8_S => doLoad(1, DataReaders.read_range_u64_i8, pushw);
I64_LOAD8_U => doLoad(1, DataReaders.read_range_u64_u8, pushw);
I64_LOAD16_S => doLoad(2, DataReaders.read_range_u64_i16, pushw);
I64_LOAD16_U => doLoad(2, DataReaders.read_range_u64_u16, pushw);
I64_LOAD32_S => doLoad(4, DataReaders.read_range_u64_i32, pushw);
I64_LOAD32_U => doLoad(4, DataReaders.read_range_u64_u32, pushw);
I32_STORE => doStore(4, DataWriters.write_range_u32, popu());
I64_STORE => doStore(8, DataWriters.write_range_u64, popw());
F32_STORE => doStore(4, DataWriters.write_range_float, popf());
F64_STORE => doStore(8, DataWriters.write_range_double, popd());
I32_STORE8 => doStore(1, DataWriters.write_range_u8, popu8());
I32_STORE16 => doStore(2, DataWriters.write_range_u16, popu16());
I64_STORE8 => doStore(1, DataWriters.write_range_u8, popw8());
I64_STORE16 => doStore(2, DataWriters.write_range_u16, popw16());
I64_STORE32 => doStore(4, DataWriters.write_range_u32, popw32());
I32_LOAD => doLoad(ACCESS_U32, pushu);
I64_LOAD => doLoad(ACCESS_U64, pushw);
F32_LOAD => doLoad(ACCESS_F32, pushf);
F64_LOAD => doLoad(ACCESS_F64, pushd);
I32_LOAD8_S => doLoad(ACCESS_I8, pushi8);
I32_LOAD8_U => doLoad(ACCESS_U8, pushu8);
I32_LOAD16_S => doLoad(ACCESS_I16, pushi16);
I32_LOAD16_U => doLoad(ACCESS_U16, pushu16);
I64_LOAD8_S => doLoad(ACCESS_I8, pushl8);
I64_LOAD8_U => doLoad(ACCESS_U8, pushw8);
I64_LOAD16_S => doLoad(ACCESS_I16, pushl16);
I64_LOAD16_U => doLoad(ACCESS_U16, pushw16);
I64_LOAD32_S => doLoad(ACCESS_I32, pushl32);
I64_LOAD32_U => doLoad(ACCESS_U32, pushw32);
I32_STORE => doStore(ACCESS_U32, popu());
I64_STORE => doStore(ACCESS_U64, popw());
F32_STORE => doStore(ACCESS_F32, popf());
F64_STORE => doStore(ACCESS_F64, popd());
I32_STORE8 => doStore(ACCESS_U8, popu8());
I32_STORE16 => doStore(ACCESS_U16, popu16());
I64_STORE8 => doStore(ACCESS_U8, popw8());
I64_STORE16 => doStore(ACCESS_U16, popw16());
I64_STORE32 => doStore(ACCESS_U32, popw32());

// Atomic operations
MEMORY_ATOMIC_NOTIFY => {
Expand All @@ -483,29 +483,29 @@ class V3Interpreter extends WasmStack {
Runtime.ATOMIC_FENCE();
}

I32_ATOMIC_LOAD => doAtomicLoad(4, DataReaders.read_range_u32, pushu);
I64_ATOMIC_LOAD => doAtomicLoad(8, DataReaders.read_range_u64, pushw);
I32_ATOMIC_LOAD8_U => doAtomicLoad(1, DataReaders.read_range_u32_u8, pushu);
I32_ATOMIC_LOAD16_U => doAtomicLoad(2, DataReaders.read_range_u32_u16, pushu);
I64_ATOMIC_LOAD8_U => doAtomicLoad(1, DataReaders.read_range_u64_u8, pushw);
I64_ATOMIC_LOAD16_U => doAtomicLoad(2, DataReaders.read_range_u64_u16, pushw);
I64_ATOMIC_LOAD32_U => doAtomicLoad(4, DataReaders.read_range_u64_u32, pushw);
I32_ATOMIC_LOAD => doAtomicLoad(ACCESS_U32, pushu);
I64_ATOMIC_LOAD => doAtomicLoad(ACCESS_U64, pushw);
I32_ATOMIC_LOAD8_U => doAtomicLoad(ACCESS_U8, pushu8);
I32_ATOMIC_LOAD16_U => doAtomicLoad(ACCESS_U16, pushu16);
I64_ATOMIC_LOAD8_U => doAtomicLoad(ACCESS_U8, pushw8);
I64_ATOMIC_LOAD16_U => doAtomicLoad(ACCESS_U16, pushw16);
I64_ATOMIC_LOAD32_U => doAtomicLoad(ACCESS_U32, pushw32);

I32_ATOMIC_RMW_ADD => doAtomicRmw(4, DataReaders.read_range_u32, DataWriters.write_range_u32, popu(), V3Eval.I32_ADD, pushu);
I32_ATOMIC_RMW8_ADD_U => doAtomicRmw(1, DataReaders.read_range_u8, DataWriters.write_range_u8, popu8(), u8.+, pushu8);
I32_ATOMIC_RMW16_ADD_U => doAtomicRmw(2, DataReaders.read_range_u16, DataWriters.write_range_u16, popu16(), u16.+, pushu16);
I32_ATOMIC_RMW_SUB => doAtomicRmw(4, DataReaders.read_range_u32, DataWriters.write_range_u32, popu(), V3Eval.I32_SUB, pushu);
I32_ATOMIC_RMW8_SUB_U => doAtomicRmw(1, DataReaders.read_range_u8, DataWriters.write_range_u8, popu8(), u8.-, pushu8);
I32_ATOMIC_RMW16_SUB_U => doAtomicRmw(2, DataReaders.read_range_u16, DataWriters.write_range_u16, popu16(), u16.-, pushu16);
I32_ATOMIC_RMW_AND => doAtomicRmw(4, DataReaders.read_range_u32, DataWriters.write_range_u32, popu(), V3Eval.I32_AND, pushu);
I32_ATOMIC_RMW8_AND_U => doAtomicRmw(1, DataReaders.read_range_u8, DataWriters.write_range_u8, popu8(), u8.&, pushu8);
I32_ATOMIC_RMW16_AND_U => doAtomicRmw(2, DataReaders.read_range_u16, DataWriters.write_range_u16, popu16(), u16.&, pushu16);
I32_ATOMIC_RMW_OR => doAtomicRmw(4, DataReaders.read_range_u32, DataWriters.write_range_u32, popu(), V3Eval.I32_OR, pushu);
I32_ATOMIC_RMW8_OR_U => doAtomicRmw(1, DataReaders.read_range_u8, DataWriters.write_range_u8, popu8(), u8.|, pushu8);
I32_ATOMIC_RMW16_OR_U => doAtomicRmw(2, DataReaders.read_range_u16, DataWriters.write_range_u16, popu16(), u16.|, pushu16);
I32_ATOMIC_RMW_XOR => doAtomicRmw(4, DataReaders.read_range_u32, DataWriters.write_range_u32, popu(), V3Eval.I32_XOR, pushu);
I32_ATOMIC_RMW8_XOR_U => doAtomicRmw(1, DataReaders.read_range_u8, DataWriters.write_range_u8, popu8(), u8.^, pushu8);
I32_ATOMIC_RMW16_XOR_U => doAtomicRmw(2, DataReaders.read_range_u16, DataWriters.write_range_u16, popu16(), u16.^, pushu16);
I32_ATOMIC_RMW_ADD => doAtomicRmw(ACCESS_U32, popu(), V3Eval.I32_ADD, pushu);
I32_ATOMIC_RMW8_ADD_U => doAtomicRmw(ACCESS_U8, popu8(), u8.+, pushu8);
I32_ATOMIC_RMW16_ADD_U => doAtomicRmw(ACCESS_U16, popu16(), u16.+, pushu16);
I32_ATOMIC_RMW_SUB => doAtomicRmw(ACCESS_U32, popu(), V3Eval.I32_SUB, pushu);
I32_ATOMIC_RMW8_SUB_U => doAtomicRmw(ACCESS_U8, popu8(), u8.-, pushu8);
I32_ATOMIC_RMW16_SUB_U => doAtomicRmw(ACCESS_U16, popu16(), u16.-, pushu16);
I32_ATOMIC_RMW_AND => doAtomicRmw(ACCESS_U32, popu(), V3Eval.I32_AND, pushu);
I32_ATOMIC_RMW8_AND_U => doAtomicRmw(ACCESS_U8, popu8(), u8.&, pushu8);
I32_ATOMIC_RMW16_AND_U => doAtomicRmw(ACCESS_U16, popu16(), u16.&, pushu16);
I32_ATOMIC_RMW_OR => doAtomicRmw(ACCESS_U32, popu(), V3Eval.I32_OR, pushu);
I32_ATOMIC_RMW8_OR_U => doAtomicRmw(ACCESS_U8, popu8(), u8.|, pushu8);
I32_ATOMIC_RMW16_OR_U => doAtomicRmw(ACCESS_U16, popu16(), u16.|, pushu16);
I32_ATOMIC_RMW_XOR => doAtomicRmw(ACCESS_U32, popu(), V3Eval.I32_XOR, pushu);
I32_ATOMIC_RMW8_XOR_U => doAtomicRmw(ACCESS_U8, popu8(), u8.^, pushu8);
I32_ATOMIC_RMW16_XOR_U => doAtomicRmw(ACCESS_U16, popu16(), u16.^, pushu16);

MEMORY_SIZE => {
var index = codeptr.read_uleb32();
Expand Down Expand Up @@ -899,28 +899,28 @@ class V3Interpreter extends WasmStack {
var table_index = codeptr.read_uleb31();
Runtime.TABLE_FILL(this, frame.func.instance, table_index);
}
V128_LOAD => doLoad(16, DataReaders.read_range_u128, pushs);
V128_LOAD_64_LANE => doLoadLane(8, 3, DataReaders.read_range_u64);
V128_LOAD_32_LANE => doLoadLane(4, 2, DataReaders.read_range_u32);
V128_LOAD_16_LANE => doLoadLane(2, 1, DataReaders.read_range_u32_u16);
V128_LOAD_8_LANE => doLoadLane(1, 0, DataReaders.read_range_u32_u8);
V128_LOAD_64_SPLAT => doLoadSplat(8, 3, DataReaders.read_range_u64);
V128_LOAD_32_SPLAT => doLoadSplat(4, 2, DataReaders.read_range_u32);
V128_LOAD_16_SPLAT => doLoadSplat(2, 1, DataReaders.read_range_u32_u16);
V128_LOAD_8_SPLAT => doLoadSplat(1, 0, DataReaders.read_range_u32_u8);
V128_LOAD_32_ZERO => doLoadZero(4, DataReaders.read_range_u32);
V128_LOAD_64_ZERO => doLoadZero(8, DataReaders.read_range_u64);
V128_LOAD => doLoad(ACCESS_V128, pushs);
V128_LOAD_64_LANE => doLoadLane(ACCESS_U64);
V128_LOAD_32_LANE => doLoadLane(ACCESS_U32);
V128_LOAD_16_LANE => doLoadLane(ACCESS_U16);
V128_LOAD_8_LANE => doLoadLane(ACCESS_U8);
V128_LOAD_64_SPLAT => doLoadSplat(ACCESS_U64);
V128_LOAD_32_SPLAT => doLoadSplat(ACCESS_U32);
V128_LOAD_16_SPLAT => doLoadSplat(ACCESS_U16);
V128_LOAD_8_SPLAT => doLoadSplat(ACCESS_U8);
V128_LOAD_32_ZERO => doLoadZero(ACCESS_U32);
V128_LOAD_64_ZERO => doLoadZero(ACCESS_U64);
V128_LOAD_8X8_S => doLoadExtend(1, i8.view<u64>, i16.view<i8>, u16.view<i16>);
V128_LOAD_8X8_U => doLoadExtend(1, u8.view<u64>, u16.view<u8>, u16.view<u16>);
V128_LOAD_16X4_S => doLoadExtend(2, i16.view<u64>, i32.view<i16>, u32.view<i32>);
V128_LOAD_16X4_U => doLoadExtend(2, u16.view<u64>, u32.view<u16>, u32.view<u32>);
V128_LOAD_32X2_S => doLoadExtend(4, i32.view<u64>, i64.view<i32>, u64.view<i64>);
V128_LOAD_32X2_U => doLoadExtend(4, u32.view<u64>, u64.view<u32>, u64.view<u64>);
V128_STORE => doStore(16, DataWriters.write_range_u128, pops());
V128_STORE_8_LANE => doStoreLane(1, 0, u8.view<u64>, DataWriters.write_range_u8);
V128_STORE_16_LANE => doStoreLane(2, 1, u16.view<u64>, DataWriters.write_range_u16);
V128_STORE_32_LANE => doStoreLane(4, 2, u32.view<u64>, DataWriters.write_range_u32);
V128_STORE_64_LANE => doStoreLane(8, 3, u64.view<u64>, DataWriters.write_range_u64);
V128_STORE => doStore(ACCESS_V128, pops());
V128_STORE_8_LANE => doStoreLane(ACCESS_U8, u8.view<u64>);
V128_STORE_16_LANE => doStoreLane(ACCESS_U16, u16.view<u64>);
V128_STORE_32_LANE => doStoreLane(ACCESS_U32, u32.view<u64>);
V128_STORE_64_LANE => doStoreLane(ACCESS_U64, u64.view<u64>);
V128_CONST => {
var low = codeptr.read_u64();
var high = codeptr.read_u64();
Expand Down Expand Up @@ -1215,49 +1215,49 @@ class V3Interpreter extends WasmStack {
var index = popa(memory.decl.size);
return memory.range_oil_64(memarg.offset, index, size);
}
def doLoad<T>(size: byte, read: Range<byte> -> T, push: T -> void) {
var t = decodeMemArgAndGetMemoryRange(size);
def doLoad<T>(accessor: MemoryAccessor<T>, push: T -> void) {
var t = decodeMemArgAndGetMemoryRange(accessor.size);
if (t.reason != TrapReason.NONE) return void(trap(t.reason));
push(read(t.result));
push(accessor.read(t.result));
}
def doAtomicLoad<T>(size: byte, read: Range<byte> -> T, push: T -> void) {
var t = decodeMemArgAndGetMemoryRange(size);
def doAtomicLoad<T>(accessor: MemoryAccessor<T>, push: T -> void) {
var t = decodeMemArgAndGetMemoryRange(accessor.size);
if (t.reason != TrapReason.NONE) return void(trap(t.reason));
push(read(t.result)); // XXX: assumes "read" function is atomic
push(accessor.read(t.result)); // XXX: assumes "read" function is atomic
}
def doAtomicRmw<T>(size: byte, read: Range<byte> -> T, write: (Range<byte>, T) -> void, val: T, op: (T, T) -> T, push: T -> void) {
var t = decodeMemArgAndGetMemoryRange(size);
def doAtomicRmw<T>(accessor: MemoryAccessor<T>, val: T, op: (T, T) -> T, push: T -> void) {
var t = decodeMemArgAndGetMemoryRange(accessor.size);
if (t.reason != TrapReason.NONE) return void(trap(t.reason));
var prev = read(t.result); // XXX: assumes no real threads
var prev = accessor.read(t.result); // XXX: assumes no real threads
var updated = op(prev, val);
write(t.result, updated);
accessor.write(t.result, updated);
push(prev);
}
def doLoadLane<T>(size: byte, log2_size: u3, read: Range<byte> -> T) {
def doLoadLane<T>(accessor: MemoryAccessor<T>) {
var v = pops();
var t = decodeMemArgAndGetMemoryRange(size);
var t = decodeMemArgAndGetMemoryRange(accessor.size);
var idx = codeptr.read1();
if (t.reason != TrapReason.NONE) return void(trap(t.reason));
var val = u64.!(read(t.result));
var val = u64.!(accessor.read(t.result));
var low = v.0, high = v.1;
def half_lanes = 8 >> log2_size;
def half_lanes = 8 >> accessor.log2_size;
if (idx < half_lanes) { // Update a lane in low
var shift = u6.view(idx << u3.+(log2_size, 3));
var shift = u6.view(idx << u3.+(accessor.log2_size, 3));
val <<= shift;
low |= val;
} else { // Update a lane in high
var shift = u6.view((idx - half_lanes) << u3.+(log2_size, 3));
var shift = u6.view((idx - half_lanes) << u3.+(accessor.log2_size, 3));
val <<= shift;
high |= val;
}
push(Value.V128(low, high));
pushs(low, high);
}
def doLoadZero<T>(size: byte, read: Range<byte> -> T) {
var t = decodeMemArgAndGetMemoryRange(size);
def doLoadZero<T>(accessor: MemoryAccessor<T>) {
var t = decodeMemArgAndGetMemoryRange(accessor.size);
if (t.reason != TrapReason.NONE) return void(trap(t.reason));
var val = u64.!(read(t.result));
var val = u64.!(accessor.read(t.result));
var high = 0ul;
push(Value.V128(val, high));
pushs(val, high);
}
// Tn: narrow source type, Sw: (signed) wide type, Uw: corresponding unsigned wide type
def doLoadExtend<Tn, Sw, Uw>(size: byte, view: u64 -> Tn, extend: Tn -> Sw, convert: Sw -> Uw) {
Expand Down Expand Up @@ -1358,17 +1358,17 @@ class V3Interpreter extends WasmStack {
}
return res;
}
def doLoadSplat<T>(size: byte, log2_size: byte, read: Range<byte> -> T) {
var t = decodeMemArgAndGetMemoryRange(size);
def doLoadSplat<T>(accessor: MemoryAccessor<T>) {
var t = decodeMemArgAndGetMemoryRange(accessor.size);
if (t.reason != TrapReason.NONE) return void(trap(t.reason));
var val = u64.!(read(t.result));
doSplatV(size, log2_size, val);
var val = u64.!(accessor.read(t.result));
doSplatV(accessor.size, accessor.log2_size, val);
}
def doSplat<T, S>(size: byte, log2_size: byte, view: S -> T, stack_pop: void -> S) {
def doSplat<T, S>(size: byte, log2_size: u5, view: S -> T, stack_pop: void -> S) {
var val = u64.!(view((stack_pop())));
doSplatV(size, log2_size, val);
}
def doSplatV(size: byte, log2_size: byte, val: u64) {
def doSplatV(size: byte, log2_size: u5, val: u64) {
var low = 0ul, high = 0ul;
def half_lanes = 8 >> log2_size;
var shift = u6.view(size << 3);
Expand All @@ -1380,29 +1380,29 @@ class V3Interpreter extends WasmStack {
}
push(Value.V128(low, high));
}
def doStore<T>(size: byte, write: (Range<byte>, T) -> void, val: T) {
var t = decodeMemArgAndGetMemoryRange(size);
def doStore<T>(accessor: MemoryAccessor<T>, val: T) {
var t = decodeMemArgAndGetMemoryRange(accessor.size);
if (t.reason != TrapReason.NONE) trap(t.reason);
else write(t.result, val);
else accessor.write(t.result, val);
}
def doStoreLane<T>(size: byte, log2_size: u3, view: u64 -> T, write: (Range<byte>, T) -> void) {
def doStoreLane<T>(accessor: MemoryAccessor<T>, view: u64 -> T) {
var v = pops();
var t = decodeMemArgAndGetMemoryRange(size);
var t = decodeMemArgAndGetMemoryRange(accessor.size);
var idx = codeptr.read1(); // get lane immediate
if (t.reason != TrapReason.NONE) trap(t.reason);
// Extract lane
var low = v.0, high = v.1;
var val: T;
def half_lanes = 8 >> log2_size;
def half_lanes = 8 >> accessor.log2_size;
if (idx < half_lanes) { // Extract a lane from low
var shift = u6.view(idx << u3.+(log2_size, 3));
var shift = u6.view(idx << u3.+(accessor.log2_size, 3));
val = view(low >> shift);
} else { // Extract a lane from high
var shift = u6.view((idx - half_lanes) << u3.+(log2_size, 3));
var shift = u6.view((idx - half_lanes) << u3.+(accessor.log2_size, 3));
val = view(high >> shift);
}
// Write to memory
write(t.result, val);
accessor.write(t.result, val);
}
def doFallthru() {
frame.stp += 4;
Expand Down Expand Up @@ -1576,10 +1576,15 @@ class V3Interpreter extends WasmStack {
def popObject() -> Object { return Value.Ref.!(values.pop()).val; }
def push(val: Value) { values.push(val); }
def pushi(val: i32) { values.push(Value.I32(u32.view(val))); }
def pushi8(val: i8) { values.push(Value.I32(u32.view(val))); }
def pushi16(val: i16) { values.push(Value.I32(u32.view(val))); }
def pushu(val: u32) { values.push(Value.I32(val)); }
def pushu8(val: u8) { values.push(Value.I32(val)); }
def pushu16(val: u16) { values.push(Value.I32(val)); }
def pushl(val: i64) { values.push(Value.I64(u64.view(val))); }
def pushl8(val: i8) { values.push(Value.I64(u64.view(val))); }
def pushl16(val: i16) { values.push(Value.I64(u64.view(val))); }
def pushl32(val: i32) { values.push(Value.I64(u64.view(val))); }
def pushw(val: u64) { values.push(Value.I64(val)); }
def pushw8(val: u8) { values.push(Value.I64(val)); }
def pushw16(val: u16) { values.push(Value.I64(val)); }
Expand Down Expand Up @@ -1928,3 +1933,17 @@ private class V3FrameWriter extends FrameWriter {
}
}
def fatal = System.error("V3InterpreterError", _);

// Connect a Virgil type <T> to accessing memory (read, write, and push/pop from value stack).
private class MemoryAccessor<T>(size: byte, log2_size: u3, read: Range<byte> -> T, write: (Range<byte>, T) -> void) { }

def ACCESS_I8 = MemoryAccessor.new(1, 0, DataReaders.read_range_i8, DataWriters.write_range_i8);
def ACCESS_U8 = MemoryAccessor.new(1, 0, DataReaders.read_range_u8, DataWriters.write_range_u8);
def ACCESS_I16 = MemoryAccessor.new(2, 1, DataReaders.read_range_i16, DataWriters.write_range_i16);
def ACCESS_U16 = MemoryAccessor.new(2, 1, DataReaders.read_range_u16, DataWriters.write_range_u16);
def ACCESS_I32 = MemoryAccessor.new(4, 2, DataReaders.read_range_i32, DataWriters.write_range_i32);
def ACCESS_U32 = MemoryAccessor.new(4, 2, DataReaders.read_range_u32, DataWriters.write_range_u32);
def ACCESS_U64 = MemoryAccessor.new(8, 3, DataReaders.read_range_u64, DataWriters.write_range_u64);
def ACCESS_F32 = MemoryAccessor.new(4, 2, DataReaders.read_range_float, DataWriters.write_range_float);
def ACCESS_F64 = MemoryAccessor.new(8, 3, DataReaders.read_range_double, DataWriters.write_range_double);
def ACCESS_V128 = MemoryAccessor.new(16, 4, DataReaders.read_range_u128, DataWriters.write_range_u128);

0 comments on commit f36a13b

Please sign in to comment.