| // 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/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(¶meters_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<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_( | |
| handle(shared_info->GetBytecodeArray(), jsgraph->isolate())), | |
| 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), | |
| 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) { | |
| return VectorSlotPair(feedback_vector(), FeedbackVector::ToSlot(slot_id)); | |
| } | |
| 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::IndexAndPosition source_iterator_state_; | |
| IteratorsStates( | |
| int exception_handler_index, | |
| SourcePositionTableIterator::IndexAndPosition 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) { | |
| 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_CODE | |
| } | |
| } | |
| } | |
| 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); | |
| } | |
| for (; !iterator.done(); iterator.Advance()) { | |
| VisitSingleBytecode(&source_position_iterator); | |
| } | |
| 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::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::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()); | |
| FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1); | |
| FeedbackNexus nexus(feedback_vector(), slot); | |
| PretenureFlag tenured = | |
| interpreter::CreateClosureFlags::PretenuredBit::decode( | |
| bytecode_iterator().GetFlagOperand(2)) | |
| ? TENURED | |
| : NOT_TENURED; | |
| const Operator* op = javascript()->CreateClosure( | |
| shared_info, nexus.GetFeedbackCell(), | |
| handle(jsgraph()->isolate()->builtins()->builtin(Builtins::kCompileLazy), | |
| isolate()), | |
| tenured); | |
| 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::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()->BindRegister(bytecode_iterator().GetRegisterOperand(3), | |
| 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::kZero)) { | |
| // 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::CreateTemplateObject(isolate(), description); | |
| nexus.vector()->Set(slot, *cached_value); | |
| } else { | |
| cached_value = handle( | |
| JSArray::cast(nexus.GetFeedback()->ToStrongHeapObject()), 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::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::kThrowReferenceError) { | |
| 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::kThrowReferenceError, 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)); | |
| return CallFrequency(nexus.ComputeCallFrequency() * | |
| 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 = NewNode(op, left, right); | |
| } | |
| environment()->BindAccumulator(node, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitTestEqual() { | |
| BuildCompareOp(javascript()->Equal(GetCompareOperationHint())); | |
| } | |
| void BytecodeGraphBuilder::VisitTestEqualStrict() { | |
| BuildCompareOp(javascript()->StrictEqual(GetCompareOperationHint())); | |
| } | |
| void BytecodeGraphBuilder::VisitTestLessThan() { | |
| BuildCompareOp(javascript()->LessThan(GetCompareOperationHint())); | |
| } | |
| void BytecodeGraphBuilder::VisitTestGreaterThan() { | |
| BuildCompareOp(javascript()->GreaterThan(GetCompareOperationHint())); | |
| } | |
| void BytecodeGraphBuilder::VisitTestLessThanOrEqual() { | |
| BuildCompareOp(javascript()->LessThanOrEqual(GetCompareOperationHint())); | |
| } | |
| void BytecodeGraphBuilder::VisitTestGreaterThanOrEqual() { | |
| BuildCompareOp(javascript()->GreaterThanOrEqual(GetCompareOperationHint())); | |
| } | |
| void BytecodeGraphBuilder::VisitTestReferenceEqual() { | |
| Node* left = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| Node* right = environment()->LookupAccumulator(); | |
| Node* result = NewNode(simplified()->ReferenceEqual(), left, right); | |
| environment()->BindAccumulator(result); | |
| } | |
| void BytecodeGraphBuilder::VisitTestIn() { | |
| PrepareEagerCheckpoint(); | |
| Node* object = environment()->LookupAccumulator(); | |
| Node* key = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| Node* node = NewNode(javascript()->HasProperty(), object, key); | |
| environment()->BindAccumulator(node, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitTestInstanceOf() { | |
| int const slot_index = bytecode_iterator().GetIndexOperand(1); | |
| BuildCompareOp(javascript()->InstanceOf(CreateVectorSlotPair(slot_index))); | |
| } | |
| void BytecodeGraphBuilder::VisitTestUndetectable() { | |
| Node* object = environment()->LookupAccumulator(); | |
| Node* node = NewNode(jsgraph()->simplified()->ObjectIsUndetectable(), object); | |
| environment()->BindAccumulator(node); | |
| } | |
| void BytecodeGraphBuilder::VisitTestNull() { | |
| Node* object = environment()->LookupAccumulator(); | |
| Node* result = NewNode(simplified()->ReferenceEqual(), object, | |
| jsgraph()->NullConstant()); | |
| environment()->BindAccumulator(result); | |
| } | |
| void BytecodeGraphBuilder::VisitTestUndefined() { | |
| Node* object = environment()->LookupAccumulator(); | |
| Node* result = NewNode(simplified()->ReferenceEqual(), object, | |
| jsgraph()->UndefinedConstant()); | |
| environment()->BindAccumulator(result); | |
| } | |
| void BytecodeGraphBuilder::VisitTestTypeOf() { | |
| Node* object = environment()->LookupAccumulator(); | |
| auto literal_flag = interpreter::TestTypeOfFlags::Decode( | |
| bytecode_iterator().GetFlagOperand(0)); | |
| Node* result; | |
| switch (literal_flag) { | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kNumber: | |
| result = NewNode(simplified()->ObjectIsNumber(), object); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kString: | |
| result = NewNode(simplified()->ObjectIsString(), object); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kSymbol: | |
| result = NewNode(simplified()->ObjectIsSymbol(), object); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kBigInt: | |
| result = NewNode(simplified()->ObjectIsBigInt(), object); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kBoolean: | |
| result = NewNode(common()->Select(MachineRepresentation::kTagged), | |
| NewNode(simplified()->ReferenceEqual(), object, | |
| jsgraph()->TrueConstant()), | |
| jsgraph()->TrueConstant(), | |
| NewNode(simplified()->ReferenceEqual(), object, | |
| jsgraph()->FalseConstant())); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kUndefined: | |
| result = graph()->NewNode( | |
| common()->Select(MachineRepresentation::kTagged), | |
| graph()->NewNode(simplified()->ReferenceEqual(), object, | |
| jsgraph()->NullConstant()), | |
| jsgraph()->FalseConstant(), | |
| graph()->NewNode(simplified()->ObjectIsUndetectable(), object)); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kFunction: | |
| result = | |
| graph()->NewNode(simplified()->ObjectIsDetectableCallable(), object); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kObject: | |
| result = graph()->NewNode( | |
| common()->Select(MachineRepresentation::kTagged), | |
| graph()->NewNode(simplified()->ObjectIsNonCallable(), object), | |
| jsgraph()->TrueConstant(), | |
| graph()->NewNode(simplified()->ReferenceEqual(), object, | |
| jsgraph()->NullConstant())); | |
| break; | |
| case interpreter::TestTypeOfFlags::LiteralFlag::kOther: | |
| UNREACHABLE(); // Should never be emitted. | |
| break; | |
| } | |
| environment()->BindAccumulator(result); | |
| } | |
| void BytecodeGraphBuilder::BuildCastOperator(const Operator* js_op) { | |
| Node* value = NewNode(js_op, environment()->LookupAccumulator()); | |
| environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), value, | |
| Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitToName() { | |
| BuildCastOperator(javascript()->ToName()); | |
| } | |
| void BytecodeGraphBuilder::VisitToObject() { | |
| BuildCastOperator(javascript()->ToObject()); | |
| } | |
| void BytecodeGraphBuilder::VisitToString() { | |
| Node* value = | |
| NewNode(javascript()->ToString(), environment()->LookupAccumulator()); | |
| environment()->BindAccumulator(value, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitToNumber() { | |
| PrepareEagerCheckpoint(); | |
| Node* object = environment()->LookupAccumulator(); | |
| FeedbackSlot slot = bytecode_iterator().GetSlotOperand(0); | |
| JSTypeHintLowering::LoweringResult lowering = | |
| TryBuildSimplifiedToNumber(object, slot); | |
| Node* node = nullptr; | |
| if (lowering.IsSideEffectFree()) { | |
| node = lowering.value(); | |
| } else { | |
| DCHECK(!lowering.Changed()); | |
| node = NewNode(javascript()->ToNumber(), object); | |
| } | |
| environment()->BindAccumulator(node, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitToNumeric() { | |
| PrepareEagerCheckpoint(); | |
| Node* object = environment()->LookupAccumulator(); | |
| // If we have some kind of Number feedback, we do the same lowering as for | |
| // ToNumber. | |
| FeedbackSlot slot = bytecode_iterator().GetSlotOperand(0); | |
| JSTypeHintLowering::LoweringResult lowering = | |
| TryBuildSimplifiedToNumber(object, slot); | |
| Node* node = nullptr; | |
| if (lowering.IsSideEffectFree()) { | |
| node = lowering.value(); | |
| } else { | |
| DCHECK(!lowering.Changed()); | |
| node = NewNode(javascript()->ToNumeric(), object); | |
| } | |
| environment()->BindAccumulator(node, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitJump() { BuildJump(); } | |
| void BytecodeGraphBuilder::VisitJumpConstant() { BuildJump(); } | |
| void BytecodeGraphBuilder::VisitJumpIfTrue() { BuildJumpIfTrue(); } | |
| void BytecodeGraphBuilder::VisitJumpIfTrueConstant() { BuildJumpIfTrue(); } | |
| void BytecodeGraphBuilder::VisitJumpIfFalse() { BuildJumpIfFalse(); } | |
| void BytecodeGraphBuilder::VisitJumpIfFalseConstant() { BuildJumpIfFalse(); } | |
| void BytecodeGraphBuilder::VisitJumpIfToBooleanTrue() { | |
| BuildJumpIfToBooleanTrue(); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfToBooleanTrueConstant() { | |
| BuildJumpIfToBooleanTrue(); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfToBooleanFalse() { | |
| BuildJumpIfToBooleanFalse(); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstant() { | |
| BuildJumpIfToBooleanFalse(); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfJSReceiver() { BuildJumpIfJSReceiver(); } | |
| void BytecodeGraphBuilder::VisitJumpIfJSReceiverConstant() { | |
| BuildJumpIfJSReceiver(); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfNull() { | |
| BuildJumpIfEqual(jsgraph()->NullConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfNullConstant() { | |
| BuildJumpIfEqual(jsgraph()->NullConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfNotNull() { | |
| BuildJumpIfNotEqual(jsgraph()->NullConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfNotNullConstant() { | |
| BuildJumpIfNotEqual(jsgraph()->NullConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfUndefined() { | |
| BuildJumpIfEqual(jsgraph()->UndefinedConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfUndefinedConstant() { | |
| BuildJumpIfEqual(jsgraph()->UndefinedConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfNotUndefined() { | |
| BuildJumpIfNotEqual(jsgraph()->UndefinedConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpIfNotUndefinedConstant() { | |
| BuildJumpIfNotEqual(jsgraph()->UndefinedConstant()); | |
| } | |
| void BytecodeGraphBuilder::VisitJumpLoop() { BuildJump(); } | |
| void BytecodeGraphBuilder::BuildSwitchOnSmi(Node* condition) { | |
| interpreter::JumpTableTargetOffsets offsets = | |
| bytecode_iterator().GetJumpTableTargetOffsets(); | |
| NewSwitch(condition, offsets.size() + 1); | |
| for (const auto& entry : offsets) { | |
| SubEnvironment sub_environment(this); | |
| NewIfValue(entry.case_value); | |
| MergeIntoSuccessorEnvironment(entry.target_offset); | |
| } | |
| NewIfDefault(); | |
| } | |
| void BytecodeGraphBuilder::VisitSwitchOnSmiNoFeedback() { | |
| PrepareEagerCheckpoint(); | |
| Node* acc = environment()->LookupAccumulator(); | |
| Node* acc_smi = NewNode(simplified()->CheckSmi(VectorSlotPair()), acc); | |
| BuildSwitchOnSmi(acc_smi); | |
| } | |
| void BytecodeGraphBuilder::VisitStackCheck() { | |
| PrepareEagerCheckpoint(); | |
| Node* node = NewNode(javascript()->StackCheck()); | |
| environment()->RecordAfterState(node, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitSetPendingMessage() { | |
| Node* previous_message = NewNode(javascript()->LoadMessage()); | |
| NewNode(javascript()->StoreMessage(), environment()->LookupAccumulator()); | |
| environment()->BindAccumulator(previous_message); | |
| } | |
| void BytecodeGraphBuilder::BuildReturn(const BytecodeLivenessState* liveness) { | |
| BuildLoopExitsForFunctionExit(liveness); | |
| Node* pop_node = jsgraph()->ZeroConstant(); | |
| Node* control = | |
| NewNode(common()->Return(), pop_node, environment()->LookupAccumulator()); | |
| MergeControlToLeaveFunction(control); | |
| } | |
| void BytecodeGraphBuilder::VisitReturn() { | |
| BuildReturn(bytecode_analysis()->GetInLivenessFor( | |
| bytecode_iterator().current_offset())); | |
| } | |
| void BytecodeGraphBuilder::VisitDebugger() { | |
| PrepareEagerCheckpoint(); | |
| Node* call = NewNode(javascript()->Debugger()); | |
| environment()->RecordAfterState(call, Environment::kAttachFrameState); | |
| } | |
| // We cannot create a graph from the debugger copy of the bytecode array. | |
| #define DEBUG_BREAK(Name, ...) \ | |
| void BytecodeGraphBuilder::Visit##Name() { UNREACHABLE(); } | |
| DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK); | |
| #undef DEBUG_BREAK | |
| void BytecodeGraphBuilder::VisitIncBlockCounter() { | |
| Node* closure = GetFunctionClosure(); | |
| Node* coverage_array_slot = | |
| jsgraph()->Constant(bytecode_iterator().GetIndexOperand(0)); | |
| const Operator* op = javascript()->CallRuntime(Runtime::kIncBlockCounter); | |
| NewNode(op, closure, coverage_array_slot); | |
| } | |
| void BytecodeGraphBuilder::VisitForInEnumerate() { | |
| Node* receiver = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| Node* enumerator = NewNode(javascript()->ForInEnumerate(), receiver); | |
| environment()->BindAccumulator(enumerator, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitForInPrepare() { | |
| PrepareEagerCheckpoint(); | |
| Node* enumerator = environment()->LookupAccumulator(); | |
| FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1); | |
| JSTypeHintLowering::LoweringResult lowering = | |
| TryBuildSimplifiedForInPrepare(enumerator, slot); | |
| if (lowering.IsExit()) return; | |
| DCHECK(!lowering.Changed()); | |
| Node* node = NewNode(javascript()->ForInPrepare(GetForInMode(1)), enumerator); | |
| environment()->BindRegistersToProjections( | |
| bytecode_iterator().GetRegisterOperand(0), node); | |
| } | |
| void BytecodeGraphBuilder::VisitForInContinue() { | |
| PrepareEagerCheckpoint(); | |
| Node* index = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| Node* cache_length = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1)); | |
| Node* exit_cond = NewNode(simplified()->SpeculativeNumberLessThan( | |
| NumberOperationHint::kSignedSmall), | |
| index, cache_length); | |
| environment()->BindAccumulator(exit_cond); | |
| } | |
| void BytecodeGraphBuilder::VisitForInNext() { | |
| PrepareEagerCheckpoint(); | |
| Node* receiver = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| Node* index = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1)); | |
| int catch_reg_pair_index = bytecode_iterator().GetRegisterOperand(2).index(); | |
| Node* cache_type = environment()->LookupRegister( | |
| interpreter::Register(catch_reg_pair_index)); | |
| Node* cache_array = environment()->LookupRegister( | |
| interpreter::Register(catch_reg_pair_index + 1)); | |
| // We need to rename the {index} here, as in case of OSR we loose the | |
| // information that the {index} is always a valid unsigned Smi value. | |
| index = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), index, | |
| environment()->GetEffectDependency(), | |
| environment()->GetControlDependency()); | |
| environment()->UpdateEffectDependency(index); | |
| FeedbackSlot slot = bytecode_iterator().GetSlotOperand(3); | |
| JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedForInNext( | |
| receiver, cache_array, cache_type, index, slot); | |
| if (lowering.IsExit()) return; | |
| DCHECK(!lowering.Changed()); | |
| Node* node = NewNode(javascript()->ForInNext(GetForInMode(3)), receiver, | |
| cache_array, cache_type, index); | |
| environment()->BindAccumulator(node, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitForInStep() { | |
| PrepareEagerCheckpoint(); | |
| Node* index = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| index = NewNode(simplified()->SpeculativeSafeIntegerAdd( | |
| NumberOperationHint::kSignedSmall), | |
| index, jsgraph()->OneConstant()); | |
| environment()->BindAccumulator(index, Environment::kAttachFrameState); | |
| } | |
| void BytecodeGraphBuilder::VisitSuspendGenerator() { | |
| Node* generator = environment()->LookupRegister( | |
| bytecode_iterator().GetRegisterOperand(0)); | |
| interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1); | |
| // We assume we are storing a range starting from index 0. | |
| CHECK_EQ(0, first_reg.index()); | |
| int register_count = | |
| static_cast<int>(bytecode_iterator().GetRegisterCountOperand(2)); | |
| int parameter_count_without_receiver = | |
| bytecode_array()->parameter_count() - 1; | |
| Node* suspend_id = jsgraph()->SmiConstant( | |
| bytecode_iterator().GetUnsignedImmediateOperand(3)); | |
| // The offsets used by the bytecode iterator are relative to a different base | |
| // than what is used in the interpreter, hence the addition. | |
| Node* offset = | |
| jsgraph()->Constant(bytecode_iterator().current_offset() + | |
| (BytecodeArray::kHeaderSize - kHeapObjectTag)); | |
| const BytecodeLivenessState* liveness = bytecode_analysis()->GetInLivenessFor( | |
| bytecode_iterator().current_offset()); | |
| // Maybe overallocate the value list since we don't know how many registers | |
| // are live. | |
| // TODO(leszeks): We could get this count from liveness rather than the | |
| // register list. | |
| int value_input_count = 3 + parameter_count_without_receiver + register_count; | |
| Node** value_inputs = local_zone()->NewArray<Node*>(value_input_count); | |
| value_inputs[0] = generator; | |
| value_inputs[1] = suspend_id; | |
| value_inputs[2] = offset; | |
| int count_written = 0; | |
| // Store the parameters. | |
| for (int i = 0; i < parameter_count_without_receiver; i++) { | |
| value_inputs[3 + count_written++] = | |
| environment()->LookupRegister(interpreter::Register::FromParameterIndex( | |
| i, parameter_count_without_receiver)); | |
| } | |
| // Store the registers. | |
| for (int i = 0; i < register_count; ++i) { | |
| if (liveness == nullptr || liveness->RegisterIsLive(i)) { | |
| int index_in_parameters_and_registers = | |
| parameter_count_without_receiver + i; | |
| while (count_written < index_in_parameters_and_registers) { | |
| value_inputs[3 + count_written++] = jsgraph()->OptimizedOutConstant(); | |
| } | |
| value_inputs[3 + count_written++] = | |
| environment()->LookupRegister(interpreter::Register(i)); | |
| DCHECK_EQ(count_written, index_in_parameters_and_registers + 1); | |
| } | |
| } | |
| // Use the actual written count rather than the register count to create the | |
| // node. | |
| MakeNode(javascript()->GeneratorStore(count_written), 3 + count_written, | |
| value_inputs, false); | |
| // TODO(leszeks): This over-approximates the liveness at exit, only the | |
| // accumulator should be live by this point. | |
| BuildReturn(bytecode_analysis()->GetInLivenessFor( | |
| bytecode_iterator().current_offset())); | |
| } | |
| void BytecodeGraphBuilder::BuildSwitchOnGeneratorState( | |
| const ZoneVector<ResumeJumpTarget>& resume_jump_targets, | |
| bool allow_fallthrough_on_executing) { | |
| Node* generator_state = environment()->LookupGeneratorState(); | |
| int extra_cases = allow_fallthrough_on_executing ? 2 : 1; | |
| NewSwitch(generator_state, | |
| static_cast<int>(resume_jump_targets.size() + extra_cases)); | |
| for (const ResumeJumpTarget& target : resume_jump_targets) { | |
| SubEnvironment sub_environment(this); | |
| NewIfValue(target.suspend_id()); | |
| if (target.is_leaf()) { | |
| // Mark that we are resuming executing. | |
| environment()->BindGeneratorState( | |
| jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting)); | |
| } | |
| // Jump to the target offset, whether it's a loop header or the resume. | |
| MergeIntoSuccessorEnvironment(target.target_offset()); | |
| } | |
| { | |
| SubEnvironment sub_environment(this); | |
| // We should never hit the default case (assuming generator state cannot be | |
| // corrupted), so abort if we do. | |
| // TODO(leszeks): Maybe only check this in debug mode, and otherwise use | |
| // the default to represent one of the cases above/fallthrough below? | |
| NewIfDefault(); | |
| NewNode(simplified()->RuntimeAbort(AbortReason::kInvalidJumpTableIndex)); | |
| // TODO(7099): Investigate if we need LoopExit here. | |
| Node* control = NewNode(common()->Throw()); | |
| MergeControlToLeaveFunction(control); | |
| } | |
| if (allow_fallthrough_on_executing) { | |
| // If we are executing (rather than resuming), and we allow it, just fall | |
| // through to the actual loop body. | |
| NewIfValue(JSGeneratorObject::kGeneratorExecuting); | |
| } else { | |
| // Otherwise, this environment is dead. | |
| set_environment(nullptr); | |
| } | |
| } | |
| void BytecodeGraphBuilder::VisitSwitchOnGeneratorState() { | |
| Node* generator = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| Node* generator_is_undefined = | |
| NewNode(simplified()->ReferenceEqual(), generator, | |
| jsgraph()->UndefinedConstant()); | |
| NewBranch(generator_is_undefined); | |
| { | |
| SubEnvironment resume_env(this); | |
| NewIfFalse(); | |
| Node* generator_state = | |
| NewNode(javascript()->GeneratorRestoreContinuation(), generator); | |
| environment()->BindGeneratorState(generator_state); | |
| Node* generator_context = | |
| NewNode(javascript()->GeneratorRestoreContext(), generator); | |
| environment()->SetContext(generator_context); | |
| BuildSwitchOnGeneratorState(bytecode_analysis()->resume_jump_targets(), | |
| false); | |
| } | |
| // Fallthrough for the first-call case. | |
| NewIfTrue(); | |
| } | |
| void BytecodeGraphBuilder::VisitResumeGenerator() { | |
| Node* generator = | |
| environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0)); | |
| interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1); | |
| // We assume we are restoring registers starting fromm index 0. | |
| CHECK_EQ(0, first_reg.index()); | |
| const BytecodeLivenessState* liveness = | |
| bytecode_analysis()->GetOutLivenessFor( | |
| bytecode_iterator().current_offset()); | |
| int parameter_count_without_receiver = | |
| bytecode_array()->parameter_count() - 1; | |
| // Mapping between registers and array indices must match that used in | |
| // InterpreterAssembler::ExportParametersAndRegisterFile. | |
| for (int i = 0; i < environment()->register_count(); ++i) { | |
| if (liveness == nullptr || liveness->RegisterIsLive(i)) { | |
| Node* value = NewNode(javascript()->GeneratorRestoreRegister( | |
| parameter_count_without_receiver + i), | |
| generator); | |
| environment()->BindRegister(interpreter::Register(i), value); | |
| } | |
| } | |
| // Update the accumulator with the generator's input_or_debug_pos. | |
| Node* input_or_debug_pos = | |
| NewNode(javascript()->GeneratorRestoreInputOrDebugPos(), generator); | |
| environment()->BindAccumulator(input_or_debug_pos); | |
| } | |
| void BytecodeGraphBuilder::VisitWide() { | |
| // Consumed by the BytecodeArrayIterator. | |
| UNREACHABLE(); | |
| } | |
| void BytecodeGraphBuilder::VisitExtraWide() { | |
| // Consumed by the BytecodeArrayIterator. | |
| UNREACHABLE(); | |
| } | |
| void BytecodeGraphBuilder::VisitIllegal() { | |
| // Not emitted in valid bytecode. | |
| UNREACHABLE(); | |
| } | |
| void BytecodeGraphBuilder::SwitchToMergeEnvironment(int current_offset) { | |
| auto it = merge_environments_.find(current_offset); | |
| if (it != merge_environments_.end()) { | |
| mark_as_needing_eager_checkpoint(true); | |
| if (environment() != nullptr) { | |
| it->second->Merge(environment(), | |
| bytecode_analysis()->GetInLivenessFor(current_offset)); | |
| } | |
| set_environment(it->second); | |
| } | |
| } | |
| void BytecodeGraphBuilder::BuildLoopHeaderEnvironment(int current_offset) { | |
| if (bytecode_analysis()->IsLoopHeader(current_offset)) { | |
| mark_as_needing_eager_checkpoint(true); | |
| const LoopInfo& loop_info = | |
| bytecode_analysis()->GetLoopInfoFor(current_offset); | |
| const BytecodeLivenessState* liveness = | |
| bytecode_analysis()->GetInLivenessFor(current_offset); | |
| const auto& resume_jump_targets = loop_info.resume_jump_targets(); | |
| bool generate_suspend_switch = !resume_jump_targets.empty(); | |
| // Add loop header. | |
| environment()->PrepareForLoop(loop_info.assignments(), liveness); | |
| // Store a copy of the environment so we can connect merged back edge inputs | |
| // to the loop header. | |
| merge_environments_[current_offset] = environment()->Copy(); | |
| // If this loop contains resumes, create a new switch just after the loop | |
| // for those resumes. | |
| if (generate_suspend_switch) { | |
| BuildSwitchOnGeneratorState(loop_info.resume_jump_targets(), true); | |
| // TODO(leszeks): At this point we know we are executing rather than | |
| // resuming, so we should be able to prune off the phis in the environment | |
| // related to the resume path. | |
| // Set the generator state to a known constant. | |
| environment()->BindGeneratorState( | |
| jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting)); | |
| } | |
| } | |
| } | |
| void BytecodeGraphBuilder::MergeIntoSuccessorEnvironment(int target_offset) { | |
| BuildLoopExitsForBranch(target_offset); | |
| Environment*& merge_environment = merge_environments_[target_offset]; | |
| if (merge_environment == nullptr) { | |
| // Append merge nodes to the environment. We may merge here with another | |
| // environment. So add a place holder for merge nodes. We may add redundant | |
| // but will be eliminated in a later pass. | |
| // TODO(mstarzinger): Be smarter about this! | |
| NewMerge(); | |
| merge_environment = environment(); | |
| } else { | |
| // Merge any values which are live coming into the successor. | |
| merge_environment->Merge( | |
| environment(), bytecode_analysis()->GetInLivenessFor(target_offset)); | |
| } | |
| set_environment(nullptr); | |
| } | |
| void BytecodeGraphBuilder::MergeControlToLeaveFunction(Node* exit) { | |
| exit_controls_.push_back(exit); | |
| set_environment(nullptr); | |
| } | |
| void BytecodeGraphBuilder::BuildLoopExitsForBranch(int target_offset) { | |
| int origin_offset = bytecode_iterator().current_offset(); | |
| // Only build loop exits for forward edges. | |
| if (target_offset > origin_offset) { | |
| BuildLoopExitsUntilLoop( | |
| bytecode_analysis()->GetLoopOffsetFor(target_offset), | |
| bytecode_analysis()->GetInLivenessFor(target_offset)); | |
| } | |
| } | |
| void BytecodeGraphBuilder::BuildLoopExitsUntilLoop( | |
| int loop_offset, const BytecodeLivenessState* liveness) { | |
| int origin_offset = bytecode_iterator().current_offset(); | |
| int current_loop = bytecode_analysis()->GetLoopOffsetFor(origin_offset); | |
| // The limit_offset is the stop offset for building loop exists, used for OSR. | |
| // It prevents the creations of loopexits for loops which do not exist. | |
| loop_offset = std::max(loop_offset, currently_peeled_loop_offset_); | |
| while (loop_offset < current_loop) { | |
| Node* loop_node = merge_environments_[current_loop]->GetControlDependency(); | |
| const LoopInfo& loop_info = | |
| bytecode_analysis()->GetLoopInfoFor(current_loop); | |
| environment()->PrepareForLoopExit(loop_node, loop_info.assignments(), | |
| liveness); | |
| current_loop = loop_info.parent_offset(); | |
| } | |
| } | |
| void BytecodeGraphBuilder::BuildLoopExitsForFunctionExit( | |
| const BytecodeLivenessState* liveness) { | |
| BuildLoopExitsUntilLoop(-1, liveness); | |
| } | |
| void BytecodeGraphBuilder::BuildJump() { | |
| MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset()); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIf(Node* condition) { | |
| NewBranch(condition, BranchHint::kNone, IsSafetyCheck::kNoSafetyCheck); | |
| { | |
| SubEnvironment sub_environment(this); | |
| NewIfTrue(); | |
| MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset()); | |
| } | |
| NewIfFalse(); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfNot(Node* condition) { | |
| NewBranch(condition, BranchHint::kNone, IsSafetyCheck::kNoSafetyCheck); | |
| { | |
| SubEnvironment sub_environment(this); | |
| NewIfFalse(); | |
| MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset()); | |
| } | |
| NewIfTrue(); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfEqual(Node* comperand) { | |
| Node* accumulator = environment()->LookupAccumulator(); | |
| Node* condition = | |
| NewNode(simplified()->ReferenceEqual(), accumulator, comperand); | |
| BuildJumpIf(condition); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfNotEqual(Node* comperand) { | |
| Node* accumulator = environment()->LookupAccumulator(); | |
| Node* condition = | |
| NewNode(simplified()->ReferenceEqual(), accumulator, comperand); | |
| BuildJumpIfNot(condition); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfFalse() { | |
| NewBranch(environment()->LookupAccumulator(), BranchHint::kNone, | |
| IsSafetyCheck::kNoSafetyCheck); | |
| { | |
| SubEnvironment sub_environment(this); | |
| NewIfFalse(); | |
| environment()->BindAccumulator(jsgraph()->FalseConstant()); | |
| MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset()); | |
| } | |
| NewIfTrue(); | |
| environment()->BindAccumulator(jsgraph()->TrueConstant()); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfTrue() { | |
| NewBranch(environment()->LookupAccumulator(), BranchHint::kNone, | |
| IsSafetyCheck::kNoSafetyCheck); | |
| { | |
| SubEnvironment sub_environment(this); | |
| NewIfTrue(); | |
| environment()->BindAccumulator(jsgraph()->TrueConstant()); | |
| MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset()); | |
| } | |
| NewIfFalse(); | |
| environment()->BindAccumulator(jsgraph()->FalseConstant()); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfToBooleanTrue() { | |
| Node* accumulator = environment()->LookupAccumulator(); | |
| Node* condition = NewNode(simplified()->ToBoolean(), accumulator); | |
| BuildJumpIf(condition); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfToBooleanFalse() { | |
| Node* accumulator = environment()->LookupAccumulator(); | |
| Node* condition = NewNode(simplified()->ToBoolean(), accumulator); | |
| BuildJumpIfNot(condition); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfNotHole() { | |
| Node* accumulator = environment()->LookupAccumulator(); | |
| Node* condition = NewNode(simplified()->ReferenceEqual(), accumulator, | |
| jsgraph()->TheHoleConstant()); | |
| BuildJumpIfNot(condition); | |
| } | |
| void BytecodeGraphBuilder::BuildJumpIfJSReceiver() { | |
| Node* accumulator = environment()->LookupAccumulator(); | |
| Node* condition = NewNode(simplified()->ObjectIsReceiver(), accumulator); | |
| BuildJumpIf(condition); | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedUnaryOp(const Operator* op, | |
| Node* operand, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceUnaryOperation(op, operand, effect, control, | |
| slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedBinaryOp(const Operator* op, Node* left, | |
| Node* right, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceBinaryOperation(op, left, right, effect, | |
| control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedForInNext(Node* receiver, | |
| Node* cache_array, | |
| Node* cache_type, Node* index, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceForInNextOperation( | |
| receiver, cache_array, cache_type, index, effect, control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedForInPrepare(Node* enumerator, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceForInPrepareOperation(enumerator, effect, | |
| control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedToNumber(Node* value, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceToNumberOperation(value, effect, control, | |
| slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult BytecodeGraphBuilder::TryBuildSimplifiedCall( | |
| const Operator* op, Node* const* args, int arg_count, FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceCallOperation(op, args, arg_count, effect, | |
| control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedConstruct(const Operator* op, | |
| Node* const* args, | |
| int arg_count, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceConstructOperation(op, args, arg_count, effect, | |
| control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedLoadNamed(const Operator* op, | |
| Node* receiver, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult early_reduction = | |
| type_hint_lowering().ReduceLoadNamedOperation(op, receiver, effect, | |
| control, slot); | |
| ApplyEarlyReduction(early_reduction); | |
| return early_reduction; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedLoadKeyed(const Operator* op, | |
| Node* receiver, Node* key, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceLoadKeyedOperation(op, receiver, key, effect, | |
| control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedStoreNamed(const Operator* op, | |
| Node* receiver, Node* value, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceStoreNamedOperation(op, receiver, value, | |
| effect, control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| JSTypeHintLowering::LoweringResult | |
| BytecodeGraphBuilder::TryBuildSimplifiedStoreKeyed(const Operator* op, | |
| Node* receiver, Node* key, | |
| Node* value, | |
| FeedbackSlot slot) { | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* control = environment()->GetControlDependency(); | |
| JSTypeHintLowering::LoweringResult result = | |
| type_hint_lowering().ReduceStoreKeyedOperation(op, receiver, key, value, | |
| effect, control, slot); | |
| ApplyEarlyReduction(result); | |
| return result; | |
| } | |
| void BytecodeGraphBuilder::ApplyEarlyReduction( | |
| JSTypeHintLowering::LoweringResult reduction) { | |
| if (reduction.IsExit()) { | |
| MergeControlToLeaveFunction(reduction.control()); | |
| } else if (reduction.IsSideEffectFree()) { | |
| environment()->UpdateEffectDependency(reduction.effect()); | |
| environment()->UpdateControlDependency(reduction.control()); | |
| } else { | |
| DCHECK(!reduction.Changed()); | |
| // At the moment, we assume side-effect free reduction. To support | |
| // side-effects, we would have to invalidate the eager checkpoint, | |
| // so that deoptimization does not repeat the side effect. | |
| } | |
| } | |
| Node** BytecodeGraphBuilder::EnsureInputBufferSize(int size) { | |
| if (size > input_buffer_size_) { | |
| size = size + kInputBufferSizeIncrement + input_buffer_size_; | |
| input_buffer_ = local_zone()->NewArray<Node*>(size); | |
| input_buffer_size_ = size; | |
| } | |
| return input_buffer_; | |
| } | |
| void BytecodeGraphBuilder::ExitThenEnterExceptionHandlers(int current_offset) { | |
| HandlerTable table(*bytecode_array()); | |
| // Potentially exit exception handlers. | |
| while (!exception_handlers_.empty()) { | |
| int current_end = exception_handlers_.top().end_offset_; | |
| if (current_offset < current_end) break; // Still covered by range. | |
| exception_handlers_.pop(); | |
| } | |
| // Potentially enter exception handlers. | |
| int num_entries = table.NumberOfRangeEntries(); | |
| while (current_exception_handler_ < num_entries) { | |
| int next_start = table.GetRangeStart(current_exception_handler_); | |
| if (current_offset < next_start) break; // Not yet covered by range. | |
| int next_end = table.GetRangeEnd(current_exception_handler_); | |
| int next_handler = table.GetRangeHandler(current_exception_handler_); | |
| int context_register = table.GetRangeData(current_exception_handler_); | |
| exception_handlers_.push( | |
| {next_start, next_end, next_handler, context_register}); | |
| current_exception_handler_++; | |
| } | |
| } | |
| Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count, | |
| Node* const* value_inputs, | |
| bool incomplete) { | |
| DCHECK_EQ(op->ValueInputCount(), value_input_count); | |
| bool has_context = OperatorProperties::HasContextInput(op); | |
| bool has_frame_state = OperatorProperties::HasFrameStateInput(op); | |
| bool has_control = op->ControlInputCount() == 1; | |
| bool has_effect = op->EffectInputCount() == 1; | |
| DCHECK_LT(op->ControlInputCount(), 2); | |
| DCHECK_LT(op->EffectInputCount(), 2); | |
| Node* result = nullptr; | |
| if (!has_context && !has_frame_state && !has_control && !has_effect) { | |
| result = graph()->NewNode(op, value_input_count, value_inputs, incomplete); | |
| } else { | |
| bool inside_handler = !exception_handlers_.empty(); | |
| int input_count_with_deps = value_input_count; | |
| if (has_context) ++input_count_with_deps; | |
| if (has_frame_state) ++input_count_with_deps; | |
| if (has_control) ++input_count_with_deps; | |
| if (has_effect) ++input_count_with_deps; | |
| Node** buffer = EnsureInputBufferSize(input_count_with_deps); | |
| memcpy(buffer, value_inputs, kPointerSize * value_input_count); | |
| Node** current_input = buffer + value_input_count; | |
| if (has_context) { | |
| *current_input++ = environment()->Context(); | |
| } | |
| if (has_frame_state) { | |
| // The frame state will be inserted later. Here we misuse the {Dead} node | |
| // as a sentinel to be later overwritten with the real frame state by the | |
| // calls to {PrepareFrameState} within individual visitor methods. | |
| *current_input++ = jsgraph()->Dead(); | |
| } | |
| if (has_effect) { | |
| *current_input++ = environment()->GetEffectDependency(); | |
| } | |
| if (has_control) { | |
| *current_input++ = environment()->GetControlDependency(); | |
| } | |
| result = graph()->NewNode(op, input_count_with_deps, buffer, incomplete); | |
| // Update the current control dependency for control-producing nodes. | |
| if (result->op()->ControlOutputCount() > 0) { | |
| environment()->UpdateControlDependency(result); | |
| } | |
| // Update the current effect dependency for effect-producing nodes. | |
| if (result->op()->EffectOutputCount() > 0) { | |
| environment()->UpdateEffectDependency(result); | |
| } | |
| // Add implicit exception continuation for throwing nodes. | |
| if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) { | |
| int handler_offset = exception_handlers_.top().handler_offset_; | |
| int context_index = exception_handlers_.top().context_register_; | |
| interpreter::Register context_register(context_index); | |
| Environment* success_env = environment()->Copy(); | |
| const Operator* op = common()->IfException(); | |
| Node* effect = environment()->GetEffectDependency(); | |
| Node* on_exception = graph()->NewNode(op, effect, result); | |
| Node* context = environment()->LookupRegister(context_register); | |
| environment()->UpdateControlDependency(on_exception); | |
| environment()->UpdateEffectDependency(on_exception); | |
| environment()->BindAccumulator(on_exception); | |
| environment()->SetContext(context); | |
| MergeIntoSuccessorEnvironment(handler_offset); | |
| set_environment(success_env); | |
| } | |
| // Add implicit success continuation for throwing nodes. | |
| if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) { | |
| const Operator* if_success = common()->IfSuccess(); | |
| Node* on_success = graph()->NewNode(if_success, result); | |
| environment()->UpdateControlDependency(on_success); | |
| } | |
| // Ensure checkpoints are created after operations with side-effects. | |
| if (has_effect && !result->op()->HasProperty(Operator::kNoWrite)) { | |
| mark_as_needing_eager_checkpoint(true); | |
| } | |
| } | |
| return result; | |
| } | |
| Node* BytecodeGraphBuilder::NewPhi(int count, Node* input, Node* control) { | |
| const Operator* phi_op = common()->Phi(MachineRepresentation::kTagged, count); | |
| Node** buffer = EnsureInputBufferSize(count + 1); | |
| MemsetPointer(buffer, input, count); | |
| buffer[count] = control; | |
| return graph()->NewNode(phi_op, count + 1, buffer, true); | |
| } | |
| Node* BytecodeGraphBuilder::NewEffectPhi(int count, Node* input, | |
| Node* control) { | |
| const Operator* phi_op = common()->EffectPhi(count); | |
| Node** buffer = EnsureInputBufferSize(count + 1); | |
| MemsetPointer(buffer, input, count); | |
| buffer[count] = control; | |
| return graph()->NewNode(phi_op, count + 1, buffer, true); | |
| } | |
| Node* BytecodeGraphBuilder::MergeControl(Node* control, Node* other) { | |
| int inputs = control->op()->ControlInputCount() + 1; | |
| if (control->opcode() == IrOpcode::kLoop) { | |
| // Control node for loop exists, add input. | |
| const Operator* op = common()->Loop(inputs); | |
| control->AppendInput(graph_zone(), other); | |
| NodeProperties::ChangeOp(control, op); | |
| } else if (control->opcode() == IrOpcode::kMerge) { | |
| // Control node for merge exists, add input. | |
| const Operator* op = common()->Merge(inputs); | |
| control->AppendInput(graph_zone(), other); | |
| NodeProperties::ChangeOp(control, op); | |
| } else { | |
| // Control node is a singleton, introduce a merge. | |
| const Operator* op = common()->Merge(inputs); | |
| Node* merge_inputs[] = {control, other}; | |
| control = graph()->NewNode(op, arraysize(merge_inputs), merge_inputs, true); | |
| } | |
| return control; | |
| } | |
| Node* BytecodeGraphBuilder::MergeEffect(Node* value, Node* other, | |
| Node* control) { | |
| int inputs = control->op()->ControlInputCount(); | |
| if (value->opcode() == IrOpcode::kEffectPhi && | |
| NodeProperties::GetControlInput(value) == control) { | |
| // Phi already exists, add input. | |
| value->InsertInput(graph_zone(), inputs - 1, other); | |
| NodeProperties::ChangeOp(value, common()->EffectPhi(inputs)); | |
| } else if (value != other) { | |
| // Phi does not exist yet, introduce one. | |
| value = NewEffectPhi(inputs, value, control); | |
| value->ReplaceInput(inputs - 1, other); | |
| } | |
| return value; | |
| } | |
| Node* BytecodeGraphBuilder::MergeValue(Node* value, Node* other, | |
| Node* control) { | |
| int inputs = control->op()->ControlInputCount(); | |
| if (value->opcode() == IrOpcode::kPhi && | |
| NodeProperties::GetControlInput(value) == control) { | |
| // Phi already exists, add input. | |
| value->InsertInput(graph_zone(), inputs - 1, other); | |
| NodeProperties::ChangeOp( | |
| value, common()->Phi(MachineRepresentation::kTagged, inputs)); | |
| } else if (value != other) { | |
| // Phi does not exist yet, introduce one. | |
| value = NewPhi(inputs, value, control); | |
| value->ReplaceInput(inputs - 1, other); | |
| } | |
| return value; | |
| } | |
| void BytecodeGraphBuilder::UpdateSourcePosition(SourcePositionTableIterator* it, | |
| int offset) { | |
| if (it->done()) return; | |
| if (it->code_offset() == offset) { | |
| source_positions_->SetCurrentPosition(SourcePosition( | |
| it->source_position().ScriptOffset(), start_position_.InliningId())); | |
| it->Advance(); | |
| } else { | |
| DCHECK_GT(it->code_offset(), offset); | |
| } | |
| } | |
| } // namespace compiler | |
| } // namespace internal | |
| } // namespace v8 |