diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index b75beca4e62..50312a71248 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -17,6 +17,14 @@ ) ) +(defmacro mda (file &rest path) + "Make Debug Asm Only: make + print disassembly (asm-only mode) for a file" + (if (null? path) + `(asm-file ,file :color :write :disassemble :disasm-code-only) + `(asm-file ,file :color :write :disassemble :disasm-code-only ,(first path)) + ) + ) + (defmacro ml (file) "Make Load: make and load the file through the listener" `(asm-file ,file :color :load :write) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 4fb01e083bf..f1d96bffaca 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -324,13 +324,14 @@ std::vector Compiler::codegen_object_file(FileEnv* env) { bool Compiler::codegen_and_disassemble_object_file(FileEnv* env, std::vector* data_out, - std::string* asm_out) { + std::string* asm_out, + bool omit_ir) { auto debug_info = &m_debugger.get_debug_info_for_object(env->name()); debug_info->clear(); CodeGenerator gen(env, debug_info, m_version); *data_out = gen.run(&m_ts); bool ok = true; - *asm_out = debug_info->disassemble_all_functions(&ok, &m_goos.reader); + *asm_out = debug_info->disassemble_all_functions(&ok, &m_goos.reader, omit_ir); return ok; } @@ -482,7 +483,7 @@ void Compiler::asm_file(const CompilationOptions& options) { std::vector data; std::string disasm; if (options.disassemble) { - codegen_and_disassemble_object_file(obj_file, &data, &disasm); + codegen_and_disassemble_object_file(obj_file, &data, &disasm, options.disasm_code_only); if (options.disassembly_output_file.empty()) { printf("%s\n", disasm.c_str()); } else { diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index c3ccb329211..da368ba8652 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -34,6 +34,7 @@ struct CompilationOptions { bool write = false; // write object file to out/obj bool no_code = false; // file shouldn't generate code, throw error if it does bool disassemble = false; // either print disassembly to stdout or output_file + bool disasm_code_only = false; // if on, IR and source lines are not printed bool print_time = false; // print timing statistics }; @@ -210,7 +211,8 @@ class Compiler { std::vector codegen_object_file(FileEnv* env); bool codegen_and_disassemble_object_file(FileEnv* env, std::vector* data_out, - std::string* asm_out); + std::string* asm_out, + bool omit_ir); void for_each_in_list(const goos::Object& list, const std::function& f); diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 5fe136b0805..e53cfd4890e 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -161,6 +161,8 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re } else if (setting == ":disassemble") { options.disassemble = true; last_was_disasm = true; + } else if (setting == ":disasm-code-only") { + options.disasm_code_only = true; } else { throw_compiler_error(form, "The option {} was not recognized for asm-file.", setting); } diff --git a/goalc/debugger/DebugInfo.cpp b/goalc/debugger/DebugInfo.cpp index c896e72361d..20fc66e49f0 100644 --- a/goalc/debugger/DebugInfo.cpp +++ b/goalc/debugger/DebugInfo.cpp @@ -7,19 +7,22 @@ DebugInfo::DebugInfo(std::string obj_name) : m_obj_name(std::move(obj_name)) {} std::string FunctionDebugInfo::disassemble_debug_info(bool* had_failure, - const goos::Reader* reader) { + const goos::Reader* reader, + bool omit_ir) { std::string result = fmt::format("[{}]\n", name); - result += - disassemble_x86_function(generated_code.data(), generated_code.size(), reader, 0x10000, - 0x10000, instructions, code_sources, ir_strings, had_failure, true); + result += disassemble_x86_function(generated_code.data(), generated_code.size(), reader, 0x10000, + 0x10000, instructions, code_sources, ir_strings, had_failure, + true, omit_ir); return result; } -std::string DebugInfo::disassemble_all_functions(bool* had_failure, const goos::Reader* reader) { +std::string DebugInfo::disassemble_all_functions(bool* had_failure, + const goos::Reader* reader, + bool omit_ir) { std::string result; for (auto& kv : m_functions) { - result += kv.second.disassemble_debug_info(had_failure, reader) + "\n\n"; + result += kv.second.disassemble_debug_info(had_failure, reader, omit_ir) + "\n\n"; } return result; } @@ -30,7 +33,7 @@ std::string DebugInfo::disassemble_function_by_name(const std::string& name, std::string result; for (auto& kv : m_functions) { if (kv.second.name == name) { - result += kv.second.disassemble_debug_info(had_failure, reader) + "\n\n"; + result += kv.second.disassemble_debug_info(had_failure, reader, false) + "\n\n"; } } return result; diff --git a/goalc/debugger/DebugInfo.h b/goalc/debugger/DebugInfo.h index 86ad0ca7372..dc17255f43b 100644 --- a/goalc/debugger/DebugInfo.h +++ b/goalc/debugger/DebugInfo.h @@ -40,7 +40,7 @@ struct FunctionDebugInfo { std::vector generated_code; std::optional stack_usage; - std::string disassemble_debug_info(bool* had_failure, const goos::Reader* reader); + std::string disassemble_debug_info(bool* had_failure, const goos::Reader* reader, bool omit_ir); }; class DebugInfo { @@ -74,7 +74,9 @@ class DebugInfo { void clear() { m_functions.clear(); } - std::string disassemble_all_functions(bool* had_failure, const goos::Reader* reader); + std::string disassemble_all_functions(bool* had_failure, + const goos::Reader* reader, + bool omit_ir); std::string disassemble_function_by_name(const std::string& name, bool* had_failure, const goos::Reader* reader); diff --git a/goalc/debugger/Debugger.cpp b/goalc/debugger/Debugger.cpp index 3018dee97bd..364a07fc219 100644 --- a/goalc/debugger/Debugger.cpp +++ b/goalc/debugger/Debugger.cpp @@ -428,7 +428,7 @@ Disassembly Debugger::disassemble_at_rip(const InstructionPointerInfo& info) { function_mem.data(), function_mem.size(), m_reader, m_debug_context.base + info.map_entry->start_addr + func_info->offset_in_seg, rip + rip_offset, func_info->instructions, func_info->code_sources, func_info->ir_strings, - &result.failed, false); + &result.failed, false, false); } } else { result.failed = true; diff --git a/goalc/debugger/disassemble.cpp b/goalc/debugger/disassemble.cpp index 9a644d55f3c..38e56886779 100644 --- a/goalc/debugger/disassemble.cpp +++ b/goalc/debugger/disassemble.cpp @@ -76,6 +76,8 @@ std::string disassemble_x86(u8* data, int len, u64 base_addr, u64 highlight_addr // how many "forms" to look at ahead of / behind rip when stopping static constexpr int FORM_DUMP_SIZE_REV = 4; static constexpr int FORM_DUMP_SIZE_FWD = 4; +// how long the bytecode part of the disassembly is, IR comes after this +static constexpr int DISASM_LINE_LEN = 60; std::string disassemble_x86_function( u8* data, @@ -87,7 +89,8 @@ std::string disassemble_x86_function( const std::vector>& code_sources, const std::vector& ir_strings, bool* had_failure, - bool print_whole_function) { + bool print_whole_function, + bool omit_ir) { std::string result; ZydisDecoder decoder; ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); @@ -145,7 +148,8 @@ std::string disassemble_x86_function( } } - if (current_instruction_idx >= 0 && current_instruction_idx < int(x86_instructions.size())) { + if (!omit_ir && current_instruction_idx >= 0 && + current_instruction_idx < int(x86_instructions.size())) { const auto& debug_instr = x86_instructions.at(current_instruction_idx); if (debug_instr.kind == InstructionInfo::Kind::IR && debug_instr.ir_idx != current_ir_idx) { current_ir_idx = debug_instr.ir_idx; @@ -154,8 +158,9 @@ std::string disassemble_x86_function( } std::string line; + size_t line_size_offset = 0; - if (current_ir_idx >= 0 && current_ir_idx < int(ir_strings.size())) { + if (!omit_ir && current_ir_idx >= 0 && current_ir_idx < int(ir_strings.size())) { auto source = reader->db.try_get_short_info(code_sources.at(current_ir_idx)); if (source) { if (source->filename != current_filename || @@ -171,6 +176,7 @@ std::string disassemble_x86_function( std::string pointer(current_offset_in_line + 3, ' '); pointer += "^\n"; line += fmt::format(fmt::emphasis::bold | fg(fmt::color::lime_green), "{}", pointer); + line_size_offset = line.size(); } } } @@ -188,8 +194,8 @@ std::string disassemble_x86_function( line += print_buff; if (print_ir && current_ir_idx >= 0 && current_ir_idx < int(ir_strings.size())) { - if (line.size() < 50) { - line.append(50 - line.size(), ' '); + if (line.size() - line_size_offset < DISASM_LINE_LEN) { + line.append(DISASM_LINE_LEN - (line.size() - line_size_offset), ' '); } line += " "; line += ir_strings.at(current_ir_idx); diff --git a/goalc/debugger/disassemble.h b/goalc/debugger/disassemble.h index d95d6ef121f..c3679d56f04 100644 --- a/goalc/debugger/disassemble.h +++ b/goalc/debugger/disassemble.h @@ -42,4 +42,5 @@ std::string disassemble_x86_function( const std::vector>& code_sources, const std::vector& ir_strings, bool* had_failure, - bool print_whole_function); \ No newline at end of file + bool print_whole_function, + bool omit_ir); \ No newline at end of file diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index 8611800d7b8..4defa8f34e9 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -292,7 +292,8 @@ TEST_F(WithGameTests, DebuggerMemoryMap) { TEST_F(WithGameTests, DebuggerDisassemble) { auto di = shared_compiler->compiler.get_debugger().get_debug_info_for_object("gcommon"); bool fail = false; - auto result = di.disassemble_all_functions(&fail, &shared_compiler->compiler.get_goos().reader); + auto result = + di.disassemble_all_functions(&fail, &shared_compiler->compiler.get_goos().reader, false); // printf("Got\n%s\n", result.c_str()); EXPECT_FALSE(fail); }