From 6ec651498d26fd371136b821a1f8937021cc7613 Mon Sep 17 00:00:00 2001 From: Mat M Date: Fri, 2 Sep 2016 12:34:33 -0400 Subject: [PATCH] arm: Add PSR helper type (#3) --- src/CMakeLists.txt | 1 + src/backend_x64/interface_x64.cpp | 4 +- src/frontend/arm/PSR.h | 225 ++++++++++++++++++++++++++++++ src/frontend/arm_types.h | 42 +++--- tests/arm/fuzz_arm.cpp | 2 +- tests/arm/fuzz_thumb.cpp | 5 +- 6 files changed, 258 insertions(+), 21 deletions(-) create mode 100644 src/frontend/arm/PSR.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fee1e88e..9fc7d62e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(HEADERS common/scope_exit.h common/string_util.h frontend/arm/FPSCR.h + frontend/arm/PSR.h frontend/arm_types.h frontend/decoder/arm.h frontend/decoder/decoder_detail.h diff --git a/src/backend_x64/interface_x64.cpp b/src/backend_x64/interface_x64.cpp index 842c06bb..e94b1cea 100644 --- a/src/backend_x64/interface_x64.cpp +++ b/src/backend_x64/interface_x64.cpp @@ -46,10 +46,8 @@ struct Jit::Impl { size_t Execute(size_t cycle_count) { u32 pc = jit_state.Reg[15]; - bool TFlag = Common::Bit<5>(jit_state.Cpsr); - bool EFlag = Common::Bit<9>(jit_state.Cpsr); - Arm::LocationDescriptor descriptor{pc, TFlag, EFlag, jit_state.guest_FPSCR_mode}; + Arm::LocationDescriptor descriptor{pc, Arm::PSR{jit_state.Cpsr}, jit_state.guest_FPSCR_mode}; CodePtr code_ptr = GetBasicBlock(descriptor).code_ptr; return block_of_code.RunCode(&jit_state, code_ptr, cycle_count); diff --git a/src/frontend/arm/PSR.h b/src/frontend/arm/PSR.h new file mode 100644 index 00000000..9f60f9a0 --- /dev/null +++ b/src/frontend/arm/PSR.h @@ -0,0 +1,225 @@ +/* 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. + */ + +#pragma once + +#include "common/bit_util.h" +#include "common/common_types.h" + +namespace Dynarmic { +namespace Arm { + +/** + * Program Status Register + * + * | Bit(s) | Description | + * |:-------:|:----------------------------------------------| + * | N | Negative | + * | Z | Zero | + * | C | Carry | + * | V | Overflow | + * | Q | Sticky overflow for DSP-oriented instructions | + * | IT[1:0] | Lower two bits of the If-Then execution state | + * | J | Jazelle bit | + * | GE | Greater-than or Equal | + * | IT[7:2] | Upper six bits of the If-Then execution state | + * | E | Endian (0 is little endian, 1 is big endian) | + * | A | Imprecise data abort (disables them when set) | + * | I | IRQ interrupts (disabled when set) | + * | F | FIQ interrupts (disabled when set) | + * | T | Thumb bit | + * | M | Current processor mode | + */ +class PSR final { +public: + /// Valid processor modes that may be indicated. + enum class Mode : u32 + { + User = 0b10000, + FIQ = 0b10001, + IRQ = 0b10010, + Supervisor = 0b10011, + Monitor = 0b10110, + Abort = 0b10111, + Hypervisor = 0b11010, + Undefined = 0b11011, + System = 0b11111 + }; + + /// Instruction sets that may be signified through a PSR. + enum class InstructionSet + { + ARM, + Jazelle, + Thumb, + ThumbEE + }; + + PSR() = default; + explicit PSR(u32 data) : value{data & mask} {} + + PSR& operator=(u32 data) { + value = data & mask; + return *this; + } + + bool N() const { + return Common::Bit<31>(value); + } + void N(bool set) { + value = (value & ~0x80000000) | static_cast(set) << 31; + } + + bool Z() const { + return Common::Bit<30>(value); + } + void Z(bool set) { + value = (value & ~0x40000000) | static_cast(set) << 30; + } + + bool C() const { + return Common::Bit<29>(value); + } + void C(bool set) { + value = (value & ~0x20000000) | static_cast(set) << 29; + } + + bool V() const { + return Common::Bit<28>(value); + } + void V(bool set) { + value = (value & ~0x10000000) | static_cast(set) << 28; + } + + bool Q() const { + return Common::Bit<27>(value); + } + void Q(bool set) { + value = (value & ~0x8000000) | static_cast(set) << 27; + } + + bool J() const { + return Common::Bit<24>(value); + } + void J(bool set) { + value = (value & ~0x1000000) | static_cast(set) << 24; + } + + u32 GE() const { + return Common::Bits<16, 19>(value); + } + void GE(u32 data) { + value = (value & ~0xF0000) | (data & 0xF) << 16; + } + + u32 IT() const { + return (value & 0x6000000) >> 25 | (value & 0xFC00) >> 8; + } + void IT(u32 data) { + value = (value & ~0x000FC00) | (data & 0b11111100) << 8; + value = (value & ~0x6000000) | (data & 0b00000011) << 25; + } + + bool E() const { + return Common::Bit<9>(value); + } + void E(bool set) { + value = (value & ~0x200) | static_cast(set) << 9; + } + + bool A() const { + return Common::Bit<8>(value); + } + void A(bool set) { + value = (value & ~0x100) | static_cast(set) << 8; + } + + bool I() const { + return Common::Bit<7>(value); + } + void I(bool set) { + value = (value & ~0x80) | static_cast(set) << 7; + } + + bool F() const { + return Common::Bit<6>(value); + } + void F(bool set) { + value = (value & ~0x40) | static_cast(set) << 6; + } + + bool T() const { + return Common::Bit<5>(value); + } + void T(bool set) { + value = (value & ~0x20) | static_cast(set) << 5; + } + + Mode M() const { + return static_cast(Common::Bits<0, 4>(value)); + } + void M(Mode mode) { + value = (value & ~0x1F) | (static_cast(mode) & 0x1F); + } + + u32 Value() const { + return value; + } + + InstructionSet CurrentInstructionSet() const { + const bool j_bit = J(); + const bool t_bit = T(); + + if (j_bit && t_bit) + return InstructionSet::ThumbEE; + + if (t_bit) + return InstructionSet::Thumb; + + if (j_bit) + return InstructionSet::Jazelle; + + return InstructionSet::ARM; + } + + void CurrentInstructionSet(InstructionSet instruction_set) { + switch (instruction_set) { + case InstructionSet::ARM: + T(false); + J(false); + break; + case InstructionSet::Jazelle: + T(false); + J(true); + break; + case InstructionSet::Thumb: + T(true); + J(false); + break; + case InstructionSet::ThumbEE: + T(true); + J(true); + break; + } + } + +private: + // Bits 20-23 are reserved and should be zero. + static constexpr u32 mask = 0xFF0FFFFF; + + u32 value = 0; +}; + +inline bool operator==(PSR lhs, PSR rhs) { + return lhs.Value() == rhs.Value(); +} + +inline bool operator!=(PSR lhs, PSR rhs) { + return !operator==(lhs, rhs); +} + +} // namespace Arm +} // namespace Dynarmic diff --git a/src/frontend/arm_types.h b/src/frontend/arm_types.h index 5a185970..c9582121 100644 --- a/src/frontend/arm_types.h +++ b/src/frontend/arm_types.h @@ -14,6 +14,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "frontend/arm/FPSCR.h" +#include "frontend/arm/PSR.h" namespace Dynarmic { namespace Arm { @@ -72,18 +73,22 @@ enum class SignExtendRotation { * tells us if the processor is in Thumb or Arm mode. */ struct LocationDescriptor { + // Indicates bits that should be preserved within descriptors. + static constexpr u32 CPSR_MODE_MASK = 0x00000220; static constexpr u32 FPSCR_MODE_MASK = 0x03F79F00; - LocationDescriptor(u32 arm_pc, bool tflag, bool eflag, FPSCR fpscr) - : arm_pc(arm_pc), tflag(tflag), eflag(eflag), fpscr(fpscr.Value() & FPSCR_MODE_MASK) {} + LocationDescriptor(u32 arm_pc, PSR cpsr, FPSCR fpscr) + : arm_pc(arm_pc), cpsr(cpsr.Value() & CPSR_MODE_MASK), fpscr(fpscr.Value() & FPSCR_MODE_MASK) {} u32 PC() const { return arm_pc; } - bool TFlag() const { return tflag; } - bool EFlag() const { return eflag; } + bool TFlag() const { return cpsr.T(); } + bool EFlag() const { return cpsr.E(); } + + Arm::PSR CPSR() const { return cpsr; } Arm::FPSCR FPSCR() const { return fpscr; } bool operator == (const LocationDescriptor& o) const { - return std::tie(arm_pc, tflag, eflag, fpscr) == std::tie(o.arm_pc, o.tflag, o.eflag, o.fpscr); + return std::tie(arm_pc, cpsr, fpscr) == std::tie(o.arm_pc, o.cpsr, o.fpscr); } bool operator != (const LocationDescriptor& o) const { @@ -91,23 +96,29 @@ struct LocationDescriptor { } LocationDescriptor SetPC(u32 new_arm_pc) const { - return LocationDescriptor(new_arm_pc, tflag, eflag, fpscr); + return LocationDescriptor(new_arm_pc, cpsr, fpscr); } LocationDescriptor AdvancePC(int amount) const { - return LocationDescriptor(static_cast(arm_pc + amount), tflag, eflag, fpscr); + return LocationDescriptor(static_cast(arm_pc + amount), cpsr, fpscr); } LocationDescriptor SetTFlag(bool new_tflag) const { - return LocationDescriptor(arm_pc, new_tflag, eflag, fpscr); + PSR new_cpsr = cpsr; + new_cpsr.T(new_tflag); + + return LocationDescriptor(arm_pc, new_cpsr, fpscr); } LocationDescriptor SetEFlag(bool new_eflag) const { - return LocationDescriptor(arm_pc, tflag, new_eflag, fpscr); + PSR new_cpsr = cpsr; + new_cpsr.E(new_eflag); + + return LocationDescriptor(arm_pc, new_cpsr, fpscr); } LocationDescriptor SetFPSCR(u32 new_fpscr) const { - return LocationDescriptor(arm_pc, tflag, eflag, new_fpscr & FPSCR_MODE_MASK); + return LocationDescriptor(arm_pc, cpsr, new_fpscr & FPSCR_MODE_MASK); } u64 UniqueHash() const { @@ -115,16 +126,15 @@ struct LocationDescriptor { // This calculation has to match up with EmitX64::EmitTerminalPopRSBHint u64 pc_u64 = u64(arm_pc); u64 fpscr_u64 = u64(fpscr.Value()) << 32; - u64 t_u64 = tflag ? (1ull << 35) : 0; - u64 e_u64 = eflag ? (1ull << 39) : 0; + u64 t_u64 = cpsr.T() ? (1ull << 35) : 0; + u64 e_u64 = cpsr.E() ? (1ull << 39) : 0; return pc_u64 | fpscr_u64 | t_u64 | e_u64; } private: - u32 arm_pc; - bool tflag; ///< Thumb / ARM - bool eflag; ///< Big / Little Endian - Arm::FPSCR fpscr; ///< Floating point status control register + u32 arm_pc; ///< Current program counter value. + Arm::PSR cpsr; ///< Current program status register. + Arm::FPSCR fpscr; ///< Floating point status control register. }; struct LocationDescriptorHash { diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index eefce98b..218dbc1e 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -291,7 +291,7 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe size_t num_insts = 0; while (num_insts < instructions_to_execute_count) { - Dynarmic::Arm::LocationDescriptor descriptor = {u32(num_insts * 4), false, false, 0}; + Dynarmic::Arm::LocationDescriptor descriptor = {u32(num_insts * 4), Dynarmic::Arm::PSR{}, 0}; Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate(descriptor, &MemoryRead32); Dynarmic::Optimization::GetSetElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block); diff --git a/tests/arm/fuzz_thumb.cpp b/tests/arm/fuzz_thumb.cpp index 9f092dd6..deace638 100644 --- a/tests/arm/fuzz_thumb.cpp +++ b/tests/arm/fuzz_thumb.cpp @@ -244,7 +244,10 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data); } - Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate({0, true, false, 0}, MemoryRead32); + Dynarmic::Arm::PSR cpsr; + cpsr.T(true); + + Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate({0, cpsr, 0}, MemoryRead32); Dynarmic::Optimization::GetSetElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block); Dynarmic::Optimization::VerificationPass(ir_block);