protoverse

A metaverse protocol
git clone git://jb55.com/protoverse
Log | Files | Refs | README | LICENSE

commit 18bc859edca6dfac38141836fb44b42a0ba291c8
parent d5f5405b8020f2c999b0b282534f4123d4d60ed6
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 16 Jul 2021 13:45:03 -0700

unresolved label breaks

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Msrc/wasm.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/wasm.h | 11+++++++++--
2 files changed, 146 insertions(+), 78 deletions(-)

diff --git a/src/wasm.c b/src/wasm.c @@ -1343,13 +1343,13 @@ static INLINE void make_expr_parser(struct errors *errs, struct cursor *code, } static int parse_instrs_until(struct expr_parser *p, u8 stop_instr, - struct cursor *parsed_instrs) + u8 **parsed_instrs, int *instr_len) { u8 tag; struct instr op; - parsed_instrs->start = p->code->p; - parsed_instrs->p = p->code->p; + *parsed_instrs = p->code->p; + *instr_len = 0; for (;;) { if (!pull_byte(p->code, &tag)) @@ -1360,7 +1360,7 @@ static int parse_instrs_until(struct expr_parser *p, u8 stop_instr, "parse %s instr (0x%x)", instr_name(tag), tag); if (tag == stop_instr) { - parsed_instrs->end = p->code->p; + *instr_len = p->code->p - *parsed_instrs; return 1; } } @@ -1369,16 +1369,12 @@ static int parse_instrs_until(struct expr_parser *p, u8 stop_instr, static int parse_expr(struct wasm_parser *p, struct expr *expr) { struct expr_parser parser; - struct cursor code; make_expr_parser(&p->errs, &p->cur, &parser); - if (!parse_instrs_until(&parser, i_end, &code)) + if (!parse_instrs_until(&parser, i_end, &expr->code, &expr->code_len)) return parse_err(p, "instrs"); - expr->code = code.start; - expr->code_len = code.end - code.start; - return 1; } @@ -2427,13 +2423,14 @@ static int resolve_label(struct label *label, struct cursor *code) return 1; } -static INLINE int pop_resolver(struct wasm_interp *interp, u16 *label_ind) +static INLINE int pop_resolver(struct wasm_interp *interp, struct resolver *resolver) { - if (!cursor_pop(&interp->resolver_stack, (u8*)&label_ind, sizeof(u16))) { + if (!cursor_pop(&interp->resolver_stack, (u8*)resolver, sizeof(*resolver))) { return interp_error(interp, "pop resolver"); } - debug("popped resolver stack %ld\n", - cursor_count(&interp->resolver_stack, sizeof(u16))); + debug("popped resolver stack %d i_%s %ld\n", + resolver->label, instr_name(resolver->end_tag), + cursor_count(&interp->resolver_stack, sizeof(*resolver))); return 1; } @@ -2441,15 +2438,18 @@ static int pop_label_checkpoint(struct wasm_interp *interp) { struct label *label; struct callframe *frame; - u16 label_ind = 0; + struct resolver resolver; + + resolver.label = 0; + resolver.end_tag = 0; - if (unlikely(!pop_resolver(interp, &label_ind))) + if (unlikely(!pop_resolver(interp, &resolver))) return interp_error(interp, "couldn't pop jump resolver stack"); if (unlikely(!(frame = top_callframe(&interp->callframes)))) return interp_error(interp, "no callframe?"); - if (unlikely(!(label = index_label(&interp->labels, frame->fn, label_ind)))) + if (unlikely(!(label = index_label(&interp->labels, frame->fn, resolver.label)))) return interp_error(interp, "index label"); if (unlikely(!resolve_label(label, &frame->code))) @@ -2524,17 +2524,23 @@ static int upsert_label(struct wasm_interp *interp, int fn, return 2; } +static INLINE int cursor_push_resolver(struct cursor *stack, struct resolver *resolver) +{ + return cursor_push(stack, (u8*)resolver, sizeof(*resolver)); +} // when we encounter a control instruction, try to resolve the label, otherwise // push the label index to the resolver stack for resolution later -static int push_label_checkpoint(struct wasm_interp *interp, struct label **label) +static int push_label_checkpoint(struct wasm_interp *interp, struct label **label, u8 end_tag) { u32 instr_pos; - int ind; - - int fns; + int ind, fns; + struct resolver resolver; struct callframe *frame; + resolver.end_tag = end_tag; + resolver.label = 0; + *label = NULL; fns = functions_count(interp->module); @@ -2555,16 +2561,47 @@ static int push_label_checkpoint(struct wasm_interp *interp, struct label **labe return interp_error(interp, "couldn't index label"); } - if (unlikely(!cursor_push_u16(&interp->resolver_stack, ind))) { + resolver.label = ind; + + if (unlikely(!cursor_push_resolver(&interp->resolver_stack, &resolver))) { return interp_error(interp, "push label index to resolver stack oob"); } - debug("pushed resolver stack %ld\n", - cursor_count(&interp->resolver_stack, sizeof(u16))); + debug("pushed resolver stack %d i_%s %ld \n", + resolver.label, instr_name(resolver.end_tag), + cursor_count(&interp->resolver_stack, sizeof(resolver))); + + return 1; +} + +static int interp_jump(struct wasm_interp *interp, int jmp) +{ + struct callframe *frame; + + frame = top_callframe(&interp->callframes); + if (unlikely(!frame)) { + return interp_error(interp, "no callframe?"); + } + + debug("jumping to %04x\n", jmp); + frame->code.p = frame->code.start + jmp; + + if (unlikely(frame->code.p >= frame->code.end)) { + return interp_error(interp, + "code pointer at or past end, evil jump?"); + } return 1; } +static int pop_label_and_jump(struct wasm_interp *interp, int jump) +{ + if (!pop_label_checkpoint(interp)) + return interp_error(interp, "pop checkpoint"); + + return interp_jump(interp, jump); +} + static int parse_block(struct expr_parser *p, struct block *block, u8 end_tag) { struct label *label = NULL; @@ -2574,20 +2611,22 @@ static int parse_block(struct expr_parser *p, struct block *block, u8 end_tag) // if we don't have an interpreter instance, we don't care about // label resolution (NOT TRUE ANYMORE!) - if (p->interp != NULL && !push_label_checkpoint(p->interp, &label)) + if (p->interp != NULL && !push_label_checkpoint(p->interp, &label, i_end)) return note_error(p->errs, p->code, "push checkpoint"); if (label && is_label_resolved(label)) { - if (!pop_label_checkpoint(p->interp)) - return note_error(p->errs, p->code, "pop checkpoint"); - make_cursor(p->code->p + label_instr_pos(label), - p->code->p + label->jump, - &block->instrs); - p->code->p = p->code->start + label->jump; - return 1; + // TODO verify this is correct + block->instrs = p->code->start + label_instr_pos(label); + block->instrs_len = (p->code->start + label->jump) - block->instrs; + + if (unlikely(block->instrs_len < 0)) { + return interp_error(p->interp, "jump is before instr_pos ??"); + } + + return pop_label_and_jump(p->interp, label->jump); } - if (!parse_instrs_until(p, end_tag, &block->instrs)) + if (!parse_instrs_until(p, end_tag, &block->instrs, &block->instrs_len)) return note_error(p->errs, p->code, "parse instrs"); if (p->interp) @@ -2753,33 +2792,14 @@ static int parse_instr(struct expr_parser *p, u8 tag, struct instr *op) return note_error(p->errs, p->code, "unhandled tag: 0x%x", tag); } -static int interp_jump(struct wasm_interp *interp, int jmp) -{ - struct callframe *frame; - - frame = top_callframe(&interp->callframes); - if (unlikely(!frame)) { - return interp_error(interp, "no callframe?"); - } - - debug("jumping to %04x\n", jmp); - frame->code.p = frame->code.start + jmp; - - if (unlikely(frame->code.p >= frame->code.end)) { - return interp_error(interp, - "code pointer at or past end, evil jump?"); - } - - return 1; -} - static int branch_jump(struct wasm_interp *interp, u8 end_tag) { - struct cursor instrs; + u8 *instrs; + int instrs_len; struct expr_parser parser; struct label *label; - if (!push_label_checkpoint(interp, &label)) { + if (!push_label_checkpoint(interp, &label, end_tag)) { return interp_error(interp, "label checkpoint"); } @@ -2788,18 +2808,14 @@ static int branch_jump(struct wasm_interp *interp, u8 end_tag) } if (is_label_resolved(label)) { - if (!pop_label_checkpoint(interp)) { - return interp_error(interp, "pop label resolver"); - } - return interp_jump(interp, label->jump); + return pop_label_and_jump(interp, label->jump); } make_interp_expr_parser(interp, &parser); // consume instructions, use resolver stack to resolve jumps - if (!parse_instrs_until(&parser, end_tag, &instrs)) { - interp_error(interp, "parse instrs"); - return 0; + if (!parse_instrs_until(&parser, end_tag, &instrs, &instrs_len)) { + return interp_error(interp, "parse instrs"); } return pop_label_checkpoint(interp); @@ -2820,15 +2836,11 @@ static int interp_block(struct wasm_interp *interp) return interp_error(interp, "couldn't parse blocktype"); } - if (unlikely(!push_label_checkpoint(interp, &label))) { + if (unlikely(!push_label_checkpoint(interp, &label, i_end))) { return interp_error(interp, "block label checkpoint"); } - if (unlikely(!interp_code(interp))) { - return interp_error(interp, "interp code"); - } - - return pop_label_checkpoint(interp); + return 1; } static int interp_if(struct wasm_interp *interp) @@ -2875,26 +2887,74 @@ static int interp_i32_eqz(struct wasm_interp *interp) return cursor_pushval(&interp->stack, &res); } -static INLINE int top_resolver_stack(struct cursor *stack, int index, int *label) +static INLINE int top_resolver_stack(struct cursor *stack, int index, struct resolver **resolver) { - u16 *p = (u16*)stack->p; + struct resolver *p = (struct resolver*)stack->p; p = &p[-(index+1)]; - if (p < (u16*)stack->start) + if (p < (struct resolver*)stack->start) return 0; - *label = *p; + *resolver = p; + return 1; +} + +static int unresolved_break(struct wasm_interp *interp, int index) +{ + struct expr_parser parser; + struct resolver *resolver; + struct callframe *frame; + struct label *label; + u8 *instrs; + int instrs_len; + + make_interp_expr_parser(interp, &parser); + + if (unlikely(!(frame = top_callframe(&interp->callframes)))) { + return interp_error(interp, "no top callframe?"); + } + + while (index-- >= 0) { + if (unlikely(!top_resolver_stack(&interp->resolver_stack, 0, + &resolver))) { + return interp_error(interp, "invalid resolver index %d", + index); + } + + debug("unresolved break index %d, ends at i_%s\n", index, + instr_name(resolver->end_tag)); + + // TODO: breaking from functions (return) + if (unlikely(!(label = index_label(&interp->labels, frame->fn, + resolver->label)))) { + return interp_error(interp, "no label"); + } + + if (is_label_resolved(label)) { + return pop_label_and_jump(interp, label->jump); + } + + if (unlikely(!parse_instrs_until(&parser, resolver->end_tag, + &instrs, &instrs_len))) { + return interp_error(interp, "parsing instrs"); + } + + if (unlikely(!pop_label_checkpoint(interp))) { + return interp_error(interp, "pop label"); + } + } + return 1; } static int interp_br_jump(struct wasm_interp *interp, int index) { - int ind; struct label *label; + struct resolver *resolver = NULL; - if (unlikely(!top_resolver_stack(&interp->resolver_stack, index, &ind))) { + if (unlikely(!top_resolver_stack(&interp->resolver_stack, index, &resolver))) { return interp_error(interp, "invalid resolver index %d", index); } - if (unlikely(!(label = index_frame_label(interp, ind)))) { + if (unlikely(!(label = index_frame_label(interp, resolver->label)))) { return interp_error(interp, "index label"); } @@ -2902,7 +2962,7 @@ static int interp_br_jump(struct wasm_interp *interp, int index) return interp_jump(interp, label->jump); } - return interp_error(interp, "TODO: br_jump on unresolved label"); + return unresolved_break(interp, index); } static int interp_br_if(struct wasm_interp *interp) @@ -2948,6 +3008,7 @@ static int interp_instr(struct wasm_interp *interp, u8 tag) case i_i32_const: return interp_i32_const(interp); case i_i32_gt_u: return interp_i32_gt_u(interp); case i_if: return interp_if(interp); + case i_end: return pop_label_checkpoint(interp); case i_call: return interp_call(interp); case i_block: return interp_block(interp); case i_br_if: return interp_br_if(interp); @@ -3064,7 +3125,7 @@ void wasm_interp_init(struct wasm_interp *interp, struct module *module) locals_size = sizeof(struct val) * 0xFF; offsets_size = sizeof(int) * 0xFF; callframes_size = sizeof(struct callframe) * 0xFF; - resolver_size = sizeof(u16) * MAX_LABELS; + resolver_size = sizeof(struct resolver) * MAX_LABELS; interp->labels.elem_size = sizeof(struct label); interp->num_labels.elem_size = num_labels_elemsize; diff --git a/src/wasm.h b/src/wasm.h @@ -388,7 +388,8 @@ struct blocktype { struct block { struct blocktype type; - struct cursor instrs; + unsigned char *instrs; + int instrs_len; }; struct memarg { @@ -463,6 +464,12 @@ struct callframe { int fn; }; +struct resolver { + u16 label; + u8 end_tag; + u8 __unused; +}; + struct wasm_interp { struct module *module; @@ -481,7 +488,7 @@ struct wasm_interp { // resolve stack for the current function. every time a control // instruction is encountered, the label index is pushed. When an // instruction is popped, we can resolve the label - struct cursor resolver_stack; /* u16 */ + struct cursor resolver_stack; /* struct resolver */ }; struct builtin {