diff options
| author | 2025-09-06 00:48:38 +1000 | |
|---|---|---|
| committer | 2025-09-06 00:48:38 +1000 | |
| commit | 13185675cc59db8fceb5246d6ce15c2df00ac6bc (patch) | |
| tree | f121e822b27cd2361a558b3ce16be9394f718181 | |
| parent | 2cf970d4429cda5abc17eba13a1ff886efdae685 (diff) | |
cpu/drcbearm64.cpp: Optimised shift and rotate operations.
* Don't calculate carry flag when not requested.
* Don't calculate zero and sign flags when only carry is requested.
* Avoid branches when calculating carry, optimise for non-zero shift.
* Improved strategy for selecting working registers.
| -rw-r--r-- | src/devices/cpu/drcbearm64.cpp | 152 |
1 files changed, 79 insertions, 73 deletions
diff --git a/src/devices/cpu/drcbearm64.cpp b/src/devices/cpu/drcbearm64.cpp index 7411f03ef5c..045a7954896 100644 --- a/src/devices/cpu/drcbearm64.cpp +++ b/src/devices/cpu/drcbearm64.cpp @@ -1408,83 +1408,79 @@ void drcbe_arm64::set_flags(a64::Assembler &a) a.and_(FLAGS_REG, FLAGS_REG, TEMP_REG2); } -void drcbe_arm64::calculate_carry_shift_left(a64::Assembler &a, const a64::Gp ®, const a64::Gp &shift, int maxBits) +inline void drcbe_arm64::calculate_carry_shift_left(a64::Assembler &a, const a64::Gp ®, const a64::Gp &shift, int maxBits) { m_carry_state = carry_state::POISON; - Label calc = a.newLabel(); - Label end = a.newLabel(); - - a.cbnz(shift, calc); - store_carry_reg(a, a64::xzr); - a.b(end); - - a.bind(calc); const a64::Gp scratch = select_register(SCRATCH_REG1, reg.isGpW() ? 4 : 8); + const a64::Gp zero = select_register(a64::xzr, reg.isGpW() ? 4 : 8); + + a.tst(shift, shift); - // carry = ((PARAM1 << (shift - 1)) >> maxBits) & 1 + // scratch = ((PARAM1 << (shift - 1)) >> maxBits) & 1 a.movz(scratch, maxBits + 1); a.sub(scratch, scratch, shift); a.lsr(scratch, reg, scratch); - store_carry_reg(a, scratch); - a.bind(end); + a.csel(scratch, zero, scratch, a64::CondCode::kZero); + store_carry_reg(a, scratch); } -void drcbe_arm64::calculate_carry_shift_left_imm(a64::Assembler &a, const a64::Gp ®, const int shift, int maxBits) +inline void drcbe_arm64::calculate_carry_shift_left_imm(a64::Assembler &a, const a64::Gp ®, const int shift, int maxBits) { m_carry_state = carry_state::POISON; if (shift == 0) { store_carry_reg(a, a64::xzr); - return; } + else + { + const a64::Gp scratch = select_register(SCRATCH_REG1, reg.isGpW() ? 4 : 8); - const a64::Gp scratch = select_register(SCRATCH_REG1, reg.isGpW() ? 4 : 8); - - // carry = ((PARAM1 << (shift - 1)) >> maxBits) & 1 - a.lsr(scratch, reg, maxBits + 1 - shift); - store_carry_reg(a, scratch); + // carry = ((PARAM1 << (shift - 1)) >> maxBits) & 1 + a.lsr(scratch, reg, maxBits + 1 - shift); + store_carry_reg(a, scratch); + } } -void drcbe_arm64::calculate_carry_shift_right(a64::Assembler &a, const a64::Gp ®, const a64::Gp &shift) +inline void drcbe_arm64::calculate_carry_shift_right(a64::Assembler &a, const a64::Gp ®, const a64::Gp &shift) { m_carry_state = carry_state::POISON; - Label calc = a.newLabel(); - Label end = a.newLabel(); - - a.cbnz(shift, calc); - store_carry_reg(a, a64::xzr); - a.b(end); - - a.bind(calc); const a64::Gp scratch = select_register(SCRATCH_REG1, reg.isGpW() ? 4 : 8); + const a64::Gp zero = select_register(a64::xzr, reg.isGpW() ? 4 : 8); + + a.tst(shift, shift); - // carry = (PARAM1 >> (shift - 1)) & 1 + // scrach = (PARAM1 >> (shift - 1)) & 1 a.sub(scratch, shift, 1); a.lsr(scratch, reg, scratch); - store_carry_reg(a, scratch); - a.bind(end); + a.csel(scratch, zero, scratch, a64::CondCode::kZero); + store_carry_reg(a, scratch); } -void drcbe_arm64::calculate_carry_shift_right_imm(a64::Assembler &a, const a64::Gp ®, const int shift) +inline void drcbe_arm64::calculate_carry_shift_right_imm(a64::Assembler &a, const a64::Gp ®, const int shift) { m_carry_state = carry_state::POISON; if (shift == 0) { store_carry_reg(a, a64::xzr); - return; } + else if (shift == 1) + { + store_carry_reg(a, reg); + } + else + { + const a64::Gp scratch = select_register(SCRATCH_REG1, reg.isGpW() ? 4 : 8); - const a64::Gp scratch = select_register(SCRATCH_REG1, reg.isGpW() ? 4 : 8); - - // carry = (PARAM1 >> (shift - 1)) & 1 - a.lsr(scratch, reg, shift - 1); - store_carry_reg(a, scratch); + // carry = (PARAM1 >> (shift - 1)) & 1 + a.lsr(scratch, reg, shift - 1); + store_carry_reg(a, scratch); + } } drcbe_arm64::drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits) @@ -4316,53 +4312,62 @@ template <a64::Inst::Id Opcode> void drcbe_arm64::op_shift(a64::Assembler &a, co be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); - size_t const maxBits = inst.size() * 8 - 1; + size_t const maxBits = (inst.size() * 8) - 1; + bool const rightShift = (Opcode == a64::Inst::kIdRor) || (Opcode == a64::Inst::kIdLsr) || (Opcode == a64::Inst::kIdAsr); - // If possible it's more optimal to write directly to the dst register, - // but be careful to not overwrite one of the source values since they're needed for later calculations - bool can_use_dst_reg = dstp.is_int_register(); - if (can_use_dst_reg && src1p.is_int_register()) - can_use_dst_reg = src1p.ireg() != dstp.ireg(); - if (can_use_dst_reg && src2p.is_int_register()) - can_use_dst_reg = src2p.ireg() != dstp.ireg(); + const a64::Gp dst = dstp.select_register(TEMP_REG1, inst.size()); + const a64::Gp src = src1p.select_register(dst, inst.size()); + const a64::Gp scratch = select_register(TEMP_REG2, inst.size()); + const a64::Gp shift = src2p.select_register(scratch, inst.size()); - const a64::Gp src = src1p.select_register(TEMP_REG1, inst.size()); - const a64::Gp shift = src2p.select_register(TEMP_REG2, inst.size()); - const a64::Gp dst = can_use_dst_reg ? dstp.select_register(TEMP_REG3, inst.size()) : select_register(TEMP_REG3, inst.size()); - const a64::Gp scratch = select_register(FUNC_SCRATCH_REG, inst.size()); + if (src2p.is_immediate()) + { + const auto shift = src2p.immediate() & maxBits; - mov_reg_param(a, inst.size(), src, src1p); + if (!shift) + { + if (inst.flags() & FLAG_C) + store_carry_reg(a, a64::xzr); - if (src2p.is_immediate() && is_valid_immediate(src2p.immediate(), (inst.size() == 8) ? 5 : 4)) - { - const auto shift = src2p.immediate() % (inst.size() * 8); + mov_reg_param(a, inst.size(), dst, src1p); + } + else + { + mov_reg_param(a, inst.size(), src, src1p); - a.emit(Opcode, dst, src, shift); + if (inst.flags() & FLAG_C) + { + if (rightShift) + calculate_carry_shift_right_imm(a, src, shift); + else + calculate_carry_shift_left_imm(a, src, shift, maxBits); + } - if (Opcode == a64::Inst::kIdRor || Opcode == a64::Inst::kIdLsr || Opcode == a64::Inst::kIdAsr) - calculate_carry_shift_right_imm(a, src, shift); - else if (Opcode == a64::Inst::kIdLsl) - calculate_carry_shift_left_imm(a, src, shift, maxBits); + a.emit(Opcode, dst, src, shift); + } } else { mov_reg_param(a, inst.size(), shift, src2p); + a.and_(scratch, shift, maxBits); + + mov_reg_param(a, inst.size(), src, src1p); - a.and_(scratch, shift, inst.size() * 8 - 1); + if (inst.flags() & FLAG_C) + { + if (rightShift) + calculate_carry_shift_right(a, src, scratch); + else + calculate_carry_shift_left(a, src, scratch, maxBits); + } a.emit(Opcode, dst, src, scratch); - - if (Opcode == a64::Inst::kIdRor || Opcode == a64::Inst::kIdLsr || Opcode == a64::Inst::kIdAsr) - calculate_carry_shift_right(a, src, scratch); - else if (Opcode == a64::Inst::kIdLsl) - calculate_carry_shift_left(a, src, scratch, maxBits); } - if (inst.flags()) - { + if (inst.flags() & (FLAG_Z | FLAG_S)) a.tst(dst, dst); + if (inst.flags()) m_carry_state = carry_state::POISON; - } // save dst after using inputs for calculations so the registers have no chance of being overwritten mov_param_reg(a, inst.size(), dstp, dst); @@ -4408,7 +4413,8 @@ void drcbe_arm64::op_rol(a64::Assembler &a, const uml::instruction &inst) a.ror(output, param, s2); } - calculate_carry_shift_left_imm(a, param, s, maxBits); + if (inst.flags() & FLAG_C) + calculate_carry_shift_left_imm(a, param, s, maxBits); } else { @@ -4420,14 +4426,14 @@ void drcbe_arm64::op_rol(a64::Assembler &a, const uml::instruction &inst) a.sub(scratch, scratch, scratch2); a.ror(output, param, scratch); - calculate_carry_shift_left(a, param, scratch2, maxBits); + if (inst.flags() & FLAG_C) + calculate_carry_shift_left(a, param, scratch2, maxBits); } - if (inst.flags()) - { + if (inst.flags() & (FLAG_Z | FLAG_S)) a.tst(output, output); + if (inst.flags()) m_carry_state = carry_state::POISON; - } mov_param_reg(a, inst.size(), dstp, output); } |
