summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/fuzz_opt.py1
-rw-r--r--src/ir/effects.h25
-rw-r--r--test/lit/passes/simplify-locals-strings.wast549
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)
+ )
+ )
+)