[Interpreter] Bytecodes for exchanging registers.
New bytecodes for making registers with indicies wider than 1-byte accessible. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1555713002 Cr-Commit-Position: refs/heads/master@{#33091}
This commit is contained in:
parent
5b4626ad19
commit
c958c98c4f
@ -164,12 +164,23 @@ void BytecodeGraphBuilder::Environment::BindAccumulator(
|
||||
values()->at(accumulator_base_) = node;
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::Environment::RecordAfterState(
|
||||
Node* node, FrameStateBeforeAndAfter* states) {
|
||||
states->AddToNode(node, AccumulatorUpdateMode::kOutputIgnored);
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::Environment::ExchangeRegisters(
|
||||
interpreter::Register reg0, interpreter::Register reg1) {
|
||||
int reg0_index = RegisterToValuesIndex(reg0);
|
||||
int reg1_index = RegisterToValuesIndex(reg1);
|
||||
Node* saved_reg0_value = values()->at(reg0_index);
|
||||
values()->at(reg0_index) = values()->at(reg1_index);
|
||||
values()->at(reg1_index) = saved_reg0_value;
|
||||
}
|
||||
|
||||
|
||||
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const {
|
||||
return values()->at(accumulator_base_);
|
||||
}
|
||||
@ -561,6 +572,20 @@ void BytecodeGraphBuilder::VisitMov(
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitExchange(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
environment()->ExchangeRegisters(iterator.GetRegisterOperand(0),
|
||||
iterator.GetRegisterOperand(1));
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitExchangeWide(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
environment()->ExchangeRegisters(iterator.GetRegisterOperand(0),
|
||||
iterator.GetRegisterOperand(1));
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::BuildLoadGlobal(
|
||||
const interpreter::BytecodeArrayIterator& iterator,
|
||||
TypeofMode typeof_mode) {
|
||||
|
@ -267,6 +267,8 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
||||
|
||||
void BindRegister(interpreter::Register the_register, Node* node);
|
||||
Node* LookupRegister(interpreter::Register the_register) const;
|
||||
void ExchangeRegisters(interpreter::Register reg0,
|
||||
interpreter::Register reg1);
|
||||
|
||||
void BindAccumulator(Node* node, FrameStateBeforeAndAfter* states = nullptr);
|
||||
Node* LookupAccumulator() const;
|
||||
|
@ -209,6 +209,46 @@ Node* InterpreterAssembler::BytecodeOperandShort(int operand_index) {
|
||||
}
|
||||
|
||||
|
||||
Node* InterpreterAssembler::BytecodeOperandShortSignExtended(
|
||||
int operand_index) {
|
||||
DCHECK_LT(operand_index, interpreter::Bytecodes::NumberOfOperands(bytecode_));
|
||||
DCHECK_EQ(interpreter::OperandSize::kShort,
|
||||
interpreter::Bytecodes::GetOperandSize(bytecode_, operand_index));
|
||||
int operand_offset =
|
||||
interpreter::Bytecodes::GetOperandOffset(bytecode_, operand_index);
|
||||
Node* load;
|
||||
if (TargetSupportsUnalignedAccess()) {
|
||||
load = raw_assembler_->Load(
|
||||
MachineType::Int16(), BytecodeArrayTaggedPointer(),
|
||||
IntPtrAdd(BytecodeOffset(), Int32Constant(operand_offset)));
|
||||
} else {
|
||||
#if V8_TARGET_LITTLE_ENDIAN
|
||||
Node* hi_byte_offset = Int32Constant(operand_offset + 1);
|
||||
Node* lo_byte_offset = Int32Constant(operand_offset);
|
||||
#elif V8_TARGET_BIG_ENDIAN
|
||||
Node* hi_byte_offset = Int32Constant(operand_offset);
|
||||
Node* lo_byte_offset = Int32Constant(operand_offset + 1);
|
||||
#else
|
||||
#error "Unknown Architecture"
|
||||
#endif
|
||||
Node* hi_byte =
|
||||
raw_assembler_->Load(MachineType::Int8(), BytecodeArrayTaggedPointer(),
|
||||
IntPtrAdd(BytecodeOffset(), hi_byte_offset));
|
||||
Node* lo_byte =
|
||||
raw_assembler_->Load(MachineType::Uint8(), BytecodeArrayTaggedPointer(),
|
||||
IntPtrAdd(BytecodeOffset(), lo_byte_offset));
|
||||
hi_byte = raw_assembler_->Word32Shl(hi_byte, Int32Constant(kBitsPerByte));
|
||||
load = raw_assembler_->Word32Or(hi_byte, lo_byte);
|
||||
}
|
||||
|
||||
// Ensure that we sign extend to full pointer size
|
||||
if (kPointerSize == 8) {
|
||||
load = raw_assembler_->ChangeInt32ToInt64(load);
|
||||
}
|
||||
return load;
|
||||
}
|
||||
|
||||
|
||||
Node* InterpreterAssembler::BytecodeOperandCount(int operand_index) {
|
||||
switch (interpreter::Bytecodes::GetOperandSize(bytecode_, operand_index)) {
|
||||
case interpreter::OperandSize::kByte:
|
||||
@ -255,13 +295,22 @@ Node* InterpreterAssembler::BytecodeOperandIdx(int operand_index) {
|
||||
|
||||
|
||||
Node* InterpreterAssembler::BytecodeOperandReg(int operand_index) {
|
||||
#ifdef DEBUG
|
||||
interpreter::OperandType operand_type =
|
||||
interpreter::Bytecodes::GetOperandType(bytecode_, operand_index);
|
||||
DCHECK(operand_type == interpreter::OperandType::kReg8 ||
|
||||
operand_type == interpreter::OperandType::kMaybeReg8);
|
||||
#endif
|
||||
switch (interpreter::Bytecodes::GetOperandType(bytecode_, operand_index)) {
|
||||
case interpreter::OperandType::kReg8:
|
||||
case interpreter::OperandType::kMaybeReg8:
|
||||
DCHECK_EQ(
|
||||
interpreter::OperandSize::kByte,
|
||||
interpreter::Bytecodes::GetOperandSize(bytecode_, operand_index));
|
||||
return BytecodeOperandSignExtended(operand_index);
|
||||
case interpreter::OperandType::kReg16:
|
||||
DCHECK_EQ(
|
||||
interpreter::OperandSize::kShort,
|
||||
interpreter::Bytecodes::GetOperandSize(bytecode_, operand_index));
|
||||
return BytecodeOperandShortSignExtended(operand_index);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -181,6 +181,7 @@ class InterpreterAssembler {
|
||||
Node* BytecodeOperand(int operand_index);
|
||||
Node* BytecodeOperandSignExtended(int operand_index);
|
||||
Node* BytecodeOperandShort(int operand_index);
|
||||
Node* BytecodeOperandShortSignExtended(int operand_index);
|
||||
|
||||
Node* CallN(CallDescriptor* descriptor, Node* code_target, Node** args);
|
||||
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node** args);
|
||||
|
@ -379,6 +379,20 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::ExchangeRegisters(Register reg0,
|
||||
Register reg1) {
|
||||
DCHECK(reg0 != reg1);
|
||||
if (FitsInReg8Operand(reg0)) {
|
||||
Output(Bytecode::kExchange, reg0.ToOperand(), reg1.ToWideOperand());
|
||||
} else if (FitsInReg8Operand(reg1)) {
|
||||
Output(Bytecode::kExchange, reg1.ToOperand(), reg0.ToWideOperand());
|
||||
} else {
|
||||
Output(Bytecode::kExchangeWide, reg0.ToWideOperand(), reg1.ToWideOperand());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(
|
||||
const Handle<String> name, int feedback_slot, LanguageMode language_mode,
|
||||
TypeofMode typeof_mode) {
|
||||
@ -1163,6 +1177,24 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
|
||||
return TemporaryRegisterIsLive(reg);
|
||||
}
|
||||
}
|
||||
case OperandType::kReg16: {
|
||||
if (bytecode != Bytecode::kExchange &&
|
||||
bytecode != Bytecode::kExchangeWide) {
|
||||
return false;
|
||||
}
|
||||
Register reg =
|
||||
Register::FromWideOperand(static_cast<uint16_t>(operand_value));
|
||||
if (reg.is_function_context() || reg.is_function_closure() ||
|
||||
reg.is_new_target()) {
|
||||
return false;
|
||||
} else if (reg.is_parameter()) {
|
||||
return false;
|
||||
} else if (reg.index() < fixed_register_count()) {
|
||||
return true;
|
||||
} else {
|
||||
return TemporaryRegisterIsLive(reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
@ -1468,7 +1500,7 @@ bool BytecodeArrayBuilder::FitsInIdx8Operand(size_t value) {
|
||||
|
||||
// static
|
||||
bool BytecodeArrayBuilder::FitsInImm8Operand(int value) {
|
||||
return kMinInt8 <= value && value < kMaxInt8;
|
||||
return kMinInt8 <= value && value <= kMaxInt8;
|
||||
}
|
||||
|
||||
|
||||
@ -1484,6 +1516,18 @@ bool BytecodeArrayBuilder::FitsInIdx16Operand(size_t value) {
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
bool BytecodeArrayBuilder::FitsInReg8Operand(Register value) {
|
||||
return kMinInt8 <= value.index() && value.index() <= kMaxInt8;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
bool BytecodeArrayBuilder::FitsInReg16Operand(Register value) {
|
||||
return kMinInt16 <= value.index() && value.index() <= kMaxInt16;
|
||||
}
|
||||
|
||||
|
||||
TemporaryRegisterScope::TemporaryRegisterScope(BytecodeArrayBuilder* builder)
|
||||
: builder_(builder),
|
||||
allocated_(builder->zone()),
|
||||
|
@ -100,6 +100,7 @@ class BytecodeArrayBuilder final {
|
||||
|
||||
// Register-register transfer.
|
||||
BytecodeArrayBuilder& MoveRegister(Register from, Register to);
|
||||
BytecodeArrayBuilder& ExchangeRegisters(Register reg0, Register reg1);
|
||||
|
||||
// Named load property.
|
||||
BytecodeArrayBuilder& LoadNamedProperty(Register object,
|
||||
@ -249,12 +250,17 @@ class BytecodeArrayBuilder final {
|
||||
static bool FitsInImm8Operand(int value);
|
||||
static bool FitsInIdx16Operand(int value);
|
||||
static bool FitsInIdx16Operand(size_t value);
|
||||
static bool FitsInReg8Operand(Register value);
|
||||
static bool FitsInReg16Operand(Register value);
|
||||
|
||||
static Bytecode GetJumpWithConstantOperand(Bytecode jump_with_smi8_operand);
|
||||
static Bytecode GetJumpWithToBoolean(Bytecode jump);
|
||||
|
||||
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(&oprands)[N]));
|
||||
INLINE(void Output(Bytecode bytecode, uint32_t(&operands)[N]));
|
||||
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
|
||||
uint32_t operand2, uint32_t operand3);
|
||||
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
|
||||
|
@ -75,11 +75,11 @@ int BytecodeArrayIterator::GetCountOperand(int operand_index) const {
|
||||
|
||||
|
||||
int BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
|
||||
OperandSize size =
|
||||
Bytecodes::GetOperandSize(current_bytecode(), operand_index);
|
||||
OperandType type =
|
||||
(size == OperandSize::kByte) ? OperandType::kIdx8 : OperandType::kIdx16;
|
||||
uint32_t operand = GetRawOperand(operand_index, type);
|
||||
OperandType operand_type =
|
||||
Bytecodes::GetOperandType(current_bytecode(), operand_index);
|
||||
DCHECK(operand_type == OperandType::kIdx8 ||
|
||||
operand_type == OperandType::kIdx16);
|
||||
uint32_t operand = GetRawOperand(operand_index, operand_type);
|
||||
return static_cast<int>(operand);
|
||||
}
|
||||
|
||||
@ -88,7 +88,8 @@ Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
|
||||
OperandType operand_type =
|
||||
Bytecodes::GetOperandType(current_bytecode(), operand_index);
|
||||
DCHECK(operand_type == OperandType::kReg8 ||
|
||||
operand_type == OperandType::kMaybeReg8);
|
||||
operand_type == OperandType::kMaybeReg8 ||
|
||||
operand_type == OperandType::kReg16);
|
||||
uint32_t operand = GetRawOperand(operand_index, operand_type);
|
||||
return Register::FromOperand(operand);
|
||||
}
|
||||
|
@ -273,6 +273,18 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case interpreter::OperandType::kReg16: {
|
||||
Register reg =
|
||||
Register::FromWideOperand(ReadUnalignedUInt16(operand_start));
|
||||
if (reg.is_parameter()) {
|
||||
int parameter_index = reg.ToParameterIndex(parameter_count);
|
||||
DCHECK_NE(parameter_index, 0);
|
||||
os << "a" << parameter_index - 1;
|
||||
} else {
|
||||
os << "r" << reg.index();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case interpreter::OperandType::kNone:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@ -322,7 +334,7 @@ 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, Register::kMinRegisterIndex);
|
||||
DCHECK_GE(register_index, kMinInt8);
|
||||
return Register(register_index);
|
||||
}
|
||||
|
||||
@ -364,7 +376,11 @@ bool Register::is_new_target() const {
|
||||
int Register::MaxParameterIndex() { return kMaxParameterIndex; }
|
||||
|
||||
|
||||
uint8_t Register::ToOperand() const { return static_cast<uint8_t>(-index_); }
|
||||
uint8_t Register::ToOperand() const {
|
||||
DCHECK_GE(index_, kMinInt8);
|
||||
DCHECK_LE(index_, kMaxInt8);
|
||||
return static_cast<uint8_t>(-index_);
|
||||
}
|
||||
|
||||
|
||||
Register Register::FromOperand(uint8_t operand) {
|
||||
@ -372,6 +388,18 @@ Register Register::FromOperand(uint8_t operand) {
|
||||
}
|
||||
|
||||
|
||||
uint16_t Register::ToWideOperand() const {
|
||||
DCHECK_GE(index_, kMinInt16);
|
||||
DCHECK_LE(index_, kMaxInt16);
|
||||
return static_cast<uint16_t>(-index_);
|
||||
}
|
||||
|
||||
|
||||
Register Register::FromWideOperand(uint16_t operand) {
|
||||
return Register(-static_cast<int16_t>(operand));
|
||||
}
|
||||
|
||||
|
||||
bool Register::AreContiguous(Register reg1, Register reg2, Register reg3,
|
||||
Register reg4, Register reg5) {
|
||||
if (reg1.index() + 1 != reg2.index()) {
|
||||
|
@ -25,12 +25,13 @@ namespace interpreter {
|
||||
V(Count8, OperandSize::kByte) \
|
||||
V(Imm8, OperandSize::kByte) \
|
||||
V(Idx8, OperandSize::kByte) \
|
||||
V(Reg8, OperandSize::kByte) \
|
||||
V(MaybeReg8, OperandSize::kByte) \
|
||||
V(Reg8, OperandSize::kByte) \
|
||||
\
|
||||
/* Short operands. */ \
|
||||
V(Count16, OperandSize::kShort) \
|
||||
V(Idx16, OperandSize::kShort)
|
||||
V(Idx16, OperandSize::kShort) \
|
||||
V(Reg16, OperandSize::kShort)
|
||||
|
||||
// The list of bytecodes which are interpreted by the interpreter.
|
||||
#define BYTECODE_LIST(V) \
|
||||
@ -78,6 +79,8 @@ namespace interpreter {
|
||||
\
|
||||
/* Register-register transfers */ \
|
||||
V(Mov, OperandType::kReg8, OperandType::kReg8) \
|
||||
V(Exchange, OperandType::kReg8, OperandType::kReg16) \
|
||||
V(ExchangeWide, OperandType::kReg16, OperandType::kReg16) \
|
||||
\
|
||||
/* LoadIC operations */ \
|
||||
V(LoadICSloppy, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \
|
||||
@ -249,15 +252,9 @@ enum class Bytecode : uint8_t {
|
||||
// in its stack-frame. Register hold parameters, this, and expression values.
|
||||
class Register {
|
||||
public:
|
||||
static const int kMaxRegisterIndex = 127;
|
||||
static const int kMinRegisterIndex = -128;
|
||||
|
||||
Register() : index_(kIllegalIndex) {}
|
||||
|
||||
explicit Register(int index) : index_(index) {
|
||||
DCHECK_LE(index_, kMaxRegisterIndex);
|
||||
DCHECK_GE(index_, kMinRegisterIndex);
|
||||
}
|
||||
explicit Register(int index) : index_(index) {}
|
||||
|
||||
int index() const {
|
||||
DCHECK(index_ != kIllegalIndex);
|
||||
@ -285,6 +282,9 @@ class Register {
|
||||
static Register FromOperand(uint8_t operand);
|
||||
uint8_t ToOperand() const;
|
||||
|
||||
static Register FromWideOperand(uint16_t operand);
|
||||
uint16_t ToWideOperand() const;
|
||||
|
||||
static bool AreContiguous(Register reg1, Register reg2,
|
||||
Register reg3 = Register(),
|
||||
Register reg4 = Register(),
|
||||
|
@ -201,6 +201,28 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
|
||||
}
|
||||
|
||||
|
||||
// Exchange <reg8> <reg16>
|
||||
//
|
||||
// Exchange two registers.
|
||||
void Interpreter::DoExchange(compiler::InterpreterAssembler* assembler) {
|
||||
Node* reg0_index = __ BytecodeOperandReg(0);
|
||||
Node* reg1_index = __ BytecodeOperandReg(1);
|
||||
Node* reg0_value = __ LoadRegister(reg0_index);
|
||||
Node* reg1_value = __ LoadRegister(reg1_index);
|
||||
__ StoreRegister(reg1_value, reg0_index);
|
||||
__ StoreRegister(reg0_value, reg1_index);
|
||||
__ Dispatch();
|
||||
}
|
||||
|
||||
|
||||
// ExchangeWide <reg16> <reg16>
|
||||
//
|
||||
// Exchange two registers.
|
||||
void Interpreter::DoExchangeWide(compiler::InterpreterAssembler* assembler) {
|
||||
return DoExchange(assembler);
|
||||
}
|
||||
|
||||
|
||||
// Mov <src> <dst>
|
||||
//
|
||||
// Stores the value of register <src> to register <dst>.
|
||||
|
@ -340,7 +340,7 @@ TEST(InterpreterLoadLiteral) {
|
||||
TEST(InterpreterLoadStoreRegisters) {
|
||||
HandleAndZoneScope handles;
|
||||
Handle<Object> true_value = handles.main_isolate()->factory()->true_value();
|
||||
for (int i = 0; i <= Register::kMaxRegisterIndex; i++) {
|
||||
for (int i = 0; i <= kMaxInt8; i++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
builder.set_locals_count(i + 1);
|
||||
builder.set_context_count(0);
|
||||
@ -361,6 +361,117 @@ TEST(InterpreterLoadStoreRegisters) {
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterExchangeRegisters) {
|
||||
for (int locals_count = 2; locals_count < 300; locals_count += 126) {
|
||||
HandleAndZoneScope handles;
|
||||
for (int exchanges = 1; exchanges < 4; exchanges++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
builder.set_locals_count(locals_count);
|
||||
builder.set_context_count(0);
|
||||
builder.set_parameter_count(0);
|
||||
|
||||
Register r0(0);
|
||||
Register r1(locals_count - 1);
|
||||
builder.LoadTrue();
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
builder.ExchangeRegisters(r0, r1);
|
||||
builder.LoadFalse();
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
|
||||
bool expected = false;
|
||||
for (int i = 0; i < exchanges; i++) {
|
||||
builder.ExchangeRegisters(r0, r1);
|
||||
expected = !expected;
|
||||
}
|
||||
builder.LoadAccumulatorWithRegister(r0);
|
||||
builder.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||
auto callable = tester.GetCallable<>();
|
||||
Handle<Object> return_val = callable().ToHandleChecked();
|
||||
Handle<Object> expected_val =
|
||||
handles.main_isolate()->factory()->ToBoolean(expected);
|
||||
CHECK(return_val.is_identical_to(expected_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterExchangeRegistersWithParameter) {
|
||||
for (int locals_count = 2; locals_count < 300; locals_count += 126) {
|
||||
HandleAndZoneScope handles;
|
||||
for (int exchanges = 1; exchanges < 4; exchanges++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
builder.set_locals_count(locals_count);
|
||||
builder.set_context_count(0);
|
||||
builder.set_parameter_count(3);
|
||||
|
||||
Register r0 = Register::FromParameterIndex(2, 3);
|
||||
Register r1(locals_count - 1);
|
||||
builder.LoadTrue();
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
builder.ExchangeRegisters(r0, r1);
|
||||
builder.LoadFalse();
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
|
||||
bool expected = false;
|
||||
for (int i = 0; i < exchanges; i++) {
|
||||
builder.ExchangeRegisters(r0, r1);
|
||||
expected = !expected;
|
||||
}
|
||||
builder.LoadAccumulatorWithRegister(r0);
|
||||
builder.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||
auto callable = tester.GetCallable<>();
|
||||
Handle<Object> return_val = callable().ToHandleChecked();
|
||||
Handle<Object> expected_val =
|
||||
handles.main_isolate()->factory()->ToBoolean(expected);
|
||||
CHECK(return_val.is_identical_to(expected_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(InterpreterExchangeWideRegisters) {
|
||||
for (int locals_count = 3; locals_count < 300; locals_count += 126) {
|
||||
HandleAndZoneScope handles;
|
||||
for (int exchanges = 0; exchanges < 7; exchanges++) {
|
||||
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
|
||||
builder.set_locals_count(locals_count);
|
||||
builder.set_context_count(0);
|
||||
builder.set_parameter_count(0);
|
||||
|
||||
Register r0(0);
|
||||
Register r1(locals_count - 1);
|
||||
Register r2(locals_count - 2);
|
||||
builder.LoadLiteral(Smi::FromInt(200));
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
builder.ExchangeRegisters(r0, r1);
|
||||
builder.LoadLiteral(Smi::FromInt(100));
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
builder.ExchangeRegisters(r0, r2);
|
||||
builder.LoadLiteral(Smi::FromInt(0));
|
||||
builder.StoreAccumulatorInRegister(r0);
|
||||
for (int i = 0; i < exchanges; i++) {
|
||||
builder.ExchangeRegisters(r1, r2);
|
||||
builder.ExchangeRegisters(r0, r1);
|
||||
}
|
||||
builder.LoadAccumulatorWithRegister(r0);
|
||||
builder.Return();
|
||||
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||
auto callable = tester.GetCallable<>();
|
||||
Handle<Object> return_val = callable().ToHandleChecked();
|
||||
Handle<Object> expected_val =
|
||||
handles.main_isolate()->factory()->NewNumberFromInt(100 *
|
||||
(exchanges % 3));
|
||||
CHECK(return_val.is_identical_to(expected_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const Token::Value kShiftOperators[] = {
|
||||
Token::Value::SHL, Token::Value::SAR, Token::Value::SHR};
|
||||
|
||||
|
@ -140,6 +140,47 @@ InterpreterAssemblerTest::InterpreterAssemblerForTest::IsBytecodeOperandShort(
|
||||
}
|
||||
|
||||
|
||||
Matcher<Node*> InterpreterAssemblerTest::InterpreterAssemblerForTest::
|
||||
IsBytecodeOperandShortSignExtended(int offset) {
|
||||
Matcher<Node*> load_matcher;
|
||||
if (TargetSupportsUnalignedAccess()) {
|
||||
load_matcher = IsLoad(
|
||||
MachineType::Int16(),
|
||||
IsParameter(Linkage::kInterpreterBytecodeArrayParameter),
|
||||
IsIntPtrAdd(IsParameter(Linkage::kInterpreterBytecodeOffsetParameter),
|
||||
IsInt32Constant(offset)));
|
||||
} else {
|
||||
#if V8_TARGET_LITTLE_ENDIAN
|
||||
int hi_byte_offset = offset + 1;
|
||||
int lo_byte_offset = offset;
|
||||
|
||||
#elif V8_TARGET_BIG_ENDIAN
|
||||
int hi_byte_offset = offset;
|
||||
int lo_byte_offset = offset + 1;
|
||||
#else
|
||||
#error "Unknown Architecture"
|
||||
#endif
|
||||
Matcher<Node*> hi_byte = IsLoad(
|
||||
MachineType::Int8(),
|
||||
IsParameter(Linkage::kInterpreterBytecodeArrayParameter),
|
||||
IsIntPtrAdd(IsParameter(Linkage::kInterpreterBytecodeOffsetParameter),
|
||||
IsInt32Constant(hi_byte_offset)));
|
||||
hi_byte = IsWord32Shl(hi_byte, IsInt32Constant(kBitsPerByte));
|
||||
Matcher<Node*> lo_byte = IsLoad(
|
||||
MachineType::Uint8(),
|
||||
IsParameter(Linkage::kInterpreterBytecodeArrayParameter),
|
||||
IsIntPtrAdd(IsParameter(Linkage::kInterpreterBytecodeOffsetParameter),
|
||||
IsInt32Constant(lo_byte_offset)));
|
||||
load_matcher = IsWord32Or(hi_byte, lo_byte);
|
||||
}
|
||||
|
||||
if (kPointerSize == 8) {
|
||||
load_matcher = IsChangeInt32ToInt64(load_matcher);
|
||||
}
|
||||
return load_matcher;
|
||||
}
|
||||
|
||||
|
||||
TARGET_TEST_F(InterpreterAssemblerTest, Dispatch) {
|
||||
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
||||
InterpreterAssemblerForTest m(this, bytecode);
|
||||
@ -320,6 +361,10 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
|
||||
EXPECT_THAT(m.BytecodeOperandIdx(i),
|
||||
m.IsBytecodeOperandShort(offset));
|
||||
break;
|
||||
case interpreter::OperandType::kReg16:
|
||||
EXPECT_THAT(m.BytecodeOperandReg(i),
|
||||
m.IsBytecodeOperandShortSignExtended(offset));
|
||||
break;
|
||||
case interpreter::OperandType::kNone:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
@ -40,6 +40,7 @@ class InterpreterAssemblerTest : public TestWithIsolateAndZone {
|
||||
Matcher<Node*> IsBytecodeOperand(int offset);
|
||||
Matcher<Node*> IsBytecodeOperandSignExtended(int offset);
|
||||
Matcher<Node*> IsBytecodeOperandShort(int offset);
|
||||
Matcher<Node*> IsBytecodeOperandShortSignExtended(int offset);
|
||||
|
||||
using InterpreterAssembler::call_descriptor;
|
||||
using InterpreterAssembler::graph;
|
||||
|
@ -22,12 +22,12 @@ class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
|
||||
TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
BytecodeArrayBuilder builder(isolate(), zone());
|
||||
|
||||
builder.set_locals_count(2);
|
||||
builder.set_locals_count(200);
|
||||
builder.set_context_count(1);
|
||||
builder.set_parameter_count(0);
|
||||
CHECK_EQ(builder.locals_count(), 2);
|
||||
CHECK_EQ(builder.locals_count(), 200);
|
||||
CHECK_EQ(builder.context_count(), 1);
|
||||
CHECK_EQ(builder.fixed_register_count(), 3);
|
||||
CHECK_EQ(builder.fixed_register_count(), 201);
|
||||
|
||||
// Emit constant loads.
|
||||
builder.LoadLiteral(Smi::FromInt(0))
|
||||
@ -50,6 +50,13 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
Register other(1);
|
||||
builder.MoveRegister(reg, other);
|
||||
|
||||
// Emit register-register exchanges.
|
||||
Register wide(150);
|
||||
builder.ExchangeRegisters(reg, wide);
|
||||
builder.ExchangeRegisters(wide, reg);
|
||||
Register wider(151);
|
||||
builder.ExchangeRegisters(wide, wider);
|
||||
|
||||
// Emit global load / store operations.
|
||||
Factory* factory = isolate()->factory();
|
||||
Handle<String> name = factory->NewStringFromStaticChars("var_name");
|
||||
|
@ -42,7 +42,7 @@ TEST(OperandConversion, Parameters) {
|
||||
TEST(OperandConversion, RegistersParametersNoOverlap) {
|
||||
std::vector<uint8_t> operand_count(256);
|
||||
|
||||
for (int i = 0; i <= Register::kMaxRegisterIndex; i++) {
|
||||
for (int i = 0; i <= kMaxInt8; i++) {
|
||||
Register r = Register(i);
|
||||
uint8_t operand = r.ToOperand();
|
||||
operand_count[operand] += 1;
|
||||
|
Loading…
Reference in New Issue
Block a user