blob: 29713e2ef95349581843ba4097e151ee9cfd8ed8 [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/interpreter/bytecode-array-builder.h"
#include "src/compiler.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-dead-code-optimizer.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"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeArrayBuilder::BytecodeArrayBuilder(
Isolate* isolate, Zone* zone, int parameter_count, int context_count,
int locals_count, FunctionLiteral* literal,
SourcePositionTableBuilder::RecordingMode source_position_mode)
: isolate_(isolate),
zone_(zone),
bytecode_generated_(false),
constant_array_builder_(isolate, zone),
handler_table_builder_(isolate, zone),
return_seen_in_block_(false),
parameter_count_(parameter_count),
local_register_count_(locals_count),
context_register_count_(context_count),
temporary_allocator_(zone, fixed_register_count()),
bytecode_array_writer_(isolate, zone, &constant_array_builder_,
source_position_mode),
pipeline_(&bytecode_array_writer_) {
DCHECK_GE(parameter_count_, 0);
DCHECK_GE(context_register_count_, 0);
DCHECK_GE(local_register_count_, 0);
if (FLAG_ignition_deadcode) {
pipeline_ = new (zone) BytecodeDeadCodeOptimizer(pipeline_);
}
if (FLAG_ignition_peephole) {
pipeline_ = new (zone)
BytecodePeepholeOptimizer(&constant_array_builder_, pipeline_);
}
if (FLAG_ignition_reo) {
pipeline_ = new (zone) BytecodeRegisterOptimizer(
zone, &temporary_allocator_, parameter_count, pipeline_);
}
return_position_ =
literal ? std::max(literal->start_position(), literal->end_position() - 1)
: kNoSourcePosition;
}
Register BytecodeArrayBuilder::first_context_register() const {
DCHECK_GT(context_register_count_, 0);
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(return_seen_in_block_);
DCHECK(!bytecode_generated_);
bytecode_generated_ = true;
Handle<FixedArray> handler_table = handler_table_builder()->ToHandlerTable();
return pipeline_->ToBytecodeArray(fixed_register_count(), parameter_count(),
handler_table);
}
namespace {
static bool ExpressionPositionIsNeeded(Bytecode bytecode) {
// An expression position is always needed if filtering is turned
// off. Otherwise an expression is only needed if the bytecode has
// external side effects.
return !FLAG_ignition_filter_expression_positions ||
!Bytecodes::IsWithoutExternalSideEffects(bytecode);
}
} // namespace
void BytecodeArrayBuilder::AttachSourceInfo(BytecodeNode* node) {
if (latest_source_info_.is_valid()) {
// Statement positions need to be emitted immediately. Expression
// positions can be pushed back until a bytecode is found that can
// throw. Hence we only invalidate the existing source position
// information if it is used.
if (latest_source_info_.is_statement() ||
ExpressionPositionIsNeeded(node->bytecode())) {
node->source_info().Clone(latest_source_info_);
latest_source_info_.set_invalid();
}
}
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2,
uint32_t operand3) {
DCHECK(OperandsAreValid(bytecode, 4, operand0, operand1, operand2, operand3));
BytecodeNode node(bytecode, operand0, operand1, operand2, operand3);
AttachSourceInfo(&node);
pipeline()->Write(&node);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2) {
DCHECK(OperandsAreValid(bytecode, 3, operand0, operand1, operand2));
BytecodeNode node(bytecode, operand0, operand1, operand2);
AttachSourceInfo(&node);
pipeline()->Write(&node);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1) {
DCHECK(OperandsAreValid(bytecode, 2, operand0, operand1));
BytecodeNode node(bytecode, operand0, operand1);
AttachSourceInfo(&node);
pipeline()->Write(&node);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0) {
DCHECK(OperandsAreValid(bytecode, 1, operand0));
BytecodeNode node(bytecode, operand0);
AttachSourceInfo(&node);
pipeline()->Write(&node);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode) {
DCHECK(OperandsAreValid(bytecode, 0));
BytecodeNode node(bytecode);
AttachSourceInfo(&node);
pipeline()->Write(&node);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
Register reg) {
Output(BytecodeForBinaryOperation(op), RegisterOperand(reg));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op) {
Output(BytecodeForCountOperation(op));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
Output(Bytecode::kToBooleanLogicalNot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() {
Output(Bytecode::kTypeOf);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(Token::Value op,
Register reg) {
Output(BytecodeForCompareOperation(op), RegisterOperand(reg));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadConstantPoolEntry(
size_t entry) {
Output(Bytecode::kLdaConstant, UnsignedOperand(entry));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
v8::internal::Smi* smi) {
int32_t raw_smi = smi->value();
if (raw_smi == 0) {
Output(Bytecode::kLdaZero);
} else {
Output(Bytecode::kLdaSmi, SignedOperand(raw_smi));
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
size_t entry = GetConstantPoolEntry(object);
Output(Bytecode::kLdaConstant, UnsignedOperand(entry));
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;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
Register reg) {
Output(Bytecode::kLdar, RegisterOperand(reg));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
Register reg) {
Output(Bytecode::kStar, RegisterOperand(reg));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
Register to) {
DCHECK(from != to);
Output(Bytecode::kMov, RegisterOperand(from), RegisterOperand(to));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int feedback_slot,
TypeofMode typeof_mode) {
// TODO(rmcilroy): Potentially store typeof information in an
// operand rather than having extra bytecodes.
Bytecode bytecode = BytecodeForLoadGlobal(typeof_mode);
Output(bytecode, UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal(
const Handle<String> name, int feedback_slot, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreGlobal(language_mode);
size_t name_index = GetConstantPoolEntry(name);
Output(bytecode, UnsignedOperand(name_index), UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context,
int slot_index) {
Output(Bytecode::kLdaContextSlot, RegisterOperand(context),
UnsignedOperand(slot_index));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context,
int slot_index) {
Output(Bytecode::kStaContextSlot, RegisterOperand(context),
UnsignedOperand(slot_index));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupSlot(
const Handle<String> name, TypeofMode typeof_mode) {
Bytecode bytecode = (typeof_mode == INSIDE_TYPEOF)
? Bytecode::kLdaLookupSlotInsideTypeof
: Bytecode::kLdaLookupSlot;
size_t name_index = GetConstantPoolEntry(name);
Output(bytecode, UnsignedOperand(name_index));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot(
const Handle<String> name, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreLookupSlot(language_mode);
size_t name_index = GetConstantPoolEntry(name);
Output(bytecode, UnsignedOperand(name_index));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
Register object, const Handle<Name> name, int feedback_slot) {
size_t name_index = GetConstantPoolEntry(name);
Output(Bytecode::kLdaNamedProperty, RegisterOperand(object),
UnsignedOperand(name_index), UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
Register object, int feedback_slot) {
Output(Bytecode::kLdaKeyedProperty, RegisterOperand(object),
UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
Register object, const Handle<Name> name, int feedback_slot,
LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreNamedProperty(language_mode);
size_t name_index = GetConstantPoolEntry(name);
Output(bytecode, RegisterOperand(object), UnsignedOperand(name_index),
UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
Register object, Register key, int feedback_slot,
LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreKeyedProperty(language_mode);
Output(bytecode, RegisterOperand(object), RegisterOperand(key),
UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(size_t entry,
int flags) {
Output(Bytecode::kCreateClosure, UnsignedOperand(entry),
UnsignedOperand(flags));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateFunctionContext(int slots) {
Output(Bytecode::kCreateFunctionContext, UnsignedOperand(slots));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
CreateArgumentsType type) {
// TODO(rmcilroy): Consider passing the type as a bytecode operand rather
// than having two different bytecodes once we have better support for
// branches in the InterpreterAssembler.
Bytecode bytecode = BytecodeForCreateArguments(type);
Output(bytecode);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
Handle<String> pattern, int literal_index, int flags) {
size_t pattern_entry = GetConstantPoolEntry(pattern);
Output(Bytecode::kCreateRegExpLiteral, UnsignedOperand(pattern_entry),
UnsignedOperand(literal_index), UnsignedOperand(flags));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
Handle<FixedArray> constant_elements, int literal_index, int flags) {
size_t constant_elements_entry = GetConstantPoolEntry(constant_elements);
Output(Bytecode::kCreateArrayLiteral,
UnsignedOperand(constant_elements_entry),
UnsignedOperand(literal_index), UnsignedOperand(flags));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
Handle<FixedArray> constant_properties, int literal_index, int flags) {
size_t constant_properties_entry = GetConstantPoolEntry(constant_properties);
Output(Bytecode::kCreateObjectLiteral,
UnsignedOperand(constant_properties_entry),
UnsignedOperand(literal_index), UnsignedOperand(flags));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
Output(Bytecode::kPushContext, RegisterOperand(context));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
Output(Bytecode::kPopContext, RegisterOperand(context));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject(
Register out) {
Output(Bytecode::kToObject, RegisterOperand(out));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName(
Register out) {
Output(Bytecode::kToName, RegisterOperand(out));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber(
Register out) {
Output(Bytecode::kToNumber, RegisterOperand(out));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
pipeline_->BindLabel(label);
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
BytecodeLabel* label) {
pipeline_->BindLabel(target, label);
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label) {
BytecodeNode node(jump_bytecode, 0);
AttachSourceInfo(&node);
pipeline_->WriteJump(&node, label);
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) {
return OutputJump(Bytecode::kJump, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) {
// The peephole optimizer attempts to simplify JumpIfToBooleanTrue
// to JumpIfTrue.
return OutputJump(Bytecode::kJumpIfToBooleanTrue, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) {
// The peephole optimizer attempts to simplify JumpIfToBooleanFalse
// to JumpIfFalse.
return OutputJump(Bytecode::kJumpIfToBooleanFalse, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfNull, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined(
BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfUndefined, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole(
BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfNotHole, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) {
if (position != kNoSourcePosition) {
// We need to attach a non-breakable source position to a stack
// check, so we simply add it as expression position. There can be
// a prior statement position from constructs like:
//
// do var x; while (false);
//
// A Nop could be inserted for empty statements, but since no code
// is associated with these positions, instead we force the stack
// check's expression position which eliminates the empty
// statement's position.
latest_source_info_.ForceExpressionPosition(position);
}
Output(Bytecode::kStackCheck);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::OsrPoll(int loop_depth) {
Output(Bytecode::kOsrPoll, UnsignedOperand(loop_depth));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
Output(Bytecode::kThrow);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ReThrow() {
Output(Bytecode::kReThrow);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
SetReturnPosition();
Output(Bytecode::kReturn);
return_seen_in_block_ = true;
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Debugger() {
Output(Bytecode::kDebugger);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(
Register receiver, Register cache_info_triple) {
Output(Bytecode::kForInPrepare, RegisterOperand(receiver),
RegisterOperand(cache_info_triple));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index,
Register cache_length) {
Output(Bytecode::kForInDone, RegisterOperand(index),
RegisterOperand(cache_length));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(
Register receiver, Register index, Register cache_type_array_pair,
int feedback_slot) {
Output(Bytecode::kForInNext, RegisterOperand(receiver),
RegisterOperand(index), RegisterOperand(cache_type_array_pair),
UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
Output(Bytecode::kForInStep, RegisterOperand(index));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator(
Register generator) {
Output(Bytecode::kSuspendGenerator, RegisterOperand(generator));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator(
Register generator) {
Output(Bytecode::kResumeGenerator, RegisterOperand(generator));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(
int handler_id, HandlerTable::CatchPrediction catch_prediction) {
BytecodeLabel handler;
Bind(&handler);
handler_table_builder()->SetHandlerTarget(handler_id, handler.offset());
handler_table_builder()->SetPrediction(handler_id, catch_prediction);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id,
Register context) {
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) {
BytecodeLabel try_end;
Bind(&try_end);
handler_table_builder()->SetTryRegionEnd(handler_id, try_end.offset());
return *this;
}
void BytecodeArrayBuilder::EnsureReturn() {
if (!return_seen_in_block_) {
LoadUndefined();
Return();
}
DCHECK(return_seen_in_block_);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
Register receiver_args,
size_t receiver_args_count,
int feedback_slot,
TailCallMode tail_call_mode) {
Bytecode bytecode = BytecodeForCall(tail_call_mode);
Output(bytecode, RegisterOperand(callable), RegisterOperand(receiver_args),
UnsignedOperand(receiver_args_count), UnsignedOperand(feedback_slot));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
Register first_arg,
size_t arg_count,
int feedback_slot_id) {
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
Output(Bytecode::kNew, RegisterOperand(constructor),
RegisterOperand(first_arg), UnsignedOperand(arg_count),
UnsignedOperand(feedback_slot_id));
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);
DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort);
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
Bytecode bytecode;
uint32_t id;
if (IntrinsicsHelper::IsSupported(function_id)) {
bytecode = Bytecode::kInvokeIntrinsic;
id = static_cast<uint32_t>(IntrinsicsHelper::FromRuntimeId(function_id));
} else {
bytecode = Bytecode::kCallRuntime;
id = static_cast<uint32_t>(function_id);
}
Output(bytecode, id, RegisterOperand(first_arg), UnsignedOperand(arg_count));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count,
Register first_return) {
DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort);
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id),
RegisterOperand(first_arg), UnsignedOperand(arg_count),
RegisterOperand(first_return));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(
int context_index, Register receiver_args, size_t receiver_args_count) {
Output(Bytecode::kCallJSRuntime, UnsignedOperand(context_index),
RegisterOperand(receiver_args), UnsignedOperand(receiver_args_count));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
LanguageMode language_mode) {
Output(BytecodeForDelete(language_mode), RegisterOperand(object));
return *this;
}
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
return constant_array_builder()->Insert(object);
}
size_t BytecodeArrayBuilder::AllocateConstantPoolEntry() {
return constant_array_builder()->AllocateEntry();
}
void BytecodeArrayBuilder::InsertConstantPoolEntryAt(size_t entry,
Handle<Object> object) {
constant_array_builder()->InsertAllocatedEntry(entry, object);
}
void BytecodeArrayBuilder::SetReturnPosition() {
if (return_position_ == kNoSourcePosition) return;
latest_source_info_.MakeStatementPosition(return_position_);
}
void BytecodeArrayBuilder::SetStatementPosition(Statement* stmt) {
if (stmt->position() == kNoSourcePosition) return;
latest_source_info_.MakeStatementPosition(stmt->position());
}
void BytecodeArrayBuilder::SetExpressionPosition(Expression* expr) {
if (expr->position() == kNoSourcePosition) return;
if (!latest_source_info_.is_statement()) {
// Ensure the current expression position is overwritten with the
// latest value.
latest_source_info_.MakeExpressionPosition(expr->position());
}
}
void BytecodeArrayBuilder::SetExpressionAsStatementPosition(Expression* expr) {
if (expr->position() == kNoSourcePosition) return;
latest_source_info_.MakeStatementPosition(expr->position());
}
bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const {
return temporary_register_allocator()->RegisterIsLive(reg);
}
bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const {
if (!reg.is_valid()) {
return false;
}
if (reg.is_current_context() || reg.is_function_closure() ||
reg.is_new_target()) {
return true;
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count());
return parameter_index >= 0 && parameter_index < parameter_count();
} else if (reg.index() < fixed_register_count()) {
return true;
} else {
return TemporaryRegisterIsLive(reg);
}
}
bool BytecodeArrayBuilder::OperandsAreValid(
Bytecode bytecode, int operand_count, uint32_t operand0, uint32_t operand1,
uint32_t operand2, uint32_t operand3) const {
if (Bytecodes::NumberOfOperands(bytecode) != operand_count) {
return false;
}
uint32_t operands[] = {operand0, operand1, operand2, operand3};
const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
for (int i = 0; i < operand_count; ++i) {
switch (operand_types[i]) {
case OperandType::kNone:
return false;
case OperandType::kRegCount: {
CHECK_NE(i, 0);
CHECK(operand_types[i - 1] == OperandType::kMaybeReg ||
operand_types[i - 1] == OperandType::kReg);
if (i > 0 && operands[i] > 0) {
Register start = Register::FromOperand(operands[i - 1]);
Register end(start.index() + static_cast<int>(operands[i]) - 1);
if (!RegisterIsValid(start) || !RegisterIsValid(end) || start > end) {
return false;
}
}
break;
}
case OperandType::kFlag8:
case OperandType::kIntrinsicId:
if (Bytecodes::SizeForUnsignedOperand(operands[i]) >
OperandSize::kByte) {
return false;
}
break;
case OperandType::kRuntimeId:
if (Bytecodes::SizeForUnsignedOperand(operands[i]) >
OperandSize::kShort) {
return false;
}
break;
case OperandType::kIdx:
// TODO(oth): Consider splitting OperandType::kIdx into two
// operand types. One which is a constant pool index that can
// be checked, and the other is an unsigned value.
break;
case OperandType::kImm:
break;
case OperandType::kMaybeReg:
if (Register::FromOperand(operands[i]) == Register(0)) {
break;
}
// Fall-through to kReg case.
case OperandType::kReg:
case OperandType::kRegOut: {
Register reg = Register::FromOperand(operands[i]);
if (!RegisterIsValid(reg)) {
return false;
}
break;
}
case OperandType::kRegOutPair:
case OperandType::kRegPair: {
Register reg0 = Register::FromOperand(operands[i]);
Register reg1 = Register(reg0.index() + 1);
if (!RegisterIsValid(reg0) || !RegisterIsValid(reg1)) {
return false;
}
break;
}
case OperandType::kRegOutTriple: {
Register reg0 = Register::FromOperand(operands[i]);
Register reg1 = Register(reg0.index() + 1);
Register reg2 = Register(reg0.index() + 2);
if (!RegisterIsValid(reg0) || !RegisterIsValid(reg1) ||
!RegisterIsValid(reg2)) {
return false;
}
break;
}
}
}
return true;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
switch (op) {
case Token::Value::ADD:
return Bytecode::kAdd;
case Token::Value::SUB:
return Bytecode::kSub;
case Token::Value::MUL:
return Bytecode::kMul;
case Token::Value::DIV:
return Bytecode::kDiv;
case Token::Value::MOD:
return Bytecode::kMod;
case Token::Value::BIT_OR:
return Bytecode::kBitwiseOr;
case Token::Value::BIT_XOR:
return Bytecode::kBitwiseXor;
case Token::Value::BIT_AND:
return Bytecode::kBitwiseAnd;
case Token::Value::SHL:
return Bytecode::kShiftLeft;
case Token::Value::SAR:
return Bytecode::kShiftRight;
case Token::Value::SHR:
return Bytecode::kShiftRightLogical;
default:
UNREACHABLE();
return Bytecode::kIllegal;
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) {
switch (op) {
case Token::Value::ADD:
return Bytecode::kInc;
case Token::Value::SUB:
return Bytecode::kDec;
default:
UNREACHABLE();
return Bytecode::kIllegal;
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
switch (op) {
case Token::Value::EQ:
return Bytecode::kTestEqual;
case Token::Value::NE:
return Bytecode::kTestNotEqual;
case Token::Value::EQ_STRICT:
return Bytecode::kTestEqualStrict;
case Token::Value::LT:
return Bytecode::kTestLessThan;
case Token::Value::GT:
return Bytecode::kTestGreaterThan;
case Token::Value::LTE:
return Bytecode::kTestLessThanOrEqual;
case Token::Value::GTE:
return Bytecode::kTestGreaterThanOrEqual;
case Token::Value::INSTANCEOF:
return Bytecode::kTestInstanceOf;
case Token::Value::IN:
return Bytecode::kTestIn;
default:
UNREACHABLE();
return Bytecode::kIllegal;
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreNamedProperty(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStaNamedPropertySloppy;
case STRICT:
return Bytecode::kStaNamedPropertyStrict;
default:
UNREACHABLE();
}
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreKeyedProperty(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStaKeyedPropertySloppy;
case STRICT:
return Bytecode::kStaKeyedPropertyStrict;
default:
UNREACHABLE();
}
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) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStaGlobalSloppy;
case STRICT:
return Bytecode::kStaGlobalStrict;
default:
UNREACHABLE();
}
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreLookupSlot(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStaLookupSlotSloppy;
case STRICT:
return Bytecode::kStaLookupSlotStrict;
default:
UNREACHABLE();
}
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments(
CreateArgumentsType type) {
switch (type) {
case CreateArgumentsType::kMappedArguments:
return Bytecode::kCreateMappedArguments;
case CreateArgumentsType::kUnmappedArguments:
return Bytecode::kCreateUnmappedArguments;
case CreateArgumentsType::kRestParameter:
return Bytecode::kCreateRestParameter;
}
UNREACHABLE();
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kDeletePropertySloppy;
case STRICT:
return Bytecode::kDeletePropertyStrict;
default:
UNREACHABLE();
}
return Bytecode::kIllegal;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCall(TailCallMode tail_call_mode) {
switch (tail_call_mode) {
case TailCallMode::kDisallow:
return Bytecode::kCall;
case TailCallMode::kAllow:
return Bytecode::kTailCall;
default:
UNREACHABLE();
}
return Bytecode::kIllegal;
}
} // namespace interpreter
} // namespace internal
} // namespace v8