diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3692c24a..71736a49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,7 @@ set(SRCS frontend/translate/translate_arm/exception_generating.cpp frontend/translate/translate_arm/extension.cpp frontend/translate/translate_arm/load_store.cpp + frontend/translate/translate_arm/misc.cpp frontend/translate/translate_arm/multiply.cpp frontend/translate/translate_arm/packing.cpp frontend/translate/translate_arm/parallel.cpp diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index a5095e12..63be3014 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -1540,6 +1540,27 @@ void EmitX64::EmitPackedSaturatedSubS16(IR::Block& block, IR::Inst* inst) { EmitPackedOperation(code, reg_alloc, inst, &Xbyak::CodeGenerator::psubsw); } +void EmitX64::EmitCountLeadingZeros(IR::Block& block, IR::Inst* inst) { + IR::Value a = inst->GetArg(0); + + if (cpu_info.has(Xbyak::util::Cpu::tLZCNT)) { + Xbyak::Reg32 source = reg_alloc.UseGpr(a).cvt32(); + Xbyak::Reg32 result = reg_alloc.DefGpr(inst).cvt32(); + + code->lzcnt(result, source); + } else { + Xbyak::Reg32 source = reg_alloc.UseScratchGpr(a).cvt32(); + Xbyak::Reg32 result = reg_alloc.DefGpr(inst).cvt32(); + + // The result of a bsr of zero is undefined, but zf is set after it. + code->bsr(result, source); + code->mov(source, 0xFFFFFFFF); + code->cmovz(result, source); + code->neg(result); + code->add(result, 31); + } +} + static void DenormalsAreZero32(BlockOfCode* code, Xbyak::Xmm xmm_value, Xbyak::Reg32 gpr_scratch) { using namespace Xbyak::util; Xbyak::Label end; diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 7d2d7120..11e1bb2b 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -386,6 +386,10 @@ Value IREmitter::PackedSaturatedSubS16(const Value& a, const Value& b) { return Inst(Opcode::PackedSaturatedSubS16, {a, b}); } +Value IREmitter::CountLeadingZeros(const Value& a) { + return Inst(Opcode::CountLeadingZeros, {a}); +} + Value IREmitter::TransferToFP32(const Value& a) { return Inst(Opcode::TransferToFP32, {a}); } diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index dca52ebc..908aef64 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -142,6 +142,7 @@ public: Value PackedSaturatedAddS16(const Value& a, const Value& b); Value PackedSaturatedSubU16(const Value& a, const Value& b); Value PackedSaturatedSubS16(const Value& a, const Value& b); + Value CountLeadingZeros(const Value& a); Value TransferToFP32(const Value& a); Value TransferToFP64(const Value& a); diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 39eef49a..a2f5c3c5 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -87,6 +87,7 @@ OPCODE(PackedSaturatedAddU16, T::U32, T::U32, T::U32 OPCODE(PackedSaturatedAddS16, T::U32, T::U32, T::U32 ) OPCODE(PackedSaturatedSubU16, T::U32, T::U32, T::U32 ) OPCODE(PackedSaturatedSubS16, T::U32, T::U32, T::U32 ) +OPCODE(CountLeadingZeros, T::U32, T::U32 ) // Floating-point operations OPCODE(TransferToFP32, T::F32, T::U32 ) diff --git a/src/frontend/translate/translate_arm/misc.cpp b/src/frontend/translate/translate_arm/misc.cpp new file mode 100644 index 00000000..1892458d --- /dev/null +++ b/src/frontend/translate/translate_arm/misc.cpp @@ -0,0 +1,22 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2016 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "translate_arm.h" + +namespace Dynarmic { +namespace Arm { + +bool ArmTranslatorVisitor::arm_CLZ(Cond cond, Reg d, Reg m) { + if (d == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + if (ConditionPassed(cond)) { + ir.SetRegister(d, ir.CountLeadingZeros(ir.GetRegister(m))); + } + return true; +} + +} // namespace Arm +} // namespace Dynarmic diff --git a/src/frontend/translate/translate_arm/translate_arm.h b/src/frontend/translate/translate_arm/translate_arm.h index 664a8345..7687faa2 100644 --- a/src/frontend/translate/translate_arm/translate_arm.h +++ b/src/frontend/translate/translate_arm/translate_arm.h @@ -209,7 +209,7 @@ struct ArmTranslatorVisitor final { bool arm_STM_usr(); // Miscellaneous instructions - bool arm_CLZ(Cond cond, Reg d, Reg m) { return InterpretThisInstruction(); } + bool arm_CLZ(Cond cond, Reg d, Reg m); bool arm_NOP() { return true; } bool arm_SEL(Cond cond, Reg n, Reg d, Reg m); diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index b703057a..70b2ad91 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -947,7 +947,7 @@ TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") { }); } -TEST_CASE("Test ARM SEL instruction", "[JitX64]") { +TEST_CASE("Test ARM misc instructions", "[JitX64]") { const auto is_sel_valid = [](u32 instr) -> bool { // R15 as Rd, Rn, or Rm is UNPREDICTABLE return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111; @@ -958,8 +958,14 @@ TEST_CASE("Test ARM SEL instruction", "[JitX64]") { return Bits<18, 19>(instr) != 0b00; }; + const auto is_clz_valid = [](u32 instr) -> bool { + // R15 as Rd, or Rm is UNPREDICTABLE + return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; + }; + const InstructionGenerator cpsr_setter = InstructionGenerator("11100011001001001111rrrrvvvvvvvv", is_msr_valid); // MSR_Imm write GE const InstructionGenerator sel_instr = InstructionGenerator("111001101000nnnndddd11111011mmmm", is_sel_valid); // SEL + const InstructionGenerator clz_instr = InstructionGenerator("cccc000101101111dddd11110001mmmm", is_clz_valid); // CLZ SECTION("Fuzz SEL") { // Alternate between a SEL and a MSR to change the CPSR, thus changing the expected result of the next SEL @@ -971,6 +977,12 @@ TEST_CASE("Test ARM SEL instruction", "[JitX64]") { return sel_instr.Generate(false); }); } + + SECTION("Fuzz CLZ") { + FuzzJitArm(1, 1, 1000, [&clz_instr]() -> u32 { + return clz_instr.Generate(); + }); + } } TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") {