i32 label_idx;
i32 static_arr;
i32 static_idx;
+ bool targets_else;
};
ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, i32 param_count, i32 local_count);
label_target_t ovm_code_builder_wasm_target_idx(ovm_code_builder_t *builder, i32 idx);
i32 ovm_code_builder_push_label_target(ovm_code_builder_t *builder, label_kind_t kind);
void ovm_code_builder_pop_label_target(ovm_code_builder_t *builder);
+void ovm_code_builder_patch_else(ovm_code_builder_t *builder, label_target_t if_target);
void ovm_code_builder_free(ovm_code_builder_t *builder);
void ovm_code_builder_add_nop(ovm_code_builder_t *builder);
void ovm_code_builder_add_binop(ovm_code_builder_t *builder, u32 instr);
void ovm_code_builder_add_unop(ovm_code_builder_t *builder, u32 instr);
void ovm_code_builder_add_imm(ovm_code_builder_t *builder, u32 ovm_type, void *imm);
void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 label_idx);
-void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx, bool branch_if_true);
+void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx, bool branch_if_true, bool targets_else);
void ovm_code_builder_add_branch_table(ovm_code_builder_t *builder, i32 count, i32 *label_indicies, i32 default_label_idx);
void ovm_code_builder_add_return(ovm_code_builder_t *builder);
void ovm_code_builder_add_call(ovm_code_builder_t *builder, i32 func_idx, i32 param_count, bool has_return_value);
void ovm_code_builder_drop_value(ovm_code_builder_t *builder);
void ovm_code_builder_add_local_get(ovm_code_builder_t *builder, i32 local_idx);
void ovm_code_builder_add_local_set(ovm_code_builder_t *builder, i32 local_idx);
+void ovm_code_builder_add_local_tee(ovm_code_builder_t *builder, i32 local_idx);
void ovm_code_builder_add_register_get(ovm_code_builder_t *builder, i32 local_idx);
void ovm_code_builder_add_register_set(ovm_code_builder_t *builder, i32 local_idx);
void ovm_code_builder_add_load(ovm_code_builder_t *builder, u32 ovm_type, i32 offset);
}
}
+void ovm_code_builder_patch_else(ovm_code_builder_t *builder, label_target_t if_target) {
+ assert(if_target.kind == label_kind_if);
+
+ fori (i, 0, bh_arr_length(builder->branch_patches)) {
+ branch_patch_t patch = builder->branch_patches[i];
+ if (patch.label_idx != if_target.idx) continue;
+ if (!patch.targets_else) continue;
+
+ int br_delta = bh_arr_length(builder->program->code) - patch.branch_instr - 1;
+ assert(patch.kind == branch_patch_instr_a);
+
+ builder->program->code[patch.branch_instr].a = br_delta;
+
+ bh_arr_fastdelete(builder->branch_patches, i);
+ return;
+ }
+}
+
void ovm_code_builder_add_nop(ovm_code_builder_t *builder) {
ovm_instr_t nop = {0};
nop.full_instr = OVMI_NOP;
patch.kind = branch_patch_instr_a;
patch.branch_instr = bh_arr_length(builder->program->code);
patch.label_idx = label_idx;
+ patch.targets_else = false;
bh_arr_push(builder->branch_patches, patch);
ovm_program_add_instructions(builder->program, 1, &branch_instr);
}
-void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx, bool branch_if_true) {
+void ovm_code_builder_add_cond_branch(ovm_code_builder_t *builder, i32 label_idx, bool branch_if_true, bool targets_else) {
ovm_instr_t branch_instr = {0};
if (branch_if_true) {
branch_instr.full_instr = OVMI_BR_NZ;
patch.kind = branch_patch_instr_a;
patch.branch_instr = bh_arr_length(builder->program->code);
patch.label_idx = label_idx;
+ patch.targets_else = targets_else;
bh_arr_push(builder->branch_patches, patch);
patch.label_idx = label_indicies[i];
patch.static_arr = table_idx;
patch.static_idx = i;
+ patch.targets_else = false;
bh_arr_push(builder->branch_patches, patch);
}
default_patch.kind = branch_patch_instr_a;
default_patch.branch_instr = bh_arr_length(builder->program->code) + 2;
default_patch.label_idx = default_label_idx;
+ default_patch.targets_else = false;
bh_arr_push(builder->branch_patches, default_patch);
ovm_program_add_instructions(builder->program, 5, instrs);
ovm_program_add_instructions(builder->program, 1, &instr);
}
+void ovm_code_builder_add_local_tee(ovm_code_builder_t *builder, i32 local_idx) {
+ ovm_instr_t instr = {0};
+ instr.full_instr = OVMI_MOV;
+ instr.r = local_idx; // This makes the assumption that the params will be in
+ // the lower "address space" of the value numbers. This
+ // will be true for web assembly, because that's how it
+ // it was spec'd; but in the future for other things,
+ // this will be incorrect.
+ instr.a = POP_VALUE(builder);
+
+ ovm_program_add_instructions(builder->program, 1, &instr);
+
+ PUSH_VALUE(builder, instr.a);
+}
+
void ovm_code_builder_add_register_get(ovm_code_builder_t *builder, i32 reg_idx) {
ovm_instr_t instr = {0};
instr.full_instr = OVMI_REG_GET;
int param_count;
int result_count;
wasm_func_t *func;
+ wasm_val_vec_t param_buffer;
};
#define WASM_TO_OVM(w, o) { \
static void ovm_to_wasm_func_call_binding(void *env, ovm_value_t* params, ovm_value_t *res) {
ovm_wasm_binding *binding = (ovm_wasm_binding *) env;
- wasm_val_vec_t wasm_params;
- wasm_params.data = alloca(sizeof(wasm_val_t) * binding->param_count);
- wasm_params.size = binding->param_count;
-
fori (i, 0, binding->param_count) {
- OVM_TO_WASM(params[i], wasm_params.data[i]);
+ OVM_TO_WASM(params[i], binding->param_buffer.data[i]);
}
wasm_val_t return_value;
wasm_results.data = &return_value;
wasm_results.size = binding->result_count;
- wasm_trap_t *trap = wasm_func_call(binding->func, &wasm_params, &wasm_results);
+ wasm_trap_t *trap = wasm_func_call(binding->func, &binding->param_buffer, &wasm_results);
assert(!trap);
if (binding->result_count > 0) {
binding->param_count = functype->params.size;
binding->result_count = functype->results.size;
binding->func = func;
+ binding->param_buffer.data = bh_alloc(ovm_store->arena_allocator, sizeof(wasm_val_t) * binding->param_count);
+ binding->param_buffer.size = binding->param_count;
ovm_state_register_external_func(ovm_state, importtype->external_func_idx, ovm_to_wasm_func_call_binding, binding);
break;
//
// This uses the pattern of "branch if zero" to skip a section of
// code if the condition was not true.
- ovm_code_builder_add_cond_branch(&ctx->builder, if_target, false);
+ ovm_code_builder_add_cond_branch(&ctx->builder, if_target, false, true);
break;
}
case 0x05: {
- //
- // HACK HACK HACK
- // I'm not entirely happy with the way that this happens, but let me explain.
- // When trying to make an "else", you need to have a unconditional branch that
- // skips the alternate code path. This branch instruction both needs to know
- // the label index of the else statment, and be part of the existing block.
- // Therefore, this "peeks" at what the next label index will be, so it can
- // know what label to track for where to jump. It feels like there should be
- // a better way to do this.
- ovm_code_builder_add_branch(&ctx->builder, ctx->builder.next_label_idx);
- ovm_code_builder_pop_label_target(&ctx->builder);
- ovm_code_builder_push_label_target(&ctx->builder, label_kind_block);
+ label_target_t if_target = ovm_code_builder_wasm_target_idx(&ctx->builder, 0);
+ ovm_code_builder_add_branch(&ctx->builder, if_target.idx);
+ ovm_code_builder_patch_else(&ctx->builder, if_target);
break;
}
int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
label_target_t target = ovm_code_builder_wasm_target_idx(&ctx->builder, label_idx);
- ovm_code_builder_add_cond_branch(&ctx->builder, target.idx, true);
+ ovm_code_builder_add_cond_branch(&ctx->builder, target.idx, true, false);
break;
}
case 0x22: {
int local_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
- ovm_code_builder_add_local_set(&ctx->builder, local_idx);
- ovm_code_builder_add_local_get(&ctx->builder, local_idx);
+ ovm_code_builder_add_local_tee(&ctx->builder, local_idx);
break;
}