summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp37
-rw-r--r--src/wasm/wasm-s-parser.cpp22
-rw-r--r--src/wasm/wasm-stack.cpp8
-rw-r--r--src/wasm/wasm-type.cpp6
-rw-r--r--src/wasm/wasm-validator.cpp34
-rw-r--r--src/wasm/wasm.cpp18
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) {