summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/cost.h10
-rw-r--r--src/ir/effects.h9
-rw-r--r--src/ir/module-utils.h9
-rw-r--r--src/literal.h15
-rw-r--r--src/passes/Print.cpp41
-rw-r--r--src/tools/fuzzing.h40
-rw-r--r--src/wasm-binary.h8
-rw-r--r--src/wasm-builder.h29
-rw-r--r--src/wasm-delegations-fields.h4
-rw-r--r--src/wasm-interpreter.h10
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm-type.h2
-rw-r--r--src/wasm.h7
-rw-r--r--src/wasm/literal.cpp15
-rw-r--r--src/wasm/wasm-binary.cpp60
-rw-r--r--src/wasm/wasm-s-parser.cpp68
-rw-r--r--src/wasm/wasm-stack.cpp14
-rw-r--r--src/wasm/wasm-type.cpp4
-rw-r--r--src/wasm/wasm-validator.cpp9
-rw-r--r--src/wasm/wasm.cpp10
20 files changed, 261 insertions, 104 deletions
diff --git a/src/ir/cost.h b/src/ir/cost.h
index 333f599ee..1e383afca 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -566,12 +566,20 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> {
Index visitRttCanon(RttCanon* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitRttSub(RttSub* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitStructNew(StructNew* curr) { WASM_UNREACHABLE("TODO: GC"); }
- Index visitStructGet(StructGet* curr) { WASM_UNREACHABLE("TODO: GC"); }
+ Index visitStructGet(StructGet* curr) {
+ return 1 + nullCheckCost(curr->value) + visit(curr->value);
+ }
Index visitStructSet(StructSet* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArrayNew(ArrayNew* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArrayGet(ArrayGet* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArraySet(ArraySet* curr) { WASM_UNREACHABLE("TODO: GC"); }
Index visitArrayLen(ArrayLen* curr) { WASM_UNREACHABLE("TODO: GC"); }
+
+private:
+ Index nullCheckCost(Expression* ref) {
+ // A nullable type requires a bounds check in most VMs.
+ return ref->type.isNullable();
+ }
};
} // namespace wasm
diff --git a/src/ir/effects.h b/src/ir/effects.h
index c0210c221..80aa74cb6 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -515,12 +515,12 @@ private:
if (parent.tryDepth == 0) {
parent.throws = true;
}
- // rethrow traps when the arg is null
+ // traps when the arg is null
parent.implicitTrap = true;
}
void visitBrOnExn(BrOnExn* curr) {
parent.breakTargets.insert(curr->name);
- // br_on_exn traps when the arg is null
+ // traps when the arg is null
parent.implicitTrap = true;
}
void visitNop(Nop* curr) {}
@@ -562,7 +562,10 @@ private:
WASM_UNREACHABLE("TODO (gc): struct.new");
}
void visitStructGet(StructGet* curr) {
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ // traps when the arg is null
+ if (curr->value->type.isNullable()) {
+ parent.implicitTrap = true;
+ }
}
void visitStructSet(StructSet* curr) {
WASM_UNREACHABLE("TODO (gc): struct.set");
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index de37e0875..64f04da31 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -425,10 +425,12 @@ inline void collectHeapTypes(Module& wasm,
TypeCounter(Counts& counts) : counts(counts) {}
void visitExpression(Expression* curr) {
- if (curr->is<RefNull>()) {
- counts.maybeNote(curr->type);
- } else if (auto* call = curr->dynCast<CallIndirect>()) {
+ if (auto* call = curr->dynCast<CallIndirect>()) {
counts.note(call->sig);
+ } else if (curr->is<RefNull>()) {
+ counts.maybeNote(curr->type);
+ } else if (auto* get = curr->dynCast<StructGet>()) {
+ counts.maybeNote(get->value->type);
} else if (Properties::isControlFlowStructure(curr)) {
counts.maybeNote(curr->type);
if (curr->type.isTuple()) {
@@ -436,7 +438,6 @@ inline void collectHeapTypes(Module& wasm,
counts.note(Signature(Type::none, curr->type));
}
}
- // TODO struct.get and others contain a type index
}
};
TypeCounter(counts).walk(func->body);
diff --git a/src/literal.h b/src/literal.h
index 3d867a119..12d3c6d07 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -43,6 +43,11 @@ class Literal {
Name func;
// exnref package. `nullptr` indicates a `null` value.
std::unique_ptr<ExceptionPackage> exn;
+ // A reference to GC data, either a Struct or an Array. For both of those
+ // we store the referred data as a Literals object (which is natural for an
+ // Array, and for a Struct, is just the fields in order). The type is used
+ // to indicate whether this is a Struct or an Array, and of what type.
+ std::shared_ptr<Literals> gcData;
// TODO: Literals of type `externref` can only be `null` currently but we
// will need to represent extern values eventually, to
// 1) run the spec tests and fuzzer with reference types enabled and
@@ -74,12 +79,17 @@ public:
explicit Literal(Name func, Type type) : func(func), type(type) {}
explicit Literal(std::unique_ptr<ExceptionPackage>&& exn)
: exn(std::move(exn)), type(Type::exnref) {}
+ explicit Literal(std::shared_ptr<Literals> gcData, Type type)
+ : gcData(gcData), type(type) {}
Literal(const Literal& other);
Literal& operator=(const Literal& other);
~Literal() {
if (type.isException()) {
exn.~unique_ptr();
}
+ if (type.isStruct() || type.isArray()) {
+ gcData.~shared_ptr();
+ }
}
bool isConcrete() const { return type != Type::none; }
@@ -92,6 +102,9 @@ public:
if (type.isException()) {
return !exn;
}
+ if (isGCData()) {
+ return !gcData;
+ }
return true;
}
return false;
@@ -156,6 +169,7 @@ public:
WASM_UNREACHABLE("unexpected type");
}
}
+ bool isGCData() const { return type.isStruct() || type.isArray(); }
static Literals makeZeros(Type type);
static Literals makeOnes(Type type);
@@ -276,6 +290,7 @@ public:
return func;
}
ExceptionPackage getExceptionPackage() const;
+ std::shared_ptr<Literals> getGCData() const;
// careful!
int32_t* geti32Ptr() {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 711c1b604..ee7aa9ac7 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -222,13 +222,13 @@ struct PrintExpressionContents
printName(curr->name, o);
}
if (curr->type.isConcrete()) {
- o << ' ' << ResultType(curr->type);
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitIf(If* curr) {
printMedium(o, "if");
if (curr->type.isConcrete()) {
- o << ' ' << ResultType(curr->type);
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitLoop(Loop* curr) {
@@ -238,7 +238,7 @@ struct PrintExpressionContents
printName(curr->name, o);
}
if (curr->type.isConcrete()) {
- o << ' ' << ResultType(curr->type);
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitBreak(Break* curr) {
@@ -1600,7 +1600,7 @@ struct PrintExpressionContents
void visitSelect(Select* curr) {
prepareColor(o) << "select";
if (curr->type.isRef()) {
- o << " (result " << curr->type << ')';
+ o << ' ' << ResultTypeName(curr->type);
}
}
void visitDrop(Drop* curr) { printMedium(o, "drop"); }
@@ -1609,7 +1609,7 @@ struct PrintExpressionContents
void visitMemoryGrow(MemoryGrow* curr) { printMedium(o, "memory.grow"); }
void visitRefNull(RefNull* curr) {
printMedium(o, "ref.null ");
- o << curr->type.getHeapType();
+ printHeapTypeName(o, curr->type.getHeapType());
}
void visitRefIsNull(RefIsNull* curr) { printMedium(o, "ref.is_null"); }
void visitRefFunc(RefFunc* curr) {
@@ -1684,7 +1684,20 @@ struct PrintExpressionContents
WASM_UNREACHABLE("TODO (gc): struct.new");
}
void visitStructGet(StructGet* curr) {
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ const auto& field =
+ curr->value->type.getHeapType().getStruct().fields[curr->index];
+ if (field.type == Type::i32 && field.packedType != Field::not_packed) {
+ if (curr->signed_) {
+ printMedium(o, "struct.get_s ");
+ } else {
+ printMedium(o, "struct.get_u ");
+ }
+ } else {
+ printMedium(o, "struct.get ");
+ }
+ printHeapTypeName(o, curr->value->type.getHeapType());
+ o << ' ';
+ o << curr->index;
}
void visitStructSet(StructSet* curr) {
printMedium(o, "struct.set");
@@ -2358,7 +2371,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
void visitStructGet(StructGet* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ incIndent();
+ printFullLine(curr->value);
+ decIndent();
}
void visitStructSet(StructSet* curr) {
o << '(';
@@ -2417,7 +2432,17 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
if (field.mutable_) {
o << "(mut ";
}
- o << TypeName(field.type);
+ if (field.type == Type::i32 && field.packedType != Field::not_packed) {
+ if (field.packedType == Field::i8) {
+ o << "i8";
+ } else if (field.packedType == Field::i16) {
+ o << "i16";
+ } else {
+ WASM_UNREACHABLE("invalid packed type");
+ }
+ } else {
+ o << TypeName(field.type);
+ }
if (field.mutable_) {
o << ')';
}
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 63f10cac4..621555ab1 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -327,28 +327,31 @@ private:
}
SmallVector<Type, 2> options;
options.push_back(type); // includes itself
- TODO_SINGLE_COMPOUND(type);
- switch (type.getBasic()) {
- case Type::anyref:
- if (wasm.features.hasReferenceTypes()) {
- options.push_back(Type::funcref);
- options.push_back(Type::externref);
- if (wasm.features.hasExceptionHandling()) {
- options.push_back(Type::exnref);
+ // TODO: interesting uses of typed function types
+ // TODO: interesting subtypes of compound types
+ if (type.isBasic()) {
+ switch (type.getBasic()) {
+ case Type::anyref:
+ if (wasm.features.hasReferenceTypes()) {
+ options.push_back(Type::funcref);
+ options.push_back(Type::externref);
+ if (wasm.features.hasExceptionHandling()) {
+ options.push_back(Type::exnref);
+ }
+ if (wasm.features.hasGC()) {
+ options.push_back(Type::eqref);
+ options.push_back(Type::i31ref);
+ }
}
+ break;
+ case Type::eqref:
if (wasm.features.hasGC()) {
- options.push_back(Type::eqref);
options.push_back(Type::i31ref);
}
- }
- break;
- case Type::eqref:
- if (wasm.features.hasGC()) {
- options.push_back(Type::i31ref);
- }
- break;
- default:
- break;
+ break;
+ default:
+ break;
+ }
}
return pick(options);
}
@@ -1120,6 +1123,7 @@ private:
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
&Self::makeI31New);
}
+ // TODO: struct.get and other GC things
return (this->*pick(options))(type);
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index d22205a0a..4c4789b69 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -338,6 +338,8 @@ enum EncodedType {
f32 = -0x3, // 0x7d
f64 = -0x4, // 0x7c
v128 = -0x5, // 0x7b
+ i8 = -0x6, // 0x7a
+ i16 = -0x7, // 0x79
// function reference type
funcref = -0x10, // 0x70
// opaque host reference type
@@ -1252,7 +1254,12 @@ public:
int32_t getS32LEB();
int64_t getS64LEB();
uint64_t getUPtrLEB();
+
+ // Read a value and get a type for it.
Type getType();
+ // Get a type given the initial S32LEB has already been read, and is provided.
+ Type getType(int initial);
+
HeapType getHeapType();
Field getField();
Type getConcreteType();
@@ -1261,7 +1268,6 @@ public:
void verifyInt16(int16_t x);
void verifyInt32(int32_t x);
void verifyInt64(int64_t x);
- void ungetInt8();
void readHeader();
void readStart();
void readMemory();
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 50a6e97fb..e3b12d23f 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -727,9 +727,15 @@ public:
ret->finalize();
return ret;
}
- StructGet* makeStructGet() {
+ StructGet* makeStructGet(Index index,
+ Expression* value,
+ Type type,
+ bool signed_ = false) {
auto* ret = wasm.allocator.alloc<StructGet>();
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ ret->index = index;
+ ret->value = value;
+ ret->type = type;
+ ret->signed_ = signed_;
ret->finalize();
return ret;
}
@@ -780,12 +786,12 @@ public:
if (type.isNumber()) {
return makeConst(value);
}
- if (type.isFunction()) {
- if (!value.isNull()) {
- return makeRefFunc(value.getFunc(), type);
- }
+ if (value.isNull()) {
return makeRefNull(type);
}
+ if (type.isFunction()) {
+ return makeRefFunc(value.getFunc(), type);
+ }
TODO_SINGLE_COMPOUND(type);
switch (type.getBasic()) {
case Type::externref:
@@ -962,13 +968,12 @@ public:
if (curr->type.isTuple()) {
return makeConstantExpression(Literal::makeZeros(curr->type));
}
+ if (curr->type.isNullable()) {
+ return ExpressionManipulator::refNull(curr, curr->type);
+ }
if (curr->type.isFunction()) {
- if (curr->type.isNullable()) {
- return ExpressionManipulator::refNull(curr, curr->type);
- } else {
- // We can't do any better, keep the original.
- return curr;
- }
+ // We can't do any better, keep the original.
+ return curr;
}
Literal value;
// TODO: reuse node conditionally when possible for literals
diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h
index ca0a8f7cb..f3c5b9546 100644
--- a/src/wasm-delegations-fields.h
+++ b/src/wasm-delegations-fields.h
@@ -595,7 +595,9 @@ switch (DELEGATE_ID) {
}
case Expression::Id::StructGetId: {
DELEGATE_START(StructGet);
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ DELEGATE_FIELD_INT(StructGet, index);
+ DELEGATE_FIELD_CHILD(StructGet, value);
+ DELEGATE_FIELD_INT(Load, signed_);
DELEGATE_END(StructGet);
break;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 93c409797..b6e526544 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1402,7 +1402,15 @@ public:
}
Flow visitStructGet(StructGet* curr) {
NOTE_ENTER("StructGet");
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ Flow flow = this->visit(curr->value);
+ if (flow.breaking()) {
+ return flow;
+ }
+ auto data = flow.getSingleValue().getGCData();
+ if (!data) {
+ trap("null ref");
+ }
+ return (*data)[curr->index];
}
Flow visitStructSet(StructSet* curr) {
NOTE_ENTER("StructSet");
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 7e20ff4c9..2c70445cf 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -262,6 +262,7 @@ private:
Expression* makeRttCanon(Element& s);
Expression* makeRttSub(Element& s);
Expression* makeStructNew(Element& s, bool default_);
+ Index getStructIndex(const HeapType& type, Element& s);
Expression* makeStructGet(Element& s, bool signed_ = false);
Expression* makeStructSet(Element& s);
Expression* makeArrayNew(Element& s, bool default_);
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 98c6c70ed..4d842cdca 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -116,6 +116,8 @@ public:
bool isException() const;
bool isNullable() const;
bool isRtt() const;
+ bool isStruct() const;
+ bool isArray() const;
private:
template<bool (Type::*pred)() const> bool hasPredicate() {
diff --git a/src/wasm.h b/src/wasm.h
index 6367fa345..cc390573d 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1352,7 +1352,12 @@ class StructGet : public SpecificExpression<Expression::StructGetId> {
public:
StructGet(MixedArena& allocator) {}
- void finalize() { WASM_UNREACHABLE("TODO (gc): struct.get"); }
+ Index index;
+ Expression* value;
+ // Packed fields have a sign.
+ bool signed_ = false;
+
+ void finalize();
};
class StructSet : public SpecificExpression<Expression::StructSetId> {
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index 4edaf8039..b7cf5084e 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -55,6 +55,9 @@ Literal::Literal(const Literal& other) : type(other.type) {
} else {
new (&exn) std::unique_ptr<ExceptionPackage>();
}
+ } else if (other.isGCData()) {
+ // Avoid calling the destructor on an uninitialized value
+ new (&gcData) std::shared_ptr<Literals>(other.gcData);
} else if (type.isFunction()) {
func = other.func;
} else {
@@ -189,6 +192,11 @@ ExceptionPackage Literal::getExceptionPackage() const {
return *exn;
}
+std::shared_ptr<Literals> Literal::getGCData() const {
+ assert(isGCData());
+ return gcData;
+}
+
Literal Literal::castToF32() {
assert(type == Type::i32);
Literal ret(Type::f32);
@@ -428,6 +436,13 @@ std::ostream& operator<<(std::ostream& o, Literal literal) {
} else {
o << "funcref(" << literal.getFunc() << ")";
}
+ } else if (literal.isGCData()) {
+ auto data = literal.getGCData();
+ if (data) {
+ o << "[ref " << *data << ']';
+ } else {
+ o << "[ref null " << literal.type << ']';
+ }
} else {
TODO_SINGLE_COMPOUND(literal.type);
switch (literal.type.getBasic()) {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 3aace6721..361faf631 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1030,7 +1030,7 @@ void WasmBinaryWriter::writeType(Type type) {
void WasmBinaryWriter::writeHeapType(HeapType type) {
if (type.isSignature() || type.isStruct() || type.isArray()) {
- o << S32LEB(getTypeIndex(type));
+ o << S64LEB(getTypeIndex(type)); // TODO: Actually s33
return;
}
int ret = 0;
@@ -1058,11 +1058,21 @@ void WasmBinaryWriter::writeHeapType(HeapType type) {
case HeapType::ArrayKind:
WASM_UNREACHABLE("TODO: compound GC types");
}
- o << S32LEB(ret); // TODO: Actually encoded as s33
+ o << S64LEB(ret); // TODO: Actually s33
}
void WasmBinaryWriter::writeField(const Field& field) {
- writeType(field.type);
+ if (field.type == Type::i32 && field.packedType != Field::not_packed) {
+ if (field.packedType == Field::i8) {
+ o << S32LEB(BinaryConsts::EncodedType::i8);
+ } else if (field.packedType == Field::i16) {
+ o << S32LEB(BinaryConsts::EncodedType::i16);
+ } else {
+ WASM_UNREACHABLE("invalid packed type");
+ }
+ } else {
+ writeType(field.type);
+ }
o << U32LEB(field.mutable_);
}
@@ -1335,14 +1345,13 @@ uint64_t WasmBinaryBuilder::getUPtrLEB() {
return wasm.memory.is64() ? getU64LEB() : getU32LEB();
}
-Type WasmBinaryBuilder::getType() {
- int type = getS32LEB();
+Type WasmBinaryBuilder::getType(int initial) {
// Single value types are negative; signature indices are non-negative
- if (type >= 0) {
+ if (initial >= 0) {
// TODO: Handle block input types properly.
- return getSignatureByTypeIndex(type).results;
+ return getSignatureByTypeIndex(initial).results;
}
- switch (type) {
+ switch (initial) {
// None only used for block signatures. TODO: Separate out?
case BinaryConsts::EncodedType::Empty:
return Type::none;
@@ -1374,13 +1383,15 @@ Type WasmBinaryBuilder::getType() {
case BinaryConsts::EncodedType::i31ref:
return Type::i31ref;
default:
- throwError("invalid wasm type: " + std::to_string(type));
+ throwError("invalid wasm type: " + std::to_string(initial));
}
WASM_UNREACHABLE("unexpected type");
}
+Type WasmBinaryBuilder::getType() { return getType(getS32LEB()); }
+
HeapType WasmBinaryBuilder::getHeapType() {
- int type = getS32LEB(); // TODO: Actually encoded as s33
+ auto type = getS64LEB(); // TODO: Actually s33
// Single heap types are negative; heap type indices are non-negative
if (type >= 0) {
if (size_t(type) >= types.size()) {
@@ -1408,7 +1419,19 @@ HeapType WasmBinaryBuilder::getHeapType() {
}
Field WasmBinaryBuilder::getField() {
- auto type = getConcreteType();
+ // The value may be a general wasm type, or one of the types only possible in
+ // a field.
+ auto initial = getS32LEB();
+ if (initial == BinaryConsts::EncodedType::i8) {
+ auto mutable_ = getU32LEB();
+ return Field(Field::i8, mutable_);
+ }
+ if (initial == BinaryConsts::EncodedType::i16) {
+ auto mutable_ = getU32LEB();
+ return Field(Field::i16, mutable_);
+ }
+ // It's a regular wasm value.
+ auto type = getType(initial);
auto mutable_ = getU32LEB();
return Field(type, mutable_);
}
@@ -1466,12 +1489,6 @@ void WasmBinaryBuilder::verifyInt64(int64_t x) {
}
}
-void WasmBinaryBuilder::ungetInt8() {
- assert(pos > 0);
- BYN_TRACE("ungetInt8 (at " << pos << ")\n");
- pos--;
-}
-
void WasmBinaryBuilder::readHeader() {
BYN_TRACE("== readHeader\n");
verifyInt32(BinaryConsts::Magic);
@@ -5601,20 +5618,21 @@ bool WasmBinaryBuilder::maybeVisitStructGet(Expression*& out, uint32_t code) {
switch (code) {
case BinaryConsts::StructGet:
curr = allocator.alloc<StructGet>();
- // ...
break;
case BinaryConsts::StructGetS:
curr = allocator.alloc<StructGet>();
- // ...
+ curr->signed_ = true;
break;
case BinaryConsts::StructGetU:
curr = allocator.alloc<StructGet>();
- // ...
+ curr->signed_ = false;
break;
default:
return false;
}
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ auto type = getHeapType();
+ curr->index = getU32LEB();
+ curr->value = popNonVoidExpression();
curr->finalize();
out = curr;
return true;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index c5296aec3..3e605429d 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -49,7 +49,8 @@ int unhex(char c) {
namespace wasm {
-static Name STRUCT("struct"), FIELD("field"), ARRAY("array");
+static Name STRUCT("struct"), FIELD("field"), ARRAY("array"), I8("i8"),
+ I16("i16");
static Address getAddress(const Element* s) { return atoll(s->c_str()); }
@@ -1888,28 +1889,13 @@ Expression* SExpressionWasmBuilder::makeRefNull(Element& s) {
throw ParseException("invalid heap type reference", s.line, s.col);
}
auto ret = allocator.alloc<RefNull>();
- if (s[1]->isStr()) {
- // For example, this parses
- // (ref.null func)
- ret->finalize(stringToHeapType(s[1]->str()));
+ // The heap type may be just "func", that is, the whole thing is just
+ // (ref.null func), or it may be the name of a defined type, such as
+ // (ref.null $struct.FOO)
+ if (s[1]->dollared()) {
+ ret->finalize(parseHeapType(*s[1]));
} else {
- // To parse a heap type, create an element around it, and call that method.
- // That is, given (func) we wrap to (ref (func)).
- // For example, this parses
- // (ref.null (func (param i32)))
- // TODO add a helper method, but this is the only user atm, and we are
- // waiting on https://github.com/WebAssembly/function-references/issues/42
- Element wrapper(wasm.allocator);
- auto& list = wrapper.list();
- list.resize(3);
- Element ref(wasm.allocator);
- ref.setString(REF, false, false);
- Element null(wasm.allocator);
- null.setString(NULL_, false, false);
- list[0] = &ref;
- list[1] = &null;
- list[2] = s[1];
- ret->finalize(elementToType(wrapper));
+ ret->finalize(stringToHeapType(s[1]->str()));
}
return ret;
}
@@ -2136,11 +2122,28 @@ Expression* SExpressionWasmBuilder::makeStructNew(Element& s, bool default_) {
return ret;
}
+Index SExpressionWasmBuilder::getStructIndex(const HeapType& type, Element& s) {
+ if (s.dollared()) {
+ auto name = s.str();
+ auto struct_ = type.getStruct();
+ auto& fields = struct_.fields;
+ for (Index i = 0; i < fields.size(); i++) {
+ if (fields[i].name == name) {
+ return i;
+ }
+ }
+ throw ParseException("bad struct name", s.line, s.col);
+ }
+ // this is a numeric index
+ return atoi(s.c_str());
+}
+
Expression* SExpressionWasmBuilder::makeStructGet(Element& s, bool signed_) {
- auto ret = allocator.alloc<StructGet>();
- WASM_UNREACHABLE("TODO (gc): struct.get_s/u");
- ret->finalize();
- return ret;
+ auto structType = parseHeapType(*s[1]);
+ auto index = getStructIndex(structType, *s[2]);
+ auto type = structType.getStruct().fields[index].type;
+ auto value = parseExpression(*s[3]);
+ return Builder(wasm).makeStructGet(index, value, type, signed_);
}
Expression* SExpressionWasmBuilder::makeStructSet(Element& s) {
@@ -2793,10 +2796,6 @@ HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
// It's a struct or an array.
auto parseField = [&](Element* t) {
bool mutable_ = false;
- if (t->isStr()) {
- // t is a simple string name like "i32"
- return Field(elementToType(*t), mutable_);
- }
// t is a list, containing either
// TYPE
// or
@@ -2815,6 +2814,15 @@ HeapType SExpressionWasmBuilder::parseHeapType(Element& s) {
mutable_ = true;
t = (*t)[1];
}
+ if (t->isStr()) {
+ // t is a simple string name like "i32". It can be a normal wasm type, or
+ // one of the special types only available in fields.
+ if (*t == I8) {
+ return Field(Field::i8, mutable_, name);
+ } else if (*t == I16) {
+ return Field(Field::i16, mutable_, name);
+ }
+ }
// Otherwise it's an arbitrary type.
return Field(elementToType(*t), mutable_, name);
};
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index a6a4ce171..4791c4a23 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1910,7 +1910,19 @@ void BinaryInstWriter::visitStructNew(StructNew* curr) {
}
void BinaryInstWriter::visitStructGet(StructGet* curr) {
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ const auto& heapType = curr->value->type.getHeapType();
+ const auto& field = heapType.getStruct().fields[curr->index];
+ int8_t op;
+ if (field.type != Type::i32 || field.packedType == Field::not_packed) {
+ op = BinaryConsts::StructGet;
+ } else if (curr->signed_) {
+ op = BinaryConsts::StructGetS;
+ } else {
+ op = BinaryConsts::StructGetU;
+ }
+ o << int8_t(BinaryConsts::GCPrefix) << int8_t(op);
+ parent.writeHeapType(heapType);
+ o << U32LEB(curr->index);
}
void BinaryInstWriter::visitStructSet(StructSet* curr) {
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index 286542303..22a7c9888 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -392,6 +392,10 @@ bool Type::isRtt() const {
}
}
+bool Type::isStruct() const { return isRef() && getHeapType().isStruct(); }
+
+bool Type::isArray() const { return isRef() && getHeapType().isArray(); }
+
bool Type::operator<(const Type& other) const {
auto comp = [](const Type& a, const Type& b) {
if (a.isBasic() && b.isBasic()) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index fb55417d5..9a8644b28 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2231,7 +2231,14 @@ void FunctionValidator::visitStructGet(StructGet* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
"struct.get requires gc to be enabled");
- WASM_UNREACHABLE("TODO (gc): struct.get");
+ if (curr->value->type != Type::unreachable) {
+ const auto& fields = curr->value->type.getHeapType().getStruct().fields;
+ shouldBeTrue(curr->index < fields.size(), curr, "bad struct.get field");
+ shouldBeEqual(curr->type,
+ fields[curr->index].type,
+ curr,
+ "struct.get must have the proper type");
+ }
}
void FunctionValidator::visitStructSet(StructSet* curr) {
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index cbf573950..20f3ec843 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1083,7 +1083,15 @@ void CallRef::finalize(Type type_) {
// TODO (gc): rtt.canon
// TODO (gc): rtt.sub
// TODO (gc): struct.new
-// TODO (gc): struct.get
+
+void StructGet::finalize() {
+ if (value->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = value->type.getHeapType().getStruct().fields[index].type;
+ }
+}
+
// TODO (gc): struct.set
// TODO (gc): array.new
// TODO (gc): array.get