test-utils-arm64.cc 13.2 KB
Newer Older
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
// 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.

28 29 30
// TODO(mythria): Remove this after this flag is turned on globally
#define V8_IMMINENT_DEPRECATION_WARNINGS

31
#include "src/v8.h"
32

33
#include "src/arm64/utils-arm64.h"
34
#include "src/macro-assembler.h"
35 36
#include "test/cctest/cctest.h"
#include "test/cctest/test-utils-arm64.h"
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

using namespace v8::internal;


#define __ masm->


bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result) {
  if (result != expected) {
    printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n",
           expected, result);
  }

  return expected == result;
}


bool Equal64(uint64_t expected, const RegisterDump*, uint64_t result) {
  if (result != expected) {
    printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
           expected, result);
  }

  return expected == result;
}


bool EqualFP32(float expected, const RegisterDump*, float result) {
  if (float_to_rawbits(expected) == float_to_rawbits(result)) {
    return true;
  } else {
68
    if (std::isnan(expected) || (expected == 0.0)) {
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
      printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n",
             float_to_rawbits(expected), float_to_rawbits(result));
    } else {
      printf("Expected %.9f (0x%08" PRIx32 ")\t "
             "Found %.9f (0x%08" PRIx32 ")\n",
             expected, float_to_rawbits(expected),
             result, float_to_rawbits(result));
    }
    return false;
  }
}


bool EqualFP64(double expected, const RegisterDump*, double result) {
  if (double_to_rawbits(expected) == double_to_rawbits(result)) {
    return true;
  }

87
  if (std::isnan(expected) || (expected == 0.0)) {
88 89 90 91 92 93 94 95 96 97 98 99 100
    printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
           double_to_rawbits(expected), double_to_rawbits(result));
  } else {
    printf("Expected %.17f (0x%016" PRIx64 ")\t "
           "Found %.17f (0x%016" PRIx64 ")\n",
           expected, double_to_rawbits(expected),
           result, double_to_rawbits(result));
  }
  return false;
}


bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg) {
101
  DCHECK(reg.Is32Bits());
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
  // Retrieve the corresponding X register so we can check that the upper part
  // was properly cleared.
  int64_t result_x = core->xreg(reg.code());
  if ((result_x & 0xffffffff00000000L) != 0) {
    printf("Expected 0x%08" PRIx32 "\t Found 0x%016" PRIx64 "\n",
           expected, result_x);
    return false;
  }
  uint32_t result_w = core->wreg(reg.code());
  return Equal32(expected, core, result_w);
}


bool Equal64(uint64_t expected,
             const RegisterDump* core,
             const Register& reg) {
118
  DCHECK(reg.Is64Bits());
119 120 121 122 123 124 125 126
  uint64_t result = core->xreg(reg.code());
  return Equal64(expected, core, result);
}


bool EqualFP32(float expected,
               const RegisterDump* core,
               const FPRegister& fpreg) {
127
  DCHECK(fpreg.Is32Bits());
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  // Retrieve the corresponding D register so we can check that the upper part
  // was properly cleared.
  uint64_t result_64 = core->dreg_bits(fpreg.code());
  if ((result_64 & 0xffffffff00000000L) != 0) {
    printf("Expected 0x%08" PRIx32 " (%f)\t Found 0x%016" PRIx64 "\n",
           float_to_rawbits(expected), expected, result_64);
    return false;
  }

  return EqualFP32(expected, core, core->sreg(fpreg.code()));
}


bool EqualFP64(double expected,
               const RegisterDump* core,
               const FPRegister& fpreg) {
144
  DCHECK(fpreg.Is64Bits());
145 146 147 148 149 150 151
  return EqualFP64(expected, core, core->dreg(fpreg.code()));
}


bool Equal64(const Register& reg0,
             const RegisterDump* core,
             const Register& reg1) {
152
  DCHECK(reg0.Is64Bits() && reg1.Is64Bits());
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
  int64_t expected = core->xreg(reg0.code());
  int64_t result = core->xreg(reg1.code());
  return Equal64(expected, core, result);
}


static char FlagN(uint32_t flags) {
  return (flags & NFlag) ? 'N' : 'n';
}


static char FlagZ(uint32_t flags) {
  return (flags & ZFlag) ? 'Z' : 'z';
}


static char FlagC(uint32_t flags) {
  return (flags & CFlag) ? 'C' : 'c';
}


static char FlagV(uint32_t flags) {
  return (flags & VFlag) ? 'V' : 'v';
}


bool EqualNzcv(uint32_t expected, uint32_t result) {
180 181
  DCHECK((expected & ~NZCVFlag) == 0);
  DCHECK((result & ~NZCVFlag) == 0);
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
  if (result != expected) {
    printf("Expected: %c%c%c%c\t Found: %c%c%c%c\n",
        FlagN(expected), FlagZ(expected), FlagC(expected), FlagV(expected),
        FlagN(result), FlagZ(result), FlagC(result), FlagV(result));
    return false;
  }

  return true;
}


