diff options
author | 2025-04-08 06:42:46 +1000 | |
---|---|---|
committer | 2025-04-08 06:42:46 +1000 | |
commit | 63be095e58583a5029536e030ebed297ea9fcbf6 (patch) | |
tree | f36f3b341ae188e3fac5bffa1be9a37d089f9ddd /src/devices/cpu | |
parent | 113d351625d042ae8bde7dd4b8e40e1e5263d093 (diff) |
-cpu/uml.cpp: Rewrote the UML simplification code.
* Fixed many cases that could cause the upper bits of a register not to
cleared following a 32-bit operation.
* Added more simplifications.
* Allow many simplifications when flag updates are requested.
* Fixed various bits of unreachable code.
* cpu/drcbearm64.cpp, cpu/drcbex64.cpp: Removed code for special-casing
some situations the simplifier can now take care of consistently.
-cpu/drcbex64.cpp: Fixed a bug causing some shifts to be treated as a
no-op when the destination and source are not the same.
-cpu/drcbearm64.cpp, cpu/drcbex64.cpp: Added a special case for
comparing something to itself.
-cpu/e132xs: Use the CARRY instruction rather than a right shift to set
up carry in.
Diffstat (limited to 'src/devices/cpu')
-rw-r--r-- | src/devices/cpu/drcbearm64.cpp | 51 | ||||
-rw-r--r-- | src/devices/cpu/drcbex64.cpp | 371 | ||||
-rw-r--r-- | src/devices/cpu/e132xs/e132xsdrc_ops.hxx | 4 | ||||
-rw-r--r-- | src/devices/cpu/uml.cpp | 1141 | ||||
-rw-r--r-- | src/devices/cpu/uml.h | 5 |
5 files changed, 957 insertions, 615 deletions
diff --git a/src/devices/cpu/drcbearm64.cpp b/src/devices/cpu/drcbearm64.cpp index 635df218248..882f70324a7 100644 --- a/src/devices/cpu/drcbearm64.cpp +++ b/src/devices/cpu/drcbearm64.cpp @@ -3718,23 +3718,32 @@ void drcbe_arm64::op_cmp(a64::Assembler &a, const uml::instruction &inst) be_parameter src1p(*this, inst.param(0), PTYPE_MRI); be_parameter src2p(*this, inst.param(1), PTYPE_MRI); - const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size()); - - mov_reg_param(a, inst.size(), src1, src1p); - - if (src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate())) + if (src1p == src2p) { - if (src2p.is_immediate_value(0)) - a.cmp(src1, select_register(a64::xzr, inst.size())); - else - a.cmp(src1, src2p.immediate()); + const a64::Gp zero = select_register(a64::xzr, inst.size()); + + a.cmp(zero, zero); } else { - const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size()); + const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size()); - mov_reg_param(a, inst.size(), src2, src2p); - a.cmp(src1, src2); + mov_reg_param(a, inst.size(), src1, src1p); + + if (src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate())) + { + if (src2p.is_immediate_value(0)) + a.cmp(src1, select_register(a64::xzr, inst.size())); + else + a.cmp(src1, src2p.immediate()); + } + else + { + const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size()); + + mov_reg_param(a, inst.size(), src2, src2p); + a.cmp(src1, src2); + } } if (inst.flags() & FLAG_C) @@ -4087,12 +4096,6 @@ void drcbe_arm64::op_and(a64::Assembler &a, const uml::instruction &inst) be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); - if (src1p.is_immediate() || (dstp.is_int_register() && (dstp == src2p))) - { - using std::swap; - swap(src1p, src2p); - } - const a64::Gp dst = dstp.select_register(TEMP_REG3, inst.size()); const a64::Gp src1 = src1p.select_register(dst, inst.size()); @@ -4210,12 +4213,6 @@ void drcbe_arm64::op_or(a64::Assembler &a, const uml::instruction &inst) be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); - if (src1p.is_immediate() || (dstp.is_int_register() && (dstp == src2p))) - { - using std::swap; - swap(src1p, src2p); - } - const a64::Gp dst = dstp.select_register(TEMP_REG3, inst.size()); const a64::Gp src1 = src1p.select_register(dst, inst.size()); @@ -4274,12 +4271,6 @@ void drcbe_arm64::op_xor(a64::Assembler &a, const uml::instruction &inst) be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); - if (src1p.is_immediate() || (dstp.is_int_register() && (dstp == src2p))) - { - using std::swap; - swap(src1p, src2p); - } - const a64::Gp dst = dstp.select_register(TEMP_REG3, inst.size()); const a64::Gp src1 = src1p.select_register(dst, inst.size()); diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp index c5bf514f14e..b3eb216a7ac 100644 --- a/src/devices/cpu/drcbex64.cpp +++ b/src/devices/cpu/drcbex64.cpp @@ -1395,11 +1395,13 @@ void drcbe_x64::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const if (is64 && !short_immediate(param.immediate())) { // use scratch register for 64-bit immediate - a.mov(r11, param.immediate()); // mov r11,param - a.emit(opcode, dst, r11); // op dst,r11 + a.mov(r11, param.immediate()); + a.emit(opcode, dst, r11); } else - a.emit(opcode, dst, param.immediate()); // op dst,param + { + a.emit(opcode, dst, param.immediate()); + } } } else if (param.is_memory()) @@ -1407,7 +1409,7 @@ void drcbe_x64::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const if (dst.isMem()) { // use temporary register for memory,memory - Gp const tmp = is64 ? param.select_register(rax) : param.select_register(eax); + Gp const tmp = param.select_register(is64 ? Gp(rax) : Gp(eax)); a.mov(tmp, MABS(param.memory())); a.emit(opcode, dst, tmp); @@ -1555,7 +1557,7 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz if (bitshift) a.emit(opcode, dst, imm(param.immediate())); - else if (update_flags & FLAG_C) + else if (!carryin && (update_flags & FLAG_C)) a.clc(); // throw away carry since it'll never be used if (update_flags & (FLAG_S | FLAG_Z)) @@ -1594,8 +1596,7 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz if (carryin) a.mov(rax, r10); - - if (update_flags & FLAG_C) + else if (update_flags & FLAG_C) a.clc(); // throw away carry since it'll never be used a.short_().jmp(end); @@ -3832,11 +3833,7 @@ void drcbe_x64::op_roland(Assembler &a, const instruction &inst) // pick a target register Gp dstreg = dstp.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax), maskp); - if (maskp.is_immediate_value(0)) - { - a.xor_(dstreg, dstreg); - } - else if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate())) + if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate())) { const unsigned shift = shiftp.immediate() & (bits - 1); const uint64_t sizemask = util::make_bitmask<uint64_t>(bits); @@ -3875,55 +3872,33 @@ void drcbe_x64::op_roland(Assembler &a, const instruction &inst) { const uint64_t mask = maskp.immediate() & sizemask; mov_reg_param(a, dstreg, srcp); - if (mask == sizemask) + a.rol(dstreg, shift); + if (!inst.flags() && (mask == 0x000000ff)) { - if (shift) - a.rol(dstreg, shift); - if (inst.flags()) - a.test(dstreg, dstreg); + a.movzx(dstreg, dstreg.r8()); } - else if (mask == (util::make_bitmask<uint64_t>(shift) & sizemask)) + else if (!inst.flags() && (mask == 0x0000ffff)) { - a.shr(dstreg, bits - shift); - if (inst.flags()) - a.test(dstreg, dstreg); + a.movzx(dstreg, dstreg.r16()); } - else if (mask == (~util::make_bitmask<uint64_t>(shift) & sizemask)) + else if (!inst.flags() && (mask == 0xffffffff)) { - a.shl(dstreg, shift); + a.mov(dstreg.r32(), dstreg.r32()); + } + else if ((bits == 32) || (util::sext(mask, 32) == mask)) + { + a.and_(dstreg, mask); + } + else if (uint32_t(mask) == mask) + { + a.and_(dstreg, mask); // asmjit converts this to a DWORD-size operation if (inst.flags()) a.test(dstreg, dstreg); } else { - a.rol(dstreg, shift); - if (!inst.flags() && (mask == 0x000000ff)) - { - a.movzx(dstreg, dstreg.r8()); - } - else if (!inst.flags() && (mask == 0x0000ffff)) - { - a.movzx(dstreg, dstreg.r16()); - } - else if (!inst.flags() && (mask == 0xffffffff)) - { - a.mov(dstreg.r32(), dstreg.r32()); - } - else if ((bits == 32) || (util::sext(mask, 32) == mask)) - { - a.and_(dstreg, mask); - } - else if (uint32_t(mask) == mask) - { - a.and_(dstreg, mask); - if (inst.flags()) - a.test(dstreg, dstreg); - } - else - { - a.mov(rdx, mask); - a.and_(dstreg, rdx); - } + a.mov(rdx, mask); + a.and_(dstreg, rdx); } } } @@ -3940,19 +3915,7 @@ void drcbe_x64::op_roland(Assembler &a, const instruction &inst) mov_reg_param(a, dstreg, srcp); a.rol(dstreg, cl); } - alu_op_param(a, Inst::kIdAnd, dstreg, maskp, - [inst] (Assembler &a, Operand const &dst, be_parameter const &src) - { - // all-one cases - if (ones(src.immediate(), inst.size())) - { - if (inst.flags()) - a.test(dst.as<Gp>(), dst.as<Gp>()); - return true; - } - - return false; - }); + alu_op_param(a, Inst::kIdAnd, dstreg, maskp); } mov_param_reg(a, dstp, dstreg); @@ -3984,15 +3947,7 @@ void drcbe_x64::op_rolins(Assembler &a, const instruction &inst) const unsigned bits = inst.size() * 8; const uint64_t sizemask = util::make_bitmask<uint64_t>(bits); - if (maskp.is_immediate_value(0)) - { - if (inst.flags()) - { - mov_reg_param(a, dstreg, dstp); - a.test(dstreg, dstreg); - } - } - else if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate())) + if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate())) { const unsigned shift = shiftp.immediate() & (bits - 1); if (srcp.is_immediate()) @@ -4006,68 +3961,53 @@ void drcbe_x64::op_rolins(Assembler &a, const instruction &inst) { const uint64_t mask = maskp.immediate() & sizemask; src &= mask; - if (mask == sizemask) + + bool flags = false; + mov_reg_param(a, dstreg, dstp); + if (mask == 0xffffffff'00000000) { - if (src) - { - a.mov(dstreg, src); - if (inst.flags()) - a.test(dstreg, dstreg); - } - else - { - a.xor_(dstreg, dstreg); - } + a.mov(dstreg.r32(), dstreg.r32()); + } + else if (mask == (0xffffffff'ffff0000 & sizemask)) + { + a.movzx(dstreg, dstreg.r16()); + } + else if (mask == (0xffffffff'ffffff00 & sizemask)) + { + a.movzx(dstreg, dstreg.r8()); + } + else if ((bits == 32) || (util::sext(~mask, 32) == ~mask)) + { + a.and_(dstreg, ~mask); + flags = true; + } + else if (uint32_t(~mask) == ~mask) + { + a.and_(dstreg, ~mask); } else { - bool flags = false; - mov_reg_param(a, dstreg, dstp); - if (mask == 0xffffffff'00000000) - { - a.mov(dstreg.r32(), dstreg.r32()); - } - else if (mask == (0xffffffff'ffff0000 & sizemask)) - { - a.movzx(dstreg, dstreg.r16()); - } - else if (mask == (0xffffffff'ffffff00 & sizemask)) - { - a.movzx(dstreg, dstreg.r8()); - } - else if ((bits == 32) || (util::sext(~mask, 32) == ~mask)) - { - a.and_(dstreg, ~mask); - flags = true; - } - else if (uint32_t(~mask) == ~mask) - { - a.and_(dstreg, ~mask); - } - else - { - a.mov(maskreg, ~mask); - a.and_(dstreg, maskreg); - flags = true; - } + a.mov(maskreg, ~mask); + a.and_(dstreg, maskreg); + flags = true; + } - if (src) + if (src) + { + if ((bits == 32) || (util::sext(src, 32) == src)) { - if ((bits == 32) || (util::sext(src, 32) == src)) - { - a.or_(dstreg, src); - } - else - { - a.mov(srcreg, src); - a.or_(dstreg, srcreg); - } + a.or_(dstreg, src); } - else if (!flags && inst.flags()) + else { - a.test(dstreg, dstreg); + a.mov(srcreg, src); + a.or_(dstreg, srcreg); } } + else if (!flags && inst.flags()) + { + a.test(dstreg, dstreg); + } } else { @@ -4101,110 +4041,100 @@ void drcbe_x64::op_rolins(Assembler &a, const instruction &inst) { // variables source, immediate mask const uint64_t mask = maskp.immediate() & sizemask; - if (mask == sizemask) - { - mov_reg_param(a, dstreg, srcp); - if (shift) - a.rol(dstreg, shift); - if (inst.flags()) - a.test(dstreg, dstreg); - } - else - { - mov_reg_param(a, dstreg, dstp); - bool maskloaded = false; - if (!shift) + mov_reg_param(a, dstreg, dstp); + + bool maskloaded = false; + if (!shift) + { + if (mask == 0x00000000'000000ff) { - if (mask == 0x00000000'000000ff) - { - if (srcp.is_int_register()) - a.movzx(srcreg, GpbLo(srcp.ireg())); - else if (srcp.is_memory()) - a.movzx(srcreg, MABS(srcp.memory(), 1)); - } - else if (mask == 0x00000000'0000ffff) - { - if (srcp.is_int_register()) - a.movzx(srcreg, Gpw(srcp.ireg())); - else if (srcp.is_memory()) - a.movzx(srcreg, MABS(srcp.memory(), 2)); - } - else if (mask == 0x00000000'ffffffff) - { - mov_reg_param(a, srcreg.r32(), srcp); - } - else - { - mov_reg_param(a, srcreg, srcp); - a.and_(srcreg, mask); - } + if (srcp.is_int_register()) + a.movzx(srcreg, GpbLo(srcp.ireg())); + else if (srcp.is_memory()) + a.movzx(srcreg, MABS(srcp.memory(), 1)); } - else if (mask == (util::make_bitmask<uint64_t>(shift) & sizemask)) + else if (mask == 0x00000000'0000ffff) { - mov_reg_param(a, srcreg, srcp); - a.shr(srcreg, bits - shift); + if (srcp.is_int_register()) + a.movzx(srcreg, Gpw(srcp.ireg())); + else if (srcp.is_memory()) + a.movzx(srcreg, MABS(srcp.memory(), 2)); } - else if (mask == (~util::make_bitmask<uint64_t>(shift) & sizemask)) + else if (mask == 0x00000000'ffffffff) { - mov_reg_param(a, srcreg, srcp); - a.shl(srcreg, shift); + mov_reg_param(a, srcreg.r32(), srcp); } else { mov_reg_param(a, srcreg, srcp); - a.rol(srcreg, shift); - if (mask == 0x00000000'000000ff) - { - a.movzx(srcreg, srcreg.r8()); - } - else if (mask == 0x00000000'0000ffff) - { - a.movzx(srcreg, srcreg.r16()); - } - else if (mask == 0x00000000'ffffffff) - { - a.mov(srcreg.r32(), srcreg.r32()); - } - else if ((bits == 32) || (util::sext(mask, 32) == mask) || (uint32_t(mask) == mask)) - { - a.and_(srcreg, mask); - } - else - { - a.mov(maskreg, mask); - a.and_(srcreg, maskreg); - maskloaded = true; - } + a.and_(srcreg, mask); } - - if (mask == 0xffffffff'00000000) + } + else if (mask == (util::make_bitmask<uint64_t>(shift) & sizemask)) + { + mov_reg_param(a, srcreg, srcp); + a.shr(srcreg, bits - shift); + } + else if (mask == (~util::make_bitmask<uint64_t>(shift) & sizemask)) + { + mov_reg_param(a, srcreg, srcp); + a.shl(srcreg, shift); + } + else + { + mov_reg_param(a, srcreg, srcp); + a.rol(srcreg, shift); + if (mask == 0x00000000'000000ff) { - a.mov(dstreg.r32(), dstreg.r32()); + a.movzx(srcreg, srcreg.r8()); } - else if (mask == (0xffffffff'ffff0000 & sizemask)) + else if (mask == 0x00000000'0000ffff) { - a.movzx(dstreg, dstreg.r16()); + a.movzx(srcreg, srcreg.r16()); } - else if (mask == (0xffffffff'ffffff00 & sizemask)) + else if (mask == 0x00000000'ffffffff) { - a.movzx(dstreg, dstreg.r8()); + a.mov(srcreg.r32(), srcreg.r32()); } - else if ((bits == 32) || (util::sext(~mask, 32) == ~mask) || (uint32_t(~mask) == ~mask)) + else if ((bits == 32) || (util::sext(mask, 32) == mask) || (uint32_t(mask) == mask)) { - a.and_(dstreg, ~mask & sizemask); + a.and_(srcreg, mask); } else { - if (maskloaded) - a.not_(maskreg); - else - a.mov(maskreg, ~mask); - a.and_(dstreg, maskreg); + a.mov(maskreg, mask); + a.and_(srcreg, maskreg); + maskloaded = true; } + } - a.or_(dstreg, srcreg); + if (mask == 0xffffffff'00000000) + { + a.mov(dstreg.r32(), dstreg.r32()); + } + else if (mask == (0xffffffff'ffff0000 & sizemask)) + { + a.movzx(dstreg, dstreg.r16()); + } + else if (mask == (0xffffffff'ffffff00 & sizemask)) + { + a.movzx(dstreg, dstreg.r8()); } + else if ((bits == 32) || (util::sext(~mask, 32) == ~mask) || (uint32_t(~mask) == ~mask)) + { + a.and_(dstreg, ~mask & sizemask); + } + else + { + if (maskloaded) + a.not_(maskreg); + else + a.mov(maskreg, ~mask); + a.and_(dstreg, maskreg); + } + + a.or_(dstreg, srcreg); } mov_param_reg(a, dstp, dstreg); @@ -4469,24 +4399,33 @@ void drcbe_x64::op_cmp(Assembler &a, const instruction &inst) be_parameter src1p(*this, inst.param(0), PTYPE_MRI); be_parameter src2p(*this, inst.param(1), PTYPE_MRI); - // memory versus anything - if (src1p.is_memory()) - alu_op_param(a, Inst::kIdCmp, MABS(src1p.memory(), inst.size()), src2p); // cmp [dstp],src2p + if (src1p == src2p) + { + // doesn't matter what we compare if it's always equal + const Gp srcreg = (inst.size() == 4) ? Gp(eax) : Gp(rax); // TODO: consider false dependencies - // general case + a.cmp(srcreg, srcreg); + } + else if (src1p.is_memory()) + { + // memory versus anything + alu_op_param(a, Inst::kIdCmp, MABS(src1p.memory(), inst.size()), src2p); + } else { + // general case + // pick a target register for the general case - Gp src1reg = (inst.size() == 4) ? src1p.select_register(eax) : src1p.select_register(rax); + const Gp src1reg = src1p.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax)); if (src1p.is_immediate()) { if (inst.size() == 4) - a.mov(src1reg, src1p.immediate()); // mov src1reg,imm + a.mov(src1reg, src1p.immediate()); else - mov_r64_imm(a, src1reg, src1p.immediate()); // mov src1reg,imm + mov_r64_imm(a, src1reg, src1p.immediate()); } - alu_op_param(a, Inst::kIdCmp, src1reg, src2p); // cmp src1reg,src2p + alu_op_param(a, Inst::kIdCmp, src1reg, src2p); } } @@ -5361,10 +5300,6 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst) const bool carry = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr); - // optimize immediate zero case - if (!carry && !inst.flags() && src2p.is_immediate() && !(src2p.immediate() & (inst.size() * 8 - 1))) - return; // FIXME: needs to clear upper bits for 32-bit form - if (dstp.is_memory() && ((inst.size() == 8) || !dstp.is_cold_register()) && (dstp == src1p)) { // dstp == src1p in memory @@ -5375,7 +5310,7 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst) // general case // pick a target register - Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p); + const Gp dstreg = dstp.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax), src2p); if (carry) mov_reg_param(a, dstreg, src1p, true); diff --git a/src/devices/cpu/e132xs/e132xsdrc_ops.hxx b/src/devices/cpu/e132xs/e132xsdrc_ops.hxx index 1f17b598819..072c695f3be 100644 --- a/src/devices/cpu/e132xs/e132xsdrc_ops.hxx +++ b/src/devices/cpu/e132xs/e132xsdrc_ops.hxx @@ -1582,7 +1582,7 @@ void hyperstone_device::generate_subc(drcuml_block &block, compiler_state &compi if (!SrcGlobal || (src_code != SR_REGISTER)) { - UML_SHR(block, I4, I2, C_SHIFT + 1); // set up carry in, result unused + UML_CARRY(block, I2, C_SHIFT); UML_SUBB(block, I0, I0, I1); } else @@ -1680,7 +1680,7 @@ void hyperstone_device::generate_addc(drcuml_block &block, compiler_state &compi if (!SrcGlobal || (src_code != SR_REGISTER)) { - UML_SHR(block, I4, I2, C_SHIFT + 1); // set up carry in, result unused + UML_CARRY(block, I2, C_SHIFT); UML_ADDC(block, I0, I0, I1); } else diff --git a/src/devices/cpu/uml.cpp b/src/devices/cpu/uml.cpp index 7133cfb6980..6670725a18b 100644 --- a/src/devices/cpu/uml.cpp +++ b/src/devices/cpu/uml.cpp @@ -259,6 +259,739 @@ void uml::code_handle::set_codeptr(drccodeptr code) // UML INSTRUCTION //************************************************************************** +struct uml::instruction::simplify_op +{ +private: + static inline constexpr u64 paramsizemask[] = { 0xffU, 0xffffU, 0xffffffffU, 0xffffffff'ffffffffU }; + + static u64 size_mask(instruction const &inst) + { + return (inst.size() == 4) ? 0xffffffffU : 0xffffffff'ffffffffU; + } + + static void truncate_immediate(instruction &inst, int pnum, u64 mask) + { + if (inst.param(pnum).is_immediate()) + inst.m_param[pnum] = inst.param(pnum).immediate() & mask; + } + + static void normalise_commutative(instruction &inst) + { + const u64 mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, mask); + + // if a source is the destination put it first, and put a single immediate last + if ((inst.param(0) == inst.param(2)) || (inst.param(1).is_immediate() && !inst.param(2).is_immediate())) + { + using std::swap; + swap(inst.m_param[1], inst.m_param[2]); + } + } + + static void convert_to_mov_immediate(instruction &inst, u64 immediate) + { + u64 const mask = size_mask(inst); + immediate &= mask; + if (!inst.flags()) + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + inst.m_param[1] = immediate; + } + else if (immediate == mask) + { + inst.m_opcode = OP_OR; + inst.m_numparams = 3; + inst.m_param[1] = inst.param(0); + inst.m_param[2] = mask; + } + else + { + inst.m_opcode = OP_AND; + inst.m_numparams = 3; + if (immediate) + { + inst.m_param[1] = immediate; + inst.m_param[2] = mask; + } + else + { + inst.m_param[1] = inst.param(0); + inst.m_param[2] = 0; + } + } + } + +public: + static void truncate_imm(instruction &inst) + { + u64 const mask = size_mask(inst); + + for (int i = 0; inst.numparams() > i; ++i) + truncate_immediate(inst, i, mask); + } + + static void read(instruction &inst) + { + // truncate immediate address to size + truncate_immediate(inst, 1, 0xffffffff); + } + + static void readm(instruction &inst) + { + auto const size = inst.param(3).size(); + u64 const mask = paramsizemask[size]; + + // truncate immediate address and mask to size + truncate_immediate(inst, 1, 0xffffffff); + truncate_immediate(inst, 2, mask); + + // convert to READ if the mask is all ones + if (inst.param(2).is_immediate_value(mask)) + { + inst.m_opcode = OP_READ; + inst.m_numparams = 3; + inst.m_param[2] = inst.param(3); + } + } + + static void write(instruction &inst) + { + auto const size = inst.param(2).size(); + + // truncate immediate address and data to size + truncate_immediate(inst, 0, 0xffffffff); + truncate_immediate(inst, 1, paramsizemask[size]); + } + + static void writem(instruction &inst) + { + auto const size = inst.param(3).size(); + u64 const mask = paramsizemask[size]; + + // truncate immediate address, data and mask to size + truncate_immediate(inst, 0, 0xffffffff); + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, mask); + + // convert to WRITE if the mask is all ones + if (inst.param(2).is_immediate_value(mask)) + { + inst.m_opcode = OP_WRITE; + inst.m_numparams = 3; + inst.m_param[2] = inst.param(3); + } + } + + static void set(instruction &inst) + { + // convert to MOV if the condition is constant + if (inst.condition() == COND_ALWAYS) + convert_to_mov_immediate(inst, 1); + } + + static void mov(instruction &inst) + { + // convert move-to-self to NOP if the destination isn't larger than the size + if (inst.param(0) == inst.param(1)) + { + if (!inst.param(0).is_int_register() || (inst.size() == 8)) + { + inst.nop(); + return; + } + } + + // truncate immediate source to instruction size + truncate_immediate(inst, 1, size_mask(inst)); + } + + static void sext(instruction &inst) + { + // convert immediate source to MOV or a logic operation if flags are requested + if (inst.param(1).is_immediate()) + { + u64 val = inst.param(1).immediate(); + switch (inst.param(2).size()) + { + case SIZE_BYTE: val = u64(s64(s8(u8(val)))); break; + case SIZE_WORD: val = u64(s64(s16(u16(val)))); break; + case SIZE_DWORD: val = u64(s64(s32(u32(val)))); break; + case SIZE_QWORD: break; + } + convert_to_mov_immediate(inst, val); + } + } + + static void roland(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + truncate_immediate(inst, 3, mask); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate() && inst.param(3).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, rotl_32(inst.param(1).immediate(), inst.param(2).immediate()) & inst.param(3).immediate()); + else + convert_to_mov_immediate(inst, rotl_64(inst.param(1).immediate(), inst.param(2).immediate()) & inst.param(3).immediate()); + } + else if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // only mask is variable, convert to AND + inst.m_opcode = OP_AND; + inst.m_numparams = 3; + if (size == 4) + inst.m_param[1] = parameter(rotl_32(inst.param(1).immediate(), inst.param(2).immediate())); + else + inst.m_param[1] = parameter(rotl_64(inst.param(1).immediate(), inst.param(2).immediate())); + inst.m_param[2] = inst.param(3); + } + else if (inst.param(2).is_immediate_value(0) || inst.param(3).is_immediate_value(0)) + { + // no shift or zero mask, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_numparams = 3; + inst.m_param[2] = inst.param(3); + } + else if (inst.param(3).is_immediate_value(mask)) + { + // all mask bits set, convert to ROL + inst.m_opcode = OP_ROL; + inst.m_numparams = 3; + } + else if (inst.param(2).is_immediate() && inst.param(3).is_immediate_value((mask << inst.param(2).immediate()) & mask)) + { + // equivalent to shift left + inst.m_opcode = OP_SHL; + inst.m_numparams = 3; + } + else if (inst.param(2).is_immediate() && inst.param(3).is_immediate_value(mask >> (bits - inst.param(2).immediate()))) + { + // equivalent to shift right + inst.m_opcode = OP_SHR; + inst.m_numparams = 3; + inst.m_param[2] = bits - inst.param(2).immediate(); + } + } + + static void rolins(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + truncate_immediate(inst, 3, mask); + + if (inst.param(3).is_immediate_value(mask)) + { + // all mask bits set, convert to ROL + inst.m_opcode = OP_ROL; + inst.m_numparams = 3; + } + else if (inst.param(3).is_immediate_value(0)) + { + // no mask bits set, convert to AND + inst.m_opcode = OP_AND; + inst.m_numparams = 3; + inst.m_param[1] = inst.param(0); + inst.m_param[2] = mask; + } + } + + static void add(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + // can't optimise carry or overflow flag generation + if (inst.flags() & (FLAG_C | FLAG_V)) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() + inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // add zero, convert to MOV or AND + if (inst.flags()) + { + inst.m_opcode = OP_AND; + inst.m_param[2] = size_mask(inst); + } + else + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + } + } + + static void sub(instruction &inst) + { + u64 const mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, mask); + + // can't optimise carry or overflow flag generation + if (inst.flags() & (FLAG_C | FLAG_V)) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() - inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // subtract zero, convert to MOV or AND + if (inst.flags()) + { + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + else + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + } + } + + static void cmp(instruction &inst) + { + u64 const mask = size_mask(inst); + + // completely elide if flags are not used + if (!inst.flags()) + { + inst.nop(); + return; + } + + // truncate immediates to instruction size + truncate_immediate(inst, 0, mask); + truncate_immediate(inst, 1, mask); + } + + template <opcode_t LoWordOp> + static void mul(instruction &inst) + { + // if the two destination operands are identical, convert to MUL.LW + if (inst.param(0) == inst.param(1)) + { + inst.m_opcode = LoWordOp; + inst.m_numparams = 3; + inst.m_param[1] = inst.param(2); + inst.m_param[2] = inst.param(3); + return; + } + + const u64 mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 2, mask); + truncate_immediate(inst, 3, mask); + + // put a single immediate last + if (inst.param(2).is_immediate() && !inst.param(3).is_immediate()) + { + using std::swap; + swap(inst.m_param[2], inst.m_param[3]); + } + } + + template <typename Short, typename Long> + static void mullw(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + // can't optimise overflow flag generation + if (inst.flags() & FLAG_V) + return; + + if (inst.param(1).is_immediate_value(0) || inst.param(2).is_immediate_value(0)) + { + // multiplying anything by zero yields zero + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // convert constant result to MOV or a logic op + auto const size = inst.size(); + assert((size == 4) || (size == 8)); + + if (size == 4) + convert_to_mov_immediate(inst, u32(Short(u32(inst.param(1).immediate())) * Short(u32(inst.param(2).immediate())))); + else + convert_to_mov_immediate(inst, u64(Long(inst.param(1).immediate()) * Long(inst.param(2).immediate()))); + } + } + + template <typename Short, typename Long> + static void div(instruction &inst) + { + auto const size = inst.size(); + u64 const mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 2, mask); + truncate_immediate(inst, 3, mask); + + // can't optimise overflow flag generation + if (inst.flags() & FLAG_V) + return; + + // optimise the quotient-only form if not dividing by zero + if ((inst.param(0) == inst.param(1)) && !inst.param(3).is_immediate_value(0)) + { + if (inst.param(2).is_immediate_value(0)) + { + // dividing zero by anything yields zero + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(2).is_immediate() && inst.param(3).is_immediate()) + { + // convert constant result to MOV or a logic op + assert((size == 4) || (size == 8)); + + if (size == 4) + convert_to_mov_immediate(inst, u32(Short(u32(inst.param(2).immediate())) / Short(u32(inst.param(3).immediate())))); + else + convert_to_mov_immediate(inst, u64(Long(inst.param(2).immediate()) / Long(inst.param(3).immediate()))); + } + } + } + + static void _and(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() & inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // any immediate zero always yields zero + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(2).is_immediate_value(size_mask(inst))) + { + if (!inst.flags()) + { + // convert to MOV if the value will be unaffected and flags aren't updated + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + else if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + // convert to TEST if the value will be unaffected and the destination is no larger than the operand size + inst.m_opcode = OP_TEST; + inst.m_numparams = 2; + } + } + } + + static void test(instruction &inst) + { + u64 const mask = size_mask(inst); + + // completely elide if flags are not used + if (!inst.flags()) + { + inst.nop(); + return; + } + + // truncate immediates to instruction size + truncate_immediate(inst, 0, mask); + truncate_immediate(inst, 1, mask); + + // put a single immediate second + if (inst.param(0).is_immediate() && !inst.param(1).is_immediate()) + { + using std::swap; + swap(inst.m_param[0], inst.m_param[1]); + } + } + + static void _or(instruction &inst) + { + u64 const mask = size_mask(inst); + + // clean up operands + normalise_commutative(inst); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() | inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(mask)) + { + // an immediate with all bits set is unaffected by the other value + convert_to_mov_immediate(inst, mask); + } + else if (inst.param(2).is_immediate_value(0)) + { + if (!inst.flags()) + { + // convert to MOV if the value will be unaffected and flags aren't updated + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + else if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + // convert to TEST if the value will be unaffected and the destination is no larger than the operand size + inst.m_opcode = OP_TEST; + inst.m_numparams = 2; + } + } + } + + static void _xor(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() ^ inst.param(2).immediate()); + } + else if (inst.param(1) == inst.param(2)) + { + // equivalent values cancel out + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(2).is_immediate_value(0)) + { + if (!inst.flags()) + { + // convert to MOV if the value will be unaffected and flags aren't updated + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + else if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + // convert to TEST if the value will be unaffected and the destination is no larger than the operand size + inst.m_opcode = OP_TEST; + inst.m_numparams = 2; + } + } + } + + static void lzcnt(instruction &inst) + { + // convert immediate source to MOV or a logic operation if flags are requested + if (inst.param(1).is_immediate()) + { + auto const size = inst.size(); + assert((size == 4) || (size == 8)); + + u64 const val = inst.param(1).immediate(); + convert_to_mov_immediate(inst, (size == 4) ? count_leading_zeros_32(u32(val)) : count_leading_zeros_64(val)); + } + } + + static void bswap(instruction &inst) + { + // convert immediate source to MOV or a logic operation if flags are requested + if (inst.param(1).is_immediate()) + { + auto const size = inst.size(); + assert((size == 4) || (size == 8)); + + u64 const val = inst.param(1).immediate(); + convert_to_mov_immediate(inst, (size == 4) ? swapendian_int32(u32(val)) : swapendian_int64(val)); + } + } + + static void shl(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, u32(inst.param(1).immediate()) << inst.param(2).immediate()); + else + convert_to_mov_immediate(inst, inst.param(1).immediate() << inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + template <typename Short, typename Long> + static void shr(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, u32(Short(u32(inst.param(1).immediate())) >> inst.param(2).immediate())); + else + convert_to_mov_immediate(inst, u64(Long(inst.param(1).immediate()) >> inst.param(2).immediate())); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + static void rol(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, rotl_32(inst.param(1).immediate(), inst.param(2).immediate())); + else + convert_to_mov_immediate(inst, rotl_64(inst.param(1).immediate(), inst.param(2).immediate())); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + static void ror(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, rotr_32(inst.param(1).immediate(), inst.param(2).immediate())); + else + convert_to_mov_immediate(inst, rotr_64(inst.param(1).immediate(), inst.param(2).immediate())); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + static void rolrc(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise zero or sign flag generation + if (inst.flags() & (FLAG_Z | FLAG_S)) + return; + + // convert to NOP or MOV if there's no rotation + if (inst.param(2).is_immediate_value(0)) + { + if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + inst.nop(); + } + else + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + } + } + + static void fmov(instruction &inst) + { + // convert move-to-self to NOP + if (inst.param(0) == inst.param(1)) + inst.nop(); + } +}; + + //------------------------------------------------- // configure - configure an opcode with no // parameters @@ -372,13 +1105,6 @@ void uml::instruction::configure(opcode_t op, u8 size, parameter p0, parameter p void uml::instruction::simplify() { - // can't simplify if flags are in play - if (m_flags != 0) - return; - - static constexpr u64 instsizemask[] = { 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0xffffffffffffffffU }; - static constexpr u64 paramsizemask[] = { 0xff, 0xffff, 0xffffffff, 0xffffffffffffffffU }; - // loop until we've simplified all we can opcode_t origop; do @@ -387,365 +1113,56 @@ void uml::instruction::simplify() origop = m_opcode; switch (m_opcode) { - // READM: convert to READ if the mask is wide open - case OP_READM: - if (m_param[2].is_immediate_value(paramsizemask[m_param[3].size()])) - { - m_opcode = OP_READ; - m_numparams = 3; - m_param[2] = m_param[3]; - } - break; - - // WRITEM: convert to WRITE if the mask is wide open - case OP_WRITEM: - if (m_param[2].is_immediate_value(paramsizemask[m_param[3].size()])) - { - m_opcode = OP_WRITE; - m_numparams = 3; - m_param[2] = m_param[3]; - } - break; - - // SET: convert to MOV if constant condition - case OP_SET: - if (m_condition == COND_ALWAYS) - convert_to_mov_immediate(1); - break; - - // MOV: convert to NOP if move-to-self - case OP_MOV: - if (m_param[0] == m_param[1]) - nop(); - break; - - // SEXT: convert immediates to MOV - case OP_SEXT: - if (m_param[1].is_immediate()) - switch (m_param[2].size()) - { - case SIZE_BYTE: convert_to_mov_immediate(s8(m_param[1].immediate())); break; - case SIZE_WORD: convert_to_mov_immediate(s16(m_param[1].immediate())); break; - case SIZE_DWORD: convert_to_mov_immediate(s32(m_param[1].immediate())); break; - case SIZE_QWORD: convert_to_mov_immediate(s64(m_param[1].immediate())); break; - } - break; - - // ROLAND: convert to MOV if all immediate, or to ROL or AND if one is not needed, or to SHL/SHR if the mask is right - case OP_ROLAND: - if (m_param[1].is_immediate() && m_param[2].is_immediate() && m_param[3].is_immediate()) - { - assert(m_size == 4 || m_size == 8); - if (m_size == 4) - convert_to_mov_immediate(rotl_32(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate()); - else - convert_to_mov_immediate(rotl_64(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate()); - } - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - assert(m_size == 4 || m_size == 8); - m_opcode = OP_AND; - m_numparams = 3; - if (m_size == 4) - m_param[1] = parameter(rotl_32(m_param[1].immediate(), m_param[2].immediate())); - else - m_param[1] = parameter(rotl_64(m_param[1].immediate(), m_param[2].immediate())); - m_param[2] = m_param[3]; - } - else if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0)) - { - m_opcode = OP_AND; - m_numparams = 3; - m_param[2] = m_param[3]; - } - else if (m_param[3].is_immediate_value(instsizemask[m_size])) - { - m_opcode = OP_ROL; - m_numparams = 3; - } - else if (m_param[2].is_immediate() && m_param[3].is_immediate_value((0xffffffffffffffffU << m_param[2].immediate()) & instsizemask[m_size])) - { - m_opcode = OP_SHL; - m_numparams = 3; - } - else if (m_param[2].is_immediate() && m_param[3].is_immediate_value(instsizemask[m_size] >> (8 * m_size - m_param[2].immediate()))) - { - m_opcode = OP_SHR; - m_numparams = 3; - m_param[2] = 8 * m_size - m_param[2].immediate(); - } - break; - - // ROLINS: convert to ROLAND if the mask is full - case OP_ROLINS: - if (m_param[3].is_immediate_value(instsizemask[m_size])) - m_opcode = OP_ROLAND; - break; - - // ADD: convert to MOV if immediate, or if adding 0 - case OP_ADD: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() + m_param[2].immediate()); - else if (m_param[1].is_immediate_value(0)) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // SUB: convert to MOV if immediate, or if subtracting 0 - case OP_SUB: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() - m_param[2].immediate()); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // CMP: no-op if no flags needed, compare i0 to i0 if the parameters are equal - case OP_CMP: - if (m_flags == 0) - nop(); - else if (m_param[0] == m_param[1]) - cmp(I0, I0); - break; - - // MULU: convert simple form to MOV if immediate, or if multiplying by 0 - case OP_MULU: - if (m_param[0] == m_param[1]) - { - if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(u32(m_param[2].immediate()) * u32(m_param[3].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(u64(u64(m_param[2].immediate()) * u64(m_param[3].immediate()))); - } - } - break; - - // MULULW: convert simple form to MOV if immediate, or if multiplying by 0 - case OP_MULULW: - if (m_param[1].is_immediate_value(0) || m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(u32(m_param[1].immediate()) * u32(m_param[2].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(u64(u64(m_param[1].immediate()) * u64(m_param[2].immediate()))); - } - break; - - // MULS: convert simple form to MOV if immediate, or if multiplying by 0 - case OP_MULS: - if (m_param[0] == m_param[1]) - { - if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(s32(m_param[2].immediate()) * s32(m_param[3].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(s64(s64(m_param[2].immediate()) * s64(m_param[3].immediate()))); - } - } - break; - - // MULSLW: convert simple form to MOV if immediate, or if multiplying by 0 - case OP_MULSLW: - if (m_param[1].is_immediate_value(0) || m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(s32(m_param[1].immediate()) * s32(m_param[2].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(s64(s64(m_param[1].immediate()) * s64(m_param[2].immediate()))); - } - break; - - // DIVU: convert simple form to MOV if immediate, or if dividing with 0 - case OP_DIVU: - if (m_param[0] == m_param[1] && !m_param[3].is_immediate_value(0)) - { - if (m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(u32(m_param[2].immediate()) / u32(m_param[3].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(u64(u64(m_param[2].immediate()) / u64(m_param[3].immediate()))); - } - } - break; - - // DIVS: convert simple form to MOV if immediate, or if dividing with 0 - case OP_DIVS: - if (m_param[0] == m_param[1] && !m_param[3].is_immediate_value(0)) - { - if (m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(s32(m_param[2].immediate()) / s32(m_param[3].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(s64(s64(m_param[2].immediate()) / s64(m_param[3].immediate()))); - } - } - break; - - // AND: convert to MOV if immediate, or if anding against 0 or 0xffffffff - case OP_AND: - if (m_param[1].is_immediate_value(0) || m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() & m_param[2].immediate()); - else if (m_param[1].is_immediate_value(instsizemask[m_size])) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(instsizemask[m_size])) - convert_to_mov_param(1); - break; - - // TEST: no-op if no flags needed - case OP_TEST: - if (m_flags == 0) - nop(); - break; - - // OR: convert to MOV if immediate, or if oring against 0 or 0xffffffff - case OP_OR: - if (m_param[1].is_immediate_value(instsizemask[m_size]) || m_param[2].is_immediate_value(instsizemask[m_size])) - convert_to_mov_immediate(instsizemask[m_size]); - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() | m_param[2].immediate()); - else if (m_param[1].is_immediate_value(0)) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // XOR: convert to MOV if immediate, or if xoring against 0 - case OP_XOR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() ^ m_param[2].immediate()); - else if (m_param[1].is_immediate_value(0)) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // LZCNT: convert to MOV if immediate - case OP_LZCNT: - if (m_param[1].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(count_leading_zeros_32(m_param[1].immediate())); - else if (m_size == 8) - convert_to_mov_immediate(count_leading_zeros_64(m_param[1].immediate())); - } - break; - - // BSWAP: convert to MOV if immediate - case OP_BSWAP: - if (m_param[1].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(swapendian_int32(m_param[1].immediate())); - else if (m_size == 8) - convert_to_mov_immediate(swapendian_int64(m_param[1].immediate())); - } - break; - - // SHL: convert to MOV if immediate or shifting by 0 - case OP_SHL: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(m_param[1].immediate()) << (m_param[2].immediate() & 31)); - else if (m_size == 8) - convert_to_mov_immediate(u64(m_param[1].immediate()) << (m_param[2].immediate() & 63)); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // SHR: convert to MOV if immediate or shifting by 0 - case OP_SHR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(m_param[1].immediate()) >> (m_param[2].immediate() & 31)); - else if (m_size == 8) - convert_to_mov_immediate(u64(m_param[1].immediate()) >> (m_param[2].immediate() & 63)); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // SAR: convert to MOV if immediate or shifting by 0 - case OP_SAR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(m_param[1].immediate()) >> (m_param[2].immediate() & 31)); - else if (m_size == 8) - convert_to_mov_immediate(s64(m_param[1].immediate()) >> (m_param[2].immediate() & 63)); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // ROL: convert to NOP if immediate or rotating by 0 - case OP_ROL: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(rotl_32(m_param[1].immediate(), m_param[2].immediate() & 31)); - else if (m_size == 8) - convert_to_mov_immediate(rotl_64(m_param[1].immediate(), m_param[2].immediate() & 63)); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // ROR: convert to NOP if immediate or rotating by 0 - case OP_ROR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(rotr_32(m_param[1].immediate(), m_param[2].immediate() & 31)); - else if (m_size == 8) - convert_to_mov_immediate(rotr_64(m_param[1].immediate(), m_param[2].immediate() & 63)); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // FMOV: convert to NOP if move-to-self - case OP_FMOV: - if (m_param[0] == m_param[1]) - nop(); - break; - - default: - break; + case OP_READ: simplify_op::read(*this); break; + case OP_READM: simplify_op::readm(*this); break; + case OP_WRITE: simplify_op::write(*this); break; + case OP_WRITEM: simplify_op::writem(*this); break; + case OP_SET: simplify_op::set(*this); break; + case OP_MOV: simplify_op::mov(*this); break; + case OP_SEXT: simplify_op::sext(*this); break; + case OP_ROLAND: simplify_op::roland(*this); break; + case OP_ROLINS: simplify_op::rolins(*this); break; + case OP_ADD: simplify_op::add(*this); break; + case OP_ADDC: simplify_op::truncate_imm(*this); break; + case OP_SUB: simplify_op::sub(*this); break; + case OP_SUBB: simplify_op::truncate_imm(*this); break; + case OP_CMP: simplify_op::cmp(*this); break; + case OP_MULU: simplify_op::mul<OP_MULULW>(*this); break; + case OP_MULULW: simplify_op::mullw<u32, u64>(*this); break; + case OP_MULS: simplify_op::mul<OP_MULSLW>(*this); break; + case OP_MULSLW: simplify_op::mullw<s32, s64>(*this); break; + case OP_DIVU: simplify_op::div<u32, u64>(*this); break; + case OP_DIVS: simplify_op::div<s32, s64>(*this); break; + case OP_AND: simplify_op::_and(*this); break; + case OP_TEST: simplify_op::test(*this); break; + case OP_OR: simplify_op::_or(*this); break; + case OP_XOR: simplify_op::_xor(*this); break; + case OP_LZCNT: simplify_op::lzcnt(*this); break; + case OP_TZCNT: simplify_op::truncate_imm(*this); break; + case OP_BSWAP: simplify_op::bswap(*this); break; + case OP_SHL: simplify_op::shl(*this); break; + case OP_SHR: simplify_op::shr<u32, u64>(*this); break; + case OP_SAR: simplify_op::shr<s32, s64>(*this); break; + case OP_ROL: simplify_op::rol(*this); break; + case OP_ROLC: simplify_op::rolrc(*this); break; + case OP_ROR: simplify_op::ror(*this); break; + case OP_RORC: simplify_op::rolrc(*this); break; + + default: break; } - /* - if (LOG_SIMPLIFICATIONS && memcmp(&orig, inst, sizeof(orig)) != 0) - { - std::string disasm1 = orig.disasm(block->drcuml); - std::string disasm2 = inst->disasm(block->drcuml); - osd_printf_debug("Simplified: %-50.50s -> %s\n", disasm1, disasm2); - } - */ +#if 0 + if (LOG_SIMPLIFICATIONS && memcmp(&orig, inst, sizeof(orig)) != 0) + { + std::string disasm1 = orig.disasm(block->drcuml); + std::string disasm2 = inst->disasm(block->drcuml); + osd_printf_debug("Simplified: %-50.50s -> %s\n", disasm1, disasm2); + } +#endif // loop until we stop changing opcodes - } while (m_opcode != origop); + } + while (m_opcode != origop); } diff --git a/src/devices/cpu/uml.h b/src/devices/cpu/uml.h index 9577b393fcd..5aefea60e6c 100644 --- a/src/devices/cpu/uml.h +++ b/src/devices/cpu/uml.h @@ -222,7 +222,7 @@ namespace uml }; // C function callback definition - typedef void (*c_function)(void *ptr); + using c_function = void (*)(void *ptr); // class describing a global code handle class code_handle @@ -585,8 +585,6 @@ namespace uml // opcode validation and simplification void validate(); - void convert_to_mov_immediate(u64 immediate) { m_opcode = OP_MOV; m_numparams = 2; m_param[1] = immediate; } - void convert_to_mov_param(int pnum) { m_opcode = OP_MOV; m_numparams = 2; m_param[1] = m_param[pnum]; } // internal state opcode_t m_opcode = OP_INVALID; // opcode @@ -597,6 +595,7 @@ namespace uml parameter m_param[MAX_PARAMS]; // up to 4 parameters static opcode_info const s_opcode_info_table[OP_MAX]; + struct simplify_op; }; // structure describing rules for parameter encoding |