Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 1 | // Copyright 2014, VIXL authors |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are met: |
| 6 | // |
| 7 | // * Redistributions of source code must retain the above copyright notice, |
| 8 | // this list of conditions and the following disclaimer. |
| 9 | // * Redistributions in binary form must reproduce the above copyright notice, |
| 10 | // this list of conditions and the following disclaimer in the documentation |
| 11 | // and/or other materials provided with the distribution. |
| 12 | // * Neither the name of ARM Limited nor the names of its contributors may be |
| 13 | // used to endorse or promote products derived from this software without |
| 14 | // specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
| 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| 20 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 21 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 24 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 25 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | |
| 27 | #ifndef VIXL_AARCH64_TEST_UTILS_AARCH64_H_ |
| 28 | #define VIXL_AARCH64_TEST_UTILS_AARCH64_H_ |
| 29 | |
| 30 | #include "test-runner.h" |
| 31 | |
| 32 | #include "aarch64/cpu-aarch64.h" |
| 33 | #include "aarch64/disasm-aarch64.h" |
| 34 | #include "aarch64/macro-assembler-aarch64.h" |
| 35 | #include "aarch64/simulator-aarch64.h" |
| 36 | |
| 37 | namespace vixl { |
| 38 | namespace aarch64 { |
| 39 | |
| 40 | // Signalling and quiet NaNs in double format, constructed such that the bottom |
| 41 | // 32 bits look like a signalling or quiet NaN (as appropriate) when interpreted |
| 42 | // as a float. These values are not architecturally significant, but they're |
| 43 | // useful in tests for initialising registers. |
| 44 | extern const double kFP64SignallingNaN; |
| 45 | extern const double kFP64QuietNaN; |
| 46 | |
| 47 | // Signalling and quiet NaNs in float format. |
| 48 | extern const float kFP32SignallingNaN; |
| 49 | extern const float kFP32QuietNaN; |
| 50 | |
Jacob Bramley | ca78974 | 2018-09-13 14:25:46 +0100 | [diff] [blame] | 51 | // Signalling and quiet NaNs in half-precision float format. |
| 52 | extern const Float16 kFP16SignallingNaN; |
| 53 | extern const Float16 kFP16QuietNaN; |
| 54 | |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 55 | // Vector registers don't naturally fit any C++ native type, so define a class |
| 56 | // with convenient accessors. |
| 57 | // Note that this has to be a POD type so that we can use 'offsetof' with it. |
| 58 | template <int kSizeInBytes> |
| 59 | struct VectorValue { |
| 60 | template <typename T> |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 61 | T GetLane(int lane) const { |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 62 | size_t lane_size = sizeof(T); |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 63 | VIXL_CHECK(lane >= 0); |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 64 | VIXL_CHECK(kSizeInBytes >= ((lane + 1) * lane_size)); |
| 65 | T result; |
| 66 | memcpy(&result, bytes + (lane * lane_size), lane_size); |
| 67 | return result; |
| 68 | } |
| 69 | |
| 70 | template <typename T> |
| 71 | void SetLane(int lane, T value) { |
| 72 | size_t lane_size = sizeof(value); |
| 73 | VIXL_CHECK(kSizeInBytes >= ((lane + 1) * lane_size)); |
| 74 | memcpy(bytes + (lane * lane_size), &value, lane_size); |
| 75 | } |
| 76 | |
| 77 | bool Equals(const VectorValue<kSizeInBytes>& other) const { |
| 78 | return memcmp(bytes, other.bytes, kSizeInBytes) == 0; |
| 79 | } |
| 80 | |
| 81 | uint8_t bytes[kSizeInBytes]; |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 82 | }; |
| 83 | |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 84 | // It would be convenient to make these subclasses, so we can provide convenient |
| 85 | // constructors and utility methods specific to each register type, but we can't |
| 86 | // do that because it makes the result a non-POD type, and then we can't use |
| 87 | // 'offsetof' in RegisterDump::Dump. |
| 88 | typedef VectorValue<kQRegSizeInBytes> QRegisterValue; |
| 89 | typedef VectorValue<kZRegMaxSizeInBytes> ZRegisterValue; |
| 90 | typedef VectorValue<kPRegMaxSizeInBytes> PRegisterValue; |
| 91 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 92 | // RegisterDump: Object allowing integer, floating point and flags registers |
| 93 | // to be saved to itself for future reference. |
| 94 | class RegisterDump { |
| 95 | public: |
| 96 | RegisterDump() : completed_(false) { |
| 97 | VIXL_ASSERT(sizeof(dump_.d_[0]) == kDRegSizeInBytes); |
| 98 | VIXL_ASSERT(sizeof(dump_.s_[0]) == kSRegSizeInBytes); |
Carey Williams | d8bb357 | 2018-04-10 11:58:07 +0100 | [diff] [blame] | 99 | VIXL_ASSERT(sizeof(dump_.h_[0]) == kHRegSizeInBytes); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 100 | VIXL_ASSERT(sizeof(dump_.d_[0]) == kXRegSizeInBytes); |
| 101 | VIXL_ASSERT(sizeof(dump_.s_[0]) == kWRegSizeInBytes); |
| 102 | VIXL_ASSERT(sizeof(dump_.x_[0]) == kXRegSizeInBytes); |
| 103 | VIXL_ASSERT(sizeof(dump_.w_[0]) == kWRegSizeInBytes); |
| 104 | VIXL_ASSERT(sizeof(dump_.q_[0]) == kQRegSizeInBytes); |
| 105 | } |
| 106 | |
| 107 | // The Dump method generates code to store a snapshot of the register values. |
| 108 | // It needs to be able to use the stack temporarily, and requires that the |
| 109 | // current stack pointer is sp, and is properly aligned. |
| 110 | // |
| 111 | // The dumping code is generated though the given MacroAssembler. No registers |
| 112 | // are corrupted in the process, but the stack is used briefly. The flags will |
| 113 | // be corrupted during this call. |
| 114 | void Dump(MacroAssembler* assm); |
| 115 | |
| 116 | // Register accessors. |
| 117 | inline int32_t wreg(unsigned code) const { |
| 118 | if (code == kSPRegInternalCode) { |
| 119 | return wspreg(); |
| 120 | } |
| 121 | VIXL_ASSERT(RegAliasesMatch(code)); |
| 122 | return dump_.w_[code]; |
| 123 | } |
| 124 | |
| 125 | inline int64_t xreg(unsigned code) const { |
| 126 | if (code == kSPRegInternalCode) { |
| 127 | return spreg(); |
| 128 | } |
| 129 | VIXL_ASSERT(RegAliasesMatch(code)); |
| 130 | return dump_.x_[code]; |
| 131 | } |
| 132 | |
Jacob Bramley | cf93ad5 | 2019-04-15 16:00:22 +0100 | [diff] [blame] | 133 | // VRegister accessors. |
Carey Williams | d8bb357 | 2018-04-10 11:58:07 +0100 | [diff] [blame] | 134 | inline uint16_t hreg_bits(unsigned code) const { |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 135 | VIXL_ASSERT(VRegAliasesMatch(code)); |
Carey Williams | d8bb357 | 2018-04-10 11:58:07 +0100 | [diff] [blame] | 136 | return dump_.h_[code]; |
| 137 | } |
| 138 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 139 | inline uint32_t sreg_bits(unsigned code) const { |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 140 | VIXL_ASSERT(VRegAliasesMatch(code)); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 141 | return dump_.s_[code]; |
| 142 | } |
| 143 | |
Jacob Bramley | ca78974 | 2018-09-13 14:25:46 +0100 | [diff] [blame] | 144 | inline Float16 hreg(unsigned code) const { |
| 145 | return RawbitsToFloat16(hreg_bits(code)); |
Carey Williams | d8bb357 | 2018-04-10 11:58:07 +0100 | [diff] [blame] | 146 | } |
| 147 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 148 | inline float sreg(unsigned code) const { |
| 149 | return RawbitsToFloat(sreg_bits(code)); |
| 150 | } |
| 151 | |
| 152 | inline uint64_t dreg_bits(unsigned code) const { |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 153 | VIXL_ASSERT(VRegAliasesMatch(code)); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 154 | return dump_.d_[code]; |
| 155 | } |
| 156 | |
| 157 | inline double dreg(unsigned code) const { |
| 158 | return RawbitsToDouble(dreg_bits(code)); |
| 159 | } |
| 160 | |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 161 | inline QRegisterValue qreg(unsigned code) const { return dump_.q_[code]; } |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 162 | |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 163 | template <typename T> |
| 164 | inline T zreg_lane(unsigned code, int lane) const { |
| 165 | VIXL_ASSERT(VRegAliasesMatch(code)); |
| 166 | VIXL_ASSERT(CPUHas(CPUFeatures::kSVE)); |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 167 | VIXL_ASSERT(lane < GetSVELaneCount(sizeof(T) * kBitsPerByte)); |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 168 | return dump_.z_[code].GetLane<T>(lane); |
| 169 | } |
| 170 | |
| 171 | inline uint64_t zreg_lane(unsigned code, |
| 172 | unsigned size_in_bits, |
| 173 | int lane) const { |
| 174 | switch (size_in_bits) { |
| 175 | case kBRegSize: |
| 176 | return zreg_lane<uint8_t>(code, lane); |
| 177 | case kHRegSize: |
| 178 | return zreg_lane<uint16_t>(code, lane); |
| 179 | case kSRegSize: |
| 180 | return zreg_lane<uint32_t>(code, lane); |
| 181 | case kDRegSize: |
| 182 | return zreg_lane<uint64_t>(code, lane); |
| 183 | } |
| 184 | VIXL_UNREACHABLE(); |
| 185 | return 0; |
| 186 | } |
| 187 | |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 188 | inline uint64_t preg_lane(unsigned code, |
| 189 | unsigned p_bits_per_lane, |
| 190 | int lane) const { |
| 191 | VIXL_ASSERT(CPUHas(CPUFeatures::kSVE)); |
| 192 | VIXL_ASSERT(lane < GetSVELaneCount(p_bits_per_lane * kZRegBitsPerPRegBit)); |
| 193 | // Load a chunk and extract the necessary bits. The chunk size is arbitrary. |
| 194 | typedef uint64_t Chunk; |
| 195 | const size_t kChunkSizeInBits = sizeof(Chunk) * kBitsPerByte; |
| 196 | VIXL_ASSERT(IsPowerOf2(p_bits_per_lane)); |
| 197 | VIXL_ASSERT(p_bits_per_lane <= kChunkSizeInBits); |
| 198 | |
| 199 | int chunk_index = (lane * p_bits_per_lane) / kChunkSizeInBits; |
| 200 | int bit_index = (lane * p_bits_per_lane) % kChunkSizeInBits; |
| 201 | Chunk chunk = dump_.p_[code].GetLane<Chunk>(chunk_index); |
| 202 | return (chunk >> bit_index) & GetUintMask(p_bits_per_lane); |
| 203 | } |
| 204 | |
| 205 | inline int GetSVELaneCount(int lane_size_in_bits) const { |
Jacob Bramley | 85a9c10 | 2019-12-09 17:48:29 +0000 | [diff] [blame] | 206 | VIXL_ASSERT(lane_size_in_bits > 0); |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 207 | VIXL_ASSERT((dump_.vl_ % lane_size_in_bits) == 0); |
| 208 | uint64_t count = dump_.vl_ / lane_size_in_bits; |
| 209 | VIXL_ASSERT(count <= INT_MAX); |
| 210 | return static_cast<int>(count); |
| 211 | } |
| 212 | |
| 213 | template <typename T> |
| 214 | inline bool HasSVELane(T reg, int lane) const { |
| 215 | VIXL_ASSERT(reg.IsZRegister() || reg.IsPRegister()); |
| 216 | return lane < GetSVELaneCount(reg.GetLaneSizeInBits()); |
| 217 | } |
| 218 | |
Jacob Bramley | 199339d | 2019-08-05 18:49:13 +0100 | [diff] [blame] | 219 | template <typename T> |
| 220 | inline uint64_t GetSVELane(T reg, int lane) const { |
| 221 | VIXL_ASSERT(HasSVELane(reg, lane)); |
| 222 | if (reg.IsZRegister()) { |
| 223 | return zreg_lane(reg.GetCode(), reg.GetLaneSizeInBits(), lane); |
| 224 | } else if (reg.IsPRegister()) { |
| 225 | VIXL_ASSERT((reg.GetLaneSizeInBits() % kZRegBitsPerPRegBit) == 0); |
| 226 | return preg_lane(reg.GetCode(), |
| 227 | reg.GetLaneSizeInBits() / kZRegBitsPerPRegBit, |
| 228 | lane); |
| 229 | } else { |
| 230 | VIXL_ABORT(); |
| 231 | } |
| 232 | } |
| 233 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 234 | // Stack pointer accessors. |
| 235 | inline int64_t spreg() const { |
| 236 | VIXL_ASSERT(SPRegAliasesMatch()); |
| 237 | return dump_.sp_; |
| 238 | } |
| 239 | |
| 240 | inline int32_t wspreg() const { |
| 241 | VIXL_ASSERT(SPRegAliasesMatch()); |
| 242 | return static_cast<int32_t>(dump_.wsp_); |
| 243 | } |
| 244 | |
| 245 | // Flags accessors. |
| 246 | inline uint32_t flags_nzcv() const { |
| 247 | VIXL_ASSERT(IsComplete()); |
| 248 | VIXL_ASSERT((dump_.flags_ & ~Flags_mask) == 0); |
| 249 | return dump_.flags_ & Flags_mask; |
| 250 | } |
| 251 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 252 | inline bool IsComplete() const { return completed_; } |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 253 | |
| 254 | private: |
| 255 | // Indicate whether the dump operation has been completed. |
| 256 | bool completed_; |
| 257 | |
| 258 | // Check that the lower 32 bits of x<code> exactly match the 32 bits of |
| 259 | // w<code>. A failure of this test most likely represents a failure in the |
| 260 | // ::Dump method, or a failure in the simulator. |
| 261 | bool RegAliasesMatch(unsigned code) const { |
| 262 | VIXL_ASSERT(IsComplete()); |
| 263 | VIXL_ASSERT(code < kNumberOfRegisters); |
| 264 | return ((dump_.x_[code] & kWRegMask) == dump_.w_[code]); |
| 265 | } |
| 266 | |
| 267 | // As RegAliasesMatch, but for the stack pointer. |
| 268 | bool SPRegAliasesMatch() const { |
| 269 | VIXL_ASSERT(IsComplete()); |
| 270 | return ((dump_.sp_ & kWRegMask) == dump_.wsp_); |
| 271 | } |
| 272 | |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 273 | // As RegAliasesMatch, but for Z and V registers. |
| 274 | bool VRegAliasesMatch(unsigned code) const { |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 275 | VIXL_ASSERT(IsComplete()); |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 276 | VIXL_ASSERT(code < kNumberOfVRegisters); |
| 277 | bool match = ((dump_.q_[code].GetLane<uint64_t>(0) == dump_.d_[code]) && |
| 278 | ((dump_.d_[code] & kSRegMask) == dump_.s_[code]) && |
| 279 | ((dump_.s_[code] & kHRegMask) == dump_.h_[code])); |
| 280 | if (CPUHas(CPUFeatures::kSVE)) { |
| 281 | bool z_match = |
| 282 | memcmp(&dump_.q_[code], &dump_.z_[code], kQRegSizeInBytes) == 0; |
| 283 | match = match && z_match; |
| 284 | } |
| 285 | return match; |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 286 | } |
| 287 | |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 288 | // Record the CPUFeatures enabled when Dump was called. |
| 289 | CPUFeatures dump_cpu_features_; |
| 290 | |
| 291 | // Convenience pass-through for CPU feature checks. |
| 292 | bool CPUHas(CPUFeatures::Feature feature0, |
| 293 | CPUFeatures::Feature feature1 = CPUFeatures::kNone, |
| 294 | CPUFeatures::Feature feature2 = CPUFeatures::kNone, |
| 295 | CPUFeatures::Feature feature3 = CPUFeatures::kNone) const { |
| 296 | return dump_cpu_features_.Has(feature0, feature1, feature2, feature3); |
| 297 | } |
| 298 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 299 | // Store all the dumped elements in a simple struct so the implementation can |
| 300 | // use offsetof to quickly find the correct field. |
| 301 | struct dump_t { |
| 302 | // Core registers. |
| 303 | uint64_t x_[kNumberOfRegisters]; |
| 304 | uint32_t w_[kNumberOfRegisters]; |
| 305 | |
| 306 | // Floating-point registers, as raw bits. |
Jacob Bramley | cf93ad5 | 2019-04-15 16:00:22 +0100 | [diff] [blame] | 307 | uint64_t d_[kNumberOfVRegisters]; |
| 308 | uint32_t s_[kNumberOfVRegisters]; |
| 309 | uint16_t h_[kNumberOfVRegisters]; |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 310 | |
| 311 | // Vector registers. |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 312 | QRegisterValue q_[kNumberOfVRegisters]; |
| 313 | ZRegisterValue z_[kNumberOfZRegisters]; |
| 314 | |
| 315 | PRegisterValue p_[kNumberOfPRegisters]; |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 316 | |
| 317 | // The stack pointer. |
| 318 | uint64_t sp_; |
| 319 | uint64_t wsp_; |
| 320 | |
| 321 | // NZCV flags, stored in bits 28 to 31. |
| 322 | // bit[31] : Negative |
| 323 | // bit[30] : Zero |
| 324 | // bit[29] : Carry |
| 325 | // bit[28] : oVerflow |
| 326 | uint64_t flags_; |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 327 | |
| 328 | // The SVE "VL" (vector length) in bits. |
| 329 | uint64_t vl_; |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 330 | } dump_; |
| 331 | }; |
| 332 | |
Jacob Bramley | a1bc22a | 2019-02-08 17:20:37 +0000 | [diff] [blame] | 333 | // Some tests want to check that a value is _not_ equal to a reference value. |
| 334 | // These enum values can be used to control the error reporting behaviour. |
| 335 | enum ExpectedResult { kExpectEqual, kExpectNotEqual }; |
| 336 | |
| 337 | // The Equal* methods return true if the result matches the reference value. |
| 338 | // They all print an error message to the console if the result is incorrect |
| 339 | // (according to the ExpectedResult argument, or kExpectEqual if it is absent). |
| 340 | // |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 341 | // Some of these methods don't use the RegisterDump argument, but they have to |
| 342 | // accept them so that they can overload those that take register arguments. |
| 343 | bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result); |
Jacob Bramley | a1bc22a | 2019-02-08 17:20:37 +0000 | [diff] [blame] | 344 | bool Equal64(uint64_t reference, |
| 345 | const RegisterDump*, |
| 346 | uint64_t result, |
| 347 | ExpectedResult option = kExpectEqual); |
Jacob Bramley | aca39bd | 2025-01-14 12:00:22 +0000 | [diff] [blame] | 348 | bool Equal64(std::vector<uint64_t> reference_list, |
| 349 | const RegisterDump*, |
| 350 | uint64_t result, |
| 351 | ExpectedResult option = kExpectEqual); |
Jacob Bramley | d77a8e4 | 2019-02-12 16:52:24 +0000 | [diff] [blame] | 352 | bool Equal128(QRegisterValue expected, |
| 353 | const RegisterDump*, |
| 354 | QRegisterValue result); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 355 | |
Jacob Bramley | ca78974 | 2018-09-13 14:25:46 +0100 | [diff] [blame] | 356 | bool EqualFP16(Float16 expected, const RegisterDump*, uint16_t result); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 357 | bool EqualFP32(float expected, const RegisterDump*, float result); |
| 358 | bool EqualFP64(double expected, const RegisterDump*, double result); |
| 359 | |
| 360 | bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg); |
Jacob Bramley | a1bc22a | 2019-02-08 17:20:37 +0000 | [diff] [blame] | 361 | bool Equal64(uint64_t reference, |
| 362 | const RegisterDump* core, |
| 363 | const Register& reg, |
| 364 | ExpectedResult option = kExpectEqual); |
Jacob Bramley | aca39bd | 2025-01-14 12:00:22 +0000 | [diff] [blame] | 365 | bool Equal64(std::vector<uint64_t> reference_list, |
| 366 | const RegisterDump* core, |
| 367 | const Register& reg, |
| 368 | ExpectedResult option = kExpectEqual); |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 369 | bool Equal64(uint64_t expected, |
| 370 | const RegisterDump* core, |
| 371 | const VRegister& vreg); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 372 | |
Jacob Bramley | ca78974 | 2018-09-13 14:25:46 +0100 | [diff] [blame] | 373 | bool EqualFP16(Float16 expected, |
| 374 | const RegisterDump* core, |
Jacob Bramley | cf93ad5 | 2019-04-15 16:00:22 +0100 | [diff] [blame] | 375 | const VRegister& fpreg); |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 376 | bool EqualFP32(float expected, |
| 377 | const RegisterDump* core, |
Jacob Bramley | cf93ad5 | 2019-04-15 16:00:22 +0100 | [diff] [blame] | 378 | const VRegister& fpreg); |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 379 | bool EqualFP64(double expected, |
| 380 | const RegisterDump* core, |
Jacob Bramley | cf93ad5 | 2019-04-15 16:00:22 +0100 | [diff] [blame] | 381 | const VRegister& fpreg); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 382 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 383 | bool Equal64(const Register& reg0, |
| 384 | const RegisterDump* core, |
Jacob Bramley | a1bc22a | 2019-02-08 17:20:37 +0000 | [diff] [blame] | 385 | const Register& reg1, |
| 386 | ExpectedResult option = kExpectEqual); |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 387 | bool Equal128(uint64_t expected_h, |
| 388 | uint64_t expected_l, |
| 389 | const RegisterDump* core, |
| 390 | const VRegister& reg); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 391 | |
| 392 | bool EqualNzcv(uint32_t expected, uint32_t result); |
| 393 | |
| 394 | bool EqualRegisters(const RegisterDump* a, const RegisterDump* b); |
| 395 | |
Jacob Bramley | a1bc22a | 2019-02-08 17:20:37 +0000 | [diff] [blame] | 396 | template <typename T0, typename T1> |
| 397 | bool NotEqual64(T0 reference, const RegisterDump* core, T1 result) { |
| 398 | return !Equal64(reference, core, result, kExpectNotEqual); |
| 399 | } |
| 400 | |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 401 | bool EqualSVELane(uint64_t expected, |
| 402 | const RegisterDump* core, |
| 403 | const ZRegister& reg, |
| 404 | int lane); |
| 405 | |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 406 | bool EqualSVELane(uint64_t expected, |
| 407 | const RegisterDump* core, |
Jacob Bramley | 199339d | 2019-08-05 18:49:13 +0100 | [diff] [blame] | 408 | const PRegister& reg, |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 409 | int lane); |
| 410 | |
Jacob Bramley | 6069fd4 | 2019-06-24 10:20:45 +0100 | [diff] [blame] | 411 | // Check that each SVE lane matches the corresponding expected[] value. The |
| 412 | // highest-indexed array element maps to the lowest-numbered lane. |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 413 | template <typename T, int N, typename R> |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 414 | bool EqualSVE(const T (&expected)[N], |
| 415 | const RegisterDump* core, |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 416 | const R& reg, |
| 417 | bool* printed_warning) { |
| 418 | VIXL_ASSERT(reg.IsZRegister() || reg.IsPRegister()); |
| 419 | VIXL_ASSERT(reg.HasLaneSize()); |
Jacob Bramley | 9d06c4d | 2019-05-13 18:15:06 +0100 | [diff] [blame] | 420 | // Evaluate and report errors on every lane, rather than just the first. |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 421 | bool equal = true; |
| 422 | for (int lane = 0; lane < N; ++lane) { |
Jacob Bramley | 2eaecf1 | 2019-05-01 15:46:34 +0100 | [diff] [blame] | 423 | if (!core->HasSVELane(reg, lane)) { |
| 424 | if (*printed_warning == false) { |
| 425 | *printed_warning = true; |
| 426 | printf( |
| 427 | "Warning: Ignoring SVE lanes beyond VL (%d bytes) " |
| 428 | "because the CPU does not implement them.\n", |
| 429 | core->GetSVELaneCount(kBRegSize)); |
| 430 | } |
| 431 | break; |
| 432 | } |
Jacob Bramley | 9d06c4d | 2019-05-13 18:15:06 +0100 | [diff] [blame] | 433 | // Map the highest-indexed array element to the lowest-numbered lane. |
| 434 | equal = EqualSVELane(expected[N - lane - 1], core, reg, lane) && equal; |
Jacob Bramley | 03c0b51 | 2019-02-22 16:42:06 +0000 | [diff] [blame] | 435 | } |
| 436 | return equal; |
| 437 | } |
| 438 | |
Jacob Bramley | 6069fd4 | 2019-06-24 10:20:45 +0100 | [diff] [blame] | 439 | // Check that each SVE lanes matches the `expected` value. |
Jacob Bramley | 199339d | 2019-08-05 18:49:13 +0100 | [diff] [blame] | 440 | template <typename R> |
| 441 | bool EqualSVE(uint64_t expected, |
Jacob Bramley | 6069fd4 | 2019-06-24 10:20:45 +0100 | [diff] [blame] | 442 | const RegisterDump* core, |
| 443 | const R& reg, |
| 444 | bool* printed_warning) { |
| 445 | VIXL_ASSERT(reg.IsZRegister() || reg.IsPRegister()); |
| 446 | VIXL_ASSERT(reg.HasLaneSize()); |
| 447 | USE(printed_warning); |
| 448 | // Evaluate and report errors on every lane, rather than just the first. |
| 449 | bool equal = true; |
| 450 | for (int lane = 0; lane < core->GetSVELaneCount(reg.GetLaneSizeInBits()); |
| 451 | ++lane) { |
| 452 | equal = EqualSVELane(expected, core, reg, lane) && equal; |
| 453 | } |
| 454 | return equal; |
| 455 | } |
| 456 | |
Jacob Bramley | 199339d | 2019-08-05 18:49:13 +0100 | [diff] [blame] | 457 | // Check that two Z or P registers are equal. |
| 458 | template <typename R> |
| 459 | bool EqualSVE(const R& expected, |
| 460 | const RegisterDump* core, |
| 461 | const R& result, |
| 462 | bool* printed_warning) { |
| 463 | VIXL_ASSERT(result.IsZRegister() || result.IsPRegister()); |
| 464 | VIXL_ASSERT(AreSameFormat(expected, result)); |
| 465 | USE(printed_warning); |
| 466 | |
| 467 | // If the lane size is omitted, pick a default. |
| 468 | if (!result.HasLaneSize()) { |
| 469 | return EqualSVE(expected.VnB(), core, result.VnB(), printed_warning); |
| 470 | } |
| 471 | |
| 472 | // Evaluate and report errors on every lane, rather than just the first. |
| 473 | bool equal = true; |
| 474 | int lane_size = result.GetLaneSizeInBits(); |
| 475 | for (int lane = 0; lane < core->GetSVELaneCount(lane_size); ++lane) { |
| 476 | uint64_t expected_lane = core->GetSVELane(expected, lane); |
| 477 | equal = equal && EqualSVELane(expected_lane, core, result, lane); |
| 478 | } |
| 479 | return equal; |
| 480 | } |
| 481 | |
| 482 | bool EqualMemory(const void* expected, |
| 483 | const void* result, |
Jacob Bramley | 33c99f9 | 2019-10-08 15:24:12 +0100 | [diff] [blame] | 484 | size_t size_in_bytes, |
| 485 | size_t zero_offset = 0); |
Jacob Bramley | 199339d | 2019-08-05 18:49:13 +0100 | [diff] [blame] | 486 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 487 | // Populate the w, x and r arrays with registers from the 'allowed' mask. The |
| 488 | // r array will be populated with <reg_size>-sized registers, |
| 489 | // |
| 490 | // This allows for tests which use large, parameterized blocks of registers |
| 491 | // (such as the push and pop tests), but where certain registers must be |
| 492 | // avoided as they are used for other purposes. |
| 493 | // |
| 494 | // Any of w, x, or r can be NULL if they are not required. |
| 495 | // |
| 496 | // The return value is a RegList indicating which registers were allocated. |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 497 | RegList PopulateRegisterArray(Register* w, |
| 498 | Register* x, |
| 499 | Register* r, |
| 500 | int reg_size, |
| 501 | int reg_count, |
| 502 | RegList allowed); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 503 | |
| 504 | // As PopulateRegisterArray, but for floating-point registers. |
Jacob Bramley | cf93ad5 | 2019-04-15 16:00:22 +0100 | [diff] [blame] | 505 | RegList PopulateVRegisterArray(VRegister* s, |
| 506 | VRegister* d, |
| 507 | VRegister* v, |
| 508 | int reg_size, |
| 509 | int reg_count, |
| 510 | RegList allowed); |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 511 | |
Josh Soref | b43d6ef | 2022-08-03 12:47:14 -0400 | [diff] [blame] | 512 | // Overwrite the contents of the specified registers. This enables tests to |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 513 | // check that register contents are written in cases where it's likely that the |
| 514 | // correct outcome could already be stored in the register. |
| 515 | // |
| 516 | // This always overwrites X-sized registers. If tests are operating on W |
| 517 | // registers, a subsequent write into an aliased W register should clear the |
| 518 | // top word anyway, so clobbering the full X registers should make tests more |
| 519 | // rigorous. |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 520 | void Clobber(MacroAssembler* masm, |
| 521 | RegList reg_list, |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 522 | uint64_t const value = 0xfedcba9876543210); |
| 523 | |
| 524 | // As Clobber, but for FP registers. |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 525 | void ClobberFP(MacroAssembler* masm, |
| 526 | RegList reg_list, |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 527 | double const value = kFP64SignallingNaN); |
| 528 | |
| 529 | // As Clobber, but for a CPURegList with either FP or integer registers. When |
| 530 | // using this method, the clobber value is always the default for the basic |
| 531 | // Clobber or ClobberFP functions. |
| 532 | void Clobber(MacroAssembler* masm, CPURegList reg_list); |
| 533 | |
TatWai Chong | 2cb1b61 | 2020-03-04 23:51:21 -0800 | [diff] [blame] | 534 | uint64_t GetSignallingNan(int size_in_bits); |
| 535 | |
Jacob Bramley | 6ebbba6 | 2019-10-09 15:02:10 +0100 | [diff] [blame] | 536 | // This class acts as a drop-in replacement for VIXL's MacroAssembler, giving |
| 537 | // CalculateSVEAddress public visibility. |
| 538 | // |
| 539 | // CalculateSVEAddress normally has protected visibility, but it's useful to |
| 540 | // test it in isolation because it is the basis of all SVE non-scatter-gather |
| 541 | // load and store fall-backs. |
| 542 | class CalculateSVEAddressMacroAssembler : public vixl::aarch64::MacroAssembler { |
| 543 | public: |
| 544 | void CalculateSVEAddress(const Register& xd, |
| 545 | const SVEMemOperand& addr, |
| 546 | int vl_divisor_log2) { |
| 547 | MacroAssembler::CalculateSVEAddress(xd, addr, vl_divisor_log2); |
| 548 | } |
| 549 | |
| 550 | void CalculateSVEAddress(const Register& xd, const SVEMemOperand& addr) { |
| 551 | MacroAssembler::CalculateSVEAddress(xd, addr); |
| 552 | } |
| 553 | }; |
| 554 | |
TatWai Chong | 7a0d367 | 2019-10-23 17:35:18 -0700 | [diff] [blame] | 555 | // This class acts as a drop-in replacement for VIXL's MacroAssembler, with |
| 556 | // fast NaN proparation mode switched on. |
| 557 | class FastNaNPropagationMacroAssembler : public MacroAssembler { |
| 558 | public: |
| 559 | FastNaNPropagationMacroAssembler() { |
| 560 | SetFPNaNPropagationOption(FastNaNPropagation); |
| 561 | } |
| 562 | }; |
| 563 | |
| 564 | // This class acts as a drop-in replacement for VIXL's MacroAssembler, with |
| 565 | // strict NaN proparation mode switched on. |
| 566 | class StrictNaNPropagationMacroAssembler : public MacroAssembler { |
| 567 | public: |
| 568 | StrictNaNPropagationMacroAssembler() { |
| 569 | SetFPNaNPropagationOption(StrictNaNPropagation); |
| 570 | } |
| 571 | }; |
| 572 | |
Jacob Bramley | 4fc4bec | 2020-01-15 16:54:50 +0000 | [diff] [blame] | 573 | // If the required features are available, return true. |
| 574 | // Otherwise: |
| 575 | // - Print a warning message, unless *queried_can_run indicates that we've |
| 576 | // already done so. |
| 577 | // - Return false. |
| 578 | // |
| 579 | // If *queried_can_run is NULL, it is treated as false. Otherwise, it is set to |
| 580 | // true, regardless of the return value. |
| 581 | // |
| 582 | // The warning message printed on failure is used by tools/threaded_tests.py to |
| 583 | // count skipped tests. A test must not print more than one such warning |
| 584 | // message. It is safe to call CanRun multiple times per test, as long as |
| 585 | // queried_can_run is propagated correctly between calls, and the first call to |
| 586 | // CanRun requires every feature that is required by subsequent calls. If |
| 587 | // queried_can_run is NULL, CanRun must not be called more than once per test. |
| 588 | bool CanRun(const CPUFeatures& required, bool* queried_can_run = NULL); |
| 589 | |
| 590 | // PushCalleeSavedRegisters(), PopCalleeSavedRegisters() and Dump() use NEON, so |
| 591 | // we need to enable it in the infrastructure code for each test. |
| 592 | static const CPUFeatures kInfrastructureCPUFeatures(CPUFeatures::kNEON); |
| 593 | |
TatWai Chong | ba9a148 | 2020-10-01 20:25:54 -0700 | [diff] [blame] | 594 | enum InputSet { |
| 595 | kIntInputSet = 0, |
| 596 | kFpInputSet, |
| 597 | }; |
| 598 | |
Martyn Capewell | dba51cc | 2020-08-27 13:48:26 +0100 | [diff] [blame] | 599 | // Initialise CPU registers to a predictable, non-zero set of values. This |
| 600 | // sets core, vector, predicate and flag registers, though leaves the stack |
| 601 | // pointer at its original value. |
TatWai Chong | ba9a148 | 2020-10-01 20:25:54 -0700 | [diff] [blame] | 602 | void SetInitialMachineState(MacroAssembler* masm, |
| 603 | InputSet input_set = kIntInputSet); |
Martyn Capewell | dba51cc | 2020-08-27 13:48:26 +0100 | [diff] [blame] | 604 | |
| 605 | // Compute a CRC32 hash of the machine state, and store it to dst. The hash |
| 606 | // covers core (not sp), vector (lower 128 bits), predicate (lower 16 bits) |
| 607 | // and flag registers. |
| 608 | void ComputeMachineStateHash(MacroAssembler* masm, uint32_t* dst); |
| 609 | |
| 610 | // The TEST_SVE macro works just like the usual TEST macro, but the resulting |
| 611 | // function receives a `const Test& config` argument, to allow it to query the |
| 612 | // vector length. |
| 613 | #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 |
| 614 | |
mmc28a | 1a2c1d3 | 2024-02-01 16:43:49 +0000 | [diff] [blame] | 615 | #define TEST_SVE_INNER(type, name) \ |
| 616 | void Test##name(Test* config); \ |
| 617 | Test* test_##name##_list[] = {Test::MakeSVETest(128, \ |
| 618 | "AARCH64_" type "_" #name \ |
| 619 | "_vl128", \ |
| 620 | &Test##name), \ |
Martyn Capewell | c40e288 | 2024-03-22 13:47:46 +0000 | [diff] [blame^] | 621 | Test::MakeSVETest(512, \ |
mmc28a | 1a2c1d3 | 2024-02-01 16:43:49 +0000 | [diff] [blame] | 622 | "AARCH64_" type "_" #name \ |
Martyn Capewell | c40e288 | 2024-03-22 13:47:46 +0000 | [diff] [blame^] | 623 | "_vl512", \ |
mmc28a | 1a2c1d3 | 2024-02-01 16:43:49 +0000 | [diff] [blame] | 624 | &Test##name), \ |
| 625 | Test::MakeSVETest(2048, \ |
| 626 | "AARCH64_" type "_" #name \ |
| 627 | "_vl2048", \ |
| 628 | &Test##name)}; \ |
Martyn Capewell | dba51cc | 2020-08-27 13:48:26 +0100 | [diff] [blame] | 629 | void Test##name(Test* config) |
| 630 | |
| 631 | #define SVE_SETUP_WITH_FEATURES(...) \ |
| 632 | SETUP_WITH_FEATURES(__VA_ARGS__); \ |
| 633 | simulator.SetVectorLengthInBits(config->sve_vl_in_bits()) |
| 634 | |
| 635 | #else |
| 636 | // Otherwise, just use whatever the hardware provides. |
| 637 | static const int kSVEVectorLengthInBits = |
| 638 | CPUFeatures::InferFromOS().Has(CPUFeatures::kSVE) |
| 639 | ? CPU::ReadSVEVectorLengthInBits() |
| 640 | : kZRegMinSize; |
| 641 | |
| 642 | #define TEST_SVE_INNER(type, name) \ |
| 643 | void Test##name(Test* config); \ |
| 644 | Test* test_##name##_vlauto = \ |
| 645 | Test::MakeSVETest(kSVEVectorLengthInBits, \ |
| 646 | "AARCH64_" type "_" #name "_vlauto", \ |
| 647 | &Test##name); \ |
| 648 | void Test##name(Test* config) |
| 649 | |
| 650 | #define SVE_SETUP_WITH_FEATURES(...) \ |
| 651 | SETUP_WITH_FEATURES(__VA_ARGS__); \ |
| 652 | USE(config) |
| 653 | |
| 654 | #endif |
| 655 | |
TatWai Chong | ba9a148 | 2020-10-01 20:25:54 -0700 | [diff] [blame] | 656 | // Call masm->Insr repeatedly to allow test inputs to be set up concisely. This |
| 657 | // is optimised for call-site clarity, not generated code quality, so it doesn't |
| 658 | // exist in the MacroAssembler itself. |
| 659 | // |
| 660 | // Usage: |
| 661 | // |
| 662 | // int values[] = { 42, 43, 44 }; |
| 663 | // InsrHelper(&masm, z0.VnS(), values); // Sets z0.S = { ..., 42, 43, 44 } |
| 664 | // |
| 665 | // The rightmost (highest-indexed) array element maps to the lowest-numbered |
| 666 | // lane. |
| 667 | template <typename T, size_t N> |
| 668 | void InsrHelper(MacroAssembler* masm, |
| 669 | const ZRegister& zdn, |
| 670 | const T (&values)[N]) { |
| 671 | for (size_t i = 0; i < N; i++) { |
| 672 | masm->Insr(zdn, values[i]); |
| 673 | } |
| 674 | } |
| 675 | |
Alexandre Rames | d383296 | 2016-07-04 15:03:43 +0100 | [diff] [blame] | 676 | } // namespace aarch64 |
| 677 | } // namespace vixl |
| 678 | |
| 679 | #endif // VIXL_AARCH64_TEST_UTILS_AARCH64_H_ |