From f36a13b534a08572845aa0ca1cff798cd2d46b96 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Sun, 15 Dec 2024 23:00:42 -0500 Subject: [PATCH] Simplify V3Interpreter with MemoryAccessor --- src/engine/v3/V3Interpreter.v3 | 213 ++++++++++++++++++--------------- 1 file changed, 116 insertions(+), 97 deletions(-) diff --git a/src/engine/v3/V3Interpreter.v3 b/src/engine/v3/V3Interpreter.v3 index 953ff650..cb7372ef 100644 --- a/src/engine/v3/V3Interpreter.v3 +++ b/src/engine/v3/V3Interpreter.v3 @@ -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 => { @@ -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(); @@ -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, i16.view, u16.view); V128_LOAD_8X8_U => doLoadExtend(1, u8.view, u16.view, u16.view); V128_LOAD_16X4_S => doLoadExtend(2, i16.view, i32.view, u32.view); V128_LOAD_16X4_U => doLoadExtend(2, u16.view, u32.view, u32.view); V128_LOAD_32X2_S => doLoadExtend(4, i32.view, i64.view, u64.view); V128_LOAD_32X2_U => doLoadExtend(4, u32.view, u64.view, u64.view); - V128_STORE => doStore(16, DataWriters.write_range_u128, pops()); - V128_STORE_8_LANE => doStoreLane(1, 0, u8.view, DataWriters.write_range_u8); - V128_STORE_16_LANE => doStoreLane(2, 1, u16.view, DataWriters.write_range_u16); - V128_STORE_32_LANE => doStoreLane(4, 2, u32.view, DataWriters.write_range_u32); - V128_STORE_64_LANE => doStoreLane(8, 3, u64.view, DataWriters.write_range_u64); + V128_STORE => doStore(ACCESS_V128, pops()); + V128_STORE_8_LANE => doStoreLane(ACCESS_U8, u8.view); + V128_STORE_16_LANE => doStoreLane(ACCESS_U16, u16.view); + V128_STORE_32_LANE => doStoreLane(ACCESS_U32, u32.view); + V128_STORE_64_LANE => doStoreLane(ACCESS_U64, u64.view); V128_CONST => { var low = codeptr.read_u64(); var high = codeptr.read_u64(); @@ -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(size: byte, read: Range -> T, push: T -> void) { - var t = decodeMemArgAndGetMemoryRange(size); + def doLoad(accessor: MemoryAccessor, 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(size: byte, read: Range -> T, push: T -> void) { - var t = decodeMemArgAndGetMemoryRange(size); + def doAtomicLoad(accessor: MemoryAccessor, 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(size: byte, read: Range -> T, write: (Range, T) -> void, val: T, op: (T, T) -> T, push: T -> void) { - var t = decodeMemArgAndGetMemoryRange(size); + def doAtomicRmw(accessor: MemoryAccessor, 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(size: byte, log2_size: u3, read: Range -> T) { + def doLoadLane(accessor: MemoryAccessor) { 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(size: byte, read: Range -> T) { - var t = decodeMemArgAndGetMemoryRange(size); + def doLoadZero(accessor: MemoryAccessor) { + 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(size: byte, view: u64 -> Tn, extend: Tn -> Sw, convert: Sw -> Uw) { @@ -1358,17 +1358,17 @@ class V3Interpreter extends WasmStack { } return res; } - def doLoadSplat(size: byte, log2_size: byte, read: Range -> T) { - var t = decodeMemArgAndGetMemoryRange(size); + def doLoadSplat(accessor: MemoryAccessor) { + 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(size: byte, log2_size: byte, view: S -> T, stack_pop: void -> S) { + def doSplat(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); @@ -1380,29 +1380,29 @@ class V3Interpreter extends WasmStack { } push(Value.V128(low, high)); } - def doStore(size: byte, write: (Range, T) -> void, val: T) { - var t = decodeMemArgAndGetMemoryRange(size); + def doStore(accessor: MemoryAccessor, 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(size: byte, log2_size: u3, view: u64 -> T, write: (Range, T) -> void) { + def doStoreLane(accessor: MemoryAccessor, 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; @@ -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)); } @@ -1928,3 +1933,17 @@ private class V3FrameWriter extends FrameWriter { } } def fatal = System.error("V3InterpreterError", _); + +// Connect a Virgil type to accessing memory (read, write, and push/pop from value stack). +private class MemoryAccessor(size: byte, log2_size: u3, read: Range -> T, write: (Range, 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); \ No newline at end of file