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