[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:
oth 2016-01-04 09:38:17 -08:00 committed by Commit bot
parent 5b4626ad19
commit c958c98c4f
15 changed files with 373 additions and 31 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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()),

View File

@ -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,

View File

@ -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);
}

View File

@ -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()) {

View File

@ -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(),

View File

@ -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>.

View File

@ -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};

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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;