| // Copyright 2014, ARM Limited |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // * Neither the name of ARM Limited nor the names of its contributors may be |
| // used to endorse or promote products derived from this software without |
| // specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
| // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #ifndef VIXL_A64_TEST_UTILS_A64_H_ |
| #define VIXL_A64_TEST_UTILS_A64_H_ |
| |
| #include "test-runner.h" |
| |
| #include "a64/cpu-a64.h" |
| #include "a64/disasm-a64.h" |
| #include "a64/macro-assembler-a64.h" |
| #include "a64/simulator-a64.h" |
| |
| namespace vixl { |
| |
| // Signalling and quiet NaNs in double format, constructed such that the bottom |
| // 32 bits look like a signalling or quiet NaN (as appropriate) when interpreted |
| // as a float. These values are not architecturally significant, but they're |
| // useful in tests for initialising registers. |
| extern const double kFP64SignallingNaN; |
| extern const double kFP64QuietNaN; |
| |
| // Signalling and quiet NaNs in float format. |
| extern const float kFP32SignallingNaN; |
| extern const float kFP32QuietNaN; |
| |
| // Structure representing Q registers in a RegisterDump. |
| struct vec128_t { |
| uint64_t l; |
| uint64_t h; |
| }; |
| |
| // RegisterDump: Object allowing integer, floating point and flags registers |
| // to be saved to itself for future reference. |
| class RegisterDump { |
| public: |
| RegisterDump() : completed_(false) { |
| VIXL_ASSERT(sizeof(dump_.d_[0]) == kDRegSizeInBytes); |
| VIXL_ASSERT(sizeof(dump_.s_[0]) == kSRegSizeInBytes); |
| VIXL_ASSERT(sizeof(dump_.d_[0]) == kXRegSizeInBytes); |
| VIXL_ASSERT(sizeof(dump_.s_[0]) == kWRegSizeInBytes); |
| VIXL_ASSERT(sizeof(dump_.x_[0]) == kXRegSizeInBytes); |
| VIXL_ASSERT(sizeof(dump_.w_[0]) == kWRegSizeInBytes); |
| VIXL_ASSERT(sizeof(dump_.q_[0]) == kQRegSizeInBytes); |
| } |
| |
| // The Dump method generates code to store a snapshot of the register values. |
| // It needs to be able to use the stack temporarily, and requires that the |
| // current stack pointer is sp, and is properly aligned. |
| // |
| // The dumping code is generated though the given MacroAssembler. No registers |
| // are corrupted in the process, but the stack is used briefly. The flags will |
| // be corrupted during this call. |
| void Dump(MacroAssembler* assm); |
| |
| // Register accessors. |
| inline int32_t wreg(unsigned code) const { |
| if (code == kSPRegInternalCode) { |
| return wspreg(); |
| } |
| VIXL_ASSERT(RegAliasesMatch(code)); |
| return dump_.w_[code]; |
| } |
| |
| inline int64_t xreg(unsigned code) const { |
| if (code == kSPRegInternalCode) { |
| return spreg(); |
| } |
| VIXL_ASSERT(RegAliasesMatch(code)); |
| return dump_.x_[code]; |
| } |
| |
| // FPRegister accessors. |
| inline uint32_t sreg_bits(unsigned code) const { |
| VIXL_ASSERT(FPRegAliasesMatch(code)); |
| return dump_.s_[code]; |
| } |
| |
| inline float sreg(unsigned code) const { |
| return rawbits_to_float(sreg_bits(code)); |
| } |
| |
| inline uint64_t dreg_bits(unsigned code) const { |
| VIXL_ASSERT(FPRegAliasesMatch(code)); |
| return dump_.d_[code]; |
| } |
| |
| inline double dreg(unsigned code) const { |
| return rawbits_to_double(dreg_bits(code)); |
| } |
| |
| inline vec128_t qreg(unsigned code) const { |
| return dump_.q_[code]; |
| } |
| |
| // Stack pointer accessors. |
| inline int64_t spreg() const { |
| VIXL_ASSERT(SPRegAliasesMatch()); |
| return dump_.sp_; |
| } |
| |
| inline int32_t wspreg() const { |
| VIXL_ASSERT(SPRegAliasesMatch()); |
| return static_cast<int32_t>(dump_.wsp_); |
| } |
| |
| // Flags accessors. |
| inline uint32_t flags_nzcv() const { |
| VIXL_ASSERT(IsComplete()); |
| VIXL_ASSERT((dump_.flags_ & ~Flags_mask) == 0); |
| return dump_.flags_ & Flags_mask; |
| } |
| |
| inline bool IsComplete() const { |
| return completed_; |
| } |
| |
| private: |
| // Indicate whether the dump operation has been completed. |
| bool completed_; |
| |
| // Check that the lower 32 bits of x<code> exactly match the 32 bits of |
| // w<code>. A failure of this test most likely represents a failure in the |
| // ::Dump method, or a failure in the simulator. |
| bool RegAliasesMatch(unsigned code) const { |
| VIXL_ASSERT(IsComplete()); |
| VIXL_ASSERT(code < kNumberOfRegisters); |
| return ((dump_.x_[code] & kWRegMask) == dump_.w_[code]); |
| } |
| |
| // As RegAliasesMatch, but for the stack pointer. |
| bool SPRegAliasesMatch() const { |
| VIXL_ASSERT(IsComplete()); |
| return ((dump_.sp_ & kWRegMask) == dump_.wsp_); |
| } |
| |
| // As RegAliasesMatch, but for floating-point registers. |
| bool FPRegAliasesMatch(unsigned code) const { |
| VIXL_ASSERT(IsComplete()); |
| VIXL_ASSERT(code < kNumberOfFPRegisters); |
| return (dump_.d_[code] & kSRegMask) == dump_.s_[code]; |
| } |
| |
| // Store all the dumped elements in a simple struct so the implementation can |
| // use offsetof to quickly find the correct field. |
| struct dump_t { |
| // Core registers. |
| uint64_t x_[kNumberOfRegisters]; |
| uint32_t w_[kNumberOfRegisters]; |
| |
| // Floating-point registers, as raw bits. |
| uint64_t d_[kNumberOfFPRegisters]; |
| uint32_t s_[kNumberOfFPRegisters]; |
| |
| // Vector registers. |
| vec128_t q_[kNumberOfVRegisters]; |
| |
| // The stack pointer. |
| uint64_t sp_; |
| uint64_t wsp_; |
| |
| // NZCV flags, stored in bits 28 to 31. |
| // bit[31] : Negative |
| // bit[30] : Zero |
| // bit[29] : Carry |
| // bit[28] : oVerflow |
| uint64_t flags_; |
| } dump_; |
| }; |
| |
| // Some of these methods don't use the RegisterDump argument, but they have to |
| // accept them so that they can overload those that take register arguments. |
| bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result); |
| bool Equal64(uint64_t expected, const RegisterDump*, uint64_t result); |
| |
| bool EqualFP32(float expected, const RegisterDump*, float result); |
| bool EqualFP64(double expected, const RegisterDump*, double result); |
| |
| bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg); |
| bool Equal64(uint64_t expected, const RegisterDump* core, const Register& reg); |
| |
| bool EqualFP32(float expected, const RegisterDump* core, |
| const FPRegister& fpreg); |
| bool EqualFP64(double expected, const RegisterDump* core, |
| const FPRegister& fpreg); |
| |
| bool Equal64(const Register& reg0, const RegisterDump* core, |
| const Register& reg1); |
| bool Equal128(uint64_t expected_h, uint64_t expected_l, |
| const RegisterDump* core, const VRegister& reg); |
| |
| bool EqualNzcv(uint32_t expected, uint32_t result); |
| |
| bool EqualRegisters(const RegisterDump* a, const RegisterDump* b); |
| |
| // Populate the w, x and r arrays with registers from the 'allowed' mask. The |
| // r array will be populated with <reg_size>-sized registers, |
| // |
| // This allows for tests which use large, parameterized blocks of registers |
| // (such as the push and pop tests), but where certain registers must be |
| // avoided as they are used for other purposes. |
| // |
| // Any of w, x, or r can be NULL if they are not required. |
| // |
| // The return value is a RegList indicating which registers were allocated. |
| RegList PopulateRegisterArray(Register* w, Register* x, Register* r, |
| int reg_size, int reg_count, RegList allowed); |
| |
| // As PopulateRegisterArray, but for floating-point registers. |
| RegList PopulateFPRegisterArray(FPRegister* s, FPRegister* d, FPRegister* v, |
| int reg_size, int reg_count, RegList allowed); |
| |
| // Ovewrite the contents of the specified registers. This enables tests to |
| // check that register contents are written in cases where it's likely that the |
| // correct outcome could already be stored in the register. |
| // |
| // This always overwrites X-sized registers. If tests are operating on W |
| // registers, a subsequent write into an aliased W register should clear the |
| // top word anyway, so clobbering the full X registers should make tests more |
| // rigorous. |
| void Clobber(MacroAssembler* masm, RegList reg_list, |
| uint64_t const value = 0xfedcba9876543210); |
| |
| // As Clobber, but for FP registers. |
| void ClobberFP(MacroAssembler* masm, RegList reg_list, |
| double const value = kFP64SignallingNaN); |
| |
| // As Clobber, but for a CPURegList with either FP or integer registers. When |
| // using this method, the clobber value is always the default for the basic |
| // Clobber or ClobberFP functions. |
| void Clobber(MacroAssembler* masm, CPURegList reg_list); |
| |
| } // namespace vixl |
| |
| #endif // VIXL_A64_TEST_UTILS_A64_H_ |