diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 31a3aa253e3..be69c38c8bc 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -578,14 +578,19 @@ struct NullInstrParserCtx { MemoryOrder) { return Ok{}; } - template - Result<> makeArrayLoad( - Index, const std::vector&, Type, int, bool, HeapTypeT) { + template + Result<> makeArrayLoad(Index, + const std::vector&, + Type, + int, + bool, + MemargT, + HeapTypeT) { return Ok{}; } - template - Result<> - makeArrayStore(Index, const std::vector&, Type, int, HeapTypeT) { + template + Result<> makeArrayStore( + Index, const std::vector&, Type, int, MemargT, HeapTypeT) { return Ok{}; } Result<> makeAtomicRMW(Index, @@ -2388,17 +2393,23 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { Type type, int bytes, bool signed_, + Memarg memarg, HeapTypeT arrayType) { - return withLoc(pos, - irBuilder.makeArrayLoad(arrayType, bytes, signed_, type)); + return withLoc( + pos, + irBuilder.makeArrayLoad( + arrayType, bytes, signed_, memarg.offset, memarg.align, type)); } Result<> makeArrayStore(Index pos, const std::vector& annotations, Type type, int bytes, + Memarg memarg, HeapTypeT arrayType) { - return withLoc(pos, irBuilder.makeArrayStore(arrayType, bytes, type)); + return withLoc(pos, + irBuilder.makeArrayStore( + arrayType, bytes, memarg.offset, memarg.align, type)); } Result<> makeAtomicRMW(Index pos, diff --git a/src/parser/parsers.h b/src/parser/parsers.h index d6f2297ff57..7066b3516d6 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -1807,8 +1807,13 @@ Result<> makeLoad(Ctx& ctx, bool signed_, int bytes, bool isAtomic) { + auto mem = maybeMemidx(ctx); + CHECK_ERR(mem); if (ctx.in.takeSExprStart("type"sv)) { + if (mem) { + return ctx.in.err("memory index is not allowed for array load"); + } auto arrayType = typeidx(ctx); CHECK_ERR(arrayType); @@ -1816,13 +1821,13 @@ Result<> makeLoad(Ctx& ctx, return ctx.in.err("expected end of type use"); } + auto arg = memarg(ctx, bytes); + CHECK_ERR(arg); + return ctx.makeArrayLoad( - pos, annotations, type, bytes, signed_, *arrayType); + pos, annotations, type, bytes, signed_, *arg, *arrayType); } - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); - // We could only parse this when `isAtomic`, but this way gives a clearer // error when a memorder is given for non-atomic operations // since the next token can never be mistaken for a `memOrder`. @@ -1855,7 +1860,13 @@ Result<> makeStore(Ctx& ctx, Type type, int bytes, bool isAtomic) { + auto mem = maybeMemidx(ctx); + CHECK_ERR(mem); + if (ctx.in.takeSExprStart("type"sv)) { + if (mem) { + return ctx.in.err("memory index is not allowed for array store"); + } auto arrayType = typeidx(ctx); CHECK_ERR(arrayType); @@ -1863,10 +1874,11 @@ Result<> makeStore(Ctx& ctx, return ctx.in.err("expected end of type use"); } - return ctx.makeArrayStore(pos, annotations, type, bytes, *arrayType); + auto arg = memarg(ctx, bytes); + CHECK_ERR(arg); + + return ctx.makeArrayStore(pos, annotations, type, bytes, *arg, *arrayType); } - auto mem = maybeMemidx(ctx); - CHECK_ERR(mem); auto maybeOrder = maybeMemOrder(ctx); CHECK_ERR(maybeOrder); diff --git a/src/passes/DeAlign.cpp b/src/passes/DeAlign.cpp index 15adc2ed644..4de36cc88ce 100644 --- a/src/passes/DeAlign.cpp +++ b/src/passes/DeAlign.cpp @@ -36,6 +36,10 @@ struct DeAlign : public WalkerPass> { void visitStore(Store* curr) { curr->align = 1; } void visitSIMDLoad(SIMDLoad* curr) { curr->align = 1; } + + void visitArrayLoad(ArrayLoad* curr) { curr->align = 1; } + + void visitArrayStore(ArrayStore* curr) { curr->align = 1; } }; Pass* createDeAlignPass() { return new DeAlign(); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index d171caa3080..63381ae552c 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2479,6 +2479,12 @@ struct PrintExpressionContents printMinor(o, "type "); printHeapTypeName(curr->ref->type.getHeapType()); o << ')'; + if (curr->offset) { + o << " offset=" << curr->offset; + } + if (curr->align != curr->bytes) { + o << " align=" << curr->align; + } } void visitArrayStore(ArrayStore* curr) { @@ -2492,6 +2498,12 @@ struct PrintExpressionContents printMinor(o, "type "); printHeapTypeName(curr->ref->type.getHeapType()); o << ')'; + if (curr->offset) { + o << " offset=" << curr->offset; + } + if (curr->align != curr->bytes) { + o << " align=" << curr->align; + } } void visitArrayLen(ArrayLen* curr) { printMedium(o, "array.len"); } void visitArrayCopy(ArrayCopy* curr) { diff --git a/src/wasm-builder.h b/src/wasm-builder.h index de26f090dc5..0e1b8fecc2a 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1162,12 +1162,16 @@ class Builder { } ArrayLoad* makeArrayLoad(unsigned bytes, bool signed_, + Address offset, + Address align, Expression* ref, Expression* index, Type type) { auto* ret = wasm.allocator.alloc(); ret->bytes = bytes; ret->signed_ = signed_; + ret->offset = offset; + ret->align = align ? align : Address(bytes); ret->ref = ref; ret->index = index; ret->type = type; @@ -1176,11 +1180,15 @@ class Builder { } ArrayStore* makeArrayStore(unsigned bytes, + Address offset, + Address align, Expression* ref, Expression* index, Expression* value) { auto* ret = wasm.allocator.alloc(); ret->bytes = bytes; + ret->offset = offset; + ret->align = align ? align : Address(bytes); ret->ref = ref; ret->index = index; ret->value = value; diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 79ac534864f..836151ec93e 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -762,6 +762,8 @@ DELEGATE_FIELD_CASE_END(ArraySet) DELEGATE_FIELD_CASE_START(ArrayLoad) DELEGATE_FIELD_CHILD(ArrayLoad, index) DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(ArrayLoad, ref) +DELEGATE_FIELD_ADDRESS(ArrayLoad, offset) +DELEGATE_FIELD_ADDRESS(ArrayLoad, align) DELEGATE_FIELD_INT(ArrayLoad, bytes) DELEGATE_FIELD_INT(ArrayLoad, signed_) DELEGATE_FIELD_TYPE(ArrayLoad, type) @@ -771,6 +773,8 @@ DELEGATE_FIELD_CASE_START(ArrayStore) DELEGATE_FIELD_CHILD(ArrayStore, value) DELEGATE_FIELD_CHILD(ArrayStore, index) DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(ArrayStore, ref) +DELEGATE_FIELD_ADDRESS(ArrayStore, offset) +DELEGATE_FIELD_ADDRESS(ArrayStore, align) DELEGATE_FIELD_INT(ArrayStore, bytes) DELEGATE_FIELD_CASE_END(ArrayStore) diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 8c07ce54649..ea0de1662dc 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -253,9 +253,17 @@ class IRBuilder : public UnifiedExpressionVisitor> { Result<> makeArrayNewFixed(HeapType type, uint32_t arity); Result<> makeArrayGet(HeapType type, bool signed_, MemoryOrder order); Result<> makeArraySet(HeapType type, MemoryOrder order); - Result<> - makeArrayLoad(HeapType arrayType, unsigned bytes, bool signed_, Type type); - Result<> makeArrayStore(HeapType arrayType, unsigned bytes, Type type); + Result<> makeArrayLoad(HeapType arrayType, + unsigned bytes, + bool signed_, + Address offset, + Address align, + Type type); + Result<> makeArrayStore(HeapType arrayType, + unsigned bytes, + Address offset, + Address align, + Type type); Result<> makeArrayLen(); Result<> makeArrayCopy(HeapType destType, HeapType srcType); Result<> makeArrayFill(HeapType type); diff --git a/src/wasm.h b/src/wasm.h index f5cade544e6..641310653eb 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1918,6 +1918,8 @@ class ArrayLoad : public SpecificExpression { uint8_t bytes; bool signed_ = false; + Address offset = 0; + Address align = 0; Expression* ref; Expression* index; @@ -1930,6 +1932,8 @@ class ArrayStore : public SpecificExpression { ArrayStore(MixedArena& allocator) {} uint8_t bytes; + Address offset = 0; + Address align = 0; Expression* ref; Expression* index; Expression* value; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 34ab5ec51ee..00de9f37e5b 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3394,7 +3394,8 @@ Result<> WasmBinaryReader::readLoad(unsigned bytes, bool signed_, Type type) { auto [mem, align, offset, backing] = getMemarg(); if (backing == BackingType::Array) { HeapType arrayType = getIndexedHeapType(); - return builder.makeArrayLoad(arrayType, bytes, signed_, type); + return builder.makeArrayLoad( + arrayType, bytes, signed_, offset, align, type); } return builder.makeLoad(bytes, signed_, offset, align, type, mem); } @@ -3403,7 +3404,7 @@ Result<> WasmBinaryReader::readStore(unsigned bytes, Type type) { auto [mem, align, offset, backing] = getMemarg(); if (backing == BackingType::Array) { HeapType arrayType = getIndexedHeapType(); - return builder.makeArrayStore(arrayType, bytes, type); + return builder.makeArrayStore(arrayType, bytes, offset, align, type); } return builder.makeStore(bytes, offset, align, type, mem); } @@ -4111,12 +4112,10 @@ Result<> WasmBinaryReader::readInst() { return builder.makeElemDrop(elem); } case BinaryConsts::F32_F16LoadMem: { - auto [mem, align, offset, backing] = getMemarg(); - return builder.makeLoad(2, false, offset, align, Type::f32, mem); + return readLoad(2, false, Type::f32); } case BinaryConsts::F32_F16StoreMem: { - auto [mem, align, offset, backing] = getMemarg(); - return builder.makeStore(2, offset, align, Type::f32, mem); + return readStore(2, Type::f32); } } return Err{"unknown misc operation: " + std::to_string(op)}; @@ -4666,12 +4665,10 @@ Result<> WasmBinaryReader::readInst() { case BinaryConsts::V128Const: return builder.makeConst(getVec128Literal()); case BinaryConsts::V128Store: { - auto [mem, align, offset, backing] = getMemarg(); - return builder.makeStore(16, offset, align, Type::v128, mem); + return readStore(16, Type::v128); } case BinaryConsts::V128Load: { - auto [mem, align, offset, backing] = getMemarg(); - return builder.makeLoad(16, false, offset, align, Type::v128, mem); + return readLoad(16, false, Type::v128); } case BinaryConsts::V128Load8Splat: { auto [mem, align, offset, backing] = getMemarg(); @@ -5759,6 +5756,7 @@ WasmBinaryReader::readMemoryAccess(bool isAtomic, bool isRMW) { throwError( "Memory index and memory order are not allowed for array backing."); } + offset = getU32LEB(); } else { WASM_UNREACHABLE("Invalid backing type"); } diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 3a67d9c5a50..7c7ba089523 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2463,8 +2463,11 @@ Result<> IRBuilder::makeArraySet(HeapType type, MemoryOrder order) { return Ok{}; } -Result<> -IRBuilder::makeArrayStore(HeapType arrayType, unsigned bytes, Type type) { +Result<> IRBuilder::makeArrayStore(HeapType arrayType, + unsigned bytes, + Address offset, + Address align, + Type type) { if (!arrayType.isArray()) { return Err{"expected array type annotation on array store"}; } @@ -2473,13 +2476,16 @@ IRBuilder::makeArrayStore(HeapType arrayType, unsigned bytes, Type type) { CHECK_ERR(ChildPopper{*this}.visitArrayStore(&curr, arrayType, type)); CHECK_ERR(validateTypeAnnotation(arrayType, curr.ref)); - push(builder.makeArrayStore(bytes, curr.ref, curr.index, curr.value)); + push(builder.makeArrayStore( + bytes, offset, align, curr.ref, curr.index, curr.value)); return Ok{}; } Result<> IRBuilder::makeArrayLoad(HeapType arrayType, unsigned bytes, bool signed_, + Address offset, + Address align, Type type) { if (!arrayType.isArray()) { return Err{"expected array type annotation on array load"}; @@ -2489,7 +2495,8 @@ Result<> IRBuilder::makeArrayLoad(HeapType arrayType, CHECK_ERR(ChildPopper{*this}.visitArrayLoad(&curr, arrayType)); CHECK_ERR(validateTypeAnnotation(arrayType, curr.ref)); - push(builder.makeArrayLoad(bytes, signed_, curr.ref, curr.index, type)); + push(builder.makeArrayLoad( + bytes, signed_, offset, align, curr.ref, curr.index, type)); return Ok{}; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index b065b7aad36..ee3a77a5f2d 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2828,24 +2828,34 @@ void BinaryInstWriter::visitArraySet(ArraySet* curr) { } void BinaryInstWriter::visitArrayLoad(ArrayLoad* curr) { + if (curr->type == Type::unreachable) { + return; + } if (curr->ref->type.isNull()) { emitUnreachable(); return; } emitLoadOpcode(curr->bytes, curr->signed_, curr->type); - uint32_t alignmentBits = BinaryConsts::HasBackingArrayMask; + uint32_t alignmentBits = + Bits::log2(curr->align) | BinaryConsts::HasBackingArrayMask; o << U32LEB(alignmentBits); + o << U32LEB(curr->offset); parent.writeIndexedHeapType(curr->ref->type.getHeapType()); } void BinaryInstWriter::visitArrayStore(ArrayStore* curr) { + if (curr->type == Type::unreachable) { + return; + } if (curr->ref->type.isNull()) { emitUnreachable(); return; } emitStoreOpcode(curr->bytes, curr->value->type); - uint32_t alignmentBits = BinaryConsts::HasBackingArrayMask; + uint32_t alignmentBits = + Bits::log2(curr->align) | BinaryConsts::HasBackingArrayMask; o << U32LEB(alignmentBits); + o << U32LEB(curr->offset); parent.writeIndexedHeapType(curr->ref->type.getHeapType()); } diff --git a/test/lit/array-multibyte.wast b/test/lit/array-multibyte.wast index eab1bc21f46..ece8506893e 100644 --- a/test/lit/array-multibyte.wast +++ b/test/lit/array-multibyte.wast @@ -16,9 +16,13 @@ ;; RTRIP: (type $i8_array (array (mut i8))) (type $i8_array (array (mut i8))) + ;; CHECK: (type $2 (func (param (ref $i8_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: (global $arr (ref $i8_array) (array.new_default $i8_array ;; RTRIP-NEXT: (i32.const 4) ;; RTRIP-NEXT: )) @@ -1008,4 +1012,83 @@ (func $loads_index_unreachable (drop (i32.load8_u (type $i8_array) (global.get $arr) (unreachable))) ) + + ;; CHECK: (func $immediates (type $2) (param $arr (ref $i8_array)) + ;; CHECK-NEXT: (i32.store8 (type $i8_array) offset=4 + ;; CHECK-NEXT: (local.get $arr) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store (type $i8_array) offset=8 + ;; CHECK-NEXT: (local.get $arr) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store (type $i8_array) offset=12 align=2 + ;; CHECK-NEXT: (local.get $arr) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load8_u (type $i8_array) offset=4 + ;; CHECK-NEXT: (local.get $arr) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load (type $i8_array) offset=8 + ;; CHECK-NEXT: (local.get $arr) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.load (type $i8_array) offset=12 align=2 + ;; CHECK-NEXT: (local.get $arr) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; RTRIP: (func $immediates (type $2) (param $arr (ref $i8_array)) + ;; RTRIP-NEXT: (i32.store8 (type $i8_array) offset=4 + ;; RTRIP-NEXT: (local.get $arr) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i32.const 42) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (i32.store (type $i8_array) offset=8 + ;; RTRIP-NEXT: (local.get $arr) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i32.const 1337) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (i32.store (type $i8_array) offset=12 align=2 + ;; RTRIP-NEXT: (local.get $arr) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i32.const 1337) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i32.load8_u (type $i8_array) offset=4 + ;; RTRIP-NEXT: (local.get $arr) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i32.load (type $i8_array) offset=8 + ;; RTRIP-NEXT: (local.get $arr) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (i32.load (type $i8_array) offset=12 align=2 + ;; RTRIP-NEXT: (local.get $arr) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + (func $immediates (param $arr (ref $i8_array)) + (i32.store8 (type $i8_array) offset=4 align=1 (local.get $arr) (i32.const 0) (i32.const 42)) + (i32.store (type $i8_array) offset=8 align=4 (local.get $arr) (i32.const 0) (i32.const 1337)) + (i32.store (type $i8_array) offset=12 align=2 (local.get $arr) (i32.const 0) (i32.const 1337)) + (drop (i32.load8_u (type $i8_array) offset=4 align=1 (local.get $arr) (i32.const 0))) + (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))) + ) ) diff --git a/test/lit/parse-bad-array-multibyte-memidx.wast b/test/lit/parse-bad-array-multibyte-memidx.wast new file mode 100644 index 00000000000..df1f92b5ade --- /dev/null +++ b/test/lit/parse-bad-array-multibyte-memidx.wast @@ -0,0 +1,10 @@ +;; RUN: not wasm-opt -all %s 2>&1 | filecheck %s + +(module + (memory $m 1 1) + (type $arr (array (mut i8))) + ;; CHECK: Fatal: {{.*}}: error: memory index is not allowed for array load + (func $load-memidx (param $a (ref $arr)) + (drop (i32.load8_u $m (type $arr) (local.get $a) (i32.const 0))) + ) +)