diff --git a/src/literal.h b/src/literal.h index 335e117c147..07352a6eab8 100644 --- a/src/literal.h +++ b/src/literal.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "support/bits.h" @@ -213,7 +214,11 @@ class Literal { } } - static Literal makeFromMemory(const void* p, Type type); + // Reads a value of the given type from memory. For i32, a smaller byteSize + // (1 or 2) can be given to read a sign-extended packed value. By default the + // full size of the type is read. + static Literal makeFromMemory( + const void* p, Type type, std::optional byteSize = std::nullopt); static Literal makeSignedMin(Type type) { switch (type.getBasic()) { diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index c4e50e8f5e0..49457b8b113 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2349,6 +2349,94 @@ struct OptimizeInstructions replaceCurrent(block); } + void visitArrayNewData(ArrayNewData* curr) { + // Lower array.new_data with constant operands to array.new_default, + // array.new or array.new_fixed. Unlike array.new_data, those are constant + // expressions, and more optimizations understand them. + if (curr->type == Type::unreachable) { + return; + } + + auto* offsetConst = curr->offset->dynCast(); + auto* sizeConst = curr->size->dynCast(); + if (!offsetConst || !sizeConst) { + return; + } + + auto* seg = getModule()->getDataSegment(curr->segment); + if (!seg->isPassive()) { + // Active segments are implicitly dropped upon module instantiation. + return; + } + + auto heapType = curr->type.getHeapType(); + const auto& element = heapType.getArray().element; + + Address offset = offsetConst->value.getUnsigned(); + Address size = sizeConst->value.getUnsigned(); + Address elemBytes = element.getByteSize(); + Address end; + if (std::ckd_add(&end.addr, offset, size * elemBytes) || + end > seg->data.size()) { + // This will trap. + return; + } + + // A dropped segment acts as if it had length 0, so unless end == 0 this + // traps if the segment was dropped at runtime, which we can't rule out. + // TODO: Optimize without TNH if the segment is never dropped. + if (end > 0 && !getPassOptions().trapsNeverHappen) { + return; + } + + Builder builder(*getModule()); + + if (size == 0) { + replaceCurrent(builder.makeArrayNewFixed(heapType, {})); + return; + } + + auto readElement = [&](Index i) -> Literal { + const auto* src = &seg->data[offset + i * elemBytes]; + // Packed values are truncated when stored, so the extension doesn't + // matter; sign-extending gives the shorter encoding (-1 vs 255). + return Literal::makeFromMemory(src, element.type, element.getByteSize()); + }; + + // If all elements are equal, array.new is compact at any size. + auto first = readElement(0); + bool allEqual = true; + for (Index i = 1; i < size; i++) { + if (readElement(i) != first) { + allEqual = false; + break; + } + } + if (allEqual) { + // visitArrayNew optimizes this further when the value is the default + // (to array.new_default) or the size is 1 (to array.new_fixed). + auto* sizeExpr = builder.makeConst(int32_t(size)); + replaceCurrent( + builder.makeArrayNew(heapType, sizeExpr, builder.makeConst(first))); + return; + } + + // array.new_fixed needs a full *.const per element while the segment + // stores raw bytes, so only do this for small arrays. Empirically, 64 sits + // in a natural gap between small/medium array literals (2D array rows, + // enum/switch tables, etc.) and large bulk lookup tables (100+ elements). + constexpr uint64_t MaxArrayNewFixedSize = 64; + if (size > MaxArrayNewFixedSize) { + return; + } + std::vector values; + values.reserve(size); + for (Index i = 0; i < size; i++) { + values.push_back(builder.makeConst(readElement(i))); + } + replaceCurrent(builder.makeArrayNewFixed(heapType, values)); + } + void visitArrayGet(ArrayGet* curr) { skipNonNullCast(curr->ref, curr); trapOnNull(curr, curr->ref); diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 272cc5a6404..5c15e502198 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -314,11 +314,23 @@ Literal Literal::makeNegOne(Type type) { return makeFromInt32(-1, type); } -Literal Literal::makeFromMemory(const void* p, Type type) { +Literal Literal::makeFromMemory(const void* p, + Type type, + std::optional byteSize) { assert(type.isNumber()); + auto size = byteSize.value_or(type.getByteSize()); + assert(size == type.getByteSize() || + (type == Type::i32 && (size == 1 || size == 2))); switch (type.getBasic()) { case Type::i32: { - return Literal(Bits::readLE(p)); + switch (size) { + case 1: + return Literal(int32_t(Bits::readLE(p))); + case 2: + return Literal(int32_t(Bits::readLE(p))); + default: + return Literal(Bits::readLE(p)); + } } case Type::i64: { return Literal(Bits::readLE(p)); diff --git a/test/lit/passes/optimize-instructions-array-new-data.wast b/test/lit/passes/optimize-instructions-array-new-data.wast new file mode 100644 index 00000000000..cafcbdc819b --- /dev/null +++ b/test/lit/passes/optimize-instructions-array-new-data.wast @@ -0,0 +1,796 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s --optimize-instructions --traps-never-happen -all -S -o - | filecheck %s --check-prefix TNH +;; RUN: wasm-opt %s --optimize-instructions -all -S -o - | filecheck %s --check-prefix NO_TNH + +(module + ;; TNH: (type $arr-i8 (array (mut i8))) + + ;; TNH: (type $arr-i32 (array (mut i32))) + + ;; TNH: (type $arr-f32 (array (mut f32))) + + ;; TNH: (type $arr-i16 (array (mut i16))) + + ;; TNH: (type $arr-i64 (array (mut i64))) + + ;; TNH: (type $arr-f64 (array (mut f64))) + + ;; TNH: (type $arr-v128 (array (mut v128))) + + ;; TNH: (import "a" "b" (func $get-i32 (type $8) (result i32))) + ;; NO_TNH: (type $arr-i8 (array (mut i8))) + + ;; NO_TNH: (type $arr-i32 (array (mut i32))) + + ;; NO_TNH: (type $arr-f32 (array (mut f32))) + + ;; NO_TNH: (type $arr-i16 (array (mut i16))) + + ;; NO_TNH: (type $arr-i64 (array (mut i64))) + + ;; NO_TNH: (type $arr-f64 (array (mut f64))) + + ;; NO_TNH: (type $arr-v128 (array (mut v128))) + + ;; NO_TNH: (import "a" "b" (func $get-i32 (type $8) (result i32))) + (import "a" "b" (func $get-i32 (result i32))) + + (type $arr-i8 (array (mut i8))) + (type $arr-i16 (array (mut i16))) + (type $arr-i32 (array (mut i32))) + (type $arr-i64 (array (mut i64))) + (type $arr-f32 (array (mut f32))) + (type $arr-f64 (array (mut f64))) + (type $arr-v128 (array (mut v128))) + + ;; TNH: (memory $mem 1) + ;; NO_TNH: (memory $mem 1) + (memory $mem 1) + ;; TNH: (data $active (i32.const 0) "abcd") + ;; NO_TNH: (data $active (i32.const 0) "abcd") + (data $active (i32.const 0) "abcd") + ;; TNH: (data $zeros "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + ;; NO_TNH: (data $zeros "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $zeros "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + ;; Uniform means all elements are equal when read at the element width. The + ;; first 4 bytes are identical i8s; the rest is not uniform as i8, but is as + ;; i16 (0x0201) and as i32 (0x02010201). + ;; TNH: (data $uniform "****\01\02\01\02\01\02\01\02") + ;; NO_TNH: (data $uniform "****\01\02\01\02\01\02\01\02") + (data $uniform "\2a\2a\2a\2a\01\02\01\02\01\02\01\02") + ;; TNH: (data $mixed "\01\ff\03\04\05\06\07\08\00\00\80?\00\00\00\00\00\00\f0?") + ;; NO_TNH: (data $mixed "\01\ff\03\04\05\06\07\08\00\00\80?\00\00\00\00\00\00\f0?") + (data $mixed "\01\ff\03\04\05\06\07\08\00\00\80\3f\00\00\00\00\00\00\f0\3f") + ;; TNH: (data $large "\01\02\03\04\05\06\07\08\t\n\0b\0c\r\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f !\"#$%&\'()*+,-./0123456789:;<=>?@A") + ;; NO_TNH: (data $large "\01\02\03\04\05\06\07\08\t\n\0b\0c\r\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f !\"#$%&\'()*+,-./0123456789:;<=>?@A") + (data $large "\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d\3e\3f\40\41") + ;; TNH: (data $large-zeros "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + ;; NO_TNH: (data $large-zeros "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $large-zeros "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + ;; TNH: (data $large-uniform "\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07") + ;; NO_TNH: (data $large-uniform "\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07") + (data $large-uniform "\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07\07") + ;; TNH: (data $floats "\00\00\00\80\00\00\00\80\00\00\c0\7f\00\00\c0\7f") + ;; NO_TNH: (data $floats "\00\00\00\80\00\00\00\80\00\00\c0\7f\00\00\c0\7f") + (data $floats "\00\00\00\80\00\00\00\80\00\00\c0\7f\00\00\c0\7f") + + ;; TNH: (func $array-new-data-empty (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i8 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i8 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-empty (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_fixed $arr-i8 0) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-empty + ;; Offset 0 and size 0 never traps (even if the segment was dropped), so it + ;; can be lowered to array.new_fixed 0 even without trapsNeverHappen. + (drop + (array.new_data $arr-i8 $mixed + (i32.const 0) + (i32.const 0) + ) + ) + ;; Non-zero in-bounds offset with size 0 traps if the segment was dropped at + ;; runtime, so it is only lowered with trapsNeverHappen. + (drop + (array.new_data $arr-i8 $mixed + (i32.const 4) + (i32.const 0) + ) + ) + ) + + ;; TNH: (func $array-new-data-zeros (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-i8 + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-i8))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-i8 + ;; TNH-NEXT: (i32.const 5) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-i16))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-i16 + ;; TNH-NEXT: (i32.const 4) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-i32))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-i32 + ;; TNH-NEXT: (i32.const 3) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-i64))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (i64.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-i64 + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-f32))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (f32.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-f32 + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-f64))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (f64.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-f64 + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-v128 + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-zeros (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $zeros + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: (i32.const 5) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i16 $zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i32 $zeros + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: (i32.const 3) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i64 $zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-f32 $zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-f64 $zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-v128 $zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-zeros + ;; All-zero slices become array.new_default in TNH mode. + (drop + (array.new_data $arr-i8 $zeros + (i32.const 0) + (i32.const 1) + ) + ) + (drop + (array.new_data $arr-i8 $zeros + (i32.const 2) + (i32.const 5) + ) + ) + (drop + (array.new_data $arr-i16 $zeros + (i32.const 0) + (i32.const 4) + ) + ) + (drop + (array.new_data $arr-i32 $zeros + (i32.const 4) + (i32.const 3) + ) + ) + (drop + (array.new_data $arr-i64 $zeros + (i32.const 0) + (i32.const 2) + ) + ) + (drop + (array.new_data $arr-f32 $zeros + (i32.const 0) + (i32.const 2) + ) + ) + (drop + (array.new_data $arr-f64 $zeros + (i32.const 0) + (i32.const 2) + ) + ) + (drop + (array.new_data $arr-v128 $zeros + (i32.const 0) + (i32.const 1) + ) + ) + ) + + ;; TNH: (func $array-new-data-uniform (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i8 1 + ;; TNH-NEXT: (i32.const 42) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new $arr-i8 + ;; TNH-NEXT: (i32.const 42) + ;; TNH-NEXT: (i32.const 4) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new $arr-i16 + ;; TNH-NEXT: (i32.const 513) + ;; TNH-NEXT: (i32.const 4) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new $arr-i32 + ;; TNH-NEXT: (i32.const 33620481) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-uniform (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $uniform + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $uniform + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i16 $uniform + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i32 $uniform + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-uniform + ;; Size 1 non-zero becomes array.new_fixed of size 1 (more compact than + ;; array.new). + (drop + (array.new_data $arr-i8 $uniform + (i32.const 0) + (i32.const 1) + ) + ) + ;; Size >= 2 with identical non-zero elements becomes array.new. + (drop + (array.new_data $arr-i8 $uniform + (i32.const 0) + (i32.const 4) + ) + ) + (drop + (array.new_data $arr-i16 $uniform + (i32.const 4) + (i32.const 4) + ) + ) + (drop + (array.new_data $arr-i32 $uniform + (i32.const 4) + (i32.const 2) + ) + ) + ) + + ;; TNH: (func $array-new-data-fixed (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i8 4 + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: (i32.const -1) + ;; TNH-NEXT: (i32.const 3) + ;; TNH-NEXT: (i32.const 4) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i16 2 + ;; TNH-NEXT: (i32.const -255) + ;; TNH-NEXT: (i32.const 1027) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i32 2 + ;; TNH-NEXT: (i32.const 67370753) + ;; TNH-NEXT: (i32.const 134678021) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-i64 1 + ;; TNH-NEXT: (i64.const 578437695752371969) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-f32 1 + ;; TNH-NEXT: (f32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-f64 1 + ;; TNH-NEXT: (f64.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_fixed $arr-v128 1 + ;; TNH-NEXT: (v128.const i32x4 0x0403ff01 0x08070605 0x3f800000 0x00000000) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-fixed (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 4) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i16 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i32 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i64 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-f32 $mixed + ;; NO_TNH-NEXT: (i32.const 8) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-f64 $mixed + ;; NO_TNH-NEXT: (i32.const 12) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-v128 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-fixed + ;; Small non-uniform slices (size <= 64) become array.new_fixed. + (drop + (array.new_data $arr-i8 $mixed + (i32.const 0) + (i32.const 4) + ) + ) + (drop + (array.new_data $arr-i16 $mixed + (i32.const 0) + (i32.const 2) + ) + ) + (drop + (array.new_data $arr-i32 $mixed + (i32.const 0) + (i32.const 2) + ) + ) + (drop + (array.new_data $arr-i64 $mixed + (i32.const 0) + (i32.const 1) + ) + ) + (drop + (array.new_data $arr-f32 $mixed + (i32.const 8) + (i32.const 1) + ) + ) + (drop + (array.new_data $arr-f64 $mixed + (i32.const 12) + (i32.const 1) + ) + ) + (drop + (array.new_data $arr-v128 $mixed + (i32.const 0) + (i32.const 1) + ) + ) + ) + + ;; TNH: (func $array-new-data-negative (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $large + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: (i32.const 65) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; TNH-NEXT: (i32.const 21) + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i32 $mixed + ;; TNH-NEXT: (i32.const 18) + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: (i32.const -1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $active + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; TNH-NEXT: (call $get-i32) + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: (call $get-i32) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-negative (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $large + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 65) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (i32.const 21) + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i32 $mixed + ;; NO_TNH-NEXT: (i32.const 18) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const -1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $active + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (call $get-i32) + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (call $get-i32) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-negative + ;; Size > 64 with non-uniform elements is not lowered. + (drop + (array.new_data $arr-i8 $large + (i32.const 0) + (i32.const 65) + ) + ) + ;; Out-of-bounds offset or size is not lowered. + (drop + (array.new_data $arr-i8 $mixed + (i32.const 21) + (i32.const 0) + ) + ) + (drop + (array.new_data $arr-i32 $mixed + (i32.const 18) + (i32.const 1) + ) + ) + ;; Negative (i.e. huge unsigned) size is out of bounds and not lowered. + (drop + (array.new_data $arr-i8 $mixed + (i32.const 0) + (i32.const -1) + ) + ) + ;; Active segment is not lowered. + (drop + (array.new_data $arr-i8 $active + (i32.const 0) + (i32.const 1) + ) + ) + ;; Non-constant offset or size is not lowered. + (drop + (array.new_data $arr-i8 $mixed + (call $get-i32) + (i32.const 1) + ) + ) + (drop + (array.new_data $arr-i8 $mixed + (i32.const 0) + (call $get-i32) + ) + ) + ) + + ;; TNH: (func $array-new-data-large-uniform (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result (ref (exact $arr-i8))) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (i32.const 0) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (array.new_default $arr-i8 + ;; TNH-NEXT: (i32.const 72) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new $arr-i8 + ;; TNH-NEXT: (i32.const 7) + ;; TNH-NEXT: (i32.const 72) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-large-uniform (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $large-zeros + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 72) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $large-uniform + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 72) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-large-uniform + ;; Uniform slices are lowered regardless of size, even above the + ;; array.new_fixed limit. + (drop + (array.new_data $arr-i8 $large-zeros + (i32.const 0) + (i32.const 72) + ) + ) + (drop + (array.new_data $arr-i8 $large-uniform + (i32.const 0) + (i32.const 72) + ) + ) + ) + + ;; TNH: (func $array-new-data-floats (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new $arr-f32 + ;; TNH-NEXT: (f32.const -0) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new $arr-f32 + ;; TNH-NEXT: (f32.const nan:0x400000) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-floats (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-f32 $floats + ;; NO_TNH-NEXT: (i32.const 0) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-f32 $floats + ;; NO_TNH-NEXT: (i32.const 8) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-floats + ;; -0.0 is not the default value, so this becomes array.new, not + ;; array.new_default. + (drop + (array.new_data $arr-f32 $floats + (i32.const 0) + (i32.const 2) + ) + ) + ;; NaNs with identical bits are uniform. + (drop + (array.new_data $arr-f32 $floats + (i32.const 8) + (i32.const 2) + ) + ) + ) + + ;; TNH: (func $array-new-data-overflow (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; TNH-NEXT: (i32.const -1) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-overflow (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (array.new_data $arr-i8 $mixed + ;; NO_TNH-NEXT: (i32.const -1) + ;; NO_TNH-NEXT: (i32.const 2) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-overflow + ;; offset + size overflows 32 bits and is out of bounds; not lowered. + (drop + (array.new_data $arr-i8 $mixed + (i32.const -1) + (i32.const 2) + ) + ) + ) + + ;; TNH: (func $array-new-data-unreachable (type $1) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block ;; (replaces unreachable ArrayNewData we can't emit) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $array-new-data-unreachable (type $1) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (block ;; (replaces unreachable ArrayNewData we can't emit) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (i32.const 1) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $array-new-data-unreachable + ;; Unreachable code is not lowered. + (drop + (array.new_data $arr-i8 $mixed + (unreachable) + (i32.const 1) + ) + ) + ) +)