[Interpreter] Add support for jumps using constants with wide operands.

This increases the size of addressable constant pool entries for jumps
to match other bytecodes using operands indexing the constant pool.

This change also introduces reservations for constant pool entries.
Reservations are used for forward jumps to ensure a constant pool entry
will be available when the jump target (label) is bound and the jump is
patched up in the bytecode array.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#33125}
This commit is contained in:
oth 2016-01-05 11:08:11 -08:00 committed by Commit bot
parent 7ab3ccb031
commit 8109f63fd5
19 changed files with 1018 additions and 146 deletions

View File

@ -1087,6 +1087,8 @@ source_set("v8_base") {
"src/interpreter/bytecode-generator.cc",
"src/interpreter/bytecode-generator.h",
"src/interpreter/bytecode-traits.h",
"src/interpreter/constant-array-builder.cc",
"src/interpreter/constant-array-builder.h",
"src/interpreter/control-flow-builders.cc",
"src/interpreter/control-flow-builders.h",
"src/interpreter/interpreter.cc",

View File

@ -1515,87 +1515,117 @@ void BytecodeGraphBuilder::VisitJumpConstant(
}
void BytecodeGraphBuilder::VisitJumpConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJump();
}
void BytecodeGraphBuilder::VisitJumpIfTrue(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->TrueConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::VisitJumpIfTrueConstant(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->TrueConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::VisitJumpIfTrueConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJumpIfEqual(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::VisitJumpIfFalse(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->FalseConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::VisitJumpIfFalseConstant(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->FalseConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::VisitJumpIfFalseConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJumpIfEqual(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::VisitJumpIfToBooleanTrue(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildToBooleanCondition(jsgraph()->TrueConstant());
BuildConditionalJump(condition);
BuildJumpIfToBooleanEqual(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::VisitJumpIfToBooleanTrueConstant(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildToBooleanCondition(jsgraph()->TrueConstant());
BuildConditionalJump(condition);
BuildJumpIfToBooleanEqual(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::VisitJumpIfToBooleanTrueConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJumpIfToBooleanEqual(jsgraph()->TrueConstant());
}
void BytecodeGraphBuilder::VisitJumpIfToBooleanFalse(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildToBooleanCondition(jsgraph()->FalseConstant());
BuildConditionalJump(condition);
BuildJumpIfToBooleanEqual(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstant(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildToBooleanCondition(jsgraph()->FalseConstant());
BuildConditionalJump(condition);
BuildJumpIfToBooleanEqual(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJumpIfToBooleanEqual(jsgraph()->FalseConstant());
}
void BytecodeGraphBuilder::VisitJumpIfNull(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->NullConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->NullConstant());
}
void BytecodeGraphBuilder::VisitJumpIfNullConstant(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->NullConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->NullConstant());
}
void BytecodeGraphBuilder::VisitJumpIfNullConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJumpIfEqual(jsgraph()->NullConstant());
}
void BytecodeGraphBuilder::VisitJumpIfUndefined(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->UndefinedConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->UndefinedConstant());
}
void BytecodeGraphBuilder::VisitJumpIfUndefinedConstant(
const interpreter::BytecodeArrayIterator& iterator) {
Node* condition = BuildCondition(jsgraph()->UndefinedConstant());
BuildConditionalJump(condition);
BuildJumpIfEqual(jsgraph()->UndefinedConstant());
}
void BytecodeGraphBuilder::VisitJumpIfUndefinedConstantWide(
const interpreter::BytecodeArrayIterator& iterator) {
BuildJumpIfEqual(jsgraph()->UndefinedConstant());
}
@ -1740,17 +1770,20 @@ void BytecodeGraphBuilder::BuildConditionalJump(Node* condition) {
}
Node* BytecodeGraphBuilder::BuildCondition(Node* comperand) {
void BytecodeGraphBuilder::BuildJumpIfEqual(Node* comperand) {
Node* accumulator = environment()->LookupAccumulator();
return NewNode(javascript()->StrictEqual(), accumulator, comperand);
Node* condition =
NewNode(javascript()->StrictEqual(), accumulator, comperand);
BuildConditionalJump(condition);
}
Node* BytecodeGraphBuilder::BuildToBooleanCondition(Node* comperand) {
void BytecodeGraphBuilder::BuildJumpIfToBooleanEqual(Node* comperand) {
Node* accumulator = environment()->LookupAccumulator();
Node* to_boolean =
NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator);
return NewNode(javascript()->StrictEqual(), to_boolean, comperand);
Node* condition = NewNode(javascript()->StrictEqual(), to_boolean, comperand);
BuildConditionalJump(condition);
}

View File

@ -164,10 +164,8 @@ class BytecodeGraphBuilder {
void BuildJump(int source_offset, int target_offset);
void BuildJump();
void BuildConditionalJump(Node* condition);
// Helpers for building conditions for conditional jumps.
Node* BuildCondition(Node* comperand);
Node* BuildToBooleanCondition(Node* comperand);
void BuildJumpIfEqual(Node* comperand);
void BuildJumpIfToBooleanEqual(Node* boolean_comperand);
// Constructing merge and loop headers.
void MergeEnvironmentsOfBackwardBranches(int source_offset,

View File

@ -52,7 +52,8 @@ class BytecodeArrayBuilder::PreviousBytecodeHelper {
}
Handle<Object> GetConstantForIndexOperand(int operand_index) const {
return array_builder_.constants_.at(GetOperand(operand_index));
return array_builder_.constant_array_builder()->At(
GetOperand(operand_index));
}
private:
@ -68,12 +69,11 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
zone_(zone),
bytecodes_(zone),
bytecode_generated_(false),
constant_array_builder_(isolate, zone),
last_block_end_(0),
last_bytecode_start_(~0),
exit_seen_in_block_(false),
unbound_jumps_(0),
constants_map_(isolate->heap(), zone),
constants_(zone),
parameter_count_(-1),
local_register_count_(-1),
context_register_count_(-1),
@ -144,21 +144,14 @@ bool BytecodeArrayBuilder::RegisterIsTemporary(Register reg) const {
Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() {
DCHECK_EQ(bytecode_generated_, false);
EnsureReturn();
int bytecode_size = static_cast<int>(bytecodes_.size());
int register_count = fixed_register_count() + temporary_register_count_;
int frame_size = register_count * kPointerSize;
Factory* factory = isolate_->factory();
int constants_count = static_cast<int>(constants_.size());
Handle<FixedArray> constant_pool =
factory->NewFixedArray(constants_count, TENURED);
for (int i = 0; i < constants_count; i++) {
constant_pool->set(i, *constants_[i]);
}
constant_array_builder()->ToFixedArray(factory);
Handle<BytecodeArray> output =
factory->NewBytecodeArray(bytecode_size, &bytecodes_.front(), frame_size,
parameter_count(), constant_pool);
@ -773,44 +766,33 @@ Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand(
return Bytecode::kJumpIfUndefinedConstant;
default:
UNREACHABLE();
return Bytecode::kJumpConstant;
return static_cast<Bytecode>(-1);
}
}
void BytecodeArrayBuilder::PatchJump(
const ZoneVector<uint8_t>::iterator& jump_target,
ZoneVector<uint8_t>::iterator jump_location) {
Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
int delta = static_cast<int>(jump_target - jump_location);
DCHECK(Bytecodes::IsJump(jump_bytecode));
DCHECK_EQ(Bytecodes::Size(jump_bytecode), 2);
DCHECK_NE(delta, 0);
if (FitsInImm8Operand(delta)) {
// Just update the operand
jump_location++;
*jump_location = static_cast<uint8_t>(delta);
} else {
// Update the jump type and operand
size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdx8Operand(entry)) {
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
*jump_location++ = Bytecodes::ToByte(jump_bytecode);
*jump_location = static_cast<uint8_t>(entry);
} else {
// TODO(oth): OutputJump should reserve a constant pool entry
// when jump is written. The reservation should be used here if
// needed, or cancelled if not. This is due to the patch needing
// to match the size of the code it's replacing. In future,
// there will probably be a jump with 32-bit operand for cases
// when constant pool is full, but that needs to be emitted in
// OutputJump too.
UNIMPLEMENTED();
}
// static
Bytecode BytecodeArrayBuilder::GetJumpWithConstantWideOperand(
Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
return Bytecode::kJumpConstantWide;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfTrueConstantWide;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfFalseConstantWide;
case Bytecode::kJumpIfToBooleanTrue:
return Bytecode::kJumpIfToBooleanTrueConstantWide;
case Bytecode::kJumpIfToBooleanFalse:
return Bytecode::kJumpIfToBooleanFalseConstantWide;
case Bytecode::kJumpIfNull:
return Bytecode::kJumpIfNullConstantWide;
case Bytecode::kJumpIfUndefined:
return Bytecode::kJumpIfUndefinedConstantWide;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
unbound_jumps_--;
}
@ -832,6 +814,66 @@ Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) {
}
void BytecodeArrayBuilder::PatchIndirectJumpWith8BitOperand(
const ZoneVector<uint8_t>::iterator& jump_location, int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
ZoneVector<uint8_t>::iterator operand_location = jump_location + 1;
DCHECK_EQ(*operand_location, 0);
if (FitsInImm8Operand(delta)) {
// The jump fits within the range of an Imm8 operand, so cancel
// the reservation and jump directly.
constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
*operand_location = static_cast<uint8_t>(delta);
} else {
// The jump does not fit within the range of an Imm8 operand, so
// commit reservation putting the offset into the constant pool,
// and update the jump instruction and operand.
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kByte, handle(Smi::FromInt(delta), isolate()));
DCHECK(FitsInIdx8Operand(entry));
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
*jump_location = Bytecodes::ToByte(jump_bytecode);
*operand_location = static_cast<uint8_t>(entry);
}
}
void BytecodeArrayBuilder::PatchIndirectJumpWith16BitOperand(
const ZoneVector<uint8_t>::iterator& jump_location, int delta) {
DCHECK(Bytecodes::IsJumpConstantWide(Bytecodes::FromByte(*jump_location)));
ZoneVector<uint8_t>::iterator operand_location = jump_location + 1;
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kShort, handle(Smi::FromInt(delta), isolate()));
DCHECK(FitsInIdx16Operand(entry));
uint8_t operand_bytes[2];
WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry));
DCHECK(*operand_location == 0 && *(operand_location + 1) == 0);
*operand_location++ = operand_bytes[0];
*operand_location = operand_bytes[1];
}
void BytecodeArrayBuilder::PatchJump(
const ZoneVector<uint8_t>::iterator& jump_target,
const ZoneVector<uint8_t>::iterator& jump_location) {
Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
int delta = static_cast<int>(jump_target - jump_location);
DCHECK(Bytecodes::IsJump(jump_bytecode));
switch (Bytecodes::GetOperandSize(jump_bytecode, 0)) {
case OperandSize::kByte:
PatchIndirectJumpWith8BitOperand(jump_location, delta);
break;
case OperandSize::kShort:
PatchIndirectJumpWith16BitOperand(jump_location, delta);
break;
case OperandSize::kNone:
UNREACHABLE();
}
unbound_jumps_--;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label) {
// Don't emit dead code.
@ -843,30 +885,48 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
jump_bytecode = GetJumpWithToBoolean(jump_bytecode);
}
int delta;
if (label->is_bound()) {
// Label has been bound already so this is a backwards jump.
CHECK_GE(bytecodes()->size(), label->offset());
CHECK_LE(bytecodes()->size(), static_cast<size_t>(kMaxInt));
size_t abs_delta = bytecodes()->size() - label->offset();
delta = -static_cast<int>(abs_delta);
} else {
// Label has not yet been bound so this is a forward reference
// that will be patched when the label is bound.
label->set_referrer(bytecodes()->size());
delta = 0;
unbound_jumps_++;
}
int delta = -static_cast<int>(abs_delta);
if (FitsInImm8Operand(delta)) {
Output(jump_bytecode, static_cast<uint8_t>(delta));
} else {
size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdx8Operand(entry)) {
Output(GetJumpWithConstantOperand(jump_bytecode),
static_cast<uint8_t>(entry));
if (FitsInImm8Operand(delta)) {
Output(jump_bytecode, static_cast<uint8_t>(delta));
} else {
UNIMPLEMENTED();
size_t entry =
GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdx8Operand(entry)) {
Output(GetJumpWithConstantOperand(jump_bytecode),
static_cast<uint8_t>(entry));
} else if (FitsInIdx16Operand(entry)) {
Output(GetJumpWithConstantWideOperand(jump_bytecode),
static_cast<uint16_t>(entry));
} else {
UNREACHABLE();
}
}
} else {
// The label has not yet been bound so this is a forward reference
// that will be patched when the label is bound. We create a
// reservation in the constant pool so the jump can be patched
// when the label is bound. The reservation means the maximum size
// of the operand for the constant is known and the jump can
// be emitted into the bytecode stream with space for the operand.
label->set_referrer(bytecodes()->size());
unbound_jumps_++;
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry();
switch (reserved_operand_size) {
case OperandSize::kByte:
Output(jump_bytecode, 0);
break;
case OperandSize::kShort:
Output(GetJumpWithConstantWideOperand(jump_bytecode), 0);
break;
case OperandSize::kNone:
UNREACHABLE();
}
}
LeaveBasicBlock();
@ -1032,22 +1092,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::DeleteLookupSlot() {
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
// These constants shouldn't be added to the constant pool, the should use
// specialzed bytecodes instead.
DCHECK(!object.is_identical_to(isolate_->factory()->undefined_value()));
DCHECK(!object.is_identical_to(isolate_->factory()->null_value()));
DCHECK(!object.is_identical_to(isolate_->factory()->the_hole_value()));
DCHECK(!object.is_identical_to(isolate_->factory()->true_value()));
DCHECK(!object.is_identical_to(isolate_->factory()->false_value()));
size_t* entry = constants_map_.Find(object);
if (!entry) {
entry = constants_map_.Get(object);
*entry = constants_.size();
constants_.push_back(object);
}
DCHECK(constants_[*entry].is_identical_to(object));
return *entry;
return constant_array_builder()->Insert(object);
}

View File

@ -5,12 +5,9 @@
#ifndef V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#include <vector>
#include "src/ast/ast.h"
#include "src/identity-map.h"
#include "src/interpreter/bytecodes.h"
#include "src/zone.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/zone-containers.h"
namespace v8 {
@ -21,6 +18,7 @@ class Isolate;
namespace interpreter {
class BytecodeLabel;
class ConstantArrayBuilder;
class Register;
// TODO(rmcilroy): Unify this with CreateArgumentsParameters::Type in Turbofan
@ -229,6 +227,12 @@ class BytecodeArrayBuilder final {
ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; }
const ZoneVector<uint8_t>* bytecodes() const { return &bytecodes_; }
Isolate* isolate() const { return isolate_; }
ConstantArrayBuilder* constant_array_builder() {
return &constant_array_builder_;
}
const ConstantArrayBuilder* constant_array_builder() const {
return &constant_array_builder_;
}
static Bytecode BytecodeForBinaryOperation(Token::Value op);
static Bytecode BytecodeForCountOperation(Token::Value op);
@ -253,8 +257,9 @@ class BytecodeArrayBuilder final {
static bool FitsInReg8Operand(Register value);
static bool FitsInReg16Operand(Register value);
static Bytecode GetJumpWithConstantOperand(Bytecode jump_with_smi8_operand);
static Bytecode GetJumpWithToBoolean(Bytecode jump);
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);
@ -272,7 +277,11 @@ class BytecodeArrayBuilder final {
BytecodeArrayBuilder& OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label);
void PatchJump(const ZoneVector<uint8_t>::iterator& jump_target,
ZoneVector<uint8_t>::iterator jump_location);
const ZoneVector<uint8_t>::iterator& jump_location);
void PatchIndirectJumpWith8BitOperand(
const ZoneVector<uint8_t>::iterator& jump_location, int delta);
void PatchIndirectJumpWith16BitOperand(
const ZoneVector<uint8_t>::iterator& jump_location, int delta);
void LeaveBasicBlock();
void EnsureReturn();
@ -284,6 +293,7 @@ class BytecodeArrayBuilder final {
bool NeedToBooleanCast();
bool IsRegisterInAccumulator(Register reg);
// Temporary register management.
int BorrowTemporaryRegister();
int BorrowTemporaryRegisterNotInRange(int start_index, int end_index);
int AllocateAndBorrowTemporaryRegister();
@ -302,19 +312,16 @@ class BytecodeArrayBuilder final {
Zone* zone_;
ZoneVector<uint8_t> bytecodes_;
bool bytecode_generated_;
ConstantArrayBuilder constant_array_builder_;
size_t last_block_end_;
size_t last_bytecode_start_;
bool exit_seen_in_block_;
int unbound_jumps_;
IdentityMap<size_t> constants_map_;
ZoneVector<Handle<Object>> constants_;
int parameter_count_;
int local_register_count_;
int context_register_count_;
int temporary_register_count_;
ZoneSet<int> free_temporaries_;
class PreviousBytecodeHelper;

View File

@ -107,7 +107,8 @@ int BytecodeArrayIterator::GetJumpTargetOffset() const {
if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) {
int relative_offset = GetImmediateOperand(0);
return current_offset() + relative_offset;
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode) ||
interpreter::Bytecodes::IsJumpConstantWide(bytecode)) {
Smi* smi = Smi::cast(*GetConstantForIndexOperand(0));
return current_offset() + smi->value();
} else {

View File

@ -180,10 +180,22 @@ bool Bytecodes::IsConditionalJumpConstant(Bytecode bytecode) {
}
// static
bool Bytecodes::IsConditionalJumpConstantWide(Bytecode bytecode) {
return bytecode == Bytecode::kJumpIfTrueConstantWide ||
bytecode == Bytecode::kJumpIfFalseConstantWide ||
bytecode == Bytecode::kJumpIfToBooleanTrueConstantWide ||
bytecode == Bytecode::kJumpIfToBooleanFalseConstantWide ||
bytecode == Bytecode::kJumpIfNullConstantWide ||
bytecode == Bytecode::kJumpIfUndefinedConstantWide;
}
// static
bool Bytecodes::IsConditionalJump(Bytecode bytecode) {
return IsConditionalJumpImmediate(bytecode) ||
IsConditionalJumpConstant(bytecode);
IsConditionalJumpConstant(bytecode) ||
IsConditionalJumpConstantWide(bytecode);
}
@ -200,9 +212,17 @@ bool Bytecodes::IsJumpConstant(Bytecode bytecode) {
}
// static
bool Bytecodes::IsJumpConstantWide(Bytecode bytecode) {
return bytecode == Bytecode::kJumpConstantWide ||
IsConditionalJumpConstantWide(bytecode);
}
// static
bool Bytecodes::IsJump(Bytecode bytecode) {
return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode);
return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode) ||
IsJumpConstantWide(bytecode);
}

View File

@ -193,18 +193,25 @@ namespace interpreter {
/* Control Flow */ \
V(Jump, OperandType::kImm8) \
V(JumpConstant, OperandType::kIdx8) \
V(JumpConstantWide, OperandType::kIdx16) \
V(JumpIfTrue, OperandType::kImm8) \
V(JumpIfTrueConstant, OperandType::kIdx8) \
V(JumpIfTrueConstantWide, OperandType::kIdx16) \
V(JumpIfFalse, OperandType::kImm8) \
V(JumpIfFalseConstant, OperandType::kIdx8) \
V(JumpIfFalseConstantWide, OperandType::kIdx16) \
V(JumpIfToBooleanTrue, OperandType::kImm8) \
V(JumpIfToBooleanTrueConstant, OperandType::kIdx8) \
V(JumpIfToBooleanTrueConstantWide, OperandType::kIdx16) \
V(JumpIfToBooleanFalse, OperandType::kImm8) \
V(JumpIfToBooleanFalseConstant, OperandType::kIdx8) \
V(JumpIfToBooleanFalseConstantWide, OperandType::kIdx16) \
V(JumpIfNull, OperandType::kImm8) \
V(JumpIfNullConstant, OperandType::kIdx8) \
V(JumpIfNullConstantWide, OperandType::kIdx16) \
V(JumpIfUndefined, OperandType::kImm8) \
V(JumpIfUndefinedConstant, OperandType::kIdx8) \
V(JumpIfUndefinedConstantWide, OperandType::kIdx16) \
\
/* Complex flow control For..in */ \
V(ForInPrepare, OperandType::kReg8, OperandType::kReg8, OperandType::kReg8) \
@ -358,9 +365,13 @@ class Bytecodes {
static bool IsConditionalJumpImmediate(Bytecode bytecode);
// Return true if the bytecode is a conditional jump taking
// a constant pool entry (OperandType::kIdx).
// a constant pool entry (OperandType::kIdx8).
static bool IsConditionalJumpConstant(Bytecode bytecode);
// Return 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
// any kind of operand.
static bool IsConditionalJump(Bytecode bytecode);
@ -370,9 +381,13 @@ class Bytecodes {
static bool IsJumpImmediate(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking a
// constant pool entry (OperandType::kIdx).
// constant pool entry (OperandType::kIdx8).
static bool IsJumpConstant(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking a
// constant pool entry (OperandType::kIdx16).
static bool IsJumpConstantWide(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking
// any kind of operand.
static bool IsJump(Bytecode bytecode);

View File

@ -0,0 +1,174 @@
// 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/constant-array-builder.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
ConstantArrayBuilder::ConstantArraySlice::ConstantArraySlice(Zone* zone,
size_t start_index,
size_t capacity)
: start_index_(start_index),
capacity_(capacity),
reserved_(0),
constants_(zone) {}
void ConstantArrayBuilder::ConstantArraySlice::Reserve() {
DCHECK_GT(available(), 0u);
reserved_++;
DCHECK_LE(reserved_, capacity() - constants_.size());
}
void ConstantArrayBuilder::ConstantArraySlice::Unreserve() {
DCHECK_GT(reserved_, 0u);
reserved_--;
}
size_t ConstantArrayBuilder::ConstantArraySlice::Allocate(
Handle<Object> object) {
DCHECK_GT(available(), 0u);
size_t index = constants_.size();
DCHECK_LT(index, capacity());
constants_.push_back(object);
return index + start_index();
}
Handle<Object> ConstantArrayBuilder::ConstantArraySlice::At(
size_t index) const {
return constants_[index - start_index()];
}
STATIC_CONST_MEMBER_DEFINITION const size_t ConstantArrayBuilder::kMaxCapacity;
STATIC_CONST_MEMBER_DEFINITION const size_t ConstantArrayBuilder::kLowCapacity;
ConstantArrayBuilder::ConstantArrayBuilder(Isolate* isolate, Zone* zone)
: isolate_(isolate),
idx8_slice_(zone, 0, kLowCapacity),
idx16_slice_(zone, kLowCapacity, kHighCapacity),
constants_map_(isolate->heap(), zone) {
STATIC_ASSERT(kMaxCapacity == static_cast<size_t>(kMaxUInt16 + 1));
DCHECK_EQ(idx8_slice_.start_index(), 0u);
DCHECK_EQ(idx8_slice_.capacity(), kLowCapacity);
DCHECK_EQ(idx16_slice_.start_index(), kLowCapacity);
DCHECK_EQ(idx16_slice_.capacity(), kMaxCapacity - kLowCapacity);
}
size_t ConstantArrayBuilder::size() const {
if (idx16_slice_.size() > 0) {
return idx16_slice_.start_index() + idx16_slice_.size();
} else {
return idx8_slice_.size();
}
}
Handle<Object> ConstantArrayBuilder::At(size_t index) const {
if (index >= idx16_slice_.start_index()) {
return idx16_slice_.At(index);
} else if (index < idx8_slice_.size()) {
return idx8_slice_.At(index);
} else {
return isolate_->factory()->the_hole_value();
}
}
Handle<FixedArray> ConstantArrayBuilder::ToFixedArray(Factory* factory) const {
Handle<FixedArray> fixed_array =
factory->NewFixedArray(static_cast<int>(size()), PretenureFlag::TENURED);
for (int i = 0; i < fixed_array->length(); i++) {
fixed_array->set(i, *At(static_cast<size_t>(i)));
}
return fixed_array;
}
size_t ConstantArrayBuilder::Insert(Handle<Object> object) {
index_t* entry = constants_map_.Find(object);
return (entry == nullptr) ? AllocateEntry(object) : *entry;
}
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateEntry(
Handle<Object> object) {
DCHECK(!object->IsOddball());
size_t index;
index_t* entry = constants_map_.Get(object);
if (idx8_slice_.available() > 0) {
index = idx8_slice_.Allocate(object);
} else {
index = idx16_slice_.Allocate(object);
}
CHECK_LT(index, kMaxCapacity);
*entry = static_cast<index_t>(index);
return *entry;
}
OperandSize ConstantArrayBuilder::CreateReservedEntry() {
if (idx8_slice_.available() > 0) {
idx8_slice_.Reserve();
return OperandSize::kByte;
} else if (idx16_slice_.available() > 0) {
idx16_slice_.Reserve();
return OperandSize::kShort;
} else {
UNREACHABLE();
return OperandSize::kNone;
}
}
size_t ConstantArrayBuilder::CommitReservedEntry(OperandSize operand_size,
Handle<Object> object) {
DiscardReservedEntry(operand_size);
size_t index;
index_t* entry = constants_map_.Find(object);
if (nullptr == entry) {
index = AllocateEntry(object);
} else {
if (operand_size == OperandSize::kByte &&
*entry >= idx8_slice_.capacity()) {
// The object is already in the constant array, but has an index
// outside the range of an idx8 operand so we need to create a
// duplicate entry in the idx8 operand range to satisfy the
// commitment.
*entry = static_cast<index_t>(idx8_slice_.Allocate(object));
}
index = *entry;
}
DCHECK(operand_size == OperandSize::kShort || index < idx8_slice_.capacity());
DCHECK_LT(index, kMaxCapacity);
return index;
}
void ConstantArrayBuilder::DiscardReservedEntry(OperandSize operand_size) {
switch (operand_size) {
case OperandSize::kByte:
idx8_slice_.Unreserve();
return;
case OperandSize::kShort:
idx16_slice_.Unreserve();
return;
default:
UNREACHABLE();
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,97 @@
// 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_CONSTANT_ARRAY_BUILDER_H_
#define V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_
#include "src/identity-map.h"
#include "src/interpreter/bytecodes.h"
#include "src/zone-containers.h"
namespace v8 {
namespace internal {
class Factory;
class Isolate;
namespace interpreter {
// A helper class for constructing constant arrays for the interpreter.
class ConstantArrayBuilder final : public ZoneObject {
public:
// Capacity of the 8-bit operand slice.
static const size_t kLowCapacity = 1u << kBitsPerByte;
// Capacity of the combined 8-bit and 16-bit operand slices.
static const size_t kMaxCapacity = 1u << (2 * kBitsPerByte);
// Capacity of the 16-bit operand slice.
static const size_t kHighCapacity = kMaxCapacity - kLowCapacity;
ConstantArrayBuilder(Isolate* isolate, Zone* zone);
// Generate a fixed array of constants based on inserted objects.
Handle<FixedArray> ToFixedArray(Factory* factory) const;
// Returns the object in the constant pool array that at index
// |index|.
Handle<Object> At(size_t index) const;
// Returns the number of elements in the array.
size_t size() const;
// Insert an object into the constants array if it is not already
// present. Returns the array index associated with the object.
size_t Insert(Handle<Object> object);
// Creates a reserved entry in the constant pool and returns
// the size of the operand that'll be required to hold the entry
// when committed.
OperandSize CreateReservedEntry();
// Commit reserved entry and returns the constant pool index for the
// object.
size_t CommitReservedEntry(OperandSize operand_size, Handle<Object> object);
// Discards constant pool reservation.
void DiscardReservedEntry(OperandSize operand_size);
private:
typedef uint16_t index_t;
index_t AllocateEntry(Handle<Object> object);
struct ConstantArraySlice final {
ConstantArraySlice(Zone* zone, size_t start_index, size_t capacity);
void Reserve();
void Unreserve();
size_t Allocate(Handle<Object> object);
Handle<Object> At(size_t index) const;
inline size_t available() const { return capacity() - reserved() - size(); }
inline size_t reserved() const { return reserved_; }
inline size_t capacity() const { return capacity_; }
inline size_t size() const { return constants_.size(); }
inline size_t start_index() const { return start_index_; }
private:
const size_t start_index_;
const size_t capacity_;
size_t reserved_;
ZoneVector<Handle<Object>> constants_;
DISALLOW_COPY_AND_ASSIGN(ConstantArraySlice);
};
Isolate* isolate_;
ConstantArraySlice idx8_slice_;
ConstantArraySlice idx16_slice_;
IdentityMap<index_t> constants_map_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_

View File

@ -1258,9 +1258,9 @@ void Interpreter::DoJump(compiler::InterpreterAssembler* assembler) {
}
// JumpConstant <idx>
// JumpConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool.
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool.
void Interpreter::DoJumpConstant(compiler::InterpreterAssembler* assembler) {
Node* index = __ BytecodeOperandIdx(0);
Node* constant = __ LoadConstantPoolEntry(index);
@ -1269,6 +1269,16 @@ void Interpreter::DoJumpConstant(compiler::InterpreterAssembler* assembler) {
}
// JumpConstantWide <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the
// constant pool.
void Interpreter::DoJumpConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpConstant(assembler);
}
// JumpIfTrue <imm8>
//
// Jump by number of bytes represented by an immediate operand if the
@ -1281,9 +1291,9 @@ void Interpreter::DoJumpIfTrue(compiler::InterpreterAssembler* assembler) {
}
// JumpIfTrueConstant <idx>
// JumpIfTrueConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool
// if the accumulator contains true.
void Interpreter::DoJumpIfTrueConstant(
compiler::InterpreterAssembler* assembler) {
@ -1296,6 +1306,16 @@ void Interpreter::DoJumpIfTrueConstant(
}
// JumpIfTrueConstantWide <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool
// if the accumulator contains true.
void Interpreter::DoJumpIfTrueConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpIfTrueConstant(assembler);
}
// JumpIfFalse <imm8>
//
// Jump by number of bytes represented by an immediate operand if the
@ -1308,9 +1328,9 @@ void Interpreter::DoJumpIfFalse(compiler::InterpreterAssembler* assembler) {
}
// JumpIfFalseConstant <idx>
// JumpIfFalseConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool
// if the accumulator contains false.
void Interpreter::DoJumpIfFalseConstant(
compiler::InterpreterAssembler* assembler) {
@ -1323,6 +1343,16 @@ void Interpreter::DoJumpIfFalseConstant(
}
// JumpIfFalseConstant <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool
// if the accumulator contains false.
void Interpreter::DoJumpIfFalseConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpIfFalseConstant(assembler);
}
// JumpIfToBooleanTrue <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
@ -1338,9 +1368,9 @@ void Interpreter::DoJumpIfToBooleanTrue(
}
// JumpIfToBooleanTrueConstant <idx>
// JumpIfToBooleanTrueConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool
// if the object referenced by the accumulator is true when the object is cast
// to boolean.
void Interpreter::DoJumpIfToBooleanTrueConstant(
@ -1356,6 +1386,17 @@ void Interpreter::DoJumpIfToBooleanTrueConstant(
}
// JumpIfToBooleanTrueConstantWide <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool
// if the object referenced by the accumulator is true when the object is cast
// to boolean.
void Interpreter::DoJumpIfToBooleanTrueConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpIfToBooleanTrueConstant(assembler);
}
// JumpIfToBooleanFalse <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
@ -1371,9 +1412,9 @@ void Interpreter::DoJumpIfToBooleanFalse(
}
// JumpIfToBooleanFalseConstant <idx>
// JumpIfToBooleanFalseConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool
// if the object referenced by the accumulator is false when the object is cast
// to boolean.
void Interpreter::DoJumpIfToBooleanFalseConstant(
@ -1389,6 +1430,17 @@ void Interpreter::DoJumpIfToBooleanFalseConstant(
}
// JumpIfToBooleanFalseConstantWide <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool
// if the object referenced by the accumulator is false when the object is cast
// to boolean.
void Interpreter::DoJumpIfToBooleanFalseConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpIfToBooleanFalseConstant(assembler);
}
// JumpIfNull <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
@ -1401,9 +1453,9 @@ void Interpreter::DoJumpIfNull(compiler::InterpreterAssembler* assembler) {
}
// JumpIfNullConstant <idx>
// JumpIfNullConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool
// if the object referenced by the accumulator is the null constant.
void Interpreter::DoJumpIfNullConstant(
compiler::InterpreterAssembler* assembler) {
@ -1416,7 +1468,17 @@ void Interpreter::DoJumpIfNullConstant(
}
// JumpIfUndefined <imm8>
// JumpIfNullConstantWide <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool
// if the object referenced by the accumulator is the null constant.
void Interpreter::DoJumpIfNullConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpIfNullConstant(assembler);
}
// jumpifundefined <imm8>
//
// Jump by number of bytes represented by an immediate operand if the object
// referenced by the accumulator is the undefined constant.
@ -1429,9 +1491,9 @@ void Interpreter::DoJumpIfUndefined(compiler::InterpreterAssembler* assembler) {
}
// JumpIfUndefinedConstant <idx>
// JumpIfUndefinedConstant <idx8>
//
// Jump by number of bytes in the Smi in the |idx| entry in the constant pool
// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool
// if the object referenced by the accumulator is the undefined constant.
void Interpreter::DoJumpIfUndefinedConstant(
compiler::InterpreterAssembler* assembler) {
@ -1445,6 +1507,16 @@ void Interpreter::DoJumpIfUndefinedConstant(
}
// JumpIfUndefinedConstantWide <idx16>
//
// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool
// if the object referenced by the accumulator is the undefined constant.
void Interpreter::DoJumpIfUndefinedConstantWide(
compiler::InterpreterAssembler* assembler) {
DoJumpIfUndefinedConstant(assembler);
}
void Interpreter::DoCreateLiteral(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler) {
Node* index = __ BytecodeOperandIdx(0);

View File

@ -14864,15 +14864,24 @@ void BytecodeArray::Disassemble(std::ostream& os) {
SNPrintF(buf, "%p", bytecode_start);
os << buf.start() << " : ";
interpreter::Bytecodes::Decode(os, bytecode_start, parameter_count());
if (interpreter::Bytecodes::IsJump(bytecode)) {
int offset = static_cast<int8_t>(bytecode_start[1]);
if (interpreter::Bytecodes::IsJumpConstantWide(bytecode)) {
DCHECK_EQ(bytecode_size, 3);
int index = static_cast<int>(ReadUnalignedUInt16(bytecode_start + 1));
int offset = Smi::cast(constant_pool()->get(index))->value();
SNPrintF(buf, " (%p)", bytecode_start + offset);
os << buf.start();
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
DCHECK_EQ(bytecode_size, 2);
int index = static_cast<int>(bytecode_start[1]);
int offset = Smi::cast(constant_pool()->get(index))->value();
SNPrintF(buf, " (%p)", bytecode_start + offset);
os << buf.start();
} else if (interpreter::Bytecodes::IsJump(bytecode)) {
DCHECK_EQ(bytecode_size, 2);
int offset = static_cast<int8_t>(bytecode_start[1]);
SNPrintF(buf, " (%p)", bytecode_start + offset);
os << buf.start();
}
os << "\n";
}

View File

@ -2152,6 +2152,46 @@ TEST(BytecodeGraphBuilderForIn) {
}
TEST(JumpWithConstantsAndWideConstants) {
HandleAndZoneScope scope;
auto isolate = scope.main_isolate();
const int kStep = 19;
int start = 7;
for (int constants = start; constants < 256 + 3 * kStep; constants += kStep) {
std::stringstream filler_os;
// Generate a string that consumes constant pool entries and
// spread out branch distances in script below.
for (int i = 0; i < constants; i++) {
filler_os << "var x_ = 'x_" << i << "';\n";
}
std::string filler(filler_os.str());
std::stringstream script_os;
script_os << "function " << kFunctionName << "(a) {\n";
script_os << " " << filler;
script_os << " for (var i = a; i < 2; i++) {\n";
script_os << " " << filler;
script_os << " if (i == 0) { " << filler << "i = 10; continue; }\n";
script_os << " else if (i == a) { " << filler << "i = 12; break; }\n";
script_os << " else { " << filler << " }\n";
script_os << " }\n";
script_os << " return i;\n";
script_os << "}\n";
script_os << kFunctionName << "(0);\n";
std::string script(script_os.str());
auto factory = isolate->factory();
auto zone = scope.main_zone();
for (int a = 0; a < 3; a++) {
BytecodeGraphTester tester(isolate, zone, script.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(factory->NewNumberFromInt(a)).ToHandleChecked();
static const int results[] = {11, 12, 2};
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), results[a]);
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -65,9 +65,12 @@ class BytecodeGeneratorHelper {
}
Handle<BytecodeArray> MakeBytecodeForFunctionBody(const char* body) {
ScopedVector<char> program(3072);
SNPrintF(program, "function %s() { %s }\n%s();", kFunctionName, body,
kFunctionName);
static const char kFormat[] = "function %s() { %s }\n%s();";
static const int kFormatLength = arraysize(kFormat);
int length = kFormatLength + 2 * StrLength(kFunctionName) + StrLength(body);
ScopedVector<char> program(length);
length = SNPrintF(program, kFormat, kFunctionName, body, kFunctionName);
CHECK_GT(length, 0);
return MakeBytecode(program.start(), kFunctionName);
}
@ -94,9 +97,13 @@ class BytecodeGeneratorHelper {
#if defined(V8_TARGET_LITTLE_ENDIAN)
#define U16(x) static_cast<uint8_t>((x) & 0xff), \
static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff)
#define U16I(x) static_cast<uint8_t>((x) & 0xff), \
static_cast<uint8_t>(((x++) >> kBitsPerByte) & 0xff)
#elif defined(V8_TARGET_BIG_ENDIAN)
#define U16(x) static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff), \
static_cast<uint8_t>((x) & 0xff)
#define U16I(x) static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff), \
static_cast<uint8_t>((x++) & 0xff)
#else
#error Unknown byte ordering
#endif
@ -104,7 +111,7 @@ class BytecodeGeneratorHelper {
#define COMMA() ,
#define SPACE()
#define REPEAT_2(SEP, ...) \
#define REPEAT_2(SEP, ...) \
__VA_ARGS__ SEP() __VA_ARGS__
#define REPEAT_4(SEP, ...) \
REPEAT_2(SEP, __VA_ARGS__) SEP() REPEAT_2(SEP, __VA_ARGS__)
@ -2630,6 +2637,77 @@ TEST(BasicLoops) {
}
TEST(JumpsRequiringConstantWideOperands) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int constant_count = 0;
ExpectedSnippet<Handle<Object>, 315> snippets[] = {
{
REPEAT_256(SPACE, "var x = 0.1;")
REPEAT_32(SPACE, "var x = 0.2;")
REPEAT_16(SPACE, "var x = 0.3;")
REPEAT_8(SPACE, "var x = 0.4;")
"for (var i = 0; i < 3; i++) {\n"
" if (i == 1) continue;\n"
" if (i == 2) break;\n"
"}\n"
"return 3;",
kPointerSize * 3,
1,
1347,
{
#define L(c) B(LdaConstant), U8(c), B(Star), R(0)
REPEAT_256(COMMA, L(constant_count++)),
#undef L
#define LW(c) B(LdaConstantWide), U16I(c), B(Star), R(0)
REPEAT_32(COMMA, LW(constant_count)),
REPEAT_16(COMMA, LW(constant_count)),
REPEAT_8(COMMA, LW(constant_count)),
#undef LW
B(LdaZero), //
B(Star), R(1), //
B(LdaSmi8), U8(3), //
B(TestLessThan), R(1), //
B(JumpIfFalseConstantWide), U16(313), //
B(LdaSmi8), U8(1), //
B(TestEqual), R(1), //
B(JumpIfFalseConstantWide), U16(312), //
B(JumpConstantWide), U16(314), //
B(LdaSmi8), U8(2), //
B(TestEqual), R(1), //
B(JumpIfFalseConstantWide), U16(312), //
B(JumpConstantWide), U16(314), //
B(Ldar), R(1), //
B(ToNumber), //
B(Star), R(2), //
B(Inc), //
B(Star), R(1), //
B(Jump), U8(-35), //
B(LdaSmi8), U8(3), //
B(Return) //
},
315,
{
#define S(x) CcTest::i_isolate()->factory()->NewNumber(x)
REPEAT_256(COMMA, S(0.1)),
REPEAT_32(COMMA, S(0.2)),
REPEAT_16(COMMA, S(0.3)),
REPEAT_8(COMMA, S(0.4)),
#undef S
#define N(x) CcTest::i_isolate()->factory()->NewNumberFromInt(x)
N(6), N(33), N(13),
#undef N
}}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(UnaryOperators) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;

View File

@ -3350,6 +3350,43 @@ TEST(InterpreterDeleteLookupSlot) {
}
}
TEST(JumpWithConstantsAndWideConstants) {
HandleAndZoneScope handles;
auto isolate = handles.main_isolate();
auto factory = isolate->factory();
const int kStep = 13;
for (int constants = 3; constants < 256 + 3 * kStep; constants += kStep) {
std::ostringstream filler_os;
// Generate a string that consumes constant pool entries and
// spread out branch distances in script below.
for (int i = 0; i < constants; i++) {
filler_os << "var x_ = 'x_" << i << "';\n";
}
std::string filler(filler_os.str());
std::ostringstream script_os;
script_os << "function " << InterpreterTester::function_name() << "(a) {\n";
script_os << " " << filler;
script_os << " for (var i = a; i < 2; i++) {\n";
script_os << " " << filler;
script_os << " if (i == 0) { " << filler << "i = 10; continue; }\n";
script_os << " else if (i == a) { " << filler << "i = 12; break; }\n";
script_os << " else { " << filler << " }\n";
script_os << " }\n";
script_os << " return i;\n";
script_os << "}\n";
std::string script(script_os.str());
for (int a = 0; a < 3; a++) {
InterpreterTester tester(handles.main_isolate(), script.c_str());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(factory->NewNumberFromInt(a)).ToHandleChecked();
static const int results[] = {11, 12, 2};
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), results[a]);
}
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -258,6 +258,21 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CreateArrayLiteral(factory->NewFixedArray(2), 0, 0)
.CreateObjectLiteral(factory->NewFixedArray(2), 0, 0);
// Longer jumps requiring ConstantWide operand
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)
.JumpIfTrue(&start)
.CompareOperation(Token::Value::EQ, reg, Strength::WEAK)
.JumpIfFalse(&start);
// Perform an operation that returns a non-boolean operation to
// generate JumpIfToBooleanTrue/False.
builder.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
.JumpIfTrue(&start)
.BinaryOperation(Token::Value::ADD, reg, Strength::WEAK)
.JumpIfFalse(&start);
builder.Return();
// Generate BytecodeArray.
@ -691,6 +706,7 @@ TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
CHECK(iterator.done());
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,225 @@
// 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 "src/v8.h"
#include "src/factory.h"
#include "src/handles-inl.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/isolate.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
class ConstantArrayBuilderTest : public TestWithIsolateAndZone {
public:
ConstantArrayBuilderTest() {}
~ConstantArrayBuilderTest() override {}
static const size_t kLowCapacity = ConstantArrayBuilder::kLowCapacity;
static const size_t kMaxCapacity = ConstantArrayBuilder::kMaxCapacity;
};
STATIC_CONST_MEMBER_DEFINITION const size_t
ConstantArrayBuilderTest::kMaxCapacity;
STATIC_CONST_MEMBER_DEFINITION const size_t
ConstantArrayBuilderTest::kLowCapacity;
TEST_F(ConstantArrayBuilderTest, AllocateAllEntries) {
ConstantArrayBuilder builder(isolate(), zone());
for (size_t i = 0; i < kMaxCapacity; i++) {
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.Insert(object);
CHECK_EQ(builder.size(), i + 1);
CHECK(builder.At(i)->SameValue(*object));
}
for (size_t i = 0; i < kMaxCapacity; i++) {
CHECK_EQ(Handle<Smi>::cast(builder.At(i))->value(), static_cast<double>(i));
}
}
TEST_F(ConstantArrayBuilderTest, AllocateEntriesWithIdx8Reservations) {
for (size_t reserved = 1; reserved < kLowCapacity; reserved *= 3) {
ConstantArrayBuilder builder(isolate(), zone());
for (size_t i = 0; i < reserved; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(operand_size == OperandSize::kByte);
}
for (size_t i = 0; i < 2 * kLowCapacity; i++) {
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.Insert(object);
if (i + reserved < kLowCapacity) {
CHECK_LE(builder.size(), kLowCapacity);
CHECK_EQ(builder.size(), i + 1);
CHECK(builder.At(i)->SameValue(*object));
} else {
CHECK_GE(builder.size(), kLowCapacity);
CHECK_EQ(builder.size(), i + reserved + 1);
CHECK(builder.At(i + reserved)->SameValue(*object));
}
}
CHECK_EQ(builder.size(), 2 * kLowCapacity + reserved);
// Check reserved values represented by the hole.
for (size_t i = 0; i < reserved; i++) {
Handle<Object> empty = builder.At(kLowCapacity - reserved + i);
CHECK(empty->SameValue(isolate()->heap()->the_hole_value()));
}
// Commmit reserved entries with duplicates and check size does not change.
DCHECK_EQ(reserved + 2 * kLowCapacity, builder.size());
size_t duplicates_in_idx8_space =
std::min(reserved, kLowCapacity - reserved);
for (size_t i = 0; i < duplicates_in_idx8_space; i++) {
builder.CommitReservedEntry(OperandSize::kByte,
isolate()->factory()->NewNumberFromSize(i));
DCHECK_EQ(reserved + 2 * kLowCapacity, builder.size());
}
// Check all committed values match expected (holes where
// duplicates_in_idx8_space allocated).
for (size_t i = 0; i < kLowCapacity - reserved; i++) {
Smi* smi = Smi::FromInt(static_cast<int>(i));
CHECK(Handle<Smi>::cast(builder.At(i))->SameValue(smi));
}
for (size_t i = kLowCapacity; i < 2 * kLowCapacity + reserved; i++) {
Smi* smi = Smi::FromInt(static_cast<int>(i - reserved));
CHECK(Handle<Smi>::cast(builder.At(i))->SameValue(smi));
}
for (size_t i = 0; i < reserved; i++) {
size_t index = kLowCapacity - reserved + i;
CHECK(builder.At(index)->IsTheHole());
}
// Now make reservations, and commit them with unique entries.
for (size_t i = 0; i < duplicates_in_idx8_space; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(operand_size == OperandSize::kByte);
}
for (size_t i = 0; i < duplicates_in_idx8_space; i++) {
Handle<Object> object =
isolate()->factory()->NewNumberFromSize(2 * kLowCapacity + i);
size_t index = builder.CommitReservedEntry(OperandSize::kByte, object);
CHECK_EQ(static_cast<int>(index), kLowCapacity - reserved + i);
CHECK(builder.At(static_cast<int>(index))->SameValue(*object));
}
CHECK_EQ(builder.size(), 2 * kLowCapacity + reserved);
}
}
TEST_F(ConstantArrayBuilderTest, AllocateEntriesWithIdx16Reservations) {
for (size_t reserved = 1; reserved < kLowCapacity; reserved *= 3) {
ConstantArrayBuilder builder(isolate(), zone());
for (size_t i = 0; i < kLowCapacity; i++) {
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.Insert(object);
CHECK(builder.At(i)->SameValue(*object));
CHECK_EQ(builder.size(), i + 1);
}
for (size_t i = 0; i < reserved; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(operand_size == OperandSize::kShort);
CHECK_EQ(builder.size(), kLowCapacity);
}
for (size_t i = 0; i < reserved; i++) {
builder.DiscardReservedEntry(OperandSize::kShort);
CHECK_EQ(builder.size(), kLowCapacity);
}
for (size_t i = 0; i < reserved; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(operand_size == OperandSize::kShort);
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.CommitReservedEntry(operand_size, object);
CHECK_EQ(builder.size(), kLowCapacity);
}
for (size_t i = kLowCapacity; i < kLowCapacity + reserved; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(operand_size == OperandSize::kShort);
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.CommitReservedEntry(operand_size, object);
CHECK_EQ(builder.size(), i + 1);
}
}
}
TEST_F(ConstantArrayBuilderTest, ToFixedArray) {
ConstantArrayBuilder builder(isolate(), zone());
static const size_t kNumberOfElements = 37;
for (size_t i = 0; i < kNumberOfElements; i++) {
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.Insert(object);
CHECK(builder.At(i)->SameValue(*object));
}
Handle<FixedArray> constant_array =
builder.ToFixedArray(isolate()->factory());
CHECK_EQ(constant_array->length(), kNumberOfElements);
for (size_t i = 0; i < kNumberOfElements; i++) {
CHECK(constant_array->get(static_cast<int>(i))->SameValue(*builder.At(i)));
}
}
TEST_F(ConstantArrayBuilderTest, GapFilledWhenLowReservationCommitted) {
ConstantArrayBuilder builder(isolate(), zone());
for (size_t i = 0; i < kLowCapacity; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(OperandSize::kByte == operand_size);
CHECK_EQ(builder.size(), 0);
}
for (size_t i = 0; i < kLowCapacity; i++) {
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.Insert(object);
CHECK_EQ(builder.size(), i + kLowCapacity + 1);
}
for (size_t i = 0; i < kLowCapacity; i++) {
builder.CommitReservedEntry(OperandSize::kByte,
builder.At(i + kLowCapacity));
CHECK_EQ(builder.size(), 2 * kLowCapacity);
}
for (size_t i = 0; i < kLowCapacity; i++) {
Handle<Object> original = builder.At(kLowCapacity + i);
Handle<Object> duplicate = builder.At(i);
CHECK(original->SameValue(*duplicate));
Handle<Object> reference = isolate()->factory()->NewNumberFromSize(i);
CHECK(original->SameValue(*reference));
}
}
TEST_F(ConstantArrayBuilderTest, GapNotFilledWhenLowReservationDiscarded) {
ConstantArrayBuilder builder(isolate(), zone());
for (size_t i = 0; i < kLowCapacity; i++) {
OperandSize operand_size = builder.CreateReservedEntry();
CHECK(OperandSize::kByte == operand_size);
CHECK_EQ(builder.size(), 0);
}
for (size_t i = 0; i < kLowCapacity; i++) {
Handle<Object> object = isolate()->factory()->NewNumberFromSize(i);
builder.Insert(object);
CHECK_EQ(builder.size(), i + kLowCapacity + 1);
}
for (size_t i = 0; i < kLowCapacity; i++) {
builder.DiscardReservedEntry(OperandSize::kByte);
builder.Insert(builder.At(i + kLowCapacity));
CHECK_EQ(builder.size(), 2 * kLowCapacity);
}
for (size_t i = 0; i < kLowCapacity; i++) {
Handle<Object> reference = isolate()->factory()->NewNumberFromSize(i);
Handle<Object> original = builder.At(kLowCapacity + i);
CHECK(original->SameValue(*reference));
Handle<Object> duplicate = builder.At(i);
CHECK(duplicate->SameValue(*isolate()->factory()->the_hole_value()));
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -97,6 +97,7 @@
'interpreter/bytecodes-unittest.cc',
'interpreter/bytecode-array-builder-unittest.cc',
'interpreter/bytecode-array-iterator-unittest.cc',
'interpreter/constant-array-builder-unittest.cc',
'libplatform/default-platform-unittest.cc',
'libplatform/task-queue-unittest.cc',
'libplatform/worker-thread-unittest.cc',

View File

@ -864,6 +864,8 @@
'../../src/interpreter/bytecode-generator.cc',
'../../src/interpreter/bytecode-generator.h',
'../../src/interpreter/bytecode-traits.h',
'../../src/interpreter/constant-array-builder.cc',
'../../src/interpreter/constant-array-builder.h',
'../../src/interpreter/control-flow-builders.cc',
'../../src/interpreter/control-flow-builders.h',
'../../src/interpreter/interpreter.cc',