diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 37 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 22 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 8 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 6 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 34 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 18 |
6 files changed, 101 insertions, 24 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 20edab3cf..314364f38 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -5612,22 +5612,29 @@ bool WasmBinaryBuilder::maybeVisitRttSub(Expression*& out, uint32_t code) { } bool WasmBinaryBuilder::maybeVisitStructNew(Expression*& out, uint32_t code) { - StructNew* curr; - switch (code) { - case BinaryConsts::StructNewWithRtt: - curr = allocator.alloc<StructNew>(); - // ... - break; - case BinaryConsts::StructNewDefaultWithRtt: - curr = allocator.alloc<StructNew>(); - // ... - break; - default: - return false; + if (code != BinaryConsts::StructNewWithRtt && + code != BinaryConsts::StructNewDefaultWithRtt) { + return false; } - WASM_UNREACHABLE("TODO (gc): struct.new"); - curr->finalize(); - out = curr; + auto heapType = getHeapType(); + if (!heapType.isStruct()) { + throwError("Non-heap type for struct.new"); + } + auto* rtt = popNonVoidExpression(); + if (rtt->type != Type::unreachable) { + if (!rtt->type.isRtt() || rtt->type.getHeapType() != heapType) { + throwError("Bad heap type for struct.new"); + } + } + std::vector<Expression*> operands; + if (code == BinaryConsts::StructNewWithRtt) { + auto numOperands = heapType.getStruct().fields.size(); + operands.resize(numOperands); + for (Index i = 0; i < numOperands; i++) { + operands[numOperands - i - 1] = popNonVoidExpression(); + } + } + out = Builder(wasm).makeStructNew(rtt, operands); return true; } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 63b6e34bd..7efb2f2fb 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2124,10 +2124,24 @@ Expression* SExpressionWasmBuilder::makeRttSub(Element& s) { } Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) { - auto ret = allocator.alloc<StructNew>(); - WASM_UNREACHABLE("TODO (gc): struct.new"); - ret->finalize(); - return ret; + auto heapType = parseHeapType(*s[1]); + auto* rtt = parseExpression(*s[2]); + if (rtt->type != Type::unreachable) { + if (!rtt->type.isRtt() || rtt->type.getHeapType() != heapType) { + throw ParseException("bad struct.new heap type", s.line, s.col); + } + } + auto numOperands = s.size() - 3; + if (default_ && numOperands > 0) { + throw ParseException( + "arguments provided for struct.new_with_default", s.line, s.col); + } + std::vector<Expression*> operands; + operands.resize(numOperands); + for (Index i = 0; i < numOperands; i++) { + operands[i] = parseExpression(*s[i + 3]); + } + return Builder(wasm).makeStructNew(rtt, operands); } Index SExpressionWasmBuilder::getStructIndex(const HeapType& type, Element& s) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index cca351b6f..2e4e9d332 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1908,7 +1908,13 @@ void BinaryInstWriter::visitRttSub(RttSub* curr) { } void BinaryInstWriter::visitStructNew(StructNew* curr) { - WASM_UNREACHABLE("TODO (gc): struct.new"); + o << int8_t(BinaryConsts::GCPrefix); + if (curr->isWithDefault()) { + o << U32LEB(BinaryConsts::StructNewDefaultWithRtt); + } else { + o << U32LEB(BinaryConsts::StructNewWithRtt); + } + parent.writeHeapType(curr->rtt->type.getHeapType()); } void BinaryInstWriter::visitStructGet(StructGet* curr) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 013dadcf4..a27cbabc6 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -577,6 +577,12 @@ bool Type::isSubType(Type left, Type right) { if (left.getHeapType().isSignature() && right == Type::funcref) { return true; } + // A non-nullable type is a supertype of a nullable one + if (left.getHeapType() == right.getHeapType() && !left.isNullable()) { + // The only difference is the nullability. + assert(right.isNullable()); + return true; + } return false; } if (left.isTuple() && right.isTuple()) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index c6e22899c..e97541444 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2238,7 +2238,39 @@ void FunctionValidator::visitStructNew(StructNew* curr) { shouldBeTrue(getModule()->features.hasGC(), curr, "struct.new requires gc to be enabled"); - WASM_UNREACHABLE("TODO (gc): struct.new"); + if (curr->type == Type::unreachable) { + return; + } + if (!shouldBeTrue( + curr->rtt->type.isRtt(), curr, "struct.new rtt must be rtt")) { + return; + } + auto heapType = curr->rtt->type.getHeapType(); + if (!shouldBeTrue( + heapType.isStruct(), curr, "struct.new heap type must be struct")) { + return; + } + const auto& fields = heapType.getStruct().fields; + if (curr->isWithDefault()) { + shouldBeTrue(curr->operands.empty(), + curr, + "struct.new_with_default should have no operands"); + // All the fields must be defaultable. + for (const auto& field : fields) { + // TODO: add type.isDefaultable()? + shouldBeTrue(!field.type.isRef() || field.type.isNullable(), + field, + "struct.new_with_default value type must be defaultable"); + } + } else { + // All the fields must have the proper type. + for (Index i = 0; i < fields.size(); i++) { + shouldBeSubType(curr->operands[i]->type, + fields[i].type, + curr, + "struct.new operand must have proper type"); + } + } } void FunctionValidator::visitStructGet(StructGet* curr) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 1e8bf93ae..5954dd617 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -463,13 +463,16 @@ void Break::finalize() { void Switch::finalize() { type = Type::unreachable; } -template<typename T> void handleUnreachableOperands(T* curr) { +// Sets the type to unreachable if there is an unreachable operand. Returns true +// if so. +template<typename T> bool handleUnreachableOperands(T* curr) { for (auto* child : curr->operands) { if (child->type == Type::unreachable) { curr->type = Type::unreachable; - break; + return true; } } + return false; } void Call::finalize() { @@ -1093,7 +1096,16 @@ void RttSub::finalize() { // construction. } -// TODO (gc): struct.new +void StructNew::finalize() { + if (rtt->type == Type::unreachable) { + type = Type::unreachable; + return; + } + if (handleUnreachableOperands(this)) { + return; + } + type = Type(rtt->type.getHeapType(), /* nullable = */ false); +} void StructGet::finalize() { if (ref->type == Type::unreachable) { |