typedef enum OnyxIntrinsic {
ONYX_INTRINSIC_UNDEFINED,
+ ONYX_INTRINSIC_UNREACHABLE,
+
ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
IntrinsicTable intrinsic_table;
static IntrinsicMap builtin_intrinsics[] = {
+ { "unreachable", ONYX_INTRINSIC_UNREACHABLE },
+
{ "memory_size", ONYX_INTRINSIC_MEMORY_SIZE },
{ "memory_grow", ONYX_INTRINSIC_MEMORY_GROW },
{ "memory_copy", ONYX_INTRINSIC_MEMORY_COPY },
}
switch (call->intrinsic) {
+ case ONYX_INTRINSIC_UNREACHABLE: WI(call->token, WI_UNREACHABLE); break;
+
case ONYX_INTRINSIC_MEMORY_SIZE: WID(call->token, WI_MEMORY_SIZE, 0x00); break;
case ONYX_INTRINSIC_MEMORY_GROW: WID(call->token, WI_MEMORY_GROW, 0x00); break;
case ONYX_INTRINSIC_MEMORY_COPY:
i32 leb_len;
u8* leb;
- if (instr->type == WI_UNREACHABLE) assert(("EMITTING UNREACHABLE!!", 0));
-
if (instr->type == WI_NOP && !context.options->debug_enabled) return;
if (instr->type & SIMD_INSTR_MASK) {
package core.intrinsics.wasm
+// This does not return.
+unreachable :: () -> void #intrinsic ---
+
memory_size :: () -> u32 #intrinsic ---
memory_grow :: (val: u32) -> i32 #intrinsic ---
memory_copy :: (dst: rawptr, src: rawptr, count: i32) -> void #intrinsic ---
--- /dev/null
+package core.misc
+
+use runtime.info {
+ get_type_info,
+ Type_Info_Pointer,
+ Type_Info_Struct,
+
+ get_struct_member
+}
+
+to_any :: macro (x: ^$T) => any.{x, T};
+
+any_deference :: (v: any) -> any {
+ t := get_type_info(v.type);
+ if t.kind == .Pointer {
+ p := cast(^Type_Info_Pointer) t;
+ return any.{*cast(^rawptr) v.data, p.to};
+ }
+
+ return .{null, void};
+}
+
+any_get_member :: (v: any, member_name: str) -> any {
+ t := get_type_info(v.type);
+ if t.kind == .Struct {
+ member := get_struct_member(v.type, member_name);
+ if member != null {
+ return any.{cast(^u8) v.data + member.offset, member.type};
+ }
+ }
+
+ return .{null, void};
+}
+
+any_to_map :: (v: any) -> (Map(str, any), success: bool) {
+ vals := v;
+ if get_type_info(vals.type).kind == .Pointer {
+ vals = any_deference(vals);
+ }
+
+ val_info := cast(^Type_Info_Struct) get_type_info(vals.type);
+ if val_info.kind != .Struct {
+ return .{}, false;
+ }
+
+ out: Map(str, any);
+ for ^ val_info.members {
+ out->put(it.name, any_get_member(vals, it.name));
+ }
+
+ return out, true;
+}
+
__output_string("\n");
- // __exit(1);
- *(cast(^i32) 0xffffffff);
+ core.intrinsics.wasm.unreachable();
+
+ // This will never return
+ return;
}
// This procedure should only be called once at the very begining
#load "./test/testing"
+#load "./misc/any_utils"
+
#local runtime :: package runtime
#if runtime.runtime == .Wasi || runtime.runtime == .Onyx {
#load "./os/file"
typedef enum debug_pause_reason_t {
debug_pause_entry = 1,
debug_pause_step = 2,
+ debug_pause_exception = 3,
} debug_pause_reason_t;
typedef struct debug_breakpoint_t {
#define OVMI_CMPXCHG 0x70 // %r = %r == %a ? %b : %r
+#define OVMI_BREAK 0xff
+
//
// OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32) == instruction for adding i32s
//
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_break(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);
ovm_program_add_instructions(builder->program, 1, &nop);
}
+void ovm_code_builder_add_break(ovm_code_builder_t *builder) {
+ ovm_instr_t break_ = {0};
+ break_.full_instr = OVMI_BREAK;
+ debug_info_builder_emit_location(builder->debug_builder);
+ ovm_program_add_instructions(builder->program, 1, &break_);
+}
+
void ovm_code_builder_add_binop(ovm_code_builder_t *builder, u32 instr) {
i32 right = POP_VALUE(builder);
i32 left = POP_VALUE(builder);
#undef CMPXCHG
+ case OVMI_BREAK:
+ printf("onyx: exiting early due to reaching an unreachable instruction.\n");
+
+ if (state->debug) {
+ state->debug->state = debug_state_pausing;
+ state->debug->pause_reason = debug_pause_exception;
+
+ assert(write(state->debug->state_change_write_fd, "1", 1));
+ sem_wait(&state->debug->wait_semaphore);
+ }
+
+ return ((ovm_value_t) {0});
+
default:
printf("ERROR:\n");
ovm_program_print_instructions(program, state->pc - 1, 1);
#define OVM_TO_WASM(o, w) { \
(w).of.i64 = 0;\
switch ((o).type) { \
+ case OVM_TYPE_NONE: \
+ (w).kind = WASM_I32; \
+ (w).of.i32 = 0; \
+ break; \
+ \
case OVM_TYPE_I8: \
(w).kind = WASM_I32; \
(w).of.i32 = (i32) (o).i8; \
unsigned char instr_byte = CONSUME_BYTE(ctx);
switch (instr_byte) {
case 0x00:
+ ovm_code_builder_add_break(&ctx->builder);
break;
case 0x01:
case 2:
reason = "step";
break;
+ case 3:
+ reason = "exception";
+ break;
}
this.sendEvent("paused", { reason, threadId: thread_id });
break;
switch (reason_id) {
case 1: reason = "entry"; break;
case 2: reason = "step"; break;
+ case 3: reason = "exception"; break;
}
this.sendEvent("paused", { reason, threadId: thread_id });