bool EqualRegisters(const RegisterDump* a, const RegisterDump* b) {
  for (unsigned i = 0; i < kNumberOfRegisters; i++) {
    if (a->xreg(i) != b->xreg(i)) {
      printf("x%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
             i, a->xreg(i), b->xreg(i));
      return false;
    }
  }

  for (unsigned i = 0; i < kNumberOfFPRegisters; i++) {
    uint64_t a_bits = a->dreg_bits(i);
    uint64_t b_bits = b->dreg_bits(i);
    if (a_bits != b_bits) {
      printf("d%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n",
             i, a_bits, b_bits);
      return false;
    }
  }

  return true;
}


RegList PopulateRegisterArray(Register* w, Register* x, Register* r,
                              int reg_size, int reg_count, RegList allowed) {
  RegList list = 0;
  int i = 0;
  for (unsigned n = 0; (n < kNumberOfRegisters) && (i < reg_count); n++) {
    if (((1UL << n) & allowed) != 0) {
      // Only assign allowed registers.
      if (r) {
        r[i] = Register::Create(n, reg_size);
      }
      if (x) {
227
        x[i] = Register::Create(n, kXRegSizeInBits);
228 229
      }
      if (w) {
230
        w[i] = Register::Create(n, kWRegSizeInBits);
231 232 233 234 235 236
      }
      list |= (1UL << n);
      i++;
    }
  }
  // Check that we got enough registers.
237
  DCHECK(CountSetBits(list, kNumberOfRegisters) == reg_count);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

  return list;
}


RegList PopulateFPRegisterArray(FPRegister* s, FPRegister* d, FPRegister* v,
                                int reg_size, int reg_count, RegList allowed) {
  RegList list = 0;
  int i = 0;
  for (unsigned n = 0; (n < kNumberOfFPRegisters) && (i < reg_count); n++) {
    if (((1UL << n) & allowed) != 0) {
      // Only assigned allowed registers.
      if (v) {
        v[i] = FPRegister::Create(n, reg_size);
      }
      if (d) {
254
        d[i] = FPRegister::Create(n, kDRegSizeInBits);
255 256
      }
      if (s) {
257
        s[i] = FPRegister::Create(n, kSRegSizeInBits);
258 259 260 261 262 263
      }
      list |= (1UL << n);
      i++;
    }
  }
  // Check that we got enough registers.
264
  DCHECK(CountSetBits(list, kNumberOfFPRegisters) == reg_count);
265 266 267 268 269 270 271 272 273

  return list;
}


void Clobber(MacroAssembler* masm, RegList reg_list, uint64_t const value) {
  Register first = NoReg;
  for (unsigned i = 0; i < kNumberOfRegisters; i++) {
    if (reg_list & (1UL << i)) {
274
      Register xn = Register::Create(i, kXRegSizeInBits);
275
      // We should never write into csp here.
276
      DCHECK(!xn.Is(csp));
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
      if (!xn.IsZero()) {
        if (!first.IsValid()) {
          // This is the first register we've hit, so construct the literal.
          __ Mov(xn, value);
          first = xn;
        } else {
          // We've already loaded the literal, so re-use the value already
          // loaded into the first register we hit.
          __ Mov(xn, first);
        }
      }
    }
  }
}


void ClobberFP(MacroAssembler* masm, RegList reg_list, double const value) {
  FPRegister first = NoFPReg;
  for (unsigned i = 0; i < kNumberOfFPRegisters; i++) {
    if (reg_list & (1UL << i)) {
297
      FPRegister dn = FPRegister::Create(i, kDRegSizeInBits);
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
      if (!first.IsValid()) {
        // This is the first register we've hit, so construct the literal.
        __ Fmov(dn, value);
        first = dn;
      } else {
        // We've already loaded the literal, so re-use the value already loaded
        // into the first register we hit.
        __ Fmov(dn, first);
      }
    }
  }
}


void Clobber(MacroAssembler* masm, CPURegList reg_list) {
  if (reg_list.type() == CPURegister::kRegister) {
    // This will always clobber X registers.
    Clobber(masm, reg_list.list());
  } else if (reg_list.type() == CPURegister::kFPRegister) {
    // This will always clobber D registers.
    ClobberFP(masm, reg_list.list());
  } else {
    UNREACHABLE();
  }
}


