From b5fc51dbb87fd689669a4ad4f2a569e5331a85e7 Mon Sep 17 00:00:00 2001 From: Goktug Gokdogan Date: Fri, 9 Oct 2026 00:09:04 -0700 Subject: [PATCH] Lower constant array.new_data in OptimizeInstructions array.new_data is not a constant expression, which blocks optimizations such as hoisting array literals into immutable globals, and passes like Precompute, Heap2Local, GUFA and SimplifyGlobals don't understand it. When the offset and size are constant and in bounds of a passive segment, lower it to: * array.new_default, if all elements are zero * array.new, if all elements are equal * array.new_fixed, if there are at most 64 elements Non-empty slices are only lowered with --traps-never-happen, since array.new_data traps if the segment was dropped. An empty slice at offset 0 can never trap, so it is always lowered. The 64-element limit exists because array.new_fixed costs a full *.const per element, while a data segment stores raw bytes. On large J2Wasm applications this removes over 99.7% of passive data segments and reduces binary size by ~1.8% (~1.3% gzipped). --- src/passes/OptimizeInstructions.cpp | 102 +++ src/wasm-interpreter.h | 10 +- .../optimize-instructions-array-new-data.wast | 758 ++++++++++++++++++ 3 files changed, 863 insertions(+), 7 deletions(-) create mode 100644 test/lit/passes/optimize-instructions-array-new-data.wast diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index c4e50e8f5e0..193128e92b3 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2349,6 +2349,108 @@ 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()) { + return; + } + + auto heapType = curr->type.getHeapType(); + const auto& element = heapType.getArray().element; + if (!element.type.isNumber()) { + return; + } + + uint64_t offset = offsetConst->value.getUnsigned(); + uint64_t size = sizeConst->value.getUnsigned(); + uint64_t elemBytes = element.getByteSize(); + // offset and size are u32 and elemBytes <= 16, so this can't overflow. + uint64_t end = offset + size * elemBytes; + if (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]; + switch (element.packedType) { + case Field::NotPacked: + return Literal::makeFromMemory(src, element.type); + // Packed values are truncated when stored, so the extension doesn't + // matter; sign-extending gives the shorter encoding (-1 vs 255). + case Field::i8: + return Literal(int32_t(int8_t(*src))); + case Field::i16: + return Literal(int32_t(Bits::readLE(src))); + } + WASM_UNREACHABLE("unexpected packed type"); + }; + + // If all elements are equal, array.new(_default) 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) { + auto* sizeExpr = builder.makeConst(int32_t(size)); + // Literal comparison is bitwise, so e.g. -0.0 is not considered zero. + if (first == Literal::makeZero(element.type)) { + replaceCurrent(builder.makeArrayNew(heapType, sizeExpr)); + } else { + // visitArrayNew turns the size == 1 case into array.new_fixed. + 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-interpreter.h b/src/wasm-interpreter.h index d5e358a081d..ceb3a794909 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -4747,11 +4747,9 @@ class ModuleRunnerBase : public ExpressionRunner { const auto& seg = *wasm.getDataSegment(curr->segment); auto elemBytes = element.getByteSize(); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - - uint64_t end; - if (std::ckd_add(&end, offset, size * elemBytes) || end > seg.data.size()) { + // offset and size are u32 and elemBytes <= 16, so this can't overflow. + uint64_t end = offset + size * elemBytes; + if (end > seg.data.size()) { trap("out of bounds segment access in array.new_data"); } if (droppedDataSegments.contains(curr->segment) && end > 0) { @@ -4762,8 +4760,6 @@ class ModuleRunnerBase : public ExpressionRunner { memcpy(contents.data(), &seg.data[offset], size * elemBytes); } -#pragma GCC diagnostic pop - return self()->makeGCData(std::move(contents), curr->type); } Flow visitArrayNewElem(ArrayNewElem* curr) { 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..8f601d813cd --- /dev/null +++ b/test/lit/passes/optimize-instructions-array-new-data.wast @@ -0,0 +1,758 @@ +;; 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") + ;; 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: (array.new_default $arr-i8 + ;; TNH-NEXT: (i32.const 5) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-i16 + ;; TNH-NEXT: (i32.const 4) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-i32 + ;; TNH-NEXT: (i32.const 3) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-i64 + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-f32 + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (array.new_default $arr-f64 + ;; TNH-NEXT: (i32.const 2) + ;; 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: (array.new_default $arr-i8 + ;; TNH-NEXT: (i32.const 72) + ;; 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) + ) + ) + ) +)