mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[compiler] asm-only disasm output + fix spacing bug (#3104)
This commit is contained in:
parent
7c74b0c999
commit
09536c68ac
|
@ -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)
|
(defmacro ml (file)
|
||||||
"Make Load: make and load the file through the listener"
|
"Make Load: make and load the file through the listener"
|
||||||
`(asm-file ,file :color :load :write)
|
`(asm-file ,file :color :load :write)
|
||||||
|
|
|
@ -324,13 +324,14 @@ std::vector<u8> Compiler::codegen_object_file(FileEnv* env) {
|
||||||
|
|
||||||
bool Compiler::codegen_and_disassemble_object_file(FileEnv* env,
|
bool Compiler::codegen_and_disassemble_object_file(FileEnv* env,
|
||||||
std::vector<u8>* data_out,
|
std::vector<u8>* 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());
|
auto debug_info = &m_debugger.get_debug_info_for_object(env->name());
|
||||||
debug_info->clear();
|
debug_info->clear();
|
||||||
CodeGenerator gen(env, debug_info, m_version);
|
CodeGenerator gen(env, debug_info, m_version);
|
||||||
*data_out = gen.run(&m_ts);
|
*data_out = gen.run(&m_ts);
|
||||||
bool ok = true;
|
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;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +483,7 @@ void Compiler::asm_file(const CompilationOptions& options) {
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
std::string disasm;
|
std::string disasm;
|
||||||
if (options.disassemble) {
|
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()) {
|
if (options.disassembly_output_file.empty()) {
|
||||||
printf("%s\n", disasm.c_str());
|
printf("%s\n", disasm.c_str());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct CompilationOptions {
|
||||||
bool write = false; // write object file to out/obj
|
bool write = false; // write object file to out/obj
|
||||||
bool no_code = false; // file shouldn't generate code, throw error if it does
|
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 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
|
bool print_time = false; // print timing statistics
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,7 +211,8 @@ class Compiler {
|
||||||
std::vector<u8> codegen_object_file(FileEnv* env);
|
std::vector<u8> codegen_object_file(FileEnv* env);
|
||||||
bool codegen_and_disassemble_object_file(FileEnv* env,
|
bool codegen_and_disassemble_object_file(FileEnv* env,
|
||||||
std::vector<u8>* data_out,
|
std::vector<u8>* data_out,
|
||||||
std::string* asm_out);
|
std::string* asm_out,
|
||||||
|
bool omit_ir);
|
||||||
|
|
||||||
void for_each_in_list(const goos::Object& list,
|
void for_each_in_list(const goos::Object& list,
|
||||||
const std::function<void(const goos::Object&)>& f);
|
const std::function<void(const goos::Object&)>& f);
|
||||||
|
|
|
@ -161,6 +161,8 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re
|
||||||
} else if (setting == ":disassemble") {
|
} else if (setting == ":disassemble") {
|
||||||
options.disassemble = true;
|
options.disassemble = true;
|
||||||
last_was_disasm = true;
|
last_was_disasm = true;
|
||||||
|
} else if (setting == ":disasm-code-only") {
|
||||||
|
options.disasm_code_only = true;
|
||||||
} else {
|
} else {
|
||||||
throw_compiler_error(form, "The option {} was not recognized for asm-file.", setting);
|
throw_compiler_error(form, "The option {} was not recognized for asm-file.", setting);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,22 @@
|
||||||
DebugInfo::DebugInfo(std::string obj_name) : m_obj_name(std::move(obj_name)) {}
|
DebugInfo::DebugInfo(std::string obj_name) : m_obj_name(std::move(obj_name)) {}
|
||||||
|
|
||||||
std::string FunctionDebugInfo::disassemble_debug_info(bool* had_failure,
|
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);
|
std::string result = fmt::format("[{}]\n", name);
|
||||||
result +=
|
result += disassemble_x86_function(generated_code.data(), generated_code.size(), reader, 0x10000,
|
||||||
disassemble_x86_function(generated_code.data(), generated_code.size(), reader, 0x10000,
|
0x10000, instructions, code_sources, ir_strings, had_failure,
|
||||||
0x10000, instructions, code_sources, ir_strings, had_failure, true);
|
true, omit_ir);
|
||||||
|
|
||||||
return result;
|
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;
|
std::string result;
|
||||||
for (auto& kv : m_functions) {
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +33,7 @@ std::string DebugInfo::disassemble_function_by_name(const std::string& name,
|
||||||
std::string result;
|
std::string result;
|
||||||
for (auto& kv : m_functions) {
|
for (auto& kv : m_functions) {
|
||||||
if (kv.second.name == name) {
|
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;
|
return result;
|
||||||
|
|
|
@ -40,7 +40,7 @@ struct FunctionDebugInfo {
|
||||||
std::vector<u8> generated_code;
|
std::vector<u8> generated_code;
|
||||||
std::optional<int> stack_usage;
|
std::optional<int> 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 {
|
class DebugInfo {
|
||||||
|
@ -74,7 +74,9 @@ class DebugInfo {
|
||||||
|
|
||||||
void clear() { m_functions.clear(); }
|
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,
|
std::string disassemble_function_by_name(const std::string& name,
|
||||||
bool* had_failure,
|
bool* had_failure,
|
||||||
const goos::Reader* reader);
|
const goos::Reader* reader);
|
||||||
|
|
|
@ -428,7 +428,7 @@ Disassembly Debugger::disassemble_at_rip(const InstructionPointerInfo& info) {
|
||||||
function_mem.data(), function_mem.size(), m_reader,
|
function_mem.data(), function_mem.size(), m_reader,
|
||||||
m_debug_context.base + info.map_entry->start_addr + func_info->offset_in_seg,
|
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,
|
rip + rip_offset, func_info->instructions, func_info->code_sources, func_info->ir_strings,
|
||||||
&result.failed, false);
|
&result.failed, false, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.failed = true;
|
result.failed = true;
|
||||||
|
|
|
@ -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
|
// 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_REV = 4;
|
||||||
static constexpr int FORM_DUMP_SIZE_FWD = 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(
|
std::string disassemble_x86_function(
|
||||||
u8* data,
|
u8* data,
|
||||||
|
@ -87,7 +89,8 @@ std::string disassemble_x86_function(
|
||||||
const std::vector<std::shared_ptr<goos::HeapObject>>& code_sources,
|
const std::vector<std::shared_ptr<goos::HeapObject>>& code_sources,
|
||||||
const std::vector<std::string>& ir_strings,
|
const std::vector<std::string>& ir_strings,
|
||||||
bool* had_failure,
|
bool* had_failure,
|
||||||
bool print_whole_function) {
|
bool print_whole_function,
|
||||||
|
bool omit_ir) {
|
||||||
std::string result;
|
std::string result;
|
||||||
ZydisDecoder decoder;
|
ZydisDecoder decoder;
|
||||||
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
|
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);
|
const auto& debug_instr = x86_instructions.at(current_instruction_idx);
|
||||||
if (debug_instr.kind == InstructionInfo::Kind::IR && debug_instr.ir_idx != current_ir_idx) {
|
if (debug_instr.kind == InstructionInfo::Kind::IR && debug_instr.ir_idx != current_ir_idx) {
|
||||||
current_ir_idx = debug_instr.ir_idx;
|
current_ir_idx = debug_instr.ir_idx;
|
||||||
|
@ -154,8 +158,9 @@ std::string disassemble_x86_function(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
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));
|
auto source = reader->db.try_get_short_info(code_sources.at(current_ir_idx));
|
||||||
if (source) {
|
if (source) {
|
||||||
if (source->filename != current_filename ||
|
if (source->filename != current_filename ||
|
||||||
|
@ -171,6 +176,7 @@ std::string disassemble_x86_function(
|
||||||
std::string pointer(current_offset_in_line + 3, ' ');
|
std::string pointer(current_offset_in_line + 3, ' ');
|
||||||
pointer += "^\n";
|
pointer += "^\n";
|
||||||
line += fmt::format(fmt::emphasis::bold | fg(fmt::color::lime_green), "{}", pointer);
|
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;
|
line += print_buff;
|
||||||
|
|
||||||
if (print_ir && current_ir_idx >= 0 && current_ir_idx < int(ir_strings.size())) {
|
if (print_ir && current_ir_idx >= 0 && current_ir_idx < int(ir_strings.size())) {
|
||||||
if (line.size() < 50) {
|
if (line.size() - line_size_offset < DISASM_LINE_LEN) {
|
||||||
line.append(50 - line.size(), ' ');
|
line.append(DISASM_LINE_LEN - (line.size() - line_size_offset), ' ');
|
||||||
}
|
}
|
||||||
line += " ";
|
line += " ";
|
||||||
line += ir_strings.at(current_ir_idx);
|
line += ir_strings.at(current_ir_idx);
|
||||||
|
|
|
@ -42,4 +42,5 @@ std::string disassemble_x86_function(
|
||||||
const std::vector<std::shared_ptr<goos::HeapObject>>& code_sources,
|
const std::vector<std::shared_ptr<goos::HeapObject>>& code_sources,
|
||||||
const std::vector<std::string>& ir_strings,
|
const std::vector<std::string>& ir_strings,
|
||||||
bool* had_failure,
|
bool* had_failure,
|
||||||
bool print_whole_function);
|
bool print_whole_function,
|
||||||
|
bool omit_ir);
|
|
@ -292,7 +292,8 @@ TEST_F(WithGameTests, DebuggerMemoryMap) {
|
||||||
TEST_F(WithGameTests, DebuggerDisassemble) {
|
TEST_F(WithGameTests, DebuggerDisassemble) {
|
||||||
auto di = shared_compiler->compiler.get_debugger().get_debug_info_for_object("gcommon");
|
auto di = shared_compiler->compiler.get_debugger().get_debug_info_for_object("gcommon");
|
||||||
bool fail = false;
|
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());
|
// printf("Got\n%s\n", result.c_str());
|
||||||
EXPECT_FALSE(fail);
|
EXPECT_FALSE(fail);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue