#define PUSH_VALUE(b, r) (bh_arr_push((b)->execution_stack, r))
#define POP_VALUE(b) (bh_arr_length((b)->execution_stack) == 0 ? (assert(("invalid value pop", 0)), 0) : bh_arr_pop((b)->execution_stack))
-#define NEXT_VALUE(b) ((bh_arr_length((b)->execution_stack) == 0 ? (b)->param_count + (b)->local_count : bh_arr_last((b)->execution_stack)) + 1)
+#define NEXT_VALUE(b) (bh_arr_length((b)->execution_stack) == 0 ? \
+ (b)->param_count + (b)->local_count : \
+ ((b)->highest_value_number = bh_max((b)->highest_value_number, bh_arr_last((b)->execution_stack)), \
+ bh_arr_last((b)->execution_stack) + 1))
// #define POP_VALUE(b) bh_arr_pop((b)->execution_stack)
bh_arr_new(bh_heap_allocator(), builder.label_stack, 32);
bh_arr_new(bh_heap_allocator(), builder.branch_patches, 32);
+ builder.highest_value_number = param_count + local_count;
+
return builder;
}
PUSH_VALUE(builder, unop.r);
}
-void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 instr_delta) {
+void ovm_code_builder_add_branch(ovm_code_builder_t *builder, i32 label_idx) {
+ ovm_instr_t branch_instr = {0};
+ branch_instr.full_instr = OVMI_BR;
+ branch_instr.a = -1;
+
+ branch_patch_t patch;
+ patch.branch_instr = bh_arr_length(builder->program->code);
+ patch.label_idx = label_idx;
+
+ 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) {
+ ovm_instr_t branch_instr = {0};
+ branch_instr.full_instr = OVMI_BR_Z;
+ branch_instr.a = -1;
+ branch_instr.b = POP_VALUE(builder);
+ branch_patch_t patch;
+ patch.branch_instr = bh_arr_length(builder->program->code);
+ patch.label_idx = label_idx;
+
+ bh_arr_push(builder->branch_patches, patch);
+
+ ovm_program_add_instructions(builder->program, 1, &branch_instr);
}
void ovm_code_builder_add_return(ovm_code_builder_t *builder) {
}
void ovm_code_builder_add_indirect_call(ovm_code_builder_t *builder, i32 param_count, bool has_return_value) {
- ovm_instr_t call_instr = {0};
- call_instr.full_instr = OVMI_CALLI;
- call_instr.a = POP_VALUE(builder);
- call_instr.r = -1;
+ ovm_instr_t call_instrs[2] = {0};
+
+ // idxarr %k, table, %j
+ call_instrs[0].full_instr = OVMI_IDX_ARR;
+ call_instrs[0].r = NEXT_VALUE(builder);
+ call_instrs[0].a = builder->func_table_arr_idx;
+ call_instrs[0].b = POP_VALUE(builder);
+
+ call_instrs[1].full_instr = OVMI_CALLI;
+ call_instrs[1].a = call_instrs[0].r;
+ call_instrs[1].r = -1;
ovm_code_builder_add_params(builder, param_count);
if (has_return_value) {
- call_instr.r = NEXT_VALUE(builder);
+ call_instrs[1].r = NEXT_VALUE(builder);
}
- ovm_program_add_instructions(builder->program, 1, &call_instr);
+ ovm_program_add_instructions(builder->program, 2, call_instrs);
if (has_return_value) {
- PUSH_VALUE(builder, call_instr.r);
+ PUSH_VALUE(builder, call_instrs[1].r);
}
}
// This file is not to be compile like normal.
// It is instead included in wasm/module.c
//
+// Currently, this file has a lot of code that directly manipulates
+// the code builder object. I would like to move this into the API
+// for the code builder itself, to make it more portable and easy
+// to read.
typedef struct build_context build_context;
struct build_context {
wasm_importtype_t *import = wasm_importtype_new(&module_name, &import_name, import_type);
ctx->module->imports.data[i] = import;
+
+ if (import_type->kind == WASM_EXTERN_FUNC) {
+ char *external_func_name = bh_aprintf(ctx->program->store->arena_allocator, "%b.%b",
+ module_name.data, module_name.size,
+ import_name.data, import_name.size);
+
+ int external_func_idx = ctx->builder.next_external_func_idx++;
+
+ ovm_program_register_external_func(ctx->program, external_func_name, import_type->func.params.size, external_func_idx);
+ }
}
}
// Instruction building
//
-static void push_label_target(build_context *ctx, label_kind_t kind) {
+static int push_label_target(build_context *ctx, label_kind_t kind) {
label_target_t target;
target.kind = kind;
target.idx = ctx->builder.next_label_idx++;
}
bh_arr_push(ctx->builder.label_stack, target);
+
+ return target.idx;
}
static void pop_label_target(build_context *ctx) {
case 0x04: {
// Currently, only "void" block types are valid.
assert(CONSUME_BYTE(ctx) == 0x40);
- push_label_target(ctx, label_kind_block);
- // TODO: Ifs
+ int if_target = push_label_target(ctx, label_kind_block);
+
+ //
+ // 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);
break;
}
case 0x05: {
- // TODO: If-elses
+ //
+ // 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);
+ pop_label_target(ctx);
+ push_label_target(ctx, label_kind_block);
break;
}
case 0x0C: {
int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
- ovm_code_builder_add_branch(&ctx->builder, label_idx);
+
+ label_target_t target = *((&bh_arr_last(ctx->builder.label_stack)) - label_idx);
+ ovm_code_builder_add_branch(&ctx->builder, target.idx);
break;
}
case 0x0D: {
int label_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
- ovm_code_builder_add_cond_branch(&ctx->builder, label_idx);
+
+ label_target_t target = *((&bh_arr_last(ctx->builder.label_stack)) - label_idx);
+ ovm_code_builder_add_cond_branch(&ctx->builder, target.idx);
break;
}
case 0x10: {
int func_idx = uleb128_to_uint(ctx->binary.data, &ctx->offset);
- int param_count = ctx->program->funcs[func_idx].param_count;
- wasm_functype_t *functype = ctx->module->functypes.data[func_idx];
+ wasm_functype_t *functype = wasm_module_index_functype(ctx->module, func_idx);
+ int param_count = functype->type.func.params.size;
ovm_code_builder_add_call(&ctx->builder, func_idx, param_count, functype->type.func.results.size != 0);
break;
wasm_functype_t *functype = ctx->module->type_section.data[type_idx];
int param_count = functype->type.func.params.size;
- printf("indirect call with %d params.\n", param_count);
ovm_code_builder_add_indirect_call(&ctx->builder, param_count, functype->type.func.results.size != 0);
break;
}
total_locals += local_count;
}
-
// Set up a lot of stuff...
- ctx->builder = ovm_code_builder_new(ctx->program, 0, total_locals);
- push_label_target(ctx, label_kind_func);
- parse_expression(ctx);
+ i32 param_count = wasm_module_index_functype(ctx->module, i)->type.func.params.size;
+ ctx->builder = ovm_code_builder_new(ctx->program, param_count, total_locals);
+ ctx->builder.func_table_arr_idx = 0; // This might not be right
+
+ push_label_target(ctx, label_kind_func);
+ parse_expression(ctx);
ovm_code_builder_add_return(&ctx->builder);
+
+ char *func_name = bh_aprintf(bh_heap_allocator(), "wasm_loaded_%d", bh_arr_length(ctx->program->funcs));
+ ovm_program_register_func(ctx->program, func_name, ctx->builder.start_instr, ctx->builder.param_count, ctx->builder.highest_value_number);
ovm_code_builder_free(&ctx->builder);
}
}