mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-15 00:00:42 +02:00
300 lines
12 KiB
C++
300 lines
12 KiB
C++
// Collection of utilities from vixl.
|
|
// Following is the vixl license.
|
|
// Copyright 2015, VIXL authors
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
// Test if a given value can be encoded in the immediate field of a logical
|
|
// instruction.
|
|
// If it can be encoded, the function returns true, and values pointed to by n,
|
|
// imm_s and imm_r are updated with immediates encoded in the format required
|
|
// by the corresponding fields in the logical instruction.
|
|
// If it can not be encoded, the function returns false, and the values pointed
|
|
// to by n, imm_s and imm_r are undefined.
|
|
static bool IsImmLogical(uint64_t value, unsigned width, unsigned* n = nullptr, unsigned* imm_s = nullptr, unsigned* imm_r = nullptr) {
|
|
[[maybe_unused]] constexpr auto kBRegSize = 8;
|
|
[[maybe_unused]] constexpr auto kHRegSize = 16;
|
|
[[maybe_unused]] constexpr auto kSRegSize = 32;
|
|
[[maybe_unused]] constexpr auto kDRegSize = 64;
|
|
|
|
constexpr auto kWRegSize = 32;
|
|
|
|
LOGMAN_THROW_A_FMT((width == kBRegSize) || (width == kHRegSize) || (width == kSRegSize) || (width == kDRegSize), "Unexpected imm size");
|
|
|
|
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;
|
|
}
|
|
|
|
if (width <= kWRegSize) {
|
|
// To handle 8/16/32-bit logical immediates, the very easiest thing is to repeat
|
|
// the input value to fill a 64-bit word. The correct encoding of that as a
|
|
// logical immediate will also be the correct encoding of the value.
|
|
|
|
// Avoid making the assumption that the most-significant 56/48/32 bits are zero by
|
|
// shifting the value left and duplicating it.
|
|
for (unsigned bits = width; bits <= kWRegSize; bits *= 2) {
|
|
value <<= bits;
|
|
uint64_t mask = (UINT64_C(1) << bits) - 1;
|
|
value |= ((value >> bits) & mask);
|
|
}
|
|
}
|
|
|
|
// 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 = LowestSetBit(value);
|
|
uint64_t value_plus_a = value + a;
|
|
uint64_t b = LowestSetBit(value_plus_a);
|
|
uint64_t value_plus_a_minus_b = value_plus_a - b;
|
|
uint64_t c = LowestSetBit(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 = std::countl_zero(a);
|
|
int clz_c = std::countl_zero(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 false;
|
|
} 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 = std::countl_zero(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 (!std::has_single_bit(uint32_t(d))) {
|
|
return false;
|
|
}
|
|
|
|
if (((b - a) & ~mask) != 0) {
|
|
// If the bit stretch (b - a) does not fit within the mask derived from the
|
|
// repeat period, then fail.
|
|
return false;
|
|
}
|
|
|
|
// 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 uint64_t multipliers[] = {
|
|
0x0000000000000001UL, 0x0000000100000001UL, 0x0001000100010001UL, 0x0101010101010101UL, 0x1111111111111111UL, 0x5555555555555555UL,
|
|
};
|
|
uint64_t multiplier = multipliers[std::countl_zero(uint64_t(d)) - 57];
|
|
uint64_t candidate = (b - a) * multiplier;
|
|
|
|
if (value != candidate) {
|
|
// The candidate pattern doesn't match our input value, so fail.
|
|
return false;
|
|
}
|
|
|
|
// 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).
|
|
int clz_b = (b == 0) ? -1 : std::countl_zero(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' (2 * -d) with our computed s to form imms.
|
|
if (n != nullptr) {
|
|
*n = out_n;
|
|
}
|
|
if (imm_s != nullptr) {
|
|
*imm_s = ((2 * -d) | (s - 1)) & 0x3f;
|
|
}
|
|
if (imm_r != nullptr) {
|
|
*imm_r = r;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool IsIntN(unsigned n, int64_t x) {
|
|
if (n == 64) {
|
|
return true;
|
|
}
|
|
int64_t limit = INT64_C(1) << (n - 1);
|
|
return (-limit <= x) && (x < limit);
|
|
}
|
|
|
|
static inline bool IsUintN(unsigned n, int64_t x) {
|
|
// Convert to an unsigned integer to avoid implementation-defined behavior.
|
|
return !(static_cast<uint64_t>(x) >> n);
|
|
}
|
|
|
|
// clang-format off
|
|
#define INT_1_TO_32_LIST(V) \
|
|
V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \
|
|
V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \
|
|
V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \
|
|
V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32)
|
|
|
|
#define INT_33_TO_63_LIST(V) \
|
|
V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \
|
|
V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \
|
|
V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \
|
|
V(57) V(58) V(59) V(60) V(61) V(62) V(63)
|
|
|
|
#define INT_1_TO_63_LIST(V) INT_1_TO_32_LIST(V) INT_33_TO_63_LIST(V)
|
|
|
|
// clang-format on
|
|
|
|
#define DECLARE_IS_INT_N(N) \
|
|
static inline bool IsInt##N(int64_t x) { \
|
|
return IsIntN(N, x); \
|
|
}
|
|
|
|
#define DECLARE_IS_UINT_N(N) \
|
|
static inline bool IsUint##N(int64_t x) { \
|
|
return IsUintN(N, x); \
|
|
}
|
|
|
|
INT_1_TO_63_LIST(DECLARE_IS_INT_N)
|
|
INT_1_TO_63_LIST(DECLARE_IS_UINT_N)
|
|
|
|
#undef DECLARE_IS_INT_N
|
|
#undef DECLARE_IS_UINT_N
|
|
|
|
private:
|
|
|
|
// Some compilers dislike negating unsigned integers,
|
|
// so we provide an equivalent.
|
|
template<typename T>
|
|
static inline T UnsignedNegate(T value) {
|
|
static_assert(std::is_unsigned<T>::value);
|
|
return ~value + 1;
|
|
}
|
|
|
|
static inline uint64_t LowestSetBit(uint64_t value) {
|
|
return value & UnsignedNegate(value);
|
|
}
|
|
|
|
public:
|