aboutsummaryrefslogtreecommitdiff
path: root/test/aarch64/test-utils-aarch64.h
blob: ee09d6b8073700e130c169c695a8fbea4e10a913 (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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// Copyright 2014, VIXL 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 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_AARCH64_TEST_UTILS_AARCH64_H_
#define VIXL_AARCH64_TEST_UTILS_AARCH64_H_

#include "test-runner.h"

#include "aarch64/cpu-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/macro-assembler-aarch64.h"
#include "aarch64/simulator-aarch64.h"

namespace vixl {
namespace aarch64 {

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

// Signalling and quiet NaNs in half-precision float format.
extern const Float16 kFP16SignallingNaN;
extern const Float16 kFP16QuietNaN;

// 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_.h_[0]) == kHRegSizeInBytes);
    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];
  }

  // VRegister accessors.
  inline uint16_t hreg_bits(unsigned code) const {
    VIXL_ASSERT(FPRegAliasesMatch(code));
    return dump_.h_[code];
  }

  inline uint32_t sreg_bits(unsigned code) const {
    VIXL_ASSERT(FPRegAliasesMatch(code));
    return dump_.s_[code];
  }

  inline Float16 hreg(unsigned code) const {
    return RawbitsToFloat16(hreg_bits(code));
  }

  inline float sreg(unsigned code) const {
    return RawbitsToFloat(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 RawbitsToDouble(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 < kNumberOfVRegisters);
    return (((dump_.d_[code] & kSRegMask) == dump_.s_[code]) ||
            ((dump_.s_[code] & kHRegMask) == dump_.h_[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];
    uint16_t h_[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_;
};

// Some tests want to check that a value is _not_ equal to a reference value.
// These enum values can be used to control the error reporting behaviour.
enum ExpectedResult { kExpectEqual, kExpectNotEqual };

// The Equal* methods return true if the result matches the reference value.
// They all print an error message to the console if the result is incorrect
// (according to the ExpectedResult argument, or kExpectEqual if it is absent).
//
// 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 reference,
             const RegisterDump*,
             uint64_t result,
             ExpectedResult option = kExpectEqual);

bool EqualFP16(Float16 expected, const RegisterDump*, uint16_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 reference,
             const RegisterDump* core,
             const Register& reg,
             ExpectedResult option = kExpectEqual);
bool Equal64(uint64_t expected,
             const RegisterDump* core,
             const VRegister& vreg);

bool EqualFP16(Float16 expected,
               const RegisterDump* core,
               const VRegister& fpreg);
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,
             ExpectedResult option = kExpectEqual);
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);

template <typename T0, typename T1>
bool NotEqual64(T0 reference, const RegisterDump* core, T1 result) {
  return !Equal64(reference, core, result, kExpectNotEqual);
}

// 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 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 = 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 aarch64
}  // namespace vixl

#endif  // VIXL_AARCH64_TEST_UTILS_AARCH64_H_