summaryrefslogtreecommitdiff
path: root/Source/Core/Common
diff options
context:
space:
mode:
authorMarkus Wick <degasus@users.noreply.github.com>2021-07-11 12:55:48 +0200
committerGitHub <noreply@github.com>2021-07-11 12:55:48 +0200
commit88fd9fd577ffc3c1af5a04cdf337ecd2990ccfe2 (patch)
treef9c3ab7e3d204c0f7a6c1c75964d93bbaf2f92d0 /Source/Core/Common
parent4157967f04bb72e37c0aa383b3f377c0d2177418 (diff)
parent0f3b9a8874b12200326e6dd0904c30c2b296b159 (diff)
Merge pull request #9869 from JosJuice/jitarm64-constexpr-isimmlogical
JitArm64: Encode logical immediates at compile-time where possible
Diffstat (limited to 'Source/Core/Common')
-rw-r--r--Source/Core/Common/Arm64Emitter.cpp280
-rw-r--r--Source/Core/Common/Arm64Emitter.h247
-rw-r--r--Source/Core/Common/BitUtils.h9
3 files changed, 292 insertions, 244 deletions
diff --git a/Source/Core/Common/Arm64Emitter.cpp b/Source/Core/Common/Arm64Emitter.cpp
index 141336e212..2e7cd2fd4d 100644
--- a/Source/Core/Common/Arm64Emitter.cpp
+++ b/Source/Core/Common/Arm64Emitter.cpp
@@ -28,11 +28,6 @@ namespace Arm64Gen
{
namespace
{
-uint64_t LargestPowerOf2Divisor(uint64_t value)
-{
- return value & -(int64_t)value;
-}
-
// For ADD/SUB
std::optional<std::pair<u32, bool>> IsImmArithmetic(uint64_t input)
{
@@ -45,214 +40,6 @@ std::optional<std::pair<u32, bool>> IsImmArithmetic(uint64_t input)
return std::nullopt;
}
-// For AND/TST/ORR/EOR etc
-std::optional<std::tuple<u32, u32, u32>> IsImmLogical(u64 value, u32 width)
-{
- bool negate = false;
-
- // Logical immediates are encoded using parameters n, imm_s and imm_r using
- // the following table:
- //
- // N imms immr size S R
- // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
- // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
- // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
- // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
- // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
- // 0 11110s xxxxxr 2 UInt(s) UInt(r)
- // (s bits must not be all set)
- //
- // A pattern is constructed of size bits, where the least significant S+1 bits
- // are set. The pattern is rotated right by R, and repeated across a 32 or
- // 64-bit value, depending on destination register width.
- //
- // Put another way: the basic format of a logical immediate is a single
- // contiguous stretch of 1 bits, repeated across the whole word at intervals
- // given by a power of 2. To identify them quickly, we first locate the
- // lowest stretch of 1 bits, then the next 1 bit above that; that combination
- // is different for every logical immediate, so it gives us all the
- // information we need to identify the only logical immediate that our input
- // could be, and then we simply check if that's the value we actually have.
- //
- // (The rotation parameter does give the possibility of the stretch of 1 bits
- // going 'round the end' of the word. To deal with that, we observe that in
- // any situation where that happens the bitwise NOT of the value is also a
- // valid logical immediate. So we simply invert the input whenever its low bit
- // is set, and then we know that the rotated case can't arise.)
-
- if (value & 1)
- {
- // If the low bit is 1, negate the value, and set a flag to remember that we
- // did (so that we can adjust the return values appropriately).
- negate = true;
- value = ~value;
- }
-
- constexpr int kWRegSizeInBits = 32;
-
- if (width == kWRegSizeInBits)
- {
- // To handle 32-bit logical immediates, the very easiest thing is to repeat
- // the input value twice to make a 64-bit word. The correct encoding of that
- // as a logical immediate will also be the correct encoding of the 32-bit
- // value.
-
- // The most-significant 32 bits may not be zero (ie. negate is true) so
- // shift the value left before duplicating it.
- value <<= kWRegSizeInBits;
- value |= value >> kWRegSizeInBits;
- }
-
- // The basic analysis idea: imagine our input word looks like this.
- //
- // 0011111000111110001111100011111000111110001111100011111000111110
- // c b a
- // |<--d-->|
- //
- // We find the lowest set bit (as an actual power-of-2 value, not its index)
- // and call it a. Then we add a to our original number, which wipes out the
- // bottommost stretch of set bits and replaces it with a 1 carried into the
- // next zero bit. Then we look for the new lowest set bit, which is in
- // position b, and subtract it, so now our number is just like the original
- // but with the lowest stretch of set bits completely gone. Now we find the
- // lowest set bit again, which is position c in the diagram above. Then we'll
- // measure the distance d between bit positions a and c (using CLZ), and that
- // tells us that the only valid logical immediate that could possibly be equal
- // to this number is the one in which a stretch of bits running from a to just
- // below b is replicated every d bits.
- uint64_t a = LargestPowerOf2Divisor(value);
- uint64_t value_plus_a = value + a;
- uint64_t b = LargestPowerOf2Divisor(value_plus_a);
- uint64_t value_plus_a_minus_b = value_plus_a - b;
- uint64_t c = LargestPowerOf2Divisor(value_plus_a_minus_b);
-
- int d, clz_a, out_n;
- uint64_t mask;
-
- if (c != 0)
- {
- // The general case, in which there is more than one stretch of set bits.
- // Compute the repeat distance d, and set up a bitmask covering the basic
- // unit of repetition (i.e. a word with the bottom d bits set). Also, in all
- // of these cases the N bit of the output will be zero.
- clz_a = Common::CountLeadingZeros(a);
- int clz_c = Common::CountLeadingZeros(c);
- d = clz_a - clz_c;
- mask = ((UINT64_C(1) << d) - 1);
- out_n = 0;
- }
- else
- {
- // Handle degenerate cases.
- //
- // If any of those 'find lowest set bit' operations didn't find a set bit at
- // all, then the word will have been zero thereafter, so in particular the
- // last lowest_set_bit operation will have returned zero. So we can test for
- // all the special case conditions in one go by seeing if c is zero.
- if (a == 0)
- {
- // The input was zero (or all 1 bits, which will come to here too after we
- // inverted it at the start of the function), for which we just return
- // false.
- return std::nullopt;
- }
- else
- {
- // Otherwise, if c was zero but a was not, then there's just one stretch
- // of set bits in our word, meaning that we have the trivial case of
- // d == 64 and only one 'repetition'. Set up all the same variables as in
- // the general case above, and set the N bit in the output.
- clz_a = Common::CountLeadingZeros(a);
- d = 64;
- mask = ~UINT64_C(0);
- out_n = 1;
- }
- }
-
- // If the repeat period d is not a power of two, it can't be encoded.
- if (!MathUtil::IsPow2<u64>(d))
- return std::nullopt;
-
- // If the bit stretch (b - a) does not fit within the mask derived from the
- // repeat period, then fail.
- if (((b - a) & ~mask) != 0)
- return std::nullopt;
-
- // The only possible option is b - a repeated every d bits. Now we're going to
- // actually construct the valid logical immediate derived from that
- // specification, and see if it equals our original input.
- //
- // To repeat a value every d bits, we multiply it by a number of the form
- // (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can
- // be derived using a table lookup on CLZ(d).
- static const std::array<uint64_t, 6> multipliers = {{
- 0x0000000000000001UL,
- 0x0000000100000001UL,
- 0x0001000100010001UL,
- 0x0101010101010101UL,
- 0x1111111111111111UL,
- 0x5555555555555555UL,
- }};
-
- const int multiplier_idx = Common::CountLeadingZeros((u64)d) - 57;
-
- // Ensure that the index to the multipliers array is within bounds.
- DEBUG_ASSERT((multiplier_idx >= 0) && (static_cast<size_t>(multiplier_idx) < multipliers.size()));
-
- const u64 multiplier = multipliers[multiplier_idx];
- const u64 candidate = (b - a) * multiplier;
-
- // The candidate pattern doesn't match our input value, so fail.
- if (value != candidate)
- return std::nullopt;
-
- // We have a match! This is a valid logical immediate, so now we have to
- // construct the bits and pieces of the instruction encoding that generates
- // it.
-
- // Count the set bits in our basic stretch. The special case of clz(0) == -1
- // makes the answer come out right for stretches that reach the very top of
- // the word (e.g. numbers like 0xffffc00000000000).
- const int clz_b = (b == 0) ? -1 : Common::CountLeadingZeros(b);
- int s = clz_a - clz_b;
-
- // Decide how many bits to rotate right by, to put the low bit of that basic
- // stretch in position a.
- int r;
- if (negate)
- {
- // If we inverted the input right at the start of this function, here's
- // where we compensate: the number of set bits becomes the number of clear
- // bits, and the rotation count is based on position b rather than position
- // a (since b is the location of the 'lowest' 1 bit after inversion).
- s = d - s;
- r = (clz_b + 1) & (d - 1);
- }
- else
- {
- r = (clz_a + 1) & (d - 1);
- }
-
- // Now we're done, except for having to encode the S output in such a way that
- // it gives both the number of set bits and the length of the repeated
- // segment. The s field is encoded like this:
- //
- // imms size S
- // ssssss 64 UInt(ssssss)
- // 0sssss 32 UInt(sssss)
- // 10ssss 16 UInt(ssss)
- // 110sss 8 UInt(sss)
- // 1110ss 4 UInt(ss)
- // 11110s 2 UInt(s)
- //
- // So we 'or' (-d << 1) with our computed s to form imms.
- return std::tuple{
- static_cast<u32>(out_n),
- static_cast<u32>(((-d << 1) | (s - 1)) & 0x3f),
- static_cast<u32>(r),
- };
-}
-
float FPImm8ToFloat(u8 bits)
{
const u32 sign = bits >> 7;
@@ -780,10 +567,18 @@ void ARM64XEmitter::EncodeLogicalImmInst(u32 op, ARM64Reg Rd, ARM64Reg Rn, u32 i
// Use Rn to determine bitness here.
bool b64Bit = Is64Bit(Rn);
+ ASSERT_MSG(DYNAREC, b64Bit || !n, "64-bit logical immediate does not fit in 32-bit register");
+
Write32((b64Bit << 31) | (op << 29) | (0x24 << 23) | (n << 22) | (immr << 16) | (imms << 10) |
(DecodeReg(Rn) << 5) | DecodeReg(Rd));
}
+void ARM64XEmitter::EncodeLogicalImmInst(u32 op, ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm)
+{
+ ASSERT_MSG(DYNAREC, imm.valid, "Invalid logical immediate");
+ EncodeLogicalImmInst(op, Rd, Rn, imm.r, imm.s, imm.n);
+}
+
void ARM64XEmitter::EncodeLoadStorePair(u32 op, u32 load, IndexType type, ARM64Reg Rt, ARM64Reg Rt2,
ARM64Reg Rn, s32 imm)
{
@@ -1545,22 +1340,42 @@ void ARM64XEmitter::AND(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool inver
{
EncodeLogicalImmInst(0, Rd, Rn, immr, imms, invert);
}
+void ARM64XEmitter::AND(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm)
+{
+ EncodeLogicalImmInst(0, Rd, Rn, imm);
+}
void ARM64XEmitter::ANDS(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert)
{
EncodeLogicalImmInst(3, Rd, Rn, immr, imms, invert);
}
+void ARM64XEmitter::ANDS(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm)
+{
+ EncodeLogicalImmInst(3, Rd, Rn, imm);
+}
void ARM64XEmitter::EOR(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert)
{
EncodeLogicalImmInst(2, Rd, Rn, immr, imms, invert);
}
+void ARM64XEmitter::EOR(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm)
+{
+ EncodeLogicalImmInst(2, Rd, Rn, imm);
+}
void ARM64XEmitter::ORR(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert)
{
EncodeLogicalImmInst(1, Rd, Rn, immr, imms, invert);
}
+void ARM64XEmitter::ORR(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm)
+{
+ EncodeLogicalImmInst(1, Rd, Rn, imm);
+}
void ARM64XEmitter::TST(ARM64Reg Rn, u32 immr, u32 imms, bool invert)
{
EncodeLogicalImmInst(3, Is64Bit(Rn) ? ARM64Reg::ZR : ARM64Reg::WZR, Rn, immr, imms, invert);
}
+void ARM64XEmitter::TST(ARM64Reg Rn, LogicalImm imm)
+{
+ EncodeLogicalImmInst(3, Is64Bit(Rn) ? ARM64Reg::ZR : ARM64Reg::WZR, Rn, imm);
+}
// Add/subtract (immediate)
void ARM64XEmitter::ADD(ARM64Reg Rd, ARM64Reg Rn, u32 imm, bool shift)
@@ -2067,13 +1882,13 @@ void ARM64XEmitter::MOVI2RImpl(ARM64Reg Rd, T imm)
(imm & 0xFFFF'FFFF'0000'0000) | (imm >> 32),
(imm << 48) | (imm & 0x0000'FFFF'FFFF'0000) | (imm >> 48)})
{
- if (IsImmLogical(orr_imm, 64))
+ if (LogicalImm(orr_imm, 64))
try_base(orr_imm, Approach::ORRBase, false);
}
}
else
{
- if (IsImmLogical(imm, 32))
+ if (LogicalImm(imm, 32))
try_base(imm, Approach::ORRBase, false);
}
}
@@ -4127,10 +3942,9 @@ void ARM64XEmitter::ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
if (!Is64Bit(Rn))
imm &= 0xFFFFFFFF;
- if (const auto result = IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rn) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- AND(Rd, Rn, imm_r, imm_s, n != 0);
+ AND(Rd, Rn, result);
}
else
{
@@ -4144,10 +3958,9 @@ void ARM64XEmitter::ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
void ARM64XEmitter::ORRI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
{
- if (const auto result = IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rn) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- ORR(Rd, Rn, imm_r, imm_s, n != 0);
+ ORR(Rd, Rn, result);
}
else
{
@@ -4161,10 +3974,9 @@ void ARM64XEmitter::ORRI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
void ARM64XEmitter::EORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
{
- if (const auto result = IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rn) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- EOR(Rd, Rn, imm_r, imm_s, n != 0);
+ EOR(Rd, Rn, result);
}
else
{
@@ -4178,10 +3990,9 @@ void ARM64XEmitter::EORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
void ARM64XEmitter::ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
{
- if (const auto result = IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rn) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- ANDS(Rd, Rn, imm_r, imm_s, n != 0);
+ ANDS(Rd, Rn, result);
}
else
{
@@ -4342,10 +4153,9 @@ bool ARM64XEmitter::TryCMPI2R(ARM64Reg Rn, u64 imm)
bool ARM64XEmitter::TryANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm)
{
- if (const auto result = IsImmLogical(imm, Is64Bit(Rd) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rd) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- AND(Rd, Rn, imm_r, imm_s, n != 0);
+ AND(Rd, Rn, result);
return true;
}
@@ -4354,10 +4164,9 @@ bool ARM64XEmitter::TryANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm)
bool ARM64XEmitter::TryORRI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm)
{
- if (const auto result = IsImmLogical(imm, Is64Bit(Rd) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rd) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- ORR(Rd, Rn, imm_r, imm_s, n != 0);
+ ORR(Rd, Rn, result);
return true;
}
@@ -4366,10 +4175,9 @@ bool ARM64XEmitter::TryORRI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm)
bool ARM64XEmitter::TryEORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm)
{
- if (const auto result = IsImmLogical(imm, Is64Bit(Rd) ? 64 : 32))
+ if (const auto result = LogicalImm(imm, Is64Bit(Rd) ? 64 : 32))
{
- const auto& [n, imm_s, imm_r] = *result;
- EOR(Rd, Rn, imm_r, imm_s, n != 0);
+ EOR(Rd, Rn, result);
return true;
}
diff --git a/Source/Core/Common/Arm64Emitter.h b/Source/Core/Common/Arm64Emitter.h
index 5702bebbd9..3f5c18d8c9 100644
--- a/Source/Core/Common/Arm64Emitter.h
+++ b/Source/Core/Common/Arm64Emitter.h
@@ -5,12 +5,17 @@
#include <cstring>
#include <functional>
+#include <optional>
+#include <utility>
#include "Common/ArmCommon.h"
#include "Common/Assert.h"
#include "Common/BitSet.h"
+#include "Common/BitUtils.h"
#include "Common/CodeBlock.h"
#include "Common/Common.h"
+#include "Common/CommonTypes.h"
+#include "Common/MathUtil.h"
namespace Arm64Gen
{
@@ -496,6 +501,225 @@ public:
bool IsExtended() const { return m_type == TypeSpecifier::ExtendedReg; }
};
+struct LogicalImm
+{
+ constexpr LogicalImm() {}
+
+ constexpr LogicalImm(u8 r_, u8 s_, bool n_) : r(r_), s(s_), n(n_), valid(true) {}
+
+ constexpr LogicalImm(u64 value, u32 width)
+ {
+ bool negate = false;
+
+ // Logical immediates are encoded using parameters n, imm_s and imm_r using
+ // the following table:
+ //
+ // N imms immr size S R
+ // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
+ // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
+ // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
+ // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
+ // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
+ // 0 11110s xxxxxr 2 UInt(s) UInt(r)
+ // (s bits must not be all set)
+ //
+ // A pattern is constructed of size bits, where the least significant S+1 bits
+ // are set. The pattern is rotated right by R, and repeated across a 32 or
+ // 64-bit value, depending on destination register width.
+ //
+ // Put another way: the basic format of a logical immediate is a single
+ // contiguous stretch of 1 bits, repeated across the whole word at intervals
+ // given by a power of 2. To identify them quickly, we first locate the
+ // lowest stretch of 1 bits, then the next 1 bit above that; that combination
+ // is different for every logical immediate, so it gives us all the
+ // information we need to identify the only logical immediate that our input
+ // could be, and then we simply check if that's the value we actually have.
+ //
+ // (The rotation parameter does give the possibility of the stretch of 1 bits
+ // going 'round the end' of the word. To deal with that, we observe that in
+ // any situation where that happens the bitwise NOT of the value is also a
+ // valid logical immediate. So we simply invert the input whenever its low bit
+ // is set, and then we know that the rotated case can't arise.)
+
+ if (value & 1)
+ {
+ // If the low bit is 1, negate the value, and set a flag to remember that we
+ // did (so that we can adjust the return values appropriately).
+ negate = true;
+ value = ~value;
+ }
+
+ constexpr int kWRegSizeInBits = 32;
+
+ if (width == kWRegSizeInBits)
+ {
+ // To handle 32-bit logical immediates, the very easiest thing is to repeat
+ // the input value twice to make a 64-bit word. The correct encoding of that
+ // as a logical immediate will also be the correct encoding of the 32-bit
+ // value.
+
+ // The most-significant 32 bits may not be zero (ie. negate is true) so
+ // shift the value left before duplicating it.
+ value <<= kWRegSizeInBits;
+ value |= value >> kWRegSizeInBits;
+ }
+
+ // The basic analysis idea: imagine our input word looks like this.
+ //
+ // 0011111000111110001111100011111000111110001111100011111000111110
+ // c b a
+ // |<--d-->|
+ //
+ // We find the lowest set bit (as an actual power-of-2 value, not its index)
+ // and call it a. Then we add a to our original number, which wipes out the
+ // bottommost stretch of set bits and replaces it with a 1 carried into the
+ // next zero bit. Then we look for the new lowest set bit, which is in
+ // position b, and subtract it, so now our number is just like the original
+ // but with the lowest stretch of set bits completely gone. Now we find the
+ // lowest set bit again, which is position c in the diagram above. Then we'll
+ // measure the distance d between bit positions a and c (using CLZ), and that
+ // tells us that the only valid logical immediate that could possibly be equal
+ // to this number is the one in which a stretch of bits running from a to just
+ // below b is replicated every d bits.
+ u64 a = Common::LargestPowerOf2Divisor(value);
+ u64 value_plus_a = value + a;
+ u64 b = Common::LargestPowerOf2Divisor(value_plus_a);
+ u64 value_plus_a_minus_b = value_plus_a - b;
+ u64 c = Common::LargestPowerOf2Divisor(value_plus_a_minus_b);
+
+ int d = 0, clz_a = 0, out_n = 0;
+ u64 mask = 0;
+
+ if (c != 0)
+ {
+ // The general case, in which there is more than one stretch of set bits.
+ // Compute the repeat distance d, and set up a bitmask covering the basic
+ // unit of repetition (i.e. a word with the bottom d bits set). Also, in all
+ // of these cases the N bit of the output will be zero.
+ clz_a = Common::CountLeadingZeros(a);
+ int clz_c = Common::CountLeadingZeros(c);
+ d = clz_a - clz_c;
+ mask = ((UINT64_C(1) << d) - 1);
+ out_n = 0;
+ }
+ else
+ {
+ // Handle degenerate cases.
+ //
+ // If any of those 'find lowest set bit' operations didn't find a set bit at
+ // all, then the word will have been zero thereafter, so in particular the
+ // last lowest_set_bit operation will have returned zero. So we can test for
+ // all the special case conditions in one go by seeing if c is zero.
+ if (a == 0)
+ {
+ // The input was zero (or all 1 bits, which will come to here too after we
+ // inverted it at the start of the function), which is invalid.
+ return;
+ }
+ else
+ {
+ // Otherwise, if c was zero but a was not, then there's just one stretch
+ // of set bits in our word, meaning that we have the trivial case of
+ // d == 64 and only one 'repetition'. Set up all the same variables as in
+ // the general case above, and set the N bit in the output.
+ clz_a = Common::CountLeadingZeros(a);
+ d = 64;
+ mask = ~UINT64_C(0);
+ out_n = 1;
+ }
+ }
+
+ // If the repeat period d is not a power of two, it can't be encoded.
+ if (!MathUtil::IsPow2<u64>(d))
+ return;
+
+ // If the bit stretch (b - a) does not fit within the mask derived from the
+ // repeat period, then fail.
+ if (((b - a) & ~mask) != 0)
+ return;
+
+ // The only possible option is b - a repeated every d bits. Now we're going to
+ // actually construct the valid logical immediate derived from that
+ // specification, and see if it equals our original input.
+ //
+ // To repeat a value every d bits, we multiply it by a number of the form
+ // (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can
+ // be derived using a table lookup on CLZ(d).
+ constexpr std::array<u64, 6> multipliers = {{
+ 0x0000000000000001UL,
+ 0x0000000100000001UL,
+ 0x0001000100010001UL,
+ 0x0101010101010101UL,
+ 0x1111111111111111UL,
+ 0x5555555555555555UL,
+ }};
+
+ const int multiplier_idx = Common::CountLeadingZeros((u64)d) - 57;
+
+ // Ensure that the index to the multipliers array is within bounds.
+ DEBUG_ASSERT((multiplier_idx >= 0) &&
+ (static_cast<size_t>(multiplier_idx) < multipliers.size()));
+
+ const u64 multiplier = multipliers[multiplier_idx];
+ const u64 candidate = (b - a) * multiplier;
+
+ // The candidate pattern doesn't match our input value, so fail.
+ if (value != candidate)
+ return;
+
+ // We have a match! This is a valid logical immediate, so now we have to
+ // construct the bits and pieces of the instruction encoding that generates
+ // it.
+ n = out_n;
+
+ // Count the set bits in our basic stretch. The special case of clz(0) == -1
+ // makes the answer come out right for stretches that reach the very top of
+ // the word (e.g. numbers like 0xffffc00000000000).
+ const int clz_b = (b == 0) ? -1 : Common::CountLeadingZeros(b);
+ s = clz_a - clz_b;
+
+ // Decide how many bits to rotate right by, to put the low bit of that basic
+ // stretch in position a.
+ if (negate)
+ {
+ // If we inverted the input right at the start of this function, here's
+ // where we compensate: the number of set bits becomes the number of clear
+ // bits, and the rotation count is based on position b rather than position
+ // a (since b is the location of the 'lowest' 1 bit after inversion).
+ s = d - s;
+ r = (clz_b + 1) & (d - 1);
+ }
+ else
+ {
+ r = (clz_a + 1) & (d - 1);
+ }
+
+ // Now we're done, except for having to encode the S output in such a way that
+ // it gives both the number of set bits and the length of the repeated
+ // segment. The s field is encoded like this:
+ //
+ // imms size S
+ // ssssss 64 UInt(ssssss)
+ // 0sssss 32 UInt(sssss)
+ // 10ssss 16 UInt(ssss)
+ // 110sss 8 UInt(sss)
+ // 1110ss 4 UInt(ss)
+ // 11110s 2 UInt(s)
+ //
+ // So we 'or' (-d << 1) with our computed s to form imms.
+ s = ((-d << 1) | (s - 1)) & 0x3f;
+
+ valid = true;
+ }
+
+ constexpr operator bool() const { return valid; }
+
+ u8 r = 0;
+ u8 s = 0;
+ bool n = false;
+ bool valid = false;
+};
+
class ARM64XEmitter
{
friend class ARM64FloatEmitter;
@@ -531,6 +755,7 @@ private:
void EncodeLoadStoreRegisterOffset(u32 size, u32 opc, ARM64Reg Rt, ARM64Reg Rn, ArithOption Rm);
void EncodeAddSubImmInst(u32 op, bool flags, u32 shift, u32 imm, ARM64Reg Rn, ARM64Reg Rd);
void EncodeLogicalImmInst(u32 op, ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, int n);
+ void EncodeLogicalImmInst(u32 op, ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm);
void EncodeLoadStorePair(u32 op, u32 load, IndexType type, ARM64Reg Rt, ARM64Reg Rt2, ARM64Reg Rn,
s32 imm);
void EncodeAddressInst(u32 op, ARM64Reg Rd, s32 imm);
@@ -772,10 +997,15 @@ public:
// Logical (immediate)
void AND(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert = false);
+ void AND(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm);
void ANDS(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert = false);
+ void ANDS(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm);
void EOR(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert = false);
+ void EOR(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm);
void ORR(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms, bool invert = false);
+ void ORR(ARM64Reg Rd, ARM64Reg Rn, LogicalImm imm);
void TST(ARM64Reg Rn, u32 immr, u32 imms, bool invert = false);
+ void TST(ARM64Reg Rn, LogicalImm imm);
// Add/subtract (immediate)
void ADD(ARM64Reg Rd, ARM64Reg Rn, u32 imm, bool shift = false);
void ADDS(ARM64Reg Rd, ARM64Reg Rn, u32 imm, bool shift = false);
@@ -893,17 +1123,17 @@ public:
MOVI2R(Rd, (uintptr_t)ptr);
}
- // Wrapper around AND x, y, imm etc. If you are sure the imm will work, no need to pass a scratch
- // register.
- void ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
- void ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
- void TSTI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG)
+ // Wrapper around AND x, y, imm etc.
+ // If you are sure the imm will work, preferably construct a LogicalImm directly instead,
+ // since that is constexpr and thus can be done at compile-time for constant values.
+ void ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
+ void ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
+ void TSTI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch)
{
ANDSI2R(Is64Bit(Rn) ? ARM64Reg::ZR : ARM64Reg::WZR, Rn, imm, scratch);
}
- void ORRI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
- void EORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
- void CMPI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
+ void ORRI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
+ void EORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
void ADDI2R_internal(ARM64Reg Rd, ARM64Reg Rn, u64 imm, bool negative, bool flags,
ARM64Reg scratch);
@@ -911,6 +1141,7 @@ public:
void ADDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
void SUBI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
void SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
+ void CMPI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch = ARM64Reg::INVALID_REG);
bool TryADDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm);
bool TrySUBI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm);
diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h
index d4d23d63d8..09ab4bd78c 100644
--- a/Source/Core/Common/BitUtils.h
+++ b/Source/Core/Common/BitUtils.h
@@ -413,4 +413,13 @@ constexpr int CountLeadingZeros(uint32_t value)
#undef CONSTEXPR_FROM_INTRINSIC
+template <typename T>
+constexpr T LargestPowerOf2Divisor(T value)
+{
+ static_assert(std::is_unsigned<T>(),
+ "LargestPowerOf2Divisor only makes sense for unsigned types.");
+
+ return value & -static_cast<std::make_signed_t<T>>(value);
+}
+
} // namespace Common