diff --git a/decompiler/IR/CfgBuilder.cpp b/decompiler/IR/CfgBuilder.cpp index e7d1ef96c..efb875b2c 100644 --- a/decompiler/IR/CfgBuilder.cpp +++ b/decompiler/IR/CfgBuilder.cpp @@ -130,6 +130,29 @@ void clean_up_cond_with_else(std::shared_ptr* ir, LinkedObjectFile& file) { } } +/* + * before we do this we'll have to to recognize ash... + +bool try_clean_up_sc_as_and(const std::shared_ptr& ir) { + for(size_t i = 0; i < ir->entries.size(); i++) { + auto& e = ir->entries.at(i); + if (i < ir->entries.size() - 1) { + // check we load the delay slot with false + auto branch = get_condition_branch(&e.condition); + assert(branch.first); + } + } +// ir->kind = IR_ShortCircuit::AND; +// return true; + return false; +} + +void clean_up_sc(std::shared_ptr ir) { + assert(ir->entries.size() > 1); + //try_clean_up_sc_as_and(ir); +} + */ + /*! * A GOAL comparison which produces a boolean is recognized as a cond-no-else by the CFG analysis. * But it should not be decompiled as a branching statement. @@ -153,7 +176,11 @@ void convert_cond_no_else_to_compare(std::shared_ptr* ir) { // as far as I can tell this is totally valid but just happens to not appear? // if this case is ever hit in the future it's fine and we just need to implement this. // but leaving empty for now so there's fewer things to test. - assert(false); + // assert(false); + + auto replacement = std::make_shared( + IR_Set::REG_64, dst, std::make_shared(condition.first->condition)); + *ir = replacement; } else { auto condition_as_seq = dynamic_cast(cne->entries.front().condition.get()); assert(condition_as_seq); @@ -354,6 +381,43 @@ std::shared_ptr try_sc_as_type_of(Function& f, LinkedObjectFile& file, Short } } +std::shared_ptr merge_cond_else_with_sc_cond(CondWithElse* cwe, + const std::shared_ptr& else_ir, + Function& f, + LinkedObjectFile& file) { + auto as_seq = dynamic_cast(else_ir.get()); + if (!as_seq || as_seq->forms.size() != 2) { + return nullptr; + } + + auto first = dynamic_cast(as_seq->forms.at(0).get()); + auto second = dynamic_cast(as_seq->forms.at(1).get()); + if (!first || !second) { + return nullptr; + } + + std::vector entries; + for (auto& x : cwe->entries) { + IR_Cond::Entry e; + e.condition = cfg_to_ir(f, file, x.condition); + e.body = cfg_to_ir(f, file, x.body); + entries.push_back(std::move(e)); + } + + auto first_condition = std::make_shared(); + first_condition->forms.push_back(as_seq->forms.at(0)); + first_condition->forms.push_back(second->entries.front().condition); + + second->entries.front().condition = first_condition; + + for (auto& x : second->entries) { + entries.push_back(x); + } + std::shared_ptr result = std::make_shared(entries); + clean_up_cond_no_else(&result, file); + return result; +} + /*! * Main CFG vertex to IR conversion. Will pull basic IR ops from the provided function as needed. */ @@ -400,6 +464,10 @@ std::shared_ptr cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx) // I don't know if this is sufficient to catch all cases. it may even recognize the wrong // thing in some cases... maybe we should check the delay slot instead? auto else_ir = cfg_to_ir(f, file, cvtx->else_vtx); + auto fancy_compact_result = merge_cond_else_with_sc_cond(cvtx, else_ir, f, file); + if (fancy_compact_result) { + return fancy_compact_result; + } if (dynamic_cast(else_ir.get())) { auto extra_cond = dynamic_cast(else_ir.get()); @@ -430,10 +498,21 @@ std::shared_ptr cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx) } } else if (dynamic_cast(vtx)) { auto* svtx = dynamic_cast(vtx); + // try as a type of expression first auto as_type_of = try_sc_as_type_of(f, file, svtx); if (as_type_of) { return as_type_of; } + // now try as a normal and/or + std::vector entries; + for (auto& x : svtx->entries) { + IR_ShortCircuit::Entry e; + e.condition = cfg_to_ir(f, file, x); + entries.push_back(e); + } + auto result = std::make_shared(entries); + clean_up_sc(result); + return result; } else if (dynamic_cast(vtx)) { auto* cvtx = dynamic_cast(vtx); std::vector entries; diff --git a/decompiler/IR/IR.cpp b/decompiler/IR/IR.cpp index 00d52d728..3fc59f7b0 100644 --- a/decompiler/IR/IR.cpp +++ b/decompiler/IR/IR.cpp @@ -646,13 +646,32 @@ void IR_Cond::get_children(std::vector>* output) const { } } -std::shared_ptr
IR_PartialNot::to_form(const LinkedObjectFile& file) const { - return buildList("INCOMPLETE-NOT", dst->to_form(file), src->to_form(file)); +std::shared_ptr IR_ShortCircuit::to_form(const LinkedObjectFile& file) const { + std::vector> forms; + switch (kind) { + case UNKNOWN: + forms.push_back(toForm("unknown-sc")); + break; + case AND: + forms.push_back(toForm("and")); + break; + case OR: + forms.push_back(toForm("or")); + break; + default: + assert(false); + } + for (auto& x : entries) { + forms.push_back(x.condition->to_form(file)); + } + return buildList(forms); } -void IR_PartialNot::get_children(std::vector>* output) const { - // probably we could get away with not returning anything here because these should - // always be registers? - output->push_back(dst); - output->push_back(src); -} \ No newline at end of file +void IR_ShortCircuit::get_children(std::vector>* output) const { + for (auto& x : entries) { + output->push_back(x.condition); + if (x.output) { + output->push_back(x.output); + } + } +} diff --git a/decompiler/IR/IR.h b/decompiler/IR/IR.h index 0bf2733e3..0b51f2df4 100644 --- a/decompiler/IR/IR.h +++ b/decompiler/IR/IR.h @@ -306,7 +306,7 @@ class IR_Cond : public IR { bool cleaned = false; }; std::vector entries; - IR_Cond(std::vector _entries) : entries(std::move(_entries)) {} + explicit IR_Cond(std::vector _entries) : entries(std::move(_entries)) {} std::shared_ptr to_form(const LinkedObjectFile& file) const override; void get_children(std::vector>* output) const override; }; @@ -321,11 +321,18 @@ class IR_GetRuntimeType : public IR { void get_children(std::vector>* output) const override; }; -class IR_PartialNot : public IR { +class IR_ShortCircuit : public IR { public: - std::shared_ptr dst, src; - IR_PartialNot(std::shared_ptr _dst, std::shared_ptr _src) - : dst(std::move(_dst)), src(std::move(_src)) {} + struct Entry { + std::shared_ptr condition = nullptr; + std::shared_ptr output = nullptr; // where the delay slot writes to. + bool cleaned = false; + }; + + enum Kind { UNKNOWN, AND, OR } kind = UNKNOWN; + + std::vector entries; + explicit IR_ShortCircuit(std::vector _entries) : entries(std::move(_entries)) {} std::shared_ptr to_form(const LinkedObjectFile& file) const override; void get_children(std::vector>* output) const override; }; diff --git a/decompiler/ObjectFile/ObjectFileDB.cpp b/decompiler/ObjectFile/ObjectFileDB.cpp index 8a5a6872e..5c5b7fd61 100644 --- a/decompiler/ObjectFile/ObjectFileDB.cpp +++ b/decompiler/ObjectFile/ObjectFileDB.cpp @@ -606,7 +606,7 @@ void ObjectFileDB::analyze_functions() { timer.start(); int total_basic_blocks = 0; for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { - printf("in %s\n", func.guessed_name.to_string().c_str()); + // printf("in %s\n", func.guessed_name.to_string().c_str()); auto blocks = find_blocks_in_function(data.linked_data, segment_id, func); total_basic_blocks += blocks.size(); func.basic_blocks = blocks; diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index d9328cb6b..666119a07 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -42,9 +42,12 @@ // this one fails due to false compaction where an else case has only a not expression in it. "master-is-hopeful-better?", + // temp remove me + "target-falling-anim-trans", + // real asm "cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", "render-boundary-quad", - "(method 19 collide-shape-prim-sphere)", + "(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp", "(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", "vblank-handler", "vif1-handler", "vif1-handler-debug", "entity-actor-count", "decompress-frame-data-pair-to-accumulator", @@ -52,7 +55,8 @@ "generic-copy-vtx-dclr-dtex", "generic-no-light-dproc-only", "generic-no-light-proc", "mercneric-bittable-asm", "generic-tie-decompress", "matrix-axis-sin-cos!", "matrix-axis-sin-cos-vu!", "generic-prepare-dma-single", "(method 13 collide-shape-prim-sphere)", "(method 14 collide-shape-prim-sphere)", "(method 12 collide-shape-prim-sphere)", - "adgif-shader<-texture-with-update!", "generic-interp-dproc", + "adgif-shader<-texture-with-update!", "generic-interp-dproc", "sprite-draw-distorters", "draw-bones", "(method 9 collide-mesh-cache)", + "(method 18 collide-shape-prim-sphere)", "collide-do-primitives", "draw-bones-check-longest-edge-asm", "sp-launch-particles-var", "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)",