summaryrefslogtreecommitdiff
path: root/deps/v8/test/cctest/test-utils-arm64.h
blob: 78b266cb0b962d0c7a7702e2555104395bb1792a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// Copyright 2013 the V8 project authors. 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 Google Inc. 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 AND 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 V8_ARM64_TEST_UTILS_ARM64_H_
#define V8_ARM64_TEST_UTILS_ARM64_H_

#include "src/v8.h"
#include "test/cctest/cctest.h"

#include "src/arm64/macro-assembler-arm64.h"
#include "src/arm64/utils-arm64.h"
#include "src/macro-assembler.h"


namespace v8 {
namespace internal {

// 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) {}

  // 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 csp, 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();
    }
    CHECK(RegAliasesMatch(code));
    return dump_.w_[code];
  }

  inline int64_t xreg(unsigned code) const {
    if (code == kSPRegInternalCode) {
      return spreg();
    }
    CHECK(RegAliasesMatch(code));
    return dump_.x_[code];
  }

  // VRegister accessors.
  inline uint32_t sreg_bits(unsigned code) const {
    CHECK(FPRegAliasesMatch(code));
    return dump_.s_[code];
  }

  inline float sreg(unsigned code) const {
    return bit_cast<float>(sreg_bits(code));
  }

  inline uint64_t dreg_bits(unsigned code) const {
    CHECK(FPRegAliasesMatch(code));
    return dump_.d_[code];
  }

  inline double dreg(unsigned code) const {
    return bit_cast<double>(dreg_bits(code));
  }

  inline vec128_t qreg(unsigned code) const { return dump_.q_[code]; }

  // Stack pointer accessors.
  inline int64_t spreg() const {
    CHECK(SPRegAliasesMatch());
    return dump_.sp_;
  }

  inline int32_t wspreg() const {
    CHECK(SPRegAliasesMatch());
    return static_cast<int32_t>(dump_.wsp_);
  }

  // Flags accessors.
  inline uint32_t flags_nzcv() const {
    CHECK(IsComplete());
    CHECK_EQ(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 {
    CHECK(IsComplete());
    CHECK_LT(code, kNumberOfRegisters);
    return ((dump_.x_[code] & kWRegMask) == dump_.w_[code]);
  }

  // As RegAliasesMatch, but for the stack pointer.
  bool SPRegAliasesMatch() const {
    CHECK(IsComplete());
    return ((dump_.sp_ & kWRegMask) == dump_.wsp_);
  }

  // As RegAliasesMatch, but for floating-point registers.
  bool FPRegAliasesMatch(unsigned code) const {
    CHECK(IsComplete());
    CHECK_LT(code, kNumberOfVRegisters);
    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_[kNumberOfVRegisters];
    uint32_t s_[kNumberOfVRegisters];

    // 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_;

  static dump_t for_sizeof();
  static_assert(kXRegSize == kDRegSize, "X and D registers must be same size.");
  static_assert(kWRegSize == kSRegSize, "W and S registers must be same size.");
  static_assert(sizeof(for_sizeof().q_[0]) == kQRegSize,
                "Array elements must be size of Q register.");
  static_assert(sizeof(for_sizeof().d_[0]) == kDRegSize,
                "Array elements must be size of D register.");
  static_assert(sizeof(for_sizeof().s_[0]) == kSRegSize,
                "Array elements must be size of S register.");
  static_assert(sizeof(for_sizeof().x_[0]) == kXRegSize,
                "Array elements must be size of X register.");
  static_assert(sizeof(for_sizeof().w_[0]) == kWRegSize,
                "Array elements must be size of W register.");
};

// 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 VRegister& fpreg);
bool EqualFP64(double expected, const RegisterDump* core,
               const VRegister& 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);

// Create an array of type {RegType}, size {Size}, filled with {NoReg}.
template <typename RegType, size_t Size>
std::array<RegType, Size> CreateRegisterArray() {
  return base::make_array<Size>([](size_t) { return RegType::no_reg(); });
}

// 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 nullptr 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 PopulateVRegisterArray(VRegister* s, VRegister* d, VRegister* 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 = 0xFEDCBA9876543210UL);

// 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 internal
}  // namespace v8

#endif  // V8_ARM64_TEST_UTILS_ARM64_H_