diff --git a/src/alloy/backend/ivm/ivm_assembler.cc b/src/alloy/backend/ivm/ivm_assembler.cc index d5d0c48aa..54ae04393 100644 --- a/src/alloy/backend/ivm/ivm_assembler.cc +++ b/src/alloy/backend/ivm/ivm_assembler.cc @@ -25,6 +25,7 @@ using namespace alloy::runtime; IVMAssembler::IVMAssembler(Backend* backend) : + source_map_arena_(128 * 1024), Assembler(backend) { } @@ -47,6 +48,7 @@ int IVMAssembler::Initialize() { void IVMAssembler::Reset() { intcode_arena_.Reset(); + source_map_arena_.Reset(); scratch_arena_.Reset(); Assembler::Reset(); } @@ -62,6 +64,8 @@ int IVMAssembler::Assemble( ctx.register_count = 0; ctx.intcode_count = 0; ctx.intcode_arena = &intcode_arena_; + ctx.source_map_count = 0; + ctx.source_map_arena = &source_map_arena_; ctx.scratch_arena = &scratch_arena_; ctx.label_ref_head = NULL; diff --git a/src/alloy/backend/ivm/ivm_assembler.h b/src/alloy/backend/ivm/ivm_assembler.h index dcf93f059..052ab49e2 100644 --- a/src/alloy/backend/ivm/ivm_assembler.h +++ b/src/alloy/backend/ivm/ivm_assembler.h @@ -35,6 +35,7 @@ public: private: Arena intcode_arena_; + Arena source_map_arena_; Arena scratch_arena_; }; diff --git a/src/alloy/backend/ivm/ivm_function.cc b/src/alloy/backend/ivm/ivm_function.cc index 03f574312..1aaffbe40 100644 --- a/src/alloy/backend/ivm/ivm_function.cc +++ b/src/alloy/backend/ivm/ivm_function.cc @@ -21,27 +21,86 @@ using namespace alloy::runtime; IVMFunction::IVMFunction(FunctionInfo* symbol_info) : register_count_(0), intcode_count_(0), intcodes_(0), + source_map_count_(0), source_map_(0), GuestFunction(symbol_info) { } IVMFunction::~IVMFunction() { xe_free(intcodes_); + xe_free(source_map_); } void IVMFunction::Setup(TranslationContext& ctx) { register_count_ = ctx.register_count; intcode_count_ = ctx.intcode_count; intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents(); + source_map_count_ = ctx.source_map_count; + source_map_ = (SourceMapEntry*)ctx.source_map_arena->CloneContents(); +} + +IntCode* IVMFunction::GetIntCodeAtSourceOffset(uint64_t offset) { + for (size_t n = 0; n < source_map_count_; n++) { + auto entry = &source_map_[n]; + if (entry->source_offset == offset) { + return &intcodes_[entry->intcode_index]; + } + } + return NULL; } int IVMFunction::AddBreakpointImpl(Breakpoint* breakpoint) { + auto i = GetIntCodeAtSourceOffset(breakpoint->address()); + if (!i) { + return 1; + } + + // TEMP breakpoints always overwrite normal ones. + if (!i->debug_flags || + breakpoint->type() == Breakpoint::TEMP_TYPE) { + uint64_t breakpoint_ptr = (uint64_t)breakpoint; + i->src2_reg = (uint32_t)breakpoint_ptr; + i->src3_reg = (uint32_t)(breakpoint_ptr >> 32); + } + + // Increment breakpoint counter. + ++i->debug_flags; + return 0; } int IVMFunction::RemoveBreakpointImpl(Breakpoint* breakpoint) { + auto i = GetIntCodeAtSourceOffset(breakpoint->address()); + if (!i) { + return 1; + } + + // Decrement breakpoint counter. + --i->debug_flags; + i->src2_reg = i->src3_reg = 0; + + // If there were other breakpoints, see what they were. + if (i->debug_flags) { + auto old_breakpoint = FindBreakpoint(breakpoint->address()); + if (old_breakpoint) { + uint64_t breakpoint_ptr = (uint64_t)breakpoint; + i->src2_reg = (uint32_t)breakpoint_ptr; + i->src3_reg = (uint32_t)(breakpoint_ptr >> 32); + } + } + return 0; } +void IVMFunction::OnBreakpointHit(ThreadState* thread_state, IntCode* i) { + uint64_t breakpoint_ptr = i->src2_reg | (uint64_t(i->src3_reg) << 32); + Breakpoint* breakpoint = (Breakpoint*)breakpoint_ptr; + + // Notify debugger. + // The debugger may choose to wait (blocking us). + auto debugger = thread_state->runtime()->debugger(); + debugger->OnBreakpointHit(thread_state, breakpoint); +} + int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) { // Setup register file on stack. size_t register_file_size = register_count_ * sizeof(Register); @@ -65,7 +124,12 @@ int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) { uint32_t ia = 0; while (true) { - const IntCode* i = &intcodes_[ia]; + IntCode* i = &intcodes_[ia]; + + if (i->debug_flags) { + OnBreakpointHit(thread_state, i); + } + uint32_t new_ia = i->intcode_fn(ics, i); if (new_ia == IA_NEXT) { ia++; diff --git a/src/alloy/backend/ivm/ivm_function.h b/src/alloy/backend/ivm/ivm_function.h index 7e7147bbe..f7bff33a6 100644 --- a/src/alloy/backend/ivm/ivm_function.h +++ b/src/alloy/backend/ivm/ivm_function.h @@ -35,13 +35,16 @@ protected: uint64_t return_address); private: + IntCode* GetIntCodeAtSourceOffset(uint64_t offset); + void OnBreakpointHit(runtime::ThreadState* thread_state, IntCode* i); private: size_t register_count_; Register* constant_regiters_; size_t intcode_count_; IntCode* intcodes_; - // ... source_map_; + size_t source_map_count_; + SourceMapEntry* source_map_; }; diff --git a/src/alloy/backend/ivm/ivm_intcode.cc b/src/alloy/backend/ivm/ivm_intcode.cc index d885fb371..052823867 100644 --- a/src/alloy/backend/ivm/ivm_intcode.cc +++ b/src/alloy/backend/ivm/ivm_intcode.cc @@ -53,6 +53,7 @@ uint32_t AllocConstant(TranslationContext& ctx, uint64_t value, IntCode* ic = ctx.intcode_arena->Alloc(); ic->intcode_fn = IntCode_INT_LOAD_CONSTANT; ic->flags = 0; + ic->debug_flags = 0; ic->dest_reg = ctx.register_count++; ic->constant.u64 = value; if (out_ic) { @@ -66,6 +67,7 @@ uint32_t AllocConstant(TranslationContext& ctx, Value* value) { IntCode* ic = ctx.intcode_arena->Alloc(); ic->intcode_fn = IntCode_INT_LOAD_CONSTANT; ic->flags = 0; + ic->debug_flags = 0; ic->dest_reg = ctx.register_count++; ic->constant.v128 = value->constant.v128; return ic->dest_reg; @@ -155,6 +157,7 @@ int DispatchToC(TranslationContext& ctx, Instr* i, IntCodeFn fn) { IntCode* ic = ctx.intcode_arena->Alloc(); ic->intcode_fn = fn; ic->flags = i->flags; + ic->debug_flags = 0; ic->dest_reg = dest_reg; ic->src1_reg = src1_reg; ic->src2_reg = src2_reg; @@ -210,6 +213,7 @@ int DispatchRegisterRead( IntCode* ic = ctx.intcode_arena->Alloc(); ic->intcode_fn = fn; ic->flags = i->flags; + ic->debug_flags = 0; ic->dest_reg = dest_reg; ic->src1_reg = src1_reg; ic->src2_reg = (uint32_t)((uint64_t)cbs); @@ -312,6 +316,7 @@ int DispatchRegisterWrite( IntCode* ic = ctx.intcode_arena->Alloc(); ic->intcode_fn = fn; ic->flags = i->flags; + ic->debug_flags = 0; ic->dest_reg = (uint32_t)(((uint64_t)cbs) >> 32); ic->src1_reg = src1_reg; ic->src2_reg = src2_reg; @@ -391,6 +396,7 @@ int Translate_COMMENT(TranslationContext& ctx, Instr* i) { IntCode* ic = ctx.intcode_arena->Alloc(); ic->intcode_fn = IntCode_COMMENT; ic->flags = i->flags; + ic->debug_flags = 0; // HACK HACK HACK char* src = xestrdupa((char*)i->src1.offset); uint64_t src_p = (uint64_t)src; @@ -406,6 +412,21 @@ int Translate_NOP(TranslationContext& ctx, Instr* i) { return DispatchToC(ctx, i, IntCode_NOP); } +uint32_t IntCode_SOURCE_OFFSET(IntCodeState& ics, const IntCode* i) { + return IA_NEXT; +} +int Translate_SOURCE_OFFSET(TranslationContext& ctx, Instr* i) { + int result = DispatchToC(ctx, i, IntCode_SOURCE_OFFSET); + if (result) { + return result; + } + auto entry = ctx.source_map_arena->Alloc(); + entry->intcode_index = ctx.intcode_count - 1; + entry->source_offset = i->src1.offset; + ctx.source_map_count++; + return 0; +} + uint32_t IntCode_DEBUG_BREAK(IntCodeState& ics, const IntCode* i) { DFLUSH(); __debugbreak(); @@ -3061,6 +3082,8 @@ static const TranslateFn dispatch_table[] = { Translate_NOP, + Translate_SOURCE_OFFSET, + Translate_DEBUG_BREAK, Translate_DEBUG_BREAK_TRUE, diff --git a/src/alloy/backend/ivm/ivm_intcode.h b/src/alloy/backend/ivm/ivm_intcode.h index 9312ff2ce..d609e94a5 100644 --- a/src/alloy/backend/ivm/ivm_intcode.h +++ b/src/alloy/backend/ivm/ivm_intcode.h @@ -63,6 +63,7 @@ typedef uint32_t (*IntCodeFn)( typedef struct IntCode_s { IntCodeFn intcode_fn; uint16_t flags; + uint16_t debug_flags; uint32_t dest_reg; union { @@ -88,12 +89,20 @@ typedef struct LabelRef_s { } LabelRef; +typedef struct SourceMapEntry_s { + uint64_t source_offset; + uint64_t intcode_index; +} SourceMapEntry; + + typedef struct { runtime::RegisterAccessCallbacks* access_callbacks; uint32_t register_count; size_t intcode_count; Arena* intcode_arena; + size_t source_map_count; + Arena* source_map_arena; Arena* scratch_arena; LabelRef* label_ref_head; } TranslationContext; diff --git a/src/alloy/frontend/ppc/ppc_function_builder.cc b/src/alloy/frontend/ppc/ppc_function_builder.cc index c4ba68514..53a400cf2 100644 --- a/src/alloy/frontend/ppc/ppc_function_builder.cc +++ b/src/alloy/frontend/ppc/ppc_function_builder.cc @@ -108,6 +108,10 @@ int PPCFunctionBuilder::Emit(FunctionInfo* symbol_info) { } } + // Mark source offset for debugging. + // We could omit this if we never wanted to debug. + SourceOffset(i.address); + if (!i.type) { XELOGCPU("Invalid instruction %.8X %.8X", i.address, i.code); Comment("INVALID!"); diff --git a/src/alloy/hir/function_builder.cc b/src/alloy/hir/function_builder.cc index 8c0c3dcce..1bf7deda4 100644 --- a/src/alloy/hir/function_builder.cc +++ b/src/alloy/hir/function_builder.cc @@ -119,6 +119,10 @@ void FunctionBuilder::Dump(StringBuffer* str) { Instr* i = block->instr_head; while (i) { + if (i->opcode->flags & OPCODE_FLAG_HIDE) { + i = i->next; + continue; + } if (i->opcode->num == OPCODE_COMMENT) { str->Append(" ; %s\n", (char*)i->src1.offset); i = i->next; @@ -370,6 +374,12 @@ void FunctionBuilder::Nop() { i->src1.value = i->src2.value = i->src3.value = NULL; } +void FunctionBuilder::SourceOffset(uint64_t offset) { + Instr* i = AppendInstr(OPCODE_SOURCE_OFFSET_info, 0); + i->src1.offset = offset; + i->src2.value = i->src3.value = NULL; +} + void FunctionBuilder::DebugBreak() { Instr* i = AppendInstr(OPCODE_DEBUG_BREAK_info, 0); i->src1.value = i->src2.value = i->src3.value = NULL; diff --git a/src/alloy/hir/function_builder.h b/src/alloy/hir/function_builder.h index e6ac4c84f..9386a9537 100644 --- a/src/alloy/hir/function_builder.h +++ b/src/alloy/hir/function_builder.h @@ -57,6 +57,8 @@ public: void Nop(); + void SourceOffset(uint64_t offset); + // trace info/etc void DebugBreak(); void DebugBreakTrue(Value* cond); diff --git a/src/alloy/hir/opcodes.h b/src/alloy/hir/opcodes.h index 11072aa0b..033b9573e 100644 --- a/src/alloy/hir/opcodes.h +++ b/src/alloy/hir/opcodes.h @@ -69,6 +69,8 @@ enum Opcode { OPCODE_NOP, + OPCODE_SOURCE_OFFSET, + OPCODE_DEBUG_BREAK, OPCODE_DEBUG_BREAK_TRUE, @@ -174,6 +176,7 @@ enum OpcodeFlags { OPCODE_FLAG_COMMUNATIVE = (1 << 3), OPCODE_FLAG_VOLATILE = (1 << 4), OPCODE_FLAG_IGNORE = (1 << 5), + OPCODE_FLAG_HIDE = (1 << 6), }; enum OpcodeSignatureType { @@ -188,6 +191,7 @@ enum OpcodeSignatureType { enum OpcodeSignature { OPCODE_SIG_X = (OPCODE_SIG_TYPE_X), OPCODE_SIG_X_L = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_L << 3), + OPCODE_SIG_X_O = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3), OPCODE_SIG_X_O_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_V << 6), OPCODE_SIG_X_S = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_S << 3), OPCODE_SIG_X_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3), diff --git a/src/alloy/hir/opcodes.inl b/src/alloy/hir/opcodes.inl index 3442dfd1c..cecc62bde 100644 --- a/src/alloy/hir/opcodes.inl +++ b/src/alloy/hir/opcodes.inl @@ -20,6 +20,12 @@ DEFINE_OPCODE( OPCODE_SIG_X, OPCODE_FLAG_IGNORE); +DEFINE_OPCODE( + OPCODE_SOURCE_OFFSET, + "source_offset", + OPCODE_SIG_X_O, + OPCODE_FLAG_IGNORE | OPCODE_FLAG_HIDE); + DEFINE_OPCODE( OPCODE_DEBUG_BREAK, "debug_break", diff --git a/src/alloy/runtime/debugger.cc b/src/alloy/runtime/debugger.cc index bc348eda8..2211daaab 100644 --- a/src/alloy/runtime/debugger.cc +++ b/src/alloy/runtime/debugger.cc @@ -131,3 +131,8 @@ void Debugger::OnFunctionDefined(FunctionInfo* symbol_info, } } } + +void Debugger::OnBreakpointHit( + ThreadState* thread_state, Breakpoint* breakpoint) { + // +} \ No newline at end of file diff --git a/src/alloy/runtime/debugger.h b/src/alloy/runtime/debugger.h index 22497bf97..be2ef3687 100644 --- a/src/alloy/runtime/debugger.h +++ b/src/alloy/runtime/debugger.h @@ -21,6 +21,7 @@ namespace runtime { class Function; class FunctionInfo; class Runtime; +class ThreadState; class Breakpoint { @@ -55,6 +56,7 @@ public: uint64_t address, std::vector& out_breakpoints); void OnFunctionDefined(FunctionInfo* symbol_info, Function* function); + void OnBreakpointHit(ThreadState* thread_state, Breakpoint* breakpoint); private: Runtime* runtime_; diff --git a/src/alloy/runtime/function.cc b/src/alloy/runtime/function.cc index af3e9bf74..0f393ebba 100644 --- a/src/alloy/runtime/function.cc +++ b/src/alloy/runtime/function.cc @@ -9,6 +9,7 @@ #include +#include #include #include @@ -57,6 +58,20 @@ int Function::RemoveBreakpoint(Breakpoint* breakpoint) { return found ? 0 : 1; } +Breakpoint* Function::FindBreakpoint(uint64_t address) { + LockMutex(lock_); + Breakpoint* result = NULL; + for (auto it = breakpoints_.begin(); it != breakpoints_.end(); ++it) { + Breakpoint* breakpoint = *it; + if (breakpoint->address() == address) { + result = breakpoint; + break; + } + } + UnlockMutex(lock_); + return result; +} + int Function::Call(ThreadState* thread_state, uint64_t return_address) { ThreadState* original_thread_state = ThreadState::Get(); if (original_thread_state != thread_state) { diff --git a/src/alloy/runtime/function.h b/src/alloy/runtime/function.h index 8c9d95748..634f93e5f 100644 --- a/src/alloy/runtime/function.h +++ b/src/alloy/runtime/function.h @@ -45,6 +45,7 @@ public: int Call(ThreadState* thread_state, uint64_t return_address); protected: + Breakpoint* FindBreakpoint(uint64_t address); virtual int AddBreakpointImpl(Breakpoint* breakpoint) { return 0; } virtual int RemoveBreakpointImpl(Breakpoint* breakpoint) { return 0; } virtual int CallImpl(ThreadState* thread_state, uint64_t return_address) = 0;