Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT 2 : // This file is part of AsmJit project <https://asmjit.com> 3 : // 4 : // See asmjit.h or LICENSE.md for license and copyright information 5 : // SPDX-License-Identifier: Zlib 6 : 7 : #ifndef ASMJIT_ARM_A64UTILS_H_INCLUDED 8 : #define ASMJIT_ARM_A64UTILS_H_INCLUDED 9 : 10 : #include "a64globals.h" 11 : 12 : ASMJIT_BEGIN_SUB_NAMESPACE(a64) 13 : 14 : //! \addtogroup asmjit_a64 15 : //! \{ 16 : 17 : //! Public utilities and helpers for targeting AArch64 architecture. 18 : namespace Utils { 19 : 20 : //! Decomposed fields of a logical immediate value (AArch64). 21 : struct LogicalImm { 22 : uint32_t n; 23 : uint32_t s; 24 : uint32_t r; 25 : }; 26 : 27 : //! Encodes the given `imm` value of the given `width` to a logical immediate value represented as N, S, and R fields 28 : //! and writes these fields to `out`. 29 : //! 30 : //! Encoding Table: 31 : //! 32 : //! ``` 33 : //! +---+--------+--------+------+ 34 : //! | N | ImmS | ImmR | Size | 35 : //! +---+--------+--------+------+ 36 : //! | 1 | ssssss | rrrrrr | 64 | 37 : //! | 0 | 0sssss | .rrrrr | 32 | 38 : //! | 0 | 10ssss | ..rrrr | 16 | 39 : //! | 0 | 110sss | ...rrr | 8 | 40 : //! | 0 | 1110ss | ....rr | 4 | 41 : //! | 0 | 11110s | .....r | 2 | 42 : //! +---+--------+--------+------+ 43 : //! ``` 44 : ASMJIT_MAYBE_UNUSED 45 0 : static bool encodeLogicalImm(uint64_t imm, uint32_t width, a64::Utils::LogicalImm* out) noexcept { 46 : // Determine the element width, which must be 2, 4, 8, 16, 32, or 64 bits. 47 : do { 48 0 : width /= 2; 49 : uint64_t mask = (uint64_t(1) << width) - 1u; 50 0 : if ((imm & mask) != ((imm >> width) & mask)) { 51 0 : width *= 2; 52 0 : break; 53 : } 54 0 : } while (width > 2); 55 : 56 : // Patterns of all zeros and all ones are not encodable. 57 : uint64_t lsbMask = Support::lsbMask<uint64_t>(width); 58 0 : imm &= lsbMask; 59 : 60 0 : if (imm == 0 || imm == lsbMask) 61 : return false; 62 : 63 : // Inspect the pattern and get the most important bit indexes. 64 : // 65 : // oIndex <-+ +-> zIndex 66 : // | | 67 : // |..zeros..|oCount|zCount|..ones..| 68 : // |000000000|111111|000000|11111111| 69 : 70 0 : uint32_t zIndex = Support::ctz(~imm); 71 0 : uint64_t zImm = imm ^ ((uint64_t(1) << zIndex) - 1); 72 0 : uint32_t zCount = (zImm ? Support::ctz(zImm) : width) - zIndex; 73 : 74 : uint32_t oIndex = zIndex + zCount; 75 0 : uint64_t oImm = ~(zImm ^ Support::lsbMask<uint64_t>(oIndex)); 76 0 : uint32_t oCount = (oImm ? Support::ctz(oImm) : width) - (oIndex); 77 : 78 : // Verify whether the bit-pattern is encodable. 79 : uint64_t mustBeZero = oImm ^ ~Support::lsbMask<uint64_t>(oIndex + oCount); 80 0 : if (mustBeZero != 0 || (zIndex > 0 && width - (oIndex + oCount) != 0)) 81 : return false; 82 : 83 0 : out->n = width == 64; 84 0 : out->s = (oCount + zIndex - 1) | (Support::neg(width * 2) & 0x3F); 85 0 : out->r = width - oIndex; 86 0 : return true; 87 : } 88 : 89 : //! Returns true if the given `imm` value is encodable as a logical immediate. The `width` argument describes the 90 : //! width of the operation, and must be either 32 or 64. This function can be used to test whether an immediate 91 : //! value can be used with AND, ANDS, BIC, BICS, EON, EOR, ORN, and ORR instruction. 92 : ASMJIT_MAYBE_UNUSED 93 : static inline bool isLogicalImm(uint64_t imm, uint32_t width) noexcept { 94 : LogicalImm dummy; 95 : return encodeLogicalImm(imm, width, &dummy); 96 : } 97 : 98 : //! Returns true if the given `imm` value is a byte mask. Byte mask has each byte part of the value set to either 99 : //! 0x00 or 0xFF. Some ARM instructions accept immediates that form a byte-mask and this function can be used to 100 : //! verify that the immediate is encodable before using the value. 101 : template<typename T> 102 : static inline bool isByteMaskImm8(const T& imm) noexcept { 103 : constexpr T kMask = T(0x0101010101010101 & Support::allOnes<T>()); 104 0 : return imm == (imm & kMask) * T(255); 105 : } 106 : 107 : //! \cond 108 : //! A generic implementation that checjs whether a floating point value can be converted to ARM Imm8. 109 : template<typename T, uint32_t kNumBBits, uint32_t kNumCDEFGHBits, uint32_t kNumZeroBits> 110 : static inline bool isFPImm8Generic(T val) noexcept { 111 : constexpr uint32_t kAllBsMask = Support::lsbMask<uint32_t>(kNumBBits); 112 : constexpr uint32_t kB0Pattern = Support::bitMask(kNumBBits - 1); 113 : constexpr uint32_t kB1Pattern = kAllBsMask ^ kB0Pattern; 114 : 115 0 : T immZ = val & Support::lsbMask<T>(kNumZeroBits); 116 0 : uint32_t immB = uint32_t(val >> (kNumZeroBits + kNumCDEFGHBits)) & kAllBsMask; 117 : 118 : // ImmZ must be all zeros and ImmB must either be B0 or B1 pattern. 119 0 : return immZ == 0 && (immB == kB0Pattern || immB == kB1Pattern); 120 : } 121 : //! \endcond 122 : 123 : //! Returns true if the given half precision floating point `val` can be encoded as ARM IMM8 value, which represents 124 : //! a limited set of floating point immediate values, which can be used with FMOV instruction. 125 : //! 126 : //! The floating point must have bits distributed in the following way: 127 : //! 128 : //! ``` 129 : //! [aBbbcdef|gh000000] 130 : //! ``` 131 : static inline bool isFP16Imm8(uint32_t val) noexcept { return isFPImm8Generic<uint32_t, 3, 6, 6>(val); } 132 : 133 : //! Returns true if the given single precision floating point `val` can be encoded as ARM IMM8 value, which represents 134 : //! a limited set of floating point immediate values, which can be used with FMOV instruction. 135 : //! 136 : //! The floating point must have bits distributed in the following way: 137 : //! 138 : //! ``` 139 : //! [aBbbbbbc|defgh000|00000000|00000000] 140 : //! ``` 141 : static inline bool isFP32Imm8(uint32_t val) noexcept { return isFPImm8Generic<uint32_t, 6, 6, 19>(val); } 142 : //! \overload 143 : static inline bool isFP32Imm8(float val) noexcept { return isFP32Imm8(Support::bitCast<uint32_t>(val)); } 144 : 145 : //! Returns true if the given double precision floating point `val` can be encoded as ARM IMM8 value, which represents 146 : //! a limited set of floating point immediate values, which can be used with FMOV instruction. 147 : //! 148 : //! The floating point must have bits distributed in the following way: 149 : //! 150 : //! ``` 151 : //! [aBbbbbbb|bbcdefgh|00000000|00000000|00000000|00000000|00000000|00000000] 152 : //! ``` 153 : static inline bool isFP64Imm8(uint64_t val) noexcept { return isFPImm8Generic<uint64_t, 9, 6, 48>(val); } 154 : //! \overload 155 : static inline bool isFP64Imm8(double val) noexcept { return isFP64Imm8(Support::bitCast<uint64_t>(val)); } 156 : 157 : //! \cond 158 : template<typename T, uint32_t kNumBBits, uint32_t kNumCDEFGHBits, uint32_t kNumZeroBits> 159 : static inline uint32_t encodeFPToImm8Generic(T val) noexcept { 160 0 : uint32_t bits = uint32_t(val >> kNumZeroBits); 161 0 : return ((bits >> (kNumBBits + kNumCDEFGHBits - 7)) & 0x80u) | (bits & 0x7F); 162 : } 163 : //! \endcond 164 : 165 : //! Encodes a double precision floating point value into IMM8 format. 166 : //! 167 : //! \note This function expects that `isFP64Imm8(val) == true` so it doesn't perform any checks of the value and just 168 : //! rearranges some bits into Imm8 order. 169 : static inline uint32_t encodeFP64ToImm8(uint64_t val) noexcept { return encodeFPToImm8Generic<uint64_t, 9, 6, 48>(val); } 170 : //! \overload 171 : static inline uint32_t encodeFP64ToImm8(double val) noexcept { return encodeFP64ToImm8(Support::bitCast<uint64_t>(val)); } 172 : 173 : } // {Utils} 174 : 175 : //! \} 176 : 177 : ASMJIT_END_SUB_NAMESPACE 178 : 179 : #endif // ASMJIT_ARM_A64UTILS_H_INCLUDED 180 : 181 : 182 : #endif // __PLUMED_HAS_ASMJIT