Commit de9d1d8b authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Move jump processing to bytecode array writer.

This moves processing of jumps out of bytecode array builder and into
bytecode array writer. This simplifies the pipeline by avoiding having
to flush for offset and patch up offsets in bytecode array builder based
on what was emitted by the bytecode array writer.

This also enables future refactorings to add dead code elimination back
into the pipeline, and move processing of scalable operand sizes to the
end of the pipeline (in the bytecode array writer) rather than having to
deal with scalable operand types throughout pipeline.

BUG=v8:4280,chromium:616064

Review-Url: https://codereview.chromium.org/2035813002
Cr-Commit-Position: refs/heads/master@{#36716}
parent a09fb95b
......@@ -1229,6 +1229,7 @@ v8_source_set("v8_base") {
"src/interpreter/bytecode-array-writer.h",
"src/interpreter/bytecode-generator.cc",
"src/interpreter/bytecode-generator.h",
"src/interpreter/bytecode-label.h",
"src/interpreter/bytecode-peephole-optimizer.cc",
"src/interpreter/bytecode-peephole-optimizer.h",
"src/interpreter/bytecode-pipeline.cc",
......
......@@ -6,6 +6,7 @@
#include "src/compiler.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-peephole-optimizer.h"
#include "src/interpreter/bytecode-register-optimizer.h"
#include "src/interpreter/interpreter-intrinsics.h"
......@@ -23,14 +24,12 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone,
bytecode_generated_(false),
constant_array_builder_(isolate, zone),
handler_table_builder_(isolate, zone),
source_position_table_builder_(isolate, zone),
return_seen_in_block_(false),
unbound_jumps_(0),
parameter_count_(parameter_count),
local_register_count_(locals_count),
context_register_count_(context_count),
temporary_allocator_(zone, fixed_register_count()),
bytecode_array_writer_(zone, &source_position_table_builder_),
bytecode_array_writer_(isolate, zone, &constant_array_builder_),
pipeline_(&bytecode_array_writer_) {
DCHECK_GE(parameter_count_, 0);
DCHECK_GE(context_register_count_, 0);
......@@ -49,8 +48,6 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone,
return_position_ =
literal ? std::max(literal->start_position(), literal->end_position() - 1)
: RelocInfo::kNoPosition;
LOG_CODE_EVENT(isolate_, CodeStartLinePosInfoRecordEvent(
source_position_table_builder()));
}
Register BytecodeArrayBuilder::first_context_register() const {
......@@ -58,55 +55,28 @@ Register BytecodeArrayBuilder::first_context_register() const {
return Register(local_register_count_);
}
Register BytecodeArrayBuilder::last_context_register() const {
DCHECK_GT(context_register_count_, 0);
return Register(local_register_count_ + context_register_count_ - 1);
}
Register BytecodeArrayBuilder::Parameter(int parameter_index) const {
DCHECK_GE(parameter_index, 0);
return Register::FromParameterIndex(parameter_index, parameter_count());
}
bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const {
return reg.is_parameter() || reg.index() < locals_count();
}
Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() {
DCHECK_EQ(0, unbound_jumps_);
DCHECK_EQ(bytecode_generated_, false);
DCHECK(return_seen_in_block_);
DCHECK(!bytecode_generated_);
bytecode_generated_ = true;
pipeline()->FlushBasicBlock();
const ZoneVector<uint8_t>* bytecodes = bytecode_array_writer()->bytecodes();
int bytecode_size = static_cast<int>(bytecodes->size());
// All locals need a frame slot for the debugger, but may not be
// present in generated code.
int frame_size_for_locals = fixed_register_count() * kPointerSize;
int frame_size_used = bytecode_array_writer()->GetMaximumFrameSizeUsed();
int frame_size = std::max(frame_size_for_locals, frame_size_used);
Handle<FixedArray> constant_pool = constant_array_builder()->ToFixedArray();
Handle<FixedArray> handler_table = handler_table_builder()->ToHandlerTable();
Handle<ByteArray> source_position_table =
source_position_table_builder()->ToSourcePositionTable();
Handle<BytecodeArray> bytecode_array = isolate_->factory()->NewBytecodeArray(
bytecode_size, &bytecodes->front(), frame_size, parameter_count(),
constant_pool);
bytecode_array->set_handler_table(*handler_table);
bytecode_array->set_source_position_table(*source_position_table);
void* line_info = source_position_table_builder()->DetachJITHandlerData();
LOG_CODE_EVENT(isolate_, CodeEndLinePosInfoRecordEvent(
AbstractCode::cast(*bytecode_array), line_info));
bytecode_generated_ = true;
return bytecode_array;
return pipeline_->ToBytecodeArray(fixed_register_count(), parameter_count(),
handler_table);
}
void BytecodeArrayBuilder::AttachSourceInfo(BytecodeNode* node) {
......@@ -181,7 +151,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
Output(Bytecode::kToBooleanLogicalNot);
return *this;
......@@ -202,7 +171,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(Token::Value op,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
v8::internal::Smi* smi) {
int32_t raw_smi = smi->value();
......@@ -217,7 +185,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
size_t entry = GetConstantPoolEntry(object);
OperandScale operand_scale =
......@@ -226,31 +193,26 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadUndefined() {
Output(Bytecode::kLdaUndefined);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNull() {
Output(Bytecode::kLdaNull);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTheHole() {
Output(Bytecode::kLdaTheHole);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTrue() {
Output(Bytecode::kLdaTrue);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() {
Output(Bytecode::kLdaFalse);
return *this;
......@@ -264,7 +226,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
Register reg) {
OperandScale operand_scale =
......@@ -273,7 +234,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
Register to) {
DCHECK(from != to);
......@@ -310,7 +270,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context,
int slot_index) {
OperandScale operand_scale = Bytecodes::OperandSizesToScale(
......@@ -320,7 +279,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context,
int slot_index) {
OperandScale operand_scale = Bytecodes::OperandSizesToScale(
......@@ -386,7 +344,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
Register object, Register key, int feedback_slot,
LanguageMode language_mode) {
......@@ -399,7 +356,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
Handle<SharedFunctionInfo> shared_info, PretenureFlag tenured) {
size_t entry = GetConstantPoolEntry(shared_info);
......@@ -410,7 +366,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
CreateArgumentsType type) {
// TODO(rmcilroy): Consider passing the type as a bytecode operand rather
......@@ -421,7 +376,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
Handle<String> pattern, int literal_index, int flags) {
size_t pattern_entry = GetConstantPoolEntry(pattern);
......@@ -435,7 +389,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
Handle<FixedArray> constant_elements, int literal_index, int flags) {
size_t constant_elements_entry = GetConstantPoolEntry(constant_elements);
......@@ -449,7 +402,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
Handle<FixedArray> constant_properties, int literal_index, int flags) {
size_t constant_properties_entry = GetConstantPoolEntry(constant_properties);
......@@ -463,7 +415,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
OperandScale operand_scale =
Bytecodes::OperandSizesToScale(context.SizeOfOperand());
......@@ -471,7 +422,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
OperandScale operand_scale =
Bytecodes::OperandSizesToScale(context.SizeOfOperand());
......@@ -479,13 +429,11 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject() {
Output(Bytecode::kToObject);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() {
Output(Bytecode::kToName);
return *this;
......@@ -496,204 +444,24 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
size_t current_offset = pipeline()->FlushForOffset();
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(current_offset, label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(current_offset);
pipeline_->BindLabel(label);
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
BytecodeLabel* label) {
DCHECK(!label->is_bound());
DCHECK(target.is_bound());
// There is no need to flush the pipeline here, it will have been
// flushed when |target| was bound.
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(target.offset(), label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(target.offset());
pipeline_->BindLabel(target, label);
LeaveBasicBlock();
return *this;
}
// static
Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand(
Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
return Bytecode::kJumpConstant;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfTrueConstant;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfFalseConstant;
case Bytecode::kJumpIfToBooleanTrue:
return Bytecode::kJumpIfToBooleanTrueConstant;
case Bytecode::kJumpIfToBooleanFalse:
return Bytecode::kJumpIfToBooleanFalseConstant;
case Bytecode::kJumpIfNotHole:
return Bytecode::kJumpIfNotHoleConstant;
case Bytecode::kJumpIfNull:
return Bytecode::kJumpIfNullConstant;
case Bytecode::kJumpIfUndefined:
return Bytecode::kJumpIfUndefinedConstant;
default:
UNREACHABLE();
return Bytecode::kIllegal;
}
}
void BytecodeArrayBuilder::PatchJumpWith8BitOperand(
ZoneVector<uint8_t>* bytecodes, size_t jump_location, int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes->at(jump_location));
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
size_t operand_location = jump_location + 1;
DCHECK_EQ(bytecodes->at(operand_location), 0);
if (Bytecodes::SizeForSignedOperand(delta) == OperandSize::kByte) {
// The jump fits within the range of an Imm operand, so cancel
// the reservation and jump directly.
constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
bytecodes->at(operand_location) = static_cast<uint8_t>(delta);
} else {
// The jump does not fit within the range of an Imm 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(Bytecodes::SizeForUnsignedOperand(entry) == OperandSize::kByte);
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
bytecodes->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
bytecodes->at(operand_location) = static_cast<uint8_t>(entry);
}
}
void BytecodeArrayBuilder::PatchJumpWith16BitOperand(
ZoneVector<uint8_t>* bytecodes, size_t jump_location, int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes->at(jump_location));
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
size_t operand_location = jump_location + 1;
uint8_t operand_bytes[2];
if (Bytecodes::SizeForSignedOperand(delta) <= OperandSize::kShort) {
constant_array_builder()->DiscardReservedEntry(OperandSize::kShort);
WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(delta));
} else {
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
bytecodes->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kShort, handle(Smi::FromInt(delta), isolate()));
WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry));
}
DCHECK(bytecodes->at(operand_location) == 0 &&
bytecodes->at(operand_location + 1) == 0);
bytecodes->at(operand_location++) = operand_bytes[0];
bytecodes->at(operand_location) = operand_bytes[1];
}
void BytecodeArrayBuilder::PatchJumpWith32BitOperand(
ZoneVector<uint8_t>* bytecodes, size_t jump_location, int delta) {
DCHECK(Bytecodes::IsJumpImmediate(
Bytecodes::FromByte(bytecodes->at(jump_location))));
constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad);
uint8_t operand_bytes[4];
WriteUnalignedUInt32(operand_bytes, static_cast<uint32_t>(delta));
size_t operand_location = jump_location + 1;
DCHECK(bytecodes->at(operand_location) == 0 &&
bytecodes->at(operand_location + 1) == 0 &&
bytecodes->at(operand_location + 2) == 0 &&
bytecodes->at(operand_location + 3) == 0);
bytecodes->at(operand_location++) = operand_bytes[0];
bytecodes->at(operand_location++) = operand_bytes[1];
bytecodes->at(operand_location++) = operand_bytes[2];
bytecodes->at(operand_location) = operand_bytes[3];
}
void BytecodeArrayBuilder::PatchJump(size_t jump_target, size_t jump_location) {
ZoneVector<uint8_t>* bytecodes = bytecode_array_writer()->bytecodes();
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes->at(jump_location));
int delta = static_cast<int>(jump_target - jump_location);
int prefix_offset = 0;
OperandScale operand_scale = OperandScale::kSingle;
if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) {
// If a prefix scaling bytecode is emitted the target offset is one
// less than the case of no prefix scaling bytecode.
delta -= 1;
prefix_offset = 1;
operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode);
jump_bytecode =
Bytecodes::FromByte(bytecodes->at(jump_location + prefix_offset));
}
DCHECK(Bytecodes::IsJump(jump_bytecode));
switch (operand_scale) {
case OperandScale::kSingle:
PatchJumpWith8BitOperand(bytecodes, jump_location, delta);
break;
case OperandScale::kDouble:
PatchJumpWith16BitOperand(bytecodes, jump_location + prefix_offset,
delta);
break;
case OperandScale::kQuadruple:
PatchJumpWith32BitOperand(bytecodes, jump_location + prefix_offset,
delta);
break;
default:
UNREACHABLE();
}
unbound_jumps_--;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label) {
if (label->is_bound()) {
// Label has been bound already so this is a backwards jump.
size_t current_offset = pipeline()->FlushForOffset();
CHECK_GE(current_offset, label->offset());
CHECK_LE(current_offset, static_cast<size_t>(kMaxInt));
size_t abs_delta = current_offset - label->offset();
int delta = -static_cast<int>(abs_delta);
OperandSize operand_size = Bytecodes::SizeForSignedOperand(delta);
if (operand_size > OperandSize::kByte) {
// Adjust for scaling byte prefix for wide jump offset.
DCHECK_LE(delta, 0);
delta -= 1;
}
OutputScaled(jump_bytecode, Bytecodes::OperandSizesToScale(operand_size),
SignedOperand(delta, operand_size));
} 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.
unbound_jumps_++;
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry();
OutputScaled(jump_bytecode,
Bytecodes::OperandSizesToScale(reserved_operand_size), 0);
// Calculate the label position by flushing for offset after emitting the
// jump bytecode.
size_t offset = pipeline()->FlushForOffset();
OperandScale operand_scale =
Bytecodes::OperandSizesToScale(reserved_operand_size);
offset -= Bytecodes::Size(jump_bytecode, operand_scale);
if (Bytecodes::OperandScaleRequiresPrefixBytecode(operand_scale)) {
offset -= 1;
}
label->set_referrer(offset);
}
BytecodeNode node(jump_bytecode, 0, OperandScale::kSingle);
AttachSourceInfo(&node);
pipeline_->WriteJump(&node, label);
LeaveBasicBlock();
return *this;
}
......@@ -723,6 +491,11 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined(
return OutputJump(Bytecode::kJumpIfUndefined, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole(
BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfNotHole, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) {
if (position != RelocInfo::kNoPosition) {
// We need to attach a non-breakable source position to a stack check,
......@@ -733,23 +506,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole(
BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfNotHole, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
Output(Bytecode::kThrow);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ReThrow() {
Output(Bytecode::kReThrow);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn);
return_seen_in_block_ = true;
......@@ -792,7 +558,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
OperandScale operand_scale =
Bytecodes::OperandSizesToScale(index.SizeOfOperand());
......@@ -800,7 +565,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator(
Register generator) {
OperandScale operand_scale =
......@@ -810,7 +574,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator(
Register generator) {
OperandScale operand_scale =
......@@ -820,37 +583,31 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(int handler_id,
bool will_catch) {
size_t offset = pipeline()->FlushForOffset();
handler_table_builder()->SetHandlerTarget(handler_id, offset);
BytecodeLabel handler;
Bind(&handler);
handler_table_builder()->SetHandlerTarget(handler_id, handler.offset());
handler_table_builder()->SetPrediction(handler_id, will_catch);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id,
Register context) {
size_t offset = pipeline()->FlushForOffset();
handler_table_builder()->SetTryRegionStart(handler_id, offset);
BytecodeLabel try_begin;
Bind(&try_begin);
handler_table_builder()->SetTryRegionStart(handler_id, try_begin.offset());
handler_table_builder()->SetContextRegister(handler_id, context);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryEnd(int handler_id) {
size_t offset = pipeline()->FlushForOffset();
handler_table_builder()->SetTryRegionEnd(handler_id, offset);
BytecodeLabel try_end;
Bind(&try_end);
handler_table_builder()->SetTryRegionEnd(handler_id, try_end.offset());
return *this;
}
void BytecodeArrayBuilder::LeaveBasicBlock() {
pipeline()->FlushBasicBlock();
return_seen_in_block_ = false;
}
void BytecodeArrayBuilder::EnsureReturn() {
if (!return_seen_in_block_) {
LoadUndefined();
......@@ -892,7 +649,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size);
......@@ -911,7 +667,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count,
Register first_return) {
......@@ -942,7 +697,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
LanguageMode language_mode) {
OperandScale operand_scale =
......@@ -1093,7 +847,6 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) {
switch (op) {
......@@ -1107,7 +860,6 @@ Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) {
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
switch (op) {
......@@ -1135,7 +887,6 @@ Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreNamedProperty(
LanguageMode language_mode) {
......@@ -1150,7 +901,6 @@ Bytecode BytecodeArrayBuilder::BytecodeForStoreNamedProperty(
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreKeyedProperty(
LanguageMode language_mode) {
......@@ -1165,14 +915,12 @@ Bytecode BytecodeArrayBuilder::BytecodeForStoreKeyedProperty(
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForLoadGlobal(TypeofMode typeof_mode) {
return typeof_mode == INSIDE_TYPEOF ? Bytecode::kLdaGlobalInsideTypeof
: Bytecode::kLdaGlobal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal(
LanguageMode language_mode) {
......@@ -1187,7 +935,6 @@ Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal(
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreLookupSlot(
LanguageMode language_mode) {
......@@ -1217,7 +964,6 @@ Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments(
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) {
switch (language_mode) {
......
......@@ -11,7 +11,6 @@
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
#include "src/interpreter/source-position-table.h"
#include "src/zone-containers.h"
namespace v8 {
......@@ -297,8 +296,6 @@ class BytecodeArrayBuilder final : public ZoneObject {
static Bytecode BytecodeForDelete(LanguageMode language_mode);
static Bytecode BytecodeForCall(TailCallMode tail_call_mode);
static Bytecode GetJumpWithConstantOperand(Bytecode jump_smi8_operand);
void Output(Bytecode bytecode);
void OutputScaled(Bytecode bytecode, OperandScale operand_scale,
uint32_t operand0, uint32_t operand1, uint32_t operand2,
......@@ -312,15 +309,7 @@ class BytecodeArrayBuilder final : public ZoneObject {
BytecodeArrayBuilder& OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label);
void PatchJump(size_t jump_target, size_t jump_location);
void PatchJumpWith8BitOperand(ZoneVector<uint8_t>* bytecodes,
size_t jump_location, int delta);
void PatchJumpWith16BitOperand(ZoneVector<uint8_t>* bytecodes,
size_t jump_location, int delta);
void PatchJumpWith32BitOperand(ZoneVector<uint8_t>* bytecodes,
size_t jump_location, int delta);
void LeaveBasicBlock();
bool OperandIsValid(Bytecode bytecode, OperandScale operand_scale,
int operand_index, uint32_t operand_value) const;
......@@ -337,6 +326,8 @@ class BytecodeArrayBuilder final : public ZoneObject {
// during bytecode generation.
BytecodeArrayBuilder& Illegal();
void LeaveBasicBlock() { return_seen_in_block_ = false; }
Isolate* isolate() const { return isolate_; }
BytecodeArrayWriter* bytecode_array_writer() {
return &bytecode_array_writer_;
......@@ -351,18 +342,13 @@ class BytecodeArrayBuilder final : public ZoneObject {
HandlerTableBuilder* handler_table_builder() {
return &handler_table_builder_;
}
SourcePositionTableBuilder* source_position_table_builder() {
return &source_position_table_builder_;
}
Isolate* isolate_;
Zone* zone_;
bool bytecode_generated_;
ConstantArrayBuilder constant_array_builder_;
HandlerTableBuilder handler_table_builder_;
SourcePositionTableBuilder source_position_table_builder_;
bool return_seen_in_block_;
int unbound_jumps_;
int parameter_count_;
int local_register_count_;
int context_register_count_;
......@@ -375,47 +361,6 @@ class BytecodeArrayBuilder final : public ZoneObject {
DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder);
};
// A label representing a branch target in a bytecode array. When a
// label is bound, it represents a known position in the bytecode
// array. For labels that are forward references there can be at most
// one reference whilst it is unbound.
class BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
bool is_bound() const { return bound_; }
size_t offset() const { return offset_; }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void bind_to(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset);
offset_ = offset;
bound_ = true;
}
void set_referrer(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset);
offset_ = offset;
}
bool is_forward_target() const {
return offset() != kInvalidOffset && !is_bound();
}
// There are three states for a label:
// bound_ offset_
// UNSET false kInvalidOffset
// FORWARD_TARGET false Offset of referring jump
// BACKWARD_TARGET true Offset of label in bytecode array when bound
bool bound_;
size_t offset_;
friend class BytecodeArrayBuilder;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
......
......@@ -4,39 +4,104 @@
#include "src/interpreter/bytecode-array-writer.h"
#include <iomanip>
#include "src/interpreter/source-position-table.h"
#include "src/api.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/log.h"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeArrayWriter::BytecodeArrayWriter(
Zone* zone, SourcePositionTableBuilder* source_position_table_builder)
: bytecodes_(zone),
Isolate* isolate, Zone* zone, ConstantArrayBuilder* constant_array_builder)
: isolate_(isolate),
bytecodes_(zone),
max_register_count_(0),
source_position_table_builder_(source_position_table_builder) {}
unbound_jumps_(0),
source_position_table_builder_(isolate, zone),
constant_array_builder_(constant_array_builder) {
LOG_CODE_EVENT(isolate_, CodeStartLinePosInfoRecordEvent(
source_position_table_builder()));
}
// override
BytecodeArrayWriter::~BytecodeArrayWriter() {}
// override
size_t BytecodeArrayWriter::FlushForOffset() { return bytecodes()->size(); }
Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) {
DCHECK_EQ(0, unbound_jumps_);
int bytecode_size = static_cast<int>(bytecodes()->size());
// All locals need a frame slot for the debugger, but may not be
// present in generated code.
int frame_size_for_locals = fixed_register_count * kPointerSize;
int frame_size_used = max_register_count() * kPointerSize;
int frame_size = std::max(frame_size_for_locals, frame_size_used);
Handle<FixedArray> constant_pool = constant_array_builder()->ToFixedArray();
Handle<ByteArray> source_position_table =
source_position_table_builder()->ToSourcePositionTable();
Handle<BytecodeArray> bytecode_array = isolate_->factory()->NewBytecodeArray(
bytecode_size, &bytecodes()->front(), frame_size, parameter_count,
constant_pool);
bytecode_array->set_handler_table(*handler_table);
bytecode_array->set_source_position_table(*source_position_table);
void* line_info = source_position_table_builder()->DetachJITHandlerData();
LOG_CODE_EVENT(isolate_, CodeEndLinePosInfoRecordEvent(
AbstractCode::cast(*bytecode_array), line_info));
return bytecode_array;
}
// override
void BytecodeArrayWriter::Write(BytecodeNode* node) {
DCHECK(!Bytecodes::IsJump(node->bytecode()));
UpdateSourcePositionTable(node);
EmitBytecode(node);
}
// override
void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsJump(node->bytecode()));
UpdateSourcePositionTable(node);
EmitJump(node, label);
}
// override
void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) {
size_t current_offset = bytecodes()->size();
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(current_offset, label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(current_offset);
}
// override
void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target,
BytecodeLabel* label) {
DCHECK(!label->is_bound());
DCHECK(target.is_bound());
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(target.offset(), label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(target.offset());
}
void BytecodeArrayWriter::UpdateSourcePositionTable(
const BytecodeNode* const node) {
int bytecode_offset = static_cast<int>(bytecodes()->size());
const BytecodeSourceInfo& source_info = node->source_info();
if (source_info.is_valid()) {
source_position_table_builder_->AddPosition(bytecode_offset,
source_info.source_position(),
source_info.is_statement());
source_position_table_builder()->AddPosition(bytecode_offset,
source_info.source_position(),
source_info.is_statement());
}
}
......@@ -95,11 +160,180 @@ void BytecodeArrayWriter::EmitBytecode(const BytecodeNode* const node) {
}
}
// override
void BytecodeArrayWriter::FlushBasicBlock() {}
// TODO(rmcilroy): This is the same as SignedOperand in BytecodeArrayBuilder.
// Once we move the scalable operand processing here remove the SignedOperand
// in BytecodeArrayBuilder.
static uint32_t SignedOperand(int value, OperandSize size) {
switch (size) {
case OperandSize::kByte:
return static_cast<uint8_t>(value & 0xff);
case OperandSize::kShort:
return static_cast<uint16_t>(value & 0xffff);
case OperandSize::kQuad:
return static_cast<uint32_t>(value);
case OperandSize::kNone:
UNREACHABLE();
}
return 0;
}
int BytecodeArrayWriter::GetMaximumFrameSizeUsed() {
return max_register_count_ * kPointerSize;
// static
Bytecode GetJumpWithConstantOperand(Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
return Bytecode::kJumpConstant;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfTrueConstant;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfFalseConstant;
case Bytecode::kJumpIfToBooleanTrue:
return Bytecode::kJumpIfToBooleanTrueConstant;
case Bytecode::kJumpIfToBooleanFalse:
return Bytecode::kJumpIfToBooleanFalseConstant;
case Bytecode::kJumpIfNotHole:
return Bytecode::kJumpIfNotHoleConstant;
case Bytecode::kJumpIfNull:
return Bytecode::kJumpIfNullConstant;
case Bytecode::kJumpIfUndefined:
return Bytecode::kJumpIfUndefinedConstant;
default:
UNREACHABLE();
return Bytecode::kIllegal;
}
}
void BytecodeArrayWriter::PatchJumpWith8BitOperand(size_t jump_location,
int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
size_t operand_location = jump_location + 1;
DCHECK_EQ(bytecodes()->at(operand_location), 0);
if (Bytecodes::SizeForSignedOperand(delta) == OperandSize::kByte) {
// The jump fits within the range of an Imm operand, so cancel
// the reservation and jump directly.
constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
bytecodes()->at(operand_location) = static_cast<uint8_t>(delta);
} else {
// The jump does not fit within the range of an Imm 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(Bytecodes::SizeForUnsignedOperand(entry) == OperandSize::kByte);
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
bytecodes()->at(operand_location) = static_cast<uint8_t>(entry);
}
}
void BytecodeArrayWriter::PatchJumpWith16BitOperand(size_t jump_location,
int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
size_t operand_location = jump_location + 1;
uint8_t operand_bytes[2];
if (Bytecodes::SizeForSignedOperand(delta) <= OperandSize::kShort) {
constant_array_builder()->DiscardReservedEntry(OperandSize::kShort);
WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(delta));
} else {
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kShort, handle(Smi::FromInt(delta), isolate()));
WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry));
}
DCHECK(bytecodes()->at(operand_location) == 0 &&
bytecodes()->at(operand_location + 1) == 0);
bytecodes()->at(operand_location++) = operand_bytes[0];
bytecodes()->at(operand_location) = operand_bytes[1];
}
void BytecodeArrayWriter::PatchJumpWith32BitOperand(size_t jump_location,
int delta) {
DCHECK(Bytecodes::IsJumpImmediate(
Bytecodes::FromByte(bytecodes()->at(jump_location))));
constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad);
uint8_t operand_bytes[4];
WriteUnalignedUInt32(operand_bytes, static_cast<uint32_t>(delta));
size_t operand_location = jump_location + 1;
DCHECK(bytecodes()->at(operand_location) == 0 &&
bytecodes()->at(operand_location + 1) == 0 &&
bytecodes()->at(operand_location + 2) == 0 &&
bytecodes()->at(operand_location + 3) == 0);
bytecodes()->at(operand_location++) = operand_bytes[0];
bytecodes()->at(operand_location++) = operand_bytes[1];
bytecodes()->at(operand_location++) = operand_bytes[2];
bytecodes()->at(operand_location) = operand_bytes[3];
}
void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
int delta = static_cast<int>(jump_target - jump_location);
int prefix_offset = 0;
OperandScale operand_scale = OperandScale::kSingle;
if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) {
// If a prefix scaling bytecode is emitted the target offset is one
// less than the case of no prefix scaling bytecode.
delta -= 1;
prefix_offset = 1;
operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode);
jump_bytecode =
Bytecodes::FromByte(bytecodes()->at(jump_location + prefix_offset));
}
DCHECK(Bytecodes::IsJump(jump_bytecode));
switch (operand_scale) {
case OperandScale::kSingle:
PatchJumpWith8BitOperand(jump_location, delta);
break;
case OperandScale::kDouble:
PatchJumpWith16BitOperand(jump_location + prefix_offset, delta);
break;
case OperandScale::kQuadruple:
PatchJumpWith32BitOperand(jump_location + prefix_offset, delta);
break;
default:
UNREACHABLE();
}
unbound_jumps_--;
}
void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsJump(node->bytecode()));
DCHECK_EQ(0, node->operand(0));
size_t current_offset = bytecodes()->size();
if (label->is_bound()) {
CHECK_GE(current_offset, label->offset());
CHECK_LE(current_offset, static_cast<size_t>(kMaxInt));
// Label has been bound already so this is a backwards jump.
size_t abs_delta = current_offset - label->offset();
int delta = -static_cast<int>(abs_delta);
OperandSize operand_size = Bytecodes::SizeForSignedOperand(delta);
if (operand_size > OperandSize::kByte) {
// Adjust for scaling byte prefix for wide jump offset.
DCHECK_LE(delta, 0);
delta -= 1;
}
node->set_bytecode(node->bytecode(), SignedOperand(delta, operand_size),
Bytecodes::OperandSizesToScale(operand_size));
} 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.
unbound_jumps_++;
label->set_referrer(current_offset);
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry();
OperandScale operand_scale =
Bytecodes::OperandSizesToScale(reserved_operand_size);
node->set_bytecode(node->bytecode(), 0, operand_scale);
}
EmitBytecode(node);
}
} // namespace interpreter
......
......@@ -6,40 +6,61 @@
#define V8_INTERPRETER_BYTECODE_ARRAY_WRITER_H_
#include "src/interpreter/bytecode-pipeline.h"
#include "src/interpreter/source-position-table.h"
namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeLabel;
class SourcePositionTableBuilder;
class ConstantArrayBuilder;
// Class for emitting bytecode as the final stage of the bytecode
// generation pipeline.
class BytecodeArrayWriter final : public BytecodePipelineStage {
public:
BytecodeArrayWriter(
Zone* zone, SourcePositionTableBuilder* source_position_table_builder);
BytecodeArrayWriter(Isolate* isolate, Zone* zone,
ConstantArrayBuilder* constant_array_builder);
virtual ~BytecodeArrayWriter();
// BytecodePipelineStage interface.
void Write(BytecodeNode* node) override;
size_t FlushForOffset() override;
void FlushBasicBlock() override;
// Get the bytecode vector.
ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; }
// Returns the size in bytes of the frame associated with the
// bytecode written.
int GetMaximumFrameSizeUsed();
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override;
void BindLabel(BytecodeLabel* label) override;
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override;
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) override;
private:
void PatchJump(size_t jump_target, size_t jump_location);
void PatchJumpWith8BitOperand(size_t jump_location, int delta);
void PatchJumpWith16BitOperand(size_t jump_location, int delta);
void PatchJumpWith32BitOperand(size_t jump_location, int delta);
void EmitBytecode(const BytecodeNode* const node);
void EmitJump(BytecodeNode* node, BytecodeLabel* label);
void UpdateSourcePositionTable(const BytecodeNode* const node);
Isolate* isolate() { return isolate_; }
ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; }
SourcePositionTableBuilder* source_position_table_builder() {
return &source_position_table_builder_;
}
ConstantArrayBuilder* constant_array_builder() {
return constant_array_builder_;
}
int max_register_count() { return max_register_count_; }
Isolate* isolate_;
ZoneVector<uint8_t> bytecodes_;
int max_register_count_;
SourcePositionTableBuilder* source_position_table_builder_;
int unbound_jumps_;
SourcePositionTableBuilder source_position_table_builder_;
ConstantArrayBuilder* constant_array_builder_;
friend class BytecodeArrayWriterUnittest;
DISALLOW_COPY_AND_ASSIGN(BytecodeArrayWriter);
};
......
......@@ -17,7 +17,6 @@ namespace v8 {
namespace internal {
namespace interpreter {
// Scoped class tracking context objects created by the visitor. Represents
// mutations of the context chain within the function body, allowing pushing and
// popping of the current {context_register} during visitation.
......@@ -88,7 +87,6 @@ class BytecodeGenerator::ContextScope BASE_EMBEDDED {
bool should_pop_context_;
};
// Scoped class for tracking control statements entered by the
// visitor. The pattern derives AstGraphBuilder::ControlScope.
class BytecodeGenerator::ControlScope BASE_EMBEDDED {
......@@ -124,7 +122,6 @@ class BytecodeGenerator::ControlScope BASE_EMBEDDED {
DISALLOW_COPY_AND_ASSIGN(ControlScope);
};
// Helper class for a try-finally control scope. It can record intercepted
// control-flow commands that cause entry into a finally-block, and re-apply
// them after again leaving that block. Special tokens are used to identify
......@@ -203,7 +200,6 @@ class BytecodeGenerator::ControlScope::DeferredCommands final {
Register result_register_;
};
// Scoped class for dealing with control flow reaching the function level.
class BytecodeGenerator::ControlScopeForTopLevel final
: public BytecodeGenerator::ControlScope {
......@@ -229,7 +225,6 @@ class BytecodeGenerator::ControlScopeForTopLevel final
}
};
// Scoped class for enabling break inside blocks and switch blocks.
class BytecodeGenerator::ControlScopeForBreakable final
: public BytecodeGenerator::ControlScope {
......@@ -261,7 +256,6 @@ class BytecodeGenerator::ControlScopeForBreakable final
BreakableControlFlowBuilder* control_builder_;
};
// Scoped class for enabling 'break' and 'continue' in iteration
// constructs, e.g. do...while, while..., for...
class BytecodeGenerator::ControlScopeForIteration final
......@@ -296,7 +290,6 @@ class BytecodeGenerator::ControlScopeForIteration final
LoopBuilder* loop_builder_;
};
// Scoped class for enabling 'throw' in try-catch constructs.
class BytecodeGenerator::ControlScopeForTryCatch final
: public BytecodeGenerator::ControlScope {
......@@ -325,7 +318,6 @@ class BytecodeGenerator::ControlScopeForTryCatch final
}
};
// Scoped class for enabling control flow through try-finally constructs.
class BytecodeGenerator::ControlScopeForTryFinally final
: public BytecodeGenerator::ControlScope {
......@@ -361,7 +353,6 @@ class BytecodeGenerator::ControlScopeForTryFinally final
DeferredCommands* commands_;
};
void BytecodeGenerator::ControlScope::PerformCommand(Command command,
Statement* statement) {
ControlScope* current = this;
......@@ -384,7 +375,6 @@ void BytecodeGenerator::ControlScope::PerformCommand(Command command,
UNREACHABLE();
}
class BytecodeGenerator::RegisterAllocationScope {
public:
explicit RegisterAllocationScope(BytecodeGenerator* generator)
......@@ -442,7 +432,6 @@ class BytecodeGenerator::RegisterAllocationScope {
DISALLOW_COPY_AND_ASSIGN(RegisterAllocationScope);
};
// Scoped base class for determining where the result of an expression
// is stored.
class BytecodeGenerator::ExpressionResultScope {
......@@ -490,7 +479,6 @@ class BytecodeGenerator::ExpressionResultScope {
DISALLOW_COPY_AND_ASSIGN(ExpressionResultScope);
};
// Scoped class used when the result of the current expression is not
// expected to produce a result.
class BytecodeGenerator::EffectResultScope final
......@@ -505,7 +493,6 @@ class BytecodeGenerator::EffectResultScope final
virtual void SetResultInRegister(Register reg) {}
};
// Scoped class used when the result of the current expression to be
// evaluated should go into the interpreter's accumulator register.
class BytecodeGenerator::AccumulatorResultScope final
......@@ -522,7 +509,6 @@ class BytecodeGenerator::AccumulatorResultScope final
}
};
// Scoped class used when the result of the current expression to be
// evaluated should go into an interpreter register.
class BytecodeGenerator::RegisterResultScope final
......@@ -614,7 +600,6 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode() {
return builder()->ToBytecodeArray();
}
void BytecodeGenerator::MakeBytecodeBody() {
// Build the arguments object if it is used.
VisitArgumentsObject(scope()->arguments());
......@@ -729,7 +714,6 @@ void BytecodeGenerator::VisitBlock(Block* stmt) {
}
}
void BytecodeGenerator::VisitBlockDeclarationsAndStatements(Block* stmt) {
BlockBuilder block_builder(builder());
ControlScopeForBreakable execution_control(this, stmt, &block_builder);
......@@ -740,7 +724,6 @@ void BytecodeGenerator::VisitBlockDeclarationsAndStatements(Block* stmt) {
if (stmt->labels() != nullptr) block_builder.EndBlock();
}
void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
Variable* variable = decl->proxy()->var();
VariableMode mode = decl->mode();
......@@ -802,7 +785,6 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
}
}
void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
Variable* variable = decl->proxy()->var();
switch (variable->location()) {
......@@ -849,17 +831,14 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
}
}
void BytecodeGenerator::VisitImportDeclaration(ImportDeclaration* decl) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitDeclarations(
ZoneList<Declaration*>* declarations) {
RegisterAllocationScope register_scope(this);
......@@ -888,7 +867,6 @@ void BytecodeGenerator::VisitDeclarations(
globals()->clear();
}
void BytecodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
for (int i = 0; i < statements->length(); i++) {
// Allocate an outer register allocations scope for the statement.
......@@ -899,17 +877,14 @@ void BytecodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
}
}
void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
builder()->SetStatementPosition(stmt);
VisitForEffect(stmt->expression());
}
void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
}
void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
builder()->SetStatementPosition(stmt);
BytecodeLabel else_label, end_label;
......@@ -939,32 +914,27 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
}
}
void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
SloppyBlockFunctionStatement* stmt) {
Visit(stmt->statement());
}
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
builder()->SetStatementPosition(stmt);
execution_control()->Continue(stmt->target());
}
void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
builder()->SetStatementPosition(stmt);
execution_control()->Break(stmt->target());
}
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
builder()->SetStatementPosition(stmt);
VisitForAccumulatorValue(stmt->expression());
execution_control()->ReturnAccumulator();
}
void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
builder()->SetStatementPosition(stmt);
VisitForAccumulatorValue(stmt->expression());
......@@ -973,7 +943,6 @@ void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
VisitInScope(stmt->statement(), stmt->scope());
}
void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// We need this scope because we visit for register values. We have to
// maintain a execution result scope where registers can be allocated.
......@@ -1024,7 +993,6 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
switch_builder.SetBreakTarget(done_label);
}
void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
// Handled entirely in VisitSwitchStatement.
UNREACHABLE();
......@@ -1073,7 +1041,6 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
loop_builder.EndLoop();
}
void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
if (stmt->init() != nullptr) {
Visit(stmt->init());
......@@ -1100,7 +1067,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
loop_builder.EndLoop();
}
void BytecodeGenerator::VisitForInAssignment(Expression* expr,
FeedbackVectorSlot slot) {
DCHECK(expr->IsValidReferenceExpression());
......@@ -1174,7 +1140,6 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
}
}
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
if (stmt->subject()->IsNullLiteral() ||
stmt->subject()->IsUndefinedLiteral()) {
......@@ -1226,7 +1191,6 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
builder()->Bind(&subject_undefined_label);
}
void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
LoopBuilder loop_builder(builder());
ControlScopeForIteration control_scope(this, stmt, &loop_builder);
......@@ -1246,7 +1210,6 @@ void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
loop_builder.EndLoop();
}
void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
TryCatchBuilder try_control_builder(builder());
Register no_reg;
......@@ -1283,7 +1246,6 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
try_control_builder.EndCatch();
}
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
TryFinallyBuilder try_control_builder(builder(), IsInsideTryCatch());
Register no_reg;
......@@ -1348,13 +1310,11 @@ void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
commands.ApplyDeferredCommands();
}
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
builder()->SetStatementPosition(stmt);
builder()->Debugger();
}
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
// Find or build a shared function info.
Handle<SharedFunctionInfo> shared_info =
......@@ -1367,7 +1327,6 @@ void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
if (expr->scope()->ContextLocalCount() > 0) {
VisitNewLocalBlockContext(expr->scope());
......@@ -1525,13 +1484,11 @@ void BytecodeGenerator::VisitNativeFunctionLiteral(
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitDoExpression(DoExpression* expr) {
VisitBlock(expr->block());
VisitVariableProxy(expr->result());
}
void BytecodeGenerator::VisitConditional(Conditional* expr) {
// TODO(rmcilroy): Spot easy cases where there code would not need to
// emit the then block or the else block, e.g. condition is
......@@ -1552,7 +1509,6 @@ void BytecodeGenerator::VisitConditional(Conditional* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitLiteral(Literal* expr) {
if (!execution_result()->IsEffect()) {
Handle<Object> value = expr->value();
......@@ -1575,7 +1531,6 @@ void BytecodeGenerator::VisitLiteral(Literal* expr) {
}
}
void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
// Materialize a regular expression literal.
builder()->CreateRegExpLiteral(expr->pattern(), expr->literal_index(),
......@@ -1583,7 +1538,6 @@ void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
// Copy the literal boilerplate.
int fast_clone_properties_count = 0;
......@@ -1787,7 +1741,6 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
execution_result()->SetResultInRegister(literal);
}
void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Deep-copy the literal boilerplate.
builder()->CreateArrayLiteral(expr->constant_elements(),
......@@ -1827,7 +1780,6 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
builder()->SetExpressionPosition(proxy);
VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot());
......@@ -2129,7 +2081,6 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
}
}
void BytecodeGenerator::VisitAssignment(Assignment* expr) {
DCHECK(expr->target()->IsValidReferenceExpressionOrThis());
Register object, key, home_object, value;
......@@ -2357,7 +2308,6 @@ void BytecodeGenerator::VisitThrow(Throw* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) {
LhsKind property_kind = Property::GetAssignType(expr);
FeedbackVectorSlot slot = expr->PropertyFeedbackSlot();
......@@ -2651,7 +2601,6 @@ void BytecodeGenerator::VisitCallNew(CallNew* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
if (expr->is_jsruntime()) {
......@@ -2672,14 +2621,12 @@ void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitVoid(UnaryOperation* expr) {
VisitForEffect(expr->expression());
builder()->LoadUndefined();
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) {
if (expr->expression()->IsVariableProxy()) {
// Typeof does not throw a reference error on global variables, hence we
......@@ -2694,14 +2641,12 @@ void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitNot(UnaryOperation* expr) {
VisitForAccumulatorValue(expr->expression());
builder()->LogicalNot();
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
switch (expr->op()) {
case Token::Value::NOT:
......@@ -2727,7 +2672,6 @@ void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
}
void BytecodeGenerator::VisitDelete(UnaryOperation* expr) {
if (expr->expression()->IsProperty()) {
// Delete of an object property is allowed both in sloppy
......@@ -2789,7 +2733,6 @@ void BytecodeGenerator::VisitDelete(UnaryOperation* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
DCHECK(expr->expression()->IsValidReferenceExpressionOrThis());
......@@ -2911,7 +2854,6 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
}
}
void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
switch (binop->op()) {
case Token::COMMA:
......@@ -2929,7 +2871,6 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
}
}
void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
Register lhs = VisitForRegisterValue(expr->left());
VisitForAccumulatorValue(expr->right());
......@@ -2938,7 +2879,6 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
Register lhs = VisitForRegisterValue(expr->left());
VisitForAccumulatorValue(expr->right());
......@@ -2946,39 +2886,32 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitSpread(Spread* expr) { UNREACHABLE(); }
void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
UNREACHABLE();
}
void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) {
execution_result()->SetResultInRegister(Register::function_closure());
}
void BytecodeGenerator::VisitSuperCallReference(SuperCallReference* expr) {
// Handled by VisitCall().
UNREACHABLE();
}
void BytecodeGenerator::VisitSuperPropertyReference(
SuperPropertyReference* expr) {
builder()->CallRuntime(Runtime::kThrowUnsupportedSuperError, Register(0), 0);
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitCommaExpression(BinaryOperation* binop) {
VisitForEffect(binop->left());
Visit(binop->right());
}
void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
Expression* left = binop->left();
Expression* right = binop->right();
......@@ -2997,7 +2930,6 @@ void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
Expression* left = binop->left();
Expression* right = binop->right();
......@@ -3016,12 +2948,10 @@ void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitRewritableExpression(RewritableExpression* expr) {
Visit(expr->expression());
}
void BytecodeGenerator::VisitNewLocalFunctionContext() {
AccumulatorResultScope accumulator_execution_result(this);
Scope* scope = this->scope();
......@@ -3045,7 +2975,6 @@ void BytecodeGenerator::VisitNewLocalFunctionContext() {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitBuildLocalActivationContext() {
Scope* scope = this->scope();
......@@ -3074,7 +3003,6 @@ void BytecodeGenerator::VisitBuildLocalActivationContext() {
}
}
void BytecodeGenerator::VisitNewLocalBlockContext(Scope* scope) {
AccumulatorResultScope accumulator_execution_result(this);
DCHECK(scope->is_block_scope());
......@@ -3128,7 +3056,6 @@ void BytecodeGenerator::VisitNewLocalCatchContext(Variable* variable) {
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitObjectLiteralAccessor(
Register home_object, ObjectLiteralProperty* property, Register value_out) {
// TODO(rmcilroy): Replace value_out with VisitForRegister();
......@@ -3154,7 +3081,6 @@ void BytecodeGenerator::VisitSetHomeObject(Register value, Register home_object,
}
}
void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
if (variable == nullptr) return;
......@@ -3189,7 +3115,6 @@ void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
}
void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
if (variable == nullptr) return;
......@@ -3198,7 +3123,6 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
VisitVariableAssignment(variable, Token::INIT, FeedbackVectorSlot::Invalid());
}
void BytecodeGenerator::VisitFunctionClosureForContext() {
AccumulatorResultScope accumulator_execution_result(this);
Scope* closure_scope = execution_context()->scope()->ClosureScope();
......@@ -3225,7 +3149,6 @@ void BytecodeGenerator::VisitFunctionClosureForContext() {
execution_result()->SetResultInAccumulator();
}
// Visits the expression |expr| and places the result in the accumulator.
void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) {
AccumulatorResultScope accumulator_scope(this);
......@@ -3246,7 +3169,6 @@ void BytecodeGenerator::VisitForEffect(Expression* expr) {
Visit(expr);
}
// Visits the expression |expr| and returns the register containing
// the expression result.
Register BytecodeGenerator::VisitForRegisterValue(Expression* expr) {
......@@ -3270,12 +3192,10 @@ void BytecodeGenerator::VisitInScope(Statement* stmt, Scope* scope) {
Visit(stmt);
}
LanguageMode BytecodeGenerator::language_mode() const {
return execution_context()->scope()->language_mode();
}
int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const {
return TypeFeedbackVector::GetIndex(slot);
}
......
......@@ -7,6 +7,7 @@
#include "src/ast/ast.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_INTERPRETER_BYTECODE_LABEL_H_
#define V8_INTERPRETER_BYTECODE_LABEL_H_
namespace v8 {
namespace internal {
namespace interpreter {
// A label representing a branch target in a bytecode array. When a
// label is bound, it represents a known position in the bytecode
// array. For labels that are forward references there can be at most
// one reference whilst it is unbound.
class BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
bool is_bound() const { return bound_; }
size_t offset() const { return offset_; }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void bind_to(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset);
offset_ = offset;
bound_ = true;
}
void set_referrer(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset);
offset_ = offset;
}
bool is_forward_target() const {
return offset() != kInvalidOffset && !is_bound();
}
// There are three states for a label:
// bound_ offset_
// UNSET false kInvalidOffset
// FORWARD_TARGET false Offset of referring jump
// BACKWARD_TARGET true Offset of label in bytecode array when bound
bool bound_;
size_t offset_;
friend class BytecodeArrayWriter;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_LABEL_H_
......@@ -15,67 +15,67 @@ namespace interpreter {
BytecodePeepholeOptimizer::BytecodePeepholeOptimizer(
ConstantArrayBuilder* constant_array_builder,
BytecodePipelineStage* next_stage)
: constant_array_builder_(constant_array_builder),
next_stage_(next_stage),
last_is_discardable_(false) {
: constant_array_builder_(constant_array_builder), next_stage_(next_stage) {
InvalidateLast();
}
void BytecodePeepholeOptimizer::InvalidateLast() {
last_.set_bytecode(Bytecode::kIllegal);
// override
Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) {
Flush();
return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count,
handler_table);
}
bool BytecodePeepholeOptimizer::LastIsValid() const {
return last_.bytecode() != Bytecode::kIllegal;
// override
void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
node = OptimizeAndEmitLast(node);
if (node != nullptr) {
SetLast(node);
}
}
void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
last_.Clone(node);
last_is_discardable_ = true;
// override
void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node,
BytecodeLabel* label) {
node = OptimizeAndEmitLast(node);
next_stage_->WriteJump(node, label);
}
// override
size_t BytecodePeepholeOptimizer::FlushForOffset() {
size_t buffered_size = next_stage_->FlushForOffset();
if (LastIsValid()) {
if (last_.bytecode() == Bytecode::kNop &&
!last_.source_info().is_statement()) {
// The Nop can be dropped as it doesn't have a statement
// position for the debugger and doesn't have any effects by
// definition.
InvalidateLast();
} else {
buffered_size += last_.Size();
last_is_discardable_ = false;
}
}
return buffered_size;
void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) {
Flush();
next_stage_->BindLabel(label);
}
// override
void BytecodePeepholeOptimizer::FlushBasicBlock() {
void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target,
BytecodeLabel* label) {
// There is no need to flush here, it will have been flushed when |target|
// was bound.
next_stage_->BindLabel(target, label);
}
void BytecodePeepholeOptimizer::Flush() {
// TODO(oth/rmcilroy): We could check CanElideLast() here to potentially
// eliminate last rather than writing it.
if (LastIsValid()) {
next_stage_->Write(&last_);
InvalidateLast();
}
next_stage_->FlushBasicBlock();
}
// override
void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
// Attempt optimization if there is an earlier node to optimize with.
if (LastIsValid()) {
node = Optimize(node);
// Only output the last node if it wasn't invalidated by the optimization.
if (LastIsValid()) {
next_stage_->Write(&last_);
InvalidateLast();
}
}
void BytecodePeepholeOptimizer::InvalidateLast() {
last_.set_bytecode(Bytecode::kIllegal);
}
if (node != nullptr) {
SetLast(node);
}
bool BytecodePeepholeOptimizer::LastIsValid() const {
return last_.bytecode() != Bytecode::kIllegal;
}
void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
last_.Clone(node);
}
Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand(
......@@ -260,10 +260,6 @@ bool BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes(
bool BytecodePeepholeOptimizer::CanElideLast(
const BytecodeNode* const current) const {
if (!last_is_discardable_) {
return false;
}
if (last_.bytecode() == Bytecode::kNop) {
// Nop are placeholders for holding source position information.
return true;
......@@ -311,6 +307,20 @@ BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) {
return current;
}
BytecodeNode* BytecodePeepholeOptimizer::OptimizeAndEmitLast(
BytecodeNode* current) {
// Attempt optimization if there is an earlier node to optimize with.
if (LastIsValid()) {
current = Optimize(current);
// Only output the last node if it wasn't invalidated by the optimization.
if (LastIsValid()) {
next_stage_->Write(&last_);
InvalidateLast();
}
}
return current;
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -24,11 +24,17 @@ class BytecodePeepholeOptimizer final : public BytecodePipelineStage,
// BytecodePipelineStage interface.
void Write(BytecodeNode* node) override;
size_t FlushForOffset() override;
void FlushBasicBlock() override;
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override;
void BindLabel(BytecodeLabel* label) override;
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override;
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) override;
private:
BytecodeNode* OptimizeAndEmitLast(BytecodeNode* current);
BytecodeNode* Optimize(BytecodeNode* current);
void Flush();
void TryToRemoveLastExpressionPosition(const BytecodeNode* const current);
bool TransformLastAndCurrentBytecodes(BytecodeNode* const current);
......@@ -54,7 +60,6 @@ class BytecodePeepholeOptimizer final : public BytecodePipelineStage,
ConstantArrayBuilder* constant_array_builder_;
BytecodePipelineStage* next_stage_;
BytecodeNode last_;
bool last_is_discardable_;
DISALLOW_COPY_AND_ASSIGN(BytecodePeepholeOptimizer);
};
......
......@@ -13,6 +13,7 @@ namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeLabel;
class BytecodeNode;
class BytecodeSourceInfo;
......@@ -26,12 +27,26 @@ class BytecodePipelineStage {
// deferring Write() to the next stage.
virtual void Write(BytecodeNode* node) = 0;
// Flush state for bytecode array offset calculation. Returns the
// current size of bytecode array.
virtual size_t FlushForOffset() = 0;
// Flush state to terminate basic block.
virtual void FlushBasicBlock() = 0;
// Write jump bytecode node |node| which jumps to |label| into pipeline.
// The node and label are only valid for the duration of the call. This call
// implicitly ends the current basic block so should always write to the next
// stage.
virtual void WriteJump(BytecodeNode* node, BytecodeLabel* label) = 0;
// Binds |label| to the current bytecode location. This call implicitly
// ends the current basic block and so any deferred bytecodes should be
// written to the next stage.
virtual void BindLabel(BytecodeLabel* label) = 0;
// Binds |label| to the location of |target|. This call implicitly
// ends the current basic block and so any deferred bytecodes should be
// written to the next stage.
virtual void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) = 0;
// Flush the pipeline and generate a bytecode array.
virtual Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) = 0;
};
// Source code position information.
......
......@@ -198,44 +198,13 @@ BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
DCHECK(accumulator_info_->register_value() == accumulator_);
}
void BytecodeRegisterOptimizer::FlushState() {
if (flushed_) {
return;
}
// Materialize all live registers.
size_t count = register_info_table_.size();
for (size_t i = 0; i < count; ++i) {
RegisterInfo* reg_info = register_info_table_[i];
if (!reg_info->IsOnlyMemberOfEquivalenceSet() &&
!reg_info->materialized()) {
DCHECK(RegisterIsTemporary(reg_info->register_value()) ||
reg_info->register_value() == accumulator_);
Materialize(reg_info);
}
}
// Break all existing equivalences.
for (size_t i = 0; i < count; ++i) {
RegisterInfo* reg_info = register_info_table_[i];
if (!reg_info->IsOnlyMemberOfEquivalenceSet()) {
reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
}
}
flushed_ = true;
}
// override
void BytecodeRegisterOptimizer::FlushBasicBlock() {
FlushState();
next_stage_->FlushBasicBlock();
}
// override
size_t BytecodeRegisterOptimizer::FlushForOffset() {
Handle<BytecodeArray> BytecodeRegisterOptimizer::ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) {
FlushState();
return next_stage_->FlushForOffset();
return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count,
handler_table);
}
// override
......@@ -283,6 +252,55 @@ void BytecodeRegisterOptimizer::Write(BytecodeNode* node) {
WriteToNextStage(node);
}
// override
void BytecodeRegisterOptimizer::WriteJump(BytecodeNode* node,
BytecodeLabel* label) {
FlushState();
next_stage_->WriteJump(node, label);
}
// override
void BytecodeRegisterOptimizer::BindLabel(BytecodeLabel* label) {
FlushState();
next_stage_->BindLabel(label);
}
// override
void BytecodeRegisterOptimizer::BindLabel(const BytecodeLabel& target,
BytecodeLabel* label) {
// There is no need to flush here, it will have been flushed when |target|
// was bound.
next_stage_->BindLabel(target, label);
}
void BytecodeRegisterOptimizer::FlushState() {
if (flushed_) {
return;
}
// Materialize all live registers.
size_t count = register_info_table_.size();
for (size_t i = 0; i < count; ++i) {
RegisterInfo* reg_info = register_info_table_[i];
if (!reg_info->IsOnlyMemberOfEquivalenceSet() &&
!reg_info->materialized()) {
DCHECK(RegisterIsTemporary(reg_info->register_value()) ||
reg_info->register_value() == accumulator_);
Materialize(reg_info);
}
}
// Break all existing equivalences.
for (size_t i = 0; i < count; ++i) {
RegisterInfo* reg_info = register_info_table_[i];
if (!reg_info->IsOnlyMemberOfEquivalenceSet()) {
reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
}
}
flushed_ = true;
}
void BytecodeRegisterOptimizer::WriteToNextStage(BytecodeNode* node) const {
next_stage_->Write(node);
}
......
......@@ -26,9 +26,13 @@ class BytecodeRegisterOptimizer final : public BytecodePipelineStage,
virtual ~BytecodeRegisterOptimizer() {}
// BytecodePipelineStage interface.
size_t FlushForOffset() override;
void FlushBasicBlock() override;
void Write(BytecodeNode* node) override;
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override;
void BindLabel(BytecodeLabel* label) override;
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override;
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) override;
private:
static const uint32_t kInvalidEquivalenceId = kMaxUInt32;
......
......@@ -7,6 +7,7 @@
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-label.h"
#include "src/zone-containers.h"
namespace v8 {
......
......@@ -902,6 +902,7 @@
'interpreter/bytecode-array-iterator.h',
'interpreter/bytecode-array-writer.cc',
'interpreter/bytecode-array-writer.h',
'interpreter/bytecode-label.h',
'interpreter/bytecode-peephole-optimizer.cc',
'interpreter/bytecode-peephole-optimizer.h',
'interpreter/bytecode-pipeline.cc',
......
......@@ -8,6 +8,7 @@
#include "src/handles.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/cctest.h"
#include "test/cctest/interpreter/interpreter-tester.h"
......
......@@ -6,6 +6,7 @@
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "test/unittests/test-utils.h"
......@@ -277,6 +278,19 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.StoreLookupSlot(wide_name, LanguageMode::SLOPPY)
.StoreLookupSlot(wide_name, LanguageMode::STRICT);
// Emit loads which will be transformed to Ldr equivalents by the peephole
// optimizer.
builder.LoadNamedProperty(reg, name, 0)
.StoreAccumulatorInRegister(reg)
.LoadKeyedProperty(reg, 0)
.StoreAccumulatorInRegister(reg)
.LoadContextSlot(reg, 1)
.StoreAccumulatorInRegister(reg)
.LoadGlobal(name, 0, TypeofMode::NOT_INSIDE_TYPEOF)
.StoreAccumulatorInRegister(reg)
.LoadUndefined()
.StoreAccumulatorInRegister(reg);
// CreateClosureWide
Handle<SharedFunctionInfo> shared_info2 = factory->NewSharedFunctionInfo(
factory->NewStringFromStaticChars("function_b"), MaybeHandle<Code>(),
......@@ -352,12 +366,20 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Insert entry for nop bytecode as this often gets optimized out.
scorecard[Bytecodes::ToByte(Bytecode::kNop)] = 1;
// Insert entries for bytecodes only emiited by peephole optimizer.
scorecard[Bytecodes::ToByte(Bytecode::kLdrNamedProperty)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrKeyedProperty)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrGlobal)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrContextSlot)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrUndefined)] = 1;
if (!FLAG_ignition_peephole) {
// Insert entries for bytecodes only emitted by peephole optimizer.
scorecard[Bytecodes::ToByte(Bytecode::kLdrNamedProperty)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrKeyedProperty)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrGlobal)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrContextSlot)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLdrUndefined)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kLogicalNot)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kJump)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kJumpIfTrue)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kJumpIfFalse)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kJumpIfTrueConstant)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kJumpIfFalseConstant)] = 1;
}
// Check return occurs at the end and only once in the BytecodeArray.
CHECK_EQ(final_bytecode, Bytecode::kReturn);
......@@ -470,6 +492,11 @@ TEST_F(BytecodeArrayBuilderTest, Constants) {
CHECK_EQ(array->constant_pool()->length(), 3);
}
static Bytecode PeepholeToBoolean(Bytecode jump_bytecode) {
return FLAG_ignition_peephole
? Bytecodes::GetJumpWithoutToBoolean(jump_bytecode)
: jump_bytecode;
}
TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
static const int kFarJumpDistance = 256;
......@@ -520,14 +547,16 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrue));
CHECK_EQ(iterator.GetImmediateOperand(0), 14);
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalse));
CHECK_EQ(iterator.GetImmediateOperand(0), 10);
iterator.Advance();
......@@ -553,7 +582,8 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrueConstant));
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance - 4));
iterator.Advance();
......@@ -561,7 +591,8 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalseConstant));
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance - 8));
iterator.Advance();
......@@ -628,13 +659,15 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrue));
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
CHECK_EQ(iterator.GetImmediateOperand(0), -2);
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalse));
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
CHECK_EQ(iterator.GetImmediateOperand(0), -2);
iterator.Advance();
......@@ -675,13 +708,15 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalse));
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
CHECK_EQ(iterator.GetImmediateOperand(0), -409);
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
CHECK_EQ(iterator.current_bytecode(),
PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrue));
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
CHECK_EQ(iterator.GetImmediateOperand(0), -419);
iterator.Advance();
......
......@@ -4,7 +4,11 @@
#include "src/v8.h"
#include "src/api.h"
#include "src/factory.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/source-position-table.h"
#include "src/isolate.h"
#include "src/utils.h"
......@@ -18,8 +22,8 @@ namespace interpreter {
class BytecodeArrayWriterUnittest : public TestWithIsolateAndZone {
public:
BytecodeArrayWriterUnittest()
: source_position_builder_(isolate(), zone()),
bytecode_array_writer_(zone(), &source_position_builder_) {}
: constant_array_builder_(isolate(), zone()),
bytecode_array_writer_(isolate(), zone(), &constant_array_builder_) {}
~BytecodeArrayWriterUnittest() override {}
void Write(BytecodeNode* node, const BytecodeSourceInfo& info);
......@@ -37,13 +41,19 @@ class BytecodeArrayWriterUnittest : public TestWithIsolateAndZone {
uint32_t operand2, uint32_t operand3, OperandScale operand_scale,
const BytecodeSourceInfo& info = BytecodeSourceInfo());
SourcePositionTableBuilder* source_position_builder() {
return &source_position_builder_;
}
void WriteJump(Bytecode bytecode, BytecodeLabel* label,
OperandScale operand_scale,
const BytecodeSourceInfo& info = BytecodeSourceInfo());
BytecodeArrayWriter* writer() { return &bytecode_array_writer_; }
ZoneVector<unsigned char>* bytecodes() { return writer()->bytecodes(); }
SourcePositionTableBuilder* source_position_table_builder() {
return writer()->source_position_table_builder();
}
int max_register_count() { return writer()->max_register_count(); }
private:
SourcePositionTableBuilder source_position_builder_;
ConstantArrayBuilder constant_array_builder_;
BytecodeArrayWriter bytecode_array_writer_;
};
......@@ -94,40 +104,50 @@ void BytecodeArrayWriterUnittest::Write(Bytecode bytecode, uint32_t operand0,
Write(&node, info);
}
void BytecodeArrayWriterUnittest::WriteJump(Bytecode bytecode,
BytecodeLabel* label,
OperandScale operand_scale,
const BytecodeSourceInfo& info) {
BytecodeNode node(bytecode, 0, operand_scale);
if (info.is_valid()) {
node.source_info().Update(info);
}
writer()->WriteJump(&node, label);
}
TEST_F(BytecodeArrayWriterUnittest, SimpleExample) {
CHECK_EQ(writer()->bytecodes()->size(), 0);
CHECK_EQ(bytecodes()->size(), 0);
Write(Bytecode::kStackCheck, {10, false});
CHECK_EQ(writer()->bytecodes()->size(), 1);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 0);
CHECK_EQ(bytecodes()->size(), 1);
CHECK_EQ(max_register_count(), 0);
Write(Bytecode::kLdaSmi, 0xff, OperandScale::kSingle, {55, true});
CHECK_EQ(writer()->bytecodes()->size(), 3);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 0);
CHECK_EQ(bytecodes()->size(), 3);
CHECK_EQ(max_register_count(), 0);
Write(Bytecode::kLdar, Register(1).ToOperand(), OperandScale::kDouble);
CHECK_EQ(writer()->bytecodes()->size(), 7);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 2 * kPointerSize);
CHECK_EQ(bytecodes()->size(), 7);
CHECK_EQ(max_register_count(), 2);
Write(Bytecode::kReturn, {70, true});
CHECK_EQ(writer()->bytecodes()->size(), 8);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 2 * kPointerSize);
CHECK_EQ(bytecodes()->size(), 8);
CHECK_EQ(max_register_count(), 2);
static const uint8_t bytes[] = {B(StackCheck), B(LdaSmi), U8(0xff), B(Wide),
B(Ldar), R16(1), B(Return)};
CHECK_EQ(writer()->bytecodes()->size(), arraysize(bytes));
CHECK_EQ(bytecodes()->size(), arraysize(bytes));
for (size_t i = 0; i < arraysize(bytes); ++i) {
CHECK_EQ(writer()->bytecodes()->at(i), bytes[i]);
CHECK_EQ(bytecodes()->at(i), bytes[i]);
}
CHECK_EQ(writer()->FlushForOffset(), arraysize(bytes));
writer()->FlushBasicBlock();
CHECK_EQ(writer()->bytecodes()->size(), arraysize(bytes));
writer()->ToBytecodeArray(0, 0, factory()->empty_fixed_array());
CHECK_EQ(bytecodes()->size(), arraysize(bytes));
PositionTableEntry expected_positions[] = {
{0, 10, false}, {1, 55, true}, {7, 70, true}};
Handle<ByteArray> source_positions =
source_position_builder()->ToSourcePositionTable();
source_position_table_builder()->ToSourcePositionTable();
SourcePositionTableIterator source_iterator(*source_positions);
for (size_t i = 0; i < arraysize(expected_positions); ++i) {
const PositionTableEntry& expected = expected_positions[i];
......@@ -173,50 +193,58 @@ TEST_F(BytecodeArrayWriterUnittest, ComplexExample) {
{0, 30, false}, {1, 42, true}, {3, 42, false}, {5, 68, true},
{17, 63, true}, {31, 54, false}, {36, 85, true}, {44, 85, true}};
BytecodeLabel back_jump, jump_for_in, jump_end_1, jump_end_2, jump_end_3;
#define R(i) static_cast<uint32_t>(Register(i).ToOperand())
Write(Bytecode::kStackCheck, {30, false});
Write(Bytecode::kLdaConstant, U8(0), OperandScale::kSingle, {42, true});
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 0 * kPointerSize);
CHECK_EQ(max_register_count(), 0);
Write(Bytecode::kStar, R(1), OperandScale::kSingle, {42, false});
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 2 * kPointerSize);
Write(Bytecode::kJumpIfUndefined, U8(38), OperandScale::kSingle, {68, true});
Write(Bytecode::kJumpIfNull, U8(36), OperandScale::kSingle);
CHECK_EQ(max_register_count(), 2);
WriteJump(Bytecode::kJumpIfUndefined, &jump_end_1, OperandScale::kSingle,
{68, true});
WriteJump(Bytecode::kJumpIfNull, &jump_end_2, OperandScale::kSingle);
Write(Bytecode::kToObject);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 2 * kPointerSize);
CHECK_EQ(max_register_count(), 2);
Write(Bytecode::kStar, R(3), OperandScale::kSingle);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 4 * kPointerSize);
CHECK_EQ(max_register_count(), 4);
Write(Bytecode::kForInPrepare, R(4), OperandScale::kSingle);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 7 * kPointerSize);
CHECK_EQ(max_register_count(), 7);
Write(Bytecode::kLdaZero);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 7 * kPointerSize);
CHECK_EQ(max_register_count(), 7);
Write(Bytecode::kStar, R(7), OperandScale::kSingle);
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 8 * kPointerSize);
CHECK_EQ(max_register_count(), 8);
writer()->BindLabel(&back_jump);
Write(Bytecode::kForInDone, R(7), R(6), OperandScale::kSingle, {63, true});
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 8 * kPointerSize);
Write(Bytecode::kJumpIfTrue, U8(23), OperandScale::kSingle);
CHECK_EQ(max_register_count(), 8);
WriteJump(Bytecode::kJumpIfTrue, &jump_end_3, OperandScale::kSingle);
Write(Bytecode::kForInNext, R(3), R(7), R(4), U8(1), OperandScale::kSingle);
Write(Bytecode::kJumpIfUndefined, U8(10), OperandScale::kSingle);
WriteJump(Bytecode::kJumpIfUndefined, &jump_for_in, OperandScale::kSingle);
Write(Bytecode::kStar, R(0), OperandScale::kSingle);
Write(Bytecode::kStackCheck, {54, false});
Write(Bytecode::kLdar, R(0), OperandScale::kSingle);
Write(Bytecode::kStar, R(2), OperandScale::kSingle);
Write(Bytecode::kReturn, {85, true});
writer()->BindLabel(&jump_for_in);
Write(Bytecode::kForInStep, R(7), OperandScale::kSingle);
Write(Bytecode::kStar, R(7), OperandScale::kSingle);
Write(Bytecode::kJump, U8(-24), OperandScale::kSingle);
WriteJump(Bytecode::kJump, &back_jump, OperandScale::kSingle);
writer()->BindLabel(&jump_end_1);
writer()->BindLabel(&jump_end_2);
writer()->BindLabel(&jump_end_3);
Write(Bytecode::kLdaUndefined);
Write(Bytecode::kReturn, {85, true});
CHECK_EQ(writer()->GetMaximumFrameSizeUsed(), 8 * kPointerSize);
CHECK_EQ(max_register_count(), 8);
#undef R
CHECK_EQ(writer()->bytecodes()->size(), arraysize(expected_bytes));
CHECK_EQ(bytecodes()->size(), arraysize(expected_bytes));
for (size_t i = 0; i < arraysize(expected_bytes); ++i) {
CHECK_EQ(static_cast<int>(writer()->bytecodes()->at(i)),
CHECK_EQ(static_cast<int>(bytecodes()->at(i)),
static_cast<int>(expected_bytes[i]));
}
Handle<ByteArray> source_positions =
source_position_builder()->ToSourcePositionTable();
source_position_table_builder()->ToSourcePositionTable();
SourcePositionTableIterator source_iterator(*source_positions);
for (size_t i = 0; i < arraysize(expected_positions); ++i) {
const PositionTableEntry& expected = expected_positions[i];
......
......@@ -5,6 +5,7 @@
#include "src/v8.h"
#include "src/factory.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-peephole-optimizer.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/objects-inl.h"
......@@ -23,23 +24,31 @@ class BytecodePeepholeOptimizerTest : public BytecodePipelineStage,
peephole_optimizer_(&constant_array_builder_, this) {}
~BytecodePeepholeOptimizerTest() override {}
size_t FlushForOffset() override {
flush_for_offset_count_++;
return 0;
};
void FlushBasicBlock() override { flush_basic_block_count_++; }
void Write(BytecodeNode* node) override {
write_count_++;
last_written_.Clone(node);
}
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
write_count_++;
last_written_.Clone(node);
}
void BindLabel(BytecodeLabel* label) override {}
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handle_table) override {
return Handle<BytecodeArray>();
}
void Flush() {
optimizer()->ToBytecodeArray(0, 0, factory()->empty_fixed_array());
}
BytecodePeepholeOptimizer* optimizer() { return &peephole_optimizer_; }
ConstantArrayBuilder* constant_array() { return &constant_array_builder_; }
int flush_for_offset_count() const { return flush_for_offset_count_; }
int flush_basic_block_count() const { return flush_basic_block_count_; }
int write_count() const { return write_count_; }
const BytecodeNode& last_written() const { return last_written_; }
......@@ -47,70 +56,77 @@ class BytecodePeepholeOptimizerTest : public BytecodePipelineStage,
ConstantArrayBuilder constant_array_builder_;
BytecodePeepholeOptimizer peephole_optimizer_;
int flush_for_offset_count_ = 0;
int flush_basic_block_count_ = 0;
int write_count_ = 0;
BytecodeNode last_written_;
};
// Sanity tests.
TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetPassThrough) {
CHECK_EQ(flush_for_offset_count(), 0);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
}
TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetRightSize) {
BytecodeNode node(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kQuadruple);
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), node.Size());
CHECK_EQ(flush_for_offset_count(), 1);
TEST_F(BytecodePeepholeOptimizerTest, FlushOnJump) {
CHECK_EQ(write_count(), 0);
}
TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetNop) {
BytecodeNode node(Bytecode::kNop);
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kSingle);
optimizer()->Write(&add);
CHECK_EQ(write_count(), 0);
BytecodeLabel target;
BytecodeNode jump(Bytecode::kJump, 0, OperandScale::kSingle);
optimizer()->WriteJump(&jump, &target);
CHECK_EQ(write_count(), 2);
CHECK_EQ(jump, last_written());
}
TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetNopExpression) {
BytecodeNode node(Bytecode::kNop);
node.source_info().Update({3, false});
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
TEST_F(BytecodePeepholeOptimizerTest, FlushOnBind) {
CHECK_EQ(write_count(), 0);
}
TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetNopStatement) {
BytecodeNode node(Bytecode::kNop);
node.source_info().Update({3, true});
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), node.Size());
CHECK_EQ(flush_for_offset_count(), 1);
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kSingle);
optimizer()->Write(&add);
CHECK_EQ(write_count(), 0);
BytecodeLabel target;
optimizer()->BindLabel(&target);
CHECK_EQ(write_count(), 1);
CHECK_EQ(add, last_written());
}
TEST_F(BytecodePeepholeOptimizerTest, FlushBasicBlockPassThrough) {
CHECK_EQ(flush_basic_block_count(), 0);
optimizer()->FlushBasicBlock();
CHECK_EQ(flush_basic_block_count(), 1);
CHECK_EQ(write_count(), 0);
// Nop elimination tests.
TEST_F(BytecodePeepholeOptimizerTest, ElideEmptyNop) {
BytecodeNode nop(Bytecode::kNop);
optimizer()->Write(&nop);
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kSingle);
optimizer()->Write(&add);
Flush();
CHECK_EQ(write_count(), 1);
CHECK_EQ(add, last_written());
}
TEST_F(BytecodePeepholeOptimizerTest, WriteOneFlushBasicBlock) {
BytecodeNode node(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kQuadruple);
optimizer()->Write(&node);
CHECK_EQ(write_count(), 0);
optimizer()->FlushBasicBlock();
TEST_F(BytecodePeepholeOptimizerTest, ElideExpressionNop) {
BytecodeNode nop(Bytecode::kNop);
nop.source_info().Update({3, false});
optimizer()->Write(&nop);
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kSingle);
optimizer()->Write(&add);
Flush();
CHECK_EQ(write_count(), 1);
CHECK_EQ(node, last_written());
CHECK_EQ(add, last_written());
}
TEST_F(BytecodePeepholeOptimizerTest, KeepStatementNop) {
BytecodeNode nop(Bytecode::kNop);
nop.source_info().Update({3, true});
optimizer()->Write(&nop);
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kSingle);
add.source_info().Update({3, false});
optimizer()->Write(&add);
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(add, last_written());
}
// Tests covering BytecodePeepholeOptimizer::UpdateCurrentBytecode().
......@@ -123,7 +139,7 @@ TEST_F(BytecodePeepholeOptimizerTest, KeepJumpIfToBooleanTrue) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written(), second);
}
......@@ -136,7 +152,7 @@ TEST_F(BytecodePeepholeOptimizerTest, ElideJumpIfToBooleanTrue) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kJumpIfTrue);
CHECK_EQ(last_written().operand(0), second.operand(0));
......@@ -150,7 +166,7 @@ TEST_F(BytecodePeepholeOptimizerTest, KeepToBooleanLogicalNot) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written(), second);
}
......@@ -163,64 +179,63 @@ TEST_F(BytecodePeepholeOptimizerTest, ElideToBooleanLogicalNot) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kLogicalNot);
}
// Tests covering BytecodePeepholeOptimizer::CanElideCurrent().
TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRy) {
BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(),
TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRy) {
BytecodeNode first(Bytecode::kStar, Register(0).ToOperand(),
OperandScale::kSingle);
BytecodeNode second(Bytecode::kLdar, Register(1).ToOperand(),
OperandScale::kSingle);
optimizer()->Write(&first);
optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|.
CHECK_EQ(write_count(), 0);
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written(), second);
}
TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRx) {
BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(),
TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRx) {
BytecodeLabel label;
BytecodeNode first(Bytecode::kStar, Register(0).ToOperand(),
OperandScale::kSingle);
BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand(),
OperandScale::kSingle);
optimizer()->Write(&first);
CHECK_EQ(write_count(), 0);
optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|.
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
}
TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRxStatement) {
BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(),
TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatement) {
BytecodeNode first(Bytecode::kStar, Register(0).ToOperand(),
OperandScale::kSingle);
BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand(),
OperandScale::kSingle);
second.source_info().Update({0, true});
optimizer()->Write(&first);
CHECK_EQ(write_count(), 0);
optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|.
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kNop);
CHECK_EQ(last_written().source_info(), second.source_info());
}
TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRxStatementStarRy) {
BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(),
TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatementStarRy) {
BytecodeLabel label;
BytecodeNode first(Bytecode::kStar, Register(0).ToOperand(),
OperandScale::kSingle);
BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand(),
OperandScale::kSingle);
......@@ -229,13 +244,12 @@ TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRxStatementStarRy) {
second.source_info().Update({0, true});
optimizer()->Write(&first);
CHECK_EQ(write_count(), 0);
optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|.
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->Write(&third);
CHECK_EQ(write_count(), 1);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
// Source position should move |second| to |third| when |second| is elided.
third.source_info().Update(second.source_info());
......@@ -251,7 +265,7 @@ TEST_F(BytecodePeepholeOptimizerTest, LdarToName) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written(), second);
}
......@@ -264,7 +278,7 @@ TEST_F(BytecodePeepholeOptimizerTest, ToNameToName) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
}
......@@ -276,7 +290,7 @@ TEST_F(BytecodePeepholeOptimizerTest, TypeOfToName) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
}
......@@ -292,7 +306,7 @@ TEST_F(BytecodePeepholeOptimizerTest, LdaConstantStringToName) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
}
......@@ -307,27 +321,13 @@ TEST_F(BytecodePeepholeOptimizerTest, LdaConstantNumberToName) {
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written(), second);
}
// Tests covering BytecodePeepholeOptimizer::CanElideLast().
TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalseNotDiscardable) {
BytecodeNode first(Bytecode::kLdaTrue);
BytecodeNode second(Bytecode::kLdaFalse);
optimizer()->Write(&first);
optimizer()->FlushForOffset(); // Prevent discarding of |first|.
CHECK_EQ(write_count(), 0);
optimizer()->Write(&second);
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), first);
optimizer()->FlushBasicBlock();
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written(), second);
}
TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalse) {
BytecodeNode first(Bytecode::kLdaTrue);
BytecodeNode second(Bytecode::kLdaFalse);
......@@ -335,7 +335,7 @@ TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalse) {
CHECK_EQ(write_count(), 0);
optimizer()->Write(&second);
CHECK_EQ(write_count(), 0);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), second);
}
......@@ -348,7 +348,7 @@ TEST_F(BytecodePeepholeOptimizerTest, LdaTrueStatementLdaFalse) {
CHECK_EQ(write_count(), 0);
optimizer()->Write(&second);
CHECK_EQ(write_count(), 0);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
second.source_info().Update(first.source_info());
CHECK_EQ(last_written(), second);
......@@ -361,7 +361,7 @@ TEST_F(BytecodePeepholeOptimizerTest, NopStackCheck) {
CHECK_EQ(write_count(), 0);
optimizer()->Write(&second);
CHECK_EQ(write_count(), 0);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
CHECK_EQ(last_written(), second);
}
......@@ -374,7 +374,7 @@ TEST_F(BytecodePeepholeOptimizerTest, NopStatementStackCheck) {
CHECK_EQ(write_count(), 0);
optimizer()->Write(&second);
CHECK_EQ(write_count(), 0);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(write_count(), 1);
second.source_info().Update(first.source_info());
CHECK_EQ(last_written(), second);
......@@ -406,7 +406,7 @@ TEST_F(BytecodePeepholeOptimizerTest, MergeLoadICStar) {
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(last_written().bytecode(), third.bytecode());
}
......@@ -434,7 +434,7 @@ TEST_F(BytecodePeepholeOptimizerTest, MergeLdaKeyedPropertyStar) {
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(last_written().bytecode(), third.bytecode());
}
......@@ -461,7 +461,7 @@ TEST_F(BytecodePeepholeOptimizerTest, MergeLdaGlobalStar) {
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(last_written().bytecode(), third.bytecode());
}
......@@ -489,7 +489,7 @@ TEST_F(BytecodePeepholeOptimizerTest, MergeLdaContextSlotStar) {
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(last_written().bytecode(), third.bytecode());
}
......@@ -515,7 +515,7 @@ TEST_F(BytecodePeepholeOptimizerTest, MergeLdaUndefinedStar) {
CHECK_EQ(write_count(), 2);
CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
optimizer()->FlushBasicBlock();
Flush();
CHECK_EQ(last_written().bytecode(), third.bytecode());
}
......
......@@ -5,6 +5,7 @@
#include "src/v8.h"
#include "src/factory.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register-optimizer.h"
#include "src/objects-inl.h"
#include "src/objects.h"
......@@ -27,14 +28,17 @@ class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
zone(), register_allocator_, number_of_parameters, this);
}
size_t FlushForOffset() override {
flush_for_offset_count_++;
return 0;
};
void FlushBasicBlock() override { flush_basic_block_count_++; }
void Write(BytecodeNode* node) override { output_.push_back(*node); }
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
output_.push_back(*node);
}
void BindLabel(BytecodeLabel* label) override {}
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handle_table) override {
return Handle<BytecodeArray>();
}
TemporaryRegisterAllocator* allocator() { return register_allocator_; }
BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; }
......@@ -47,8 +51,6 @@ class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
allocator()->ReturnTemporaryRegister(reg.index());
}
int flush_for_offset_count() const { return flush_for_offset_count_; }
int flush_basic_block_count() const { return flush_basic_block_count_; }
size_t write_count() const { return output_.size(); }
const BytecodeNode& last_written() const { return output_.back(); }
const std::vector<BytecodeNode>* output() { return &output_; }
......@@ -57,76 +59,65 @@ class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
TemporaryRegisterAllocator* register_allocator_;
BytecodeRegisterOptimizer* register_optimizer_;
int flush_for_offset_count_ = 0;
int flush_basic_block_count_ = 0;
std::vector<BytecodeNode> output_;
};
// Sanity tests.
TEST_F(BytecodeRegisterOptimizerTest, FlushForOffsetPassThrough) {
Initialize(1, 1);
CHECK_EQ(flush_for_offset_count(), 0);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
}
TEST_F(BytecodeRegisterOptimizerTest, FlushForOffsetRightSize) {
Initialize(1, 1);
BytecodeNode node(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kQuadruple);
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
CHECK_EQ(write_count(), 1);
}
TEST_F(BytecodeRegisterOptimizerTest, FlushForOffsetNop) {
TEST_F(BytecodeRegisterOptimizerTest, WriteNop) {
Initialize(1, 1);
BytecodeNode node(Bytecode::kNop);
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
CHECK_EQ(write_count(), 1);
CHECK_EQ(node, last_written());
}
TEST_F(BytecodeRegisterOptimizerTest, FlushForOffsetNopExpression) {
TEST_F(BytecodeRegisterOptimizerTest, WriteNopExpression) {
Initialize(1, 1);
BytecodeNode node(Bytecode::kNop);
node.source_info().Update({3, false});
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
CHECK_EQ(write_count(), 1);
CHECK_EQ(node, last_written());
}
TEST_F(BytecodeRegisterOptimizerTest, FlushForOffsetNopStatement) {
TEST_F(BytecodeRegisterOptimizerTest, WriteNopStatement) {
Initialize(1, 1);
BytecodeNode node(Bytecode::kNop);
node.source_info().Update({3, true});
optimizer()->Write(&node);
CHECK_EQ(optimizer()->FlushForOffset(), 0);
CHECK_EQ(flush_for_offset_count(), 1);
CHECK_EQ(write_count(), 1);
CHECK_EQ(node, last_written());
}
TEST_F(BytecodeRegisterOptimizerTest, FlushBasicBlockPassThrough) {
TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) {
Initialize(1, 1);
CHECK_EQ(flush_basic_block_count(), 0);
optimizer()->FlushBasicBlock();
CHECK_EQ(flush_basic_block_count(), 1);
Register temp = NewTemporary();
BytecodeNode node(Bytecode::kStar, temp.ToOperand(), OperandScale::kSingle);
optimizer()->Write(&node);
CHECK_EQ(write_count(), 0);
BytecodeLabel label;
BytecodeNode jump(Bytecode::kJump, 0, OperandScale::kSingle);
optimizer()->WriteJump(&jump, &label);
CHECK_EQ(write_count(), 2);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
CHECK_EQ(output()->at(0).operand_scale(), OperandScale::kSingle);
CHECK_EQ(output()->at(1).bytecode(), Bytecode::kJump);
}
TEST_F(BytecodeRegisterOptimizerTest, WriteOneFlushBasicBlock) {
TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForBind) {
Initialize(1, 1);
BytecodeNode node(Bytecode::kAdd, Register(0).ToOperand(),
OperandScale::kQuadruple);
Register temp = NewTemporary();
BytecodeNode node(Bytecode::kStar, temp.ToOperand(), OperandScale::kSingle);
optimizer()->Write(&node);
CHECK_EQ(write_count(), 0);
BytecodeLabel label;
optimizer()->BindLabel(&label);
CHECK_EQ(write_count(), 1);
optimizer()->FlushBasicBlock();
CHECK_EQ(write_count(), 1);
CHECK_EQ(node, last_written());
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
CHECK_EQ(output()->at(0).operand_scale(), OperandScale::kSingle);
}
// Basic Register Optimizations
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment