diff --git a/goalc/emitter/CodeTester.cpp b/goalc/emitter/CodeTester.cpp index 6ab824e4e6..da5bfab9ae 100644 --- a/goalc/emitter/CodeTester.cpp +++ b/goalc/emitter/CodeTester.cpp @@ -1,3 +1,11 @@ +/*! + * @file CodeTester.cpp + * The CodeTester is a utility to run the output of the compiler as part of a unit test. + * This is effective for tests which try all combinations of registers, etc. + * + * The CodeTester can't be used for tests requiring the full GOAL language/linking. + */ + #include #include "CodeTester.h" #include "IGen.h" @@ -6,6 +14,9 @@ namespace emitter { CodeTester::CodeTester() : m_info(RegisterInfo::make_register_info()) {} +/*! + * Convert to a string for comparison against an assembler or tests. + */ std::string CodeTester::dump_to_hex_string(bool nospace) { std::string result; char buff[32]; @@ -26,20 +37,25 @@ std::string CodeTester::dump_to_hex_string(bool nospace) { return result; } +/*! + * Add an instruction to the buffer. + */ void CodeTester::emit(const Instruction& instr) { code_buffer_size += instr.emit(code_buffer + code_buffer_size); assert(code_buffer_size <= code_buffer_capacity); } -void CodeTester::emit_set_gpr_as_return(Register gpr) { - assert(gpr.is_gpr()); - emit(IGen::mov_gpr64_gpr64(RAX, gpr)); -} - +/*! + * Add a return instruction to the buffer. + */ void CodeTester::emit_return() { emit(IGen::ret()); } +/*! + * Pop all GPRs off of the stack. Optionally exclude rax. + * Pops RSP always, which is weird, but doesn't cause issues. + */ void CodeTester::emit_pop_all_gprs(bool exclude_rax) { for (int i = 16; i-- > 0;) { if (i != RAX || !exclude_rax) { @@ -48,6 +64,10 @@ void CodeTester::emit_pop_all_gprs(bool exclude_rax) { } } +/*! + * Push all GPRs onto the stack. Optionally exclude RAX. + * Pushes RSP always, which is weird, but doesn't cause issues. + */ void CodeTester::emit_push_all_gprs(bool exclude_rax) { for (int i = 0; i < 16; i++) { if (i != RAX || !exclude_rax) { @@ -56,6 +76,9 @@ void CodeTester::emit_push_all_gprs(bool exclude_rax) { } } +/*! + * Push all xmm registers (all 128-bits) to the stack. + */ void CodeTester::emit_push_all_xmms() { emit(IGen::sub_gpr64_imm8s(RSP, 8)); for (int i = 0; i < 16; i++) { @@ -64,6 +87,9 @@ void CodeTester::emit_push_all_xmms() { } } +/*! + * Pop all xmm registers (all 128-bits) from the stack + */ void CodeTester::emit_pop_all_xmms() { for (int i = 0; i < 16; i++) { emit(IGen::load128_xmm128_gpr64(XMM0 + i, RSP)); @@ -72,18 +98,31 @@ void CodeTester::emit_pop_all_xmms() { emit(IGen::add_gpr64_imm8s(RSP, 8)); } +/*! + * Remove everything from the code buffer + */ void CodeTester::clear() { code_buffer_size = 0; } +/*! + * Execute the buffered code with no arguments, return the value of RAX. + */ u64 CodeTester::execute() { return ((u64(*)())code_buffer)(); } +/*! + * Execute code buffer with arguments. Use get_c_abi_arg to figure out which registers the + * arguments will appear in (will handle windows/linux differences) + */ u64 CodeTester::execute(u64 in0, u64 in1, u64 in2, u64 in3) { return ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3); } +/*! + * Allocate a code buffer of the given size. + */ void CodeTester::init_code_buffer(int capacity) { code_buffer = (u8*)mmap(nullptr, capacity, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); diff --git a/goalc/emitter/CodeTester.h b/goalc/emitter/CodeTester.h index dbab244d4a..ee174ac659 100644 --- a/goalc/emitter/CodeTester.h +++ b/goalc/emitter/CodeTester.h @@ -1,3 +1,11 @@ +/*! + * @file CodeTester.h + * The CodeTester is a utility to run the output of the compiler as part of a unit test. + * This is effective for tests which try all combinations of registers, etc. + * + * The CodeTester can't be used for tests requiring the full GOAL language/linking. + */ + #ifndef JAK_CODETESTER_H #define JAK_CODETESTER_H @@ -17,11 +25,13 @@ class CodeTester { void emit_push_all_xmms(); void emit_pop_all_xmms(); void emit_return(); - void emit_set_gpr_as_return(Register gpr); void emit(const Instruction& instr); u64 execute(); u64 execute(u64 in0, u64 in1, u64 in2, u64 in3); + /*! + * Execute the function, get the return value in RAX, convert to a T, and return it. + */ template T execute_ret(u64 in0, u64 in1, u64 in2, u64 in3) { u64 result_u64 = ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3); @@ -30,6 +40,9 @@ class CodeTester { return result_T; } + /*! + * Add data to the code buffer. + */ template int emit_data(T x) { auto ret = code_buffer_size; @@ -39,12 +52,8 @@ class CodeTester { return ret; } - void clear(); - ~CodeTester(); - /*! * Should allow emitter tests which run code to do the right thing on windows. - * Assumes RAX is return and RSP is stack pointer. */ Register get_c_abi_arg_reg(int i) { #ifdef _WIN32 @@ -76,10 +85,20 @@ class CodeTester { #endif } + /*! + * Get the name of the given register, for debugging. + */ std::string reg_name(Register x) { return m_info.get_info(x).name; } + /*! + * Get number of bytes currently in use (offset of the next thing to be added) + */ int size() const { return code_buffer_size; } + const u8* data() const { return code_buffer; } + /*! + * Write over existing data at the given offset. + */ template void write(T x, int at) { assert(at >= 0); @@ -87,6 +106,9 @@ class CodeTester { memcpy(code_buffer + at, &x, sizeof(T)); } + /*! + * Read existing data at the given offset. + */ template T read(int at) { assert(at >= 0); @@ -96,7 +118,8 @@ class CodeTester { return result; } - const u8* data() const { return code_buffer; } + void clear(); + ~CodeTester(); private: int code_buffer_size = 0; diff --git a/goalc/emitter/Register.h b/goalc/emitter/Register.h index f8d17260b1..1e46393bb3 100644 --- a/goalc/emitter/Register.h +++ b/goalc/emitter/Register.h @@ -16,23 +16,23 @@ namespace emitter { // registers by name enum X86_REG : u8 { RAX, // return, temp - RCX, // arg 3 - RDX, // arg 2 - RBX, // X saved + RCX, // arg 3, temp + RDX, // arg 2, temp + RBX, // saved - RSP, // stack pointer !!!! - RBP, // saved !!!! - RSI, // arg 1 - RDI, // arg 0 + RSP, // stack pointer (special) + RBP, // saved + RSI, // arg 1, temp + RDI, // arg 0, temp - R8, // arg 4 - R9, // arg 5, saved + R8, // arg 4, temp + R9, // arg 5, temp R10, // arg 6, saved (arg in GOAL only) R11, // arg 7, saved (arg in GOAL only) - R12, // X saved - pp register (like s6) !! - R13, // X saved - function call register (like t9) !! - R14, // X saved - offset (added in GOAL x86) - R15, // X saved - st (like s7) + R12, // saved + R13, // pp (special!) + R14, // st (special!) + R15, // offset (special!) XMM0, XMM1, XMM2,