blob: 2849e00937b3229eb66eb70fa8226f6096853b3f [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/compiler/bytecode-graph-builder.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-generator.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/smi.h"
#include "src/objects/template-objects-inl.h"
#include "src/vector-slot-pair.h"
namespace v8 {
namespace internal {
namespace compiler {
// The abstract execution environment simulates the content of the interpreter
// register file. The environment performs SSA-renaming of all tracked nodes at
// split and merge points in the control flow.
class BytecodeGraphBuilder::Environment : public ZoneObject {
public:
Environment(BytecodeGraphBuilder* builder, int register_count,
int parameter_count,
interpreter::Register incoming_new_target_or_generator,
Node* control_dependency);
// Specifies whether environment binding methods should attach frame state
// inputs to nodes representing the value being bound. This is done because
// the {OutputFrameStateCombine} is closely related to the binding method.
enum FrameStateAttachmentMode { kAttachFrameState, kDontAttachFrameState };
int parameter_count() const { return parameter_count_; }
int register_count() const { return register_count_; }
Node* LookupAccumulator() const;
Node* LookupRegister(interpreter::Register the_register) const;
Node* LookupGeneratorState() const;
void BindAccumulator(Node* node,
FrameStateAttachmentMode mode = kDontAttachFrameState);
void BindRegister(interpreter::Register the_register, Node* node,
FrameStateAttachmentMode mode = kDontAttachFrameState);
void BindRegistersToProjections(
interpreter::Register first_reg, Node* node,
FrameStateAttachmentMode mode = kDontAttachFrameState);
void BindGeneratorState(Node* node);
void RecordAfterState(Node* node,
FrameStateAttachmentMode mode = kDontAttachFrameState);
// Effect dependency tracked by this environment.
Node* GetEffectDependency() { return effect_dependency_; }
void UpdateEffectDependency(Node* dependency) {
effect_dependency_ = dependency;
}
// Preserve a checkpoint of the environment for the IR graph. Any
// further mutation of the environment will not affect checkpoints.
Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine,
const BytecodeLivenessState* liveness);
// Control dependency tracked by this environment.
Node* GetControlDependency() const { return control_dependency_; }
void UpdateControlDependency(Node* dependency) {
control_dependency_ = dependency;
}
Node* Context() const { return context_; }
void SetContext(Node* new_context) { context_ = new_context; }
Environment* Copy();
void Merge(Environment* other, const BytecodeLivenessState* liveness);
void FillWithOsrValues();
void PrepareForLoop(const BytecodeLoopAssignments& assignments,
const BytecodeLivenessState* liveness);
void PrepareForLoopExit(Node* loop,
const BytecodeLoopAssignments& assignments,
const BytecodeLivenessState* liveness);
private:
explicit Environment(const Environment* copy);
bool StateValuesRequireUpdate(Node** state_values, Node** values, int count);
void UpdateStateValues(Node** state_values, Node** values, int count);
Node* GetStateValuesFromCache(Node** values, int count,
const BitVector* liveness, int liveness_offset);
int RegisterToValuesIndex(interpreter::Register the_register) const;
Zone* zone() const { return builder_->local_zone(); }
Graph* graph() const { return builder_->graph(); }
CommonOperatorBuilder* common() const { return builder_->common(); }
BytecodeGraphBuilder* builder() const { return builder_; }
const NodeVector* values() const { return &values_; }
NodeVector* values() { return &values_; }
int register_base() const { return register_base_; }
int accumulator_base() const { return accumulator_base_; }
BytecodeGraphBuilder* builder_;
int register_count_;
int parameter_count_;
Node* context_;
Node* control_dependency_;
Node* effect_dependency_;
NodeVector values_;
Node* parameters_state_values_;
Node* generator_state_;
int register_base_;
int accumulator_base_;
};
// A helper for creating a temporary sub-environment for simple branches.
struct BytecodeGraphBuilder::SubEnvironment final {
public:
explicit SubEnvironment(BytecodeGraphBuilder* builder)
: builder_(builder), parent_(builder->environment()->Copy()) {}
~SubEnvironment() { builder_->set_environment(parent_); }
private:
BytecodeGraphBuilder* builder_;
BytecodeGraphBuilder::Environment* parent_;
};
// Issues:
// - Scopes - intimately tied to AST. Need to eval what is needed.
// - Need to resolve closure parameter treatment.
BytecodeGraphBuilder::Environment::Environment(
BytecodeGraphBuilder* builder, int register_count, int parameter_count,
interpreter::Register incoming_new_target_or_generator,
Node* control_dependency)
: builder_(builder),
register_count_(register_count),
parameter_count_(parameter_count),
control_dependency_(control_dependency),
effect_dependency_(control_dependency),
values_(builder->local_zone()),
parameters_state_values_(nullptr),
generator_state_(nullptr) {
// The layout of values_ is:
//
// [receiver] [parameters] [registers] [accumulator]
//
// parameter[0] is the receiver (this), parameters 1..N are the
// parameters supplied to the method (arg0..argN-1). The accumulator
// is stored separately.
// Parameters including the receiver
for (int i = 0; i < parameter_count; i++) {
const char* debug_name = (i == 0) ? "%this" : nullptr;
const Operator* op = common()->Parameter(i, debug_name);
Node* parameter = builder->graph()->NewNode(op, graph()->start());
values()->push_back(parameter);
}
// Registers
register_base_ = static_cast<int>(values()->size());
Node* undefined_constant = builder->jsgraph()->UndefinedConstant();
values()->insert(values()->end(), register_count, undefined_constant);
// Accumulator
accumulator_base_ = static_cast<int>(values()->size());
values()->push_back(undefined_constant);
// Context
int context_index = Linkage::GetJSCallContextParamIndex(parameter_count);
const Operator* op = common()->Parameter(context_index, "%context");
context_ = builder->graph()->NewNode(op, graph()->start());
// Incoming new.target or generator register
if (incoming_new_target_or_generator.is_valid()) {
int new_target_index =
Linkage::GetJSCallNewTargetParamIndex(parameter_count);
const Operator* op = common()->Parameter(new_target_index, "%new.target");
Node* new_target_node = builder->graph()->NewNode(op, graph()->start());
int values_index = RegisterToValuesIndex(incoming_new_target_or_generator);
values()->at(values_index) = new_target_node;
}
}
BytecodeGraphBuilder::Environment::Environment(
const BytecodeGraphBuilder::Environment* other)
: builder_(other->builder_),
register_count_(other->register_count_),
parameter_count_(other->parameter_count_),
context_(other->context_),
control_dependency_(other->control_dependency_),
effect_dependency_(other->effect_dependency_),
values_(other->zone()),
parameters_state_values_(other->parameters_state_values_),
generator_state_(other->generator_state_),
register_base_(other->register_base_),
accumulator_base_(other->accumulator_base_) {
values_ = other->values_;
}
int BytecodeGraphBuilder::Environment::RegisterToValuesIndex(
interpreter::Register the_register) const {
if (the_register.is_parameter()) {
return the_register.ToParameterIndex(parameter_count());
} else {
return the_register.index() + register_base();
}
}
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const {
return values()->at(accumulator_base_);
}
Node* BytecodeGraphBuilder::Environment::LookupGeneratorState() const {
DCHECK_NOT_NULL(generator_state_);
return generator_state_;
}
Node* BytecodeGraphBuilder::Environment::LookupRegister(
interpreter::Register the_register) const {
if (the_register.is_current_context()) {
return Context();
} else if (the_register.is_function_closure()) {
return builder()->GetFunctionClosure();
} else {
int values_index = RegisterToValuesIndex(the_register);
return values()->at(values_index);
}
}
void BytecodeGraphBuilder::Environment::BindAccumulator(
Node* node, FrameStateAttachmentMode mode) {
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(0));
}
values()->at(accumulator_base_) = node;
}
void BytecodeGraphBuilder::Environment::BindGeneratorState(Node* node) {
generator_state_ = node;
}
void BytecodeGraphBuilder::Environment::BindRegister(
interpreter::Register the_register, Node* node,
FrameStateAttachmentMode mode) {
int values_index = RegisterToValuesIndex(the_register);
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(
accumulator_base_ - values_index));
}
values()->at(values_index) = node;
}
void BytecodeGraphBuilder::Environment::BindRegistersToProjections(
interpreter::Register first_reg, Node* node,
FrameStateAttachmentMode mode) {
int values_index = RegisterToValuesIndex(first_reg);
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(
accumulator_base_ - values_index));
}
for (int i = 0; i < node->op()->ValueOutputCount(); i++) {
values()->at(values_index + i) =
builder()->NewNode(common()->Projection(i), node);
}
}
void BytecodeGraphBuilder::Environment::RecordAfterState(
Node* node, FrameStateAttachmentMode mode) {
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
builder()->PrepareFrameState(node, OutputFrameStateCombine::Ignore());
}
}
BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::Environment::Copy() {
return new (zone()) Environment(this);
}
void BytecodeGraphBuilder::Environment::Merge(
BytecodeGraphBuilder::Environment* other,
const BytecodeLivenessState* liveness) {
// Create a merge of the control dependencies of both environments and update
// the current environment's control dependency accordingly.
Node* control = builder()->MergeControl(GetControlDependency(),
other->GetControlDependency());
UpdateControlDependency(control);
// Create a merge of the effect dependencies of both environments and update
// the current environment's effect dependency accordingly.
Node* effect = builder()->MergeEffect(GetEffectDependency(),
other->GetEffectDependency(), control);
UpdateEffectDependency(effect);
// Introduce Phi nodes for values that are live and have differing inputs at
// the merge point, potentially extending an existing Phi node if possible.
context_ = builder()->MergeValue(context_, other->context_, control);
for (int i = 0; i < parameter_count(); i++) {
values_[i] = builder()->MergeValue(values_[i], other->values_[i], control);
}
for (int i = 0; i < register_count(); i++) {
int index = register_base() + i;
if (liveness == nullptr || liveness->RegisterIsLive(i)) {
#if DEBUG
// We only do these DCHECKs when we are not in the resume path of a
// generator -- this is, when either there is no generator state at all,
// or the generator state is not the constant "executing" value.
if (generator_state_ == nullptr ||
NumberMatcher(generator_state_)
.Is(JSGeneratorObject::kGeneratorExecuting)) {
DCHECK_NE(values_[index], builder()->jsgraph()->OptimizedOutConstant());
DCHECK_NE(other->values_[index],
builder()->jsgraph()->OptimizedOutConstant());
}
#endif
values_[index] =
builder()->MergeValue(values_[index], other->values_[index], control);
} else {
values_[index] = builder()->jsgraph()->OptimizedOutConstant();
}
}
if (liveness == nullptr || liveness->AccumulatorIsLive()) {
DCHECK_NE(values_[accumulator_base()],
builder()->jsgraph()->OptimizedOutConstant());
DCHECK_NE(other->values_[accumulator_base()],
builder()->jsgraph()->OptimizedOutConstant());
values_[accumulator_base()] =
builder()->MergeValue(values_[accumulator_base()],
other->values_[accumulator_base()], control);
} else {
values_[accumulator_base()] = builder()->jsgraph()->OptimizedOutConstant();
}
if (generator_state_ != nullptr) {
DCHECK_NOT_NULL(other->generator_state_);
generator_state_ = builder()->MergeValue(generator_state_,
other->generator_state_, control);
}
}
void BytecodeGraphBuilder::Environment::PrepareForLoop(
const BytecodeLoopAssignments& assignments,
const BytecodeLivenessState* liveness) {
// Create a control node for the loop header.
Node* control = builder()->NewLoop();
// Create a Phi for external effects.
Node* effect = builder()->NewEffectPhi(1, GetEffectDependency(), control);
UpdateEffectDependency(effect);
// Create Phis for any values that are live on entry to the loop and may be
// updated by the end of the loop.
context_ = builder()->NewPhi(1, context_, control);
for (int i = 0; i < parameter_count(); i++) {
if (assignments.ContainsParameter(i)) {
values_[i] = builder()->NewPhi(1, values_[i], control);
}
}
for (int i = 0; i < register_count(); i++) {
if (assignments.ContainsLocal(i) &&
(liveness == nullptr || liveness->RegisterIsLive(i))) {
int index = register_base() + i;
values_[index] = builder()->NewPhi(1, values_[index], control);
}
}
// The accumulator should not be live on entry.
DCHECK_IMPLIES(liveness != nullptr, !liveness->AccumulatorIsLive());
if (generator_state_ != nullptr) {
generator_state_ = builder()->NewPhi(1, generator_state_, control);
}
// Connect to the loop end.
Node* terminate = builder()->graph()->NewNode(
builder()->common()->Terminate(), effect, control);
builder()->exit_controls_.push_back(terminate);
}
void BytecodeGraphBuilder::Environment::FillWithOsrValues() {
Node* start = graph()->start();
// Create OSR values for each environment value.
SetContext(graph()->NewNode(
common()->OsrValue(Linkage::kOsrContextSpillSlotIndex), start));
int size = static_cast<int>(values()->size());
for (int i = 0; i < size; i++) {
int idx = i; // Indexing scheme follows {StandardFrame}, adapt accordingly.
if (i >= register_base()) idx += InterpreterFrameConstants::kExtraSlotCount;
if (i >= accumulator_base()) idx = Linkage::kOsrAccumulatorRegisterIndex;
values()->at(i) = graph()->NewNode(common()->OsrValue(idx), start);
}
}
bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
Node** state_values, Node** values, int count) {
if (*state_values == nullptr) {
return true;
}
Node::Inputs inputs = (*state_values)->inputs();
if (inputs.count() != count) return true;
for (int i = 0; i < count; i++) {
if (inputs[i] != values[i]) {
return true;
}
}
return false;
}
void BytecodeGraphBuilder::Environment::PrepareForLoopExit(
Node* loop, const BytecodeLoopAssignments& assignments,
const BytecodeLivenessState* liveness) {
DCHECK_EQ(loop->opcode(), IrOpcode::kLoop);
Node* control = GetControlDependency();
// Create the loop exit node.
Node* loop_exit = graph()->NewNode(common()->LoopExit(), control, loop);
UpdateControlDependency(loop_exit);
// Rename the effect.
Node* effect_rename = graph()->NewNode(common()->LoopExitEffect(),
GetEffectDependency(), loop_exit);
UpdateEffectDependency(effect_rename);
// TODO(jarin) We should also rename context here. However, unconditional
// renaming confuses global object and native context specialization.
// We should only rename if the context is assigned in the loop.
// Rename the environment values if they were assigned in the loop and are
// live after exiting the loop.
for (int i = 0; i < parameter_count(); i++) {
if (assignments.ContainsParameter(i)) {
Node* rename =
graph()->NewNode(common()->LoopExitValue(), values_[i], loop_exit);
values_[i] = rename;
}
}
for (int i = 0; i < register_count(); i++) {
if (assignments.ContainsLocal(i) &&
(liveness == nullptr || liveness->RegisterIsLive(i))) {
Node* rename = graph()->NewNode(common()->LoopExitValue(),
values_[register_base() + i], loop_exit);
values_[register_base() + i] = rename;
}
}
if (liveness == nullptr || liveness->AccumulatorIsLive()) {
Node* rename = graph()->NewNode(common()->LoopExitValue(),
values_[accumulator_base()], loop_exit);
values_[accumulator_base()] = rename;
}
if (generator_state_ != nullptr) {
generator_state_ = graph()->NewNode(common()->LoopExitValue(),
generator_state_, loop_exit);
}
}
void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
Node** values,
int count) {
if (StateValuesRequireUpdate(state_values, values, count)) {
const Operator* op = common()->StateValues(count, SparseInputMask::Dense());
(*state_values) = graph()->NewNode(op, count, values);
}
}
Node* BytecodeGraphBuilder::Environment::GetStateValuesFromCache(
Node** values, int count, const BitVector* liveness, int liveness_offset) {
return builder_->state_values_cache_.GetNodeForValues(
values, static_cast<size_t>(count), liveness, liveness_offset);
}
Node* BytecodeGraphBuilder::Environment::Checkpoint(
BailoutId bailout_id, OutputFrameStateCombine combine,
const BytecodeLivenessState* liveness) {
if (parameter_count() == register_count()) {
// Re-use the state-value cache if the number of local registers happens
// to match the parameter count.
parameters_state_values_ = GetStateValuesFromCache(
&values()->at(0), parameter_count(), nullptr, 0);
} else {
UpdateStateValues(&parameters_state_values_, &values()->at(0),
parameter_count());
}
Node* registers_state_values =
GetStateValuesFromCache(&values()->at(register_base()), register_count(),
liveness ? &liveness->bit_vector() : nullptr, 0);
bool accumulator_is_live = !liveness || liveness->AccumulatorIsLive();
Node* accumulator_state_value =
accumulator_is_live && combine != OutputFrameStateCombine::PokeAt(0)
? values()->at(accumulator_base())
: builder()->jsgraph()->OptimizedOutConstant();
const Operator* op = common()->FrameState(
bailout_id, combine, builder()->frame_state_function_info());
Node* result = graph()->NewNode(
op, parameters_state_values_, registers_state_values,
accumulator_state_value, Context(), builder()->GetFunctionClosure(),
builder()->graph()->start());
return result;
}
BytecodeGraphBuilder::BytecodeGraphBuilder(
Zone* local_zone, Handle<BytecodeArray> bytecode_array,
Handle<SharedFunctionInfo> shared_info,
Handle<FeedbackVector> feedback_vector, BailoutId osr_offset,
JSGraph* jsgraph, CallFrequency& invocation_frequency,
SourcePositionTable* source_positions, Handle<Context> native_context,
int inlining_id, JSTypeHintLowering::Flags flags, bool stack_check,
bool analyze_environment_liveness)
: local_zone_(local_zone),
jsgraph_(jsgraph),
invocation_frequency_(invocation_frequency),
bytecode_array_(bytecode_array),
feedback_vector_(feedback_vector),
type_hint_lowering_(jsgraph, feedback_vector, flags),
frame_state_function_info_(common()->CreateFrameStateFunctionInfo(
FrameStateType::kInterpretedFunction,
bytecode_array->parameter_count(), bytecode_array->register_count(),
shared_info)),
bytecode_iterator_(nullptr),
bytecode_analysis_(nullptr),
environment_(nullptr),
osr_offset_(osr_offset),
currently_peeled_loop_offset_(-1),
stack_check_(stack_check),
analyze_environment_liveness_(analyze_environment_liveness),
merge_environments_(local_zone),
generator_merge_environments_(local_zone),
exception_handlers_(local_zone),
current_exception_handler_(0),
input_buffer_size_(0),
input_buffer_(nullptr),
needs_eager_checkpoint_(true),
exit_controls_(local_zone),
state_values_cache_(jsgraph),
source_positions_(source_positions),
start_position_(shared_info->StartPosition(), inlining_id),
shared_info_(shared_info),
native_context_(native_context) {}
Node* BytecodeGraphBuilder::GetFunctionClosure() {
if (!function_closure_.is_set()) {
int index = Linkage::kJSCallClosureParamIndex;
const Operator* op = common()->Parameter(index, "%closure");
Node* node = NewNode(op, graph()->start());
function_closure_.set(node);
}
return function_closure_.get();
}
Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) {
const Operator* op =
javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true);
Node* native_context = NewNode(op);
Node* result = NewNode(javascript()->LoadContext(0, index, true));
NodeProperties::ReplaceContextInput(result, native_context);
return result;
}
VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) {
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
FeedbackNexus nexus(feedback_vector(), slot);
return VectorSlotPair(feedback_vector(), slot, nexus.ic_state());
}
void BytecodeGraphBuilder::CreateGraph() {
SourcePositionTable::Scope pos_scope(source_positions_, start_position_);
// Set up the basic structure of the graph. Outputs for {Start} are the formal
// parameters (including the receiver) plus new target, number of arguments,
// context and closure.
int actual_parameter_count = bytecode_array()->parameter_count() + 4;
graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count)));
Environment env(this, bytecode_array()->register_count(),
bytecode_array()->parameter_count(),
bytecode_array()->incoming_new_target_or_generator_register(),
graph()->start());
set_environment(&env);
VisitBytecodes();
// Finish the basic structure of the graph.
DCHECK_NE(0u, exit_controls_.size());
int const input_count = static_cast<int>(exit_controls_.size());
Node** const inputs = &exit_controls_.front();
Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs);
graph()->SetEnd(end);
}
void BytecodeGraphBuilder::PrepareEagerCheckpoint() {
if (needs_eager_checkpoint()) {
// Create an explicit checkpoint node for before the operation. This only
// needs to happen if we aren't effect-dominated by a {Checkpoint} already.
mark_as_needing_eager_checkpoint(false);
Node* node = NewNode(common()->Checkpoint());
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node)->opcode());
BailoutId bailout_id(bytecode_iterator().current_offset());
const BytecodeLivenessState* liveness_before =
bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset());
Node* frame_state_before = environment()->Checkpoint(
bailout_id, OutputFrameStateCombine::Ignore(), liveness_before);
NodeProperties::ReplaceFrameStateInput(node, frame_state_before);
#ifdef DEBUG
} else {
// In case we skipped checkpoint creation above, we must be able to find an
// existing checkpoint that effect-dominates the nodes about to be created.
// Starting a search from the current effect-dependency has to succeed.
Node* effect = environment()->GetEffectDependency();
while (effect->opcode() != IrOpcode::kCheckpoint) {
DCHECK(effect->op()->HasProperty(Operator::kNoWrite));
DCHECK_EQ(1, effect->op()->EffectInputCount());
effect = NodeProperties::GetEffectInput(effect);
}
}
#else
}
#endif // DEBUG
}
void BytecodeGraphBuilder::PrepareFrameState(Node* node,
OutputFrameStateCombine combine) {
if (OperatorProperties::HasFrameStateInput(node->op())) {
// Add the frame state for after the operation. The node in question has
// already been created and had a {Dead} frame state input up until now.
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node)->opcode());
BailoutId bailout_id(bytecode_iterator().current_offset());
const BytecodeLivenessState* liveness_after =
bytecode_analysis()->GetOutLivenessFor(
bytecode_iterator().current_offset());
Node* frame_state_after =
environment()->Checkpoint(bailout_id, combine, liveness_after);
NodeProperties::ReplaceFrameStateInput(node, frame_state_after);
}
}
// Stores the state of the SourcePosition iterator, and the index to the
// current exception handlers stack. We need, during the OSR graph generation,
// to backup the states of these iterators at the LoopHeader offset of each
// outer loop which contains the OSR loop. The iterators are then restored when
// peeling the loops, so that both exception handling and synchronisation with
// the source position can be achieved.
class BytecodeGraphBuilder::OsrIteratorState {
public:
OsrIteratorState(interpreter::BytecodeArrayIterator* iterator,
SourcePositionTableIterator* source_position_iterator,
BytecodeGraphBuilder* graph_builder)
: iterator_(iterator),
source_position_iterator_(source_position_iterator),
graph_builder_(graph_builder),
saved_states_(graph_builder->local_zone()) {}
void ProcessOsrPrelude() {
ZoneVector<int> outer_loop_offsets(graph_builder_->local_zone());
const BytecodeAnalysis& bytecode_analysis =
*(graph_builder_->bytecode_analysis());
int osr_offset = bytecode_analysis.osr_entry_point();
// We find here the outermost loop which contains the OSR loop.
int outermost_loop_offset = osr_offset;
while ((outermost_loop_offset =
bytecode_analysis.GetLoopInfoFor(outermost_loop_offset)
.parent_offset()) != -1) {
outer_loop_offsets.push_back(outermost_loop_offset);
}
outermost_loop_offset =
outer_loop_offsets.empty() ? osr_offset : outer_loop_offsets.back();
// We will not processs any bytecode before the outermost_loop_offset, but
// the source_position_iterator needs to be advanced step by step through
// the bytecode.
for (; iterator_->current_offset() != outermost_loop_offset;
iterator_->Advance()) {
graph_builder_->UpdateSourcePosition(source_position_iterator_,
iterator_->current_offset());
}
// We save some iterators states at the offsets of the loop headers of the
// outer loops (the ones containing the OSR loop). They will be used for
// jumping back in the bytecode.
for (ZoneVector<int>::const_reverse_iterator it =
outer_loop_offsets.crbegin();
it != outer_loop_offsets.crend(); ++it) {
int next_loop_offset = *it;
for (; iterator_->current_offset() != next_loop_offset;
iterator_->Advance()) {
graph_builder_->UpdateSourcePosition(source_position_iterator_,
iterator_->current_offset());
}
graph_builder_->ExitThenEnterExceptionHandlers(
iterator_->current_offset());
saved_states_.push(
IteratorsStates(graph_builder_->current_exception_handler(),
source_position_iterator_->GetState()));
}
// Finishing by advancing to the OSR entry
for (; iterator_->current_offset() != osr_offset; iterator_->Advance()) {
graph_builder_->UpdateSourcePosition(source_position_iterator_,
iterator_->current_offset());
}
// Enters all remaining exception handler which end before the OSR loop
// so that on next call of VisitSingleBytecode they will get popped from
// the exception handlers stack.
graph_builder_->ExitThenEnterExceptionHandlers(osr_offset);
graph_builder_->set_currently_peeled_loop_offset(
bytecode_analysis.GetLoopInfoFor(osr_offset).parent_offset());
}
void RestoreState(int target_offset, int new_parent_offset) {
iterator_->SetOffset(target_offset);
// In case of a return, we must not build loop exits for
// not-yet-built outer loops.
graph_builder_->set_currently_peeled_loop_offset(new_parent_offset);
IteratorsStates saved_state = saved_states_.top();
source_position_iterator_->RestoreState(saved_state.source_iterator_state_);
graph_builder_->set_current_exception_handler(
saved_state.exception_handler_index_);
saved_states_.pop();
}
private:
struct IteratorsStates {
int exception_handler_index_;
SourcePositionTableIterator::IndexAndPositionState source_iterator_state_;
IteratorsStates(int exception_handler_index,
SourcePositionTableIterator::IndexAndPositionState
source_iterator_state)
: exception_handler_index_(exception_handler_index),
source_iterator_state_(source_iterator_state) {}
};
interpreter::BytecodeArrayIterator* iterator_;
SourcePositionTableIterator* source_position_iterator_;
BytecodeGraphBuilder* graph_builder_;
ZoneStack<IteratorsStates> saved_states_;
};
void BytecodeGraphBuilder::RemoveMergeEnvironmentsBeforeOffset(
int limit_offset) {
if (!merge_environments_.empty()) {
ZoneMap<int, Environment*>::iterator it = merge_environments_.begin();
ZoneMap<int, Environment*>::iterator stop_it = merge_environments_.end();
while (it != stop_it && it->first <= limit_offset) {
it = merge_environments_.erase(it);
}
}
}
// We will iterate through the OSR loop, then its parent, and so on
// until we have reached the outmost loop containing the OSR loop. We do
// not generate nodes for anything before the outermost loop.
void BytecodeGraphBuilder::AdvanceToOsrEntryAndPeelLoops(
interpreter::BytecodeArrayIterator* iterator,
SourcePositionTableIterator* source_position_iterator) {
const BytecodeAnalysis& analysis = *(bytecode_analysis());
int osr_offset = analysis.osr_entry_point();
OsrIteratorState iterator_states(iterator, source_position_iterator, this);
iterator_states.ProcessOsrPrelude();
DCHECK_EQ(iterator->current_offset(), osr_offset);
environment()->FillWithOsrValues();
// Suppose we have n nested loops, loop_0 being the outermost one, and
// loop_n being the OSR loop. We start iterating the bytecode at the header
// of loop_n (the OSR loop), and then we peel the part of the the body of
// loop_{n-1} following the end of loop_n. We then rewind the iterator to
// the header of loop_{n-1}, and so on until we have partly peeled loop 0.
// The full loop_0 body will be generating with the rest of the function,
// outside the OSR generation.
// To do so, if we are visiting a loop, we continue to visit what's left
// of its parent, and then when reaching the parent's JumpLoop, we do not
// create any jump for that but rewind the bytecode iterator to visit the
// parent loop entirely, and so on.
int current_parent_offset =
analysis.GetLoopInfoFor(osr_offset).parent_offset();
while (current_parent_offset != -1) {
const LoopInfo& current_parent_loop =
analysis.GetLoopInfoFor(current_parent_offset);
// We iterate until the back edge of the parent loop, which we detect by
// the offset that the JumpLoop targets.
for (; !iterator->done(); iterator->Advance()) {
if (iterator->current_bytecode() == interpreter::Bytecode::kJumpLoop &&
iterator->GetJumpTargetOffset() == current_parent_offset) {
// Reached the end of the current parent loop.
break;
}
VisitSingleBytecode(source_position_iterator);
}
DCHECK(!iterator->done()); // Should have found the loop's jump target.
// We also need to take care of the merge environments and exceptions
// handlers here because the omitted JumpLoop bytecode can still be the
// target of jumps or the first bytecode after a try block.
ExitThenEnterExceptionHandlers(iterator->current_offset());
SwitchToMergeEnvironment(iterator->current_offset());
// This jump is the jump of our parent loop, which is not yet created.
// So we do not build the jump nodes, but restore the bytecode and the
// SourcePosition iterators to the values they had when we were visiting
// the offset pointed at by the JumpLoop we've just reached.
// We have already built nodes for inner loops, but now we will
// iterate again over them and build new nodes corresponding to the same
// bytecode offsets. Any jump or reference to this inner loops must now
// point to the new nodes we will build, hence we clear the relevant part
// of the environment.
// Completely clearing the environment is not possible because merge
// environments for forward jumps out of the loop need to be preserved
// (e.g. a return or a labeled break in the middle of a loop).
RemoveMergeEnvironmentsBeforeOffset(iterator->current_offset());
iterator_states.RestoreState(current_parent_offset,
current_parent_loop.parent_offset());
current_parent_offset = current_parent_loop.parent_offset();
}
}
void BytecodeGraphBuilder::VisitSingleBytecode(
SourcePositionTableIterator* source_position_iterator) {
const interpreter::BytecodeArrayIterator& iterator = bytecode_iterator();
int current_offset = iterator.current_offset();
UpdateSourcePosition(source_position_iterator, current_offset);
ExitThenEnterExceptionHandlers(current_offset);
DCHECK_GE(exception_handlers_.empty() ? current_offset
: exception_handlers_.top().end_offset_,
current_offset);
SwitchToMergeEnvironment(current_offset);
if (environment() != nullptr) {
BuildLoopHeaderEnvironment(current_offset);
// Skip the first stack check if stack_check is false
if (!stack_check() &&
iterator.current_bytecode() == interpreter::Bytecode::kStackCheck) {
set_stack_check(true);
return;
}
switch (iterator.current_bytecode()) {
#define BYTECODE_CASE(name, ...) \
case interpreter::Bytecode::k##name: \
Visit##name(); \
break;
BYTECODE_LIST(BYTECODE_CASE)
#undef BYTECODE_CASE
}
}
}
void BytecodeGraphBuilder::VisitBytecodes() {
BytecodeAnalysis bytecode_analysis(bytecode_array(), local_zone(),
analyze_environment_liveness());
bytecode_analysis.Analyze(osr_offset_);
set_bytecode_analysis(&bytecode_analysis);
interpreter::BytecodeArrayIterator iterator(bytecode_array());
set_bytecode_iterator(&iterator);
SourcePositionTableIterator source_position_iterator(
handle(bytecode_array()->SourcePositionTable(), isolate()));
if (analyze_environment_liveness() && FLAG_trace_environment_liveness) {
StdoutStream of;
bytecode_analysis.PrintLivenessTo(of);
}
if (!bytecode_analysis.resume_jump_targets().empty()) {
environment()->BindGeneratorState(
jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting));
}
if (bytecode_analysis.HasOsrEntryPoint()) {
// We peel the OSR loop and any outer loop containing it except that we
// leave the nodes corresponding to the whole outermost loop (including
// the last copies of the loops it contains) to be generated by the normal
// bytecode iteration below.
AdvanceToOsrEntryAndPeelLoops(&iterator, &source_position_iterator);
}
bool has_one_shot_bytecode = false;
for (; !iterator.done(); iterator.Advance()) {
if (interpreter::Bytecodes::IsOneShotBytecode(
iterator.current_bytecode())) {
has_one_shot_bytecode = true;
}
VisitSingleBytecode(&source_position_iterator);
}
if (has_one_shot_bytecode) {
isolate()->CountUsage(
v8::Isolate::UseCounterFeature::kOptimizedFunctionWithOneShotBytecode);
}
set_bytecode_analysis(nullptr);
set_bytecode_iterator(nullptr);
DCHECK(exception_handlers_.empty());
}
void BytecodeGraphBuilder::VisitLdaZero() {
Node* node = jsgraph()->ZeroConstant();
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaSmi() {
Node* node = jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaConstant() {
Node* node = jsgraph()->Constant(
handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaUndefined() {
Node* node = jsgraph()->UndefinedConstant();
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaNull() {
Node* node = jsgraph()->NullConstant();
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaTheHole() {
Node* node = jsgraph()->TheHoleConstant();
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaTrue() {
Node* node = jsgraph()->TrueConstant();
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaFalse() {
Node* node = jsgraph()->FalseConstant();
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdar() {
Node* value =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
environment()->BindAccumulator(value);
}
void BytecodeGraphBuilder::VisitStar() {
Node* value = environment()->LookupAccumulator();
environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), value);
}
void BytecodeGraphBuilder::VisitMov() {
Node* value =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
environment()->BindRegister(bytecode_iterator().GetRegisterOperand(1), value);
}
Node* BytecodeGraphBuilder::BuildLoadGlobal(Handle<Name> name,
uint32_t feedback_slot_index,
TypeofMode typeof_mode) {
VectorSlotPair feedback = CreateVectorSlotPair(feedback_slot_index);
DCHECK(IsLoadGlobalICKind(feedback_vector()->GetKind(feedback.slot())));
const Operator* op = javascript()->LoadGlobal(name, feedback, typeof_mode);
return NewNode(op);
}
void BytecodeGraphBuilder::VisitLdaGlobal() {
PrepareEagerCheckpoint();
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)), isolate());
uint32_t feedback_slot_index = bytecode_iterator().GetIndexOperand(1);
Node* node =
BuildLoadGlobal(name, feedback_slot_index, TypeofMode::NOT_INSIDE_TYPEOF);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaGlobalInsideTypeof() {
PrepareEagerCheckpoint();
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)), isolate());
uint32_t feedback_slot_index = bytecode_iterator().GetIndexOperand(1);
Node* node =
BuildLoadGlobal(name, feedback_slot_index, TypeofMode::INSIDE_TYPEOF);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitStaGlobal() {
PrepareEagerCheckpoint();
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)), isolate());
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(1));
Node* value = environment()->LookupAccumulator();
LanguageMode language_mode =
feedback.vector()->GetLanguageMode(feedback.slot());
const Operator* op = javascript()->StoreGlobal(language_mode, name, feedback);
Node* node = NewNode(op, value);
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitStaInArrayLiteral() {
PrepareEagerCheckpoint();
Node* value = environment()->LookupAccumulator();
Node* array =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* index =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
const Operator* op = javascript()->StoreInArrayLiteral(feedback);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedStoreKeyed(op, array, index, value, feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, array, index, value);
}
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitStaDataPropertyInLiteral() {
PrepareEagerCheckpoint();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* name =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
Node* value = environment()->LookupAccumulator();
int flags = bytecode_iterator().GetFlagOperand(2);
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(3));
const Operator* op = javascript()->StoreDataPropertyInLiteral(feedback);
Node* node = NewNode(op, object, name, value, jsgraph()->Constant(flags));
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCollectTypeProfile() {
PrepareEagerCheckpoint();
Node* position =
jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));
Node* value = environment()->LookupAccumulator();
Node* vector = jsgraph()->Constant(feedback_vector());
const Operator* op = javascript()->CallRuntime(Runtime::kCollectTypeProfile);
Node* node = NewNode(op, position, value, vector);
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaContextSlot() {
const Operator* op = javascript()->LoadContext(
bytecode_iterator().GetUnsignedImmediateOperand(2),
bytecode_iterator().GetIndexOperand(1), false);
Node* node = NewNode(op);
Node* context =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
NodeProperties::ReplaceContextInput(node, context);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaImmutableContextSlot() {
const Operator* op = javascript()->LoadContext(
bytecode_iterator().GetUnsignedImmediateOperand(2),
bytecode_iterator().GetIndexOperand(1), true);
Node* node = NewNode(op);
Node* context =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
NodeProperties::ReplaceContextInput(node, context);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaCurrentContextSlot() {
const Operator* op = javascript()->LoadContext(
0, bytecode_iterator().GetIndexOperand(0), false);
Node* node = NewNode(op);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLdaImmutableCurrentContextSlot() {
const Operator* op = javascript()->LoadContext(
0, bytecode_iterator().GetIndexOperand(0), true);
Node* node = NewNode(op);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitStaContextSlot() {
const Operator* op = javascript()->StoreContext(
bytecode_iterator().GetUnsignedImmediateOperand(2),
bytecode_iterator().GetIndexOperand(1));
Node* value = environment()->LookupAccumulator();
Node* node = NewNode(op, value);
Node* context =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
NodeProperties::ReplaceContextInput(node, context);
}
void BytecodeGraphBuilder::VisitStaCurrentContextSlot() {
const Operator* op =
javascript()->StoreContext(0, bytecode_iterator().GetIndexOperand(0));
Node* value = environment()->LookupAccumulator();
NewNode(op, value);
}
void BytecodeGraphBuilder::BuildLdaLookupSlot(TypeofMode typeof_mode) {
PrepareEagerCheckpoint();
Node* name = jsgraph()->Constant(
handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
const Operator* op =
javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
? Runtime::kLoadLookupSlot
: Runtime::kLoadLookupSlotInsideTypeof);
Node* value = NewNode(op, name);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaLookupSlot() {
BuildLdaLookupSlot(TypeofMode::NOT_INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::VisitLdaLookupSlotInsideTypeof() {
BuildLdaLookupSlot(TypeofMode::INSIDE_TYPEOF);
}
BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
uint32_t depth) {
// Output environment where the context has an extension
Environment* slow_environment = nullptr;
// We only need to check up to the last-but-one depth, because the an eval
// in the same scope as the variable itself has no way of shadowing it.
for (uint32_t d = 0; d < depth; d++) {
Node* extension_slot =
NewNode(javascript()->LoadContext(d, Context::EXTENSION_INDEX, false));
Node* check_no_extension =
NewNode(simplified()->ReferenceEqual(), extension_slot,
jsgraph()->TheHoleConstant());
NewBranch(check_no_extension);
{
SubEnvironment sub_environment(this);
NewIfFalse();
// If there is an extension, merge into the slow path.
if (slow_environment == nullptr) {
slow_environment = environment();
NewMerge();
} else {
slow_environment->Merge(environment(),
bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset()));
}
}
NewIfTrue();
// Do nothing on if there is no extension, eventually falling through to
// the fast path.
}
// The depth can be zero, in which case no slow-path checks are built, and
// the slow path environment can be null.
DCHECK(depth == 0 || slow_environment != nullptr);
return slow_environment;
}
void BytecodeGraphBuilder::BuildLdaLookupContextSlot(TypeofMode typeof_mode) {
uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(2);
// Check if any context in the depth has an extension.
Environment* slow_environment = CheckContextExtensions(depth);
// Fast path, do a context load.
{
uint32_t slot_index = bytecode_iterator().GetIndexOperand(1);
const Operator* op = javascript()->LoadContext(depth, slot_index, false);
environment()->BindAccumulator(NewNode(op));
}
// Only build the slow path if there were any slow-path checks.
if (slow_environment != nullptr) {
// Add a merge to the fast environment.
NewMerge();
Environment* fast_environment = environment();
// Slow path, do a runtime load lookup.
set_environment(slow_environment);
{
Node* name = jsgraph()->Constant(
handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
const Operator* op =
javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
? Runtime::kLoadLookupSlot
: Runtime::kLoadLookupSlotInsideTypeof);
Node* value = NewNode(op, name);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
fast_environment->Merge(environment(),
bytecode_analysis()->GetOutLivenessFor(
bytecode_iterator().current_offset()));
set_environment(fast_environment);
mark_as_needing_eager_checkpoint(true);
}
}
void BytecodeGraphBuilder::VisitLdaLookupContextSlot() {
BuildLdaLookupContextSlot(TypeofMode::NOT_INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::VisitLdaLookupContextSlotInsideTypeof() {
BuildLdaLookupContextSlot(TypeofMode::INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::BuildLdaLookupGlobalSlot(TypeofMode typeof_mode) {
uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(2);
// Check if any context in the depth has an extension.
Environment* slow_environment = CheckContextExtensions(depth);
// Fast path, do a global load.
{
PrepareEagerCheckpoint();
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
uint32_t feedback_slot_index = bytecode_iterator().GetIndexOperand(1);
Node* node = BuildLoadGlobal(name, feedback_slot_index, typeof_mode);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
// Only build the slow path if there were any slow-path checks.
if (slow_environment != nullptr) {
// Add a merge to the fast environment.
NewMerge();
Environment* fast_environment = environment();
// Slow path, do a runtime load lookup.
set_environment(slow_environment);
{
Node* name = jsgraph()->Constant(
handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
const Operator* op =
javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
? Runtime::kLoadLookupSlot
: Runtime::kLoadLookupSlotInsideTypeof);
Node* value = NewNode(op, name);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
fast_environment->Merge(environment(),
bytecode_analysis()->GetOutLivenessFor(
bytecode_iterator().current_offset()));
set_environment(fast_environment);
mark_as_needing_eager_checkpoint(true);
}
}
void BytecodeGraphBuilder::VisitLdaLookupGlobalSlot() {
BuildLdaLookupGlobalSlot(TypeofMode::NOT_INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::VisitLdaLookupGlobalSlotInsideTypeof() {
BuildLdaLookupGlobalSlot(TypeofMode::INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::VisitStaLookupSlot() {
PrepareEagerCheckpoint();
Node* value = environment()->LookupAccumulator();
Node* name = jsgraph()->Constant(
handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
int bytecode_flags = bytecode_iterator().GetFlagOperand(1);
LanguageMode language_mode = static_cast<LanguageMode>(
interpreter::StoreLookupSlotFlags::LanguageModeBit::decode(
bytecode_flags));
LookupHoistingMode lookup_hoisting_mode = static_cast<LookupHoistingMode>(
interpreter::StoreLookupSlotFlags::LookupHoistingModeBit::decode(
bytecode_flags));
DCHECK_IMPLIES(lookup_hoisting_mode == LookupHoistingMode::kLegacySloppy,
is_sloppy(language_mode));
const Operator* op = javascript()->CallRuntime(
is_strict(language_mode)
? Runtime::kStoreLookupSlot_Strict
: lookup_hoisting_mode == LookupHoistingMode::kLegacySloppy
? Runtime::kStoreLookupSlot_SloppyHoisting
: Runtime::kStoreLookupSlot_Sloppy);
Node* store = NewNode(op, name, value);
environment()->BindAccumulator(store, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaNamedProperty() {
PrepareEagerCheckpoint();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
const Operator* op = javascript()->LoadNamed(name, feedback);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedLoadNamed(op, object, feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, object);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaNamedPropertyNoFeedback() {
PrepareEagerCheckpoint();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
const Operator* op = javascript()->LoadNamed(name, VectorSlotPair());
Node* node = NewNode(op, object);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaKeyedProperty() {
PrepareEagerCheckpoint();
Node* key = environment()->LookupAccumulator();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(1));
const Operator* op = javascript()->LoadProperty(feedback);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedLoadKeyed(op, object, key, feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, object, key);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::BuildNamedStore(StoreMode store_mode) {
PrepareEagerCheckpoint();
Node* value = environment()->LookupAccumulator();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
const Operator* op;
if (store_mode == StoreMode::kOwn) {
DCHECK_EQ(FeedbackSlotKind::kStoreOwnNamed,
feedback.vector()->GetKind(feedback.slot()));
op = javascript()->StoreNamedOwn(name, feedback);
} else {
DCHECK_EQ(StoreMode::kNormal, store_mode);
LanguageMode language_mode =
feedback.vector()->GetLanguageMode(feedback.slot());
op = javascript()->StoreNamed(language_mode, name, feedback);
}
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedStoreNamed(op, object, value, feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, object, value);
}
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitStaNamedProperty() {
BuildNamedStore(StoreMode::kNormal);
}
void BytecodeGraphBuilder::VisitStaNamedPropertyNoFeedback() {
PrepareEagerCheckpoint();
Node* value = environment()->LookupAccumulator();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Handle<Name> name(
Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
LanguageMode language_mode =
static_cast<LanguageMode>(bytecode_iterator().GetFlagOperand(2));
const Operator* op =
javascript()->StoreNamed(language_mode, name, VectorSlotPair());
Node* node = NewNode(op, object, value);
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitStaNamedOwnProperty() {
BuildNamedStore(StoreMode::kOwn);
}
void BytecodeGraphBuilder::VisitStaKeyedProperty() {
PrepareEagerCheckpoint();
Node* value = environment()->LookupAccumulator();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* key =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
LanguageMode language_mode =
feedback.vector()->GetLanguageMode(feedback.slot());
const Operator* op = javascript()->StoreProperty(language_mode, feedback);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedStoreKeyed(op, object, key, value, feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, object, key, value);
}
environment()->RecordAfterState(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitLdaModuleVariable() {
int32_t cell_index = bytecode_iterator().GetImmediateOperand(0);
uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(1);
Node* module =
NewNode(javascript()->LoadContext(depth, Context::EXTENSION_INDEX, true));
Node* value = NewNode(javascript()->LoadModule(cell_index), module);
environment()->BindAccumulator(value);
}
void BytecodeGraphBuilder::VisitStaModuleVariable() {
int32_t cell_index = bytecode_iterator().GetImmediateOperand(0);
uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(1);
Node* module =
NewNode(javascript()->LoadContext(depth, Context::EXTENSION_INDEX, true));
Node* value = environment()->LookupAccumulator();
NewNode(javascript()->StoreModule(cell_index), module, value);
}
void BytecodeGraphBuilder::VisitPushContext() {
Node* new_context = environment()->LookupAccumulator();
environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0),
environment()->Context());
environment()->SetContext(new_context);
}
void BytecodeGraphBuilder::VisitPopContext() {
Node* context =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
environment()->SetContext(context);
}
void BytecodeGraphBuilder::VisitCreateClosure() {
Handle<SharedFunctionInfo> shared_info(
SharedFunctionInfo::cast(
bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
AllocationType allocation =
interpreter::CreateClosureFlags::PretenuredBit::decode(
bytecode_iterator().GetFlagOperand(2))
? AllocationType::kOld
: AllocationType::kYoung;
const Operator* op = javascript()->CreateClosure(
shared_info,
feedback_vector()->GetClosureFeedbackCell(
bytecode_iterator().GetIndexOperand(1)),
handle(jsgraph()->isolate()->builtins()->builtin(Builtins::kCompileLazy),
isolate()),
allocation);
Node* closure = NewNode(op);
environment()->BindAccumulator(closure);
}
void BytecodeGraphBuilder::VisitCreateBlockContext() {
Handle<ScopeInfo> scope_info(
ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
const Operator* op = javascript()->CreateBlockContext(scope_info);
Node* context = NewNode(op);
environment()->BindAccumulator(context);
}
void BytecodeGraphBuilder::VisitCreateFunctionContext() {
Handle<ScopeInfo> scope_info(
ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
uint32_t slots = bytecode_iterator().GetUnsignedImmediateOperand(1);
const Operator* op =
javascript()->CreateFunctionContext(scope_info, slots, FUNCTION_SCOPE);
Node* context = NewNode(op);
environment()->BindAccumulator(context);
}
void BytecodeGraphBuilder::VisitCreateEvalContext() {
Handle<ScopeInfo> scope_info(
ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
uint32_t slots = bytecode_iterator().GetUnsignedImmediateOperand(1);
const Operator* op =
javascript()->CreateFunctionContext(scope_info, slots, EVAL_SCOPE);
Node* context = NewNode(op);
environment()->BindAccumulator(context);
}
void BytecodeGraphBuilder::VisitCreateCatchContext() {
interpreter::Register reg = bytecode_iterator().GetRegisterOperand(0);
Node* exception = environment()->LookupRegister(reg);
Handle<ScopeInfo> scope_info(
ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(1)),
isolate());
const Operator* op = javascript()->CreateCatchContext(scope_info);
Node* context = NewNode(op, exception);
environment()->BindAccumulator(context);
}
void BytecodeGraphBuilder::VisitCreateWithContext() {
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Handle<ScopeInfo> scope_info(
ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(1)),
isolate());
const Operator* op = javascript()->CreateWithContext(scope_info);
Node* context = NewNode(op, object);
environment()->BindAccumulator(context);
}
void BytecodeGraphBuilder::BuildCreateArguments(CreateArgumentsType type) {
const Operator* op = javascript()->CreateArguments(type);
Node* object = NewNode(op, GetFunctionClosure());
environment()->BindAccumulator(object, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCreateMappedArguments() {
BuildCreateArguments(CreateArgumentsType::kMappedArguments);
}
void BytecodeGraphBuilder::VisitCreateUnmappedArguments() {
BuildCreateArguments(CreateArgumentsType::kUnmappedArguments);
}
void BytecodeGraphBuilder::VisitCreateRestParameter() {
BuildCreateArguments(CreateArgumentsType::kRestParameter);
}
void BytecodeGraphBuilder::VisitCreateRegExpLiteral() {
Handle<String> constant_pattern(
String::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
int const slot_id = bytecode_iterator().GetIndexOperand(1);
VectorSlotPair pair = CreateVectorSlotPair(slot_id);
int literal_flags = bytecode_iterator().GetFlagOperand(2);
Node* literal = NewNode(
javascript()->CreateLiteralRegExp(constant_pattern, pair, literal_flags));
environment()->BindAccumulator(literal, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCreateArrayLiteral() {
Handle<ArrayBoilerplateDescription> array_boilerplate_description(
ArrayBoilerplateDescription::cast(
bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
int const slot_id = bytecode_iterator().GetIndexOperand(1);
VectorSlotPair pair = CreateVectorSlotPair(slot_id);
int bytecode_flags = bytecode_iterator().GetFlagOperand(2);
int literal_flags =
interpreter::CreateArrayLiteralFlags::FlagsBits::decode(bytecode_flags);
// Disable allocation site mementos. Only unoptimized code will collect
// feedback about allocation site. Once the code is optimized we expect the
// data to converge. So, we disable allocation site mementos in optimized
// code. We can revisit this when we have data to the contrary.
literal_flags |= ArrayLiteral::kDisableMementos;
// TODO(mstarzinger): Thread through number of elements. The below number is
// only an estimate and does not match {ArrayLiteral::values::length}.
int number_of_elements =
array_boilerplate_description->constant_elements()->length();
Node* literal = NewNode(javascript()->CreateLiteralArray(
array_boilerplate_description, pair, literal_flags, number_of_elements));
environment()->BindAccumulator(literal, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCreateEmptyArrayLiteral() {
int const slot_id = bytecode_iterator().GetIndexOperand(0);
VectorSlotPair pair = CreateVectorSlotPair(slot_id);
Node* literal = NewNode(javascript()->CreateEmptyLiteralArray(pair));
environment()->BindAccumulator(literal);
}
void BytecodeGraphBuilder::VisitCreateArrayFromIterable() {
Node* iterable = NewNode(javascript()->CreateArrayFromIterable(),
environment()->LookupAccumulator());
environment()->BindAccumulator(iterable, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCreateObjectLiteral() {
Handle<ObjectBoilerplateDescription> constant_properties(
ObjectBoilerplateDescription::cast(
bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
int const slot_id = bytecode_iterator().GetIndexOperand(1);
VectorSlotPair pair = CreateVectorSlotPair(slot_id);
int bytecode_flags = bytecode_iterator().GetFlagOperand(2);
int literal_flags =
interpreter::CreateObjectLiteralFlags::FlagsBits::decode(bytecode_flags);
// TODO(mstarzinger): Thread through number of properties. The below number is
// only an estimate and does not match {ObjectLiteral::properties_count}.
int number_of_properties = constant_properties->size();
Node* literal = NewNode(javascript()->CreateLiteralObject(
constant_properties, pair, literal_flags, number_of_properties));
environment()->BindAccumulator(literal, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCreateEmptyObjectLiteral() {
Node* literal =
NewNode(javascript()->CreateEmptyLiteralObject(), GetFunctionClosure());
environment()->BindAccumulator(literal);
}
void BytecodeGraphBuilder::VisitCloneObject() {
PrepareEagerCheckpoint();
Node* source =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
int flags = bytecode_iterator().GetFlagOperand(1);
int slot = bytecode_iterator().GetIndexOperand(2);
const Operator* op =
javascript()->CloneObject(CreateVectorSlotPair(slot), flags);
Node* value = NewNode(op, source);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitGetTemplateObject() {
Handle<TemplateObjectDescription> description(
TemplateObjectDescription::cast(
bytecode_iterator().GetConstantForIndexOperand(0)),
isolate());
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
FeedbackNexus nexus(feedback_vector(), slot);
Handle<JSArray> cached_value;
if (nexus.GetFeedback() == MaybeObject::FromSmi(Smi::zero())) {
// It's not observable when the template object is created, so we
// can just create it eagerly during graph building and bake in
// the JSArray constant here.
cached_value = TemplateObjectDescription::GetTemplateObject(
isolate(), native_context(), description, shared_info(), slot.ToInt());
nexus.vector()->Set(slot, *cached_value);
} else {
cached_value =
handle(JSArray::cast(nexus.GetFeedback()->GetHeapObjectAssumeStrong()),
isolate());
}
Node* template_object = jsgraph()->HeapConstant(cached_value);
environment()->BindAccumulator(template_object);
}
Node* const* BytecodeGraphBuilder::GetCallArgumentsFromRegisters(
Node* callee, Node* receiver, interpreter::Register first_arg,
int arg_count) {
// The arity of the Call node -- includes the callee, receiver and function
// arguments.
int arity = 2 + arg_count;
Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));
all[0] = callee;
all[1] = receiver;
// The function arguments are in consecutive registers.
int arg_base = first_arg.index();
for (int i = 0; i < arg_count; ++i) {
all[2 + i] =
environment()->LookupRegister(interpreter::Register(arg_base + i));
}
return all;
}
Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
Node* const* args,
int arg_count) {
return MakeNode(call_op, arg_count, args, false);
}
Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
Node* callee,
interpreter::Register receiver,
size_t reg_count) {
Node* receiver_node = environment()->LookupRegister(receiver);
// The receiver is followed by the arguments in the consecutive registers.
DCHECK_GE(reg_count, 1);
interpreter::Register first_arg = interpreter::Register(receiver.index() + 1);
int arg_count = static_cast<int>(reg_count) - 1;
Node* const* call_args = GetCallArgumentsFromRegisters(callee, receiver_node,
first_arg, arg_count);
return ProcessCallArguments(call_op, call_args, 2 + arg_count);
}
void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
Node* const* args, size_t arg_count,
int slot_id) {
DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
bytecode_iterator().current_bytecode()),
receiver_mode);
PrepareEagerCheckpoint();
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op =
javascript()->Call(arg_count, frequency, feedback, receiver_mode,
GetSpeculationMode(slot_id));
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = ProcessCallArguments(op, args, static_cast<int>(arg_count));
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
Node* const* BytecodeGraphBuilder::ProcessCallVarArgs(
ConvertReceiverMode receiver_mode, Node* callee,
interpreter::Register first_reg, int arg_count) {
DCHECK_GE(arg_count, 0);
Node* receiver_node;
interpreter::Register first_arg;
if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
// The receiver is implicit (and undefined), the arguments are in
// consecutive registers.
receiver_node = jsgraph()->UndefinedConstant();
first_arg = first_reg;
} else {
// The receiver is the first register, followed by the arguments in the
// consecutive registers.
receiver_node = environment()->LookupRegister(first_reg);
first_arg = interpreter::Register(first_reg.index() + 1);
}
Node* const* call_args = GetCallArgumentsFromRegisters(callee, receiver_node,
first_arg, arg_count);
return call_args;
}
void BytecodeGraphBuilder::BuildCallVarArgs(ConvertReceiverMode receiver_mode) {
DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
bytecode_iterator().current_bytecode()),
receiver_mode);
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
int const slot_id = bytecode_iterator().GetIndexOperand(3);
int arg_count = receiver_mode == ConvertReceiverMode::kNullOrUndefined
? static_cast<int>(reg_count)
: static_cast<int>(reg_count) - 1;
Node* const* call_args =
ProcessCallVarArgs(receiver_mode, callee, first_reg, arg_count);
BuildCall(receiver_mode, call_args, static_cast<size_t>(2 + arg_count),
slot_id);
}
void BytecodeGraphBuilder::VisitCallAnyReceiver() {
BuildCallVarArgs(ConvertReceiverMode::kAny);
}
void BytecodeGraphBuilder::VisitCallNoFeedback() {
DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
bytecode_iterator().current_bytecode()),
ConvertReceiverMode::kAny);
PrepareEagerCheckpoint();
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
// The receiver is the first register, followed by the arguments in the
// consecutive registers.
int arg_count = static_cast<int>(reg_count) - 1;
// The arity of the Call node -- includes the callee, receiver and function
// arguments.
int arity = 2 + arg_count;
// Setting call frequency to a value less than min_inlining frequency to
// prevent inlining of one-shot call node.
DCHECK(CallFrequency::kNoFeedbackCallFrequency < FLAG_min_inlining_frequency);
const Operator* call = javascript()->Call(
arity, CallFrequency(CallFrequency::kNoFeedbackCallFrequency));
Node* const* call_args = ProcessCallVarArgs(ConvertReceiverMode::kAny, callee,
first_reg, arg_count);
Node* value = ProcessCallArguments(call, call_args, arity);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCallProperty() {
BuildCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined);
}
void BytecodeGraphBuilder::VisitCallProperty0() {
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* receiver =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
int const slot_id = bytecode_iterator().GetIndexOperand(2);
BuildCall(ConvertReceiverMode::kNotNullOrUndefined, {callee, receiver},
slot_id);
}
void BytecodeGraphBuilder::VisitCallProperty1() {
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* receiver =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
Node* arg0 =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
int const slot_id = bytecode_iterator().GetIndexOperand(3);
BuildCall(ConvertReceiverMode::kNotNullOrUndefined, {callee, receiver, arg0},
slot_id);
}
void BytecodeGraphBuilder::VisitCallProperty2() {
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* receiver =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
Node* arg0 =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
Node* arg1 =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(3));
int const slot_id = bytecode_iterator().GetIndexOperand(4);
BuildCall(ConvertReceiverMode::kNotNullOrUndefined,
{callee, receiver, arg0, arg1}, slot_id);
}
void BytecodeGraphBuilder::VisitCallUndefinedReceiver() {
BuildCallVarArgs(ConvertReceiverMode::kNullOrUndefined);
}
void BytecodeGraphBuilder::VisitCallUndefinedReceiver0() {
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* receiver = jsgraph()->UndefinedConstant();
int const slot_id = bytecode_iterator().GetIndexOperand(1);
BuildCall(ConvertReceiverMode::kNullOrUndefined, {callee, receiver}, slot_id);
}
void BytecodeGraphBuilder::VisitCallUndefinedReceiver1() {
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* receiver = jsgraph()->UndefinedConstant();
Node* arg0 =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
int const slot_id = bytecode_iterator().GetIndexOperand(2);
BuildCall(ConvertReceiverMode::kNullOrUndefined, {callee, receiver, arg0},
slot_id);
}
void BytecodeGraphBuilder::VisitCallUndefinedReceiver2() {
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* receiver = jsgraph()->UndefinedConstant();
Node* arg0 =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
Node* arg1 =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
int const slot_id = bytecode_iterator().GetIndexOperand(3);
BuildCall(ConvertReceiverMode::kNullOrUndefined,
{callee, receiver, arg0, arg1}, slot_id);
}
void BytecodeGraphBuilder::VisitCallWithSpread() {
PrepareEagerCheckpoint();
Node* callee =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
Node* receiver_node = environment()->LookupRegister(receiver);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
interpreter::Register first_arg = interpreter::Register(receiver.index() + 1);
int arg_count = static_cast<int>(reg_count) - 1;
Node* const* args = GetCallArgumentsFromRegisters(callee, receiver_node,
first_arg, arg_count);
int const slot_id = bytecode_iterator().GetIndexOperand(3);
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op = javascript()->CallWithSpread(
static_cast<int>(reg_count + 1), frequency, feedback);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = ProcessCallArguments(op, args, 2 + arg_count);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitCallJSRuntime() {
PrepareEagerCheckpoint();
Node* callee = BuildLoadNativeContextField(
bytecode_iterator().GetNativeContextIndexOperand(0));
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
int arg_count = static_cast<int>(reg_count);
const Operator* call = javascript()->Call(2 + arg_count);
Node* const* call_args = ProcessCallVarArgs(
ConvertReceiverMode::kNullOrUndefined, callee, first_reg, arg_count);
Node* value = ProcessCallArguments(call, call_args, 2 + arg_count);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
Node* BytecodeGraphBuilder::ProcessCallRuntimeArguments(
const Operator* call_runtime_op, interpreter::Register receiver,
size_t reg_count) {
int arg_count = static_cast<int>(reg_count);
// arity is args.
int arity = arg_count;
Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));
int first_arg_index = receiver.index();
for (int i = 0; i < static_cast<int>(reg_count); ++i) {
all[i] = environment()->LookupRegister(
interpreter::Register(first_arg_index + i));
}
Node* value = MakeNode(call_runtime_op, arity, all, false);
return value;
}
void BytecodeGraphBuilder::VisitCallRuntime() {
PrepareEagerCheckpoint();
Runtime::FunctionId function_id = bytecode_iterator().GetRuntimeIdOperand(0);
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
// Create node to perform the runtime call.
const Operator* call = javascript()->CallRuntime(function_id, reg_count);
Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
// Connect to the end if {function_id} is non-returning.
if (Runtime::IsNonReturning(function_id)) {
// TODO(7099): Investigate if we need LoopExit node here.
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
}
}
void BytecodeGraphBuilder::VisitCallRuntimeForPair() {
PrepareEagerCheckpoint();
Runtime::FunctionId functionId = bytecode_iterator().GetRuntimeIdOperand(0);
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
interpreter::Register first_return =
bytecode_iterator().GetRegisterOperand(3);
// Create node to perform the runtime call.
const Operator* call = javascript()->CallRuntime(functionId, reg_count);
Node* return_pair = ProcessCallRuntimeArguments(call, receiver, reg_count);
environment()->BindRegistersToProjections(first_return, return_pair,
Environment::kAttachFrameState);
}
Node* const* BytecodeGraphBuilder::GetConstructArgumentsFromRegister(
Node* target, Node* new_target, interpreter::Register first_arg,
int arg_count) {
// arity is args + callee and new target.
int arity = arg_count + 2;
Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));
all[0] = target;
int first_arg_index = first_arg.index();
for (int i = 0; i < arg_count; ++i) {
all[1 + i] = environment()->LookupRegister(
interpreter::Register(first_arg_index + i));
}
all[arity - 1] = new_target;
return all;
}
Node* BytecodeGraphBuilder::ProcessConstructArguments(const Operator* op,
Node* const* args,
int arg_count) {
return MakeNode(op, arg_count, args, false);
}
void BytecodeGraphBuilder::VisitConstruct() {
PrepareEagerCheckpoint();
interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
int const slot_id = bytecode_iterator().GetIndexOperand(3);
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
Node* new_target = environment()->LookupAccumulator();
Node* callee = environment()->LookupRegister(callee_reg);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op = javascript()->Construct(
static_cast<uint32_t>(reg_count + 2), frequency, feedback);
int arg_count = static_cast<int>(reg_count);
Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
first_reg, arg_count);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedConstruct(
op, args, static_cast<int>(arg_count), feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = ProcessConstructArguments(op, args, 2 + arg_count);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitConstructWithSpread() {
PrepareEagerCheckpoint();
interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
int const slot_id = bytecode_iterator().GetIndexOperand(3);
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
Node* new_target = environment()->LookupAccumulator();
Node* callee = environment()->LookupRegister(callee_reg);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op = javascript()->ConstructWithSpread(
static_cast<uint32_t>(reg_count + 2), frequency, feedback);
int arg_count = static_cast<int>(reg_count);
Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
first_reg, arg_count);
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedConstruct(
op, args, static_cast<int>(arg_count), feedback.slot());
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = ProcessConstructArguments(op, args, 2 + arg_count);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitInvokeIntrinsic() {
PrepareEagerCheckpoint();
Runtime::FunctionId functionId = bytecode_iterator().GetIntrinsicIdOperand(0);
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
// Create node to perform the runtime call. Turbofan will take care of the
// lowering.
const Operator* call = javascript()->CallRuntime(functionId, reg_count);
Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitThrow() {
BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset()));
Node* value = environment()->LookupAccumulator();
Node* call = NewNode(javascript()->CallRuntime(Runtime::kThrow), value);
environment()->BindAccumulator(call, Environment::kAttachFrameState);
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
}
void BytecodeGraphBuilder::VisitAbort() {
BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset()));
AbortReason reason =
static_cast<AbortReason>(bytecode_iterator().GetIndexOperand(0));
NewNode(simplified()->RuntimeAbort(reason));
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
}
void BytecodeGraphBuilder::VisitReThrow() {
BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset()));
Node* value = environment()->LookupAccumulator();
NewNode(javascript()->CallRuntime(Runtime::kReThrow), value);
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
}
void BytecodeGraphBuilder::BuildHoleCheckAndThrow(
Node* condition, Runtime::FunctionId runtime_id, Node* name) {
Node* accumulator = environment()->LookupAccumulator();
NewBranch(condition, BranchHint::kFalse);
{
SubEnvironment sub_environment(this);
NewIfTrue();
BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset()));
Node* node;
const Operator* op = javascript()->CallRuntime(runtime_id);
if (runtime_id == Runtime::kThrowAccessedUninitializedVariable) {
DCHECK_NOT_NULL(name);
node = NewNode(op, name);
} else {
DCHECK(runtime_id == Runtime::kThrowSuperAlreadyCalledError ||
runtime_id == Runtime::kThrowSuperNotCalled);
node = NewNode(op);
}
environment()->RecordAfterState(node, Environment::kAttachFrameState);
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
}
NewIfFalse();
environment()->BindAccumulator(accumulator);
}
void BytecodeGraphBuilder::VisitThrowReferenceErrorIfHole() {
Node* accumulator = environment()->LookupAccumulator();
Node* check_for_hole = NewNode(simplified()->ReferenceEqual(), accumulator,
jsgraph()->TheHoleConstant());
Node* name = jsgraph()->Constant(
handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
BuildHoleCheckAndThrow(check_for_hole,
Runtime::kThrowAccessedUninitializedVariable, name);
}
void BytecodeGraphBuilder::VisitThrowSuperNotCalledIfHole() {
Node* accumulator = environment()->LookupAccumulator();
Node* check_for_hole = NewNode(simplified()->ReferenceEqual(), accumulator,
jsgraph()->TheHoleConstant());
BuildHoleCheckAndThrow(check_for_hole, Runtime::kThrowSuperNotCalled);
}
void BytecodeGraphBuilder::VisitThrowSuperAlreadyCalledIfNotHole() {
Node* accumulator = environment()->LookupAccumulator();
Node* check_for_hole = NewNode(simplified()->ReferenceEqual(), accumulator,
jsgraph()->TheHoleConstant());
Node* check_for_not_hole =
NewNode(simplified()->BooleanNot(), check_for_hole);
BuildHoleCheckAndThrow(check_for_not_hole,
Runtime::kThrowSuperAlreadyCalledError);
}
void BytecodeGraphBuilder::BuildUnaryOp(const Operator* op) {
PrepareEagerCheckpoint();
Node* operand = environment()->LookupAccumulator();
FeedbackSlot slot =
bytecode_iterator().GetSlotOperand(kUnaryOperationHintIndex);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedUnaryOp(op, operand, slot);
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, operand);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op) {
PrepareEagerCheckpoint();
Node* left =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* right = environment()->LookupAccumulator();
FeedbackSlot slot =
bytecode_iterator().GetSlotOperand(kBinaryOperationHintIndex);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedBinaryOp(op, left, right, slot);
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, left, right);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
// Helper function to create binary operation hint from the recorded type
// feedback.
BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint(
int operand_index) {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
FeedbackNexus nexus(feedback_vector(), slot);
return nexus.GetBinaryOperationFeedback();
}
// Helper function to create compare operation hint from the recorded type
// feedback.
CompareOperationHint BytecodeGraphBuilder::GetCompareOperationHint() {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
FeedbackNexus nexus(feedback_vector(), slot);
return nexus.GetCompareOperationFeedback();
}
// Helper function to create for-in mode from the recorded type feedback.
ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
FeedbackNexus nexus(feedback_vector(), slot);
switch (nexus.GetForInFeedback()) {
case ForInHint::kNone:
case ForInHint::kEnumCacheKeysAndIndices:
return ForInMode::kUseEnumCacheKeysAndIndices;
case ForInHint::kEnumCacheKeys:
return ForInMode::kUseEnumCacheKeys;
case ForInHint::kAny:
return ForInMode::kGeneric;
}
UNREACHABLE();
}
CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
if (invocation_frequency_.IsUnknown()) return CallFrequency();
FeedbackNexus nexus(feedback_vector(), FeedbackVector::ToSlot(slot_id));
float feedback_frequency = nexus.ComputeCallFrequency();
if (feedback_frequency == 0.0f) {
// This is to prevent multiplying zero and infinity.
return CallFrequency(0.0f);
} else {
return CallFrequency(feedback_frequency * invocation_frequency_.value());
}
}
SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const {
FeedbackNexus nexus(feedback_vector(), FeedbackVector::ToSlot(slot_id));
return nexus.GetSpeculationMode();
}
void BytecodeGraphBuilder::VisitBitwiseNot() {
BuildUnaryOp(javascript()->BitwiseNot());
}
void BytecodeGraphBuilder::VisitDec() {
BuildUnaryOp(javascript()->Decrement());
}
void BytecodeGraphBuilder::VisitInc() {
BuildUnaryOp(javascript()->Increment());
}
void BytecodeGraphBuilder::VisitNegate() {
BuildUnaryOp(javascript()->Negate());
}
void BytecodeGraphBuilder::VisitAdd() {
BuildBinaryOp(
javascript()->Add(GetBinaryOperationHint(kBinaryOperationHintIndex)));
}
void BytecodeGraphBuilder::VisitSub() {
BuildBinaryOp(javascript()->Subtract());
}
void BytecodeGraphBuilder::VisitMul() {
BuildBinaryOp(javascript()->Multiply());
}
void BytecodeGraphBuilder::VisitDiv() { BuildBinaryOp(javascript()->Divide()); }
void BytecodeGraphBuilder::VisitMod() {
BuildBinaryOp(javascript()->Modulus());
}
void BytecodeGraphBuilder::VisitExp() {
BuildBinaryOp(javascript()->Exponentiate());
}
void BytecodeGraphBuilder::VisitBitwiseOr() {
BuildBinaryOp(javascript()->BitwiseOr());
}
void BytecodeGraphBuilder::VisitBitwiseXor() {
BuildBinaryOp(javascript()->BitwiseXor());
}
void BytecodeGraphBuilder::VisitBitwiseAnd() {
BuildBinaryOp(javascript()->BitwiseAnd());
}
void BytecodeGraphBuilder::VisitShiftLeft() {
BuildBinaryOp(javascript()->ShiftLeft());
}
void BytecodeGraphBuilder::VisitShiftRight() {
BuildBinaryOp(javascript()->ShiftRight());
}
void BytecodeGraphBuilder::VisitShiftRightLogical() {
BuildBinaryOp(javascript()->ShiftRightLogical());
}
void BytecodeGraphBuilder::BuildBinaryOpWithImmediate(const Operator* op) {
PrepareEagerCheckpoint();
Node* left = environment()->LookupAccumulator();
Node* right = jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));
FeedbackSlot slot =
bytecode_iterator().GetSlotOperand(kBinaryOperationSmiHintIndex);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedBinaryOp(op, left, right, slot);
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, left, right);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitAddSmi() {
BuildBinaryOpWithImmediate(
javascript()->Add(GetBinaryOperationHint(kBinaryOperationSmiHintIndex)));
}
void BytecodeGraphBuilder::VisitSubSmi() {
BuildBinaryOpWithImmediate(javascript()->Subtract());
}
void BytecodeGraphBuilder::VisitMulSmi() {
BuildBinaryOpWithImmediate(javascript()->Multiply());
}
void BytecodeGraphBuilder::VisitDivSmi() {
BuildBinaryOpWithImmediate(javascript()->Divide());
}
void BytecodeGraphBuilder::VisitModSmi() {
BuildBinaryOpWithImmediate(javascript()->Modulus());
}
void BytecodeGraphBuilder::VisitExpSmi() {
BuildBinaryOpWithImmediate(javascript()->Exponentiate());
}
void BytecodeGraphBuilder::VisitBitwiseOrSmi() {
BuildBinaryOpWithImmediate(javascript()->BitwiseOr());
}
void BytecodeGraphBuilder::VisitBitwiseXorSmi() {
BuildBinaryOpWithImmediate(javascript()->BitwiseXor());
}
void BytecodeGraphBuilder::VisitBitwiseAndSmi() {
BuildBinaryOpWithImmediate(javascript()->BitwiseAnd());
}
void BytecodeGraphBuilder::VisitShiftLeftSmi() {
BuildBinaryOpWithImmediate(javascript()->ShiftLeft());
}
void BytecodeGraphBuilder::VisitShiftRightSmi() {
BuildBinaryOpWithImmediate(javascript()->ShiftRight());
}
void BytecodeGraphBuilder::VisitShiftRightLogicalSmi() {
BuildBinaryOpWithImmediate(javascript()->ShiftRightLogical());
}
void BytecodeGraphBuilder::VisitLogicalNot() {
Node* value = environment()->LookupAccumulator();
Node* node = NewNode(simplified()->BooleanNot(), value);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitToBooleanLogicalNot() {
Node* value =
NewNode(simplified()->ToBoolean(), environment()->LookupAccumulator());
Node* node = NewNode(simplified()->BooleanNot(), value);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitTypeOf() {
Node* node =
NewNode(simplified()->TypeOf(), environment()->LookupAccumulator());
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::BuildDelete(LanguageMode language_mode) {
PrepareEagerCheckpoint();
Node* key = environment()->LookupAccumulator();
Node* object =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* mode = jsgraph()->Constant(static_cast<int32_t>(language_mode));
Node* node = NewNode(javascript()->DeleteProperty(), object, key, mode);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitDeletePropertyStrict() {
BuildDelete(LanguageMode::kStrict);
}
void BytecodeGraphBuilder::VisitDeletePropertySloppy() {
BuildDelete(LanguageMode::kSloppy);
}
void BytecodeGraphBuilder::VisitGetSuperConstructor() {
Node* node = NewNode(javascript()->GetSuperConstructor(),
environment()->LookupAccumulator());
environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), node,
Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::BuildCompareOp(const Operator* op) {
PrepareEagerCheckpoint();
Node* left =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* right = environment()->LookupAccumulator();
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedBinaryOp(op, left, right, slot);
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node =