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) + ) + ) + ) +)