summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2020-12-07 15:23:24 -0800
committerGitHub <noreply@github.com>2020-12-07 15:23:24 -0800
commit72a7881b42ebed6b2ef36e912a8f5937106e5824 (patch)
tree5a79bfdbac295b47163e20cf76d25af9d8475b56 /src
parent295780928d576613ea47bcc55dafc54619086bc8 (diff)
downloadbinaryen-72a7881b42ebed6b2ef36e912a8f5937106e5824.tar.gz
binaryen-72a7881b42ebed6b2ef36e912a8f5937106e5824.tar.bz2
binaryen-72a7881b42ebed6b2ef36e912a8f5937106e5824.zip
[GC] Add struct.get instruction parsing and execution (#3429)
This is the first instruction that uses a GC Struct or Array, so it's where we start to actually need support in the interpreter for those values, which is added here. GC data is modeled as a gcData field on a Literal, which is just a Literals. That is, both a struct and an array are represented as an array of values. The type which is alongside would indicate if it's a struct or an array. Note that the data is referred to using a shared_ptr so it should "just work", but we'll only be able to really test that once we add struct.new and so can verify that references are by reference and not value, etc. As the first instruction to care about i8/16 types (which are only possible in a Struct or Array) this adds support for parsing and emitting them. This PR includes fuzz fixes for some minor things the fuzzer found, including some bad printing of not having ResultTypeName in necessary places (found by the text format roundtripping fuzzer).
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