From 6b7a042390b387eab666c6158c4d2f873cac5782 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Fri, 2 Jan 2026 10:19:18 -0500 Subject: [PATCH 01/11] Adds paired array and typed array representations for arrays and paired array for structs --- src/engine/Instance.v3 | 424 +++- src/engine/Runtime.v3 | 1752 ++++++++++++++++- src/engine/Tuning.v3 | 31 +- src/engine/Type.v3 | 57 +- src/engine/Value.v3 | 893 ++++++++- src/engine/WasmStack.v3 | 10 +- src/engine/compiler/MacroAssembler.v3 | 31 +- src/engine/compiler/SinglePassCompiler.v3 | 292 ++- src/engine/v3/V3Interpreter.v3 | 34 +- src/engine/x86-64/V3Offsets.v3 | 10 +- src/engine/x86-64/X86_64Interpreter.v3 | 19 +- src/engine/x86-64/X86_64MacroAssembler.v3 | 37 +- src/engine/x86-64/X86_64SinglePassCompiler.v3 | 112 ++ src/engine/x86-64/X86_64Stack.v3 | 8 +- src/modules/wizeng/WizengModule.v3 | 7 +- src/util/ArrayUtil.v3 | 74 +- test/unittest/ExeTest.v3 | 198 +- test/unittest/ExeTester.v3 | 20 +- test/unittest/WasmStackTest.v3 | 4 +- test/wasm-spec/SpecTestParser.v3 | 4 +- 20 files changed, 3841 insertions(+), 176 deletions(-) diff --git a/src/engine/Instance.v3 b/src/engine/Instance.v3 index 0595f541a..953b102c1 100644 --- a/src/engine/Instance.v3 +++ b/src/engine/Instance.v3 @@ -105,18 +105,430 @@ class Instance(module: Module, imports: Array) { } Array(t, len, elem) => { var vlen = Values.unbox_i(evalInitExpr(len)); - var vvals = Array.new(vlen); // TODO: check for OOM + var decl = t.array; + var st = decl.elem_types[0]; var velem = evalInitExpr(elem); - for (i < vvals.length) vvals[i] = velem; - return Value.Ref(HeapArray.new(t.array, vvals)); + + match (ObjTuning.arrayMode) { + Typed => { + match (st.pack) { + UNPACKED => { + match (st.valtype) { + I32 => { + var ielem = Value.I32.!(velem).val; + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = ielem; + return Value.Ref(HeapArrayI32.new(t.array, vvals)); + } + I64 => { + var ielem = Value.I64.!(velem).val; + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = ielem; + return Value.Ref(HeapArrayI64.new(t.array, vvals)); + } + F32 => { + var felem = Value.F32.!(velem).bits; + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = felem; + return Value.Ref(HeapArrayF32.new(t.array, vvals)); + } + F64 => { + var felem = Value.F64.!(velem).bits; + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = felem; + return Value.Ref(HeapArrayF64.new(t.array, vvals)); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var oelem = Value.I31.!(velem).val; + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = oelem; + return Value.Ref(HeapArrayI31.new(t.array, vvals)); + } else { + var oelem = Value.Ref.!(velem).val; + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = oelem; + return Value.Ref(HeapArrayRef.new(t.array, vvals)); + } + } + V128 => { + var v128 = Value.V128.!(velem); + var vvals = Array.new(vlen << 1); + var low = v128.low; + var high = v128.high; + for (i = 0; i < vvals.length; i += 2) { + vvals[i] = low; + vvals[i+1] = high; + } + return Value.Ref(HeapArrayV128.new(t.array, vvals)); + } + _ => { + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = velem; + return Value.Ref(HeapArrayValue.new(t.array, vvals)); + } + } + } + PACKED_I8 => { + var ielem = u8.view(Value.I32.!(velem).val); + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = ielem; + return Value.Ref(HeapArrayI8.new(t.array, vvals)); + } + PACKED_I16 => { + var ielem = u16.view(Value.I32.!(velem).val); + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = ielem; + return Value.Ref(HeapArrayI16.new(t.array, vvals)); + } + } + } + Pair => { + match (st.pack) { + UNPACKED => { + match (st.valtype) { + I32 => { + var bytes = Array.new(vlen << 2); + var ielem = Value.I32.!(velem).val; + var offset = 0; + for (i < vlen) { + Ref.at(bytes, offset).val = ielem; + offset += 4; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); + } + I64 => { + var bytes = Array.new(vlen << 3); + var ielem = Value.I64.!(velem).val; + var offset = 0; + for (i < vlen) { + Ref.at(bytes, offset).val = ielem; + offset += 8; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); + } + F32 => { + var bytes = Array.new(vlen << 2); + var felem = Value.F32.!(velem).bits; + var offset = 0; + for (i < vlen) { + Ref.at(bytes, offset).val = felem; + offset += 4; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); + } + F64 => { + var bytes = Array.new(vlen << 3); + var felem = Value.F64.!(velem).bits; + var offset = 0; + for (i < vlen) { + Ref.at(bytes, offset).val = felem; + offset += 8; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var bytes = Array.new(vlen << 2); + var oelem = Value.I31.!(velem).val; + var offset = 0; + for (i < vlen) { + Ref.at(bytes, offset).val = oelem; + offset += 4; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); + } else { + var vals = Array.new(vlen); + var oelem = Value.Ref.!(velem).val; + for (i < vlen) vals[i] = oelem; + return Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT)); + } + } + V128 => { + var bytes = Array.new(vlen << 4); + var v128 = Value.V128.!(velem); + var low = v128.low; + var high = v128.high; + var offset = 0; + for (i < vlen) { + var r = Ref.at(bytes, offset); + r.lo_val = low; + r.hi_val = high; + offset += 16; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 4)); + } + _ => { + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = velem; + return Value.Ref(HeapArrayValue.new(t.array, vvals)); + } + } + } + PACKED_I8 => { + var bytes = Array.new(vlen); + var ielem = u8.view(Value.I32.!(velem).val); + for (i < vlen) { + bytes[i] = u8.view(ielem); + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 0)); + } + PACKED_I16 => { + var bytes = Array.new(vlen << 1); + var ielem = u16.view(Value.I32.!(velem).val); + var offset = 0; + for (i < vlen) { + Ref.at(bytes, offset).val = u16.view(ielem); + offset += 2; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 1)); + } + } + } + _ => { + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = velem; + return Value.Ref(HeapArrayValue.new(t.array, vvals)); + } + } } FixedArray(t, vals) => { - var vvals = Arrays.map(vals, evalInitExpr); - return Value.Ref(HeapArray.new(t.array, vvals)); + var decl = t.array; + var st = decl.elem_types[0]; + match (ObjTuning.arrayMode) { + Typed => { + match (st.pack) { + UNPACKED => { + match (st.valtype) { + I32 => { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = Value.I32.!(velem).val; + } + return Value.Ref(HeapArrayI32.new(t.array, vvals)); + } + I64 => { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = Value.I64.!(velem).val; + } + return Value.Ref(HeapArrayI64.new(t.array, vvals)); + } + F32 => { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = Value.F32.!(velem).bits; + } + return Value.Ref(HeapArrayF32.new(t.array, vvals)); + } + F64 => { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = Value.F64.!(velem).bits; + } + return Value.Ref(HeapArrayF64.new(t.array, vvals)); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = Value.I31.!(velem).val; + } + return Value.Ref(HeapArrayI31.new(t.array, vvals)); + } else { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = Value.Ref.!(velem).val; + } + return Value.Ref(HeapArrayRef.new(t.array, vvals)); + } + } + V128 => { + var vlen = vals.length; + var vvals = Array.new(vlen << 1); + for (i = 0; i < vlen; i += 2) { + var velem = evalInitExpr(vals[i]); + var v128 = Value.V128.!(velem); + vvals[i] = v128.low; + vvals[i+1] = v128.high; + } + return Value.Ref(HeapArrayV128.new(t.array, vvals)); + } + _ => { + var vvals = Arrays.map(vals, evalInitExpr); + return Value.Ref(HeapArrayValue.new(t.array, vvals)); + } + } + } + PACKED_I8 => { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = u8.view(Value.I32.!(velem).val); + } + return Value.Ref(HeapArrayI8.new(t.array, vvals)); + } + PACKED_I16 => { + var vlen = vals.length; + var vvals = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + vvals[i] = u16.view(Value.I32.!(velem).val); + } + return Value.Ref(HeapArrayI16.new(t.array, vvals)); + } + } + } + Pair => { + var decl = t.array; + var st = decl.elem_types[0]; + match (st.pack) { + UNPACKED => { + match (st.valtype) { + I32 => { + var vlen = vals.length; + var bytes = Array.new(vlen << 2); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + Ref.at(bytes, offset).val = Value.I32.!(velem).val; + offset += 4; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); + } + I64 => { + var vlen = vals.length; + var bytes = Array.new(vlen << 3); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + Ref.at(bytes, offset).val = Value.I64.!(velem).val; + offset += 8; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); + } + F32 => { + var vlen = vals.length; + var bytes = Array.new(vlen << 2); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + Ref.at(bytes, offset).val = Value.F32.!(velem).bits; + offset += 4; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); + } + F64 => { + var vlen = vals.length; + var bytes = Array.new(vlen << 3); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + Ref.at(bytes, offset).val = Value.F64.!(velem).bits; + offset += 8; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vlen = vals.length; + var bytes = Array.new(vlen << 2); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + Ref.at(bytes, offset).val = Value.I31.!(velem).val; + offset += 4; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); + } else { + var vlen = vals.length; + var objs = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + objs[i] = Value.Ref.!(velem).val; + } + return Value.Ref(HeapArrayPair.new(decl, objs, null, Values.OBJS_SHIFT)); + } + } + V128 => { + var vlen = vals.length; + var bytes = Array.new(vlen << 4); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + var r = Ref.at(bytes, offset); + var pair = Value.V128.!(velem); + r.lo_val = pair.low; + r.hi_val = pair.high; + offset += 16; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 4)); + } + _ => { + var vvals = Arrays.map(vals, evalInitExpr); + return Value.Ref(HeapArrayValue.new(t.array, vvals)); + } + } + } + PACKED_I8 => { + var vlen = vals.length; + var bytes = Array.new(vlen); + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + bytes[i] = u8.view(Value.I32.!(velem).val); + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 0)); + } + PACKED_I16 => { + var vlen = vals.length; + var bytes = Array.new(vlen << 1); + var offset = 0; + for (i < vlen) { + var velem = evalInitExpr(vals[i]); + Ref.at(bytes, offset).val = u16.view(Value.I32.!(velem).val); + offset += 2; + } + return Value.Ref(HeapArrayPair.new(decl, null, bytes, 1)); + } + } + } + _ => { + var vvals = Arrays.map(vals, evalInitExpr); + return Value.Ref(HeapArrayValue.new(t.array, vvals)); + } + } } Struct(t, vals) => { var vvals = Arrays.map(vals, evalInitExpr); - return Value.Ref(HeapStruct.new(t.sdecl, vvals)); + match (ObjTuning.structMode) { + Original => { + return Value.Ref(HeapStruct.new(t.sdecl, vvals)); + } + Pair => { + var decl = t.sdecl; + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var obj = HeapStructPair.new(decl, objs, bytes); + + for (i < decl.field_types.length) { + obj.setFieldValue(u31.view(i), vvals[i]); + } + return Value.Ref(obj); + } + } } ArrayNewData(t, data_index, offset, len) => { var voffset = evalInitExpr(offset); diff --git a/src/engine/Runtime.v3 b/src/engine/Runtime.v3 index 92163aeae..559ff6e5e 100644 --- a/src/engine/Runtime.v3 +++ b/src/engine/Runtime.v3 @@ -37,38 +37,156 @@ component Runtime { // --- GC operations ----------------------------------------------------------------------------------------- def STRUCT_NEW(stack: ExecStack, instance: Instance, struct_index: u31) { var decl = StructDecl.!(instance.heaptypes[struct_index]); - var fields = Array.new(decl.field_types.length); - for (i = fields.length - 1; i >= 0; i--) { - fields[i] = stack.popV(decl.field_types[i].valtype); + var obj: HeapStructGeneric; + match (ObjTuning.structMode) { + Pair => { + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + obj = HeapStructPair.new(decl, objs, bytes); + + for (i = decl.field_types.length - 1; i >= 0; i--) { + var index = u31.!(i); + match (decl.field_shifts[i]) { + 0 => { + var val = stack.popi(); + obj.setFieldI8(index, i8.view(val)); + } + 1 => { + var val = stack.popi(); + obj.setFieldI16(index, i16.view(val)); + } + 2 => { + if (decl.field_types[i].valtype == ValueType.I32) { + var val = stack.popi(); + obj.setFieldI32(index, val); + } else { + var val = stack.popf(); + obj.setFieldF32(index, val); + } + } + 3 => { + if (decl.field_types[i].valtype == ValueType.I64) { + var val = stack.popl(); + obj.setFieldI64(index, val); + } else { + var val = stack.popd(); + obj.setFieldF64(index, val); + } + } + 4 => { + var pair = stack.pops(); + obj.setFieldV128(index, pair.0, pair.1); + } + -1 => { + var o = stack.popObject(); + obj.setFieldRef(index, o); + } + } + } + } + _ => { + var fields = Array.new(decl.field_types.length); + for (i = fields.length - 1; i >= 0; i--) { + fields[i] = stack.popV(decl.field_types[i].valtype); + } + obj = HeapStruct.new(decl, fields); + } } - stack.push(Value.Ref(HeapStruct.new(decl, fields))); + stack.push(Value.Ref(obj)); } def STRUCT_NEW_DEFAULT(stack: ExecStack, instance: Instance, struct_index: u31) { var decl = StructDecl.!(instance.heaptypes[struct_index]); - var fields = Array.new(decl.field_types.length); - for (i < fields.length) { - fields[i] = Values.default(decl.field_types[i].valtype); + var obj: HeapStructGeneric; + match (ObjTuning.structMode) { + Pair => { + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + obj = HeapStructPair.new(decl, objs, bytes); + + for (i < decl.field_types.length) { + var index = u31.view(i); + match (decl.field_shifts[i]) { + 0 => { + obj.setFieldI8(index, 0); + } + 1 => { + obj.setFieldI16(index, 0); + } + 2 => { + if (decl.field_types[i].valtype == ValueType.I32) { + obj.setFieldI32(index, 0); + } else { + obj.setFieldF32(index, 0.0f); + } + } + 3 => { + if (decl.field_types[i].valtype == ValueType.I64) { + obj.setFieldI64(index, 0); + } else { + obj.setFieldF64(index, 0.0d); + } + } + 4 => { + obj.setFieldV128(index, 0, 0); + } + -1 => { + obj.setFieldRef(index, null); + } + } + } + } + _ => { + var fields = Array.new(decl.field_types.length); + for (i < fields.length) { + fields[i] = Values.default(decl.field_types[i].valtype); + } + obj = HeapStruct.new(decl, fields); + } } - stack.push(Value.Ref(HeapStruct.new(decl, fields))); + stack.push(Value.Ref(obj)); } def STRUCT_GET(stack: ExecStack, instance: Instance, struct_index: u31, field_index: u31) -> Throwable { var obj = stack.popStruct(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - stack.push(obj.vals[field_index]); + stack.push(obj.getFieldValue(field_index)); return null; } def STRUCT_GET_S(stack: ExecStack, instance: Instance, struct_index: u31, field_index: u31) -> Throwable { var obj = stack.popStruct(); var decl = StructDecl.!(instance.heaptypes[struct_index]); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - stack.push(V3Eval.signExtend(decl.field_types[field_index], obj.vals[field_index])); // XXX: pushi + var val: Value; + match (decl.field_types[field_index].pack) { + PACKED_I8 => { + val = obj.getFieldSignExtend8Value(field_index); + } + PACKED_I16 => { + val = obj.getFieldSignExtend16Value(field_index); + } + UNPACKED => { + return stack.trap(TrapReason.ERROR); + } + } + stack.push(val); // XXX: pushi return null; } def STRUCT_GET_U(stack: ExecStack, instance: Instance, struct_index: u31, field_index: u31) -> Throwable { var obj = stack.popStruct(); var decl = StructDecl.!(instance.heaptypes[struct_index]); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - stack.push(V3Eval.zeroExtend(decl.field_types[field_index], obj.vals[field_index])); // XXX: pushi + var val: Value; + match (decl.field_types[field_index].pack) { + PACKED_I8 => { + val = obj.getFieldZeroExtend8Value(field_index); + } + PACKED_I16 => { + val = obj.getFieldZeroExtend16Value(field_index); + } + UNPACKED => { + return stack.trap(TrapReason.ERROR); + } + } + stack.push(val); // XXX: pushi return null; } def STRUCT_SET(stack: ExecStack, instance: Instance, struct_index: u31, field_index: u31) -> Throwable { @@ -76,36 +194,501 @@ component Runtime { var val = stack.popV(decl.field_types[field_index].valtype); var obj = stack.popStruct(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - obj.vals[field_index] = val; + obj.setFieldValue(field_index, val); return null; } def ARRAY_NEW(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { var decl = ArrayDecl.!(instance.heaptypes[array_index]); var length = stack.popu(); - var elem = stack.popV(decl.elem_types[0].valtype); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - var vals = Array.new(u31.!(length)); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArray.new(decl, vals))); + match (ObjTuning.arrayMode) { + Typed => { + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + var elem = stack.popu(); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + var elem = stack.popw(); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + var elem = u32.view(stack.popf()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + var elem = u64.view(stack.popd()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); + var val = ObjectI31.!(Value.Ref.!(elem).val).val; + for (i < vals.length) vals[i] = val; + stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + } else { + var vals = Array.new(u31.!(length)); + var elem = stack.popObject(); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + var v128 = stack.pops(); + var low = v128.0; + var high = v128.1; + for (i = 0; i < vals.length; i += 2) { + vals[i] = low; + vals[i+1] = high; + } + stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + var elem = u8.view(stack.popu()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + var elem = u16.view(stack.popu()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); + } + } + } + Pair => { + var len = u31.!(length); + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var bytes = Array.new(len << 2); + var elem = stack.popu(); + var offset = 0; + for (i < len) { + Ref.at(bytes, offset).val = elem; + offset += 4; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } + I64 => { + var bytes = Array.new(len << 3); + var elem = stack.popw(); + var offset = 0; + for (i < len) { + Ref.at(bytes, offset).val = elem; + offset += 8; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); + } + F32 => { + var bytes = Array.new(len << 2); + var elem = stack.popf(); + var offset = 0; + for (i < len) { + Ref.at(bytes, offset).val = u32.view(elem); + offset += 4; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } + F64 => { + var bytes = Array.new(len << 3); + var elem = stack.popd(); + var offset = 0; + for (i < len) { + Ref.at(bytes, offset).val = u64.view(elem); + offset += 8; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var bytes = Array.new(len << 2); + var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); + var val = ObjectI31.!(Value.Ref.!(elem).val).val; + var offset = 0; + for (i < len) { + Ref.at(bytes, offset).val = val; + offset += 4; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } else { + var vals = Array.new(len); + var elem = stack.popObject(); + for (i < len) vals[i] = elem; + stack.push(Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT))); + } + } + V128 => { + var bytes = Array.new(len << 4); + var v128 = stack.pops(); + var low = v128.0; + var high = v128.1; + var offset = 0; + for (i < len) { + var r = Ref.at(bytes, offset); + r.lo_val = low; + r.hi_val = high; + offset += 16; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 4))); + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var bytes = Array.new(len); + var elem = u8.view(stack.popu()); + for (i < len) { + bytes[i] = elem; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 0))); + } + PACKED_I16 => { + var bytes = Array.new(len << 1); + var elem = u16.view(stack.popu()); + var offset = 0; + for (i < len) { + Ref.at(bytes, offset).val = elem; + offset += 2; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 1))); + } + } + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } return null; } def ARRAY_NEW_DEFAULT(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { var decl = ArrayDecl.!(instance.heaptypes[array_index]); var length = stack.popu(); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - var vals = Array.new(u31.!(length)); - var elem = Values.default(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArray.new(decl, vals))); + match (ObjTuning.arrayMode) { + Typed => { + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u32; + stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u64; + stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = u32.view(0.0f); + stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = u64.view(0.0d); + stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = null; + stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + } else { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = null; + stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + // for (i < vals.length) vals[i] = 0u64; + stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = Values.default(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u8; + stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u16; + stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); + } + } + } + Pair => { + var len = u31.!(length); + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32, F32 => { + var bytes = Array.new(len << 2); + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } + I64, F64 => { + var bytes = Array.new(len << 3); + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var bytes = Array.new(len << 2); + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } else { + var vals = Array.new(len); + stack.push(Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT))); + } + } + V128 => { + var bytes = Array.new(len << 4); + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 4))); + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = Values.default(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var bytes = Array.new(len); + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 0))); + } + PACKED_I16 => { + var bytes = Array.new(len << 1); + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 1))); + } + } + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = Values.default(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } return null; } def ARRAY_NEW_FIXED(stack: ExecStack, instance: Instance, array_index: u31, length: u32) -> Throwable { var decl = ArrayDecl.!(instance.heaptypes[array_index]); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - var vals = Array.new(u31.!(length)); - var t = decl.elem_types[0].valtype; - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); - stack.push(Value.Ref(HeapArray.new(decl, vals))); + + match (ObjTuning.arrayMode) { + Typed => { + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popu()); + stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popw()); + stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popf()); + stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popd()); + stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = ObjectI31.!(stack.popObject()).val; + stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + + } else { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popObject(); + stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + for (i = vals.length - 2; i >= 0; i -= 2) { + var v128 = stack.pops(); + vals[i] = v128.0; + vals[i+1] = v128.1; + } + stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var t = decl.elem_types[0].valtype; + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u8.view(stack.popu()); + stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u16.view(stack.popu()); + stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); + } + } + } + Pair => { + var len = u31.!(length); + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var nbytes = len << 2; + var bytes = Array.new(nbytes); + for (i < len) { + var elem = stack.popu(); + nbytes -= 4; + Ref.at(bytes, nbytes).val = elem; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } + I64 => { + var nbytes = len << 3; + var bytes = Array.new(nbytes); + for (i < len) { + var elem = stack.popw(); + nbytes -= 8; + Ref.at(bytes, nbytes).val = elem; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); + } + F32 => { + var nbytes = len << 2; + var bytes = Array.new(nbytes); + for (i < len) { + var elem = stack.popf(); + nbytes -= 4; + Ref.at(bytes, nbytes).val = u32.view(elem); + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } + F64 => { + var nbytes = len << 3; + var bytes = Array.new(nbytes); + for (i < len) { + var elem = stack.popd(); + nbytes -= 8; + Ref.at(bytes, nbytes).val = u64.view(elem); + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && ! nullable) { + var nbytes = len << 2; + var bytes = Array.new(nbytes); + for (i < len) { + var elem = ObjectI31.!(stack.popObject()).val; + nbytes -= 4; + Ref.at(bytes, nbytes).val = elem; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); + } else { + var vals = Array.new(len); + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popObject(); + stack.push(Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT))); + } + } + V128 => { + var nbytes = len << 4; + var bytes = Array.new(nbytes); + for (i < len) { + var v128 = stack.pops(); + nbytes -= 16; + var r = Ref.at(bytes, nbytes); + r.lo_val = v128.0; + r.hi_val = v128.1; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 4))); + } + _ => { + var vals = Array.new(u31.!(length)); + var t = decl.elem_types[0].valtype; + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var bytes = Array.new(len); + var nbytes = len; + for (i < len) { + var elem = u8.view(stack.popu()); + nbytes -= 1; + bytes[nbytes] = elem; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 0))); + } + PACKED_I16 => { + var nbytes = len << 1; + var bytes = Array.new(nbytes); + for (i < len) { + var elem = u16.view(stack.popu()); + nbytes -= 2; + Ref.at(bytes, nbytes).val = elem; + } + stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 1))); + } + } + } + _ => { + var vals = Array.new(u31.!(length)); + var t = decl.elem_types[0].valtype; + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } return null; } def ARRAY_NEW_DATA(stack: ExecStack, instance: Instance, array_index: u31, data_index: u31) -> Throwable { @@ -115,9 +698,102 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var ddecl = instance.module.data[data_index]; if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArray.new(rtt, t.1))); + match (ObjTuning.arrayMode) { + Typed => { + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32 => { + var t = bytesToI32s(ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI32.new(rtt, t.1))); + } + I64 => { + var t = bytesToI64s(ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI64.new(rtt, t.1))); + } + F32 => { + var t = bytesToI32s(ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayF32.new(rtt, t.1))); + } + F64 => { + var t = bytesToI64s(ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayF64.new(rtt, t.1))); + } + Ref => { + return stack.trap(TrapReason.ERROR); + } + V128 => { + var t = bytesToI64s(ddecl.data, offset, length << 1); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayV128.new(rtt, t.1))); + } + _ => { + var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); + } + } + } + PACKED_I8 => { + var t = bytesToI8s(ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI8.new(rtt, t.1))); + } + PACKED_I16 => { + var t = bytesToI16s(ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI16.new(rtt, t.1))); + } + } + } + Pair => { + var elemSize = sizeOfStorage(rtt.elem_types[0]); + var t = bytesToBytes(ddecl.data, offset, length * elemSize); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 2))); + } + I64 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 3))); + } + F32 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 2))); + } + F64 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 3))); + } + V128 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 4))); + } + _ => { + if (true) return stack.trap(TrapReason.ERROR); + var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); + } + } + } + PACKED_I8 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 0))); + } + PACKED_I16 => { + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 1))); + } + } + } + _ => { + var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); + } + } return null; } def ARRAY_NEW_ELEM(stack: ExecStack, instance: Instance, array_index: u31, elem_index: u31) -> Throwable { @@ -127,18 +803,153 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var edecl = instance.module.elems[elem_index]; if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - var vals = Array.new(u31.!(length)); - var r = copyElemsInto(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds - stack.push(Value.Ref(HeapArray.new(rtt, vals))); + match (ObjTuning.arrayMode) { + Typed => { + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI32(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayI32.new(rtt, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI64(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayI64.new(rtt, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI32(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayF32.new(rtt, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI64(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayF64.new(rtt, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI31(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayI31.new(rtt, vals))); + } else { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoObject(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayRef.new(rtt, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + var r = copyElemsIntoI64(vals, instance, 0, edecl, offset, length << 1); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayV128.new(rtt, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var r = copyElemsInto(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds + stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI8(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayI8.new(rtt, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI16(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayI16.new(rtt, vals))); + } + } + } + Pair => { + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32, F32 => { + var len = length << 2; + var vals = Array.new(u31.!(len)); + var r = copyElemsIntoI32bytes(vals, instance, 0, edecl, offset, len); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 2))); + } + I64, F64 => { + var len = length << 3; + var vals = Array.new(u31.!(len)); + var r = copyElemsIntoI64bytes(vals, instance, 0, edecl, offset, len); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 3))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var len = length << 2; + var bytes = Array.new(u31.!(len)); + var r = copyElemsIntoI32bytes(bytes, instance, 0, edecl, offset, len); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, bytes, 2))); + } else { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoObject(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, vals, null, Values.OBJS_SHIFT))); + } + } + V128 => { + var len = length << 4; + var vals = Array.new(u31.!(len)); + var r = copyElemsIntoV128bytes(vals, instance, 0, edecl, offset, len); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 4))); + } + _ => { + var vals = Array.new(u31.!(length)); + var r = copyElemsInto(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds + stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + var r = copyElemsIntoI8(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 1))); + } + PACKED_I16 => { + var len = length << 1; + var vals = Array.new(u31.!(len)); + var r = copyElemsIntoI16bytes(vals, instance, 0, edecl, offset, len); + if (!r) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 1))); + } + } + } + _ => { + var vals = Array.new(u31.!(length)); + var r = copyElemsInto(vals, instance, 0, edecl, offset, length); + if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds + stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); + } + } return null; } def ARRAY_GET(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { var index = stack.popu(); var obj = stack.popArray(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - if (index >= u32.view(obj.vals.length)) return stack.trap(TrapReason.ARRAY_OOB); - stack.push(obj.vals[index]); + if (index >= u32.view(obj.length())) return stack.trap(TrapReason.ARRAY_OOB); + stack.push(obj.getValue(index)); return null; } def ARRAY_GET_S(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { @@ -146,8 +957,8 @@ component Runtime { var index = stack.popu(); var obj = stack.popArray(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - if (index >= u32.view(obj.vals.length)) return stack.trap(TrapReason.ARRAY_OOB); - stack.push(V3Eval.signExtend(decl.elem_types[0], obj.vals[index])); // XXX: pushi + if (index >= u32.view(obj.length())) return stack.trap(TrapReason.ARRAY_OOB); + stack.push(V3Eval.signExtend(decl.elem_types[0], obj.getValue(index))); // XXX: pushi return null; } def ARRAY_GET_U(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { @@ -155,8 +966,8 @@ component Runtime { var index = stack.popu(); var obj = stack.popArray(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - if (index >= u32.view(obj.vals.length)) return stack.trap(TrapReason.ARRAY_OOB); - stack.push(V3Eval.zeroExtend(decl.elem_types[0], obj.vals[index])); // XXX: pushi + if (index >= u32.view(obj.length())) return stack.trap(TrapReason.ARRAY_OOB); + stack.push(V3Eval.zeroExtend(decl.elem_types[0], obj.getValue(index))); // XXX: pushi return null; } def ARRAY_SET(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { @@ -165,27 +976,164 @@ component Runtime { var index = stack.popu(); var obj = stack.popArray(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - if (index >= u32.view(obj.vals.length)) return stack.trap(TrapReason.ARRAY_OOB); - obj.vals[index] = val; + if (index >= u32.view(obj.length())) return stack.trap(TrapReason.ARRAY_OOB); + obj.setValue(index, val); return null; } def ARRAY_LEN(stack: ExecStack, instance: Instance) -> Throwable { var obj = stack.popArray(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - stack.pushi(obj.vals.length); + stack.pushi(obj.length()); return null; } def ARRAY_FILL(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var size = stack.popu(); - var val = stack.popV(rtt.elem_types[0].valtype); // XXX: polymorphic pop + var stype = rtt.elem_types[0]; + var val = stack.popV(stype.valtype); // XXX: polymorphic pop var offset = stack.popu(); var obj = stack.popArray(); if (obj == null) return stack.trap(TrapReason.NULL_DEREF); - var index = ArrayUtil.boundsCheck(obj.vals, offset, size); + var index = ArrayUtil.boundsCheckWithLength(u31.view(obj.length()), offset, 0, size); if (index < 0) return stack.trap(TrapReason.ARRAY_OOB); - var r = obj.vals[index ..+ size]; - for (i < r.length) r[i] = val; + match (ObjTuning.arrayMode) { + Typed => { + match (obj) { + x: HeapArrayValue => { + var r = x.vals[index ..+ size]; + for (i < r.length) r[i] = val; + } + x: HeapArrayI32 => { + var r = x.vals[index ..+ size]; + var v = Value.I32.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayI31 => { + var r = x.vals[index ..+ size]; + var v = Value.I31.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayI64 => { + var r = x.vals[index ..+ size]; + var v = Value.I64.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayI8 => { + var r = x.vals[index ..+ size]; + var v = u8.view(Value.I32.!(val).val); + for (i < r.length) r[i] = v; + } + x: HeapArrayI16 => { + var r = x.vals[index ..+ size]; + var v = u16.view(Value.I32.!(val).val); + for (i < r.length) r[i] = v; + } + x: HeapArrayF32 => { + var r = x.vals[index ..+ size]; + var v = Value.F32.!(val).bits; + for (i < r.length) r[i] = v; + } + x: HeapArrayF64 => { + var r = x.vals[index ..+ size]; + var v = Value.F64.!(val).bits; + for (i < r.length) r[i] = v; + } + x: HeapArrayRef => { + var r = x.vals[index ..+ size]; + var v = Value.Ref.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayV128 => { + var r = x.vals[index ..+ (size << 1)]; + var v128 = Value.V128.!(val); + for (i=0; i < r.length; i+=2) { + r[i] = v128.low; + r[i+1] = v128.high; + } + } + } + } + Pair => { + match (obj) { + x: HeapArrayValue => { + var r = x.vals[index ..+ size]; + for (i < r.length) r[i] = val; + } + x: HeapArrayPair => { + var uindex = u32.!(index); + match (stype.pack) { + UNPACKED => { + match (stype.valtype) { + I32 => { + var ival = i32.!(Value.I32.!(val).val); + for (u = uindex; u < size; u++) { + x.setI32(u, ival); + } + } + I64 => { + var ival = i64.!(Value.I64.!(val).val); + for (u = uindex; u < size; u++) { + x.setI64(u, ival); + } + } + F32 => { + var fval = float.!(Value.F32.!(val).bits); + for (u = uindex; u < size; u++) { + x.setF32(u, fval); + } + } + F64 => { + var fval = double.!(Value.F64.!(val).bits); + for (u = uindex; u < size; u++) { + x.setF64(u, fval); + } + } + V128 => { + var vals = Value.V128.!(val); + var low = vals.low; + var high = vals.high; + for (u = uindex; u < size; u++) { + x.setV128(u, low, high); + } + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var ival = i31.!(Value.I31.!(val).val); + for (u = uindex; u < size; u++) { + x.setI31(u, ival); + } + } else { + var rval = Value.Ref.!(val).val; + for (u = uindex; u < size; u++) { + x.setRef(u, rval); + } + } + } + _ => {} + } + } + PACKED_I8 => { + var ival = i8.!(Value.I32.!(val).val); + for (u = uindex; u < size; u++) { + x.setI8(u, ival); + } + } + PACKED_I16 => { + var ival = i16.!(Value.I32.!(val).val); + for (u = uindex; u < size; u++) { + x.setI16(u, ival); + } + } + } + } + } + } + _ => { + var vals = obj.getArray(); + var r = vals[index ..+ size]; + for (i < r.length) r[i] = val; + } + } return null; } def ARRAY_COPY(stack: ExecStack, instance: Instance, array_index1: u31, array_index2: u31) -> Throwable { @@ -195,9 +1143,200 @@ component Runtime { var dst_offset = stack.popu(); var dst = stack.popArray(); if (src == null || dst == null) return stack.trap(TrapReason.NULL_DEREF); - var r = ArrayUtil.safeCopy(dst.vals, dst_offset, src.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; + match (ObjTuning.arrayMode) { + Typed => { + if (HeapArrayValue.?(src) && HeapArrayValue.?(dst)) { + var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + if (HeapArrayValue.?(dst)) { + var arr = HeapArrayValue.!(dst); + var r = ArrayUtil.safeCopyDiffTypes(arr.vals, dst_offset, u31.view(src.length()), src_offset, size, src.getValue); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + if (HeapArrayValue.?(src)){ + var arr = HeapArrayValue.!(src); + match (dst) { + x: HeapArrayI32 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI31 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, fun (x: u32) => u31.!(arr.getI31(x))); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI64 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI8 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU8); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI16 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU16); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayF32 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayF64 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayRef => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getRef); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayV128 => { + var r = ArrayUtil.safeCopyFromV128(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getV128); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + _ => { return stack.trap(TrapReason.ERROR); } + } + } + + match (src) { + x: HeapArrayI32 => { + if (HeapArrayI32.?(dst)) { + var arr = HeapArrayI32.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI31 => { + if (HeapArrayI31.?(dst)) { + var arr = HeapArrayI31.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI64 => { + if (HeapArrayI64.?(dst)) { + var arr = HeapArrayI64.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI8 => { + if (HeapArrayI8.?(dst)) { + var arr = HeapArrayI8.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI16 => { + if (HeapArrayI16.?(dst)) { + var arr = HeapArrayI16.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayF32 => { + if (HeapArrayF32.?(dst)) { + var arr = HeapArrayF32.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayF64 => { + if (HeapArrayF64.?(dst)) { + var arr = HeapArrayF64.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayRef => { + if (HeapArrayRef.?(dst)) { + var arr = HeapArrayRef.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayV128 => { + if (HeapArrayV128.?(dst)) { + var arr = HeapArrayV128.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size << 1); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + } + } + Pair => { + // HeapArrayValue -> HeapArrayValue + if (HeapArrayValue.?(src) && HeapArrayValue.?(dst)) { + var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + // HeapArrayPair -> HeapArrayPair + if (HeapArrayPair.?(src) && HeapArrayPair.?(dst)) { + var src_arr = HeapArrayPair.!(src); + var dst_arr = HeapArrayPair.!(dst); + var shift = src_arr.shift; + + if (shift != dst_arr.shift) return stack.trap(TrapReason.ERROR); // Validation should prevent this + if (shift == Values.OBJS_SHIFT) { + var r = ArrayUtil.safeCopy(dst_arr.objs, dst_offset, src_arr.objs, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } else { + var r = ArrayUtil.safeCopy(dst_arr.bytes, dst_offset << shift, src_arr.bytes, src_offset << shift, size << shift); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + return null; + } + + // HeapArrayPair -> HeapArrayValue: shouldn't happen + if (HeapArrayValue.?(dst)) { + var arr = HeapArrayValue.!(dst); + var r = ArrayUtil.safeCopyDiffTypes(arr.vals, dst_offset, u31.view(src.length()), src_offset, size, src.getValue); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + // HeapArrayValue -> HeapArrayPair: shouldn't happen + if (HeapArrayValue.?(src)){ + var src_arr = HeapArrayValue.!(src); + var dst_arr = HeapArrayPair.!(dst); + + var r = ArrayUtil.safeCopyFromValueToPair(dst_arr, dst_offset, src_arr, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + _ => { + var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + + return stack.trap(TrapReason.ERROR); } def ARRAY_INIT_DATA(stack: ExecStack, instance: Instance, array_index: u31, data_index: u31) -> Throwable { var size = stack.popu(); @@ -208,11 +1347,142 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); if (instance.dropped_data[data_index]) return if(size > 0, stack.trap(TrapReason.DATA_SEGMENT_DROPPED), null); var data = instance.module.data[data_index].data; - if (ArrayUtil.boundsCheck(obj.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); - var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(obj.vals, dst_offset, t.1, 0, size); - return null; + if (ArrayUtil.boundsCheckWithLength(u31.view(obj.length()), dst_offset, 0, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + match (ObjTuning.arrayMode) { + Typed => { + match (obj) { + x: HeapArrayValue => { + var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32 => { + var t = bytesToI32s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayI32 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + I64 => { + var t = bytesToI64s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayI64 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + F32 => { + var t = bytesToI32s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayF32 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + F64 => { + var t = bytesToI64s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayF64 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + V128 => { + var t = bytesToI64s(data, src_offset, size << 1); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayV128 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size << 1); + return null; + } + } + } + _ => { } + } + } + PACKED_I8 => { + match (obj) { + x: HeapArrayI8 => { + var t = bytesToI8s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + PACKED_I16 => { + match (obj) { + x: HeapArrayI16 => { + var t = bytesToI16s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + } + } + Pair => { + match (obj) { + x: HeapArrayValue => { + var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + x: HeapArrayPair => { + var stype = rtt.elem_types[0]; + match (stype.pack) { + UNPACKED => { + match (stype.valtype) { + I32, F32 => { + size = size << 2; + } + I64, F64 => { + size = size << 3; + } + V128 => { + size = size << 4; + } + _ => {} + } + } + PACKED_I8 => { /* leave the size alone */ } + PACKED_I16 => { + size = size << 1; + } + } + var t = bytesToBytes(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.bytes, dst_offset, t.1, 0, size); + return null; + } + } + } + _ => { + var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + var vals = obj.getArray(); + ArrayUtil.safeCopy(vals, dst_offset, t.1, 0, size); + return null; + } + } + + return stack.trap(TrapReason.ERROR); } def ARRAY_INIT_ELEM(stack: ExecStack, instance: Instance, array_index: u31, elem_index: u31) -> Throwable { var size = stack.popu(); @@ -223,8 +1493,91 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); if (instance.dropped_elems[elem_index]) return if(size > 0, stack.trap(TrapReason.ELEM_SEGMENT_DROPPED), null); var edecl = instance.module.elems[elem_index]; - var r = copyElemsInto(obj.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); + match (ObjTuning.arrayMode) { + Typed => { + match (obj) { + x: HeapArrayValue => { + var r = copyElemsInto(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayI32 => { + var r = copyElemsIntoI32(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayI31 => { + var r = copyElemsIntoI31(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayI64 => { + var r = copyElemsIntoI64(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayI8 => { + var r = copyElemsIntoI8(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayI16 => { + var r = copyElemsIntoI16(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayF32 => { + var r = copyElemsIntoI32(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayF64 => { + var r = copyElemsIntoI64(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayRef => { + var r = copyElemsIntoObject(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayV128 => { + var r = copyElemsIntoI64(x.vals, instance, dst_offset, edecl, src_offset, size << 1); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + _ => {return stack.trap(TrapReason.ARRAY_OOB);} + } + } + Pair => { + match (obj) { + x: HeapArrayValue => { // shouldn't happen ... + var r = copyElemsInto(x.vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + x: HeapArrayPair => { + var stype = rtt.elem_types[0]; + match (stype.pack) { + UNPACKED => { + match (stype.valtype) { + I32, F32 => { + size = size << 2; + } + I64, F64 => { + size = size << 3; + } + V128 => { + size = size << 4; + } + _ => {} + } + } + PACKED_I8 => { /* leave the size alone */ } + PACKED_I16 => { + size = size << 1; + } + } + var r = copyElemsIntoI8(x.bytes, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + } + } + _ => { + var vals = obj.getArray(); + var r = copyElemsInto(vals, instance, dst_offset, edecl, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + } + } return null; } // --- Table operations ----------------------------------------------------------------------------------------- @@ -327,7 +1680,7 @@ component Runtime { var func_ref = stack.popV(ValueType.Ref(true, HeapType.Func(cont_decl.sig))); var func = Function.!(Value.Ref.!(func_ref).val); if (func == null) return stack.trap(TrapReason.NULL_DEREF); - + var new_stack = Target.newWasmStack().reset(func); new_stack.cont_bottom = new_stack; var cont = Continuations.makeContinuation(new_stack); @@ -399,8 +1752,8 @@ component Runtime { match (ht_val) { BpHeapTypeCode.FUNC.val => return Function.?(obj); BpHeapTypeCode.EXTERN.val => return Object.?(obj); - BpHeapTypeCode.ARRAY.val => return HeapArray.?(obj); - BpHeapTypeCode.STRUCT.val => return HeapStruct.?(obj); + BpHeapTypeCode.ARRAY.val => return HeapArrayGeneric.?(obj); + BpHeapTypeCode.STRUCT.val => return HeapStructGeneric.?(obj); BpHeapTypeCode.ANY.val => return Object.?(obj); BpHeapTypeCode.EQ.val => return HeapObject.?(obj); BpHeapTypeCode.I31.val, // fallthru @@ -430,14 +1783,214 @@ component Runtime { } } } + def evalInitExprU32(instance: Instance, i: InitExpr) -> u32 { + var v = instance.evalInitExpr(i); + return Value.I32.!(v).val; + } + def copyElemsIntoI32(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU32(instance, _)); + } + } + } + def store4(bytes: Array, index: u64, val: u32) { + Ref.at(bytes, int.!(index)).val = val; + } + def copyElemsIntoI32bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprU32(instance, _), store4(_, _, _), 4); + } + } + } + def evalInitExprU31(instance: Instance, i: InitExpr) -> u31 { + var v = instance.evalInitExpr(i); + return Value.I31.!(v).val; + } + def copyElemsIntoI31(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU31(instance, _)); + } + } + } + def evalInitExprU64(instance: Instance, i: InitExpr) -> u64 { + var v = instance.evalInitExpr(i); + return Value.I64.!(v).val; + } + def copyElemsIntoI64(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { + FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU64(instance, _)); + } + } + } + def store8(bytes: Array, index: u64, val: u64) { + Ref.at(bytes, int.!(index)).val = val; + } + def copyElemsIntoI64bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprU64(instance, _), store8(_, _, _), 8); + } + } + } + def evalInitExprV128(instance: Instance, i: InitExpr) -> (u64, u64) { + var v = instance.evalInitExpr(i); + var val = Value.V128.!(v); + return (val.low, val.high); + } + def store16(bytes: Array, index: u64, pair: (u64, u64)) { + var r = Ref.at(bytes, int.!(index)); + r.lo_val = pair.0; + r.hi_val = pair.1; + } + def copyElemsIntoV128bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprV128(instance, _), store16(_, _, _), 16); + } + } + } + def evalInitExprU8(instance: Instance, i: InitExpr) -> u8 { + var v = instance.evalInitExpr(i); + return u8.view(Value.I32.!(v).val); + } + def copyElemsIntoI8(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { + FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU8(instance, _)); + } + } + } + def evalInitExprU16(instance: Instance, i: InitExpr) -> u16 { + var v = instance.evalInitExpr(i); + return u16.view(Value.I32.!(v).val); + } + def copyElemsIntoI16(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { + FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU16(instance, _)); + } + } + } + def store2(bytes: Array, index: u64, val: u16) { + Ref.at(bytes, int.!(index)).val = val; + } + def copyElemsIntoI16bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprU16(instance, _), store2(_, _, _), 2); + } + } + } + def evalInitExprObject(instance: Instance, i: InitExpr) -> Object { + var v = instance.evalInitExpr(i); + match (v) { + Ref(obj) => return obj; + I31(val) => return ObjectI31.new(val); + _ => return null; + } + } + def copyElemsIntoObject(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + if (elem == null) { + if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; + return true; + } + match (elem.details) { FuncRefs(vals) => { + return false; + } + Exprs(vals) => { + return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprObject(instance, _)); + } + } + } +} +class ExtendedDataReader extends DataReader { + new(data: Range) super(data) { } + def read_u8() => read1(); + def read_u16() -> u16 { + var range = acquire(2); + if (range.length != 2) return 0; + at(pos + 2); + return DataReaders.read_range_u16(range); + } } def bytesToVals(storage: StorageType, data: Array, offset: u32, length: u32) -> (bool, Array) { // TODO: MaybeTrap var vals = Array.new(int.!(length)); - var start = ArrayUtil.boundsCheck(data, offset, length); + var size = sizeOfStorage(storage); + var nbytes = length * size; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); if (start < 0) return (false, null); - var limit = sizeOfStorage(storage) * length + offset; - if (limit > data.length) return (false, null); - var d = DataReader.new(data).reset(data, start, int.!(limit)); + var d = ExtendedDataReader.new(data).reset(data, start, start + int.!(nbytes)); match (storage.pack) { UNPACKED => { match (storage.valtype) { @@ -449,11 +2002,76 @@ def bytesToVals(storage: StorageType, data: Array, offset: u32, length: u3 _ => ; } } - PACKED_I8 => for (i < vals.length) vals[i] = Value.I32(d.read1()); - PACKED_I16 => for (i < vals.length) vals[i] = Value.I32(d.read1() | (u32.!(d.read1()) << 8)); + PACKED_I8 => for (i < vals.length) vals[i] = Value.I32(d.read_u8()); + PACKED_I16 => for (i < vals.length) vals[i] = Value.I32(d.read_u16()); } return (d.ok, vals); } +def bytesToBytes(data: Array, offset: u32, length: u32) -> (bool, Array) { + var start = ArrayUtil.boundsCheck(data, offset, length); + if (start < 0) return (false, null); + var limit = length + offset; + var bytes = DataReader.new(data).reset(data, start, int.!(limit)).readN(int.!(length)); + return (true, bytes); +} +def bytesToI8s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var vals = Array.new(int.!(length)); + var start = ArrayUtil.boundsCheck(data, offset, length); + if (start < 0) return (false, null); + var limit = length + offset; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u8(); + return (d.ok, vals); +} +def bytesToI8sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var vals = Array.new(int.!(length)); + var start = ArrayUtil.boundsCheck(data, offset, length); + if (start < 0) return (false, null); + var limit = length + offset; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = u32.view(d.read_u8()); + return (d.ok, vals); +} +def bytesToI16s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var vals = Array.new(int.!(length)); + var nbytes = length << 1; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var limit = offset + nbytes; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u16(); + return (d.ok, vals); +} +def bytesToI16sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var vals = Array.new(int.!(length)); + var nbytes = length << 1; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var limit = offset + nbytes; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = u32.view(d.read_u16()); + return (d.ok, vals); +} +def bytesToI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var vals = Array.new(int.!(length)); + var nbytes = length << 2; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var limit = offset + nbytes; + var d = DataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u32(); + return (d.ok, vals); +} +def bytesToI64s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var vals = Array.new(int.!(length)); + var nbytes = length << 3; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var limit = offset + nbytes; + var d = DataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u64(); + return (d.ok, vals); +} def sizeOfStorage(storage: StorageType) -> u32 { match (storage.pack) { UNPACKED => { @@ -468,3 +2086,11 @@ def sizeOfStorage(storage: StorageType) -> u32 { PACKED_I16 => return 2; } } +layout Layout_i31 { + +0 val: i31; + =4; +} +layout Layout_u31 { + +0 val: u31; + =4; +} diff --git a/src/engine/Tuning.v3 b/src/engine/Tuning.v3 index 8259cdec5..49e7a7a90 100644 --- a/src/engine/Tuning.v3 +++ b/src/engine/Tuning.v3 @@ -1,4 +1,4 @@ -// Copyright 2022 Ben L. Titzer. All rights reserved. +// Copyright 2022-2025 Wizard authors. All rights reserved. // See LICENSE for details of Apache 2.0 license. // Contains flags that explicitly disable features to test their impact on performance. @@ -66,3 +66,32 @@ component SpcTuning { var inlineGlobalAccess = true; // enable inline access of (primitive) globals var disableMemoryBoundsChecks = false; // unsafe! don't emit bounds checks } + +enum ArrayMode(code: int) { + Original(0), // Default value: HeapArrayValue only + Typed(1), // HeapArrayX where X can be one of the storage types + Pair(2) // Having Array and Array where one of them is null +} + +enum StructMode(code: int) { + Original(0), // Default value: Values only + Pair(1) // Having Array (holdings the refs) and Array (holding prims) +} + +def arrayModeFor(code: int) -> ArrayMode { + def modes = [ArrayMode.Original, ArrayMode.Typed, ArrayMode.Pair]; + return if(code >= 0 && code < modes.length, modes[code], ArrayMode.Original); +} + +def structModeFor(code: int) -> StructMode { + def modes = [StructMode.Original, StructMode.Pair]; + return if(code >= 0 && code < modes.length, modes[code], StructMode.Original); +} + +component ObjTuning { + def arrayModeCode = 0; + def arrayMode = arrayModeFor(arrayModeCode); + + def structModeCode = 0; + def structMode = structModeFor(structModeCode); +} diff --git a/src/engine/Type.v3 b/src/engine/Type.v3 index 07a84b88d..6fbef801c 100644 --- a/src/engine/Type.v3 +++ b/src/engine/Type.v3 @@ -399,7 +399,62 @@ class StructDecl extends HeapTypeDecl { def field_types: Array; def defaultable = allHaveDefaultValues(field_types); - new(final: bool, supertypes: Array, field_types) super(final, supertypes) {} + // These fields are used with paired array representation + def var field_shifts: Array; + def var field_offsets: Array; + def var num_bytes: int; + def var num_refs: int; + + new(final: bool, supertypes: Array, field_types) + super(final, supertypes) { + match (ObjTuning.structMode) { + Pair => { + field_shifts = Array.new(field_types.length); + field_offsets = Array.new(field_types.length); + for (i < field_types.length) { + var ft = field_types[i]; + match (ft.pack) { + UNPACKED => { + match (ft.valtype) { + I32, F32 => { + field_shifts[i] = 2; + field_offsets[i] = num_bytes; + num_bytes += 4; + } + I64, F64 => { + field_shifts[i] = 3; + field_offsets[i] = num_bytes; + num_bytes += 8; + } + V128 => { + field_shifts[i] = 4; + field_offsets[i] = num_bytes; + num_bytes += 16; + } + Ref => { + field_shifts[i] = -1; + field_offsets[i] = num_refs++; + } + _ => { } + } + } + PACKED_I8 => { + field_shifts[i] = 0; + field_offsets[i] = num_bytes; + num_bytes += 1; + } + PACKED_I16 => { + field_shifts[i] = 1; + field_offsets[i] = num_bytes; + num_bytes += 2; + } + } + } + } + _ => { } + } + + } def render(buf: StringBuilder) -> StringBuilder { return putUid(buf.put1("struct #%d", heaptype_index)); diff --git a/src/engine/Value.v3 b/src/engine/Value.v3 index 1af66678d..9b4b2a92c 100644 --- a/src/engine/Value.v3 +++ b/src/engine/Value.v3 @@ -1,4 +1,4 @@ -// Copyright 2020 Ben L. Titzer. All rights reserved. +// Copyright 2020-2025 Wizard authors. All rights reserved. // See LICENSE for details of Apache 2.0 license. // WebAssembly program values. @@ -25,13 +25,885 @@ enum ValueKind(code: byte) { // Superclass of all objects referred to by Value.Ref, including external refs. class Object extends Exportable { } +class ObjectI31(val: u31) extends Object { } + // Objects allocated on the "wasm" GC heap, i.e. from the GC proposal. -class HeapObject(decl: HeapTypeDecl, vals: Array) extends Object {} -class HeapStruct extends HeapObject { - new(decl: StructDecl, vals: Array) super(decl, vals) { } + +class HeapObject extends Object { + def decl: HeapTypeDecl; + new(decl) { } +} + +// superclass of struct representations; defines getter/setter interface +class HeapStructGeneric extends HeapObject { + new(decl: StructDecl) super(decl) { } + + def getFieldValue(index: u31) -> Value; + def getFieldSignExtend8Value(index: u31) -> Value; + def getFieldSignExtend16Value(index: u31) -> Value; + def getFieldZeroExtend8Value(index: u31) -> Value; + def getFieldZeroExtend16Value(index: u31) -> Value; + def getFieldI32(index: u32) -> i32; + def getFieldI64(index: u32) -> i64; + def getFieldI8(index: u32) -> i8; + def getFieldI16(index: u32) -> i16; + def getFieldF32(index: u32) -> float; + def getFieldF64(index: u32) -> double; + def getFieldRef(index: u32) -> Object; + def getFieldTRef(index: u32) -> Object; + def getFieldV128(index: u32) -> (u64, u64); + + def setFieldValue(index: u31, val: Value); + def setFieldI32(index: u32, val: i32); + def setFieldI64(index: u32, val: i64); + def setFieldI8(index: u32, val: i8); + def setFieldI16(index: u32, val: i16); + def setFieldF32(index: u32, val: float); + def setFieldF64(index: u32, val: double); + def setFieldRef(index: u32, val: Object); + def setFieldTRef(index: u32, val: Object); + def setFieldV128(index: u32, low: u64, high: u64); +} + +// Original Array struct representation +// Provides definitions for Value getters/setter (only) +class HeapStruct extends HeapStructGeneric { + def vals: Array; + new(decl: StructDecl, vals) super(decl) { } + + def getFieldValue(index: u31) -> Value { return vals[index]; } + def getFieldSignExtend8Value(index: u31) -> Value { return Value.I32(u32.view(i8.view(Value.I32.!(vals[index]).val))); } + def getFieldSignExtend16Value(index: u31) -> Value { return Value.I32(u32.view(i16.view(Value.I32.!(vals[index]).val))); } + def getFieldZeroExtend8Value(index: u31) -> Value { return Value.I32(u32.view(u8.view(Value.I32.!(vals[index]).val))); } + def getFieldZeroExtend16Value(index: u31) -> Value { return Value.I32(u32.view(u16.view(Value.I32.!(vals[index]).val))); } + + def setFieldValue(index: u31, val: Value) { vals[index] = val; } +} + +// Paired array representation: an Array holds all the reference fields, +// and an Array holds all the primitive values +class HeapStructPair extends HeapStructGeneric { + def objs: Array; + def bytes: Array; + new(decl: StructDecl, objs, bytes) super(decl) { } + + def getFieldValue(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + match (decl.field_shifts[index]) { + -1 => { + return Value.Ref(objs[decl.field_offsets[index]]); + } + 0 => { + var v = bytes[decl.field_offsets[index]]; + return Value.I32(u32.view(v)); + } + 1 => { + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(v)); + } + 2 => { + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return if(decl.field_types[index].valtype == ValueType.I32, Value.I32(v), Value.F32(v)); + } + 3 => { + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return if(decl.field_types[index].valtype == ValueType.I64, Value.I64(v), Value.F64(v)); + } + 4 => { + var r = Ref.at(bytes, decl.field_offsets[index]); + return Value.V128(r.lo_val, r.hi_val); + } + } + return Value.I32(u32.view(-1)); + } + def getFieldSignExtend8Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(i32.!(v))); + } + def getFieldSignExtend16Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(i32.!(v))); + } + def getFieldZeroExtend8Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(v)); + } + def getFieldZeroExtend16Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(v)); + } + def getFieldI32(index: u32) -> i32 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I32)) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldI64(index: u32) -> i64 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I64)) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldI8(index: u32) -> i8 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I8) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldI16(index: u32) -> i16 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I16) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldF32(index: u32) -> float { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F32)) { return -1f; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldF64(index: u32) -> double { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F64)) { return -1d; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldRef(index: u32) -> Object { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && ValueType.Ref.?(ft.valtype)) { return null; } + return objs[decl.field_offsets[index]]; + } + def getFieldV128(index: u32) -> (u64, u64) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.V128)) { return (u64.view(-1), u64.view(-1)); } + var r = Ref.at(bytes, decl.field_offsets[index]); + return (r.lo_val, r.hi_val); + } + def setFieldValue(index: u31, val: Value) { + var decl = StructDecl.!(this.decl); + match (decl.field_shifts[index]) { + -1 => { + objs[decl.field_offsets[index]] = Value.Ref.!(val).val; + } + 0 => { + bytes[decl.field_offsets[index]] = u8.view(Value.I32.!(val).val); + } + 1 => { + Ref.at(bytes, decl.field_offsets[index]).val = u16.view(Value.I32.!(val).val); + } + 2 => { + if (Value.I32.?(val)) { + Ref.at(bytes, decl.field_offsets[index]).val = Value.I32.!(val).val; + } else { + Ref.at(bytes, decl.field_offsets[index]).val = Value.F32.!(val).bits; + } + } + 3 => { + if (Value.I64.?(val)) { + Ref.at(bytes, decl.field_offsets[index]).val = Value.I64.!(val).val; + } else { + Ref.at(bytes, decl.field_offsets[index]).val = Value.F64.!(val).bits; + } + } + 4 => { + var r = Ref.at(bytes, decl.field_offsets[index]); + var pair = Value.V128.!(val); + r.lo_val = pair.low; + r.hi_val = pair.high; + } + } + } + def setFieldI32(index: u32, val: i32) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I32)) { + Ref.at(bytes, decl.field_offsets[index]).val = u32.view(val); + } + } + def setFieldI64(index: u32, val: i64) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I64)) { + Ref.at(bytes, decl.field_offsets[index]).val = u64.view(val); + } + } + def setFieldI8(index: u32, val: i8) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I8) { + bytes[decl.field_offsets[index]] = u8.view(val); + } + } + def setFieldI16(index: u32, val: i16) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I16) { + Ref.at(bytes, decl.field_offsets[index]).val = val; + } + } + def setFieldF32(index: u32, val: float) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F32)) { + Ref.at(bytes, decl.field_offsets[index]).val = val; + } + } + def setFieldF64(index: u32, val: double) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F64)) { + Ref.at(bytes, decl.field_offsets[index]).val = val; + } + } + def setFieldRef(index: u32, val: Object) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || ValueType.Ref.?(ft.valtype)) { + objs[decl.field_offsets[index]] = val; + } + } + def setFieldV128(index: u32, low: u64, high: u64) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.V128)) { + var r = Ref.at(bytes, decl.field_offsets[index]); + r.lo_val = low; + r.hi_val = high; + } + } +} + +// superclass of array representations; defines getter/setter interface +class HeapArrayGeneric extends HeapObject { + new(decl: ArrayDecl) super(decl) { } + + def length() -> int; + + // get whole array - useful for copy operations, etc. + def getArray() -> Array; + + def getValues() -> Array { + var vals = Array.new(length()); + for (i < length()) { + vals[i] = getValue(u32.!(i)); + } + return vals; + } + + def getValue(index: u32) -> Value; + def setValue(index: u32, val: Value); + + def getI32(index: u32) -> i32; + def setI32(index: u32, val: i32); + + def getI31(index: u32) -> i31; + def setI31(index: u32, val: i31); + + def getI64(index: u32) -> i64; + def setI64(index: u32, val: i64); + + def getI8(index: u32) -> i8; + def setI8(index: u32, val: i8); + + def getI16(index: u32) -> i16; + def setI16(index: u32, val: i16); + + def getF32(index: u32) -> float; + def setF32(index: u32, val: float); + + def getF64(index: u32) -> double; + def setF64(index: u32, val: double); + + def getRef(index: u32) -> Object; + def setRef(index: u32, val: Object); + + def getV128(index: u32) -> (u64, u64); + def setV128(index: u32, low: u64, high: u64); + + def getU32(index: u32) -> u32; + def getU64(index: u32) -> u64; + def getU8(index: u32) -> u8; + def getU16(index: u32) -> u16; +} + +// Original Array representation +// Note: bounds checks will already have been done +class HeapArrayValue extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getArray() -> Array { + return Array.!(vals); + } + def getValues() -> Array { + return vals; + } + + def getValue(index: u32) -> Value { + return vals[index]; + } + def setValue(index: u32, val: Value) { + vals[index] = val; + } + def getI32(index: u32) -> i32 { + return i32.view(Value.I32.!(vals[index]).val); + } + def setI32(index: u32, val: i32) { + vals[index] = Value.I32(u32.view(val)); + } + def getI31(index: u32) -> i31 { + return i31.view(Value.I31.!(vals[index]).val); + } + def setI31(index: u32, val: i31) { + vals[index] = Value.I31(u31.view(val)); + } + def getI64(index: u32) -> i64 { + return i64.view(Value.I64.!(vals[index]).val); + } + def setI64(index: u32, val: i64) { + vals[index] = Value.I64(u64.view(val)); + } + def getI8(index: u32) -> i8 { + return i8.view(Value.I32.!(vals[index]).val); + } + def setI8(index: u32, val: i8) { + vals[index] = Value.I32(u32.view(val)); + } + def getI16(index: u32) -> i16 { + return i16.view(Value.I32.!(vals[index]).val); + } + def setI16(index: u32, val: i16) { + vals[index] = Value.I32(u32.view(val)); + } + def getF32(index: u32) -> float { + return float.view(Value.F32.!(vals[index]).bits); + } + def setF32(index: u32, val: float) { + vals[index] = Value.F32(u32.view(val)); + } + def getF64(index: u32) -> double { + return double.view(Value.F64.!(vals[index]).bits); + } + def setF64(index: u32, val: double) { + vals[index] = Value.F64(u64.view(val)); + } + def getRef(index: u32) -> Object { + return Value.Ref.!(vals[index]).val; + } + def setRef(index: u32, val: Object) { + vals[index] = Value.Ref(val); + } + def getV128(index: u32) -> (u64, u64) { + var val = Value.V128.!(vals[index]); + return (val.low, val.high); + } + def setV128(index: u32, low: u64, high: u64) { + vals[index] = Value.V128(low, high); + } +} + +// Paired array representation: an Array is used for reference arrays, +// and an Array for primitive arrays; the unused array will be null. +class HeapArrayPair extends HeapArrayGeneric { + def objs: Array; + def bytes: Array; + def shift: byte; // the shift amount for indexing the byte array, etc. + new(decl: ArrayDecl, objs, bytes, shift) super(decl) { } + + def length() -> int { + // Check shift to decide which array: shift == OBJS_SHIFT means Ref array + if (shift == Values.OBJS_SHIFT) { + return objs.length; + } else { + return bytes.length >> shift; + } + } + + def getValue(index: u32) -> Value { + var d = ArrayDecl.!(decl); + match (d.elem_types[0].pack) { + UNPACKED => { + match (d.elem_types[0].valtype) { + I32 => return Value.I32(getU32(index)); + I64 => return Value.I64(getU64(index)); + F32 => return Value.F32(getU32(index)); + F64 => return Value.F64(getU64(index)); + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + return Value.I31(u31.view(getI31(index))); + } else { + match (getRef(index)) { + x: ObjectI31 => return Value.I31(x.val); + x: Object => return Value.Ref(x); + _ => return Values.REF_NULL; + } + } + } + V128 => { + var v128 = getV128(index); + return Value.V128(v128.0, v128.1); + } + _ => { + // This case should not happen + return Value.Ref(null); + } + } + } + PACKED_I8 => return Value.I32(u32.view(i32.!(getI8(index)))); + PACKED_I16 => return Value.I32(u32.view(i32.!(getI16(index)))); + } + } + def setValue(index: u32, val: Value) { + var d = ArrayDecl.!(decl); + match (d.elem_types[0].pack) { + UNPACKED => { + match (d.elem_types[0].valtype) { + I32 => setI32(index, i32.view(Value.I32.!(val).val)); + I64 => setI64(index, i64.view(Value.I64.!(val).val)); + F32 => setF32(index, float.view(Value.F32.!(val).bits)); + F64 => setF64(index, double.view(Value.F64.!(val).bits)); + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var uv: i31; + match (val) { + I31(v) => uv = i31.!(v); + _ => ; + } + setI31(index, uv); + } else { + var obj: Object; + match (val) { + Ref(o) => obj = o; + I31(v) => obj = ObjectI31.new(v); + _ => obj = null; + } + setRef(index, obj); + } + } + V128 => { + var v128 = Value.V128.!(val); + setV128(index, v128.low, v128.high); + } + _ => ; // This case should not happen + } + } + PACKED_I8 => setI8(index, i8.view(Value.I32.!(val).val)); + PACKED_I16 => setI16(index, i16.view(Value.I32.!(val).val)); + } + } + + def getI32(index: u32) -> i32 { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I32)) { + var offset = int.!(index << 2); + return Ref.at(bytes, offset).val; + } else { + return -1; + } + } + def setI32(index: u32, val: i32) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I32)) { + var offset = int.!(index << 2); + Ref.at(bytes, offset).val = val; + } + } + def getI31(index: u32) -> i31 { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.Ref(false, HeapType.I31))) { + var offset = int.!(index << 2); + return Ref.at(bytes, offset).val; + } else { + return -1; + } + } + def setI31(index: u32, val: i31) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.Ref(false, HeapType.I31))) { + var offset = int.!(index << 2); + Ref.at(bytes, offset).val = val; + } + } + def getI64(index: u32) -> i64 { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I64)) { + var offset = int.!(index << 3); + return Ref.at(bytes, offset).val; + } else { + return -1; + } + } + def setI64(index: u32, val: i64) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I64)) { + var offset = int.!(index << 3); + Ref.at(bytes, offset).val = val; + } + } + + def getI8(index: u32) -> i8 { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I8) { + var offset = int.!(index); + return i8.view(bytes[offset]); + } else { + return -1; + } + } + def setI8(index: u32, val: i8) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I8) { + var offset = int.!(index); + bytes[offset] = u8.view(val); + } + } + def getI16(index: u32) -> i16 { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I16) { + var offset = int.!(index << 1); + return Ref.at(bytes, offset).val; + } else { + return -1; + } + } + def setI16(index: u32, val: i16) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I16) { + var offset = int.!(index << 1); + Ref.at(bytes, offset).val = val; + } + } + def getF32(index: u32) -> float { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F32)) { + var offset = int.!(index << 2); + return Ref.at(bytes, offset).val; + } else { + return -1f; + } + } + def setF32(index: u32, val: float) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F32)) { + var offset = int.!(index << 2); + Ref.at(bytes, offset).val = val; + } + } + def getF64(index: u32) -> double { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F64)) { + var offset = int.!(index << 3); + return Ref.at(bytes, offset).val; + } else { + return -1d; + } + } + def setF64(index: u32, val: double) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F64)) { + var offset = int.!(index << 3); + Ref.at(bytes, offset).val = val; + } + } + def getRef(index: u32) -> Object { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || ValueType.Ref.?(etype.valtype)) { + return objs[index]; + } else { + return null; + } + } + def setRef(index: u32, val: Object) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || ValueType.Ref.?(etype.valtype)) { + objs[index] = val; + } + } + def getV128(index: u32) -> (u64, u64) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || etype.valtype == ValueType.V128) { + var offset = int.!(index << 4); + var r = Ref.at(bytes, offset); + return (r.lo_val, r.hi_val); + } else { + return (u64.view(-1), u64.view(-1)); + } + } + def setV128(index: u32, low: u64, high: u64) { + var d = ArrayDecl.!(decl); + var etype = d.elem_types[0]; + if (Values.NO_CHECKS || etype.valtype == ValueType.V128) { + var offset = int.!(index << 4); + var r = Ref.at(bytes, offset); + r.lo_val = low; + r.hi_val = high; + } + } + def getU32(index: u32) -> u32 { + if (Values.NO_CHECKS || shift == 2) { + var offset = int.!(index << 2); + return Ref.at(bytes, offset).val; + } else { + return u32.view(-1); + } + } + def getU64(index: u32) -> u64 { + if (Values.NO_CHECKS || shift == 3) { + var offset = int.!(index << 3); + return Ref.at(bytes, offset).val; + } else { + return u64.view(-1); + } + } + def getU8(index: u32) -> u8 { + if (Values.NO_CHECKS || shift == 0) { + var offset = int.!(index); + return bytes[offset]; + } else { + return u8.view(-1); + } + } + def getU16(index: u32) -> u16 { + if (Values.NO_CHECKS || shift == 1) { + var offset = int.!(index << 1); + return Ref.at(bytes, offset).val; + } else { + return u16.view(-1); + } + } +} + + +// Implementations of arrays of specific types +class HeapArrayI32 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u32.view(Value.I32.!(val).val); + } + def getI32(index: u32) -> i32 { + return i32.view(vals[index]); + } + def setI32(index: u32, val: i32) { + vals[index] = u32.view(val); + } +} + +class HeapArrayI31 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I31(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u31.view(Value.I31.!(val).val); + } + def getI31(index: u32) -> i31 { + return i31.view(vals[index]); + } + def setI31(index: u32, val: i31) { + vals[index] = u31.view(val); + } } -class HeapArray extends HeapObject { - new(decl: ArrayDecl, vals: Array) super(decl, vals) { } // XXX: unboxed prim arrays + +class HeapArrayI64 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I64(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u64.view(Value.I64.!(val).val); + } + def getI64(index: u32) -> i64 { + return i64.view(vals[index]); + } + def setI64(index: u32, val: i64) { + vals[index] = u64.view(val); + } +} + +class HeapArrayI8 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u8.view(Value.I32.!(val).val); + } + def getI8(index: u32) -> i8 { + return i8.view(vals[index]); + } + def setI8(index: u32, val: i8) { + vals[index] = u8.view(val); + } +} + +class HeapArrayI16 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u16.view(Value.I32.!(val).val); + } + def getI16(index: u32) -> i16 { + return i16.view(vals[index]); + } + def setI16(index: u32, val: i16) { + vals[index] = u16.view(val); + } +} + +class HeapArrayF32 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.F32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u32.view(Value.F32.!(val).bits); + } + def getF32(index: u32) -> float { + return if(index >= 0 && index < vals.length, float.view(vals[index]), -1); + } + def setF32(index: u32, val: float) { + if (index >= 0 && index < vals.length) { + vals[index] = u32.view(val); + } + } +} + +class HeapArrayF64 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.F64(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u64.view(Value.F64.!(val).bits); + } + def getF64(index: u32) -> double { + return double.view(vals[index]); + } + def setF64(index: u32, val: double) { + vals[index] = u64.view(val); + } +} + +class HeapArrayRef extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + match (vals[index]) { + x: ObjectI31 => return Value.I31(x.val); + x: Object => return Value.Ref(x); + _ => return Values.REF_NULL; + } + } + def setValue(index: u32, val: Value) { + var obj: Object; + match (val) { + Ref(v) => obj = v; + I31(v) => obj = ObjectI31.new(v); + _ => ; + } + vals[index] = obj; + } + def getRef(index: u32) -> Object { + return vals[index]; + } + def setRef(index: u32, val: Object) { + vals[index] = val; + } +} + +// A v128 uses two adjacent u64 entries in the vals array +class HeapArrayV128 extends HeapArrayGeneric { + def vals: Array; // The array format: [low 0, high 0, low 1, high 1, ...] + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length >> 1; } + + def getValue(index: u32) -> Value { + var idx = index << 1; + return Value.V128(vals[idx], vals[idx+1]); + } + def setValue(index: u32, val: Value) { + var idx = index << 1; + var v128 = Value.V128.!(val); + vals[idx] = u64.view(v128.low); + vals[idx+1] = u64.view(v128.high); + } + def getV128(index: u32) -> (u64, u64) { + var idx = index << 1; + return (vals[idx], vals[idx+1]); + } + def setV128(index: u32, low: u64, high: u64) { + var idx = index << 1; + vals[idx] = low; + vals[idx+1] = high; + } } // Utilities associated with values. @@ -53,7 +925,8 @@ component Values { def FUNCREF_NULL = Value.Ref(null); def REF_NULL = FUNCREF_NULL; def NONE = Array.new(0); - def NO_SUPERS = Array.new(0); + def OBJS_SHIFT = 255u8; + def NO_CHECKS = false; def render(v: Value, buf: StringBuilder) -> StringBuilder { match (v) { @@ -64,7 +937,7 @@ component Values { var id = if(x.decl == null, -1, x.decl.heaptype_index); buf.put1("", id); } - x: HeapArray => { + x: HeapArrayGeneric => { var id = if(x.decl == null, -1, x.decl.heaptype_index); buf.put1("", id); } @@ -95,13 +968,13 @@ component Values { } def isData(v: Value) -> bool { match (v) { - Ref(o) => return HeapObject.?(o) || HeapArray.?(o); + Ref(o) => return HeapObject.?(o) || HeapArrayGeneric.?(o); _ => return false; } } def isArray(v: Value) -> bool { match (v) { - Ref(o) => return HeapArray.?(o); + Ref(o) => return HeapArrayGeneric.?(o); _ => return false; } } diff --git a/src/engine/WasmStack.v3 b/src/engine/WasmStack.v3 index 328daa4d7..a3523ac2c 100644 --- a/src/engine/WasmStack.v3 +++ b/src/engine/WasmStack.v3 @@ -8,6 +8,7 @@ class ExecStack { def popu() -> u32; def popl() -> i64; def popw() -> u64; + def pops() -> (u64, u64); def popf() -> float; def popd() -> double; def popr() -> Value.Ref; @@ -20,6 +21,7 @@ class ExecStack { def pushu(val: u32); def pushl(val: i64); def pushw(val: u64); + def pushs(low: u64, high: u64); def pushf(val: float); def pushd(val: double); def pushz(val: bool); @@ -39,11 +41,11 @@ class ExecStack { def popa(size: SizeConstraint) -> u64 { return if(size.is64, popw(), popu()); } - def popStruct() -> HeapStruct { - return HeapStruct.!(popObject()); + def popStruct() -> HeapStructGeneric { + return HeapStructGeneric.!(popObject()); } - def popArray() -> HeapArray { - return HeapArray.!(popObject()); + def popArray() -> HeapArrayGeneric { + return HeapArrayGeneric.!(popObject()); } def popFunction() -> Function { return Function.!(popObject()); diff --git a/src/engine/compiler/MacroAssembler.v3 b/src/engine/compiler/MacroAssembler.v3 index 36513c8c5..5a9ca0fa4 100644 --- a/src/engine/compiler/MacroAssembler.v3 +++ b/src/engine/compiler/MacroAssembler.v3 @@ -27,9 +27,13 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { var embeddedRefOffsets: Vector; private var offsets: V3Offsets; + // define in subclass to support tracing + def setTargetTracing(outf: Range -> void) { } + new() { unimplemented = fatalUnimplemented; newTrapLabel = makeSharedTrapLabel; + if (Trace.asm) setTargetTracing(System.out.putr); } def getOffsets() -> V3Offsets { @@ -142,9 +146,16 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_v3_Array_elem_r_ri(kind: ValueKind, dst: Reg, array: Reg, index: int) { emit_mov_r_m(kind, dst, MasmAddr(array, getOffsets().Array_contents + index * scaleOf(kind))); } - def emit_v3_Array_length_r_r(dst: Reg, array: Reg) { - emit_mov_r_m(ValueKind.I32, dst, MasmAddr(array, getOffsets().Array_length)); + def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, dst: Reg) { + // Generic code, may be overridden to be more efficient + emit_shlw_r_i(index, logScaleOf(kind)); + emit_addw_r_r(array, index); + emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents), dst); + } + def emit_v3_Array_elem_ri_r(kind: ValueKind, array: Reg, index: int, dst: Reg) { + emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents + index * scaleOf(kind)), dst); } + def emit_v3_Array_length_r_r(dst: Reg, array: Reg); def emit_v3_Array_bounds_check_rr(array: Reg, index: Reg, oob_label: MasmLabel) { // Generic code, may be overridden to be more efficient var scratch = regConfig.scratch; @@ -155,6 +166,18 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_v3_HeapArray_vals_r_r(dst: Reg, ptr: Reg) { emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArray_vals)); } + def emit_v3_HeapArrayPair_objs_r_r(dst: Reg, ptr: Reg) { + emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArrayPair_objs)); + } + def emit_v3_HeapArrayPair_bytes_r_r(dst: Reg, ptr: Reg) { + emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArrayPair_bytes)); + } + def emit_v3_HeapArrayPair_shift_r_r(dst: Reg, ptr: Reg) { + emit_movbsx_r_m(ValueKind.I32, dst, MasmAddr(ptr, getOffsets().HeapArrayPair_shift)); + } + def emit_v3_HeapArrayI32_vals_r_r(dst: Reg, ptr: Reg) { + emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArrayI32_vals)); + } def emit_v3_Instance_dropped_elems_r_r(dst: Reg, ptr: Reg) { emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().Instance_dropped_elems)); } @@ -247,6 +270,9 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_loaddzx_r_r_r_i(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32); def emit_load_r_r_r_i(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32); + def emit_loadwsx_r_r_r_i_s(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32, scale: byte); + def emit_loadwzx_r_r_r_i_s(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32, scale: byte); + def emit_storeb_r_r_r_i(kind: ValueKind, val: Reg, base: Reg, index: Reg, offset: u32); def emit_storew_r_r_r_i(kind: ValueKind, val: Reg, base: Reg, index: Reg, offset: u32); def emit_store_r_r_r_i(kind: ValueKind, val: Reg, base: Reg, index: Reg, offset: u32); @@ -273,6 +299,7 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_mov_r_Object(reg: Reg, obj: Object); def emit_mov_r_Function(reg: Reg, func: Function); def emit_mov_r_Instance(reg: Reg, instance: Instance); + def emit_movbsx_r_m(kind: ValueKind, reg: Reg, addr: MasmAddr); def emit_mov_m_r(kind: ValueKind, addr: MasmAddr, reg: Reg); def emit_mov_m_i(addr: MasmAddr, val: int); diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index 57e187f78..9dccb49b2 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1661,19 +1661,111 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } def visit_STRUCT_GET(struct_index: u31, field_index: u31) { var decl = StructDecl.!(module.heaptypes[struct_index]); - emit_call_runtime_op2n(Opcode.STRUCT_GET, struct_index, field_index, 1, [decl.field_types[field_index].valtype], true); + var stg_type = decl.field_types[field_index]; + var etype = stg_type.valtype; + if ((stg_type.pack != Packedness.UNPACKED) || + (ValueType.Ref.?(etype) && ValueType.Ref.!(etype).heap == HeapType.I31)) { + emit_call_runtime_op2n(Opcode.STRUCT_GET, struct_index, field_index, 1, [etype], true); + return; + } + match (ObjTuning.structMode) { + Pair => { + var offset = decl.field_offsets[field_index]; + var kind = ValueTypes.kind(etype); + var str = popReg().reg; + var dst = allocRegTos(kind); + var tmp = allocTmp(ValueKind.REF); + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(str, 0, label); + if (ValueType.Ref.?(etype)) { + masm.emit_v3_HeapArrayPair_objs_r_r(tmp, str); + } else { + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + } + masm.emit_mov_r_m(kind, dst, MasmAddr(tmp, offset + masm.getOffsets().Array_contents)); + state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); + } + _ => { + emit_call_runtime_op2n(Opcode.STRUCT_GET, struct_index, field_index, 1, [etype], true); + } + } } def visit_STRUCT_GET_S(struct_index: u31, field_index: u31) { var decl = StructDecl.!(module.heaptypes[struct_index]); - emit_call_runtime_op2n(Opcode.STRUCT_GET_S, struct_index, field_index, 1, [decl.field_types[field_index].valtype], true); + match (ObjTuning.structMode) { + Pair => { + var offset = decl.field_offsets[field_index]; + var str = popReg().reg; + var dst = allocRegTos(ValueKind.I32); + var tmp = allocTmp(ValueKind.REF); + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(str, 0, label); + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + match (decl.field_types[field_index].pack) { + PACKED_I8 => masm.emit_loadbsx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); + PACKED_I16 => masm.emit_loadwsx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); + _ => ; + } + state.push(KIND_I32 | IN_REG, dst, 0); + } + _ => { + emit_call_runtime_op2n(Opcode.STRUCT_GET_S, struct_index, field_index, 1, [decl.field_types[field_index].valtype], true); + } + } } def visit_STRUCT_GET_U(struct_index: u31, field_index: u31) { var decl = StructDecl.!(module.heaptypes[struct_index]); - emit_call_runtime_op2n(Opcode.STRUCT_GET_U, struct_index, field_index, 1, [decl.field_types[field_index].valtype], true); + match (ObjTuning.structMode) { + Pair => { + var offset = decl.field_offsets[field_index]; + var str = popReg().reg; + var dst = allocRegTos(ValueKind.I32); + var tmp = allocTmp(ValueKind.REF); + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(str, 0, label); + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + match (decl.field_types[field_index].pack) { + PACKED_I8 => masm.emit_loadbzx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); + PACKED_I16 => masm.emit_loadwzx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); + _ => ; + } + state.push(KIND_I32 | IN_REG, dst, 0); + } + _ => { + emit_call_runtime_op2n(Opcode.STRUCT_GET_U, struct_index, field_index, 1, [decl.field_types[field_index].valtype], true); + } + } } def visit_STRUCT_SET(struct_index: u31, field_index: u31) { var decl = StructDecl.!(module.heaptypes[struct_index]); - emit_call_runtime_op2n(Opcode.STRUCT_SET, struct_index, field_index, 2, ValueTypes.NONE, true); + var stg_type = decl.field_types[field_index]; + var etype = stg_type.valtype; + if ((stg_type.pack != Packedness.UNPACKED) || + (ValueType.Ref.?(etype) && ValueType.Ref.!(etype).heap == HeapType.I31)) { + emit_call_runtime_op2n(Opcode.STRUCT_SET, struct_index, field_index, 2, ValueTypes.NONE, true); + return; + } + match (ObjTuning.structMode) { + Pair => { + var offset = decl.field_offsets[field_index]; + var kind = ValueTypes.kind(etype); + var val = popReg().reg; + var str = popReg().reg; + var dst = allocRegTos(kind); + var tmp = allocTmp(ValueKind.REF); + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(str, 0, label); + if (ValueType.Ref.?(etype)) { + masm.emit_v3_HeapArrayPair_objs_r_r(tmp, str); + } else { + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + } + masm.emit_mov_m_r(kind, MasmAddr(tmp, offset + masm.getOffsets().Array_contents), val); + } + _ => { + emit_call_runtime_op2n(Opcode.STRUCT_SET, struct_index, field_index, 1, ValueTypes.NONE, true); + } + } } def visit_ARRAY_NEW(ht_index: u31) { emit_call_runtime_op1n(Opcode.ARRAY_NEW, ht_index, 2, ValueTypes.ONE_ARRAYREF_TYPE, true); @@ -1690,30 +1782,182 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl def visit_ARRAY_NEW_ELEM(ht_index: u31, elem_index: u31) { emit_call_runtime_op2n(Opcode.ARRAY_NEW_ELEM, ht_index, elem_index, 2, ValueTypes.ONE_ARRAYREF_TYPE, true); } + def visit_ARRAY_GET_Pair(kind: ValueKind, arr: Reg, idx: Reg); def visit_ARRAY_GET(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); - emit_call_runtime_op1n(Opcode.ARRAY_GET, ht_index, 2, [decl.elem_types[0].valtype], true); + var stg_type = decl.elem_types[0]; + var etype = stg_type.valtype; + var kind = ValueTypes.kind(etype); + // PACKED forms should not get here, but default to run-time call, in case + if ((stg_type.pack != Packedness.UNPACKED) || + (ValueType.Ref.?(etype) && ValueType.Ref.!(etype).heap == HeapType.I31)) { + emit_call_runtime_op1n(Opcode.ARRAY_GET, ht_index, 2, [decl.elem_types[0].valtype], true); + return; + } + match (ObjTuning.arrayMode) { + Pair => { + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + visit_ARRAY_GET_Pair(kind, arr, idx); + } + Typed => { + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + var tmp = allocTmp(ValueKind.REF); + masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + var dst = allocRegTos(kind); + masm.emit_v3_Array_elem_r_rr(kind, dst, tmp, idx); + state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); + } + _ => { + // Original format, and any other added formats without explicit coverage, use run-time + emit_call_runtime_op1n(Opcode.ARRAY_GET, ht_index, 2, [decl.elem_types[0].valtype], true); + } + } } + def visit_ARRAY_GET_S_Pair(pack: Packedness, arr: Reg, idx: Reg); def visit_ARRAY_GET_S(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); - emit_call_runtime_op1n(Opcode.ARRAY_GET_S, ht_index, 2, [decl.elem_types[0].valtype], true); + var stg_type = decl.elem_types[0]; + var etype = stg_type.valtype; + var kind = ValueTypes.kind(etype); + match (ObjTuning.arrayMode) { + Pair => { + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + visit_ARRAY_GET_S_Pair(stg_type.pack, arr, idx); + } + Typed => { + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + var tmp = allocTmp(ValueKind.REF); + masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + var dst = allocRegTos(ValueKind.I32); + match (stg_type.pack) { + PACKED_I8 => masm.emit_loadbsx_r_r_r_i(kind, dst, tmp, idx, u32.!(masm.getOffsets().Array_contents)); + PACKED_I16 => masm.emit_loadwsx_r_r_r_i_s(kind, dst, tmp, idx, u32.!(masm.getOffsets().Array_contents), 2); + _ => ; + } + state.push(SpcConsts.KIND_I32 | IN_REG, dst, 0); + } + _ => { + emit_call_runtime_op1n(Opcode.ARRAY_GET_S, ht_index, 2, [decl.elem_types[0].valtype], true); + } + } } + def visit_ARRAY_GET_U_Pair(pack: Packedness, arr: Reg, idx: Reg); def visit_ARRAY_GET_U(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); - emit_call_runtime_op1n(Opcode.ARRAY_GET_U, ht_index, 2, [decl.elem_types[0].valtype], true); + var stg_type = decl.elem_types[0]; + var etype = stg_type.valtype; + var kind = ValueTypes.kind(etype); + match (ObjTuning.arrayMode) { + Pair => { + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + visit_ARRAY_GET_U_Pair(stg_type.pack, arr, idx); + } + Typed => { + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + var tmp = allocTmp(ValueKind.REF); + masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + var dst = allocRegTos(ValueKind.I32); + match (stg_type.pack) { + PACKED_I8 => masm.emit_loadbzx_r_r_r_i(kind, dst, tmp, idx, u32.!(masm.getOffsets().Array_contents)); + PACKED_I16 => masm.emit_loadwzx_r_r_r_i_s(kind, dst, tmp, idx, u32.!(masm.getOffsets().Array_contents), 2); + _ => ; + } + state.push(SpcConsts.KIND_I32 | IN_REG, dst, 0); + } + _ => { + emit_call_runtime_op1n(Opcode.ARRAY_GET_U, ht_index, 2, [decl.elem_types[0].valtype], true); + } + } } + def visit_ARRAY_SET_Pair(kind: ValueKind, arr: Reg, idx: Reg, val: Reg); // requires target specific code sequences def visit_ARRAY_SET(ht_index: u31) { - emit_call_runtime_op1n(Opcode.ARRAY_SET, ht_index, 3, ValueTypes.NONE, true); + var decl = ArrayDecl.!(module.heaptypes[ht_index]); + var stg_type = decl.elem_types[0]; + var etype = stg_type.valtype; + var kind = ValueTypes.kind(etype); + // PACKED forms may get here, but use run-time call for the moment + if ((stg_type.pack != Packedness.UNPACKED) || + (ValueType.Ref.?(etype) && ValueType.Ref.!(etype).heap == HeapType.I31)) { + emit_call_runtime_op1n(Opcode.ARRAY_SET, ht_index, 3, ValueTypes.NONE, true); + return; + } + match (ObjTuning.arrayMode) { + Pair => { + var val = popReg().reg; + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + visit_ARRAY_SET_Pair(kind, arr, idx, val); + } + Typed => { + var val = popReg().reg; + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + var tmp = allocTmp(ValueKind.REF); + masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + masm.emit_shlw_r_i(idx, masm.logScaleOf(kind)); + masm.emit_addw_r_r(tmp, idx); + masm.emit_mov_m_r(kind, MasmAddr(tmp, masm.getOffsets().Array_contents), val); + } + _ => { + // Original format, and any other added formats without explicit coverage, use run-time + emit_call_runtime_op1n(Opcode.ARRAY_SET, ht_index, 3, ValueTypes.NONE, true); + } + } } + def visit_ARRAY_LEN_Pair(reg: Reg); // requires target specific code sequences def visit_ARRAY_LEN() { - var sv = popReg(); - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(sv.reg, 0, label); - var tmp = allocTmp(ValueKind.REF); - var dst = allocRegTos(ValueKind.I32); - masm.emit_v3_HeapArray_vals_r_r(tmp, sv.reg); - masm.emit_v3_Array_length_r_r(dst, tmp); - state.push(KIND_I32 | IN_REG, dst, 0); + match (ObjTuning.arrayMode) { + Original, Typed => { + var sv = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(sv, 0, label); + var tmp = allocTmp(ValueKind.REF); + var dst = allocRegTos(ValueKind.I32); + masm.emit_v3_HeapArray_vals_r_r(tmp, sv); + masm.emit_v3_Array_length_r_r(dst, tmp); + state.push(KIND_I32 | IN_REG, dst, 0); + } + Pair => { + var sv = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(sv, 0, label); + visit_ARRAY_LEN_Pair(sv); + } + // use as an easy handler if new array modes are added in the future + // _ => { + // emit_call_runtime_op0n(Opcode.ARRAY_LEN, 1, [ValueType.I32], true); + // } + } } def visit_ARRAY_FILL(ht_index: u31) { emit_call_runtime_op1n(Opcode.ARRAY_FILL, ht_index, 4, ValueTypes.NONE, true); @@ -1882,6 +2126,22 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var count = u32.!(op.sig.params.length); emit_call_runtime_op2n(op, arg1, arg2, count, op.sig.results, canTrap); } + def emit_call_runtime_op0n(op: Opcode, args: u32, results: Array, canTrap: bool) { + state.emitSaveAll(resolver, runtimeSpillMode); + emit_compute_vsp(regs.vsp, state.sp); + masm.emit_store_curstack_vsp(regs.vsp); + masm.emit_get_curstack(regs.runtime_arg0); + masm.emit_v3_set_X86_64Stack_rsp_r_r(regs.runtime_arg0, regs.sp); + masm.emit_push_X86_64Stack_rsp_r_r(regs.runtime_arg0); + emit_load_instance(regs.runtime_arg1); + masm.emit_call_runtime_op(op); + masm.emit_get_curstack(regs.scratch); + masm.emit_pop_X86_64Stack_rsp_r_r(regs.scratch); + dropN(args); + for (t in results) state.push(typeToKindFlags(t) | TAG_STORED | IS_STORED, NO_REG, 0); + emit_reload_regs(); + if (!runtimeSpillMode.free_regs) state.emitRestoreAll(resolver); + } def emit_call_runtime_op1n(op: Opcode, arg1: u31, args: u32, results: Array, canTrap: bool) { state.emitSaveAll(resolver, runtimeSpillMode); emit_compute_vsp(regs.vsp, state.sp); diff --git a/src/engine/v3/V3Interpreter.v3 b/src/engine/v3/V3Interpreter.v3 index 01b86304c..af7d6ae94 100644 --- a/src/engine/v3/V3Interpreter.v3 +++ b/src/engine/v3/V3Interpreter.v3 @@ -416,7 +416,6 @@ class V3Interpreter extends WasmStack { } LOCAL_GET => { var index = codeptr.read_uleb32(); - var val = values.elems[frame.fp + int.!(index)]; push(values.elems[frame.fp + int.!(index)]); } LOCAL_SET => { @@ -808,7 +807,7 @@ class V3Interpreter extends WasmStack { ARRAY_LEN => { var obj = popArray(); if (obj == null) trap(TrapReason.NULL_DEREF); - else push(Value.I32(u32.view(obj.vals.length))); + else push(Value.I32(u32.view(obj.length()))); } ARRAY_FILL => { var array_index = codeptr.read_uleb31(); @@ -850,12 +849,26 @@ class V3Interpreter extends WasmStack { I31_GET_S => { var v = pop(); if (v == Values.REF_NULL) trap(TrapReason.NULL_DEREF); - else pushi(i31.view(Value.I31.!(v).val)); +// else pushi(i31.view(Value.I31.!(v).val)); + else { + match (v) { + I31(val) => pushi(i31.view(val)); + Ref(obj) => pushi(i31.view(ObjectI31.!(obj).val)); + _ => pushi(0); + } + } } I31_GET_U => { var v = pop(); if (v == Values.REF_NULL) trap(TrapReason.NULL_DEREF); - else pushu(Value.I31.!(v).val); +// else pushu(u31.view(Value.I31.!(v).val)); + else { + match (v) { + I31(val) => pushu(val); + Ref(obj) => pushu(ObjectI31.!(obj).val); + _ => pushu(0); + } + } } REF_TEST, REF_TEST_NULL => { @@ -1778,9 +1791,16 @@ class V3Interpreter extends WasmStack { def popd() -> double { return Values.unbox_d(values.pop()); } def pops() -> (u64, u64) { return Values.unbox_s(values.pop()); } def popr() -> Value.Ref { return Value.Ref.!(values.pop()); } // TODO: i31 - def popObject() -> Object { return Value.Ref.!(values.pop()).val; } - def popArray() -> HeapArray { return HeapArray.!(Value.Ref.!(values.pop()).val); } - def popStruct() -> HeapStruct { return HeapStruct.!(Value.Ref.!(values.pop()).val); } + def popObject() -> Object { + var v = values.pop(); + match (v) { + Ref(val) => return val; + I31(val) => return ObjectI31.new(val); + _ => return null; + } + } + def popArray() -> HeapArrayGeneric { return HeapArrayGeneric.!(Value.Ref.!(values.pop()).val); } + def popStruct() -> HeapStructGeneric { return HeapStructGeneric.!(Value.Ref.!(values.pop()).val); } def popContinuation() -> Continuation { return Continuation.!(Value.Ref.!(values.pop()).val); } def push(val: Value) { values.push(val); } def pushi(val: i32) { values.push(Value.I32(u32.view(val))); } diff --git a/src/engine/x86-64/V3Offsets.v3 b/src/engine/x86-64/V3Offsets.v3 index b0eb06a08..6d7a9fd01 100644 --- a/src/engine/x86-64/V3Offsets.v3 +++ b/src/engine/x86-64/V3Offsets.v3 @@ -16,7 +16,9 @@ class V3Offsets { private def mem = NativeWasmMemory.new(null); private def vs = X86_64Stack.new(2u * 4096u); private def acc = X86_64FrameAccessor.new(vs, Pointer.NULL, decl); - private def ha = HeapArray.new(null, []); + private def ha = HeapArrayValue.new(null, []); + private def hp = HeapArrayPair.new(null, null, null, 0); + private def hi = HeapArrayI32.new(null, []); private def cnt = CountProbe.new(); private def whamm_Probe = WhammProbe.new(null, []); private def cont = Continuation.new(null); @@ -77,6 +79,12 @@ class V3Offsets { def HeapArray_vals = int.view(Pointer.atField(ha.vals) - Pointer.atObject(ha)); + def HeapArrayPair_objs = int.view(Pointer.atField(hp.objs) - Pointer.atObject(hp)); + def HeapArrayPair_bytes = int.view(Pointer.atField(hp.bytes) - Pointer.atObject(hp)); + def HeapArrayPair_shift = int.view(Pointer.atField(hp.shift) - Pointer.atObject(hp)); + + def HeapArrayI32_vals = int.view(Pointer.atField(hi.vals) - Pointer.atObject(hi)); + def CountProbe_count = int.view(Pointer.atField(cnt.count) - Pointer.atObject(cnt)); def WhammProbe_trampoline = int.view(Pointer.atField(whamm_Probe.trampoline.spc_entry) - Pointer.atObject(whamm_Probe)); def WhammProbe_func = int.view(Pointer.atField(whamm_Probe.func) - Pointer.atObject(whamm_Probe)); diff --git a/src/engine/x86-64/X86_64Interpreter.v3 b/src/engine/x86-64/X86_64Interpreter.v3 index 4f782241a..e28dc2af8 100644 --- a/src/engine/x86-64/X86_64Interpreter.v3 +++ b/src/engine/x86-64/X86_64Interpreter.v3 @@ -2272,10 +2272,21 @@ class X86_64InterpreterGen(ic: X86_64InterpreterCode, w: DataWriter) { asm.movq_r_m(r_tmp0, vsph[-1].value); asm.q.cmp_r_i(r_tmp0, 0); asm.jc_rel_far(X86_64Conds.Z, newTrapLabel(TrapReason.NULL_DEREF)); - asm.movq_r_m(r_tmp0, r_tmp0.plus(offsets.HeapArray_vals)); - asm.movd_r_m(r_tmp0, r_tmp0.plus(offsets.Array_length)); - asm.movd_m_r(vsph[-1].value, r_tmp0); - genTagUpdate(BpTypeCode.I32.code); + match (ObjTuning.arrayMode) { + Original, Typed => { + asm.movq_r_m(r_tmp0, r_tmp0.plus(offsets.HeapArray_vals)); + asm.movd_r_m(r_tmp0, r_tmp0.plus(offsets.Array_length)); + asm.movd_m_r(vsph[-1].value, r_tmp0); + genTagUpdate(BpTypeCode.I32.code); + } + _ => { + masm.emit_get_curstack(xenv.tmp3); + saveCallerIVars(); + callRuntime(refRuntimeCall(RT.ARRAY_LEN), [r_tmp3, r_instance], false); + asm.movbzx_r_r(r_tmp0, Target.V3_RET_GPRS[0]); // XXX: restore just VSP and update first? + restoreCallerIVars(); + } + } endHandler(); } bindHandler(Opcode.REF_TEST); { diff --git a/src/engine/x86-64/X86_64MacroAssembler.v3 b/src/engine/x86-64/X86_64MacroAssembler.v3 index a83e961e8..75fd268bc 100644 --- a/src/engine/x86-64/X86_64MacroAssembler.v3 +++ b/src/engine/x86-64/X86_64MacroAssembler.v3 @@ -26,6 +26,9 @@ class X86_64MacroAssembler extends MacroAssembler { new(w, regConfig: RegConfig) super(Target.tagging, regConfig) { scratch = G(regConfig.scratch); } + def setTargetTracing(outf: Range -> void) { + asm.setTracing(outf); + } def printCodeBytes(sb: StringBuilder, from: u64, to: u64) { for (i = from; i < to; i++) { sb.put1("%x ", w.data[i]); @@ -108,9 +111,22 @@ class X86_64MacroAssembler extends MacroAssembler { V128 => asm.movdqu_s_m(X(dst), X86_64Addr.new(a, i, 16, getOffsets().Array_contents)); // TODO: can't scale by 16 } } + def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, dst: Reg) { + var a = G(array), i = G(index); + match (kind) { + I32 => asm.movd_m_r(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), G(dst)); + F32 => asm.movss_m_s(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), X(dst)); + I64, REF => asm.movq_m_r(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), G(dst)); + F64 => asm.movsd_m_s(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), X(dst)); + V128 => asm.movdqu_m_s(X86_64Addr.new(a, i, 16, getOffsets().Array_contents), X(dst)); // TODO: can't scale by 16 + } + } + def emit_v3_Array_length_r_r(dst: Reg, array: Reg) { + asm.q.movd_r_m(G(dst), X86_64Addr.new(G(array), null, 1, getOffsets().Array_length));; + } def emit_v3_Array_bounds_check_rr(array: Reg, index: Reg, oob_label: MasmLabel) { asm.d.cmp_r_m(G(index), X86_64Addr.new(G(array), null, 1, getOffsets().Array_length)); - asm.jc_rel_far(X86_64Conds.GE, X86_64MasmLabel.!(oob_label).label); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); } def emit_v3_Memory_start_r_r(dst: Reg, memobj: Reg) { asm.movq_r_m(G(dst), X86_64Addr.new(G(memobj), null, 1, getOffsets().NativeWasmMemory_start)); @@ -136,7 +152,6 @@ class X86_64MacroAssembler extends MacroAssembler { def emit_pop_X86_64Stack_rsp_r_r(stk: Reg) { asm.q.add_m_i(G(stk).plus(offsets.X86_64Stack_rsp), Pointer.SIZE); } - def emit_loadbsx_r_r_r_i(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32) { var t = handle_large_offset(index, offset); recordCurSourceLoc(); @@ -170,6 +185,16 @@ class X86_64MacroAssembler extends MacroAssembler { recordCurSourceLoc(); asm.q.movd_r_m(G(dst), X86_64Addr.new(G(base), t.0, 1, t.1)); } + def emit_loadwsx_r_r_r_i_s(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32, scale: byte) { + var t = handle_large_offset(index, offset); + recordCurSourceLoc(); + var x = if (kind == ValueKind.I64, asm.q, asm.d).movwsx_r_m(G(dst), X86_64Addr.new(G(base), t.0, scale, t.1)); + } + def emit_loadwzx_r_r_r_i_s(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32, scale: byte) { + var t = handle_large_offset(index, offset); + recordCurSourceLoc(); + var x = if (kind == ValueKind.I64, asm.q, asm.d).movwzx_r_m(G(dst), X86_64Addr.new(G(base), t.0, scale, t.1)); + } def emit_load_r_r_r_i(kind: ValueKind, dst: Reg, base: Reg, index: Reg, offset: u32) { var b = G(base), t = handle_large_offset(index, offset); recordCurSourceLoc(); @@ -251,6 +276,14 @@ class X86_64MacroAssembler extends MacroAssembler { V128 => asm.movdqu_s_m(X(reg), addr); } } + def emit_movbsx_r_m(kind: ValueKind, reg: Reg, ma: MasmAddr) { + var addr = A(ma); + match (kind) { + I32 => asm.d.movbsx_r_m(G(reg), addr); + I64 => asm.q.movbsx_r_m(G(reg), addr); + _ => ; + } + } def emit_mov_r_i(reg: Reg, val: int) { asm.movd_r_i(G(reg), val); } diff --git a/src/engine/x86-64/X86_64SinglePassCompiler.v3 b/src/engine/x86-64/X86_64SinglePassCompiler.v3 index 5022106be..d3521ba25 100644 --- a/src/engine/x86-64/X86_64SinglePassCompiler.v3 +++ b/src/engine/x86-64/X86_64SinglePassCompiler.v3 @@ -1006,6 +1006,118 @@ class X86_64SinglePassCompiler extends SinglePassCompiler { asm.mfence(); } + def visit_ARRAY_GET_S_Pair(pack: Packedness, arr: Reg, idx: Reg) { + var tmp = allocTmp(ValueKind.REF); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); + var dst = allocRegTos(ValueKind.I32); + match (pack) { + PACKED_I8 => { + asm.d.cmp_r_m(G(idx), X86_64Addr.new(G(tmp), null, 1, masm.getOffsets().Array_length)); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); + asm.d.movbsx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 1, masm.getOffsets().Array_contents)); + } + PACKED_I16 => { + var tmp2 = allocTmp(ValueKind.I32); + masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); + masm.emit_shrw_r_i(tmp2, 1); + asm.d.cmp_r_r(G(idx), G(tmp2)); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); + asm.d.movwsx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 2, masm.getOffsets().Array_contents)); + } + _ => ; + } + state.push(SpcConsts.KIND_I32 | IN_REG, dst, 0); + } + + def visit_ARRAY_GET_U_Pair(pack: Packedness, arr: Reg, idx: Reg) { + var tmp = allocTmp(ValueKind.REF); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); + var dst = allocRegTos(ValueKind.I32); + match (pack) { + PACKED_I8 => { + asm.d.cmp_r_m(G(idx), X86_64Addr.new(G(tmp), null, 1, masm.getOffsets().Array_length)); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); + asm.d.movbzx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 1, masm.getOffsets().Array_contents)); + } + PACKED_I16 => { + var tmp2 = allocTmp(ValueKind.I32); + masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); + masm.emit_shrw_r_i(tmp2, 1); + asm.d.cmp_r_r(G(idx), G(tmp2)); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); + asm.d.movwzx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 2, masm.getOffsets().Array_contents)); + } + _ => ; + } + state.push(SpcConsts.KIND_I32 | IN_REG, dst, 0); + } + + def visit_ARRAY_GET_Pair(kind: ValueKind, arr: Reg, idx: Reg) { + var tmp = allocTmp(ValueKind.REF); + var dst = allocRegTos(kind); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + match (kind) { + REF => { + masm.emit_v3_HeapArrayPair_objs_r_r(tmp, arr); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + } + _ => { + // use the bytes array; shift index by size factor + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); + var tmp2 = allocTmp(ValueKind.I32); + masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); + masm.emit_shrw_r_i(tmp2, masm.logScaleOf(kind)); + asm.d.cmp_r_r(G(idx), G(tmp2)); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); + } + } + masm.emit_v3_Array_elem_r_rr(kind, dst, tmp, idx); + state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); + } + + def visit_ARRAY_SET_Pair(kind: ValueKind, arr: Reg, idx: Reg, val: Reg) { + var tmp = allocTmp(ValueKind.REF); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + match (kind) { + REF => { + masm.emit_v3_HeapArrayPair_objs_r_r(tmp, arr); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + } + _ => { + // use the bytes array; shift index by size factor + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); + var tmp2 = allocTmp(ValueKind.I32); + masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); + masm.emit_shrw_r_i(tmp2, masm.logScaleOf(kind)); + asm.d.cmp_r_r(G(idx), G(tmp2)); + asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); + } + } + masm.emit_v3_Array_elem_rr_r(kind, tmp, idx, val); + } + + def visit_ARRAY_LEN_Pair(reg: Reg) { + var tmp0 = allocTmpFixed(ValueKind.I32, X86_64MasmRegs.RCX); // shift count must be here + var tmp = allocTmp(ValueKind.REF); // will hold ref to array + var dst = allocRegTos(ValueKind.I32); // will receive the length result + asm.q.movbsx_r_m(G(tmp0), X86_64Addr.new(G(reg), null, 1, V3Offsets.new().HeapArrayPair_shift)); + var objs = X86_64Label.new(); + asm.d.cmp_r_i(G(tmp0), 0); + asm.jc_rel_near(X86_64Conds.L, objs); + masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, reg); // bytes case; get bytes array + masm.emit_v3_Array_length_r_r(dst, tmp); // get its length + asm.d.shr_r_cl(G(dst)); // shift right by shift count + var done = X86_64Label.new(); + asm.jmp_rel_near(done); + asm.bind(objs); + masm.emit_v3_HeapArrayPair_objs_r_r(tmp, reg); // objs case; get objs array + masm.emit_v3_Array_length_r_r(dst, tmp); // get its length + asm.bind(done); + state.push(KIND_I32 | IN_REG, dst, 0); + } + // r1 = op(r1) private def do_op1_r(kind: ValueKind, emit: (X86_64Gpr -> T)) -> bool { var sv = popRegToOverwrite(), r = G(sv.reg); diff --git a/src/engine/x86-64/X86_64Stack.v3 b/src/engine/x86-64/X86_64Stack.v3 index 39c153437..956ac21cb 100644 --- a/src/engine/x86-64/X86_64Stack.v3 +++ b/src/engine/x86-64/X86_64Stack.v3 @@ -391,6 +391,12 @@ class X86_64Stack extends WasmStack { def popw() -> u64 { return popb64(BpTypeCode.I64.code); } + def popf() -> float { + return float.view(popb32(BpTypeCode.F32.code)); + } + def popd() -> double { + return double.view(popb64(BpTypeCode.F64.code)); + } def popb32(tag: byte) -> u32 { checkTopTag(tag); vsp += -(valuerep.slot_size); @@ -408,7 +414,7 @@ class X86_64Stack extends WasmStack { } vsp += -(valuerep.slot_size); var val = (vsp + valuerep.tag_size).load(); - if ((val & 1) == 1) fatal("expected ref, got i31"); + if ((val & 1) == 1) return ObjectI31.new(u31.view(val >> 1)); return (vsp + valuerep.tag_size).load(); } def checkTopTag(tag: byte) -> byte { diff --git a/src/modules/wizeng/WizengModule.v3 b/src/modules/wizeng/WizengModule.v3 index bb2323c5d..93e495a67 100644 --- a/src/modules/wizeng/WizengModule.v3 +++ b/src/modules/wizeng/WizengModule.v3 @@ -177,13 +177,14 @@ class WizengModule extends HostModule("wizeng") { def puta(args: Range) -> HostResult { var raw_arr = Value.Ref.!(args[0]).val; if (raw_arr == null) return HostResult.Throw(Trap.new(TrapReason.NULL_DEREF, null, null)); - var arr = HeapArray.!(raw_arr); + var arr = HeapArrayGeneric.!(raw_arr); var offset_u32 = Values.unbox_u(args[1]); var size_u32 = Values.unbox_u(args[2]); - if (ArrayUtil.boundsCheck(arr.vals, offset_u32, size_u32) < 0) { + var vals = arr.getValues(); + if (ArrayUtil.boundsCheck(vals, offset_u32, size_u32) < 0) { return HostResult.Throw(Trap.new(TrapReason.ARRAY_OOB, "when calling wizeng.puta()", null)); } - var result = Ranges.map(arr.vals[offset_u32 ..+ size_u32], Values.unbox_u8); + var result = Ranges.map(vals[offset_u32 ..+ size_u32], Values.unbox_u8); System.write(1, result); return HostResult.Value0; } diff --git a/src/util/ArrayUtil.v3 b/src/util/ArrayUtil.v3 index aab421324..af6bb13d7 100644 --- a/src/util/ArrayUtil.v3 +++ b/src/util/ArrayUtil.v3 @@ -10,17 +10,32 @@ component ArrayUtil { for (i < len) dst[dst_start + i] = src[src_start + i]; } def boundsCheck(data: Array, offset: u64, size: u64) -> int { + if (data != null) { + var l = data.length; + if (offset > l || size > l || (offset + size) > l) return int.min; + return int.view(offset); + } + if (offset > 0 || size > 0) return int.min; + return 0; + } + def boundsCheckX(data: Array, offset: u64, index: u64, size: u64) -> int { if (data != null) { var l = data.length; if (offset > l) return int.min; if (size > l) return int.min; - if ((offset + size) > l) return int.min; - return int.view(offset); + var x = offset + index; + if ((x + size) > l) return int.min; + return int.view(x); } if (offset > 0) return int.min; if (size > 0) return int.min; + if (index > 0) return int.min; return 0; } + def boundsCheckWithLength(length: u31, offset: u64, index: u64, size: u64) -> int { + var x = offset + index; + return if(x + size > length, int.min, int.!(x)); + } def safeCopy(dst: Array, dst_offset: u64, src: Array, src_offset: u64, size: u64) -> bool { var i = boundsCheck(dst, dst_offset, size); if (i < 0) return false; @@ -34,6 +49,49 @@ component ArrayUtil { } return true; } + def safeCopyFromValueToPair(dst: HeapArrayPair, dst_offset: u32, src: HeapArrayValue, src_offset: u32, size: u32) -> bool { + // Check HeapArrayPair + var shift = dst.shift; + var i = -1; + if (shift == Values.OBJS_SHIFT) { + i = boundsCheck(dst.objs, dst_offset, size); + if (i < 0) return false; + } else { + i = boundsCheck(dst.bytes, dst_offset << shift, size << shift); + if (i < 0) return false; + } + // Check HeapArrayValue + var j = boundsCheck(src.vals, src_offset, size); + if (j < 0) return false; + + for (k < int.!(size)) { + dst.setValue(u32.view(i + k), src.getValue(u32.view(j + k))); + } + return true; + } + def safeCopyDiffTypes(dst: Array, dst_offset: u32, src_length: u31, src_offset: u32, size: u32, + f: u32 -> T) -> bool { + var i = boundsCheck(dst, dst_offset, size); + if (i < 0) return false; + var j = boundsCheckWithLength(src_length, 0, src_offset, size); + if (j < 0) return false; + // We assume T and U are different types, so src and dst are not overlapping + for (k < int.!(size)) dst[i + k] = f(u32.view(j + k)); + return true; + } + def safeCopyFromV128(dst: Array, dst_offset: u32, src_length: u31, src_offset: u32, size: u32, + f: u32 -> (u64, u64)) -> bool { + var i = boundsCheck(dst, dst_offset, size << 1); + if (i < 0) return false; + var j = boundsCheckWithLength(src_length, 0, src_offset, size); + if (j < 0) return false; + for (k < int.!(size)) { + var v128 = f(u32.view(j++)); + dst[i++] = v128.0; + dst[i++] = v128.1; + } + return true; + } def safeCopyF(dst: Array, dst_offset: u64, src: Array, src_offset: u64, size: u64, f: S -> D) -> bool { var i = boundsCheck(dst, dst_offset, size); if (i < 0) return false; @@ -44,6 +102,18 @@ component ArrayUtil { } return true; } + def safeCopyFbytes(dst: Array, dst_offset: u64, src: Array, src_offset: u64, size: u64, f: S -> D, g: (Array, u64, D) -> void, esize: u32) -> bool { + var i = boundsCheck(dst, dst_offset, size * esize); + if (i < 0) return false; + var j = boundsCheck(src, src_offset, size); + if (j < 0) return false; + var pos = dst_offset; + for (k < int.!(size)) { + g(dst, pos, f(src[j + k])); + pos += esize; + } + return true; + } def mapInPlace(array: Array, f: T -> T) -> Array { for (i < array.length) array[i] = f(array[i]); return array; diff --git a/test/unittest/ExeTest.v3 b/test/unittest/ExeTest.v3 index 7b015399c..6b231c951 100644 --- a/test/unittest/ExeTest.v3 +++ b/test/unittest/ExeTest.v3 @@ -2366,7 +2366,7 @@ def test_struct_get(t: ExeTester) { ]); t.extensions |= Extension.GC; t.sig0([ValueTypes.RefStruct(false, st)], SigCache.arr_i); - var obj = HeapStruct.new(st, array_uuu(99, 98, 97)); + var obj = heapStructNew(st, array_uuu(99, 98, 97)); for (f < st.field_types.length) { t.codev([ u8.!(Opcode.LOCAL_GET.code), 0, @@ -2374,7 +2374,7 @@ def test_struct_get(t: ExeTester) { heapIndexByte(st), byte.view(f) ]); for (i in [366u, 2777u, 0x18776655u]) { - obj.vals[f] = Value.I32(i); + obj.setFieldValue(u31.!(f), Value.I32(i)); t.args_r(obj).assert2_u(i); } } @@ -2395,7 +2395,7 @@ def test_struct_get_su(t: ExeTester) { t.sig0([ValueTypes.RefStruct(false, st)], SigCache.arr_i); var vals: Array = [0x87, 0x9765]; - var obj = HeapStruct.new(st, array_uu(vals[0], vals[1])); + var obj = heapStructNew(st, array_uu(vals[0], vals[1])); for (f < 2) { var shift: u5 = if(f == 0, 24, 16); for (opcode in [Opcode.STRUCT_GET_S, Opcode.STRUCT_GET_U]) { @@ -2418,7 +2418,7 @@ def test_struct_set(t: ExeTester) { I32_FIELD ]); t.sig0([ValueTypes.RefStruct(false, st), ValueType.I32], SigCache.arr_v); - var obj = HeapStruct.new(st, array_uuu(99, 98, 97)); + var obj = heapStructNew(st, array_uuu(99, 98, 97)); for (f < st.field_types.length) { t.codev([ u8.!(Opcode.LOCAL_GET.code), 0, @@ -2427,10 +2427,17 @@ def test_struct_set(t: ExeTester) { heapIndexByte(st), byte.view(f) ]); for (i in [366u, 2777u, 0x18776655u]) { - var prev = Arrays.dup(obj.vals); + var prev = Array.new(3); + for (j < 3) { + prev[j] = obj.getFieldValue(u31.!(j)); + } t.argsN([Value.Ref(obj), Values.box_u(i)]).assert2_none(); prev[f] = Values.box_u(i); - t.t.assertar("fields", prev, obj.vals, Values.render); + var cur = Array.new(3); + for (j < 3) { + cur[j] = obj.getFieldValue(u31.!(j)); + } + t.t.assertar("fields", prev, cur, Values.render); } } } @@ -2484,12 +2491,26 @@ def test_array_get(t: ExeTester) { Opcode.ARRAY_GET.prefix, u8.!(Opcode.ARRAY_GET.code), heapIndexByte(at) ]); for (len < 5) { - var elems = Array.new(len); - var obj = Value.Ref(HeapArray.new(at, elems)); - for (j < len) elems[j] = Value.F64(0x99u + u32.view(len)); + var arr: HeapArrayGeneric; + match (ObjTuning.arrayMode) { + Typed => { + var vals = Array.new(len); + arr = HeapArrayF64.new(at, vals); + } + Pair => { + var bytes = Array.new(len << 3); + arr = HeapArrayPair.new(at, null, bytes, 3); + } + _ => { + var vals = Array.new(len); + arr = HeapArrayValue.new(at, vals); + } + } + var obj = Value.Ref(arr); + for (j < len) arr.setValue(u32.!(j), Value.F64(0x99u + u32.view(len))); for (i < len) { - t.argsN([obj, Value.I32(u32.view(i))]).assert2_val(elems[i]); + t.argsN([obj, Value.I32(u32.view(i))]).assert2_val(arr.getValue(u32.!(i))); } t.argsN([obj, Value.I32(99)]).assert2_trap(TrapReason.ARRAY_OOB); t.argsN([obj, Value.I32(0x80000000u)]).assert2_trap(TrapReason.ARRAY_OOB); @@ -2514,10 +2535,50 @@ def test_array_get_su(t: ExeTester) { opcode.prefix, u8.!(opcode.code), heapIndexByte(at) ]); for (len < 5) { - var elems = Array.new(len); - var obj = Value.Ref(HeapArray.new(at, elems)); - for (j < len) elems[j] = Value.I32(0xFF7Fu + u32.view(j)); + var arr: HeapArrayGeneric; + match (ObjTuning.arrayMode) { + Original => { + var elems = Array.new(len); + arr = HeapArrayValue.new(at, elems); + for (j < len) elems[j] = Value.I32(0xFF7Fu + u32.view(j)); + } + Typed => { + match (pack) { + PACKED_I8 => { + var elems = Array.new(len); + arr = HeapArrayI8.new(at, elems); + for (j < len) elems[j] = u8.view(0xFF7Fu + u32.view(j)); + } + PACKED_I16 => { + var elems = Array.new(len); + arr = HeapArrayI16.new(at, elems); + for (j < len) elems[j] = u16.view(0xFF7Fu + u32.view(j)); + } + _ => ; + } + } + Pair => { + match (pack) { + PACKED_I8 => { + var elems = Array.new(len); + arr = HeapArrayPair.new(at, null, elems, 0); + for (j < len) elems[j] = u8.view(0xFF7Fu + u32.view(j)); + } + PACKED_I16 => { + var elems = Array.new(len << 1); + arr = HeapArrayPair.new(at, null, elems, 1); + for (j < len) { + var val = u16.view(0xFF7Fu + u32.view(j)); + Ref.at(elems, j << 1).val = val; + } + } + _ => ; + } + } + } + var obj = Value.Ref(arr); + var elems = arr.getValues(); for (i < len) { var expected = unpackI32(Values.unbox_u(elems[i]), pack, opcode == Opcode.ARRAY_GET_S); t.argsN([obj, Value.I32(u32.view(i))]).assert2_u(expected); @@ -2543,15 +2604,30 @@ def test_array_set(t: ExeTester) { u8.!(Opcode.LOCAL_GET.code), 2, Opcode.ARRAY_SET.prefix, u8.!(Opcode.ARRAY_SET.code), heapIndexByte(at) ]); - var obj = HeapArray.new(at, [Value.F64(0x99999), Value.F64(55)]); + var obj: HeapArrayGeneric; + match (ObjTuning.arrayMode) { + Typed => { + var vals = [0x99999u64, 55u64]; + obj = HeapArrayF64.new(at, vals); + } + Pair => { + var bytes = [0x99u8, 0x99u8, 0x9u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 55u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]; + obj = HeapArrayPair.new(at, null, bytes, 3); + } + _ => { + var vals: Array = [Value.F64(0x99999), Value.F64(55)]; + obj = HeapArrayValue.new(at, vals); + } + } var ref = Value.Ref(obj); - for (i < obj.vals.length) { + for (i < obj.length()) { for (v in [366u, 2777u, 0x18776655u]) { - var prev = Arrays.dup(obj.vals); + var prev = obj.getValues(); var got = t.run([ref, Value.I32(u32.view(i)), Value.F64(v)]).1; t.assert_req(got, Result.Value([])); prev[i] = Value.F64(v); - t.t.assertar("elems", prev, obj.vals, Values.render); + t.t.assertar("elems", prev, obj.getValues(), Values.render); } } t.argsN([ref, Value.I32(99), Values.F64_0]).assert2_trap(TrapReason.ARRAY_OOB); @@ -2570,7 +2646,22 @@ def test_array_len(t: ExeTester) { Opcode.ARRAY_LEN.prefix, u8.!(Opcode.ARRAY_LEN.code) ]); for (i < 5) { - var obj = Value.Ref(HeapArray.new(at, Array.new(i))); + var arr: HeapArrayGeneric; + match (ObjTuning.arrayMode) { + Typed => { + var vals = Array.new(i); + arr = HeapArrayF64.new(at, vals); + } + Pair => { + var bytes = Array.new(i << 3); + arr = HeapArrayPair.new(at, null, bytes, 3); + } + _ => { + var vals = Array.new(i); + arr = HeapArrayValue.new(at, vals); + } + } + var obj = Value.Ref(arr); var r = t.run([obj]); t.assert_req(r.1, i_r(i)); } @@ -2654,8 +2745,8 @@ class RttTestVals(t: ExeTester) { def rtt1 = (st1); def rtt2a = (st2); - def o1 = HeapStruct.new(rtt1, []); - def o2a = HeapStruct.new(rtt2a, array_u(0)); + def o1 = Value.Ref(heapStructNew(rtt1, [])); + def o2a = Value.Ref(heapStructNew(rtt2a, array_u(0))); } def test_ref_test(t: ExeTester) { @@ -2669,8 +2760,8 @@ def test_ref_test(t: ExeTester) { ]); t.args_r(null).assert2_i(0); - t.args_r(x.o1).assert2_i(1); - t.args_r(x.o2a).assert2_i(1); + t.argsN([x.o1]).assert2_i(1); + t.argsN([x.o2a]).assert2_i(1); } def test_ref_test_null(t: ExeTester) { @@ -2684,8 +2775,8 @@ def test_ref_test_null(t: ExeTester) { ]); t.args_r(null).assert2_i(1); - t.args_r(x.o1).assert2_i(1); - t.args_r(x.o2a).assert2_i(1); + t.argsN([x.o1]).assert2_i(1); + t.argsN([x.o2a]).assert2_i(1); } def test_ref_cast(t: ExeTester) { @@ -2699,9 +2790,9 @@ def test_ref_cast(t: ExeTester) { var fail = TrapReason.FAILED_CAST; t.args_r(null).assert2_trap(fail); - t.args_r(x.o1).assert2_trap(fail); - t.args_r(x.o1).assert2_trap_at(fail, [3]); - t.args_r(x.o2a).assert2_r(x.o2a); + t.argsN([x.o1]).assert2_trap(fail); + t.argsN([x.o1]).assert2_trap_at(fail, [3]); + t.argsN([x.o2a]).assert2_val(x.o2a); } def test_ref_cast_null(t: ExeTester) { @@ -2715,9 +2806,9 @@ def test_ref_cast_null(t: ExeTester) { var fail = TrapReason.FAILED_CAST; t.args_r(null).assert2_r(null); - t.args_r(x.o1).assert2_trap(fail); - t.args_r(x.o1).assert2_trap_at(fail, [3]); - t.args_r(x.o2a).assert2_r(x.o2a); + t.argsN([x.o1]).assert2_trap(fail); + t.argsN([x.o1]).assert2_trap_at(fail, [3]); + t.argsN([x.o2a]).assert2_val(x.o2a); } def NULL1: byte = 1; @@ -2734,10 +2825,10 @@ def test_br_on_cast(t: ExeTester) { u8.!(Opcode.UNREACHABLE.code) ]); var fail = TrapReason.UNREACHABLE; - t.args_r(x.o2a).assert2_r(x.o2a); + t.argsN([x.o2a]).assert2_val(x.o2a); t.args_r(null).assert2_trap(fail); - t.args_r(x.o1).assert2_trap(fail); - t.args_r(x.o1).assert2_trap_at(fail, [9]); + t.argsN([x.o1]).assert2_trap(fail); + t.argsN([x.o1]).assert2_trap_at(fail, [9]); } def test_br_on_cast_null(t: ExeTester) { @@ -2752,9 +2843,9 @@ def test_br_on_cast_null(t: ExeTester) { ]); var fail = TrapReason.UNREACHABLE; t.args_r(null).assert2_r(null); - t.args_r(x.o1).assert2_trap(fail); - t.args_r(x.o1).assert2_trap_at(fail, [9]); - t.args_r(x.o2a).assert2_r(x.o2a); + t.argsN([x.o1]).assert2_trap(fail); + t.argsN([x.o1]).assert2_trap_at(fail, [9]); + t.argsN([x.o2a]).assert2_val(x.o2a); } def test_br_on_cast_fail(t: ExeTester) { @@ -2769,9 +2860,9 @@ def test_br_on_cast_fail(t: ExeTester) { ]); var fail = TrapReason.UNREACHABLE; t.args_r(null).assert2_trap(TrapReason.UNREACHABLE); - t.args_r(x.o1).assert2_r(x.o1); - t.args_r(x.o2a).assert2_trap(fail); - t.args_r(x.o2a).assert2_trap_at(fail, [9]); + t.argsN([x.o1]).assert2_val(x.o1); + t.argsN([x.o2a]).assert2_trap(fail); + t.argsN([x.o2a]).assert2_trap_at(fail, [9]); } def test_br_on_cast_fail_null(t: ExeTester) { @@ -2786,9 +2877,9 @@ def test_br_on_cast_fail_null(t: ExeTester) { ]); var fail = TrapReason.UNREACHABLE; t.args_r(null).assert2_trap(fail); - t.args_r(x.o1).assert2_r(x.o1); - t.args_r(x.o2a).assert2_trap(fail); - t.args_r(x.o2a).assert2_trap_at(fail, [9]); + t.argsN([x.o1]).assert2_val(x.o1); + t.argsN([x.o2a]).assert2_trap(fail); + t.argsN([x.o2a]).assert2_trap_at(fail, [9]); } def test_br_on_null(t: ExeTester) { @@ -2872,7 +2963,7 @@ def test_externalize(i: ExeTester) { var st = i.newStruct([ I32_FIELD ]); - var obj = HeapStruct.new(st, array_u(99)); + var obj = heapStructNew(st, array_u(99)); i.args_r(obj).assert2_r(obj); } @@ -3085,3 +3176,24 @@ def test_table_get_traploc(i: ExeTester) { ]; test_traplocs(i.args_u(3), 2, code, TrapReason.TABLE_OOB); } + +def heapStructNew(decl: StructDecl, vals: Array) -> HeapStructGeneric { + match (ObjTuning.structMode) { + Original => { + var fields = Array.new(decl.field_types.length); + for (i < fields.length) { + fields[i] = vals[i]; + } + return HeapStruct.new(decl, fields); + } + Pair => { + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var obj = HeapStructPair.new(decl, objs, bytes); + for (i < decl.field_types.length) { + obj.setFieldValue(u31.view(i), vals[i]); + } + return obj; + } + } +} \ No newline at end of file diff --git a/test/unittest/ExeTester.v3 b/test/unittest/ExeTester.v3 index 682555774..6eefa7cfa 100644 --- a/test/unittest/ExeTester.v3 +++ b/test/unittest/ExeTester.v3 @@ -211,22 +211,30 @@ class ExeTester(t: Tester, tiering: ExecutionStrategy) extends ModuleBuilder { def assert_struct(got: Result, expected_struct: StructDecl, rtt: HeapTypeDecl, fields: Array) { var r = assert_val1(got, "struct"), ok = r.0, hr = r.1; if (!ok) return; - if (!HeapStruct.?(hr.val)) return t.fail1("expected struct, got %q", got.render); - var obj = HeapStruct.!(hr.val); + if (!HeapStructGeneric.?(hr.val)) return t.fail1("expected struct, got %q", got.render); + var obj = HeapStructGeneric.!(hr.val); if (obj.decl != expected_struct) return t.fail2("expected heap %q, got %q", expected_struct.render, obj.decl.render); if (rtt != null && obj.decl != rtt) return t.fail2("expected rtt %q, got %q", rtt.render, obj.decl.render); - t.assertar("fields", fields, obj.vals, Values.render); + var vals = Array.new(expected_struct.field_types.length); + for (i < vals.length) { + vals[i] = obj.getFieldValue(u31.!(i)); + } + t.assertar("fields", fields, vals, Values.render); } def assert_array(got: Result, expected_array: ArrayDecl, rtt: HeapTypeDecl, elems: Array) { var r = assert_val1(got, "array"), ok = r.0, hr = r.1; if (!ok) return; - if (!HeapArray.?(hr.val)) return t.fail1("expected array, got %q", got.render); - var obj = HeapArray.!(hr.val); + if (!HeapArrayGeneric.?(hr.val)) return t.fail1("expected array, got %q", got.render); + var obj = HeapArrayGeneric.!(hr.val); if (obj.decl != expected_array) return t.fail2("expected heap %q, got %q", expected_array.render, obj.decl.render); if (rtt != null && obj.decl != rtt) return t.fail2("expected rtt %q, got %q", rtt.render, obj.decl.render); - t.assertar("elements", elems, obj.vals, Values.render); + var vals = Array.new(obj.length()); + for (i < vals.length) { + vals[i] = obj.getValue(u31.!(i)); + } + t.assertar("elements", elems, vals, Values.render); } } diff --git a/test/unittest/WasmStackTest.v3 b/test/unittest/WasmStackTest.v3 index 6f9c724d8..5974bf266 100644 --- a/test/unittest/WasmStackTest.v3 +++ b/test/unittest/WasmStackTest.v3 @@ -203,7 +203,7 @@ def test_resume_host_tc_wasm(t: WasmStackTester) { } def bindArgs(s: WasmStack) { - var a = HeapObject.new(null, []); + var a = HeapObject.new(null); var h = HostObject.new(); var f = HostFunction.new("host_a_minus_2b", SigCache.ii_i, host_a_minus_2b); s.bind([Value.Ref(a), Value.Ref(h), Value.Ref(f)]); @@ -251,7 +251,7 @@ def test_gc_stacks1(t: WasmStackTester) { var f = t.makeFunc(); s1.reset(f); - var a = HeapObject.new(null, []); + var a = HeapObject.new(null); s1.bind([Value.Ref(a), Value.I31(666777), Value.Ref(f)]); Target.forceGC(); diff --git a/test/wasm-spec/SpecTestParser.v3 b/test/wasm-spec/SpecTestParser.v3 index 327ea961a..9e2485313 100644 --- a/test/wasm-spec/SpecTestParser.v3 +++ b/test/wasm-spec/SpecTestParser.v3 @@ -844,11 +844,11 @@ type ExpectedValue { _ => return false; } RefArray => match (that) { - Ref(val) => return HeapArray.?(val); + Ref(val) => return HeapArrayGeneric.?(val); _ => return false; } RefStruct => match (that) { - Ref(val) => return HeapStruct.?(val); + Ref(val) => return HeapStructGeneric.?(val); _ => return false; } RefEq => match (that) { From a0e872728a6426443552f487106a261508467f13 Mon Sep 17 00:00:00 2001 From: Meng-chieh Chiu Date: Thu, 15 Jan 2026 17:43:10 -0500 Subject: [PATCH 02/11] refactoring starts --- src/engine/Instance.v3 | 337 ++----- src/engine/Runtime.v3 | 933 ++---------------- src/engine/Tuning.v3 | 3 +- src/engine/Value.v3 | 701 +------------ src/engine/compiler/MacroAssembler.v3 | 11 +- src/engine/compiler/SinglePassCompiler.v3 | 52 +- src/engine/x86-64/V3Offsets.v3 | 7 +- src/engine/x86-64/X86_64Interpreter.v3 | 7 - src/engine/x86-64/X86_64SinglePassCompiler.v3 | 113 --- src/util/ArrayUtil.v3 | 20 - test/unittest/ExeTest.v3 | 33 +- 11 files changed, 176 insertions(+), 2041 deletions(-) diff --git a/src/engine/Instance.v3 b/src/engine/Instance.v3 index 953b102c1..de7265b0a 100644 --- a/src/engine/Instance.v3 +++ b/src/engine/Instance.v3 @@ -103,6 +103,45 @@ class Instance(module: Module, imports: Array) { var v = evalInitExpr(val); return Value.I31(u31.view(Values.unbox_i(v))); } + Array(t, len, elem), + FixedArray(t, vals), + Struct(t, vals) => { + return Value.Ref(evalInitExprObject(init)); + } + ArrayNewData(t, data_index, offset, len) => { + var voffset = evalInitExpr(offset); + var vlen = evalInitExpr(len); + return Values.I32_0; // TODO + } + ArrayNewElem(t, elem_index, offset, len) => { + var voffset = evalInitExpr(offset); + var vlen = evalInitExpr(len); + return Values.I32_0; // TODO + } + I32_ADD(a, b) => return Value.I32(Values.unbox_u(evalInitExpr(a)) + Values.unbox_u(evalInitExpr(b))); + I32_SUB(a, b) => return Value.I32(Values.unbox_u(evalInitExpr(a)) - Values.unbox_u(evalInitExpr(b))); + I32_MUL(a, b) => return Value.I32(Values.unbox_u(evalInitExpr(a)) * Values.unbox_u(evalInitExpr(b))); + I64_ADD(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) + Values.unbox_w(evalInitExpr(b))); + I64_SUB(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) - Values.unbox_w(evalInitExpr(b))); + I64_MUL(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) * Values.unbox_w(evalInitExpr(b))); + } + } + def evalInitExprObject(init: InitExpr) -> Object { + match (init) { + FuncRefNull, + RefNull(ht), + ExternRefNull => return null; + Global(global_index, g) => { + return Values.unbox(globals[global_index].get()); + } + FuncRef(func_index, f) => { + return functions[func_index]; + } + Const(val) => return Values.unbox(val); + I31(val) => { + var v = evalInitExpr(val); + return ObjectI31.new(u31.view(Values.unbox_i(v))); + } Array(t, len, elem) => { var vlen = Values.unbox_i(evalInitExpr(len)); var decl = t.array; @@ -118,37 +157,37 @@ class Instance(module: Module, imports: Array) { var ielem = Value.I32.!(velem).val; var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = ielem; - return Value.Ref(HeapArrayI32.new(t.array, vvals)); + return HeapArrayI32.new(t.array, vvals); } I64 => { var ielem = Value.I64.!(velem).val; var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = ielem; - return Value.Ref(HeapArrayI64.new(t.array, vvals)); + return HeapArrayI64.new(t.array, vvals); } F32 => { var felem = Value.F32.!(velem).bits; var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = felem; - return Value.Ref(HeapArrayF32.new(t.array, vvals)); + return HeapArrayF32.new(t.array, vvals); } F64 => { var felem = Value.F64.!(velem).bits; var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = felem; - return Value.Ref(HeapArrayF64.new(t.array, vvals)); + return HeapArrayF64.new(t.array, vvals); } Ref(nullable, heaptype) => { if (heaptype == HeapType.I31 && !nullable) { var oelem = Value.I31.!(velem).val; var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = oelem; - return Value.Ref(HeapArrayI31.new(t.array, vvals)); + return HeapArrayI31.new(t.array, vvals); } else { var oelem = Value.Ref.!(velem).val; var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = oelem; - return Value.Ref(HeapArrayRef.new(t.array, vvals)); + return HeapArrayRef.new(t.array, vvals); } } V128 => { @@ -160,12 +199,12 @@ class Instance(module: Module, imports: Array) { vvals[i] = low; vvals[i+1] = high; } - return Value.Ref(HeapArrayV128.new(t.array, vvals)); + return HeapArrayV128.new(t.array, vvals); } _ => { var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = velem; - return Value.Ref(HeapArrayValue.new(t.array, vvals)); + return HeapArrayValue.new(t.array, vvals); } } } @@ -173,122 +212,20 @@ class Instance(module: Module, imports: Array) { var ielem = u8.view(Value.I32.!(velem).val); var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = ielem; - return Value.Ref(HeapArrayI8.new(t.array, vvals)); + return HeapArrayI8.new(t.array, vvals); } PACKED_I16 => { var ielem = u16.view(Value.I32.!(velem).val); var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = ielem; - return Value.Ref(HeapArrayI16.new(t.array, vvals)); - } - } - } - Pair => { - match (st.pack) { - UNPACKED => { - match (st.valtype) { - I32 => { - var bytes = Array.new(vlen << 2); - var ielem = Value.I32.!(velem).val; - var offset = 0; - for (i < vlen) { - Ref.at(bytes, offset).val = ielem; - offset += 4; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); - } - I64 => { - var bytes = Array.new(vlen << 3); - var ielem = Value.I64.!(velem).val; - var offset = 0; - for (i < vlen) { - Ref.at(bytes, offset).val = ielem; - offset += 8; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); - } - F32 => { - var bytes = Array.new(vlen << 2); - var felem = Value.F32.!(velem).bits; - var offset = 0; - for (i < vlen) { - Ref.at(bytes, offset).val = felem; - offset += 4; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); - } - F64 => { - var bytes = Array.new(vlen << 3); - var felem = Value.F64.!(velem).bits; - var offset = 0; - for (i < vlen) { - Ref.at(bytes, offset).val = felem; - offset += 8; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var bytes = Array.new(vlen << 2); - var oelem = Value.I31.!(velem).val; - var offset = 0; - for (i < vlen) { - Ref.at(bytes, offset).val = oelem; - offset += 4; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); - } else { - var vals = Array.new(vlen); - var oelem = Value.Ref.!(velem).val; - for (i < vlen) vals[i] = oelem; - return Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT)); - } - } - V128 => { - var bytes = Array.new(vlen << 4); - var v128 = Value.V128.!(velem); - var low = v128.low; - var high = v128.high; - var offset = 0; - for (i < vlen) { - var r = Ref.at(bytes, offset); - r.lo_val = low; - r.hi_val = high; - offset += 16; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 4)); - } - _ => { - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = velem; - return Value.Ref(HeapArrayValue.new(t.array, vvals)); - } - } - } - PACKED_I8 => { - var bytes = Array.new(vlen); - var ielem = u8.view(Value.I32.!(velem).val); - for (i < vlen) { - bytes[i] = u8.view(ielem); - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 0)); - } - PACKED_I16 => { - var bytes = Array.new(vlen << 1); - var ielem = u16.view(Value.I32.!(velem).val); - var offset = 0; - for (i < vlen) { - Ref.at(bytes, offset).val = u16.view(ielem); - offset += 2; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 1)); + return HeapArrayI16.new(t.array, vvals); } } } _ => { var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = velem; - return Value.Ref(HeapArrayValue.new(t.array, vvals)); + return HeapArrayValue.new(t.array, vvals); } } } @@ -307,7 +244,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = Value.I32.!(velem).val; } - return Value.Ref(HeapArrayI32.new(t.array, vvals)); + return HeapArrayI32.new(t.array, vvals); } I64 => { var vlen = vals.length; @@ -316,7 +253,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = Value.I64.!(velem).val; } - return Value.Ref(HeapArrayI64.new(t.array, vvals)); + return HeapArrayI64.new(t.array, vvals); } F32 => { var vlen = vals.length; @@ -325,7 +262,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = Value.F32.!(velem).bits; } - return Value.Ref(HeapArrayF32.new(t.array, vvals)); + return HeapArrayF32.new(t.array, vvals); } F64 => { var vlen = vals.length; @@ -334,7 +271,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = Value.F64.!(velem).bits; } - return Value.Ref(HeapArrayF64.new(t.array, vvals)); + return HeapArrayF64.new(t.array, vvals); } Ref(nullable, heaptype) => { if (heaptype == HeapType.I31 && !nullable) { @@ -344,7 +281,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = Value.I31.!(velem).val; } - return Value.Ref(HeapArrayI31.new(t.array, vvals)); + return HeapArrayI31.new(t.array, vvals); } else { var vlen = vals.length; var vvals = Array.new(vlen); @@ -352,7 +289,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = Value.Ref.!(velem).val; } - return Value.Ref(HeapArrayRef.new(t.array, vvals)); + return HeapArrayRef.new(t.array, vvals); } } V128 => { @@ -364,11 +301,11 @@ class Instance(module: Module, imports: Array) { vvals[i] = v128.low; vvals[i+1] = v128.high; } - return Value.Ref(HeapArrayV128.new(t.array, vvals)); + return HeapArrayV128.new(t.array, vvals); } _ => { var vvals = Arrays.map(vals, evalInitExpr); - return Value.Ref(HeapArrayValue.new(t.array, vvals)); + return HeapArrayValue.new(t.array, vvals); } } } @@ -379,7 +316,7 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = u8.view(Value.I32.!(velem).val); } - return Value.Ref(HeapArrayI8.new(t.array, vvals)); + return HeapArrayI8.new(t.array, vvals); } PACKED_I16 => { var vlen = vals.length; @@ -388,126 +325,13 @@ class Instance(module: Module, imports: Array) { var velem = evalInitExpr(vals[i]); vvals[i] = u16.view(Value.I32.!(velem).val); } - return Value.Ref(HeapArrayI16.new(t.array, vvals)); - } - } - } - Pair => { - var decl = t.array; - var st = decl.elem_types[0]; - match (st.pack) { - UNPACKED => { - match (st.valtype) { - I32 => { - var vlen = vals.length; - var bytes = Array.new(vlen << 2); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - Ref.at(bytes, offset).val = Value.I32.!(velem).val; - offset += 4; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); - } - I64 => { - var vlen = vals.length; - var bytes = Array.new(vlen << 3); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - Ref.at(bytes, offset).val = Value.I64.!(velem).val; - offset += 8; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); - } - F32 => { - var vlen = vals.length; - var bytes = Array.new(vlen << 2); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - Ref.at(bytes, offset).val = Value.F32.!(velem).bits; - offset += 4; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); - } - F64 => { - var vlen = vals.length; - var bytes = Array.new(vlen << 3); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - Ref.at(bytes, offset).val = Value.F64.!(velem).bits; - offset += 8; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 3)); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vlen = vals.length; - var bytes = Array.new(vlen << 2); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - Ref.at(bytes, offset).val = Value.I31.!(velem).val; - offset += 4; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 2)); - } else { - var vlen = vals.length; - var objs = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - objs[i] = Value.Ref.!(velem).val; - } - return Value.Ref(HeapArrayPair.new(decl, objs, null, Values.OBJS_SHIFT)); - } - } - V128 => { - var vlen = vals.length; - var bytes = Array.new(vlen << 4); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - var r = Ref.at(bytes, offset); - var pair = Value.V128.!(velem); - r.lo_val = pair.low; - r.hi_val = pair.high; - offset += 16; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 4)); - } - _ => { - var vvals = Arrays.map(vals, evalInitExpr); - return Value.Ref(HeapArrayValue.new(t.array, vvals)); - } - } - } - PACKED_I8 => { - var vlen = vals.length; - var bytes = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - bytes[i] = u8.view(Value.I32.!(velem).val); - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 0)); - } - PACKED_I16 => { - var vlen = vals.length; - var bytes = Array.new(vlen << 1); - var offset = 0; - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - Ref.at(bytes, offset).val = u16.view(Value.I32.!(velem).val); - offset += 2; - } - return Value.Ref(HeapArrayPair.new(decl, null, bytes, 1)); + return HeapArrayI16.new(t.array, vvals); } } } _ => { var vvals = Arrays.map(vals, evalInitExpr); - return Value.Ref(HeapArrayValue.new(t.array, vvals)); + return HeapArrayValue.new(t.array, vvals); } } } @@ -515,7 +339,7 @@ class Instance(module: Module, imports: Array) { var vvals = Arrays.map(vals, evalInitExpr); match (ObjTuning.structMode) { Original => { - return Value.Ref(HeapStruct.new(t.sdecl, vvals)); + return HeapStructValue.new(t.sdecl, vvals); } Pair => { var decl = t.sdecl; @@ -526,26 +350,45 @@ class Instance(module: Module, imports: Array) { for (i < decl.field_types.length) { obj.setFieldValue(u31.view(i), vvals[i]); } - return Value.Ref(obj); + return obj; } } } ArrayNewData(t, data_index, offset, len) => { var voffset = evalInitExpr(offset); var vlen = evalInitExpr(len); - return Values.I32_0; // TODO + return null; } ArrayNewElem(t, elem_index, offset, len) => { var voffset = evalInitExpr(offset); var vlen = evalInitExpr(len); - return Values.I32_0; // TODO + return null; } - I32_ADD(a, b) => return Value.I32(Values.unbox_u(evalInitExpr(a)) + Values.unbox_u(evalInitExpr(b))); - I32_SUB(a, b) => return Value.I32(Values.unbox_u(evalInitExpr(a)) - Values.unbox_u(evalInitExpr(b))); - I32_MUL(a, b) => return Value.I32(Values.unbox_u(evalInitExpr(a)) * Values.unbox_u(evalInitExpr(b))); - I64_ADD(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) + Values.unbox_w(evalInitExpr(b))); - I64_SUB(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) - Values.unbox_w(evalInitExpr(b))); - I64_MUL(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) * Values.unbox_w(evalInitExpr(b))); + _ => return null; + } + } + def evalInitExprScalar(init: InitExpr) -> T { + match (init) { + I32(val) => return T.!(u32.view(val)); + I64(val) => return T.!(u64.view(val)); + F32(val) => return T.!(val); + F64(val) => return T.!(val); + V128(low, high) => return T.!((low, high)); + Global(global_index, g) => { + return Values.unbox(globals[global_index].get()); + } + Const(val) => return Values.unbox(val); + I31(val) => { + var v = evalInitExpr(val); + return T.!(u31.view(Values.unbox_i(v))); + } + I32_ADD(a, b) => return T.!(Values.unbox_u(evalInitExpr(a)) + Values.unbox_u(evalInitExpr(b))); + I32_SUB(a, b) => return T.!(Values.unbox_u(evalInitExpr(a)) - Values.unbox_u(evalInitExpr(b))); + I32_MUL(a, b) => return T.!(Values.unbox_u(evalInitExpr(a)) * Values.unbox_u(evalInitExpr(b))); + I64_ADD(a, b) => return T.!(Values.unbox_w(evalInitExpr(a)) + Values.unbox_w(evalInitExpr(b))); + I64_SUB(a, b) => return T.!(Values.unbox_w(evalInitExpr(a)) - Values.unbox_w(evalInitExpr(b))); + I64_MUL(a, b) => return T.!(Values.unbox_w(evalInitExpr(a)) * Values.unbox_w(evalInitExpr(b))); + _ => return T.!(0); } } def findExportOfType(matcher: GlobMatcher) -> T { diff --git a/src/engine/Runtime.v3 b/src/engine/Runtime.v3 index 559ff6e5e..5a6064cb1 100644 --- a/src/engine/Runtime.v3 +++ b/src/engine/Runtime.v3 @@ -89,7 +89,7 @@ component Runtime { for (i = fields.length - 1; i >= 0; i--) { fields[i] = stack.popV(decl.field_types[i].valtype); } - obj = HeapStruct.new(decl, fields); + obj = HeapStructValue.new(decl, fields); } } stack.push(Value.Ref(obj)); @@ -140,7 +140,7 @@ component Runtime { for (i < fields.length) { fields[i] = Values.default(decl.field_types[i].valtype); } - obj = HeapStruct.new(decl, fields); + obj = HeapStructValue.new(decl, fields); } } stack.push(Value.Ref(obj)); @@ -277,111 +277,6 @@ component Runtime { } } } - Pair => { - var len = u31.!(length); - match (decl.elem_types[0].pack) { - UNPACKED => { - match (decl.elem_types[0].valtype) { - I32 => { - var bytes = Array.new(len << 2); - var elem = stack.popu(); - var offset = 0; - for (i < len) { - Ref.at(bytes, offset).val = elem; - offset += 4; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } - I64 => { - var bytes = Array.new(len << 3); - var elem = stack.popw(); - var offset = 0; - for (i < len) { - Ref.at(bytes, offset).val = elem; - offset += 8; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); - } - F32 => { - var bytes = Array.new(len << 2); - var elem = stack.popf(); - var offset = 0; - for (i < len) { - Ref.at(bytes, offset).val = u32.view(elem); - offset += 4; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } - F64 => { - var bytes = Array.new(len << 3); - var elem = stack.popd(); - var offset = 0; - for (i < len) { - Ref.at(bytes, offset).val = u64.view(elem); - offset += 8; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var bytes = Array.new(len << 2); - var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); - var val = ObjectI31.!(Value.Ref.!(elem).val).val; - var offset = 0; - for (i < len) { - Ref.at(bytes, offset).val = val; - offset += 4; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } else { - var vals = Array.new(len); - var elem = stack.popObject(); - for (i < len) vals[i] = elem; - stack.push(Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT))); - } - } - V128 => { - var bytes = Array.new(len << 4); - var v128 = stack.pops(); - var low = v128.0; - var high = v128.1; - var offset = 0; - for (i < len) { - var r = Ref.at(bytes, offset); - r.lo_val = low; - r.hi_val = high; - offset += 16; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 4))); - } - _ => { - var vals = Array.new(u31.!(length)); - var elem = stack.popV(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } - } - } - PACKED_I8 => { - var bytes = Array.new(len); - var elem = u8.view(stack.popu()); - for (i < len) { - bytes[i] = elem; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 0))); - } - PACKED_I16 => { - var bytes = Array.new(len << 1); - var elem = u16.view(stack.popu()); - var offset = 0; - for (i < len) { - Ref.at(bytes, offset).val = elem; - offset += 2; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 1))); - } - } - } _ => { var vals = Array.new(u31.!(length)); var elem = stack.popV(decl.elem_types[0].valtype); @@ -456,50 +351,6 @@ component Runtime { } } } - Pair => { - var len = u31.!(length); - match (decl.elem_types[0].pack) { - UNPACKED => { - match (decl.elem_types[0].valtype) { - I32, F32 => { - var bytes = Array.new(len << 2); - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } - I64, F64 => { - var bytes = Array.new(len << 3); - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var bytes = Array.new(len << 2); - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } else { - var vals = Array.new(len); - stack.push(Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT))); - } - } - V128 => { - var bytes = Array.new(len << 4); - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 4))); - } - _ => { - var vals = Array.new(u31.!(length)); - var elem = Values.default(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } - } - } - PACKED_I8 => { - var bytes = Array.new(len); - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 0))); - } - PACKED_I16 => { - var bytes = Array.new(len << 1); - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 1))); - } - } - } _ => { var vals = Array.new(u31.!(length)); var elem = Values.default(decl.elem_types[0].valtype); @@ -579,109 +430,6 @@ component Runtime { } } } - Pair => { - var len = u31.!(length); - match (decl.elem_types[0].pack) { - UNPACKED => { - match (decl.elem_types[0].valtype) { - I32 => { - var nbytes = len << 2; - var bytes = Array.new(nbytes); - for (i < len) { - var elem = stack.popu(); - nbytes -= 4; - Ref.at(bytes, nbytes).val = elem; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } - I64 => { - var nbytes = len << 3; - var bytes = Array.new(nbytes); - for (i < len) { - var elem = stack.popw(); - nbytes -= 8; - Ref.at(bytes, nbytes).val = elem; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); - } - F32 => { - var nbytes = len << 2; - var bytes = Array.new(nbytes); - for (i < len) { - var elem = stack.popf(); - nbytes -= 4; - Ref.at(bytes, nbytes).val = u32.view(elem); - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } - F64 => { - var nbytes = len << 3; - var bytes = Array.new(nbytes); - for (i < len) { - var elem = stack.popd(); - nbytes -= 8; - Ref.at(bytes, nbytes).val = u64.view(elem); - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 3))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && ! nullable) { - var nbytes = len << 2; - var bytes = Array.new(nbytes); - for (i < len) { - var elem = ObjectI31.!(stack.popObject()).val; - nbytes -= 4; - Ref.at(bytes, nbytes).val = elem; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 2))); - } else { - var vals = Array.new(len); - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popObject(); - stack.push(Value.Ref(HeapArrayPair.new(decl, vals, null, Values.OBJS_SHIFT))); - } - } - V128 => { - var nbytes = len << 4; - var bytes = Array.new(nbytes); - for (i < len) { - var v128 = stack.pops(); - nbytes -= 16; - var r = Ref.at(bytes, nbytes); - r.lo_val = v128.0; - r.hi_val = v128.1; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 4))); - } - _ => { - var vals = Array.new(u31.!(length)); - var t = decl.elem_types[0].valtype; - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } - } - } - PACKED_I8 => { - var bytes = Array.new(len); - var nbytes = len; - for (i < len) { - var elem = u8.view(stack.popu()); - nbytes -= 1; - bytes[nbytes] = elem; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 0))); - } - PACKED_I16 => { - var nbytes = len << 1; - var bytes = Array.new(nbytes); - for (i < len) { - var elem = u16.view(stack.popu()); - nbytes -= 2; - Ref.at(bytes, nbytes).val = elem; - } - stack.push(Value.Ref(HeapArrayPair.new(decl, null, bytes, 1))); - } - } - } _ => { var vals = Array.new(u31.!(length)); var t = decl.elem_types[0].valtype; @@ -704,87 +452,44 @@ component Runtime { UNPACKED => { match (rtt.elem_types[0].valtype) { I32 => { - var t = bytesToI32s(ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI32.new(rtt, t.1))); - } - I64 => { - var t = bytesToI64s(ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI64.new(rtt, t.1))); - } - F32 => { - var t = bytesToI32s(ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayF32.new(rtt, t.1))); - } - F64 => { - var t = bytesToI64s(ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayF64.new(rtt, t.1))); - } - Ref => { - return stack.trap(TrapReason.ERROR); - } - V128 => { - var t = bytesToI64s(ddecl.data, offset, length << 1); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayV128.new(rtt, t.1))); - } - _ => { - var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); - } - } - } - PACKED_I8 => { - var t = bytesToI8s(ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI8.new(rtt, t.1))); - } - PACKED_I16 => { - var t = bytesToI16s(ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI16.new(rtt, t.1))); - } - } - } - Pair => { - var elemSize = sizeOfStorage(rtt.elem_types[0]); - var t = bytesToBytes(ddecl.data, offset, length * elemSize); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (rtt.elem_types[0].pack) { - UNPACKED => { - match (rtt.elem_types[0].valtype) { - I32 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 2))); + var r = getData(instance, data_index, offset, length, DataReader.read_u32); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI32.new(rtt, r.1))); } I64 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 3))); + var r = getData(instance, data_index, offset, length, DataReader.read_u64); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI64.new(rtt, r.1))); } F32 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 2))); + var r = getData(instance, data_index, offset, length, DataReader.read_u32); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayF32.new(rtt, r.1))); } F64 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 3))); + var r = getData(instance, data_index, offset, length, DataReader.read_u64); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayF64.new(rtt, r.1))); } V128 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 4))); + var r = getData(instance, data_index, offset, length << 1, DataReader.read_u64); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayV128.new(rtt, r.1))); } _ => { - if (true) return stack.trap(TrapReason.ERROR); - var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); + return stack.trap(TrapReason.ERROR); } } } PACKED_I8 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 0))); + var r = getData(instance, data_index, offset, length, DataReader.read1); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI8.new(rtt, r.1))); } PACKED_I16 => { - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, t.1, 1))); + var r = getData(instance, data_index, offset, length, fun (d: DataReader) => DataReaders.read_range_u16(d.acquire(2))); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI16.new(rtt, r.1))); } } } @@ -803,147 +508,18 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var edecl = instance.module.elems[elem_index]; if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - match (ObjTuning.arrayMode) { - Typed => { - match (rtt.elem_types[0].pack) { - UNPACKED => { - match (rtt.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI32(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayI32.new(rtt, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI64(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayI64.new(rtt, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI32(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayF32.new(rtt, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI64(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayF64.new(rtt, vals))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI31(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayI31.new(rtt, vals))); - } else { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoObject(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayRef.new(rtt, vals))); - } - } - V128 => { - var vals = Array.new(u31.!(length << 1)); - var r = copyElemsIntoI64(vals, instance, 0, edecl, offset, length << 1); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayV128.new(rtt, vals))); - } - _ => { - var vals = Array.new(u31.!(length)); - var r = copyElemsInto(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds - stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); - } - } - } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI8(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayI8.new(rtt, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI16(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayI16.new(rtt, vals))); - } - } - } - Pair => { - match (rtt.elem_types[0].pack) { - UNPACKED => { - match (rtt.elem_types[0].valtype) { - I32, F32 => { - var len = length << 2; - var vals = Array.new(u31.!(len)); - var r = copyElemsIntoI32bytes(vals, instance, 0, edecl, offset, len); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 2))); - } - I64, F64 => { - var len = length << 3; - var vals = Array.new(u31.!(len)); - var r = copyElemsIntoI64bytes(vals, instance, 0, edecl, offset, len); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 3))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var len = length << 2; - var bytes = Array.new(u31.!(len)); - var r = copyElemsIntoI32bytes(bytes, instance, 0, edecl, offset, len); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, bytes, 2))); - } else { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoObject(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, vals, null, Values.OBJS_SHIFT))); - } - } - V128 => { - var len = length << 4; - var vals = Array.new(u31.!(len)); - var r = copyElemsIntoV128bytes(vals, instance, 0, edecl, offset, len); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 4))); - } - _ => { - var vals = Array.new(u31.!(length)); - var r = copyElemsInto(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds - stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); - } - } - } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - var r = copyElemsIntoI8(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 1))); - } - PACKED_I16 => { - var len = length << 1; - var vals = Array.new(u31.!(len)); - var r = copyElemsIntoI16bytes(vals, instance, 0, edecl, offset, len); - if (!r) return stack.trap(TrapReason.TABLE_OOB); - stack.push(Value.Ref(HeapArrayPair.new(rtt, null, vals, 1))); - } - } - } - _ => { - var vals = Array.new(u31.!(length)); - var r = copyElemsInto(vals, instance, 0, edecl, offset, length); - if (!r) return stack.trap(TrapReason.TABLE_OOB); // TODO: elem out of bounds - stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var r = getElems(instance, edecl, offset, length); + if (!r.0) return stack.trap(TrapReason.TABLE_OOB); + var vals = Array.new(u31.!(length)); + for (i < vals.length) vals[i] = Value.Ref(r.1[i]); + stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); + } else { + return ObjectRuntime.ARRAY_NEW_ELEM(stack, instance, rtt, edecl, offset, length); } return null; } + // JOE TODO: move our code below to ObjectReps. Check the ARRAY_NEW_ELEM function. def ARRAY_GET(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { var index = stack.popu(); var obj = stack.popArray(); @@ -1053,81 +629,6 @@ component Runtime { } } } - Pair => { - match (obj) { - x: HeapArrayValue => { - var r = x.vals[index ..+ size]; - for (i < r.length) r[i] = val; - } - x: HeapArrayPair => { - var uindex = u32.!(index); - match (stype.pack) { - UNPACKED => { - match (stype.valtype) { - I32 => { - var ival = i32.!(Value.I32.!(val).val); - for (u = uindex; u < size; u++) { - x.setI32(u, ival); - } - } - I64 => { - var ival = i64.!(Value.I64.!(val).val); - for (u = uindex; u < size; u++) { - x.setI64(u, ival); - } - } - F32 => { - var fval = float.!(Value.F32.!(val).bits); - for (u = uindex; u < size; u++) { - x.setF32(u, fval); - } - } - F64 => { - var fval = double.!(Value.F64.!(val).bits); - for (u = uindex; u < size; u++) { - x.setF64(u, fval); - } - } - V128 => { - var vals = Value.V128.!(val); - var low = vals.low; - var high = vals.high; - for (u = uindex; u < size; u++) { - x.setV128(u, low, high); - } - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var ival = i31.!(Value.I31.!(val).val); - for (u = uindex; u < size; u++) { - x.setI31(u, ival); - } - } else { - var rval = Value.Ref.!(val).val; - for (u = uindex; u < size; u++) { - x.setRef(u, rval); - } - } - } - _ => {} - } - } - PACKED_I8 => { - var ival = i8.!(Value.I32.!(val).val); - for (u = uindex; u < size; u++) { - x.setI8(u, ival); - } - } - PACKED_I16 => { - var ival = i16.!(Value.I32.!(val).val); - for (u = uindex; u < size; u++) { - x.setI16(u, ival); - } - } - } - } - } - } _ => { var vals = obj.getArray(); var r = vals[index ..+ size]; @@ -1286,49 +787,6 @@ component Runtime { } } } - Pair => { - // HeapArrayValue -> HeapArrayValue - if (HeapArrayValue.?(src) && HeapArrayValue.?(dst)) { - var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - // HeapArrayPair -> HeapArrayPair - if (HeapArrayPair.?(src) && HeapArrayPair.?(dst)) { - var src_arr = HeapArrayPair.!(src); - var dst_arr = HeapArrayPair.!(dst); - var shift = src_arr.shift; - - if (shift != dst_arr.shift) return stack.trap(TrapReason.ERROR); // Validation should prevent this - if (shift == Values.OBJS_SHIFT) { - var r = ArrayUtil.safeCopy(dst_arr.objs, dst_offset, src_arr.objs, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } else { - var r = ArrayUtil.safeCopy(dst_arr.bytes, dst_offset << shift, src_arr.bytes, src_offset << shift, size << shift); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - return null; - } - - // HeapArrayPair -> HeapArrayValue: shouldn't happen - if (HeapArrayValue.?(dst)) { - var arr = HeapArrayValue.!(dst); - var r = ArrayUtil.safeCopyDiffTypes(arr.vals, dst_offset, u31.view(src.length()), src_offset, size, src.getValue); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - // HeapArrayValue -> HeapArrayPair: shouldn't happen - if (HeapArrayValue.?(src)){ - var src_arr = HeapArrayValue.!(src); - var dst_arr = HeapArrayPair.!(dst); - - var r = ArrayUtil.safeCopyFromValueToPair(dst_arr, dst_offset, src_arr, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } _ => { var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); if (!r) return stack.trap(TrapReason.ARRAY_OOB); @@ -1436,43 +894,6 @@ component Runtime { } } } - Pair => { - match (obj) { - x: HeapArrayValue => { - var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - x: HeapArrayPair => { - var stype = rtt.elem_types[0]; - match (stype.pack) { - UNPACKED => { - match (stype.valtype) { - I32, F32 => { - size = size << 2; - } - I64, F64 => { - size = size << 3; - } - V128 => { - size = size << 4; - } - _ => {} - } - } - PACKED_I8 => { /* leave the size alone */ } - PACKED_I16 => { - size = size << 1; - } - } - var t = bytesToBytes(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.bytes, dst_offset, t.1, 0, size); - return null; - } - } - } _ => { var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); @@ -1496,86 +917,33 @@ component Runtime { match (ObjTuning.arrayMode) { Typed => { match (obj) { - x: HeapArrayValue => { - var r = copyElemsInto(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayI32 => { - var r = copyElemsIntoI32(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } x: HeapArrayI31 => { - var r = copyElemsIntoI31(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayI64 => { - var r = copyElemsIntoI64(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayI8 => { - var r = copyElemsIntoI8(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayI16 => { - var r = copyElemsIntoI16(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayF32 => { - var r = copyElemsIntoI32(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayF64 => { - var r = copyElemsIntoI64(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); + var r = getElems(instance, edecl, src_offset, size); + if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); + if (ArrayUtil.boundsCheck(x.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + for (i < r.1.length) { + x.vals[int.!(dst_offset)+i] = ObjectI31.!(r.1[i]).val; + } } x: HeapArrayRef => { - var r = copyElemsIntoObject(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayV128 => { - var r = copyElemsIntoI64(x.vals, instance, dst_offset, edecl, src_offset, size << 1); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - _ => {return stack.trap(TrapReason.ARRAY_OOB);} - } - } - Pair => { - match (obj) { - x: HeapArrayValue => { // shouldn't happen ... - var r = copyElemsInto(x.vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - } - x: HeapArrayPair => { - var stype = rtt.elem_types[0]; - match (stype.pack) { - UNPACKED => { - match (stype.valtype) { - I32, F32 => { - size = size << 2; - } - I64, F64 => { - size = size << 3; - } - V128 => { - size = size << 4; - } - _ => {} - } - } - PACKED_I8 => { /* leave the size alone */ } - PACKED_I16 => { - size = size << 1; - } + var r = getElems(instance, edecl, src_offset, size); + if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); + if (ArrayUtil.boundsCheck(x.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + for (i < r.1.length) { + x.vals[int.!(dst_offset)+i] = r.1[i]; } - var r = copyElemsIntoI8(x.bytes, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); } + _ => {return stack.trap(TrapReason.ARRAY_OOB);} } } _ => { - var vals = obj.getArray(); - var r = copyElemsInto(vals, instance, dst_offset, edecl, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); + var vals = HeapArrayValue.!(obj).vals; + var r = getElems(instance, edecl, src_offset, size); + if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); + if (ArrayUtil.boundsCheck(vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + for (i < r.1.length) { + vals[int.!(dst_offset)+i] = Value.Ref(r.1[i]); + } } } return null; @@ -1783,195 +1151,30 @@ component Runtime { } } } - def evalInitExprU32(instance: Instance, i: InitExpr) -> u32 { - var v = instance.evalInitExpr(i); - return Value.I32.!(v).val; - } - def copyElemsIntoI32(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { + def getElems(instance: Instance, elem: ElemDecl, src_offset: u64, size: u64) -> (bool, Array) { if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU32(instance, _)); - } - } - } - def store4(bytes: Array, index: u64, val: u32) { - Ref.at(bytes, int.!(index)).val = val; - } - def copyElemsIntoI32bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprU32(instance, _), store4(_, _, _), 4); - } - } - } - def evalInitExprU31(instance: Instance, i: InitExpr) -> u31 { - var v = instance.evalInitExpr(i); - return Value.I31.!(v).val; - } - def copyElemsIntoI31(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU31(instance, _)); - } - } - } - def evalInitExprU64(instance: Instance, i: InitExpr) -> u64 { - var v = instance.evalInitExpr(i); - return Value.I64.!(v).val; - } - def copyElemsIntoI64(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { - FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU64(instance, _)); - } - } - } - def store8(bytes: Array, index: u64, val: u64) { - Ref.at(bytes, int.!(index)).val = val; - } - def copyElemsIntoI64bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprU64(instance, _), store8(_, _, _), 8); - } - } - } - def evalInitExprV128(instance: Instance, i: InitExpr) -> (u64, u64) { - var v = instance.evalInitExpr(i); - var val = Value.V128.!(v); - return (val.low, val.high); - } - def store16(bytes: Array, index: u64, pair: (u64, u64)) { - var r = Ref.at(bytes, int.!(index)); - r.lo_val = pair.0; - r.hi_val = pair.1; - } - def copyElemsIntoV128bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprV128(instance, _), store16(_, _, _), 16); - } - } - } - def evalInitExprU8(instance: Instance, i: InitExpr) -> u8 { - var v = instance.evalInitExpr(i); - return u8.view(Value.I32.!(v).val); - } - def copyElemsIntoI8(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { - FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU8(instance, _)); - } - } - } - def evalInitExprU16(instance: Instance, i: InitExpr) -> u16 { - var v = instance.evalInitExpr(i); - return u16.view(Value.I32.!(v).val); - } - def copyElemsIntoI16(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; + if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return (false, null); + return (true, Array.new(0)); } + var dest = Array.new(int.!(size)); + var result = false; match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprU16(instance, _)); - } - } - } - def store2(bytes: Array, index: u64, val: u16) { - Ref.at(bytes, int.!(index)).val = val; - } - def copyElemsIntoI16bytes(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; - } - Exprs(vals) => { - return ArrayUtil.safeCopyFbytes(dest, dst_offset, vals, src_offset, size, evalInitExprU16(instance, _), store2(_, _, _), 2); - } - } - } - def evalInitExprObject(instance: Instance, i: InitExpr) -> Object { - var v = instance.evalInitExpr(i); - match (v) { - Ref(obj) => return obj; - I31(val) => return ObjectI31.new(val); - _ => return null; - } - } - def copyElemsIntoObject(dest: Array, instance: Instance, dst_offset: u32, elem: ElemDecl, src_offset: u32, size: u32) -> bool { - if (elem == null) { - if (ArrayUtil.boundsCheck(dest, dst_offset, size) < 0) return false; - if (ArrayUtil.boundsCheck(null, src_offset, size) < 0) return false; - return true; - } - match (elem.details) { FuncRefs(vals) => { - return false; + result = ArrayUtil.safeCopyF(dest, 0, vals, src_offset, size, fun(idx: int) => instance.functions[idx]); } Exprs(vals) => { - return ArrayUtil.safeCopyF(dest, dst_offset, vals, src_offset, size, evalInitExprObject(instance, _)); + result = ArrayUtil.safeCopyF(dest, 0, vals, src_offset, size, instance.evalInitExprObject); } } + return (result, if(result, dest)); + } + def getData(instance: Instance, data_index: u31, src_offset: u64, size: u64, func: DataReader -> T) -> (bool, Array) { + var data = if(!instance.dropped_data[data_index], instance.module.data[data_index]); + if (data == null) return (false, null); + var vals = Array.new(int.!(size)); + var d = DataReader.new(data.data).reset(data.data, int.!(src_offset), data.data.length); + for (i < vals.length) vals[i] = func(d); + return (d.ok, vals); } } class ExtendedDataReader extends DataReader { diff --git a/src/engine/Tuning.v3 b/src/engine/Tuning.v3 index 49e7a7a90..3215d6f09 100644 --- a/src/engine/Tuning.v3 +++ b/src/engine/Tuning.v3 @@ -70,7 +70,6 @@ component SpcTuning { enum ArrayMode(code: int) { Original(0), // Default value: HeapArrayValue only Typed(1), // HeapArrayX where X can be one of the storage types - Pair(2) // Having Array and Array where one of them is null } enum StructMode(code: int) { @@ -79,7 +78,7 @@ enum StructMode(code: int) { } def arrayModeFor(code: int) -> ArrayMode { - def modes = [ArrayMode.Original, ArrayMode.Typed, ArrayMode.Pair]; + def modes = [ArrayMode.Original, ArrayMode.Typed]; return if(code >= 0 && code < modes.length, modes[code], ArrayMode.Original); } diff --git a/src/engine/Value.v3 b/src/engine/Value.v3 index 9b4b2a92c..a8d58d17c 100644 --- a/src/engine/Value.v3 +++ b/src/engine/Value.v3 @@ -67,7 +67,7 @@ class HeapStructGeneric extends HeapObject { // Original Array struct representation // Provides definitions for Value getters/setter (only) -class HeapStruct extends HeapStructGeneric { +class HeapStructValue extends HeapStructGeneric { def vals: Array; new(decl: StructDecl, vals) super(decl) { } @@ -80,211 +80,6 @@ class HeapStruct extends HeapStructGeneric { def setFieldValue(index: u31, val: Value) { vals[index] = val; } } -// Paired array representation: an Array holds all the reference fields, -// and an Array holds all the primitive values -class HeapStructPair extends HeapStructGeneric { - def objs: Array; - def bytes: Array; - new(decl: StructDecl, objs, bytes) super(decl) { } - - def getFieldValue(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - match (decl.field_shifts[index]) { - -1 => { - return Value.Ref(objs[decl.field_offsets[index]]); - } - 0 => { - var v = bytes[decl.field_offsets[index]]; - return Value.I32(u32.view(v)); - } - 1 => { - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(v)); - } - 2 => { - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return if(decl.field_types[index].valtype == ValueType.I32, Value.I32(v), Value.F32(v)); - } - 3 => { - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return if(decl.field_types[index].valtype == ValueType.I64, Value.I64(v), Value.F64(v)); - } - 4 => { - var r = Ref.at(bytes, decl.field_offsets[index]); - return Value.V128(r.lo_val, r.hi_val); - } - } - return Value.I32(u32.view(-1)); - } - def getFieldSignExtend8Value(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(i32.!(v))); - } - def getFieldSignExtend16Value(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(i32.!(v))); - } - def getFieldZeroExtend8Value(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(v)); - } - def getFieldZeroExtend16Value(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(v)); - } - def getFieldI32(index: u32) -> i32 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I32)) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; - } - def getFieldI64(index: u32) -> i64 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I64)) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; - } - def getFieldI8(index: u32) -> i8 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I8) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; - } - def getFieldI16(index: u32) -> i16 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I16) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; - } - def getFieldF32(index: u32) -> float { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F32)) { return -1f; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; - } - def getFieldF64(index: u32) -> double { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F64)) { return -1d; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; - } - def getFieldRef(index: u32) -> Object { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && ValueType.Ref.?(ft.valtype)) { return null; } - return objs[decl.field_offsets[index]]; - } - def getFieldV128(index: u32) -> (u64, u64) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.V128)) { return (u64.view(-1), u64.view(-1)); } - var r = Ref.at(bytes, decl.field_offsets[index]); - return (r.lo_val, r.hi_val); - } - def setFieldValue(index: u31, val: Value) { - var decl = StructDecl.!(this.decl); - match (decl.field_shifts[index]) { - -1 => { - objs[decl.field_offsets[index]] = Value.Ref.!(val).val; - } - 0 => { - bytes[decl.field_offsets[index]] = u8.view(Value.I32.!(val).val); - } - 1 => { - Ref.at(bytes, decl.field_offsets[index]).val = u16.view(Value.I32.!(val).val); - } - 2 => { - if (Value.I32.?(val)) { - Ref.at(bytes, decl.field_offsets[index]).val = Value.I32.!(val).val; - } else { - Ref.at(bytes, decl.field_offsets[index]).val = Value.F32.!(val).bits; - } - } - 3 => { - if (Value.I64.?(val)) { - Ref.at(bytes, decl.field_offsets[index]).val = Value.I64.!(val).val; - } else { - Ref.at(bytes, decl.field_offsets[index]).val = Value.F64.!(val).bits; - } - } - 4 => { - var r = Ref.at(bytes, decl.field_offsets[index]); - var pair = Value.V128.!(val); - r.lo_val = pair.low; - r.hi_val = pair.high; - } - } - } - def setFieldI32(index: u32, val: i32) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I32)) { - Ref.at(bytes, decl.field_offsets[index]).val = u32.view(val); - } - } - def setFieldI64(index: u32, val: i64) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I64)) { - Ref.at(bytes, decl.field_offsets[index]).val = u64.view(val); - } - } - def setFieldI8(index: u32, val: i8) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I8) { - bytes[decl.field_offsets[index]] = u8.view(val); - } - } - def setFieldI16(index: u32, val: i16) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I16) { - Ref.at(bytes, decl.field_offsets[index]).val = val; - } - } - def setFieldF32(index: u32, val: float) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F32)) { - Ref.at(bytes, decl.field_offsets[index]).val = val; - } - } - def setFieldF64(index: u32, val: double) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F64)) { - Ref.at(bytes, decl.field_offsets[index]).val = val; - } - } - def setFieldRef(index: u32, val: Object) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || ValueType.Ref.?(ft.valtype)) { - objs[decl.field_offsets[index]] = val; - } - } - def setFieldV128(index: u32, low: u64, high: u64) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.V128)) { - var r = Ref.at(bytes, decl.field_offsets[index]); - r.lo_val = low; - r.hi_val = high; - } - } -} - // superclass of array representations; defines getter/setter interface class HeapArrayGeneric extends HeapObject { new(decl: ArrayDecl) super(decl) { } @@ -338,7 +133,7 @@ class HeapArrayGeneric extends HeapObject { def getU16(index: u32) -> u16; } -// Original Array representation +// Original Array array representation // Note: bounds checks will already have been done class HeapArrayValue extends HeapArrayGeneric { def vals: Array; @@ -416,496 +211,6 @@ class HeapArrayValue extends HeapArrayGeneric { } } -// Paired array representation: an Array is used for reference arrays, -// and an Array for primitive arrays; the unused array will be null. -class HeapArrayPair extends HeapArrayGeneric { - def objs: Array; - def bytes: Array; - def shift: byte; // the shift amount for indexing the byte array, etc. - new(decl: ArrayDecl, objs, bytes, shift) super(decl) { } - - def length() -> int { - // Check shift to decide which array: shift == OBJS_SHIFT means Ref array - if (shift == Values.OBJS_SHIFT) { - return objs.length; - } else { - return bytes.length >> shift; - } - } - - def getValue(index: u32) -> Value { - var d = ArrayDecl.!(decl); - match (d.elem_types[0].pack) { - UNPACKED => { - match (d.elem_types[0].valtype) { - I32 => return Value.I32(getU32(index)); - I64 => return Value.I64(getU64(index)); - F32 => return Value.F32(getU32(index)); - F64 => return Value.F64(getU64(index)); - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - return Value.I31(u31.view(getI31(index))); - } else { - match (getRef(index)) { - x: ObjectI31 => return Value.I31(x.val); - x: Object => return Value.Ref(x); - _ => return Values.REF_NULL; - } - } - } - V128 => { - var v128 = getV128(index); - return Value.V128(v128.0, v128.1); - } - _ => { - // This case should not happen - return Value.Ref(null); - } - } - } - PACKED_I8 => return Value.I32(u32.view(i32.!(getI8(index)))); - PACKED_I16 => return Value.I32(u32.view(i32.!(getI16(index)))); - } - } - def setValue(index: u32, val: Value) { - var d = ArrayDecl.!(decl); - match (d.elem_types[0].pack) { - UNPACKED => { - match (d.elem_types[0].valtype) { - I32 => setI32(index, i32.view(Value.I32.!(val).val)); - I64 => setI64(index, i64.view(Value.I64.!(val).val)); - F32 => setF32(index, float.view(Value.F32.!(val).bits)); - F64 => setF64(index, double.view(Value.F64.!(val).bits)); - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var uv: i31; - match (val) { - I31(v) => uv = i31.!(v); - _ => ; - } - setI31(index, uv); - } else { - var obj: Object; - match (val) { - Ref(o) => obj = o; - I31(v) => obj = ObjectI31.new(v); - _ => obj = null; - } - setRef(index, obj); - } - } - V128 => { - var v128 = Value.V128.!(val); - setV128(index, v128.low, v128.high); - } - _ => ; // This case should not happen - } - } - PACKED_I8 => setI8(index, i8.view(Value.I32.!(val).val)); - PACKED_I16 => setI16(index, i16.view(Value.I32.!(val).val)); - } - } - - def getI32(index: u32) -> i32 { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I32)) { - var offset = int.!(index << 2); - return Ref.at(bytes, offset).val; - } else { - return -1; - } - } - def setI32(index: u32, val: i32) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I32)) { - var offset = int.!(index << 2); - Ref.at(bytes, offset).val = val; - } - } - def getI31(index: u32) -> i31 { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.Ref(false, HeapType.I31))) { - var offset = int.!(index << 2); - return Ref.at(bytes, offset).val; - } else { - return -1; - } - } - def setI31(index: u32, val: i31) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.Ref(false, HeapType.I31))) { - var offset = int.!(index << 2); - Ref.at(bytes, offset).val = val; - } - } - def getI64(index: u32) -> i64 { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I64)) { - var offset = int.!(index << 3); - return Ref.at(bytes, offset).val; - } else { - return -1; - } - } - def setI64(index: u32, val: i64) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.I64)) { - var offset = int.!(index << 3); - Ref.at(bytes, offset).val = val; - } - } - - def getI8(index: u32) -> i8 { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I8) { - var offset = int.!(index); - return i8.view(bytes[offset]); - } else { - return -1; - } - } - def setI8(index: u32, val: i8) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I8) { - var offset = int.!(index); - bytes[offset] = u8.view(val); - } - } - def getI16(index: u32) -> i16 { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I16) { - var offset = int.!(index << 1); - return Ref.at(bytes, offset).val; - } else { - return -1; - } - } - def setI16(index: u32, val: i16) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || etype.pack == Packedness.PACKED_I16) { - var offset = int.!(index << 1); - Ref.at(bytes, offset).val = val; - } - } - def getF32(index: u32) -> float { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F32)) { - var offset = int.!(index << 2); - return Ref.at(bytes, offset).val; - } else { - return -1f; - } - } - def setF32(index: u32, val: float) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F32)) { - var offset = int.!(index << 2); - Ref.at(bytes, offset).val = val; - } - } - def getF64(index: u32) -> double { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F64)) { - var offset = int.!(index << 3); - return Ref.at(bytes, offset).val; - } else { - return -1d; - } - } - def setF64(index: u32, val: double) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || (etype.pack == Packedness.UNPACKED && etype.valtype == ValueType.F64)) { - var offset = int.!(index << 3); - Ref.at(bytes, offset).val = val; - } - } - def getRef(index: u32) -> Object { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || ValueType.Ref.?(etype.valtype)) { - return objs[index]; - } else { - return null; - } - } - def setRef(index: u32, val: Object) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || ValueType.Ref.?(etype.valtype)) { - objs[index] = val; - } - } - def getV128(index: u32) -> (u64, u64) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || etype.valtype == ValueType.V128) { - var offset = int.!(index << 4); - var r = Ref.at(bytes, offset); - return (r.lo_val, r.hi_val); - } else { - return (u64.view(-1), u64.view(-1)); - } - } - def setV128(index: u32, low: u64, high: u64) { - var d = ArrayDecl.!(decl); - var etype = d.elem_types[0]; - if (Values.NO_CHECKS || etype.valtype == ValueType.V128) { - var offset = int.!(index << 4); - var r = Ref.at(bytes, offset); - r.lo_val = low; - r.hi_val = high; - } - } - def getU32(index: u32) -> u32 { - if (Values.NO_CHECKS || shift == 2) { - var offset = int.!(index << 2); - return Ref.at(bytes, offset).val; - } else { - return u32.view(-1); - } - } - def getU64(index: u32) -> u64 { - if (Values.NO_CHECKS || shift == 3) { - var offset = int.!(index << 3); - return Ref.at(bytes, offset).val; - } else { - return u64.view(-1); - } - } - def getU8(index: u32) -> u8 { - if (Values.NO_CHECKS || shift == 0) { - var offset = int.!(index); - return bytes[offset]; - } else { - return u8.view(-1); - } - } - def getU16(index: u32) -> u16 { - if (Values.NO_CHECKS || shift == 1) { - var offset = int.!(index << 1); - return Ref.at(bytes, offset).val; - } else { - return u16.view(-1); - } - } -} - - -// Implementations of arrays of specific types -class HeapArrayI32 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.I32(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u32.view(Value.I32.!(val).val); - } - def getI32(index: u32) -> i32 { - return i32.view(vals[index]); - } - def setI32(index: u32, val: i32) { - vals[index] = u32.view(val); - } -} - -class HeapArrayI31 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.I31(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u31.view(Value.I31.!(val).val); - } - def getI31(index: u32) -> i31 { - return i31.view(vals[index]); - } - def setI31(index: u32, val: i31) { - vals[index] = u31.view(val); - } -} - -class HeapArrayI64 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.I64(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u64.view(Value.I64.!(val).val); - } - def getI64(index: u32) -> i64 { - return i64.view(vals[index]); - } - def setI64(index: u32, val: i64) { - vals[index] = u64.view(val); - } -} - -class HeapArrayI8 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.I32(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u8.view(Value.I32.!(val).val); - } - def getI8(index: u32) -> i8 { - return i8.view(vals[index]); - } - def setI8(index: u32, val: i8) { - vals[index] = u8.view(val); - } -} - -class HeapArrayI16 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.I32(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u16.view(Value.I32.!(val).val); - } - def getI16(index: u32) -> i16 { - return i16.view(vals[index]); - } - def setI16(index: u32, val: i16) { - vals[index] = u16.view(val); - } -} - -class HeapArrayF32 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.F32(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u32.view(Value.F32.!(val).bits); - } - def getF32(index: u32) -> float { - return if(index >= 0 && index < vals.length, float.view(vals[index]), -1); - } - def setF32(index: u32, val: float) { - if (index >= 0 && index < vals.length) { - vals[index] = u32.view(val); - } - } -} - -class HeapArrayF64 extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - return Value.F64(vals[index]); - } - def setValue(index: u32, val: Value) { - vals[index] = u64.view(Value.F64.!(val).bits); - } - def getF64(index: u32) -> double { - return double.view(vals[index]); - } - def setF64(index: u32, val: double) { - vals[index] = u64.view(val); - } -} - -class HeapArrayRef extends HeapArrayGeneric { - def vals: Array; - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length; } - - def getValue(index: u32) -> Value { - match (vals[index]) { - x: ObjectI31 => return Value.I31(x.val); - x: Object => return Value.Ref(x); - _ => return Values.REF_NULL; - } - } - def setValue(index: u32, val: Value) { - var obj: Object; - match (val) { - Ref(v) => obj = v; - I31(v) => obj = ObjectI31.new(v); - _ => ; - } - vals[index] = obj; - } - def getRef(index: u32) -> Object { - return vals[index]; - } - def setRef(index: u32, val: Object) { - vals[index] = val; - } -} - -// A v128 uses two adjacent u64 entries in the vals array -class HeapArrayV128 extends HeapArrayGeneric { - def vals: Array; // The array format: [low 0, high 0, low 1, high 1, ...] - new(decl: ArrayDecl, vals) super(decl) { } - - def length() -> int { return vals.length >> 1; } - - def getValue(index: u32) -> Value { - var idx = index << 1; - return Value.V128(vals[idx], vals[idx+1]); - } - def setValue(index: u32, val: Value) { - var idx = index << 1; - var v128 = Value.V128.!(val); - vals[idx] = u64.view(v128.low); - vals[idx+1] = u64.view(v128.high); - } - def getV128(index: u32) -> (u64, u64) { - var idx = index << 1; - return (vals[idx], vals[idx+1]); - } - def setV128(index: u32, low: u64, high: u64) { - var idx = index << 1; - vals[idx] = low; - vals[idx+1] = high; - } -} - // Utilities associated with values. component Values { def I32_1 = Value.I32(1); @@ -933,7 +238,7 @@ component Values { Ref(val) => match (val) { x: HostObject => buf.put1("", x.render); x: WasmFunction => buf.put1("", x.decl.func_index); - x: HeapStruct => { + x: HeapStructGeneric => { var id = if(x.decl == null, -1, x.decl.heaptype_index); buf.put1("", id); } diff --git a/src/engine/compiler/MacroAssembler.v3 b/src/engine/compiler/MacroAssembler.v3 index 5a9ca0fa4..4fb1c9fa1 100644 --- a/src/engine/compiler/MacroAssembler.v3 +++ b/src/engine/compiler/MacroAssembler.v3 @@ -166,14 +166,11 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_v3_HeapArray_vals_r_r(dst: Reg, ptr: Reg) { emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArray_vals)); } - def emit_v3_HeapArrayPair_objs_r_r(dst: Reg, ptr: Reg) { - emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArrayPair_objs)); + def emit_v3_HeapStructPair_objs_r_r(dst: Reg, ptr: Reg) { + emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapStructPair_objs)); } - def emit_v3_HeapArrayPair_bytes_r_r(dst: Reg, ptr: Reg) { - emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArrayPair_bytes)); - } - def emit_v3_HeapArrayPair_shift_r_r(dst: Reg, ptr: Reg) { - emit_movbsx_r_m(ValueKind.I32, dst, MasmAddr(ptr, getOffsets().HeapArrayPair_shift)); + def emit_v3_HeapStructPair_bytes_r_r(dst: Reg, ptr: Reg) { + emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapStructPair_bytes)); } def emit_v3_HeapArrayI32_vals_r_r(dst: Reg, ptr: Reg) { emit_mov_r_m(ValueKind.REF, dst, MasmAddr(ptr, getOffsets().HeapArrayI32_vals)); diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index 9dccb49b2..a516e4a37 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1678,9 +1678,9 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck masm.emit_breq_r_l(str, 0, label); if (ValueType.Ref.?(etype)) { - masm.emit_v3_HeapArrayPair_objs_r_r(tmp, str); + masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); } else { - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); } masm.emit_mov_r_m(kind, dst, MasmAddr(tmp, offset + masm.getOffsets().Array_contents)); state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); @@ -1700,7 +1700,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var tmp = allocTmp(ValueKind.REF); var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck masm.emit_breq_r_l(str, 0, label); - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); match (decl.field_types[field_index].pack) { PACKED_I8 => masm.emit_loadbsx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); PACKED_I16 => masm.emit_loadwsx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); @@ -1723,7 +1723,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var tmp = allocTmp(ValueKind.REF); var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck masm.emit_breq_r_l(str, 0, label); - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); match (decl.field_types[field_index].pack) { PACKED_I8 => masm.emit_loadbzx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); PACKED_I16 => masm.emit_loadwzx_r_r_r_i(ValueKind.I32, dst, tmp, NO_REG, u32.!(offset + masm.getOffsets().Array_contents)); @@ -1756,9 +1756,9 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck masm.emit_breq_r_l(str, 0, label); if (ValueType.Ref.?(etype)) { - masm.emit_v3_HeapArrayPair_objs_r_r(tmp, str); + masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); } else { - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, str); + masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); } masm.emit_mov_m_r(kind, MasmAddr(tmp, offset + masm.getOffsets().Array_contents), val); } @@ -1782,7 +1782,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl def visit_ARRAY_NEW_ELEM(ht_index: u31, elem_index: u31) { emit_call_runtime_op2n(Opcode.ARRAY_NEW_ELEM, ht_index, elem_index, 2, ValueTypes.ONE_ARRAYREF_TYPE, true); } - def visit_ARRAY_GET_Pair(kind: ValueKind, arr: Reg, idx: Reg); def visit_ARRAY_GET(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); var stg_type = decl.elem_types[0]; @@ -1795,13 +1794,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl return; } match (ObjTuning.arrayMode) { - Pair => { - var idx = popReg().reg; - var arr = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(arr, 0, label); - visit_ARRAY_GET_Pair(kind, arr, idx); - } Typed => { var idx = popReg().reg; var arr = popReg().reg; @@ -1821,20 +1813,12 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } } } - def visit_ARRAY_GET_S_Pair(pack: Packedness, arr: Reg, idx: Reg); def visit_ARRAY_GET_S(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); var stg_type = decl.elem_types[0]; var etype = stg_type.valtype; var kind = ValueTypes.kind(etype); match (ObjTuning.arrayMode) { - Pair => { - var idx = popReg().reg; - var arr = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(arr, 0, label); - visit_ARRAY_GET_S_Pair(stg_type.pack, arr, idx); - } Typed => { var idx = popReg().reg; var arr = popReg().reg; @@ -1857,20 +1841,12 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } } } - def visit_ARRAY_GET_U_Pair(pack: Packedness, arr: Reg, idx: Reg); def visit_ARRAY_GET_U(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); var stg_type = decl.elem_types[0]; var etype = stg_type.valtype; var kind = ValueTypes.kind(etype); match (ObjTuning.arrayMode) { - Pair => { - var idx = popReg().reg; - var arr = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(arr, 0, label); - visit_ARRAY_GET_U_Pair(stg_type.pack, arr, idx); - } Typed => { var idx = popReg().reg; var arr = popReg().reg; @@ -1893,7 +1869,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } } } - def visit_ARRAY_SET_Pair(kind: ValueKind, arr: Reg, idx: Reg, val: Reg); // requires target specific code sequences def visit_ARRAY_SET(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); var stg_type = decl.elem_types[0]; @@ -1906,14 +1881,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl return; } match (ObjTuning.arrayMode) { - Pair => { - var val = popReg().reg; - var idx = popReg().reg; - var arr = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(arr, 0, label); - visit_ARRAY_SET_Pair(kind, arr, idx, val); - } Typed => { var val = popReg().reg; var idx = popReg().reg; @@ -1934,7 +1901,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } } } - def visit_ARRAY_LEN_Pair(reg: Reg); // requires target specific code sequences def visit_ARRAY_LEN() { match (ObjTuning.arrayMode) { Original, Typed => { @@ -1947,12 +1913,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_v3_Array_length_r_r(dst, tmp); state.push(KIND_I32 | IN_REG, dst, 0); } - Pair => { - var sv = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(sv, 0, label); - visit_ARRAY_LEN_Pair(sv); - } // use as an easy handler if new array modes are added in the future // _ => { // emit_call_runtime_op0n(Opcode.ARRAY_LEN, 1, [ValueType.I32], true); diff --git a/src/engine/x86-64/V3Offsets.v3 b/src/engine/x86-64/V3Offsets.v3 index 6d7a9fd01..c8b2ff334 100644 --- a/src/engine/x86-64/V3Offsets.v3 +++ b/src/engine/x86-64/V3Offsets.v3 @@ -17,7 +17,7 @@ class V3Offsets { private def vs = X86_64Stack.new(2u * 4096u); private def acc = X86_64FrameAccessor.new(vs, Pointer.NULL, decl); private def ha = HeapArrayValue.new(null, []); - private def hp = HeapArrayPair.new(null, null, null, 0); + private def hsp = HeapStructPair.new(null, null, null); private def hi = HeapArrayI32.new(null, []); private def cnt = CountProbe.new(); private def whamm_Probe = WhammProbe.new(null, []); @@ -79,9 +79,8 @@ class V3Offsets { def HeapArray_vals = int.view(Pointer.atField(ha.vals) - Pointer.atObject(ha)); - def HeapArrayPair_objs = int.view(Pointer.atField(hp.objs) - Pointer.atObject(hp)); - def HeapArrayPair_bytes = int.view(Pointer.atField(hp.bytes) - Pointer.atObject(hp)); - def HeapArrayPair_shift = int.view(Pointer.atField(hp.shift) - Pointer.atObject(hp)); + def HeapStructPair_objs = int.view(Pointer.atField(hsp.objs) - Pointer.atObject(hsp)); + def HeapStructPair_bytes = int.view(Pointer.atField(hsp.bytes) - Pointer.atObject(hsp)); def HeapArrayI32_vals = int.view(Pointer.atField(hi.vals) - Pointer.atObject(hi)); diff --git a/src/engine/x86-64/X86_64Interpreter.v3 b/src/engine/x86-64/X86_64Interpreter.v3 index e28dc2af8..21c79db8d 100644 --- a/src/engine/x86-64/X86_64Interpreter.v3 +++ b/src/engine/x86-64/X86_64Interpreter.v3 @@ -2279,13 +2279,6 @@ class X86_64InterpreterGen(ic: X86_64InterpreterCode, w: DataWriter) { asm.movd_m_r(vsph[-1].value, r_tmp0); genTagUpdate(BpTypeCode.I32.code); } - _ => { - masm.emit_get_curstack(xenv.tmp3); - saveCallerIVars(); - callRuntime(refRuntimeCall(RT.ARRAY_LEN), [r_tmp3, r_instance], false); - asm.movbzx_r_r(r_tmp0, Target.V3_RET_GPRS[0]); // XXX: restore just VSP and update first? - restoreCallerIVars(); - } } endHandler(); } diff --git a/src/engine/x86-64/X86_64SinglePassCompiler.v3 b/src/engine/x86-64/X86_64SinglePassCompiler.v3 index d3521ba25..aae13bbc9 100644 --- a/src/engine/x86-64/X86_64SinglePassCompiler.v3 +++ b/src/engine/x86-64/X86_64SinglePassCompiler.v3 @@ -1005,119 +1005,6 @@ class X86_64SinglePassCompiler extends SinglePassCompiler { def visit_ATOMIC_FENCE(flags: u8) { asm.mfence(); } - - def visit_ARRAY_GET_S_Pair(pack: Packedness, arr: Reg, idx: Reg) { - var tmp = allocTmp(ValueKind.REF); - var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); - var dst = allocRegTos(ValueKind.I32); - match (pack) { - PACKED_I8 => { - asm.d.cmp_r_m(G(idx), X86_64Addr.new(G(tmp), null, 1, masm.getOffsets().Array_length)); - asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); - asm.d.movbsx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 1, masm.getOffsets().Array_contents)); - } - PACKED_I16 => { - var tmp2 = allocTmp(ValueKind.I32); - masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); - masm.emit_shrw_r_i(tmp2, 1); - asm.d.cmp_r_r(G(idx), G(tmp2)); - asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); - asm.d.movwsx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 2, masm.getOffsets().Array_contents)); - } - _ => ; - } - state.push(SpcConsts.KIND_I32 | IN_REG, dst, 0); - } - - def visit_ARRAY_GET_U_Pair(pack: Packedness, arr: Reg, idx: Reg) { - var tmp = allocTmp(ValueKind.REF); - var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); - var dst = allocRegTos(ValueKind.I32); - match (pack) { - PACKED_I8 => { - asm.d.cmp_r_m(G(idx), X86_64Addr.new(G(tmp), null, 1, masm.getOffsets().Array_length)); - asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); - asm.d.movbzx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 1, masm.getOffsets().Array_contents)); - } - PACKED_I16 => { - var tmp2 = allocTmp(ValueKind.I32); - masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); - masm.emit_shrw_r_i(tmp2, 1); - asm.d.cmp_r_r(G(idx), G(tmp2)); - asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); - asm.d.movwzx_r_m(G(dst), X86_64Addr.new(G(tmp), G(idx), 2, masm.getOffsets().Array_contents)); - } - _ => ; - } - state.push(SpcConsts.KIND_I32 | IN_REG, dst, 0); - } - - def visit_ARRAY_GET_Pair(kind: ValueKind, arr: Reg, idx: Reg) { - var tmp = allocTmp(ValueKind.REF); - var dst = allocRegTos(kind); - var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); - match (kind) { - REF => { - masm.emit_v3_HeapArrayPair_objs_r_r(tmp, arr); - masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); - } - _ => { - // use the bytes array; shift index by size factor - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); - var tmp2 = allocTmp(ValueKind.I32); - masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); - masm.emit_shrw_r_i(tmp2, masm.logScaleOf(kind)); - asm.d.cmp_r_r(G(idx), G(tmp2)); - asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); - } - } - masm.emit_v3_Array_elem_r_rr(kind, dst, tmp, idx); - state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); - } - - def visit_ARRAY_SET_Pair(kind: ValueKind, arr: Reg, idx: Reg, val: Reg) { - var tmp = allocTmp(ValueKind.REF); - var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); - match (kind) { - REF => { - masm.emit_v3_HeapArrayPair_objs_r_r(tmp, arr); - masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); - } - _ => { - // use the bytes array; shift index by size factor - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, arr); - var tmp2 = allocTmp(ValueKind.I32); - masm.emit_mov_r_m(ValueKind.I32, tmp2, MasmAddr(tmp, masm.getOffsets().Array_length)); - masm.emit_shrw_r_i(tmp2, masm.logScaleOf(kind)); - asm.d.cmp_r_r(G(idx), G(tmp2)); - asm.jc_rel_far(X86_64Conds.NC, X86_64MasmLabel.!(oob_label).label); - } - } - masm.emit_v3_Array_elem_rr_r(kind, tmp, idx, val); - } - - def visit_ARRAY_LEN_Pair(reg: Reg) { - var tmp0 = allocTmpFixed(ValueKind.I32, X86_64MasmRegs.RCX); // shift count must be here - var tmp = allocTmp(ValueKind.REF); // will hold ref to array - var dst = allocRegTos(ValueKind.I32); // will receive the length result - asm.q.movbsx_r_m(G(tmp0), X86_64Addr.new(G(reg), null, 1, V3Offsets.new().HeapArrayPair_shift)); - var objs = X86_64Label.new(); - asm.d.cmp_r_i(G(tmp0), 0); - asm.jc_rel_near(X86_64Conds.L, objs); - masm.emit_v3_HeapArrayPair_bytes_r_r(tmp, reg); // bytes case; get bytes array - masm.emit_v3_Array_length_r_r(dst, tmp); // get its length - asm.d.shr_r_cl(G(dst)); // shift right by shift count - var done = X86_64Label.new(); - asm.jmp_rel_near(done); - asm.bind(objs); - masm.emit_v3_HeapArrayPair_objs_r_r(tmp, reg); // objs case; get objs array - masm.emit_v3_Array_length_r_r(dst, tmp); // get its length - asm.bind(done); - state.push(KIND_I32 | IN_REG, dst, 0); - } - // r1 = op(r1) private def do_op1_r(kind: ValueKind, emit: (X86_64Gpr -> T)) -> bool { var sv = popRegToOverwrite(), r = G(sv.reg); diff --git a/src/util/ArrayUtil.v3 b/src/util/ArrayUtil.v3 index af6bb13d7..1a62ce53d 100644 --- a/src/util/ArrayUtil.v3 +++ b/src/util/ArrayUtil.v3 @@ -49,26 +49,6 @@ component ArrayUtil { } return true; } - def safeCopyFromValueToPair(dst: HeapArrayPair, dst_offset: u32, src: HeapArrayValue, src_offset: u32, size: u32) -> bool { - // Check HeapArrayPair - var shift = dst.shift; - var i = -1; - if (shift == Values.OBJS_SHIFT) { - i = boundsCheck(dst.objs, dst_offset, size); - if (i < 0) return false; - } else { - i = boundsCheck(dst.bytes, dst_offset << shift, size << shift); - if (i < 0) return false; - } - // Check HeapArrayValue - var j = boundsCheck(src.vals, src_offset, size); - if (j < 0) return false; - - for (k < int.!(size)) { - dst.setValue(u32.view(i + k), src.getValue(u32.view(j + k))); - } - return true; - } def safeCopyDiffTypes(dst: Array, dst_offset: u32, src_length: u31, src_offset: u32, size: u32, f: u32 -> T) -> bool { var i = boundsCheck(dst, dst_offset, size); diff --git a/test/unittest/ExeTest.v3 b/test/unittest/ExeTest.v3 index 6b231c951..703048403 100644 --- a/test/unittest/ExeTest.v3 +++ b/test/unittest/ExeTest.v3 @@ -2497,10 +2497,6 @@ def test_array_get(t: ExeTester) { var vals = Array.new(len); arr = HeapArrayF64.new(at, vals); } - Pair => { - var bytes = Array.new(len << 3); - arr = HeapArrayPair.new(at, null, bytes, 3); - } _ => { var vals = Array.new(len); arr = HeapArrayValue.new(at, vals); @@ -2557,24 +2553,6 @@ def test_array_get_su(t: ExeTester) { _ => ; } } - Pair => { - match (pack) { - PACKED_I8 => { - var elems = Array.new(len); - arr = HeapArrayPair.new(at, null, elems, 0); - for (j < len) elems[j] = u8.view(0xFF7Fu + u32.view(j)); - } - PACKED_I16 => { - var elems = Array.new(len << 1); - arr = HeapArrayPair.new(at, null, elems, 1); - for (j < len) { - var val = u16.view(0xFF7Fu + u32.view(j)); - Ref.at(elems, j << 1).val = val; - } - } - _ => ; - } - } } var obj = Value.Ref(arr); @@ -2610,11 +2588,6 @@ def test_array_set(t: ExeTester) { var vals = [0x99999u64, 55u64]; obj = HeapArrayF64.new(at, vals); } - Pair => { - var bytes = [0x99u8, 0x99u8, 0x9u8, 0u8, 0u8, 0u8, 0u8, 0u8, - 55u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]; - obj = HeapArrayPair.new(at, null, bytes, 3); - } _ => { var vals: Array = [Value.F64(0x99999), Value.F64(55)]; obj = HeapArrayValue.new(at, vals); @@ -2652,10 +2625,6 @@ def test_array_len(t: ExeTester) { var vals = Array.new(i); arr = HeapArrayF64.new(at, vals); } - Pair => { - var bytes = Array.new(i << 3); - arr = HeapArrayPair.new(at, null, bytes, 3); - } _ => { var vals = Array.new(i); arr = HeapArrayValue.new(at, vals); @@ -3184,7 +3153,7 @@ def heapStructNew(decl: StructDecl, vals: Array) -> HeapStructGeneric { for (i < fields.length) { fields[i] = vals[i]; } - return HeapStruct.new(decl, fields); + return HeapStructValue.new(decl, fields); } Pair => { var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); From b4a40f716a41598764e097c10ecf143abd241c34 Mon Sep 17 00:00:00 2001 From: Meng-chieh Chiu Date: Fri, 23 Jan 2026 12:08:27 -0500 Subject: [PATCH 03/11] create all the functions in ObjectReps --- src/engine/Runtime.v3 | 780 ++++-------------------------------------- 1 file changed, 70 insertions(+), 710 deletions(-) diff --git a/src/engine/Runtime.v3 b/src/engine/Runtime.v3 index 5a6064cb1..4e57b8c0b 100644 --- a/src/engine/Runtime.v3 +++ b/src/engine/Runtime.v3 @@ -38,110 +38,28 @@ component Runtime { def STRUCT_NEW(stack: ExecStack, instance: Instance, struct_index: u31) { var decl = StructDecl.!(instance.heaptypes[struct_index]); var obj: HeapStructGeneric; - match (ObjTuning.structMode) { - Pair => { - var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); - obj = HeapStructPair.new(decl, objs, bytes); - - for (i = decl.field_types.length - 1; i >= 0; i--) { - var index = u31.!(i); - match (decl.field_shifts[i]) { - 0 => { - var val = stack.popi(); - obj.setFieldI8(index, i8.view(val)); - } - 1 => { - var val = stack.popi(); - obj.setFieldI16(index, i16.view(val)); - } - 2 => { - if (decl.field_types[i].valtype == ValueType.I32) { - var val = stack.popi(); - obj.setFieldI32(index, val); - } else { - var val = stack.popf(); - obj.setFieldF32(index, val); - } - } - 3 => { - if (decl.field_types[i].valtype == ValueType.I64) { - var val = stack.popl(); - obj.setFieldI64(index, val); - } else { - var val = stack.popd(); - obj.setFieldF64(index, val); - } - } - 4 => { - var pair = stack.pops(); - obj.setFieldV128(index, pair.0, pair.1); - } - -1 => { - var o = stack.popObject(); - obj.setFieldRef(index, o); - } - } - } - } - _ => { - var fields = Array.new(decl.field_types.length); - for (i = fields.length - 1; i >= 0; i--) { - fields[i] = stack.popV(decl.field_types[i].valtype); - } - obj = HeapStructValue.new(decl, fields); + if (ObjTuning.structMode == StructMode.Original) { + var fields = Array.new(decl.field_types.length); + for (i = fields.length - 1; i >= 0; i--) { + fields[i] = stack.popV(decl.field_types[i].valtype); } + obj = HeapStructValue.new(decl, fields); + } else { + obj = ObjectRuntime.STRUCT_NEW(stack, decl); } stack.push(Value.Ref(obj)); } def STRUCT_NEW_DEFAULT(stack: ExecStack, instance: Instance, struct_index: u31) { var decl = StructDecl.!(instance.heaptypes[struct_index]); var obj: HeapStructGeneric; - match (ObjTuning.structMode) { - Pair => { - var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); - obj = HeapStructPair.new(decl, objs, bytes); - - for (i < decl.field_types.length) { - var index = u31.view(i); - match (decl.field_shifts[i]) { - 0 => { - obj.setFieldI8(index, 0); - } - 1 => { - obj.setFieldI16(index, 0); - } - 2 => { - if (decl.field_types[i].valtype == ValueType.I32) { - obj.setFieldI32(index, 0); - } else { - obj.setFieldF32(index, 0.0f); - } - } - 3 => { - if (decl.field_types[i].valtype == ValueType.I64) { - obj.setFieldI64(index, 0); - } else { - obj.setFieldF64(index, 0.0d); - } - } - 4 => { - obj.setFieldV128(index, 0, 0); - } - -1 => { - obj.setFieldRef(index, null); - } - } - } - } - _ => { - var fields = Array.new(decl.field_types.length); - for (i < fields.length) { - fields[i] = Values.default(decl.field_types[i].valtype); - } - obj = HeapStructValue.new(decl, fields); + if (ObjTuning.structMode == StructMode.Original) { + var fields = Array.new(decl.field_types.length); + for (i < fields.length) { + fields[i] = Values.default(decl.field_types[i].valtype); } + obj = HeapStructValue.new(decl, fields); + } else { + obj = ObjectRuntime.STRUCT_NEW_DEFAULT(decl); } stack.push(Value.Ref(obj)); } @@ -201,88 +119,13 @@ component Runtime { var decl = ArrayDecl.!(instance.heaptypes[array_index]); var length = stack.popu(); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - match (ObjTuning.arrayMode) { - Typed => { - match (decl.elem_types[0].pack) { - UNPACKED => { - match (decl.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - var elem = stack.popu(); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - var elem = stack.popw(); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - var elem = u32.view(stack.popf()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - var elem = u64.view(stack.popd()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); - var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); - var val = ObjectI31.!(Value.Ref.!(elem).val).val; - for (i < vals.length) vals[i] = val; - stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); - } else { - var vals = Array.new(u31.!(length)); - var elem = stack.popObject(); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); - } - } - V128 => { - var vals = Array.new(u31.!(length << 1)); - var v128 = stack.pops(); - var low = v128.0; - var high = v128.1; - for (i = 0; i < vals.length; i += 2) { - vals[i] = low; - vals[i+1] = high; - } - stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); - } - _ => { - var vals = Array.new(u31.!(length)); - var elem = stack.popV(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } - } - } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - var elem = u8.view(stack.popu()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - var elem = u16.view(stack.popu()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); - } - } - } - _ => { - var vals = Array.new(u31.!(length)); - var elem = stack.popV(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } else { + return ObjectRuntime.ARRAY_NEW(stack, decl, length); } return null; } @@ -290,152 +133,26 @@ component Runtime { var decl = ArrayDecl.!(instance.heaptypes[array_index]); var length = stack.popu(); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - match (ObjTuning.arrayMode) { - Typed => { - match (decl.elem_types[0].pack) { - UNPACKED => { - match (decl.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u32; - stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u64; - stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = u32.view(0.0f); - stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = u64.view(0.0d); - stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = null; - stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); - } else { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = null; - stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); - } - } - V128 => { - var vals = Array.new(u31.!(length << 1)); - // for (i < vals.length) vals[i] = 0u64; - stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); - } - _ => { - var vals = Array.new(u31.!(length)); - var elem = Values.default(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } - } - } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u8; - stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u16; - stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); - } - } - } - _ => { - var vals = Array.new(u31.!(length)); - var elem = Values.default(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var vals = Array.new(u31.!(length)); + var elem = Values.default(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } else { + return ObjectRuntime.ARRAY_NEW_DEFAULT(stack, decl, length); } return null; } def ARRAY_NEW_FIXED(stack: ExecStack, instance: Instance, array_index: u31, length: u32) -> Throwable { var decl = ArrayDecl.!(instance.heaptypes[array_index]); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - - match (ObjTuning.arrayMode) { - Typed => { - match (decl.elem_types[0].pack) { - UNPACKED => { - match (decl.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popu()); - stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popw()); - stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popf()); - stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popd()); - stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = ObjectI31.!(stack.popObject()).val; - stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); - - } else { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popObject(); - stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); - } - } - V128 => { - var vals = Array.new(u31.!(length << 1)); - for (i = vals.length - 2; i >= 0; i -= 2) { - var v128 = stack.pops(); - vals[i] = v128.0; - vals[i+1] = v128.1; - } - stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); - } - _ => { - var vals = Array.new(u31.!(length)); - var t = decl.elem_types[0].valtype; - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } - } - } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u8.view(stack.popu()); - stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u16.view(stack.popu()); - stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); - } - } - } - _ => { - var vals = Array.new(u31.!(length)); - var t = decl.elem_types[0].valtype; - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var vals = Array.new(u31.!(length)); + var t = decl.elem_types[0].valtype; + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } else { + return ObjectRuntime.ARRAY_NEW_FIXED(stack, decl, length); } return null; } @@ -446,58 +163,12 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var ddecl = instance.module.data[data_index]; if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - match (ObjTuning.arrayMode) { - Typed => { - match (rtt.elem_types[0].pack) { - UNPACKED => { - match (rtt.elem_types[0].valtype) { - I32 => { - var r = getData(instance, data_index, offset, length, DataReader.read_u32); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI32.new(rtt, r.1))); - } - I64 => { - var r = getData(instance, data_index, offset, length, DataReader.read_u64); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI64.new(rtt, r.1))); - } - F32 => { - var r = getData(instance, data_index, offset, length, DataReader.read_u32); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayF32.new(rtt, r.1))); - } - F64 => { - var r = getData(instance, data_index, offset, length, DataReader.read_u64); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayF64.new(rtt, r.1))); - } - V128 => { - var r = getData(instance, data_index, offset, length << 1, DataReader.read_u64); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayV128.new(rtt, r.1))); - } - _ => { - return stack.trap(TrapReason.ERROR); - } - } - } - PACKED_I8 => { - var r = getData(instance, data_index, offset, length, DataReader.read1); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI8.new(rtt, r.1))); - } - PACKED_I16 => { - var r = getData(instance, data_index, offset, length, fun (d: DataReader) => DataReaders.read_range_u16(d.acquire(2))); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI16.new(rtt, r.1))); - } - } - } - _ => { - var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); + } else { + return ObjectRuntime.ARRAY_NEW_DATA(stack, instance, rtt, ddecl, offset, length, data_index); } return null; } @@ -519,7 +190,6 @@ component Runtime { } return null; } - // JOE TODO: move our code below to ObjectReps. Check the ARRAY_NEW_ELEM function. def ARRAY_GET(stack: ExecStack, instance: Instance, array_index: u31) -> Throwable { var index = stack.popu(); var obj = stack.popArray(); @@ -572,68 +242,12 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); var index = ArrayUtil.boundsCheckWithLength(u31.view(obj.length()), offset, 0, size); if (index < 0) return stack.trap(TrapReason.ARRAY_OOB); - match (ObjTuning.arrayMode) { - Typed => { - match (obj) { - x: HeapArrayValue => { - var r = x.vals[index ..+ size]; - for (i < r.length) r[i] = val; - } - x: HeapArrayI32 => { - var r = x.vals[index ..+ size]; - var v = Value.I32.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayI31 => { - var r = x.vals[index ..+ size]; - var v = Value.I31.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayI64 => { - var r = x.vals[index ..+ size]; - var v = Value.I64.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayI8 => { - var r = x.vals[index ..+ size]; - var v = u8.view(Value.I32.!(val).val); - for (i < r.length) r[i] = v; - } - x: HeapArrayI16 => { - var r = x.vals[index ..+ size]; - var v = u16.view(Value.I32.!(val).val); - for (i < r.length) r[i] = v; - } - x: HeapArrayF32 => { - var r = x.vals[index ..+ size]; - var v = Value.F32.!(val).bits; - for (i < r.length) r[i] = v; - } - x: HeapArrayF64 => { - var r = x.vals[index ..+ size]; - var v = Value.F64.!(val).bits; - for (i < r.length) r[i] = v; - } - x: HeapArrayRef => { - var r = x.vals[index ..+ size]; - var v = Value.Ref.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayV128 => { - var r = x.vals[index ..+ (size << 1)]; - var v128 = Value.V128.!(val); - for (i=0; i < r.length; i+=2) { - r[i] = v128.low; - r[i+1] = v128.high; - } - } - } - } - _ => { - var vals = obj.getArray(); - var r = vals[index ..+ size]; - for (i < r.length) r[i] = val; - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var vals = obj.getArray(); + var r = vals[index ..+ size]; + for (i < r.length) r[i] = val; + } else { + return ObjectRuntime.ARRAY_FILL(obj, rtt, index, size, val); } return null; } @@ -644,157 +258,13 @@ component Runtime { var dst_offset = stack.popu(); var dst = stack.popArray(); if (src == null || dst == null) return stack.trap(TrapReason.NULL_DEREF); - match (ObjTuning.arrayMode) { - Typed => { - if (HeapArrayValue.?(src) && HeapArrayValue.?(dst)) { - var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - if (HeapArrayValue.?(dst)) { - var arr = HeapArrayValue.!(dst); - var r = ArrayUtil.safeCopyDiffTypes(arr.vals, dst_offset, u31.view(src.length()), src_offset, size, src.getValue); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - if (HeapArrayValue.?(src)){ - var arr = HeapArrayValue.!(src); - match (dst) { - x: HeapArrayI32 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI31 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, fun (x: u32) => u31.!(arr.getI31(x))); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI64 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI8 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU8); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI16 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU16); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayF32 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayF64 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayRef => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getRef); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayV128 => { - var r = ArrayUtil.safeCopyFromV128(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getV128); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - _ => { return stack.trap(TrapReason.ERROR); } - } - } - - match (src) { - x: HeapArrayI32 => { - if (HeapArrayI32.?(dst)) { - var arr = HeapArrayI32.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI31 => { - if (HeapArrayI31.?(dst)) { - var arr = HeapArrayI31.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI64 => { - if (HeapArrayI64.?(dst)) { - var arr = HeapArrayI64.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI8 => { - if (HeapArrayI8.?(dst)) { - var arr = HeapArrayI8.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI16 => { - if (HeapArrayI16.?(dst)) { - var arr = HeapArrayI16.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayF32 => { - if (HeapArrayF32.?(dst)) { - var arr = HeapArrayF32.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayF64 => { - if (HeapArrayF64.?(dst)) { - var arr = HeapArrayF64.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayRef => { - if (HeapArrayRef.?(dst)) { - var arr = HeapArrayRef.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayV128 => { - if (HeapArrayV128.?(dst)) { - var arr = HeapArrayV128.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size << 1); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - } - } - _ => { - var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } else { + return ObjectRuntime.ARRAY_COPY(stack, dst, src, dst_offset, src_offset, size); } - - return stack.trap(TrapReason.ERROR); } def ARRAY_INIT_DATA(stack: ExecStack, instance: Instance, array_index: u31, data_index: u31) -> Throwable { var size = stack.popu(); @@ -806,104 +276,15 @@ component Runtime { if (instance.dropped_data[data_index]) return if(size > 0, stack.trap(TrapReason.DATA_SEGMENT_DROPPED), null); var data = instance.module.data[data_index].data; if (ArrayUtil.boundsCheckWithLength(u31.view(obj.length()), dst_offset, 0, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); - match (ObjTuning.arrayMode) { - Typed => { - match (obj) { - x: HeapArrayValue => { - var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - match (rtt.elem_types[0].pack) { - UNPACKED => { - match (rtt.elem_types[0].valtype) { - I32 => { - var t = bytesToI32s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayI32 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - I64 => { - var t = bytesToI64s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayI64 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - F32 => { - var t = bytesToI32s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayF32 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - F64 => { - var t = bytesToI64s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayF64 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - V128 => { - var t = bytesToI64s(data, src_offset, size << 1); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayV128 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size << 1); - return null; - } - } - } - _ => { } - } - } - PACKED_I8 => { - match (obj) { - x: HeapArrayI8 => { - var t = bytesToI8s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - PACKED_I16 => { - match (obj) { - x: HeapArrayI16 => { - var t = bytesToI16s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - } - } - _ => { - var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - var vals = obj.getArray(); - ArrayUtil.safeCopy(vals, dst_offset, t.1, 0, size); - return null; - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + var vals = obj.getArray(); + ArrayUtil.safeCopy(vals, dst_offset, t.1, 0, size); + return null; + } else { + return ObjectRuntime.ARRAY_INIT_DATA(stack, obj, rtt, data, dst_offset, src_offset, size); } - - return stack.trap(TrapReason.ERROR); } def ARRAY_INIT_ELEM(stack: ExecStack, instance: Instance, array_index: u31, elem_index: u31) -> Throwable { var size = stack.popu(); @@ -914,37 +295,16 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); if (instance.dropped_elems[elem_index]) return if(size > 0, stack.trap(TrapReason.ELEM_SEGMENT_DROPPED), null); var edecl = instance.module.elems[elem_index]; - match (ObjTuning.arrayMode) { - Typed => { - match (obj) { - x: HeapArrayI31 => { - var r = getElems(instance, edecl, src_offset, size); - if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); - if (ArrayUtil.boundsCheck(x.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); - for (i < r.1.length) { - x.vals[int.!(dst_offset)+i] = ObjectI31.!(r.1[i]).val; - } - } - x: HeapArrayRef => { - var r = getElems(instance, edecl, src_offset, size); - if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); - if (ArrayUtil.boundsCheck(x.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); - for (i < r.1.length) { - x.vals[int.!(dst_offset)+i] = r.1[i]; - } - } - _ => {return stack.trap(TrapReason.ARRAY_OOB);} - } - } - _ => { - var vals = HeapArrayValue.!(obj).vals; - var r = getElems(instance, edecl, src_offset, size); - if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); - if (ArrayUtil.boundsCheck(vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); - for (i < r.1.length) { - vals[int.!(dst_offset)+i] = Value.Ref(r.1[i]); - } + if (ObjTuning.arrayMode == ArrayMode.Original) { + var vals = HeapArrayValue.!(obj).vals; + var r = getElems(instance, edecl, src_offset, size); + if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); + if (ArrayUtil.boundsCheck(vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + for (i < r.1.length) { + vals[int.!(dst_offset)+i] = Value.Ref(r.1[i]); } + } else { + return ObjectRuntime.ARRAY_INIT_ELEM(stack, obj, instance, edecl, dst_offset, src_offset, size); } return null; } From e2bc5bf3ceec8d64db9f75144565f040a9e43822 Mon Sep 17 00:00:00 2001 From: Meng-chieh Chiu Date: Fri, 23 Jan 2026 16:08:07 -0500 Subject: [PATCH 04/11] 1st round refactoring complete --- src/engine/Runtime.v3 | 144 ++++++++++++------------------------------ src/engine/Tuning.v3 | 4 +- 2 files changed, 43 insertions(+), 105 deletions(-) diff --git a/src/engine/Runtime.v3 b/src/engine/Runtime.v3 index 4e57b8c0b..83435ffaa 100644 --- a/src/engine/Runtime.v3 +++ b/src/engine/Runtime.v3 @@ -183,7 +183,10 @@ component Runtime { var r = getElems(instance, edecl, offset, length); if (!r.0) return stack.trap(TrapReason.TABLE_OOB); var vals = Array.new(u31.!(length)); - for (i < vals.length) vals[i] = Value.Ref(r.1[i]); + for (i < vals.length) { + var val = r.1[i]; + vals[i] = if(ObjectI31.?(val), Value.I31(ObjectI31.!(val).val), Value.Ref(val)); + } stack.push(Value.Ref(HeapArrayValue.new(rtt, vals))); } else { return ObjectRuntime.ARRAY_NEW_ELEM(stack, instance, rtt, edecl, offset, length); @@ -534,8 +537,45 @@ component Runtime { var vals = Array.new(int.!(size)); var d = DataReader.new(data.data).reset(data.data, int.!(src_offset), data.data.length); for (i < vals.length) vals[i] = func(d); + return (d.ok, if(d.ok, vals)); + } + def bytesToVals(storage: StorageType, data: Array, offset: u32, length: u32) -> (bool, Array) { // TODO: MaybeTrap + var vals = Array.new(int.!(length)); + var size = sizeOfStorage(storage); + var nbytes = length * size; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var d = ExtendedDataReader.new(data).reset(data, start, start + int.!(nbytes)); + match (storage.pack) { + UNPACKED => { + match (storage.valtype) { + I32 => for (i < vals.length) vals[i] = Value.I32(d.read_u32()); + I64 => for (i < vals.length) vals[i] = Value.I64(d.read_u64()); + F32 => for (i < vals.length) vals[i] = Value.F32(d.read_u32()); + F64 => for (i < vals.length) vals[i] = Value.F64(d.read_u64()); + V128 => for (i < vals.length) vals[i] = Value.V128(d.read_u64(), d.read_u64()); + _ => ; + } + } + PACKED_I8 => for (i < vals.length) vals[i] = Value.I32(d.read_u8()); + PACKED_I16 => for (i < vals.length) vals[i] = Value.I32(d.read_u16()); + } return (d.ok, vals); } + def sizeOfStorage(storage: StorageType) -> u32 { + match (storage.pack) { + UNPACKED => { + match (storage.valtype) { + I32, F32 => return 4; + I64, F64 => return 8; + V128 => return 16; + _ => return 8; + } + } + PACKED_I8 => return 1; + PACKED_I16 => return 2; + } + } } class ExtendedDataReader extends DataReader { new(data: Range) super(data) { } @@ -547,108 +587,6 @@ class ExtendedDataReader extends DataReader { return DataReaders.read_range_u16(range); } } -def bytesToVals(storage: StorageType, data: Array, offset: u32, length: u32) -> (bool, Array) { // TODO: MaybeTrap - var vals = Array.new(int.!(length)); - var size = sizeOfStorage(storage); - var nbytes = length * size; - var start = ArrayUtil.boundsCheck(data, offset, nbytes); - if (start < 0) return (false, null); - var d = ExtendedDataReader.new(data).reset(data, start, start + int.!(nbytes)); - match (storage.pack) { - UNPACKED => { - match (storage.valtype) { - I32 => for (i < vals.length) vals[i] = Value.I32(d.read_u32()); - I64 => for (i < vals.length) vals[i] = Value.I64(d.read_u64()); - F32 => for (i < vals.length) vals[i] = Value.F32(d.read_u32()); - F64 => for (i < vals.length) vals[i] = Value.F64(d.read_u64()); - V128 => for (i < vals.length) vals[i] = Value.V128(d.read_u64(), d.read_u64()); - _ => ; - } - } - PACKED_I8 => for (i < vals.length) vals[i] = Value.I32(d.read_u8()); - PACKED_I16 => for (i < vals.length) vals[i] = Value.I32(d.read_u16()); - } - return (d.ok, vals); -} -def bytesToBytes(data: Array, offset: u32, length: u32) -> (bool, Array) { - var start = ArrayUtil.boundsCheck(data, offset, length); - if (start < 0) return (false, null); - var limit = length + offset; - var bytes = DataReader.new(data).reset(data, start, int.!(limit)).readN(int.!(length)); - return (true, bytes); -} -def bytesToI8s(data: Array, offset: u32, length: u32) -> (bool, Array) { - var vals = Array.new(int.!(length)); - var start = ArrayUtil.boundsCheck(data, offset, length); - if (start < 0) return (false, null); - var limit = length + offset; - var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); - for (i < vals.length) vals[i] = d.read_u8(); - return (d.ok, vals); -} -def bytesToI8sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { - var vals = Array.new(int.!(length)); - var start = ArrayUtil.boundsCheck(data, offset, length); - if (start < 0) return (false, null); - var limit = length + offset; - var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); - for (i < vals.length) vals[i] = u32.view(d.read_u8()); - return (d.ok, vals); -} -def bytesToI16s(data: Array, offset: u32, length: u32) -> (bool, Array) { - var vals = Array.new(int.!(length)); - var nbytes = length << 1; - var start = ArrayUtil.boundsCheck(data, offset, nbytes); - if (start < 0) return (false, null); - var limit = offset + nbytes; - var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); - for (i < vals.length) vals[i] = d.read_u16(); - return (d.ok, vals); -} -def bytesToI16sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { - var vals = Array.new(int.!(length)); - var nbytes = length << 1; - var start = ArrayUtil.boundsCheck(data, offset, nbytes); - if (start < 0) return (false, null); - var limit = offset + nbytes; - var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); - for (i < vals.length) vals[i] = u32.view(d.read_u16()); - return (d.ok, vals); -} -def bytesToI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { - var vals = Array.new(int.!(length)); - var nbytes = length << 2; - var start = ArrayUtil.boundsCheck(data, offset, nbytes); - if (start < 0) return (false, null); - var limit = offset + nbytes; - var d = DataReader.new(data).reset(data, start, int.!(limit)); - for (i < vals.length) vals[i] = d.read_u32(); - return (d.ok, vals); -} -def bytesToI64s(data: Array, offset: u32, length: u32) -> (bool, Array) { - var vals = Array.new(int.!(length)); - var nbytes = length << 3; - var start = ArrayUtil.boundsCheck(data, offset, nbytes); - if (start < 0) return (false, null); - var limit = offset + nbytes; - var d = DataReader.new(data).reset(data, start, int.!(limit)); - for (i < vals.length) vals[i] = d.read_u64(); - return (d.ok, vals); -} -def sizeOfStorage(storage: StorageType) -> u32 { - match (storage.pack) { - UNPACKED => { - match (storage.valtype) { - I32, F32 => return 4; - I64, F64 => return 8; - V128 => return 16; - _ => return 8; - } - } - PACKED_I8 => return 1; - PACKED_I16 => return 2; - } -} layout Layout_i31 { +0 val: i31; =4; diff --git a/src/engine/Tuning.v3 b/src/engine/Tuning.v3 index 3215d6f09..4967cd560 100644 --- a/src/engine/Tuning.v3 +++ b/src/engine/Tuning.v3 @@ -88,9 +88,9 @@ def structModeFor(code: int) -> StructMode { } component ObjTuning { - def arrayModeCode = 0; + def arrayModeCode = 1; def arrayMode = arrayModeFor(arrayModeCode); - def structModeCode = 0; + def structModeCode = 1; def structMode = structModeFor(structModeCode); } From b3a187ef3ee07cd77b77794426ce7793eebfd07a Mon Sep 17 00:00:00 2001 From: Meng-chieh Chiu Date: Fri, 23 Jan 2026 16:25:03 -0500 Subject: [PATCH 05/11] 1st round refactoring complete --- src/engine/ObjectReps.v3 | 1133 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1133 insertions(+) create mode 100644 src/engine/ObjectReps.v3 diff --git a/src/engine/ObjectReps.v3 b/src/engine/ObjectReps.v3 new file mode 100644 index 000000000..5e530f5aa --- /dev/null +++ b/src/engine/ObjectReps.v3 @@ -0,0 +1,1133 @@ +// Paired array representation: an Array holds all the reference fields, +// and an Array holds all the primitive values +class HeapStructPair extends HeapStructGeneric { + def objs: Array; + def bytes: Array; + new(decl: StructDecl, objs, bytes) super(decl) { } + + def getFieldValue(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + match (decl.field_shifts[index]) { + -1 => { + return Value.Ref(objs[decl.field_offsets[index]]); + } + 0 => { + var v = bytes[decl.field_offsets[index]]; + return Value.I32(u32.view(v)); + } + 1 => { + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(v)); + } + 2 => { + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return if(decl.field_types[index].valtype == ValueType.I32, Value.I32(v), Value.F32(v)); + } + 3 => { + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return if(decl.field_types[index].valtype == ValueType.I64, Value.I64(v), Value.F64(v)); + } + 4 => { + var r = Ref.at(bytes, decl.field_offsets[index]); + return Value.V128(r.lo_val, r.hi_val); + } + } + return Value.I32(u32.view(-1)); + } + def getFieldSignExtend8Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(i32.!(v))); + } + def getFieldSignExtend16Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(i32.!(v))); + } + def getFieldZeroExtend8Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(v)); + } + def getFieldZeroExtend16Value(index: u31) -> Value { + var decl = StructDecl.!(this.decl); + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return Value.I32(u32.view(v)); + } + def getFieldI32(index: u32) -> i32 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I32)) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldI64(index: u32) -> i64 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I64)) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldI8(index: u32) -> i8 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I8) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldI16(index: u32) -> i16 { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I16) { return -1; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldF32(index: u32) -> float { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F32)) { return -1f; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldF64(index: u32) -> double { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F64)) { return -1d; } + var v = Ref.at(bytes, decl.field_offsets[index]).val; + return v; + } + def getFieldRef(index: u32) -> Object { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && ValueType.Ref.?(ft.valtype)) { return null; } + return objs[decl.field_offsets[index]]; + } + def getFieldV128(index: u32) -> (u64, u64) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.V128)) { return (u64.view(-1), u64.view(-1)); } + var r = Ref.at(bytes, decl.field_offsets[index]); + return (r.lo_val, r.hi_val); + } + def setFieldValue(index: u31, val: Value) { + var decl = StructDecl.!(this.decl); + match (decl.field_shifts[index]) { + -1 => { + objs[decl.field_offsets[index]] = Value.Ref.!(val).val; + } + 0 => { + bytes[decl.field_offsets[index]] = u8.view(Value.I32.!(val).val); + } + 1 => { + Ref.at(bytes, decl.field_offsets[index]).val = u16.view(Value.I32.!(val).val); + } + 2 => { + if (Value.I32.?(val)) { + Ref.at(bytes, decl.field_offsets[index]).val = Value.I32.!(val).val; + } else { + Ref.at(bytes, decl.field_offsets[index]).val = Value.F32.!(val).bits; + } + } + 3 => { + if (Value.I64.?(val)) { + Ref.at(bytes, decl.field_offsets[index]).val = Value.I64.!(val).val; + } else { + Ref.at(bytes, decl.field_offsets[index]).val = Value.F64.!(val).bits; + } + } + 4 => { + var r = Ref.at(bytes, decl.field_offsets[index]); + var pair = Value.V128.!(val); + r.lo_val = pair.low; + r.hi_val = pair.high; + } + } + } + def setFieldI32(index: u32, val: i32) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I32)) { + Ref.at(bytes, decl.field_offsets[index]).val = u32.view(val); + } + } + def setFieldI64(index: u32, val: i64) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I64)) { + Ref.at(bytes, decl.field_offsets[index]).val = u64.view(val); + } + } + def setFieldI8(index: u32, val: i8) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I8) { + bytes[decl.field_offsets[index]] = u8.view(val); + } + } + def setFieldI16(index: u32, val: i16) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I16) { + Ref.at(bytes, decl.field_offsets[index]).val = val; + } + } + def setFieldF32(index: u32, val: float) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F32)) { + Ref.at(bytes, decl.field_offsets[index]).val = val; + } + } + def setFieldF64(index: u32, val: double) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F64)) { + Ref.at(bytes, decl.field_offsets[index]).val = val; + } + } + def setFieldRef(index: u32, val: Object) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || ValueType.Ref.?(ft.valtype)) { + objs[decl.field_offsets[index]] = val; + } + } + def setFieldV128(index: u32, low: u64, high: u64) { + var decl = StructDecl.!(this.decl); + var ft = decl.field_types[index]; + if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.V128)) { + var r = Ref.at(bytes, decl.field_offsets[index]); + r.lo_val = low; + r.hi_val = high; + } + } +} + +// Implementations of arrays of specific types +class HeapArrayI32 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u32.view(Value.I32.!(val).val); + } + def getI32(index: u32) -> i32 { + return i32.view(vals[index]); + } + def setI32(index: u32, val: i32) { + vals[index] = u32.view(val); + } +} + +class HeapArrayI31 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I31(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u31.view(Value.I31.!(val).val); + } + def getI31(index: u32) -> i31 { + return i31.view(vals[index]); + } + def setI31(index: u32, val: i31) { + vals[index] = u31.view(val); + } +} + +class HeapArrayI64 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I64(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u64.view(Value.I64.!(val).val); + } + def getI64(index: u32) -> i64 { + return i64.view(vals[index]); + } + def setI64(index: u32, val: i64) { + vals[index] = u64.view(val); + } +} + +class HeapArrayI8 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u8.view(Value.I32.!(val).val); + } + def getI8(index: u32) -> i8 { + return i8.view(vals[index]); + } + def setI8(index: u32, val: i8) { + vals[index] = u8.view(val); + } +} + +class HeapArrayI16 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.I32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u16.view(Value.I32.!(val).val); + } + def getI16(index: u32) -> i16 { + return i16.view(vals[index]); + } + def setI16(index: u32, val: i16) { + vals[index] = u16.view(val); + } +} + +class HeapArrayF32 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.F32(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u32.view(Value.F32.!(val).bits); + } + def getF32(index: u32) -> float { + return if(index >= 0 && index < vals.length, float.view(vals[index]), -1); + } + def setF32(index: u32, val: float) { + if (index >= 0 && index < vals.length) { + vals[index] = u32.view(val); + } + } +} + +class HeapArrayF64 extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.F64(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = u64.view(Value.F64.!(val).bits); + } + def getF64(index: u32) -> double { + return double.view(vals[index]); + } + def setF64(index: u32, val: double) { + vals[index] = u64.view(val); + } +} + +class HeapArrayRef extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + match (vals[index]) { + x: ObjectI31 => return Value.I31(x.val); + x: Object => return Value.Ref(x); + _ => return Values.REF_NULL; + } + } + def setValue(index: u32, val: Value) { + var obj: Object; + match (val) { + Ref(v) => obj = v; + I31(v) => obj = ObjectI31.new(v); + _ => ; + } + vals[index] = obj; + } + def getRef(index: u32) -> Object { + return vals[index]; + } + def setRef(index: u32, val: Object) { + vals[index] = val; + } +} + +// A v128 uses two adjacent u64 entries in the vals array +class HeapArrayV128 extends HeapArrayGeneric { + def vals: Array; // The array format: [low 0, high 0, low 1, high 1, ...] + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length >> 1; } + + def getValue(index: u32) -> Value { + var idx = index << 1; + return Value.V128(vals[idx], vals[idx+1]); + } + def setValue(index: u32, val: Value) { + var idx = index << 1; + var v128 = Value.V128.!(val); + vals[idx] = u64.view(v128.low); + vals[idx+1] = u64.view(v128.high); + } + def getV128(index: u32) -> (u64, u64) { + var idx = index << 1; + return (vals[idx], vals[idx+1]); + } + def setV128(index: u32, low: u64, high: u64) { + var idx = index << 1; + vals[idx] = low; + vals[idx+1] = high; + } +} + +component ObjectRuntime { + def STRUCT_NEW(stack: ExecStack, decl: StructDecl) -> HeapStructGeneric { + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var obj: HeapStructGeneric = HeapStructPair.new(decl, objs, bytes); + for (i = decl.field_types.length - 1; i >= 0; i--) { + var index = u31.!(i); + match (decl.field_shifts[i]) { + 0 => { + var val = stack.popi(); + obj.setFieldI8(index, i8.view(val)); + } + 1 => { + var val = stack.popi(); + obj.setFieldI16(index, i16.view(val)); + } + 2 => { + if (decl.field_types[i].valtype == ValueType.I32) { + var val = stack.popi(); + obj.setFieldI32(index, val); + } else { + var val = stack.popf(); + obj.setFieldF32(index, val); + } + } + 3 => { + if (decl.field_types[i].valtype == ValueType.I64) { + var val = stack.popl(); + obj.setFieldI64(index, val); + } else { + var val = stack.popd(); + obj.setFieldF64(index, val); + } + } + 4 => { + var pair = stack.pops(); + obj.setFieldV128(index, pair.0, pair.1); + } + -1 => { + var o = stack.popObject(); + obj.setFieldRef(index, o); + } + } + } + return obj; + } + def STRUCT_NEW_DEFAULT(decl: StructDecl) -> HeapStructGeneric { + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var obj: HeapStructGeneric = HeapStructPair.new(decl, objs, bytes); + for (i < decl.field_types.length) { + var index = u31.view(i); + match (decl.field_shifts[i]) { + 0 => { + obj.setFieldI8(index, 0); + } + 1 => { + obj.setFieldI16(index, 0); + } + 2 => { + if (decl.field_types[i].valtype == ValueType.I32) { + obj.setFieldI32(index, 0); + } else { + obj.setFieldF32(index, 0.0f); + } + } + 3 => { + if (decl.field_types[i].valtype == ValueType.I64) { + obj.setFieldI64(index, 0); + } else { + obj.setFieldF64(index, 0.0d); + } + } + 4 => { + obj.setFieldV128(index, 0, 0); + } + -1 => { + obj.setFieldRef(index, null); + } + } + } + return obj; + } + def ARRAY_NEW(stack: ExecStack, decl: ArrayDecl, length: u32) -> Throwable { + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + var elem = stack.popu(); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + var elem = stack.popw(); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + var elem = u32.view(stack.popf()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + var elem = u64.view(stack.popd()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); + var val = ObjectI31.!(Value.Ref.!(elem).val).val; + for (i < vals.length) vals[i] = val; + stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + } else { + var vals = Array.new(u31.!(length)); + var elem = stack.popObject(); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + var v128 = stack.pops(); + var low = v128.0; + var high = v128.1; + for (i = 0; i < vals.length; i += 2) { + vals[i] = low; + vals[i+1] = high; + } + stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = stack.popV(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + var elem = u8.view(stack.popu()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + var elem = u16.view(stack.popu()); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); + } + } + return null; + } + def ARRAY_NEW_DEFAULT(stack: ExecStack, decl: ArrayDecl, length: u32) -> Throwable { + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u32; + stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u64; + stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = u32.view(0.0f); + stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = u64.view(0.0d); + stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = null; + stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + } else { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = null; + stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + // for (i < vals.length) vals[i] = 0u64; + stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var elem = Values.default(decl.elem_types[0].valtype); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u8; + stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + // for (i < vals.length) vals[i] = 0u16; + stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); + } + } + return null; + } + def ARRAY_NEW_FIXED(stack: ExecStack, decl: ArrayDecl, length: u32) -> Throwable { + match (decl.elem_types[0].pack) { + UNPACKED => { + match (decl.elem_types[0].valtype) { + I32 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popu()); + stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); + } + I64 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popw()); + stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); + } + F32 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popf()); + stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); + } + F64 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popd()); + stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); + } + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = ObjectI31.!(stack.popObject()).val; + stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + + } else { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popObject(); + stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + } + } + V128 => { + var vals = Array.new(u31.!(length << 1)); + for (i = vals.length - 2; i >= 0; i -= 2) { + var v128 = stack.pops(); + vals[i] = v128.0; + vals[i+1] = v128.1; + } + stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); + } + _ => { + var vals = Array.new(u31.!(length)); + var t = decl.elem_types[0].valtype; + for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); + stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + } + } + } + PACKED_I8 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u8.view(stack.popu()); + stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); + } + PACKED_I16 => { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = u16.view(stack.popu()); + stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); + } + } + return null; + } + def ARRAY_NEW_DATA(stack: ExecStack, instance: Instance, rtt: ArrayDecl, ddecl: DataDecl, offset: u32, length: u32, data_index: u31) -> Throwable { + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32 => { + var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u32); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI32.new(rtt, r.1))); + } + I64 => { + var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u64); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI64.new(rtt, r.1))); + } + F32 => { + var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u32); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayF32.new(rtt, r.1))); + } + F64 => { + var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u64); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayF64.new(rtt, r.1))); + } + V128 => { + var r = Runtime.getData(instance, data_index, offset, length << 1, DataReader.read_u64); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayV128.new(rtt, r.1))); + } + _ => { + return stack.trap(TrapReason.ERROR); + } + } + } + PACKED_I8 => { + var r = Runtime.getData(instance, data_index, offset, length, DataReader.read1); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI8.new(rtt, r.1))); + } + PACKED_I16 => { + var r = Runtime.getData(instance, data_index, offset, length, fun (d: DataReader) => DataReaders.read_range_u16(d.acquire(2))); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(HeapArrayI16.new(rtt, r.1))); + } + } + return null; + } + def ARRAY_NEW_ELEM(stack: ExecStack, instance: Instance, rtt: ArrayDecl, edecl: ElemDecl, offset: u32, length: u32) -> Throwable { + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + Ref(nullable, heaptype) => { + var r = Runtime.getElems(instance, edecl, offset, length); + if (!r.0) return stack.trap(TrapReason.TABLE_OOB); + stack.push(Value.Ref(HeapArrayRef.new(rtt, r.1))); + } + _ => return stack.trap(TrapReason.ERROR); + } + } + _ => return stack.trap(TrapReason.ERROR); + } + return null; + } + def ARRAY_FILL(obj: HeapArrayGeneric, rtt: ArrayDecl, index: int, size: u32, val: Value) -> Throwable { + match (obj) { + x: HeapArrayValue => { + var r = x.vals[index ..+ size]; + for (i < r.length) r[i] = val; + } + x: HeapArrayI32 => { + var r = x.vals[index ..+ size]; + var v = Value.I32.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayI31 => { + var r = x.vals[index ..+ size]; + var v = Value.I31.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayI64 => { + var r = x.vals[index ..+ size]; + var v = Value.I64.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayI8 => { + var r = x.vals[index ..+ size]; + var v = u8.view(Value.I32.!(val).val); + for (i < r.length) r[i] = v; + } + x: HeapArrayI16 => { + var r = x.vals[index ..+ size]; + var v = u16.view(Value.I32.!(val).val); + for (i < r.length) r[i] = v; + } + x: HeapArrayF32 => { + var r = x.vals[index ..+ size]; + var v = Value.F32.!(val).bits; + for (i < r.length) r[i] = v; + } + x: HeapArrayF64 => { + var r = x.vals[index ..+ size]; + var v = Value.F64.!(val).bits; + for (i < r.length) r[i] = v; + } + x: HeapArrayRef => { + var r = x.vals[index ..+ size]; + var v = Value.Ref.!(val).val; + for (i < r.length) r[i] = v; + } + x: HeapArrayV128 => { + var r = x.vals[index ..+ (size << 1)]; + var v128 = Value.V128.!(val); + for (i=0; i < r.length; i+=2) { + r[i] = v128.low; + r[i+1] = v128.high; + } + } + } + return null; + } + def ARRAY_COPY(stack: ExecStack, src: HeapArrayGeneric, dst: HeapArrayGeneric, src_offset: u32, dst_offset: u32, size: u32) -> Throwable { + if (HeapArrayValue.?(src) && HeapArrayValue.?(dst)) { + var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + if (HeapArrayValue.?(dst)) { + var arr = HeapArrayValue.!(dst); + var r = ArrayUtil.safeCopyDiffTypes(arr.vals, dst_offset, u31.view(src.length()), src_offset, size, src.getValue); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + if (HeapArrayValue.?(src)){ + var arr = HeapArrayValue.!(src); + match (dst) { + x: HeapArrayI32 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI31 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, fun (x: u32) => u31.!(arr.getI31(x))); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI64 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI8 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU8); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayI16 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU16); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayF32 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayF64 => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayRef => { + var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getRef); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + x: HeapArrayV128 => { + var r = ArrayUtil.safeCopyFromV128(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getV128); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + + _ => { return stack.trap(TrapReason.ERROR); } + } + } + + match (src) { + x: HeapArrayI32 => { + if (HeapArrayI32.?(dst)) { + var arr = HeapArrayI32.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI31 => { + if (HeapArrayI31.?(dst)) { + var arr = HeapArrayI31.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI64 => { + if (HeapArrayI64.?(dst)) { + var arr = HeapArrayI64.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI8 => { + if (HeapArrayI8.?(dst)) { + var arr = HeapArrayI8.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayI16 => { + if (HeapArrayI16.?(dst)) { + var arr = HeapArrayI16.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayF32 => { + if (HeapArrayF32.?(dst)) { + var arr = HeapArrayF32.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayF64 => { + if (HeapArrayF64.?(dst)) { + var arr = HeapArrayF64.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayRef => { + if (HeapArrayRef.?(dst)) { + var arr = HeapArrayRef.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + x: HeapArrayV128 => { + if (HeapArrayV128.?(dst)) { + var arr = HeapArrayV128.!(dst); + var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size << 1); + if (!r) return stack.trap(TrapReason.ARRAY_OOB); + return null; + } + } + } + return stack.trap(TrapReason.ERROR); + } + def ARRAY_INIT_DATA(stack: ExecStack, obj: HeapArrayGeneric, rtt: ArrayDecl, data: Array, src_offset: u32, dst_offset: u32, size: u32) -> Throwable { + /*match (obj) { + x: HeapArrayValue => { + var t = Runtime.bytesToVals(rtt.elem_types[0], data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + }*/ + match (rtt.elem_types[0].pack) { + UNPACKED => { + match (rtt.elem_types[0].valtype) { + I32 => { + var t = bytesToI32s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayI32 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + I64 => { + var t = bytesToI64s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayI64 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + F32 => { + var t = bytesToI32s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayF32 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + F64 => { + var t = bytesToI64s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayF64 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + V128 => { + var t = bytesToI64s(data, src_offset, size << 1); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: HeapArrayV128 => { + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size << 1); + return null; + } + } + } + _ => { } + } + } + PACKED_I8 => { + match (obj) { + x: HeapArrayI8 => { + var t = bytesToI8s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + PACKED_I16 => { + match (obj) { + x: HeapArrayI16 => { + var t = bytesToI16s(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + return null; + } + } + } + } + return stack.trap(TrapReason.ERROR); + } + def ARRAY_INIT_ELEM(stack: ExecStack, obj: HeapArrayGeneric, instance: Instance, edecl: ElemDecl, src_offset: u32, dst_offset: u32, size: u32) -> Throwable { + match (obj) { + x: HeapArrayI31 => { + var r = Runtime.getElems(instance, edecl, src_offset, size); + if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); + if (ArrayUtil.boundsCheck(x.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + for (i < r.1.length) { + x.vals[int.!(dst_offset)+i] = ObjectI31.!(r.1[i]).val; + } + } + x: HeapArrayRef => { + var r = Runtime.getElems(instance, edecl, src_offset, size); + if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); + if (ArrayUtil.boundsCheck(x.vals, dst_offset, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); + for (i < r.1.length) { + x.vals[int.!(dst_offset)+i] = r.1[i]; + } + } + _ => {return stack.trap(TrapReason.ARRAY_OOB);} + } + return null; + } +} + +def bytesToBytes(data: Array, offset: u32, length: u32) -> (bool, Array) { + var start = ArrayUtil.boundsCheck(data, offset, length); + if (start < 0) return (false, null); + var limit = length + offset; + var bytes = DataReader.new(data).reset(data, start, int.!(limit)).readN(int.!(length)); + return (true, bytes); +} +def bytesToI8s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var start = ArrayUtil.boundsCheck(data, offset, length); + if (start < 0) return (false, null); + var vals = Array.new(int.!(length)); + var limit = length + offset; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u8(); + return (d.ok, if(d.ok, vals)); +} +def bytesToI8sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var start = ArrayUtil.boundsCheck(data, offset, length); + if (start < 0) return (false, null); + var vals = Array.new(int.!(length)); + var limit = length + offset; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = u32.view(d.read_u8()); + return (d.ok, if(d.ok, vals)); +} +def bytesToI16s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var nbytes = length << 1; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var vals = Array.new(int.!(length)); + var limit = offset + nbytes; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u16(); + return (d.ok, if(d.ok, vals)); +} +def bytesToI16sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var nbytes = length << 1; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var vals = Array.new(int.!(length)); + var limit = offset + nbytes; + var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = u32.view(d.read_u16()); + return (d.ok, if(d.ok, vals)); +} +def bytesToI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var nbytes = length << 2; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var vals = Array.new(int.!(length)); + var limit = offset + nbytes; + var d = DataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u32(); + return (d.ok, if(d.ok, vals)); +} +def bytesToI64s(data: Array, offset: u32, length: u32) -> (bool, Array) { + var nbytes = length << 3; + var start = ArrayUtil.boundsCheck(data, offset, nbytes); + if (start < 0) return (false, null); + var vals = Array.new(int.!(length)); + var limit = offset + nbytes; + var d = DataReader.new(data).reset(data, start, int.!(limit)); + for (i < vals.length) vals[i] = d.read_u64(); + return (d.ok, if(d.ok, vals)); +} + From 27127b4203be2925a94ddf7714b262441c3c7b61 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Sun, 25 Jan 2026 22:00:08 -0500 Subject: [PATCH 06/11] Refactoring to separate better the alternate object representations and to use more parameterization of functions --- src/engine/Instance.v3 | 215 +---- src/engine/ObjectReps.v3 | 920 +++++++--------------- src/engine/Runtime.v3 | 57 +- src/engine/Tuning.v3 | 28 - src/engine/Type.v3 | 3 +- src/engine/Value.v3 | 1 - src/engine/compiler/MacroAssembler.v3 | 8 +- src/engine/compiler/SinglePassCompiler.v3 | 22 +- src/engine/x86-64/X86_64Interpreter.v3 | 2 +- src/util/ArrayUtil.v3 | 37 - test/unittest/ExeTest.v3 | 35 +- 11 files changed, 370 insertions(+), 958 deletions(-) diff --git a/src/engine/Instance.v3 b/src/engine/Instance.v3 index de7265b0a..c234216ac 100644 --- a/src/engine/Instance.v3 +++ b/src/engine/Instance.v3 @@ -147,222 +147,41 @@ class Instance(module: Module, imports: Array) { var decl = t.array; var st = decl.elem_types[0]; var velem = evalInitExpr(elem); - - match (ObjTuning.arrayMode) { - Typed => { - match (st.pack) { - UNPACKED => { - match (st.valtype) { - I32 => { - var ielem = Value.I32.!(velem).val; - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = ielem; - return HeapArrayI32.new(t.array, vvals); - } - I64 => { - var ielem = Value.I64.!(velem).val; - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = ielem; - return HeapArrayI64.new(t.array, vvals); - } - F32 => { - var felem = Value.F32.!(velem).bits; - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = felem; - return HeapArrayF32.new(t.array, vvals); - } - F64 => { - var felem = Value.F64.!(velem).bits; - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = felem; - return HeapArrayF64.new(t.array, vvals); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var oelem = Value.I31.!(velem).val; - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = oelem; - return HeapArrayI31.new(t.array, vvals); - } else { - var oelem = Value.Ref.!(velem).val; - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = oelem; - return HeapArrayRef.new(t.array, vvals); - } - } - V128 => { - var v128 = Value.V128.!(velem); - var vvals = Array.new(vlen << 1); - var low = v128.low; - var high = v128.high; - for (i = 0; i < vvals.length; i += 2) { - vvals[i] = low; - vvals[i+1] = high; - } - return HeapArrayV128.new(t.array, vvals); - } - _ => { - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = velem; - return HeapArrayValue.new(t.array, vvals); - } - } - } - PACKED_I8 => { - var ielem = u8.view(Value.I32.!(velem).val); - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = ielem; - return HeapArrayI8.new(t.array, vvals); - } - PACKED_I16 => { - var ielem = u16.view(Value.I32.!(velem).val); - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = ielem; - return HeapArrayI16.new(t.array, vvals); - } - } - } - _ => { - var vvals = Array.new(vlen); - for (i < vvals.length) vvals[i] = velem; - return HeapArrayValue.new(t.array, vvals); - } + if (ObjReps.arrayMode == ArrayMode.Original) { + var vvals = Array.new(vlen); + for (i < vvals.length) vvals[i] = velem; + return HeapArrayValue.new(t.array, vvals); + } else { + return ObjectRuntime.evalInitExprObjectArray(t, st, velem, vlen); } } FixedArray(t, vals) => { var decl = t.array; var st = decl.elem_types[0]; - match (ObjTuning.arrayMode) { - Typed => { - match (st.pack) { - UNPACKED => { - match (st.valtype) { - I32 => { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = Value.I32.!(velem).val; - } - return HeapArrayI32.new(t.array, vvals); - } - I64 => { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = Value.I64.!(velem).val; - } - return HeapArrayI64.new(t.array, vvals); - } - F32 => { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = Value.F32.!(velem).bits; - } - return HeapArrayF32.new(t.array, vvals); - } - F64 => { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = Value.F64.!(velem).bits; - } - return HeapArrayF64.new(t.array, vvals); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = Value.I31.!(velem).val; - } - return HeapArrayI31.new(t.array, vvals); - } else { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = Value.Ref.!(velem).val; - } - return HeapArrayRef.new(t.array, vvals); - } - } - V128 => { - var vlen = vals.length; - var vvals = Array.new(vlen << 1); - for (i = 0; i < vlen; i += 2) { - var velem = evalInitExpr(vals[i]); - var v128 = Value.V128.!(velem); - vvals[i] = v128.low; - vvals[i+1] = v128.high; - } - return HeapArrayV128.new(t.array, vvals); - } - _ => { - var vvals = Arrays.map(vals, evalInitExpr); - return HeapArrayValue.new(t.array, vvals); - } - } - } - PACKED_I8 => { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = u8.view(Value.I32.!(velem).val); - } - return HeapArrayI8.new(t.array, vvals); - } - PACKED_I16 => { - var vlen = vals.length; - var vvals = Array.new(vlen); - for (i < vlen) { - var velem = evalInitExpr(vals[i]); - vvals[i] = u16.view(Value.I32.!(velem).val); - } - return HeapArrayI16.new(t.array, vvals); - } - } - } - _ => { - var vvals = Arrays.map(vals, evalInitExpr); - return HeapArrayValue.new(t.array, vvals); - } + if (ObjReps.arrayMode == ArrayMode.Original) { + var vvals = Arrays.map(vals, evalInitExpr); + return HeapArrayValue.new(t.array, vvals); + } else { + return ObjectRuntime.evalInitExprObjectFixedArray(this, t, st, vals); } } Struct(t, vals) => { var vvals = Arrays.map(vals, evalInitExpr); - match (ObjTuning.structMode) { - Original => { - return HeapStructValue.new(t.sdecl, vvals); - } - Pair => { - var decl = t.sdecl; - var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); - var obj = HeapStructPair.new(decl, objs, bytes); - - for (i < decl.field_types.length) { - obj.setFieldValue(u31.view(i), vvals[i]); - } - return obj; - } + if (ObjReps.structMode == StructMode.Original) { + return HeapStructValue.new(t.sdecl, vvals); + } else { + return ObjectRuntime.evalInitExprObjectStruct(t, vvals); } } ArrayNewData(t, data_index, offset, len) => { var voffset = evalInitExpr(offset); var vlen = evalInitExpr(len); - return null; + return null; // TODO } ArrayNewElem(t, elem_index, offset, len) => { var voffset = evalInitExpr(offset); var vlen = evalInitExpr(len); - return null; + return null; // TODO } _ => return null; } diff --git a/src/engine/ObjectReps.v3 b/src/engine/ObjectReps.v3 index 5e530f5aa..05268b476 100644 --- a/src/engine/ObjectReps.v3 +++ b/src/engine/ObjectReps.v3 @@ -1,3 +1,33 @@ +// Alternate, possibly faster, representations for Wasm heap objects + +enum ArrayMode(code: int) { + Original(0), // Default value: HeapArrayValue only + Typed(1), // HeapArrayX where X can be one of the storage types +} + +enum StructMode(code: int) { + Original(0), // Default value: Values only + Pair(1) // Having Array (holdings the refs) and Array (holding prims) +} + +def arrayModeFor(code: int) -> ArrayMode { + def modes = [ArrayMode.Original, ArrayMode.Typed]; + return if(code >= 0 && code < modes.length, modes[code], ArrayMode.Original); +} + +def structModeFor(code: int) -> StructMode { + def modes = [StructMode.Original, StructMode.Pair]; + return if(code >= 0 && code < modes.length, modes[code], StructMode.Original); +} + +component ObjReps { + def arrayModeCode = 1; + def arrayMode = arrayModeFor(arrayModeCode); + + def structModeCode = 1; + def structMode = structModeFor(structModeCode); +} + // Paired array representation: an Array holds all the reference fields, // and an Array holds all the primitive values class HeapStructPair extends HeapStructGeneric { @@ -5,201 +35,115 @@ class HeapStructPair extends HeapStructGeneric { def bytes: Array; new(decl: StructDecl, objs, bytes) super(decl) { } + private def getHelperV(offset: i32, len: int, fetch: Range -> T, wrap: T -> Value) -> Value { + return wrap(fetch(bytes[offset ..+ len])); + } def getFieldValue(index: u31) -> Value { var decl = StructDecl.!(this.decl); + var offset = decl.field_offsets[index]; match (decl.field_shifts[index]) { - -1 => { - return Value.Ref(objs[decl.field_offsets[index]]); - } - 0 => { - var v = bytes[decl.field_offsets[index]]; - return Value.I32(u32.view(v)); - } - 1 => { - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(v)); - } - 2 => { - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return if(decl.field_types[index].valtype == ValueType.I32, Value.I32(v), Value.F32(v)); - } - 3 => { - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return if(decl.field_types[index].valtype == ValueType.I64, Value.I64(v), Value.F64(v)); - } - 4 => { - var r = Ref.at(bytes, decl.field_offsets[index]); - return Value.V128(r.lo_val, r.hi_val); - } + -1 => return Value.Ref(objs[offset]); + 0 => return getHelperV(offset, 1, DataReaders.read_range_u32_u8, Values.box_u); + 1 => return getHelperV(offset, 2, DataReaders.read_range_u32_u16, Values.box_u); + 2 => return getHelperV(offset, 4, DataReaders.read_range_u32, + if(decl.field_types[index].valtype == ValueType.I32, Values.box_u, Values.box_fu32)); + 3 => return getHelperV(offset, 8, DataReaders.read_range_u64, + if(decl.field_types[index].valtype == ValueType.I64, Values.box_w, Values.box_du64)); + 4 => return getHelperV<(u64, u64)>(offset, 16, DataReaders.read_range_u128, Values.box_s); } return Value.I32(u32.view(-1)); } - def getFieldSignExtend8Value(index: u31) -> Value { + private def getHelperV2(index: u31, len: int, fetch: Range -> T, widen: T -> U, wrap: U -> Value) -> Value { var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(i32.!(v))); + return wrap(widen(fetch(bytes[decl.field_offsets[index] ..+ len]))); + } + def getFieldSignExtend8Value(index: u31) -> Value { + return getHelperV2(index, 1, DataReaders.read_range_i8, i32.!, Values.box_i); } def getFieldSignExtend16Value(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(i32.!(v))); + return getHelperV2(index, 2, DataReaders.read_range_i16, i32.!, Values.box_i); } def getFieldZeroExtend8Value(index: u31) -> Value { - var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(v)); + return getHelperV2(index, 1, DataReaders.read_range_u8, u32.!, Values.box_u); } def getFieldZeroExtend16Value(index: u31) -> Value { + return getHelperV2(index, 2, DataReaders.read_range_u16, u32.!, Values.box_u); + } + private def getHelper(index: u32, len: int, fetch: Range -> T) -> T { var decl = StructDecl.!(this.decl); - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return Value.I32(u32.view(v)); - } + var offset = decl.field_offsets[index]; + return fetch(bytes[offset ..+ len]); + } def getFieldI32(index: u32) -> i32 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I32)) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; + return getHelper(index, 4, DataReaders.read_range_i32); } def getFieldI64(index: u32) -> i64 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.I64)) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; + return getHelper(index, 8, DataReaders.read_range_i64); } def getFieldI8(index: u32) -> i8 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I8) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; + return getHelper(index, 1, DataReaders.read_range_i8); } def getFieldI16(index: u32) -> i16 { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && ft.pack != Packedness.PACKED_I16) { return -1; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; + return getHelper(index, 2, DataReaders.read_range_i16); } def getFieldF32(index: u32) -> float { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F32)) { return -1f; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; + return getHelper(index, 4, DataReaders.read_range_float); } def getFieldF64(index: u32) -> double { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.F64)) { return -1d; } - var v = Ref.at(bytes, decl.field_offsets[index]).val; - return v; + return getHelper(index, 8, DataReaders.read_range_double); } def getFieldRef(index: u32) -> Object { var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && ValueType.Ref.?(ft.valtype)) { return null; } return objs[decl.field_offsets[index]]; } def getFieldV128(index: u32) -> (u64, u64) { + return getHelper<(u64, u64)>(index, 16, DataReaders.read_range_u128); + } + private def setHelperV(index: u32, len: int, val: Value, unbox: Value -> T, store: (Range, T) -> void) { var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (!Values.NO_CHECKS && (ft.pack != Packedness.UNPACKED || ft.valtype != ValueType.V128)) { return (u64.view(-1), u64.view(-1)); } - var r = Ref.at(bytes, decl.field_offsets[index]); - return (r.lo_val, r.hi_val); + var offset = decl.field_offsets[index]; + return store(bytes[offset ..+ len], unbox(val)); } def setFieldValue(index: u31, val: Value) { var decl = StructDecl.!(this.decl); match (decl.field_shifts[index]) { - -1 => { - objs[decl.field_offsets[index]] = Value.Ref.!(val).val; - } - 0 => { - bytes[decl.field_offsets[index]] = u8.view(Value.I32.!(val).val); - } - 1 => { - Ref.at(bytes, decl.field_offsets[index]).val = u16.view(Value.I32.!(val).val); - } - 2 => { - if (Value.I32.?(val)) { - Ref.at(bytes, decl.field_offsets[index]).val = Value.I32.!(val).val; - } else { - Ref.at(bytes, decl.field_offsets[index]).val = Value.F32.!(val).bits; - } - } - 3 => { - if (Value.I64.?(val)) { - Ref.at(bytes, decl.field_offsets[index]).val = Value.I64.!(val).val; - } else { - Ref.at(bytes, decl.field_offsets[index]).val = Value.F64.!(val).bits; - } - } - 4 => { - var r = Ref.at(bytes, decl.field_offsets[index]); - var pair = Value.V128.!(val); - r.lo_val = pair.low; - r.hi_val = pair.high; - } + -1 => objs[decl.field_offsets[index]] = Value.Ref.!(val).val; + 0 => setHelperV(index, 1, val, Values.unbox_u8, DataWriters.write_range_u8); + 1 => setHelperV(index, 2, val, Values.unbox_u16, DataWriters.write_range_u16); + 2 => setHelperV(index, 4, val, if(Value.I32.?(val), Values.unbox_u, Values.unbox_fu32), DataWriters.write_range_u32); + 3 => setHelperV(index, 8, val, if(Value.I64.?(val), Values.unbox_w, Values.unbox_du64), DataWriters.write_range_u64); + 4 => setHelperV<(u64, u64)>(index, 16, val, Values.unbox_s, DataWriters.write_range_u128); } } - def setFieldI32(index: u32, val: i32) { + private def setHelper(index: u32, len: int, val: T, store: (Range, T) -> void) { var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I32)) { - Ref.at(bytes, decl.field_offsets[index]).val = u32.view(val); - } + var offset = decl.field_offsets[index]; + return store(bytes[offset ..+ len], val); + } + def setFieldI32(index: u32, val: i32) { + setHelper(index, 4, val, DataWriters.write_range_i32); } def setFieldI64(index: u32, val: i64) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.I64)) { - Ref.at(bytes, decl.field_offsets[index]).val = u64.view(val); - } + setHelper(index, 8, val, DataWriters.write_range_i64); } def setFieldI8(index: u32, val: i8) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I8) { - bytes[decl.field_offsets[index]] = u8.view(val); - } + setHelper(index, 1, val, DataWriters.write_range_i8); } def setFieldI16(index: u32, val: i16) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || ft.pack == Packedness.PACKED_I16) { - Ref.at(bytes, decl.field_offsets[index]).val = val; - } + setHelper(index, 2, val, DataWriters.write_range_i16); } def setFieldF32(index: u32, val: float) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F32)) { - Ref.at(bytes, decl.field_offsets[index]).val = val; - } + setHelper(index, 4, val, DataWriters.write_range_float); } def setFieldF64(index: u32, val: double) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.F64)) { - Ref.at(bytes, decl.field_offsets[index]).val = val; - } + setHelper(index, 8, val, DataWriters.write_range_double); } def setFieldRef(index: u32, val: Object) { var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || ValueType.Ref.?(ft.valtype)) { - objs[decl.field_offsets[index]] = val; - } + objs[decl.field_offsets[index]] = val; } def setFieldV128(index: u32, low: u64, high: u64) { - var decl = StructDecl.!(this.decl); - var ft = decl.field_types[index]; - if (Values.NO_CHECKS || (ft.pack == Packedness.UNPACKED && ft.valtype == ValueType.V128)) { - var r = Ref.at(bytes, decl.field_offsets[index]); - r.lo_val = low; - r.hi_val = high; - } + setHelper<(u64, u64)>(index, 16, (low, high), DataWriters.write_range_u128); } } @@ -320,9 +264,7 @@ class HeapArrayF32 extends HeapArrayGeneric { return if(index >= 0 && index < vals.length, float.view(vals[index]), -1); } def setF32(index: u32, val: float) { - if (index >= 0 && index < vals.length) { - vals[index] = u32.view(val); - } + vals[index] = u32.view(val); } } @@ -412,40 +354,27 @@ component ObjectRuntime { for (i = decl.field_types.length - 1; i >= 0; i--) { var index = u31.!(i); match (decl.field_shifts[i]) { - 0 => { - var val = stack.popi(); - obj.setFieldI8(index, i8.view(val)); - } - 1 => { - var val = stack.popi(); - obj.setFieldI16(index, i16.view(val)); - } + 0 => obj.setFieldI8(index, i8.view(stack.popi())); + 1 => obj.setFieldI16(index, i16.view(stack.popi())); 2 => { if (decl.field_types[i].valtype == ValueType.I32) { - var val = stack.popi(); - obj.setFieldI32(index, val); + obj.setFieldI32(index, stack.popi()); } else { - var val = stack.popf(); - obj.setFieldF32(index, val); + obj.setFieldF32(index, stack.popf()); } } 3 => { if (decl.field_types[i].valtype == ValueType.I64) { - var val = stack.popl(); - obj.setFieldI64(index, val); + obj.setFieldI64(index, stack.popl()); } else { - var val = stack.popd(); - obj.setFieldF64(index, val); + obj.setFieldF64(index, stack.popd()); } } 4 => { var pair = stack.pops(); obj.setFieldV128(index, pair.0, pair.1); } - -1 => { - var o = stack.popObject(); - obj.setFieldRef(index, o); - } + -1 => obj.setFieldRef(index, stack.popObject()); } } return obj; @@ -457,12 +386,8 @@ component ObjectRuntime { for (i < decl.field_types.length) { var index = u31.view(i); match (decl.field_shifts[i]) { - 0 => { - obj.setFieldI8(index, 0); - } - 1 => { - obj.setFieldI16(index, 0); - } + 0 => obj.setFieldI8(index, 0); + 1 => obj.setFieldI16(index, 0); 2 => { if (decl.field_types[i].valtype == ValueType.I32) { obj.setFieldI32(index, 0); @@ -477,89 +402,48 @@ component ObjectRuntime { obj.setFieldF64(index, 0.0d); } } - 4 => { - obj.setFieldV128(index, 0, 0); - } - -1 => { - obj.setFieldRef(index, null); - } + 4 => obj.setFieldV128(index, 0, 0); + -1 => obj.setFieldRef(index, null); } } return obj; } + private def arrayNewHelper(stack: ExecStack, decl: ArrayDecl, elem: T, length: u32, make: (ArrayDecl, Array) -> HeapArrayGeneric) { + var vals = Array.new(u31.!(length)); + for (i < vals.length) vals[i] = elem; + stack.push(Value.Ref(make(decl, vals))); + } def ARRAY_NEW(stack: ExecStack, decl: ArrayDecl, length: u32) -> Throwable { match (decl.elem_types[0].pack) { UNPACKED => { match (decl.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - var elem = stack.popu(); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - var elem = stack.popw(); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - var elem = u32.view(stack.popf()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - var elem = u64.view(stack.popd()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); - } + I32 => arrayNewHelper(stack, decl, stack.popu(), length, HeapArrayI32.new); + I64 => arrayNewHelper(stack, decl, stack.popw(), length, HeapArrayI64.new); + F32 => arrayNewHelper(stack, decl, u32.view(stack.popf()), length, HeapArrayF32.new); + F64 => arrayNewHelper(stack, decl, u64.view(stack.popd()), length, HeapArrayF64.new); Ref(nullable, heaptype) => { if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); var val = ObjectI31.!(Value.Ref.!(elem).val).val; - for (i < vals.length) vals[i] = val; - stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); + arrayNewHelper(stack, decl, val, length, HeapArrayI31.new); } else { - var vals = Array.new(u31.!(length)); - var elem = stack.popObject(); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); + arrayNewHelper(stack, decl, stack.popObject(), length, HeapArrayRef.new); } } V128 => { var vals = Array.new(u31.!(length << 1)); var v128 = stack.pops(); - var low = v128.0; - var high = v128.1; for (i = 0; i < vals.length; i += 2) { - vals[i] = low; - vals[i+1] = high; + vals[i ] = v128.0; + vals[i+1] = v128.1; } stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); } - _ => { - var vals = Array.new(u31.!(length)); - var elem = stack.popV(decl.elem_types[0].valtype); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); - } + _ => arrayNewHelper(stack, decl, stack.popV(decl.elem_types[0].valtype), length, HeapArrayValue.new); } } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - var elem = u8.view(stack.popu()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - var elem = u16.view(stack.popu()); - for (i < vals.length) vals[i] = elem; - stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); - } + PACKED_I8 => arrayNewHelper(stack, decl, u8.view(stack.popu()), length, HeapArrayI8.new); + PACKED_I16 => arrayNewHelper(stack, decl, u16.view(stack.popu()), length, HeapArrayI16.new); } return null; } @@ -567,42 +451,15 @@ component ObjectRuntime { match (decl.elem_types[0].pack) { UNPACKED => { match (decl.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u32; - stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u64; - stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = u32.view(0.0f); - stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = u64.view(0.0d); - stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); - } - Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = null; - stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); - } else { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = null; - stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); - } - } - V128 => { - var vals = Array.new(u31.!(length << 1)); - // for (i < vals.length) vals[i] = 0u64; - stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); - } + I32 => stack.push(Value.Ref(HeapArrayI32.new(decl, Array.new(u31.!(length))))); + I64 => stack.push(Value.Ref(HeapArrayI64.new(decl, Array.new(u31.!(length))))); + F32 => stack.push(Value.Ref(HeapArrayF32.new(decl, Array.new(u31.!(length))))); + F64 => stack.push(Value.Ref(HeapArrayF64.new(decl, Array.new(u31.!(length))))); + Ref(nullable, heaptype) => + stack.push(Value.Ref(if(heaptype == HeapType.I31 && !nullable, + HeapArrayI31.new(decl, Array.new(u31.!(length))), + HeapArrayRef.new(decl, Array.new(u31.!(length)))))); + V128 => stack.push(Value.Ref(HeapArrayV128.new(decl, Array.new(u31.!(length << 1))))); _ => { var vals = Array.new(u31.!(length)); var elem = Values.default(decl.elem_types[0].valtype); @@ -611,43 +468,25 @@ component ObjectRuntime { } } } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u8; - stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - // for (i < vals.length) vals[i] = 0u16; - stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); - } + PACKED_I8 => stack.push(Value.Ref(HeapArrayI8.new(decl, Array.new(u31.!(length))))); + PACKED_I16 => stack.push(Value.Ref(HeapArrayI16.new(decl, Array.new(u31.!(length))))); } return null; } + private def arrayNewFixedHelper(stack: ExecStack, decl: ArrayDecl, length: u32, + popper: void -> T, make: (ArrayDecl, Array) -> HeapArrayGeneric) { + var vals = Array.new(u31.!(length)); + for (i = vals.length - 1; i >= 0; i--) vals[i] = popper(); + stack.push(Value.Ref(make(decl, vals))); + } def ARRAY_NEW_FIXED(stack: ExecStack, decl: ArrayDecl, length: u32) -> Throwable { match (decl.elem_types[0].pack) { UNPACKED => { match (decl.elem_types[0].valtype) { - I32 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popu()); - stack.push(Value.Ref(HeapArrayI32.new(decl, vals))); - } - I64 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popw()); - stack.push(Value.Ref(HeapArrayI64.new(decl, vals))); - } - F32 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u32.view(stack.popf()); - stack.push(Value.Ref(HeapArrayF32.new(decl, vals))); - } - F64 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u64.view(stack.popd()); - stack.push(Value.Ref(HeapArrayF64.new(decl, vals))); - } + I32 => arrayNewFixedHelper(stack, decl, length, stack.popu, HeapArrayI32.new); + I64 => arrayNewFixedHelper(stack, decl, length, stack.popw, HeapArrayI64.new); + F32 => arrayNewFixedHelper(stack, decl, length, fun () -> u32 { return u32.view(stack.popf()); }, HeapArrayF32.new); + F64 => arrayNewFixedHelper(stack, decl, length, fun () -> u64 { return u64.view(stack.popd()); }, HeapArrayF64.new); Ref(nullable, heaptype) => { if (heaptype == HeapType.I31 && !nullable) { var vals = Array.new(u31.!(length)); @@ -664,78 +503,45 @@ component ObjectRuntime { var vals = Array.new(u31.!(length << 1)); for (i = vals.length - 2; i >= 0; i -= 2) { var v128 = stack.pops(); - vals[i] = v128.0; + vals[i ] = v128.0; vals[i+1] = v128.1; } stack.push(Value.Ref(HeapArrayV128.new(decl, vals))); } _ => { - var vals = Array.new(u31.!(length)); var t = decl.elem_types[0].valtype; - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); - stack.push(Value.Ref(HeapArrayValue.new(decl, vals))); + arrayNewFixedHelper(stack, decl, length, fun () -> Value { return stack.popV(t); }, HeapArrayValue.new); } } } - PACKED_I8 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u8.view(stack.popu()); - stack.push(Value.Ref(HeapArrayI8.new(decl, vals))); - } - PACKED_I16 => { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = u16.view(stack.popu()); - stack.push(Value.Ref(HeapArrayI16.new(decl, vals))); - } + PACKED_I8 => arrayNewFixedHelper(stack, decl, length, fun () -> u8 { return u8.view(stack.popu()); }, HeapArrayI8.new); + PACKED_I16 => arrayNewFixedHelper(stack, decl, length, fun () -> u16 { return u16.view(stack.popu()); }, HeapArrayI16.new); } return null; } + private def arrayNewDataHelper(stack: ExecStack, instance: Instance, rtt: ArrayDecl, offset: u32, length: u32, data_index: u31, + read: DataReader -> T, make: (ArrayDecl, Array) -> HeapArrayGeneric) -> Throwable { + var r = Runtime.getData(instance, data_index, offset, length, read); + if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); + stack.push(Value.Ref(make(rtt, r.1))); + return null; + } def ARRAY_NEW_DATA(stack: ExecStack, instance: Instance, rtt: ArrayDecl, ddecl: DataDecl, offset: u32, length: u32, data_index: u31) -> Throwable { match (rtt.elem_types[0].pack) { UNPACKED => { match (rtt.elem_types[0].valtype) { - I32 => { - var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u32); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI32.new(rtt, r.1))); - } - I64 => { - var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u64); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI64.new(rtt, r.1))); - } - F32 => { - var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u32); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayF32.new(rtt, r.1))); - } - F64 => { - var r = Runtime.getData(instance, data_index, offset, length, DataReader.read_u64); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayF64.new(rtt, r.1))); - } - V128 => { - var r = Runtime.getData(instance, data_index, offset, length << 1, DataReader.read_u64); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayV128.new(rtt, r.1))); - } - _ => { - return stack.trap(TrapReason.ERROR); - } + I32 => return arrayNewDataHelper(stack, instance, rtt, offset, length, data_index, DataReader.read_u32, HeapArrayI32.new); + I64 => return arrayNewDataHelper(stack, instance, rtt, offset, length, data_index, DataReader.read_u64, HeapArrayI64.new); + F32 => return arrayNewDataHelper(stack, instance, rtt, offset, length, data_index, DataReader.read_u32, HeapArrayF32.new); + F64 => return arrayNewDataHelper(stack, instance, rtt, offset, length, data_index, DataReader.read_u64, HeapArrayF64.new); + V128 => return arrayNewDataHelper(stack, instance, rtt, offset, length << 1, data_index, DataReader.read_u64, HeapArrayV128.new); + _ => return stack.trap(TrapReason.ERROR); } } - PACKED_I8 => { - var r = Runtime.getData(instance, data_index, offset, length, DataReader.read1); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI8.new(rtt, r.1))); - } - PACKED_I16 => { - var r = Runtime.getData(instance, data_index, offset, length, fun (d: DataReader) => DataReaders.read_range_u16(d.acquire(2))); - if (!r.0) return stack.trap(TrapReason.MEMORY_OOB); - stack.push(Value.Ref(HeapArrayI16.new(rtt, r.1))); - } + PACKED_I8 => return arrayNewDataHelper(stack, instance, rtt, offset, length, data_index, DataReader.read1, HeapArrayI8.new); + PACKED_I16 => return arrayNewDataHelper(stack, instance, rtt, offset, length, data_index, + fun (d: DataReader) => DataReaders.read_range_u16(d.acquire(2)), HeapArrayI16.new); } - return null; } def ARRAY_NEW_ELEM(stack: ExecStack, instance: Instance, rtt: ArrayDecl, edecl: ElemDecl, offset: u32, length: u32) -> Throwable { match (rtt.elem_types[0].pack) { @@ -753,295 +559,83 @@ component ObjectRuntime { } return null; } + def rangeFill(r: Range, val: T) { for (i < r.length) r[i] = val; } def ARRAY_FILL(obj: HeapArrayGeneric, rtt: ArrayDecl, index: int, size: u32, val: Value) -> Throwable { match (obj) { - x: HeapArrayValue => { - var r = x.vals[index ..+ size]; - for (i < r.length) r[i] = val; - } - x: HeapArrayI32 => { - var r = x.vals[index ..+ size]; - var v = Value.I32.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayI31 => { - var r = x.vals[index ..+ size]; - var v = Value.I31.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayI64 => { - var r = x.vals[index ..+ size]; - var v = Value.I64.!(val).val; - for (i < r.length) r[i] = v; - } - x: HeapArrayI8 => { - var r = x.vals[index ..+ size]; - var v = u8.view(Value.I32.!(val).val); - for (i < r.length) r[i] = v; - } - x: HeapArrayI16 => { - var r = x.vals[index ..+ size]; - var v = u16.view(Value.I32.!(val).val); - for (i < r.length) r[i] = v; - } - x: HeapArrayF32 => { - var r = x.vals[index ..+ size]; - var v = Value.F32.!(val).bits; - for (i < r.length) r[i] = v; - } - x: HeapArrayF64 => { - var r = x.vals[index ..+ size]; - var v = Value.F64.!(val).bits; - for (i < r.length) r[i] = v; - } - x: HeapArrayRef => { - var r = x.vals[index ..+ size]; - var v = Value.Ref.!(val).val; - for (i < r.length) r[i] = v; - } + x: HeapArrayValue => rangeFill(x.vals[index ..+ size], val); + x: HeapArrayI32 => rangeFill(x.vals[index ..+ size], Values.unbox_u(val)); + x: HeapArrayI31 => rangeFill(x.vals[index ..+ size], Value.I31.!(val).val); + x: HeapArrayI64 => rangeFill(x.vals[index ..+ size], Values.unbox_w(val)); + x: HeapArrayI8 => rangeFill(x.vals[index ..+ size], Values.unbox_u8(val)); + x: HeapArrayI16 => rangeFill(x.vals[index ..+ size], Values.unbox_u16(val)); + x: HeapArrayF32 => rangeFill(x.vals[index ..+ size], Values.unbox_fu32(val)); + x: HeapArrayF64 => rangeFill(x.vals[index ..+ size], Values.unbox_du64(val)); + x: HeapArrayRef => rangeFill(x.vals[index ..+ size], Value.Ref.!(val).val); x: HeapArrayV128 => { var r = x.vals[index ..+ (size << 1)]; var v128 = Value.V128.!(val); - for (i=0; i < r.length; i+=2) { - r[i] = v128.low; + for (i = 0; i < r.length; i += 2) { + r[i ] = v128.low; r[i+1] = v128.high; } } } return null; } - def ARRAY_COPY(stack: ExecStack, src: HeapArrayGeneric, dst: HeapArrayGeneric, src_offset: u32, dst_offset: u32, size: u32) -> Throwable { - if (HeapArrayValue.?(src) && HeapArrayValue.?(dst)) { - var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); + private def arrayCopyHelper(stack: ExecStack, elems: A -> Array, + dst: HeapArrayGeneric, src: A, dst_offset: u32, src_offset: u32, size: u32) -> Throwable { + if (A.?(dst)) { + var arr = A.!(dst); + var r = ArrayUtil.safeCopy(elems(arr), dst_offset, elems(src), src_offset, size); if (!r) return stack.trap(TrapReason.ARRAY_OOB); return null; } - - if (HeapArrayValue.?(dst)) { - var arr = HeapArrayValue.!(dst); - var r = ArrayUtil.safeCopyDiffTypes(arr.vals, dst_offset, u31.view(src.length()), src_offset, size, src.getValue); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - if (HeapArrayValue.?(src)){ - var arr = HeapArrayValue.!(src); - match (dst) { - x: HeapArrayI32 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI31 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, fun (x: u32) => u31.!(arr.getI31(x))); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI64 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI8 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU8); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayI16 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU16); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayF32 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU32); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayF64 => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getU64); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayRef => { - var r = ArrayUtil.safeCopyDiffTypes(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getRef); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - x: HeapArrayV128 => { - var r = ArrayUtil.safeCopyFromV128(x.vals, dst_offset, u31.view(arr.length()), src_offset, size, arr.getV128); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - - _ => { return stack.trap(TrapReason.ERROR); } - } - } - + return stack.trap(TrapReason.ERROR); + } + def ARRAY_COPY(stack: ExecStack, dst: HeapArrayGeneric, src: HeapArrayGeneric, dst_offset: u32, src_offset: u32, size: u32) -> Throwable { match (src) { - x: HeapArrayI32 => { - if (HeapArrayI32.?(dst)) { - var arr = HeapArrayI32.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI31 => { - if (HeapArrayI31.?(dst)) { - var arr = HeapArrayI31.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI64 => { - if (HeapArrayI64.?(dst)) { - var arr = HeapArrayI64.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI8 => { - if (HeapArrayI8.?(dst)) { - var arr = HeapArrayI8.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayI16 => { - if (HeapArrayI16.?(dst)) { - var arr = HeapArrayI16.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayF32 => { - if (HeapArrayF32.?(dst)) { - var arr = HeapArrayF32.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayF64 => { - if (HeapArrayF64.?(dst)) { - var arr = HeapArrayF64.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayRef => { - if (HeapArrayRef.?(dst)) { - var arr = HeapArrayRef.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } - x: HeapArrayV128 => { - if (HeapArrayV128.?(dst)) { - var arr = HeapArrayV128.!(dst); - var r = ArrayUtil.safeCopy(x.vals, dst_offset, arr.vals, src_offset, size << 1); - if (!r) return stack.trap(TrapReason.ARRAY_OOB); - return null; - } - } + x: HeapArrayI32 => return arrayCopyHelper(stack, HeapArrayI32.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayI31 => return arrayCopyHelper(stack, HeapArrayI31.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayI64 => return arrayCopyHelper(stack, HeapArrayI64.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayI8 => return arrayCopyHelper(stack, HeapArrayI8.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayI16 => return arrayCopyHelper(stack, HeapArrayI16.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayF32 => return arrayCopyHelper(stack, HeapArrayF32.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayF64 => return arrayCopyHelper(stack, HeapArrayF64.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayRef => return arrayCopyHelper(stack, HeapArrayRef.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayV128 => return arrayCopyHelper(stack, HeapArrayV128.vals, dst, x, dst_offset, src_offset, size << 1); } return stack.trap(TrapReason.ERROR); } - def ARRAY_INIT_DATA(stack: ExecStack, obj: HeapArrayGeneric, rtt: ArrayDecl, data: Array, src_offset: u32, dst_offset: u32, size: u32) -> Throwable { - /*match (obj) { - x: HeapArrayValue => { - var t = Runtime.bytesToVals(rtt.elem_types[0], data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); + private def arrayInitDataHelper(stack: ExecStack, obj: HeapArrayGeneric, getBytes: (Array, u32, u32) -> (bool, Array), elems: A -> Array, + data: Array, dst_offset: u32, src_offset: u32, size: u32) -> Throwable { + var t = getBytes(data, src_offset, size); + if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); + match (obj) { + x: A => { + ArrayUtil.safeCopy(elems(x), dst_offset, t.1, 0, size); return null; } - }*/ + } + return stack.trap(TrapReason.ERROR); + } + def ARRAY_INIT_DATA(stack: ExecStack, obj: HeapArrayGeneric, rtt: ArrayDecl, data: Array, dst_offset: u32, src_offset: u32, size: u32) -> Throwable { match (rtt.elem_types[0].pack) { UNPACKED => { match (rtt.elem_types[0].valtype) { - I32 => { - var t = bytesToI32s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayI32 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - I64 => { - var t = bytesToI64s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayI64 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - F32 => { - var t = bytesToI32s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayF32 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - F64 => { - var t = bytesToI64s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayF64 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - V128 => { - var t = bytesToI64s(data, src_offset, size << 1); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - match (obj) { - x: HeapArrayV128 => { - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size << 1); - return null; - } - } - } + I32 => return arrayInitDataHelper(stack, obj, bytesToI32s, HeapArrayI32.vals, data, dst_offset, src_offset, size); + I64 => return arrayInitDataHelper(stack, obj, bytesToI64s, HeapArrayI64.vals, data, dst_offset, src_offset, size); + F32 => return arrayInitDataHelper(stack, obj, bytesToI32s, HeapArrayF32.vals, data, dst_offset, src_offset, size); + F64 => return arrayInitDataHelper(stack, obj, bytesToI64s, HeapArrayF64.vals, data, dst_offset, src_offset, size); + V128 => return arrayInitDataHelper(stack, obj, bytesToI64s, HeapArrayV128.vals, data, dst_offset, src_offset, size << 1); _ => { } } } - PACKED_I8 => { - match (obj) { - x: HeapArrayI8 => { - var t = bytesToI8s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } - PACKED_I16 => { - match (obj) { - x: HeapArrayI16 => { - var t = bytesToI16s(data, src_offset, size); - if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); - ArrayUtil.safeCopy(x.vals, dst_offset, t.1, 0, size); - return null; - } - } - } + PACKED_I8 => return arrayInitDataHelper(stack, obj, bytesToI8s, HeapArrayI8.vals, data, dst_offset, src_offset, size); + PACKED_I16 => return arrayInitDataHelper(stack, obj, bytesToI16s, HeapArrayI16.vals, data, dst_offset, src_offset, size); } return stack.trap(TrapReason.ERROR); } - def ARRAY_INIT_ELEM(stack: ExecStack, obj: HeapArrayGeneric, instance: Instance, edecl: ElemDecl, src_offset: u32, dst_offset: u32, size: u32) -> Throwable { + def ARRAY_INIT_ELEM(stack: ExecStack, obj: HeapArrayGeneric, instance: Instance, edecl: ElemDecl, dst_offset: u32, src_offset: u32, size: u32) -> Throwable { match (obj) { x: HeapArrayI31 => { var r = Runtime.getElems(instance, edecl, src_offset, size); @@ -1063,6 +657,104 @@ component ObjectRuntime { } return null; } + def arrayFromVal(decl: ArrayDecl, val: Value, len: int, f: Value -> T, + build: (ArrayDecl, Array) -> HeapArrayGeneric) -> HeapArrayGeneric { + var elem: T = f(val); + var vvals = Array.new(len); + for (i < len) vvals[i] = elem; + return build(decl, vvals); + } + def evalInitExprObjectArray(t: HeapType.Array, st: StorageType, velem: Value, vlen: int) -> HeapArrayGeneric { + var decl = t.array; + match (st.pack) { + UNPACKED => { + match (st.valtype) { + I32 => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u32 { return Value.I32.!(v).val; }, HeapArrayI32.new); + I64 => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u64 { return Value.I64.!(v).val; }, HeapArrayI64.new); + F32 => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u32 { return Value.F32.!(v).bits; }, HeapArrayF32.new); + F64 => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u64 { return Value.F64.!(v).bits; }, HeapArrayF64.new); + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u31 { return Value.I31.!(v).val; }, HeapArrayI31.new); + } else { + return arrayFromVal(decl, velem, vlen, fun (v: Value) -> Object { return Value.Ref.!(v).val; }, HeapArrayRef.new); + } + } + V128 => { + // does not fit the arrayFromVal pattern + var v128 = Value.V128.!(velem); + var vvals = Array.new(vlen << 1); + for (i = 0; i < vvals.length; i += 2) { + vvals[i ] = v128.low; + vvals[i+1] = v128.high; + } + return HeapArrayV128.new(t.array, vvals); + } + _ => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> Value { return v; }, HeapArrayValue.new); + } + } + PACKED_I8 => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u8 { return u8.view(Value.I32.!(v).val); }, HeapArrayI8.new); + PACKED_I16 => return arrayFromVal(decl, velem, vlen, fun (v: Value) -> u16 { return u16.view(Value.I32.!(v).val); }, HeapArrayI16.new); + } + } + def arrayFromVals(instance: Instance, decl: ArrayDecl, vals: Array, + f: Value -> T, build: (ArrayDecl, Array) -> HeapArrayGeneric) -> HeapArrayGeneric { + var len = vals.length; + var vvals = Array.new(len); + for (i < len) { + var velem = instance.evalInitExpr(vals[i]); + vvals[i] = f(velem); + } + return build(decl, vvals); + } + def evalInitExprObjectFixedArray(instance: Instance, t: HeapType.Array, st: StorageType, vals: Array) -> HeapArrayGeneric { + var vlen = vals.length; + var decl = t.array; + match (st.pack) { + UNPACKED => { + match (st.valtype) { + I32 => return arrayFromVals(instance, decl, vals, fun (v: Value) -> u32 { return Value.I32.!(v).val; }, HeapArrayI32.new); + I64 => return arrayFromVals(instance, decl, vals, fun (v: Value) -> u64 { return Value.I64.!(v).val; }, HeapArrayI64.new); + F32 => return arrayFromVals(instance, decl, vals, fun (v: Value) -> u32 { return Value.F32.!(v).bits; }, HeapArrayF32.new); + F64 => return arrayFromVals(instance, decl, vals, fun (v: Value) -> u64 { return Value.F64.!(v).bits; }, HeapArrayF64.new); + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && !nullable) { + return arrayFromVals(instance, decl, vals, fun (v: Value) -> u31 { return Value.I31.!(v).val; }, HeapArrayI31.new); + } else { + return arrayFromVals(instance, decl, vals, fun (v: Value) -> Object { return Value.Ref.!(v).val; }, HeapArrayRef.new); + } + } + V128 => { + // does not fit the arrayFromVals pattern + var vvals = Array.new(vlen << 1); + for (i = 0; i < vlen; i += 2) { + var velem = instance.evalInitExpr(vals[i]); + var v128 = Value.V128.!(velem); + vvals[i ] = v128.low; + vvals[i+1] = v128.high; + } + return HeapArrayV128.new(t.array, vvals); + } + _ => { + var vvals = Arrays.map(vals, instance.evalInitExpr); + return HeapArrayValue.new(t.array, vvals); + } + } + } + PACKED_I8 => return arrayFromVals(instance, decl, vals, fun (v: Value) -> u8 { return u8.view(Value.I32.!(v).val); }, HeapArrayI8.new); + PACKED_I16 => return arrayFromVals(instance, decl, vals, fun (v: Value) -> u16 { return u16.view(Value.I32.!(v).val); }, HeapArrayI16.new); + } + } + def evalInitExprObjectStruct(t: HeapType.Struct, vvals: Array) -> Object { + var decl = t.sdecl; + var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var obj = HeapStructPair.new(decl, objs, bytes); + for (i < decl.field_types.length) { + obj.setFieldValue(u31.view(i), vvals[i]); + } + return obj; + } } def bytesToBytes(data: Array, offset: u32, length: u32) -> (bool, Array) { @@ -1079,7 +771,7 @@ def bytesToI8s(data: Array, offset: u32, length: u32) -> (bool, Array) var limit = length + offset; var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); for (i < vals.length) vals[i] = d.read_u8(); - return (d.ok, if(d.ok, vals)); + return (d.ok, vals); } def bytesToI8sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { var start = ArrayUtil.boundsCheck(data, offset, length); @@ -1088,7 +780,7 @@ def bytesToI8sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Arra var limit = length + offset; var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); for (i < vals.length) vals[i] = u32.view(d.read_u8()); - return (d.ok, if(d.ok, vals)); + return (d.ok, vals); } def bytesToI16s(data: Array, offset: u32, length: u32) -> (bool, Array) { var nbytes = length << 1; @@ -1098,7 +790,7 @@ def bytesToI16s(data: Array, offset: u32, length: u32) -> (bool, Array, offset: u32, length: u32) -> (bool, Array) { var nbytes = length << 1; @@ -1108,7 +800,7 @@ def bytesToI16sAsI32s(data: Array, offset: u32, length: u32) -> (bool, Arr var limit = offset + nbytes; var d = ExtendedDataReader.new(data).reset(data, start, int.!(limit)); for (i < vals.length) vals[i] = u32.view(d.read_u16()); - return (d.ok, if(d.ok, vals)); + return (d.ok, vals); } def bytesToI32s(data: Array, offset: u32, length: u32) -> (bool, Array) { var nbytes = length << 2; @@ -1118,7 +810,7 @@ def bytesToI32s(data: Array, offset: u32, length: u32) -> (bool, Array, offset: u32, length: u32) -> (bool, Array) { var nbytes = length << 3; @@ -1128,6 +820,6 @@ def bytesToI64s(data: Array, offset: u32, length: u32) -> (bool, Array.new(decl.field_types.length); for (i = fields.length - 1; i >= 0; i--) { fields[i] = stack.popV(decl.field_types[i].valtype); @@ -52,7 +52,7 @@ component Runtime { def STRUCT_NEW_DEFAULT(stack: ExecStack, instance: Instance, struct_index: u31) { var decl = StructDecl.!(instance.heaptypes[struct_index]); var obj: HeapStructGeneric; - if (ObjTuning.structMode == StructMode.Original) { + if (ObjReps.structMode == StructMode.Original) { var fields = Array.new(decl.field_types.length); for (i < fields.length) { fields[i] = Values.default(decl.field_types[i].valtype); @@ -75,15 +75,9 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); var val: Value; match (decl.field_types[field_index].pack) { - PACKED_I8 => { - val = obj.getFieldSignExtend8Value(field_index); - } - PACKED_I16 => { - val = obj.getFieldSignExtend16Value(field_index); - } - UNPACKED => { - return stack.trap(TrapReason.ERROR); - } + PACKED_I8 => val = obj.getFieldSignExtend8Value(field_index); + PACKED_I16 => val = obj.getFieldSignExtend16Value(field_index); + UNPACKED => return stack.trap(TrapReason.ERROR); } stack.push(val); // XXX: pushi return null; @@ -94,15 +88,9 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); var val: Value; match (decl.field_types[field_index].pack) { - PACKED_I8 => { - val = obj.getFieldZeroExtend8Value(field_index); - } - PACKED_I16 => { - val = obj.getFieldZeroExtend16Value(field_index); - } - UNPACKED => { - return stack.trap(TrapReason.ERROR); - } + PACKED_I8 => val = obj.getFieldZeroExtend8Value(field_index); + PACKED_I16 => val = obj.getFieldZeroExtend16Value(field_index); + UNPACKED => return stack.trap(TrapReason.ERROR); } stack.push(val); // XXX: pushi return null; @@ -119,7 +107,7 @@ component Runtime { var decl = ArrayDecl.!(instance.heaptypes[array_index]); var length = stack.popu(); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var vals = Array.new(u31.!(length)); var elem = stack.popV(decl.elem_types[0].valtype); for (i < vals.length) vals[i] = elem; @@ -133,7 +121,7 @@ component Runtime { var decl = ArrayDecl.!(instance.heaptypes[array_index]); var length = stack.popu(); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var vals = Array.new(u31.!(length)); var elem = Values.default(decl.elem_types[0].valtype); for (i < vals.length) vals[i] = elem; @@ -146,7 +134,7 @@ component Runtime { def ARRAY_NEW_FIXED(stack: ExecStack, instance: Instance, array_index: u31, length: u32) -> Throwable { var decl = ArrayDecl.!(instance.heaptypes[array_index]); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var vals = Array.new(u31.!(length)); var t = decl.elem_types[0].valtype; for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popV(t); @@ -163,7 +151,7 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var ddecl = instance.module.data[data_index]; if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var t = bytesToVals(rtt.elem_types[0], ddecl.data, offset, length); if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); stack.push(Value.Ref(HeapArrayValue.new(rtt, t.1))); @@ -179,7 +167,7 @@ component Runtime { var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var edecl = instance.module.elems[elem_index]; if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var r = getElems(instance, edecl, offset, length); if (!r.0) return stack.trap(TrapReason.TABLE_OOB); var vals = Array.new(u31.!(length)); @@ -245,7 +233,7 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); var index = ArrayUtil.boundsCheckWithLength(u31.view(obj.length()), offset, 0, size); if (index < 0) return stack.trap(TrapReason.ARRAY_OOB); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var vals = obj.getArray(); var r = vals[index ..+ size]; for (i < r.length) r[i] = val; @@ -261,7 +249,7 @@ component Runtime { var dst_offset = stack.popu(); var dst = stack.popArray(); if (src == null || dst == null) return stack.trap(TrapReason.NULL_DEREF); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var r = ArrayUtil.safeCopy(dst.getArray(), dst_offset, src.getArray(), src_offset, size); if (!r) return stack.trap(TrapReason.ARRAY_OOB); return null; @@ -279,7 +267,7 @@ component Runtime { if (instance.dropped_data[data_index]) return if(size > 0, stack.trap(TrapReason.DATA_SEGMENT_DROPPED), null); var data = instance.module.data[data_index].data; if (ArrayUtil.boundsCheckWithLength(u31.view(obj.length()), dst_offset, 0, size) < 0) return stack.trap(TrapReason.ARRAY_OOB); - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var t = bytesToVals(rtt.elem_types[0], data, src_offset, size); if (!t.0) return stack.trap(TrapReason.MEMORY_OOB); var vals = obj.getArray(); @@ -298,7 +286,7 @@ component Runtime { if (obj == null) return stack.trap(TrapReason.NULL_DEREF); if (instance.dropped_elems[elem_index]) return if(size > 0, stack.trap(TrapReason.ELEM_SEGMENT_DROPPED), null); var edecl = instance.module.elems[elem_index]; - if (ObjTuning.arrayMode == ArrayMode.Original) { + if (ObjReps.arrayMode == ArrayMode.Original) { var vals = HeapArrayValue.!(obj).vals; var r = getElems(instance, edecl, src_offset, size); if (!r.0) return stack.trap(TrapReason.ARRAY_OOB); @@ -534,6 +522,8 @@ component Runtime { def getData(instance: Instance, data_index: u31, src_offset: u64, size: u64, func: DataReader -> T) -> (bool, Array) { var data = if(!instance.dropped_data[data_index], instance.module.data[data_index]); if (data == null) return (false, null); + var start = ArrayUtil.boundsCheck(data.data, src_offset, size); + if (start < 0) return (false, null); var vals = Array.new(int.!(size)); var d = DataReader.new(data.data).reset(data.data, int.!(src_offset), data.data.length); for (i < vals.length) vals[i] = func(d); @@ -576,6 +566,7 @@ component Runtime { PACKED_I16 => return 2; } } + } class ExtendedDataReader extends DataReader { new(data: Range) super(data) { } @@ -587,11 +578,3 @@ class ExtendedDataReader extends DataReader { return DataReaders.read_range_u16(range); } } -layout Layout_i31 { - +0 val: i31; - =4; -} -layout Layout_u31 { - +0 val: u31; - =4; -} diff --git a/src/engine/Tuning.v3 b/src/engine/Tuning.v3 index 4967cd560..95cde21f3 100644 --- a/src/engine/Tuning.v3 +++ b/src/engine/Tuning.v3 @@ -66,31 +66,3 @@ component SpcTuning { var inlineGlobalAccess = true; // enable inline access of (primitive) globals var disableMemoryBoundsChecks = false; // unsafe! don't emit bounds checks } - -enum ArrayMode(code: int) { - Original(0), // Default value: HeapArrayValue only - Typed(1), // HeapArrayX where X can be one of the storage types -} - -enum StructMode(code: int) { - Original(0), // Default value: Values only - Pair(1) // Having Array (holdings the refs) and Array (holding prims) -} - -def arrayModeFor(code: int) -> ArrayMode { - def modes = [ArrayMode.Original, ArrayMode.Typed]; - return if(code >= 0 && code < modes.length, modes[code], ArrayMode.Original); -} - -def structModeFor(code: int) -> StructMode { - def modes = [StructMode.Original, StructMode.Pair]; - return if(code >= 0 && code < modes.length, modes[code], StructMode.Original); -} - -component ObjTuning { - def arrayModeCode = 1; - def arrayMode = arrayModeFor(arrayModeCode); - - def structModeCode = 1; - def structMode = structModeFor(structModeCode); -} diff --git a/src/engine/Type.v3 b/src/engine/Type.v3 index 6fbef801c..65a1e2c63 100644 --- a/src/engine/Type.v3 +++ b/src/engine/Type.v3 @@ -407,8 +407,9 @@ class StructDecl extends HeapTypeDecl { new(final: bool, supertypes: Array, field_types) super(final, supertypes) { - match (ObjTuning.structMode) { + match (ObjReps.structMode) { Pair => { + // determine field sizes and offsets field_shifts = Array.new(field_types.length); field_offsets = Array.new(field_types.length); for (i < field_types.length) { diff --git a/src/engine/Value.v3 b/src/engine/Value.v3 index a8d58d17c..53e5d5b47 100644 --- a/src/engine/Value.v3 +++ b/src/engine/Value.v3 @@ -231,7 +231,6 @@ component Values { def REF_NULL = FUNCREF_NULL; def NONE = Array.new(0); def OBJS_SHIFT = 255u8; - def NO_CHECKS = false; def render(v: Value, buf: StringBuilder) -> StringBuilder { match (v) { diff --git a/src/engine/compiler/MacroAssembler.v3 b/src/engine/compiler/MacroAssembler.v3 index 4fb1c9fa1..78b833711 100644 --- a/src/engine/compiler/MacroAssembler.v3 +++ b/src/engine/compiler/MacroAssembler.v3 @@ -146,14 +146,14 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_v3_Array_elem_r_ri(kind: ValueKind, dst: Reg, array: Reg, index: int) { emit_mov_r_m(kind, dst, MasmAddr(array, getOffsets().Array_contents + index * scaleOf(kind))); } - def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, dst: Reg) { + def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, src: Reg) { // Generic code, may be overridden to be more efficient emit_shlw_r_i(index, logScaleOf(kind)); emit_addw_r_r(array, index); - emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents), dst); + emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents), src); } - def emit_v3_Array_elem_ri_r(kind: ValueKind, array: Reg, index: int, dst: Reg) { - emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents + index * scaleOf(kind)), dst); + def emit_v3_Array_elem_ri_r(kind: ValueKind, array: Reg, index: int, src: Reg) { + emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents + index * scaleOf(kind)), src); } def emit_v3_Array_length_r_r(dst: Reg, array: Reg); def emit_v3_Array_bounds_check_rr(array: Reg, index: Reg, oob_label: MasmLabel) { diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index a516e4a37..55b272199 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1668,7 +1668,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl emit_call_runtime_op2n(Opcode.STRUCT_GET, struct_index, field_index, 1, [etype], true); return; } - match (ObjTuning.structMode) { + match (ObjReps.structMode) { Pair => { var offset = decl.field_offsets[field_index]; var kind = ValueTypes.kind(etype); @@ -1692,7 +1692,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } def visit_STRUCT_GET_S(struct_index: u31, field_index: u31) { var decl = StructDecl.!(module.heaptypes[struct_index]); - match (ObjTuning.structMode) { + match (ObjReps.structMode) { Pair => { var offset = decl.field_offsets[field_index]; var str = popReg().reg; @@ -1715,7 +1715,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } def visit_STRUCT_GET_U(struct_index: u31, field_index: u31) { var decl = StructDecl.!(module.heaptypes[struct_index]); - match (ObjTuning.structMode) { + match (ObjReps.structMode) { Pair => { var offset = decl.field_offsets[field_index]; var str = popReg().reg; @@ -1745,7 +1745,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl emit_call_runtime_op2n(Opcode.STRUCT_SET, struct_index, field_index, 2, ValueTypes.NONE, true); return; } - match (ObjTuning.structMode) { + match (ObjReps.structMode) { Pair => { var offset = decl.field_offsets[field_index]; var kind = ValueTypes.kind(etype); @@ -1793,7 +1793,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl emit_call_runtime_op1n(Opcode.ARRAY_GET, ht_index, 2, [decl.elem_types[0].valtype], true); return; } - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Typed => { var idx = popReg().reg; var arr = popReg().reg; @@ -1818,7 +1818,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var stg_type = decl.elem_types[0]; var etype = stg_type.valtype; var kind = ValueTypes.kind(etype); - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Typed => { var idx = popReg().reg; var arr = popReg().reg; @@ -1846,7 +1846,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var stg_type = decl.elem_types[0]; var etype = stg_type.valtype; var kind = ValueTypes.kind(etype); - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Typed => { var idx = popReg().reg; var arr = popReg().reg; @@ -1880,7 +1880,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl emit_call_runtime_op1n(Opcode.ARRAY_SET, ht_index, 3, ValueTypes.NONE, true); return; } - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Typed => { var val = popReg().reg; var idx = popReg().reg; @@ -1891,9 +1891,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); - masm.emit_shlw_r_i(idx, masm.logScaleOf(kind)); - masm.emit_addw_r_r(tmp, idx); - masm.emit_mov_m_r(kind, MasmAddr(tmp, masm.getOffsets().Array_contents), val); + masm.emit_v3_Array_elem_rr_r(kind, tmp, idx, val); } _ => { // Original format, and any other added formats without explicit coverage, use run-time @@ -1902,7 +1900,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl } } def visit_ARRAY_LEN() { - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Original, Typed => { var sv = popReg().reg; var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck diff --git a/src/engine/x86-64/X86_64Interpreter.v3 b/src/engine/x86-64/X86_64Interpreter.v3 index 21c79db8d..ab2d1a76e 100644 --- a/src/engine/x86-64/X86_64Interpreter.v3 +++ b/src/engine/x86-64/X86_64Interpreter.v3 @@ -2272,7 +2272,7 @@ class X86_64InterpreterGen(ic: X86_64InterpreterCode, w: DataWriter) { asm.movq_r_m(r_tmp0, vsph[-1].value); asm.q.cmp_r_i(r_tmp0, 0); asm.jc_rel_far(X86_64Conds.Z, newTrapLabel(TrapReason.NULL_DEREF)); - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Original, Typed => { asm.movq_r_m(r_tmp0, r_tmp0.plus(offsets.HeapArray_vals)); asm.movd_r_m(r_tmp0, r_tmp0.plus(offsets.Array_length)); diff --git a/src/util/ArrayUtil.v3 b/src/util/ArrayUtil.v3 index 1a62ce53d..915679904 100644 --- a/src/util/ArrayUtil.v3 +++ b/src/util/ArrayUtil.v3 @@ -18,20 +18,6 @@ component ArrayUtil { if (offset > 0 || size > 0) return int.min; return 0; } - def boundsCheckX(data: Array, offset: u64, index: u64, size: u64) -> int { - if (data != null) { - var l = data.length; - if (offset > l) return int.min; - if (size > l) return int.min; - var x = offset + index; - if ((x + size) > l) return int.min; - return int.view(x); - } - if (offset > 0) return int.min; - if (size > 0) return int.min; - if (index > 0) return int.min; - return 0; - } def boundsCheckWithLength(length: u31, offset: u64, index: u64, size: u64) -> int { var x = offset + index; return if(x + size > length, int.min, int.!(x)); @@ -49,29 +35,6 @@ component ArrayUtil { } return true; } - def safeCopyDiffTypes(dst: Array, dst_offset: u32, src_length: u31, src_offset: u32, size: u32, - f: u32 -> T) -> bool { - var i = boundsCheck(dst, dst_offset, size); - if (i < 0) return false; - var j = boundsCheckWithLength(src_length, 0, src_offset, size); - if (j < 0) return false; - // We assume T and U are different types, so src and dst are not overlapping - for (k < int.!(size)) dst[i + k] = f(u32.view(j + k)); - return true; - } - def safeCopyFromV128(dst: Array, dst_offset: u32, src_length: u31, src_offset: u32, size: u32, - f: u32 -> (u64, u64)) -> bool { - var i = boundsCheck(dst, dst_offset, size << 1); - if (i < 0) return false; - var j = boundsCheckWithLength(src_length, 0, src_offset, size); - if (j < 0) return false; - for (k < int.!(size)) { - var v128 = f(u32.view(j++)); - dst[i++] = v128.0; - dst[i++] = v128.1; - } - return true; - } def safeCopyF(dst: Array, dst_offset: u64, src: Array, src_offset: u64, size: u64, f: S -> D) -> bool { var i = boundsCheck(dst, dst_offset, size); if (i < 0) return false; diff --git a/test/unittest/ExeTest.v3 b/test/unittest/ExeTest.v3 index 703048403..81d697156 100644 --- a/test/unittest/ExeTest.v3 +++ b/test/unittest/ExeTest.v3 @@ -2492,15 +2492,9 @@ def test_array_get(t: ExeTester) { ]); for (len < 5) { var arr: HeapArrayGeneric; - match (ObjTuning.arrayMode) { - Typed => { - var vals = Array.new(len); - arr = HeapArrayF64.new(at, vals); - } - _ => { - var vals = Array.new(len); - arr = HeapArrayValue.new(at, vals); - } + match (ObjReps.arrayMode) { + Typed => arr = HeapArrayF64.new(at, Array.new(len)); + _ => arr = HeapArrayValue.new(at, Array.new(len)); } var obj = Value.Ref(arr); for (j < len) arr.setValue(u32.!(j), Value.F64(0x99u + u32.view(len))); @@ -2532,7 +2526,7 @@ def test_array_get_su(t: ExeTester) { ]); for (len < 5) { var arr: HeapArrayGeneric; - match (ObjTuning.arrayMode) { + match (ObjReps.arrayMode) { Original => { var elems = Array.new(len); arr = HeapArrayValue.new(at, elems); @@ -2583,11 +2577,8 @@ def test_array_set(t: ExeTester) { Opcode.ARRAY_SET.prefix, u8.!(Opcode.ARRAY_SET.code), heapIndexByte(at) ]); var obj: HeapArrayGeneric; - match (ObjTuning.arrayMode) { - Typed => { - var vals = [0x99999u64, 55u64]; - obj = HeapArrayF64.new(at, vals); - } + match (ObjReps.arrayMode) { + Typed => obj = HeapArrayF64.new(at, [0x99999u64, 55u64]); _ => { var vals: Array = [Value.F64(0x99999), Value.F64(55)]; obj = HeapArrayValue.new(at, vals); @@ -2620,15 +2611,9 @@ def test_array_len(t: ExeTester) { ]); for (i < 5) { var arr: HeapArrayGeneric; - match (ObjTuning.arrayMode) { - Typed => { - var vals = Array.new(i); - arr = HeapArrayF64.new(at, vals); - } - _ => { - var vals = Array.new(i); - arr = HeapArrayValue.new(at, vals); - } + match (ObjReps.arrayMode) { + Typed => arr = HeapArrayF64.new(at, Array.new(i)); + _ => arr = HeapArrayValue.new(at, Array.new(i)); } var obj = Value.Ref(arr); var r = t.run([obj]); @@ -3147,7 +3132,7 @@ def test_table_get_traploc(i: ExeTester) { } def heapStructNew(decl: StructDecl, vals: Array) -> HeapStructGeneric { - match (ObjTuning.structMode) { + match (ObjReps.structMode) { Original => { var fields = Array.new(decl.field_types.length); for (i < fields.length) { From db3f330beef5b36d8fe08b7c36c6ae689e4e2804 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Tue, 27 Jan 2026 17:53:25 -0500 Subject: [PATCH 07/11] These edits fix a bug in SinglePassCompiler and small issues around continuations. I'm hoping the CI now passes, but I note that a Wasm array of continuations will probably break Wizard. I'll be emailing about that ... --- src/engine/Value.v3 | 2 ++ src/engine/compiler/SinglePassCompiler.v3 | 2 ++ src/engine/v3/V3Interpreter.v3 | 2 +- src/engine/x86-64/X86_64MacroAssembler.v3 | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine/Value.v3 b/src/engine/Value.v3 index 1b99efbce..6de70f55a 100644 --- a/src/engine/Value.v3 +++ b/src/engine/Value.v3 @@ -375,6 +375,7 @@ component Values { x: u32 => return box_u(x); x: i64 => return box_l(x); x: u64 => return box_w(x); + x: Continuation => return Value.Cont(x); x: Object => return Value.Ref(x); _ => System.error("BoxError", "no matching boxing operation for Virgil value"); @@ -398,6 +399,7 @@ component Values { F32 => return ValueKind.F32; F64 => return ValueKind.F64; V128 => return ValueKind.V128; + Cont => return Continuations.valueKind; _ => return ValueKind.REF; } } diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index 2d500a809..8a32b0016 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1706,6 +1706,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_breq_r_l(str, 0, label); if (ValueType.Ref.?(etype)) { masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); + offset = offset << 3; } else { masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); } @@ -1784,6 +1785,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_breq_r_l(str, 0, label); if (ValueType.Ref.?(etype)) { masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); + offset = offset << 3; } else { masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); } diff --git a/src/engine/v3/V3Interpreter.v3 b/src/engine/v3/V3Interpreter.v3 index 2eb9e096b..b483fc07b 100644 --- a/src/engine/v3/V3Interpreter.v3 +++ b/src/engine/v3/V3Interpreter.v3 @@ -1809,7 +1809,7 @@ class V3Interpreter extends WasmStack { } def popArray() -> HeapArrayGeneric { return HeapArrayGeneric.!(Value.Ref.!(values.pop()).val); } def popStruct() -> HeapStructGeneric { return HeapStructGeneric.!(Value.Ref.!(values.pop()).val); } - def popContinuation() -> Continuation { return Continuation.!(Value.Ref.!(values.pop()).val); } + def popContinuation() -> Continuation { return Continuation.!(Value.Cont.!(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))); } diff --git a/src/engine/x86-64/X86_64MacroAssembler.v3 b/src/engine/x86-64/X86_64MacroAssembler.v3 index b23252f50..3205dda80 100644 --- a/src/engine/x86-64/X86_64MacroAssembler.v3 +++ b/src/engine/x86-64/X86_64MacroAssembler.v3 @@ -117,7 +117,7 @@ class X86_64MacroAssembler extends MacroAssembler { F32 => asm.movss_m_s(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), X(dst)); I64, REF => asm.movq_m_r(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), G(dst)); F64 => asm.movsd_m_s(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), X(dst)); - V128 => asm.movdqu_m_s(X86_64Addr.new(a, i, 16, getOffsets().Array_contents), X(dst)); // TODO: can't scale by 16 + V128, REF_U64 => asm.movdqu_m_s(X86_64Addr.new(a, i, 16, getOffsets().Array_contents), X(dst)); // TODO: can't scale by 16 } } def emit_v3_Array_length_r_r(dst: Reg, array: Reg) { From b5751cc918571eb3be8e5b17507d576c763fe6aa Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 2 Feb 2026 20:50:19 -0500 Subject: [PATCH 08/11] Updates for unboxed continuations to work with new struct/array representations --- src/engine/ObjectReps.v3 | 117 ++++++-- src/engine/Type.v3 | 17 +- src/engine/Value.v3 | 16 +- src/engine/WasmStack.v3 | 2 +- src/engine/compiler/MacroAssembler.v3 | 8 +- src/engine/compiler/SinglePassCompiler.v3 | 142 ++++++---- src/engine/continuation/BoxedContinuation.v3 | 13 +- .../continuation/UnboxedContinuation.v3 | 16 +- src/engine/v3/V3Interpreter.v3 | 4 +- src/engine/x86-64/X86_64Interpreter.v3 | 2 +- src/engine/x86-64/X86_64MacroAssembler.v3 | 30 +- src/engine/x86-64/X86_64Stack.v3 | 28 +- src/engine/x86-64/X86_64Target.v3 | 2 +- test/unittest/CodeWriter.v3 | 76 ++++++ test/unittest/ExeTest.v3 | 257 +++++++++++++++++- 15 files changed, 612 insertions(+), 118 deletions(-) create mode 100644 test/unittest/CodeWriter.v3 diff --git a/src/engine/ObjectReps.v3 b/src/engine/ObjectReps.v3 index 05268b476..90dd99b29 100644 --- a/src/engine/ObjectReps.v3 +++ b/src/engine/ObjectReps.v3 @@ -26,12 +26,23 @@ component ObjReps { def structModeCode = 1; def structMode = structModeFor(structModeCode); + + // other constants related to representations + def REF_SHIFT = -1i8; // indicates an object slot for StructMode.Pair + def REF_U64_SHIFT = -2i8; // indicates a REF_U64 for StructMode.Pair + + def packFieldOffsets(refs_offset: i32, bytes_offset: i32) -> i32 { + return (refs_offset << 16) | bytes_offset; + } + def unpackFieldOffsets(offset: i32) -> (i32, i32) { + return (offset >>> 16, offset & 0xFFFF); + } } // Paired array representation: an Array holds all the reference fields, // and an Array holds all the primitive values class HeapStructPair extends HeapStructGeneric { - def objs: Array; + def objs: Array; def bytes: Array; new(decl: StructDecl, objs, bytes) super(decl) { } @@ -42,7 +53,10 @@ class HeapStructPair extends HeapStructGeneric { var decl = StructDecl.!(this.decl); var offset = decl.field_offsets[index]; match (decl.field_shifts[index]) { - -1 => return Value.Ref(objs[offset]); + ObjReps.REF_SHIFT => return if(HeapType.Cont.?(ValueType.Ref.!(decl.field_types[index].valtype).heap), + Value.Cont(Continuations.fromObject(objs[offset])), + Value.Ref(Object.!(objs[offset]))); + ObjReps.REF_U64_SHIFT => return Value.Cont(getFieldCont(index)); 0 => return getHelperV(offset, 1, DataReaders.read_range_u32_u8, Values.box_u); 1 => return getHelperV(offset, 2, DataReaders.read_range_u32_u16, Values.box_u); 2 => return getHelperV(offset, 4, DataReaders.read_range_u32, @@ -94,7 +108,14 @@ class HeapStructPair extends HeapStructGeneric { } def getFieldRef(index: u32) -> Object { var decl = StructDecl.!(this.decl); - return objs[decl.field_offsets[index]]; + return Object.!(objs[decl.field_offsets[index]]); + } + def getFieldCont(index: u32) -> Continuation { // use this only for REF_U64 Continuations + var decl = StructDecl.!(this.decl); + var offsets = ObjReps.unpackFieldOffsets(decl.field_offsets[index]); + var stack = WasmStack.!(objs[offsets.0]); + var version = DataReaders.read_range_u64(bytes[offsets.1 ..+ 8]); + return Continuations.continuationWithVersion(stack, version); } def getFieldV128(index: u32) -> (u64, u64) { return getHelper<(u64, u64)>(index, 16, DataReaders.read_range_u128); @@ -107,7 +128,8 @@ class HeapStructPair extends HeapStructGeneric { def setFieldValue(index: u31, val: Value) { var decl = StructDecl.!(this.decl); match (decl.field_shifts[index]) { - -1 => objs[decl.field_offsets[index]] = Value.Ref.!(val).val; + ObjReps.REF_SHIFT => objs[decl.field_offsets[index]] = if(Value.Cont.?(val), Continuations.getObject(Value.Cont.!(val).val), Value.Ref.!(val).val); + ObjReps.REF_U64_SHIFT => setFieldCont(index, Value.Cont.!(val).val); 0 => setHelperV(index, 1, val, Values.unbox_u8, DataWriters.write_range_u8); 1 => setHelperV(index, 2, val, Values.unbox_u16, DataWriters.write_range_u16); 2 => setHelperV(index, 4, val, if(Value.I32.?(val), Values.unbox_u, Values.unbox_fu32), DataWriters.write_range_u32); @@ -142,6 +164,12 @@ class HeapStructPair extends HeapStructGeneric { var decl = StructDecl.!(this.decl); objs[decl.field_offsets[index]] = val; } + def setFieldCont(index: u32, val: Continuation) { // use this only for REF_U64 Continuations + var decl = StructDecl.!(this.decl); + var offsets = ObjReps.unpackFieldOffsets(decl.field_offsets[index]); + objs[offsets.0] = Continuations.getStoredStack(val); + DataWriters.write_range_u64(bytes[offsets.1 ..+ 8], Continuations.getStoredVersion(val)); + } def setFieldV128(index: u32, low: u64, high: u64) { setHelper<(u64, u64)>(index, 16, (low, high), DataWriters.write_range_u128); } @@ -318,6 +346,26 @@ class HeapArrayRef extends HeapArrayGeneric { } } +class HeapArrayCont extends HeapArrayGeneric { + def vals: Array; + new(decl: ArrayDecl, vals) super(decl) { } + + def length() -> int { return vals.length; } + + def getValue(index: u32) -> Value { + return Value.Cont(vals[index]); + } + def setValue(index: u32, val: Value) { + vals[index] = Value.Cont.!(val).val; + } + def getCont(index: u32) -> Continuation { + return vals[index]; + } + def setCont(index: u32, val: Continuation) { + vals[index] = val; + } +} + // A v128 uses two adjacent u64 entries in the vals array class HeapArrayV128 extends HeapArrayGeneric { def vals: Array; // The array format: [low 0, high 0, low 1, high 1, ...] @@ -349,7 +397,7 @@ class HeapArrayV128 extends HeapArrayGeneric { component ObjectRuntime { def STRUCT_NEW(stack: ExecStack, decl: StructDecl) -> HeapStructGeneric { var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); var obj: HeapStructGeneric = HeapStructPair.new(decl, objs, bytes); for (i = decl.field_types.length - 1; i >= 0; i--) { var index = u31.!(i); @@ -374,14 +422,15 @@ component ObjectRuntime { var pair = stack.pops(); obj.setFieldV128(index, pair.0, pair.1); } - -1 => obj.setFieldRef(index, stack.popObject()); + ObjReps.REF_SHIFT => obj.setFieldRef(index, stack.popObject()); + ObjReps.REF_U64_SHIFT => obj.setFieldCont(index, stack.popContinuation()); } } return obj; } def STRUCT_NEW_DEFAULT(decl: StructDecl) -> HeapStructGeneric { var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); var obj: HeapStructGeneric = HeapStructPair.new(decl, objs, bytes); for (i < decl.field_types.length) { var index = u31.view(i); @@ -403,7 +452,8 @@ component ObjectRuntime { } } 4 => obj.setFieldV128(index, 0, 0); - -1 => obj.setFieldRef(index, null); + ObjReps.REF_SHIFT => obj.setFieldRef(index, null); + ObjReps.REF_U64_SHIFT => obj.setFieldCont(index, Continuations.NULL); } } return obj; @@ -426,6 +476,9 @@ component ObjectRuntime { var elem = stack.popV(ValueType.Ref(false, HeapType.I31)); var val = ObjectI31.!(Value.Ref.!(elem).val).val; arrayNewHelper(stack, decl, val, length, HeapArrayI31.new); + } else if (HeapType.Cont.?(heaptype)) { + var val = stack.popContinuation(); + arrayNewHelper(stack, decl, val, length, HeapArrayCont.new); } else { arrayNewHelper(stack, decl, stack.popObject(), length, HeapArrayRef.new); } @@ -447,18 +500,23 @@ component ObjectRuntime { } return null; } + private def arrayNewDefaultHelper(stack: ExecStack, decl: ArrayDecl, length: u32, make: (ArrayDecl, Array) -> HeapArrayGeneric) { + var vals = Array.new(u31.!(length)); + stack.push(Value.Ref(make(decl, vals))); + } def ARRAY_NEW_DEFAULT(stack: ExecStack, decl: ArrayDecl, length: u32) -> Throwable { match (decl.elem_types[0].pack) { UNPACKED => { match (decl.elem_types[0].valtype) { - I32 => stack.push(Value.Ref(HeapArrayI32.new(decl, Array.new(u31.!(length))))); - I64 => stack.push(Value.Ref(HeapArrayI64.new(decl, Array.new(u31.!(length))))); - F32 => stack.push(Value.Ref(HeapArrayF32.new(decl, Array.new(u31.!(length))))); - F64 => stack.push(Value.Ref(HeapArrayF64.new(decl, Array.new(u31.!(length))))); - Ref(nullable, heaptype) => - stack.push(Value.Ref(if(heaptype == HeapType.I31 && !nullable, - HeapArrayI31.new(decl, Array.new(u31.!(length))), - HeapArrayRef.new(decl, Array.new(u31.!(length)))))); + I32 => arrayNewDefaultHelper(stack, decl, length, HeapArrayI32.new); + I64 => arrayNewDefaultHelper(stack, decl, length, HeapArrayI64.new); + F32 => arrayNewDefaultHelper(stack, decl, length, HeapArrayF32.new); + F64 => arrayNewDefaultHelper(stack, decl, length, HeapArrayF64.new); + Ref(nullable, heaptype) => { + if (heaptype == HeapType.I31 && ! nullable) arrayNewDefaultHelper(stack, decl, length, HeapArrayI31.new); + else if (HeapType.Cont.?(heaptype)) arrayNewDefaultHelper(stack, decl, length, HeapArrayCont.new); + else arrayNewDefaultHelper(stack, decl, length, HeapArrayRef.new); + } V128 => stack.push(Value.Ref(HeapArrayV128.new(decl, Array.new(u31.!(length << 1))))); _ => { var vals = Array.new(u31.!(length)); @@ -468,8 +526,8 @@ component ObjectRuntime { } } } - PACKED_I8 => stack.push(Value.Ref(HeapArrayI8.new(decl, Array.new(u31.!(length))))); - PACKED_I16 => stack.push(Value.Ref(HeapArrayI16.new(decl, Array.new(u31.!(length))))); + PACKED_I8 => arrayNewDefaultHelper(stack, decl, length, HeapArrayI8.new); + PACKED_I16 => arrayNewDefaultHelper(stack, decl, length, HeapArrayI16.new); } return null; } @@ -488,16 +546,12 @@ component ObjectRuntime { F32 => arrayNewFixedHelper(stack, decl, length, fun () -> u32 { return u32.view(stack.popf()); }, HeapArrayF32.new); F64 => arrayNewFixedHelper(stack, decl, length, fun () -> u64 { return u64.view(stack.popd()); }, HeapArrayF64.new); Ref(nullable, heaptype) => { - if (heaptype == HeapType.I31 && !nullable) { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = ObjectI31.!(stack.popObject()).val; - stack.push(Value.Ref(HeapArrayI31.new(decl, vals))); - - } else { - var vals = Array.new(u31.!(length)); - for (i = vals.length - 1; i >= 0; i--) vals[i] = stack.popObject(); - stack.push(Value.Ref(HeapArrayRef.new(decl, vals))); - } + if (heaptype == HeapType.I31 && !nullable) + arrayNewFixedHelper(stack, decl, length, fun () -> u31 { return ObjectI31.!(stack.popObject()).val; }, HeapArrayI31.new); + else if (HeapType.Cont.?(heaptype)) + arrayNewFixedHelper(stack, decl, length, stack.popContinuation, HeapArrayCont.new); + else + arrayNewFixedHelper(stack, decl, length, stack.popObject, HeapArrayRef.new); } V128 => { var vals = Array.new(u31.!(length << 1)); @@ -543,6 +597,7 @@ component ObjectRuntime { fun (d: DataReader) => DataReaders.read_range_u16(d.acquire(2)), HeapArrayI16.new); } } + // Note: handles only Ref, not I31 (limitation) and not continuations (should not happen (?)) def ARRAY_NEW_ELEM(stack: ExecStack, instance: Instance, rtt: ArrayDecl, edecl: ElemDecl, offset: u32, length: u32) -> Throwable { match (rtt.elem_types[0].pack) { UNPACKED => { @@ -571,6 +626,7 @@ component ObjectRuntime { x: HeapArrayF32 => rangeFill(x.vals[index ..+ size], Values.unbox_fu32(val)); x: HeapArrayF64 => rangeFill(x.vals[index ..+ size], Values.unbox_du64(val)); x: HeapArrayRef => rangeFill(x.vals[index ..+ size], Value.Ref.!(val).val); + x: HeapArrayCont => rangeFill(x.vals[index ..+ size], Value.Cont.!(val).val); x: HeapArrayV128 => { var r = x.vals[index ..+ (size << 1)]; var v128 = Value.V128.!(val); @@ -602,6 +658,7 @@ component ObjectRuntime { x: HeapArrayF32 => return arrayCopyHelper(stack, HeapArrayF32.vals, dst, x, dst_offset, src_offset, size); x: HeapArrayF64 => return arrayCopyHelper(stack, HeapArrayF64.vals, dst, x, dst_offset, src_offset, size); x: HeapArrayRef => return arrayCopyHelper(stack, HeapArrayRef.vals, dst, x, dst_offset, src_offset, size); + x: HeapArrayCont => return arrayCopyHelper(stack, HeapArrayCont.vals, dst, x, dst_offset, src_offset, size); x: HeapArrayV128 => return arrayCopyHelper(stack, HeapArrayV128.vals, dst, x, dst_offset, src_offset, size << 1); } return stack.trap(TrapReason.ERROR); @@ -653,7 +710,7 @@ component ObjectRuntime { x.vals[int.!(dst_offset)+i] = r.1[i]; } } - _ => {return stack.trap(TrapReason.ARRAY_OOB);} + _ => { return stack.trap(TrapReason.ARRAY_OOB); } // on the belief that continuations cannot be Elements } return null; } @@ -748,7 +805,7 @@ component ObjectRuntime { def evalInitExprObjectStruct(t: HeapType.Struct, vvals: Array) -> Object { var decl = t.sdecl; var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); var obj = HeapStructPair.new(decl, objs, bytes); for (i < decl.field_types.length) { obj.setFieldValue(u31.view(i), vvals[i]); diff --git a/src/engine/Type.v3 b/src/engine/Type.v3 index 73efb6825..72c4a6d03 100644 --- a/src/engine/Type.v3 +++ b/src/engine/Type.v3 @@ -224,6 +224,9 @@ component ValueTypes { def RefFunc(nullable: bool, x: SigDecl) -> ValueType.Ref { return ValueType.Ref(nullable, HeapType.Func(x)); } + def RefCont(nullable: bool, x: ContDecl) -> ValueType.Ref { + return ValueType.Ref(nullable, HeapType.Cont(x)); + } } // Implementation detail in comparing recursive types. Computes the relation between @@ -433,9 +436,17 @@ class StructDecl extends HeapTypeDecl { field_offsets[i] = num_bytes; num_bytes += 16; } - Ref => { - field_shifts[i] = -1; - field_offsets[i] = num_refs++; + Ref(nullable, ht) => { + if (HeapType.Cont.?(ht) && !Continuations.boxed) { + field_shifts[i] = ObjReps.REF_U64_SHIFT; + var refs_offset = num_refs++; + var bytes_offset = num_bytes; + num_bytes += 8; + field_offsets[i] = ObjReps.packFieldOffsets(refs_offset, bytes_offset); + } else { + field_shifts[i] = ObjReps.REF_SHIFT; + field_offsets[i] = num_refs++; + } } _ => { } } diff --git a/src/engine/Value.v3 b/src/engine/Value.v3 index 6de70f55a..4bb338cad 100644 --- a/src/engine/Value.v3 +++ b/src/engine/Value.v3 @@ -52,7 +52,7 @@ class HeapStructGeneric extends HeapObject { def getFieldF32(index: u32) -> float; def getFieldF64(index: u32) -> double; def getFieldRef(index: u32) -> Object; - def getFieldTRef(index: u32) -> Object; + def getFieldCont(index: u32) -> Continuation; def getFieldV128(index: u32) -> (u64, u64); def setFieldValue(index: u31, val: Value); @@ -63,7 +63,7 @@ class HeapStructGeneric extends HeapObject { def setFieldF32(index: u32, val: float); def setFieldF64(index: u32, val: double); def setFieldRef(index: u32, val: Object); - def setFieldTRef(index: u32, val: Object); + def setFieldCont(index: u32, val: Continuation); def setFieldV128(index: u32, low: u64, high: u64); } @@ -126,6 +126,9 @@ class HeapArrayGeneric extends HeapObject { def getRef(index: u32) -> Object; def setRef(index: u32, val: Object); + def getCont(index: u32) -> Continuation; + def setCont(index: u32, val: Continuation); + def getV128(index: u32) -> (u64, u64); def setV128(index: u32, low: u64, high: u64); @@ -204,6 +207,12 @@ class HeapArrayValue extends HeapArrayGeneric { def setRef(index: u32, val: Object) { vals[index] = Value.Ref(val); } + def getCont(index: u32) -> Continuation { + return Value.Cont.!(vals[index]).val; + } + def setCont(index: u32, val: Continuation) { + vals[index] = Value.Cont(val); + } def getV128(index: u32) -> (u64, u64) { var val = Value.V128.!(vals[index]); return (val.low, val.high); @@ -233,7 +242,6 @@ component Values { def REF_NULL = FUNCREF_NULL; def CONT_NULL = Value.Cont(Continuations.NULL); def NONE = Array.new(0); - def OBJS_SHIFT = 255u8; def render(v: Value, buf: StringBuilder) -> StringBuilder { match (v) { @@ -257,7 +265,7 @@ component Values { F32(val) => buf.put1("f32:%x", val); F64(val) => buf.put1("f64:%x", val); V128(low, high) => buf.puts("v128:").putx_64(high).putc('_').putx_64(low); - Cont(val) => buf.put1("", val.render); + Cont(val) => buf.put1("", Continuations.render(val, _)); } return buf; } diff --git a/src/engine/WasmStack.v3 b/src/engine/WasmStack.v3 index 22d8cfe7a..8cab234cf 100644 --- a/src/engine/WasmStack.v3 +++ b/src/engine/WasmStack.v3 @@ -2,7 +2,7 @@ // See LICENSE for details of Apache 2.0 license. // An execution stack. -class ExecStack { +class ExecStack extends Exportable { def popV(t: ValueType) -> Value; def popi() -> i32; def popu() -> u32; diff --git a/src/engine/compiler/MacroAssembler.v3 b/src/engine/compiler/MacroAssembler.v3 index 64aa387c8..2382eb023 100644 --- a/src/engine/compiler/MacroAssembler.v3 +++ b/src/engine/compiler/MacroAssembler.v3 @@ -137,18 +137,20 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { } // Routines to emit code to access various V3 constructs. + // EBM Broken: some users assume array/index not side-effected def emit_v3_Array_elem_r_rr(kind: ValueKind, dst: Reg, array: Reg, index: Reg) { // Generic code, may be overridden to be more efficient - emit_shlw_r_i(index, logScaleOf(kind)); + emit_shlw_r_i(index, logScaleOf(kind)); // TODO: possibly wrong for REF_U64 (Continuations) emit_addw_r_r(array, index); emit_mov_r_m(kind, dst, MasmAddr(array, getOffsets().Array_contents)); } def emit_v3_Array_elem_r_ri(kind: ValueKind, dst: Reg, array: Reg, index: int) { emit_mov_r_m(kind, dst, MasmAddr(array, getOffsets().Array_contents + index * scaleOf(kind))); } + // EBM Broken: some users assume array/index not side-effected def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, src: Reg) { // Generic code, may be overridden to be more efficient - emit_shlw_r_i(index, logScaleOf(kind)); + emit_shlw_r_i(index, logScaleOf(kind)); // TODO: possibly wrong for REF_U64 (Continuations) emit_addw_r_r(array, index); emit_mov_m_r(kind, MasmAddr(array, getOffsets().Array_contents), src); } @@ -369,6 +371,8 @@ class MacroAssembler(valuerep: Tagging, regConfig: RegConfig) { def emit_ref_from_ref_u64(to: Reg, from: Reg); def emit_u64_from_ref_u64(to: Reg, from: Reg); + def emit_ref_u64_from_ref_and_u64_mem(to: Reg, ref_addr: MasmAddr, u64_addr: MasmAddr); + def emit_ref_and_u64_mem_from_ref_u64(ref_addr: MasmAddr, u64_addr: MasmAddr, from: Reg); def emit_mov_r_Cont(to: Reg, cont: Continuation); // stk.state_ = state; diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index 8a32b0016..f01dce9f5 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1704,13 +1704,28 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var tmp = allocTmp(ValueKind.REF); var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck masm.emit_breq_r_l(str, 0, label); - if (ValueType.Ref.?(etype)) { - masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); - offset = offset << 3; - } else { - masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); + match (kind) { + REF => { + masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); + offset = offset << 3; + masm.emit_mov_r_m(kind, dst, MasmAddr(tmp, offset + masm.getOffsets().Array_contents)); + } + REF_U64 => { + var offsets = ObjReps.unpackFieldOffsets(offset); + var refs_offset = offsets.0 << 3; + var bytes_offset = offsets.1; + masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); + var tmp1 = allocTmp(ValueKind.REF); + masm.emit_v3_HeapStructPair_bytes_r_r(tmp1, str); + var ref_addr = MasmAddr(tmp, refs_offset + masm.getOffsets().Array_contents); + var u64_addr = MasmAddr(tmp1, bytes_offset + masm.getOffsets().Array_contents); + masm.emit_ref_u64_from_ref_and_u64_mem(dst, ref_addr, u64_addr); + } + _ => { + masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); + masm.emit_mov_r_m(kind, dst, MasmAddr(tmp, offset + masm.getOffsets().Array_contents)); + } } - masm.emit_mov_r_m(kind, dst, MasmAddr(tmp, offset + masm.getOffsets().Array_contents)); state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); } _ => { @@ -1783,13 +1798,28 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var tmp = allocTmp(ValueKind.REF); var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck masm.emit_breq_r_l(str, 0, label); - if (ValueType.Ref.?(etype)) { - masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); - offset = offset << 3; - } else { - masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); + match (kind) { + REF => { + masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); + offset = offset << 3; + masm.emit_mov_m_r(kind, MasmAddr(tmp, offset + masm.getOffsets().Array_contents), val); + } + REF_U64 => { + var offsets = ObjReps.unpackFieldOffsets(offset); + var refs_offset = offsets.0 << 3; + var bytes_offset = offsets.1; + masm.emit_v3_HeapStructPair_objs_r_r(tmp, str); + var tmp1 = allocTmp(ValueKind.REF); + masm.emit_v3_HeapStructPair_bytes_r_r(tmp1, str); + var ref_addr = MasmAddr(tmp, refs_offset + masm.getOffsets().Array_contents); + var u64_addr = MasmAddr(tmp1, bytes_offset + masm.getOffsets().Array_contents); + masm.emit_ref_and_u64_mem_from_ref_u64(ref_addr, u64_addr, val); + } + _ => { + masm.emit_v3_HeapStructPair_bytes_r_r(tmp, str); + masm.emit_mov_m_r(kind, MasmAddr(tmp, offset + masm.getOffsets().Array_contents), val); + } } - masm.emit_mov_m_r(kind, MasmAddr(tmp, offset + masm.getOffsets().Array_contents), val); } _ => { emit_call_runtime_op2n(Opcode.STRUCT_SET, struct_index, field_index, 1, ValueTypes.NONE, true); @@ -1817,30 +1847,33 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var etype = stg_type.valtype; var kind = ValueTypes.kind(etype); // PACKED forms should not get here, but default to run-time call, in case - if ((stg_type.pack != Packedness.UNPACKED) || - (ValueType.Ref.?(etype) && ValueType.Ref.!(etype).heap == HeapType.I31)) { + var useRunTime = false; + if (ObjReps.arrayMode != ArrayMode.Typed) useRunTime = true; + else if (stg_type.pack != Packedness.UNPACKED) useRunTime = true; + else if (ValueType.Ref.?(etype)) { + var ht = ValueType.Ref.!(etype).heap; + match (ht) { + I31 => useRunTime = true; + Cont => useRunTime = (!Continuations.boxed && !CiRuntime.FEATURE_MIXED_ARRAYS); + _ => ; + } + } + if (useRunTime) { emit_call_runtime_op1n(Opcode.ARRAY_GET, ht_index, 2, [decl.elem_types[0].valtype], true); return; } - match (ObjReps.arrayMode) { - Typed => { - var idx = popReg().reg; - var arr = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(arr, 0, label); - var tmp = allocTmp(ValueKind.REF); - masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); - var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); - masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); - var dst = allocRegTos(kind); - masm.emit_v3_Array_elem_r_rr(kind, dst, tmp, idx); - state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); - } - _ => { - // Original format, and any other added formats without explicit coverage, use run-time - emit_call_runtime_op1n(Opcode.ARRAY_GET, ht_index, 2, [decl.elem_types[0].valtype], true); - } - } + // only ArrayMode.Typed gets here + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + var tmp = allocTmp(ValueKind.REF); + masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + var dst = allocRegTos(kind); + masm.emit_v3_Array_elem_r_rr(kind, dst, tmp, idx); + state.push(SpcConsts.kindToFlags(kind) | IN_REG, dst, 0); } def visit_ARRAY_GET_S(ht_index: u31) { var decl = ArrayDecl.!(module.heaptypes[ht_index]); @@ -1904,29 +1937,32 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl var etype = stg_type.valtype; var kind = ValueTypes.kind(etype); // PACKED forms may get here, but use run-time call for the moment - if ((stg_type.pack != Packedness.UNPACKED) || - (ValueType.Ref.?(etype) && ValueType.Ref.!(etype).heap == HeapType.I31)) { + var useRunTime = false; + if (ObjReps.arrayMode != ArrayMode.Typed) useRunTime = true; + else if (stg_type.pack != Packedness.UNPACKED) useRunTime = true; + else if (ValueType.Ref.?(etype)) { + var ht = ValueType.Ref.!(etype).heap; + match (ht) { + I31 => useRunTime = true; + Cont => useRunTime = (!Continuations.boxed && !CiRuntime.FEATURE_MIXED_ARRAYS); + _ => ; + } + } + if (useRunTime) { emit_call_runtime_op1n(Opcode.ARRAY_SET, ht_index, 3, ValueTypes.NONE, true); return; } - match (ObjReps.arrayMode) { - Typed => { - var val = popReg().reg; - var idx = popReg().reg; - var arr = popReg().reg; - var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck - masm.emit_breq_r_l(arr, 0, label); - var tmp = allocTmp(ValueKind.REF); - masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); - var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); - masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); - masm.emit_v3_Array_elem_rr_r(kind, tmp, idx, val); - } - _ => { - // Original format, and any other added formats without explicit coverage, use run-time - emit_call_runtime_op1n(Opcode.ARRAY_SET, ht_index, 3, ValueTypes.NONE, true); - } - } + // only ArrayMode.Typed gets here + var val = popReg().reg; + var idx = popReg().reg; + var arr = popReg().reg; + var label = masm.newTrapLabel(TrapReason.NULL_DEREF); // XXX: constant-fold nullcheck + masm.emit_breq_r_l(arr, 0, label); + var tmp = allocTmp(ValueKind.REF); + masm.emit_v3_HeapArrayI32_vals_r_r(tmp, arr); + var oob_label = masm.newTrapLabel(TrapReason.ARRAY_OOB); + masm.emit_v3_Array_bounds_check_rr(tmp, idx, oob_label); + masm.emit_v3_Array_elem_rr_r(kind, tmp, idx, val); } def visit_ARRAY_LEN() { match (ObjReps.arrayMode) { diff --git a/src/engine/continuation/BoxedContinuation.v3 b/src/engine/continuation/BoxedContinuation.v3 index 1bed91b0b..012129cc8 100644 --- a/src/engine/continuation/BoxedContinuation.v3 +++ b/src/engine/continuation/BoxedContinuation.v3 @@ -6,14 +6,18 @@ class Continuation extends Object { new(stack) { } - def render(buf: StringBuilder) -> StringBuilder { - return buf.puts("cont"); - } } component Continuations { def NULL: Continuation = null; def valueKind = ValueKind.REF; + def boxed = true; + + // write this as a component method since having it as a method on Continuation + // leads to its use as a closure, which defeats unboxing in UnboxedContinuation + def render(cont: Continuation, sb: StringBuilder) -> StringBuilder { + return sb.put2("cont(isNull=%z,isUsed=%z)", Continuations.isNull(this), Continuations.isUsed(this)); + } def isNull(cont: Continuation) -> bool { return cont == null; } def isUsed(cont: Continuation) -> bool { return cont.stack == null; } @@ -29,4 +33,7 @@ component Continuations { def getStoredStack(cont: Continuation) -> WasmStack { return cont.stack; } def getStoredVersion(cont: Continuation) -> u64 { return 0; } // boxed cont does not store version + + def getObject(cont: Continuation) -> Object { return cont; } + def fromObject(obj: Exportable) -> Continuation { return Continuation.!(obj); } } diff --git a/src/engine/continuation/UnboxedContinuation.v3 b/src/engine/continuation/UnboxedContinuation.v3 index f67cee887..29d26e3de 100644 --- a/src/engine/continuation/UnboxedContinuation.v3 +++ b/src/engine/continuation/UnboxedContinuation.v3 @@ -1,15 +1,18 @@ // Copyright 2025 Wizard authors. All rights reserved. // See LICENSE for details of Apache 2.0 license. -type Continuation(stack: WasmStack, version: u64) #unboxed { - def render(buf: StringBuilder) -> StringBuilder { - return buf.puts("cont"); - } -} +type Continuation(stack: WasmStack, version: u64) #unboxed { } component Continuations { def NULL = Continuation(null, 0); def valueKind = ValueKind.REF_U64; + def boxed = false; + + // write this as a component method since having it as a method on Continuation + // leads to its use as a closure, which defeats unboxing + def render(cont: Continuation, sb: StringBuilder) -> StringBuilder{ + return sb.put2("cont(isNull=%z,version=%d)", cont.stack == null, cont.version); + } def isNull(cont: Continuation) -> bool { return cont.stack == null; } def isUsed(cont: Continuation) -> bool { return cont.stack.version != cont.version; } @@ -25,4 +28,7 @@ component Continuations { def getStoredStack(cont: Continuation) -> WasmStack { return cont.stack; } def getStoredVersion(cont: Continuation) -> u64 { return cont.version; } + + def getObject(cont: Continuation) -> Object; // should never be called + def fromObject(obj: Exportable) -> Continuation; // should never be called } diff --git a/src/engine/v3/V3Interpreter.v3 b/src/engine/v3/V3Interpreter.v3 index b483fc07b..1493c6c11 100644 --- a/src/engine/v3/V3Interpreter.v3 +++ b/src/engine/v3/V3Interpreter.v3 @@ -1257,7 +1257,7 @@ class V3Interpreter extends WasmStack { var tag = frame.func.instance.tags[tag_idx]; var ex: Throwable = Exception.new(tag, popN(tag.sig.params), null); - if (Trace.stack) Trace.OUT.put2("resume_throw %q %q", tag.render, cont.render).ln(); + if (Trace.stack) Trace.OUT.put2("resume_throw %q %q", tag.render, Continuations.render(cont, _)).ln(); if (Continuations.isNull(cont)) return void(trap(TrapReason.NULL_DEREF)); if (Continuations.isUsed(cont)) return void(trap(TrapReason.USED_CONTINUATION)); @@ -1290,7 +1290,7 @@ class V3Interpreter extends WasmStack { var cont = popContinuation(); var ex = Exception.!(popr().val); - if (Trace.stack) Trace.OUT.put1("resume_throw_ref %q", cont.render).ln(); + if (Trace.stack) Trace.OUT.put1("resume_throw_ref %q", Continuations.render(cont, _)).ln(); if (Continuations.isNull(cont)) return void(trap(TrapReason.NULL_DEREF)); if (Continuations.isUsed(cont)) return void(trap(TrapReason.USED_CONTINUATION)); diff --git a/src/engine/x86-64/X86_64Interpreter.v3 b/src/engine/x86-64/X86_64Interpreter.v3 index 4d9092c57..3c3d0b93c 100644 --- a/src/engine/x86-64/X86_64Interpreter.v3 +++ b/src/engine/x86-64/X86_64Interpreter.v3 @@ -851,7 +851,7 @@ class X86_64InterpreterGen(ic: X86_64InterpreterCode, w: DataWriter) { // dispatch through LEB table after skipping LEB and clearing upper bit // not used when encoding is minimal genSkipLeb(); - asm.movd_r_r(r_tmp0, r_tmp2); // EBM check this! + asm.movd_r_r(r_tmp0, r_tmp2); asm.d.and_r_i(r_tmp0, 0x7F); genDispatch0(null, refsndary, false); writeDispatchEntry(refleb, 0x01, leb01_pos); diff --git a/src/engine/x86-64/X86_64MacroAssembler.v3 b/src/engine/x86-64/X86_64MacroAssembler.v3 index a3342ffe4..028bc15b3 100644 --- a/src/engine/x86-64/X86_64MacroAssembler.v3 +++ b/src/engine/x86-64/X86_64MacroAssembler.v3 @@ -107,17 +107,25 @@ class X86_64MacroAssembler extends MacroAssembler { F32 => asm.movss_s_m(X(dst), X86_64Addr.new(a, i, 4, getOffsets().Array_contents)); I64, REF => asm.movq_r_m(G(dst), X86_64Addr.new(a, i, 8, getOffsets().Array_contents)); F64 => asm.movsd_s_m(X(dst), X86_64Addr.new(a, i, 8, getOffsets().Array_contents)); - V128, REF_U64 => asm.movdqu_s_m(X(dst), X86_64Addr.new(a, i, 16, getOffsets().Array_contents)); // TODO: can't scale by 16 + V128, REF_U64 => { + asm.movq_r_r(scratch, i); + asm.q.shl_r_i(scratch, 4); // can't scale by 16 + asm.movdqu_s_m(X(dst), X86_64Addr.new(a, scratch, 1, getOffsets().Array_contents)); + } } } - def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, dst: Reg) { + def emit_v3_Array_elem_rr_r(kind: ValueKind, array: Reg, index: Reg, src: Reg) { var a = G(array), i = G(index); match (kind) { - I32 => asm.movd_m_r(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), G(dst)); - F32 => asm.movss_m_s(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), X(dst)); - I64, REF => asm.movq_m_r(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), G(dst)); - F64 => asm.movsd_m_s(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), X(dst)); - V128, REF_U64 => asm.movdqu_m_s(X86_64Addr.new(a, i, 16, getOffsets().Array_contents), X(dst)); // TODO: can't scale by 16 + I32 => asm.movd_m_r(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), G(src)); + F32 => asm.movss_m_s(X86_64Addr.new(a, i, 4, getOffsets().Array_contents), X(src)); + I64, REF => asm.movq_m_r(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), G(src)); + F64 => asm.movsd_m_s(X86_64Addr.new(a, i, 8, getOffsets().Array_contents), X(src)); + V128, REF_U64 => { + asm.movq_r_r(scratch, i); + asm.q.shl_r_i(scratch, 4); // can't scale by 16 + asm.movdqu_m_s(X86_64Addr.new(a, scratch, 1, getOffsets().Array_contents), X(src)); + } } } def emit_v3_Array_length_r_r(dst: Reg, array: Reg) { @@ -1619,6 +1627,14 @@ class X86_64MacroAssembler extends MacroAssembler { def emit_u64_from_ref_u64(to: Reg, from: Reg) { asm.pextrq_r_s_i(G(to), X(from), 1); } + def emit_ref_u64_from_ref_and_u64_mem(to: Reg, ref_addr: MasmAddr, u64_addr: MasmAddr) { + asm.pinsrq_s_m_i(X(to), A(ref_addr), 0); + asm.pinsrq_s_m_i(X(to), A(u64_addr), 1); + } + def emit_ref_and_u64_mem_from_ref_u64(ref_addr: MasmAddr, u64_addr: MasmAddr, from: Reg) { + asm.pextrq_m_s_i(A(ref_addr), X(from), 0); + asm.pextrq_m_s_i(A(u64_addr), X(from), 1); + } // Reads a 32- or 64-bit unsigned LEB from {rw_ptr} into {w_dest}. def emit_read_uleb(w_dest: X86_64Gpr, rw_ptr: X86_64Gpr, w_scratch1: X86_64Gpr, w_scratch2: X86_64Gpr) -> this { diff --git a/src/engine/x86-64/X86_64Stack.v3 b/src/engine/x86-64/X86_64Stack.v3 index c121bbad9..671db6884 100644 --- a/src/engine/x86-64/X86_64Stack.v3 +++ b/src/engine/x86-64/X86_64Stack.v3 @@ -373,7 +373,7 @@ class X86_64Stack extends WasmStack { return Value.Ref(null); } def popContinuation() -> Continuation { - if (FeatureDisable.unboxedConts) { + if (Continuations.boxed) { var ptr = vsp + (valuerep.tag_size - valuerep.slot_size); var cont = ptr.load(); vsp += -(valuerep.slot_size); @@ -392,10 +392,15 @@ class X86_64Stack extends WasmStack { return val; } def peekRef() -> Value { - // TODO[ss]: filter out a Value.Cont after giving it its own BpTypeCode in storage if (valuerep.tagged) { var got = peekTag(); if (!valuerep.maybeRefTag(got)) fatal(Strings.format1("value stack tag mismatch, expected ref, got %x", got)); + if (got == BpTypeCode.CONTREF.code) { + var vp = vsp + (valuerep.tag_size - valuerep.slot_size); + var cont = Continuations.continuationWithVersion(vp.load(), (vp + 8).load()); + Trace.OUT.put1("Peeked continuation from stack: %q", showCont(cont, _)).ln(); + return Value.Cont(cont); + } } return readI31OrObject(vsp + (valuerep.tag_size - valuerep.slot_size)); } @@ -431,6 +436,7 @@ class X86_64Stack extends WasmStack { if (valuerep.tagged) { var got = peekTag(); if (!valuerep.maybeRefTag(got)) fatal(Strings.format1("value stack tag mismatch, expected ref, got %x", got)); + if (got == BpTypeCode.CONTREF.code) fatal("expected ref, got unboxed continuation"); } vsp += -(valuerep.slot_size); var val = (vsp + valuerep.tag_size).load(); @@ -502,12 +508,23 @@ class X86_64Stack extends WasmStack { BpTypeCode.F32.code => return Value.F32(vp.load()); BpTypeCode.F64.code => return Value.F64(vp.load()); BpTypeCode.V128.code => return Value.V128(vp.load(), (vp + 8).load()); + + BpTypeCode.CONTREF.code => { + var cont = Continuations.continuationWithVersion(vp.load(), (vp + 8).load()); + Trace.OUT.put1("Read continuation from stack: %q", showCont(cont, _)).ln(); + return Value.Cont(cont); + } + _ => { fatal(Strings.format2("unknown value tag 0x%x @ 0x%x", tag, (tp - Pointer.NULL))); return Values.REF_NULL; } } } + def showCont(cont: Continuation, sb: StringBuilder) -> StringBuilder { + var stk = Continuations.getStoredStack(cont), version = Continuations.getStoredVersion(cont); + return sb.put2("", if(stk == null, 0i64, Pointer.atObject(stk) - Pointer.NULL), version); + } def storeValue(ptr: Pointer, v: Value) { var tag_ptr = ptr; var val_ptr = ptr + valuerep.tag_size; @@ -542,10 +559,11 @@ class X86_64Stack extends WasmStack { (val_ptr + 8).store(u64.view(high)); } Cont(val) => { - if (valuerep.tagged) tag_ptr.store(BpTypeCode.REF_NULL.code); - if (FeatureDisable.unboxedConts) { // boxed continuation + if (Continuations.boxed) { + if (valuerep.tagged) tag_ptr.store(BpTypeCode.REF_NULL.code); (val_ptr + 0).store(val); - } else { // unboxed continuation + } else { + if (valuerep.tagged) tag_ptr.store(BpTypeCode.CONTREF.code); (val_ptr + 0).store(Continuations.getStoredStack(val)); (val_ptr + 8).store(Continuations.getStoredVersion(val)); } diff --git a/src/engine/x86-64/X86_64Target.v3 b/src/engine/x86-64/X86_64Target.v3 index 5b2c05033..5f2286a8b 100644 --- a/src/engine/x86-64/X86_64Target.v3 +++ b/src/engine/x86-64/X86_64Target.v3 @@ -18,7 +18,7 @@ component Target { def forceGC = RiGc.forceGC; def newWasmStack = X86_64StackManager.getFreshStack; def recycleWasmStack = X86_64StackManager.recycleStack; - def tagging = Tagging.new(!FeatureDisable.valueTags, !FeatureDisable.simd); + def tagging = Tagging.new(!FeatureDisable.valueTags, !(FeatureDisable.simd && FeatureDisable.unboxedConts)); private var offsets: V3Offsets; diff --git a/test/unittest/CodeWriter.v3 b/test/unittest/CodeWriter.v3 new file mode 100644 index 000000000..402120cc6 --- /dev/null +++ b/test/unittest/CodeWriter.v3 @@ -0,0 +1,76 @@ +// Copyright 2025 Wizard authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +class CodeWriter extends DataWriter { + new() super () { } + def putOp(op: Opcode) -> this { + if (op.prefix == 0) { + putb(op.code & 0xFF); + } else { + putb(op.prefix); + var page = Opcodes.page_by_prefix[op.prefix]; + if (page.oneByte) { + putb(op.code & 0xFF); + } else { + put_sleb32(op.code); + } + } + } + def putValueType(vt: ValueType) -> this { + var tc: i32 = 0; + match (vt) { + I32 => tc = BpTypeCode.I32.val; + I64 => tc = BpTypeCode.I64.val; + F32 => tc = BpTypeCode.F32.val; + F64 => tc = BpTypeCode.F64.val; + V128 => tc = BpTypeCode.V128.val; + Ref(nullable, ht) => { + match (ht) { + ANY => if (nullable) tc = BpTypeCode.ANYREF.val; + EXTERN => if (nullable) tc = BpTypeCode.EXTERNREF.val; + EQ => if (nullable) tc = BpTypeCode.EQREF.val; + I31 => if (nullable) tc = BpTypeCode.I31REF.val; + EXN => if (nullable) tc = BpTypeCode.EXNREF.val; + NONE => if (nullable) tc = BpTypeCode.NULLREF.val; + NOFUNC => if (nullable) tc = BpTypeCode.NULLFUNCREF.val; + NOEXTERN => if (nullable) tc = BpTypeCode.NULLEXTERNREF.val; + NOCONT => if (nullable) tc = BpTypeCode.NULLCONTREF.val; + Func(x) => if (nullable && x == null) + tc = BpTypeCode.FUNCREF.val; + Struct(x) => if (nullable && x == null) + tc = BpTypeCode.STRUCTREF.val; + Array(x) => if (nullable && x == null) + tc = BpTypeCode.ARRAYREF.val; + _ => ; + } + if (tc == 0) + tc = if(nullable, BpTypeCode.REF_NULL.val, BpTypeCode.REF.val); + } + _ => ; + } + put_sleb32(tc); + match (tc) { + BpTypeCode.REF.val, BpTypeCode.REF_NULL.val => + putHeapType(ValueType.Ref.!(vt).heap); + _ => ; + } + } + def putHeapType(ht: HeapType) -> this { + match (ht) { + Func(x) => put_sleb32(if(x == null, BpHeapTypeCode.FUNC.val, x.heaptype_index)); + EXTERN => put_sleb32(BpHeapTypeCode.EXTERN.val); + ANY => put_sleb32(BpHeapTypeCode.ANY.val); + EQ => put_sleb32(BpHeapTypeCode.EQ.val); + I31 => put_sleb32(BpHeapTypeCode.I31.val); + NOFUNC => put_sleb32(BpHeapTypeCode.NOFUNC.val); + NOEXTERN => put_sleb32(BpHeapTypeCode.NOEXTERN.val); + NOCONT => put_sleb32(BpHeapTypeCode.NOCONT.val); + Struct(x) => put_sleb32(if(x == null, BpHeapTypeCode.STRUCT.val, x.heaptype_index)); + Array(x) => put_sleb32(if(x == null, BpHeapTypeCode.ARRAY.val, x.heaptype_index)); + EXN => put_sleb32(BpHeapTypeCode.EXN.val); + NONE => put_sleb32(BpHeapTypeCode.NONE.val); + + _ => ; + } + } +} diff --git a/test/unittest/ExeTest.v3 b/test/unittest/ExeTest.v3 index 81d697156..1a945b918 100644 --- a/test/unittest/ExeTest.v3 +++ b/test/unittest/ExeTest.v3 @@ -198,6 +198,7 @@ def unused_ = TestTiers.addTests([ ("struct.get", test_struct_get), ("struct.get_su", test_struct_get_su), ("struct.set", test_struct_set), + ("struct_continuations", test_struct_continuations), ("array.new", test_array_new), ("array.new_default", test_array_new_default), ("array.get", test_array_get), @@ -205,6 +206,7 @@ def unused_ = TestTiers.addTests([ ("array.set", test_array_set), ("array.len", test_array_len), ("array.new_fixed", test_array_new_fixed), + ("array_continuations", test_array_continuations), ("ref.i31", test_ref_i31), ("i31.get_s", test_i31_get_s), ("i31.get_u", test_i31_get_u), @@ -2442,6 +2444,166 @@ def test_struct_set(t: ExeTester) { } } +// This test aims to ensure that Continuations are handled properly whether +// boxed or unboxed. +def test_struct_continuations(t: ExeTester) { + t.extensions |= Extension.GC; + var st0 = t.newStruct([I32_FIELD]); // to have a Ref for the main struct + var st0_vt = ValueTypes.RefStruct(true, st0); + def REF_FIELD = StorageType(st0_vt, Packedness.UNPACKED, true); + def sig_ref = HeapType.Func(SigDecl.new(true, [], [ValueType.I32], [])); + def cont_decl = ContDecl.new(true, [], sig_ref); + def cont_vt = ValueTypes.RefCont(true, cont_decl); + def CONT_FIELD = StorageType(cont_vt, Packedness.UNPACKED, true); + + var st = t.newStruct([ // so that there is at least one Ref and one + I32_FIELD, // bytes field before and after the Continuation + REF_FIELD, + CONT_FIELD, + I32_FIELD, + REF_FIELD + ]); + var st_vt = ValueTypes.RefStruct(false, st); + + var obj00 = heapStructNew(st0, [Value.I32(1)]); // some distinct Ref objects + var obj01 = heapStructNew(st0, [Value.I32(2)]); + var obj02 = heapStructNew(st0, [Value.I32(3)]); + var obj03 = heapStructNew(st0, [Value.I32(4)]); + + var stk0 = Target.newWasmStack(); + var stk1 = Target.newWasmStack(); + var cont0 = Continuations.continuationWithVersion(stk0, 0); + var cont1 = Continuations.continuationWithVersion(stk1, 100); + var initial: Array = [Value.I32(1), Value.Ref(obj00), Value.Cont(cont0), Value.I32(2), Value.Ref(obj01)]; + + // create and run function to STRUCT_NEW this struct + t.sig0([ValueType.I32, st0_vt, cont_vt, ValueType.I32, st0_vt], [st_vt]); + var ht = heapIndexByte(st); + var code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.LOCAL_GET).putb(2) + .putOp(Opcode.LOCAL_GET).putb(3) + .putOp(Opcode.LOCAL_GET).putb(4) + .putOp(Opcode.STRUCT_NEW).putb(ht) + .extract(); + t.codev(code); + var res = t.run(initial); + var rtt = (st); + t.assert_struct(res.1, st, rtt, initial); + var objval = Result.Value.!(res.1).vals[0]; + + // update field 0 and check all fields + t.sig0([st_vt, ValueType.I32], [ValueType.I32, st0_vt, cont_vt, ValueType.I32, st0_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.STRUCT_SET).putb(ht).putb(0) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(0) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(1) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(2) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(3) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(4) + .extract(); + t.codev(code); + t.argsN([objval, Value.I32(3)]); + initial[0] = Value.I32(3); + t.assert2_res(Result.Value(initial)); + + // update field 1 and check all fields + t.sig0([st_vt, st0_vt], [ValueType.I32, st0_vt, cont_vt, ValueType.I32, st0_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.STRUCT_SET).putb(ht).putb(1) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(0) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(1) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(2) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(3) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(4) + .extract(); + t.codev(code); + t.argsN([objval, Value.Ref(obj02)]); + initial[1] = Value.Ref(obj02); + t.assert2_res(Result.Value(initial)); + + // update field 2 and check all fields + t.sig0([st_vt, cont_vt], [ValueType.I32, st0_vt, cont_vt, ValueType.I32, st0_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.STRUCT_SET).putb(ht).putb(2) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(0) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(1) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(2) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(3) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(4) + .extract(); + t.codev(code); + t.argsN([objval, Value.Cont(cont1)]); + initial[2] = Value.Cont(cont1); + t.assert2_res(Result.Value(initial)); + + // update field 3 and check all fields + t.sig0([st_vt, ValueType.I32], [ValueType.I32, st0_vt, cont_vt, ValueType.I32, st0_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.STRUCT_SET).putb(ht).putb(3) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(0) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(1) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(2) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(3) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(4) + .extract(); + t.codev(code); + t.argsN([objval, Value.I32(4)]); + initial[3] = Value.I32(4); + t.assert2_res(Result.Value(initial)); + + // update field 4 and check all fields + t.sig0([st_vt, st0_vt], [ValueType.I32, st0_vt, cont_vt, ValueType.I32, st0_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.STRUCT_SET).putb(ht).putb(4) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(0) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(1) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(2) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(3) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.STRUCT_GET).putb(ht).putb(4) + .extract(); + t.codev(code); + t.argsN([objval, Value.Ref(obj03)]); + initial[4] = Value.Ref(obj03); + t.assert2_res(Result.Value(initial)); +} + def test_array_new(t: ExeTester) { t.extensions |= Extension.GC; var at = t.newArray([I32_FIELD]); @@ -2651,6 +2813,99 @@ def test_array_new_fixed(t: ExeTester) { } } +// This test aims to ensure that Continuations are handled properly whether +// boxed or unboxed, and whether array are mixed or not +// EBM TODO +def test_array_continuations(t: ExeTester) { + t.extensions |= Extension.GC; + + def sig_ref = HeapType.Func(SigDecl.new(true, [], [ValueType.I32], [])); + def cont_decl = ContDecl.new(true, [], sig_ref); + def cont_vt = ValueTypes.RefCont(true, cont_decl); + def CONT_FIELD = StorageType(cont_vt, Packedness.UNPACKED, true); + + var stk0 = Target.newWasmStack(); + var stk1 = Target.newWasmStack(); + var stk2 = Target.newWasmStack(); + var stk3 = Target.newWasmStack(); + var stk4 = Target.newWasmStack(); + var stk5 = Target.newWasmStack(); + + var cont0 = Continuations.continuationWithVersion(stk0, 0); + var cont1 = Continuations.continuationWithVersion(stk1, 100); + var cont2 = Continuations.continuationWithVersion(stk2, 200); + var cont3 = Continuations.continuationWithVersion(stk3, 300); + var cont4 = Continuations.continuationWithVersion(stk4, 400); + var cont5 = Continuations.continuationWithVersion(stk5, 500); + var initial: Array = [Value.Cont(cont0), Value.Cont(cont1), Value.Cont(cont2)]; + + var at = t.newArray([CONT_FIELD]); + var at_vt = ValueType.Ref(true, HeapType.Array(at)); + + t.sig0([cont_vt, cont_vt, cont_vt], [at_vt]); + var ht = heapIndexByte(at); + var code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.LOCAL_GET).putb(2) + .putOp(Opcode.ARRAY_NEW_FIXED).putb(ht).putb(3) + .extract(); + t.codev(code); + var res = t.run(initial); + var rtt = (at); + t.assert_array(res.1, at, rtt, initial); + var arrval = Result.Value.!(res.1).vals[0]; + + // check all elements + t.sig0([at_vt], [cont_vt, cont_vt, cont_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.I32_CONST).putb(0) + .putOp(Opcode.ARRAY_GET).putb(ht) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.I32_CONST).putb(1) + .putOp(Opcode.ARRAY_GET).putb(ht) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.I32_CONST).putb(2) + .putOp(Opcode.ARRAY_GET).putb(ht) + .extract(); + t.codev(code); + t.argsN([arrval]); + t.assert2_res(Result.Value(initial)); + + // update element 0 and check all elements + t.sig0([at_vt, ValueType.I32, cont_vt], [cont_vt, cont_vt, cont_vt]); + code = CodeWriter.new() + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.LOCAL_GET).putb(1) + .putOp(Opcode.LOCAL_GET).putb(2) + .putOp(Opcode.ARRAY_SET).putb(ht) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.I32_CONST).putb(0) + .putOp(Opcode.ARRAY_GET).putb(ht) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.I32_CONST).putb(1) + .putOp(Opcode.ARRAY_GET).putb(ht) + .putOp(Opcode.LOCAL_GET).putb(0) + .putOp(Opcode.I32_CONST).putb(2) + .putOp(Opcode.ARRAY_GET).putb(ht) + .extract(); + t.codev(code); + t.argsN([arrval, Value.I32(0), Value.Cont(cont3)]); + initial[0] = Value.Cont(cont3); + t.assert2_res(Result.Value(initial)); + + // update element 1 and check all elements + t.argsN([arrval, Value.I32(1), Value.Cont(cont4)]); + initial[1] = Value.Cont(cont4); + t.assert2_res(Result.Value(initial)); + + // update element 2 and check all elements + t.argsN([arrval, Value.I32(2), Value.Cont(cont5)]); + initial[2] = Value.Cont(cont5); + t.assert2_res(Result.Value(initial)); +} + def test_ref_i31(t: ExeTester) { t.extensions |= Extension.GC; t.sig0(SigCache.arr_i, [ValueTypes.I31REF]); @@ -3142,7 +3397,7 @@ def heapStructNew(decl: StructDecl, vals: Array) -> HeapStructGeneric { } Pair => { var bytes = if(decl.num_bytes == 0, null, Array.new(decl.num_bytes)); - var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); + var objs = if(decl.num_refs == 0, null, Array.new(decl.num_refs)); var obj = HeapStructPair.new(decl, objs, bytes); for (i < decl.field_types.length) { obj.setFieldValue(u31.view(i), vals[i]); From 5d1b26e18dbba1e5203d58e77e8f921ca9f7fbdf Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 2 Feb 2026 20:54:10 -0500 Subject: [PATCH 09/11] Fixed bug introduced during refactoring --- src/engine/continuation/BoxedContinuation.v3 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/continuation/BoxedContinuation.v3 b/src/engine/continuation/BoxedContinuation.v3 index 012129cc8..cd72528e5 100644 --- a/src/engine/continuation/BoxedContinuation.v3 +++ b/src/engine/continuation/BoxedContinuation.v3 @@ -16,7 +16,7 @@ component Continuations { // write this as a component method since having it as a method on Continuation // leads to its use as a closure, which defeats unboxing in UnboxedContinuation def render(cont: Continuation, sb: StringBuilder) -> StringBuilder { - return sb.put2("cont(isNull=%z,isUsed=%z)", Continuations.isNull(this), Continuations.isUsed(this)); + return sb.put2("cont(isNull=%z,isUsed=%z)", Continuations.isNull(cont), Continuations.isUsed(cont)); } def isNull(cont: Continuation) -> bool { return cont == null; } From fa0959bd1b3e15b0e8e78365409a7cb5d6b85848 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 30 Mar 2026 18:05:44 -0400 Subject: [PATCH 10/11] Working to resolve merge --- src/engine/Runtime.v3 | 1 + src/engine/v3/V3Interpreter.v3 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/Runtime.v3 b/src/engine/Runtime.v3 index 0301700e8..3f78efc4f 100644 --- a/src/engine/Runtime.v3 +++ b/src/engine/Runtime.v3 @@ -165,6 +165,7 @@ component Runtime { var offset = stack.popu(); if (instance.dropped_elems[elem_index]) return stack.trap(TrapReason.ELEM_SEGMENT_DROPPED); if (length > Execute.limits.max_array_length) return stack.trap(TrapReason.OOM); + var rtt = ArrayDecl.!(instance.heaptypes[array_index]); var edecl = instance.module.elems[elem_index]; if (ObjReps.arrayMode == ArrayMode.Original) { var r = getElems(instance, edecl, offset, length); diff --git a/src/engine/v3/V3Interpreter.v3 b/src/engine/v3/V3Interpreter.v3 index b76277c66..63039dc3a 100644 --- a/src/engine/v3/V3Interpreter.v3 +++ b/src/engine/v3/V3Interpreter.v3 @@ -1261,7 +1261,7 @@ class V3Interpreter extends WasmStack { if (Continuations.isNull(cont)) return void(trap(TrapReason.NULL_DEREF)); if (Continuations.isUsed(cont)) return void(trap(TrapReason.USED_CONTINUATION)); - if (Trace.stack) Trace.OUT.put2("resume_throw %q %q", tag.render, cont.render).ln(); + if (Trace.stack) Trace.OUT.put2("resume_throw %q %q", tag.render, Continuations.render(cont, _)).ln(); var cont_stack = cont.stack; Continuations.setUsed(cont); From c52c23051230e32bbe83f65c65fdc4373abaaa07 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 30 Mar 2026 22:07:07 -0400 Subject: [PATCH 11/11] ELEM init expressions that return Objects should be evaluated only once --- src/engine/Instance.v3 | 31 ++++++++++++++++++++----------- src/engine/ObjectReps.v3 | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/engine/Instance.v3 b/src/engine/Instance.v3 index 67f3cc4e8..7b57adad4 100644 --- a/src/engine/Instance.v3 +++ b/src/engine/Instance.v3 @@ -101,8 +101,7 @@ class Instance(module: Module, imports: Array) { } Const(val) => return val; I31(val) => { - var v = evalInitExpr(val); - return Value.I31(u31.view(Values.unbox_i(v))); + return Value.I31(u31.view(Values.unbox_i(evalInitExpr(val)))); } Array(t, len, elem), FixedArray(t, vals), @@ -127,7 +126,10 @@ class Instance(module: Module, imports: Array) { I64_MUL(a, b) => return Value.I64(Values.unbox_w(evalInitExpr(a)) * Values.unbox_w(evalInitExpr(b))); } } + // most init exprs are pure and can be evaluated repeatedly, but object ones are not pure + def initExprCacheObject = HashMap.new(fun (ie: InitExpr) => int.!(ie.tag), InitExpr.==); def evalInitExprObject(init: InitExpr) -> Object { + if (initExprCacheObject.has(init)) return initExprCacheObject[init]; match (init) { FuncRefNull, RefNull(ht), @@ -140,39 +142,46 @@ class Instance(module: Module, imports: Array) { } Const(val) => return Values.unbox(val); I31(val) => { - var v = evalInitExpr(val); - return ObjectI31.new(u31.view(Values.unbox_i(v))); + return ObjectI31.new(u31.view(Values.unbox_i(evalInitExpr(val)))); } Array(t, len, elem) => { var vlen = Values.unbox_i(evalInitExpr(len)); var decl = t.array; var st = decl.elem_types[0]; var velem = evalInitExpr(elem); + var obj: Object; if (ObjReps.arrayMode == ArrayMode.Original) { var vvals = Array.new(vlen); for (i < vvals.length) vvals[i] = velem; - return HeapArrayValue.new(t.array, vvals); + obj = HeapArrayValue.new(t.array, vvals); } else { - return ObjectRuntime.evalInitExprObjectArray(t, st, velem, vlen); + obj = ObjectRuntime.evalInitExprObjectArray(t, st, velem, vlen); } + initExprCacheObject[init] = obj; + return obj; } FixedArray(t, vals) => { var decl = t.array; var st = decl.elem_types[0]; + var obj: Object; if (ObjReps.arrayMode == ArrayMode.Original) { - var vvals = Arrays.map(vals, evalInitExpr); - return HeapArrayValue.new(t.array, vvals); + obj = HeapArrayValue.new(t.array, Arrays.map(vals, evalInitExpr)); } else { - return ObjectRuntime.evalInitExprObjectFixedArray(this, t, st, vals); + obj = ObjectRuntime.evalInitExprObjectFixedArray(this, t, st, vals); } + initExprCacheObject[init] = obj; + return obj; } Struct(t, vals) => { var vvals = Arrays.map(vals, evalInitExpr); + var obj: Object; if (ObjReps.structMode == StructMode.Original) { - return HeapStructValue.new(t.sdecl, vvals); + obj = HeapStructValue.new(t.sdecl, vvals); } else { - return ObjectRuntime.evalInitExprObjectStruct(t, vvals); + obj = ObjectRuntime.evalInitExprObjectStruct(t, vvals); } + initExprCacheObject[init] = obj; + return obj; } ArrayNewData(t, data_index, offset, len) => { var voffset = evalInitExpr(offset); diff --git a/src/engine/ObjectReps.v3 b/src/engine/ObjectReps.v3 index 90dd99b29..645a57fb8 100644 --- a/src/engine/ObjectReps.v3 +++ b/src/engine/ObjectReps.v3 @@ -21,7 +21,7 @@ def structModeFor(code: int) -> StructMode { } component ObjReps { - def arrayModeCode = 1; + def arrayModeCode = 0; def arrayMode = arrayModeFor(arrayModeCode); def structModeCode = 1;