diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gen-s-parser.inc | 58 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 2 | ||||
-rw-r--r-- | src/ir/cost.h | 8 | ||||
-rw-r--r-- | src/ir/effects.h | 19 | ||||
-rw-r--r-- | src/ir/module-utils.cpp | 7 | ||||
-rw-r--r-- | src/ir/possible-contents.cpp | 17 | ||||
-rw-r--r-- | src/passes/Print.cpp | 25 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 11 | ||||
-rw-r--r-- | src/wasm-binary.h | 5 | ||||
-rw-r--r-- | src/wasm-builder.h | 28 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 20 | ||||
-rw-r--r-- | src/wasm-delegations.def | 2 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 136 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 2 | ||||
-rw-r--r-- | src/wasm.h | 34 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 50 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 22 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 31 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 122 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 18 | ||||
-rw-r--r-- | src/wasm/wat-parser.cpp | 35 | ||||
-rw-r--r-- | src/wasm2js.h | 8 |
22 files changed, 629 insertions, 31 deletions
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index cbd2c027c..64027fc59 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -16,6 +16,9 @@ switch (buf[0]) { case 'c': if (op == "array.copy"sv) { return makeArrayCopy(s); } goto parse_error; + case 'f': + if (op == "array.fill"sv) { return makeArrayFill(s); } + goto parse_error; case 'g': { switch (buf[9]) { case '\0': @@ -35,9 +38,20 @@ switch (buf[0]) { default: goto parse_error; } } - case 'i': - if (op == "array.init_static"sv) { return makeArrayNewFixed(s); } - goto parse_error; + case 'i': { + switch (buf[11]) { + case 'd': + if (op == "array.init_data"sv) { return makeArrayInit(s, InitData); } + goto parse_error; + case 'e': + if (op == "array.init_elem"sv) { return makeArrayInit(s, InitElem); } + goto parse_error; + case 's': + if (op == "array.init_static"sv) { return makeArrayNewFixed(s); } + goto parse_error; + default: goto parse_error; + } + } case 'l': if (op == "array.len"sv) { return makeArrayLen(s); } goto parse_error; @@ -3607,6 +3621,13 @@ switch (buf[0]) { return *ret; } goto parse_error; + case 'f': + if (op == "array.fill"sv) { + auto ret = makeArrayFill(ctx, pos); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; case 'g': { switch (buf[9]) { case '\0': @@ -3638,13 +3659,32 @@ switch (buf[0]) { default: goto parse_error; } } - case 'i': - if (op == "array.init_static"sv) { - auto ret = makeArrayNewFixed(ctx, pos); - CHECK_ERR(ret); - return *ret; + case 'i': { + switch (buf[11]) { + case 'd': + if (op == "array.init_data"sv) { + auto ret = makeArrayInit(ctx, pos, InitData); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; + case 'e': + if (op == "array.init_elem"sv) { + auto ret = makeArrayInit(ctx, pos, InitElem); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; + case 's': + if (op == "array.init_static"sv) { + auto ret = makeArrayNewFixed(ctx, pos); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; + default: goto parse_error; } - goto parse_error; + } case 'l': if (op == "array.len"sv) { auto ret = makeArrayLen(ctx, pos); diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 9c4946342..9d68a9fe8 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -170,6 +170,8 @@ void ReFinalize::visitArrayGet(ArrayGet* curr) { curr->finalize(); } void ReFinalize::visitArraySet(ArraySet* curr) { curr->finalize(); } void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); } void ReFinalize::visitArrayCopy(ArrayCopy* curr) { curr->finalize(); } +void ReFinalize::visitArrayFill(ArrayFill* curr) { curr->finalize(); } +void ReFinalize::visitArrayInit(ArrayInit* curr) { curr->finalize(); } void ReFinalize::visitRefAs(RefAs* curr) { curr->finalize(); } void ReFinalize::visitStringNew(StringNew* curr) { curr->finalize(); } void ReFinalize::visitStringConst(StringConst* curr) { curr->finalize(); } diff --git a/src/ir/cost.h b/src/ir/cost.h index 3e12318b6..c19441555 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -661,6 +661,14 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { return 6 + visit(curr->destRef) + visit(curr->destIndex) + visit(curr->srcRef) + visit(curr->srcIndex) + visit(curr->length); } + CostType visitArrayFill(ArrayFill* curr) { + return 6 + visit(curr->ref) + visit(curr->index) + visit(curr->value) + + visit(curr->size); + } + CostType visitArrayInit(ArrayInit* curr) { + return 6 + visit(curr->ref) + visit(curr->index) + visit(curr->offset) + + visit(curr->size); + } CostType visitRefAs(RefAs* curr) { return 1 + visit(curr->value); } CostType visitStringNew(StringNew* curr) { return 8 + visit(curr->ptr) + maybeVisit(curr->length) + diff --git a/src/ir/effects.h b/src/ir/effects.h index 59ef8bace..c5251ae64 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -799,6 +799,25 @@ private: // traps when a ref is null, or when out of bounds. parent.implicitTrap = true; } + void visitArrayFill(ArrayFill* curr) { + if (curr->ref->type.isNull()) { + parent.trap = true; + return; + } + parent.writesArray = true; + // Traps when the destination is null or when out of bounds. + parent.implicitTrap = true; + } + void visitArrayInit(ArrayInit* curr) { + if (curr->ref->type.isNull()) { + parent.trap = true; + return; + } + parent.writesArray = true; + // Traps when the destination is null, when out of bounds in source or + // destination, or when the source segment has been dropped. + parent.implicitTrap = true; + } void visitRefAs(RefAs* curr) { if (curr->op == ExternInternalize || curr->op == ExternExternalize) { // These conversions are infallible. diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index a2abcd5b5..efab9e20b 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -74,6 +74,13 @@ struct CodeScanner counts.note(curr->type); } else if (curr->is<ArrayNewFixed>()) { counts.note(curr->type); + } else if (auto* copy = curr->dynCast<ArrayCopy>()) { + counts.note(copy->destRef->type); + counts.note(copy->srcRef->type); + } else if (auto* fill = curr->dynCast<ArrayFill>()) { + counts.note(fill->ref->type); + } else if (auto* init = curr->dynCast<ArrayInit>()) { + counts.note(init->ref->type); } else if (auto* cast = curr->dynCast<RefCast>()) { counts.note(cast->type); } else if (auto* cast = curr->dynCast<RefTest>()) { diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 05faf7b1e..8ed2118fe 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -988,7 +988,22 @@ struct InfoCollector auto* set = builder.makeArraySet(curr->destRef, curr->destIndex, get); visitArraySet(set); } - + void visitArrayFill(ArrayFill* curr) { + if (curr->type == Type::unreachable) { + return; + } + Builder builder(*getModule()); + auto* set = builder.makeArraySet(curr->ref, curr->index, curr->value); + visitArraySet(set); + } + void visitArrayInit(ArrayInit* curr) { + if (curr->type == Type::unreachable) { + return; + } + // TODO: Modeling the write to the array can be similar to the above, but + // how should the read from the segment be modeled? + WASM_UNREACHABLE("unimplemented"); + } void visitStringNew(StringNew* curr) { if (curr->type == Type::unreachable) { return; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 9f765bb79..650a17203 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2273,7 +2273,6 @@ struct PrintExpressionContents switch (curr->op) { case NewData: printMedium(o, "data"); - break; case NewElem: printMedium(o, "elem"); @@ -2327,6 +2326,30 @@ struct PrintExpressionContents o << ' '; TypeNamePrinter(o, wasm).print(curr->srcRef->type.getHeapType()); } + void visitArrayFill(ArrayFill* curr) { + if (printUnreachableOrNullReplacement(curr->ref)) { + return; + } + printMedium(o, "array.fill "); + TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType()); + } + void visitArrayInit(ArrayInit* curr) { + if (printUnreachableOrNullReplacement(curr->ref)) { + return; + } + switch (curr->op) { + case InitData: + printMedium(o, "array.init_data "); + break; + case InitElem: + printMedium(o, "array.init_elem "); + break; + default: + WASM_UNREACHABLE("unexpected op"); + } + TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType()); + o << " $" << curr->segment; + } void visitRefAs(RefAs* curr) { switch (curr->op) { case RefAsNonNull: diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 943448cfa..228b3afc1 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -218,6 +218,17 @@ struct ReferenceFinder : public PostWalker<ReferenceFinder> { } WASM_UNREACHABLE("unexpected op"); } + void visitArrayInit(ArrayInit* curr) { + switch (curr->op) { + case InitData: + note({ModuleElementKind::DataSegment, curr->segment}); + return; + case InitElem: + note({ModuleElementKind::ElementSegment, curr->segment}); + return; + } + WASM_UNREACHABLE("unexpected op"); + } }; // Analyze a module to find what things are referenced and what things are used. diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 4f224e1dd..e010135ee 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1138,6 +1138,9 @@ enum ASTNodes { BrOnNonI31 = 0x65, ExternInternalize = 0x70, ExternExternalize = 0x71, + ArrayFill = 0x0f, + ArrayInitData = 0x54, + ArrayInitElem = 0x55, StringNewWTF8 = 0x80, StringNewWTF16 = 0x81, StringConst = 0x82, @@ -1731,6 +1734,8 @@ public: bool maybeVisitArraySet(Expression*& out, uint32_t code); bool maybeVisitArrayLen(Expression*& out, uint32_t code); bool maybeVisitArrayCopy(Expression*& out, uint32_t code); + bool maybeVisitArrayFill(Expression*& out, uint32_t code); + bool maybeVisitArrayInit(Expression*& out, uint32_t code); bool maybeVisitStringNew(Expression*& out, uint32_t code); bool maybeVisitStringConst(Expression*& out, uint32_t code); bool maybeVisitStringMeasure(Expression*& out, uint32_t code); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index dbddb248b..020badf16 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -991,6 +991,34 @@ public: ret->finalize(); return ret; } + ArrayFill* makeArrayFill(Expression* ref, + Expression* index, + Expression* value, + Expression* size) { + auto* ret = wasm.allocator.alloc<ArrayFill>(); + ret->ref = ref; + ret->index = index; + ret->value = value; + ret->size = size; + ret->finalize(); + return ret; + } + ArrayInit* makeArrayInit(ArrayInitOp op, + Name seg, + Expression* ref, + Expression* index, + Expression* offset, + Expression* size) { + auto* ret = wasm.allocator.alloc<ArrayInit>(); + ret->op = op; + ret->segment = seg; + ret->ref = ref; + ret->index = index; + ret->offset = offset; + ret->size = size; + ret->finalize(); + return ret; + } RefAs* makeRefAs(RefAsOp op, Expression* value) { auto* ret = wasm.allocator.alloc<RefAs>(); ret->op = op; diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 480789ca3..a4de0b2ad 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -710,6 +710,26 @@ switch (DELEGATE_ID) { DELEGATE_END(ArrayCopy); break; } + case Expression::Id::ArrayFillId: { + DELEGATE_START(ArrayFill); + DELEGATE_FIELD_CHILD(ArrayFill, size); + DELEGATE_FIELD_CHILD(ArrayFill, value); + DELEGATE_FIELD_CHILD(ArrayFill, index); + DELEGATE_FIELD_CHILD(ArrayFill, ref); + DELEGATE_END(ArrayFill); + break; + } + case Expression::Id::ArrayInitId: { + DELEGATE_START(ArrayInit); + DELEGATE_FIELD_INT(ArrayInit, op); + DELEGATE_FIELD_NAME(ArrayInit, segment); + DELEGATE_FIELD_CHILD(ArrayInit, size); + DELEGATE_FIELD_CHILD(ArrayInit, offset); + DELEGATE_FIELD_CHILD(ArrayInit, index); + DELEGATE_FIELD_CHILD(ArrayInit, ref); + DELEGATE_END(ArrayInit); + break; + } case Expression::Id::RefAsId: { DELEGATE_START(RefAs); DELEGATE_FIELD_INT(RefAs, op); diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index 922bb7c87..0a6471f89 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -83,6 +83,8 @@ DELEGATE(ArrayGet); DELEGATE(ArraySet); DELEGATE(ArrayLen); DELEGATE(ArrayCopy); +DELEGATE(ArrayFill); +DELEGATE(ArrayInit); DELEGATE(RefAs); DELEGATE(StringNew); DELEGATE(StringConst); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 4b7d652b4..b5b03743d 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1758,25 +1758,62 @@ public: size_t destVal = destIndex.getSingleValue().getUnsigned(); size_t srcVal = srcIndex.getSingleValue().getUnsigned(); size_t lengthVal = length.getSingleValue().getUnsigned(); - if (lengthVal >= ArrayLimit) { - hostLimit("allocation failure"); + if (destVal + lengthVal > destData->values.size()) { + trap("oob"); + } + if (srcVal + lengthVal > srcData->values.size()) { + trap("oob"); } std::vector<Literal> copied; copied.resize(lengthVal); for (size_t i = 0; i < lengthVal; i++) { - if (srcVal + i >= srcData->values.size()) { - trap("oob"); - } copied[i] = srcData->values[srcVal + i]; } for (size_t i = 0; i < lengthVal; i++) { - if (destVal + i >= destData->values.size()) { - trap("oob"); - } destData->values[destVal + i] = copied[i]; } return Flow(); } + Flow visitArrayFill(ArrayFill* curr) { + NOTE_ENTER("ArrayFill"); + Flow ref = self()->visit(curr->ref); + if (ref.breaking()) { + return ref; + } + Flow index = self()->visit(curr->index); + if (index.breaking()) { + return index; + } + Flow value = self()->visit(curr->value); + if (value.breaking()) { + return value; + } + Flow size = self()->visit(curr->size); + if (size.breaking()) { + return size; + } + auto data = ref.getSingleValue().getGCData(); + if (!data) { + trap("null ref"); + } + size_t indexVal = index.getSingleValue().getUnsigned(); + Literal fillVal = value.getSingleValue(); + size_t sizeVal = size.getSingleValue().getUnsigned(); + + auto field = curr->ref->type.getHeapType().getArray().element; + fillVal = truncateForPacking(fillVal, field); + + size_t arraySize = data->values.size(); + if (indexVal > arraySize || sizeVal > arraySize || + indexVal + sizeVal > arraySize || indexVal + sizeVal < indexVal) { + trap("out of bounds array access in array.fill"); + } + for (size_t i = 0; i < sizeVal; ++i) { + data->values[indexVal + i] = fillVal; + } + return {}; + } + Flow visitArrayInit(MemoryFill* curr) { WASM_UNREACHABLE("unimp"); } Flow visitRefAs(RefAs* curr) { NOTE_ENTER("RefAs"); Flow flow = visit(curr->value); @@ -2216,6 +2253,18 @@ public: NOTE_ENTER("ArrayNewSeg"); return Flow(NONCONSTANT_FLOW); } + Flow visitArrayCopy(ArrayCopy* curr) { + NOTE_ENTER("ArrayCopy"); + return Flow(NONCONSTANT_FLOW); + } + Flow visitArrayFill(ArrayFill* curr) { + NOTE_ENTER("ArrayFill"); + return Flow(NONCONSTANT_FLOW); + } + Flow visitArrayInit(ArrayInit* curr) { + NOTE_ENTER("ArrayInit"); + return Flow(NONCONSTANT_FLOW); + } Flow visitPop(Pop* curr) { NOTE_ENTER("Pop"); return Flow(NONCONSTANT_FLOW); @@ -3576,6 +3625,77 @@ public: } return Literal(std::make_shared<GCData>(heapType, contents), heapType); } + Flow visitArrayInit(ArrayInit* curr) { + NOTE_ENTER("ArrayInit"); + Flow ref = self()->visit(curr->ref); + if (ref.breaking()) { + return ref; + } + Flow index = self()->visit(curr->index); + if (index.breaking()) { + return index; + } + Flow offset = self()->visit(curr->offset); + if (offset.breaking()) { + return offset; + } + Flow size = self()->visit(curr->size); + if (size.breaking()) { + return size; + } + auto data = ref.getSingleValue().getGCData(); + if (!data) { + trap("null ref"); + } + size_t indexVal = index.getSingleValue().getUnsigned(); + size_t offsetVal = offset.getSingleValue().getUnsigned(); + size_t sizeVal = size.getSingleValue().getUnsigned(); + + size_t arraySize = data->values.size(); + if ((uint64_t)indexVal + sizeVal > arraySize) { + trap("out of bounds array access in array.init"); + } + + Module& wasm = *self()->getModule(); + + switch (curr->op) { + case InitData: { + auto* seg = wasm.getDataSegment(curr->segment); + auto elem = curr->ref->type.getHeapType().getArray().element; + size_t elemSize = elem.getByteSize(); + uint64_t readSize = (uint64_t)sizeVal * elemSize; + if (offsetVal + readSize > seg->data.size()) { + trap("out of bounds segment access in array.init_data"); + } + if (offsetVal + sizeVal > 0 && droppedSegments.count(curr->segment)) { + trap("out of bounds segment access in array.init_data"); + } + for (size_t i = 0; i < sizeVal; i++) { + void* addr = (void*)&seg->data[offsetVal + i * elemSize]; + data->values[indexVal + i] = Literal::makeFromMemory(addr, elem); + } + return {}; + } + case InitElem: { + auto* seg = wasm.getElementSegment(curr->segment); + if ((uint64_t)offsetVal + sizeVal > seg->data.size()) { + trap("out of bounds segment access in array.init"); + } + // TODO: Check whether the segment has been dropped once we support + // dropping element segments. + for (size_t i = 0; i < sizeVal; i++) { + // TODO: This is not correct because it does not preserve the identity + // of references in the table! ArrayNewSeg suffers the same problem. + // Fixing it will require changing how we represent segments, at least + // in the interpreter. + data->values[indexVal + i] = + self()->visit(seg->data[i]).getSingleValue(); + } + return {}; + } + }; + WASM_UNREACHABLE("unexpected op"); + } Flow visitTry(Try* curr) { NOTE_ENTER("Try"); try { diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c684ae58a..c5598ef1e 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -306,6 +306,8 @@ private: Expression* makeArraySet(Element& s); Expression* makeArrayLen(Element& s); Expression* makeArrayCopy(Element& s); + Expression* makeArrayFill(Element& s); + Expression* makeArrayInit(Element& s, ArrayInitOp op); Expression* makeRefAs(Element& s, RefAsOp op); Expression* makeRefAsNonNull(Element& s); Expression* makeStringNew(Element& s, StringNewOp op, bool try_); diff --git a/src/wasm.h b/src/wasm.h index dd989602a..07a2f6bf9 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -568,6 +568,12 @@ enum ArrayNewSegOp { NewElem, }; +// TODO: Deduplicate with ArrayNewSegOp? +enum ArrayInitOp { + InitData, + InitElem, +}; + enum BrOnOp { BrOnNull, BrOnNonNull, @@ -722,6 +728,8 @@ public: ArraySetId, ArrayLenId, ArrayCopyId, + ArrayFillId, + ArrayInitId, RefAsId, StringNewId, StringConstId, @@ -1669,6 +1677,32 @@ public: void finalize(); }; +class ArrayFill : public SpecificExpression<Expression::ArrayFillId> { +public: + ArrayFill(MixedArena& allocator) {} + + Expression* ref; + Expression* index; + Expression* value; + Expression* size; + + void finalize(); +}; + +class ArrayInit : public SpecificExpression<Expression::ArrayInitId> { +public: + ArrayInit(MixedArena& allocator) {} + + ArrayInitOp op; + Name segment; + Expression* ref; + Expression* index; + Expression* offset; + Expression* size; + + void finalize(); +}; + class RefAs : public SpecificExpression<Expression::RefAsId> { public: RefAs(MixedArena& allocator) {} diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index f92f1ae26..e85740614 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -4040,6 +4040,12 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (maybeVisitArrayCopy(curr, opcode)) { break; } + if (maybeVisitArrayFill(curr, opcode)) { + break; + } + if (maybeVisitArrayInit(curr, opcode)) { + break; + } if (maybeVisitStringNew(curr, opcode)) { break; } @@ -7216,6 +7222,50 @@ bool WasmBinaryBuilder::maybeVisitArrayCopy(Expression*& out, uint32_t code) { return true; } +bool WasmBinaryBuilder::maybeVisitArrayFill(Expression*& out, uint32_t code) { + if (code != BinaryConsts::ArrayFill) { + return false; + } + auto heapType = getIndexedHeapType(); + auto* size = popNonVoidExpression(); + auto* value = popNonVoidExpression(); + auto* index = popNonVoidExpression(); + auto* ref = popNonVoidExpression(); + validateHeapTypeUsingChild(ref, heapType); + out = Builder(wasm).makeArrayFill(ref, index, value, size); + return true; +} + +bool WasmBinaryBuilder::maybeVisitArrayInit(Expression*& out, uint32_t code) { + ArrayInitOp op; + switch (code) { + case BinaryConsts::ArrayInitData: + op = InitData; + break; + case BinaryConsts::ArrayInitElem: + op = InitElem; + break; + default: + return false; + } + auto heapType = getIndexedHeapType(); + Index segIdx = getU32LEB(); + auto* size = popNonVoidExpression(); + auto* offset = popNonVoidExpression(); + auto* index = popNonVoidExpression(); + auto* ref = popNonVoidExpression(); + validateHeapTypeUsingChild(ref, heapType); + auto* built = + Builder(wasm).makeArrayInit(op, Name(), ref, index, offset, size); + if (op == InitData) { + dataRefs[segIdx].push_back(&built->segment); + } else { + elemRefs[segIdx].push_back(&built->segment); + } + out = built; + return true; +} + bool WasmBinaryBuilder::maybeVisitStringNew(Expression*& out, uint32_t code) { StringNewOp op; Expression* length = nullptr; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 51df46544..968c8bd45 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -3041,6 +3041,28 @@ Expression* SExpressionWasmBuilder::makeArrayCopy(Element& s) { destRef, destIndex, srcRef, srcIndex, length); } +Expression* SExpressionWasmBuilder::makeArrayFill(Element& s) { + auto heapType = parseHeapType(*s[1]); + auto ref = parseExpression(*s[2]); + validateHeapTypeUsingChild(ref, heapType, s); + auto index = parseExpression(*s[3]); + auto value = parseExpression(*s[4]); + auto size = parseExpression(*s[5]); + return Builder(wasm).makeArrayFill(ref, index, value, size); +} + +Expression* SExpressionWasmBuilder::makeArrayInit(Element& s, ArrayInitOp op) { + auto heapType = parseHeapType(*s[1]); + auto seg = + op == InitData ? getDataSegmentName(*s[2]) : getElemSegmentName(*s[2]); + auto ref = parseExpression(*s[3]); + validateHeapTypeUsingChild(ref, heapType, s); + auto index = parseExpression(*s[4]); + auto offset = parseExpression(*s[5]); + auto size = parseExpression(*s[6]); + return Builder(wasm).makeArrayInit(op, seg, ref, index, offset, size); +} + Expression* SExpressionWasmBuilder::makeRefAs(Element& s, RefAsOp op) { auto* value = parseExpression(s[1]); if (!value->type.isRef() && value->type != Type::unreachable) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 9e22efc0c..286b049aa 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2185,6 +2185,37 @@ void BinaryInstWriter::visitArrayCopy(ArrayCopy* curr) { parent.writeIndexedHeapType(curr->srcRef->type.getHeapType()); } +void BinaryInstWriter::visitArrayFill(ArrayFill* curr) { + if (curr->ref->type.isNull()) { + emitUnreachable(); + return; + } + o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::ArrayFill); + parent.writeIndexedHeapType(curr->ref->type.getHeapType()); +} + +void BinaryInstWriter::visitArrayInit(ArrayInit* curr) { + if (curr->ref->type.isNull()) { + emitUnreachable(); + return; + } + o << int8_t(BinaryConsts::GCPrefix); + switch (curr->op) { + case InitData: + o << U32LEB(BinaryConsts::ArrayInitData); + parent.writeIndexedHeapType(curr->ref->type.getHeapType()); + o << U32LEB(parent.getDataSegmentIndex(curr->segment)); + break; + case InitElem: + o << U32LEB(BinaryConsts::ArrayInitElem); + parent.writeIndexedHeapType(curr->ref->type.getHeapType()); + o << U32LEB(parent.getElementSegmentIndex(curr->segment)); + break; + default: + WASM_UNREACHABLE("unexpected op"); + } +} + void BinaryInstWriter::visitRefAs(RefAs* curr) { switch (curr->op) { case RefAsNonNull: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 951481dac..845f0f6b6 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -462,6 +462,8 @@ public: void visitArraySet(ArraySet* curr); void visitArrayLen(ArrayLen* curr); void visitArrayCopy(ArrayCopy* curr); + void visitArrayFill(ArrayFill* curr); + void visitArrayInit(ArrayInit* curr); void visitFunction(Function* curr); // helpers @@ -2882,8 +2884,10 @@ void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { if (!shouldBeSubType(curr->srcRef->type, Type(HeapType::array, Nullable), curr, - "array.copy source should be an array reference") || - !shouldBeSubType(curr->destRef->type, + "array.copy source should be an array reference")) { + return; + } + if (!shouldBeSubType(curr->destRef->type, Type(HeapType::array, Nullable), curr, "array.copy destination should be an array reference")) { @@ -2891,17 +2895,16 @@ void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { } auto srcHeapType = curr->srcRef->type.getHeapType(); auto destHeapType = curr->destRef->type.getHeapType(); - if (srcHeapType == HeapType::none || destHeapType == HeapType::none) { + if (srcHeapType == HeapType::none || + !shouldBeTrue(srcHeapType.isArray(), + curr, + "array.copy source should be an array reference")) { return; } - if (!shouldBeTrue( - srcHeapType != HeapType::array, - curr, - "array.copy source needs to be a specific array reference") || - !shouldBeTrue( - srcHeapType != HeapType::array, - curr, - "array.copy destination needs to be a specific array reference")) { + if (destHeapType == HeapType::none || + !shouldBeTrue(destHeapType.isArray(), + curr, + "array.copy destination should be an array reference")) { return; } const auto& srcElement = srcHeapType.getArray().element; @@ -2910,7 +2913,102 @@ void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { destElement.type, curr, "array.copy must have the proper types"); - shouldBeTrue(destElement.mutable_, curr, "array.copy type must be mutable"); + shouldBeEqual(srcElement.packedType, + destElement.packedType, + curr, + "array.copy types must match"); + shouldBeTrue( + destElement.mutable_, curr, "array.copy destination must be mutable"); +} + +void FunctionValidator::visitArrayFill(ArrayFill* curr) { + shouldBeTrue(getModule()->features.hasGC(), + curr, + "array.fill requires gc [--enable-gc]"); + shouldBeEqualOrFirstIsUnreachable(curr->index->type, + Type(Type::i32), + curr, + "array.fill index must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->size->type, Type(Type::i32), curr, "array.fill size must be an i32"); + if (curr->type == Type::unreachable) { + return; + } + if (!shouldBeSubType(curr->ref->type, + Type(HeapType::array, Nullable), + curr, + "array.fill destination should be an array reference")) { + return; + } + auto heapType = curr->ref->type.getHeapType(); + if (heapType == HeapType::none || + !shouldBeTrue(heapType.isArray(), + curr, + "array.fill destination should be an array reference")) { + return; + } + auto element = heapType.getArray().element; + shouldBeSubType(curr->value->type, + element.type, + curr, + "array.fill value must match destination element type"); + shouldBeTrue( + element.mutable_, curr, "array.fill destination must be mutable"); +} + +void FunctionValidator::visitArrayInit(ArrayInit* curr) { + shouldBeTrue(getModule()->features.hasGC(), + curr, + "array.init_* requires gc [--enable-gc]"); + shouldBeEqualOrFirstIsUnreachable(curr->index->type, + Type(Type::i32), + curr, + "array.init_* index must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->offset->type, + Type(Type::i32), + curr, + "array.init_* offset must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->size->type, + Type(Type::i32), + curr, + "array.init_* size must be an i32"); + if (curr->type == Type::unreachable) { + return; + } + if (!shouldBeSubType(curr->ref->type, + Type(HeapType::array, Nullable), + curr, + "array.init_* destination must be an array reference")) { + return; + } + auto heapType = curr->ref->type.getHeapType(); + if (heapType == HeapType::none || + !shouldBeTrue(heapType.isArray(), + curr, + "array.init_* destination must be an array reference")) { + return; + } + auto element = heapType.getArray().element; + shouldBeTrue( + element.mutable_, curr, "array.init_* destination must be mutable"); + if (curr->op == InitData) { + shouldBeTrue(getModule()->getDataSegmentOrNull(curr->segment), + curr, + "array.init_data segment must exist"); + shouldBeTrue(element.type.isNumber(), + curr, + "array.init_data destination must be numeric"); + } else { + assert(curr->op == InitElem); + auto* seg = getModule()->getElementSegmentOrNull(curr->segment); + if (!shouldBeTrue(seg, curr, "array.init_elem segment must exist")) { + return; + } + shouldBeSubType(seg->type, + element.type, + curr, + "array.init_elem segment type must match destination type"); + } } void FunctionValidator::visitFunction(Function* curr) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index b21c1849a..47a04d7f8 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1109,6 +1109,24 @@ void ArrayCopy::finalize() { } } +void ArrayFill::finalize() { + if (ref->type == Type::unreachable || index->type == Type::unreachable || + value->type == Type::unreachable || size->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type::none; + } +} + +void ArrayInit::finalize() { + if (ref->type == Type::unreachable || index->type == Type::unreachable || + offset->type == Type::unreachable || size->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type::none; + } +} + void RefAs::finalize() { if (value->type == Type::unreachable) { type = Type::unreachable; diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 567502c93..51527b5e3 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -800,6 +800,9 @@ struct NullInstrParserCtx { InstrT makeArrayCopy(Index, HeapTypeT, HeapTypeT) { return Ok{}; } + template<typename HeapTypeT> InstrT makeArrayFill(Index, HeapTypeT) { + return Ok{}; + } }; // Phase 1: Parse definition spans for top-level module elements and determine @@ -2212,6 +2215,22 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { return push( pos, builder.makeArrayCopy(*destRef, *destIdx, *srcRef, *srcIdx, *len)); } + + Result<> makeArrayFill(Index pos, HeapType type) { + if (!type.isArray()) { + return in.err(pos, "expected array type annotation"); + } + auto size = pop(pos); + CHECK_ERR(size); + auto value = pop(pos); + CHECK_ERR(value); + auto index = pop(pos); + CHECK_ERR(index); + auto ref = pop(pos); + CHECK_ERR(ref); + CHECK_ERR(validateTypeAnnotation(pos, type, *ref)); + return push(pos, builder.makeArrayFill(*ref, *index, *value, *size)); + } }; // ================ @@ -2369,6 +2388,9 @@ Result<typename Ctx::InstrT> makeArrayGet(Ctx&, Index, bool signed_ = false); template<typename Ctx> Result<typename Ctx::InstrT> makeArraySet(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeArrayLen(Ctx&, Index); template<typename Ctx> Result<typename Ctx::InstrT> makeArrayCopy(Ctx&, Index); +template<typename Ctx> Result<typename Ctx::InstrT> makeArrayFill(Ctx&, Index); +template<typename Ctx> +Result<typename Ctx::InstrT> makeArrayInit(Ctx&, Index, ArrayInitOp); template<typename Ctx> Result<typename Ctx::InstrT> makeRefAs(Ctx&, Index, RefAsOp op); template<typename Ctx> @@ -3556,6 +3578,19 @@ Result<typename Ctx::InstrT> makeArrayCopy(Ctx& ctx, Index pos) { } template<typename Ctx> +Result<typename Ctx::InstrT> makeArrayFill(Ctx& ctx, Index pos) { + auto type = typeidx(ctx); + CHECK_ERR(type); + return ctx.makeArrayFill(pos, *type); +} + +template<typename Ctx> +Result<typename Ctx::InstrT> +makeArrayInit(Ctx& ctx, Index pos, ArrayInitOp op) { + return ctx.in.err("unimplemented instruction"); +} + +template<typename Ctx> Result<typename Ctx::InstrT> makeRefAs(Ctx& ctx, Index pos, RefAsOp op) { return ctx.in.err("unimplemented instruction"); } diff --git a/src/wasm2js.h b/src/wasm2js.h index c86788e5a..d33e45c4c 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2339,6 +2339,14 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitArrayFill(ArrayFill* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } + Ref visitArrayInit(ArrayInit* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitStringNew(StringNew* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); |