diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/cost.h | 10 | ||||
-rw-r--r-- | src/ir/effects.h | 9 | ||||
-rw-r--r-- | src/ir/module-utils.h | 9 | ||||
-rw-r--r-- | src/literal.h | 15 | ||||
-rw-r--r-- | src/passes/Print.cpp | 41 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 40 | ||||
-rw-r--r-- | src/wasm-binary.h | 8 | ||||
-rw-r--r-- | src/wasm-builder.h | 29 | ||||
-rw-r--r-- | src/wasm-delegations-fields.h | 4 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 10 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm-type.h | 2 | ||||
-rw-r--r-- | src/wasm.h | 7 | ||||
-rw-r--r-- | src/wasm/literal.cpp | 15 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 60 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 68 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 14 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 9 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 10 |
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 |