Commit 19df7a20 authored by oth's avatar oth Committed by Commit bot

[interpreter] Wide register support.

This increases the size of register operands to be 16-bit.

Not all bytecodes have wide register variants, so when they are
needed a register translator will copy them into a small area
reserved at the top of the 8-bit register range and these registers
are supplied as arguments to the bytecode with 8-bit operands.

This is non-intrusive for typical bytecode where the number of
registers is less than 120. For bytecodes with wide register
operands (above the window) their index needs to be translated
to avoid the reserved translation window.

Enables splay.js to run in Octane and a handful of mjsunit tests.

BUG=v8:4280,v8:4675
LOG=NO

Review URL: https://codereview.chromium.org/1613163002

Cr-Commit-Position: refs/heads/master@{#33516}
parent 8391d425
......@@ -1118,6 +1118,8 @@ source_set("v8_base") {
"src/interpreter/handler-table-builder.h",
"src/interpreter/interpreter.cc",
"src/interpreter/interpreter.h",
"src/interpreter/register-translator.cc",
"src/interpreter/register-translator.h",
"src/isolate-inl.h",
"src/isolate.cc",
"src/isolate.h",
......
This diff is collapsed.
......@@ -9,6 +9,7 @@
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
#include "src/interpreter/register-translator.h"
#include "src/zone-containers.h"
namespace v8 {
......@@ -25,7 +26,7 @@ class Register;
// when rest parameters implementation has settled down.
enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments };
class BytecodeArrayBuilder final {
class BytecodeArrayBuilder final : private RegisterMover {
public:
BytecodeArrayBuilder(Isolate* isolate, Zone* zone);
~BytecodeArrayBuilder();
......@@ -59,6 +60,18 @@ class BytecodeArrayBuilder final {
// Returns the number of fixed (non-temporary) registers.
int fixed_register_count() const { return context_count() + locals_count(); }
// Returns the number of fixed and temporary registers.
int fixed_and_temporary_register_count() const {
return fixed_register_count() + temporary_register_count_;
}
// Returns the number of registers used for translating wide
// register operands into byte sized register operands.
int translation_register_count() const {
return RegisterTranslator::RegisterCountAdjustment(
fixed_and_temporary_register_count(), parameter_count());
}
Register Parameter(int parameter_index) const;
// Return true if the register |reg| represents a parameter or a
......@@ -264,15 +277,18 @@ class BytecodeArrayBuilder final {
static bool FitsInIdx16Operand(int value);
static bool FitsInIdx16Operand(size_t value);
static bool FitsInReg8Operand(Register value);
static bool FitsInReg8OperandUntranslated(Register value);
static bool FitsInReg16Operand(Register value);
static bool FitsInReg16OperandUntranslated(Register value);
// RegisterMover interface methods.
void MoveRegisterUntranslated(Register from, Register to) override;
bool RegisterOperandIsMovable(Bytecode bytecode, int operand_index) override;
static Bytecode GetJumpWithConstantOperand(Bytecode jump_smi8_operand);
static Bytecode GetJumpWithConstantWideOperand(Bytecode jump_smi8_operand);
static Bytecode GetJumpWithToBoolean(Bytecode jump_smi8_operand);
Register MapRegister(Register reg);
Register MapRegisters(Register reg, Register args_base, int args_length = 1);
template <size_t N>
INLINE(void Output(Bytecode bytecode, uint32_t(&operands)[N]));
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
......@@ -297,13 +313,14 @@ class BytecodeArrayBuilder final {
bool OperandIsValid(Bytecode bytecode, int operand_index,
uint32_t operand_value) const;
bool LastBytecodeInSameBlock() const;
bool RegisterIsValid(Register reg, OperandType reg_type) const;
bool LastBytecodeInSameBlock() const;
bool NeedToBooleanCast();
bool IsRegisterInAccumulator(Register reg);
// Temporary register management.
void ForgeTemporaryRegister();
int BorrowTemporaryRegister();
int BorrowTemporaryRegisterNotInRange(int start_index, int end_index);
void ReturnTemporaryRegister(int reg_index);
......@@ -329,6 +346,7 @@ class BytecodeArrayBuilder final {
HandlerTableBuilder* handler_table_builder() {
return &handler_table_builder_;
}
RegisterTranslator* register_translator() { return &register_translator_; }
Isolate* isolate_;
Zone* zone_;
......@@ -340,12 +358,12 @@ class BytecodeArrayBuilder final {
size_t last_bytecode_start_;
bool exit_seen_in_block_;
int unbound_jumps_;
int parameter_count_;
int local_register_count_;
int context_register_count_;
int temporary_register_count_;
ZoneSet<int> free_temporaries_;
RegisterTranslator register_translator_;
DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder);
};
......
......@@ -47,14 +47,14 @@ uint32_t BytecodeArrayIterator::GetRawOperand(int operand_index,
bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_ +
Bytecodes::GetOperandOffset(current_bytecode(), operand_index);
switch (Bytecodes::SizeOfOperand(operand_type)) {
default:
case OperandSize::kNone:
UNREACHABLE();
case OperandSize::kByte:
return static_cast<uint32_t>(*operand_start);
case OperandSize::kShort:
return ReadUnalignedUInt16(operand_start);
case OperandSize::kNone:
UNREACHABLE();
}
return 0;
}
......
......@@ -28,6 +28,18 @@ struct OperandTraits {};
OPERAND_TYPE_LIST(DECLARE_OPERAND_SIZE)
#undef DECLARE_OPERAND_SIZE
template <OperandType>
struct RegisterOperandTraits {
static const int kIsRegisterOperand = 0;
};
#define DECLARE_REGISTER_OPERAND(Name, _) \
template <> \
struct RegisterOperandTraits<OperandType::k##Name> { \
static const int kIsRegisterOperand = 1; \
};
REGISTER_OPERAND_TYPE_LIST(DECLARE_REGISTER_OPERAND)
#undef DECLARE_REGISTER_OPERAND
template <OperandType... Args>
struct BytecodeTraits {};
......@@ -63,13 +75,28 @@ struct BytecodeTraits<operand_0, operand_1, operand_2, operand_3,
return kOperandOffsets[i];
}
template <OperandType ot>
static inline bool HasAnyOperandsOfType() {
return operand_0 == ot || operand_1 == ot || operand_2 == ot ||
operand_3 == ot;
}
static const int kOperandCount = 4;
static const int kRegisterOperandCount =
RegisterOperandTraits<operand_0>::kIsRegisterOperand +
RegisterOperandTraits<operand_1>::kIsRegisterOperand +
RegisterOperandTraits<operand_2>::kIsRegisterOperand +
RegisterOperandTraits<operand_3>::kIsRegisterOperand;
static const int kRegisterOperandBitmap =
RegisterOperandTraits<operand_0>::kIsRegisterOperand +
(RegisterOperandTraits<operand_1>::kIsRegisterOperand << 1) +
(RegisterOperandTraits<operand_2>::kIsRegisterOperand << 2) +
(RegisterOperandTraits<operand_3>::kIsRegisterOperand << 3);
static const int kSize =
1 + OperandTraits<operand_0>::kSize + OperandTraits<operand_1>::kSize +
OperandTraits<operand_2>::kSize + OperandTraits<operand_3>::kSize;
};
template <OperandType operand_0, OperandType operand_1, OperandType operand_2>
struct BytecodeTraits<operand_0, operand_1, operand_2, OPERAND_TERM> {
static inline OperandType GetOperandType(int i) {
......@@ -96,7 +123,20 @@ struct BytecodeTraits<operand_0, operand_1, operand_2, OPERAND_TERM> {
return kOperandOffsets[i];
}
template <OperandType ot>
static inline bool HasAnyOperandsOfType() {
return operand_0 == ot || operand_1 == ot || operand_2 == ot;
}
static const int kOperandCount = 3;
static const int kRegisterOperandCount =
RegisterOperandTraits<operand_0>::kIsRegisterOperand +
RegisterOperandTraits<operand_1>::kIsRegisterOperand +
RegisterOperandTraits<operand_2>::kIsRegisterOperand;
static const int kRegisterOperandBitmap =
RegisterOperandTraits<operand_0>::kIsRegisterOperand +
(RegisterOperandTraits<operand_1>::kIsRegisterOperand << 1) +
(RegisterOperandTraits<operand_2>::kIsRegisterOperand << 2);
static const int kSize =
1 + OperandTraits<operand_0>::kSize + OperandTraits<operand_1>::kSize +
OperandTraits<operand_2>::kSize;
......@@ -126,7 +166,18 @@ struct BytecodeTraits<operand_0, operand_1, OPERAND_TERM> {
return kOperandOffsets[i];
}
template <OperandType ot>
static inline bool HasAnyOperandsOfType() {
return operand_0 == ot || operand_1 == ot;
}
static const int kOperandCount = 2;
static const int kRegisterOperandCount =
RegisterOperandTraits<operand_0>::kIsRegisterOperand +
RegisterOperandTraits<operand_1>::kIsRegisterOperand;
static const int kRegisterOperandBitmap =
RegisterOperandTraits<operand_0>::kIsRegisterOperand +
(RegisterOperandTraits<operand_1>::kIsRegisterOperand << 1);
static const int kSize =
1 + OperandTraits<operand_0>::kSize + OperandTraits<operand_1>::kSize;
};
......@@ -148,7 +199,16 @@ struct BytecodeTraits<operand_0, OPERAND_TERM> {
return 1;
}
template <OperandType ot>
static inline bool HasAnyOperandsOfType() {
return operand_0 == ot;
}
static const int kOperandCount = 1;
static const int kRegisterOperandCount =
RegisterOperandTraits<operand_0>::kIsRegisterOperand;
static const int kRegisterOperandBitmap =
RegisterOperandTraits<operand_0>::kIsRegisterOperand;
static const int kSize = 1 + OperandTraits<operand_0>::kSize;
};
......@@ -169,7 +229,14 @@ struct BytecodeTraits<OperandType::kNone, OPERAND_TERM> {
return 1;
}
template <OperandType ot>
static inline bool HasAnyOperandsOfType() {
return false;
}
static const int kOperandCount = 0;
static const int kRegisterOperandCount = 0;
static const int kRegisterOperandBitmap = 0;
static const int kSize = 1 + OperandTraits<OperandType::kNone>::kSize;
};
......
......@@ -57,6 +57,7 @@ const char* Bytecodes::OperandSizeToString(OperandSize operand_size) {
// static
uint8_t Bytecodes::ToByte(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
return static_cast<uint8_t>(bytecode);
}
......@@ -99,6 +100,21 @@ int Bytecodes::NumberOfOperands(Bytecode bytecode) {
}
// static
int Bytecodes::NumberOfRegisterOperands(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
typedef BytecodeTraits<__VA_ARGS__, OPERAND_TERM> Name##Trait; \
return Name##Trait::kRegisterOperandCount;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return false;
}
// static
OperandType Bytecodes::GetOperandType(Bytecode bytecode, int i) {
DCHECK(bytecode <= Bytecode::kLast);
......@@ -129,6 +145,21 @@ OperandSize Bytecodes::GetOperandSize(Bytecode bytecode, int i) {
}
// static
int Bytecodes::GetRegisterOperandBitmap(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
typedef BytecodeTraits<__VA_ARGS__, OPERAND_TERM> Name##Trait; \
return Name##Trait::kRegisterOperandBitmap;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return false;
}
// static
int Bytecodes::GetOperandOffset(Bytecode bytecode, int i) {
DCHECK(bytecode <= Bytecode::kLast);
......@@ -363,12 +394,24 @@ static const int kCurrentContextRegisterIndex =
static const int kNewTargetRegisterIndex =
-InterpreterFrameConstants::kNewTargetFromRegisterPointer / kPointerSize;
// The register space is a signed 16-bit space. Register operands
// occupy range above 0. Parameter indices are biased with the
// negative value kLastParamRegisterIndex for ease of access in the
// interpreter.
static const int kMaxParameterIndex = kMaxInt16 + kLastParamRegisterIndex;
static const int kMaxRegisterIndex = -kMinInt16;
static const int kMaxReg8Index = -kMinInt8;
static const int kMinReg8Index = -kMaxInt8;
static const int kMaxReg16Index = -kMinInt16;
static const int kMinReg16Index = -kMaxInt16;
// Registers occupy range 0-127 in 8-bit value leaving 128 unused values.
// Parameter indices are biased with the negative value kLastParamRegisterIndex
// for ease of access in the interpreter.
static const int kMaxParameterIndex = 128 + kLastParamRegisterIndex;
bool Register::is_byte_operand() const {
return index_ >= kMinReg8Index && index_ <= kMaxReg8Index;
}
bool Register::is_short_operand() const {
return index_ >= kMinReg16Index && index_ <= kMaxReg16Index;
}
Register Register::FromParameterIndex(int index, int parameter_count) {
DCHECK_GE(index, 0);
......@@ -376,7 +419,6 @@ Register Register::FromParameterIndex(int index, int parameter_count) {
DCHECK_LE(parameter_count, kMaxParameterIndex + 1);
int register_index = kLastParamRegisterIndex - parameter_count + index + 1;
DCHECK_LT(register_index, 0);
DCHECK_GE(register_index, kMinInt8);
return Register(register_index);
}
......@@ -417,10 +459,12 @@ bool Register::is_new_target() const {
int Register::MaxParameterIndex() { return kMaxParameterIndex; }
int Register::MaxRegisterIndex() { return kMaxRegisterIndex; }
int Register::MaxRegisterIndexForByteOperand() { return kMaxReg8Index; }
uint8_t Register::ToOperand() const {
DCHECK_GE(index_, kMinInt8);
DCHECK_LE(index_, kMaxInt8);
DCHECK(is_byte_operand());
return static_cast<uint8_t>(-index_);
}
......@@ -431,8 +475,7 @@ Register Register::FromOperand(uint8_t operand) {
uint16_t Register::ToWideOperand() const {
DCHECK_GE(index_, kMinInt16);
DCHECK_LE(index_, kMaxInt16);
DCHECK(is_short_operand());
return static_cast<uint16_t>(-index_);
}
......
......@@ -104,7 +104,6 @@ namespace interpreter {
V(LoadICStrict, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \
V(KeyedLoadICSloppy, OperandType::kReg8, OperandType::kIdx8) \
V(KeyedLoadICStrict, OperandType::kReg8, OperandType::kIdx8) \
/* TODO(rmcilroy): Wide register operands too? */ \
V(LoadICSloppyWide, OperandType::kReg8, OperandType::kIdx16, \
OperandType::kIdx16) \
V(LoadICStrictWide, OperandType::kReg8, OperandType::kIdx16, \
......@@ -119,7 +118,6 @@ namespace interpreter {
OperandType::kIdx8) \
V(KeyedStoreICStrict, OperandType::kReg8, OperandType::kReg8, \
OperandType::kIdx8) \
/* TODO(rmcilroy): Wide register operands too? */ \
V(StoreICSloppyWide, OperandType::kReg8, OperandType::kIdx16, \
OperandType::kIdx16) \
V(StoreICStrictWide, OperandType::kReg8, OperandType::kIdx16, \
......@@ -289,20 +287,22 @@ enum class Bytecode : uint8_t {
// in its stack-frame. Register hold parameters, this, and expression values.
class Register {
public:
Register() : index_(kIllegalIndex) {}
explicit Register(int index) : index_(index) {}
explicit Register(int index = kInvalidIndex) : index_(index) {}
int index() const {
DCHECK(index_ != kIllegalIndex);
DCHECK(index_ != kInvalidIndex);
return index_;
}
bool is_parameter() const { return index() < 0; }
bool is_valid() const { return index_ != kIllegalIndex; }
bool is_valid() const { return index_ != kInvalidIndex; }
bool is_byte_operand() const;
bool is_short_operand() const;
static Register FromParameterIndex(int index, int parameter_count);
int ToParameterIndex(int parameter_count) const;
static int MaxParameterIndex();
static int MaxRegisterIndex();
static int MaxRegisterIndexForByteOperand();
// Returns the register for the function's closure object.
static Register function_closure();
......@@ -350,7 +350,7 @@ class Register {
}
private:
static const int kIllegalIndex = kMaxInt;
static const int kInvalidIndex = kMaxInt;
void* operator new(size_t size);
void operator delete(void* p);
......@@ -379,6 +379,9 @@ class Bytecodes {
// Returns the number of operands expected by |bytecode|.
static int NumberOfOperands(Bytecode bytecode);
// Returns the number of register operands expected by |bytecode|.
static int NumberOfRegisterOperands(Bytecode bytecode);
// Return the i-th operand of |bytecode|.
static OperandType GetOperandType(Bytecode bytecode, int i);
......@@ -389,37 +392,41 @@ class Bytecodes {
// of the bytecode.
static int GetOperandOffset(Bytecode bytecode, int i);
// Returns a zero-based bitmap of the register operand positions of
// |bytecode|.
static int GetRegisterOperandBitmap(Bytecode bytecode);
// Returns the size of the bytecode including its operands.
static int Size(Bytecode bytecode);
// Returns the size of |operand|.
static OperandSize SizeOfOperand(OperandType operand);
// Return true if the bytecode is a conditional jump taking
// Returns true if the bytecode is a conditional jump taking
// an immediate byte operand (OperandType::kImm8).
static bool IsConditionalJumpImmediate(Bytecode bytecode);
// Return true if the bytecode is a conditional jump taking
// Returns true if the bytecode is a conditional jump taking
// a constant pool entry (OperandType::kIdx8).
static bool IsConditionalJumpConstant(Bytecode bytecode);
// Return true if the bytecode is a conditional jump taking
// Returns true if the bytecode is a conditional jump taking
// a constant pool entry (OperandType::kIdx16).
static bool IsConditionalJumpConstantWide(Bytecode bytecode);
// Return true if the bytecode is a conditional jump taking
// Returns true if the bytecode is a conditional jump taking
// any kind of operand.
static bool IsConditionalJump(Bytecode bytecode);
// Return true if the bytecode is a jump or a conditional jump taking
// Returns true if the bytecode is a jump or a conditional jump taking
// an immediate byte operand (OperandType::kImm8).
static bool IsJumpImmediate(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking a
// Returns true if the bytecode is a jump or conditional jump taking a
// constant pool entry (OperandType::kIdx8).
static bool IsJumpConstant(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking a
// Returns true if the bytecode is a jump or conditional jump taking a
// constant pool entry (OperandType::kIdx16).
static bool IsJumpConstantWide(Bytecode bytecode);
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/interpreter/register-translator.h"
#include "src/interpreter/bytecode-array-builder.h"
namespace v8 {
namespace internal {
namespace interpreter {
RegisterTranslator::RegisterTranslator(RegisterMover* mover)
: mover_(mover), emitting_moves_(false), window_registers_count_(0) {}
void RegisterTranslator::TranslateInputRegisters(Bytecode bytecode,
uint32_t* raw_operands,
int raw_operand_count) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), raw_operand_count);
if (!emitting_moves_) {
emitting_moves_ = true;
DCHECK_EQ(window_registers_count_, 0);
int register_bitmap = Bytecodes::GetRegisterOperandBitmap(bytecode);
for (int i = 0; i < raw_operand_count; i++) {
if ((register_bitmap & (1 << i)) == 0) {
continue;
}
Register in_reg = Register::FromRawOperand(raw_operands[i]);
Register out_reg = TranslateAndMove(bytecode, i, in_reg);
raw_operands[i] = out_reg.ToRawOperand();
}
emitting_moves_ = false;
} else {
// When the register translator is translating registers, it will
// cause the bytecode generator to emit moves on it's behalf. This
// path is reached by these moves.
DCHECK(bytecode == Bytecode::kMovWide && raw_operand_count == 2 &&
Register::FromRawOperand(raw_operands[0]).is_valid() &&
Register::FromRawOperand(raw_operands[1]).is_valid());
}
}
Register RegisterTranslator::TranslateAndMove(Bytecode bytecode,
int operand_index, Register reg) {
OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index);
Register translated_reg = Translate(reg);
Register addressable_reg = MakeAddressable(translated_reg, operand_type);
if (addressable_reg != translated_reg) {
CHECK(operand_type == OperandType::kReg8 &&
mover()->RegisterOperandIsMovable(bytecode, operand_index));
mover()->MoveRegisterUntranslated(translated_reg, addressable_reg);
}
return addressable_reg;
}
void RegisterTranslator::TranslateOutputRegisters() {
if (!emitting_moves_) {
emitting_moves_ = true;
while (window_registers_count_ > 0) {
window_registers_count_ -= 1;
Register source(kTranslationWindowStart + window_registers_count_);
Register destination = window_registers_[window_registers_count_];
mover()->MoveRegisterUntranslated(source, destination);
}
emitting_moves_ = false;
}
}
Register RegisterTranslator::MakeAddressable(Register reg,
OperandType reg_type) {
DCHECK(!InTranslationWindow(reg));
OperandSize reg_size = Bytecodes::SizeOfOperand(reg_type);
if (reg_size == OperandSize::kByte && !FitsInReg8Operand(reg)) {
// TODO(oth): Moves into and out from translation window could be
// decoupled if there were metadata to say whether the register
// operand was an input, output, or input-and-output for a given
// bytecode.
Register destination(kTranslationWindowStart + window_registers_count_);
window_registers_[window_registers_count_] = reg;
window_registers_count_ += 1;
DCHECK_LE(window_registers_count_, kTranslationWindowLength);
return destination;
} else {
return reg;
}
}
// static
Register RegisterTranslator::Translate(Register reg) {
if (reg.index() >= kTranslationWindowStart) {
return Register(reg.index() + kTranslationWindowLength);
} else {
return reg;
}
}
// static
bool RegisterTranslator::InTranslationWindow(Register reg) {
return (reg.index() >= kTranslationWindowStart &&
reg.index() <= kTranslationWindowLimit);
}
// static
Register RegisterTranslator::UntranslateRegister(Register reg) {
if (reg.index() >= kTranslationWindowStart) {
return Register(reg.index() - kTranslationWindowLength);
} else {
return reg;
}
}
// static
int RegisterTranslator::DistanceToTranslationWindow(Register reg) {
return kTranslationWindowStart - reg.index();
}
// static
bool RegisterTranslator::FitsInReg8Operand(Register reg) {
return reg.is_byte_operand() && reg.index() < kTranslationWindowStart;
}
// static
bool RegisterTranslator::FitsInReg16Operand(Register reg) {
int max_index = Register::MaxRegisterIndex() - kTranslationWindowLength + 1;
return reg.is_short_operand() && reg.index() < max_index;
}
// static
int RegisterTranslator::RegisterCountAdjustment(int register_count,
int parameter_count) {
if (register_count > kTranslationWindowStart) {
return kTranslationWindowLength;
} else if (parameter_count > 0) {
Register param0 = Register::FromParameterIndex(0, parameter_count);
if (!param0.is_byte_operand()) {
// TODO(oth): Number of parameters means translation is
// required, but the translation window location is such that
// some space is wasted. Hopefully a rare corner case, but could
// relocate window to limit waste.
return kTranslationWindowLimit + 1 - register_count;
}
}
return 0;
}
} // namespace interpreter
} // namespace internal
} // namespace v8
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_INTERPRETER_REGISTER_TRANSLATOR_H_
#define V8_INTERPRETER_REGISTER_TRANSLATOR_H_
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
namespace interpreter {
class RegisterMover;
// A class that enables bytecodes having only byte sized register operands
// to access all registers in the two byte space. Most bytecode uses few
// registers so space can be saved if most bytecodes with register operands
// just take byte operands.
//
// To reach the wider register space, a translation window is reserved in
// the byte addressable space specifically for copying registers into and
// out of before a bytecode is emitted. The translation window occupies
// the last register slots at the top of the byte addressable range.
//
// Because of the translation window any registers which naturally lie
// at above the translation window have to have their register index
// incremented by the window width before they are emitted.
//
// This class does not support moving ranges of registers to and from
// the translation window. It would be straightforward to add support
// for constrained ranges, e.g. kRegPair8, kRegTriple8 operands, but
// these would have two negative effects. The translation window would
// need to be wider, further limiting the space for byte operands. And
// every register in a range would need to be moved consuming more
// space in the bytecode array.
class RegisterTranslator final {
public:
explicit RegisterTranslator(RegisterMover* mover);
// Translate and re-write the register operands that are inputs
// to |bytecode| when it is about to be emitted.
void TranslateInputRegisters(Bytecode bytecode, uint32_t* raw_operands,
int raw_operand_count);
// Translate and re-write the register operands that are outputs
// from |bytecode| when it has just been output.
void TranslateOutputRegisters();
// Returns true if |reg| is in the translation window.
static bool InTranslationWindow(Register reg);
// Return register value as if it had been translated.
static Register UntranslateRegister(Register reg);
// Returns the distance in registers between the translation window
// start and |reg|. The result is negative when |reg| is above the
// start of the translation window.
static int DistanceToTranslationWindow(Register reg);
// Returns true if |reg| can be represented as an 8-bit operand
// after translation.
static bool FitsInReg8Operand(Register reg);
// Returns true if |reg| can be represented as an 16-bit operand
// after translation.
static bool FitsInReg16Operand(Register reg);
// Returns the increment to the register count necessary if the
// value indicates the translation window is required.
static int RegisterCountAdjustment(int register_count, int parameter_count);
private:
static const int kTranslationWindowLength = 4;
static const int kTranslationWindowLimit = -kMinInt8;
static const int kTranslationWindowStart =
kTranslationWindowLimit - kTranslationWindowLength + 1;
Register TranslateAndMove(Bytecode bytecode, int operand_index, Register reg);
Register MakeAddressable(Register reg, OperandType reg_type);
static Register Translate(Register reg);
RegisterMover* mover() const { return mover_; }
// Entity to perform register moves necessary to translate registers
// and ensure reachability.
RegisterMover* mover_;
// Flag to avoid re-entrancy when emitting move bytecodes for
// translation.
bool emitting_moves_;
// State for restoring registers after bytecode.
Register window_registers_[kTranslationWindowLength];
int window_registers_count_;
};
// Interface for RegisterTranslator helper class that will emit
// register move bytecodes at the translator's behest.
class RegisterMover {
public:
virtual ~RegisterMover() {}
// Move register |from| to register |to| with no translation.
// returns false if either register operand is invalid. Implementations
// of this method must be aware that register moves with bad
// register values are a security hole.
virtual void MoveRegisterUntranslated(Register from, Register to) = 0;
// Returns true if the register operand can be moved into the
// translation window.
virtual bool RegisterOperandIsMovable(Bytecode bytecode,
int operand_index) = 0;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_REGISTER_TRANSLATOR_H_
......@@ -3680,6 +3680,151 @@ TEST(InterpreterEvalFunctionDecl) {
}
}
TEST(InterpreterWideRegisterArithmetic) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
static const size_t kMaxRegisterForTest = 150;
std::ostringstream os;
os << "function " << InterpreterTester::function_name() << "(arg) {\n";
os << " var retval = -77;\n";
for (size_t i = 0; i < kMaxRegisterForTest; i++) {
os << " var x" << i << " = " << i << ";\n";
}
for (size_t i = 0; i < kMaxRegisterForTest / 2; i++) {
size_t j = kMaxRegisterForTest - i - 1;
os << " var tmp = x" << j << ";\n";
os << " var x" << j << " = x" << i << ";\n";
os << " var x" << i << " = tmp;\n";
}
for (size_t i = 0; i < kMaxRegisterForTest / 2; i++) {
size_t j = kMaxRegisterForTest - i - 1;
os << " var tmp = x" << j << ";\n";
os << " var x" << j << " = x" << i << ";\n";
os << " var x" << i << " = tmp;\n";
}
for (size_t i = 0; i < kMaxRegisterForTest; i++) {
os << " if (arg == " << i << ") {\n" //
<< " retval = x" << i << ";\n" //
<< " }\n"; //
}
os << " return retval;\n";
os << "}\n";
std::string source = os.str();
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
for (size_t i = 0; i < kMaxRegisterForTest; i++) {
Handle<Object> arg = handle(Smi::FromInt(static_cast<int>(i)), isolate);
Handle<Object> return_value = callable(arg).ToHandleChecked();
CHECK(return_value->SameValue(*arg));
}
}
TEST(InterpreterCallWideRegisters) {
static const int kPeriod = 25;
static const int kLength = 512;
static const int kStartChar = 65;
for (int pass = 0; pass < 3; pass += 1) {
std::ostringstream os;
for (int i = 0; i < pass * 97; i += 1) {
os << "var x" << i << " = " << i << "\n";
}
os << "return String.fromCharCode(";
os << kStartChar;
for (int i = 1; i < kLength; i += 1) {
os << "," << kStartChar + (i % kPeriod);
}
os << ");";
std::string source = InterpreterTester::SourceForBody(os.str().c_str());
HandleAndZoneScope handles;
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable();
Handle<Object> return_val = callable().ToHandleChecked();
Handle<String> return_string = Handle<String>::cast(return_val);
CHECK_EQ(return_string->length(), kLength);
for (int i = 0; i < kLength; i += 1) {
CHECK_EQ(return_string->Get(i), 65 + (i % kPeriod));
}
}
}
TEST(InterpreterWideParametersPickOne) {
static const int kParameterCount = 130;
for (int parameter = 0; parameter < 10; parameter++) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::ostringstream os;
os << "function " << InterpreterTester::function_name() << "(arg) {\n";
os << " function selector(i";
for (int i = 0; i < kParameterCount; i++) {
os << ","
<< "a" << i;
}
os << ") {\n";
os << " return a" << parameter << ";\n";
os << " };\n";
os << " return selector(arg";
for (int i = 0; i < kParameterCount; i++) {
os << "," << i;
}
os << ");";
os << "}\n";
std::string source = os.str();
InterpreterTester tester(handles.main_isolate(), source.c_str(), "*");
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> arg = handle(Smi::FromInt(0xaa55), isolate);
Handle<Object> return_value = callable(arg).ToHandleChecked();
Handle<Smi> actual = Handle<Smi>::cast(return_value);
CHECK_EQ(actual->value(), parameter);
}
}
TEST(InterpreterWideParametersSummation) {
static int kParameterCount = 200;
static int kBaseValue = 17000;
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::ostringstream os;
os << "function " << InterpreterTester::function_name() << "(arg) {\n";
os << " function summation(i";
for (int i = 0; i < kParameterCount; i++) {
os << ","
<< "a" << i;
}
os << ") {\n";
os << " var sum = " << kBaseValue << ";\n";
os << " switch(i) {\n";
for (int i = 0; i < kParameterCount; i++) {
int j = kParameterCount - i - 1;
os << " case " << j << ": sum += a" << j << ";\n";
}
os << " }\n";
os << " return sum;\n";
os << " };\n";
os << " return summation(arg";
for (int i = 0; i < kParameterCount; i++) {
os << "," << i;
}
os << ");";
os << "}\n";
std::string source = os.str();
InterpreterTester tester(handles.main_isolate(), source.c_str(), "*");
auto callable = tester.GetCallable<Handle<Object>>();
for (int i = 0; i < kParameterCount; i++) {
Handle<Object> arg = handle(Smi::FromInt(i), isolate);
Handle<Object> return_value = callable(arg).ToHandleChecked();
int expected = kBaseValue + i * (i + 1) / 2;
Handle<Smi> actual = Handle<Smi>::cast(return_value);
CHECK_EQ(actual->value(), expected);
}
}
// TODO(oth): Test for..in with wide registers.
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -765,7 +765,7 @@
'arguments-load-across-eval': [SKIP],
'arguments-read-and-assignment': [SKIP],
'array-constructor': [SKIP],
'array-constructor': [PASS, SLOW],
'array-functions-prototype-misc': [SKIP],
'array-join': [SKIP],
'array-literal-feedback': [SKIP],
......@@ -777,7 +777,6 @@
'compiler/expression-trees': [SKIP],
'compiler/optimized-for-in': [SKIP],
'compiler/opt-next-call-turbo': [SKIP],
'compiler/regress-3786': [SKIP],
'compiler/regress-446647': [SKIP],
'compiler/regress-447567': [SKIP],
'compiler/regress-96989': [SKIP],
......@@ -824,7 +823,6 @@
'regress/regress-1125': [SKIP],
'regress/regress-1170187': [SKIP],
'regress/regress-1170': [SKIP],
'regress/regress-1177809': [SKIP],
'regress/regress-1178598': [SKIP],
'regress/regress-119609': [SKIP],
'regress/regress-1199637': [SKIP],
......@@ -907,8 +905,6 @@
'regress/regress-crbug-135008': [SKIP],
'regress/regress-crbug-259300': [SKIP],
'regress/regress-crbug-352058': [SKIP],
'regress/regress-crbug-357137': [SKIP],
'regress/regress-crbug-385002': [SKIP],
'regress/regress-crbug-387599': [SKIP],
'regress/regress-crbug-405517': [SKIP],
'regress/regress-crbug-405922': [SKIP],
......
......@@ -164,9 +164,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeLabel start;
builder.Bind(&start);
// Short jumps with Imm8 operands
builder.Jump(&start)
.JumpIfNull(&start)
.JumpIfUndefined(&start);
builder.Jump(&start).JumpIfNull(&start).JumpIfUndefined(&start);
// Perform an operation that returns boolean value to
// generate JumpIfTrue/False
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
......@@ -184,9 +182,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.LoadTrue();
}
// Longer jumps requiring Constant operand
builder.Jump(&start)
.JumpIfNull(&start)
.JumpIfUndefined(&start);
builder.Jump(&start).JumpIfNull(&start).JumpIfUndefined(&start);
// Perform an operation that returns boolean value to
// generate JumpIfTrue/False
builder.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
......@@ -285,7 +281,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Generate BytecodeArray.
Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
CHECK_EQ(the_array->frame_size(),
builder.fixed_register_count() * kPointerSize);
(builder.fixed_and_temporary_register_count() +
builder.translation_register_count()) *
kPointerSize);
// Build scorecard of bytecodes encountered in the BytecodeArray.
std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
......@@ -687,7 +685,6 @@ TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
CHECK(iterator.done());
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -9,16 +9,37 @@
#include "src/interpreter/bytecodes.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
TEST(OperandConversion, Registers) {
for (int i = 0; i < 128; i++) {
uint8_t operand_value = Register(i).ToOperand();
Register r = Register::FromOperand(operand_value);
CHECK_EQ(i, r.index());
int register_count = Register::MaxRegisterIndex() + 1;
int step = register_count / 7;
for (int i = 0; i < register_count; i += step) {
if (i <= kMaxInt8) {
uint8_t operand0 = Register(i).ToOperand();
Register reg0 = Register::FromOperand(operand0);
CHECK_EQ(i, reg0.index());
}
uint16_t operand1 = Register(i).ToWideOperand();
Register reg1 = Register::FromWideOperand(operand1);
CHECK_EQ(i, reg1.index());
uint32_t operand2 = Register(i).ToRawOperand();
Register reg2 = Register::FromRawOperand(operand2);
CHECK_EQ(i, reg2.index());
}
for (int i = 0; i <= kMaxUInt8; i++) {
uint8_t operand = static_cast<uint8_t>(i);
Register reg = Register::FromOperand(operand);
if (i > 0 && i < -kMinInt8) {
CHECK(reg.is_parameter());
} else {
CHECK(!reg.is_parameter());
}
}
}
......@@ -40,24 +61,63 @@ TEST(OperandConversion, Parameters) {
TEST(OperandConversion, RegistersParametersNoOverlap) {
std::vector<uint8_t> operand_count(256);
int register_count = Register::MaxRegisterIndex() + 1;
int parameter_count = Register::MaxParameterIndex() + 1;
int32_t register_space_size = base::bits::RoundUpToPowerOfTwo32(
static_cast<uint32_t>(register_count + parameter_count));
uint32_t range = static_cast<uint32_t>(register_space_size);
std::vector<uint8_t> operand_count(range);
for (int i = 0; i <= kMaxInt8; i++) {
for (int i = 0; i < register_count; i += 1) {
Register r = Register(i);
uint8_t operand = r.ToOperand();
uint32_t operand = r.ToWideOperand();
CHECK_LT(operand, operand_count.size());
operand_count[operand] += 1;
CHECK_EQ(operand_count[operand], 1);
}
int parameter_count = Register::MaxParameterIndex() + 1;
for (int i = 0; i < parameter_count; i++) {
for (int i = 0; i < parameter_count; i += 1) {
Register r = Register::FromParameterIndex(i, parameter_count);
uint8_t operand = r.ToOperand();
uint32_t operand = r.ToWideOperand();
CHECK_LT(operand, operand_count.size());
operand_count[operand] += 1;
CHECK_EQ(operand_count[operand], 1);
}
}
TEST(Bytecodes, HasAnyRegisterOperands) {
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kAdd), 1);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kCall), 2);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kCallRuntime), 1);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kCallRuntimeWide), 1);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kCallRuntimeForPair),
2);
CHECK_EQ(
Bytecodes::NumberOfRegisterOperands(Bytecode::kCallRuntimeForPairWide),
2);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kDeletePropertyStrict),
1);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kDeleteLookupSlot), 0);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kForInPrepare), 1);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kForInPrepareWide), 1);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kInc), 0);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kJumpIfTrue), 0);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kNew), 2);
CHECK_EQ(Bytecodes::NumberOfRegisterOperands(Bytecode::kToName), 0);
}
TEST(Bytecodes, RegisterOperandBitmaps) {
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kAdd), 1);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kCallRuntimeForPair),
10);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kStar), 1);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kMov), 3);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kTestIn), 1);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kForInPrepare), 1);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kForInDone), 3);
CHECK_EQ(Bytecodes::GetRegisterOperandBitmap(Bytecode::kForInNext), 7);
}
} // namespace interpreter
} // namespace internal
} // namespace v8
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stack>
#include "src/v8.h"
#include "src/interpreter/register-translator.h"
#include "src/isolate.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
class RegisterTranslatorTest : public TestWithIsolateAndZone,
private RegisterMover {
public:
RegisterTranslatorTest() : translator_(this), move_count_(0) {
window_start_ =
RegisterTranslator::DistanceToTranslationWindow(Register(0));
window_width_ =
Register::MaxRegisterIndexForByteOperand() - window_start_ + 1;
}
~RegisterTranslatorTest() override {}
bool PopMoveAndMatch(Register from, Register to) {
CHECK(from.is_valid() && to.is_valid());
const std::pair<Register, Register> top = moves_.top();
moves_.pop();
return top.first == from && top.second == to;
}
int move_count() const { return move_count_; }
RegisterTranslator* translator() { return &translator_; }
int window_start() const { return window_start_; }
int window_width() const { return window_width_; }
int window_limit() const { return window_start_ + window_width_; }
protected:
static const char* const kBadOperandRegex;
private:
void MoveRegisterUntranslated(Register from, Register to) override {
moves_.push(std::make_pair(from, to));
move_count_++;
}
bool RegisterOperandIsMovable(Bytecode bytecode, int operand_index) override {
OperandType operand_type =
Bytecodes::GetOperandType(bytecode, operand_index);
if (operand_type == OperandType::kReg8 ||
operand_type == OperandType::kReg16) {
if (operand_index == Bytecodes::NumberOfOperands(bytecode) - 1) {
return true;
}
OperandType next_operand_type =
Bytecodes::GetOperandType(bytecode, operand_index + 1);
return (next_operand_type != OperandType::kRegCount8 &&
next_operand_type != OperandType::kRegCount16);
} else {
return false;
}
}
RegisterTranslator translator_;
std::stack<std::pair<Register, Register>> moves_;
int move_count_;
int window_start_;
int window_width_;
};
const char* const RegisterTranslatorTest::kBadOperandRegex =
".*OperandType::kReg8 && mover\\(\\)->RegisterOperandIsMovable.*";
TEST_F(RegisterTranslatorTest, TestFrameSizeAdjustmentsForTranslationWindow) {
EXPECT_EQ(0, RegisterTranslator::RegisterCountAdjustment(0, 0));
EXPECT_EQ(0, RegisterTranslator::RegisterCountAdjustment(10, 10));
EXPECT_EQ(window_width(),
RegisterTranslator::RegisterCountAdjustment(173, 0));
EXPECT_EQ(window_width(),
RegisterTranslator::RegisterCountAdjustment(173, 137));
EXPECT_EQ(window_width(),
RegisterTranslator::RegisterCountAdjustment(173, 137));
EXPECT_EQ(0, RegisterTranslator::RegisterCountAdjustment(0, 120));
EXPECT_EQ(window_limit(),
RegisterTranslator::RegisterCountAdjustment(0, 128));
EXPECT_EQ(window_limit(),
RegisterTranslator::RegisterCountAdjustment(0, 129));
EXPECT_EQ(window_limit() - 32,
RegisterTranslator::RegisterCountAdjustment(32, 129));
}
TEST_F(RegisterTranslatorTest, TestInTranslationWindow) {
EXPECT_GE(window_start(), 0);
EXPECT_FALSE(
RegisterTranslator::InTranslationWindow(Register(window_start() - 1)));
EXPECT_TRUE(RegisterTranslator::InTranslationWindow(
Register(Register::MaxRegisterIndexForByteOperand())));
EXPECT_FALSE(RegisterTranslator::InTranslationWindow(
Register(Register::MaxRegisterIndexForByteOperand() + 1)));
for (int index = window_start(); index < window_limit(); index += 1) {
EXPECT_TRUE(RegisterTranslator::InTranslationWindow(Register(index)));
}
}
TEST_F(RegisterTranslatorTest, FitsInReg8Operand) {
EXPECT_GT(window_start(), 0);
EXPECT_TRUE(RegisterTranslator::FitsInReg8Operand(
Register::FromParameterIndex(0, 3)));
EXPECT_TRUE(RegisterTranslator::FitsInReg8Operand(
Register::FromParameterIndex(2, 3)));
EXPECT_TRUE(RegisterTranslator::FitsInReg8Operand(Register(0)));
EXPECT_TRUE(
RegisterTranslator::FitsInReg8Operand(Register(window_start() - 1)));
EXPECT_FALSE(RegisterTranslator::FitsInReg8Operand(Register(kMaxInt8)));
EXPECT_FALSE(RegisterTranslator::FitsInReg8Operand(Register(kMaxInt8 + 1)));
for (int index = window_start(); index < window_limit(); index += 1) {
EXPECT_FALSE(RegisterTranslator::FitsInReg8Operand(Register(index)));
}
}
TEST_F(RegisterTranslatorTest, FitsInReg16Operand) {
EXPECT_GT(window_start(), 0);
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(
Register::FromParameterIndex(0, 3)));
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(
Register::FromParameterIndex(2, 3)));
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(
Register::FromParameterIndex(0, 999)));
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(
Register::FromParameterIndex(0, Register::MaxParameterIndex() + 1)));
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(Register(0)));
EXPECT_TRUE(
RegisterTranslator::FitsInReg16Operand(Register(window_start() - 1)));
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(Register(kMaxInt8 + 1)));
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(Register(kMaxInt8 + 2)));
for (int index = 0; index <= kMaxInt16 - window_width(); index += 1) {
EXPECT_TRUE(RegisterTranslator::FitsInReg16Operand(Register(index)));
}
for (int index = Register::MaxRegisterIndex() - window_width() + 1;
index < Register::MaxRegisterIndex() + 2; index += 1) {
EXPECT_FALSE(RegisterTranslator::FitsInReg16Operand(Register(index)));
}
}
TEST_F(RegisterTranslatorTest, NoTranslationRequired) {
Register window_reg(window_start());
Register local_reg(57);
uint32_t operands[] = {local_reg.ToRawOperand()};
translator()->TranslateInputRegisters(Bytecode::kLdar, operands, 1);
translator()->TranslateOutputRegisters();
EXPECT_EQ(0, move_count());
Register param_reg = Register::FromParameterIndex(129, 130);
operands[0] = param_reg.ToRawOperand();
translator()->TranslateInputRegisters(Bytecode::kLdar, operands, 1);
translator()->TranslateOutputRegisters();
EXPECT_EQ(0, move_count());
}
TEST_F(RegisterTranslatorTest, TranslationRequired) {
Register window_reg(window_start());
Register local_reg(137);
Register local_reg_translated(local_reg.index() + window_width());
uint32_t operands[] = {local_reg.ToRawOperand()};
translator()->TranslateInputRegisters(Bytecode::kLdar, operands, 1);
EXPECT_EQ(1, move_count());
EXPECT_TRUE(PopMoveAndMatch(local_reg_translated, window_reg));
translator()->TranslateOutputRegisters();
EXPECT_EQ(2, move_count());
EXPECT_TRUE(PopMoveAndMatch(window_reg, local_reg_translated));
Register param_reg = Register::FromParameterIndex(0, 130);
operands[0] = {param_reg.ToRawOperand()};
translator()->TranslateInputRegisters(Bytecode::kLdar, operands, 1);
EXPECT_EQ(3, move_count());
EXPECT_TRUE(PopMoveAndMatch(param_reg, window_reg));
translator()->TranslateOutputRegisters();
EXPECT_EQ(4, move_count());
EXPECT_TRUE(PopMoveAndMatch(window_reg, param_reg));
}
TEST_F(RegisterTranslatorTest, RangeTranslation) {
Register window0(window_start());
Register window1(window_start() + 1);
Register window2(window_start() + 2);
uint32_t operands[3];
// Bytecode::kNew with valid range operand.
Register constructor0(0);
Register args0(1);
operands[0] = constructor0.ToRawOperand();
operands[1] = args0.ToRawOperand();
operands[2] = 1;
translator()->TranslateInputRegisters(Bytecode::kNew, operands, 3);
translator()->TranslateOutputRegisters();
EXPECT_EQ(0, move_count());
// Bytecode::kNewWide with valid range operand.
Register constructor1(128);
Register constructor1_translated(constructor1.index() + window_width());
Register args1(129);
Register args1_translated(args1.index() + window_width());
operands[0] = constructor1.ToRawOperand();
operands[1] = args1.ToRawOperand();
operands[2] = 3;
translator()->TranslateInputRegisters(Bytecode::kNewWide, operands, 3);
translator()->TranslateOutputRegisters();
EXPECT_EQ(0, move_count());
}
TEST_F(RegisterTranslatorTest, BadRange0) {
// Bytecode::kNew with invalid range operand (kMaybeReg8).
Register constructor1(128);
Register args1(129);
uint32_t operands[] = {constructor1.ToRawOperand(), args1.ToRawOperand(), 3};
ASSERT_DEATH_IF_SUPPORTED(
translator()->TranslateInputRegisters(Bytecode::kNew, operands, 3),
kBadOperandRegex);
}
TEST_F(RegisterTranslatorTest, BadRange1) {
// Bytecode::kForInPrepare with invalid range operand (kRegTriple8)
Register for_in_state(160);
Register for_in_state_translated(for_in_state.index() + window_width());
uint32_t operands[] = {for_in_state.ToRawOperand()};
ASSERT_DEATH_IF_SUPPORTED(translator()->TranslateInputRegisters(
Bytecode::kForInPrepare, operands, 1),
kBadOperandRegex);
}
TEST_F(RegisterTranslatorTest, BadRange2) {
// Bytecode::kForInNext with invalid range operand (kRegPair8)
Register receiver(192);
Register receiver_translated(receiver.index() + window_width());
Register index(193);
Register index_translated(index.index() + window_width());
Register cache_info_pair(194);
Register cache_info_pair_translated(cache_info_pair.index() + window_width());
uint32_t operands[] = {receiver.ToRawOperand(), index.ToRawOperand(),
cache_info_pair.ToRawOperand()};
ASSERT_DEATH_IF_SUPPORTED(
translator()->TranslateInputRegisters(Bytecode::kForInNext, operands, 3),
kBadOperandRegex);
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -99,6 +99,7 @@
'interpreter/bytecode-array-iterator-unittest.cc',
'interpreter/bytecode-register-allocator-unittest.cc',
'interpreter/constant-array-builder-unittest.cc',
'interpreter/register-translator-unittest.cc',
'libplatform/default-platform-unittest.cc',
'libplatform/task-queue-unittest.cc',
'libplatform/worker-thread-unittest.cc',
......
......@@ -877,6 +877,8 @@
'../../src/interpreter/handler-table-builder.h',
'../../src/interpreter/interpreter.cc',
'../../src/interpreter/interpreter.h',
'../../src/interpreter/register-translator.cc',
'../../src/interpreter/register-translator.h',
'../../src/isolate-inl.h',
'../../src/isolate.cc',
'../../src/isolate.h',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment