mirror of
https://github.com/open-goal/jak-project
synced 2026-09-09 20:21:28 -04:00
[Decompiler] Stack Variables (#338)
* clean up type analysis * get everything set up * basic stack variables working * partial load fix * most of matrix * add offline tests
This commit is contained in:
@@ -22,6 +22,7 @@ add_executable(goalc-test
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_AtomicOpBuilder.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormBeforeExpressions.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuild.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuild2.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_FormExpressionBuildLong.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionDecode.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_InstructionParser.cpp
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "FormRegressionTest.h"
|
||||
|
||||
#include "decompiler/analysis/type_analysis.h"
|
||||
#include "decompiler/analysis/variable_naming.h"
|
||||
#include "decompiler/analysis/reg_usage.h"
|
||||
#include "decompiler/analysis/cfg_builder.h"
|
||||
#include "decompiler/analysis/expression_build.h"
|
||||
#include "decompiler/analysis/final_output.h"
|
||||
#include "decompiler/analysis/insert_lets.h"
|
||||
#include "decompiler/util/config_parsers.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/util/json_util.h"
|
||||
#include "decompiler/IR2/Form.h"
|
||||
@@ -108,55 +110,78 @@ parse_var_json(const std::string& str) {
|
||||
std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
|
||||
const std::string& code,
|
||||
const TypeSpec& function_type,
|
||||
bool do_expressions,
|
||||
bool allow_pairs,
|
||||
const std::string& method_name,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings,
|
||||
const std::unordered_map<int, std::vector<TypeCast>>& casts,
|
||||
const std::string& var_map_json) {
|
||||
dts->type_prop_settings.locked = true;
|
||||
const TestSettings& settings) {
|
||||
// Set up decompiler type system
|
||||
dts->type_prop_settings.reset();
|
||||
dts->type_prop_settings.allow_pair = allow_pairs;
|
||||
dts->type_prop_settings.current_method_type = method_name;
|
||||
dts->type_prop_settings.current_method_type = settings.method_name;
|
||||
|
||||
// set up label names for string constants
|
||||
std::vector<std::string> string_label_names;
|
||||
for (auto& x : strings) {
|
||||
for (auto& x : settings.strings) {
|
||||
string_label_names.push_back(x.first);
|
||||
}
|
||||
|
||||
// parse the assembly
|
||||
auto program = parser->parse_program(code, string_label_names);
|
||||
// printf("prg:\n%s\n\n", program.print().c_str());
|
||||
|
||||
// create the test data collection
|
||||
auto test = std::make_unique<TestData>(program.instructions.size());
|
||||
// populate the LinkedObjectFile
|
||||
test->file.words_by_seg.resize(3);
|
||||
test->file.labels = program.labels;
|
||||
// Set up the environment
|
||||
test->func.ir2.env.file = &test->file;
|
||||
test->func.ir2.env.dts = dts.get();
|
||||
// Set up the function
|
||||
test->func.instructions = program.instructions;
|
||||
test->func.guessed_name.set_as_global("test-function");
|
||||
test->func.type = function_type;
|
||||
|
||||
for (auto& str : strings) {
|
||||
// set up string constants in the data
|
||||
for (auto& str : settings.strings) {
|
||||
test->add_string_at_label(str.first, str.second);
|
||||
}
|
||||
|
||||
// find basic blocks
|
||||
test->func.basic_blocks = find_blocks_in_function(test->file, 0, test->func);
|
||||
// analyze function prologue/epilogue
|
||||
test->func.analyze_prologue(test->file);
|
||||
// build control flow graph
|
||||
test->func.cfg = build_cfg(test->file, 0, test->func);
|
||||
EXPECT_TRUE(test->func.cfg->is_fully_resolved());
|
||||
if (!test->func.cfg->is_fully_resolved()) {
|
||||
fmt::print("CFG:\n{}\n", test->func.cfg->to_dot());
|
||||
}
|
||||
|
||||
// convert instruction to atomic ops
|
||||
DecompWarnings warnings;
|
||||
auto ops = convert_function_to_atomic_ops(test->func, program.labels, warnings);
|
||||
test->func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(std::move(ops));
|
||||
test->func.ir2.atomic_ops_succeeded = true;
|
||||
test->func.ir2.env.set_end_var(test->func.ir2.atomic_ops->end_op().return_var());
|
||||
|
||||
EXPECT_TRUE(test->func.run_type_analysis_ir2(function_type, *dts, test->file, casts, {}));
|
||||
// set up type settings
|
||||
if (!settings.casts_json.empty()) {
|
||||
test->func.ir2.env.set_type_casts(parse_cast_hints(nlohmann::json::parse(settings.casts_json)));
|
||||
}
|
||||
|
||||
if (settings.allow_pairs) {
|
||||
test->func.ir2.env.set_sloppy_pair_typing();
|
||||
}
|
||||
|
||||
if (!settings.stack_var_json.empty()) {
|
||||
auto stack_hints = parse_stack_var_hints(nlohmann::json::parse(settings.stack_var_json));
|
||||
test->func.ir2.env.set_stack_var_hints(stack_hints);
|
||||
}
|
||||
|
||||
// analyze types
|
||||
EXPECT_TRUE(run_type_analysis_ir2(function_type, *dts, test->func));
|
||||
test->func.ir2.env.types_succeeded = true;
|
||||
|
||||
// analyze registers
|
||||
test->func.ir2.env.set_reg_use(analyze_ir2_register_usage(test->func));
|
||||
|
||||
// split to variables
|
||||
auto result = run_variable_renaming(test->func, test->func.ir2.env.reg_use(),
|
||||
*test->func.ir2.atomic_ops, *dts);
|
||||
if (result.has_value()) {
|
||||
@@ -165,16 +190,18 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
|
||||
// structure
|
||||
build_initial_forms(test->func);
|
||||
EXPECT_TRUE(test->func.ir2.top_form);
|
||||
|
||||
// for now, just test that this can at least be called.
|
||||
if (test->func.ir2.top_form) {
|
||||
// just make sure this doesn't crash
|
||||
RegAccessSet vars;
|
||||
test->func.ir2.top_form->collect_vars(vars, true);
|
||||
|
||||
if (do_expressions) {
|
||||
auto config = parse_var_json(var_map_json);
|
||||
if (settings.do_expressions) {
|
||||
auto config = parse_var_json(settings.var_map_json);
|
||||
// build expressions (most of the fancy decompilation happens here)
|
||||
bool success = convert_to_expressions(test->func.ir2.top_form, *test->func.ir2.form_pool,
|
||||
test->func, config.first, config.second, *dts);
|
||||
|
||||
@@ -182,6 +209,7 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
// move variables into lets.
|
||||
insert_lets(test->func, test->func.ir2.env, *test->func.ir2.form_pool,
|
||||
test->func.ir2.top_form);
|
||||
}
|
||||
@@ -205,15 +233,9 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
|
||||
void FormRegressionTest::test(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool do_expressions,
|
||||
bool allow_pairs,
|
||||
const std::string& method_name,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings,
|
||||
const std::unordered_map<int, std::vector<TypeCast>>& casts,
|
||||
const std::string& var_map_json) {
|
||||
const TestSettings& settings) {
|
||||
auto ts = dts->parse_type_spec(type);
|
||||
auto test = make_function(code, ts, do_expressions, allow_pairs, method_name, strings, casts,
|
||||
var_map_json);
|
||||
auto test = make_function(code, ts, settings);
|
||||
ASSERT_TRUE(test);
|
||||
auto expected_form =
|
||||
pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car;
|
||||
@@ -237,10 +259,16 @@ void FormRegressionTest::test_final_function(
|
||||
const std::string& expected,
|
||||
bool allow_pairs,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings,
|
||||
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts,
|
||||
const std::string& cast_json,
|
||||
const std::string& var_map_json) {
|
||||
auto ts = dts->parse_type_spec(type);
|
||||
auto test = make_function(code, ts, true, allow_pairs, "", strings, casts, var_map_json);
|
||||
TestSettings settings;
|
||||
settings.allow_pairs = allow_pairs;
|
||||
settings.strings = strings;
|
||||
settings.casts_json = cast_json;
|
||||
settings.var_map_json = var_map_json;
|
||||
settings.do_expressions = true;
|
||||
auto test = make_function(code, ts, settings);
|
||||
ASSERT_TRUE(test);
|
||||
auto expected_form =
|
||||
pretty_print::get_pretty_printer_reader().read_from_string(expected, false).as_pair()->car;
|
||||
@@ -256,23 +284,18 @@ void FormRegressionTest::test_final_function(
|
||||
EXPECT_TRUE(expected_form == actual_form);
|
||||
}
|
||||
|
||||
std::unordered_map<int, std::vector<decompiler::TypeCast>> FormRegressionTest::parse_cast_json(
|
||||
const std::string& in) {
|
||||
std::unordered_map<int, std::vector<decompiler::TypeCast>> out;
|
||||
auto casts = nlohmann::json::parse(in);
|
||||
|
||||
for (auto& cast : casts) {
|
||||
auto idx_range = parse_json_optional_integer_range(cast.at(0));
|
||||
for (auto idx : idx_range) {
|
||||
TypeCast type_cast;
|
||||
type_cast.atomic_op_idx = idx;
|
||||
type_cast.reg = Register(cast.at(1));
|
||||
type_cast.type_name = cast.at(2).get<std::string>();
|
||||
out[idx].push_back(type_cast);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
void FormRegressionTest::test_with_stack_vars(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
const std::string& stack_map_json,
|
||||
const std::string& cast_json,
|
||||
const std::string& var_map_json) {
|
||||
TestSettings settings;
|
||||
settings.do_expressions = true;
|
||||
settings.stack_var_json = stack_map_json;
|
||||
settings.var_map_json = var_map_json;
|
||||
settings.casts_json = cast_json;
|
||||
test(code, type, expected, settings);
|
||||
}
|
||||
|
||||
std::unique_ptr<InstructionParser> FormRegressionTest::parser;
|
||||
|
||||
@@ -11,6 +11,16 @@ namespace decompiler {
|
||||
struct TypeCast;
|
||||
}
|
||||
|
||||
struct TestSettings {
|
||||
bool do_expressions = false;
|
||||
bool allow_pairs = false;
|
||||
std::string method_name;
|
||||
std::vector<std::pair<std::string, std::string>> strings;
|
||||
std::string casts_json;
|
||||
std::string var_map_json;
|
||||
std::string stack_var_json;
|
||||
};
|
||||
|
||||
class FormRegressionTest : public ::testing::Test {
|
||||
protected:
|
||||
static std::unique_ptr<decompiler::InstructionParser> parser;
|
||||
@@ -27,25 +37,22 @@ class FormRegressionTest : public ::testing::Test {
|
||||
void add_string_at_label(const std::string& label_name, const std::string& data);
|
||||
};
|
||||
|
||||
std::unique_ptr<TestData> make_function(
|
||||
const std::string& code,
|
||||
const TypeSpec& function_type,
|
||||
bool do_expressions,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {},
|
||||
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts = {},
|
||||
const std::string& var_map_json = "");
|
||||
std::unique_ptr<TestData> make_function(const std::string& code,
|
||||
const TypeSpec& function_type,
|
||||
const TestSettings& settings);
|
||||
|
||||
void test(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool do_expressions,
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {},
|
||||
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts = {},
|
||||
const std::string& var_map_json = "");
|
||||
const TestSettings& settings);
|
||||
|
||||
void test_final_function(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool allow_pairs = false,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {},
|
||||
const std::string& cast_json = "",
|
||||
const std::string& var_map_json = "");
|
||||
|
||||
void test_no_expr(const std::string& code,
|
||||
const std::string& type,
|
||||
@@ -53,9 +60,16 @@ class FormRegressionTest : public ::testing::Test {
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {},
|
||||
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts = {},
|
||||
const std::string& cast_json = "",
|
||||
const std::string& var_map_json = "") {
|
||||
test(code, type, expected, false, allow_pairs, method_name, strings, casts, var_map_json);
|
||||
TestSettings settings;
|
||||
settings.allow_pairs = allow_pairs;
|
||||
settings.method_name = method_name;
|
||||
settings.strings = strings;
|
||||
settings.casts_json = cast_json;
|
||||
settings.var_map_json = var_map_json;
|
||||
settings.do_expressions = false;
|
||||
test(code, type, expected, settings);
|
||||
}
|
||||
|
||||
void test_with_expr(const std::string& code,
|
||||
@@ -64,19 +78,22 @@ class FormRegressionTest : public ::testing::Test {
|
||||
bool allow_pairs = false,
|
||||
const std::string& method_name = "",
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {},
|
||||
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts = {},
|
||||
const std::string& cast_json = "",
|
||||
const std::string& var_map_json = "") {
|
||||
test(code, type, expected, true, allow_pairs, method_name, strings, casts, var_map_json);
|
||||
TestSettings settings;
|
||||
settings.allow_pairs = allow_pairs;
|
||||
settings.method_name = method_name;
|
||||
settings.strings = strings;
|
||||
settings.casts_json = cast_json;
|
||||
settings.var_map_json = var_map_json;
|
||||
settings.do_expressions = true;
|
||||
test(code, type, expected, settings);
|
||||
}
|
||||
|
||||
void test_final_function(
|
||||
const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
bool allow_pairs = false,
|
||||
const std::vector<std::pair<std::string, std::string>>& strings = {},
|
||||
const std::unordered_map<int, std::vector<decompiler::TypeCast>>& casts = {},
|
||||
const std::string& var_map_json = "");
|
||||
|
||||
std::unordered_map<int, std::vector<decompiler::TypeCast>> parse_cast_json(const std::string& in);
|
||||
void test_with_stack_vars(const std::string& code,
|
||||
const std::string& type,
|
||||
const std::string& expected,
|
||||
const std::string& stack_map_json,
|
||||
const std::string& cast_json = "",
|
||||
const std::string& var_map_json = "");
|
||||
};
|
||||
@@ -87,4 +87,14 @@
|
||||
(define-extern fabs (function float float))
|
||||
(define-extern abs (function int int))
|
||||
(define-extern rand-vu-init (function float none))
|
||||
(define-extern rand-vu (function float))
|
||||
(define-extern rand-vu (function float))
|
||||
|
||||
;; matrix
|
||||
(declare-type matrix structure)
|
||||
(declare-type vector structure)
|
||||
(define-extern matrix-transpose! (function matrix matrix matrix))
|
||||
(define-extern sin (function float float))
|
||||
(define-extern cos (function float float))
|
||||
(define-extern vector-sincos! (function vector vector vector int))
|
||||
(define-extern matrix-axis-sin-cos! (function matrix vector float float none))
|
||||
(define-extern atan (function float float float))
|
||||
@@ -45,8 +45,8 @@
|
||||
(.max.vf vf2 vf4 vf5)
|
||||
(.mov.vf vf1 vf0 :mask #b1000)
|
||||
(.mov.vf vf2 vf0 :mask #b1000)
|
||||
(.svf obj vf1)
|
||||
(.svf obj vf2 :offset 16)
|
||||
(.svf (&-> obj min quad) vf1)
|
||||
(.svf (&-> obj max quad) vf2)
|
||||
0
|
||||
)
|
||||
)
|
||||
@@ -57,13 +57,13 @@
|
||||
(vf2 :class vf)
|
||||
(vf3 :class vf)
|
||||
)
|
||||
(.lvf vf1 obj)
|
||||
(.lvf vf2 obj :offset 16)
|
||||
(.lvf vf1 (&-> obj min quad))
|
||||
(.lvf vf2 (&-> obj max quad))
|
||||
(.lvf vf3 arg0)
|
||||
(.min.vf vf1 vf1 vf3)
|
||||
(.max.vf vf2 vf2 vf3)
|
||||
(.svf obj vf1)
|
||||
(.svf obj vf2 :offset 16)
|
||||
(.svf (&-> obj min quad) vf1)
|
||||
(.svf (&-> obj max quad) vf2)
|
||||
0
|
||||
)
|
||||
)
|
||||
@@ -75,14 +75,14 @@
|
||||
(vf3 :class vf)
|
||||
(vf4 :class vf)
|
||||
)
|
||||
(.lvf vf1 obj)
|
||||
(.lvf vf2 obj :offset 16)
|
||||
(.lvf vf3 arg0)
|
||||
(.lvf vf4 arg0 :offset 16)
|
||||
(.lvf vf1 (&-> obj min quad))
|
||||
(.lvf vf2 (&-> obj max quad))
|
||||
(.lvf vf3 (&-> arg0 min quad))
|
||||
(.lvf vf4 (&-> arg0 max quad))
|
||||
(.min.vf vf1 vf1 vf3)
|
||||
(.max.vf vf2 vf2 vf4)
|
||||
(.svf obj vf1)
|
||||
(.svf obj vf2 :offset 16)
|
||||
(.svf (&-> obj min quad) vf1)
|
||||
(.svf (&-> obj max quad) vf2)
|
||||
0
|
||||
)
|
||||
)
|
||||
@@ -111,8 +111,8 @@
|
||||
(.sub.x.vf vf2 vf2 vf1 :mask #b111)
|
||||
(.mov.vf vf2 vf0 :mask #b1000)
|
||||
(.mov.vf vf3 vf0 :mask #b1000)
|
||||
(.svf obj vf2)
|
||||
(.svf obj vf3 :offset 16)
|
||||
(.svf (&-> obj min quad) vf2)
|
||||
(.svf (&-> obj max quad) vf3)
|
||||
0
|
||||
)
|
||||
)
|
||||
@@ -125,13 +125,13 @@
|
||||
(vf3 :class vf)
|
||||
)
|
||||
(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
||||
(.lvf vf1 arg0)
|
||||
(.lvf vf1 (&-> arg0 quad))
|
||||
(.sub.w.vf vf2 vf1 vf1 :mask #b111)
|
||||
(.add.w.vf vf3 vf1 vf1 :mask #b111)
|
||||
(.mov.vf vf2 vf0 :mask #b1000)
|
||||
(.mov.vf vf3 vf0 :mask #b1000)
|
||||
(.svf obj vf2)
|
||||
(.svf obj vf3 :offset 16)
|
||||
(.svf (&-> obj min quad) vf2)
|
||||
(.svf (&-> obj max quad) vf3)
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
)
|
||||
|
||||
;; definition for method 3 of type matrix
|
||||
;; INFO: this function exists in multiple non-identical object files
|
||||
(defmethod inspect matrix ((obj matrix))
|
||||
(format #t "[~8x] ~A~%" obj 'matrix)
|
||||
(format #t "~Tdata[16] @ #x~X~%" (-> obj data))
|
||||
@@ -36,6 +37,7 @@
|
||||
)
|
||||
|
||||
;; definition for method 3 of type matrix3
|
||||
;; INFO: this function exists in multiple non-identical object files
|
||||
(defmethod inspect matrix3 ((obj matrix3))
|
||||
(format #t "[~8x] ~A~%" obj 'matrix3)
|
||||
(format #t "~Tdata[12] @ #x~X~%" (-> obj data))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -849,8 +849,8 @@
|
||||
(rlet ((vf1 :class vf)
|
||||
(vf2 :class vf)
|
||||
)
|
||||
(.lvf vf1 arg0)
|
||||
(.lvf vf2 arg1)
|
||||
(.lvf vf1 (&-> arg0 quad))
|
||||
(.lvf vf2 (&-> arg1 quad))
|
||||
(.mul.vf vf1 vf1 vf2)
|
||||
(.add.y.vf vf1 vf1 vf1 :mask #b1)
|
||||
(.add.z.vf vf1 vf1 vf1 :mask #b1)
|
||||
@@ -895,8 +895,8 @@
|
||||
(vf3 :class vf)
|
||||
)
|
||||
(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
||||
(.lvf vf1 arg0)
|
||||
(.lvf vf2 arg1)
|
||||
(.lvf vf1 (&-> arg0 quad))
|
||||
(.lvf vf2 (&-> arg1 quad))
|
||||
(.mul.vf vf1 vf1 vf2)
|
||||
(.add.w.vf vf3 vf0 vf0 :mask #b1)
|
||||
(.mul.x.vf acc vf3 vf1 :mask #b1)
|
||||
@@ -917,10 +917,10 @@
|
||||
)
|
||||
(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
||||
(.mov.vf vf6 vf0 :mask #b1000)
|
||||
(.lvf vf4 arg1)
|
||||
(.lvf vf5 arg2)
|
||||
(.lvf vf4 (&-> arg1 quad))
|
||||
(.lvf vf5 (&-> arg2 quad))
|
||||
(.add.vf vf6 vf4 vf5 :mask #b111)
|
||||
(.svf arg0 vf6)
|
||||
(.svf (&-> arg0 quad) vf6)
|
||||
arg0
|
||||
)
|
||||
)
|
||||
@@ -933,11 +933,11 @@
|
||||
(vf6 :class vf)
|
||||
)
|
||||
(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
||||
(.lvf vf4 arg1)
|
||||
(.lvf vf5 arg2)
|
||||
(.lvf vf4 (&-> arg1 quad))
|
||||
(.lvf vf5 (&-> arg2 quad))
|
||||
(.mov.vf vf6 vf0 :mask #b1000)
|
||||
(.sub.vf vf6 vf4 vf5 :mask #b111)
|
||||
(.svf arg0 vf6)
|
||||
(.svf (&-> arg0 quad) vf6)
|
||||
arg0
|
||||
)
|
||||
)
|
||||
@@ -953,7 +953,7 @@
|
||||
(defun vector-reset! ((arg0 vector))
|
||||
(rlet ((vf0 :class vf))
|
||||
(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
||||
(.svf arg0 vf0)
|
||||
(.svf (&-> arg0 quad) vf0)
|
||||
arg0
|
||||
)
|
||||
)
|
||||
|
||||
@@ -11,8 +11,9 @@ TEST_F(FormRegressionTest, StringTest) {
|
||||
"L101:\n"
|
||||
" jr ra\n"
|
||||
" daddu sp, sp, r0";
|
||||
auto test = make_function(func, TypeSpec("function", {TypeSpec("none")}), false, false, "",
|
||||
{{"L100", "testing-string"}, {"L101", "testing-string-2"}});
|
||||
TestSettings settings;
|
||||
settings.strings = {{"L100", "testing-string"}, {"L101", "testing-string-2"}};
|
||||
auto test = make_function(func, TypeSpec("function", {TypeSpec("none")}), settings);
|
||||
|
||||
EXPECT_EQ(test->file.get_goal_string_by_label(test->file.get_label_by_name("L100")),
|
||||
"testing-string");
|
||||
|
||||
@@ -2282,8 +2282,8 @@ TEST_F(FormRegressionTest, ExprPrintName) {
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_cast_json("[\t\t[24, \"a1\", \"symbol\"],\n"
|
||||
"\t\t[39, \"a0\", \"symbol\"]]"));
|
||||
"[\t\t[24, \"a1\", \"symbol\"],\n"
|
||||
"\t\t[39, \"a0\", \"symbol\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprProfileBarMethod9) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "FormRegressionTest.h"
|
||||
|
||||
using namespace decompiler;
|
||||
|
||||
TEST_F(FormRegressionTest, MatrixPMult) {
|
||||
std::string func =
|
||||
"sll r0, r0, 0\n"
|
||||
" daddiu sp, sp, -112\n"
|
||||
" sd ra, 0(sp)\n"
|
||||
" sq s5, 80(sp)\n"
|
||||
" sq gp, 96(sp)\n"
|
||||
|
||||
" or gp, a0, r0\n"
|
||||
" daddiu s5, sp, 16\n"
|
||||
" sq r0, 0(s5)\n"
|
||||
" sq r0, 16(s5)\n"
|
||||
" sq r0, 32(s5)\n"
|
||||
" sq r0, 48(s5)\n"
|
||||
" lw t9, matrix*!(s7)\n"
|
||||
" or a0, s5, r0\n"
|
||||
" jalr ra, t9\n"
|
||||
" sll v0, ra, 0\n"
|
||||
|
||||
" lq v1, 0(s5)\n"
|
||||
" sq v1, 0(gp)\n"
|
||||
" lq v1, 16(s5)\n"
|
||||
" sq v1, 16(gp)\n"
|
||||
" lq v1, 32(s5)\n"
|
||||
" sq v1, 32(gp)\n"
|
||||
" lq v1, 48(s5)\n"
|
||||
" sq v1, 48(gp)\n"
|
||||
" or v0, gp, r0\n"
|
||||
" ld ra, 0(sp)\n"
|
||||
" lq gp, 96(sp)\n"
|
||||
" lq s5, 80(sp)\n"
|
||||
" jr ra\n"
|
||||
" daddiu sp, sp, 112";
|
||||
std::string type = "(function matrix matrix matrix matrix)";
|
||||
std::string expected =
|
||||
"(begin\n"
|
||||
" (let ((s5-0 (new (quote stack) (quote matrix))))\n"
|
||||
" (set! (-> s5-0 vector 0 quad) (the-as uint128 0))\n"
|
||||
" (set! (-> s5-0 vector 1 quad) (the-as uint128 0))\n"
|
||||
" (set! (-> s5-0 vector 2 quad) (the-as uint128 0))\n"
|
||||
" (set! (-> s5-0 vector 3 quad) (the-as uint128 0))\n"
|
||||
" (matrix*! s5-0 arg1 arg2)\n"
|
||||
" (set! (-> arg0 vector 0 quad) (-> s5-0 vector 0 quad))\n"
|
||||
" (set! (-> arg0 vector 1 quad) (-> s5-0 vector 1 quad))\n"
|
||||
" (set! (-> arg0 vector 2 quad) (-> s5-0 vector 2 quad))\n"
|
||||
" (set! (-> arg0 vector 3 quad) (-> s5-0 vector 3 quad))\n"
|
||||
" )\n"
|
||||
" arg0\n"
|
||||
" )";
|
||||
test_with_stack_vars(func, type, expected,
|
||||
"[\n"
|
||||
" [16, \"matrix\"]\n"
|
||||
" ]");
|
||||
}
|
||||
@@ -687,21 +687,21 @@ TEST_F(FormRegressionTest, ExprArrayMethod2) {
|
||||
{"L336", "~A"},
|
||||
{"L335", " ~A"},
|
||||
{"L334", ")"}},
|
||||
parse_cast_json("["
|
||||
"\t\t[23, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[43, \"gp\", \"(array uint32)\"],\n"
|
||||
"\t\t[63, \"gp\", \"(array int64)\"],\n"
|
||||
"\t\t[83, \"gp\", \"(array uint64)\"],\n"
|
||||
"\t\t[102, \"gp\", \"(array int8)\"],\n"
|
||||
"\t\t[121, \"gp\", \"(array uint8)\"],\n"
|
||||
"\t\t[141, \"gp\", \"(array int16)\"],\n"
|
||||
"\t\t[161, \"gp\", \"(array uint16)\"],\n"
|
||||
"\t\t[186, \"gp\", \"(array uint128)\"],\n"
|
||||
"\t\t[204, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[223, \"gp\", \"(array float)\"],\n"
|
||||
"\t\t[232, \"gp\", \"(array float)\"],\n"
|
||||
"\t\t[249, \"gp\", \"(array basic)\"],\n"
|
||||
"\t\t[258, \"gp\", \"(array basic)\"]]"));
|
||||
"["
|
||||
"\t\t[23, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[43, \"gp\", \"(array uint32)\"],\n"
|
||||
"\t\t[63, \"gp\", \"(array int64)\"],\n"
|
||||
"\t\t[83, \"gp\", \"(array uint64)\"],\n"
|
||||
"\t\t[102, \"gp\", \"(array int8)\"],\n"
|
||||
"\t\t[121, \"gp\", \"(array uint8)\"],\n"
|
||||
"\t\t[141, \"gp\", \"(array int16)\"],\n"
|
||||
"\t\t[161, \"gp\", \"(array uint16)\"],\n"
|
||||
"\t\t[186, \"gp\", \"(array uint128)\"],\n"
|
||||
"\t\t[204, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[223, \"gp\", \"(array float)\"],\n"
|
||||
"\t\t[232, \"gp\", \"(array float)\"],\n"
|
||||
"\t\t[249, \"gp\", \"(array basic)\"],\n"
|
||||
"\t\t[258, \"gp\", \"(array basic)\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprArrayMethod3) {
|
||||
@@ -1266,18 +1266,18 @@ TEST_F(FormRegressionTest, ExprArrayMethod3) {
|
||||
{"L327", "~T [~D] #x~X~%"},
|
||||
{"L326", "~T [~D] ~f~%"},
|
||||
{"L325", "~T [~D] ~A~%"}},
|
||||
parse_cast_json("[\t\t[44, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[62, \"gp\", \"(array uint32)\"],\n"
|
||||
"\t\t[80, \"gp\", \"(array int64)\"],\n"
|
||||
"\t\t[98, \"gp\", \"(array uint64)\"],\n"
|
||||
"\t\t[115, \"gp\", \"(array int8)\"],\n"
|
||||
"\t\t[132, \"gp\", \"(array int8)\"],\n"
|
||||
"\t\t[150, \"gp\", \"(array int16)\"],\n"
|
||||
"\t\t[168, \"gp\", \"(array uint16)\"],\n"
|
||||
"\t\t[191, \"gp\", \"(array uint128)\"],\n"
|
||||
"\t\t[207, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[226, \"gp\", \"(array float)\"],\n"
|
||||
"\t\t[243, \"gp\", \"(array basic)\"]]"));
|
||||
"[\t\t[44, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[62, \"gp\", \"(array uint32)\"],\n"
|
||||
"\t\t[80, \"gp\", \"(array int64)\"],\n"
|
||||
"\t\t[98, \"gp\", \"(array uint64)\"],\n"
|
||||
"\t\t[115, \"gp\", \"(array int8)\"],\n"
|
||||
"\t\t[132, \"gp\", \"(array int8)\"],\n"
|
||||
"\t\t[150, \"gp\", \"(array int16)\"],\n"
|
||||
"\t\t[168, \"gp\", \"(array uint16)\"],\n"
|
||||
"\t\t[191, \"gp\", \"(array uint128)\"],\n"
|
||||
"\t\t[207, \"gp\", \"(array int32)\"],\n"
|
||||
"\t\t[226, \"gp\", \"(array float)\"],\n"
|
||||
"\t\t[243, \"gp\", \"(array basic)\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprValid) {
|
||||
|
||||
@@ -371,7 +371,7 @@ TEST_F(FormRegressionTest, ExprMethod0Thread) {
|
||||
" (the-as cpu-thread (the-as object obj))\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "cpu-thread", {},
|
||||
parse_cast_json("[[[13, 28], \"v0\", \"cpu-thread\"]]"),
|
||||
"[[[13, 28], \"v0\", \"cpu-thread\"]]",
|
||||
"{\"vars\":{\"v0-0\":[\"obj\", \"cpu-thread\"]}}");
|
||||
}
|
||||
|
||||
@@ -686,8 +686,8 @@ TEST_F(FormRegressionTest, ExprMethod0Process) {
|
||||
" (the-as process v0-0)\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "process", {},
|
||||
parse_cast_json("[\t\t[12, \"a0\", \"int\"],\n"
|
||||
"\t\t[[13, 43], \"v0\", \"process\"]]"));
|
||||
"[\t\t[12, \"a0\", \"int\"],\n"
|
||||
"\t\t[[13, 43], \"v0\", \"process\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
|
||||
@@ -751,8 +751,8 @@ TEST_F(FormRegressionTest, ExprInspectProcessHeap) {
|
||||
" #f\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_cast_json("[\t\t[[4,11], \"s5\", \"basic\"],\n"
|
||||
"\t\t[[17,20], \"s5\", \"pointer\"]]"),
|
||||
"[\t\t[[4,11], \"s5\", \"basic\"],\n"
|
||||
"\t\t[[17,20], \"s5\", \"pointer\"]]",
|
||||
"{\"vars\":{\"s5-0\":[\"obj\", \"pointer\"]}}");
|
||||
}
|
||||
|
||||
@@ -1123,8 +1123,8 @@ TEST_F(FormRegressionTest, ExprMethod14DeadPool) {
|
||||
func, type, expected, false, "dead-pool",
|
||||
{{"L315", "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%"},
|
||||
{"L314", "WARNING: ~A ~A could not be allocated, because ~A was empty.~%"}},
|
||||
parse_cast_json("[\t\t[24, \"v1\", \"(pointer process-tree)\"],\n"
|
||||
"\t\t[[30,39], \"s4\", \"(pointer process-tree)\"]]"));
|
||||
"[\t\t[24, \"v1\", \"(pointer process-tree)\"],\n"
|
||||
"\t\t[[30,39], \"s4\", \"(pointer process-tree)\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod15DeadPool) {
|
||||
@@ -1299,11 +1299,10 @@ TEST_F(FormRegressionTest, ExprMethod0DeadPoolHeap) {
|
||||
" (set! (-> obj heap top-base) (-> obj heap top))\n"
|
||||
" obj\n"
|
||||
" )";
|
||||
test_with_expr(
|
||||
func, type, expected, false, "dead-pool-heap", {},
|
||||
parse_cast_json("[\t\t[60, \"v0\", \"int\"],\n"
|
||||
"\t\t[61, \"a0\", \"pointer\"], [61, \"v0\", \"dead-pool-heap\"]]"),
|
||||
"{\"vars\":{\"v0-0\":[\"obj\", \"dead-pool-heap\"]}}");
|
||||
test_with_expr(func, type, expected, false, "dead-pool-heap", {},
|
||||
"[\t\t[60, \"v0\", \"int\"],\n"
|
||||
"\t\t[61, \"a0\", \"pointer\"], [61, \"v0\", \"dead-pool-heap\"]]",
|
||||
"{\"vars\":{\"v0-0\":[\"obj\", \"dead-pool-heap\"]}}");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod22DeadPoolHeap) {
|
||||
@@ -1426,9 +1425,9 @@ TEST_F(FormRegressionTest, ExprMethod21DeadPoolHeap) {
|
||||
" )\n"
|
||||
" )";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_cast_json("[\t\t[5, \"v1\", \"pointer\"],\n"
|
||||
"\t\t[13, \"a0\", \"pointer\"],\n"
|
||||
"\t\t[25, \"v1\", \"pointer\"]]"));
|
||||
"[\t\t[5, \"v1\", \"pointer\"],\n"
|
||||
"\t\t[13, \"a0\", \"pointer\"],\n"
|
||||
"\t\t[25, \"v1\", \"pointer\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod3DeadPoolHeap) {
|
||||
@@ -1623,7 +1622,7 @@ TEST_F(FormRegressionTest, ExprMethod5DeadPoolHeap) {
|
||||
std::string expected =
|
||||
"(+ (the-as int (- -4 (the-as int arg0))) (the-as int (-> arg0 heap top)))";
|
||||
test_with_expr(func, type, expected, false, "", {},
|
||||
parse_cast_json("[[3, \"v1\", \"int\"], [3, \"a0\", \"int\"]]"));
|
||||
"[[3, \"v1\", \"int\"], [3, \"a0\", \"int\"]]");
|
||||
}
|
||||
|
||||
TEST_F(FormRegressionTest, ExprMethod19DeadPoolHeap) {
|
||||
|
||||
@@ -14,30 +14,18 @@ const std::unordered_set<std::string> g_object_files_to_decompile = {
|
||||
"gcommon", "gstring-h", "gkernel-h", "gkernel",
|
||||
/*"pskernel",*/ "gstring", "dgo-h", "gstate", "types-h", "vu1-macros", "math", "vector-h",
|
||||
"bounding-box-h", "matrix-h", "quaternion-h", "euler-h", "transform-h", "geometry-h",
|
||||
"trigonometry-h",
|
||||
"trigonometry-h", /* transformq-h */ "matrix",
|
||||
/* gap */
|
||||
"bounding-box"};
|
||||
|
||||
// the object files to check against a reference in test/decompiler/reference
|
||||
const std::vector<std::string> g_object_files_to_check_against_reference = {
|
||||
"gcommon", // NOTE: this file needs work, but adding it for now just to test the framework.
|
||||
"gstring-h",
|
||||
"gkernel-h",
|
||||
"gkernel",
|
||||
"gstring",
|
||||
"dgo-h",
|
||||
"gstate",
|
||||
"types-h",
|
||||
"vu1-macros",
|
||||
"math",
|
||||
"vector-h",
|
||||
"bounding-box-h",
|
||||
"matrix-h",
|
||||
"quaternion-h",
|
||||
"euler-h",
|
||||
"transform-h",
|
||||
"geometry-h",
|
||||
"trigonometry-h",
|
||||
"gstring-h", "gkernel-h", "gkernel", "gstring", "dgo-h", "gstate", "types-h", "vu1-macros",
|
||||
"math", "vector-h", "bounding-box-h", "matrix-h", "quaternion-h", "euler-h", "transform-h",
|
||||
"geometry-h", "trigonometry-h",
|
||||
/* transformq-h, */
|
||||
"matrix",
|
||||
/* gap */ "bounding-box"};
|
||||
|
||||
// the functions we expect the decompiler to skip
|
||||
@@ -66,6 +54,10 @@ const std::unordered_set<std::string> expected_skip_in_decompiler = {
|
||||
// bounding-box
|
||||
"(method 9 bounding-box)", // handwritten asm loop
|
||||
"(method 14 bounding-box)", // handwritten asm loop
|
||||
// matrix
|
||||
"(method 9 matrix)", // handwritten asm loop
|
||||
"matrix-axis-sin-cos!",
|
||||
"matrix-axis-sin-cos-vu!",
|
||||
};
|
||||
|
||||
const std::unordered_set<std::string> skip_in_compiling = {
|
||||
@@ -100,6 +92,10 @@ const std::unordered_set<std::string> skip_in_compiling = {
|
||||
"(method 3 vector)", // this function appears twice, which confuses the compiler.
|
||||
"vector-dot", // fpu acc
|
||||
"vector4-dot", // fpu acc
|
||||
|
||||
/// MATRIX
|
||||
"matrix-transpose!", // unsupported asm ops
|
||||
"matrix-4x4-inverse!", // compiler fails to regalloc this...
|
||||
};
|
||||
|
||||
// default location for the data. It can be changed with a command line argument.
|
||||
|
||||
Reference in New Issue
Block a user