| /* |
| * Copyright (C) 2012-2021 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. |
| */ |
| |
| #pragma once |
| |
| namespace JSC { |
| |
| ALWAYS_INLINE constexpr bool isDarwin() |
| { |
| #if OS(DARWIN) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| ALWAYS_INLINE constexpr bool isIOS() |
| { |
| #if PLATFORM(IOS_FAMILY) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| template<size_t bits, typename Type> |
| ALWAYS_INLINE constexpr bool isInt(Type t) |
| { |
| constexpr size_t shift = sizeof(Type) * CHAR_BIT - bits; |
| static_assert(sizeof(Type) * CHAR_BIT > shift, "shift is larger than the size of the value"); |
| return ((t << shift) >> shift) == t; |
| } |
| |
| ALWAYS_INLINE bool isInt9(int32_t value) |
| { |
| return value == ((value << 23) >> 23); |
| } |
| |
| template<typename Type> |
| ALWAYS_INLINE bool isUInt12(Type value) |
| { |
| return !(value & ~static_cast<Type>(0xfff)); |
| } |
| |
| template<int datasize> |
| ALWAYS_INLINE bool isValidScaledUImm12(int32_t offset) |
| { |
| int32_t maxPImm = 4095 * (datasize / 8); |
| if (offset < 0) |
| return false; |
| if (offset > maxPImm) |
| return false; |
| if (offset & ((datasize / 8) - 1)) |
| return false; |
| return true; |
| } |
| |
| ALWAYS_INLINE bool isValidSignedImm9(int32_t value) |
| { |
| return isInt9(value); |
| } |
| |
| ALWAYS_INLINE bool isValidSignedImm7(int32_t value, int alignmentShiftAmount) |
| { |
| constexpr int32_t disallowedHighBits = 32 - 7; |
| int32_t shiftedValue = value >> alignmentShiftAmount; |
| bool fitsIn7Bits = shiftedValue == ((shiftedValue << disallowedHighBits) >> disallowedHighBits); |
| bool hasCorrectAlignment = value == (shiftedValue << alignmentShiftAmount); |
| return fitsIn7Bits && hasCorrectAlignment; |
| } |
| |
| class ARM64LogicalImmediate { |
| public: |
| static ARM64LogicalImmediate create32(uint32_t value) |
| { |
| // Check for 0, -1 - these cannot be encoded. |
| if (!value || !~value) |
| return InvalidLogicalImmediate; |
| |
| // First look for a 32-bit pattern, then for repeating 16-bit |
| // patterns, 8-bit, 4-bit, and finally 2-bit. |
| |
| unsigned hsb, lsb; |
| bool inverted; |
| if (findBitRange<32>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<32>(hsb, lsb, inverted); |
| |
| if ((value & 0xffff) != (value >> 16)) |
| return InvalidLogicalImmediate; |
| value &= 0xffff; |
| |
| if (findBitRange<16>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<16>(hsb, lsb, inverted); |
| |
| if ((value & 0xff) != (value >> 8)) |
| return InvalidLogicalImmediate; |
| value &= 0xff; |
| |
| if (findBitRange<8>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<8>(hsb, lsb, inverted); |
| |
| if ((value & 0xf) != (value >> 4)) |
| return InvalidLogicalImmediate; |
| value &= 0xf; |
| |
| if (findBitRange<4>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<4>(hsb, lsb, inverted); |
| |
| if ((value & 0x3) != (value >> 2)) |
| return InvalidLogicalImmediate; |
| value &= 0x3; |
| |
| if (findBitRange<2>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<2>(hsb, lsb, inverted); |
| |
| return InvalidLogicalImmediate; |
| } |
| |
| static ARM64LogicalImmediate create64(uint64_t value) |
| { |
| // Check for 0, -1 - these cannot be encoded. |
| if (!value || !~value) |
| return InvalidLogicalImmediate; |
| |
| // Look for a contiguous bit range. |
| unsigned hsb, lsb; |
| bool inverted; |
| if (findBitRange<64>(value, hsb, lsb, inverted)) |
| return encodeLogicalImmediate<64>(hsb, lsb, inverted); |
| |
| // If the high & low 32 bits are equal, we can try for a 32-bit (or narrower) pattern. |
| if (static_cast<uint32_t>(value) == static_cast<uint32_t>(value >> 32)) |
| return create32(static_cast<uint32_t>(value)); |
| return InvalidLogicalImmediate; |
| } |
| |
| int value() const |
| { |
| ASSERT(isValid()); |
| return m_value; |
| } |
| |
| bool isValid() const |
| { |
| return m_value != InvalidLogicalImmediate; |
| } |
| |
| bool is64bit() const |
| { |
| return m_value & (1 << 12); |
| } |
| |
| private: |
| ARM64LogicalImmediate(int value) |
| : m_value(value) |
| { |
| } |
| |
| // Generate a mask with bits in the range hsb..0 set, for example: |
| // hsb:63 = 0xffffffffffffffff |
| // hsb:42 = 0x000007ffffffffff |
| // hsb: 0 = 0x0000000000000001 |
| static uint64_t mask(unsigned hsb) |
| { |
| ASSERT(hsb < 64); |
| return 0xffffffffffffffffull >> (63 - hsb); |
| } |
| |
| template<unsigned N> |
| static void partialHSB(uint64_t& value, unsigned&result) |
| { |
| if (value & (0xffffffffffffffffull << N)) { |
| result += N; |
| value >>= N; |
| } |
| } |
| |
| // Find the bit number of the highest bit set in a non-zero value, for example: |
| // 0x8080808080808080 = hsb:63 |
| // 0x0000000000000001 = hsb: 0 |
| // 0x000007ffffe00000 = hsb:42 |
| static unsigned highestSetBit(uint64_t value) |
| { |
| ASSERT(value); |
| unsigned hsb = 0; |
| partialHSB<32>(value, hsb); |
| partialHSB<16>(value, hsb); |
| partialHSB<8>(value, hsb); |
| partialHSB<4>(value, hsb); |
| partialHSB<2>(value, hsb); |
| partialHSB<1>(value, hsb); |
| return hsb; |
| } |
| |
| // This function takes a value and a bit width, where value obeys the following constraints: |
| // * bits outside of the width of the value must be zero. |
| // * bits within the width of value must neither be all clear or all set. |
| // The input is inspected to detect values that consist of either two or three contiguous |
| // ranges of bits. The output range hsb..lsb will describe the second range of the value. |
| // if the range is set, inverted will be false, and if the range is clear, inverted will |
| // be true. For example (with width 8): |
| // 00001111 = hsb:3, lsb:0, inverted:false |
| // 11110000 = hsb:3, lsb:0, inverted:true |
| // 00111100 = hsb:5, lsb:2, inverted:false |
| // 11000011 = hsb:5, lsb:2, inverted:true |
| template<unsigned width> |
| static bool findBitRange(uint64_t value, unsigned& hsb, unsigned& lsb, bool& inverted) |
| { |
| ASSERT(value & mask(width - 1)); |
| ASSERT(value != mask(width - 1)); |
| ASSERT(!(value & ~mask(width - 1))); |
| |
| // Detect cases where the top bit is set; if so, flip all the bits & set invert. |
| // This halves the number of patterns we need to look for. |
| const uint64_t msb = 1ull << (width - 1); |
| if ((inverted = (value & msb))) |
| value ^= mask(width - 1); |
| |
| // Find the highest set bit in value, generate a corresponding mask & flip all |
| // bits under it. |
| hsb = highestSetBit(value); |
| value ^= mask(hsb); |
| if (!value) { |
| // If this cleared the value, then the range hsb..0 was all set. |
| lsb = 0; |
| return true; |
| } |
| |
| // Try making one more mask, and flipping the bits! |
| lsb = highestSetBit(value); |
| value ^= mask(lsb); |
| if (!value) { |
| // Success - but lsb actually points to the hsb of a third range - add one |
| // to get to the lsb of the mid range. |
| ++lsb; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Encodes the set of immN:immr:imms fields found in a logical immediate. |
| template<unsigned width> |
| static int encodeLogicalImmediate(unsigned hsb, unsigned lsb, bool inverted) |
| { |
| // Check width is a power of 2! |
| ASSERT(!(width & (width -1))); |
| ASSERT(width <= 64 && width >= 2); |
| ASSERT(hsb >= lsb); |
| ASSERT(hsb < width); |
| |
| int immN = 0; |
| int imms = 0; |
| int immr = 0; |
| |
| // For 64-bit values this is easy - just set immN to true, and imms just |
| // contains the bit number of the highest set bit of the set range. For |
| // values with narrower widths, these are encoded by a leading set of |
| // one bits, followed by a zero bit, followed by the remaining set of bits |
| // being the high bit of the range. For a 32-bit immediate there are no |
| // leading one bits, just a zero followed by a five bit number. For a |
| // 16-bit immediate there is one one bit, a zero bit, and then a four bit |
| // bit-position, etc. |
| if (width == 64) |
| immN = 1; |
| else |
| imms = 63 & ~(width + width - 1); |
| |
| if (inverted) { |
| // if width is 64 & hsb is 62, then we have a value something like: |
| // 0x80000000ffffffff (in this case with lsb 32). |
| // The ror should be by 1, imms (effectively set width minus 1) is |
| // 32. Set width is full width minus cleared width. |
| immr = (width - 1) - hsb; |
| imms |= (width - ((hsb - lsb) + 1)) - 1; |
| } else { |
| // if width is 64 & hsb is 62, then we have a value something like: |
| // 0x7fffffff00000000 (in this case with lsb 32). |
| // The value is effectively rol'ed by lsb, which is equivalent to |
| // a ror by width - lsb (or 0, in the case where lsb is 0). imms |
| // is hsb - lsb. |
| immr = (width - lsb) & (width - 1); |
| imms |= hsb - lsb; |
| } |
| |
| return immN << 12 | immr << 6 | imms; |
| } |
| |
| static constexpr int InvalidLogicalImmediate = -1; |
| |
| int m_value; |
| }; |
| |
| } // namespace JSC. |