From 7601228b1391c31830f82c4fd5ae039b053eeb70 Mon Sep 17 00:00:00 2001 From: Ben Smith Date: Mon, 12 Oct 2015 18:21:23 -0700 Subject: [PATCH] labeled blocks and loops New syntax: (block $foo *) (loop $exit *) (loop $exit $cont *) The labels are optional, and if not specified they will not "count". That is, a numeric break will skip over them: (block $here (block (block (break 0)))) ;; goes to $here Note this is only true for blocks and loops; unnamed labels are "anonymous," and can only be referenced numerically: (label (break 0)) An additional change is that the label statement no longer has an implicit block, so this is illegal: (label $foo (nop) (nop)) --- src/wasm-gen.c | 111 ++++++++--- src/wasm-parse.c | 187 +++++++++++++----- src/wasm-parse.h | 11 +- test/d8/break.txt | 44 ++++- test/d8/expr-break.txt | 7 +- ...{break-named.txt => break-block-named.txt} | 4 +- test/dump/{break.txt => break-block.txt} | 4 +- test/dump/break-label.txt | 44 +++++ test/dump/break-loop-inner-expr.txt | 57 ++++++ test/dump/break-loop-inner.txt | 48 +++++ test/dump/break-loop.txt | 39 ++++ test/dump/expr-break.txt | 3 +- test/expr/block-named.txt | 5 + test/expr/break-block.txt | 6 + test/expr/break-loop.txt | 7 + test/expr/label.txt | 2 +- test/expr/loop-named.txt | 4 + test/typecheck/bad-break-multi-type.txt | 2 +- test/typecheck/bad-break-type-mismatch-2.txt | 4 +- test/typecheck/bad-label-multi-type.txt | 7 +- test/typecheck/bad-nested-break.txt | 15 +- test/typecheck/nested-break.txt | 13 +- test/typecheck/spec-break-multi-type.txt | 2 +- test/typecheck/spec-label-multi-type.txt | 7 +- 24 files changed, 510 insertions(+), 123 deletions(-) rename test/dump/{break-named.txt => break-block-named.txt} (97%) rename test/dump/{break.txt => break-block.txt} (97%) create mode 100644 test/dump/break-label.txt create mode 100644 test/dump/break-loop-inner-expr.txt create mode 100644 test/dump/break-loop-inner.txt create mode 100644 test/dump/break-loop.txt create mode 100644 test/expr/block-named.txt create mode 100644 test/expr/break-block.txt create mode 100644 test/expr/break-loop.txt create mode 100644 test/expr/loop-named.txt diff --git a/src/wasm-gen.c b/src/wasm-gen.c index dd4b75013..ce8b0658a 100644 --- a/src/wasm-gen.c +++ b/src/wasm-gen.c @@ -62,9 +62,10 @@ typedef struct OutputBuffer { } OutputBuffer; typedef struct LabelInfo { + struct LabelInfo* next_label; uint32_t offset; int block_depth; - struct LabelInfo* next_label; + int with_expr; } LabelInfo; typedef struct Context { @@ -516,6 +517,23 @@ static void remap_locals(Context* ctx, WasmFunction* function) { } } +static LabelInfo* push_label_info(Context* ctx) { + LabelInfo* label_info = malloc(sizeof(LabelInfo)); + label_info->offset = ctx->buf.size; + label_info->block_depth = ctx->block_depth++; + label_info->next_label = ctx->top_label; + label_info->with_expr = 0; + ctx->top_label = label_info; + return label_info; +} + +static void pop_label_info(Context* ctx) { + LabelInfo* label_info = ctx->top_label; + ctx->block_depth--; + ctx->top_label = label_info->next_label; + free(label_info); +} + static void before_module(WasmModule* module, void* user_data) { Context* ctx = user_data; ctx->function_header_offsets = realloc( @@ -579,12 +597,18 @@ static void after_function(WasmModule* module, ctx->buf.size, "FIXUP func code end offset"); } -static WasmParserCookie before_block(void* user_data) { +static WasmParserCookie before_block(int with_label, void* user_data) { Context* ctx = user_data; - WasmParserCookie cookie = (WasmParserCookie)ctx->buf.size; + WasmParserCookie cookie = 0; + /* TODO(binji): clean this up, it's a bit hacky right now. */ + if (with_label) { + push_label_info(ctx); + } else { + ctx->block_depth++; + cookie = (WasmParserCookie)ctx->buf.size; + } out_opcode(&ctx->buf, WASM_OPCODE_BLOCK); out_u8(&ctx->buf, 0, "num expressions"); - ctx->block_depth++; return cookie; } @@ -593,8 +617,14 @@ static void after_block(WasmType type, WasmParserCookie cookie, void* user_data) { Context* ctx = user_data; - ctx->block_depth--; - uint32_t offset = (uint32_t)cookie; + uint32_t offset; + if (cookie == 0) { + offset = ctx->top_label->offset; + pop_label_info(ctx); + } else { + ctx->block_depth--; + offset = (uint32_t)cookie; + } if (type != WASM_TYPE_VOID) out_u8_at(&ctx->buf, offset, WASM_OPCODE_EXPR_BLOCK, "FIXUP OPCODE_EXPR_BLOCK"); @@ -614,6 +644,7 @@ static WasmParserCookie before_break(int with_expr, LabelInfo* label_info = ctx->top_label; for (; label_depth > 0; label_depth--) label_info = label_info->next_label; + label_info->with_expr = label_info->with_expr || with_expr; int block_depth = (ctx->block_depth - 1) - label_info->block_depth; out_u8(&ctx->buf, block_depth, "break depth"); return 0; @@ -719,30 +750,21 @@ static void after_if(WasmType type, static WasmParserCookie before_label(void* user_data) { Context* ctx = user_data; - LabelInfo* label_info = malloc(sizeof(LabelInfo)); - label_info->offset = ctx->buf.size; - label_info->block_depth = ctx->block_depth++; - label_info->next_label = ctx->top_label; - ctx->top_label = label_info; + push_label_info(ctx); out_opcode(&ctx->buf, WASM_OPCODE_BLOCK); - out_u8(&ctx->buf, 0, "num expressions"); - return (WasmParserCookie)label_info; + out_u8(&ctx->buf, 1, "num expressions"); + return 0; } static void after_label(WasmType type, - int num_exprs, WasmParserCookie cookie, void* user_data) { Context* ctx = user_data; - ctx->block_depth--; - LabelInfo* label_info = (LabelInfo*)cookie; - ctx->top_label = label_info->next_label; - uint32_t offset = label_info->offset; - free(label_info); + uint32_t offset = ctx->top_label->offset; + pop_label_info(ctx); if (type != WASM_TYPE_VOID) out_u8_at(&ctx->buf, offset, WASM_OPCODE_EXPR_BLOCK, "FIXUP OPCODE_EXPR_BLOCK"); - out_u8_at(&ctx->buf, offset + 1, num_exprs, "FIXUP num expressions"); } static void before_load(enum WasmOpcode opcode, @@ -759,12 +781,32 @@ static void after_load_global(int index, void* user_data) { out_leb128(&ctx->buf, index, "global index"); } -static WasmParserCookie before_loop(void* user_data) { +static WasmParserCookie before_loop(int with_inner_label, + int with_outer_label, + void* user_data) { Context* ctx = user_data; out_opcode(&ctx->buf, WASM_OPCODE_LOOP); - WasmParserCookie cookie = (WasmParserCookie)ctx->buf.size; - out_u8(&ctx->buf, 0, "num expressions"); - ctx->block_depth++; + WasmParserCookie cookie = 0; + /* TODO(binji): clean this up, it's a bit hacky right now. */ + if (with_inner_label || with_outer_label) { + /* The outer label must be defined if the inner label is defined */ + assert(with_outer_label); + push_label_info(ctx); + if (with_inner_label) { + /* The loop only has one expression: this nested block */ + out_u8(&ctx->buf, 1, "num expressions"); + out_opcode(&ctx->buf, WASM_OPCODE_BLOCK); + push_label_info(ctx); + out_u8(&ctx->buf, 0, "num expressions"); + cookie = 1; + } else { + out_u8(&ctx->buf, 0, "num expressions"); + } + } else { + cookie = (WasmParserCookie)ctx->buf.size; + out_u8(&ctx->buf, 0, "num expressions"); + ctx->block_depth++; + } return cookie; } @@ -772,9 +814,24 @@ static void after_loop(int num_exprs, WasmParserCookie cookie, void* user_data) { Context* ctx = user_data; - ctx->block_depth--; - uint32_t offset = (uint32_t)cookie; - out_u8_at(&ctx->buf, offset, num_exprs, "FIXUP num expressions"); + if (cookie == 0 || cookie == 1) { + LabelInfo* label_info = ctx->top_label; + out_u8_at(&ctx->buf, label_info->offset, num_exprs, + "FIXUP num expressions"); + if (cookie == 1) { + /* inner and outer label */ + if (label_info->with_expr) + out_u8_at(&ctx->buf, label_info->offset - 1, WASM_OPCODE_EXPR_BLOCK, + "FIXUP OPCODE_EXPR_BLOCK"); + pop_label_info(ctx); + } + pop_label_info(ctx); + } else { + /* no labels */ + ctx->block_depth--; + uint32_t offset = (uint32_t)cookie; + out_u8_at(&ctx->buf, offset, num_exprs, "FIXUP num expressions"); + } } static void after_memory_size(void* user_data) { diff --git a/src/wasm-parse.c b/src/wasm-parse.c index a56aedb02..21bf6f235 100644 --- a/src/wasm-parse.c +++ b/src/wasm-parse.c @@ -257,14 +257,6 @@ static void* append_element(void** data, return *data + (*size)++ * elt_size; } -static void pop_label(WasmFunction* function) { - assert(function->label_bindings.size > 0); - assert(function->labels.size > 0); - free(function->label_bindings.data[function->label_bindings.size - 1].name); - function->label_bindings.size--; - function->labels.size--; -} - static int get_binding_by_name(WasmBindingVector* bindings, const char* name) { int i; for (i = 0; i < bindings->size; ++i) { @@ -962,6 +954,79 @@ static void check_type_arg(WasmParser* parser, } } +static WasmType check_label_type(WasmParser* parser, + WasmSourceLocation loc, + WasmLabel* label, + WasmType type, + WasmContinuation continuation, + const char* desc) { + if (parser->type_check == WASM_PARSER_TYPE_CHECK_SPEC) { + return type & label->type; + } else { + assert(parser->type_check == WASM_PARSER_TYPE_CHECK_V8_NATIVE); + if (label->type != WASM_TYPE_ALL) { + if (type != WASM_TYPE_VOID || continuation & WASM_CONTINUATION_NORMAL) { + check_type(parser, loc, type, label->type, desc); + } + return label->type; + } else { + return WASM_TYPE_VOID; + } + } +} + +static WasmLabel* push_anonymous_label(WasmParser* parser, + WasmFunction* function, + WasmToken t) { + WasmBinding* binding = wasm_append_binding(&function->label_bindings); + CHECK_ALLOC(parser, binding, t.range.start); + binding->name = NULL; + binding->index = function->label_bindings.size; + + WasmLabel* label = wasm_append_label(&function->labels); + CHECK_ALLOC(parser, label, t.range.start); + label->type = WASM_TYPE_ALL; + return label; +} + +static WasmLabel* push_label(WasmParser* parser, + WasmFunction* function, + WasmToken t) { + expect_var_name(parser, t); + WasmBinding* binding = wasm_append_binding(&function->label_bindings); + CHECK_ALLOC(parser, binding, t.range.start); + binding->name = + strndup(t.range.start.pos, t.range.end.pos - t.range.start.pos); + binding->index = function->label_bindings.size; + + WasmLabel* label = wasm_append_label(&function->labels); + CHECK_ALLOC(parser, label, t.range.start); + label->type = WASM_TYPE_ALL; + return label; +} + +static void pop_label(WasmFunction* function) { + assert(function->label_bindings.size > 0); + assert(function->labels.size > 0); + free(function->label_bindings.data[function->label_bindings.size - 1].name); + function->label_bindings.size--; + function->labels.size--; +} + +static WasmContinuation pop_break_continuation(WasmContinuation continuation) { + if (continuation & WASM_CONTINUATION_BREAK_0) { + continuation = + (continuation & ~WASM_CONTINUATION_BREAK_0) | WASM_CONTINUATION_NORMAL; + } + /* Shift all of the break depths down by 1, keeping the other + continuations. Since we cleared WASM_CONTINUATION_BREAK_0 above if it + was set, we don't have to worry about it shifting into a + WASM_CONTINUATION_RETURN */ + continuation = (continuation & ~CONTINUATION_BREAK_MASK) | + ((continuation & CONTINUATION_BREAK_MASK) >> 1); + return continuation; +} + static void parse_generic(WasmParser* parser) { int nesting = 1; while (nesting > 0) { @@ -1306,10 +1371,25 @@ static WasmType parse_expr(WasmParser* parser, } case WASM_OP_BLOCK: { + t = read_token(parser); + WasmLabel* label = NULL; + if (t.type == WASM_TOKEN_TYPE_ATOM) + label = push_label(parser, function, t); + else + rewind_token(parser, t); + + int with_label = label != NULL; WasmParserCookie cookie = - CALLBACK(parser, before_block, (parser->user_data)); + CALLBACK(parser, before_block, (with_label, parser->user_data)); + int num_exprs; type = parse_block(parser, module, function, &num_exprs, &continuation); + if (label) { + type = check_label_type(parser, t.range.start, label, type, + continuation, " of block final expression"); + continuation = pop_break_continuation(continuation); + pop_label(function); + } CALLBACK(parser, after_block, (type, num_exprs, cookie, parser->user_data)); break; @@ -1484,58 +1564,23 @@ static WasmType parse_expr(WasmParser* parser, CALLBACK(parser, before_label, (parser->user_data)); t = read_token(parser); - WasmBinding* binding = wasm_append_binding(&function->label_bindings); - CHECK_ALLOC(parser, binding, t.range.start); - binding->name = NULL; - binding->index = function->label_bindings.size; - - WasmLabel* label = wasm_append_label(&function->labels); - CHECK_ALLOC(parser, label, t.range.start); - label->type = WASM_TYPE_ALL; - + WasmLabel* label = NULL; if (t.type == WASM_TOKEN_TYPE_ATOM) { - /* label */ - expect_var_name(parser, t); - binding->name = - strndup(t.range.start.pos, t.range.end.pos - t.range.start.pos); + label = push_label(parser, function, t); } else if (t.type == WASM_TOKEN_TYPE_OPEN_PAREN) { - /* no label */ + label = push_anonymous_label(parser, function, t); rewind_token(parser, t); } else { unexpected_token(parser, t); } - int num_exprs; - WasmType label_type = - parse_block(parser, module, function, &num_exprs, &continuation); - if (parser->type_check == WASM_PARSER_TYPE_CHECK_SPEC) { - type = label_type & label->type; - } else { - assert(parser->type_check == WASM_PARSER_TYPE_CHECK_V8_NATIVE); - if (label->type != WASM_TYPE_ALL) { - if (label_type != WASM_TYPE_VOID || - continuation & WASM_CONTINUATION_NORMAL) { - check_type(parser, t.range.start, label_type, label->type, - " of final label expression"); - } - type = label->type; - } else { - type = WASM_TYPE_VOID; - } - } - if (continuation & WASM_CONTINUATION_BREAK_0) { - continuation = (continuation & ~WASM_CONTINUATION_BREAK_0) | - WASM_CONTINUATION_NORMAL; - } - /* Shift all of the break depths down by 1, keeping the other - continuations. Since we cleared WASM_CONTINUATION_BREAK_0 above if it - was set, we don't have to worry about it shifting into a - WASM_CONTINUATION_RETURN */ - continuation = (continuation & ~CONTINUATION_BREAK_MASK) | - ((continuation & CONTINUATION_BREAK_MASK) >> 1); + type = parse_expr(parser, module, function, &continuation); + type = check_label_type(parser, t.range.start, label, type, continuation, + " of final label expression"); + continuation = pop_break_continuation(continuation); pop_label(function); - CALLBACK(parser, after_label, - (type, num_exprs, cookie, parser->user_data)); + expect_close(parser, read_token(parser)); + CALLBACK(parser, after_label, (type, cookie, parser->user_data)); break; } @@ -1560,12 +1605,46 @@ static WasmType parse_expr(WasmParser* parser, } case WASM_OP_LOOP: { + t = read_token(parser); + WasmLabel* outer_label = NULL; + WasmLabel* inner_label = NULL; + if (t.type == WASM_TOKEN_TYPE_ATOM) { + /* outer label (i.e. break continuation) */ + outer_label = push_label(parser, function, t); + + t = read_token(parser); + if (t.type == WASM_TOKEN_TYPE_ATOM) { + /* inner label (i.e. continue continuation) */ + inner_label = push_label(parser, function, t); + } else { + rewind_token(parser, t); + } + } else { + rewind_token(parser, t); + } + + int with_inner_label = inner_label != NULL; + int with_outer_label = outer_label != NULL; WasmParserCookie cookie = - CALLBACK(parser, before_loop, (parser->user_data)); + CALLBACK(parser, before_loop, + (with_inner_label, with_outer_label, parser->user_data)); + int num_exprs; type = parse_block(parser, module, function, &num_exprs, &continuation); + if (inner_label) { + type = check_label_type(parser, t.range.start, inner_label, type, + continuation, " of inner loop block"); + continuation = pop_break_continuation(continuation); + pop_label(function); + } /* Loops are infinite, so there is never a "normal" continuation */ continuation &= ~WASM_CONTINUATION_NORMAL; + if (outer_label) { + type = check_label_type(parser, t.range.start, outer_label, type, + continuation, " of outer loop block"); + continuation = pop_break_continuation(continuation); + pop_label(function); + } CALLBACK(parser, after_loop, (num_exprs, cookie, parser->user_data)); break; } diff --git a/src/wasm-parse.h b/src/wasm-parse.h index e37c2f3f0..f46822937 100644 --- a/src/wasm-parse.h +++ b/src/wasm-parse.h @@ -31,7 +31,7 @@ typedef struct WasmParserCallbacks { void* user_data); void (*before_binary)(enum WasmOpcode opcode, void* user_data); - WasmParserCookie (*before_block)(void* user_data); + WasmParserCookie (*before_block)(int with_label, void* user_data); void (*after_block)(WasmType type, int num_exprs, WasmParserCookie cookie, @@ -49,12 +49,11 @@ typedef struct WasmParserCallbacks { void* user_data); void (*before_convert)(enum WasmOpcode opcode, void* user_data); WasmParserCookie (*before_label)(void* user_data); - void (*after_label)(WasmType type, - int num_exprs, - WasmParserCookie cookie, - void* user_data); + void (*after_label)(WasmType type, WasmParserCookie cookie, void* user_data); void (*after_get_local)(int index, void* user_data); - WasmParserCookie (*before_loop)(void* user_data); + WasmParserCookie (*before_loop)(int with_inner_label, + int with_outer_label, + void* user_data); void (*after_loop)(int num_exprs, WasmParserCookie cookie, void* user_data); WasmParserCookie (*before_if)(void* user_data); void (*after_if)(WasmType type, diff --git a/test/d8/break.txt b/test/d8/break.txt index e34b8a62c..cd2c0f6bd 100644 --- a/test/d8/break.txt +++ b/test/d8/break.txt @@ -1,9 +1,10 @@ # EXE: test/run-d8.py (module + ;; basic break test (export "break0" $break0) (func $break0 (result i32) (local i32 i32) - (label + (block $exit (if (i32.const 1) (break)) (set_local 0 (i32.const 1))) ;; not executed (set_local 1 (i32.const 1)) @@ -11,11 +12,12 @@ (i32.eq (get_local 0) (i32.const 0)) (i32.eq (get_local 1) (i32.const 1))))) + ;; test breaking with a depth > 0 (export "break1" $break1) (func $break1 (result i32) (local i32 i32 i32) - (label - (label + (block $outer + (block $inner (if (i32.const 1) (break 1)) (set_local 0 (i32.const 1))) ;; not executed (set_local 1 (i32.const 1))) ;; not executed @@ -24,7 +26,43 @@ (i32.add (i32.eq (get_local 0) (i32.const 0)) (i32.eq (get_local 1) (i32.const 0))) (i32.eq (get_local 2) (i32.const 1))))) + + ;; test breaking to a label + (export "break2" $break2) + (func $break2 (result i32) + (label $exit + (block + (if (i32.const 1) + (break $exit)) + (return (i32.const 1)))) ;; not executed + (return (i32.const 2))) + + ;; test breaking in a loop with an exit and continue continuation + (export "break3" $break3) + (func $break3 (result i32) + (local i32 i32) + (loop $exit $cont + (set_local 0 (i32.add (get_local 0) (i32.const 1))) + (if (i32.ge_s (get_local 0) (i32.const 5)) + (break $exit)) + (if (i32.eq (get_local 0) (i32.const 4)) + (break $cont)) + (set_local 1 (get_local 0))) + (return (get_local 1))) + + ;; test breaking from a loop with only exit continuation + (export "break4" $break4) + (func $break4 (result i32) + (local i32) + (loop $exit + (if (i32.eq (get_local 0) (i32.const 3)) + (break $exit)) + (set_local 0 (i32.add (get_local 0) (i32.const 1)))) + (return (get_local 0))) ) # STDOUT: break0() = 2 break1() = 3 +break2() = 2 +break3() = 3 +break4() = 3 diff --git a/test/d8/expr-break.txt b/test/d8/expr-break.txt index e470c637b..210602026 100644 --- a/test/d8/expr-break.txt +++ b/test/d8/expr-break.txt @@ -2,9 +2,10 @@ (module (func $f (param i32) (result i32) (label - (if (i32.eq (get_local 0) (i32.const 0)) - (break (i32.const 1))) - (i32.const 2))) + (block + (if (i32.eq (get_local 0) (i32.const 0)) + (break (i32.const 1))) + (i32.const 2)))) (func $test1 (result i32) (call $f (i32.const 0))) diff --git a/test/dump/break-named.txt b/test/dump/break-block-named.txt similarity index 97% rename from test/dump/break-named.txt rename to test/dump/break-block-named.txt index c9a69cbc7..6472d99e4 100644 --- a/test/dump/break-named.txt +++ b/test/dump/break-block-named.txt @@ -1,11 +1,11 @@ # FLAGS: -dv (module (func - (label $outer ;; 3 + (block $outer ;; 3 (loop ;; 2 (block ;; 1 (i32.const 0) - (label $inner ;; 0 + (block $inner ;; 0 (break $inner) (break $outer))))))) # STDOUT: diff --git a/test/dump/break.txt b/test/dump/break-block.txt similarity index 97% rename from test/dump/break.txt rename to test/dump/break-block.txt index 1696bbe59..b361aeb06 100644 --- a/test/dump/break.txt +++ b/test/dump/break-block.txt @@ -1,11 +1,11 @@ # FLAGS: -dv (module (func - (label ;; 3 + (block $outer ;; 3 (loop ;; 2 (block ;; 1 (i32.const 0) - (label ;; 0 + (block $inner ;; 0 (break) (break 0) (break 1))))))) diff --git a/test/dump/break-label.txt b/test/dump/break-label.txt new file mode 100644 index 000000000..36b1e730c --- /dev/null +++ b/test/dump/break-label.txt @@ -0,0 +1,44 @@ +# FLAGS: -dv +(module + (func + (label $foo + (block + (nop) + (if (i32.const 1) + (break $foo)))))) +# STDOUT: +0000000: 00 ; mem size log 2 +0000001: 01 ; export mem +0000002: 0000 ; num globals +0000004: 0100 ; num funcs +0000006: 0000 ; num data segments +; function header 0 +0000008: 00 ; func num args +0000009: 00 ; func result type +000000a: 0000 0000 ; func name offset +000000e: 0000 0000 ; func code start offset +0000012: 0000 0000 ; func code end offset +0000016: 0000 ; num local i32 +0000018: 0000 ; num local i64 +000001a: 0000 ; num local f32 +000001c: 0000 ; num local f64 +000001e: 00 ; export func +000001f: 00 ; func external +; function data 0 +000000e: 2000 0000 ; FIXUP func code start offset +0000020: 03 ; OPCODE_BLOCK +0000021: 01 ; num expressions +0000022: 03 ; OPCODE_BLOCK +0000023: 00 ; num expressions +0000024: 00 ; OPCODE_NOP +0000025: 01 ; OPCODE_IF +0000026: 10 ; OPCODE_I8_CONST +0000027: 01 ; u8 literal +0000028: 08 ; OPCODE_BREAK +0000029: 01 ; break depth +0000023: 02 ; FIXUP num expressions +0000012: 2a00 0000 ; FIXUP func code end offset +; names +0000000: 0001 0000 0100 0000 0000 0000 0000 2000 .............. . +0000010: 0000 2a00 0000 0000 0000 0000 0000 0000 ..*............. +0000020: 0301 0302 0001 1001 0801 .......... diff --git a/test/dump/break-loop-inner-expr.txt b/test/dump/break-loop-inner-expr.txt new file mode 100644 index 000000000..8d052e9b6 --- /dev/null +++ b/test/dump/break-loop-inner-expr.txt @@ -0,0 +1,57 @@ +# FLAGS: -dv +(module + (func + (loop $exit $count + (if (i32.const 1) + (break $count (i32.const 2))) + (if (i32.const 3) + (break $exit (i32.const 4))) + (i32.const 5)))) +# STDOUT: +0000000: 00 ; mem size log 2 +0000001: 01 ; export mem +0000002: 0000 ; num globals +0000004: 0100 ; num funcs +0000006: 0000 ; num data segments +; function header 0 +0000008: 00 ; func num args +0000009: 00 ; func result type +000000a: 0000 0000 ; func name offset +000000e: 0000 0000 ; func code start offset +0000012: 0000 0000 ; func code end offset +0000016: 0000 ; num local i32 +0000018: 0000 ; num local i64 +000001a: 0000 ; num local f32 +000001c: 0000 ; num local f64 +000001e: 00 ; export func +000001f: 00 ; func external +; function data 0 +000000e: 2000 0000 ; FIXUP func code start offset +0000020: 06 ; OPCODE_LOOP +0000021: 01 ; num expressions +0000022: 03 ; OPCODE_BLOCK +0000023: 00 ; num expressions +0000024: 01 ; OPCODE_IF +0000025: 10 ; OPCODE_I8_CONST +0000026: 01 ; u8 literal +0000027: 1f ; OPCODE_EXPR_BREAK +0000028: 00 ; break depth +0000029: 10 ; OPCODE_I8_CONST +000002a: 02 ; u8 literal +000002b: 01 ; OPCODE_IF +000002c: 10 ; OPCODE_I8_CONST +000002d: 03 ; u8 literal +000002e: 1f ; OPCODE_EXPR_BREAK +000002f: 01 ; break depth +0000030: 10 ; OPCODE_I8_CONST +0000031: 04 ; u8 literal +0000032: 10 ; OPCODE_I8_CONST +0000033: 05 ; u8 literal +0000023: 03 ; FIXUP num expressions +0000022: 1d ; FIXUP OPCODE_EXPR_BLOCK +0000012: 3400 0000 ; FIXUP func code end offset +; names +0000000: 0001 0000 0100 0000 0000 0000 0000 2000 .............. . +0000010: 0000 3400 0000 0000 0000 0000 0000 0000 ..4............. +0000020: 0601 1d03 0110 011f 0010 0201 1003 1f01 ................ +0000030: 1004 1005 .... diff --git a/test/dump/break-loop-inner.txt b/test/dump/break-loop-inner.txt new file mode 100644 index 000000000..ed72d99c2 --- /dev/null +++ b/test/dump/break-loop-inner.txt @@ -0,0 +1,48 @@ +# FLAGS: -dv +(module + (func + (loop $exit $cont + (if (i32.const 1) + (break $exit)) + (if (i32.const 2) + (break $cont))))) +# STDOUT: +0000000: 00 ; mem size log 2 +0000001: 01 ; export mem +0000002: 0000 ; num globals +0000004: 0100 ; num funcs +0000006: 0000 ; num data segments +; function header 0 +0000008: 00 ; func num args +0000009: 00 ; func result type +000000a: 0000 0000 ; func name offset +000000e: 0000 0000 ; func code start offset +0000012: 0000 0000 ; func code end offset +0000016: 0000 ; num local i32 +0000018: 0000 ; num local i64 +000001a: 0000 ; num local f32 +000001c: 0000 ; num local f64 +000001e: 00 ; export func +000001f: 00 ; func external +; function data 0 +000000e: 2000 0000 ; FIXUP func code start offset +0000020: 06 ; OPCODE_LOOP +0000021: 01 ; num expressions +0000022: 03 ; OPCODE_BLOCK +0000023: 00 ; num expressions +0000024: 01 ; OPCODE_IF +0000025: 10 ; OPCODE_I8_CONST +0000026: 01 ; u8 literal +0000027: 08 ; OPCODE_BREAK +0000028: 01 ; break depth +0000029: 01 ; OPCODE_IF +000002a: 10 ; OPCODE_I8_CONST +000002b: 02 ; u8 literal +000002c: 08 ; OPCODE_BREAK +000002d: 00 ; break depth +0000023: 02 ; FIXUP num expressions +0000012: 2e00 0000 ; FIXUP func code end offset +; names +0000000: 0001 0000 0100 0000 0000 0000 0000 2000 .............. . +0000010: 0000 2e00 0000 0000 0000 0000 0000 0000 ................ +0000020: 0601 0302 0110 0108 0101 1002 0800 .............. diff --git a/test/dump/break-loop.txt b/test/dump/break-loop.txt new file mode 100644 index 000000000..1c35f50a6 --- /dev/null +++ b/test/dump/break-loop.txt @@ -0,0 +1,39 @@ +# FLAGS: -dv +(module + (func + (loop $exit + (if (i32.const 1) + (break $exit))))) +# STDOUT: +0000000: 00 ; mem size log 2 +0000001: 01 ; export mem +0000002: 0000 ; num globals +0000004: 0100 ; num funcs +0000006: 0000 ; num data segments +; function header 0 +0000008: 00 ; func num args +0000009: 00 ; func result type +000000a: 0000 0000 ; func name offset +000000e: 0000 0000 ; func code start offset +0000012: 0000 0000 ; func code end offset +0000016: 0000 ; num local i32 +0000018: 0000 ; num local i64 +000001a: 0000 ; num local f32 +000001c: 0000 ; num local f64 +000001e: 00 ; export func +000001f: 00 ; func external +; function data 0 +000000e: 2000 0000 ; FIXUP func code start offset +0000020: 06 ; OPCODE_LOOP +0000021: 00 ; num expressions +0000022: 01 ; OPCODE_IF +0000023: 10 ; OPCODE_I8_CONST +0000024: 01 ; u8 literal +0000025: 08 ; OPCODE_BREAK +0000026: 00 ; break depth +0000021: 01 ; FIXUP num expressions +0000012: 2700 0000 ; FIXUP func code end offset +; names +0000000: 0001 0000 0100 0000 0000 0000 0000 2000 .............. . +0000010: 0000 2700 0000 0000 0000 0000 0000 0000 ..'............. +0000020: 0601 0110 0108 00 ....... diff --git a/test/dump/expr-break.txt b/test/dump/expr-break.txt index 45bbd6645..46bc831c4 100644 --- a/test/dump/expr-break.txt +++ b/test/dump/expr-break.txt @@ -24,13 +24,12 @@ ; function data 0 000000e: 2000 0000 ; FIXUP func code start offset 0000020: 03 ; OPCODE_BLOCK -0000021: 00 ; num expressions +0000021: 01 ; num expressions 0000022: 1f ; OPCODE_EXPR_BREAK 0000023: 00 ; break depth 0000024: 10 ; OPCODE_I8_CONST 0000025: 01 ; u8 literal 0000020: 1d ; FIXUP OPCODE_EXPR_BLOCK -0000021: 01 ; FIXUP num expressions 0000012: 2600 0000 ; FIXUP func code end offset ; names 0000000: 0001 0000 0100 0000 0000 0000 0000 2000 .............. . diff --git a/test/expr/block-named.txt b/test/expr/block-named.txt new file mode 100644 index 000000000..24eefe1a4 --- /dev/null +++ b/test/expr/block-named.txt @@ -0,0 +1,5 @@ +(module + (func + (block $foo + (nop) + (nop)))) diff --git a/test/expr/break-block.txt b/test/expr/break-block.txt new file mode 100644 index 000000000..40f9892b2 --- /dev/null +++ b/test/expr/break-block.txt @@ -0,0 +1,6 @@ +(module + (func + (block $exit1 + (break 0)) + (block $exit2 + (break $exit2)))) diff --git a/test/expr/break-loop.txt b/test/expr/break-loop.txt new file mode 100644 index 000000000..9bc1fc493 --- /dev/null +++ b/test/expr/break-loop.txt @@ -0,0 +1,7 @@ +(module + (func + (loop $exit + (break $exit)) + (loop $outer $inner + (break $inner) + (break $outer)))) diff --git a/test/expr/label.txt b/test/expr/label.txt index 6f24a4461..283bb2feb 100644 --- a/test/expr/label.txt +++ b/test/expr/label.txt @@ -1,2 +1,2 @@ (module (func - (label (nop) (nop)))) + (label (nop)))) diff --git a/test/expr/loop-named.txt b/test/expr/loop-named.txt new file mode 100644 index 000000000..fdc975f68 --- /dev/null +++ b/test/expr/loop-named.txt @@ -0,0 +1,4 @@ +(module + (func + (loop $outer (nop)) + (loop $inner $outer (nop)))) diff --git a/test/typecheck/bad-break-multi-type.txt b/test/typecheck/bad-break-multi-type.txt index 4ac21dccd..77957d2dd 100644 --- a/test/typecheck/bad-break-multi-type.txt +++ b/test/typecheck/bad-break-multi-type.txt @@ -4,7 +4,7 @@ ;; similarly for now. (module (func - (label + (block $exit (break (i32.const 1)) (break (f32.const 2))))) # STDERR: diff --git a/test/typecheck/bad-break-type-mismatch-2.txt b/test/typecheck/bad-break-type-mismatch-2.txt index f37033831..a23f82f70 100644 --- a/test/typecheck/bad-break-type-mismatch-2.txt +++ b/test/typecheck/bad-break-type-mismatch-2.txt @@ -1,8 +1,8 @@ # ERROR: 1 (module (func - (label + (block $exit (break (i32.const 1)) (f32.const 2)))) # STDERR: -typecheck/bad-break-type-mismatch-2.txt:5:7: type mismatch of final label expression. got f32, expected i32 +typecheck/bad-break-type-mismatch-2.txt:4:12: type mismatch of block final expression. got f32, expected i32 diff --git a/test/typecheck/bad-label-multi-type.txt b/test/typecheck/bad-label-multi-type.txt index cb075e533..6096af64a 100644 --- a/test/typecheck/bad-label-multi-type.txt +++ b/test/typecheck/bad-label-multi-type.txt @@ -2,8 +2,9 @@ (module (func (label - (if (i32.const 1) - (break (i32.const 2))) - (f32.const 3)))) + (block + (if (i32.const 1) + (break (i32.const 2))) + (f32.const 3))))) # STDERR: typecheck/bad-label-multi-type.txt:5:7: type mismatch of final label expression. got f32, expected i32 diff --git a/test/typecheck/bad-nested-break.txt b/test/typecheck/bad-nested-break.txt index b87aae2c6..c5ea2a480 100644 --- a/test/typecheck/bad-nested-break.txt +++ b/test/typecheck/bad-nested-break.txt @@ -2,14 +2,15 @@ (module (func (result i32) (label $outer - (label $inner - (loop - (if (i32.const 1) - (break $outer)) - (break $inner))) - (return (i32.const 2))) + (block + (label $inner + (loop + (if (i32.const 1) + (break $outer)) + (break $inner))) + (return (i32.const 2)))) ;; the existence of the (break $outer) statement requires there to be a ;; return statement here. )) # STDERR: -typecheck/bad-nested-break.txt:13:3: type mismatch in function result. got void, expected i32 +typecheck/bad-nested-break.txt:14:3: type mismatch in function result. got void, expected i32 diff --git a/test/typecheck/nested-break.txt b/test/typecheck/nested-break.txt index bfeb56ac2..182cac995 100644 --- a/test/typecheck/nested-break.txt +++ b/test/typecheck/nested-break.txt @@ -1,10 +1,11 @@ (module (func (result i32) (label $outer - (label $inner - (loop - (if (i32.const 1) - (break $outer)) - (break $inner))) - (return (i32.const 2))) + (block + (label $inner + (loop + (if (i32.const 1) + (break $outer)) + (break $inner))) + (return (i32.const 2)))) (return (i32.const 3)))) diff --git a/test/typecheck/spec-break-multi-type.txt b/test/typecheck/spec-break-multi-type.txt index 2a50829f5..7db2c7c5a 100644 --- a/test/typecheck/spec-break-multi-type.txt +++ b/test/typecheck/spec-break-multi-type.txt @@ -1,7 +1,7 @@ # FLAGS: --typecheck-spec (module (func - (label + (block $exit (if (i32.const 1) (break (i32.const 2))) (break (f32.const 3))))) diff --git a/test/typecheck/spec-label-multi-type.txt b/test/typecheck/spec-label-multi-type.txt index 33281b274..31270b34c 100644 --- a/test/typecheck/spec-label-multi-type.txt +++ b/test/typecheck/spec-label-multi-type.txt @@ -2,6 +2,7 @@ (module (func (label - (if (i32.const 1) - (break (i32.const 2))) - (f32.const 3)))) + (block + (if (i32.const 1) + (break (i32.const 2))) + (f32.const 3)))))