2014-02-12 09:19:30 +00:00
|
|
|
// 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.
|
|
|
|
|
2014-03-21 09:28:26 +00:00
|
|
|
#ifndef V8_ARM64_TEST_UTILS_ARM64_H_
|
|
|
|
#define V8_ARM64_TEST_UTILS_ARM64_H_
|
2014-02-12 09:19:30 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/v8.h"
|
2014-06-20 08:40:11 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
2014-02-12 09:19:30 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/arm64/macro-assembler-arm64.h"
|
|
|
|
#include "src/arm64/utils-arm64.h"
|
2014-06-20 08:40:11 +00:00
|
|
|
#include "src/macro-assembler.h"
|
2014-02-12 09:19:30 +00:00
|
|
|
|
|
|
|
|
2016-07-25 18:14:11 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
2014-02-12 09:19:30 +00:00
|
|
|
|
2017-05-31 13:58:43 +00:00
|
|
|
// Structure representing Q registers in a RegisterDump.
|
|
|
|
struct vec128_t {
|
|
|
|
uint64_t l;
|
|
|
|
uint64_t h;
|
|
|
|
};
|
2014-02-12 09:19:30 +00:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(RegAliasesMatch(code));
|
2014-02-12 09:19:30 +00:00
|
|
|
return dump_.w_[code];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int64_t xreg(unsigned code) const {
|
|
|
|
if (code == kSPRegInternalCode) {
|
|
|
|
return spreg();
|
|
|
|
}
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(RegAliasesMatch(code));
|
2014-02-12 09:19:30 +00:00
|
|
|
return dump_.x_[code];
|
|
|
|
}
|
|
|
|
|
2017-05-31 13:58:43 +00:00
|
|
|
// VRegister accessors.
|
2014-02-12 09:19:30 +00:00
|
|
|
inline uint32_t sreg_bits(unsigned code) const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(FPRegAliasesMatch(code));
|
2014-02-12 09:19:30 +00:00
|
|
|
return dump_.s_[code];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline float sreg(unsigned code) const {
|
2017-05-31 13:58:43 +00:00
|
|
|
return bit_cast<float>(sreg_bits(code));
|
2014-02-12 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline uint64_t dreg_bits(unsigned code) const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(FPRegAliasesMatch(code));
|
2014-02-12 09:19:30 +00:00
|
|
|
return dump_.d_[code];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline double dreg(unsigned code) const {
|
2017-05-31 13:58:43 +00:00
|
|
|
return bit_cast<double>(dreg_bits(code));
|
2014-02-12 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
2017-05-31 13:58:43 +00:00
|
|
|
inline vec128_t qreg(unsigned code) const { return dump_.q_[code]; }
|
|
|
|
|
2014-02-12 09:19:30 +00:00
|
|
|
// Stack pointer accessors.
|
|
|
|
inline int64_t spreg() const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(SPRegAliasesMatch());
|
2014-02-12 09:19:30 +00:00
|
|
|
return dump_.sp_;
|
|
|
|
}
|
|
|
|
|
2015-05-15 05:13:15 +00:00
|
|
|
inline int32_t wspreg() const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(SPRegAliasesMatch());
|
2015-05-15 05:13:15 +00:00
|
|
|
return static_cast<int32_t>(dump_.wsp_);
|
2014-02-12 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Flags accessors.
|
2015-05-15 05:13:15 +00:00
|
|
|
inline uint32_t flags_nzcv() const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(IsComplete());
|
|
|
|
CHECK((dump_.flags_ & ~Flags_mask) == 0);
|
2014-02-12 09:19:30 +00:00
|
|
|
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 {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(IsComplete());
|
|
|
|
CHECK(code < kNumberOfRegisters);
|
2014-02-12 09:19:30 +00:00
|
|
|
return ((dump_.x_[code] & kWRegMask) == dump_.w_[code]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// As RegAliasesMatch, but for the stack pointer.
|
|
|
|
bool SPRegAliasesMatch() const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(IsComplete());
|
2014-02-12 09:19:30 +00:00
|
|
|
return ((dump_.sp_ & kWRegMask) == dump_.wsp_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// As RegAliasesMatch, but for floating-point registers.
|
|
|
|
bool FPRegAliasesMatch(unsigned code) const {
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(IsComplete());
|
2017-05-31 13:58:43 +00:00
|
|
|
CHECK(code < kNumberOfVRegisters);
|
2014-02-12 09:19:30 +00:00
|
|
|
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.
|
2017-05-31 13:58:43 +00:00
|
|
|
uint64_t d_[kNumberOfVRegisters];
|
|
|
|
uint32_t s_[kNumberOfVRegisters];
|
|
|
|
|
|
|
|
// Vector registers.
|
|
|
|
vec128_t q_[kNumberOfVRegisters];
|
2014-02-12 09:19:30 +00:00
|
|
|
|
|
|
|
// 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_;
|
|
|
|
|
2014-02-17 15:20:54 +00:00
|
|
|
static dump_t for_sizeof();
|
2017-05-31 13:58:43 +00:00
|
|
|
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.");
|
2014-02-12 09:19:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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,
|
2017-05-31 13:58:43 +00:00
|
|
|
const VRegister& fpreg);
|
2014-02-12 09:19:30 +00:00
|
|
|
bool EqualFP64(double expected, const RegisterDump* core,
|
2017-05-31 13:58:43 +00:00
|
|
|
const VRegister& fpreg);
|
2014-02-12 09:19:30 +00:00
|
|
|
|
|
|
|
bool Equal64(const Register& reg0, const RegisterDump* core,
|
|
|
|
const Register& reg1);
|
2017-05-31 13:58:43 +00:00
|
|
|
bool Equal128(uint64_t expected_h, uint64_t expected_l,
|
|
|
|
const RegisterDump* core, const VRegister& reg);
|
2014-02-12 09:19:30 +00:00
|
|
|
|
|
|
|
bool EqualNzcv(uint32_t expected, uint32_t result);
|
|
|
|
|
|
|
|
bool EqualRegisters(const RegisterDump* a, const RegisterDump* b);
|
|
|
|
|
[assembler] Make Register et al. real classes
Up to now, each architecture defined all Register types as structs,
with lots of redundancy. An often found comment noted that they cannot
be classes due to initialization order problems. As these problems are
gone with C++11 constexpr constants, I now tried making Registers
classes again.
All register types now inherit from RegisterBase, which provides a
default set of methods and named constructors (like ::from_code,
code(), bit(), is_valid(), ...).
This design allows to guarantee an interesting property: Each register
is either valid, or it's the no_reg register. There are no other
invalid registers. This is guaranteed statically by the constexpr
constructor, and dynamically by ::from_code.
I decided to disallow the default constructor completely, so instead of
"Register reg;" you now need "Register reg = no_reg;". This makes
explicit how the Register is initialized.
I did this change to the x64, ia32, arm, arm64, mips and mips64 ports.
Overall, code got much more compact and more safe. In theory, it should
also increase performance (since the is_valid() check is simpler), but
this is probably not measurable.
R=mstarzinger@chromium.org
Change-Id: I5ccfa4050daf4e146a557970e9d37fd3d2788d4a
Reviewed-on: https://chromium-review.googlesource.com/650927
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47847}
2017-09-06 08:05:07 +00:00
|
|
|
// 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(); });
|
|
|
|
}
|
|
|
|
|
2014-02-12 09:19:30 +00:00
|
|
|
// 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.
|
2017-05-31 13:58:43 +00:00
|
|
|
RegList PopulateVRegisterArray(VRegister* s, VRegister* d, VRegister* v,
|
|
|
|
int reg_size, int reg_count, RegList allowed);
|
2014-02-12 09:19:30 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2016-07-25 18:14:11 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|
|
|
|
|
2014-03-21 09:28:26 +00:00
|
|
|
#endif // V8_ARM64_TEST_UTILS_ARM64_H_
|