summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/Precompute.cpp11
-rw-r--r--src/wasm-interpreter.h40
-rw-r--r--test/passes/O1_fuzz-exec_all-features.txt52
-rw-r--r--test/passes/O1_fuzz-exec_all-features.wast37
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.txt109
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.wast70
6 files changed, 217 insertions, 102 deletions
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index bf5924720..355af7ed0 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -168,11 +168,6 @@ struct Precompute
if (curr->type.isVector()) {
return;
}
- // Don't try to precompute a reference. We can't replace it with a constant
- // expression, as that would make a copy of it by value.
- if (curr->type.isRef()) {
- return;
- }
// try to evaluate this into a const
Flow flow = precomputeExpression(curr);
if (flow.getType().hasVector()) {
@@ -228,6 +223,12 @@ private:
// Precompute an expression, returning a flow, which may be a constant
// (that we can replace the expression with if replaceExpression is set).
Flow precomputeExpression(Expression* curr, bool replaceExpression = true) {
+ // Don't try to precompute a reference. We can't replace it with a constant
+ // expression, as that would make a copy of it by value.
+ // TODO: do so when safe
+ if (curr->type.isRef()) {
+ return Flow(NONCONSTANT_FLOW);
+ }
try {
return PrecomputingExpressionRunner(
getModule(), getValues, replaceExpression)
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index d701aaf15..e4c20082e 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1427,7 +1427,8 @@ public:
if (!data) {
trap("null ref");
}
- return (*data)[curr->index];
+ auto field = curr->ref->type.getHeapType().getStruct().fields[curr->index];
+ return extendForPacking((*data)[curr->index], field, curr->signed_);
}
Flow visitStructSet(StructSet* curr) {
NOTE_ENTER("StructSet");
@@ -1444,7 +1445,7 @@ public:
trap("null ref");
}
auto field = curr->ref->type.getHeapType().getStruct().fields[curr->index];
- (*data)[curr->index] = getMaybePackedValue(value.getSingleValue(), field);
+ (*data)[curr->index] = truncateForPacking(value.getSingleValue(), field);
return Flow();
}
Flow visitArrayNew(ArrayNew* curr) {
@@ -1494,7 +1495,8 @@ public:
if (i >= data->size()) {
trap("array oob");
}
- return (*data)[i];
+ auto field = curr->ref->type.getHeapType().getArray().element;
+ return extendForPacking((*data)[i], field, curr->signed_);
}
Flow visitArraySet(ArraySet* curr) {
NOTE_ENTER("ArraySet");
@@ -1519,7 +1521,7 @@ public:
trap("array oob");
}
auto field = curr->ref->type.getHeapType().getArray().element;
- (*data)[i] = getMaybePackedValue(value.getSingleValue(), field);
+ (*data)[i] = truncateForPacking(value.getSingleValue(), field);
return Flow();
}
Flow visitArrayLen(ArrayLen* curr) {
@@ -1541,13 +1543,35 @@ public:
private:
// Truncate the value if we need to. The storage is just a list of Literals,
- // so we can't just write the value like we would to a C struct field.
- Literal getMaybePackedValue(Literal value, const Field& field) {
+ // so we can't just write the value like we would to a C struct field and
+ // expect it to truncate for us. Instead, we truncate so the stored value is
+ // proper for the type.
+ Literal truncateForPacking(Literal value, const Field& field) {
+ if (field.type == Type::i32) {
+ int32_t c = value.geti32();
+ if (field.packedType == Field::i8) {
+ value = Literal(c & 0xff);
+ } else if (field.packedType == Field::i16) {
+ value = Literal(c & 0xffff);
+ }
+ }
+ return value;
+ }
+
+ Literal extendForPacking(Literal value, const Field& field, bool signed_) {
if (field.type == Type::i32) {
+ int32_t c = value.geti32();
if (field.packedType == Field::i8) {
- value = value.and_(Literal(int32_t(0xff)));
+ // The stored value should already be truncated.
+ assert(c == (c & 0xff));
+ if (signed_) {
+ value = Literal((c << 24) >> 24);
+ }
} else if (field.packedType == Field::i16) {
- value = value.and_(Literal(int32_t(0xffff)));
+ assert(c == (c & 0xffff));
+ if (signed_) {
+ value = Literal((c << 16) >> 16);
+ }
}
}
return value;
diff --git a/test/passes/O1_fuzz-exec_all-features.txt b/test/passes/O1_fuzz-exec_all-features.txt
deleted file mode 100644
index 522943d69..000000000
--- a/test/passes/O1_fuzz-exec_all-features.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-[fuzz-exec] calling structs
-[LoggingExternalInterface logging 0]
-[LoggingExternalInterface logging 42]
-[LoggingExternalInterface logging 100]
-[LoggingExternalInterface logging 100]
-(module
- (type ${i32} (struct (field i32)))
- (type $none_=>_none (func))
- (type $i32_=>_none (func (param i32)))
- (import "fuzzing-support" "log-i32" (func $log (param i32)))
- (export "structs" (func $0))
- (func $0
- (local $0 (ref null ${i32}))
- (call $log
- (struct.get ${i32} 0
- (local.tee $0
- (struct.new_default_with_rtt ${i32}
- (rtt.canon ${i32})
- )
- )
- )
- )
- (struct.set ${i32} 0
- (local.get $0)
- (i32.const 42)
- )
- (call $log
- (struct.get ${i32} 0
- (local.get $0)
- )
- )
- (struct.set ${i32} 0
- (local.get $0)
- (i32.const 100)
- )
- (call $log
- (struct.get ${i32} 0
- (local.get $0)
- )
- )
- (call $log
- (struct.get ${i32} 0
- (local.get $0)
- )
- )
- )
-)
-[fuzz-exec] calling structs
-[LoggingExternalInterface logging 0]
-[LoggingExternalInterface logging 42]
-[LoggingExternalInterface logging 100]
-[LoggingExternalInterface logging 100]
diff --git a/test/passes/O1_fuzz-exec_all-features.wast b/test/passes/O1_fuzz-exec_all-features.wast
deleted file mode 100644
index 0740e60e7..000000000
--- a/test/passes/O1_fuzz-exec_all-features.wast
+++ /dev/null
@@ -1,37 +0,0 @@
-(module
- (type $struct (struct i32))
- (import "fuzzing-support" "log-i32" (func $log (param i32)))
- (func "structs"
- (local $x (ref null $struct))
- (local $y (ref null $struct))
- (local.set $x
- (struct.new_default_with_rtt $struct
- (rtt.canon $struct)
- )
- )
- ;; The value is initialized to 0
- (call $log
- (struct.get $struct 0 (local.get $x))
- )
- ;; Assigning a value works
- (struct.set $struct 0
- (local.get $x)
- (i32.const 42)
- )
- (call $log
- (struct.get $struct 0 (local.get $x))
- )
- ;; References are references, so writing to one's value affects the other's
- (local.set $y (local.get $x))
- (struct.set $struct 0
- (local.get $y)
- (i32.const 100)
- )
- (call $log
- (struct.get $struct 0 (local.get $x))
- )
- (call $log
- (struct.get $struct 0 (local.get $y))
- )
- )
-)
diff --git a/test/passes/Oz_fuzz-exec_all-features.txt b/test/passes/Oz_fuzz-exec_all-features.txt
new file mode 100644
index 000000000..0663a3351
--- /dev/null
+++ b/test/passes/Oz_fuzz-exec_all-features.txt
@@ -0,0 +1,109 @@
+[fuzz-exec] calling structs
+[LoggingExternalInterface logging 0]
+[LoggingExternalInterface logging 42]
+[LoggingExternalInterface logging 100]
+[LoggingExternalInterface logging 100]
+[fuzz-exec] calling arrays
+[LoggingExternalInterface logging 50]
+[LoggingExternalInterface logging 42]
+[LoggingExternalInterface logging 128]
+[LoggingExternalInterface logging -128]
+[LoggingExternalInterface logging 42]
+(module
+ (type ${i32} (struct (field i32)))
+ (type $none_=>_none (func))
+ (type $[mut:i8] (array (mut i8)))
+ (type $i32_=>_none (func (param i32)))
+ (import "fuzzing-support" "log-i32" (func $log (param i32)))
+ (export "structs" (func $0))
+ (export "arrays" (func $1))
+ (func $0 (; has Stack IR ;)
+ (local $0 (ref null ${i32}))
+ (call $log
+ (struct.get ${i32} 0
+ (local.tee $0
+ (struct.new_default_with_rtt ${i32}
+ (rtt.canon ${i32})
+ )
+ )
+ )
+ )
+ (struct.set ${i32} 0
+ (local.get $0)
+ (i32.const 42)
+ )
+ (call $log
+ (struct.get ${i32} 0
+ (local.get $0)
+ )
+ )
+ (struct.set ${i32} 0
+ (local.get $0)
+ (i32.const 100)
+ )
+ (call $log
+ (struct.get ${i32} 0
+ (local.get $0)
+ )
+ )
+ (call $log
+ (struct.get ${i32} 0
+ (local.get $0)
+ )
+ )
+ )
+ (func $1 (; has Stack IR ;)
+ (local $0 (ref null $[mut:i8]))
+ (call $log
+ (array.len $[mut:i8]
+ (local.tee $0
+ (array.new_with_rtt $[mut:i8]
+ (rtt.canon $[mut:i8])
+ (i32.const 50)
+ (i32.const 42)
+ )
+ )
+ )
+ )
+ (call $log
+ (array.get_u $[mut:i8]
+ (local.get $0)
+ (i32.const 10)
+ )
+ )
+ (array.set $[mut:i8]
+ (local.get $0)
+ (i32.const 10)
+ (i32.const 65408)
+ )
+ (call $log
+ (array.get_u $[mut:i8]
+ (local.get $0)
+ (i32.const 10)
+ )
+ )
+ (call $log
+ (array.get_s $[mut:i8]
+ (local.get $0)
+ (i32.const 10)
+ )
+ )
+ (call $log
+ (array.get_s $[mut:i8]
+ (local.get $0)
+ (i32.const 20)
+ )
+ )
+ )
+)
+[fuzz-exec] calling structs
+[LoggingExternalInterface logging 0]
+[LoggingExternalInterface logging 42]
+[LoggingExternalInterface logging 100]
+[LoggingExternalInterface logging 100]
+[fuzz-exec] calling arrays
+[LoggingExternalInterface logging 50]
+[LoggingExternalInterface logging 42]
+[LoggingExternalInterface logging 128]
+[LoggingExternalInterface logging -128]
+[LoggingExternalInterface logging 42]
diff --git a/test/passes/Oz_fuzz-exec_all-features.wast b/test/passes/Oz_fuzz-exec_all-features.wast
new file mode 100644
index 000000000..d32ee0c19
--- /dev/null
+++ b/test/passes/Oz_fuzz-exec_all-features.wast
@@ -0,0 +1,70 @@
+(module
+ (type $struct (struct i32))
+ (type $bytes (array (mut i8)))
+ (import "fuzzing-support" "log-i32" (func $log (param i32)))
+ (func "structs"
+ (local $x (ref null $struct))
+ (local $y (ref null $struct))
+ (local.set $x
+ (struct.new_default_with_rtt $struct
+ (rtt.canon $struct)
+ )
+ )
+ ;; The value is initialized to 0
+ ;; Note: -Oz will optimize all these to constants thanks to Precompute
+ (call $log
+ (struct.get $struct 0 (local.get $x))
+ )
+ ;; Assigning a value works
+ (struct.set $struct 0
+ (local.get $x)
+ (i32.const 42)
+ )
+ (call $log
+ (struct.get $struct 0 (local.get $x))
+ )
+ ;; References are references, so writing to one's value affects the other's
+ (local.set $y (local.get $x))
+ (struct.set $struct 0
+ (local.get $y)
+ (i32.const 100)
+ )
+ (call $log
+ (struct.get $struct 0 (local.get $x))
+ )
+ (call $log
+ (struct.get $struct 0 (local.get $y))
+ )
+ )
+ (func "arrays"
+ (local $x (ref null $bytes))
+ (local.set $x
+ (array.new_with_rtt $bytes
+ (rtt.canon $bytes)
+ (i32.const 50) ;; size
+ (i32.const 42) ;; value to splat into the array
+ )
+ )
+ ;; The length should be 50
+ (call $log
+ (array.len $bytes (local.get $x))
+ )
+ ;; The value should be 42
+ (call $log
+ (array.get_u $bytes (local.get $x) (i32.const 10))
+ )
+ ;; Write a value that will be truncated into an i8
+ (array.set $bytes (local.get $x) (i32.const 10) (i32.const 0xff80))
+ ;; The value should be 0x80 (-128 or 128 depending on signed/unsigned)
+ (call $log
+ (array.get_u $bytes (local.get $x) (i32.const 10))
+ )
+ (call $log
+ (array.get_s $bytes (local.get $x) (i32.const 10))
+ )
+ ;; Other items than the one at index 10 are unaffected.
+ (call $log
+ (array.get_s $bytes (local.get $x) (i32.const 20))
+ )
+ )
+)