--- /dev/null
+#ifndef _OVM_DEBUG_H
+#define _OVM_DEBUG_H
+
+#include "bh.h"
+
+typedef struct debug_loc_info_t {
+ u32 file_id;
+ u32 line;
+ u32 symbols;
+} debug_loc_info_t;
+
+typedef struct debug_func_info_t {
+ u32 func_id;
+ u32 file_id;
+ u32 line;
+ char *name;
+ b32 internal;
+ u32 stack_ptr_idx;
+
+ u32 debug_op_offset;
+} debug_func_info_t;
+
+typedef struct debug_info_t {
+ bh_allocator alloc;
+
+ bh_arr(debug_func_info_t) funcs;
+ bh_arr(debug_loc_info_t) line_info;
+ bh_arr(u32) instruction_reducer;
+
+ bh_arr(char *) file_names;
+} debug_info_t;
+
+void debug_info_init(debug_info_t *);
+void debug_info_free(debug_info_t *);
+void debug_info_import_file_info(debug_info_t *, u8 *data, u32 len);
+void debug_info_import_func_info(debug_info_t *, u8 *data, u32 len);
+
+//
+// This builder is used in conjunction with code builder to output
+// debug information for each instruction that is generated in OVM.
+//
+typedef struct debug_info_builder_t {
+ debug_info_t *info;
+
+ u8 *data;
+ u32 reader_offset;
+
+ u32 current_file_id;
+ u32 current_line;
+
+ bh_arr(char) symbol_scope_stack;
+
+ u32 remaining_reps;
+
+ b32 locked : 1;
+} debug_info_builder_t;
+
+void debug_info_builder_init(debug_info_builder_t *, debug_info_t *);
+void debug_info_builder_prepare(debug_info_builder_t *, u8 *);
+void debug_info_builder_emit_location(debug_info_builder_t *);
+void debug_info_builder_step(debug_info_builder_t *);
+void debug_info_builder_begin_func(debug_info_builder_t *, i32 func_idx);
+void debug_info_builder_end_func(debug_info_builder_t *);
+
+#endif
\ No newline at end of file
#include "wasm.h"
#include "vm.h"
+#include "ovm_debug.h"
// Core Utils
bool passive;
};
+struct wasm_custom_section_t {
+ unsigned int size;
+ char *data;
+};
+
struct wasm_module_t {
wasm_store_t *store;
int memory_init_idx;
int memory_init_external_idx;
+
+ Table(struct wasm_custom_section_t) custom_sections;
+
+ debug_info_t debug_info;
};
struct wasm_func_inner_t {
#define _OVM_CODE_BUILDER_H
#include "vm.h"
+#include "ovm_debug.h"
typedef struct ovm_code_builder_t ovm_code_builder_t;
typedef struct label_target_t label_target_t;
i32 func_table_arr_idx;
i32 highest_value_number;
+
+ debug_info_builder_t *debug_builder;
};
enum label_kind_t {
bool targets_else;
};
-ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, i32 param_count, i32 local_count);
+ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, debug_info_builder_t *debug, 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);
--- /dev/null
+
+#include "ovm_debug.h"
+
+void debug_info_init(debug_info_t *info) {
+ memset(info, 0, sizeof(*info));
+
+ info->alloc = bh_heap_allocator();
+ bh_arr_new(info->alloc, info->funcs, 16);
+ bh_arr_new(info->alloc, info->line_info, 1024);
+ bh_arr_new(info->alloc, info->instruction_reducer, 4096);
+ bh_arr_new(info->alloc, info->file_names, 16);
+}
+
+void debug_info_free(debug_info_t *info) {
+ bh_arr_free(info->funcs);
+ bh_arr_free(info->line_info);
+ bh_arr_free(info->instruction_reducer);
+
+ bh_arr_each(char *, name, info->file_names) bh_free(info->alloc, *name);
+ bh_arr_free(info->file_names);
+}
+
+void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) {
+ u32 offset = 0;
+
+ i32 count = uleb128_to_uint(data, &offset);
+ fori (i, 0, (i32) count) {
+ u32 file_id = uleb128_to_uint(data, &offset);
+ u32 name_length = uleb128_to_uint(data, &offset);
+ char *name = bh_alloc_array(info->alloc, char, name_length + 1);
+ memcpy(name, data + offset, name_length);
+ name[name_length] = 0;
+ offset += name_length;
+
+ bh_arr_set_at(info->file_names, file_id, name);
+ }
+
+ assert(offset == len);
+}
+
+void debug_info_import_func_info(debug_info_t *info, u8 *data, u32 len) {
+ u32 offset = 0;
+
+ i32 count = uleb128_to_uint(data, &offset);
+ fori (i, 0, (i32) count) {
+ debug_func_info_t func_info;
+ func_info.func_id = uleb128_to_uint(data, &offset);
+ func_info.file_id = uleb128_to_uint(data, &offset);
+ func_info.line = uleb128_to_uint(data, &offset);
+
+ u32 name_length = uleb128_to_uint(data, &offset);
+ if (name_length == 0) {
+ func_info.name = NULL;
+ } else {
+ func_info.name = bh_alloc_array(info->alloc, char, name_length + 1);
+ memcpy(func_info.name, data + offset, name_length);
+ func_info.name[name_length] = 0;
+ offset += name_length;
+ }
+
+ func_info.internal = data[offset++] != 0;
+ func_info.debug_op_offset = uleb128_to_uint(data, &offset);
+ func_info.stack_ptr_idx = uleb128_to_uint(data, &offset);
+
+ uleb128_to_uint(data, &offset);
+
+ bh_arr_set_at(info->funcs, func_info.func_id, func_info);
+ }
+
+ assert(offset == len);
+}
--- /dev/null
+
+
+#include "ovm_debug.h"
+
+
+void debug_info_builder_init(debug_info_builder_t *builder, debug_info_t *info) {
+ memset(builder, 0, sizeof(*builder));
+
+ builder->info = info;
+ bh_arr_new(bh_heap_allocator(), builder->symbol_scope_stack, 8);
+}
+
+void debug_info_builder_prepare(debug_info_builder_t *builder, u8 *data) {
+ builder->data = data;
+ builder->reader_offset = 0;
+ builder->current_file_id = 0;
+ builder->current_line = 0;
+ builder->remaining_reps = 0;
+}
+
+static void debug_info_builder_parse(debug_info_builder_t *builder) {
+ u32 count = 0;
+
+ while (1) {
+ u8 instr = builder->data[builder->reader_offset++];
+ switch (instr & 0b11000000) {
+ case 0b00000000:
+ instr &= 0b00111111;
+ switch (instr) {
+ case 0: builder->locked = 1; break;
+ case 1:
+ builder->current_file_id = uleb128_to_uint(builder->data, &builder->reader_offset);
+ builder->current_line = uleb128_to_uint(builder->data, &builder->reader_offset);
+ break;
+ case 2: break;
+ case 3: break;
+ case 4:
+ uleb128_to_uint(builder->data, &builder->reader_offset);
+ break;
+ }
+ break;
+
+ case 0b01000000:
+ count = instr & 0x3f;
+ builder->current_line += count + 1;
+ builder->remaining_reps = 1;
+ return;
+
+ case 0b10000000:
+ count = instr & 0x3f;
+ builder->current_line -= count + 1;
+ builder->remaining_reps = 1;
+ return;
+
+ case 0b11000000:
+ count = instr & 0x3f;
+ builder->remaining_reps = count + 1;
+ return;
+ }
+ }
+}
+
+void debug_info_builder_step(debug_info_builder_t *builder) {
+ while (builder->remaining_reps == 0) {
+ debug_info_builder_parse(builder);
+
+ debug_loc_info_t info;
+ info.file_id = builder->current_file_id;
+ info.line = builder->current_line;
+ info.symbols = 0;
+ bh_arr_push(builder->info->line_info, info);
+ }
+
+ if (builder->locked) return;
+
+ builder->remaining_reps -= 1;
+ return;
+}
+
+void debug_info_builder_emit_location(debug_info_builder_t *builder) {
+ bh_arr_push(builder->info->instruction_reducer, bh_arr_length(builder->info->line_info) - 1);
+}
+
+void debug_info_builder_begin_func(debug_info_builder_t *builder, i32 func_idx) {
+ assert(func_idx < bh_arr_length(builder->info->funcs));
+ debug_func_info_t *func_info = &builder->info->funcs[func_idx];
+
+ builder->reader_offset = func_info->debug_op_offset;
+ assert(builder->reader_offset < 20000);
+ builder->locked = 0;
+}
+
+void debug_info_builder_end_func(debug_info_builder_t *builder) {
+}
#include "vm_codebuilder.h"
+#include "ovm_debug.h"
// #define BUILDER_DEBUG
#endif
}
-ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, i32 param_count, i32 local_count) {
+ovm_code_builder_t ovm_code_builder_new(ovm_program_t *program, debug_info_builder_t *debug, i32 param_count, i32 local_count) {
ovm_code_builder_t builder;
builder.param_count = param_count;
builder.local_count = local_count;
builder.highest_value_number = param_count + local_count;
+ builder.debug_builder = debug;
+
return builder;
}
ovm_instr_t nop = {0};
nop.full_instr = OVMI_NOP;
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &nop);
}
binop.a = left;
binop.b = right;
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &binop);
PUSH_VALUE(builder, result);
}
default: assert(("bad ovm type for add_imm", 0));
}
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &imm_instr);
PUSH_VALUE(builder, imm_instr.r);
}
unop.r = NEXT_VALUE(builder);
unop.a = operand;
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &unop);
PUSH_VALUE(builder, unop.r);
}
bh_arr_push(builder->branch_patches, patch);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &branch_instr);
}
bh_arr_push(builder->branch_patches, patch);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &branch_instr);
}
default_patch.targets_else = false;
bh_arr_push(builder->branch_patches, default_patch);
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 5, instrs);
}
instr.a = POP_VALUE(builder);
}
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
}
param_instr.full_instr = OVMI_PARAM;
param_instr.a = flipped_params[param_count - 1 - i];
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, ¶m_instr);
}
}
call_instr.r = NEXT_VALUE(builder);
}
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &call_instr);
if (has_return_value) {
call_instrs[1].r = NEXT_VALUE(builder);
}
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 2, call_instrs);
if (has_return_value) {
// it was spec'd; but in the future for other things,
// this will be incorrect.
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
PUSH_VALUE(builder, instr.r);
// this will be incorrect.
instr.a = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
}
// this will be incorrect.
instr.a = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
PUSH_VALUE(builder, instr.a);
instr.r = NEXT_VALUE(builder);
instr.a = reg_idx;
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
PUSH_VALUE(builder, instr.r);
instr.r = reg_idx;
instr.a = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
}
load_instr.a = POP_VALUE(builder);
load_instr.r = NEXT_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &load_instr);
PUSH_VALUE(builder, load_instr.r);
store_instr.a = POP_VALUE(builder);
store_instr.r = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &store_instr);
return;
}
cmpxchg_instr.a = POP_VALUE(builder);
cmpxchg_instr.r = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &cmpxchg_instr);
PUSH_VALUE(builder, cmpxchg_instr.r);
instrs[2].a = expected_reg;
instrs[2].b = value_reg;
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 3, instrs);
PUSH_VALUE(builder, instrs[2].r);
instr.a = POP_VALUE(builder);
instr.r = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
}
instr.a = POP_VALUE(builder);
instr.r = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &instr);
}
load_instr.a = POP_VALUE(builder);
load_instr.r = NEXT_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &load_instr);
PUSH_VALUE(builder, load_instr.r);
store_instr.a = POP_VALUE(builder);
store_instr.r = POP_VALUE(builder);
+ debug_info_builder_emit_location(builder->debug_builder);
ovm_program_add_instructions(builder->program, 1, &store_instr);
}
#include "ovm_wasm.h"
#include "vm_codebuilder.h"
+#include "stb_ds.h"
#include "./module_parsing.h"
ctx.store = engine->store;
ctx.next_external_func_idx = 0;
+ debug_info_builder_init(&ctx.debug_builder, &module->debug_info);
+ sh_new_arena(module->custom_sections);
+
while (ctx.offset < binary->size) {
parse_section(&ctx);
}
memset(module, 0, sizeof(*module));
module->store = store;
+ debug_info_init(&module->debug_info);
+
bool success = module_build(module, binary);
return module;
}
// This will be set/reset for every code (function) entry.
ovm_code_builder_t builder;
+ debug_info_builder_t debug_builder;
};
#define PEEK_BYTE(ctx) ((ctx)->binary.data[(ctx)->offset])
static void parse_custom_section(build_context *ctx) {
unsigned int section_size = uleb128_to_uint(ctx->binary.data, &ctx->offset);
- ctx->offset += section_size;
+ unsigned int end_of_section = ctx->offset + section_size;
+
+ struct wasm_custom_section_t cs;
+
+ char name[256];
+ unsigned int name_len = uleb128_to_uint(ctx->binary.data, &ctx->offset);
+ if (name_len < sizeof(name) - 1) {
+ strncpy(name, &((char *) ctx->binary.data)[ctx->offset], name_len);
+ name[name_len] = '\0';
+
+ ctx->offset += name_len;
+ cs.size = end_of_section - ctx->offset;
+
+ unsigned int data_size = end_of_section - ctx->offset;
+ cs.data = bh_alloc_array(ctx->store->heap_allocator, char, data_size);
+ memcpy(cs.data, &((char *) ctx->binary.data)[ctx->offset], data_size);
+
+ shput(ctx->module->custom_sections, name, cs);
+
+ if (!strcmp(name, "ovm_debug_files")) {
+ debug_info_import_file_info(ctx->debug_builder.info, cs.data, cs.size);
+ }
+
+ if (!strcmp(name, "ovm_debug_funcs")) {
+ debug_info_import_func_info(ctx->debug_builder.info, cs.data, cs.size);
+ }
+
+ if (!strcmp(name, "ovm_debug_ops")) {
+ debug_info_builder_prepare(&ctx->debug_builder, cs.data);
+ }
+ }
+
+ ctx->offset = end_of_section;
}
static void parse_type_section(build_context *ctx) {
default: assert(("UNHANDLED INSTRUCTION", 0));
}
+
+ debug_info_builder_step(&ctx->debug_builder);
}
static void parse_expression(build_context *ctx) {
// Set up a lot of stuff...
+ i32 func_idx = bh_arr_length(ctx->program->funcs);
i32 param_count = ctx->module->functypes.data[i]->type.func.params.size;
- ctx->builder = ovm_code_builder_new(ctx->program, param_count, total_locals);
+ debug_info_builder_begin_func(&ctx->debug_builder, func_idx);
+
+ ctx->builder = ovm_code_builder_new(ctx->program, &ctx->debug_builder, param_count, total_locals);
ctx->builder.func_table_arr_idx = ctx->func_table_arr_idx;
ovm_code_builder_push_label_target(&ctx->builder, 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));
+ char *func_name = bh_aprintf(bh_heap_allocator(), "wasm_loaded_%d", func_idx);
ovm_program_register_func(ctx->program, func_name, ctx->builder.start_instr, ctx->builder.param_count, ctx->builder.highest_value_number + 1);
+
ovm_code_builder_free(&ctx->builder);
+ debug_info_builder_end_func(&ctx->debug_builder);
}
ovm_program_register_external_func(ctx->program, "__internal_wasm_memory_init", 4, ctx->module->memory_init_external_idx);