Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1 | // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 | // Redistribution and use in source and binary forms, with or without |
| 3 | // modification, are permitted provided that the following conditions are |
| 4 | // met: |
| 5 | // |
| 6 | // * Redistributions of source code must retain the above copyright |
| 7 | // notice, this list of conditions and the following disclaimer. |
| 8 | // * Redistributions in binary form must reproduce the above |
| 9 | // copyright notice, this list of conditions and the following |
| 10 | // disclaimer in the documentation and/or other materials provided |
| 11 | // with the distribution. |
| 12 | // * Neither the name of Google Inc. nor the names of its |
| 13 | // contributors may be used to endorse or promote products derived |
| 14 | // from this software without specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | |
| 28 | #ifndef V8_ARM64_TEST_UTILS_ARM64_H_ |
| 29 | #define V8_ARM64_TEST_UTILS_ARM64_H_ |
| 30 | |
| 31 | #include "src/v8.h" |
| 32 | #include "test/cctest/cctest.h" |
| 33 | |
| 34 | #include "src/arm64/macro-assembler-arm64.h" |
| 35 | #include "src/arm64/utils-arm64.h" |
| 36 | #include "src/macro-assembler.h" |
| 37 | |
| 38 | |
| 39 | using namespace v8::internal; |
| 40 | |
| 41 | |
| 42 | // RegisterDump: Object allowing integer, floating point and flags registers |
| 43 | // to be saved to itself for future reference. |
| 44 | class RegisterDump { |
| 45 | public: |
| 46 | RegisterDump() : completed_(false) {} |
| 47 | |
| 48 | // The Dump method generates code to store a snapshot of the register values. |
| 49 | // It needs to be able to use the stack temporarily, and requires that the |
| 50 | // current stack pointer is csp, and is properly aligned. |
| 51 | // |
| 52 | // The dumping code is generated though the given MacroAssembler. No registers |
| 53 | // are corrupted in the process, but the stack is used briefly. The flags will |
| 54 | // be corrupted during this call. |
| 55 | void Dump(MacroAssembler* assm); |
| 56 | |
| 57 | // Register accessors. |
| 58 | inline int32_t wreg(unsigned code) const { |
| 59 | if (code == kSPRegInternalCode) { |
| 60 | return wspreg(); |
| 61 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 62 | CHECK(RegAliasesMatch(code)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 63 | return dump_.w_[code]; |
| 64 | } |
| 65 | |
| 66 | inline int64_t xreg(unsigned code) const { |
| 67 | if (code == kSPRegInternalCode) { |
| 68 | return spreg(); |
| 69 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 70 | CHECK(RegAliasesMatch(code)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 71 | return dump_.x_[code]; |
| 72 | } |
| 73 | |
| 74 | // FPRegister accessors. |
| 75 | inline uint32_t sreg_bits(unsigned code) const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 76 | CHECK(FPRegAliasesMatch(code)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 77 | return dump_.s_[code]; |
| 78 | } |
| 79 | |
| 80 | inline float sreg(unsigned code) const { |
| 81 | return rawbits_to_float(sreg_bits(code)); |
| 82 | } |
| 83 | |
| 84 | inline uint64_t dreg_bits(unsigned code) const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 85 | CHECK(FPRegAliasesMatch(code)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 86 | return dump_.d_[code]; |
| 87 | } |
| 88 | |
| 89 | inline double dreg(unsigned code) const { |
| 90 | return rawbits_to_double(dreg_bits(code)); |
| 91 | } |
| 92 | |
| 93 | // Stack pointer accessors. |
| 94 | inline int64_t spreg() const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 95 | CHECK(SPRegAliasesMatch()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 96 | return dump_.sp_; |
| 97 | } |
| 98 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 99 | inline int32_t wspreg() const { |
| 100 | CHECK(SPRegAliasesMatch()); |
| 101 | return static_cast<int32_t>(dump_.wsp_); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | // Flags accessors. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 105 | inline uint32_t flags_nzcv() const { |
| 106 | CHECK(IsComplete()); |
| 107 | CHECK((dump_.flags_ & ~Flags_mask) == 0); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 108 | return dump_.flags_ & Flags_mask; |
| 109 | } |
| 110 | |
| 111 | inline bool IsComplete() const { |
| 112 | return completed_; |
| 113 | } |
| 114 | |
| 115 | private: |
| 116 | // Indicate whether the dump operation has been completed. |
| 117 | bool completed_; |
| 118 | |
| 119 | // Check that the lower 32 bits of x<code> exactly match the 32 bits of |
| 120 | // w<code>. A failure of this test most likely represents a failure in the |
| 121 | // ::Dump method, or a failure in the simulator. |
| 122 | bool RegAliasesMatch(unsigned code) const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 123 | CHECK(IsComplete()); |
| 124 | CHECK(code < kNumberOfRegisters); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 125 | return ((dump_.x_[code] & kWRegMask) == dump_.w_[code]); |
| 126 | } |
| 127 | |
| 128 | // As RegAliasesMatch, but for the stack pointer. |
| 129 | bool SPRegAliasesMatch() const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 130 | CHECK(IsComplete()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 131 | return ((dump_.sp_ & kWRegMask) == dump_.wsp_); |
| 132 | } |
| 133 | |
| 134 | // As RegAliasesMatch, but for floating-point registers. |
| 135 | bool FPRegAliasesMatch(unsigned code) const { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 136 | CHECK(IsComplete()); |
| 137 | CHECK(code < kNumberOfFPRegisters); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 138 | return (dump_.d_[code] & kSRegMask) == dump_.s_[code]; |
| 139 | } |
| 140 | |
| 141 | // Store all the dumped elements in a simple struct so the implementation can |
| 142 | // use offsetof to quickly find the correct field. |
| 143 | struct dump_t { |
| 144 | // Core registers. |
| 145 | uint64_t x_[kNumberOfRegisters]; |
| 146 | uint32_t w_[kNumberOfRegisters]; |
| 147 | |
| 148 | // Floating-point registers, as raw bits. |
| 149 | uint64_t d_[kNumberOfFPRegisters]; |
| 150 | uint32_t s_[kNumberOfFPRegisters]; |
| 151 | |
| 152 | // The stack pointer. |
| 153 | uint64_t sp_; |
| 154 | uint64_t wsp_; |
| 155 | |
| 156 | // NZCV flags, stored in bits 28 to 31. |
| 157 | // bit[31] : Negative |
| 158 | // bit[30] : Zero |
| 159 | // bit[29] : Carry |
| 160 | // bit[28] : oVerflow |
| 161 | uint64_t flags_; |
| 162 | } dump_; |
| 163 | |
| 164 | static dump_t for_sizeof(); |
| 165 | STATIC_ASSERT(sizeof(for_sizeof().d_[0]) == kDRegSize); |
| 166 | STATIC_ASSERT(sizeof(for_sizeof().s_[0]) == kSRegSize); |
| 167 | STATIC_ASSERT(sizeof(for_sizeof().d_[0]) == kXRegSize); |
| 168 | STATIC_ASSERT(sizeof(for_sizeof().s_[0]) == kWRegSize); |
| 169 | STATIC_ASSERT(sizeof(for_sizeof().x_[0]) == kXRegSize); |
| 170 | STATIC_ASSERT(sizeof(for_sizeof().w_[0]) == kWRegSize); |
| 171 | }; |
| 172 | |
| 173 | // Some of these methods don't use the RegisterDump argument, but they have to |
| 174 | // accept them so that they can overload those that take register arguments. |
| 175 | bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result); |
| 176 | bool Equal64(uint64_t expected, const RegisterDump*, uint64_t result); |
| 177 | |
| 178 | bool EqualFP32(float expected, const RegisterDump*, float result); |
| 179 | bool EqualFP64(double expected, const RegisterDump*, double result); |
| 180 | |
| 181 | bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg); |
| 182 | bool Equal64(uint64_t expected, const RegisterDump* core, const Register& reg); |
| 183 | |
| 184 | bool EqualFP32(float expected, const RegisterDump* core, |
| 185 | const FPRegister& fpreg); |
| 186 | bool EqualFP64(double expected, const RegisterDump* core, |
| 187 | const FPRegister& fpreg); |
| 188 | |
| 189 | bool Equal64(const Register& reg0, const RegisterDump* core, |
| 190 | const Register& reg1); |
| 191 | |
| 192 | bool EqualNzcv(uint32_t expected, uint32_t result); |
| 193 | |
| 194 | bool EqualRegisters(const RegisterDump* a, const RegisterDump* b); |
| 195 | |
| 196 | // Populate the w, x and r arrays with registers from the 'allowed' mask. The |
| 197 | // r array will be populated with <reg_size>-sized registers, |
| 198 | // |
| 199 | // This allows for tests which use large, parameterized blocks of registers |
| 200 | // (such as the push and pop tests), but where certain registers must be |
| 201 | // avoided as they are used for other purposes. |
| 202 | // |
| 203 | // Any of w, x, or r can be NULL if they are not required. |
| 204 | // |
| 205 | // The return value is a RegList indicating which registers were allocated. |
| 206 | RegList PopulateRegisterArray(Register* w, Register* x, Register* r, |
| 207 | int reg_size, int reg_count, RegList allowed); |
| 208 | |
| 209 | // As PopulateRegisterArray, but for floating-point registers. |
| 210 | RegList PopulateFPRegisterArray(FPRegister* s, FPRegister* d, FPRegister* v, |
| 211 | int reg_size, int reg_count, RegList allowed); |
| 212 | |
| 213 | // Ovewrite the contents of the specified registers. This enables tests to |
| 214 | // check that register contents are written in cases where it's likely that the |
| 215 | // correct outcome could already be stored in the register. |
| 216 | // |
| 217 | // This always overwrites X-sized registers. If tests are operating on W |
| 218 | // registers, a subsequent write into an aliased W register should clear the |
| 219 | // top word anyway, so clobbering the full X registers should make tests more |
| 220 | // rigorous. |
| 221 | void Clobber(MacroAssembler* masm, RegList reg_list, |
| 222 | uint64_t const value = 0xfedcba9876543210UL); |
| 223 | |
| 224 | // As Clobber, but for FP registers. |
| 225 | void ClobberFP(MacroAssembler* masm, RegList reg_list, |
| 226 | double const value = kFP64SignallingNaN); |
| 227 | |
| 228 | // As Clobber, but for a CPURegList with either FP or integer registers. When |
| 229 | // using this method, the clobber value is always the default for the basic |
| 230 | // Clobber or ClobberFP functions. |
| 231 | void Clobber(MacroAssembler* masm, CPURegList reg_list); |
| 232 | |
| 233 | #endif // V8_ARM64_TEST_UTILS_ARM64_H_ |