diff options
author | 2025-04-10 01:58:08 +1000 | |
---|---|---|
committer | 2025-04-10 01:58:08 +1000 | |
commit | 51c9a3a0b1ef73611120e449e622ada315bc470f (patch) | |
tree | 840d54793f49622d9b8a2069bc539d191ac5f540 | |
parent | cff4924beb77418816a72b2dfe7e833eeff93040 (diff) |
Still more recompiler fixes:
* cpu/uml.cpp: Fixed some cases where simplifying multiplication
instructons incorrectly changed output flags.
* cpu/drcbex64.cpp: Fixed some cases where a ROLC/RORC with zero shift
count could incorrectly clear the carry flag.
* cpu/drcbex86.cpp: Made 64-bit ROLC/RORC with zero bit count preserve
the carry flag.
* cpu/drcbec.cpp: Fixed FSMOV/FDMOV turning things that look like
signalling NaNs into quiet NaNs on i686.
* cpu/drcbex64.cpp: Greatly reduced copy/paste in the code generation
for multiply instructions.
-rw-r--r-- | src/devices/cpu/drcbec.cpp | 4 | ||||
-rw-r--r-- | src/devices/cpu/drcbex64.cpp | 332 | ||||
-rw-r--r-- | src/devices/cpu/drcbex86.cpp | 39 | ||||
-rw-r--r-- | src/devices/cpu/uml.cpp | 146 |
4 files changed, 199 insertions, 322 deletions
diff --git a/src/devices/cpu/drcbec.cpp b/src/devices/cpu/drcbec.cpp index cc4ab25f965..757ba9b28e9 100644 --- a/src/devices/cpu/drcbec.cpp +++ b/src/devices/cpu/drcbec.cpp @@ -2093,7 +2093,7 @@ int drcbe_c::execute(code_handle &entry) [[fallthrough]]; case MAKE_OPCODE_SHORT(OP_FMOV, 4, 0): - FSPARAM0 = FSPARAM1; + PARAM0 = PARAM1; break; case MAKE_OPCODE_SHORT(OP_FTOI4T, 4, 0): // FSTOI4T dst,src1 @@ -2236,7 +2236,7 @@ int drcbe_c::execute(code_handle &entry) [[fallthrough]]; case MAKE_OPCODE_SHORT(OP_FMOV, 8, 0): - FDPARAM0 = FDPARAM1; + DPARAM0 = DPARAM1; break; case MAKE_OPCODE_SHORT(OP_FTOI4T, 8, 0): // FDTOI4T dst,src1 diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp index c07d07d158d..cc1fe4fc952 100644 --- a/src/devices/cpu/drcbex64.cpp +++ b/src/devices/cpu/drcbex64.cpp @@ -547,10 +547,8 @@ private: void op_sub(Assembler &a, const uml::instruction &inst); void op_subc(Assembler &a, const uml::instruction &inst); void op_cmp(Assembler &a, const uml::instruction &inst); - void op_mulu(Assembler &a, const uml::instruction &inst); - void op_mululw(Assembler &a, const uml::instruction &inst); - void op_muls(Assembler &a, const uml::instruction &inst); - void op_mulslw(Assembler &a, const uml::instruction &inst); + template <Inst::Id Opcode> void op_mul(Assembler &a, const uml::instruction &inst); + template <Inst::Id Opcode> void op_mullw(Assembler &a, const uml::instruction &inst); void op_divu(Assembler &a, const uml::instruction &inst); void op_divs(Assembler &a, const uml::instruction &inst); void op_and(Assembler &a, const uml::instruction &inst); @@ -674,44 +672,44 @@ inline void drcbe_x64::generate_one(Assembler &a, const uml::instruction &inst) case uml::OP_RESTORE: op_restore(a, inst); break; // RESTORE dst // Integer Operations - case uml::OP_LOAD: op_load(a, inst); break; // LOAD dst,base,index,size - case uml::OP_LOADS: op_loads(a, inst); break; // LOADS dst,base,index,size - case uml::OP_STORE: op_store(a, inst); break; // STORE base,index,src,size - case uml::OP_READ: op_read(a, inst); break; // READ dst,src1,spacesize - case uml::OP_READM: op_readm(a, inst); break; // READM dst,src1,mask,spacesize - case uml::OP_WRITE: op_write(a, inst); break; // WRITE dst,src1,spacesize - case uml::OP_WRITEM: op_writem(a, inst); break; // WRITEM dst,src1,spacesize - case uml::OP_CARRY: op_carry(a, inst); break; // CARRY src,bitnum - case uml::OP_SET: op_set(a, inst); break; // SET dst,c - case uml::OP_MOV: op_mov(a, inst); break; // MOV dst,src[,c] - case uml::OP_SEXT: op_sext(a, inst); break; // SEXT dst,src - case uml::OP_ROLAND: op_roland(a, inst); break; // ROLAND dst,src1,src2,src3 - case uml::OP_ROLINS: op_rolins(a, inst); break; // ROLINS dst,src1,src2,src3 - case uml::OP_ADD: op_add(a, inst); break; // ADD dst,src1,src2[,f] - case uml::OP_ADDC: op_addc(a, inst); break; // ADDC dst,src1,src2[,f] - case uml::OP_SUB: op_sub(a, inst); break; // SUB dst,src1,src2[,f] - case uml::OP_SUBB: op_subc(a, inst); break; // SUBB dst,src1,src2[,f] - case uml::OP_CMP: op_cmp(a, inst); break; // CMP src1,src2[,f] - case uml::OP_MULU: op_mulu(a, inst); break; // MULU dst,edst,src1,src2[,f] - case uml::OP_MULULW: op_mululw(a, inst); break; // MULULW dst,src1,src2[,f] - case uml::OP_MULS: op_muls(a, inst); break; // MULS dst,edst,src1,src2[,f] - case uml::OP_MULSLW: op_mulslw(a, inst); break; // MULSLW dst,src1,src2[,f] - case uml::OP_DIVU: op_divu(a, inst); break; // DIVU dst,edst,src1,src2[,f] - case uml::OP_DIVS: op_divs(a, inst); break; // DIVS dst,edst,src1,src2[,f] - case uml::OP_AND: op_and(a, inst); break; // AND dst,src1,src2[,f] - case uml::OP_TEST: op_test(a, inst); break; // TEST src1,src2[,f] - case uml::OP_OR: op_or(a, inst); break; // OR dst,src1,src2[,f] - case uml::OP_XOR: op_xor(a, inst); break; // XOR dst,src1,src2[,f] - case uml::OP_LZCNT: op_lzcnt(a, inst); break; // LZCNT dst,src[,f] - case uml::OP_TZCNT: op_tzcnt(a, inst); break; // TZCNT dst,src[,f] - case uml::OP_BSWAP: op_bswap(a, inst); break; // BSWAP dst,src - case uml::OP_SHL: op_shift<Inst::kIdShl>(a, inst); break; // SHL dst,src,count[,f] - case uml::OP_SHR: op_shift<Inst::kIdShr>(a, inst); break; // SHR dst,src,count[,f] - case uml::OP_SAR: op_shift<Inst::kIdSar>(a, inst); break; // SAR dst,src,count[,f] - case uml::OP_ROL: op_shift<Inst::kIdRol>(a, inst); break; // ROL dst,src,count[,f] - case uml::OP_ROLC: op_shift<Inst::kIdRcl>(a, inst); break; // ROLC dst,src,count[,f] - case uml::OP_ROR: op_shift<Inst::kIdRor>(a, inst); break; // ROR dst,src,count[,f] - case uml::OP_RORC: op_shift<Inst::kIdRcr>(a, inst); break; // RORC dst,src,count[,f] + case uml::OP_LOAD: op_load(a, inst); break; // LOAD dst,base,index,size + case uml::OP_LOADS: op_loads(a, inst); break; // LOADS dst,base,index,size + case uml::OP_STORE: op_store(a, inst); break; // STORE base,index,src,size + case uml::OP_READ: op_read(a, inst); break; // READ dst,src1,spacesize + case uml::OP_READM: op_readm(a, inst); break; // READM dst,src1,mask,spacesize + case uml::OP_WRITE: op_write(a, inst); break; // WRITE dst,src1,spacesize + case uml::OP_WRITEM: op_writem(a, inst); break; // WRITEM dst,src1,spacesize + case uml::OP_CARRY: op_carry(a, inst); break; // CARRY src,bitnum + case uml::OP_SET: op_set(a, inst); break; // SET dst,c + case uml::OP_MOV: op_mov(a, inst); break; // MOV dst,src[,c] + case uml::OP_SEXT: op_sext(a, inst); break; // SEXT dst,src + case uml::OP_ROLAND: op_roland(a, inst); break; // ROLAND dst,src1,src2,src3 + case uml::OP_ROLINS: op_rolins(a, inst); break; // ROLINS dst,src1,src2,src3 + case uml::OP_ADD: op_add(a, inst); break; // ADD dst,src1,src2[,f] + case uml::OP_ADDC: op_addc(a, inst); break; // ADDC dst,src1,src2[,f] + case uml::OP_SUB: op_sub(a, inst); break; // SUB dst,src1,src2[,f] + case uml::OP_SUBB: op_subc(a, inst); break; // SUBB dst,src1,src2[,f] + case uml::OP_CMP: op_cmp(a, inst); break; // CMP src1,src2[,f] + case uml::OP_MULU: op_mul<Inst::kIdMul>(a, inst); break; // MULU dst,edst,src1,src2[,f] + case uml::OP_MULULW: op_mullw<Inst::kIdMul>(a, inst); break; // MULULW dst,src1,src2[,f] + case uml::OP_MULS: op_mul<Inst::kIdImul>(a, inst); break; // MULS dst,edst,src1,src2[,f] + case uml::OP_MULSLW: op_mullw<Inst::kIdImul>(a, inst); break; // MULSLW dst,src1,src2[,f] + case uml::OP_DIVU: op_divu(a, inst); break; // DIVU dst,edst,src1,src2[,f] + case uml::OP_DIVS: op_divs(a, inst); break; // DIVS dst,edst,src1,src2[,f] + case uml::OP_AND: op_and(a, inst); break; // AND dst,src1,src2[,f] + case uml::OP_TEST: op_test(a, inst); break; // TEST src1,src2[,f] + case uml::OP_OR: op_or(a, inst); break; // OR dst,src1,src2[,f] + case uml::OP_XOR: op_xor(a, inst); break; // XOR dst,src1,src2[,f] + case uml::OP_LZCNT: op_lzcnt(a, inst); break; // LZCNT dst,src[,f] + case uml::OP_TZCNT: op_tzcnt(a, inst); break; // TZCNT dst,src[,f] + case uml::OP_BSWAP: op_bswap(a, inst); break; // BSWAP dst,src + case uml::OP_SHL: op_shift<Inst::kIdShl>(a, inst); break; // SHL dst,src,count[,f] + case uml::OP_SHR: op_shift<Inst::kIdShr>(a, inst); break; // SHR dst,src,count[,f] + case uml::OP_SAR: op_shift<Inst::kIdSar>(a, inst); break; // SAR dst,src,count[,f] + case uml::OP_ROL: op_shift<Inst::kIdRol>(a, inst); break; // ROL dst,src,count[,f] + case uml::OP_ROLC: op_shift<Inst::kIdRcl>(a, inst); break; // ROLC dst,src,count[,f] + case uml::OP_ROR: op_shift<Inst::kIdRor>(a, inst); break; // ROR dst,src,count[,f] + case uml::OP_RORC: op_shift<Inst::kIdRcr>(a, inst); break; // RORC dst,src,count[,f] // Floating Point Operations case uml::OP_FLOAD: op_fload(a, inst); break; // FLOAD dst,base,index @@ -1585,7 +1583,7 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz mov_reg_param(a, shift, param); - a.and_(shift, opsize * 8 - 1); + a.and_(shift, (opsize * 8) - 1); if ((update_flags & FLAG_C) || carryin) { @@ -1595,9 +1593,14 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz a.short_().jnz(calc); if (carryin) + { + a.sahf(); // restore flags to keep carry in for rolc/rorc a.mov(rax, r10); + } else if (update_flags & FLAG_C) + { a.clc(); // throw away carry since it'll never be used + } a.short_().jmp(end); @@ -4397,10 +4400,11 @@ void drcbe_x64::op_cmp(Assembler &a, const instruction &inst) //------------------------------------------------- -// op_mulu - process a MULU opcode +// op_mul - process a MULU or MULS opcode //------------------------------------------------- -void drcbe_x64::op_mulu(Assembler &a, const instruction &inst) +template <Inst::Id Opcode> +void drcbe_x64::op_mul(Assembler &a, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); @@ -4413,179 +4417,29 @@ void drcbe_x64::op_mulu(Assembler &a, const instruction &inst) be_parameter src1p(*this, inst.param(2), PTYPE_MRI); be_parameter src2p(*this, inst.param(3), PTYPE_MRI); normalize_commutative(src1p, src2p); - bool compute_hi = (dstp != edstp); - - // 32-bit form - if (inst.size() == 4) - { - Gp dstreg = eax; - Gp edstreg = edx; - - // general case - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.mul(MABS(src2p.memory(), 4)); - else if (src2p.is_int_register()) - a.mul(Gpd(src2p.ireg())); - else if (src2p.is_immediate()) - { - a.mov(edstreg, src2p.immediate()); - a.mul(edstreg); - } - mov_param_reg(a, dstp, dstreg); - if (compute_hi) - mov_param_reg(a, edstp, edstreg); - } + const bool compute_hi = (dstp != edstp); - // 64-bit form - else if (inst.size() == 8) - { - Gp dstreg = rax; - Gp edstreg = rdx; + const Gp dstreg = (inst.size() == 4) ? Gp(eax) : Gp(rax); + const Gp edstreg = (inst.size() == 4) ? Gp(edx) : Gp(rdx); - // general case - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.mul(MABS(src2p.memory(), 8)); - else if (src2p.is_int_register()) - a.mul(Gpq(src2p.ireg())); - else if (src2p.is_immediate()) - { - mov_r64_imm(a, edstreg, src2p.immediate()); - a.mul(edstreg); - } - mov_param_reg(a, dstp, dstreg); - if (compute_hi) - mov_param_reg(a, edstp, edstreg); - } - - if (inst.flags()) - calculate_status_flags_mul(a, inst.size(), rax, rdx); -} - - -//------------------------------------------------- -// op_mululw - process a MULULW (32x32=32) opcode -//------------------------------------------------- - -void drcbe_x64::op_mululw(Assembler &a, const instruction &inst) -{ - // validate instruction - assert(inst.size() == 4 || inst.size() == 8); - assert_no_condition(inst); - assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S); - - // normalize parameters - be_parameter dstp(*this, inst.param(0), PTYPE_MR); - be_parameter src1p(*this, inst.param(1), PTYPE_MRI); - be_parameter src2p(*this, inst.param(2), PTYPE_MRI); - normalize_commutative(src1p, src2p); - - // 32-bit form - if (inst.size() == 4) - { - Gp dstreg = eax; - Gp hireg = edx; - - // general case - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.mul(MABS(src2p.memory(), 4)); - else if (src2p.is_int_register()) - a.mul(Gpd(src2p.ireg())); - else if (src2p.is_immediate()) - { - a.mov(hireg, src2p.immediate()); - a.mul(hireg); - } - mov_param_reg(a, dstp, dstreg); - } - - // 64-bit form - else if (inst.size() == 8) + // general case + mov_reg_param(a, dstreg, src1p); + if (src2p.is_memory()) { - Gp dstreg = rax; - Gp hireg = rdx; - - // general case - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.mul(MABS(src2p.memory(), 8)); - else if (src2p.is_int_register()) - a.mul(Gpq(src2p.ireg())); - else if (src2p.is_immediate()) - { - mov_r64_imm(a, hireg, src2p.immediate()); - a.mul(hireg); - } - mov_param_reg(a, dstp, dstreg); + a.emit(Opcode, MABS(src2p.memory(), inst.size())); } - - if (inst.flags()) - calculate_status_flags_mul_low(a, inst.size(), rax); -} - - -//------------------------------------------------- -// op_muls - process a MULS opcode -//------------------------------------------------- - -void drcbe_x64::op_muls(Assembler &a, const instruction &inst) -{ - // validate instruction - assert(inst.size() == 4 || inst.size() == 8); - assert_no_condition(inst); - assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S); - - // normalize parameters - be_parameter dstp(*this, inst.param(0), PTYPE_MR); - be_parameter edstp(*this, inst.param(1), PTYPE_MR); - be_parameter src1p(*this, inst.param(2), PTYPE_MRI); - be_parameter src2p(*this, inst.param(3), PTYPE_MRI); - normalize_commutative(src1p, src2p); - bool compute_hi = (dstp != edstp); - - // 32-bit form - if (inst.size() == 4) + else if (src2p.is_int_register()) { - Gp dstreg = eax; - Gp edstreg = edx; - - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.imul(MABS(src2p.memory(), 4)); - else if (src2p.is_int_register()) - a.imul(Gpd(src2p.ireg())); - else if (src2p.is_immediate()) - { - a.mov(edstreg, src2p.immediate()); - a.imul(edstreg); - } - mov_param_reg(a, dstp, dstreg); - if (compute_hi) - mov_param_reg(a, edstp, edstreg); + a.emit(Opcode, Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src2p.ireg())); } - - // 64-bit form - else if (inst.size() == 8) + else if (src2p.is_immediate()) { - Gp dstreg = rax; - Gp edstreg = rdx; - - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.imul(MABS(src2p.memory(), 8)); - else if (src2p.is_int_register()) - a.imul(Gpq(src2p.ireg())); - else if (src2p.is_immediate()) - { - mov_r64_imm(a, edstreg, src2p.immediate()); - a.imul(edstreg); - } - mov_param_reg(a, dstp, dstreg); - if (compute_hi) - mov_param_reg(a, edstp, edstreg); + a.mov(edstreg, src2p.immediate()); + a.emit(Opcode, edstreg); } + mov_param_reg(a, dstp, dstreg); + if (compute_hi) + mov_param_reg(a, edstp, edstreg); if (inst.flags()) calculate_status_flags_mul(a, inst.size(), rax, rdx); @@ -4593,10 +4447,12 @@ void drcbe_x64::op_muls(Assembler &a, const instruction &inst) //------------------------------------------------- -// op_mulslw - process a MULSLW (32x32=32) opcode +// op_mullw - process a MULULW or MULSLW +// (32x32=32) opcode //------------------------------------------------- -void drcbe_x64::op_mulslw(Assembler &a, const instruction &inst) +template <Inst::Id Opcode> +void drcbe_x64::op_mullw(Assembler &a, const instruction &inst) { // validate instruction assert(inst.size() == 4 || inst.size() == 8); @@ -4609,43 +4465,25 @@ void drcbe_x64::op_mulslw(Assembler &a, const instruction &inst) be_parameter src2p(*this, inst.param(2), PTYPE_MRI); normalize_commutative(src1p, src2p); - // 32-bit form - if (inst.size() == 4) - { - Gp dstreg = eax; - Gp hireg = edx; + const Gp dstreg = (inst.size() == 4) ? Gp(eax) : Gp(rax); + const Gp hireg = (inst.size() == 4) ? Gp(edx) : Gp(rdx); - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.imul(MABS(src2p.memory(), 4)); - else if (src2p.is_int_register()) - a.imul(Gpd(src2p.ireg())); - else if (src2p.is_immediate()) - { - a.mov(hireg, src2p.immediate()); - a.imul(hireg); - } - mov_param_reg(a, dstp, dstreg); + // general case + mov_reg_param(a, dstreg, src1p); + if (src2p.is_memory()) + { + a.emit(Opcode, MABS(src2p.memory(), inst.size())); } - - // 64-bit form - else if (inst.size() == 8) + else if (src2p.is_int_register()) { - Gp dstreg = rax; - Gp hireg = rdx; - - mov_reg_param(a, dstreg, src1p); - if (src2p.is_memory()) - a.imul(MABS(src2p.memory(), 8)); - else if (src2p.is_int_register()) - a.imul(Gpq(src2p.ireg())); - else if (src2p.is_immediate()) - { - mov_r64_imm(a, hireg, src2p.immediate()); - a.imul(hireg); - } - mov_param_reg(a, dstp, dstreg); + a.emit(Opcode, Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src2p.ireg())); } + else if (src2p.is_immediate()) + { + a.mov(hireg, src2p.immediate()); + a.emit(Opcode, hireg); + } + mov_param_reg(a, dstp, dstreg); if (inst.flags()) calculate_status_flags_mul_low(a, inst.size(), rax); @@ -5189,7 +5027,7 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst) be_parameter src1p(*this, inst.param(1), PTYPE_MRI); be_parameter src2p(*this, inst.param(2), PTYPE_MRI); - const bool carry = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr); + const bool carryin = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr); if (dstp.is_memory() && ((inst.size() == 8) || !dstp.is_cold_register()) && (dstp == src1p)) { @@ -5203,7 +5041,7 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst) // pick a target register const Gp dstreg = dstp.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax), src2p); - if (carry) + if (carryin) mov_reg_param(a, dstreg, src1p, true); else mov_reg_param(a, dstreg, src1p); diff --git a/src/devices/cpu/drcbex86.cpp b/src/devices/cpu/drcbex86.cpp index 5109bab556b..5813108c2bc 100644 --- a/src/devices/cpu/drcbex86.cpp +++ b/src/devices/cpu/drcbex86.cpp @@ -2633,10 +2633,9 @@ void drcbe_x86::emit_rcl_r64_p64(Assembler &a, Gp const ®lo, Gp const ®hi, Label loop = a.newLabel(); Label skipall = a.newLabel(); Label skiploop = a.newLabel(); - Label end = a.newLabel(); a.pushfd(); // keep carry flag after and - emit_mov_r32_p32_keepflags(a, ecx, param); + emit_mov_r32_p32(a, ecx, param); a.and_(ecx, 63); a.popfd(); @@ -2656,27 +2655,18 @@ void drcbe_x86::emit_rcl_r64_p64(Assembler &a, Gp const ®lo, Gp const ®hi, a.rcl(reglo, 1); a.rcl(reghi, 1); - if (inst.flags()) - { - calculate_status_flags(a, reglo, FLAG_Z); - a.pushfd(); - calculate_status_flags(a, reghi, FLAG_S | FLAG_Z); - emit_combine_z_flags(a); - - a.short_().jmp(end); - } - a.bind(skipall); - if (inst.flags()) { - a.test(reglo, reglo); + if (inst.flags() & FLAG_C) + calculate_status_flags(a, reglo, FLAG_Z); + else + a.test(reglo, reglo); a.pushfd(); calculate_status_flags(a, reghi, FLAG_S | FLAG_Z); emit_combine_z_flags(a); } - a.bind(end); reset_last_upper_lower_reg(); } @@ -2691,10 +2681,9 @@ void drcbe_x86::emit_rcr_r64_p64(Assembler &a, Gp const ®lo, Gp const ®hi, Label loop = a.newLabel(); Label skipall = a.newLabel(); Label skiploop = a.newLabel(); - Label end = a.newLabel(); a.pushfd(); // keep carry flag after and - emit_mov_r32_p32_keepflags(a, ecx, param); + emit_mov_r32_p32(a, ecx, param); a.and_(ecx, 63); a.popfd(); @@ -2714,26 +2703,18 @@ void drcbe_x86::emit_rcr_r64_p64(Assembler &a, Gp const ®lo, Gp const ®hi, a.rcr(reghi, 1); a.rcr(reglo, 1); - if (inst.flags()) - { - calculate_status_flags(a, reglo, FLAG_Z); - a.pushfd(); - calculate_status_flags(a, reghi, FLAG_S | FLAG_Z); - emit_combine_z_flags(a); - - a.short_().jmp(end); - } - a.bind(skipall); if (inst.flags()) { - a.test(reglo, reglo); + if (inst.flags() & FLAG_C) + calculate_status_flags(a, reglo, FLAG_Z); + else + a.test(reglo, reglo); a.pushfd(); calculate_status_flags(a, reghi, FLAG_S | FLAG_Z); emit_combine_z_flags(a); } - a.bind(end); reset_last_upper_lower_reg(); } diff --git a/src/devices/cpu/uml.cpp b/src/devices/cpu/uml.cpp index d88b30ddcfe..46bf06f3306 100644 --- a/src/devices/cpu/uml.cpp +++ b/src/devices/cpu/uml.cpp @@ -33,8 +33,12 @@ #include "emu.h" #include "drcuml.h" + #include "drcumlsh.h" +#include <type_traits> + + using namespace uml; @@ -591,11 +595,13 @@ public: truncate_immediate(inst, 1, mask); } - template <opcode_t LoWordOp> + template <opcode_t LoWordOp, typename Short, typename Long> static void mul(instruction &inst) { - // if the two destination operands are identical, convert to MUL.LW - if (inst.param(0) == inst.param(1)) + bool const low_only = inst.param(0) == inst.param(1); + + // if the two destination operands are identical and the S and Z flags aren't required, convert to MUL.LW + if (low_only && !(inst.flags() & (FLAG_Z | FLAG_S))) { inst.m_opcode = LoWordOp; inst.m_param[1] = inst.param(2); @@ -616,6 +622,48 @@ public: using std::swap; swap(inst.m_param[2], inst.m_param[3]); } + + // can only simplify the low-only form, can't optimise overflow flag generation + if (!low_only || (inst.flags() & FLAG_V)) + return; + + if (inst.param(2).is_immediate_value(0) || inst.param(3).is_immediate_value(0)) + { + // multiplying anything by zero 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 + auto const size = inst.size(); + auto const bits = size << 3; + assert((size == 4) || (size == 8)); + + if (size == 4) + { + Short const param2 = Short(u32(inst.param(2).immediate())); + Short const param3 = Short(u32(inst.param(3).immediate())); + Short const val = param2 * param3; + bool const no_overflow = (val / param2) == param3; + bool const z_ok = !(inst.flags() & FLAG_Z) || no_overflow; + bool const s_ok = !(inst.flags() & FLAG_S) || (std::is_signed_v<Short> ? !BIT(param2 ^ param3 ^ val, bits - 1) : (no_overflow && !BIT(val, bits - 1))); + + if (z_ok && s_ok) + convert_to_mov_immediate(inst, u32(val)); + } + else + { + Long const param2 = Long(inst.param(2).immediate()); + Long const param3 = Long(inst.param(3).immediate()); + Long const val = param2 * param3; + bool const no_overflow = (val / param2) == param3; + bool const z_ok = !(inst.flags() & FLAG_Z) || no_overflow; + bool const s_ok = !(inst.flags() & FLAG_S) || (std::is_signed_v<Long> ? !BIT(param2 ^ param3 ^ val, bits - 1) : (no_overflow && !BIT(val, bits - 1))); + + if (z_ok && s_ok) + convert_to_mov_immediate(inst, u64(val)); + } + } } template <typename Short, typename Long> @@ -640,9 +688,19 @@ public: 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())))); + { + Short const param1 = Short(u32(inst.param(1).immediate())); + Short const param2 = Short(u32(inst.param(2).immediate())); + Short const val = param1 * param2; + convert_to_mov_immediate(inst, u32(val)); + } else - convert_to_mov_immediate(inst, u64(Long(inst.param(1).immediate()) * Long(inst.param(2).immediate()))); + { + Long const param1 = Long(inst.param(1).immediate()); + Long const param2 = Long(inst.param(2).immediate()); + Long const val = param1 * param2; + convert_to_mov_immediate(inst, u64(val)); + } } } @@ -660,15 +718,15 @@ public: 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)) + // optimise the quotient-only form with two immediate inputs if not dividing by zero + if ((inst.param(0) == inst.param(1)) && inst.param(2).is_immediate() && inst.param(3).is_immediate() && !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()) + else { // convert constant result to MOV or a logic op assert((size == 4) || (size == 8)); @@ -1138,42 +1196,42 @@ void uml::instruction::simplify() origop = m_opcode; switch (m_opcode) { - 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; + 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, u32, u64>(*this); break; + case OP_MULULW: simplify_op::mullw<u32, u64>(*this); break; + case OP_MULS: simplify_op::mul<OP_MULSLW, s32, s64>(*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 0 |