void RegisterDump::Dump(MacroAssembler* masm) {
326
  DCHECK(__ StackPointer().Is(csp));
327 328

  // Ensure that we don't unintentionally clobber any registers.
329 330 331 332
  RegList old_tmp_list = masm->TmpList()->list();
  RegList old_fptmp_list = masm->FPTmpList()->list();
  masm->TmpList()->set_list(0);
  masm->FPTmpList()->set_list(0);
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

  // Preserve some temporary registers.
  Register dump_base = x0;
  Register dump = x1;
  Register tmp = x2;
  Register dump_base_w = dump_base.W();
  Register dump_w = dump.W();
  Register tmp_w = tmp.W();

  // Offsets into the dump_ structure.
  const int x_offset = offsetof(dump_t, x_);
  const int w_offset = offsetof(dump_t, w_);
  const int d_offset = offsetof(dump_t, d_);
  const int s_offset = offsetof(dump_t, s_);
  const int sp_offset = offsetof(dump_t, sp_);
  const int wsp_offset = offsetof(dump_t, wsp_);
  const int flags_offset = offsetof(dump_t, flags_);

  __ Push(xzr, dump_base, dump, tmp);

  // Load the address where we will dump the state.
  __ Mov(dump_base, reinterpret_cast<uint64_t>(&dump_));

  // Dump the stack pointer (csp and wcsp).
  // The stack pointer cannot be stored directly; it needs to be moved into
  // another register first. Also, we pushed four X registers, so we need to
  // compensate here.
360
  __ Add(tmp, csp, 4 * kXRegSize);
361
  __ Str(tmp, MemOperand(dump_base, sp_offset));
362
  __ Add(tmp_w, wcsp, 4 * kXRegSize);
363 364 365 366 367 368
  __ Str(tmp_w, MemOperand(dump_base, wsp_offset));

  // Dump X registers.
  __ Add(dump, dump_base, x_offset);
  for (unsigned i = 0; i < kNumberOfRegisters; i += 2) {
    __ Stp(Register::XRegFromCode(i), Register::XRegFromCode(i + 1),
369
           MemOperand(dump, i * kXRegSize));
370 371 372 373 374 375
  }

  // Dump W registers.
  __ Add(dump, dump_base, w_offset);
  for (unsigned i = 0; i < kNumberOfRegisters; i += 2) {
    __ Stp(Register::WRegFromCode(i), Register::WRegFromCode(i + 1),
376
           MemOperand(dump, i * kWRegSize));
377 378 379 380 381 382
  }

  // Dump D registers.
  __ Add(dump, dump_base, d_offset);
  for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) {
    __ Stp(FPRegister::DRegFromCode(i), FPRegister::DRegFromCode(i + 1),
383
           MemOperand(dump, i * kDRegSize));
384 385 386 387 388 389
  }

  // Dump S registers.
  __ Add(dump, dump_base, s_offset);
  for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) {
    __ Stp(FPRegister::SRegFromCode(i), FPRegister::SRegFromCode(i + 1),
390
           MemOperand(dump, i * kSRegSize));
391 392 393 394 395 396 397 398 399 400 401
  }

  // Dump the flags.
  __ Mrs(tmp, NZCV);
  __ Str(tmp, MemOperand(dump_base, flags_offset));

  // To dump the values that were in tmp amd dump, we need a new scratch
  // register.  We can use any of the already dumped registers since we can
  // easily restore them.
  Register dump2_base = x10;
  Register dump2 = x11;
402
  DCHECK(!AreAliased(dump_base, dump, tmp, dump2_base, dump2));
403 404 405 406 407 408 409

  // Don't lose the dump_ address.
  __ Mov(dump2_base, dump_base);

  __ Pop(tmp, dump, dump_base, xzr);

  __ Add(dump2, dump2_base, w_offset);
410 411 412
  __ Str(dump_base_w, MemOperand(dump2, dump_base.code() * kWRegSize));
  __ Str(dump_w, MemOperand(dump2, dump.code() * kWRegSize));
  __ Str(tmp_w, MemOperand(dump2, tmp.code() * kWRegSize));
413 414

  __ Add(dump2, dump2_base, x_offset);
415 416 417
  __ Str(dump_base, MemOperand(dump2, dump_base.code() * kXRegSize));
  __ Str(dump, MemOperand(dump2, dump.code() * kXRegSize));
  __ Str(tmp, MemOperand(dump2, tmp.code() * kXRegSize));
418 419

  // Finally, restore dump2_base and dump2.
420 421
  __ Ldr(dump2_base, MemOperand(dump2, dump2_base.code() * kXRegSize));
  __ Ldr(dump2, MemOperand(dump2, dump2.code() * kXRegSize));
422 423

  // Restore the MacroAssembler's scratch registers.
424 425
  masm->TmpList()->set_list(old_tmp_list);
  masm->FPTmpList()->set_list(old_fptmp_list);
426 427 428

  completed_ = true;
}