diff options
-rwxr-xr-x | scripts/fuzz_opt.py | 1 | ||||
-rw-r--r-- | src/ir/effects.h | 25 | ||||
-rw-r--r-- | test/lit/passes/simplify-locals-strings.wast | 549 |
3 files changed, 572 insertions, 3 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 38c5c331b..e7c9edf9c 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -272,6 +272,7 @@ INITIAL_CONTENTS_IGNORE = [ 'relaxed-simd.wast', # TODO: fuzzer and interpreter support for strings 'strings.wast', + 'simplify-locals-strings.wast', # TODO: fuzzer and interpreter support for extern conversions 'extern-conversions.wast', # ignore DWARF because it is incompatible with multivalue atm diff --git a/src/ir/effects.h b/src/ir/effects.h index c2ba794d9..63d9fafc5 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -592,7 +592,8 @@ private: parent.implicitTrap = true; break; } - default: {} + default: { + } } } void visitBinary(Binary* curr) { @@ -621,7 +622,8 @@ private: } break; } - default: {} + default: { + } } } void visitSelect(Select* curr) {} @@ -783,6 +785,22 @@ private: void visitStringNew(StringNew* curr) { // traps when out of bounds in linear memory or ref is null parent.implicitTrap = true; + switch (curr->op) { + case StringNewUTF8: + case StringNewWTF8: + case StringNewReplace: + case StringNewWTF16: + parent.readsMemory = true; + break; + case StringNewUTF8Array: + case StringNewWTF8Array: + case StringNewReplaceArray: + case StringNewWTF16Array: + parent.readsArray = true; + break; + default: { + } + } } void visitStringConst(StringConst* curr) {} void visitStringMeasure(StringMeasure* curr) { @@ -803,7 +821,8 @@ private: case StringEncodeWTF16Array: parent.writesArray = true; break; - default: {} + default: { + } } } void visitStringConcat(StringConcat* curr) { diff --git a/test/lit/passes/simplify-locals-strings.wast b/test/lit/passes/simplify-locals-strings.wast new file mode 100644 index 000000000..1dd28efa5 --- /dev/null +++ b/test/lit/passes/simplify-locals-strings.wast @@ -0,0 +1,549 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s --simplify-locals -all -S -o - \ +;; RUN: | filecheck %s + +(module + (memory 10 10) + + ;; CHECK: (type $array (array (mut i8))) + (type $array (array_subtype (mut i8) data)) + ;; CHECK: (type $array16 (array (mut i16))) + (type $array16 (array_subtype (mut i16) data)) + + ;; CHECK: (func $no-new-past-store + ;; CHECK-NEXT: (local $temp stringref) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf8 utf8 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf8 wtf8 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf8 replace + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf16 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $no-new-past-store + (local $temp stringref) + ;; A string.new cannot be moved past a memory store. + (local.set $temp + (string.new_wtf8 utf8 + (i32.const 1) + (i32.const 2) + ) + ) + (i32.store + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (string.new_wtf8 wtf8 + (i32.const 1) + (i32.const 2) + ) + ) + (i32.store + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (string.new_wtf8 replace + (i32.const 1) + (i32.const 2) + ) + ) + (i32.store + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (string.new_wtf16 + (i32.const 1) + (i32.const 2) + ) + ) + (i32.store + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + ) + + ;; CHECK: (func $yes-new-past-store + ;; CHECK-NEXT: (local $temp stringref) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.new_wtf8 utf8 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $yes-new-past-store + (local $temp stringref) + ;; A string.new can be moved past a memory load. + (local.set $temp + (string.new_wtf8 utf8 + (i32.const 1) + (i32.const 2) + ) + ) + (drop + (i32.load + (i32.const 3) + ) + ) + (drop + (local.get $temp) + ) + ) + + ;; CHECK: (func $no-new-past-store-gc (param $array (ref $array)) (param $array16 (ref $array16)) + ;; CHECK-NEXT: (local $temp stringref) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf8_array utf8 + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (array.set $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf8_array wtf8 + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (array.set $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf8_array replace + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (array.set $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (string.new_wtf16_array + ;; CHECK-NEXT: (local.get $array16) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (array.set $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $no-new-past-store-gc (param $array (ref $array)) (param $array16 (ref $array16)) + (local $temp stringref) + ;; A string.new_***_array cannot be moved past a GC store. + (local.set $temp + (string.new_wtf8_array utf8 + (local.get $array) + (i32.const 1) + (i32.const 2) + ) + ) + (array.set $array + (local.get $array) + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (string.new_wtf8_array wtf8 + (local.get $array) + (i32.const 1) + (i32.const 2) + ) + ) + (array.set $array + (local.get $array) + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (string.new_wtf8_array replace + (local.get $array) + (i32.const 1) + (i32.const 2) + ) + ) + (array.set $array + (local.get $array) + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (string.new_wtf16_array + (local.get $array16) + (i32.const 1) + (i32.const 2) + ) + ) + (array.set $array + (local.get $array) + (i32.const 3) + (i32.const 4) + ) + (drop + (local.get $temp) + ) + ) + + ;; CHECK: (func $no-load-past-encode (param $ref stringref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.encode_wtf8 wtf8 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.encode_wtf8 utf8 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.encode_wtf16 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $no-load-past-encode (param $ref stringref) + (local $temp i32) + ;; string.encode writes to memory, so a load can't be moved past it. + (local.set $temp + (i32.load + (i32.const 1) + ) + ) + (drop + (string.encode_wtf8 wtf8 + (local.get $ref) + (i32.const 10) + ) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (i32.load + (i32.const 1) + ) + ) + (drop + (string.encode_wtf8 utf8 + (local.get $ref) + (i32.const 20) + ) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (i32.load + (i32.const 1) + ) + ) + (drop + (string.encode_wtf16 + (local.get $ref) + (i32.const 30) + ) + ) + (drop + (local.get $temp) + ) + ) + + ;; CHECK: (func $no-load-past-encode-gc (param $ref stringref) (param $array (ref $array)) (param $array16 (ref $array16)) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (array.get_u $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.encode_wtf8_array wtf8 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (array.get_u $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.encode_wtf8_array utf8 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $temp + ;; CHECK-NEXT: (array.get_u $array + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (string.encode_wtf16_array + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: (local.get $array16) + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $temp) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $no-load-past-encode-gc (param $ref stringref) (param $array (ref $array)) (param $array16 (ref $array16)) + (local $temp i32) + ;; string.encode_*_array writes to an array, so an array get can't be moved + ;; past it. + (local.set $temp + (array.get $array + (local.get $array) + (i32.const 0) + ) + ) + (drop + (string.encode_wtf8_array wtf8 + (local.get $ref) + (local.get $array) + (i32.const 10) + ) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (array.get $array + (local.get $array) + (i32.const 0) + ) + ) + (drop + (string.encode_wtf8_array utf8 + (local.get $ref) + (local.get $array) + (i32.const 20) + ) + ) + (drop + (local.get $temp) + ) + (local.set $temp + (array.get $array + (local.get $array) + (i32.const 0) + ) + ) + (drop + (string.encode_wtf16_array + (local.get $ref) + (local.get $array16) + (i32.const 30) + ) + ) + (drop + (local.get $temp) + ) + ) + + ;; CHECK: (func $no-iteration-past-each-other (param $iter stringview_iter) + ;; CHECK-NEXT: (local $i32 i32) + ;; CHECK-NEXT: (local.set $i32 + ;; CHECK-NEXT: (stringview_iter.next + ;; CHECK-NEXT: (local.get $iter) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (stringview_iter.advance + ;; CHECK-NEXT: (local.get $iter) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $i32 + ;; CHECK-NEXT: (stringview_iter.next + ;; CHECK-NEXT: (local.get $iter) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (stringview_iter.rewind + ;; CHECK-NEXT: (local.get $iter) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $no-iteration-past-each-other + (param $iter stringview_iter) + (local $i32 i32) + ;; Iteration operations interact with each other, and can't be moved past + ;; each other. + (local.set $i32 + (stringview_iter.next + (local.get $iter) + ) + ) + (drop + (stringview_iter.advance + (local.get $iter) + (i32.const 3) + ) + ) + (drop + (local.get $i32) + ) + (local.set $i32 + (stringview_iter.next + (local.get $iter) + ) + ) + (drop + (stringview_iter.rewind + (local.get $iter) + (i32.const 4) + ) + ) + (drop + (local.get $i32) + ) + ) +) |