1 // Copyright 2014, VIXL authors
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 //   * Redistributions of source code must retain the above copyright notice,
8 //     this list of conditions and the following disclaimer.
9 //   * Redistributions in binary form must reproduce the above copyright notice,
10 //     this list of conditions and the following disclaimer in the documentation
11 //     and/or other materials provided with the distribution.
12 //   * Neither the name of ARM Limited nor the names of its contributors may be
13 //     used to endorse or promote products derived from this software without
14 //     specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 #include <cmath>
28 #include <queue>
29 
30 #include "test-runner.h"
31 #include "test-utils-aarch64.h"
32 
33 #include "aarch64/cpu-aarch64.h"
34 #include "aarch64/disasm-aarch64.h"
35 #include "aarch64/macro-assembler-aarch64.h"
36 #include "aarch64/simulator-aarch64.h"
37 
38 #define __ masm->
39 
40 namespace vixl {
41 namespace aarch64 {
42 
43 
44 // This value is a signalling NaN as FP64, and also as FP32 or FP16 (taking the
45 // least-significant bits).
46 const double kFP64SignallingNaN = RawbitsToDouble(UINT64_C(0x7ff000007f807c01));
47 const float kFP32SignallingNaN = RawbitsToFloat(0x7f807c01);
48 const Float16 kFP16SignallingNaN = RawbitsToFloat16(0x7c01);
49 
50 // A similar value, but as a quiet NaN.
51 const double kFP64QuietNaN = RawbitsToDouble(UINT64_C(0x7ff800007fc07e01));
52 const float kFP32QuietNaN = RawbitsToFloat(0x7fc07e01);
53 const Float16 kFP16QuietNaN = RawbitsToFloat16(0x7e01);
54 
55 
Equal32(uint32_t expected,const RegisterDump *,uint32_t result)56 bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result) {
57   if (result != expected) {
58     printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n",
59            expected,
60            result);
61   }
62 
63   return expected == result;
64 }
65 
66 
Equal64(uint64_t reference,const RegisterDump *,uint64_t result,ExpectedResult option)67 bool Equal64(uint64_t reference,
68              const RegisterDump*,
69              uint64_t result,
70              ExpectedResult option) {
71   switch (option) {
72     case kExpectEqual:
73       if (result != reference) {
74         printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
75                reference,
76                result);
77       }
78       break;
79     case kExpectNotEqual:
80       if (result == reference) {
81         printf("Expected a result not equal to 0x%016" PRIx64 "\n", reference);
82       }
83       break;
84   }
85 
86   return reference == result;
87 }
88 
89 
Equal128(QRegisterValue expected,const RegisterDump *,QRegisterValue result)90 bool Equal128(QRegisterValue expected,
91               const RegisterDump*,
92               QRegisterValue result) {
93   if (!expected.Equals(result)) {
94     printf("Expected 0x%016" PRIx64 "%016" PRIx64
95            "\t "
96            "Found 0x%016" PRIx64 "%016" PRIx64 "\n",
97            expected.GetLane<uint64_t>(1),
98            expected.GetLane<uint64_t>(0),
99            result.GetLane<uint64_t>(1),
100            result.GetLane<uint64_t>(0));
101   }
102 
103   return expected.Equals(result);
104 }
105 
106 
EqualFP16(Float16 expected,const RegisterDump *,Float16 result)107 bool EqualFP16(Float16 expected, const RegisterDump*, Float16 result) {
108   uint16_t e_rawbits = Float16ToRawbits(expected);
109   uint16_t r_rawbits = Float16ToRawbits(result);
110   if (e_rawbits == r_rawbits) {
111     return true;
112   } else {
113     if (IsNaN(expected) || IsZero(expected)) {
114       printf("Expected 0x%04" PRIx16 "\t Found 0x%04" PRIx16 "\n",
115              e_rawbits,
116              r_rawbits);
117     } else {
118       printf("Expected %.6f (16 bit): (0x%04" PRIx16
119              ")\t "
120              "Found %.6f (0x%04" PRIx16 ")\n",
121              FPToFloat(expected, kIgnoreDefaultNaN),
122              e_rawbits,
123              FPToFloat(result, kIgnoreDefaultNaN),
124              r_rawbits);
125     }
126     return false;
127   }
128 }
129 
130 
EqualFP32(float expected,const RegisterDump *,float result)131 bool EqualFP32(float expected, const RegisterDump*, float result) {
132   if (FloatToRawbits(expected) == FloatToRawbits(result)) {
133     return true;
134   } else {
135     if (IsNaN(expected) || (expected == 0.0)) {
136       printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n",
137              FloatToRawbits(expected),
138              FloatToRawbits(result));
139     } else {
140       printf("Expected %.9f (0x%08" PRIx32
141              ")\t "
142              "Found %.9f (0x%08" PRIx32 ")\n",
143              expected,
144              FloatToRawbits(expected),
145              result,
146              FloatToRawbits(result));
147     }
148     return false;
149   }
150 }
151 
152 
EqualFP64(double expected,const RegisterDump *,double result)153 bool EqualFP64(double expected, const RegisterDump*, double result) {
154   if (DoubleToRawbits(expected) == DoubleToRawbits(result)) {
155     return true;
156   }
157 
158   if (IsNaN(expected) || (expected == 0.0)) {
159     printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
160            DoubleToRawbits(expected),
161            DoubleToRawbits(result));
162   } else {
163     printf("Expected %.17f (0x%016" PRIx64
164            ")\t "
165            "Found %.17f (0x%016" PRIx64 ")\n",
166            expected,
167            DoubleToRawbits(expected),
168            result,
169            DoubleToRawbits(result));
170   }
171   return false;
172 }
173 
174 
Equal32(uint32_t expected,const RegisterDump * core,const Register & reg)175 bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg) {
176   VIXL_ASSERT(reg.Is32Bits());
177   // Retrieve the corresponding X register so we can check that the upper part
178   // was properly cleared.
179   int64_t result_x = core->xreg(reg.GetCode());
180   if ((result_x & 0xffffffff00000000) != 0) {
181     printf("Expected 0x%08" PRIx32 "\t Found 0x%016" PRIx64 "\n",
182            expected,
183            result_x);
184     return false;
185   }
186   uint32_t result_w = core->wreg(reg.GetCode());
187   return Equal32(expected, core, result_w);
188 }
189 
190 
Equal64(uint64_t reference,const RegisterDump * core,const Register & reg,ExpectedResult option)191 bool Equal64(uint64_t reference,
192              const RegisterDump* core,
193              const Register& reg,
194              ExpectedResult option) {
195   VIXL_ASSERT(reg.Is64Bits());
196   uint64_t result = core->xreg(reg.GetCode());
197   return Equal64(reference, core, result, option);
198 }
199 
200 
NotEqual64(uint64_t reference,const RegisterDump * core,const Register & reg)201 bool NotEqual64(uint64_t reference,
202                 const RegisterDump* core,
203                 const Register& reg) {
204   VIXL_ASSERT(reg.Is64Bits());
205   uint64_t result = core->xreg(reg.GetCode());
206   return NotEqual64(reference, core, result);
207 }
208 
209 
Equal128(uint64_t expected_h,uint64_t expected_l,const RegisterDump * core,const VRegister & vreg)210 bool Equal128(uint64_t expected_h,
211               uint64_t expected_l,
212               const RegisterDump* core,
213               const VRegister& vreg) {
214   VIXL_ASSERT(vreg.Is128Bits());
215   QRegisterValue expected;
216   expected.SetLane(0, expected_l);
217   expected.SetLane(1, expected_h);
218   QRegisterValue result = core->qreg(vreg.GetCode());
219   return Equal128(expected, core, result);
220 }
221 
222 
EqualFP16(Float16 expected,const RegisterDump * core,const VRegister & fpreg)223 bool EqualFP16(Float16 expected,
224                const RegisterDump* core,
225                const VRegister& fpreg) {
226   VIXL_ASSERT(fpreg.Is16Bits());
227   // Retrieve the corresponding D register so we can check that the upper part
228   // was properly cleared.
229   uint64_t result_64 = core->dreg_bits(fpreg.GetCode());
230   if ((result_64 & 0xfffffffffff0000) != 0) {
231     printf("Expected 0x%04" PRIx16 " (%f)\t Found 0x%016" PRIx64 "\n",
232            Float16ToRawbits(expected),
233            FPToFloat(expected, kIgnoreDefaultNaN),
234            result_64);
235     return false;
236   }
237   return EqualFP16(expected, core, core->hreg(fpreg.GetCode()));
238 }
239 
240 
EqualFP32(float expected,const RegisterDump * core,const VRegister & fpreg)241 bool EqualFP32(float expected,
242                const RegisterDump* core,
243                const VRegister& fpreg) {
244   VIXL_ASSERT(fpreg.Is32Bits());
245   // Retrieve the corresponding D register so we can check that the upper part
246   // was properly cleared.
247   uint64_t result_64 = core->dreg_bits(fpreg.GetCode());
248   if ((result_64 & 0xffffffff00000000) != 0) {
249     printf("Expected 0x%08" PRIx32 " (%f)\t Found 0x%016" PRIx64 "\n",
250            FloatToRawbits(expected),
251            expected,
252            result_64);
253     return false;
254   }
255 
256   return EqualFP32(expected, core, core->sreg(fpreg.GetCode()));
257 }
258 
259 
EqualFP64(double expected,const RegisterDump * core,const VRegister & fpreg)260 bool EqualFP64(double expected,
261                const RegisterDump* core,
262                const VRegister& fpreg) {
263   VIXL_ASSERT(fpreg.Is64Bits());
264   return EqualFP64(expected, core, core->dreg(fpreg.GetCode()));
265 }
266 
267 
Equal64(const Register & reg0,const RegisterDump * core,const Register & reg1,ExpectedResult option)268 bool Equal64(const Register& reg0,
269              const RegisterDump* core,
270              const Register& reg1,
271              ExpectedResult option) {
272   VIXL_ASSERT(reg0.Is64Bits() && reg1.Is64Bits());
273   int64_t reference = core->xreg(reg0.GetCode());
274   int64_t result = core->xreg(reg1.GetCode());
275   return Equal64(reference, core, result, option);
276 }
277 
278 
NotEqual64(const Register & reg0,const RegisterDump * core,const Register & reg1)279 bool NotEqual64(const Register& reg0,
280                 const RegisterDump* core,
281                 const Register& reg1) {
282   VIXL_ASSERT(reg0.Is64Bits() && reg1.Is64Bits());
283   int64_t expected = core->xreg(reg0.GetCode());
284   int64_t result = core->xreg(reg1.GetCode());
285   return NotEqual64(expected, core, result);
286 }
287 
288 
Equal64(uint64_t expected,const RegisterDump * core,const VRegister & vreg)289 bool Equal64(uint64_t expected,
290              const RegisterDump* core,
291              const VRegister& vreg) {
292   VIXL_ASSERT(vreg.Is64Bits());
293   uint64_t result = core->dreg_bits(vreg.GetCode());
294   return Equal64(expected, core, result);
295 }
296 
297 
FlagN(uint32_t flags)298 static char FlagN(uint32_t flags) { return (flags & NFlag) ? 'N' : 'n'; }
299 
300 
FlagZ(uint32_t flags)301 static char FlagZ(uint32_t flags) { return (flags & ZFlag) ? 'Z' : 'z'; }
302 
303 
FlagC(uint32_t flags)304 static char FlagC(uint32_t flags) { return (flags & CFlag) ? 'C' : 'c'; }
305 
306 
FlagV(uint32_t flags)307 static char FlagV(uint32_t flags) { return (flags & VFlag) ? 'V' : 'v'; }
308 
309 
EqualNzcv(uint32_t expected,uint32_t result)310 bool EqualNzcv(uint32_t expected, uint32_t result) {
311   VIXL_ASSERT((expected & ~NZCVFlag) == 0);
312   VIXL_ASSERT((result & ~NZCVFlag) == 0);
313   if (result != expected) {
314     printf("Expected: %c%c%c%c\t Found: %c%c%c%c\n",
315            FlagN(expected),
316            FlagZ(expected),
317            FlagC(expected),
318            FlagV(expected),
319            FlagN(result),
320            FlagZ(result),
321            FlagC(result),
322            FlagV(result));
323     return false;
324   }
325 
326   return true;
327 }
328 
329 
EqualRegisters(const RegisterDump * a,const RegisterDump * b)330 bool EqualRegisters(const RegisterDump* a, const RegisterDump* b) {
331   for (unsigned i = 0; i < kNumberOfRegisters; i++) {
332     if (a->xreg(i) != b->xreg(i)) {
333       printf("x%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
334              i,
335              a->xreg(i),
336              b->xreg(i));
337       return false;
338     }
339   }
340 
341   for (unsigned i = 0; i < kNumberOfVRegisters; i++) {
342     uint64_t a_bits = a->dreg_bits(i);
343     uint64_t b_bits = b->dreg_bits(i);
344     if (a_bits != b_bits) {
345       printf("d%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
346              i,
347              a_bits,
348              b_bits);
349       return false;
350     }
351   }
352 
353   return true;
354 }
355 
EqualSVELane(uint64_t expected,const RegisterDump * core,const ZRegister & reg,int lane)356 bool EqualSVELane(uint64_t expected,
357                   const RegisterDump* core,
358                   const ZRegister& reg,
359                   int lane) {
360   unsigned lane_size = reg.GetLaneSizeInBits();
361   // For convenience in the tests, we allow negative values to be passed into
362   // `expected`, but truncate them to an appropriately-sized unsigned value for
363   // the check. For example, in `EqualSVELane(-1, core, z0.VnB())`, the expected
364   // value is truncated from 0xffffffffffffffff to 0xff before the comparison.
365   VIXL_ASSERT(IsUintN(lane_size, expected) ||
366               IsIntN(lane_size, RawbitsToInt64(expected)));
367   expected &= GetUintMask(lane_size);
368 
369   uint64_t result = core->zreg_lane(reg.GetCode(), lane_size, lane);
370   if (expected != result) {
371     unsigned lane_size_in_hex_chars = lane_size / 4;
372     std::string reg_name = reg.GetArchitecturalName();
373     printf("%s[%d]\t Expected 0x%0*" PRIx64 "\t Found 0x%0*" PRIx64 "\n",
374            reg_name.c_str(),
375            lane,
376            lane_size_in_hex_chars,
377            expected,
378            lane_size_in_hex_chars,
379            result);
380     return false;
381   }
382   return true;
383 }
384 
EqualSVELane(uint64_t expected,const RegisterDump * core,const PRegister & reg,int lane)385 bool EqualSVELane(uint64_t expected,
386                   const RegisterDump* core,
387                   const PRegister& reg,
388                   int lane) {
389   VIXL_ASSERT(reg.HasLaneSize());
390   VIXL_ASSERT((reg.GetLaneSizeInBits() % kZRegBitsPerPRegBit) == 0);
391   unsigned p_bits_per_lane = reg.GetLaneSizeInBits() / kZRegBitsPerPRegBit;
392   VIXL_ASSERT(IsUintN(p_bits_per_lane, expected));
393   expected &= GetUintMask(p_bits_per_lane);
394 
395   uint64_t result = core->preg_lane(reg.GetCode(), p_bits_per_lane, lane);
396   if (expected != result) {
397     unsigned lane_size_in_hex_chars = (p_bits_per_lane + 3) / 4;
398     std::string reg_name = reg.GetArchitecturalName();
399     printf("%s[%d]\t Expected 0x%0*" PRIx64 "\t Found 0x%0*" PRIx64 "\n",
400            reg_name.c_str(),
401            lane,
402            lane_size_in_hex_chars,
403            expected,
404            lane_size_in_hex_chars,
405            result);
406     return false;
407   }
408   return true;
409 }
410 
411 struct EqualMemoryChunk {
412   typedef uint64_t RawChunk;
413 
414   uintptr_t address;
415   RawChunk expected;
416   RawChunk result;
417 
IsEqualvixl::aarch64::EqualMemoryChunk418   bool IsEqual() const { return expected == result; }
419 };
420 
EqualMemory(const void * expected,const void * result,size_t size_in_bytes,size_t zero_offset)421 bool EqualMemory(const void* expected,
422                  const void* result,
423                  size_t size_in_bytes,
424                  size_t zero_offset) {
425   if (memcmp(expected, result, size_in_bytes) == 0) return true;
426 
427   // Read 64-bit chunks, and print them side-by-side if they don't match.
428 
429   // Remember the last few chunks, even if they matched, so we can print some
430   // context. We don't want to print the whole buffer, because it could be huge.
431   static const size_t kContextLines = 1;
432   std::queue<EqualMemoryChunk> context;
433   static const size_t kChunkSize = sizeof(EqualMemoryChunk::RawChunk);
434 
435   // This assumption keeps the logic simple, and is acceptable for our tests.
436   VIXL_ASSERT((size_in_bytes % kChunkSize) == 0);
437 
438   const char* expected_it = reinterpret_cast<const char*>(expected);
439   const char* result_it = reinterpret_cast<const char*>(result);
440 
441   // This is the first error, so print a header row.
442   printf("  Address (of result)                  Expected           Result\n");
443 
444   // Always print some context at the start of the buffer.
445   uintptr_t print_context_to =
446       reinterpret_cast<uintptr_t>(result) + (kContextLines + 1) * kChunkSize;
447   for (size_t i = 0; i < size_in_bytes; i += kChunkSize) {
448     EqualMemoryChunk chunk;
449     chunk.address = reinterpret_cast<uintptr_t>(result_it);
450     memcpy(&chunk.expected, expected_it, kChunkSize);
451     memcpy(&chunk.result, result_it, kChunkSize);
452 
453     while (context.size() > kContextLines) context.pop();
454     context.push(chunk);
455 
456     // Print context after an error, and at the end of the buffer.
457     if (!chunk.IsEqual() || ((i + kChunkSize) >= size_in_bytes)) {
458       if (chunk.address > print_context_to) {
459         // We aren't currently printing context, so separate this context from
460         // the previous block.
461         printf("...\n");
462       }
463       print_context_to = chunk.address + (kContextLines + 1) * kChunkSize;
464     }
465 
466     // Print context (including the current line).
467     while (!context.empty() && (context.front().address < print_context_to)) {
468       uintptr_t address = context.front().address;
469       uint64_t offset = address - reinterpret_cast<uintptr_t>(result);
470       bool is_negative = (offset < zero_offset);
471       printf("0x%016" PRIxPTR " (result %c %5" PRIu64 "): 0x%016" PRIx64
472              " 0x%016" PRIx64 "\n",
473              address,
474              (is_negative ? '-' : '+'),
475              (is_negative ? (zero_offset - offset) : (offset - zero_offset)),
476              context.front().expected,
477              context.front().result);
478       context.pop();
479     }
480 
481     expected_it += kChunkSize;
482     result_it += kChunkSize;
483   }
484 
485   return false;
486 }
PopulateRegisterArray(Register * w,Register * x,Register * r,int reg_size,int reg_count,RegList allowed)487 RegList PopulateRegisterArray(Register* w,
488                               Register* x,
489                               Register* r,
490                               int reg_size,
491                               int reg_count,
492                               RegList allowed) {
493   RegList list = 0;
494   int i = 0;
495   for (unsigned n = 0; (n < kNumberOfRegisters) && (i < reg_count); n++) {
496     if (((UINT64_C(1) << n) & allowed) != 0) {
497       // Only assign allowed registers.
498       if (r) {
499         r[i] = Register(n, reg_size);
500       }
501       if (x) {
502         x[i] = Register(n, kXRegSize);
503       }
504       if (w) {
505         w[i] = Register(n, kWRegSize);
506       }
507       list |= (UINT64_C(1) << n);
508       i++;
509     }
510   }
511   // Check that we got enough registers.
512   VIXL_ASSERT(CountSetBits(list, kNumberOfRegisters) == reg_count);
513 
514   return list;
515 }
516 
517 
PopulateVRegisterArray(VRegister * s,VRegister * d,VRegister * v,int reg_size,int reg_count,RegList allowed)518 RegList PopulateVRegisterArray(VRegister* s,
519                                VRegister* d,
520                                VRegister* v,
521                                int reg_size,
522                                int reg_count,
523                                RegList allowed) {
524   RegList list = 0;
525   int i = 0;
526   for (unsigned n = 0; (n < kNumberOfVRegisters) && (i < reg_count); n++) {
527     if (((UINT64_C(1) << n) & allowed) != 0) {
528       // Only assigned allowed registers.
529       if (v) {
530         v[i] = VRegister(n, reg_size);
531       }
532       if (d) {
533         d[i] = VRegister(n, kDRegSize);
534       }
535       if (s) {
536         s[i] = VRegister(n, kSRegSize);
537       }
538       list |= (UINT64_C(1) << n);
539       i++;
540     }
541   }
542   // Check that we got enough registers.
543   VIXL_ASSERT(CountSetBits(list, kNumberOfVRegisters) == reg_count);
544 
545   return list;
546 }
547 
548 
Clobber(MacroAssembler * masm,RegList reg_list,uint64_t const value)549 void Clobber(MacroAssembler* masm, RegList reg_list, uint64_t const value) {
550   Register first = NoReg;
551   for (unsigned i = 0; i < kNumberOfRegisters; i++) {
552     if (reg_list & (UINT64_C(1) << i)) {
553       Register xn(i, kXRegSize);
554       // We should never write into sp here.
555       VIXL_ASSERT(!xn.Is(sp));
556       if (!xn.IsZero()) {
557         if (!first.IsValid()) {
558           // This is the first register we've hit, so construct the literal.
559           __ Mov(xn, value);
560           first = xn;
561         } else {
562           // We've already loaded the literal, so re-use the value already
563           // loaded into the first register we hit.
564           __ Mov(xn, first);
565         }
566       }
567     }
568   }
569 }
570 
571 
ClobberFP(MacroAssembler * masm,RegList reg_list,double const value)572 void ClobberFP(MacroAssembler* masm, RegList reg_list, double const value) {
573   VRegister first = NoVReg;
574   for (unsigned i = 0; i < kNumberOfVRegisters; i++) {
575     if (reg_list & (UINT64_C(1) << i)) {
576       VRegister dn(i, kDRegSize);
577       if (!first.IsValid()) {
578         // This is the first register we've hit, so construct the literal.
579         __ Fmov(dn, value);
580         first = dn;
581       } else {
582         // We've already loaded the literal, so re-use the value already loaded
583         // into the first register we hit.
584         __ Fmov(dn, first);
585       }
586     }
587   }
588 }
589 
590 
Clobber(MacroAssembler * masm,CPURegList reg_list)591 void Clobber(MacroAssembler* masm, CPURegList reg_list) {
592   if (reg_list.GetType() == CPURegister::kRegister) {
593     // This will always clobber X registers.
594     Clobber(masm, reg_list.GetList());
595   } else if (reg_list.GetType() == CPURegister::kVRegister) {
596     // This will always clobber D registers.
597     ClobberFP(masm, reg_list.GetList());
598   } else {
599     VIXL_UNIMPLEMENTED();
600   }
601 }
602 
603 // TODO: Once registers have sufficiently compatible interfaces, merge the two
604 // DumpRegisters templates.
605 template <typename T>
DumpRegisters(MacroAssembler * masm,Register dump_base,int offset)606 static void DumpRegisters(MacroAssembler* masm,
607                           Register dump_base,
608                           int offset) {
609   UseScratchRegisterScope temps(masm);
610   Register dump = temps.AcquireX();
611   __ Add(dump, dump_base, offset);
612   for (unsigned i = 0; i <= T::GetMaxCode(); i++) {
613     T reg(i);
614     __ Str(reg, SVEMemOperand(dump));
615     __ Add(dump, dump, reg.GetMaxSizeInBytes());
616   }
617 }
618 
619 template <typename T>
DumpRegisters(MacroAssembler * masm,Register dump_base,int offset,int reg_size_in_bytes)620 static void DumpRegisters(MacroAssembler* masm,
621                           Register dump_base,
622                           int offset,
623                           int reg_size_in_bytes) {
624   UseScratchRegisterScope temps(masm);
625   Register dump = temps.AcquireX();
626   __ Add(dump, dump_base, offset);
627   for (unsigned i = 0; i <= T::GetMaxCode(); i++) {
628     T reg(i, reg_size_in_bytes * kBitsPerByte);
629     __ Str(reg, MemOperand(dump));
630     __ Add(dump, dump, reg_size_in_bytes);
631   }
632 }
633 
Dump(MacroAssembler * masm)634 void RegisterDump::Dump(MacroAssembler* masm) {
635   VIXL_ASSERT(__ StackPointer().Is(sp));
636 
637   dump_cpu_features_ = *masm->GetCPUFeatures();
638 
639   // We need some scratch registers, but we also need to dump them, so we have
640   // to control exactly which registers are used, and dump them separately.
641   CPURegList scratch_registers(x0, x1, x2, x3);
642 
643   UseScratchRegisterScope temps(masm);
644   temps.ExcludeAll();
645   __ PushCPURegList(scratch_registers);
646   temps.Include(scratch_registers);
647 
648   Register dump_base = temps.AcquireX();
649   Register tmp = temps.AcquireX();
650 
651   // Offsets into the dump_ structure.
652   const int x_offset = offsetof(dump_t, x_);
653   const int w_offset = offsetof(dump_t, w_);
654   const int d_offset = offsetof(dump_t, d_);
655   const int s_offset = offsetof(dump_t, s_);
656   const int h_offset = offsetof(dump_t, h_);
657   const int q_offset = offsetof(dump_t, q_);
658   const int z_offset = offsetof(dump_t, z_);
659   const int p_offset = offsetof(dump_t, p_);
660   const int sp_offset = offsetof(dump_t, sp_);
661   const int wsp_offset = offsetof(dump_t, wsp_);
662   const int flags_offset = offsetof(dump_t, flags_);
663   const int vl_offset = offsetof(dump_t, vl_);
664 
665   // Load the address where we will dump the state.
666   __ Mov(dump_base, reinterpret_cast<uintptr_t>(&dump_));
667 
668   // Dump the stack pointer (sp and wsp).
669   // The stack pointer cannot be stored directly; it needs to be moved into
670   // another register first. Also, we pushed four X registers, so we need to
671   // compensate here.
672   __ Add(tmp, sp, 4 * kXRegSizeInBytes);
673   __ Str(tmp, MemOperand(dump_base, sp_offset));
674   __ Add(tmp.W(), wsp, 4 * kXRegSizeInBytes);
675   __ Str(tmp.W(), MemOperand(dump_base, wsp_offset));
676 
677   // Dump core registers.
678   DumpRegisters<Register>(masm, dump_base, x_offset, kXRegSizeInBytes);
679   DumpRegisters<Register>(masm, dump_base, w_offset, kWRegSizeInBytes);
680 
681   // Dump NEON and FP registers.
682   DumpRegisters<VRegister>(masm, dump_base, q_offset, kQRegSizeInBytes);
683   DumpRegisters<VRegister>(masm, dump_base, d_offset, kDRegSizeInBytes);
684   DumpRegisters<VRegister>(masm, dump_base, s_offset, kSRegSizeInBytes);
685   DumpRegisters<VRegister>(masm, dump_base, h_offset, kHRegSizeInBytes);
686 
687   // Dump SVE registers.
688   if (CPUHas(CPUFeatures::kSVE)) {
689     DumpRegisters<ZRegister>(masm, dump_base, z_offset);
690     DumpRegisters<PRegister>(masm, dump_base, p_offset);
691 
692     // Record the vector length.
693     __ Rdvl(tmp, kBitsPerByte);
694     __ Str(tmp, MemOperand(dump_base, vl_offset));
695   }
696 
697   // Dump the flags.
698   __ Mrs(tmp, NZCV);
699   __ Str(tmp, MemOperand(dump_base, flags_offset));
700 
701   // To dump the values we used as scratch registers, we need a new scratch
702   // register. We can use any of the already dumped registers since we can
703   // easily restore them.
704   Register dump2_base = x10;
705   VIXL_ASSERT(!scratch_registers.IncludesAliasOf(dump2_base));
706 
707   VIXL_ASSERT(scratch_registers.IncludesAliasOf(dump_base));
708 
709   // Ensure that we don't try to use the scratch registers again.
710   temps.ExcludeAll();
711 
712   // Don't lose the dump_ address.
713   __ Mov(dump2_base, dump_base);
714 
715   __ PopCPURegList(scratch_registers);
716 
717   while (!scratch_registers.IsEmpty()) {
718     CPURegister reg = scratch_registers.PopLowestIndex();
719     Register x = reg.X();
720     Register w = reg.W();
721     unsigned code = reg.GetCode();
722     __ Str(x, MemOperand(dump2_base, x_offset + (code * kXRegSizeInBytes)));
723     __ Str(w, MemOperand(dump2_base, w_offset + (code * kWRegSizeInBytes)));
724   }
725 
726   // Finally, restore dump2_base.
727   __ Ldr(dump2_base,
728          MemOperand(dump2_base,
729                     x_offset + (dump2_base.GetCode() * kXRegSizeInBytes)));
730 
731   completed_ = true;
732 }
733 
GetSignallingNan(int size_in_bits)734 uint64_t GetSignallingNan(int size_in_bits) {
735   switch (size_in_bits) {
736     case kHRegSize:
737       return Float16ToRawbits(kFP16SignallingNaN);
738     case kSRegSize:
739       return FloatToRawbits(kFP32SignallingNaN);
740     case kDRegSize:
741       return DoubleToRawbits(kFP64SignallingNaN);
742     default:
743       VIXL_UNIMPLEMENTED();
744       return 0;
745   }
746 }
747 
CanRun(const CPUFeatures & required,bool * queried_can_run)748 bool CanRun(const CPUFeatures& required, bool* queried_can_run) {
749   bool log_if_missing = true;
750   if (queried_can_run != NULL) {
751     log_if_missing = !*queried_can_run;
752     *queried_can_run = true;
753   }
754 
755 #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
756   // The Simulator can run any test that VIXL can assemble.
757   USE(required);
758   USE(log_if_missing);
759   return true;
760 #else
761   CPUFeatures cpu = CPUFeatures::InferFromOS();
762   // If InferFromOS fails, assume that basic features are present.
763   if (cpu.HasNoFeatures()) cpu = CPUFeatures::AArch64LegacyBaseline();
764   VIXL_ASSERT(cpu.Has(kInfrastructureCPUFeatures));
765 
766   if (cpu.Has(required)) return true;
767 
768   if (log_if_missing) {
769     CPUFeatures missing = required.Without(cpu);
770     // Note: This message needs to match REGEXP_MISSING_FEATURES from
771     // tools/threaded_test.py.
772     std::cout << "SKIPPED: Missing features: { " << missing << " }\n";
773     std::cout << "This test requires the following features to run its "
774                  "generated code on this CPU: "
775               << required << "\n";
776   }
777   return false;
778 #endif
779 }
780 
781 }  // namespace aarch64
782 }  // namespace vixl
783