/* Created on: Jun 5, 2019 Copyright 2019 flyinghead This file is part of reicast. reicast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. reicast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with reicast. If not, see . */ #include "blockmanager.h" #include "ssa.h" #define SHIL_MODE 2 #include "shil_canonical.h" void SSAOptimizer::InsertMov32Op(const shil_param& rd, const shil_param& rs) { shil_opcode op2(block->oplist[opnum]); op2.op = shop_mov32; op2.rd = rd; op2.rd2 = shil_param(); op2.rs1 = rs; op2.rs2 = shil_param(); op2.rs3 = shil_param(); block->oplist.insert(block->oplist.begin() + opnum + 1, op2); opnum++; } bool SSAOptimizer::ExecuteConstOp(shil_opcode* op) { if (!op->rs1.is_reg() && !op->rs2.is_reg() && !op->rs3.is_reg() && (op->rs1.is_imm() || op->rs2.is_imm() || op->rs3.is_imm()) && op->rd.is_reg()) { // Only immediate operands -> execute the op at compile time u32 rs1 = op->rs1.is_imm() ? op->rs1.imm_value() : 0; u32 rs2 = op->rs2.is_imm() ? op->rs2.imm_value() : 0; u32 rs3 = op->rs3.is_imm() ? op->rs3.imm_value() : 0; u32 rd; u32 rd2; switch (op->op) { case shop_mov32: rd = rs1; break; case shop_add: rd = shil_opcl_add::f1::impl(rs1, rs2); break; case shop_sub: rd = shil_opcl_sub::f1::impl(rs1, rs2); break; case shop_adc: case shop_sbc: case shop_negc: case shop_rocl: case shop_rocr: { u64 v; if (op->op == shop_adc) v = shil_opcl_adc::f1::impl(rs1, rs2, rs3); else if (op->op == shop_sbc) v = shil_opcl_sbc::f1::impl(rs1, rs2, rs3); else if (op->op == shop_rocl) v = shil_opcl_rocl::f1::impl(rs1, rs2); else if (op->op == shop_rocr) v = shil_opcl_rocr::f1::impl(rs1, rs2); else v = shil_opcl_negc::f1::impl(rs1, rs2); rd = (u32)v; rd2 = (u32)(v >> 32); shil_param op2_rd = shil_param(op->rd2._reg); op2_rd.version[0] = op->rd2.version[0]; InsertMov32Op(op2_rd, shil_param(FMT_IMM, rd2)); // the previous insert might have invalidated our reference op = &block->oplist[opnum - 1]; op->rd2 = shil_param(); } break; case shop_shl: rd = shil_opcl_shl::f1::impl(rs1, rs2); break; case shop_shr: rd = shil_opcl_shr::f1::impl(rs1, rs2); break; case shop_sar: rd = shil_opcl_sar::f1::impl(rs1, rs2); break; case shop_ror: rd = shil_opcl_ror::f1::impl(rs1, rs2); break; case shop_shld: rd = shil_opcl_shld::f1::impl(rs1, rs2); break; case shop_shad: rd = shil_opcl_shad::f1::impl(rs1, rs2); break; case shop_or: rd = shil_opcl_or::f1::impl(rs1, rs2); break; case shop_and: rd = shil_opcl_and::f1::impl(rs1, rs2); break; case shop_xor: rd = shil_opcl_xor::f1::impl(rs1, rs2); break; case shop_not: rd = shil_opcl_not::f1::impl(rs1); break; case shop_ext_s16: rd = shil_opcl_ext_s16::f1::impl(rs1); break; case shop_ext_s8: rd = shil_opcl_ext_s8::f1::impl(rs1); break; case shop_mul_i32: rd = shil_opcl_mul_i32::f1::impl(rs1, rs2); break; case shop_mul_u16: rd = shil_opcl_mul_u16::f1::impl(rs1, rs2); break; case shop_mul_s16: rd = shil_opcl_mul_s16::f1::impl(rs1, rs2); break; case shop_mul_u64: case shop_mul_s64: { u64 v; if (op->op == shop_mul_u64) v = shil_opcl_mul_u64::f1::impl(rs1, rs2); else v = shil_opcl_mul_s64::f1::impl(rs1, rs2); rd = (u32)v; rd2 = (u32)(v >> 32); shil_param op2_rd = shil_param(op->rd2._reg); op2_rd.version[0] = op->rd2.version[0]; InsertMov32Op(op2_rd, shil_param(FMT_IMM, rd2)); // the previous insert might have invalidated our reference op = &block->oplist[opnum - 1]; op->rd2 = shil_param(); } break; case shop_test: rd = shil_opcl_test::f1::impl(rs1, rs2); break; case shop_neg: rd = shil_opcl_neg::f1::impl(rs1); break; case shop_swaplb: rd = shil_opcl_swaplb::f1::impl(rs1); break; case shop_swap: rd = shil_opcl_swap::f1::impl(rs1); break; case shop_seteq: rd = shil_opcl_seteq::f1::impl(rs1, rs2); break; case shop_setgt: rd = shil_opcl_setgt::f1::impl(rs1, rs2); break; case shop_setge: rd = shil_opcl_setge::f1::impl(rs1, rs2); break; case shop_setab: rd = shil_opcl_setab::f1::impl(rs1, rs2); break; case shop_setae: rd = shil_opcl_setae::f1::impl(rs1, rs2); break; case shop_setpeq: rd = shil_opcl_setpeq::f1::impl(rs1, rs2); break; case shop_xtrct: rd = shil_opcl_xtrct::f1::impl(rs1, rs2); break; case shop_div32u: case shop_div32s: { u64 res = op->op == shop_div32u ? shil_opcl_div32u::f1::impl(rs1, rs2, rs3) : shil_opcl_div32s::f1::impl(rs1, rs2, rs3); rd = (u32)res; constprop_values[RegValue(op->rd, 1)] = res >> 32; shil_param op2_rd = shil_param((Sh4RegType)(op->rd._reg + 1)); op2_rd.version[0] = op->rd.version[1]; InsertMov32Op(op2_rd, shil_param(FMT_IMM, res >> 32)); // the previous insert might have invalidated our reference op = &block->oplist[opnum - 1]; } break; case shop_div32p2: rd = shil_opcl_div32p2::f1::impl(rs1, rs2, rs3); break; case shop_jdyn: { verify(BET_GET_CLS(block->BlockType) == BET_CLS_Dynamic); rs1 += rs2; switch ((block->BlockType >> 1) & 3) { case BET_SCL_Jump: case BET_SCL_Ret: block->BlockType = BET_StaticJump; block->BranchBlock = rs1; break; case BET_SCL_Call: block->BlockType = BET_StaticCall; block->BranchBlock = rs1; break; case BET_SCL_Intr: block->BlockType = BET_StaticIntr; block->NextBlock = rs1; break; default: die("Unexpected block end type\n") ; } // rd (that is jdyn) won't be updated but since it's not a real register // it shouldn't be a problem block->oplist.erase(block->oplist.begin() + opnum); opnum--; stats.dyn_to_stat_blocks++; return true; } case shop_jcond: { if (rs1 != (block->BlockType & 1)) { block->BranchBlock = block->NextBlock; } block->BlockType = BET_StaticJump; block->NextBlock = NullAddress; block->has_jcond = false; // same remark regarding jdyn as in the previous case block->oplist.erase(block->oplist.begin() + opnum); opnum--; stats.dyn_to_stat_blocks++; return true; } case shop_fneg: { f32 frd = shil_opcl_fneg::f1::impl(reinterpret_cast(rs1)); rd = reinterpret_cast(frd); } break; case shop_fadd: { f32 frd = shil_opcl_fadd::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2)); rd = reinterpret_cast(frd); } break; case shop_fsub: { f32 frd = shil_opcl_fsub::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2)); rd = reinterpret_cast(frd); } break; case shop_fmul: { f32 frd = shil_opcl_fmul::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2)); rd = reinterpret_cast(frd); } break; case shop_fdiv: { f32 frd = shil_opcl_fdiv::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2)); rd = reinterpret_cast(frd); } break; case shop_cvt_i2f_n: { f32 frd = shil_opcl_cvt_i2f_n::f1::impl(rs1); rd = reinterpret_cast(frd); } break; case shop_cvt_i2f_z: { f32 frd = shil_opcl_cvt_i2f_z::f1::impl(rs1); rd = reinterpret_cast(frd); } break; case shop_fsqrt: { f32 frd = shil_opcl_fsqrt::f1::impl(reinterpret_cast(rs1)); rd = reinterpret_cast(frd); } break; case shop_cvt_f2i_t: { f32 r1 = reinterpret_cast(rs1); rd = shil_opcl_cvt_f2i_t::f1::impl(r1); } break; case shop_fsrra: { f32 frd = shil_opcl_fsrra::f1::impl(reinterpret_cast(rs1)); rd = reinterpret_cast(frd); } break; case shop_fsca: { f32 tmp[2]; shil_opcl_fsca::fsca_table::impl(tmp, rs1); rd = reinterpret_cast(tmp[0]); u32 rd_1 = reinterpret_cast(tmp[1]); constprop_values[RegValue(op->rd, 1)] = rd_1; shil_param op2_rd = shil_param((Sh4RegType)(op->rd._reg + 1)); op2_rd.version[0] = op->rd.version[1]; InsertMov32Op(op2_rd, shil_param(FMT_IMM, rd_1)); // the previous insert might have invalidated our reference op = &block->oplist[opnum - 1]; op->rd.type = FMT_F32; } break; case shop_fabs: { f32 frd = shil_opcl_fabs::f1::impl(reinterpret_cast(rs1)); rd = reinterpret_cast(frd); } break; case shop_fmac: { f32 frd = shil_opcl_fmac::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2), reinterpret_cast(rs3)); rd = reinterpret_cast(frd); } break; case shop_fseteq: rd = shil_opcl_fseteq::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2)); break; case shop_fsetgt: rd = shil_opcl_fsetgt::f1::impl(reinterpret_cast(rs1), reinterpret_cast(rs2)); break; default: ERROR_LOG(DYNAREC, "unhandled constant op %d", op->op); die("unhandled constant op"); break; } constprop_values[RegValue(op->rd)] = rd; if (op->rd2.is_r32()) constprop_values[RegValue(op->rd2)] = rd2; ReplaceByMov32(*op, rd); return true; } else { return false; } }