mirror of
https://github.com/open-goal/jak-project
synced 2026-07-29 23:49:11 -04:00
[Decompiler - New IR] Add AtomicOp (#181)
* wip decompiler ir * add AtomicOp stuff * fix windows build and warnings * add instruction parser * include * make minilzo shared * odr fix * a * fix merge conflicts * move decompiler into namespace * update the code coverage to include the decompiler * add demo test * add register use test to example test
This commit is contained in:
+4
-2
@@ -18,12 +18,14 @@ add_executable(goalc-test
|
||||
test_pretty_print.cpp
|
||||
test_zydis.cpp
|
||||
goalc/test_goal_kernel.cpp
|
||||
decompiler/test_AtomicOpBuilder.cpp
|
||||
decompiler/test_InstructionParser.cpp
|
||||
${GOALC_TEST_FRAMEWORK_SOURCES}
|
||||
${GOALC_TEST_CASES})
|
||||
|
||||
enable_testing()
|
||||
|
||||
target_link_libraries(goalc-test common runtime compiler gtest Zydis)
|
||||
target_link_libraries(goalc-test common runtime compiler gtest decomp Zydis)
|
||||
|
||||
IF (WIN32)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
@@ -36,5 +38,5 @@ if(UNIX AND CMAKE_COMPILER_IS_GNUCXX AND CODE_COVERAGE)
|
||||
setup_target_for_coverage_lcov(NAME goalc-test_coverage
|
||||
EXECUTABLE goalc-test --gtest_color=yes
|
||||
DEPENDENCIES goalc-test
|
||||
EXCLUDE "third-party/*" "/usr/include/*" "decompiler/*")
|
||||
EXCLUDE "third-party/*" "/usr/include/*")
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "decompiler/IR2/AtomicOp.h"
|
||||
#include "decompiler/IR2/AtomicOpBuilder.h"
|
||||
#include "decompiler/Disasm/InstructionParser.h"
|
||||
|
||||
using namespace decompiler;
|
||||
TEST(DecompilerAtomicOpBuilder, Example) {
|
||||
InstructionParser parser;
|
||||
|
||||
// some MIPS instructions. Can be a sequence of instructions, possibly with labels.
|
||||
std::string input_program =
|
||||
"and v0, v1, a3\n"
|
||||
"and a1, a2, a2";
|
||||
|
||||
// convert to Instructions:
|
||||
ParsedProgram prg = parser.parse_program(input_program);
|
||||
|
||||
// this verifies we can convert from a string to an instruction, and back to a string again.
|
||||
// the instruction printer will add two leading spaces and a newline.
|
||||
EXPECT_EQ(prg.print(), " and v0, v1, a3\n and a1, a2, a2\n");
|
||||
|
||||
// next, set up a test environment for the conversion. The FunctionAtomicOps will hold
|
||||
// the result of the conversion
|
||||
FunctionAtomicOps container;
|
||||
|
||||
// treat the entire program as a single basic block, and convert!
|
||||
convert_block_to_atomic_ops(0, prg.instructions.begin(), prg.instructions.end(), prg.labels,
|
||||
&container);
|
||||
|
||||
// we should get back a single and operation:
|
||||
EXPECT_EQ(2, container.ops.size());
|
||||
|
||||
// for now, we create an empty environment. The environment will be used in the future to
|
||||
// rename register to variables, but for now, we just leave it empty and the printing will
|
||||
// use register names
|
||||
Env env;
|
||||
|
||||
// check the we get the right result:
|
||||
EXPECT_EQ(container.ops.at(0)->to_string(prg.labels, &env), "(set! v0 (logand v1 a3))");
|
||||
EXPECT_EQ(container.ops.at(1)->to_string(prg.labels, &env), "(set! a1 (logand a2 a2))");
|
||||
|
||||
// check that the registers read/written are identified for the first op (and v0, v1, a3)
|
||||
auto& first_op = container.ops.at(0);
|
||||
|
||||
// two registers read (v1 and a3)
|
||||
EXPECT_EQ(first_op->read_regs().size(), 2);
|
||||
// one register written (v0)
|
||||
EXPECT_EQ(first_op->write_regs().size(), 1);
|
||||
// no clobber registers (register which ends up with a garbage value in it)
|
||||
EXPECT_EQ(first_op->clobber_regs().size(), 0);
|
||||
|
||||
// the ordering of the two read registers doesn't matter. It happens to be in the same order
|
||||
// as the opcode here, but it may not always be the case.
|
||||
EXPECT_EQ(first_op->read_regs().at(0).to_string(), "v1");
|
||||
EXPECT_EQ(first_op->read_regs().at(1).to_string(), "a3");
|
||||
EXPECT_EQ(first_op->write_regs().at(0).to_string(), "v0");
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "decompiler/Disasm/InstructionParser.h"
|
||||
#include "decompiler/Disasm/DecompilerLabel.h"
|
||||
|
||||
using namespace decompiler;
|
||||
|
||||
TEST(DecompilerInstructionParser, SimpleTest) {
|
||||
InstructionParser parser;
|
||||
std::vector<std::string> ops = {"daddu a0, a1, a2", "addu r0, t7, s6", "daddiu r0, at, #t",
|
||||
"addiu t2, t3, 12", "slti v1, a3, -23", "sltiu s3, s4, 3",
|
||||
"sb v1, 12(a1)", "sh s7, sym(s6)", "sd s2, -12(s2)",
|
||||
"lw s3, 12(s7)", "lwu t2, sym(s7)", "add.s f0, f1, f2",
|
||||
"beq r0, r0, L312"};
|
||||
|
||||
std::vector<DecompilerLabel> labels;
|
||||
labels.push_back(DecompilerLabel{"L311", 1, 2});
|
||||
labels.push_back(DecompilerLabel{"L312", 1, 2});
|
||||
labels.push_back(DecompilerLabel{"L313", 1, 2});
|
||||
for (auto& op : ops) {
|
||||
auto instr = parser.parse_single_instruction(op, labels);
|
||||
EXPECT_EQ(op, instr.to_string(labels));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DecompilerInstructionParser, ProgramNoLabels) {
|
||||
InstructionParser parser;
|
||||
std::string program = " daddu a0, a1, a2\n sh s7, sym(s6)\n sb v1, 12(a1)\n";
|
||||
auto result = parser.parse_program(program);
|
||||
EXPECT_EQ(result.print(), program);
|
||||
}
|
||||
|
||||
TEST(DecompilerInstructionParser, ProgramLabels) {
|
||||
InstructionParser parser;
|
||||
std::string program =
|
||||
"L100:\n"
|
||||
" daddu v0, v1, v0\n"
|
||||
" beq at, r0, L102\n"
|
||||
"L102:\n"
|
||||
" jr ra\n";
|
||||
auto result = parser.parse_program(program);
|
||||
EXPECT_EQ(result.print(), program);
|
||||
}
|
||||
Reference in New Issue
Block a user