summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/drcbearm64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/drcbearm64.cpp')
-rw-r--r--src/devices/cpu/drcbearm64.cpp673
1 files changed, 467 insertions, 206 deletions
diff --git a/src/devices/cpu/drcbearm64.cpp b/src/devices/cpu/drcbearm64.cpp
index 980bd607ae0..e0d58502996 100644
--- a/src/devices/cpu/drcbearm64.cpp
+++ b/src/devices/cpu/drcbearm64.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:windyfairy
+// copyright-holders:windyfairy, Vas Crabb
/***************************************************************************
Register use:
@@ -379,16 +379,6 @@ inline void get_unordered(a64::Assembler &a, const a64::Gp &reg)
a.ubfx(reg.x(), FLAGS_REG, FLAG_BIT_U, 1);
}
-inline void check_unordered_condition(a64::Assembler &a, uml::condition_t cond, const Label &condition_met, bool not_equal)
-{
- assert((cond == uml::COND_U) || (cond == uml::COND_NU));
-
- const a64::Inst::Id opcode = ((uml::COND_U == cond) == not_equal) ? a64::Inst::kIdCbz : a64::Inst::kIdCbnz;
-
- get_unordered(a, SCRATCH_REG1);
- a.emit(opcode, SCRATCH_REG1, condition_met);
-}
-
inline void store_carry_reg(a64::Assembler &a, const a64::Gp &reg)
{
a.bfi(FLAGS_REG, reg.x(), FLAG_BIT_C, 1);
@@ -417,6 +407,13 @@ public:
virtual bool logging() const noexcept override { return false; }
private:
+ enum class carry_state
+ {
+ POISON, // does not correspond to UML carry flag
+ CANONICAL, // corresponds directly to UML carry flag
+ LOGICAL // logical borrow state
+ };
+
class be_parameter
{
static inline constexpr int REG_MAX = 30;
@@ -593,18 +590,20 @@ private:
void emit_float_ldr_mem(asmjit::a64::Assembler &a, const asmjit::a64::Vec &reg, const void *ptr) const;
void emit_float_str_mem(asmjit::a64::Assembler &a, const asmjit::a64::Vec &reg, const void *ptr) const;
+ void emit_skip(a64::Assembler &a, uml::condition_t cond, Label &skip);
+
void emit_memaccess_setup(asmjit::a64::Assembler &a, const be_parameter &addrp, const memory_accessors &accessors, const address_space::specific_access_info::side &side) const;
void emit_narrow_memwrite(asmjit::a64::Assembler &a, const be_parameter &addrp, const parameter &spacesizep, const memory_accessors &accessors) const;
- void store_carry(asmjit::a64::Assembler &a, bool inverted = false) const;
- void load_carry(asmjit::a64::Assembler &a, bool inverted = false) const;
- void set_flags(asmjit::a64::Assembler &a) const;
+ void store_carry(asmjit::a64::Assembler &a, bool inverted = false);
+ void load_carry(asmjit::a64::Assembler &a, bool inverted = false);
+ void set_flags(asmjit::a64::Assembler &a);
- void calculate_carry_shift_left(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const asmjit::a64::Gp &shift, int maxBits) const;
- void calculate_carry_shift_left_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const int shift, int maxBits) const;
+ void calculate_carry_shift_left(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const asmjit::a64::Gp &shift, int maxBits);
+ void calculate_carry_shift_left_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const int shift, int maxBits);
- void calculate_carry_shift_right(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const asmjit::a64::Gp &shift) const;
- void calculate_carry_shift_right_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const int shift) const;
+ void calculate_carry_shift_right(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const asmjit::a64::Gp &shift);
+ void calculate_carry_shift_right_imm(asmjit::a64::Assembler &a, const asmjit::a64::Gp &reg, const int shift);
void mov_float_reg_param(asmjit::a64::Assembler &a, uint32_t regsize, asmjit::a64::Vec const &dst, const be_parameter &src) const;
void mov_float_param_param(asmjit::a64::Assembler &a, uint32_t regsize, const be_parameter &dst, const be_parameter &src) const;
@@ -623,6 +622,7 @@ private:
drc_hash_table m_hash;
drc_map_variables m_map;
FILE *m_log_asmjit;
+ carry_state m_carry_state;
arm64_entry_point_func m_entry;
drccodeptr m_exit;
@@ -955,6 +955,45 @@ void drcbe_arm64::emit_strh_mem(a64::Assembler &a, const a64::Gp &reg, const voi
void drcbe_arm64::emit_float_ldr_mem(a64::Assembler &a, const a64::Vec &reg, const void *ptr) const { emit_ldr_str_base_mem(a, a64::Inst::kIdLdr_v, reg, reg.isVecS() ? 2 : 3, ptr); }
void drcbe_arm64::emit_float_str_mem(a64::Assembler &a, const a64::Vec &reg, const void *ptr) const { emit_ldr_str_base_mem(a, a64::Inst::kIdStr_v, reg, reg.isVecS() ? 2 : 3, ptr); }
+void drcbe_arm64::emit_skip(a64::Assembler &a, uml::condition_t cond, Label &skip)
+{
+ // Nothing to do if the instruction is unconditional
+ if (cond == uml::COND_ALWAYS)
+ return;
+
+ // Branch to the skip point if the condition is not met
+ skip = a.newLabel();
+ switch (cond)
+ {
+ case uml::COND_U:
+ a.tbz(FLAGS_REG, FLAG_BIT_U, skip);
+ break;
+ case uml::COND_NU:
+ a.tbnz(FLAGS_REG, FLAG_BIT_U, skip);
+ break;
+ case uml::COND_C:
+ case uml::COND_NC:
+ switch (m_carry_state)
+ {
+ case carry_state::CANONICAL:
+ a.b(ARM_CONDITION(cond), skip);
+ break;
+ case carry_state::LOGICAL:
+ a.b(ARM_NOT_CONDITION(cond), skip);
+ break;
+ default:
+ a.emit((cond == uml::COND_C) ? a64::Inst::kIdTbz : a64::Inst::kIdTbnz, FLAGS_REG, FLAG_BIT_C, skip);
+ }
+ break;
+ case uml::COND_A:
+ case uml::COND_BE:
+ load_carry(a, true);
+ [[fallthrough]];
+ default:
+ a.b(ARM_NOT_CONDITION(cond), skip);
+ }
+}
+
void drcbe_arm64::emit_memaccess_setup(asmjit::a64::Assembler &a, const be_parameter &addrp, const memory_accessors &accessors, const address_space::specific_access_info::side &side) const
{
auto const addrreg = (accessors.no_mask || accessors.mask_simple) ? REG_PARAM2 : a64::x6;
@@ -1255,8 +1294,10 @@ void drcbe_arm64::call_arm_addr(a64::Assembler &a, const void *offs) const
}
}
-void drcbe_arm64::store_carry(a64::Assembler &a, bool inverted) const
+void drcbe_arm64::store_carry(a64::Assembler &a, bool inverted)
{
+ m_carry_state = inverted ? carry_state::LOGICAL : carry_state::CANONICAL;
+
if (inverted)
a.cset(SCRATCH_REG1, a64::CondCode::kCC);
else
@@ -1265,20 +1306,28 @@ void drcbe_arm64::store_carry(a64::Assembler &a, bool inverted) const
store_carry_reg(a, SCRATCH_REG1);
}
-void drcbe_arm64::load_carry(a64::Assembler &a, bool inverted) const
+void drcbe_arm64::load_carry(a64::Assembler &a, bool inverted)
{
- a.mrs(SCRATCH_REG1, a64::Predicate::SysReg::kNZCV);
- a.bfi(SCRATCH_REG1, FLAGS_REG, 29, 1);
+ const carry_state desired = inverted ? carry_state::LOGICAL : carry_state::CANONICAL;
+ if (desired != m_carry_state)
+ {
+ m_carry_state = desired;
- if (inverted)
- a.eor(SCRATCH_REG1, SCRATCH_REG1, 1 << 29);
+ a.mrs(SCRATCH_REG1, a64::Predicate::SysReg::kNZCV);
+ a.bfi(SCRATCH_REG1, FLAGS_REG, 29, 1);
- a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
+ if (inverted)
+ a.eor(SCRATCH_REG1, SCRATCH_REG1, 1 << 29);
+
+ a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
+ }
}
-void drcbe_arm64::set_flags(asmjit::a64::Assembler &a) const
+void drcbe_arm64::set_flags(asmjit::a64::Assembler &a)
{
// Set native condition codes after loading flags register
+ m_carry_state = carry_state::POISON; // TODO: take a bet they'll try a conditional branch and set the C flag?
+
a.mrs(TEMP_REG1, a64::Predicate::SysReg::kNZCV);
a.and_(TEMP_REG2, FLAGS_REG, 0b1100); // zero + sign
@@ -1292,8 +1341,10 @@ void drcbe_arm64::set_flags(asmjit::a64::Assembler &a) const
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) const
+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();
@@ -1313,8 +1364,10 @@ void drcbe_arm64::calculate_carry_shift_left(a64::Assembler &a, const a64::Gp &r
a.bind(end);
}
-void drcbe_arm64::calculate_carry_shift_left_imm(a64::Assembler &a, const a64::Gp &reg, const int shift, int maxBits) const
+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);
@@ -1328,8 +1381,10 @@ void drcbe_arm64::calculate_carry_shift_left_imm(a64::Assembler &a, const a64::G
store_carry_reg(a, scratch);
}
-void drcbe_arm64::calculate_carry_shift_right(a64::Assembler &a, const a64::Gp &reg, const a64::Gp &shift) const
+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();
@@ -1348,8 +1403,10 @@ void drcbe_arm64::calculate_carry_shift_right(a64::Assembler &a, const a64::Gp &
a.bind(end);
}
-void drcbe_arm64::calculate_carry_shift_right_imm(a64::Assembler &a, const a64::Gp &reg, const int shift) const
+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);
@@ -1368,6 +1425,7 @@ drcbe_arm64::drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cach
, m_hash(cache, modes, addrbits, ignorebits)
, m_map(cache, 0xaaaaaaaa5555)
, m_log_asmjit(nullptr)
+ , m_carry_state(carry_state::POISON)
, m_entry(nullptr)
, m_exit(nullptr)
, m_nocode(nullptr)
@@ -1503,8 +1561,9 @@ void drcbe_arm64::reset()
// reset our hash tables
m_hash.reset();
-
m_hash.set_default_codeptr(m_nocode);
+
+ m_carry_state = carry_state::POISON;
}
int drcbe_arm64::execute(code_handle &entry)
@@ -1526,6 +1585,7 @@ void drcbe_arm64::generate(drcuml_block &block, const instruction *instlist, uin
// tell all of our utility objects that a block is beginning
m_hash.block_begin(block, instlist, numinst);
m_map.block_begin(block);
+ m_carry_state = carry_state::POISON;
// compute the base by aligning the cache top to a cache line
auto [err, linesize] = osd_get_cache_line_size();
@@ -1618,6 +1678,8 @@ void drcbe_arm64::op_handle(a64::Assembler &a, const uml::instruction &inst)
assert(inst.numparams() == 1);
assert(inst.param(0).is_code_handle());
+ m_carry_state = carry_state::POISON;
+
// make a label for documentation
Label handle = a.newNamedLabel(inst.param(0).handle().string());
a.bind(handle);
@@ -1642,6 +1704,8 @@ void drcbe_arm64::op_hash(a64::Assembler &a, const uml::instruction &inst)
assert(inst.param(0).is_immediate());
assert(inst.param(1).is_immediate());
+ m_carry_state = carry_state::POISON;
+
const uint64_t mode = inst.param(0).immediate();
const uint64_t pc = inst.param(1).immediate();
@@ -1655,6 +1719,8 @@ void drcbe_arm64::op_label(a64::Assembler &a, const uml::instruction &inst)
assert(inst.numparams() == 1);
assert(inst.param(0).is_code_label());
+ m_carry_state = carry_state::POISON;
+
std::string labelName = util::string_format("PC$%x", inst.param(0).label());
Label label = a.labelByName(labelName.c_str());
if (!label.isValid())
@@ -1696,6 +1762,8 @@ void drcbe_arm64::op_break(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
static const char *const message = "break from drc";
get_imm_relative(a, REG_PARAM1, (uintptr_t)message);
call_arm_addr(a, (const void *)&osd_break_into_debugger);
@@ -1709,6 +1777,8 @@ void drcbe_arm64::op_debug(a64::Assembler &a, const uml::instruction &inst)
if (m_device.machine().debug_flags & DEBUG_FLAG_ENABLED)
{
+ m_carry_state = carry_state::POISON;
+
const a64::Gp temp = TEMP_REG1.w();
be_parameter pcp(*this, inst.param(0), PTYPE_MRI);
@@ -1735,23 +1805,14 @@ void drcbe_arm64::op_exit(a64::Assembler &a, const uml::instruction &inst)
be_parameter retp(*this, inst.param(0), PTYPE_MRI);
+ Label skip;
+ emit_skip(a, inst.condition(), skip);
+
mov_reg_param(a, 4, REG_PARAM1, retp);
+ a.b(m_exit);
- if (inst.condition() == uml::COND_ALWAYS)
- {
- a.b(m_exit);
- }
- else if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- {
- Label skip = a.newLabel();
- check_unordered_condition(a, inst.condition(), skip, false);
- a.b(m_exit);
+ if (inst.condition() != uml::COND_ALWAYS)
a.bind(skip);
- }
- else
- {
- a.b(ARM_CONDITION(inst.condition()), m_exit);
- }
}
void drcbe_arm64::op_hashjmp(a64::Assembler &a, const uml::instruction &inst)
@@ -1859,6 +1920,8 @@ void drcbe_arm64::op_hashjmp(a64::Assembler &a, const uml::instruction &inst)
emit_ldr_mem(a, SCRATCH_REG1, targetptr);
a.blr(SCRATCH_REG1);
}
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_jmp(a64::Assembler &a, const uml::instruction &inst)
@@ -1878,17 +1941,60 @@ void drcbe_arm64::op_jmp(a64::Assembler &a, const uml::instruction &inst)
if (inst.condition() == uml::COND_ALWAYS)
{
a.b(jmptarget);
+ return;
}
- else if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- {
- check_unordered_condition(a, inst.condition(), jmptarget, false);
- }
- else
+
+ const bool bound = a.code()->isLabelBound(jmptarget);
+ const uint64_t targetoffs = a.code()->baseAddress() + a.code()->labelOffset(jmptarget);
+ const uint64_t codeoffs = a.code()->baseAddress() + a.offset();
+ const bool tbnzrange = bound && is_valid_immediate_signed(int64_t(targetoffs) - codeoffs, 14 + 2);
+
+ switch (inst.condition())
{
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
+ case uml::COND_U:
+ case uml::COND_NU:
+ if (tbnzrange)
+ {
+ const a64::Inst::Id opcode = (inst.condition() == uml::COND_U) ? a64::Inst::kIdTbnz : a64::Inst::kIdTbz;
+ a.emit(opcode, FLAGS_REG, FLAG_BIT_U, jmptarget);
+ }
+ else
+ {
+ const a64::Inst::Id opcode = (inst.condition() == uml::COND_U) ? a64::Inst::kIdCbnz : a64::Inst::kIdCbz;
+ get_unordered(a, SCRATCH_REG1);
+ a.emit(opcode, SCRATCH_REG1, jmptarget);
+ }
+ break;
+ case uml::COND_C:
+ case uml::COND_NC:
+ switch (m_carry_state)
+ {
+ case carry_state::CANONICAL:
+ a.b(ARM_NOT_CONDITION(inst.condition()), jmptarget);
+ break;
+ case carry_state::LOGICAL:
+ a.b(ARM_CONDITION(inst.condition()), jmptarget);
+ break;
+ default:
+ if (tbnzrange)
+ {
+ const a64::Inst::Id opcode = (inst.condition() == uml::COND_C) ? a64::Inst::kIdTbnz : a64::Inst::kIdTbz;
+ a.emit(opcode, FLAGS_REG, FLAG_BIT_C, jmptarget);
+ }
+ else
+ {
+ const a64::Inst::Id opcode = (inst.condition() == uml::COND_C) ? a64::Inst::kIdCbnz : a64::Inst::kIdCbz;
+ get_carry(a, SCRATCH_REG1);
+ a.emit(opcode, SCRATCH_REG1, jmptarget);
+ }
+ }
+ break;
+ case uml::COND_A:
+ case uml::COND_BE:
load_carry(a, true);
-
- a.b(ARM_CONDITION(inst.condition()), jmptarget);
+ [[fallthrough]];
+ default:
+ a.b(ARM_CONDITION(inst.condition()), jmptarget);
}
}
@@ -1903,22 +2009,7 @@ void drcbe_arm64::op_exh(a64::Assembler &a, const uml::instruction &inst)
// perform the exception processing
Label no_exception;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- no_exception = a.newLabel();
-
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- {
- check_unordered_condition(a, inst.condition(), no_exception, true);
- }
- else
- {
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
-
- a.b(ARM_NOT_CONDITION(inst.condition()), no_exception);
- }
- }
+ emit_skip(a, inst.condition(), no_exception);
mov_mem_param(a, 4, &m_state.exp, exp);
@@ -1935,6 +2026,8 @@ void drcbe_arm64::op_exh(a64::Assembler &a, const uml::instruction &inst)
if (inst.condition() != uml::COND_ALWAYS)
a.bind(no_exception);
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_callh(a64::Assembler &a, const uml::instruction &inst)
@@ -1947,22 +2040,7 @@ void drcbe_arm64::op_callh(a64::Assembler &a, const uml::instruction &inst)
assert(handp.is_code_handle());
Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.newLabel();
-
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- {
- check_unordered_condition(a, inst.condition(), skip, true);
- }
- else
- {
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
-
- a.b(ARM_NOT_CONDITION(inst.condition()), skip);
- }
- }
+ emit_skip(a, inst.condition(), skip);
drccodeptr *const targetptr = handp.handle().codeptr_addr();
if (*targetptr != nullptr)
@@ -1977,6 +2055,8 @@ void drcbe_arm64::op_callh(a64::Assembler &a, const uml::instruction &inst)
if (inst.condition() != uml::COND_ALWAYS)
a.bind(skip);
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_ret(a64::Assembler &a, const uml::instruction &inst)
@@ -1987,22 +2067,7 @@ void drcbe_arm64::op_ret(a64::Assembler &a, const uml::instruction &inst)
assert(inst.numparams() == 0);
Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.newLabel();
-
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- {
- check_unordered_condition(a, inst.condition(), skip, true);
- }
- else
- {
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
-
- a.b(ARM_NOT_CONDITION(inst.condition()), skip);
- }
- }
+ emit_skip(a, inst.condition(), skip);
a.ldp(a64::x29, a64::x30, arm::Mem(a64::sp).post(16));
a.ret(a64::x30);
@@ -2022,22 +2087,7 @@ void drcbe_arm64::op_callc(a64::Assembler &a, const uml::instruction &inst)
be_parameter paramp(*this, inst.param(1), PTYPE_M);
Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
- {
- skip = a.newLabel();
-
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- {
- check_unordered_condition(a, inst.condition(), skip, true);
- }
- else
- {
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
-
- a.b(ARM_NOT_CONDITION(inst.condition()), skip);
- }
- }
+ emit_skip(a, inst.condition(), skip);
emit_str_mem(a, FLAGS_REG.w(), &m_near.emulated_flags);
@@ -2049,6 +2099,8 @@ void drcbe_arm64::op_callc(a64::Assembler &a, const uml::instruction &inst)
if (inst.condition() != uml::COND_ALWAYS)
a.bind(skip);
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_recover(a64::Assembler &a, const uml::instruction &inst)
@@ -2057,6 +2109,8 @@ void drcbe_arm64::op_recover(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter dstp(*this, inst.param(0), PTYPE_MR);
a.ldr(REG_PARAM2, arm::Mem(a64::x29, -8)); // saved LR (x30) from first level CALLH/EXH or failed hash jump
@@ -2523,6 +2577,8 @@ void drcbe_arm64::op_read(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter dstp(*this, inst.param(0), PTYPE_MR);
be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
const parameter &spacesizep = inst.param(2);
@@ -2580,6 +2636,8 @@ void drcbe_arm64::op_readm(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter dstp(*this, inst.param(0), PTYPE_MR);
be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
be_parameter maskp(*this, inst.param(2), PTYPE_MRI);
@@ -2640,6 +2698,8 @@ void drcbe_arm64::op_write(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
const parameter &spacesizep = inst.param(2);
@@ -2703,6 +2763,8 @@ void drcbe_arm64::op_writem(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
be_parameter maskp(*this, inst.param(2), PTYPE_MRI);
@@ -2769,6 +2831,8 @@ void drcbe_arm64::op_carry(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_flags(inst, FLAG_C);
+ m_carry_state = carry_state::POISON;
+
be_parameter srcp(*this, inst.param(0), PTYPE_MRI);
be_parameter bitp(*this, inst.param(1), PTYPE_MRI);
@@ -2831,18 +2895,37 @@ void drcbe_arm64::op_set(a64::Assembler &a, const uml::instruction &inst)
const a64::Gp dst = dstp.select_register(TEMP_REG1, inst.size());
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
-
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
+ switch (inst.condition())
{
- get_unordered(a, dst);
-
- if (inst.condition() == uml::COND_NU)
- a.eor(dst, dst, 1);
+ case uml::COND_U:
+ case uml::COND_NU:
+ get_unordered(a, dst);
+ if (inst.condition() == uml::COND_NU)
+ a.eor(dst, dst, 1);
+ break;
+ case uml::COND_C:
+ case uml::COND_NC:
+ switch (m_carry_state)
+ {
+ case carry_state::CANONICAL:
+ a.cset(dst, ARM_NOT_CONDITION(inst.condition()));
+ break;
+ case carry_state::LOGICAL:
+ a.cset(dst, ARM_CONDITION(inst.condition()));
+ break;
+ default:
+ get_carry(a, dst);
+ if (inst.condition() == uml::COND_NC)
+ a.eor(dst, dst, 1);
+ }
+ break;
+ case uml::COND_A:
+ case uml::COND_BE:
+ load_carry(a, true);
+ [[fallthrough]];
+ default:
+ a.cset(dst, ARM_CONDITION(inst.condition()));
}
- else
- a.cset(dst, ARM_CONDITION(inst.condition()));
mov_param_reg(a, inst.size(), dstp, dst);
}
@@ -2856,27 +2939,92 @@ void drcbe_arm64::op_mov(a64::Assembler &a, const uml::instruction &inst)
be_parameter dstp(*this, inst.param(0), PTYPE_MR);
be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
- // add a conditional branch unless a conditional move is possible
- // TODO: optimise to use csel if the source and destination are both kept in host registers
- Label skip;
+ // decide whether a conditional select will be efficient
+ bool usesel = dstp.is_int_register() && (srcp.is_int_register() || (srcp.is_immediate() && is_simple_mov_immediate(srcp.immediate(), inst.size())));
+ switch (inst.condition())
+ {
+ case uml::COND_ALWAYS:
+ case uml::COND_U:
+ case uml::COND_NU:
+ usesel = false;
+ break;
+ case uml::COND_C:
+ case uml::COND_NC:
+ switch (m_carry_state)
+ {
+ case carry_state::CANONICAL:
+ case carry_state::LOGICAL:
+ break;
+ default:
+ usesel = false;
+ }
+ break;
+ default:
+ break;
+ }
- if (inst.condition() != uml::COND_ALWAYS)
+ if (usesel)
{
- skip = a.newLabel();
+ const bool srczero = srcp.is_immediate_value(0);
+ const bool srcone = srcp.is_immediate_value(1);
+ const bool srcnegone = srcp.is_immediate_value(util::make_bitmask<uint64_t>(inst.size() * 8));
+ const bool srcspecial = srczero || srcone || srcnegone;
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
+ const a64::Gp dst = dstp.select_register(TEMP_REG1, inst.size());
+ const a64::Gp src = srcspecial ? select_register(a64::xzr, inst.size()) : srcp.select_register(TEMP_REG2, inst.size());
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- check_unordered_condition(a, inst.condition(), skip, true);
- else
- a.b(ARM_NOT_CONDITION(inst.condition()), skip);
+ mov_reg_param(a, inst.size(), dst, dstp);
+ if (!srcspecial)
+ mov_reg_param(a, inst.size(), src, srcp);
+
+ switch (inst.condition())
+ {
+ case uml::COND_C:
+ case uml::COND_NC:
+ if (m_carry_state == carry_state::CANONICAL)
+ {
+ if (srcone)
+ a.csinc(dst, dst, src, ARM_CONDITION(inst.condition()));
+ else if (srcnegone)
+ a.csinv(dst, dst, src, ARM_CONDITION(inst.condition()));
+ else
+ a.csel(dst, src, dst, ARM_NOT_CONDITION(inst.condition()));
+ }
+ else
+ {
+ if (srcone)
+ a.csinc(dst, dst, src, ARM_NOT_CONDITION(inst.condition()));
+ else if (srcnegone)
+ a.csinv(dst, dst, src, ARM_NOT_CONDITION(inst.condition()));
+ else
+ a.csel(dst, src, dst, ARM_CONDITION(inst.condition()));
+ }
+ break;
+ case uml::COND_A:
+ case uml::COND_BE:
+ load_carry(a, true);
+ [[fallthrough]];
+ default:
+ if (srcone)
+ a.csinc(dst, dst, src, ARM_NOT_CONDITION(inst.condition()));
+ else if (srcnegone)
+ a.csinv(dst, dst, src, ARM_NOT_CONDITION(inst.condition()));
+ else
+ a.csel(dst, src, dst, ARM_CONDITION(inst.condition()));
+ }
+
+ mov_param_reg(a, inst.size(), dstp, dst);
}
+ else
+ {
+ Label skip;
+ emit_skip(a, inst.condition(), skip);
- mov_param_param(a, inst.size(), dstp, srcp);
+ mov_param_param(a, inst.size(), dstp, srcp);
- if (inst.condition() != uml::COND_ALWAYS)
- a.bind(skip);
+ if (inst.condition() != uml::COND_ALWAYS)
+ a.bind(skip);
+ }
}
void drcbe_arm64::op_sext(a64::Assembler &a, const uml::instruction &inst)
@@ -2927,7 +3075,10 @@ void drcbe_arm64::op_sext(a64::Assembler &a, const uml::instruction &inst)
}
if (inst.flags())
+ {
a.tst(dstreg, dstreg);
+ m_carry_state = carry_state::POISON;
+ }
}
void drcbe_arm64::op_roland(a64::Assembler &a, const uml::instruction &inst)
@@ -2954,7 +3105,10 @@ void drcbe_arm64::op_roland(a64::Assembler &a, const uml::instruction &inst)
mov_param_reg(a, inst.size(), dstp, zero);
if (inst.flags())
+ {
a.tst(zero, zero);
+ m_carry_state = carry_state::POISON;
+ }
return;
}
@@ -3070,6 +3224,8 @@ void drcbe_arm64::op_roland(a64::Assembler &a, const uml::instruction &inst)
{
if (optimized)
a.tst(output, output);
+
+ m_carry_state = carry_state::POISON;
}
}
@@ -3095,6 +3251,7 @@ void drcbe_arm64::op_rolins(a64::Assembler &a, const uml::instruction &inst)
dst = dstp.select_register(TEMP_REG2, inst.size());
mov_reg_param(a, inst.size(), dst, dstp);
a.tst(dst, dst);
+ m_carry_state = carry_state::POISON;
}
return;
@@ -3274,7 +3431,10 @@ void drcbe_arm64::op_rolins(a64::Assembler &a, const uml::instruction &inst)
mov_param_reg(a, inst.size(), dstp, dst);
if (inst.flags())
+ {
a.tst(dst, dst);
+ m_carry_state = carry_state::POISON;
+ }
}
template <bool CarryIn> void drcbe_arm64::op_add(a64::Assembler &a, const uml::instruction &inst)
@@ -3292,8 +3452,6 @@ template <bool CarryIn> void drcbe_arm64::op_add(a64::Assembler &a, const uml::i
be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
const a64::Gp zero = select_register(a64::xzr, inst.size());
- const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
- const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size());
const a64::Gp output = dstp.select_register(TEMP_REG3, inst.size());
if (CarryIn)
@@ -3316,19 +3474,22 @@ template <bool CarryIn> void drcbe_arm64::op_add(a64::Assembler &a, const uml::i
}
else if (!CarryIn && src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate()))
{
- a.emit(opcode, output, zero, src2p.immediate());
+ a.mov(output, zero);
+ a.emit(opcode, output, output, src2p.immediate());
mov_param_reg(a, inst.size(), dstp, output);
}
else if (!CarryIn && src2p.is_immediate() && is_valid_immediate(src2p.immediate(), 24))
{
- a.emit(opcode, output, zero, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
- a.emit(opcode, output, output, src2p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.mov(output, src2p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.emit(opcode, output, output, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
mov_param_reg(a, inst.size(), dstp, output);
}
else
{
- mov_reg_param(a, inst.size(), src2, src2p);
- a.emit(opcode, output, src2, zero);
+ const a64::Gp src = src2p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src2p);
+ a.emit(opcode, output, src, zero);
mov_param_reg(a, inst.size(), dstp, output);
}
}
@@ -3336,52 +3497,64 @@ template <bool CarryIn> void drcbe_arm64::op_add(a64::Assembler &a, const uml::i
{
if (!CarryIn && src1p.is_immediate() && is_valid_immediate_addsub(src1p.immediate()))
{
- a.emit(opcode, output, zero, src1p.immediate());
+ a.mov(output, zero);
+ a.emit(opcode, output, output, src1p.immediate());
mov_param_reg(a, inst.size(), dstp, output);
}
else if (!CarryIn && src1p.is_immediate() && is_valid_immediate(src1p.immediate(), 24))
{
- a.emit(opcode, output, zero, src1p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
- a.emit(opcode, output, output, src1p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.mov(output, src1p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.emit(opcode, output, output, src1p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
mov_param_reg(a, inst.size(), dstp, output);
}
else
{
- mov_reg_param(a, inst.size(), src1, src1p);
- a.emit(opcode, output, src1, zero);
+ const a64::Gp src = src1p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src1p);
+ a.emit(opcode, output, src, zero);
mov_param_reg(a, inst.size(), dstp, output);
}
}
else if (!CarryIn && src1p.is_immediate() && is_valid_immediate_addsub(src1p.immediate()))
{
- mov_reg_param(a, inst.size(), src2, src2p);
- a.emit(opcode, output, src2, src1p.immediate());
+ const a64::Gp src = src2p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src2p);
+ a.emit(opcode, output, src, src1p.immediate());
mov_param_reg(a, inst.size(), dstp, output);
}
else if (!CarryIn && src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate()))
{
- mov_reg_param(a, inst.size(), src1, src1p);
- a.emit(opcode, output, src1, src2p.immediate());
+ const a64::Gp src = src1p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src1p);
+ a.emit(opcode, output, src, src2p.immediate());
mov_param_reg(a, inst.size(), dstp, output);
}
else if (!CarryIn && !inst.flags() && src1p.is_immediate() && is_valid_immediate(src1p.immediate(), 24))
{
- // will still alter flags, but carry and overflow values will be incorrect for this path
- mov_reg_param(a, inst.size(), src2, src2p);
- a.emit(opcode, output, src2, src1p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
- a.emit(opcode, output, output, src1p.immediate() & util::make_bitmask<uint64_t>(12));
+ const a64::Gp src = src2p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src2p);
+ a.emit(opcode, output, src, src1p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.emit(opcode, output, output, src1p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
mov_param_reg(a, inst.size(), dstp, output);
}
else if (!CarryIn && !inst.flags() && src2p.is_immediate() && is_valid_immediate(src2p.immediate(), 24))
{
- // will still alter flags, but carry and overflow values will be incorrect for this path
- mov_reg_param(a, inst.size(), src1, src1p);
- a.emit(opcode, output, src1, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
- a.emit(opcode, output, output, src2p.immediate() & util::make_bitmask<uint64_t>(12));
+ const a64::Gp src = src1p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src1p);
+ a.emit(opcode, output, src, src2p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.emit(opcode, output, output, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
mov_param_reg(a, inst.size(), dstp, output);
}
else
{
+ const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
+ const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size());
+
mov_reg_param(a, inst.size(), src1, src1p);
mov_reg_param(a, inst.size(), src2, src2p);
a.emit(opcode, output, src1, src2);
@@ -3410,7 +3583,6 @@ template <bool CarryIn> void drcbe_arm64::op_sub(a64::Assembler &a, const uml::i
load_carry(a, true);
const a64::Gp zero = select_register(a64::xzr, inst.size());
- const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
const a64::Gp output = dstp.select_register(TEMP_REG3, inst.size());
if (src2p.is_immediate_value(0))
@@ -3430,50 +3602,42 @@ template <bool CarryIn> void drcbe_arm64::op_sub(a64::Assembler &a, const uml::i
}
else
{
- mov_reg_param(a, inst.size(), src1, src1p);
+ const a64::Gp src = src1p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src1p);
if (CarryIn)
{
- a.emit(opcode, output, src1, zero);
+ a.emit(opcode, output, src, zero);
mov_param_reg(a, inst.size(), dstp, output);
}
else
{
- mov_param_reg(a, inst.size(), dstp, src1);
- a.emit(opcode, zero, src1, zero);
+ mov_param_reg(a, inst.size(), dstp, src);
+ a.emit(opcode, zero, src, zero);
}
}
}
else if (!CarryIn && src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate()))
{
- if (src1p.is_immediate_value(0))
- {
- a.emit(opcode, output, zero, src2p.immediate());
- }
- else
- {
- mov_reg_param(a, inst.size(), src1, src1p);
- a.emit(opcode, output, src1, src2p.immediate());
- }
+ const a64::Gp src = src1p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src1p);
+ a.emit(opcode, output, src, src2p.immediate());
mov_param_reg(a, inst.size(), dstp, output);
}
else if (!CarryIn && (!inst.flags() || src1p.is_immediate_value(0)) && src2p.is_immediate() && is_valid_immediate(src2p.immediate(), 24))
{
- if (src1p.is_immediate_value(0))
- {
- a.emit(opcode, output, zero, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
- }
- else
- {
- // will still alter flags, but carry and overflow values will be incorrect for this path
- mov_reg_param(a, inst.size(), src1, src1p);
- a.emit(opcode, output, src1, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
- }
- a.emit(opcode, output, output, src2p.immediate() & util::make_bitmask<uint64_t>(12));
+ const a64::Gp src = src1p.select_register(output, inst.size());
+
+ mov_reg_param(a, inst.size(), src, src1p);
+ a.emit(opcode, output, src, src2p.immediate() & util::make_bitmask<uint64_t>(12));
+ a.emit(opcode, output, output, src2p.immediate() & (util::make_bitmask<uint64_t>(12) << 12));
mov_param_reg(a, inst.size(), dstp, output);
}
else
{
- const a64::Gp src2 = select_register(TEMP_REG2, inst.size());
+ const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
+ const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size());
mov_reg_param(a, inst.size(), src1, src1p);
mov_reg_param(a, inst.size(), src2, src2p);
@@ -3578,6 +3742,8 @@ void drcbe_arm64::op_mulu(a64::Assembler &a, const uml::instruction &inst)
a.bfi(SCRATCH_REG1, TEMP_REG3, 31, 1); // sign flag
a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
+
+ m_carry_state = carry_state::POISON;
}
}
@@ -3636,6 +3802,8 @@ void drcbe_arm64::op_mululw(a64::Assembler &a, const uml::instruction &inst)
a.bfi(TEMP_REG1, SCRATCH_REG1, 31, 1); // sign flag
a.msr(a64::Predicate::SysReg::kNZCV, TEMP_REG1);
+
+ m_carry_state = carry_state::POISON;
}
}
@@ -3711,6 +3879,8 @@ void drcbe_arm64::op_muls(a64::Assembler &a, const uml::instruction &inst)
a.bfi(SCRATCH_REG1, TEMP_REG1, 31, 1); // sign flag
a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
+
+ m_carry_state = carry_state::POISON;
}
}
@@ -3782,6 +3952,8 @@ void drcbe_arm64::op_mulslw(a64::Assembler &a, const uml::instruction &inst)
a.bfi(SCRATCH_REG1, TEMP_REG1, 31, 1); // sign flag
a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
+
+ m_carry_state = carry_state::POISON;
}
}
@@ -3837,6 +4009,7 @@ template <a64::Inst::Id Opcode> void drcbe_arm64::op_div(a64::Assembler &a, cons
a.mov(SCRATCH_REG1, 1 << 28); // set overflow flag
a.msr(a64::Predicate::SysReg::kNZCV, SCRATCH_REG1);
}
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_and(a64::Assembler &a, const uml::instruction &inst)
@@ -3867,6 +4040,13 @@ void drcbe_arm64::op_and(a64::Assembler &a, const uml::instruction &inst)
if (inst.flags())
a.tst(dst, dst);
}
+ else if (src1p.is_immediate() && is_valid_immediate_mask(src1p.immediate(), inst.size()))
+ {
+ const a64::Gp src2 = src2p.select_register(TEMP_REG1, inst.size());
+ mov_reg_param(a, inst.size(), src2, src2p);
+
+ a.emit(opcode, dst, src2, src1p.immediate());
+ }
else if (src2p.is_immediate() && is_valid_immediate_mask(src2p.immediate(), inst.size()))
{
const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
@@ -3874,12 +4054,19 @@ void drcbe_arm64::op_and(a64::Assembler &a, const uml::instruction &inst)
a.emit(opcode, dst, src1, src2p.immediate());
}
- else if (!inst.flags() && (inst.size() == 8) && src2p.is_immediate() && is_valid_immediate_mask(src2p.immediate(), 4))
+ else if ((inst.size() == 8) && src1p.is_immediate() && is_valid_immediate_mask(src1p.immediate(), 4) && (!inst.flags() || !BIT(src1p.immediate(), 31)))
+ {
+ const a64::Gp src2 = src2p.select_register(TEMP_REG1, inst.size());
+ mov_reg_param(a, inst.size(), src2, src2p);
+
+ a.emit(opcode, dst.w(), src2.w(), src1p.immediate());
+ }
+ else if ((inst.size() == 8) && src2p.is_immediate() && is_valid_immediate_mask(src2p.immediate(), 4) && (!inst.flags() || !BIT(src2p.immediate(), 31)))
{
const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
mov_reg_param(a, inst.size(), src1, src1p);
- a.and_(dst.w(), src1.w(), src2p.immediate());
+ a.emit(opcode, dst.w(), src1.w(), src2p.immediate());
}
else
{
@@ -3892,6 +4079,9 @@ void drcbe_arm64::op_and(a64::Assembler &a, const uml::instruction &inst)
}
mov_param_reg(a, inst.size(), dstp, dst);
+
+ if (inst.flags())
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_test(a64::Assembler &a, const uml::instruction &inst)
@@ -3938,6 +4128,8 @@ void drcbe_arm64::op_test(a64::Assembler &a, const uml::instruction &inst)
mov_reg_param(a, inst.size(), src2, src2p);
a.tst(src1, src2);
}
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_or(a64::Assembler &a, const uml::instruction &inst)
@@ -3988,7 +4180,10 @@ void drcbe_arm64::op_or(a64::Assembler &a, const uml::instruction &inst)
mov_param_reg(a, inst.size(), dstp, dst);
if (inst.flags())
+ {
a.tst(dst, dst);
+ m_carry_state = carry_state::POISON;
+ }
}
void drcbe_arm64::op_xor(a64::Assembler &a, const uml::instruction &inst)
@@ -4034,7 +4229,10 @@ void drcbe_arm64::op_xor(a64::Assembler &a, const uml::instruction &inst)
mov_param_reg(a, inst.size(), dstp, dst);
if (inst.flags())
+ {
a.tst(dst, dst);
+ m_carry_state = carry_state::POISON;
+ }
}
void drcbe_arm64::op_lzcnt(a64::Assembler &a, const uml::instruction &inst)
@@ -4056,7 +4254,10 @@ void drcbe_arm64::op_lzcnt(a64::Assembler &a, const uml::instruction &inst)
mov_param_reg(a, inst.size(), dstp, dst);
if (inst.flags())
+ {
a.tst(dst, dst);
+ m_carry_state = carry_state::POISON;
+ }
}
void drcbe_arm64::op_tzcnt(a64::Assembler &a, const uml::instruction &inst)
@@ -4083,6 +4284,7 @@ void drcbe_arm64::op_tzcnt(a64::Assembler &a, const uml::instruction &inst)
{
a.eor(temp, dst, inst.size() * 8);
a.tst(temp, temp);
+ m_carry_state = carry_state::POISON;
}
}
@@ -4108,7 +4310,10 @@ void drcbe_arm64::op_bswap(a64::Assembler &a, const uml::instruction &inst)
mov_param_reg(a, inst.size(), dstp, dst);
if (inst.flags())
+ {
a.tst(dst, dst);
+ m_carry_state = carry_state::POISON;
+ }
}
@@ -4165,7 +4370,10 @@ template <a64::Inst::Id Opcode> void drcbe_arm64::op_shift(a64::Assembler &a, co
}
if (inst.flags())
+ {
a.tst(dst, dst);
+ 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);
@@ -4227,7 +4435,10 @@ void drcbe_arm64::op_rol(a64::Assembler &a, const uml::instruction &inst)
}
if (inst.flags())
+ {
a.tst(output, output);
+ m_carry_state = carry_state::POISON;
+ }
mov_param_reg(a, inst.size(), dstp, output);
}
@@ -4321,6 +4532,8 @@ void drcbe_arm64::op_rolc(a64::Assembler &a, const uml::instruction &inst)
a.tst(output, output);
mov_param_reg(a, inst.size(), dstp, output);
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_rorc(a64::Assembler &a, const uml::instruction &inst)
@@ -4415,6 +4628,8 @@ void drcbe_arm64::op_rorc(a64::Assembler &a, const uml::instruction &inst)
a.tst(output, output);
mov_param_reg(a, inst.size(), dstp, output);
+
+ m_carry_state = carry_state::POISON;
}
void drcbe_arm64::op_fload(a64::Assembler &a, const uml::instruction &inst)
@@ -4485,6 +4700,8 @@ void drcbe_arm64::op_fread(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter dstp(*this, inst.param(0), PTYPE_MR);
be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
const parameter &spacesizep = inst.param(2);
@@ -4517,6 +4734,8 @@ void drcbe_arm64::op_fwrite(a64::Assembler &a, const uml::instruction &inst)
assert_no_condition(inst);
assert_no_flags(inst);
+ m_carry_state = carry_state::POISON;
+
be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
be_parameter srcp(*this, inst.param(1), PTYPE_MF);
const parameter &spacesizep = inst.param(2);
@@ -4551,25 +4770,67 @@ void drcbe_arm64::op_fmov(a64::Assembler &a, const uml::instruction &inst)
be_parameter dstp(*this, inst.param(0), PTYPE_MF);
be_parameter srcp(*this, inst.param(1), PTYPE_MF);
- // TODO: optimise to use fcsel if the source and destination are both kept in host registers
- Label skip;
- if (inst.condition() != uml::COND_ALWAYS)
+ // decide whether a conditional select will be efficient
+ bool usesel = dstp.is_float_register() && srcp.is_float_register();
+ switch (inst.condition())
+ {
+ case uml::COND_ALWAYS:
+ case uml::COND_U:
+ case uml::COND_NU:
+ usesel = false;
+ break;
+ case uml::COND_C:
+ case uml::COND_NC:
+ switch (m_carry_state)
+ {
+ case carry_state::CANONICAL:
+ case carry_state::LOGICAL:
+ break;
+ default:
+ usesel = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (usesel)
{
- skip = a.newLabel();
+ const a64::Vec dstreg = dstp.select_register(TEMPF_REG1, inst.size());
+ const a64::Vec srcreg = srcp.select_register(TEMPF_REG2, inst.size());
- if (inst.condition() == COND_C || inst.condition() == COND_NC || inst.condition() == COND_A || inst.condition() == COND_BE)
- load_carry(a, true);
+ mov_float_reg_param(a, inst.size(), dstreg, dstp);
+ mov_float_reg_param(a, inst.size(), srcreg, srcp);
- if (inst.condition() == uml::COND_U || inst.condition() == uml::COND_NU)
- check_unordered_condition(a, inst.condition(), skip, true);
- else
- a.b(ARM_NOT_CONDITION(inst.condition()), skip);
+ switch (inst.condition())
+ {
+ case uml::COND_C:
+ case uml::COND_NC:
+ if (m_carry_state == carry_state::CANONICAL)
+ a.fcsel(dstreg, srcreg, dstreg, ARM_NOT_CONDITION(inst.condition()));
+ else
+ a.fcsel(dstreg, srcreg, dstreg, ARM_CONDITION(inst.condition()));
+ break;
+ case uml::COND_A:
+ case uml::COND_BE:
+ load_carry(a, true);
+ [[fallthrough]];
+ default:
+ a.fcsel(dstreg, srcreg, dstreg, ARM_CONDITION(inst.condition()));
+ }
+
+ mov_float_param_reg(a, inst.size(), dstp, dstreg);
}
+ else
+ {
+ Label skip;
+ emit_skip(a, inst.condition(), skip);
- mov_float_param_param(a, inst.size(), dstp, srcp);
+ mov_float_param_param(a, inst.size(), dstp, srcp);
- if (inst.condition() != uml::COND_ALWAYS)
- a.bind(skip);
+ if (inst.condition() != uml::COND_ALWAYS)
+ a.bind(skip);
+ }
}
void drcbe_arm64::op_ftoint(a64::Assembler &a, const uml::instruction &inst)