diff options
-rw-r--r-- | src/wasm/wat-parser.cpp | 135 | ||||
-rw-r--r-- | test/lit/wat-kitchen-sink.wast | 81 |
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: ) |