From 100896d76e6fddf4ff0f8d69e913ea64cc7cd72a Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Mon, 7 Sep 2026 23:26:39 -0400 Subject: [PATCH] [objects] Implement a more space-efficient V3SplitObjectModel --- src/engine/Type.v3 | 28 ++ .../objmodel/split/V3SplitObjectModel.v3 | 430 ++++++++++++++++++ .../ext:gc/array-copy-subtype0.bin.wast | 146 ++++++ test/regress/ext:gc/array-copy-subtype0.wast | 135 ++++++ test/regress/ext:gc/array-data-bulk0.bin.wast | 110 +++++ test/regress/ext:gc/array-data-bulk0.wast | 92 ++++ .../ext:gc/array-fill-subtype0.bin.wast | 91 ++++ test/regress/ext:gc/array-fill-subtype0.wast | 93 ++++ .../ext:gc/array-init-subtype0.bin.wast | 87 ++++ test/regress/ext:gc/array-init-subtype0.wast | 79 ++++ .../ext:gc/array-subtype-narrow0.bin.wast | 31 ++ .../regress/ext:gc/array-subtype-narrow0.wast | 36 ++ test/regress/ext:gc/build.sh | 2 +- .../ext:gc/struct-subtype-narrow0.bin.wast | 43 ++ .../ext:gc/struct-subtype-narrow0.wast | 46 ++ .../ext:gc/struct-subtype-narrow1.bin.wast | 64 +++ .../ext:gc/struct-subtype-narrow1.wast | 73 +++ .../ext:gc/struct-subtype-narrow2.bin.wast | 15 + .../ext:gc/struct-subtype-narrow2.wast | 20 + .../ext:gc/struct-subtype-narrow3.bin.wast | 56 +++ .../ext:gc/struct-subtype-narrow3.wast | 47 ++ test/wizeng/wizeng_puta0.wasm | Bin 0 -> 164 bytes test/wizeng/wizeng_puta0.wasm.flags | 1 + test/wizeng/wizeng_puta0.wasm.out | 4 + test/wizeng/wizeng_puta0.wat | 14 + 25 files changed, 1742 insertions(+), 1 deletion(-) create mode 100644 src/engine/objmodel/split/V3SplitObjectModel.v3 create mode 100644 test/regress/ext:gc/array-copy-subtype0.bin.wast create mode 100644 test/regress/ext:gc/array-copy-subtype0.wast create mode 100644 test/regress/ext:gc/array-data-bulk0.bin.wast create mode 100644 test/regress/ext:gc/array-data-bulk0.wast create mode 100644 test/regress/ext:gc/array-fill-subtype0.bin.wast create mode 100644 test/regress/ext:gc/array-fill-subtype0.wast create mode 100644 test/regress/ext:gc/array-init-subtype0.bin.wast create mode 100644 test/regress/ext:gc/array-init-subtype0.wast create mode 100644 test/regress/ext:gc/array-subtype-narrow0.bin.wast create mode 100644 test/regress/ext:gc/array-subtype-narrow0.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow0.bin.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow0.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow1.bin.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow1.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow2.bin.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow2.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow3.bin.wast create mode 100644 test/regress/ext:gc/struct-subtype-narrow3.wast create mode 100644 test/wizeng/wizeng_puta0.wasm create mode 100644 test/wizeng/wizeng_puta0.wasm.flags create mode 100644 test/wizeng/wizeng_puta0.wasm.out create mode 100644 test/wizeng/wizeng_puta0.wat diff --git a/src/engine/Type.v3 b/src/engine/Type.v3 index 9685d6195..654c808d1 100644 --- a/src/engine/Type.v3 +++ b/src/engine/Type.v3 @@ -398,28 +398,56 @@ class HeapTypeDecl(final: bool, supertypes: Array) extends Decl { enum Packedness { UNPACKED, PACKED_I8, PACKED_I16 } type StorageType(valtype: ValueType, pack: Packedness, mutable: bool) { } +// Information added to a struct declaration, specific to the object model. +class StructDeclInfo { + var next: StructDeclInfo; +} + // Struct type declaration. (ext:gc) class StructDecl extends HeapTypeDecl { def field_types: Array; def defaultable = allHaveDefaultValues(field_types); + var infos: StructDeclInfo; // extended information about a struct new(final: bool, supertypes: Array, field_types) super(final, supertypes) {} def render(buf: StringBuilder) -> StringBuilder { return putUid(buf.put1("struct #%d", heaptype_index)); } + def addInfo(info: StructDeclInfo) -> this { + info.next = infos; + this.infos = info; + } + def getInfo() -> T { + for (s = infos; s != null; s = s.next) if (T.?(s)) return T.!(s); + return T.default; + } +} + +// Information added to an array declaration, specific to the object model. +class ArrayDeclInfo { + var next: ArrayDeclInfo; } // Array type declaration. (ext:gc) class ArrayDecl extends HeapTypeDecl { def elem_types: Array; def defaultable = allHaveDefaultValues(elem_types); + var infos: ArrayDeclInfo; // extended information about an array new(final: bool, supertypes: Array, elem_types) super(final, supertypes) {} def render(buf: StringBuilder) -> StringBuilder { return putUid(buf.put1("array #%d", heaptype_index)); } + def addInfo(info: ArrayDeclInfo) -> this { + info.next = infos; + this.infos = info; + } + def getInfo() -> T { + for (s = infos; s != null; s = s.next) if (T.?(s)) return T.!(s); + return T.default; + } } // Continuation type declaration. (ext:stack-switching) diff --git a/src/engine/objmodel/split/V3SplitObjectModel.v3 b/src/engine/objmodel/split/V3SplitObjectModel.v3 new file mode 100644 index 000000000..0e606d77f --- /dev/null +++ b/src/engine/objmodel/split/V3SplitObjectModel.v3 @@ -0,0 +1,430 @@ +// Copyright 2026 Wizard authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +// Facade for a Virgil object model where Wasm structs and arrays are +// split into primitive and reference parts. +component ObjectModel { + // Allocation functions. + def newStruct(decl: StructDecl) -> V3SplitWasmStruct { + var info = getStructInfo(decl); + var bytes = if(info.byte_size > 0, Array.new(info.byte_size), NO_BYTES); + var refs = if(info.ref_count > 0, Array.new(info.ref_count), NO_REFS); + return V3SplitWasmStruct.new(info, bytes, refs); + } + def newArray(decl: ArrayDecl, length: u32) -> WasmArray { + return getArrayInfo(decl).alloc(length); + } + + // Bulk access functions. + def newStructFromValues(decl: StructDecl, vals: Range) -> WasmStruct { + var obj = newStruct(decl); + for (i < vals.length) obj.setField(u31.!(i), vals[i]); + return obj; + } + def newArrayFromValues(decl: ArrayDecl, vals: Range) -> WasmArray { + return getArrayInfo(decl).allocFromValues(vals); + } + def arrayFill(dst: WasmArray, dst_offset: u64, size: u64, val: Value) -> TrapReason { + return V3SplitWasmArray.!(dst).fill(dst_offset, size, val); + } + def arrayCopy(dst: WasmArray, dst_offset: u64, src: WasmArray, src_offset: u64, size: u64) -> TrapReason { + return V3SplitWasmArray.!(dst).copyFrom(dst_offset, V3SplitWasmArray.!(src), src_offset, size); + } + def copyInto(dst: WasmArray, dst_offset: u64, src: Range) -> TrapReason { + return V3SplitWasmArray.!(dst).copyIn(dst_offset, src); + } + def copyOutOf(dst: Range, src: WasmArray, src_offset: u64) -> TrapReason { + return V3SplitWasmArray.!(src).copyOut(dst, src_offset); + } + // Only supported for arrays of 8-bit elements, which are stored directly as bytes. + def sendBytes(src: WasmArray, offset: u64, size: u64, f: Range -> R) -> MaybeTrap { + if (!V3SplitWasmArrayOf.?(src)) return MaybeTrap(R.default, TrapReason.UNIMPLEMENTED); + var t = V3SplitWasmArrayOf.!(src).getRange(offset, size); + if (t.reason != OK) return MaybeTrap(R.default, t.reason); + return MaybeTrap(f(t.result), OK); + } +} +def getStructInfo(decl: StructDecl) -> V3SplitStructDeclInfo { + var info = decl.getInfo(); + if (info == null) info = computeStructInfo(decl); + return info; +} +def getArrayInfo(decl: ArrayDecl) -> V3SplitArrayDeclInfo { + var info = decl.getInfo(); + if (info == null) info = computeArrayInfo(decl); + return info; +} + +// Storage representations for fields and elements in this object model. +private type SplitStorageKind { + case Bytes(access: MemStorageKind); + case Ref; + case RefI31; + case RefU64; +} +def storageKind(st: StorageType) -> SplitStorageKind { + match (st.valtype) { + I32 => { + match (st.pack) { + UNPACKED => return SplitStorageKind.Bytes(MemStorageKind.U32); + PACKED_I8 => return SplitStorageKind.Bytes(MemStorageKind.U8); + PACKED_I16 => return SplitStorageKind.Bytes(MemStorageKind.U16); + } + } + I64 => return SplitStorageKind.Bytes(MemStorageKind.U64); + F32 => return SplitStorageKind.Bytes(MemStorageKind.F32); + F64 => return SplitStorageKind.Bytes(MemStorageKind.F64); + V128 => return SplitStorageKind.Bytes(MemStorageKind.V128); + Ref(nullable, heap) => { + match (heap) { + ANY, EQ, I31, EXTERN => return SplitStorageKind.RefI31; // may hold an i31 + Cont, NOCONT => return SplitStorageKind.RefU64; + _ => return SplitStorageKind.Ref; + } + } + _ => return SplitStorageKind.Ref; // BOTTOM, Host + } +} +// Rounds {offset} up to a multiple of {size}, which must be a power of 2. +def alignUp(offset: int, size: int) -> int { + var mask = size - 1; + return (offset + mask) & ~mask; +} + +// Additional metadata for a Wasm struct declaration, including the total size for the primitive +// portion and reference portion, as well as the field offsets. +private class V3SplitStructDeclInfo extends StructDeclInfo { + def decl: StructDecl; + def field_info: Array; + def byte_size: int; + def ref_count: int; + + new(decl: StructDecl, field_info, byte_size, ref_count) { } +} +private type V3SplitFieldInfo #unboxed #packed { + case Bytes(offset: u24, access: MemStorageKind); + case Ref(offset: u24); + case RefI31(byte_offset: u24, ref_offset: u24); + case RefU64(byte_offset: u24, ref_offset: u24); +} +// Computes struct layout, assigning field offsets in the byte portion and/or the reference portion. +def computeStructInfo(decl: StructDecl) -> V3SplitStructDeclInfo { + var types = decl.field_types; + var field_info = Array.new(types.length); + var byte_size = 0, ref_count = 0, start = 0; + var sup = getSuperStructInfo(decl); + if (sup != null) { + start = sup.field_info.length; + for (i < start) field_info[i] = sup.field_info[i]; + byte_size = sup.byte_size; + ref_count = sup.ref_count; + } + for (i = start; i < types.length; i++) { + match (storageKind(types[i])) { + Bytes(access) => { + byte_size = alignUp(byte_size, access.size); + field_info[i] = V3SplitFieldInfo.Bytes(u24.!(byte_size), access); + byte_size += access.size; + } + Ref => { + field_info[i] = V3SplitFieldInfo.Ref(u24.!(ref_count++)); + } + RefI31 => { + byte_size = alignUp(byte_size, 4); + field_info[i] = V3SplitFieldInfo.RefI31(u24.!(byte_size), u24.!(ref_count++)); + byte_size += 4; + } + RefU64 => { + byte_size = alignUp(byte_size, 8); + field_info[i] = V3SplitFieldInfo.RefU64(u24.!(byte_size), u24.!(ref_count++)); + byte_size += 8; + } + } + } + var info = V3SplitStructDeclInfo.new(decl, field_info, byte_size, ref_count); + if (Debug.paranoid) verifyStructInfo(info, sup); + decl.addInfo(info); + return info; +} +// Returns the layout of the (first) supertype of {decl}, if it is a struct, computing it if necessary. +def getSuperStructInfo(decl: StructDecl) -> V3SplitStructDeclInfo { + var sup = decl.getFirstSuperType(); + if (!StructDecl.?(sup)) return null; + return getStructInfo(StructDecl.!(sup)); +} + +// A wasm struct instance that is split into two arrays: a byte array where all primitives are stored, +// and an object array where all references are stored. +class V3SplitWasmStruct extends WasmStruct { + private def info: V3SplitStructDeclInfo; + private def bytes: Array; + private def refs: Array; + + private new(info, bytes, refs) { } + + def decl() => info.decl; + def getField(index: u31) -> Value { + match (info.field_info[index]) { + Bytes(offset, access) => { + return access.accessor.readValue(bytes[offset ...]); + } + Ref(offset) => { + return Value.Ref(refs[offset]); + } + RefI31(byte_offset, ref_offset) => { + var d = DataReaders.read_range_u32(bytes[byte_offset ...]); + return box_i31ref(d, refs[ref_offset]); + } + RefU64(byte_offset, ref_offset) => { + var d = DataReaders.read_range_u64(bytes[byte_offset ...]); + return box_ref_u64(refs[ref_offset], d); + } + } + } + def setField(index: u31, v: Value) { + match (info.field_info[index]) { + Bytes(offset, access) => { + return void(access.accessor.writeValue(bytes[offset ...], v)); + } + Ref(offset) => { + return void(refs[offset] = unbox_ref(v)); + } + RefI31(byte_offset, ref_offset) => { + var t = unbox_i31ref(v); + DataWriters.write_range_u32(bytes[byte_offset ...], t.0); + refs[ref_offset] = t.1; + } + RefU64(byte_offset, ref_offset) => { + var t = unbox_ref_u64(v); + DataWriters.write_range_u64(bytes[byte_offset ...], t.1); + refs[ref_offset] = t.0; + } + } + } +} + +// Metadata for split arrays. +private class V3SplitArrayDeclInfo(decl: ArrayDecl) extends ArrayDeclInfo { + def alloc(length: u32) -> V3SplitWasmArray; + def allocFromValues(vals: Range) -> V3SplitWasmArray; +} +private class V3SplitArrayDeclInfoOf extends V3SplitArrayDeclInfo { + def box: T -> Value; + def unbox: Value -> T; + + new(decl: ArrayDecl, box, unbox) super(decl) { } + + def alloc(length: u32) -> V3SplitWasmArray { + return V3SplitWasmArrayOf.new(this, Array.new(int.!(length))); + } + def allocFromValues(vals: Range) -> V3SplitWasmArray { + var elems = Array.new(vals.length); + for (i < elems.length) elems[i] = unbox(vals[i]); + return V3SplitWasmArrayOf.new(this, elems); + } +} +// Computes the element representation of an array, selecting the Virgil type {T} used for storage +// and the functions to box and unbox elements. +def computeArrayInfo(decl: ArrayDecl) -> V3SplitArrayDeclInfo { + var info: V3SplitArrayDeclInfo; + var kind = elemStorageKind(decl); + if (Debug.paranoid) verifyArrayInfo(decl, kind); + match (kind) { + Bytes(access) => { + match (access) { + I8, U8 => info = primArrayInfo(decl, MemoryAccessors.U8); + I16, U16 => info = primArrayInfo(decl, MemoryAccessors.U16); + I32, U32 => info = primArrayInfo(decl, MemoryAccessors.U32); + U64 => info = primArrayInfo(decl, MemoryAccessors.U64); + F32 => info = V3SplitArrayDeclInfoOf.new(decl, Values.box_fu32, Values.unbox_fu32); // store raw bits + F64 => info = V3SplitArrayDeclInfoOf.new(decl, Values.box_du64, Values.unbox_du64); // store raw bits + V128 => info = primArrayInfo(decl, MemoryAccessors.V128); + } + } + Ref => info = V3SplitArrayDeclInfoOf.new(decl, Value.Ref, unbox_ref); + RefI31 => info = V3SplitArrayDeclInfoOf<(u32, Object)>.new(decl, box_i31ref, unbox_i31ref); + RefU64 => info = V3SplitArrayDeclInfoOf<(Object, u64)>.new(decl, box_ref_u64, unbox_ref_u64); + } + decl.addInfo(info); + return info; +} +// Returns the storage kind for the elements of {decl}, which is that of its root supertype. +def elemStorageKind(decl: ArrayDecl) -> SplitStorageKind { + var sup = decl.getFirstSuperType(); + if (ArrayDecl.?(sup)) return elemStorageKind(ArrayDecl.!(sup)); + return storageKind(decl.elem_types[0]); +} +def primArrayInfo(decl: ArrayDecl, access: MemoryAccessorOf) -> V3SplitArrayDeclInfo { + return V3SplitArrayDeclInfoOf.new(decl, access.box, access.unbox); +} + +// A wasm array instance whose elements are stored in a Virgil array specialized to the element +// representation. The bulk operations are virtual so that they can operate on the specialized storage. +class V3SplitWasmArray extends WasmArray { + def fill(offset: u64, size: u64, val: Value) -> TrapReason; + def copyFrom(dst_offset: u64, src: V3SplitWasmArray, src_offset: u64, size: u64) -> TrapReason; + def copyIn(offset: u64, src: Range) -> TrapReason; + def copyOut(dst: Range, offset: u64) -> TrapReason; + // Checks that the elements in {[offset, offset + size)} are within bounds. + def checkRange(offset: u64, size: u64) -> TrapReason { + var length = getLength(); + if (offset > length || size > (length - offset)) return TrapReason.ARRAY_OOB; + return OK; + } +} +class V3SplitWasmArrayOf extends V3SplitWasmArray { + private def info: V3SplitArrayDeclInfoOf; + private def vals: Array; + + private new(info, vals) {} + + def decl() => info.decl; + def getLength() -> u32 { return u32.!(vals.length); } + def getElem(index: u32) => info.box(vals[index]); + def setElem(index: u32, v: Value) => void(vals[index] = info.unbox(v)); + def getRange(offset: u64, size: u64) => Traps.getRange(vals, offset, size, TrapReason.ARRAY_OOB); + + def fill(offset: u64, size: u64, val: Value) -> TrapReason { + var t = getRange(offset, size), r = t.result; + if (t.reason != OK) return t.reason; + var v = info.unbox(val); + for (i < r.length) r[i] = v; + return OK; + } + def copyFrom(dst_offset: u64, src: V3SplitWasmArray, src_offset: u64, size: u64) -> TrapReason { + var t = getRange(dst_offset, size), dr = t.result; + if (t.reason != OK) return t.reason; + if (V3SplitWasmArrayOf.?(src)) { + // Same element representation: copy the storage directly, minding potential overlap. + var s = V3SplitWasmArrayOf.!(src); + var st = s.getRange(src_offset, size), sr = st.result; + if (st.reason != OK) return st.reason; + if (s == this && src_offset < dst_offset) { + for (i = dr.length - 1; i >= 0; i--) dr[i] = sr[i]; + } else { + for (i < dr.length) dr[i] = sr[i]; + } + return OK; + } + // Different element representation, e.g. {(ref $struct)} into {anyref}: convert each element. + var r = src.checkRange(src_offset, size); + if (r != OK) return r; + for (i < dr.length) dr[i] = info.unbox(src.getElem(u32.!(src_offset + u64.!(i)))); + return OK; + } + def copyIn(offset: u64, src: Range) -> TrapReason { + var t = getRange(offset, u64.!(src.length)), dr = t.result; + if (t.reason != OK) return t.reason; + for (i < dr.length) dr[i] = info.unbox(src[i]); + return OK; + } + def copyOut(dst: Range, offset: u64) -> TrapReason { + var t = getRange(offset, u64.!(dst.length)), sr = t.result; + if (t.reason != OK) return t.reason; + for (i < dst.length) dst[i] = info.box(sr[i]); + return OK; + } +} + +// Boxing and unboxing for references, i31-or-reference pairs, and reference + u64 pairs. +def unbox_ref(v: Value) -> Object { + return Value.Ref.!(v).val; +} +def box_i31ref(t: (u32, Object)) -> Value { + return if ((t.0 & 1) == 1, Value.I31(u31.view(t.0 >> 1)), Value.Ref(t.1)); +} +def unbox_i31ref(v: Value) -> (u32, Object) { + var d: u32, r: Object; + match (v) { + I31(val) => d = (u32.!(val) << 1) | 1; + Ref(obj) => r = obj; + _ => ; + } + return (d, r); +} +def box_ref_u64(t: (Object, u64)) -> Value { + return Value.Cont(Continuations.fromStoredObject(t.0, t.1)); +} +def unbox_ref_u64(v: Value) -> (Object, u64) { + var r: Object, d: u64; + match (v) { + Cont(c) => { + r = Continuations.getStoredObject(c); + d = Continuations.getStoredVersion(c); + } + _ => ; // null reference + } + return (r, d); +} + +def OK = TrapReason.NONE; +def NO_BYTES: Array = []; +def NO_REFS: Array = []; + +// Checks that the layout {info} is consistent: fields inherited from the supertype {sup} have the +// same offsets as in the supertype, every field's declared type is representable by its assigned +// layout, every field lies within the struct and is aligned, and no two fields overlap. +def verifyStructInfo(info: V3SplitStructDeclInfo, sup: V3SplitStructDeclInfo) { + var types = info.decl.field_types, field_info = info.field_info; + if (sup != null) { + if (sup.field_info.length > field_info.length) failLayout(info, -1, "supertype has more fields"); + if (sup.byte_size > info.byte_size) failLayout(info, -1, "supertype has larger byte portion"); + if (sup.ref_count > info.ref_count) failLayout(info, -1, "supertype has larger reference portion"); + for (i < sup.field_info.length) { + if (field_info[i] != sup.field_info[i]) failLayout(info, i, "field layout differs from supertype"); + } + } + var bytes_used = Array.new(info.byte_size), refs_used = Array.new(info.ref_count); + for (i < field_info.length) { + var kind = storageKind(types[i]); + match (field_info[i]) { + Bytes(offset, access) => { + match (kind) { + Bytes(a) => if (a != access) failLayout(info, i, "primitive field has wrong storage kind"); + _ => failLayout(info, i, "primitive field has non-primitive layout"); + } + claimBytes(info, i, bytes_used, offset, access.size); + } + Ref(offset) => { + if (kind != SplitStorageKind.Ref) failLayout(info, i, "reference field has wrong layout"); + claimRef(info, i, refs_used, offset); + } + RefI31(byte_offset, ref_offset) => { + if (kind != SplitStorageKind.RefI31 && kind != SplitStorageKind.Ref) failLayout(info, i, "i31-or-reference field has wrong layout"); + claimBytes(info, i, bytes_used, byte_offset, 4); + claimRef(info, i, refs_used, ref_offset); + } + RefU64(byte_offset, ref_offset) => { + if (kind != SplitStorageKind.RefU64) failLayout(info, i, "reference+u64 field has wrong layout"); + claimBytes(info, i, bytes_used, byte_offset, 8); + claimRef(info, i, refs_used, ref_offset); + } + } + } +} +def claimBytes(info: V3SplitStructDeclInfo, field: int, used: Array, offset: int, size: int) { + if ((offset & (size - 1)) != 0) failLayout(info, field, "misaligned primitive"); + if (offset + size > used.length) failLayout(info, field, "primitive out of bounds of byte portion"); + for (i < size) { + if (used[offset + i]) failLayout(info, field, "overlapping primitive"); + used[offset + i] = true; + } +} +def claimRef(info: V3SplitStructDeclInfo, field: int, used: Array, offset: int) { + if (offset >= used.length) failLayout(info, field, "reference out of bounds of reference portion"); + if (used[offset]) failLayout(info, field, "overlapping reference"); + used[offset] = true; +} +def failLayout(info: V3SplitStructDeclInfo, field: int, msg: string) { + var buf = StringBuilder.new(); + info.decl.render(buf); + if (field >= 0) buf.put1(" field #%d", field); + buf.puts(": ").puts(msg); + System.error("SplitObjectModelError", buf.toString()); +} +// Checks that the declared element type of {decl} is representable by the inherited {kind}. +def verifyArrayInfo(decl: ArrayDecl, kind: SplitStorageKind) { + var declared = storageKind(decl.elem_types[0]); + var ok = declared == kind || (declared == SplitStorageKind.Ref && kind == SplitStorageKind.RefI31); + if (!ok) System.error("SplitObjectModelError", decl.render(StringBuilder.new()).puts(": element representation differs from supertype").toString()); +} diff --git a/test/regress/ext:gc/array-copy-subtype0.bin.wast b/test/regress/ext:gc/array-copy-subtype0.bin.wast new file mode 100644 index 000000000..f8dabc602 --- /dev/null +++ b/test/regress/ext:gc/array-copy-subtype0.bin.wast @@ -0,0 +1,146 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\fc\80\80\80\00\16\5f" + "\01\7f\00\5e\63\00\01\5e\6e\01\5e\6d\01\5e\6c\01" + "\5e\6b\01\50\00\5e\6e\00\50\01\06\5e\64\00\00\60" + "\01\7f\01\64\00\60\01\6e\01\7f\60\01\64\02\04\7f" + "\7f\7f\7f\60\01\64\02\05\7f\7f\7f\7f\7f\60\01\64" + "\05\03\7f\7f\7f\60\01\64\01\05\7f\7f\7f\7f\7f\60" + "\00\01\64\01\60\00\01\64\02\60\00\01\64\07\60\00" + "\04\7f\7f\7f\7f\60\00\03\7f\7f\7f\60\00\05\7f\7f" + "\7f\7f\7f\60\00\00\60\00\01\7f\03\98\80\80\80\00" + "\17\08\09\0a\0b\0c\0d\0e\0e\0f\10\11\12\11\11\12" + "\13\13\13\13\14\14\14\15\07\e6\81\80\80\00\0d\0c" + "\72\65\66\73\5f\74\6f\5f\61\6e\79\73\00\0a\0f\72" + "\65\66\73\5f\74\6f\5f\73\74\72\75\63\74\73\00\0b" + "\0b\69\33\31\73\5f\74\6f\5f\65\71\73\00\0c\0f\6c" + "\65\61\66\73\75\62\5f\74\6f\5f\61\6e\79\73\00\0d" + "\12\6c\65\61\66\73\75\62\5f\74\6f\5f\73\74\72\75" + "\63\74\73\00\0e\0f\61\6e\79\73\5f\6f\76\65\72\6c" + "\61\70\5f\75\70\00\0f\11\61\6e\79\73\5f\6f\76\65" + "\72\6c\61\70\5f\64\6f\77\6e\00\10\0f\72\65\66\73" + "\5f\6f\76\65\72\6c\61\70\5f\75\70\00\11\11\72\65" + "\66\73\5f\6f\76\65\72\6c\61\70\5f\64\6f\77\6e\00" + "\12\10\72\65\66\73\5f\74\6f\5f\61\6e\79\73\5f\6f" + "\6f\62\00\13\14\72\65\66\73\5f\74\6f\5f\61\6e\79" + "\73\5f\64\73\74\5f\6f\6f\62\00\14\08\61\6e\79\73" + "\5f\6f\6f\62\00\15\0b\7a\65\72\6f\5f\61\74\5f\65" + "\6e\64\00\16\0a\80\86\80\80\00\17\87\80\80\80\00" + "\00\20\00\fb\00\00\0b\a8\80\80\80\00\00\20\00\d1" + "\04\7f\41\7f\05\20\00\fb\14\6c\04\7f\20\00\fb\16" + "\6c\fb\1d\05\41\e8\07\20\00\fb\16\00\fb\02\00\00" + "\6a\0b\0b\0b\a6\80\80\80\00\00\20\00\41\00\fb\0b" + "\02\10\01\20\00\41\01\fb\0b\02\10\01\20\00\41\02" + "\fb\0b\02\10\01\20\00\41\03\fb\0b\02\10\01\0b\8f" + "\80\80\80\00\00\20\00\10\02\20\00\41\04\fb\0b\02" + "\10\01\0b\9d\80\80\80\00\00\20\00\41\00\fb\0b\05" + "\10\01\20\00\41\01\fb\0b\05\10\01\20\00\41\02\fb" + "\0b\05\10\01\0b\af\80\80\80\00\00\20\00\41\00\fb" + "\0b\01\10\01\20\00\41\01\fb\0b\01\10\01\20\00\41" + "\02\fb\0b\01\10\01\20\00\41\03\fb\0b\01\10\01\20" + "\00\41\04\fb\0b\01\10\01\0b\90\80\80\80\00\00\41" + "\01\10\00\d0\00\41\03\10\00\fb\08\01\03\0b\96\80" + "\80\80\00\00\41\01\10\00\d0\00\41\03\10\00\d0\00" + "\41\05\10\00\fb\08\01\05\0b\98\80\80\80\00\00\41" + "\01\fb\1c\41\02\fb\1c\41\03\10\00\d0\6e\41\05\fb" + "\1c\fb\08\02\05\0b\8e\80\80\80\00\00\41\05\10\00" + "\41\06\10\00\fb\08\07\02\0b\a2\80\80\80\00\01\01" + "\64\02\41\07\fb\1c\41\04\fb\06\02\21\00\20\00\41" + "\01\10\06\41\00\41\03\fb\11\02\01\20\00\10\02\0b" + "\a0\80\80\80\00\01\01\64\05\d0\6b\41\03\fb\06\05" + "\21\00\20\00\41\00\10\06\41\01\41\02\fb\11\05\01" + "\20\00\10\04\0b\ce\80\80\80\00\01\01\64\03\d0\6d" + "\41\04\fb\06\03\21\00\20\00\41\01\41\01\fb\1c\41" + "\02\fb\1c\41\03\fb\1c\fb\08\04\03\41\00\41\03\fb" + "\11\03\04\20\00\41\00\fb\0b\03\10\01\20\00\41\01" + "\fb\0b\03\10\01\20\00\41\02\fb\0b\03\10\01\20\00" + "\41\03\fb\0b\03\10\01\0b\a2\80\80\80\00\01\01\64" + "\02\41\07\fb\1c\41\04\fb\06\02\21\00\20\00\41\01" + "\10\09\41\00\41\02\fb\11\02\07\20\00\10\02\0b\a0" + "\80\80\80\00\01\01\64\05\d0\6b\41\03\fb\06\05\21" + "\00\20\00\41\01\10\09\41\00\41\02\fb\11\05\07\20" + "\00\10\04\0b\9b\80\80\80\00\01\01\64\02\10\08\21" + "\00\20\00\41\02\20\00\41\01\41\03\fb\11\02\02\20" + "\00\10\03\0b\9b\80\80\80\00\01\01\64\02\10\08\21" + "\00\20\00\41\00\20\00\41\02\41\03\fb\11\02\02\20" + "\00\10\03\0b\9b\80\80\80\00\01\01\64\01\10\07\21" + "\00\20\00\41\02\20\00\41\01\41\03\fb\11\01\01\20" + "\00\10\05\0b\9b\80\80\80\00\01\01\64\01\10\07\21" + "\00\20\00\41\00\20\00\41\02\41\03\fb\11\01\01\20" + "\00\10\05\0b\93\80\80\80\00\00\41\04\fb\07\02\41" + "\01\10\06\41\00\41\04\fb\11\02\01\0b\93\80\80\80" + "\00\00\41\04\fb\07\02\41\02\10\06\41\00\41\03\fb" + "\11\02\01\0b\97\80\80\80\00\01\01\64\02\10\08\21" + "\00\20\00\41\03\20\00\41\00\41\03\fb\11\02\02\0b" + "\95\80\80\80\00\00\41\04\fb\07\02\41\04\10\06\41" + "\03\41\00\fb\11\02\01\41\01\0b" +) +(module instance) +(assert_return + (invoke "refs_to_anys") + (i32.const 0x7) + (i32.const 0x3e9) + (i32.const 0xffff_ffff) + (i32.const 0x3eb) +) +(assert_return + (invoke "refs_to_structs") + (i32.const 0xffff_ffff) + (i32.const 0x3eb) + (i32.const 0xffff_ffff) +) +(assert_return + (invoke "i31s_to_eqs") + (i32.const 0xffff_ffff) + (i32.const 0x1) + (i32.const 0x2) + (i32.const 0x3) +) +(assert_return + (invoke "leafsub_to_anys") + (i32.const 0x7) + (i32.const 0x3ed) + (i32.const 0x3ee) + (i32.const 0x7) +) +(assert_return + (invoke "leafsub_to_structs") + (i32.const 0xffff_ffff) + (i32.const 0x3ed) + (i32.const 0x3ee) +) +(assert_return + (invoke "anys_overlap_up") + (i32.const 0x1) + (i32.const 0x2) + (i32.const 0x2) + (i32.const 0x3eb) + (i32.const 0xffff_ffff) +) +(assert_return + (invoke "anys_overlap_down") + (i32.const 0x3eb) + (i32.const 0xffff_ffff) + (i32.const 0x5) + (i32.const 0xffff_ffff) + (i32.const 0x5) +) +(assert_return + (invoke "refs_overlap_up") + (i32.const 0x3e9) + (i32.const 0xffff_ffff) + (i32.const 0xffff_ffff) + (i32.const 0x3eb) + (i32.const 0xffff_ffff) +) +(assert_return + (invoke "refs_overlap_down") + (i32.const 0x3eb) + (i32.const 0xffff_ffff) + (i32.const 0x3ed) + (i32.const 0xffff_ffff) + (i32.const 0x3ed) +) +(assert_trap (invoke "refs_to_anys_oob") "out of bounds array access") +(assert_trap (invoke "refs_to_anys_dst_oob") "out of bounds array access") +(assert_trap (invoke "anys_oob") "out of bounds array access") +(assert_return (invoke "zero_at_end") (i32.const 0x1)) diff --git a/test/regress/ext:gc/array-copy-subtype0.wast b/test/regress/ext:gc/array-copy-subtype0.wast new file mode 100644 index 000000000..6df2da259 --- /dev/null +++ b/test/regress/ext:gc/array-copy-subtype0.wast @@ -0,0 +1,135 @@ +;;! gc = true + +;; array.copy between arrays whose element types are related by subtyping but which may have +;; different representations: struct references, anyref/eqref (which may hold i31s), and i31ref. +(module + (type $leaf (struct (field i32))) + (type $refs (array (mut (ref null $leaf)))) + (type $anys (array (mut anyref))) + (type $eqs (array (mut eqref))) + (type $i31s (array (mut i31ref))) + (type $structs (array (mut structref))) + (type $anysup (sub (array anyref))) + (type $leafsub (sub $anysup (array (ref $leaf)))) + + (func $leaf (param i32) (result (ref $leaf)) (struct.new $leaf (local.get 0))) + ;; Encodes an anyref as an i32: null -> -1, i31 -> its value, $leaf -> 1000 + its field. + (func $val (param anyref) (result i32) + (if (result i32) (ref.is_null (local.get 0)) + (then (i32.const -1)) + (else + (if (result i32) (ref.test (ref i31) (local.get 0)) + (then (i31.get_s (ref.cast (ref i31) (local.get 0)))) + (else (i32.add (i32.const 1000) (struct.get $leaf 0 (ref.cast (ref $leaf) (local.get 0))))))))) + (func $anys4 (param $a (ref $anys)) (result i32 i32 i32 i32) + (call $val (array.get $anys (local.get $a) (i32.const 0))) + (call $val (array.get $anys (local.get $a) (i32.const 1))) + (call $val (array.get $anys (local.get $a) (i32.const 2))) + (call $val (array.get $anys (local.get $a) (i32.const 3)))) + (func $anys5 (param $a (ref $anys)) (result i32 i32 i32 i32 i32) + (call $anys4 (local.get $a)) + (call $val (array.get $anys (local.get $a) (i32.const 4)))) + (func $structs3 (param $a (ref $structs)) (result i32 i32 i32) + (call $val (array.get $structs (local.get $a) (i32.const 0))) + (call $val (array.get $structs (local.get $a) (i32.const 1))) + (call $val (array.get $structs (local.get $a) (i32.const 2)))) + (func $refs5 (param $a (ref $refs)) (result i32 i32 i32 i32 i32) + (call $val (array.get $refs (local.get $a) (i32.const 0))) + (call $val (array.get $refs (local.get $a) (i32.const 1))) + (call $val (array.get $refs (local.get $a) (i32.const 2))) + (call $val (array.get $refs (local.get $a) (i32.const 3))) + (call $val (array.get $refs (local.get $a) (i32.const 4)))) + + (func $new_refs (result (ref $refs)) + (array.new_fixed $refs 3 (call $leaf (i32.const 1)) (ref.null $leaf) (call $leaf (i32.const 3)))) + (func $new_refs5 (result (ref $refs)) + (array.new_fixed $refs 5 (call $leaf (i32.const 1)) (ref.null $leaf) (call $leaf (i32.const 3)) (ref.null $leaf) (call $leaf (i32.const 5)))) + (func $new_anys5 (result (ref $anys)) + (array.new_fixed $anys 5 (ref.i31 (i32.const 1)) (ref.i31 (i32.const 2)) (call $leaf (i32.const 3)) (ref.null any) (ref.i31 (i32.const 5)))) + (func $new_leafsub (result (ref $leafsub)) + (array.new_fixed $leafsub 2 (call $leaf (i32.const 5)) (call $leaf (i32.const 6)))) + + ;; Plain references into paired anyref storage. + (func (export "refs_to_anys") (result i32 i32 i32 i32) + (local $dst (ref $anys)) + (local.set $dst (array.new $anys (ref.i31 (i32.const 7)) (i32.const 4))) + (array.copy $anys $refs (local.get $dst) (i32.const 1) (call $new_refs) (i32.const 0) (i32.const 3)) + (call $anys4 (local.get $dst))) + ;; Plain references into plain structref storage. + (func (export "refs_to_structs") (result i32 i32 i32) + (local $dst (ref $structs)) + (local.set $dst (array.new $structs (ref.null struct) (i32.const 3))) + (array.copy $structs $refs (local.get $dst) (i32.const 0) (call $new_refs) (i32.const 1) (i32.const 2)) + (call $structs3 (local.get $dst))) + ;; Paired i31ref storage into paired eqref storage. + (func (export "i31s_to_eqs") (result i32 i32 i32 i32) + (local $dst (ref $eqs)) + (local.set $dst (array.new $eqs (ref.null eq) (i32.const 4))) + (array.copy $eqs $i31s (local.get $dst) (i32.const 1) + (array.new_fixed $i31s 3 (ref.i31 (i32.const 1)) (ref.i31 (i32.const 2)) (ref.i31 (i32.const 3))) (i32.const 0) (i32.const 3)) + (call $val (array.get $eqs (local.get $dst) (i32.const 0))) + (call $val (array.get $eqs (local.get $dst) (i32.const 1))) + (call $val (array.get $eqs (local.get $dst) (i32.const 2))) + (call $val (array.get $eqs (local.get $dst) (i32.const 3)))) + ;; A subtype array of struct references, which shares the representation of its anyref + ;; supertype, into paired anyref storage. + (func (export "leafsub_to_anys") (result i32 i32 i32 i32) + (local $dst (ref $anys)) + (local.set $dst (array.new $anys (ref.i31 (i32.const 7)) (i32.const 4))) + (array.copy $anys $leafsub (local.get $dst) (i32.const 1) (call $new_leafsub) (i32.const 0) (i32.const 2)) + (call $anys4 (local.get $dst))) + ;; The same subtype array into plain structref storage. + (func (export "leafsub_to_structs") (result i32 i32 i32) + (local $dst (ref $structs)) + (local.set $dst (array.new $structs (ref.null struct) (i32.const 3))) + (array.copy $structs $leafsub (local.get $dst) (i32.const 1) (call $new_leafsub) (i32.const 0) (i32.const 2)) + (call $structs3 (local.get $dst))) + ;; Overlapping copies within one paired array, in both directions. + (func (export "anys_overlap_up") (result i32 i32 i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (call $new_anys5)) + (array.copy $anys $anys (local.get $a) (i32.const 2) (local.get $a) (i32.const 1) (i32.const 3)) + (call $anys5 (local.get $a))) + (func (export "anys_overlap_down") (result i32 i32 i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (call $new_anys5)) + (array.copy $anys $anys (local.get $a) (i32.const 0) (local.get $a) (i32.const 2) (i32.const 3)) + (call $anys5 (local.get $a))) + ;; Overlapping copies within one plain array, in both directions. + (func (export "refs_overlap_up") (result i32 i32 i32 i32 i32) + (local $a (ref $refs)) + (local.set $a (call $new_refs5)) + (array.copy $refs $refs (local.get $a) (i32.const 2) (local.get $a) (i32.const 1) (i32.const 3)) + (call $refs5 (local.get $a))) + (func (export "refs_overlap_down") (result i32 i32 i32 i32 i32) + (local $a (ref $refs)) + (local.set $a (call $new_refs5)) + (array.copy $refs $refs (local.get $a) (i32.const 0) (local.get $a) (i32.const 2) (i32.const 3)) + (call $refs5 (local.get $a))) + ;; Out-of-bounds copies trap, for both the same and different representations. + (func (export "refs_to_anys_oob") + (array.copy $anys $refs (array.new_default $anys (i32.const 4)) (i32.const 1) (call $new_refs) (i32.const 0) (i32.const 4))) + (func (export "refs_to_anys_dst_oob") + (array.copy $anys $refs (array.new_default $anys (i32.const 4)) (i32.const 2) (call $new_refs) (i32.const 0) (i32.const 3))) + (func (export "anys_oob") + (local $a (ref $anys)) + (local.set $a (call $new_anys5)) + (array.copy $anys $anys (local.get $a) (i32.const 3) (local.get $a) (i32.const 0) (i32.const 3))) + ;; Zero-length copies at the end of both arrays are fine. + (func (export "zero_at_end") (result i32) + (array.copy $anys $refs (array.new_default $anys (i32.const 4)) (i32.const 4) (call $new_refs) (i32.const 3) (i32.const 0)) + (i32.const 1)) +) +(assert_return (invoke "refs_to_anys") (i32.const 7) (i32.const 1001) (i32.const -1) (i32.const 1003)) +(assert_return (invoke "refs_to_structs") (i32.const -1) (i32.const 1003) (i32.const -1)) +(assert_return (invoke "i31s_to_eqs") (i32.const -1) (i32.const 1) (i32.const 2) (i32.const 3)) +(assert_return (invoke "leafsub_to_anys") (i32.const 7) (i32.const 1005) (i32.const 1006) (i32.const 7)) +(assert_return (invoke "leafsub_to_structs") (i32.const -1) (i32.const 1005) (i32.const 1006)) +(assert_return (invoke "anys_overlap_up") (i32.const 1) (i32.const 2) (i32.const 2) (i32.const 1003) (i32.const -1)) +(assert_return (invoke "anys_overlap_down") (i32.const 1003) (i32.const -1) (i32.const 5) (i32.const -1) (i32.const 5)) +(assert_return (invoke "refs_overlap_up") (i32.const 1001) (i32.const -1) (i32.const -1) (i32.const 1003) (i32.const -1)) +(assert_return (invoke "refs_overlap_down") (i32.const 1003) (i32.const -1) (i32.const 1005) (i32.const -1) (i32.const 1005)) +(assert_trap (invoke "refs_to_anys_oob") "out of bounds array access") +(assert_trap (invoke "refs_to_anys_dst_oob") "out of bounds array access") +(assert_trap (invoke "anys_oob") "out of bounds array access") +(assert_return (invoke "zero_at_end") (i32.const 1)) diff --git a/test/regress/ext:gc/array-data-bulk0.bin.wast b/test/regress/ext:gc/array-data-bulk0.bin.wast new file mode 100644 index 000000000..80002cb01 --- /dev/null +++ b/test/regress/ext:gc/array-data-bulk0.bin.wast @@ -0,0 +1,110 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\b9\80\80\80\00\0e\5e" + "\78\01\5e\77\01\5e\7f\01\5e\7e\01\5e\7d\01\5e\7c" + "\01\60\00\04\7f\7f\7f\7f\60\00\03\7f\7f\7f\60\00" + "\02\7f\7f\60\00\02\7e\7e\60\00\01\7d\60\00\01\7c" + "\60\00\00\60\00\01\7f\03\8f\80\80\80\00\0e\06\07" + "\08\08\09\0a\0b\06\06\09\0c\0c\0c\0d\07\e4\81\80" + "\80\00\0e\0b\6e\65\77\5f\64\61\74\61\5f\69\38\00" + "\00\0c\6e\65\77\5f\64\61\74\61\5f\69\31\36\00\01" + "\13\6e\65\77\5f\64\61\74\61\5f\69\31\36\5f\73\69" + "\67\6e\65\64\00\02\0c\6e\65\77\5f\64\61\74\61\5f" + "\69\33\32\00\03\0c\6e\65\77\5f\64\61\74\61\5f\69" + "\36\34\00\04\0c\6e\65\77\5f\64\61\74\61\5f\66\33" + "\32\00\05\0c\6e\65\77\5f\64\61\74\61\5f\66\36\34" + "\00\06\0c\69\6e\69\74\5f\64\61\74\61\5f\69\38\00" + "\07\0d\69\6e\69\74\5f\64\61\74\61\5f\69\31\36\00" + "\08\0d\69\6e\69\74\5f\64\61\74\61\5f\69\36\34\00" + "\09\0c\6e\65\77\5f\64\61\74\61\5f\6f\6f\62\00\0a" + "\11\69\6e\69\74\5f\64\61\74\61\5f\64\73\74\5f\6f" + "\6f\62\00\0b\11\69\6e\69\74\5f\64\61\74\61\5f\73" + "\72\63\5f\6f\6f\62\00\0c\0b\7a\65\72\6f\5f\61\74" + "\5f\65\6e\64\00\0d\0c\81\80\80\80\00\03\0a\db\83" + "\80\80\00\0e\a8\80\80\80\00\01\01\64\00\41\00\41" + "\04\fb\09\00\01\21\00\20\00\fb\0f\20\00\41\00\fb" + "\0c\00\20\00\41\01\fb\0d\00\20\00\41\03\fb\0c\00" + "\0b\a1\80\80\80\00\01\01\64\01\41\01\41\02\fb\09" + "\01\00\21\00\20\00\fb\0f\20\00\41\00\fb\0d\01\20" + "\00\41\01\fb\0d\01\0b\9d\80\80\80\00\01\01\64\01" + "\41\00\41\02\fb\09\01\01\21\00\20\00\41\00\fb\0c" + "\01\20\00\41\01\fb\0d\01\0b\9d\80\80\80\00\01\01" + "\64\02\41\00\41\02\fb\09\02\00\21\00\20\00\41\00" + "\fb\0b\02\20\00\41\01\fb\0b\02\0b\9d\80\80\80\00" + "\01\01\64\03\41\00\41\02\fb\09\03\00\21\00\20\00" + "\41\00\fb\0b\03\20\00\41\01\fb\0b\03\0b\8f\80\80" + "\80\00\00\41\00\41\01\fb\09\04\02\41\00\fb\0b\04" + "\0b\8f\80\80\80\00\00\41\04\41\01\fb\09\05\02\41" + "\00\fb\0b\05\0b\b4\80\80\80\00\01\01\64\00\41\04" + "\fb\07\00\21\00\20\00\41\01\41\00\41\02\fb\12\00" + "\01\20\00\41\00\fb\0d\00\20\00\41\01\fb\0d\00\20" + "\00\41\02\fb\0c\00\20\00\41\03\fb\0d\00\0b\b4\80" + "\80\80\00\01\01\64\01\41\04\fb\07\01\21\00\20\00" + "\41\01\41\02\41\02\fb\12\01\00\20\00\41\00\fb\0d" + "\01\20\00\41\01\fb\0d\01\20\00\41\02\fb\0d\01\20" + "\00\41\03\fb\0d\01\0b\a6\80\80\80\00\01\01\64\03" + "\41\02\fb\07\03\21\00\20\00\41\01\41\08\41\01\fb" + "\12\03\00\20\00\41\00\fb\0b\03\20\00\41\01\fb\0b" + "\03\0b\8b\80\80\80\00\00\41\04\41\04\fb\09\02\00" + "\1a\0b\91\80\80\80\00\00\41\04\fb\07\01\41\03\41" + "\00\41\02\fb\12\01\00\0b\91\80\80\80\00\00\41\04" + "\fb\07\03\41\00\41\01\41\02\fb\12\03\00\0b\9b\80" + "\80\80\00\00\41\02\fb\07\03\41\02\41\10\41\00\fb" + "\12\03\00\41\10\41\00\fb\09\03\00\fb\0f\0b\0b\a7" + "\80\80\80\00\03\01\10\01\02\03\04\05\06\07\08\09" + "\0a\0b\0c\0d\0e\0f\10\01\04\80\ff\00\80\01\0c\00" + "\00\c0\3f\00\00\00\00\00\00\f8\3f" +) +(module instance) +(assert_return + (invoke "new_data_i8") + (i32.const 0x4) + (i32.const 0xffff_ff80) + (i32.const 0xff) + (i32.const 0xffff_ff80) +) +(assert_return + (invoke "new_data_i16") + (i32.const 0x2) + (i32.const 0x302) + (i32.const 0x504) +) +(assert_return + (invoke "new_data_i16_signed") + (i32.const 0xffff_ff80) + (i32.const 0x8000) +) +(assert_return + (invoke "new_data_i32") + (i32.const 0x403_0201) + (i32.const 0x807_0605) +) +(assert_return + (invoke "new_data_i64") + (i64.const 0x807_0605_0403_0201) + (i64.const 0x100f_0e0d_0c0b_0a09) +) +(assert_return (invoke "new_data_f32") (f32.const 0x1.8p+0)) +(assert_return (invoke "new_data_f64") (f64.const 0x1.8p+0)) +(assert_return + (invoke "init_data_i8") + (i32.const 0x0) + (i32.const 0x80) + (i32.const 0xffff_ffff) + (i32.const 0x0) +) +(assert_return + (invoke "init_data_i16") + (i32.const 0x0) + (i32.const 0x403) + (i32.const 0x605) + (i32.const 0x0) +) +(assert_return + (invoke "init_data_i64") + (i64.const 0x0) + (i64.const 0x100f_0e0d_0c0b_0a09) +) +(assert_trap (invoke "new_data_oob") "out of bounds memory access") +(assert_trap (invoke "init_data_dst_oob") "out of bounds array access") +(assert_trap (invoke "init_data_src_oob") "out of bounds memory access") +(assert_return (invoke "zero_at_end") (i32.const 0x0)) diff --git a/test/regress/ext:gc/array-data-bulk0.wast b/test/regress/ext:gc/array-data-bulk0.wast new file mode 100644 index 000000000..4304ceccf --- /dev/null +++ b/test/regress/ext:gc/array-data-bulk0.wast @@ -0,0 +1,92 @@ +;;! gc = true + +;; array.new_data and array.init_data into packed and unpacked primitive arrays. +(module + (type $i8s (array (mut i8))) + (type $i16s (array (mut i16))) + (type $i32s (array (mut i32))) + (type $i64s (array (mut i64))) + (type $f32s (array (mut f32))) + (type $f64s (array (mut f64))) + (data $d "\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10") + (data $s "\80\ff\00\80") + (data $f "\00\00\c0\3f\00\00\00\00\00\00\f8\3f") + + (func (export "new_data_i8") (result i32 i32 i32 i32) + (local $a (ref $i8s)) + (local.set $a (array.new_data $i8s $s (i32.const 0) (i32.const 4))) + (array.len (local.get $a)) + (array.get_s $i8s (local.get $a) (i32.const 0)) + (array.get_u $i8s (local.get $a) (i32.const 1)) + (array.get_s $i8s (local.get $a) (i32.const 3))) + (func (export "new_data_i16") (result i32 i32 i32) + (local $a (ref $i16s)) + (local.set $a (array.new_data $i16s $d (i32.const 1) (i32.const 2))) + (array.len (local.get $a)) + (array.get_u $i16s (local.get $a) (i32.const 0)) + (array.get_u $i16s (local.get $a) (i32.const 1))) + (func (export "new_data_i16_signed") (result i32 i32) + (local $a (ref $i16s)) + (local.set $a (array.new_data $i16s $s (i32.const 0) (i32.const 2))) + (array.get_s $i16s (local.get $a) (i32.const 0)) + (array.get_u $i16s (local.get $a) (i32.const 1))) + (func (export "new_data_i32") (result i32 i32) + (local $a (ref $i32s)) + (local.set $a (array.new_data $i32s $d (i32.const 0) (i32.const 2))) + (array.get $i32s (local.get $a) (i32.const 0)) + (array.get $i32s (local.get $a) (i32.const 1))) + (func (export "new_data_i64") (result i64 i64) + (local $a (ref $i64s)) + (local.set $a (array.new_data $i64s $d (i32.const 0) (i32.const 2))) + (array.get $i64s (local.get $a) (i32.const 0)) + (array.get $i64s (local.get $a) (i32.const 1))) + (func (export "new_data_f32") (result f32) + (array.get $f32s (array.new_data $f32s $f (i32.const 0) (i32.const 1)) (i32.const 0))) + (func (export "new_data_f64") (result f64) + (array.get $f64s (array.new_data $f64s $f (i32.const 4) (i32.const 1)) (i32.const 0))) + (func (export "init_data_i8") (result i32 i32 i32 i32) + (local $a (ref $i8s)) + (local.set $a (array.new_default $i8s (i32.const 4))) + (array.init_data $i8s $s (local.get $a) (i32.const 1) (i32.const 0) (i32.const 2)) + (array.get_u $i8s (local.get $a) (i32.const 0)) + (array.get_u $i8s (local.get $a) (i32.const 1)) + (array.get_s $i8s (local.get $a) (i32.const 2)) + (array.get_u $i8s (local.get $a) (i32.const 3))) + (func (export "init_data_i16") (result i32 i32 i32 i32) + (local $a (ref $i16s)) + (local.set $a (array.new_default $i16s (i32.const 4))) + (array.init_data $i16s $d (local.get $a) (i32.const 1) (i32.const 2) (i32.const 2)) + (array.get_u $i16s (local.get $a) (i32.const 0)) + (array.get_u $i16s (local.get $a) (i32.const 1)) + (array.get_u $i16s (local.get $a) (i32.const 2)) + (array.get_u $i16s (local.get $a) (i32.const 3))) + (func (export "init_data_i64") (result i64 i64) + (local $a (ref $i64s)) + (local.set $a (array.new_default $i64s (i32.const 2))) + (array.init_data $i64s $d (local.get $a) (i32.const 1) (i32.const 8) (i32.const 1)) + (array.get $i64s (local.get $a) (i32.const 0)) + (array.get $i64s (local.get $a) (i32.const 1))) + (func (export "new_data_oob") + (drop (array.new_data $i32s $d (i32.const 4) (i32.const 4)))) + (func (export "init_data_dst_oob") + (array.init_data $i16s $d (array.new_default $i16s (i32.const 4)) (i32.const 3) (i32.const 0) (i32.const 2))) + (func (export "init_data_src_oob") + (array.init_data $i64s $d (array.new_default $i64s (i32.const 4)) (i32.const 0) (i32.const 1) (i32.const 2))) + (func (export "zero_at_end") (result i32) + (array.init_data $i64s $d (array.new_default $i64s (i32.const 2)) (i32.const 2) (i32.const 16) (i32.const 0)) + (array.len (array.new_data $i64s $d (i32.const 16) (i32.const 0)))) +) +(assert_return (invoke "new_data_i8") (i32.const 4) (i32.const -128) (i32.const 255) (i32.const -128)) +(assert_return (invoke "new_data_i16") (i32.const 2) (i32.const 0x0302) (i32.const 0x0504)) +(assert_return (invoke "new_data_i16_signed") (i32.const -128) (i32.const 0x8000)) +(assert_return (invoke "new_data_i32") (i32.const 0x04030201) (i32.const 0x08070605)) +(assert_return (invoke "new_data_i64") (i64.const 0x0807060504030201) (i64.const 0x100f0e0d0c0b0a09)) +(assert_return (invoke "new_data_f32") (f32.const 1.5)) +(assert_return (invoke "new_data_f64") (f64.const 1.5)) +(assert_return (invoke "init_data_i8") (i32.const 0) (i32.const 128) (i32.const -1) (i32.const 0)) +(assert_return (invoke "init_data_i16") (i32.const 0) (i32.const 0x0403) (i32.const 0x0605) (i32.const 0)) +(assert_return (invoke "init_data_i64") (i64.const 0) (i64.const 0x100f0e0d0c0b0a09)) +(assert_trap (invoke "new_data_oob") "out of bounds memory access") +(assert_trap (invoke "init_data_dst_oob") "out of bounds array access") +(assert_trap (invoke "init_data_src_oob") "out of bounds memory access") +(assert_return (invoke "zero_at_end") (i32.const 0)) diff --git a/test/regress/ext:gc/array-fill-subtype0.bin.wast b/test/regress/ext:gc/array-fill-subtype0.bin.wast new file mode 100644 index 000000000..86c9b4baa --- /dev/null +++ b/test/regress/ext:gc/array-fill-subtype0.bin.wast @@ -0,0 +1,91 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\cb\80\80\80\00\10\5f" + "\01\7f\00\5e\6e\01\50\00\5e\6e\01\50\01\02\5e\6e" + "\01\5e\78\01\5e\77\01\5e\7e\01\5e\7d\01\60\01\6e" + "\01\7f\60\01\64\01\04\7f\7f\7f\7f\60\00\04\7f\7f" + "\7f\7f\60\00\03\7f\7f\7f\60\00\02\7e\7e\60\00\02" + "\7d\7d\60\00\00\60\00\01\7f\03\8d\80\80\80\00\0c" + "\08\09\0a\0a\0b\0b\0b\0c\0d\0e\0e\0f\07\ff\80\80" + "\80\00\0a\08\66\69\6c\6c\5f\69\33\31\00\02\0a\66" + "\69\6c\6c\5f\6d\69\78\65\64\00\03\0c\66\69\6c\6c" + "\5f\76\69\61\5f\73\75\70\00\04\07\66\69\6c\6c\5f" + "\69\38\00\05\08\66\69\6c\6c\5f\69\31\36\00\06\08" + "\66\69\6c\6c\5f\69\36\34\00\07\08\66\69\6c\6c\5f" + "\66\33\32\00\08\08\66\69\6c\6c\5f\6f\6f\62\00\09" + "\0b\66\69\6c\6c\5f\69\38\5f\6f\6f\62\00\0a\10\66" + "\69\6c\6c\5f\7a\65\72\6f\5f\61\74\5f\65\6e\64\00" + "\0b\0a\f9\83\80\80\00\0c\a8\80\80\80\00\00\20\00" + "\d1\04\7f\41\7f\05\20\00\fb\14\6c\04\7f\20\00\fb" + "\16\6c\fb\1d\05\41\e8\07\20\00\fb\16\00\fb\02\00" + "\00\6a\0b\0b\0b\a6\80\80\80\00\00\20\00\41\00\fb" + "\0b\01\10\00\20\00\41\01\fb\0b\01\10\00\20\00\41" + "\02\fb\0b\01\10\00\20\00\41\03\fb\0b\01\10\00\0b" + "\9d\80\80\80\00\01\01\64\01\41\04\fb\07\01\21\00" + "\20\00\41\01\41\09\fb\1c\41\02\fb\10\01\20\00\10" + "\01\0b\b6\80\80\80\00\01\01\64\01\41\04\fb\07\01" + "\21\00\20\00\41\00\41\09\fb\1c\41\04\fb\10\01\20" + "\00\41\01\41\04\fb\00\00\41\02\fb\10\01\20\00\41" + "\02\d0\6e\41\01\fb\10\01\20\00\10\01\0b\b5\80\80" + "\80\00\01\01\64\03\41\03\fb\07\03\21\00\20\00\41" + "\00\41\03\fb\00\00\41\03\fb\10\02\20\00\41\00\fb" + "\0b\03\10\00\20\00\41\01\fb\0b\03\10\00\20\00\41" + "\02\fb\0b\03\10\00\0b\ad\80\80\80\00\01\01\64\04" + "\41\03\fb\07\04\21\00\20\00\41\01\41\ff\03\41\02" + "\fb\10\04\20\00\41\00\fb\0d\04\20\00\41\01\fb\0d" + "\04\20\00\41\02\fb\0c\04\0b\ae\80\80\80\00\01\01" + "\64\05\41\03\fb\07\05\21\00\20\00\41\00\41\80\80" + "\06\41\02\fb\10\05\20\00\41\00\fb\0d\05\20\00\41" + "\01\fb\0c\05\20\00\41\02\fb\0d\05\0b\ad\80\80\80" + "\00\01\01\64\06\41\02\fb\07\06\21\00\20\00\41\01" + "\42\88\8e\98\a8\c0\e0\80\81\01\41\01\fb\10\06\20" + "\00\41\00\fb\0b\06\20\00\41\01\fb\0b\06\0b\a8\80" + "\80\80\00\01\01\64\07\41\02\fb\07\07\21\00\20\00" + "\41\00\43\00\00\c0\3f\41\02\fb\10\07\20\00\41\00" + "\fb\0b\07\20\00\41\01\fb\0b\07\0b\92\80\80\80\00" + "\00\41\04\fb\07\01\41\02\41\01\fb\1c\41\03\fb\10" + "\01\0b\90\80\80\80\00\00\41\04\fb\07\04\41\05\41" + "\01\41\00\fb\10\04\0b\94\80\80\80\00\00\41\04\fb" + "\07\01\41\04\41\01\fb\1c\41\00\fb\10\01\41\01\0b" +) +(module instance) +(assert_return + (invoke "fill_i31") + (i32.const 0xffff_ffff) + (i32.const 0x9) + (i32.const 0x9) + (i32.const 0xffff_ffff) +) +(assert_return + (invoke "fill_mixed") + (i32.const 0x9) + (i32.const 0x3ec) + (i32.const 0xffff_ffff) + (i32.const 0x9) +) +(assert_return + (invoke "fill_via_sup") + (i32.const 0x3eb) + (i32.const 0x3eb) + (i32.const 0x3eb) +) +(assert_return + (invoke "fill_i8") + (i32.const 0x0) + (i32.const 0xff) + (i32.const 0xffff_ffff) +) +(assert_return + (invoke "fill_i16") + (i32.const 0x8000) + (i32.const 0xffff_8000) + (i32.const 0x0) +) +(assert_return + (invoke "fill_i64") + (i64.const 0x0) + (i64.const 0x102_0304_0506_0708) +) +(assert_return (invoke "fill_f32") (f32.const 0x1.8p+0) (f32.const 0x1.8p+0)) +(assert_trap (invoke "fill_oob") "out of bounds array access") +(assert_trap (invoke "fill_i8_oob") "out of bounds array access") +(assert_return (invoke "fill_zero_at_end") (i32.const 0x1)) diff --git a/test/regress/ext:gc/array-fill-subtype0.wast b/test/regress/ext:gc/array-fill-subtype0.wast new file mode 100644 index 000000000..834eb58c9 --- /dev/null +++ b/test/regress/ext:gc/array-fill-subtype0.wast @@ -0,0 +1,93 @@ +;;! gc = true + +;; array.fill on arrays of anyref (which may hold i31s or references), through a supertype, +;; and on packed and unpacked primitive arrays. +(module + (type $leaf (struct (field i32))) + (type $anys (array (mut anyref))) + (type $anysup (sub (array (mut anyref)))) + (type $anysub (sub $anysup (array (mut anyref)))) + (type $i8s (array (mut i8))) + (type $i16s (array (mut i16))) + (type $i64s (array (mut i64))) + (type $f32s (array (mut f32))) + + ;; Encodes an anyref as an i32: null -> -1, i31 -> its value, $leaf -> 1000 + its field. + (func $val (param anyref) (result i32) + (if (result i32) (ref.is_null (local.get 0)) + (then (i32.const -1)) + (else + (if (result i32) (ref.test (ref i31) (local.get 0)) + (then (i31.get_s (ref.cast (ref i31) (local.get 0)))) + (else (i32.add (i32.const 1000) (struct.get $leaf 0 (ref.cast (ref $leaf) (local.get 0))))))))) + (func $anys4 (param $a (ref $anys)) (result i32 i32 i32 i32) + (call $val (array.get $anys (local.get $a) (i32.const 0))) + (call $val (array.get $anys (local.get $a) (i32.const 1))) + (call $val (array.get $anys (local.get $a) (i32.const 2))) + (call $val (array.get $anys (local.get $a) (i32.const 3)))) + + (func (export "fill_i31") (result i32 i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (array.new_default $anys (i32.const 4))) + (array.fill $anys (local.get $a) (i32.const 1) (ref.i31 (i32.const 9)) (i32.const 2)) + (call $anys4 (local.get $a))) + ;; Successive fills of i31s, references, and null over the same paired storage. + (func (export "fill_mixed") (result i32 i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (array.new_default $anys (i32.const 4))) + (array.fill $anys (local.get $a) (i32.const 0) (ref.i31 (i32.const 9)) (i32.const 4)) + (array.fill $anys (local.get $a) (i32.const 1) (struct.new $leaf (i32.const 4)) (i32.const 2)) + (array.fill $anys (local.get $a) (i32.const 2) (ref.null any) (i32.const 1)) + (call $anys4 (local.get $a))) + ;; Fill a subtype array through its supertype. + (func (export "fill_via_sup") (result i32 i32 i32) + (local $a (ref $anysub)) + (local.set $a (array.new_default $anysub (i32.const 3))) + (array.fill $anysup (local.get $a) (i32.const 0) (struct.new $leaf (i32.const 3)) (i32.const 3)) + (call $val (array.get $anysub (local.get $a) (i32.const 0))) + (call $val (array.get $anysub (local.get $a) (i32.const 1))) + (call $val (array.get $anysub (local.get $a) (i32.const 2)))) + (func (export "fill_i8") (result i32 i32 i32) + (local $a (ref $i8s)) + (local.set $a (array.new_default $i8s (i32.const 3))) + (array.fill $i8s (local.get $a) (i32.const 1) (i32.const 0x1ff) (i32.const 2)) + (array.get_u $i8s (local.get $a) (i32.const 0)) + (array.get_u $i8s (local.get $a) (i32.const 1)) + (array.get_s $i8s (local.get $a) (i32.const 2))) + (func (export "fill_i16") (result i32 i32 i32) + (local $a (ref $i16s)) + (local.set $a (array.new_default $i16s (i32.const 3))) + (array.fill $i16s (local.get $a) (i32.const 0) (i32.const 0x18000) (i32.const 2)) + (array.get_u $i16s (local.get $a) (i32.const 0)) + (array.get_s $i16s (local.get $a) (i32.const 1)) + (array.get_u $i16s (local.get $a) (i32.const 2))) + (func (export "fill_i64") (result i64 i64) + (local $a (ref $i64s)) + (local.set $a (array.new_default $i64s (i32.const 2))) + (array.fill $i64s (local.get $a) (i32.const 1) (i64.const 0x0102030405060708) (i32.const 1)) + (array.get $i64s (local.get $a) (i32.const 0)) + (array.get $i64s (local.get $a) (i32.const 1))) + (func (export "fill_f32") (result f32 f32) + (local $a (ref $f32s)) + (local.set $a (array.new_default $f32s (i32.const 2))) + (array.fill $f32s (local.get $a) (i32.const 0) (f32.const 1.5) (i32.const 2)) + (array.get $f32s (local.get $a) (i32.const 0)) + (array.get $f32s (local.get $a) (i32.const 1))) + (func (export "fill_oob") + (array.fill $anys (array.new_default $anys (i32.const 4)) (i32.const 2) (ref.i31 (i32.const 1)) (i32.const 3))) + (func (export "fill_i8_oob") + (array.fill $i8s (array.new_default $i8s (i32.const 4)) (i32.const 5) (i32.const 1) (i32.const 0))) + (func (export "fill_zero_at_end") (result i32) + (array.fill $anys (array.new_default $anys (i32.const 4)) (i32.const 4) (ref.i31 (i32.const 1)) (i32.const 0)) + (i32.const 1)) +) +(assert_return (invoke "fill_i31") (i32.const -1) (i32.const 9) (i32.const 9) (i32.const -1)) +(assert_return (invoke "fill_mixed") (i32.const 9) (i32.const 1004) (i32.const -1) (i32.const 9)) +(assert_return (invoke "fill_via_sup") (i32.const 1003) (i32.const 1003) (i32.const 1003)) +(assert_return (invoke "fill_i8") (i32.const 0) (i32.const 255) (i32.const -1)) +(assert_return (invoke "fill_i16") (i32.const 32768) (i32.const -32768) (i32.const 0)) +(assert_return (invoke "fill_i64") (i64.const 0) (i64.const 0x0102030405060708)) +(assert_return (invoke "fill_f32") (f32.const 1.5) (f32.const 1.5)) +(assert_trap (invoke "fill_oob") "out of bounds array access") +(assert_trap (invoke "fill_i8_oob") "out of bounds array access") +(assert_return (invoke "fill_zero_at_end") (i32.const 1)) diff --git a/test/regress/ext:gc/array-init-subtype0.bin.wast b/test/regress/ext:gc/array-init-subtype0.bin.wast new file mode 100644 index 000000000..dce953f14 --- /dev/null +++ b/test/regress/ext:gc/array-init-subtype0.bin.wast @@ -0,0 +1,87 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\ba\80\80\80\00\0b\5f" + "\01\7f\00\5e\6e\01\5e\6b\01\50\00\5e\6e\00\50\01" + "\03\5e\64\00\00\60\01\6e\01\7f\60\01\64\01\04\7f" + "\7f\7f\7f\60\00\02\7f\7f\60\00\03\7f\7f\7f\60\00" + "\04\7f\7f\7f\7f\60\00\00\03\8c\80\80\80\00\0b\05" + "\06\07\07\07\08\09\09\0a\0a\0a\07\d0\81\80\80\00" + "\09\17\6e\65\77\5f\65\6c\65\6d\5f\6c\65\61\76\65" + "\73\5f\74\6f\5f\61\6e\79\73\00\02\1a\6e\65\77\5f" + "\65\6c\65\6d\5f\6c\65\61\76\65\73\5f\74\6f\5f\73" + "\74\72\75\63\74\73\00\03\1a\6e\65\77\5f\65\6c\65" + "\6d\5f\6c\65\61\76\65\73\5f\74\6f\5f\6c\65\61\66" + "\73\75\62\00\04\0d\6e\65\77\5f\65\6c\65\6d\5f\61" + "\6e\79\73\00\05\18\69\6e\69\74\5f\65\6c\65\6d\5f" + "\6c\65\61\76\65\73\5f\74\6f\5f\61\6e\79\73\00\06" + "\16\69\6e\69\74\5f\65\6c\65\6d\5f\61\6e\79\73\5f" + "\74\6f\5f\61\6e\79\73\00\07\0c\6e\65\77\5f\65\6c" + "\65\6d\5f\6f\6f\62\00\08\11\69\6e\69\74\5f\65\6c" + "\65\6d\5f\64\73\74\5f\6f\6f\62\00\09\11\69\6e\69" + "\74\5f\65\6c\65\6d\5f\73\72\63\5f\6f\6f\62\00\0a" + "\09\a2\80\80\80\00\02\05\64\00\02\41\01\fb\00\00" + "\0b\41\02\fb\00\00\0b\05\6e\03\41\03\fb\1c\0b\41" + "\04\fb\00\00\0b\d0\6e\0b\0a\80\83\80\80\00\0b\a8" + "\80\80\80\00\00\20\00\d1\04\7f\41\7f\05\20\00\fb" + "\14\6c\04\7f\20\00\fb\16\6c\fb\1d\05\41\e8\07\20" + "\00\fb\16\00\fb\02\00\00\6a\0b\0b\0b\a6\80\80\80" + "\00\00\20\00\41\00\fb\0b\01\10\00\20\00\41\01\fb" + "\0b\01\10\00\20\00\41\02\fb\0b\01\10\00\20\00\41" + "\03\fb\0b\01\10\00\0b\a1\80\80\80\00\01\01\64\01" + "\41\00\41\02\fb\0a\01\00\21\00\20\00\41\00\fb\0b" + "\01\10\00\20\00\41\01\fb\0b\01\10\00\0b\a1\80\80" + "\80\00\01\01\64\02\41\00\41\02\fb\0a\02\00\21\00" + "\20\00\41\00\fb\0b\02\10\00\20\00\41\01\fb\0b\02" + "\10\00\0b\a1\80\80\80\00\01\01\64\03\41\00\41\02" + "\fb\0a\04\00\21\00\20\00\41\00\fb\0b\03\10\00\20" + "\00\41\01\fb\0b\03\10\00\0b\aa\80\80\80\00\01\01" + "\64\01\41\00\41\03\fb\0a\01\01\21\00\20\00\41\00" + "\fb\0b\01\10\00\20\00\41\01\fb\0b\01\10\00\20\00" + "\41\02\fb\0b\01\10\00\0b\a0\80\80\80\00\01\01\64" + "\01\41\09\fb\1c\41\04\fb\06\01\21\00\20\00\41\01" + "\41\00\41\02\fb\13\01\00\20\00\10\01\0b\a0\80\80" + "\80\00\01\01\64\01\41\09\fb\1c\41\04\fb\06\01\21" + "\00\20\00\41\01\41\01\41\02\fb\13\01\01\20\00\10" + "\01\0b\8b\80\80\80\00\00\41\02\41\02\fb\0a\01\01" + "\1a\0b\91\80\80\80\00\00\41\04\fb\07\01\41\03\41" + "\00\41\02\fb\13\01\00\0b\91\80\80\80\00\00\41\04" + "\fb\07\01\41\00\41\01\41\02\fb\13\01\00\0b" +) +(module instance) +(assert_return + (invoke "new_elem_leaves_to_anys") + (i32.const 0x3e9) + (i32.const 0x3ea) +) +(assert_return + (invoke "new_elem_leaves_to_structs") + (i32.const 0x3e9) + (i32.const 0x3ea) +) +(assert_return + (invoke "new_elem_leaves_to_leafsub") + (i32.const 0x3e9) + (i32.const 0x3ea) +) +(assert_return + (invoke "new_elem_anys") + (i32.const 0x3) + (i32.const 0x3ec) + (i32.const 0xffff_ffff) +) +(assert_return + (invoke "init_elem_leaves_to_anys") + (i32.const 0x9) + (i32.const 0x3e9) + (i32.const 0x3ea) + (i32.const 0x9) +) +(assert_return + (invoke "init_elem_anys_to_anys") + (i32.const 0x9) + (i32.const 0x3ec) + (i32.const 0xffff_ffff) + (i32.const 0x9) +) +(assert_trap (invoke "new_elem_oob") "out of bounds table access") +(assert_trap (invoke "init_elem_dst_oob") "out of bounds array access") +(assert_trap (invoke "init_elem_src_oob") "out of bounds table access") diff --git a/test/regress/ext:gc/array-init-subtype0.wast b/test/regress/ext:gc/array-init-subtype0.wast new file mode 100644 index 000000000..ff7ad55bd --- /dev/null +++ b/test/regress/ext:gc/array-init-subtype0.wast @@ -0,0 +1,79 @@ +;;! gc = true + +;; array.new_elem and array.init_elem from element segments whose types are subtypes of the +;; array element type, into arrays with different representations. +(module + (type $leaf (struct (field i32))) + (type $anys (array (mut anyref))) + (type $structs (array (mut structref))) + (type $anysup (sub (array anyref))) + (type $leafsub (sub $anysup (array (ref $leaf)))) + (elem $eleaves (ref $leaf) (item (struct.new $leaf (i32.const 1))) (item (struct.new $leaf (i32.const 2)))) + (elem $eanys anyref (item (ref.i31 (i32.const 3))) (item (struct.new $leaf (i32.const 4))) (item (ref.null any))) + + ;; Encodes an anyref as an i32: null -> -1, i31 -> its value, $leaf -> 1000 + its field. + (func $val (param anyref) (result i32) + (if (result i32) (ref.is_null (local.get 0)) + (then (i32.const -1)) + (else + (if (result i32) (ref.test (ref i31) (local.get 0)) + (then (i31.get_s (ref.cast (ref i31) (local.get 0)))) + (else (i32.add (i32.const 1000) (struct.get $leaf 0 (ref.cast (ref $leaf) (local.get 0))))))))) + (func $anys4 (param $a (ref $anys)) (result i32 i32 i32 i32) + (call $val (array.get $anys (local.get $a) (i32.const 0))) + (call $val (array.get $anys (local.get $a) (i32.const 1))) + (call $val (array.get $anys (local.get $a) (i32.const 2))) + (call $val (array.get $anys (local.get $a) (i32.const 3)))) + + ;; Struct references from a segment into paired anyref storage. + (func (export "new_elem_leaves_to_anys") (result i32 i32) + (local $a (ref $anys)) + (local.set $a (array.new_elem $anys $eleaves (i32.const 0) (i32.const 2))) + (call $val (array.get $anys (local.get $a) (i32.const 0))) + (call $val (array.get $anys (local.get $a) (i32.const 1)))) + ;; Struct references from a segment into plain structref storage. + (func (export "new_elem_leaves_to_structs") (result i32 i32) + (local $a (ref $structs)) + (local.set $a (array.new_elem $structs $eleaves (i32.const 0) (i32.const 2))) + (call $val (array.get $structs (local.get $a) (i32.const 0))) + (call $val (array.get $structs (local.get $a) (i32.const 1)))) + ;; Struct references from a segment into a subtype array that shares its anyref supertype's + ;; representation, read back through the supertype. + (func (export "new_elem_leaves_to_leafsub") (result i32 i32) + (local $a (ref $anysup)) + (local.set $a (array.new_elem $leafsub $eleaves (i32.const 0) (i32.const 2))) + (call $val (array.get $anysup (local.get $a) (i32.const 0))) + (call $val (array.get $anysup (local.get $a) (i32.const 1)))) + ;; A mix of i31s, references, and null from a segment. + (func (export "new_elem_anys") (result i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (array.new_elem $anys $eanys (i32.const 0) (i32.const 3))) + (call $val (array.get $anys (local.get $a) (i32.const 0))) + (call $val (array.get $anys (local.get $a) (i32.const 1))) + (call $val (array.get $anys (local.get $a) (i32.const 2)))) + (func (export "init_elem_leaves_to_anys") (result i32 i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (array.new $anys (ref.i31 (i32.const 9)) (i32.const 4))) + (array.init_elem $anys $eleaves (local.get $a) (i32.const 1) (i32.const 0) (i32.const 2)) + (call $anys4 (local.get $a))) + (func (export "init_elem_anys_to_anys") (result i32 i32 i32 i32) + (local $a (ref $anys)) + (local.set $a (array.new $anys (ref.i31 (i32.const 9)) (i32.const 4))) + (array.init_elem $anys $eanys (local.get $a) (i32.const 1) (i32.const 1) (i32.const 2)) + (call $anys4 (local.get $a))) + (func (export "new_elem_oob") + (drop (array.new_elem $anys $eanys (i32.const 2) (i32.const 2)))) + (func (export "init_elem_dst_oob") + (array.init_elem $anys $eleaves (array.new_default $anys (i32.const 4)) (i32.const 3) (i32.const 0) (i32.const 2))) + (func (export "init_elem_src_oob") + (array.init_elem $anys $eleaves (array.new_default $anys (i32.const 4)) (i32.const 0) (i32.const 1) (i32.const 2))) +) +(assert_return (invoke "new_elem_leaves_to_anys") (i32.const 1001) (i32.const 1002)) +(assert_return (invoke "new_elem_leaves_to_structs") (i32.const 1001) (i32.const 1002)) +(assert_return (invoke "new_elem_leaves_to_leafsub") (i32.const 1001) (i32.const 1002)) +(assert_return (invoke "new_elem_anys") (i32.const 3) (i32.const 1004) (i32.const -1)) +(assert_return (invoke "init_elem_leaves_to_anys") (i32.const 9) (i32.const 1001) (i32.const 1002) (i32.const 9)) +(assert_return (invoke "init_elem_anys_to_anys") (i32.const 9) (i32.const 1004) (i32.const -1) (i32.const 9)) +(assert_trap (invoke "new_elem_oob") "out of bounds table access") +(assert_trap (invoke "init_elem_dst_oob") "out of bounds array access") +(assert_trap (invoke "init_elem_src_oob") "out of bounds table access") diff --git a/test/regress/ext:gc/array-subtype-narrow0.bin.wast b/test/regress/ext:gc/array-subtype-narrow0.bin.wast new file mode 100644 index 000000000..97a022c79 --- /dev/null +++ b/test/regress/ext:gc/array-subtype-narrow0.bin.wast @@ -0,0 +1,31 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\af\80\80\80\00\09\5f" + "\01\7f\00\50\00\5e\6e\00\50\01\01\5e\64\00\00\50" + "\00\5e\6d\00\50\01\03\5e\6c\00\60\00\01\64\02\60" + "\00\01\7f\60\01\7f\01\7f\60\00\02\7f\7f\03\88\80" + "\80\80\00\07\05\06\07\07\07\06\08\07\db\80\80\80" + "\00\06\0f\73\75\62\5f\6c\65\6e\5f\76\69\61\5f\73" + "\75\70\00\01\0f\73\75\62\5f\67\65\74\5f\76\69\61" + "\5f\73\75\70\00\02\0e\73\75\62\5f\67\65\74\5f\69" + "\73\5f\69\33\31\00\03\07\73\75\70\5f\69\33\31\00" + "\04\08\73\75\70\5f\6e\75\6c\6c\00\05\0d\65\73\75" + "\62\5f\76\69\61\5f\65\73\75\70\00\06\0a\9d\81\80" + "\80\00\07\90\80\80\80\00\00\41\01\fb\00\00\41\02" + "\fb\00\00\fb\08\02\02\0b\86\80\80\80\00\00\10\00" + "\fb\0f\0b\90\80\80\80\00\00\10\00\20\00\fb\0b\01" + "\fb\16\00\fb\02\00\00\0b\8c\80\80\80\00\00\10\00" + "\20\00\fb\0b\01\fb\14\6c\0b\95\80\80\80\00\00\20" + "\00\fb\1c\41\03\fb\06\01\41\02\fb\0b\01\fb\16\6c" + "\fb\1e\0b\8d\80\80\80\00\00\41\03\fb\07\01\41\01" + "\fb\0b\01\d1\0b\a5\80\80\80\00\01\01\64\03\41\03" + "\fb\1c\d0\6c\fb\08\04\02\21\00\20\00\41\00\fb\0b" + "\03\fb\16\6c\fb\1e\20\00\41\01\fb\0b\03\d1\0b" +) +(module instance) +(assert_return (invoke "sub_len_via_sup") (i32.const 0x2)) +(assert_return (invoke "sub_get_via_sup" (i32.const 0x0)) (i32.const 0x1)) +(assert_return (invoke "sub_get_via_sup" (i32.const 0x1)) (i32.const 0x2)) +(assert_return (invoke "sub_get_is_i31" (i32.const 0x0)) (i32.const 0x0)) +(assert_return (invoke "sup_i31" (i32.const 0x3e8)) (i32.const 0x3e8)) +(assert_return (invoke "sup_null") (i32.const 0x1)) +(assert_return (invoke "esub_via_esup") (i32.const 0x3) (i32.const 0x1)) diff --git a/test/regress/ext:gc/array-subtype-narrow0.wast b/test/regress/ext:gc/array-subtype-narrow0.wast new file mode 100644 index 000000000..3a9b06374 --- /dev/null +++ b/test/regress/ext:gc/array-subtype-narrow0.wast @@ -0,0 +1,36 @@ +;;! gc = true + +;; A subtype narrows an immutable anyref element type (which may hold an i31) to a struct +;; reference (which cannot). Elements must be accessible through the supertype. +(module + (type $leaf (struct (field i32))) + (type $asup (sub (array anyref))) + (type $asub (sub $asup (array (ref $leaf)))) + (type $esup (sub (array eqref))) + (type $esub (sub $esup (array i31ref))) + + (func $new_sub (result (ref $asub)) + (array.new_fixed $asub 2 (struct.new $leaf (i32.const 1)) (struct.new $leaf (i32.const 2)))) + + (func (export "sub_len_via_sup") (result i32) (array.len (call $new_sub))) + (func (export "sub_get_via_sup") (param i32) (result i32) + (struct.get $leaf 0 (ref.cast (ref $leaf) (array.get $asup (call $new_sub) (local.get 0))))) + (func (export "sub_get_is_i31") (param i32) (result i32) + (ref.test (ref i31) (array.get $asup (call $new_sub) (local.get 0)))) + (func (export "sup_i31") (param i32) (result i32) + (i31.get_u (ref.cast (ref i31) (array.get $asup (array.new $asup (ref.i31 (local.get 0)) (i32.const 3)) (i32.const 2))))) + (func (export "sup_null") (result i32) + (ref.is_null (array.get $asup (array.new_default $asup (i32.const 3)) (i32.const 1)))) + (func (export "esub_via_esup") (result i32 i32) + (local $a (ref $esup)) + (local.set $a (array.new_fixed $esub 2 (ref.i31 (i32.const 3)) (ref.null i31))) + (i31.get_u (ref.cast (ref i31) (array.get $esup (local.get $a) (i32.const 0)))) + (ref.is_null (array.get $esup (local.get $a) (i32.const 1)))) +) +(assert_return (invoke "sub_len_via_sup") (i32.const 2)) +(assert_return (invoke "sub_get_via_sup" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "sub_get_via_sup" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "sub_get_is_i31" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "sup_i31" (i32.const 1000)) (i32.const 1000)) +(assert_return (invoke "sup_null") (i32.const 1)) +(assert_return (invoke "esub_via_esup") (i32.const 3) (i32.const 1)) diff --git a/test/regress/ext:gc/build.sh b/test/regress/ext:gc/build.sh index 414d10aec..8b1aae3f6 100755 --- a/test/regress/ext:gc/build.sh +++ b/test/regress/ext:gc/build.sh @@ -9,7 +9,7 @@ done HERE="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" export WIZENG_LOC=$(cd $HERE/../../../ && pwd) -export SPEC_LOC=${SPEC_LOC:=$(cd $WIZENG_LOC/wasm-spec/repos/gc && pwd)} +export SPEC_LOC=${SPEC_LOC:=$(cd $WIZENG_LOC/wasm-spec/repos/spec && pwd)} if [ ! -d $SPEC_LOC ]; then echo "WebAssembly specification repo not found: $SPEC_LOC" diff --git a/test/regress/ext:gc/struct-subtype-narrow0.bin.wast b/test/regress/ext:gc/struct-subtype-narrow0.bin.wast new file mode 100644 index 000000000..7b011784b --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow0.bin.wast @@ -0,0 +1,43 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\b3\80\80\80\00\08\5f" + "\01\7f\00\50\00\5f\03\6e\00\7e\00\7f\01\50\01\01" + "\5f\03\64\00\00\7e\00\7f\01\60\00\01\64\02\60\00" + "\01\64\01\60\00\01\7f\60\00\01\7e\60\00\03\7f\7e" + "\7f\03\8b\80\80\80\00\0a\03\04\05\05\06\05\05\06" + "\05\07\07\80\81\80\80\00\08\10\73\75\62\5f\67\65" + "\74\30\5f\76\69\61\5f\73\75\70\00\02\0f\73\75\62" + "\5f\67\65\74\30\5f\69\73\5f\69\33\31\00\03\10\73" + "\75\62\5f\67\65\74\31\5f\76\69\61\5f\73\75\70\00" + "\04\10\73\75\62\5f\67\65\74\32\5f\76\69\61\5f\73" + "\75\70\00\05\08\73\75\70\5f\67\65\74\30\00\06\08" + "\73\75\70\5f\67\65\74\31\00\07\08\73\75\70\5f\67" + "\65\74\32\00\08\10\73\75\62\5f\73\65\74\32\5f\76" + "\69\61\5f\73\75\70\00\09\0a\ce\81\80\80\00\0a\97" + "\80\80\80\00\00\41\07\fb\00\00\42\88\ef\99\ab\c5" + "\e8\8c\91\11\41\e3\00\fb\00\02\0b\96\80\80\80\00" + "\00\41\2a\fb\1c\42\88\ef\99\ab\c5\e8\8c\91\11\41" + "\e3\00\fb\00\01\0b\8f\80\80\80\00\00\10\00\fb\02" + "\01\00\fb\16\00\fb\02\00\00\0b\8b\80\80\80\00\00" + "\10\00\fb\02\01\00\fb\14\6c\0b\88\80\80\80\00\00" + "\10\00\fb\02\01\01\0b\88\80\80\80\00\00\10\00\fb" + "\02\01\02\0b\8d\80\80\80\00\00\10\01\fb\02\01\00" + "\fb\16\6c\fb\1d\0b\88\80\80\80\00\00\10\01\fb\02" + "\01\01\0b\88\80\80\80\00\00\10\01\fb\02\01\02\0b" + "\a7\80\80\80\00\01\01\64\02\10\00\21\00\20\00\41" + "\7b\fb\05\01\02\20\00\fb\02\02\02\20\00\fb\02\02" + "\01\20\00\fb\02\02\00\fb\02\00\00\0b" +) +(module instance) +(assert_return (invoke "sub_get0_via_sup") (i32.const 0x7)) +(assert_return (invoke "sub_get0_is_i31") (i32.const 0x0)) +(assert_return (invoke "sub_get1_via_sup") (i64.const 0x1122_3344_5566_7788)) +(assert_return (invoke "sub_get2_via_sup") (i32.const 0x63)) +(assert_return (invoke "sup_get0") (i32.const 0x2a)) +(assert_return (invoke "sup_get1") (i64.const 0x1122_3344_5566_7788)) +(assert_return (invoke "sup_get2") (i32.const 0x63)) +(assert_return + (invoke "sub_set2_via_sup") + (i32.const 0xffff_fffb) + (i64.const 0x1122_3344_5566_7788) + (i32.const 0x7) +) diff --git a/test/regress/ext:gc/struct-subtype-narrow0.wast b/test/regress/ext:gc/struct-subtype-narrow0.wast new file mode 100644 index 000000000..9c4afe453 --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow0.wast @@ -0,0 +1,46 @@ +;;! gc = true + +;; A subtype narrows an immutable anyref field (which may hold an i31) to a struct reference +;; (which cannot). The fields following it must have the same layout in both types, so that +;; accesses through the supertype see the right values in a subtype instance. +(module + (type $leaf (struct (field i32))) + (type $sup (sub (struct (field anyref) (field i64) (field (mut i32))))) + (type $sub (sub $sup (struct (field (ref $leaf)) (field i64) (field (mut i32))))) + + (func $new_sub (result (ref $sub)) + (struct.new $sub (struct.new $leaf (i32.const 7)) (i64.const 0x1122334455667788) (i32.const 99))) + (func $new_sup (result (ref $sup)) + (struct.new $sup (ref.i31 (i32.const 42)) (i64.const 0x1122334455667788) (i32.const 99))) + + ;; Access the fields of a subtype instance through the supertype. + (func (export "sub_get0_via_sup") (result i32) + (struct.get $leaf 0 (ref.cast (ref $leaf) (struct.get $sup 0 (call $new_sub))))) + (func (export "sub_get0_is_i31") (result i32) + (ref.test (ref i31) (struct.get $sup 0 (call $new_sub)))) + (func (export "sub_get1_via_sup") (result i64) (struct.get $sup 1 (call $new_sub))) + (func (export "sub_get2_via_sup") (result i32) (struct.get $sup 2 (call $new_sub))) + + ;; Access the fields of a supertype instance. + (func (export "sup_get0") (result i32) + (i31.get_s (ref.cast (ref i31) (struct.get $sup 0 (call $new_sup))))) + (func (export "sup_get1") (result i64) (struct.get $sup 1 (call $new_sup))) + (func (export "sup_get2") (result i32) (struct.get $sup 2 (call $new_sup))) + + ;; Write through the supertype, read through the subtype. + (func (export "sub_set2_via_sup") (result i32 i64 i32) + (local $o (ref $sub)) + (local.set $o (call $new_sub)) + (struct.set $sup 2 (local.get $o) (i32.const -5)) + (struct.get $sub 2 (local.get $o)) + (struct.get $sub 1 (local.get $o)) + (struct.get $leaf 0 (struct.get $sub 0 (local.get $o)))) +) +(assert_return (invoke "sub_get0_via_sup") (i32.const 7)) +(assert_return (invoke "sub_get0_is_i31") (i32.const 0)) +(assert_return (invoke "sub_get1_via_sup") (i64.const 0x1122334455667788)) +(assert_return (invoke "sub_get2_via_sup") (i32.const 99)) +(assert_return (invoke "sup_get0") (i32.const 42)) +(assert_return (invoke "sup_get1") (i64.const 0x1122334455667788)) +(assert_return (invoke "sup_get2") (i32.const 99)) +(assert_return (invoke "sub_set2_via_sup") (i32.const -5) (i64.const 0x1122334455667788) (i32.const 7)) diff --git a/test/regress/ext:gc/struct-subtype-narrow1.bin.wast b/test/regress/ext:gc/struct-subtype-narrow1.bin.wast new file mode 100644 index 000000000..068442c7b --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow1.bin.wast @@ -0,0 +1,64 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\e0\80\80\80\00\0a\5f" + "\01\7f\00\50\00\5f\05\78\00\6e\00\77\00\6d\00\7c" + "\00\50\01\01\5f\05\78\00\6d\00\77\00\6c\00\7c\00" + "\50\01\02\5f\07\78\00\64\00\00\77\00\64\6c\00\7c" + "\00\6e\00\7f\01\60\00\01\64\03\60\00\01\64\02\60" + "\00\07\7f\7f\7f\7f\7f\7f\7c\60\00\04\7f\7f\7f\7c" + "\60\00\03\7f\7f\7f\60\00\05\7f\7f\7f\7f\7c\03\87" + "\80\80\80\00\06\04\05\06\07\08\09\07\b3\80\80\80" + "\00\04\09\74\32\5f\76\69\61\5f\74\30\00\02\09\74" + "\32\5f\76\69\61\5f\74\31\00\03\0b\74\32\5f\61\70" + "\70\65\6e\64\65\64\00\04\09\74\31\5f\76\69\61\5f" + "\74\30\00\05\0a\9a\82\80\80\00\06\a5\80\80\80\00" + "\00\41\81\01\41\07\fb\00\00\41\81\80\02\41\05\fb" + "\1c\44\00\00\00\00\00\00\0c\40\41\09\fb\1c\41\cd" + "\00\fb\00\03\0b\9b\80\80\80\00\00\41\ff\00\41\0b" + "\fb\1c\41\ff\ff\01\d0\6c\44\00\00\00\00\00\00\f4" + "\bf\fb\00\02\0b\bf\80\80\80\00\01\01\64\01\10\00" + "\21\00\20\00\fb\03\01\00\20\00\fb\04\01\00\20\00" + "\fb\02\01\01\fb\16\00\fb\02\00\00\20\00\fb\03\01" + "\02\20\00\fb\04\01\02\20\00\fb\02\01\03\fb\16\6c" + "\fb\1e\20\00\fb\02\01\04\0b\a6\80\80\80\00\01\01" + "\64\02\10\00\21\00\20\00\fb\02\02\01\fb\14\6c\20" + "\00\fb\04\02\02\20\00\fb\02\02\03\fb\1e\20\00\fb" + "\02\02\04\0b\a9\80\80\80\00\01\01\64\03\10\00\21" + "\00\20\00\fb\02\03\05\fb\16\6c\fb\1e\20\00\fb\02" + "\03\06\20\00\41\b3\7f\fb\05\03\06\20\00\fb\02\03" + "\06\0b\ad\80\80\80\00\01\01\64\01\10\01\21\00\20" + "\00\fb\03\01\00\20\00\fb\02\01\01\fb\16\6c\fb\1e" + "\20\00\fb\03\01\02\20\00\fb\02\01\03\d1\20\00\fb" + "\02\01\04\0b" +) +(module instance) +(assert_return + (invoke "t2_via_t0") + (i32.const 0xffff_ff81) + (i32.const 0x81) + (i32.const 0x7) + (i32.const 0xffff_8001) + (i32.const 0x8001) + (i32.const 0x5) + (f64.const 0x1.cp+1) +) +(assert_return + (invoke "t2_via_t1") + (i32.const 0x0) + (i32.const 0x8001) + (i32.const 0x5) + (f64.const 0x1.cp+1) +) +(assert_return + (invoke "t2_appended") + (i32.const 0x9) + (i32.const 0x4d) + (i32.const 0xffff_ffb3) +) +(assert_return + (invoke "t1_via_t0") + (i32.const 0x7f) + (i32.const 0xb) + (i32.const 0x7fff) + (i32.const 0x1) + (f64.const -0x1.4p+0) +) diff --git a/test/regress/ext:gc/struct-subtype-narrow1.wast b/test/regress/ext:gc/struct-subtype-narrow1.wast new file mode 100644 index 000000000..d96c0b13e --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow1.wast @@ -0,0 +1,73 @@ +;;! gc = true + +;; A three-level hierarchy that narrows reference fields at each level, interleaved with +;; packed and unpacked primitive fields, and appends new fields in the deepest subtype. +(module + (type $leaf (struct (field i32))) + (type $t0 (sub (struct (field i8) (field anyref) (field i16) (field eqref) (field f64)))) + (type $t1 (sub $t0 (struct (field i8) (field eqref) (field i16) (field i31ref) (field f64)))) + (type $t2 (sub $t1 (struct (field i8) (field (ref $leaf)) (field i16) (field (ref i31)) (field f64) + (field anyref) (field (mut i32))))) + + (func $new_t2 (result (ref $t2)) + (struct.new $t2 + (i32.const 0x81) + (struct.new $leaf (i32.const 7)) + (i32.const 0x8001) + (ref.i31 (i32.const 5)) + (f64.const 3.5) + (ref.i31 (i32.const 9)) + (i32.const 77))) + (func $new_t1 (result (ref $t1)) + (struct.new $t1 + (i32.const 0x7f) + (ref.i31 (i32.const 11)) + (i32.const 0x7fff) + (ref.null i31) + (f64.const -1.25))) + + ;; Read a $t2 instance through $t0. + (func (export "t2_via_t0") (result i32 i32 i32 i32 i32 i32 f64) + (local $o (ref $t0)) + (local.set $o (call $new_t2)) + (struct.get_s $t0 0 (local.get $o)) + (struct.get_u $t0 0 (local.get $o)) + (struct.get $leaf 0 (ref.cast (ref $leaf) (struct.get $t0 1 (local.get $o)))) + (struct.get_s $t0 2 (local.get $o)) + (struct.get_u $t0 2 (local.get $o)) + (i31.get_u (ref.cast (ref i31) (struct.get $t0 3 (local.get $o)))) + (struct.get $t0 4 (local.get $o))) + + ;; Read a $t2 instance through $t1. + (func (export "t2_via_t1") (result i32 i32 i32 f64) + (local $o (ref $t1)) + (local.set $o (call $new_t2)) + (ref.test (ref i31) (struct.get $t1 1 (local.get $o))) + (struct.get_u $t1 2 (local.get $o)) + (i31.get_u (struct.get $t1 3 (local.get $o))) + (struct.get $t1 4 (local.get $o))) + + ;; Read the appended fields of a $t2 instance, after writing through $t2. + (func (export "t2_appended") (result i32 i32 i32) + (local $o (ref $t2)) + (local.set $o (call $new_t2)) + (i31.get_u (ref.cast (ref i31) (struct.get $t2 5 (local.get $o)))) + (struct.get $t2 6 (local.get $o)) + (struct.set $t2 6 (local.get $o) (i32.const -77)) + (struct.get $t2 6 (local.get $o))) + + ;; Read a $t1 instance, whose narrowed fields hold i31s and null, through $t0. + (func (export "t1_via_t0") (result i32 i32 i32 i32 f64) + (local $o (ref $t0)) + (local.set $o (call $new_t1)) + (struct.get_s $t0 0 (local.get $o)) + (i31.get_u (ref.cast (ref i31) (struct.get $t0 1 (local.get $o)))) + (struct.get_s $t0 2 (local.get $o)) + (ref.is_null (struct.get $t0 3 (local.get $o))) + (struct.get $t0 4 (local.get $o))) +) +(assert_return (invoke "t2_via_t0") + (i32.const -127) (i32.const 129) (i32.const 7) (i32.const -32767) (i32.const 32769) (i32.const 5) (f64.const 3.5)) +(assert_return (invoke "t2_via_t1") (i32.const 0) (i32.const 32769) (i32.const 5) (f64.const 3.5)) +(assert_return (invoke "t2_appended") (i32.const 9) (i32.const 77) (i32.const -77)) +(assert_return (invoke "t1_via_t0") (i32.const 127) (i32.const 11) (i32.const 32767) (i32.const 1) (f64.const -1.25)) diff --git a/test/regress/ext:gc/struct-subtype-narrow2.bin.wast b/test/regress/ext:gc/struct-subtype-narrow2.bin.wast new file mode 100644 index 000000000..2e66b0ca0 --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow2.bin.wast @@ -0,0 +1,15 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\97\80\80\80\00\03\50" + "\00\5f\02\6f\00\7f\00\50\01\00\5f\02\72\00\7f\00" + "\60\00\02\7f\7f\03\83\80\80\80\00\02\02\02\07\99" + "\80\80\80\00\02\0b\73\75\62\5f\76\69\61\5f\73\75" + "\70\00\00\07\73\75\70\5f\69\33\31\00\01\0a\cb\80" + "\80\80\00\02\9b\80\80\80\00\01\01\64\01\d0\72\41" + "\0d\fb\00\01\21\00\20\00\fb\02\00\00\d1\20\00\fb" + "\02\00\01\0b\a5\80\80\80\00\01\01\64\00\41\15\fb" + "\1c\fb\1b\41\0d\fb\00\00\21\00\20\00\fb\02\00\00" + "\fb\1a\fb\16\6c\fb\1e\20\00\fb\02\00\01\0b" +) +(module instance) +(assert_return (invoke "sub_via_sup") (i32.const 0x1) (i32.const 0xd)) +(assert_return (invoke "sup_i31") (i32.const 0x15) (i32.const 0xd)) diff --git a/test/regress/ext:gc/struct-subtype-narrow2.wast b/test/regress/ext:gc/struct-subtype-narrow2.wast new file mode 100644 index 000000000..c73fffcb0 --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow2.wast @@ -0,0 +1,20 @@ +;;! gc = true + +;; A subtype narrows an externref field (which may hold a converted i31) to nullexternref. +(module + (type $sup (sub (struct (field externref) (field i32)))) + (type $sub (sub $sup (struct (field nullexternref) (field i32)))) + + (func (export "sub_via_sup") (result i32 i32) + (local $o (ref $sub)) + (local.set $o (struct.new $sub (ref.null noextern) (i32.const 13))) + (ref.is_null (struct.get $sup 0 (local.get $o))) + (struct.get $sup 1 (local.get $o))) + (func (export "sup_i31") (result i32 i32) + (local $o (ref $sup)) + (local.set $o (struct.new $sup (extern.convert_any (ref.i31 (i32.const 21))) (i32.const 13))) + (i31.get_u (ref.cast (ref i31) (any.convert_extern (struct.get $sup 0 (local.get $o))))) + (struct.get $sup 1 (local.get $o))) +) +(assert_return (invoke "sub_via_sup") (i32.const 1) (i32.const 13)) +(assert_return (invoke "sup_i31") (i32.const 21) (i32.const 13)) diff --git a/test/regress/ext:gc/struct-subtype-narrow3.bin.wast b/test/regress/ext:gc/struct-subtype-narrow3.bin.wast new file mode 100644 index 000000000..1a0efe79a --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow3.bin.wast @@ -0,0 +1,56 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\c9\80\80\80\00\0b\5f" + "\01\7f\00\50\00\5f\03\6e\00\7e\00\7f\01\50\01\01" + "\5f\03\64\00\00\7e\00\7f\01\50\00\5e\6e\00\50\01" + "\03\5e\64\00\00\60\01\6e\01\7f\60\01\64\01\03\7f" + "\7e\7f\60\00\03\7f\7e\7f\60\01\7f\01\7f\60\00\00" + "\60\01\7f\03\7f\7e\7f\03\88\80\80\80\00\07\05\06" + "\07\07\08\09\0a\04\85\80\80\80\00\01\63\01\00\02" + "\06\bc\80\80\80\00\03\64\01\00\41\07\fb\00\00\42" + "\88\ef\99\ab\c5\e8\8c\91\11\41\e3\00\fb\00\02\0b" + "\64\01\00\41\2a\fb\1c\42\7f\41\e2\00\fb\00\01\0b" + "\64\03\00\41\01\fb\00\00\41\02\fb\00\00\fb\08\04" + "\02\0b\07\a5\80\80\80\00\05\04\67\73\75\62\00\02" + "\04\67\73\75\70\00\03\04\67\61\72\72\00\04\04\69" + "\6e\69\74\00\05\05\74\61\62\6c\65\00\06\09\9e\80" + "\80\80\00\01\05\64\01\02\41\08\fb\00\00\42\05\41" + "\06\fb\00\02\0b\41\09\fb\1c\42\0a\41\0b\fb\00\01" + "\0b\0a\8e\81\80\80\00\07\a8\80\80\80\00\00\20\00" + "\d1\04\7f\41\7f\05\20\00\fb\14\6c\04\7f\20\00\fb" + "\16\6c\fb\1d\05\41\e8\07\20\00\fb\16\00\fb\02\00" + "\00\6a\0b\0b\0b\96\80\80\80\00\00\20\00\fb\02\01" + "\00\10\00\20\00\fb\02\01\01\20\00\fb\02\01\02\0b" + "\86\80\80\80\00\00\23\00\10\01\0b\86\80\80\80\00" + "\00\23\01\10\01\0b\8b\80\80\80\00\00\23\02\20\00" + "\fb\0b\03\10\00\0b\8c\80\80\80\00\00\41\00\41\00" + "\41\02\fc\0c\00\00\0b\89\80\80\80\00\00\20\00\25" + "\00\d4\10\01\0b" +) +(module instance) +(assert_return + (invoke "gsub") + (i32.const 0x3ef) + (i64.const 0x1122_3344_5566_7788) + (i32.const 0x63) +) +(assert_return + (invoke "gsup") + (i32.const 0x2a) + (i64.const 0xffff_ffff_ffff_ffff) + (i32.const 0x62) +) +(assert_return (invoke "garr" (i32.const 0x0)) (i32.const 0x3e9)) +(assert_return (invoke "garr" (i32.const 0x1)) (i32.const 0x3ea)) +(assert_return (invoke "init")) +(assert_return + (invoke "table" (i32.const 0x0)) + (i32.const 0x3f0) + (i64.const 0x5) + (i32.const 0x6) +) +(assert_return + (invoke "table" (i32.const 0x1)) + (i32.const 0x9) + (i64.const 0xa) + (i32.const 0xb) +) diff --git a/test/regress/ext:gc/struct-subtype-narrow3.wast b/test/regress/ext:gc/struct-subtype-narrow3.wast new file mode 100644 index 000000000..b44f497e7 --- /dev/null +++ b/test/regress/ext:gc/struct-subtype-narrow3.wast @@ -0,0 +1,47 @@ +;;! gc = true + +;; Structs and arrays with narrowed subtypes created by constant expressions in globals and +;; element segments, accessed through their supertypes. +(module + (type $leaf (struct (field i32))) + (type $sup (sub (struct (field anyref) (field i64) (field (mut i32))))) + (type $sub (sub $sup (struct (field (ref $leaf)) (field i64) (field (mut i32))))) + (type $anysup (sub (array anyref))) + (type $leafsub (sub $anysup (array (ref $leaf)))) + + (global $gsub (ref $sup) (struct.new $sub (struct.new $leaf (i32.const 7)) (i64.const 0x1122334455667788) (i32.const 99))) + (global $gsup (ref $sup) (struct.new $sup (ref.i31 (i32.const 42)) (i64.const -1) (i32.const 98))) + (global $garr (ref $anysup) (array.new_fixed $leafsub 2 (struct.new $leaf (i32.const 1)) (struct.new $leaf (i32.const 2)))) + (elem $e (ref $sup) + (item (struct.new $sub (struct.new $leaf (i32.const 8)) (i64.const 5) (i32.const 6))) + (item (struct.new $sup (ref.i31 (i32.const 9)) (i64.const 10) (i32.const 11)))) + (table $t 2 (ref null $sup)) + + ;; Encodes an anyref as an i32: null -> -1, i31 -> its value, $leaf -> 1000 + its field. + (func $val (param anyref) (result i32) + (if (result i32) (ref.is_null (local.get 0)) + (then (i32.const -1)) + (else + (if (result i32) (ref.test (ref i31) (local.get 0)) + (then (i31.get_s (ref.cast (ref i31) (local.get 0)))) + (else (i32.add (i32.const 1000) (struct.get $leaf 0 (ref.cast (ref $leaf) (local.get 0))))))))) + (func $fields (param $o (ref $sup)) (result i32 i64 i32) + (call $val (struct.get $sup 0 (local.get $o))) + (struct.get $sup 1 (local.get $o)) + (struct.get $sup 2 (local.get $o))) + + (func (export "gsub") (result i32 i64 i32) (call $fields (global.get $gsub))) + (func (export "gsup") (result i32 i64 i32) (call $fields (global.get $gsup))) + (func (export "garr") (param i32) (result i32) + (call $val (array.get $anysup (global.get $garr) (local.get 0)))) + (func (export "init") (table.init $t $e (i32.const 0) (i32.const 0) (i32.const 2))) + (func (export "table") (param i32) (result i32 i64 i32) + (call $fields (ref.as_non_null (table.get $t (local.get 0))))) +) +(assert_return (invoke "gsub") (i32.const 1007) (i64.const 0x1122334455667788) (i32.const 99)) +(assert_return (invoke "gsup") (i32.const 42) (i64.const -1) (i32.const 98)) +(assert_return (invoke "garr" (i32.const 0)) (i32.const 1001)) +(assert_return (invoke "garr" (i32.const 1)) (i32.const 1002)) +(assert_return (invoke "init")) +(assert_return (invoke "table" (i32.const 0)) (i32.const 1008) (i64.const 5) (i32.const 6)) +(assert_return (invoke "table" (i32.const 1)) (i32.const 9) (i64.const 10) (i32.const 11)) diff --git a/test/wizeng/wizeng_puta0.wasm b/test/wizeng/wizeng_puta0.wasm new file mode 100644 index 0000000000000000000000000000000000000000..059fabdd73bb1b8e8519da499ac9b0f085238283 GIT binary patch literal 164 zcmZQbEY4+QU|?YEYiMX-V2-O`NMKH8sIO;8U|?YC2Z=GVm1kC^=B2X~l$IniFfuoR z1)118z%)y4VrCu#BTplk$F&tqtp}5gDGZKGj(op485k596c`*C9N7dwG@By_nC5e2 h0MlHKtO5+&+*2W%g!J@0Qgd?h6~e)`Dst)R0RZFHCAk0q literal 0 HcmV?d00001 diff --git a/test/wizeng/wizeng_puta0.wasm.flags b/test/wizeng/wizeng_puta0.wasm.flags new file mode 100644 index 000000000..53dfe50cf --- /dev/null +++ b/test/wizeng/wizeng_puta0.wasm.flags @@ -0,0 +1 @@ +--ext:wizeng --ext:gc diff --git a/test/wizeng/wizeng_puta0.wasm.out b/test/wizeng/wizeng_puta0.wasm.out new file mode 100644 index 000000000..76f68ae12 --- /dev/null +++ b/test/wizeng/wizeng_puta0.wasm.out @@ -0,0 +1,4 @@ +Hello Wizeng! + +44 + @ + !trap[ARRAY_OOB, when calling wizeng.puta()] diff --git a/test/wizeng/wizeng_puta0.wat b/test/wizeng/wizeng_puta0.wat new file mode 100644 index 000000000..151eb84f4 --- /dev/null +++ b/test/wizeng/wizeng_puta0.wat @@ -0,0 +1,14 @@ +(module + (type $arr_i8 (array i8)) + (import "wizeng" "puta" (func $puta (param (ref null $arr_i8) i32 i32))) + (data $data "..Hello Wizeng!\n..") + + (func $main (export "main") + (local $a (ref $arr_i8)) + (local.set $a (array.new_data $arr_i8 $data (i32.const 2) (i32.const 14))) + (call $puta (local.get $a) (i32.const 0) (i32.const 6)) ;; "Hello " + (call $puta (local.get $a) (i32.const 6) (i32.const 8)) ;; "Wizeng!\n" + (call $puta (local.get $a) (i32.const 14) (i32.const 0)) ;; empty range at the end + (call $puta (local.get $a) (i32.const 10) (i32.const 5)) ;; out of bounds: traps + ) +)