// 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/compiler/bytecode-branch-analysis.h" #include "src/compiler/linkage.h" #include "src/compiler/operator-properties.h" #include "src/interpreter/bytecodes.h" namespace v8 { namespace internal { namespace compiler { // Helper for generating frame states for before and after a bytecode. class BytecodeGraphBuilder::FrameStateBeforeAndAfter { public: FrameStateBeforeAndAfter(BytecodeGraphBuilder* builder, const interpreter::BytecodeArrayIterator& iterator) : builder_(builder), id_after_(BailoutId::None()), added_to_node_(false), output_poke_offset_(0), output_poke_count_(0) { BailoutId id_before(iterator.current_offset()); frame_state_before_ = builder_->environment()->Checkpoint( id_before, OutputFrameStateCombine::Ignore()); id_after_ = BailoutId(id_before.ToInt() + iterator.current_bytecode_size()); } ~FrameStateBeforeAndAfter() { DCHECK(added_to_node_); DCHECK(builder_->environment()->StateValuesAreUpToDate(output_poke_offset_, output_poke_count_)); } private: friend class Environment; void AddToNode(Node* node, OutputFrameStateCombine combine) { DCHECK(!added_to_node_); int count = OperatorProperties::GetFrameStateInputCount(node->op()); DCHECK_LE(count, 2); if (count >= 1) { // Add the frame state for after the operation. DCHECK_EQ(IrOpcode::kDead, NodeProperties::GetFrameStateInput(node, 0)->opcode()); Node* frame_state_after = builder_->environment()->Checkpoint(id_after_, combine); NodeProperties::ReplaceFrameStateInput(node, 0, frame_state_after); } if (count >= 2) { // Add the frame state for before the operation. DCHECK_EQ(IrOpcode::kDead, NodeProperties::GetFrameStateInput(node, 1)->opcode()); NodeProperties::ReplaceFrameStateInput(node, 1, frame_state_before_); } if (!combine.IsOutputIgnored()) { output_poke_offset_ = static_cast(combine.GetOffsetToPokeAt()); output_poke_count_ = node->op()->ValueOutputCount(); } added_to_node_ = true; } BytecodeGraphBuilder* builder_; Node* frame_state_before_; BailoutId id_after_; bool added_to_node_; int output_poke_offset_; int output_poke_count_; }; // 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, Node* control_dependency, Node* context) : builder_(builder), register_count_(register_count), parameter_count_(parameter_count), context_(context), control_dependency_(control_dependency), effect_dependency_(control_dependency), values_(builder->local_zone()), parameters_state_values_(nullptr), registers_state_values_(nullptr), accumulator_state_values_(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(values()->size()); Node* undefined_constant = builder->jsgraph()->UndefinedConstant(); values()->insert(values()->end(), register_count, undefined_constant); // Accumulator accumulator_base_ = static_cast(values()->size()); values()->push_back(undefined_constant); } 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_(nullptr), registers_state_values_(nullptr), accumulator_state_values_(nullptr), 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::LookupRegister( interpreter::Register the_register) const { if (the_register.is_function_context()) { return builder()->GetFunctionContext(); } else if (the_register.is_function_closure()) { return builder()->GetFunctionClosure(); } else if (the_register.is_new_target()) { return builder()->GetNewTarget(); } else { int values_index = RegisterToValuesIndex(the_register); return values()->at(values_index); } } void BytecodeGraphBuilder::Environment::ExchangeRegisters( interpreter::Register reg0, interpreter::Register reg1) { int reg0_index = RegisterToValuesIndex(reg0); int reg1_index = RegisterToValuesIndex(reg1); Node* saved_reg0_value = values()->at(reg0_index); values()->at(reg0_index) = values()->at(reg1_index); values()->at(reg1_index) = saved_reg0_value; } void BytecodeGraphBuilder::Environment::BindAccumulator( Node* node, FrameStateBeforeAndAfter* states) { if (states) { states->AddToNode(node, OutputFrameStateCombine::PokeAt(0)); } values()->at(accumulator_base_) = node; } void BytecodeGraphBuilder::Environment::BindRegister( interpreter::Register the_register, Node* node, FrameStateBeforeAndAfter* states) { int values_index = RegisterToValuesIndex(the_register); if (states) { states->AddToNode(node, OutputFrameStateCombine::PokeAt(accumulator_base_ - values_index)); } values()->at(values_index) = node; } void BytecodeGraphBuilder::Environment::BindRegistersToProjections( interpreter::Register first_reg, Node* node, FrameStateBeforeAndAfter* states) { int values_index = RegisterToValuesIndex(first_reg); if (states) { states->AddToNode(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, FrameStateBeforeAndAfter* states) { states->AddToNode(node, OutputFrameStateCombine::Ignore()); } bool BytecodeGraphBuilder::Environment::IsMarkedAsUnreachable() const { return GetControlDependency()->opcode() == IrOpcode::kDead; } void BytecodeGraphBuilder::Environment::MarkAsUnreachable() { UpdateControlDependency(builder()->jsgraph()->Dead()); } BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::Environment::CopyForLoop() { PrepareForLoop(); return new (zone()) Environment(this); } BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::Environment::CopyForConditional() const { return new (zone()) Environment(this); } void BytecodeGraphBuilder::Environment::Merge( BytecodeGraphBuilder::Environment* other) { // Nothing to do if the other environment is dead. if (other->IsMarkedAsUnreachable()) { return; } // 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 have differing input at merge points, // potentially extending an existing Phi node if possible. context_ = builder()->MergeValue(context_, other->context_, control); for (size_t i = 0; i < values_.size(); i++) { values_[i] = builder()->MergeValue(values_[i], other->values_[i], control); } } void BytecodeGraphBuilder::Environment::PrepareForLoop() { // 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); // Assume everything in the loop is updated. context_ = builder()->NewPhi(1, context_, control); int size = static_cast(values()->size()); for (int i = 0; i < size; i++) { values()->at(i) = builder()->NewPhi(1, values()->at(i), control); } // Connect to the loop end. Node* terminate = builder()->graph()->NewNode( builder()->common()->Terminate(), effect, control); builder()->exit_controls_.push_back(terminate); } bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate( Node** state_values, int offset, int count) { if (!builder()->info()->is_deoptimization_enabled()) { return false; } if (*state_values == nullptr) { return true; } DCHECK_EQ((*state_values)->InputCount(), count); DCHECK_LE(static_cast(offset + count), values()->size()); Node** env_values = (count == 0) ? nullptr : &values()->at(offset); for (int i = 0; i < count; i++) { if ((*state_values)->InputAt(i) != env_values[i]) { return true; } } return false; } void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values, int offset, int count) { if (StateValuesRequireUpdate(state_values, offset, count)) { const Operator* op = common()->StateValues(count); (*state_values) = graph()->NewNode(op, count, &values()->at(offset)); } } Node* BytecodeGraphBuilder::Environment::Checkpoint( BailoutId bailout_id, OutputFrameStateCombine combine) { if (!builder()->info()->is_deoptimization_enabled()) { return builder()->jsgraph()->EmptyFrameState(); } // TODO(rmcilroy): Consider using StateValuesCache for some state values. UpdateStateValues(¶meters_state_values_, 0, parameter_count()); UpdateStateValues(®isters_state_values_, register_base(), register_count()); UpdateStateValues(&accumulator_state_values_, accumulator_base(), 1); 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_values_, Context(), builder()->GetFunctionClosure(), builder()->graph()->start()); return result; } bool BytecodeGraphBuilder::Environment::StateValuesAreUpToDate( Node** state_values, int offset, int count, int output_poke_start, int output_poke_end) { DCHECK_LE(static_cast(offset + count), values()->size()); for (int i = 0; i < count; i++, offset++) { if (offset < output_poke_start || offset >= output_poke_end) { if ((*state_values)->InputAt(i) != values()->at(offset)) { return false; } } } return true; } bool BytecodeGraphBuilder::Environment::StateValuesAreUpToDate( int output_poke_offset, int output_poke_count) { // Poke offset is relative to the top of the stack (i.e., the accumulator). int output_poke_start = accumulator_base() - output_poke_offset; int output_poke_end = output_poke_start + output_poke_count; return StateValuesAreUpToDate(¶meters_state_values_, 0, parameter_count(), output_poke_start, output_poke_end) && StateValuesAreUpToDate(®isters_state_values_, register_base(), register_count(), output_poke_start, output_poke_end) && StateValuesAreUpToDate(&accumulator_state_values_, accumulator_base(), 1, output_poke_start, output_poke_end); } BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone, CompilationInfo* compilation_info, JSGraph* jsgraph) : local_zone_(local_zone), info_(compilation_info), jsgraph_(jsgraph), bytecode_array_(handle(info()->shared_info()->bytecode_array())), frame_state_function_info_(common()->CreateFrameStateFunctionInfo( FrameStateType::kInterpretedFunction, bytecode_array()->parameter_count(), bytecode_array()->register_count(), info()->shared_info(), CALL_MAINTAINS_NATIVE_CONTEXT)), merge_environments_(local_zone), loop_header_environments_(local_zone), input_buffer_size_(0), input_buffer_(nullptr), exit_controls_(local_zone) {} Node* BytecodeGraphBuilder::GetNewTarget() { if (!new_target_.is_set()) { int params = bytecode_array()->parameter_count(); int index = Linkage::GetJSCallNewTargetParamIndex(params); const Operator* op = common()->Parameter(index, "%new.target"); Node* node = NewNode(op, graph()->start()); new_target_.set(node); } return new_target_.get(); } Node* BytecodeGraphBuilder::GetFunctionContext() { if (!function_context_.is_set()) { int params = bytecode_array()->parameter_count(); int index = Linkage::GetJSCallContextParamIndex(params); const Operator* op = common()->Parameter(index, "%context"); Node* node = NewNode(op, graph()->start()); function_context_.set(node); } return function_context_.get(); } 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::BuildLoadObjectField(Node* object, int offset) { return NewNode(jsgraph()->machine()->Load(MachineType::AnyTagged()), object, jsgraph()->IntPtrConstant(offset - kHeapObjectTag)); } Node* BytecodeGraphBuilder::BuildLoadImmutableObjectField(Node* object, int offset) { return graph()->NewNode(jsgraph()->machine()->Load(MachineType::AnyTagged()), object, jsgraph()->IntPtrConstant(offset - kHeapObjectTag), graph()->start(), graph()->start()); } Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) { const Operator* op = javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true); Node* native_context = NewNode(op, environment()->Context()); return NewNode(javascript()->LoadContext(0, index, true), native_context); } Node* BytecodeGraphBuilder::BuildLoadFeedbackVector() { if (!feedback_vector_.is_set()) { Node* closure = GetFunctionClosure(); Node* shared = BuildLoadImmutableObjectField( closure, JSFunction::kSharedFunctionInfoOffset); Node* vector = BuildLoadImmutableObjectField( shared, SharedFunctionInfo::kFeedbackVectorOffset); feedback_vector_.set(vector); } return feedback_vector_.get(); } VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) { Handle feedback_vector = info()->feedback_vector(); FeedbackVectorSlot slot; if (slot_id >= TypeFeedbackVector::kReservedIndexCount) { slot = feedback_vector->ToSlot(slot_id); } return VectorSlotPair(feedback_vector, slot); } bool BytecodeGraphBuilder::CreateGraph(bool stack_check) { // Set up the basic structure of the graph. Outputs for {Start} are // the formal parameters (including the receiver) plus context and // closure. // 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(), graph()->start(), GetFunctionContext()); set_environment(&env); CreateGraphBody(stack_check); // Finish the basic structure of the graph. DCHECK_NE(0u, exit_controls_.size()); int const input_count = static_cast(exit_controls_.size()); Node** const inputs = &exit_controls_.front(); Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs); graph()->SetEnd(end); return true; } void BytecodeGraphBuilder::CreateGraphBody(bool stack_check) { // TODO(oth): Review ast-graph-builder equivalent, i.e. arguments // object setup, this function variable if used, tracing hooks. if (stack_check) { Node* node = NewNode(javascript()->StackCheck()); PrepareEntryFrameState(node); } VisitBytecodes(); } void BytecodeGraphBuilder::VisitBytecodes() { BytecodeBranchAnalysis analysis(bytecode_array(), local_zone()); analysis.Analyze(); set_branch_analysis(&analysis); interpreter::BytecodeArrayIterator iterator(bytecode_array()); set_bytecode_iterator(&iterator); while (!iterator.done()) { int current_offset = iterator.current_offset(); if (analysis.is_reachable(current_offset)) { MergeEnvironmentsOfForwardBranches(current_offset); BuildLoopHeaderForBackwardBranches(current_offset); switch (iterator.current_bytecode()) { #define BYTECODE_CASE(name, ...) \ case interpreter::Bytecode::k##name: \ Visit##name(iterator); \ break; BYTECODE_LIST(BYTECODE_CASE) #undef BYTECODE_CODE } } iterator.Advance(); } set_branch_analysis(nullptr); set_bytecode_iterator(nullptr); } void BytecodeGraphBuilder::VisitLdaZero( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->ZeroConstant(); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaSmi8( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->Constant(iterator.GetImmediateOperand(0)); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaConstantWide( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->Constant(iterator.GetConstantForIndexOperand(0)); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaConstant( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->Constant(iterator.GetConstantForIndexOperand(0)); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaUndefined( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->UndefinedConstant(); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaNull( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->NullConstant(); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaTheHole( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->TheHoleConstant(); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaTrue( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->TrueConstant(); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaFalse( const interpreter::BytecodeArrayIterator& iterator) { Node* node = jsgraph()->FalseConstant(); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdar( const interpreter::BytecodeArrayIterator& iterator) { Node* value = environment()->LookupRegister(iterator.GetRegisterOperand(0)); environment()->BindAccumulator(value); } void BytecodeGraphBuilder::VisitStar( const interpreter::BytecodeArrayIterator& iterator) { Node* value = environment()->LookupAccumulator(); environment()->BindRegister(iterator.GetRegisterOperand(0), value); } void BytecodeGraphBuilder::VisitMov( const interpreter::BytecodeArrayIterator& iterator) { Node* value = environment()->LookupRegister(iterator.GetRegisterOperand(0)); environment()->BindRegister(iterator.GetRegisterOperand(1), value); } void BytecodeGraphBuilder::VisitExchange( const interpreter::BytecodeArrayIterator& iterator) { environment()->ExchangeRegisters(iterator.GetRegisterOperand(0), iterator.GetRegisterOperand(1)); } void BytecodeGraphBuilder::VisitExchangeWide( const interpreter::BytecodeArrayIterator& iterator) { environment()->ExchangeRegisters(iterator.GetRegisterOperand(0), iterator.GetRegisterOperand(1)); } void BytecodeGraphBuilder::BuildLoadGlobal( const interpreter::BytecodeArrayIterator& iterator, TypeofMode typeof_mode) { FrameStateBeforeAndAfter states(this, iterator); Handle name = Handle::cast(iterator.GetConstantForIndexOperand(0)); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(1)); const Operator* op = javascript()->LoadGlobal(name, feedback, typeof_mode); Node* node = NewNode(op, BuildLoadFeedbackVector()); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitLdaGlobalSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildLoadGlobal(iterator, TypeofMode::NOT_INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildLoadGlobal(iterator, TypeofMode::NOT_INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalInsideTypeofSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildLoadGlobal(iterator, TypeofMode::INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalInsideTypeofStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildLoadGlobal(iterator, TypeofMode::INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildLoadGlobal(iterator, TypeofMode::NOT_INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildLoadGlobal(iterator, TypeofMode::NOT_INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalInsideTypeofSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildLoadGlobal(iterator, TypeofMode::INSIDE_TYPEOF); } void BytecodeGraphBuilder::VisitLdaGlobalInsideTypeofStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildLoadGlobal(iterator, TypeofMode::INSIDE_TYPEOF); } void BytecodeGraphBuilder::BuildStoreGlobal( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Handle name = Handle::cast(iterator.GetConstantForIndexOperand(0)); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(1)); Node* value = environment()->LookupAccumulator(); const Operator* op = javascript()->StoreGlobal(language_mode(), name, feedback); Node* node = NewNode(op, value, BuildLoadFeedbackVector()); environment()->RecordAfterState(node, &states); } void BytecodeGraphBuilder::VisitStaGlobalSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildStoreGlobal(iterator); } void BytecodeGraphBuilder::VisitStaGlobalStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildStoreGlobal(iterator); } void BytecodeGraphBuilder::VisitStaGlobalSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildStoreGlobal(iterator); } void BytecodeGraphBuilder::VisitStaGlobalStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildStoreGlobal(iterator); } void BytecodeGraphBuilder::VisitLdaContextSlot( const interpreter::BytecodeArrayIterator& iterator) { // TODO(mythria): LoadContextSlots are unrolled by the required depth when // generating bytecode. Hence the value of depth is always 0. Update this // code, when the implementation changes. // TODO(mythria): immutable flag is also set to false. This information is not // available in bytecode array. update this code when the implementation // changes. const Operator* op = javascript()->LoadContext(0, iterator.GetIndexOperand(1), false); Node* context = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* node = NewNode(op, context); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitLdaContextSlotWide( const interpreter::BytecodeArrayIterator& iterator) { VisitLdaContextSlot(iterator); } void BytecodeGraphBuilder::VisitStaContextSlot( const interpreter::BytecodeArrayIterator& iterator) { // TODO(mythria): LoadContextSlots are unrolled by the required depth when // generating bytecode. Hence the value of depth is always 0. Update this // code, when the implementation changes. const Operator* op = javascript()->StoreContext(0, iterator.GetIndexOperand(1)); Node* context = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* value = environment()->LookupAccumulator(); NewNode(op, context, value); } void BytecodeGraphBuilder::VisitStaContextSlotWide( const interpreter::BytecodeArrayIterator& iterator) { VisitStaContextSlot(iterator); } void BytecodeGraphBuilder::BuildLdaLookupSlot( TypeofMode typeof_mode, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Handle name = Handle::cast(iterator.GetConstantForIndexOperand(0)); const Operator* op = javascript()->LoadDynamic(name, typeof_mode); Node* value = NewNode(op, BuildLoadFeedbackVector(), environment()->Context()); environment()->BindAccumulator(value, &states); } void BytecodeGraphBuilder::VisitLdaLookupSlot( const interpreter::BytecodeArrayIterator& iterator) { BuildLdaLookupSlot(TypeofMode::NOT_INSIDE_TYPEOF, iterator); } void BytecodeGraphBuilder::VisitLdaLookupSlotInsideTypeof( const interpreter::BytecodeArrayIterator& iterator) { BuildLdaLookupSlot(TypeofMode::INSIDE_TYPEOF, iterator); } void BytecodeGraphBuilder::BuildStaLookupSlot( LanguageMode language_mode, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* value = environment()->LookupAccumulator(); Node* name = jsgraph()->Constant(iterator.GetConstantForIndexOperand(0)); Node* language = jsgraph()->Constant(language_mode); const Operator* op = javascript()->CallRuntime(Runtime::kStoreLookupSlot, 4); Node* store = NewNode(op, value, environment()->Context(), name, language); environment()->BindAccumulator(store, &states); } void BytecodeGraphBuilder::VisitLdaLookupSlotWide( const interpreter::BytecodeArrayIterator& iterator) { VisitLdaLookupSlot(iterator); } void BytecodeGraphBuilder::VisitLdaLookupSlotInsideTypeofWide( const interpreter::BytecodeArrayIterator& iterator) { VisitLdaLookupSlotInsideTypeof(iterator); } void BytecodeGraphBuilder::VisitStaLookupSlotSloppy( const interpreter::BytecodeArrayIterator& iterator) { BuildStaLookupSlot(LanguageMode::SLOPPY, iterator); } void BytecodeGraphBuilder::VisitStaLookupSlotStrict( const interpreter::BytecodeArrayIterator& iterator) { BuildStaLookupSlot(LanguageMode::STRICT, iterator); } void BytecodeGraphBuilder::VisitStaLookupSlotSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { VisitStaLookupSlotSloppy(iterator); } void BytecodeGraphBuilder::VisitStaLookupSlotStrictWide( const interpreter::BytecodeArrayIterator& iterator) { VisitStaLookupSlotStrict(iterator); } void BytecodeGraphBuilder::BuildNamedLoad( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Handle name = Handle::cast(iterator.GetConstantForIndexOperand(1)); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(2)); const Operator* op = javascript()->LoadNamed(language_mode(), name, feedback); Node* node = NewNode(op, object, BuildLoadFeedbackVector()); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitLoadICSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildNamedLoad(iterator); } void BytecodeGraphBuilder::VisitLoadICStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildNamedLoad(iterator); } void BytecodeGraphBuilder::VisitLoadICSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildNamedLoad(iterator); } void BytecodeGraphBuilder::VisitLoadICStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildNamedLoad(iterator); } void BytecodeGraphBuilder::BuildKeyedLoad( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* key = environment()->LookupAccumulator(); Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0)); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(1)); const Operator* op = javascript()->LoadProperty(language_mode(), feedback); Node* node = NewNode(op, object, key, BuildLoadFeedbackVector()); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitKeyedLoadICSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildKeyedLoad(iterator); } void BytecodeGraphBuilder::VisitKeyedLoadICStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildKeyedLoad(iterator); } void BytecodeGraphBuilder::VisitKeyedLoadICSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildKeyedLoad(iterator); } void BytecodeGraphBuilder::VisitKeyedLoadICStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildKeyedLoad(iterator); } void BytecodeGraphBuilder::BuildNamedStore( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* value = environment()->LookupAccumulator(); Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Handle name = Handle::cast(iterator.GetConstantForIndexOperand(1)); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(2)); const Operator* op = javascript()->StoreNamed(language_mode(), name, feedback); Node* node = NewNode(op, object, value, BuildLoadFeedbackVector()); environment()->RecordAfterState(node, &states); } void BytecodeGraphBuilder::VisitStoreICSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildNamedStore(iterator); } void BytecodeGraphBuilder::VisitStoreICStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildNamedStore(iterator); } void BytecodeGraphBuilder::VisitStoreICSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildNamedStore(iterator); } void BytecodeGraphBuilder::VisitStoreICStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildNamedStore(iterator); } void BytecodeGraphBuilder::BuildKeyedStore( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* value = environment()->LookupAccumulator(); Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* key = environment()->LookupRegister(iterator.GetRegisterOperand(1)); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(2)); const Operator* op = javascript()->StoreProperty(language_mode(), feedback); Node* node = NewNode(op, object, key, value, BuildLoadFeedbackVector()); environment()->RecordAfterState(node, &states); } void BytecodeGraphBuilder::VisitKeyedStoreICSloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildKeyedStore(iterator); } void BytecodeGraphBuilder::VisitKeyedStoreICStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildKeyedStore(iterator); } void BytecodeGraphBuilder::VisitKeyedStoreICSloppyWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildKeyedStore(iterator); } void BytecodeGraphBuilder::VisitKeyedStoreICStrictWide( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildKeyedStore(iterator); } void BytecodeGraphBuilder::VisitPushContext( const interpreter::BytecodeArrayIterator& iterator) { Node* context = environment()->LookupAccumulator(); environment()->BindRegister(iterator.GetRegisterOperand(0), context); environment()->SetContext(context); } void BytecodeGraphBuilder::VisitPopContext( const interpreter::BytecodeArrayIterator& iterator) { Node* context = environment()->LookupRegister(iterator.GetRegisterOperand(0)); environment()->SetContext(context); } void BytecodeGraphBuilder::VisitCreateClosure( const interpreter::BytecodeArrayIterator& iterator) { Handle shared_info = Handle::cast(iterator.GetConstantForIndexOperand(0)); PretenureFlag tenured = iterator.GetImmediateOperand(1) ? TENURED : NOT_TENURED; const Operator* op = javascript()->CreateClosure(shared_info, tenured); Node* closure = NewNode(op); environment()->BindAccumulator(closure); } void BytecodeGraphBuilder::VisitCreateClosureWide( const interpreter::BytecodeArrayIterator& iterator) { VisitCreateClosure(iterator); } void BytecodeGraphBuilder::BuildCreateArguments( CreateArgumentsParameters::Type type, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); const Operator* op = javascript()->CreateArguments(type, 0); Node* object = NewNode(op, GetFunctionClosure()); environment()->BindAccumulator(object, &states); } void BytecodeGraphBuilder::VisitCreateMappedArguments( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateArguments(CreateArgumentsParameters::kMappedArguments, iterator); } void BytecodeGraphBuilder::VisitCreateUnmappedArguments( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateArguments(CreateArgumentsParameters::kUnmappedArguments, iterator); } void BytecodeGraphBuilder::BuildCreateLiteral( const Operator* op, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* literal = NewNode(op, GetFunctionClosure()); environment()->BindAccumulator(literal, &states); } void BytecodeGraphBuilder::BuildCreateRegExpLiteral( const interpreter::BytecodeArrayIterator& iterator) { Handle constant_pattern = Handle::cast(iterator.GetConstantForIndexOperand(0)); int literal_index = iterator.GetIndexOperand(1); int literal_flags = iterator.GetImmediateOperand(2); const Operator* op = javascript()->CreateLiteralRegExp( constant_pattern, literal_flags, literal_index); BuildCreateLiteral(op, iterator); } void BytecodeGraphBuilder::VisitCreateRegExpLiteral( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateRegExpLiteral(iterator); } void BytecodeGraphBuilder::VisitCreateRegExpLiteralWide( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateRegExpLiteral(iterator); } void BytecodeGraphBuilder::BuildCreateArrayLiteral( const interpreter::BytecodeArrayIterator& iterator) { Handle constant_elements = Handle::cast(iterator.GetConstantForIndexOperand(0)); int literal_index = iterator.GetIndexOperand(1); int literal_flags = iterator.GetImmediateOperand(2); const Operator* op = javascript()->CreateLiteralArray( constant_elements, literal_flags, literal_index); BuildCreateLiteral(op, iterator); } void BytecodeGraphBuilder::VisitCreateArrayLiteral( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateArrayLiteral(iterator); } void BytecodeGraphBuilder::VisitCreateArrayLiteralWide( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateArrayLiteral(iterator); } void BytecodeGraphBuilder::BuildCreateObjectLiteral( const interpreter::BytecodeArrayIterator& iterator) { Handle constant_properties = Handle::cast(iterator.GetConstantForIndexOperand(0)); int literal_index = iterator.GetIndexOperand(1); int literal_flags = iterator.GetImmediateOperand(2); const Operator* op = javascript()->CreateLiteralObject( constant_properties, literal_flags, literal_index); BuildCreateLiteral(op, iterator); } void BytecodeGraphBuilder::VisitCreateObjectLiteral( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateObjectLiteral(iterator); } void BytecodeGraphBuilder::VisitCreateObjectLiteralWide( const interpreter::BytecodeArrayIterator& iterator) { BuildCreateObjectLiteral(iterator); } Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op, Node* callee, interpreter::Register receiver, size_t arity) { Node** all = info()->zone()->NewArray(static_cast(arity)); all[0] = callee; all[1] = environment()->LookupRegister(receiver); int receiver_index = receiver.index(); for (int i = 2; i < static_cast(arity); ++i) { all[i] = environment()->LookupRegister( interpreter::Register(receiver_index + i - 1)); } Node* value = MakeNode(call_op, static_cast(arity), all, false); return value; } void BytecodeGraphBuilder::BuildCall( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); // TODO(rmcilroy): Set receiver_hint correctly based on whether the receiver // register has been loaded with null / undefined explicitly or we are sure it // is not null / undefined. ConvertReceiverMode receiver_hint = ConvertReceiverMode::kAny; Node* callee = environment()->LookupRegister(iterator.GetRegisterOperand(0)); interpreter::Register receiver = iterator.GetRegisterOperand(1); size_t arg_count = iterator.GetCountOperand(2); VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(3)); const Operator* call = javascript()->CallFunction( arg_count + 2, language_mode(), feedback, receiver_hint); Node* value = ProcessCallArguments(call, callee, receiver, arg_count + 2); environment()->BindAccumulator(value, &states); } void BytecodeGraphBuilder::VisitCall( const interpreter::BytecodeArrayIterator& iterator) { BuildCall(iterator); } void BytecodeGraphBuilder::VisitCallWide( const interpreter::BytecodeArrayIterator& iterator) { BuildCall(iterator); } void BytecodeGraphBuilder::VisitCallJSRuntime( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* callee = BuildLoadNativeContextField(iterator.GetIndexOperand(0)); interpreter::Register receiver = iterator.GetRegisterOperand(1); size_t arg_count = iterator.GetCountOperand(2); // Create node to perform the JS runtime call. const Operator* call = javascript()->CallFunction(arg_count + 2, language_mode()); Node* value = ProcessCallArguments(call, callee, receiver, arg_count + 2); environment()->BindAccumulator(value, &states); } Node* BytecodeGraphBuilder::ProcessCallRuntimeArguments( const Operator* call_runtime_op, interpreter::Register first_arg, size_t arity) { Node** all = info()->zone()->NewArray(arity); int first_arg_index = first_arg.index(); for (int i = 0; i < static_cast(arity); ++i) { all[i] = environment()->LookupRegister( interpreter::Register(first_arg_index + i)); } Node* value = MakeNode(call_runtime_op, static_cast(arity), all, false); return value; } void BytecodeGraphBuilder::VisitCallRuntime( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Runtime::FunctionId functionId = static_cast(iterator.GetIndexOperand(0)); interpreter::Register first_arg = iterator.GetRegisterOperand(1); size_t arg_count = iterator.GetCountOperand(2); // Create node to perform the runtime call. const Operator* call = javascript()->CallRuntime(functionId, arg_count); Node* value = ProcessCallRuntimeArguments(call, first_arg, arg_count); environment()->BindAccumulator(value, &states); } void BytecodeGraphBuilder::VisitCallRuntimeForPair( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Runtime::FunctionId functionId = static_cast(iterator.GetIndexOperand(0)); interpreter::Register first_arg = iterator.GetRegisterOperand(1); size_t arg_count = iterator.GetCountOperand(2); interpreter::Register first_return = iterator.GetRegisterOperand(3); // Create node to perform the runtime call. const Operator* call = javascript()->CallRuntime(functionId, arg_count); Node* return_pair = ProcessCallRuntimeArguments(call, first_arg, arg_count); environment()->BindRegistersToProjections(first_return, return_pair, &states); } Node* BytecodeGraphBuilder::ProcessCallNewArguments( const Operator* call_new_op, interpreter::Register callee, interpreter::Register first_arg, size_t arity) { Node** all = info()->zone()->NewArray(arity); all[0] = environment()->LookupRegister(callee); int first_arg_index = first_arg.index(); for (int i = 1; i < static_cast(arity) - 1; ++i) { all[i] = environment()->LookupRegister( interpreter::Register(first_arg_index + i - 1)); } // Original constructor is the same as the callee. all[arity - 1] = environment()->LookupRegister(callee); Node* value = MakeNode(call_new_op, static_cast(arity), all, false); return value; } void BytecodeGraphBuilder::VisitNew( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); interpreter::Register callee = iterator.GetRegisterOperand(0); interpreter::Register first_arg = iterator.GetRegisterOperand(1); size_t arg_count = iterator.GetCountOperand(2); // TODO(turbofan): Pass the feedback here. const Operator* call = javascript()->CallConstruct( static_cast(arg_count) + 2, VectorSlotPair()); Node* value = ProcessCallNewArguments(call, callee, first_arg, arg_count + 2); environment()->BindAccumulator(value, &states); } void BytecodeGraphBuilder::VisitThrow( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* value = environment()->LookupAccumulator(); // TODO(mythria): Change to Runtime::kThrow when we have deoptimization // information support in the interpreter. NewNode(javascript()->CallRuntime(Runtime::kReThrow, 1), value); Node* control = NewNode(common()->Throw(), value); environment()->RecordAfterState(control, &states); UpdateControlDependencyToLeaveFunction(control); } void BytecodeGraphBuilder::BuildBinaryOp( const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* right = environment()->LookupAccumulator(); Node* node = NewNode(js_op, left, right); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitAdd( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->Add(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitSub( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->Subtract(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitMul( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->Multiply(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitDiv( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->Divide(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitMod( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->Modulus(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitBitwiseOr( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->BitwiseOr(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitBitwiseXor( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->BitwiseXor(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitBitwiseAnd( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->BitwiseAnd(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitShiftLeft( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->ShiftLeft(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitShiftRight( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->ShiftRight(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitShiftRightLogical( const interpreter::BytecodeArrayIterator& iterator) { BinaryOperationHints hints = BinaryOperationHints::Any(); BuildBinaryOp(javascript()->ShiftRightLogical(language_mode(), hints), iterator); } void BytecodeGraphBuilder::VisitInc( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); const Operator* js_op = javascript()->Add(language_mode(), BinaryOperationHints::Any()); Node* node = NewNode(js_op, environment()->LookupAccumulator(), jsgraph()->OneConstant()); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitDec( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); const Operator* js_op = javascript()->Subtract(language_mode(), BinaryOperationHints::Any()); Node* node = NewNode(js_op, environment()->LookupAccumulator(), jsgraph()->OneConstant()); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitLogicalNot( const interpreter::BytecodeArrayIterator& iterator) { Node* value = NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), environment()->LookupAccumulator()); Node* node = NewNode(common()->Select(MachineRepresentation::kTagged), value, jsgraph()->FalseConstant(), jsgraph()->TrueConstant()); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::VisitTypeOf( const interpreter::BytecodeArrayIterator& iterator) { Node* node = NewNode(javascript()->TypeOf(), environment()->LookupAccumulator()); environment()->BindAccumulator(node); } void BytecodeGraphBuilder::BuildDelete( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* key = environment()->LookupAccumulator(); Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* node = NewNode(javascript()->DeleteProperty(language_mode()), object, key); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitDeletePropertyStrict( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_strict(language_mode())); BuildDelete(iterator); } void BytecodeGraphBuilder::VisitDeletePropertySloppy( const interpreter::BytecodeArrayIterator& iterator) { DCHECK(is_sloppy(language_mode())); BuildDelete(iterator); } void BytecodeGraphBuilder::VisitDeleteLookupSlot( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* name = environment()->LookupAccumulator(); const Operator* op = javascript()->CallRuntime(Runtime::kDeleteLookupSlot, 2); Node* result = NewNode(op, environment()->Context(), name); environment()->BindAccumulator(result, &states); } void BytecodeGraphBuilder::BuildCompareOp( const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* right = environment()->LookupAccumulator(); Node* node = NewNode(js_op, left, right); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitTestEqual( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->Equal(), iterator); } void BytecodeGraphBuilder::VisitTestNotEqual( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->NotEqual(), iterator); } void BytecodeGraphBuilder::VisitTestEqualStrict( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->StrictEqual(), iterator); } void BytecodeGraphBuilder::VisitTestNotEqualStrict( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->StrictNotEqual(), iterator); } void BytecodeGraphBuilder::VisitTestLessThan( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->LessThan(language_mode()), iterator); } void BytecodeGraphBuilder::VisitTestGreaterThan( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->GreaterThan(language_mode()), iterator); } void BytecodeGraphBuilder::VisitTestLessThanOrEqual( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->LessThanOrEqual(language_mode()), iterator); } void BytecodeGraphBuilder::VisitTestGreaterThanOrEqual( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->GreaterThanOrEqual(language_mode()), iterator); } void BytecodeGraphBuilder::VisitTestIn( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->HasProperty(), iterator); } void BytecodeGraphBuilder::VisitTestInstanceOf( const interpreter::BytecodeArrayIterator& iterator) { BuildCompareOp(javascript()->InstanceOf(), iterator); } void BytecodeGraphBuilder::BuildCastOperator( const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* node = NewNode(js_op, environment()->LookupAccumulator()); environment()->BindAccumulator(node, &states); } void BytecodeGraphBuilder::VisitToName( const interpreter::BytecodeArrayIterator& iterator) { BuildCastOperator(javascript()->ToName(), iterator); } void BytecodeGraphBuilder::VisitToObject( const interpreter::BytecodeArrayIterator& iterator) { BuildCastOperator(javascript()->ToObject(), iterator); } void BytecodeGraphBuilder::VisitToNumber( const interpreter::BytecodeArrayIterator& iterator) { BuildCastOperator(javascript()->ToNumber(), iterator); } void BytecodeGraphBuilder::VisitJump( const interpreter::BytecodeArrayIterator& iterator) { BuildJump(); } void BytecodeGraphBuilder::VisitJumpConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJump(); } void BytecodeGraphBuilder::VisitJumpConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJump(); } void BytecodeGraphBuilder::VisitJumpIfTrue( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->TrueConstant()); } void BytecodeGraphBuilder::VisitJumpIfTrueConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->TrueConstant()); } void BytecodeGraphBuilder::VisitJumpIfTrueConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->TrueConstant()); } void BytecodeGraphBuilder::VisitJumpIfFalse( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->FalseConstant()); } void BytecodeGraphBuilder::VisitJumpIfFalseConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->FalseConstant()); } void BytecodeGraphBuilder::VisitJumpIfFalseConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->FalseConstant()); } void BytecodeGraphBuilder::VisitJumpIfToBooleanTrue( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfToBooleanEqual(jsgraph()->TrueConstant()); } void BytecodeGraphBuilder::VisitJumpIfToBooleanTrueConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfToBooleanEqual(jsgraph()->TrueConstant()); } void BytecodeGraphBuilder::VisitJumpIfToBooleanTrueConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfToBooleanEqual(jsgraph()->TrueConstant()); } void BytecodeGraphBuilder::VisitJumpIfToBooleanFalse( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfToBooleanEqual(jsgraph()->FalseConstant()); } void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfToBooleanEqual(jsgraph()->FalseConstant()); } void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfToBooleanEqual(jsgraph()->FalseConstant()); } void BytecodeGraphBuilder::VisitJumpIfNull( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->NullConstant()); } void BytecodeGraphBuilder::VisitJumpIfNullConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->NullConstant()); } void BytecodeGraphBuilder::VisitJumpIfNullConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->NullConstant()); } void BytecodeGraphBuilder::VisitJumpIfUndefined( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->UndefinedConstant()); } void BytecodeGraphBuilder::VisitJumpIfUndefinedConstant( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->UndefinedConstant()); } void BytecodeGraphBuilder::VisitJumpIfUndefinedConstantWide( const interpreter::BytecodeArrayIterator& iterator) { BuildJumpIfEqual(jsgraph()->UndefinedConstant()); } void BytecodeGraphBuilder::VisitReturn( const interpreter::BytecodeArrayIterator& iterator) { Node* control = NewNode(common()->Return(), environment()->LookupAccumulator()); UpdateControlDependencyToLeaveFunction(control); set_environment(nullptr); } void BytecodeGraphBuilder::VisitForInPrepare( const interpreter::BytecodeArrayIterator& iterator) { Node* prepare = nullptr; { FrameStateBeforeAndAfter states(this, iterator); Node* receiver = environment()->LookupAccumulator(); prepare = NewNode(javascript()->ForInPrepare(), receiver); environment()->RecordAfterState(prepare, &states); } // Project cache_type, cache_array, cache_length into register // operands 1, 2, 3. for (int i = 0; i < 3; i++) { environment()->BindRegister(iterator.GetRegisterOperand(i), NewNode(common()->Projection(i), prepare)); } } void BytecodeGraphBuilder::VisitForInDone( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* index = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* cache_length = environment()->LookupRegister(iterator.GetRegisterOperand(1)); Node* exit_cond = NewNode(javascript()->ForInDone(), index, cache_length); environment()->BindAccumulator(exit_cond, &states); } void BytecodeGraphBuilder::VisitForInNext( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* receiver = environment()->LookupRegister(iterator.GetRegisterOperand(0)); Node* cache_type = environment()->LookupRegister(iterator.GetRegisterOperand(1)); Node* cache_array = environment()->LookupRegister(iterator.GetRegisterOperand(2)); Node* index = environment()->LookupRegister(iterator.GetRegisterOperand(3)); Node* value = NewNode(javascript()->ForInNext(), receiver, cache_array, cache_type, index); environment()->BindAccumulator(value, &states); } void BytecodeGraphBuilder::VisitForInStep( const interpreter::BytecodeArrayIterator& iterator) { FrameStateBeforeAndAfter states(this, iterator); Node* index = environment()->LookupRegister(iterator.GetRegisterOperand(0)); index = NewNode(javascript()->ForInStep(), index); environment()->BindAccumulator(index, &states); } void BytecodeGraphBuilder::MergeEnvironmentsOfBackwardBranches( int source_offset, int target_offset) { DCHECK_GE(source_offset, target_offset); const ZoneVector* branch_sites = branch_analysis()->BackwardBranchesTargetting(target_offset); if (branch_sites->back() == source_offset) { // The set of back branches is complete, merge them. DCHECK_GE(branch_sites->at(0), target_offset); Environment* merged = merge_environments_[branch_sites->at(0)]; for (size_t i = 1; i < branch_sites->size(); i++) { DCHECK_GE(branch_sites->at(i), target_offset); merged->Merge(merge_environments_[branch_sites->at(i)]); } // And now merge with loop header environment created when loop // header was visited. loop_header_environments_[target_offset]->Merge(merged); } } void BytecodeGraphBuilder::MergeEnvironmentsOfForwardBranches( int source_offset) { if (branch_analysis()->forward_branches_target(source_offset)) { // Merge environments of branches that reach this bytecode. auto branch_sites = branch_analysis()->ForwardBranchesTargetting(source_offset); DCHECK_LT(branch_sites->at(0), source_offset); Environment* merged = merge_environments_[branch_sites->at(0)]; for (size_t i = 1; i < branch_sites->size(); i++) { DCHECK_LT(branch_sites->at(i), source_offset); merged->Merge(merge_environments_[branch_sites->at(i)]); } if (environment()) { merged->Merge(environment()); } set_environment(merged); } } void BytecodeGraphBuilder::BuildLoopHeaderForBackwardBranches( int source_offset) { if (branch_analysis()->backward_branches_target(source_offset)) { // Add loop header and store a copy so we can connect merged back // edge inputs to the loop header. loop_header_environments_[source_offset] = environment()->CopyForLoop(); } } void BytecodeGraphBuilder::BuildJump(int source_offset, int target_offset) { DCHECK_NULL(merge_environments_[source_offset]); merge_environments_[source_offset] = environment(); if (source_offset >= target_offset) { MergeEnvironmentsOfBackwardBranches(source_offset, target_offset); } set_environment(nullptr); } void BytecodeGraphBuilder::BuildJump() { int source_offset = bytecode_iterator()->current_offset(); int target_offset = bytecode_iterator()->GetJumpTargetOffset(); BuildJump(source_offset, target_offset); } void BytecodeGraphBuilder::BuildConditionalJump(Node* condition) { int source_offset = bytecode_iterator()->current_offset(); NewBranch(condition); Environment* if_false_environment = environment()->CopyForConditional(); NewIfTrue(); BuildJump(source_offset, bytecode_iterator()->GetJumpTargetOffset()); set_environment(if_false_environment); NewIfFalse(); } void BytecodeGraphBuilder::BuildJumpIfEqual(Node* comperand) { Node* accumulator = environment()->LookupAccumulator(); Node* condition = NewNode(javascript()->StrictEqual(), accumulator, comperand); BuildConditionalJump(condition); } void BytecodeGraphBuilder::BuildJumpIfToBooleanEqual(Node* comperand) { Node* accumulator = environment()->LookupAccumulator(); Node* to_boolean = NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator); Node* condition = NewNode(javascript()->StrictEqual(), to_boolean, comperand); BuildConditionalJump(condition); } Node** BytecodeGraphBuilder::EnsureInputBufferSize(int size) { if (size > input_buffer_size_) { size = size + kInputBufferSizeIncrement + input_buffer_size_; input_buffer_ = local_zone()->NewArray(size); input_buffer_size_ = size; } return input_buffer_; } void BytecodeGraphBuilder::PrepareEntryFrameState(Node* node) { DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op())); DCHECK_EQ(IrOpcode::kDead, NodeProperties::GetFrameStateInput(node, 0)->opcode()); NodeProperties::ReplaceFrameStateInput( node, 0, environment()->Checkpoint(BailoutId(0), OutputFrameStateCombine::Ignore())); } Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count, Node** value_inputs, bool incomplete) { DCHECK_EQ(op->ValueInputCount(), value_input_count); bool has_context = OperatorProperties::HasContextInput(op); int frame_state_count = OperatorProperties::GetFrameStateInputCount(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 && frame_state_count == 0 && !has_control && !has_effect) { result = graph()->NewNode(op, value_input_count, value_inputs, incomplete); } else { int input_count_with_deps = value_input_count; if (has_context) ++input_count_with_deps; input_count_with_deps += frame_state_count; 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(); } for (int i = 0; i < frame_state_count; i++) { // 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. *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); if (!environment()->IsMarkedAsUnreachable()) { // Update the current control dependency for control-producing nodes. if (NodeProperties::IsControl(result)) { environment()->UpdateControlDependency(result); } // Update the current effect dependency for effect-producing nodes. if (result->op()->EffectOutputCount() > 0) { environment()->UpdateEffectDependency(result); } // Add implicit success continuation for throwing nodes. if (!result->op()->HasProperty(Operator::kNoThrow)) { const Operator* if_success = common()->IfSuccess(); Node* on_success = graph()->NewNode(if_success, result); environment_->UpdateControlDependency(on_success); } } } 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::UpdateControlDependencyToLeaveFunction(Node* exit) { if (environment()->IsMarkedAsUnreachable()) return; environment()->MarkAsUnreachable(); exit_controls_.push_back(exit); } } // namespace compiler } // namespace internal } // namespace v8