summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wat-parser.cpp135
-rw-r--r--test/lit/wat-kitchen-sink.wast81
2 files changed, 208 insertions, 8 deletions
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index fe8b7a2cb..f88bc9872 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -659,6 +659,7 @@ struct NullInstrParserCtx {
using InstrsT = Ok;
using ExprT = Ok;
+ using FieldIdxT = Ok;
using LocalT = Ok;
using GlobalT = Ok;
using MemoryT = Ok;
@@ -671,6 +672,12 @@ struct NullInstrParserCtx {
ExprT makeExpr(InstrsT) { return Ok{}; }
+ template<typename HeapTypeT> FieldIdxT getFieldFromIdx(HeapTypeT, uint32_t) {
+ return Ok{};
+ }
+ template<typename HeapTypeT> FieldIdxT getFieldFromName(HeapTypeT, Name) {
+ return Ok{};
+ }
LocalT getLocalFromIdx(uint32_t) { return Ok{}; }
LocalT getLocalFromName(Name) { return Ok{}; }
GlobalT getGlobalFromIdx(uint32_t) { return Ok{}; }
@@ -727,7 +734,7 @@ struct NullInstrParserCtx {
InstrT makeReturn(Index) { return Ok{}; }
template<typename HeapTypeT> InstrT makeRefNull(Index, HeapTypeT) {
- return {};
+ return Ok{};
}
InstrT makeRefIs(Index, RefIsOp) { return Ok{}; }
@@ -735,6 +742,21 @@ struct NullInstrParserCtx {
InstrT makeI31New(Index) { return Ok{}; }
InstrT makeI31Get(Index, bool) { return Ok{}; }
+
+ template<typename HeapTypeT> InstrT makeStructNew(Index, HeapTypeT) {
+ return Ok{};
+ }
+ template<typename HeapTypeT> InstrT makeStructNewDefault(Index, HeapTypeT) {
+ return Ok{};
+ }
+ template<typename HeapTypeT>
+ InstrT makeStructGet(Index, HeapTypeT, FieldIdxT, bool) {
+ return Ok{};
+ }
+ template<typename HeapTypeT>
+ InstrT makeStructSet(Index, HeapTypeT, FieldIdxT) {
+ return Ok{};
+ }
};
// Phase 1: Parse definition spans for top-level module elements and determine
@@ -816,7 +838,7 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::optional<InstrsT>,
Index pos) {
if (import && hasNonImport) {
- return in.err("import after non-import");
+ return in.err(pos, "import after non-import");
}
auto f = addFuncDecl(pos, name, import);
CHECK_ERR(f);
@@ -1090,8 +1112,11 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
TypeUse type,
std::optional<LocalsT> locals,
std::optional<InstrsT>,
- Index) {
+ Index pos) {
auto& f = wasm.functions[index];
+ if (!type.type.isSignature()) {
+ return in.err(pos, "expected signature type");
+ }
f->type = type.type;
for (Index i = 0; i < type.names.size(); ++i) {
if (type.names[i].is()) {
@@ -1140,6 +1165,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
using InstrsT = std::vector<Expression*>;
using ExprT = Expression*;
+ using FieldIdxT = Index;
using LocalT = Index;
using GlobalT = Name;
using MemoryT = Name;
@@ -1296,6 +1322,21 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return types[idx];
}
+ Result<Index> getFieldFromIdx(HeapType type, uint32_t idx) {
+ if (!type.isStruct()) {
+ return in.err("expected struct type");
+ }
+ if (idx >= type.getStruct().fields.size()) {
+ return in.err("struct index out of bounds");
+ }
+ return idx;
+ }
+
+ Result<Index> getFieldFromName(HeapType type, Name name) {
+ // TODO: Field names
+ return in.err("symbolic field names note yet supported");
+ }
+
Result<Index> getLocalFromIdx(uint32_t idx) {
if (!func) {
return in.err("cannot access locals outside of a funcion");
@@ -1436,6 +1477,17 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; }
+ Result<> validateTypeAnnotation(Index pos, HeapType type, Expression* child) {
+ if (child->type == Type::unreachable) {
+ return Ok{};
+ }
+ if (!child->type.isRef() ||
+ !HeapType::isSubType(child->type.getHeapType(), type)) {
+ return in.err(pos, "invalid reference type on stack");
+ }
+ return Ok{};
+ }
+
Result<Name> getMemory(Index pos, Name* mem) {
if (mem) {
return *mem;
@@ -1796,6 +1848,47 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
CHECK_ERR(val);
return push(pos, builder.makeI31Get(*val, signed_));
}
+
+ Result<> makeStructNew(Index pos, HeapType type) {
+ if (!type.isStruct()) {
+ return in.err(pos, "expected struct type annotation");
+ }
+ size_t numOps = type.getStruct().fields.size();
+ std::vector<Expression*> ops(numOps);
+ for (size_t i = 0; i < numOps; ++i) {
+ auto op = pop(pos);
+ CHECK_ERR(op);
+ ops[numOps - i - 1] = *op;
+ }
+ return push(pos, builder.makeStructNew(type, ops));
+ }
+
+ Result<> makeStructNewDefault(Index pos, HeapType type) {
+ return push(pos, builder.makeStructNew(type, std::array<Expression*, 0>{}));
+ }
+
+ Result<> makeStructGet(Index pos, HeapType type, Index field, bool signed_) {
+ assert(type.isStruct());
+ const auto& fields = type.getStruct().fields;
+ assert(fields.size() > field);
+ auto fieldType = fields[field].type;
+ auto ref = pop(pos);
+ CHECK_ERR(ref);
+ CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
+ return push(pos, builder.makeStructGet(field, *ref, fieldType, signed_));
+ }
+
+ Result<> makeStructSet(Index pos, HeapType type, Index field) {
+ assert(type.isStruct());
+ const auto& fields = type.getStruct().fields;
+ assert(fields.size() > field);
+ auto val = pop(pos);
+ CHECK_ERR(val);
+ auto ref = pop(pos);
+ CHECK_ERR(ref);
+ CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
+ return push(pos, builder.makeStructSet(field, *ref, *val));
+ }
};
// ================
@@ -1980,6 +2073,8 @@ Result<typename Ctx::InstrT> makeStringSliceIter(Ctx&, Index);
// Modules
template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx);
template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx&);
+template<typename Ctx>
+Result<typename Ctx::FieldIdxT> fieldidx(Ctx&, typename Ctx::HeapTypeT);
template<typename Ctx> MaybeResult<typename Ctx::MemoryT> maybeMemidx(Ctx&);
template<typename Ctx> Result<typename Ctx::MemoryT> memidx(Ctx&);
template<typename Ctx> Result<typename Ctx::GlobalT> globalidx(Ctx&);
@@ -2906,17 +3001,30 @@ Result<typename Ctx::InstrT> makeBrOnStatic(Ctx& ctx, Index pos, BrOnOp op) {
template<typename Ctx>
Result<typename Ctx::InstrT>
makeStructNewStatic(Ctx& ctx, Index pos, bool default_) {
- return ctx.in.err("unimplemented instruction");
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ if (default_) {
+ return ctx.makeStructNewDefault(pos, *type);
+ }
+ return ctx.makeStructNew(pos, *type);
}
template<typename Ctx>
Result<typename Ctx::InstrT> makeStructGet(Ctx& ctx, Index pos, bool signed_) {
- return ctx.in.err("unimplemented instruction");
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto field = fieldidx(ctx, *type);
+ CHECK_ERR(field);
+ return ctx.makeStructGet(pos, *type, *field, signed_);
}
template<typename Ctx>
Result<typename Ctx::InstrT> makeStructSet(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto field = fieldidx(ctx, *type);
+ CHECK_ERR(field);
+ return ctx.makeStructSet(pos, *type, *field);
}
template<typename Ctx>
@@ -3058,6 +3166,20 @@ template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx& ctx) {
return ctx.in.err("expected type index or identifier");
}
+// fieldidx_t ::= x:u32 => x
+// | v:id => x (if t.fields[x] = v)
+template<typename Ctx>
+Result<typename Ctx::FieldIdxT> fieldidx(Ctx& ctx,
+ typename Ctx::HeapTypeT type) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getFieldFromIdx(type, *x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getFieldFromName(type, *id);
+ }
+ return ctx.in.err("expected field index or identifier");
+}
+
// memidx ::= x:u32 => x
// | v:id => x (if memories[x] = v)
template<typename Ctx>
@@ -3327,7 +3449,6 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
return ctx.in.err("expected end of function");
}
- // TODO: Use `pos` instead of `in` for error position.
CHECK_ERR(
ctx.addFunc(name, *exports, import.getPtr(), *type, localVars, insts, pos));
return Ok{};
diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast
index 4c1df94f2..ca81a8a6a 100644
--- a/test/lit/wat-kitchen-sink.wast
+++ b/test/lit/wat-kitchen-sink.wast
@@ -5,6 +5,8 @@
(module $parse
;; types
+ ;; CHECK: (type $pair (struct_subtype (field (mut i32)) (field (mut i64)) data))
+
;; CHECK: (type $void (func_subtype func))
;; CHECK: (type $none_=>_i32 (func_subtype (result i32) func))
@@ -57,6 +59,18 @@
;; CHECK: (type $i31ref_=>_none (func_subtype (param i31ref) func))
+ ;; CHECK: (type $i32_i64_=>_ref|$pair| (func_subtype (param i32 i64) (result (ref $pair)) func))
+
+ ;; CHECK: (type $none_=>_ref|$pair| (func_subtype (result (ref $pair)) func))
+
+ ;; CHECK: (type $ref|$pair|_=>_i32 (func_subtype (param (ref $pair)) (result i32) func))
+
+ ;; CHECK: (type $ref|$pair|_=>_i64 (func_subtype (param (ref $pair)) (result i64) func))
+
+ ;; CHECK: (type $ref|$pair|_i32_=>_none (func_subtype (param (ref $pair) i32) func))
+
+ ;; CHECK: (type $ref|$pair|_i64_=>_none (func_subtype (param (ref $pair) i64) func))
+
;; CHECK: (rec
;; CHECK-NEXT: (type $s0 (struct_subtype data))
(type $s0 (sub (struct)))
@@ -90,6 +104,8 @@
;; CHECK: (type $a3 (array_subtype (mut f64) data))
(type $a3 (array (field $x (mut f64))))
+ (type $pair (struct (mut i32) (mut i64)))
+
(rec
(type $void (func))
)
@@ -182,7 +198,7 @@
;; CHECK-NEXT: (local $l f32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
- (func $f4 (type 14) (local i32 i64) (local $l f32))
+ (func $f4 (type 15) (local i32 i64) (local $l f32))
(func (export "f5.0") (export "f5.1") (import "mod" "f5"))
;; CHECK: (func $nop-skate (type $void)
@@ -1268,6 +1284,69 @@
drop
)
+ ;; CHECK: (func $struct-new (type $i32_i64_=>_ref|$pair|) (param $0 i32) (param $1 i64) (result (ref $pair))
+ ;; CHECK-NEXT: (struct.new $pair
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-new (param i32 i64) (result (ref $pair))
+ local.get 0
+ local.get 1
+ struct.new $pair
+ )
+
+ ;; CHECK: (func $struct-new-default (type $none_=>_ref|$pair|) (result (ref $pair))
+ ;; CHECK-NEXT: (struct.new_default $pair)
+ ;; CHECK-NEXT: )
+ (func $struct-new-default (result (ref $pair))
+ struct.new_default $pair
+ )
+
+ ;; CHECK: (func $struct-get-0 (type $ref|$pair|_=>_i32) (param $0 (ref $pair)) (result i32)
+ ;; CHECK-NEXT: (struct.get $pair 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-get-0 (param (ref $pair)) (result i32)
+ local.get 0
+ struct.get $pair 0
+ )
+
+ ;; CHECK: (func $struct-get-1 (type $ref|$pair|_=>_i64) (param $0 (ref $pair)) (result i64)
+ ;; CHECK-NEXT: (struct.get $pair 1
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-get-1 (param (ref $pair)) (result i64)
+ local.get 0
+ struct.get $pair 1
+ )
+
+ ;; CHECK: (func $struct-set-0 (type $ref|$pair|_i32_=>_none) (param $0 (ref $pair)) (param $1 i32)
+ ;; CHECK-NEXT: (struct.set $pair 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-set-0 (param (ref $pair) i32)
+ local.get 0
+ local.get 1
+ struct.set $pair 0
+ )
+
+ ;; CHECK: (func $struct-set-1 (type $ref|$pair|_i64_=>_none) (param $0 (ref $pair)) (param $1 i64)
+ ;; CHECK-NEXT: (struct.set $pair 1
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-set-1 (param (ref $pair) i64)
+ local.get 0
+ local.get 1
+ struct.set $pair 1
+ )
+
;; CHECK: (func $use-types (type $ref|$s0|_ref|$s1|_ref|$s2|_ref|$s3|_ref|$s4|_ref|$s5|_ref|$s6|_ref|$s7|_ref|$s8|_ref|$a0|_ref|$a1|_ref|$a2|_ref|$a3|_ref|$subvoid|_ref|$submany|_=>_none) (param $0 (ref $s0)) (param $1 (ref $s1)) (param $2 (ref $s2)) (param $3 (ref $s3)) (param $4 (ref $s4)) (param $5 (ref $s5)) (param $6 (ref $s6)) (param $7 (ref $s7)) (param $8 (ref $s8)) (param $9 (ref $a0)) (param $10 (ref $a1)) (param $11 (ref $a2)) (param $12 (ref $a3)) (param $13 (ref $subvoid)) (param $14 (ref $submany))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )