diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 83ad12a81b3..4ef2e8b1c3e 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3868,6 +3868,11 @@ void FunctionValidator::visitArraySet(ArraySet* curr) { shouldBeTrue(element.mutable_, curr, "array.set type must be mutable"); } +static bool isValidMultibyteElement(const Field& element) { + return element.packedType == Field::i8 || element.packedType == Field::i16 || + (element.packedType == Field::NotPacked && element.type.isNumber()); +} + void FunctionValidator::visitArrayLoad(ArrayLoad* curr) { shouldBeTrue(getModule()->features.hasMultibyte(), curr, @@ -3876,6 +3881,10 @@ void FunctionValidator::visitArrayLoad(ArrayLoad* curr) { Type(Type::i32), curr, "array load index must be an i32"); + validateMemBytes(curr->bytes, curr->type, curr); + validateOffset(curr->offset, nullptr, curr); + validateAlignment( + curr->align, curr->type, curr->bytes, /*isAtomic=*/false, curr); if (curr->type == Type::unreachable) { return; } @@ -3889,8 +3898,9 @@ void FunctionValidator::visitArrayLoad(ArrayLoad* curr) { auto heapType = curr->ref->type.getHeapType(); const auto& element = heapType.getArray().element; - shouldBeTrue( - element.packedType == Field::i8, curr, "array load type must be i8"); + shouldBeTrue(isValidMultibyteElement(element), + curr, + "array load type must be a numeric type"); } void FunctionValidator::visitArrayStore(ArrayStore* curr) { @@ -3901,6 +3911,10 @@ void FunctionValidator::visitArrayStore(ArrayStore* curr) { Type(Type::i32), curr, "array store index must be an i32"); + validateMemBytes(curr->bytes, curr->value->type, curr); + validateOffset(curr->offset, nullptr, curr); + validateAlignment( + curr->align, curr->value->type, curr->bytes, /*isAtomic=*/false, curr); if (curr->type == Type::unreachable) { return; } @@ -3914,8 +3928,9 @@ void FunctionValidator::visitArrayStore(ArrayStore* curr) { auto heapType = curr->ref->type.getHeapType(); const auto& element = heapType.getArray().element; - shouldBeTrue( - element.packedType == Field::i8, curr, "array store type must be i8"); + shouldBeTrue(isValidMultibyteElement(element), + curr, + "array store type must be a numeric type"); shouldBeTrue(element.mutable_, curr, "array store type must be mutable"); } @@ -4842,7 +4857,8 @@ void FunctionValidator::visitFunction(Function* curr) { void FunctionValidator::validateOffset(Address offset, Memory* mem, Expression* curr) { - shouldBeTrue(mem->is64() || offset <= std::numeric_limits::max(), + shouldBeTrue((mem && mem->is64()) || + offset <= std::numeric_limits::max(), curr, "offset must be u32"); } diff --git a/test/lit/array-multibyte.wast b/test/lit/array-multibyte.wast index ece8506893e..5bb339f5e21 100644 --- a/test/lit/array-multibyte.wast +++ b/test/lit/array-multibyte.wast @@ -16,12 +16,52 @@ ;; RTRIP: (type $i8_array (array (mut i8))) (type $i8_array (array (mut i8))) - ;; CHECK: (type $2 (func (param (ref $i8_array)))) + ;; CHECK: (type $i16_array (array (mut i16))) + + ;; CHECK: (type $i32_array (array (mut i32))) + + ;; CHECK: (type $i64_array (array (mut i64))) + + ;; CHECK: (type $f32_array (array (mut f32))) + + ;; CHECK: (type $f64_array (array (mut f64))) + + ;; CHECK: (type $v128_array (array (mut v128))) + + ;; CHECK: (type $imm_i8_array (array i8)) + + ;; CHECK: (type $imm_i32_array (array i32)) + + ;; CHECK: (type $10 (func (param (ref $i16_array) (ref $i32_array) (ref $i64_array) (ref $f32_array) (ref $f64_array) (ref $v128_array)))) + + ;; CHECK: (type $11 (func (param (ref $i8_array)))) + + ;; CHECK: (type $12 (func (param (ref $imm_i8_array) (ref $imm_i32_array)))) ;; CHECK: (global $arr (ref $i8_array) (array.new_default $i8_array ;; CHECK-NEXT: (i32.const 4) ;; CHECK-NEXT: )) - ;; RTRIP: (type $2 (func (param (ref $i8_array)))) + ;; RTRIP: (type $i16_array (array (mut i16))) + + ;; RTRIP: (type $i32_array (array (mut i32))) + + ;; RTRIP: (type $i64_array (array (mut i64))) + + ;; RTRIP: (type $f32_array (array (mut f32))) + + ;; RTRIP: (type $f64_array (array (mut f64))) + + ;; RTRIP: (type $v128_array (array (mut v128))) + + ;; RTRIP: (type $imm_i8_array (array i8)) + + ;; RTRIP: (type $imm_i32_array (array i32)) + + ;; RTRIP: (type $10 (func (param (ref $i16_array) (ref $i32_array) (ref $i64_array) (ref $f32_array) (ref $f64_array) (ref $v128_array)))) + + ;; RTRIP: (type $11 (func (param (ref $i8_array)))) + + ;; RTRIP: (type $12 (func (param (ref $imm_i8_array) (ref $imm_i32_array)))) ;; RTRIP: (global $arr (ref $i8_array) (array.new_default $i8_array ;; RTRIP-NEXT: (i32.const 4) @@ -995,25 +1035,167 @@ (i32.store8 (type $i8_array) (global.get $arr) (i32.const 1) (unreachable)) ) - ;; CHECK: (func $loads_index_unreachable (type $0) + (type $i16_array (array (mut i16))) + (type $i32_array (array (mut i32))) + (type $i64_array (array (mut i64))) + (type $f32_array (array (mut f32))) + (type $f64_array (array (mut f64))) + (type $v128_array (array (mut v128))) + (type $imm_i8_array (array i8)) + (type $imm_i32_array (array i32)) + + ;; CHECK: (func $expanded_types (type $10) (param $a16 (ref $i16_array)) (param $a32 (ref $i32_array)) (param $a64 (ref $i64_array)) (param $af32 (ref $f32_array)) (param $af64 (ref $f64_array)) (param $av (ref $v128_array)) + ;; CHECK-NEXT: (i32.store16 (type $i16_array) + ;; CHECK-NEXT: (local.get $a16) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.load (type $i8_array) - ;; CHECK-NEXT: (global.get $arr) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (i32.load16_u (type $i16_array) + ;; CHECK-NEXT: (local.get $a16) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store (type $i32_array) + ;; CHECK-NEXT: (local.get $a32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load (type $i32_array) + ;; CHECK-NEXT: (local.get $a32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i64.store (type $i64_array) + ;; CHECK-NEXT: (local.get $a64) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i64.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load (type $i64_array) + ;; CHECK-NEXT: (local.get $a64) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (f32.store (type $f32_array) + ;; CHECK-NEXT: (local.get $af32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (f32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (f32.load (type $f32_array) + ;; CHECK-NEXT: (local.get $af32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (f64.store (type $f64_array) + ;; CHECK-NEXT: (local.get $af64) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (f64.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (f64.load (type $f64_array) + ;; CHECK-NEXT: (local.get $af64) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (v128.store (type $v128_array) + ;; CHECK-NEXT: (local.get $av) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (v128.const i32x4 0x00000001 0x00000002 0x00000003 0x00000004) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (v128.load (type $v128_array) + ;; CHECK-NEXT: (local.get $av) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; RTRIP: (func $loads_index_unreachable (type $0) + ;; RTRIP: (func $expanded_types (type $10) (param $a16 (ref $i16_array)) (param $a32 (ref $i32_array)) (param $a64 (ref $i64_array)) (param $af32 (ref $f32_array)) (param $af64 (ref $f64_array)) (param $av (ref $v128_array)) + ;; RTRIP-NEXT: (i32.store16 (type $i16_array) + ;; RTRIP-NEXT: (local.get $a16) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i32.const 1) + ;; RTRIP-NEXT: ) ;; RTRIP-NEXT: (drop - ;; RTRIP-NEXT: (global.get $arr) + ;; RTRIP-NEXT: (i32.load16_u (type $i16_array) + ;; RTRIP-NEXT: (local.get $a16) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (i32.store (type $i32_array) + ;; RTRIP-NEXT: (local.get $a32) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i32.const 2) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i32.load (type $i32_array) + ;; RTRIP-NEXT: (local.get $a32) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (i64.store (type $i64_array) + ;; RTRIP-NEXT: (local.get $a64) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i64.const 3) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i64.load (type $i64_array) + ;; RTRIP-NEXT: (local.get $a64) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (f32.store (type $f32_array) + ;; RTRIP-NEXT: (local.get $af32) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (f32.const 4) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (f32.load (type $f32_array) + ;; RTRIP-NEXT: (local.get $af32) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (f64.store (type $f64_array) + ;; RTRIP-NEXT: (local.get $af64) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (f64.const 5) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (f64.load (type $f64_array) + ;; RTRIP-NEXT: (local.get $af64) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (v128.store (type $v128_array) + ;; RTRIP-NEXT: (local.get $av) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (v128.const i32x4 0x00000001 0x00000002 0x00000003 0x00000004) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (v128.load (type $v128_array) + ;; RTRIP-NEXT: (local.get $av) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) ;; RTRIP-NEXT: ) - ;; RTRIP-NEXT: (unreachable) ;; RTRIP-NEXT: ) - (func $loads_index_unreachable - (drop (i32.load8_u (type $i8_array) (global.get $arr) (unreachable))) + (func $expanded_types (param $a16 (ref $i16_array)) (param $a32 (ref $i32_array)) (param $a64 (ref $i64_array)) (param $af32 (ref $f32_array)) (param $af64 (ref $f64_array)) (param $av (ref $v128_array)) + (i32.store16 (type $i16_array) (local.get $a16) (i32.const 0) (i32.const 1)) + (drop (i32.load16_u (type $i16_array) (local.get $a16) (i32.const 0))) + (i32.store (type $i32_array) (local.get $a32) (i32.const 0) (i32.const 2)) + (drop (i32.load (type $i32_array) (local.get $a32) (i32.const 0))) + (i64.store (type $i64_array) (local.get $a64) (i32.const 0) (i64.const 3)) + (drop (i64.load (type $i64_array) (local.get $a64) (i32.const 0))) + (f32.store (type $f32_array) (local.get $af32) (i32.const 0) (f32.const 4.0)) + (drop (f32.load (type $f32_array) (local.get $af32) (i32.const 0))) + (f64.store (type $f64_array) (local.get $af64) (i32.const 0) (f64.const 5.0)) + (drop (f64.load (type $f64_array) (local.get $af64) (i32.const 0))) + (v128.store (type $v128_array) (local.get $av) (i32.const 0) (v128.const i32x4 1 2 3 4)) + (drop (v128.load (type $v128_array) (local.get $av) (i32.const 0))) ) - ;; CHECK: (func $immediates (type $2) (param $arr (ref $i8_array)) + ;; CHECK: (func $immediates (type $11) (param $arr (ref $i8_array)) ;; CHECK-NEXT: (i32.store8 (type $i8_array) offset=4 ;; CHECK-NEXT: (local.get $arr) ;; CHECK-NEXT: (i32.const 0) @@ -1048,7 +1230,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; RTRIP: (func $immediates (type $2) (param $arr (ref $i8_array)) + ;; RTRIP: (func $immediates (type $11) (param $arr (ref $i8_array)) ;; RTRIP-NEXT: (i32.store8 (type $i8_array) offset=4 ;; RTRIP-NEXT: (local.get $arr) ;; RTRIP-NEXT: (i32.const 0) @@ -1091,4 +1273,37 @@ (drop (i32.load (type $i8_array) offset=8 align=4 (local.get $arr) (i32.const 0))) (drop (i32.load (type $i8_array) offset=12 align=2 (local.get $arr) (i32.const 0))) ) + + ;; CHECK: (func $immutable_loads (type $12) (param $imm8 (ref $imm_i8_array)) (param $imm32 (ref $imm_i32_array)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load8_u (type $imm_i8_array) + ;; CHECK-NEXT: (local.get $imm8) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load (type $imm_i32_array) + ;; CHECK-NEXT: (local.get $imm32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; RTRIP: (func $immutable_loads (type $12) (param $imm8 (ref $imm_i8_array)) (param $imm32 (ref $imm_i32_array)) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i32.load8_u (type $imm_i8_array) + ;; RTRIP-NEXT: (local.get $imm8) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i32.load (type $imm_i32_array) + ;; RTRIP-NEXT: (local.get $imm32) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + (func $immutable_loads (param $imm8 (ref $imm_i8_array)) (param $imm32 (ref $imm_i32_array)) + (drop (i32.load8_u (type $imm_i8_array) (local.get $imm8) (i32.const 0))) + (drop (i32.load (type $imm_i32_array) (local.get $imm32) (i32.const 0))) + ) ) diff --git a/test/lit/validation/array-multibyte-invalid.wast b/test/lit/validation/array-multibyte-invalid.wast new file mode 100644 index 00000000000..263e0befc16 --- /dev/null +++ b/test/lit/validation/array-multibyte-invalid.wast @@ -0,0 +1,31 @@ +;; RUN: not wasm-opt -all %s 2>&1 | filecheck %s + +(module + (type $imm (array i8)) + (type $any_arr (array (mut anyref))) + + ;; CHECK: unexpected false: array store type must be mutable + (func $store-immutable (param $a (ref $imm)) + (i32.store8 (type $imm) (local.get $a) (i32.const 0) (i32.const 0)) + ) + + ;; CHECK: unexpected false: array load type must be a numeric type + (func $load-non-numeric (param $a (ref $any_arr)) + (drop (i32.load8_u (type $any_arr) (local.get $a) (i32.const 0))) + ) + + ;; CHECK: unexpected false: array store type must be a numeric type + (func $store-non-numeric (param $a (ref $any_arr)) + (i32.store8 (type $any_arr) (local.get $a) (i32.const 0) (i32.const 0)) + ) + + ;; CHECK: unexpected false: alignment must not exceed natural + (func $bad-align (param $a (ref $imm)) + (drop (i32.load8_u (type $imm) align=2 (local.get $a) (i32.const 0))) + ) + + ;; CHECK: unexpected false: offset must be u32 + (func $bad-offset (param $a (ref $imm)) + (drop (i32.load8_u (type $imm) offset=4294967296 (local.get $a) (i32.const 0))) + ) +)