diff options
-rw-r--r-- | src/passes/Precompute.cpp | 11 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 40 | ||||
-rw-r--r-- | test/passes/O1_fuzz-exec_all-features.txt | 52 | ||||
-rw-r--r-- | test/passes/O1_fuzz-exec_all-features.wast | 37 | ||||
-rw-r--r-- | test/passes/Oz_fuzz-exec_all-features.txt | 109 | ||||
-rw-r--r-- | test/passes/Oz_fuzz-exec_all-features.wast | 70 |
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)) + ) + ) +) |