summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2025-09-06 00:48:38 +1000
committer Vas Crabb <vas@vastheman.com>2025-09-06 00:48:38 +1000
commit13185675cc59db8fceb5246d6ce15c2df00ac6bc (patch)
treef121e822b27cd2361a558b3ce16be9394f718181
parent2cf970d4429cda5abc17eba13a1ff886efdae685 (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.cpp152
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 &reg, const a64::Gp &shift, int maxBits)
+inline void drcbe_arm64::calculate_carry_shift_left(a64::Assembler &a, const a64::Gp &reg, 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 &reg, const int shift, int maxBits)
+inline void drcbe_arm64::calculate_carry_shift_left_imm(a64::Assembler &a, const a64::Gp &reg, 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 &reg, const a64::Gp &shift)
+inline void drcbe_arm64::calculate_carry_shift_right(a64::Assembler &a, const a64::Gp &reg, 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 &reg, const int shift)
+inline void drcbe_arm64::calculate_carry_shift_right_imm(a64::Assembler &a, const a64::Gp &reg, 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);
}