Upgrade V8 to version 4.9.385.28
https://chromium.googlesource.com/v8/v8/+/4.9.385.28
FPIIM-449
Change-Id: I4b2e74289d4bf3667f2f3dc8aa2e541f63e26eb4
diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc
index cde5e71..c70dfbf 100644
--- a/src/compiler/ast-graph-builder.cc
+++ b/src/compiler/ast-graph-builder.cc
@@ -4,38 +4,481 @@
#include "src/compiler/ast-graph-builder.h"
+#include "src/ast/scopes.h"
#include "src/compiler.h"
#include "src/compiler/ast-loop-assignment-analyzer.h"
#include "src/compiler/control-builders.h"
+#include "src/compiler/linkage.h"
+#include "src/compiler/liveness-analyzer.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h"
-#include "src/compiler/node-properties-inl.h"
#include "src/compiler/node-properties.h"
-#include "src/full-codegen.h"
-#include "src/parser.h"
-#include "src/scopes.h"
+#include "src/compiler/operator-properties.h"
+#include "src/compiler/state-values-utils.h"
+#include "src/compiler/type-hint-analyzer.h"
+#include "src/parsing/parser.h"
namespace v8 {
namespace internal {
namespace compiler {
+
+// Each expression in the AST is evaluated in a specific context. This context
+// decides how the evaluation result is passed up the visitor.
+class AstGraphBuilder::AstContext BASE_EMBEDDED {
+ public:
+ bool IsEffect() const { return kind_ == Expression::kEffect; }
+ bool IsValue() const { return kind_ == Expression::kValue; }
+ bool IsTest() const { return kind_ == Expression::kTest; }
+
+ // Determines how to combine the frame state with the value
+ // that is about to be plugged into this AstContext.
+ OutputFrameStateCombine GetStateCombine() {
+ return IsEffect() ? OutputFrameStateCombine::Ignore()
+ : OutputFrameStateCombine::Push();
+ }
+
+ // Plug a node into this expression context. Call this function in tail
+ // position in the Visit functions for expressions.
+ virtual void ProduceValue(Node* value) = 0;
+
+ // Unplugs a node from this expression context. Call this to retrieve the
+ // result of another Visit function that already plugged the context.
+ virtual Node* ConsumeValue() = 0;
+
+ // Shortcut for "context->ProduceValue(context->ConsumeValue())".
+ void ReplaceValue() { ProduceValue(ConsumeValue()); }
+
+ protected:
+ AstContext(AstGraphBuilder* owner, Expression::Context kind);
+ virtual ~AstContext();
+
+ AstGraphBuilder* owner() const { return owner_; }
+ Environment* environment() const { return owner_->environment(); }
+
+// We want to be able to assert, in a context-specific way, that the stack
+// height makes sense when the context is filled.
+#ifdef DEBUG
+ int original_height_;
+#endif
+
+ private:
+ Expression::Context kind_;
+ AstGraphBuilder* owner_;
+ AstContext* outer_;
+};
+
+
+// Context to evaluate expression for its side effects only.
+class AstGraphBuilder::AstEffectContext final : public AstContext {
+ public:
+ explicit AstEffectContext(AstGraphBuilder* owner)
+ : AstContext(owner, Expression::kEffect) {}
+ ~AstEffectContext() final;
+ void ProduceValue(Node* value) final;
+ Node* ConsumeValue() final;
+};
+
+
+// Context to evaluate expression for its value (and side effects).
+class AstGraphBuilder::AstValueContext final : public AstContext {
+ public:
+ explicit AstValueContext(AstGraphBuilder* owner)
+ : AstContext(owner, Expression::kValue) {}
+ ~AstValueContext() final;
+ void ProduceValue(Node* value) final;
+ Node* ConsumeValue() final;
+};
+
+
+// Context to evaluate expression for a condition value (and side effects).
+class AstGraphBuilder::AstTestContext final : public AstContext {
+ public:
+ AstTestContext(AstGraphBuilder* owner, TypeFeedbackId feedback_id)
+ : AstContext(owner, Expression::kTest), feedback_id_(feedback_id) {}
+ ~AstTestContext() final;
+ void ProduceValue(Node* value) final;
+ Node* ConsumeValue() final;
+
+ private:
+ TypeFeedbackId const feedback_id_;
+};
+
+
+// Scoped class tracking context objects created by the visitor. Represents
+// mutations of the context chain within the function body and allows to
+// change the current {scope} and {context} during visitation.
+class AstGraphBuilder::ContextScope BASE_EMBEDDED {
+ public:
+ ContextScope(AstGraphBuilder* builder, Scope* scope, Node* context)
+ : builder_(builder),
+ outer_(builder->execution_context()),
+ scope_(scope),
+ depth_(builder_->environment()->context_chain_length()) {
+ builder_->environment()->PushContext(context); // Push.
+ builder_->set_execution_context(this);
+ }
+
+ ~ContextScope() {
+ builder_->set_execution_context(outer_); // Pop.
+ builder_->environment()->PopContext();
+ CHECK_EQ(depth_, builder_->environment()->context_chain_length());
+ }
+
+ // Current scope during visitation.
+ Scope* scope() const { return scope_; }
+
+ private:
+ AstGraphBuilder* builder_;
+ ContextScope* outer_;
+ Scope* scope_;
+ int depth_;
+};
+
+
+// Scoped class tracking control statements entered by the visitor. There are
+// different types of statements participating in this stack to properly track
+// local as well as non-local control flow:
+// - IterationStatement : Allows proper 'break' and 'continue' behavior.
+// - BreakableStatement : Allows 'break' from block and switch statements.
+// - TryCatchStatement : Intercepts 'throw' and implicit exceptional edges.
+// - TryFinallyStatement: Intercepts 'break', 'continue', 'throw' and 'return'.
+class AstGraphBuilder::ControlScope BASE_EMBEDDED {
+ public:
+ explicit ControlScope(AstGraphBuilder* builder)
+ : builder_(builder),
+ outer_(builder->execution_control()),
+ context_length_(builder->environment()->context_chain_length()),
+ stack_height_(builder->environment()->stack_height()) {
+ builder_->set_execution_control(this); // Push.
+ }
+
+ virtual ~ControlScope() {
+ builder_->set_execution_control(outer_); // Pop.
+ }
+
+ // Either 'break' or 'continue' to the target statement.
+ void BreakTo(BreakableStatement* target);
+ void ContinueTo(BreakableStatement* target);
+
+ // Either 'return' or 'throw' the given value.
+ void ReturnValue(Node* return_value);
+ void ThrowValue(Node* exception_value);
+
+ class DeferredCommands;
+
+ protected:
+ enum Command { CMD_BREAK, CMD_CONTINUE, CMD_RETURN, CMD_THROW };
+
+ // Performs one of the above commands on this stack of control scopes. This
+ // walks through the stack giving each scope a chance to execute or defer the
+ // given command by overriding the {Execute} method appropriately. Note that
+ // this also drops extra operands from the environment for each skipped scope.
+ void PerformCommand(Command cmd, Statement* target, Node* value);
+
+ // Interface to execute a given command in this scope. Returning {true} here
+ // indicates successful execution whereas {false} requests to skip scope.
+ virtual bool Execute(Command cmd, Statement* target, Node* value) {
+ // For function-level control.
+ switch (cmd) {
+ case CMD_THROW:
+ builder()->BuildThrow(value);
+ return true;
+ case CMD_RETURN:
+ builder()->BuildReturn(value);
+ return true;
+ case CMD_BREAK:
+ case CMD_CONTINUE:
+ break;
+ }
+ return false;
+ }
+
+ Environment* environment() { return builder_->environment(); }
+ AstGraphBuilder* builder() const { return builder_; }
+ int context_length() const { return context_length_; }
+ int stack_height() const { return stack_height_; }
+
+ private:
+ AstGraphBuilder* builder_;
+ ControlScope* outer_;
+ int context_length_;
+ int stack_height_;
+};
+
+
+// Helper class for a try-finally control scope. It can record intercepted
+// control-flow commands that cause entry into a finally-block, and re-apply
+// them after again leaving that block. Special tokens are used to identify
+// paths going through the finally-block to dispatch after leaving the block.
+class AstGraphBuilder::ControlScope::DeferredCommands : public ZoneObject {
+ public:
+ explicit DeferredCommands(AstGraphBuilder* owner)
+ : owner_(owner), deferred_(owner->local_zone()) {}
+
+ // One recorded control-flow command.
+ struct Entry {
+ Command command; // The command type being applied on this path.
+ Statement* statement; // The target statement for the command or {nullptr}.
+ Node* token; // A token identifying this particular path.
+ };
+
+ // Records a control-flow command while entering the finally-block. This also
+ // generates a new dispatch token that identifies one particular path.
+ Node* RecordCommand(Command cmd, Statement* stmt, Node* value) {
+ Node* token = NewPathTokenForDeferredCommand();
+ deferred_.push_back({cmd, stmt, token});
+ return token;
+ }
+
+ // Returns the dispatch token to be used to identify the implicit fall-through
+ // path at the end of a try-block into the corresponding finally-block.
+ Node* GetFallThroughToken() { return NewPathTokenForImplicitFallThrough(); }
+
+ // Applies all recorded control-flow commands after the finally-block again.
+ // This generates a dynamic dispatch on the token from the entry point.
+ void ApplyDeferredCommands(Node* token, Node* value) {
+ SwitchBuilder dispatch(owner_, static_cast<int>(deferred_.size()));
+ dispatch.BeginSwitch();
+ for (size_t i = 0; i < deferred_.size(); ++i) {
+ Node* condition = NewPathDispatchCondition(token, deferred_[i].token);
+ dispatch.BeginLabel(static_cast<int>(i), condition);
+ dispatch.EndLabel();
+ }
+ for (size_t i = 0; i < deferred_.size(); ++i) {
+ dispatch.BeginCase(static_cast<int>(i));
+ owner_->execution_control()->PerformCommand(
+ deferred_[i].command, deferred_[i].statement, value);
+ dispatch.EndCase();
+ }
+ dispatch.EndSwitch();
+ }
+
+ protected:
+ Node* NewPathTokenForDeferredCommand() {
+ return owner_->jsgraph()->Constant(static_cast<int>(deferred_.size()));
+ }
+ Node* NewPathTokenForImplicitFallThrough() {
+ return owner_->jsgraph()->Constant(-1);
+ }
+ Node* NewPathDispatchCondition(Node* t1, Node* t2) {
+ // TODO(mstarzinger): This should be machine()->WordEqual(), but our Phi
+ // nodes all have kRepTagged|kTypeAny, which causes representation mismatch.
+ return owner_->NewNode(owner_->javascript()->StrictEqual(), t1, t2);
+ }
+
+ private:
+ AstGraphBuilder* owner_;
+ ZoneVector<Entry> deferred_;
+};
+
+
+// Control scope implementation for a BreakableStatement.
+class AstGraphBuilder::ControlScopeForBreakable : public ControlScope {
+ public:
+ ControlScopeForBreakable(AstGraphBuilder* owner, BreakableStatement* target,
+ ControlBuilder* control)
+ : ControlScope(owner), target_(target), control_(control) {}
+
+ protected:
+ bool Execute(Command cmd, Statement* target, Node* value) override {
+ if (target != target_) return false; // We are not the command target.
+ switch (cmd) {
+ case CMD_BREAK:
+ control_->Break();
+ return true;
+ case CMD_CONTINUE:
+ case CMD_THROW:
+ case CMD_RETURN:
+ break;
+ }
+ return false;
+ }
+
+ private:
+ BreakableStatement* target_;
+ ControlBuilder* control_;
+};
+
+
+// Control scope implementation for an IterationStatement.
+class AstGraphBuilder::ControlScopeForIteration : public ControlScope {
+ public:
+ ControlScopeForIteration(AstGraphBuilder* owner, IterationStatement* target,
+ LoopBuilder* control)
+ : ControlScope(owner), target_(target), control_(control) {}
+
+ protected:
+ bool Execute(Command cmd, Statement* target, Node* value) override {
+ if (target != target_) return false; // We are not the command target.
+ switch (cmd) {
+ case CMD_BREAK:
+ control_->Break();
+ return true;
+ case CMD_CONTINUE:
+ control_->Continue();
+ return true;
+ case CMD_THROW:
+ case CMD_RETURN:
+ break;
+ }
+ return false;
+ }
+
+ private:
+ BreakableStatement* target_;
+ LoopBuilder* control_;
+};
+
+
+// Control scope implementation for a TryCatchStatement.
+class AstGraphBuilder::ControlScopeForCatch : public ControlScope {
+ public:
+ ControlScopeForCatch(AstGraphBuilder* owner, TryCatchBuilder* control)
+ : ControlScope(owner), control_(control) {
+ builder()->try_nesting_level_++; // Increment nesting.
+ builder()->try_catch_nesting_level_++;
+ }
+ ~ControlScopeForCatch() {
+ builder()->try_nesting_level_--; // Decrement nesting.
+ builder()->try_catch_nesting_level_--;
+ }
+
+ protected:
+ bool Execute(Command cmd, Statement* target, Node* value) override {
+ switch (cmd) {
+ case CMD_THROW:
+ control_->Throw(value);
+ return true;
+ case CMD_BREAK:
+ case CMD_CONTINUE:
+ case CMD_RETURN:
+ break;
+ }
+ return false;
+ }
+
+ private:
+ TryCatchBuilder* control_;
+};
+
+
+// Control scope implementation for a TryFinallyStatement.
+class AstGraphBuilder::ControlScopeForFinally : public ControlScope {
+ public:
+ ControlScopeForFinally(AstGraphBuilder* owner, DeferredCommands* commands,
+ TryFinallyBuilder* control)
+ : ControlScope(owner), commands_(commands), control_(control) {
+ builder()->try_nesting_level_++; // Increment nesting.
+ }
+ ~ControlScopeForFinally() {
+ builder()->try_nesting_level_--; // Decrement nesting.
+ }
+
+ protected:
+ bool Execute(Command cmd, Statement* target, Node* value) override {
+ Node* token = commands_->RecordCommand(cmd, target, value);
+ control_->LeaveTry(token, value);
+ return true;
+ }
+
+ private:
+ DeferredCommands* commands_;
+ TryFinallyBuilder* control_;
+};
+
+
+// Helper for generating before and after frame states.
+class AstGraphBuilder::FrameStateBeforeAndAfter {
+ public:
+ FrameStateBeforeAndAfter(AstGraphBuilder* builder, BailoutId id_before)
+ : builder_(builder), frame_state_before_(nullptr) {
+ frame_state_before_ = id_before == BailoutId::None()
+ ? builder_->jsgraph()->EmptyFrameState()
+ : builder_->environment()->Checkpoint(id_before);
+ }
+
+ void AddToNode(
+ Node* node, BailoutId id_after,
+ OutputFrameStateCombine combine = OutputFrameStateCombine::Ignore()) {
+ 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 =
+ id_after == BailoutId::None()
+ ? builder_->jsgraph()->EmptyFrameState()
+ : 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_);
+ }
+ }
+
+ private:
+ AstGraphBuilder* builder_;
+ Node* frame_state_before_;
+};
+
+
AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
- JSGraph* jsgraph, LoopAssignmentAnalysis* loop)
- : StructuredGraphBuilder(local_zone, jsgraph->graph(), jsgraph->common()),
+ JSGraph* jsgraph, LoopAssignmentAnalysis* loop,
+ TypeHintAnalysis* type_hint_analysis)
+ : isolate_(info->isolate()),
+ local_zone_(local_zone),
info_(info),
jsgraph_(jsgraph),
+ environment_(nullptr),
+ ast_context_(nullptr),
globals_(0, local_zone),
- breakable_(NULL),
- execution_context_(NULL),
- loop_assignment_analysis_(loop) {
- InitializeAstVisitor(local_zone);
+ execution_control_(nullptr),
+ execution_context_(nullptr),
+ try_catch_nesting_level_(0),
+ try_nesting_level_(0),
+ input_buffer_size_(0),
+ input_buffer_(nullptr),
+ exit_controls_(local_zone),
+ loop_assignment_analysis_(loop),
+ type_hint_analysis_(type_hint_analysis),
+ state_values_cache_(jsgraph),
+ liveness_analyzer_(static_cast<size_t>(info->scope()->num_stack_slots()),
+ local_zone),
+ frame_state_function_info_(common()->CreateFrameStateFunctionInfo(
+ FrameStateType::kJavaScriptFunction, info->num_parameters() + 1,
+ info->scope()->num_stack_slots(), info->shared_info(),
+ CALL_MAINTAINS_NATIVE_CONTEXT)) {
+ InitializeAstVisitor(info->isolate());
+}
+
+
+Node* AstGraphBuilder::GetFunctionClosureForContext() {
+ Scope* closure_scope = current_scope()->ClosureScope();
+ if (closure_scope->is_script_scope() ||
+ closure_scope->is_module_scope()) {
+ // Contexts nested in the native context have a canonical empty function as
+ // their closure, not the anonymous closure containing the global code.
+ return BuildLoadNativeContextField(Context::CLOSURE_INDEX);
+ } else {
+ DCHECK(closure_scope->is_function_scope());
+ return GetFunctionClosure();
+ }
}
Node* AstGraphBuilder::GetFunctionClosure() {
if (!function_closure_.is_set()) {
- // Parameter -1 is special for the function closure
- const Operator* op = common()->Parameter(-1);
+ int index = Linkage::kJSCallClosureParamIndex;
+ const Operator* op = common()->Parameter(index, "%closure");
Node* node = NewNode(op, graph()->start());
function_closure_.set(node);
}
@@ -45,8 +488,9 @@
Node* AstGraphBuilder::GetFunctionContext() {
if (!function_context_.is_set()) {
- // Parameter (arity + 1) is special for the outer context of the function
- const Operator* op = common()->Parameter(info()->num_parameters() + 1);
+ int params = info()->num_parameters_including_this();
+ int index = Linkage::GetJSCallContextParamIndex(params);
+ const Operator* op = common()->Parameter(index, "%context");
Node* node = NewNode(op, graph()->start());
function_context_.set(node);
}
@@ -54,58 +498,117 @@
}
-bool AstGraphBuilder::CreateGraph() {
- Scope* scope = info()->scope();
- DCHECK(graph() != NULL);
+Node* AstGraphBuilder::GetNewTarget() {
+ if (!new_target_.is_set()) {
+ int params = info()->num_parameters_including_this();
+ 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();
+}
- // Set up the basic structure of the graph.
- int parameter_count = info()->num_parameters();
- graph()->SetStart(graph()->NewNode(common()->Start(parameter_count)));
+
+bool AstGraphBuilder::CreateGraph(bool stack_check) {
+ Scope* scope = info()->scope();
+ DCHECK_NOT_NULL(graph());
+
+ // 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 = info()->num_parameters_including_this() + 4;
+ graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count)));
// Initialize the top-level environment.
Environment env(this, scope, graph()->start());
set_environment(&env);
+ if (info()->is_osr()) {
+ // Use OSR normal entry as the start of the top-level environment.
+ // It will be replaced with {Dead} after typing and optimizations.
+ NewNode(common()->OsrNormalEntry());
+ }
+
// Initialize the incoming context.
- Node* outer_context = GetFunctionContext();
- set_current_context(outer_context);
+ ContextScope incoming(this, scope, GetFunctionContext());
- // Build receiver check for sloppy mode if necessary.
- // TODO(mstarzinger/verwaest): Should this be moved back into the CallIC?
- Node* original_receiver = env.Lookup(scope->receiver());
- Node* patched_receiver = BuildPatchReceiverToGlobalProxy(original_receiver);
- env.Bind(scope->receiver(), patched_receiver);
+ // Initialize control scope.
+ ControlScope control(this);
- // Build node to initialize local function context.
- Node* closure = GetFunctionClosure();
- Node* inner_context = BuildLocalFunctionContext(outer_context, closure);
+ // TODO(mstarzinger): For now we cannot assume that the {this} parameter is
+ // not {the_hole}, because for derived classes {this} has a TDZ and the
+ // JSConstructStubForDerived magically passes {the_hole} as a receiver.
+ if (scope->has_this_declaration() && scope->receiver()->is_const_mode()) {
+ env.RawParameterBind(0, jsgraph()->TheHoleConstant());
+ }
- // Push top-level function scope for the function body.
- ContextScope top_context(this, scope, inner_context);
+ // Build local context only if there are context allocated variables.
+ if (info()->num_heap_slots() > 0) {
+ // Push a new inner context scope for the current activation.
+ Node* inner_context = BuildLocalActivationContext(GetFunctionContext());
+ ContextScope top_context(this, scope, inner_context);
+ CreateGraphBody(stack_check);
+ } else {
+ // Simply use the outer function context in building the graph.
+ CreateGraphBody(stack_check);
+ }
+
+ // 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);
+
+ // Compute local variable liveness information and use it to relax
+ // frame states.
+ ClearNonLiveSlotsInFrameStates();
+
+ // Failures indicated by stack overflow.
+ return !HasStackOverflow();
+}
+
+
+void AstGraphBuilder::CreateGraphBody(bool stack_check) {
+ Scope* scope = info()->scope();
// Build the arguments object if it is used.
BuildArgumentsObject(scope->arguments());
+ // Build rest arguments array if it is used.
+ int rest_index;
+ Variable* rest_parameter = scope->rest_parameter(&rest_index);
+ BuildRestArgumentsArray(rest_parameter, rest_index);
+
+ // Build assignment to {.this_function} variable if it is used.
+ BuildThisFunctionVariable(scope->this_function_var());
+
+ // Build assignment to {new.target} variable if it is used.
+ BuildNewTargetVariable(scope->new_target_var());
+
// Emit tracing call if requested to do so.
if (FLAG_trace) {
NewNode(javascript()->CallRuntime(Runtime::kTraceEnter, 0));
}
- // Visit implicit declaration of the function name.
- if (scope->is_function_scope() && scope->function() != NULL) {
- VisitVariableDeclaration(scope->function());
+ // Visit illegal re-declaration and bail out if it exists.
+ if (scope->HasIllegalRedeclaration()) {
+ VisitForEffect(scope->GetIllegalRedeclaration());
+ return;
}
// Visit declarations within the function scope.
VisitDeclarations(scope->declarations());
// Build a stack-check before the body.
- Node* node = BuildStackCheck();
- PrepareFrameState(node, BailoutId::FunctionEntry());
+ if (stack_check) {
+ Node* node = NewNode(javascript()->StackCheck());
+ PrepareFrameState(node, BailoutId::FunctionEntry());
+ }
// Visit statements in the function body.
- VisitStatements(info()->function()->body());
- if (HasStackOverflow()) return false;
+ VisitStatements(info()->literal()->body());
// Emit tracing call if requested to do so.
if (FLAG_trace) {
@@ -115,60 +618,88 @@
}
// Return 'undefined' in case we can fall off the end.
- Node* control = NewNode(common()->Return(), jsgraph()->UndefinedConstant());
- UpdateControlDependencyToLeaveFunction(control);
-
- // Finish the basic structure of the graph.
- environment()->UpdateControlDependency(exit_control());
- graph()->SetEnd(NewNode(common()->End()));
-
- return true;
+ BuildReturn(jsgraph()->UndefinedConstant());
}
-// Left-hand side can only be a property, a global or a variable slot.
-enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+void AstGraphBuilder::ClearNonLiveSlotsInFrameStates() {
+ if (!FLAG_analyze_environment_liveness ||
+ !info()->is_deoptimization_enabled()) {
+ return;
+ }
-
-// Determine the left-hand side kind of an assignment.
-static LhsKind DetermineLhsKind(Expression* expr) {
- Property* property = expr->AsProperty();
- DCHECK(expr->IsValidReferenceExpression());
- LhsKind lhs_kind =
- (property == NULL) ? VARIABLE : (property->key()->IsPropertyName())
- ? NAMED_PROPERTY
- : KEYED_PROPERTY;
- return lhs_kind;
+ NonLiveFrameStateSlotReplacer replacer(
+ &state_values_cache_, jsgraph()->UndefinedConstant(),
+ liveness_analyzer()->local_count(), local_zone());
+ Variable* arguments = info()->scope()->arguments();
+ if (arguments != nullptr && arguments->IsStackAllocated()) {
+ replacer.MarkPermanentlyLive(arguments->index());
+ }
+ liveness_analyzer()->Run(&replacer);
+ if (FLAG_trace_environment_liveness) {
+ OFStream os(stdout);
+ liveness_analyzer()->Print(os);
+ }
}
-StructuredGraphBuilder::Environment* AstGraphBuilder::CopyEnvironment(
- StructuredGraphBuilder::Environment* env) {
- return new (zone()) Environment(*reinterpret_cast<Environment*>(env));
+// Gets the bailout id just before reading a variable proxy, but only for
+// unallocated variables.
+static BailoutId BeforeId(VariableProxy* proxy) {
+ return proxy->var()->IsUnallocatedOrGlobalSlot() ? proxy->BeforeId()
+ : BailoutId::None();
+}
+
+
+static const char* GetDebugParameterName(Zone* zone, Scope* scope, int index) {
+#if DEBUG
+ const AstRawString* name = scope->parameter(index)->raw_name();
+ if (name && name->length() > 0) {
+ char* data = zone->NewArray<char>(name->length() + 1);
+ data[name->length()] = 0;
+ memcpy(data, name->raw_data(), name->length());
+ return data;
+ }
+#endif
+ return nullptr;
}
AstGraphBuilder::Environment::Environment(AstGraphBuilder* builder,
Scope* scope,
Node* control_dependency)
- : StructuredGraphBuilder::Environment(builder, control_dependency),
+ : builder_(builder),
parameters_count_(scope->num_parameters() + 1),
locals_count_(scope->num_stack_slots()),
- parameters_node_(NULL),
- locals_node_(NULL),
- stack_node_(NULL) {
+ liveness_block_(IsLivenessAnalysisEnabled()
+ ? builder_->liveness_analyzer()->NewBlock()
+ : nullptr),
+ values_(builder_->local_zone()),
+ contexts_(builder_->local_zone()),
+ control_dependency_(control_dependency),
+ effect_dependency_(control_dependency),
+ parameters_node_(nullptr),
+ locals_node_(nullptr),
+ stack_node_(nullptr) {
DCHECK_EQ(scope->num_parameters() + 1, parameters_count());
// Bind the receiver variable.
- Node* receiver = builder->graph()->NewNode(common()->Parameter(0),
- builder->graph()->start());
- values()->push_back(receiver);
+ int param_num = 0;
+ if (builder->info()->is_this_defined()) {
+ const Operator* op = common()->Parameter(param_num++, "%this");
+ Node* receiver = builder->graph()->NewNode(op, builder->graph()->start());
+ values()->push_back(receiver);
+ } else {
+ values()->push_back(builder->jsgraph()->UndefinedConstant());
+ }
// Bind all parameter variables. The parameter indices are shifted by 1
- // (receiver is parameter index -1 but environment index 0).
+ // (receiver is variable index -1 but {Parameter} node index 0 and located at
+ // index 0 in the environment).
for (int i = 0; i < scope->num_parameters(); ++i) {
- Node* parameter = builder->graph()->NewNode(common()->Parameter(i + 1),
- builder->graph()->start());
+ const char* debug_name = GetDebugParameterName(graph()->zone(), scope, i);
+ const Operator* op = common()->Parameter(param_num++, debug_name);
+ Node* parameter = builder->graph()->NewNode(op, builder->graph()->start());
values()->push_back(parameter);
}
@@ -178,21 +709,127 @@
}
-AstGraphBuilder::Environment::Environment(const Environment& copy)
- : StructuredGraphBuilder::Environment(
- static_cast<StructuredGraphBuilder::Environment>(copy)),
- parameters_count_(copy.parameters_count_),
- locals_count_(copy.locals_count_),
- parameters_node_(copy.parameters_node_),
- locals_node_(copy.locals_node_),
- stack_node_(copy.stack_node_) {}
+AstGraphBuilder::Environment::Environment(AstGraphBuilder::Environment* copy,
+ LivenessAnalyzerBlock* liveness_block)
+ : builder_(copy->builder_),
+ parameters_count_(copy->parameters_count_),
+ locals_count_(copy->locals_count_),
+ liveness_block_(liveness_block),
+ values_(copy->zone()),
+ contexts_(copy->zone()),
+ control_dependency_(copy->control_dependency_),
+ effect_dependency_(copy->effect_dependency_),
+ parameters_node_(copy->parameters_node_),
+ locals_node_(copy->locals_node_),
+ stack_node_(copy->stack_node_) {
+ const size_t kStackEstimate = 7; // optimum from experimentation!
+ values_.reserve(copy->values_.size() + kStackEstimate);
+ values_.insert(values_.begin(), copy->values_.begin(), copy->values_.end());
+ contexts_.reserve(copy->contexts_.size());
+ contexts_.insert(contexts_.begin(), copy->contexts_.begin(),
+ copy->contexts_.end());
+}
+
+
+void AstGraphBuilder::Environment::Bind(Variable* variable, Node* node) {
+ DCHECK(variable->IsStackAllocated());
+ if (variable->IsParameter()) {
+ // The parameter indices are shifted by 1 (receiver is variable
+ // index -1 but located at index 0 in the environment).
+ values()->at(variable->index() + 1) = node;
+ } else {
+ DCHECK(variable->IsStackLocal());
+ values()->at(variable->index() + parameters_count_) = node;
+ DCHECK(IsLivenessBlockConsistent());
+ if (liveness_block() != nullptr) {
+ liveness_block()->Bind(variable->index());
+ }
+ }
+}
+
+
+Node* AstGraphBuilder::Environment::Lookup(Variable* variable) {
+ DCHECK(variable->IsStackAllocated());
+ if (variable->IsParameter()) {
+ // The parameter indices are shifted by 1 (receiver is variable
+ // index -1 but located at index 0 in the environment).
+ return values()->at(variable->index() + 1);
+ } else {
+ DCHECK(variable->IsStackLocal());
+ DCHECK(IsLivenessBlockConsistent());
+ if (liveness_block() != nullptr) {
+ liveness_block()->Lookup(variable->index());
+ }
+ return values()->at(variable->index() + parameters_count_);
+ }
+}
+
+
+void AstGraphBuilder::Environment::MarkAllLocalsLive() {
+ DCHECK(IsLivenessBlockConsistent());
+ if (liveness_block() != nullptr) {
+ for (int i = 0; i < locals_count_; i++) {
+ liveness_block()->Lookup(i);
+ }
+ }
+}
+
+
+void AstGraphBuilder::Environment::RawParameterBind(int index, Node* node) {
+ DCHECK_LT(index, parameters_count());
+ values()->at(index) = node;
+}
+
+
+Node* AstGraphBuilder::Environment::RawParameterLookup(int index) {
+ DCHECK_LT(index, parameters_count());
+ return values()->at(index);
+}
+
+
+AstGraphBuilder::Environment*
+AstGraphBuilder::Environment::CopyForConditional() {
+ LivenessAnalyzerBlock* copy_liveness_block = nullptr;
+ if (liveness_block() != nullptr) {
+ copy_liveness_block =
+ builder_->liveness_analyzer()->NewBlock(liveness_block());
+ liveness_block_ = builder_->liveness_analyzer()->NewBlock(liveness_block());
+ }
+ return new (zone()) Environment(this, copy_liveness_block);
+}
+
+
+AstGraphBuilder::Environment*
+AstGraphBuilder::Environment::CopyAsUnreachable() {
+ Environment* env = new (zone()) Environment(this, nullptr);
+ env->MarkAsUnreachable();
+ return env;
+}
+
+
+AstGraphBuilder::Environment*
+AstGraphBuilder::Environment::CopyAndShareLiveness() {
+ if (liveness_block() != nullptr) {
+ // Finish the current liveness block before copying.
+ liveness_block_ = builder_->liveness_analyzer()->NewBlock(liveness_block());
+ }
+ Environment* env = new (zone()) Environment(this, liveness_block());
+ return env;
+}
+
+
+AstGraphBuilder::Environment* AstGraphBuilder::Environment::CopyForLoop(
+ BitVector* assigned, bool is_osr) {
+ PrepareForLoop(assigned, is_osr);
+ return CopyAndShareLiveness();
+}
void AstGraphBuilder::Environment::UpdateStateValues(Node** state_values,
int offset, int count) {
bool should_update = false;
- Node** env_values = (count == 0) ? NULL : &values()->at(offset);
- if (*state_values == NULL || (*state_values)->InputCount() != count) {
+ Node** env_values = (count == 0) ? nullptr : &values()->at(offset);
+ if (*state_values == nullptr || (*state_values)->InputCount() != count) {
should_update = true;
} else {
DCHECK(static_cast<size_t>(offset + count) <= values()->size());
@@ -210,18 +847,50 @@
}
+void AstGraphBuilder::Environment::UpdateStateValuesWithCache(
+ Node** state_values, int offset, int count) {
+ Node** env_values = (count == 0) ? nullptr : &values()->at(offset);
+ *state_values = builder_->state_values_cache_.GetNodeForValues(
+ env_values, static_cast<size_t>(count));
+}
+
+
Node* AstGraphBuilder::Environment::Checkpoint(
BailoutId ast_id, OutputFrameStateCombine combine) {
+ if (!builder()->info()->is_deoptimization_enabled()) {
+ return builder()->jsgraph()->EmptyFrameState();
+ }
+
UpdateStateValues(¶meters_node_, 0, parameters_count());
- UpdateStateValues(&locals_node_, parameters_count(), locals_count());
+ UpdateStateValuesWithCache(&locals_node_, parameters_count(), locals_count());
UpdateStateValues(&stack_node_, parameters_count() + locals_count(),
stack_height());
- const Operator* op = common()->FrameState(JS_FRAME, ast_id, combine);
+ const Operator* op = common()->FrameState(
+ ast_id, combine, builder()->frame_state_function_info());
- return graph()->NewNode(op, parameters_node_, locals_node_, stack_node_,
- GetContext(),
- builder()->jsgraph()->UndefinedConstant());
+ Node* result = graph()->NewNode(op, parameters_node_, locals_node_,
+ stack_node_, builder()->current_context(),
+ builder()->GetFunctionClosure(),
+ builder()->graph()->start());
+
+ DCHECK(IsLivenessBlockConsistent());
+ if (liveness_block() != nullptr) {
+ liveness_block()->Checkpoint(result);
+ }
+ return result;
+}
+
+
+bool AstGraphBuilder::Environment::IsLivenessAnalysisEnabled() {
+ return FLAG_analyze_environment_liveness &&
+ builder()->info()->is_deoptimization_enabled();
+}
+
+
+bool AstGraphBuilder::Environment::IsLivenessBlockConsistent() {
+ return (!IsLivenessAnalysisEnabled() || IsMarkedAsUnreachable()) ==
+ (liveness_block() == nullptr);
}
@@ -266,11 +935,11 @@
void AstGraphBuilder::AstTestContext::ProduceValue(Node* value) {
- environment()->Push(owner()->BuildToBoolean(value));
+ environment()->Push(owner()->BuildToBoolean(value, feedback_id_));
}
-Node* AstGraphBuilder::AstEffectContext::ConsumeValue() { return NULL; }
+Node* AstGraphBuilder::AstEffectContext::ConsumeValue() { return nullptr; }
Node* AstGraphBuilder::AstValueContext::ConsumeValue() {
@@ -283,36 +952,68 @@
}
-AstGraphBuilder::BreakableScope* AstGraphBuilder::BreakableScope::FindBreakable(
- BreakableStatement* target) {
- BreakableScope* current = this;
- while (current != NULL && current->target_ != target) {
- owner_->environment()->Drop(current->drop_extra_);
- current = current->next_;
+Scope* AstGraphBuilder::current_scope() const {
+ return execution_context_->scope();
+}
+
+
+Node* AstGraphBuilder::current_context() const {
+ return environment()->Context();
+}
+
+
+void AstGraphBuilder::ControlScope::PerformCommand(Command command,
+ Statement* target,
+ Node* value) {
+ Environment* env = environment()->CopyAsUnreachable();
+ ControlScope* current = this;
+ while (current != nullptr) {
+ environment()->TrimStack(current->stack_height());
+ environment()->TrimContextChain(current->context_length());
+ if (current->Execute(command, target, value)) break;
+ current = current->outer_;
}
- DCHECK(current != NULL); // Always found (unless stack is malformed).
- return current;
+ builder()->set_environment(env);
+ DCHECK_NOT_NULL(current); // Always handled (unless stack is malformed).
}
-void AstGraphBuilder::BreakableScope::BreakTarget(BreakableStatement* stmt) {
- FindBreakable(stmt)->control_->Break();
+void AstGraphBuilder::ControlScope::BreakTo(BreakableStatement* stmt) {
+ PerformCommand(CMD_BREAK, stmt, builder()->jsgraph()->TheHoleConstant());
}
-void AstGraphBuilder::BreakableScope::ContinueTarget(BreakableStatement* stmt) {
- FindBreakable(stmt)->control_->Continue();
+void AstGraphBuilder::ControlScope::ContinueTo(BreakableStatement* stmt) {
+ PerformCommand(CMD_CONTINUE, stmt, builder()->jsgraph()->TheHoleConstant());
+}
+
+
+void AstGraphBuilder::ControlScope::ReturnValue(Node* return_value) {
+ PerformCommand(CMD_RETURN, nullptr, return_value);
+}
+
+
+void AstGraphBuilder::ControlScope::ThrowValue(Node* exception_value) {
+ PerformCommand(CMD_THROW, nullptr, exception_value);
}
void AstGraphBuilder::VisitForValueOrNull(Expression* expr) {
- if (expr == NULL) {
+ if (expr == nullptr) {
return environment()->Push(jsgraph()->NullConstant());
}
VisitForValue(expr);
}
+void AstGraphBuilder::VisitForValueOrTheHole(Expression* expr) {
+ if (expr == nullptr) {
+ return environment()->Push(jsgraph()->TheHoleConstant());
+ }
+ VisitForValue(expr);
+}
+
+
void AstGraphBuilder::VisitForValues(ZoneList<Expression*>* exprs) {
for (int i = 0; i < exprs->length(); ++i) {
VisitForValue(exprs->at(i));
@@ -341,7 +1042,7 @@
void AstGraphBuilder::VisitForTest(Expression* expr) {
- AstTestContext for_condition(this);
+ AstTestContext for_condition(this, expr->test_id());
if (!CheckStackOverflow()) {
expr->Accept(this);
} else {
@@ -365,7 +1066,8 @@
VariableMode mode = decl->mode();
bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET;
switch (variable->location()) {
- case Variable::UNALLOCATED: {
+ case VariableLocation::GLOBAL:
+ case VariableLocation::UNALLOCATED: {
Handle<Oddball> value = variable->binding_needs_init()
? isolate()->factory()->the_hole_value()
: isolate()->factory()->undefined_value();
@@ -373,21 +1075,21 @@
globals()->push_back(value);
break;
}
- case Variable::PARAMETER:
- case Variable::LOCAL:
+ case VariableLocation::PARAMETER:
+ case VariableLocation::LOCAL:
if (hole_init) {
Node* value = jsgraph()->TheHoleConstant();
environment()->Bind(variable, value);
}
break;
- case Variable::CONTEXT:
+ case VariableLocation::CONTEXT:
if (hole_init) {
Node* value = jsgraph()->TheHoleConstant();
const Operator* op = javascript()->StoreContext(0, variable->index());
NewNode(op, current_context(), value);
}
break;
- case Variable::LOOKUP:
+ case VariableLocation::LOOKUP:
UNIMPLEMENTED();
}
}
@@ -396,40 +1098,36 @@
void AstGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* decl) {
Variable* variable = decl->proxy()->var();
switch (variable->location()) {
- case Variable::UNALLOCATED: {
- Handle<SharedFunctionInfo> function =
- Compiler::BuildFunctionInfo(decl->fun(), info()->script(), info());
+ case VariableLocation::GLOBAL:
+ case VariableLocation::UNALLOCATED: {
+ Handle<SharedFunctionInfo> function = Compiler::GetSharedFunctionInfo(
+ decl->fun(), info()->script(), info());
// Check for stack-overflow exception.
if (function.is_null()) return SetStackOverflow();
globals()->push_back(variable->name());
globals()->push_back(function);
break;
}
- case Variable::PARAMETER:
- case Variable::LOCAL: {
+ case VariableLocation::PARAMETER:
+ case VariableLocation::LOCAL: {
VisitForValue(decl->fun());
Node* value = environment()->Pop();
environment()->Bind(variable, value);
break;
}
- case Variable::CONTEXT: {
+ case VariableLocation::CONTEXT: {
VisitForValue(decl->fun());
Node* value = environment()->Pop();
const Operator* op = javascript()->StoreContext(0, variable->index());
NewNode(op, current_context(), value);
break;
}
- case Variable::LOOKUP:
+ case VariableLocation::LOOKUP:
UNIMPLEMENTED();
}
}
-void AstGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) {
- UNREACHABLE();
-}
-
-
void AstGraphBuilder::VisitImportDeclaration(ImportDeclaration* decl) {
UNREACHABLE();
}
@@ -440,43 +1138,26 @@
}
-void AstGraphBuilder::VisitModuleLiteral(ModuleLiteral* modl) { UNREACHABLE(); }
-
-
-void AstGraphBuilder::VisitModuleVariable(ModuleVariable* modl) {
- UNREACHABLE();
-}
-
-
-void AstGraphBuilder::VisitModulePath(ModulePath* modl) { UNREACHABLE(); }
-
-
-void AstGraphBuilder::VisitModuleUrl(ModuleUrl* modl) { UNREACHABLE(); }
-
-
void AstGraphBuilder::VisitBlock(Block* stmt) {
BlockBuilder block(this);
- BreakableScope scope(this, stmt, &block, 0);
- if (stmt->labels() != NULL) block.BeginBlock();
- if (stmt->scope() == NULL) {
+ ControlScopeForBreakable scope(this, stmt, &block);
+ if (stmt->labels() != nullptr) block.BeginBlock();
+ if (stmt->scope() == nullptr) {
// Visit statements in the same scope, no declarations.
VisitStatements(stmt->statements());
} else {
- const Operator* op = javascript()->CreateBlockContext();
- Node* scope_info = jsgraph()->Constant(stmt->scope()->GetScopeInfo());
- Node* context = NewNode(op, scope_info, GetFunctionClosure());
- ContextScope scope(this, stmt->scope(), context);
-
// Visit declarations and statements in a block scope.
- VisitDeclarations(stmt->scope()->declarations());
- VisitStatements(stmt->statements());
+ if (stmt->scope()->NeedsContext()) {
+ Node* context = BuildLocalBlockContext(stmt->scope());
+ ContextScope scope(this, stmt->scope(), context);
+ VisitDeclarations(stmt->scope()->declarations());
+ VisitStatements(stmt->statements());
+ } else {
+ VisitDeclarations(stmt->scope()->declarations());
+ VisitStatements(stmt->statements());
+ }
}
- if (stmt->labels() != NULL) block.EndBlock();
-}
-
-
-void AstGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
- UNREACHABLE();
+ if (stmt->labels() != nullptr) block.EndBlock();
}
@@ -490,6 +1171,12 @@
}
+void AstGraphBuilder::VisitSloppyBlockFunctionStatement(
+ SloppyBlockFunctionStatement* stmt) {
+ Visit(stmt->statement());
+}
+
+
void AstGraphBuilder::VisitIfStatement(IfStatement* stmt) {
IfBuilder compare_if(this);
VisitForTest(stmt->condition());
@@ -504,47 +1191,42 @@
void AstGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
- StructuredGraphBuilder::Environment* env = environment()->CopyAsUnreachable();
- breakable()->ContinueTarget(stmt->target());
- set_environment(env);
+ execution_control()->ContinueTo(stmt->target());
}
void AstGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
- StructuredGraphBuilder::Environment* env = environment()->CopyAsUnreachable();
- breakable()->BreakTarget(stmt->target());
- set_environment(env);
+ execution_control()->BreakTo(stmt->target());
}
void AstGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
VisitForValue(stmt->expression());
Node* result = environment()->Pop();
- Node* control = NewNode(common()->Return(), result);
- UpdateControlDependencyToLeaveFunction(control);
+ execution_control()->ReturnValue(result);
}
void AstGraphBuilder::VisitWithStatement(WithStatement* stmt) {
VisitForValue(stmt->expression());
Node* value = environment()->Pop();
+ Node* object = BuildToObject(value, stmt->ToObjectId());
const Operator* op = javascript()->CreateWithContext();
- Node* context = NewNode(op, value, GetFunctionClosure());
- ContextScope scope(this, stmt->scope(), context);
- Visit(stmt->statement());
+ Node* context = NewNode(op, object, GetFunctionClosureForContext());
+ PrepareFrameState(context, stmt->EntryId());
+ VisitInScope(stmt->statement(), stmt->scope(), context);
}
void AstGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
ZoneList<CaseClause*>* clauses = stmt->cases();
SwitchBuilder compare_switch(this, clauses->length());
- BreakableScope scope(this, stmt, &compare_switch, 0);
+ ControlScopeForBreakable scope(this, stmt, &compare_switch);
compare_switch.BeginSwitch();
int default_index = -1;
// Keep the switch value on the stack until a case matches.
VisitForValue(stmt->tag());
- Node* tag = environment()->Top();
// Iterate over all cases and create nodes for label comparison.
for (int i = 0; i < clauses->length(); i++) {
@@ -560,6 +1242,7 @@
// value is still on the operand stack while the label is evaluated.
VisitForValue(clause->label());
Node* label = environment()->Pop();
+ Node* tag = environment()->Top();
const Operator* op = javascript()->StrictEqual();
Node* condition = NewNode(op, tag, label);
compare_switch.BeginLabel(i, condition);
@@ -589,8 +1272,8 @@
void AstGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder while_loop(this);
- while_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
- VisitIterationBody(stmt, &while_loop, 0);
+ while_loop.BeginLoop(GetVariablesAssignedInLoop(stmt), CheckOsrEntry(stmt));
+ VisitIterationBody(stmt, &while_loop);
while_loop.EndBody();
VisitForTest(stmt->cond());
Node* condition = environment()->Pop();
@@ -601,11 +1284,11 @@
void AstGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
LoopBuilder while_loop(this);
- while_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
+ while_loop.BeginLoop(GetVariablesAssignedInLoop(stmt), CheckOsrEntry(stmt));
VisitForTest(stmt->cond());
Node* condition = environment()->Pop();
while_loop.BreakUnless(condition);
- VisitIterationBody(stmt, &while_loop, 0);
+ VisitIterationBody(stmt, &while_loop);
while_loop.EndBody();
while_loop.EndLoop();
}
@@ -614,219 +1297,368 @@
void AstGraphBuilder::VisitForStatement(ForStatement* stmt) {
LoopBuilder for_loop(this);
VisitIfNotNull(stmt->init());
- for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
- if (stmt->cond() != NULL) {
+ for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt), CheckOsrEntry(stmt));
+ if (stmt->cond() != nullptr) {
VisitForTest(stmt->cond());
Node* condition = environment()->Pop();
for_loop.BreakUnless(condition);
} else {
for_loop.BreakUnless(jsgraph()->TrueConstant());
}
- VisitIterationBody(stmt, &for_loop, 0);
+ VisitIterationBody(stmt, &for_loop);
for_loop.EndBody();
VisitIfNotNull(stmt->next());
for_loop.EndLoop();
}
-// TODO(dcarney): this is a big function. Try to clean up some.
void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
VisitForValue(stmt->subject());
- Node* obj = environment()->Pop();
- // Check for undefined or null before entering loop.
- IfBuilder is_undefined(this);
- Node* is_undefined_cond =
- NewNode(javascript()->StrictEqual(), obj, jsgraph()->UndefinedConstant());
- is_undefined.If(is_undefined_cond);
- is_undefined.Then();
- is_undefined.Else();
+ Node* object = environment()->Pop();
+ BlockBuilder for_block(this);
+ for_block.BeginBlock();
+ // Check for null or undefined before entering loop.
+ Node* is_null_cond =
+ NewNode(javascript()->StrictEqual(), object, jsgraph()->NullConstant());
+ for_block.BreakWhen(is_null_cond, BranchHint::kFalse);
+ Node* is_undefined_cond = NewNode(javascript()->StrictEqual(), object,
+ jsgraph()->UndefinedConstant());
+ for_block.BreakWhen(is_undefined_cond, BranchHint::kFalse);
{
- IfBuilder is_null(this);
- Node* is_null_cond =
- NewNode(javascript()->StrictEqual(), obj, jsgraph()->NullConstant());
- is_null.If(is_null_cond);
- is_null.Then();
- is_null.Else();
// Convert object to jsobject.
- // PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
- obj = NewNode(javascript()->ToObject(), obj);
- PrepareFrameState(obj, stmt->ToObjectId(), OutputFrameStateCombine::Push());
- environment()->Push(obj);
- // TODO(dcarney): should do a fast enum cache check here to skip runtime.
- environment()->Push(obj);
- Node* cache_type = ProcessArguments(
- javascript()->CallRuntime(Runtime::kGetPropertyNamesFast, 1), 1);
- PrepareFrameState(cache_type, stmt->EnumId(),
- OutputFrameStateCombine::Push());
- // TODO(dcarney): these next runtime calls should be removed in favour of
- // a few simplified instructions.
- environment()->Push(obj);
- environment()->Push(cache_type);
- Node* cache_pair =
- ProcessArguments(javascript()->CallRuntime(Runtime::kForInInit, 2), 2);
- // cache_type may have been replaced.
- Node* cache_array = NewNode(common()->Projection(0), cache_pair);
- cache_type = NewNode(common()->Projection(1), cache_pair);
+ object = BuildToObject(object, stmt->ToObjectId());
+ environment()->Push(object);
+
+ // Prepare for-in cache.
+ Node* prepare = NewNode(javascript()->ForInPrepare(), object);
+ PrepareFrameState(prepare, stmt->EnumId(), OutputFrameStateCombine::Push());
+ Node* cache_type = NewNode(common()->Projection(0), prepare);
+ Node* cache_array = NewNode(common()->Projection(1), prepare);
+ Node* cache_length = NewNode(common()->Projection(2), prepare);
+
+ // Construct the rest of the environment.
environment()->Push(cache_type);
environment()->Push(cache_array);
- Node* cache_length = ProcessArguments(
- javascript()->CallRuntime(Runtime::kForInCacheArrayLength, 2), 2);
+ environment()->Push(cache_length);
+ environment()->Push(jsgraph()->ZeroConstant());
+
+ // Build the actual loop body.
+ LoopBuilder for_loop(this);
+ for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt), CheckOsrEntry(stmt));
{
- // TODO(dcarney): this check is actually supposed to be for the
- // empty enum case only.
- IfBuilder have_no_properties(this);
- Node* empty_array_cond = NewNode(javascript()->StrictEqual(),
- cache_length, jsgraph()->ZeroConstant());
- have_no_properties.If(empty_array_cond);
- have_no_properties.Then();
- // Pop obj and skip loop.
- environment()->Pop();
- have_no_properties.Else();
+ // These stack values are renamed in the case of OSR, so reload them
+ // from the environment.
+ Node* index = environment()->Peek(0);
+ Node* cache_length = environment()->Peek(1);
+ Node* cache_array = environment()->Peek(2);
+ Node* cache_type = environment()->Peek(3);
+ Node* object = environment()->Peek(4);
+
+ // Check loop termination condition.
+ Node* exit_cond = NewNode(javascript()->ForInDone(), index, cache_length);
+ for_loop.BreakWhen(exit_cond);
+
+ // Compute the next enumerated value.
+ Node* value = NewNode(javascript()->ForInNext(), object, cache_array,
+ cache_type, index);
+ PrepareFrameState(value, stmt->FilterId(),
+ OutputFrameStateCombine::Push());
+ IfBuilder test_value(this);
+ Node* test_value_cond = NewNode(javascript()->StrictEqual(), value,
+ jsgraph()->UndefinedConstant());
+ test_value.If(test_value_cond, BranchHint::kFalse);
+ test_value.Then();
+ test_value.Else();
{
- // Construct the rest of the environment.
- environment()->Push(cache_type);
- environment()->Push(cache_array);
- environment()->Push(cache_length);
- environment()->Push(jsgraph()->ZeroConstant());
- // PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
- LoopBuilder for_loop(this);
- for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt));
- // Check loop termination condition.
- Node* index = environment()->Peek(0);
- Node* exit_cond =
- NewNode(javascript()->LessThan(), index, cache_length);
- // TODO(jarin): provide real bailout id.
- PrepareFrameState(exit_cond, BailoutId::None());
- for_loop.BreakUnless(exit_cond);
- // TODO(dcarney): this runtime call should be a handful of
- // simplified instructions that
- // basically produce
- // value = array[index]
- environment()->Push(obj);
- environment()->Push(cache_array);
- environment()->Push(cache_type);
- environment()->Push(index);
- Node* pair = ProcessArguments(
- javascript()->CallRuntime(Runtime::kForInNext, 4), 4);
- Node* value = NewNode(common()->Projection(0), pair);
- Node* should_filter = NewNode(common()->Projection(1), pair);
- environment()->Push(value);
- {
- // Test if FILTER_KEY needs to be called.
- IfBuilder test_should_filter(this);
- Node* should_filter_cond =
- NewNode(javascript()->StrictEqual(), should_filter,
- jsgraph()->TrueConstant());
- test_should_filter.If(should_filter_cond);
- test_should_filter.Then();
- value = environment()->Pop();
- Node* builtins = BuildLoadBuiltinsObject();
- Node* function = BuildLoadObjectField(
- builtins,
- JSBuiltinsObject::OffsetOfFunctionWithId(Builtins::FILTER_KEY));
- // Callee.
- environment()->Push(function);
- // Receiver.
- environment()->Push(obj);
- // Args.
- environment()->Push(value);
- // result is either the string key or Smi(0) indicating the property
- // is gone.
- Node* res = ProcessArguments(
- javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS), 3);
- // TODO(jarin): provide real bailout id.
- PrepareFrameState(res, BailoutId::None());
- Node* property_missing = NewNode(javascript()->StrictEqual(), res,
- jsgraph()->ZeroConstant());
- {
- IfBuilder is_property_missing(this);
- is_property_missing.If(property_missing);
- is_property_missing.Then();
- // Inc counter and continue.
- Node* index_inc =
- NewNode(javascript()->Add(), index, jsgraph()->OneConstant());
- // TODO(jarin): provide real bailout id.
- PrepareFrameState(index_inc, BailoutId::None());
- environment()->Poke(0, index_inc);
- for_loop.Continue();
- is_property_missing.Else();
- is_property_missing.End();
- }
- // Replace 'value' in environment.
- environment()->Push(res);
- test_should_filter.Else();
- test_should_filter.End();
- }
- value = environment()->Pop();
// Bind value and do loop body.
- VisitForInAssignment(stmt->each(), value);
- VisitIterationBody(stmt, &for_loop, 5);
- for_loop.EndBody();
- // Inc counter and continue.
- Node* index_inc =
- NewNode(javascript()->Add(), index, jsgraph()->OneConstant());
- // TODO(jarin): provide real bailout id.
- PrepareFrameState(index_inc, BailoutId::None());
- environment()->Poke(0, index_inc);
- for_loop.EndLoop();
- environment()->Drop(5);
- // PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ VectorSlotPair feedback =
+ CreateVectorSlotPair(stmt->EachFeedbackSlot());
+ VisitForInAssignment(stmt->each(), value, feedback, stmt->FilterId(),
+ stmt->AssignmentId());
+ VisitIterationBody(stmt, &for_loop);
}
- have_no_properties.End();
+ test_value.End();
+ index = environment()->Peek(0);
+ for_loop.EndBody();
+
+ // Increment counter and continue.
+ index = NewNode(javascript()->ForInStep(), index);
+ environment()->Poke(0, index);
}
- is_null.End();
+ for_loop.EndLoop();
+ environment()->Drop(5);
}
- is_undefined.End();
+ for_block.EndBlock();
}
void AstGraphBuilder::VisitForOfStatement(ForOfStatement* stmt) {
- VisitForValue(stmt->subject());
- environment()->Pop();
- // TODO(turbofan): create and use loop builder.
+ LoopBuilder for_loop(this);
+ VisitForEffect(stmt->assign_iterator());
+ for_loop.BeginLoop(GetVariablesAssignedInLoop(stmt), CheckOsrEntry(stmt));
+ VisitForEffect(stmt->next_result());
+ VisitForTest(stmt->result_done());
+ Node* condition = environment()->Pop();
+ for_loop.BreakWhen(condition);
+ VisitForEffect(stmt->assign_each());
+ VisitIterationBody(stmt, &for_loop);
+ for_loop.EndBody();
+ for_loop.EndLoop();
}
void AstGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
- UNREACHABLE();
+ TryCatchBuilder try_control(this);
+
+ // Evaluate the try-block inside a control scope. This simulates a handler
+ // that is intercepting 'throw' control commands.
+ try_control.BeginTry();
+ {
+ ControlScopeForCatch scope(this, &try_control);
+ STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
+ environment()->Push(current_context());
+ Visit(stmt->try_block());
+ environment()->Pop();
+ }
+ try_control.EndTry();
+
+ // Insert lazy bailout point.
+ // TODO(mstarzinger): We are only using a 'call' to get a lazy bailout
+ // point. Ideally, we whould not re-enter optimized code when deoptimized
+ // lazily. Tracked by issue v8:4195.
+ NewNode(common()->LazyBailout(),
+ jsgraph()->ZeroConstant(), // dummy target.
+ environment()->Checkpoint(stmt->HandlerId())); // frame state.
+
+ // Clear message object as we enter the catch block.
+ Node* the_hole = jsgraph()->TheHoleConstant();
+ NewNode(javascript()->StoreMessage(), the_hole);
+
+ // Create a catch scope that binds the exception.
+ Node* exception = try_control.GetExceptionNode();
+ Handle<String> name = stmt->variable()->name();
+ const Operator* op = javascript()->CreateCatchContext(name);
+ Node* context = NewNode(op, exception, GetFunctionClosureForContext());
+
+ // Evaluate the catch-block.
+ VisitInScope(stmt->catch_block(), stmt->scope(), context);
+ try_control.EndCatch();
}
void AstGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- UNREACHABLE();
+ TryFinallyBuilder try_control(this);
+
+ // We keep a record of all paths that enter the finally-block to be able to
+ // dispatch to the correct continuation point after the statements in the
+ // finally-block have been evaluated.
+ //
+ // The try-finally construct can enter the finally-block in three ways:
+ // 1. By exiting the try-block normally, falling through at the end.
+ // 2. By exiting the try-block with a function-local control flow transfer
+ // (i.e. through break/continue/return statements).
+ // 3. By exiting the try-block with a thrown exception.
+ Node* fallthrough_result = jsgraph()->TheHoleConstant();
+ ControlScope::DeferredCommands* commands =
+ new (local_zone()) ControlScope::DeferredCommands(this);
+
+ // Evaluate the try-block inside a control scope. This simulates a handler
+ // that is intercepting all control commands.
+ try_control.BeginTry();
+ {
+ ControlScopeForFinally scope(this, commands, &try_control);
+ STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
+ environment()->Push(current_context());
+ Visit(stmt->try_block());
+ environment()->Pop();
+ }
+ try_control.EndTry(commands->GetFallThroughToken(), fallthrough_result);
+
+ // Insert lazy bailout point.
+ // TODO(mstarzinger): We are only using a 'call' to get a lazy bailout
+ // point. Ideally, we whould not re-enter optimized code when deoptimized
+ // lazily. Tracked by issue v8:4195.
+ NewNode(common()->LazyBailout(),
+ jsgraph()->ZeroConstant(), // dummy target.
+ environment()->Checkpoint(stmt->HandlerId())); // frame state.
+
+ // The result value semantics depend on how the block was entered:
+ // - ReturnStatement: It represents the return value being returned.
+ // - ThrowStatement: It represents the exception being thrown.
+ // - BreakStatement/ContinueStatement: Filled with the hole.
+ // - Falling through into finally-block: Filled with the hole.
+ Node* result = try_control.GetResultValueNode();
+ Node* token = try_control.GetDispatchTokenNode();
+
+ // The result value, dispatch token and message is expected on the operand
+ // stack (this is in sync with FullCodeGenerator::EnterFinallyBlock).
+ Node* message = NewNode(javascript()->LoadMessage());
+ environment()->Push(token); // TODO(mstarzinger): Cook token!
+ environment()->Push(result);
+ environment()->Push(message);
+
+ // Clear message object as we enter the finally block.
+ Node* the_hole = jsgraph()->TheHoleConstant();
+ NewNode(javascript()->StoreMessage(), the_hole);
+
+ // Evaluate the finally-block.
+ Visit(stmt->finally_block());
+ try_control.EndFinally();
+
+ // The result value, dispatch token and message is restored from the operand
+ // stack (this is in sync with FullCodeGenerator::ExitFinallyBlock).
+ message = environment()->Pop();
+ result = environment()->Pop();
+ token = environment()->Pop(); // TODO(mstarzinger): Uncook token!
+ NewNode(javascript()->StoreMessage(), message);
+
+ // Dynamic dispatch after the finally-block.
+ commands->ApplyDeferredCommands(token, result);
+
+ // TODO(mstarzinger): Remove bailout once everything works.
+ if (!FLAG_turbo_try_finally) SetStackOverflow();
}
void AstGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
- // TODO(turbofan): Do we really need a separate reloc-info for this?
- Node* node = NewNode(javascript()->CallRuntime(Runtime::kDebugBreak, 0));
+ Node* node =
+ NewNode(javascript()->CallRuntime(Runtime::kHandleDebuggerStatement, 0));
PrepareFrameState(node, stmt->DebugBreakId());
+ environment()->MarkAllLocalsLive();
}
void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
- Node* context = current_context();
-
- // Build a new shared function info if we cannot find one in the baseline
- // code. We also have a stack overflow if the recursive compilation did.
- expr->InitializeSharedInfo(handle(info()->shared_info()->code()));
- Handle<SharedFunctionInfo> shared_info = expr->shared_info();
- if (shared_info.is_null()) {
- shared_info = Compiler::BuildFunctionInfo(expr, info()->script(), info());
- CHECK(!shared_info.is_null()); // TODO(mstarzinger): Set stack overflow?
- }
+ // Find or build a shared function info.
+ Handle<SharedFunctionInfo> shared_info =
+ Compiler::GetSharedFunctionInfo(expr, info()->script(), info());
+ CHECK(!shared_info.is_null()); // TODO(mstarzinger): Set stack overflow?
// Create node to instantiate a new closure.
- Node* info = jsgraph()->Constant(shared_info);
- Node* pretenure = jsgraph()->BooleanConstant(expr->pretenure());
- const Operator* op = javascript()->CallRuntime(Runtime::kNewClosure, 3);
- Node* value = NewNode(op, context, info, pretenure);
+ PretenureFlag pretenure = expr->pretenure() ? TENURED : NOT_TENURED;
+ const Operator* op = javascript()->CreateClosure(shared_info, pretenure);
+ Node* value = NewNode(op);
ast_context()->ProduceValue(value);
}
void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
- UNREACHABLE();
+ // Visit declarations and class literal in a block scope.
+ if (expr->scope()->ContextLocalCount() > 0) {
+ Node* context = BuildLocalBlockContext(expr->scope());
+ ContextScope scope(this, expr->scope(), context);
+ VisitDeclarations(expr->scope()->declarations());
+ VisitClassLiteralContents(expr);
+ } else {
+ VisitDeclarations(expr->scope()->declarations());
+ VisitClassLiteralContents(expr);
+ }
+}
+
+
+void AstGraphBuilder::VisitClassLiteralContents(ClassLiteral* expr) {
+ Node* class_name = expr->raw_name() ? jsgraph()->Constant(expr->name())
+ : jsgraph()->UndefinedConstant();
+
+ // The class name is expected on the operand stack.
+ environment()->Push(class_name);
+ VisitForValueOrTheHole(expr->extends());
+ VisitForValue(expr->constructor());
+
+ // Create node to instantiate a new class.
+ Node* constructor = environment()->Pop();
+ Node* extends = environment()->Pop();
+ Node* name = environment()->Pop();
+ Node* start = jsgraph()->Constant(expr->start_position());
+ Node* end = jsgraph()->Constant(expr->end_position());
+ const Operator* opc = javascript()->CallRuntime(Runtime::kDefineClass, 5);
+ Node* literal = NewNode(opc, name, extends, constructor, start, end);
+ PrepareFrameState(literal, expr->CreateLiteralId(),
+ OutputFrameStateCombine::Push());
+
+ // The prototype is ensured to exist by Runtime_DefineClass. No access check
+ // is needed here since the constructor is created by the class literal.
+ Node* prototype =
+ BuildLoadObjectField(literal, JSFunction::kPrototypeOrInitialMapOffset);
+
+ // The class literal and the prototype are both expected on the operand stack
+ // during evaluation of the method values.
+ environment()->Push(literal);
+ environment()->Push(prototype);
+
+ // Create nodes to store method values into the literal.
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ environment()->Push(environment()->Peek(property->is_static() ? 1 : 0));
+
+ VisitForValue(property->key());
+ Node* name = BuildToName(environment()->Pop(), expr->GetIdForProperty(i));
+ environment()->Push(name);
+
+ // The static prototype property is read only. We handle the non computed
+ // property name case in the parser. Since this is the only case where we
+ // need to check for an own read only property we special case this so we do
+ // not need to do this for every property.
+ if (property->is_static() && property->is_computed_name()) {
+ Node* check = BuildThrowIfStaticPrototype(environment()->Pop(),
+ expr->GetIdForProperty(i));
+ environment()->Push(check);
+ }
+
+ VisitForValue(property->value());
+ Node* value = environment()->Pop();
+ Node* key = environment()->Pop();
+ Node* receiver = environment()->Pop();
+
+ BuildSetHomeObject(value, receiver, property);
+
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ case ObjectLiteral::Property::PROTOTYPE:
+ UNREACHABLE();
+ case ObjectLiteral::Property::COMPUTED: {
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kDefineClassMethod, 3);
+ NewNode(op, receiver, key, value);
+ break;
+ }
+ case ObjectLiteral::Property::GETTER: {
+ Node* attr = jsgraph()->Constant(DONT_ENUM);
+ const Operator* op = javascript()->CallRuntime(
+ Runtime::kDefineGetterPropertyUnchecked, 4);
+ NewNode(op, receiver, key, value, attr);
+ break;
+ }
+ case ObjectLiteral::Property::SETTER: {
+ Node* attr = jsgraph()->Constant(DONT_ENUM);
+ const Operator* op = javascript()->CallRuntime(
+ Runtime::kDefineSetterPropertyUnchecked, 4);
+ NewNode(op, receiver, key, value, attr);
+ break;
+ }
+ }
+ }
+
+ // Set both the prototype and constructor to have fast properties, and also
+ // freeze them in strong mode.
+ prototype = environment()->Pop();
+ literal = environment()->Pop();
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kFinalizeClassDefinition, 2);
+ literal = NewNode(op, literal, prototype);
+
+ // Assign to class variable.
+ if (expr->class_variable_proxy() != nullptr) {
+ Variable* var = expr->class_variable_proxy()->var();
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ VectorSlotPair feedback = CreateVectorSlotPair(
+ expr->NeedsProxySlot() ? expr->ProxySlot()
+ : FeedbackVectorSlot::Invalid());
+ BuildVariableAssignment(var, literal, Token::INIT, feedback,
+ BailoutId::None(), states);
+ }
+ ast_context()->ProduceValue(literal);
}
@@ -835,6 +1667,13 @@
}
+void AstGraphBuilder::VisitDoExpression(DoExpression* expr) {
+ VisitBlock(expr->block());
+ VisitVariableProxy(expr->result());
+ ast_context()->ReplaceValue();
+}
+
+
void AstGraphBuilder::VisitConditional(Conditional* expr) {
IfBuilder compare_if(this);
VisitForTest(expr->condition());
@@ -851,7 +1690,9 @@
void AstGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
VectorSlotPair pair = CreateVectorSlotPair(expr->VariableFeedbackSlot());
- Node* value = BuildVariableLoad(expr->var(), expr->id(), pair);
+ FrameStateBeforeAndAfter states(this, BeforeId(expr));
+ Node* value = BuildVariableLoad(expr->var(), expr->id(), states, pair,
+ ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
@@ -866,14 +1707,9 @@
Node* closure = GetFunctionClosure();
// Create node to materialize a regular expression literal.
- Node* literals_array =
- BuildLoadObjectField(closure, JSFunction::kLiteralsOffset);
- Node* literal_index = jsgraph()->Constant(expr->literal_index());
- Node* pattern = jsgraph()->Constant(expr->pattern());
- Node* flags = jsgraph()->Constant(expr->flags());
- const Operator* op =
- javascript()->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
- Node* literal = NewNode(op, literals_array, literal_index, pattern, flags);
+ const Operator* op = javascript()->CreateLiteralRegExp(
+ expr->pattern(), expr->flags(), expr->literal_index());
+ Node* literal = NewNode(op, closure);
PrepareFrameState(literal, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(literal);
}
@@ -883,15 +1719,10 @@
Node* closure = GetFunctionClosure();
// Create node to deep-copy the literal boilerplate.
- expr->BuildConstantProperties(isolate());
- Node* literals_array =
- BuildLoadObjectField(closure, JSFunction::kLiteralsOffset);
- Node* literal_index = jsgraph()->Constant(expr->literal_index());
- Node* constants = jsgraph()->Constant(expr->constant_properties());
- Node* flags = jsgraph()->Constant(expr->ComputeFlags());
- const Operator* op =
- javascript()->CallRuntime(Runtime::kCreateObjectLiteral, 4);
- Node* literal = NewNode(op, literals_array, literal_index, constants, flags);
+ const Operator* op = javascript()->CreateLiteralObject(
+ expr->constant_properties(), expr->ComputeFlags(true),
+ expr->literal_index());
+ Node* literal = NewNode(op, closure);
PrepareFrameState(literal, expr->CreateLiteralId(),
OutputFrameStateCombine::Push());
@@ -899,18 +1730,15 @@
// property values and is the value of the entire expression.
environment()->Push(literal);
- // Mark all computed expressions that are bound to a key that is shadowed by
- // a later occurrence of the same key. For the marked expressions, no store
- // code is emitted.
- expr->CalculateEmitStore(zone());
-
// Create nodes to store computed values into the literal.
- AccessorTable accessor_table(zone());
- for (int i = 0; i < expr->properties()->length(); i++) {
- ObjectLiteral::Property* property = expr->properties()->at(i);
+ int property_index = 0;
+ AccessorTable accessor_table(local_zone());
+ for (; property_index < expr->properties()->length(); property_index++) {
+ ObjectLiteral::Property* property = expr->properties()->at(property_index);
+ if (property->is_computed_name()) break;
if (property->IsCompileTimeValue()) continue;
- Literal* key = property->key();
+ Literal* key = property->key()->AsLiteral();
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
UNREACHABLE();
@@ -923,61 +1751,73 @@
if (key->value()->IsInternalizedString()) {
if (property->emit_store()) {
VisitForValue(property->value());
+ FrameStateBeforeAndAfter states(this, property->value()->id());
Node* value = environment()->Pop();
- Unique<Name> name = MakeUnique(key->AsPropertyName());
- Node* store = NewNode(javascript()->StoreNamed(strict_mode(), name),
- literal, value);
- PrepareFrameState(store, key->id());
+ Node* literal = environment()->Top();
+ Handle<Name> name = key->AsPropertyName();
+ VectorSlotPair feedback =
+ CreateVectorSlotPair(property->GetSlot(0));
+ Node* store = BuildNamedStore(literal, name, value, feedback);
+ states.AddToNode(store, key->id(),
+ OutputFrameStateCombine::Ignore());
+ BuildSetHomeObject(value, literal, property, 1);
} else {
VisitForEffect(property->value());
}
break;
}
- environment()->Push(literal); // Duplicate receiver.
+ environment()->Push(environment()->Top()); // Duplicate receiver.
VisitForValue(property->key());
VisitForValue(property->value());
Node* value = environment()->Pop();
Node* key = environment()->Pop();
Node* receiver = environment()->Pop();
if (property->emit_store()) {
- Node* strict = jsgraph()->Constant(SLOPPY);
+ Node* language = jsgraph()->Constant(SLOPPY);
const Operator* op =
javascript()->CallRuntime(Runtime::kSetProperty, 4);
- NewNode(op, receiver, key, value, strict);
+ Node* set_property = NewNode(op, receiver, key, value, language);
+ // SetProperty should not lazy deopt on an object literal.
+ PrepareFrameState(set_property, BailoutId::None());
+ BuildSetHomeObject(value, receiver, property);
}
break;
}
case ObjectLiteral::Property::PROTOTYPE: {
- environment()->Push(literal); // Duplicate receiver.
+ environment()->Push(environment()->Top()); // Duplicate receiver.
VisitForValue(property->value());
Node* value = environment()->Pop();
Node* receiver = environment()->Pop();
- if (property->emit_store()) {
- const Operator* op =
- javascript()->CallRuntime(Runtime::kInternalSetPrototype, 2);
- Node* set_prototype = NewNode(op, receiver, value);
- // SetPrototype should not lazy deopt on an object
- // literal.
- PrepareFrameState(set_prototype, BailoutId::None());
- }
+ DCHECK(property->emit_store());
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kInternalSetPrototype, 2);
+ Node* set_prototype = NewNode(op, receiver, value);
+ // SetPrototype should not lazy deopt on an object literal.
+ PrepareFrameState(set_prototype,
+ expr->GetIdForPropertySet(property_index));
break;
}
case ObjectLiteral::Property::GETTER:
- accessor_table.lookup(key)->second->getter = property->value();
+ if (property->emit_store()) {
+ accessor_table.lookup(key)->second->getter = property;
+ }
break;
case ObjectLiteral::Property::SETTER:
- accessor_table.lookup(key)->second->setter = property->value();
+ if (property->emit_store()) {
+ accessor_table.lookup(key)->second->setter = property;
+ }
break;
}
}
// Create nodes to define accessors, using only a single call to the runtime
// for each pair of corresponding getters and setters.
+ literal = environment()->Top(); // Reload from operand stack.
for (AccessorTable::Iterator it = accessor_table.begin();
it != accessor_table.end(); ++it) {
VisitForValue(it->first);
- VisitForValueOrNull(it->second->getter);
- VisitForValueOrNull(it->second->setter);
+ VisitObjectLiteralAccessor(literal, it->second->getter);
+ VisitObjectLiteralAccessor(literal, it->second->setter);
Node* setter = environment()->Pop();
Node* getter = environment()->Pop();
Node* name = environment()->Pop();
@@ -989,7 +1829,75 @@
PrepareFrameState(call, BailoutId::None());
}
+ // Object literals have two parts. The "static" part on the left contains no
+ // computed property names, and so we can compute its map ahead of time; see
+ // Runtime_CreateObjectLiteralBoilerplate. The second "dynamic" part starts
+ // with the first computed property name and continues with all properties to
+ // its right. All the code from above initializes the static component of the
+ // object literal, and arranges for the map of the result to reflect the
+ // static order in which the keys appear. For the dynamic properties, we
+ // compile them into a series of "SetOwnProperty" runtime calls. This will
+ // preserve insertion order.
+ for (; property_index < expr->properties()->length(); property_index++) {
+ ObjectLiteral::Property* property = expr->properties()->at(property_index);
+
+ if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
+ environment()->Push(environment()->Top()); // Duplicate receiver.
+ VisitForValue(property->value());
+ Node* value = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kInternalSetPrototype, 2);
+ Node* call = NewNode(op, receiver, value);
+ PrepareFrameState(call, expr->GetIdForPropertySet(property_index));
+ continue;
+ }
+
+ environment()->Push(environment()->Top()); // Duplicate receiver.
+ VisitForValue(property->key());
+ Node* name = BuildToName(environment()->Pop(),
+ expr->GetIdForPropertyName(property_index));
+ environment()->Push(name);
+ VisitForValue(property->value());
+ Node* value = environment()->Pop();
+ Node* key = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ BuildSetHomeObject(value, receiver, property);
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ case ObjectLiteral::Property::COMPUTED:
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL: {
+ Node* attr = jsgraph()->Constant(NONE);
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
+ Node* call = NewNode(op, receiver, key, value, attr);
+ PrepareFrameState(call, BailoutId::None());
+ break;
+ }
+ case ObjectLiteral::Property::PROTOTYPE:
+ UNREACHABLE(); // Handled specially above.
+ break;
+ case ObjectLiteral::Property::GETTER: {
+ Node* attr = jsgraph()->Constant(NONE);
+ const Operator* op = javascript()->CallRuntime(
+ Runtime::kDefineGetterPropertyUnchecked, 4);
+ Node* call = NewNode(op, receiver, key, value, attr);
+ PrepareFrameState(call, BailoutId::None());
+ break;
+ }
+ case ObjectLiteral::Property::SETTER: {
+ Node* attr = jsgraph()->Constant(NONE);
+ const Operator* op = javascript()->CallRuntime(
+ Runtime::kDefineSetterPropertyUnchecked, 4);
+ Node* call = NewNode(op, receiver, key, value, attr);
+ PrepareFrameState(call, BailoutId::None());
+ break;
+ }
+ }
+ }
+
// Transform literals that contain functions to fast properties.
+ literal = environment()->Top(); // Reload from operand stack.
if (expr->has_function()) {
const Operator* op =
javascript()->CallRuntime(Runtime::kToFastProperties, 1);
@@ -1000,83 +1908,163 @@
}
+void AstGraphBuilder::VisitObjectLiteralAccessor(
+ Node* home_object, ObjectLiteralProperty* property) {
+ if (property == nullptr) {
+ VisitForValueOrNull(nullptr);
+ } else {
+ VisitForValue(property->value());
+ BuildSetHomeObject(environment()->Top(), home_object, property);
+ }
+}
+
+
void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
Node* closure = GetFunctionClosure();
// Create node to deep-copy the literal boilerplate.
- expr->BuildConstantElements(isolate());
- Node* literals_array =
- BuildLoadObjectField(closure, JSFunction::kLiteralsOffset);
- Node* literal_index = jsgraph()->Constant(expr->literal_index());
- Node* constants = jsgraph()->Constant(expr->constant_elements());
- Node* flags = jsgraph()->Constant(expr->ComputeFlags());
- const Operator* op =
- javascript()->CallRuntime(Runtime::kCreateArrayLiteral, 4);
- Node* literal = NewNode(op, literals_array, literal_index, constants, flags);
+ const Operator* op = javascript()->CreateLiteralArray(
+ expr->constant_elements(), expr->ComputeFlags(true),
+ expr->literal_index());
+ Node* literal = NewNode(op, closure);
+ PrepareFrameState(literal, expr->CreateLiteralId(),
+ OutputFrameStateCombine::Push());
- // The array and the literal index are both expected on the operand stack
- // during computation of the element values.
+ // The array is expected on the operand stack during computation of the
+ // element values.
environment()->Push(literal);
- environment()->Push(literal_index);
// Create nodes to evaluate all the non-constant subexpressions and to store
// them into the newly cloned array.
- for (int i = 0; i < expr->values()->length(); i++) {
- Expression* subexpr = expr->values()->at(i);
+ int array_index = 0;
+ for (; array_index < expr->values()->length(); array_index++) {
+ Expression* subexpr = expr->values()->at(array_index);
+ if (subexpr->IsSpread()) break;
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
VisitForValue(subexpr);
- Node* value = environment()->Pop();
- Node* index = jsgraph()->Constant(i);
- Node* store = NewNode(javascript()->StoreProperty(strict_mode()), literal,
- index, value);
- PrepareFrameState(store, expr->GetIdForElement(i));
+ {
+ FrameStateBeforeAndAfter states(this, subexpr->id());
+ VectorSlotPair pair = CreateVectorSlotPair(expr->LiteralFeedbackSlot());
+ Node* value = environment()->Pop();
+ Node* index = jsgraph()->Constant(array_index);
+ Node* literal = environment()->Top();
+ Node* store = BuildKeyedStore(literal, index, value, pair);
+ states.AddToNode(store, expr->GetIdForElement(array_index),
+ OutputFrameStateCombine::Ignore());
+ }
}
- environment()->Pop(); // Array literal index.
+ // In case the array literal contains spread expressions it has two parts. The
+ // first part is the "static" array which has a literal index is handled
+ // above. The second part is the part after the first spread expression
+ // (inclusive) and these elements gets appended to the array. Note that the
+ // number elements an iterable produces is unknown ahead of time.
+ for (; array_index < expr->values()->length(); array_index++) {
+ Expression* subexpr = expr->values()->at(array_index);
+ Node* result;
+
+ if (subexpr->IsSpread()) {
+ VisitForValue(subexpr->AsSpread()->expression());
+ FrameStateBeforeAndAfter states(this,
+ subexpr->AsSpread()->expression()->id());
+ Node* iterable = environment()->Pop();
+ Node* array = environment()->Pop();
+ Node* function = BuildLoadNativeContextField(
+ Context::CONCAT_ITERABLE_TO_ARRAY_BUILTIN_INDEX);
+ result = NewNode(javascript()->CallFunction(3, language_mode()), function,
+ array, iterable);
+ states.AddToNode(result, expr->GetIdForElement(array_index));
+ } else {
+ VisitForValue(subexpr);
+ Node* value = environment()->Pop();
+ Node* array = environment()->Pop();
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kAppendElement, 2);
+ result = NewNode(op, array, value);
+ PrepareFrameState(result, expr->GetIdForElement(array_index));
+ }
+
+ environment()->Push(result);
+ }
+
ast_context()->ProduceValue(environment()->Pop());
}
-void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value) {
- DCHECK(expr->IsValidReferenceExpression());
+void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value,
+ const VectorSlotPair& feedback,
+ BailoutId bailout_id_before,
+ BailoutId bailout_id_after) {
+ DCHECK(expr->IsValidReferenceExpressionOrThis());
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->AsProperty();
- LhsKind assign_type = DetermineLhsKind(expr);
+ LhsKind assign_type = Property::GetAssignType(property);
// Evaluate LHS expression and store the value.
switch (assign_type) {
case VARIABLE: {
Variable* var = expr->AsVariableProxy()->var();
- // TODO(jarin) Fill in the correct bailout id.
- BuildVariableAssignment(var, value, Token::ASSIGN, BailoutId::None());
+ environment()->Push(value);
+ FrameStateBeforeAndAfter states(this, bailout_id_before);
+ value = environment()->Pop();
+ BuildVariableAssignment(var, value, Token::ASSIGN, feedback,
+ bailout_id_after, states);
break;
}
case NAMED_PROPERTY: {
environment()->Push(value);
VisitForValue(property->obj());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
Node* object = environment()->Pop();
value = environment()->Pop();
- Unique<Name> name =
- MakeUnique(property->key()->AsLiteral()->AsPropertyName());
- Node* store =
- NewNode(javascript()->StoreNamed(strict_mode(), name), object, value);
- // TODO(jarin) Fill in the correct bailout id.
- PrepareFrameState(store, BailoutId::None());
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* store = BuildNamedStore(object, name, value, feedback);
+ states.AddToNode(store, bailout_id_after,
+ OutputFrameStateCombine::Ignore());
break;
}
case KEYED_PROPERTY: {
environment()->Push(value);
VisitForValue(property->obj());
VisitForValue(property->key());
+ FrameStateBeforeAndAfter states(this, property->key()->id());
Node* key = environment()->Pop();
Node* object = environment()->Pop();
value = environment()->Pop();
- Node* store = NewNode(javascript()->StoreProperty(strict_mode()), object,
- key, value);
- // TODO(jarin) Fill in the correct bailout id.
- PrepareFrameState(store, BailoutId::None());
+ Node* store = BuildKeyedStore(object, key, value, feedback);
+ states.AddToNode(store, bailout_id_after,
+ OutputFrameStateCombine::Ignore());
+ break;
+ }
+ case NAMED_SUPER_PROPERTY: {
+ environment()->Push(value);
+ VisitForValue(property->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(property->obj()->AsSuperPropertyReference()->home_object());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ value = environment()->Pop();
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* store = BuildNamedSuperStore(receiver, home_object, name, value);
+ states.AddToNode(store, bailout_id_after,
+ OutputFrameStateCombine::Ignore());
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ environment()->Push(value);
+ VisitForValue(property->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(property->obj()->AsSuperPropertyReference()->home_object());
+ VisitForValue(property->key());
+ FrameStateBeforeAndAfter states(this, property->key()->id());
+ Node* key = environment()->Pop();
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ value = environment()->Pop();
+ Node* store = BuildKeyedSuperStore(receiver, home_object, key, value);
+ states.AddToNode(store, bailout_id_after,
+ OutputFrameStateCombine::Ignore());
break;
}
}
@@ -1084,48 +2072,67 @@
void AstGraphBuilder::VisitAssignment(Assignment* expr) {
- DCHECK(expr->target()->IsValidReferenceExpression());
+ DCHECK(expr->target()->IsValidReferenceExpressionOrThis());
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->target()->AsProperty();
- LhsKind assign_type = DetermineLhsKind(expr->target());
+ LhsKind assign_type = Property::GetAssignType(property);
+ bool needs_frame_state_before = true;
// Evaluate LHS expression.
switch (assign_type) {
- case VARIABLE:
- // Nothing to do here.
+ case VARIABLE: {
+ Variable* variable = expr->target()->AsVariableProxy()->var();
+ if (variable->location() == VariableLocation::PARAMETER ||
+ variable->location() == VariableLocation::LOCAL ||
+ variable->location() == VariableLocation::CONTEXT) {
+ needs_frame_state_before = false;
+ }
break;
+ }
case NAMED_PROPERTY:
VisitForValue(property->obj());
break;
- case KEYED_PROPERTY: {
+ case KEYED_PROPERTY:
VisitForValue(property->obj());
VisitForValue(property->key());
break;
- }
+ case NAMED_SUPER_PROPERTY:
+ VisitForValue(property->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(property->obj()->AsSuperPropertyReference()->home_object());
+ break;
+ case KEYED_SUPER_PROPERTY:
+ VisitForValue(property->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(property->obj()->AsSuperPropertyReference()->home_object());
+ VisitForValue(property->key());
+ break;
}
+ BailoutId before_store_id = BailoutId::None();
// Evaluate the value and potentially handle compound assignments by loading
// the left-hand side value and performing a binary operation.
if (expr->is_compound()) {
- Node* old_value = NULL;
+ Node* old_value = nullptr;
switch (assign_type) {
case VARIABLE: {
VariableProxy* proxy = expr->target()->AsVariableProxy();
VectorSlotPair pair =
CreateVectorSlotPair(proxy->VariableFeedbackSlot());
- old_value = BuildVariableLoad(proxy->var(), expr->target()->id(), pair);
+ FrameStateBeforeAndAfter states(this, BeforeId(proxy));
+ old_value =
+ BuildVariableLoad(proxy->var(), expr->target()->id(), states, pair,
+ OutputFrameStateCombine::Push());
break;
}
case NAMED_PROPERTY: {
Node* object = environment()->Top();
- Unique<Name> name =
- MakeUnique(property->key()->AsLiteral()->AsPropertyName());
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
- old_value = NewNode(javascript()->LoadNamed(name, pair), object);
- PrepareFrameState(old_value, property->LoadId(),
- OutputFrameStateCombine::Push());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ old_value = BuildNamedLoad(object, name, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
break;
}
case KEYED_PROPERTY: {
@@ -1133,50 +2140,104 @@
Node* object = environment()->Peek(1);
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
- old_value = NewNode(javascript()->LoadProperty(pair), object, key);
- PrepareFrameState(old_value, property->LoadId(),
- OutputFrameStateCombine::Push());
+ FrameStateBeforeAndAfter states(this, property->key()->id());
+ old_value = BuildKeyedLoad(object, key, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ break;
+ }
+ case NAMED_SUPER_PROPERTY: {
+ Node* home_object = environment()->Top();
+ Node* receiver = environment()->Peek(1);
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ VectorSlotPair pair =
+ CreateVectorSlotPair(property->PropertyFeedbackSlot());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ old_value = BuildNamedSuperLoad(receiver, home_object, name, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ Node* key = environment()->Top();
+ Node* home_object = environment()->Peek(1);
+ Node* receiver = environment()->Peek(2);
+ VectorSlotPair pair =
+ CreateVectorSlotPair(property->PropertyFeedbackSlot());
+ FrameStateBeforeAndAfter states(this, property->key()->id());
+ old_value = BuildKeyedSuperLoad(receiver, home_object, key, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
break;
}
}
environment()->Push(old_value);
VisitForValue(expr->value());
- Node* right = environment()->Pop();
- Node* left = environment()->Pop();
- Node* value = BuildBinaryOp(left, right, expr->binary_op());
- PrepareFrameState(value, expr->binary_operation()->id(),
- OutputFrameStateCombine::Push());
+ Node* value;
+ {
+ FrameStateBeforeAndAfter states(this, expr->value()->id());
+ Node* right = environment()->Pop();
+ Node* left = environment()->Pop();
+ value =
+ BuildBinaryOp(left, right, expr->binary_op(),
+ expr->binary_operation()->BinaryOperationFeedbackId());
+ states.AddToNode(value, expr->binary_operation()->id(),
+ OutputFrameStateCombine::Push());
+ }
environment()->Push(value);
+ if (needs_frame_state_before) {
+ before_store_id = expr->binary_operation()->id();
+ }
} else {
VisitForValue(expr->value());
+ if (needs_frame_state_before) {
+ before_store_id = expr->value()->id();
+ }
}
+ FrameStateBeforeAndAfter store_states(this, before_store_id);
// Store the value.
Node* value = environment()->Pop();
+ VectorSlotPair feedback = CreateVectorSlotPair(expr->AssignmentSlot());
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->target()->AsVariableProxy()->var();
- BuildVariableAssignment(variable, value, expr->op(), expr->AssignmentId(),
- ast_context()->GetStateCombine());
+ BuildVariableAssignment(variable, value, expr->op(), feedback, expr->id(),
+ store_states, ast_context()->GetStateCombine());
break;
}
case NAMED_PROPERTY: {
Node* object = environment()->Pop();
- Unique<Name> name =
- MakeUnique(property->key()->AsLiteral()->AsPropertyName());
- Node* store =
- NewNode(javascript()->StoreNamed(strict_mode(), name), object, value);
- PrepareFrameState(store, expr->AssignmentId(),
- ast_context()->GetStateCombine());
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* store = BuildNamedStore(object, name, value, feedback);
+ store_states.AddToNode(store, expr->id(),
+ ast_context()->GetStateCombine());
break;
}
case KEYED_PROPERTY: {
Node* key = environment()->Pop();
Node* object = environment()->Pop();
- Node* store = NewNode(javascript()->StoreProperty(strict_mode()), object,
- key, value);
- PrepareFrameState(store, expr->AssignmentId(),
- ast_context()->GetStateCombine());
+ Node* store = BuildKeyedStore(object, key, value, feedback);
+ store_states.AddToNode(store, expr->id(),
+ ast_context()->GetStateCombine());
+ break;
+ }
+ case NAMED_SUPER_PROPERTY: {
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* store = BuildNamedSuperStore(receiver, home_object, name, value);
+ store_states.AddToNode(store, expr->id(),
+ ast_context()->GetStateCombine());
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ Node* key = environment()->Pop();
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ Node* store = BuildKeyedSuperStore(receiver, home_object, key, value);
+ store_states.AddToNode(store, expr->id(),
+ ast_context()->GetStateCombine());
break;
}
}
@@ -1186,11 +2247,8 @@
void AstGraphBuilder::VisitYield(Yield* expr) {
- VisitForValue(expr->generator_object());
- VisitForValue(expr->expression());
- environment()->Pop();
- environment()->Pop();
- // TODO(turbofan): VisitYield
+ // TODO(turbofan): Implement yield here.
+ SetStackOverflow();
ast_context()->ProduceValue(jsgraph()->UndefinedConstant());
}
@@ -1198,29 +2256,62 @@
void AstGraphBuilder::VisitThrow(Throw* expr) {
VisitForValue(expr->exception());
Node* exception = environment()->Pop();
- const Operator* op = javascript()->CallRuntime(Runtime::kThrow, 1);
- Node* value = NewNode(op, exception);
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ Node* value = BuildThrowError(exception, expr->id());
ast_context()->ProduceValue(value);
}
void AstGraphBuilder::VisitProperty(Property* expr) {
- Node* value;
+ Node* value = nullptr;
+ LhsKind property_kind = Property::GetAssignType(expr);
VectorSlotPair pair = CreateVectorSlotPair(expr->PropertyFeedbackSlot());
- if (expr->key()->IsPropertyName()) {
- VisitForValue(expr->obj());
- Node* object = environment()->Pop();
- Unique<Name> name = MakeUnique(expr->key()->AsLiteral()->AsPropertyName());
- value = NewNode(javascript()->LoadNamed(name, pair), object);
- } else {
- VisitForValue(expr->obj());
- VisitForValue(expr->key());
- Node* key = environment()->Pop();
- Node* object = environment()->Pop();
- value = NewNode(javascript()->LoadProperty(pair), object, key);
+ switch (property_kind) {
+ case VARIABLE:
+ UNREACHABLE();
+ break;
+ case NAMED_PROPERTY: {
+ VisitForValue(expr->obj());
+ FrameStateBeforeAndAfter states(this, expr->obj()->id());
+ Node* object = environment()->Pop();
+ Handle<Name> name = expr->key()->AsLiteral()->AsPropertyName();
+ value = BuildNamedLoad(object, name, pair);
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
+ break;
+ }
+ case KEYED_PROPERTY: {
+ VisitForValue(expr->obj());
+ VisitForValue(expr->key());
+ FrameStateBeforeAndAfter states(this, expr->key()->id());
+ Node* key = environment()->Pop();
+ Node* object = environment()->Pop();
+ value = BuildKeyedLoad(object, key, pair);
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
+ break;
+ }
+ case NAMED_SUPER_PROPERTY: {
+ VisitForValue(expr->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(expr->obj()->AsSuperPropertyReference()->home_object());
+ FrameStateBeforeAndAfter states(this, expr->obj()->id());
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ Handle<Name> name = expr->key()->AsLiteral()->AsPropertyName();
+ value = BuildNamedSuperLoad(receiver, home_object, name, pair);
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ VisitForValue(expr->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(expr->obj()->AsSuperPropertyReference()->home_object());
+ VisitForValue(expr->key());
+ FrameStateBeforeAndAfter states(this, expr->key()->id());
+ Node* key = environment()->Pop();
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ value = BuildKeyedSuperLoad(receiver, home_object, key, pair);
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
+ break;
+ }
}
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
@@ -1231,68 +2322,136 @@
// Prepare the callee and the receiver to the function call. This depends on
// the semantics of the underlying call type.
- CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS;
- Node* receiver_value = NULL;
- Node* callee_value = NULL;
+ ConvertReceiverMode receiver_hint = ConvertReceiverMode::kAny;
+ Node* receiver_value = nullptr;
+ Node* callee_value = nullptr;
bool possibly_eval = false;
switch (call_type) {
case Call::GLOBAL_CALL: {
VariableProxy* proxy = callee->AsVariableProxy();
VectorSlotPair pair = CreateVectorSlotPair(proxy->VariableFeedbackSlot());
+ FrameStateBeforeAndAfter states(this, BeforeId(proxy));
callee_value =
- BuildVariableLoad(proxy->var(), expr->expression()->id(), pair);
+ BuildVariableLoad(proxy->var(), expr->expression()->id(), states,
+ pair, OutputFrameStateCombine::Push());
+ receiver_hint = ConvertReceiverMode::kNullOrUndefined;
receiver_value = jsgraph()->UndefinedConstant();
break;
}
case Call::LOOKUP_SLOT_CALL: {
Variable* variable = callee->AsVariableProxy()->var();
- DCHECK(variable->location() == Variable::LOOKUP);
+ DCHECK(variable->location() == VariableLocation::LOOKUP);
Node* name = jsgraph()->Constant(variable->name());
const Operator* op =
javascript()->CallRuntime(Runtime::kLoadLookupSlot, 2);
Node* pair = NewNode(op, current_context(), name);
callee_value = NewNode(common()->Projection(0), pair);
receiver_value = NewNode(common()->Projection(1), pair);
-
- PrepareFrameState(pair, expr->EvalOrLookupId(),
+ PrepareFrameState(pair, expr->LookupId(),
OutputFrameStateCombine::Push(2));
break;
}
- case Call::PROPERTY_CALL: {
+ case Call::NAMED_PROPERTY_CALL: {
Property* property = callee->AsProperty();
- VisitForValue(property->obj());
- Node* object = environment()->Top();
- VectorSlotPair pair =
+ VectorSlotPair feedback =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
- if (property->key()->IsPropertyName()) {
- Unique<Name> name =
- MakeUnique(property->key()->AsLiteral()->AsPropertyName());
- callee_value = NewNode(javascript()->LoadNamed(name, pair), object);
- } else {
- VisitForValue(property->key());
- Node* key = environment()->Pop();
- callee_value = NewNode(javascript()->LoadProperty(pair), object, key);
- }
- PrepareFrameState(callee_value, property->LoadId(),
- OutputFrameStateCombine::Push());
+ VisitForValue(property->obj());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* object = environment()->Top();
+ callee_value = BuildNamedLoad(object, name, feedback);
+ states.AddToNode(callee_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ // Note that a property call requires the receiver to be wrapped into
+ // an object for sloppy callees. However the receiver is guaranteed
+ // not to be null or undefined at this point.
+ receiver_hint = ConvertReceiverMode::kNotNullOrUndefined;
receiver_value = environment()->Pop();
- // Note that a PROPERTY_CALL requires the receiver to be wrapped into an
- // object for sloppy callees. This could also be modeled explicitly here,
- // thereby obsoleting the need for a flag to the call operator.
- flags = CALL_AS_METHOD;
break;
}
- case Call::SUPER_CALL: {
- // todo(dslomov): implement super calls in turbofan.
- UNIMPLEMENTED();
+ case Call::KEYED_PROPERTY_CALL: {
+ Property* property = callee->AsProperty();
+ VectorSlotPair feedback =
+ CreateVectorSlotPair(property->PropertyFeedbackSlot());
+ VisitForValue(property->obj());
+ VisitForValue(property->key());
+ FrameStateBeforeAndAfter states(this, property->key()->id());
+ Node* key = environment()->Pop();
+ Node* object = environment()->Top();
+ callee_value = BuildKeyedLoad(object, key, feedback);
+ states.AddToNode(callee_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ // Note that a property call requires the receiver to be wrapped into
+ // an object for sloppy callees. However the receiver is guaranteed
+ // not to be null or undefined at this point.
+ receiver_hint = ConvertReceiverMode::kNotNullOrUndefined;
+ receiver_value = environment()->Pop();
break;
}
+ case Call::NAMED_SUPER_PROPERTY_CALL: {
+ Property* property = callee->AsProperty();
+ SuperPropertyReference* super_ref =
+ property->obj()->AsSuperPropertyReference();
+ VisitForValue(super_ref->home_object());
+ VisitForValue(super_ref->this_var());
+ Node* home = environment()->Peek(1);
+ Node* object = environment()->Top();
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ callee_value = BuildNamedSuperLoad(object, home, name, VectorSlotPair());
+ states.AddToNode(callee_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ // Note that a property call requires the receiver to be wrapped into
+ // an object for sloppy callees. Since the receiver is not the target of
+ // the load, it could very well be null or undefined at this point.
+ receiver_value = environment()->Pop();
+ environment()->Drop(1);
+ break;
+ }
+ case Call::KEYED_SUPER_PROPERTY_CALL: {
+ Property* property = callee->AsProperty();
+ SuperPropertyReference* super_ref =
+ property->obj()->AsSuperPropertyReference();
+ VisitForValue(super_ref->home_object());
+ VisitForValue(super_ref->this_var());
+ environment()->Push(environment()->Top()); // Duplicate this_var.
+ environment()->Push(environment()->Peek(2)); // Duplicate home_obj.
+ VisitForValue(property->key());
+ Node* key = environment()->Pop();
+ Node* home = environment()->Pop();
+ Node* object = environment()->Pop();
+ FrameStateBeforeAndAfter states(this, property->key()->id());
+ callee_value = BuildKeyedSuperLoad(object, home, key, VectorSlotPair());
+ states.AddToNode(callee_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ // Note that a property call requires the receiver to be wrapped into
+ // an object for sloppy callees. Since the receiver is not the target of
+ // the load, it could very well be null or undefined at this point.
+ receiver_value = environment()->Pop();
+ environment()->Drop(1);
+ break;
+ }
+ case Call::SUPER_CALL:
+ return VisitCallSuper(expr);
case Call::POSSIBLY_EVAL_CALL:
possibly_eval = true;
+ if (callee->AsVariableProxy()->var()->IsLookupSlot()) {
+ Variable* variable = callee->AsVariableProxy()->var();
+ Node* name = jsgraph()->Constant(variable->name());
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kLoadLookupSlot, 2);
+ Node* pair = NewNode(op, current_context(), name);
+ callee_value = NewNode(common()->Projection(0), pair);
+ receiver_value = NewNode(common()->Projection(1), pair);
+ PrepareFrameState(pair, expr->LookupId(),
+ OutputFrameStateCombine::Push(2));
+ break;
+ }
// Fall through.
case Call::OTHER_CALL:
VisitForValue(callee);
callee_value = environment()->Pop();
+ receiver_hint = ConvertReceiverMode::kNullOrUndefined;
receiver_value = jsgraph()->UndefinedConstant();
break;
}
@@ -1306,8 +2465,8 @@
ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args);
- // Resolve callee and receiver for a potential direct eval call. This block
- // will mutate the callee and receiver values pushed onto the environment.
+ // Resolve callee for a potential direct eval call. This block will mutate the
+ // callee value pushed onto the environment.
if (possibly_eval && args->length() > 0) {
int arg_count = args->length();
@@ -1316,29 +2475,59 @@
Node* source = environment()->Peek(arg_count - 1);
// Create node to ask for help resolving potential eval call. This will
- // provide a fully resolved callee and the corresponding receiver.
+ // provide a fully resolved callee to patch into the environment.
Node* function = GetFunctionClosure();
- Node* receiver = environment()->Lookup(info()->scope()->receiver());
- Node* strict = jsgraph()->Constant(strict_mode());
- Node* position = jsgraph()->Constant(info()->scope()->start_position());
+ Node* language = jsgraph()->Constant(language_mode());
+ Node* position = jsgraph()->Constant(current_scope()->start_position());
const Operator* op =
- javascript()->CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
- Node* pair =
- NewNode(op, callee, source, function, receiver, strict, position);
- PrepareFrameState(pair, expr->EvalOrLookupId(),
+ javascript()->CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
+ Node* new_callee =
+ NewNode(op, callee, source, function, language, position);
+ PrepareFrameState(new_callee, expr->EvalId(),
OutputFrameStateCombine::PokeAt(arg_count + 1));
- Node* new_callee = NewNode(common()->Projection(0), pair);
- Node* new_receiver = NewNode(common()->Projection(1), pair);
- // Patch callee and receiver on the environment.
+ // Patch callee on the environment.
environment()->Poke(arg_count + 1, new_callee);
- environment()->Poke(arg_count + 0, new_receiver);
}
// Create node to perform the function call.
- const Operator* call = javascript()->CallFunction(args->length() + 2, flags);
+ VectorSlotPair feedback = CreateVectorSlotPair(expr->CallFeedbackICSlot());
+ const Operator* call = javascript()->CallFunction(
+ args->length() + 2, language_mode(), feedback, receiver_hint);
+ FrameStateBeforeAndAfter states(this, expr->CallId());
Node* value = ProcessArguments(call, args->length() + 2);
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ environment()->Push(value->InputAt(0)); // The callee passed to the call.
+ states.AddToNode(value, expr->ReturnId(), OutputFrameStateCombine::Push());
+ environment()->Drop(1);
+ ast_context()->ProduceValue(value);
+}
+
+
+void AstGraphBuilder::VisitCallSuper(Call* expr) {
+ SuperCallReference* super = expr->expression()->AsSuperCallReference();
+ DCHECK_NOT_NULL(super);
+
+ // Prepare the callee to the super call.
+ VisitForValue(super->this_function_var());
+ Node* this_function = environment()->Pop();
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kInlineGetSuperConstructor, 1);
+ Node* super_function = NewNode(op, this_function);
+ environment()->Push(super_function);
+
+ // Evaluate all arguments to the super call.
+ ZoneList<Expression*>* args = expr->arguments();
+ VisitForValues(args);
+
+ // The new target is loaded from the {new.target} variable.
+ VisitForValue(super->new_target_var());
+
+ // Create node to perform the super call.
+ const Operator* call =
+ javascript()->CallConstruct(args->length() + 2, VectorSlotPair());
+ FrameStateBeforeAndAfter states(this, super->new_target_var()->id());
+ Node* value = ProcessArguments(call, args->length() + 2);
+ states.AddToNode(value, expr->ReturnId(), OutputFrameStateCombine::Push());
ast_context()->ProduceValue(value);
}
@@ -1350,29 +2539,30 @@
ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args);
+ // The baseline compiler doesn't push the new.target, so we need to record
+ // the frame state before the push.
+ FrameStateBeforeAndAfter states(
+ this, args->is_empty() ? expr->expression()->id() : args->last()->id());
+
+ // The new target is the same as the callee.
+ environment()->Push(environment()->Peek(args->length()));
+
// Create node to perform the construct call.
- const Operator* call = javascript()->CallConstruct(args->length() + 1);
- Node* value = ProcessArguments(call, args->length() + 1);
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ VectorSlotPair feedback = CreateVectorSlotPair(expr->CallNewFeedbackSlot());
+ const Operator* call =
+ javascript()->CallConstruct(args->length() + 2, feedback);
+ Node* value = ProcessArguments(call, args->length() + 2);
+ states.AddToNode(value, expr->ReturnId(), OutputFrameStateCombine::Push());
ast_context()->ProduceValue(value);
}
void AstGraphBuilder::VisitCallJSRuntime(CallRuntime* expr) {
- Handle<String> name = expr->name();
-
// The callee and the receiver both have to be pushed onto the operand stack
// before arguments are being evaluated.
- CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS;
- Node* receiver_value = BuildLoadBuiltinsObject();
- Unique<String> unique = MakeUnique(name);
- VectorSlotPair pair = CreateVectorSlotPair(expr->CallRuntimeFeedbackSlot());
- Node* callee_value =
- NewNode(javascript()->LoadNamed(unique, pair), receiver_value);
- // TODO(jarin): Find/create a bailout id to deoptimize to (crankshaft
- // refuses to optimize functions with jsruntime calls).
- PrepareFrameState(callee_value, BailoutId::None(),
- OutputFrameStateCombine::Push());
+ Node* callee_value = BuildLoadNativeContextField(expr->context_index());
+ Node* receiver_value = jsgraph()->UndefinedConstant();
+
environment()->Push(callee_value);
environment()->Push(receiver_value);
@@ -1381,23 +2571,31 @@
VisitForValues(args);
// Create node to perform the JS runtime call.
- const Operator* call = javascript()->CallFunction(args->length() + 2, flags);
+ const Operator* call =
+ javascript()->CallFunction(args->length() + 2, language_mode());
+ FrameStateBeforeAndAfter states(this, expr->CallId());
Node* value = ProcessArguments(call, args->length() + 2);
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
void AstGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
- const Runtime::Function* function = expr->function();
-
// Handle calls to runtime functions implemented in JavaScript separately as
// the call follows JavaScript ABI and the callee is statically unknown.
if (expr->is_jsruntime()) {
- DCHECK(function == NULL && expr->name()->length() > 0);
return VisitCallJSRuntime(expr);
}
+ const Runtime::Function* function = expr->function();
+
+ // TODO(mstarzinger): This bailout is a gigantic hack, the owner is ashamed.
+ if (function->function_id == Runtime::kInlineGeneratorNext ||
+ function->function_id == Runtime::kInlineGeneratorThrow) {
+ ast_context()->ProduceValue(jsgraph()->TheHoleConstant());
+ return SetStackOverflow();
+ }
+
// Evaluate all arguments to the runtime call.
ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args);
@@ -1405,8 +2603,9 @@
// Create node to perform the runtime call.
Runtime::FunctionId functionId = function->function_id;
const Operator* call = javascript()->CallRuntime(functionId, args->length());
+ FrameStateBeforeAndAfter states(this, expr->CallId());
Node* value = ProcessArguments(call, args->length());
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
@@ -1428,97 +2627,177 @@
void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
- DCHECK(expr->expression()->IsValidReferenceExpression());
+ DCHECK(expr->expression()->IsValidReferenceExpressionOrThis());
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->expression()->AsProperty();
- LhsKind assign_type = DetermineLhsKind(expr->expression());
+ LhsKind assign_type = Property::GetAssignType(property);
// Reserve space for result of postfix operation.
bool is_postfix = expr->is_postfix() && !ast_context()->IsEffect();
- if (is_postfix) environment()->Push(jsgraph()->UndefinedConstant());
+ if (is_postfix && assign_type != VARIABLE) {
+ environment()->Push(jsgraph()->ZeroConstant());
+ }
// Evaluate LHS expression and get old value.
- Node* old_value = NULL;
+ Node* old_value = nullptr;
int stack_depth = -1;
switch (assign_type) {
case VARIABLE: {
VariableProxy* proxy = expr->expression()->AsVariableProxy();
VectorSlotPair pair = CreateVectorSlotPair(proxy->VariableFeedbackSlot());
+ FrameStateBeforeAndAfter states(this, BeforeId(proxy));
old_value =
- BuildVariableLoad(proxy->var(), expr->expression()->id(), pair);
+ BuildVariableLoad(proxy->var(), expr->expression()->id(), states,
+ pair, OutputFrameStateCombine::Push());
stack_depth = 0;
break;
}
case NAMED_PROPERTY: {
VisitForValue(property->obj());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
Node* object = environment()->Top();
- Unique<Name> name =
- MakeUnique(property->key()->AsLiteral()->AsPropertyName());
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
- old_value = NewNode(javascript()->LoadNamed(name, pair), object);
- PrepareFrameState(old_value, property->LoadId(),
- OutputFrameStateCombine::Push());
+ old_value = BuildNamedLoad(object, name, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
stack_depth = 1;
break;
}
case KEYED_PROPERTY: {
VisitForValue(property->obj());
VisitForValue(property->key());
+ FrameStateBeforeAndAfter states(this, property->key()->id());
Node* key = environment()->Top();
Node* object = environment()->Peek(1);
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
- old_value = NewNode(javascript()->LoadProperty(pair), object, key);
- PrepareFrameState(old_value, property->LoadId(),
- OutputFrameStateCombine::Push());
+ old_value = BuildKeyedLoad(object, key, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
stack_depth = 2;
break;
}
+ case NAMED_SUPER_PROPERTY: {
+ VisitForValue(property->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(property->obj()->AsSuperPropertyReference()->home_object());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ Node* home_object = environment()->Top();
+ Node* receiver = environment()->Peek(1);
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ VectorSlotPair pair =
+ CreateVectorSlotPair(property->PropertyFeedbackSlot());
+ old_value = BuildNamedSuperLoad(receiver, home_object, name, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ stack_depth = 2;
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ VisitForValue(property->obj()->AsSuperPropertyReference()->this_var());
+ VisitForValue(property->obj()->AsSuperPropertyReference()->home_object());
+ VisitForValue(property->key());
+ FrameStateBeforeAndAfter states(this, property->obj()->id());
+ Node* key = environment()->Top();
+ Node* home_object = environment()->Peek(1);
+ Node* receiver = environment()->Peek(2);
+ VectorSlotPair pair =
+ CreateVectorSlotPair(property->PropertyFeedbackSlot());
+ old_value = BuildKeyedSuperLoad(receiver, home_object, key, pair);
+ states.AddToNode(old_value, property->LoadId(),
+ OutputFrameStateCombine::Push());
+ stack_depth = 3;
+ break;
+ }
}
// Convert old value into a number.
- old_value = NewNode(javascript()->ToNumber(), old_value);
+ if (!is_strong(language_mode())) {
+ old_value = NewNode(javascript()->ToNumber(), old_value);
+ PrepareFrameState(old_value, expr->ToNumberId(),
+ OutputFrameStateCombine::Push());
+ }
+
+ // Create a proper eager frame state for the stores.
+ environment()->Push(old_value);
+ FrameStateBeforeAndAfter store_states(this, expr->ToNumberId());
+ old_value = environment()->Pop();
// Save result for postfix expressions at correct stack depth.
- if (is_postfix) environment()->Poke(stack_depth, old_value);
+ if (is_postfix) {
+ if (assign_type != VARIABLE) {
+ environment()->Poke(stack_depth, old_value);
+ } else {
+ environment()->Push(old_value);
+ }
+ }
// Create node to perform +1/-1 operation.
- Node* value =
- BuildBinaryOp(old_value, jsgraph()->OneConstant(), expr->binary_op());
- // TODO(jarin) Insert proper bailout id here (will need to change
- // full code generator).
- PrepareFrameState(value, BailoutId::None());
+ Node* value;
+ {
+ // TODO(bmeurer): Cleanup this feedback/bailout mess!
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ value = BuildBinaryOp(old_value, jsgraph()->OneConstant(),
+ expr->binary_op(), TypeFeedbackId::None());
+ // This should never deoptimize outside strong mode because otherwise we
+ // have converted to number before.
+ states.AddToNode(value, is_strong(language_mode()) ? expr->ToNumberId()
+ : BailoutId::None(),
+ OutputFrameStateCombine::Ignore());
+ }
// Store the value.
+ VectorSlotPair feedback = CreateVectorSlotPair(expr->CountSlot());
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->expression()->AsVariableProxy()->var();
environment()->Push(value);
- BuildVariableAssignment(variable, value, expr->op(),
- expr->AssignmentId());
+ BuildVariableAssignment(variable, value, expr->op(), feedback,
+ expr->AssignmentId(), store_states);
environment()->Pop();
break;
}
case NAMED_PROPERTY: {
Node* object = environment()->Pop();
- Unique<Name> name =
- MakeUnique(property->key()->AsLiteral()->AsPropertyName());
- Node* store =
- NewNode(javascript()->StoreNamed(strict_mode(), name), object, value);
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* store = BuildNamedStore(object, name, value, feedback);
environment()->Push(value);
- PrepareFrameState(store, expr->AssignmentId());
+ store_states.AddToNode(store, expr->AssignmentId(),
+ OutputFrameStateCombine::Ignore());
environment()->Pop();
break;
}
case KEYED_PROPERTY: {
Node* key = environment()->Pop();
Node* object = environment()->Pop();
- Node* store = NewNode(javascript()->StoreProperty(strict_mode()), object,
- key, value);
+ Node* store = BuildKeyedStore(object, key, value, feedback);
environment()->Push(value);
- PrepareFrameState(store, expr->AssignmentId());
+ store_states.AddToNode(store, expr->AssignmentId(),
+ OutputFrameStateCombine::Ignore());
+ environment()->Pop();
+ break;
+ }
+ case NAMED_SUPER_PROPERTY: {
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
+ Node* store = BuildNamedSuperStore(receiver, home_object, name, value);
+ environment()->Push(value);
+ store_states.AddToNode(store, expr->AssignmentId(),
+ OutputFrameStateCombine::Ignore());
+ environment()->Pop();
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ Node* key = environment()->Pop();
+ Node* home_object = environment()->Pop();
+ Node* receiver = environment()->Pop();
+ Node* store = BuildKeyedSuperStore(receiver, home_object, key, value);
+ environment()->Push(value);
+ store_states.AddToNode(store, expr->AssignmentId(),
+ OutputFrameStateCombine::Ignore());
environment()->Pop();
break;
}
@@ -1541,10 +2820,12 @@
default: {
VisitForValue(expr->left());
VisitForValue(expr->right());
+ FrameStateBeforeAndAfter states(this, expr->right()->id());
Node* right = environment()->Pop();
Node* left = environment()->Pop();
- Node* value = BuildBinaryOp(left, right, expr->op());
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ Node* value = BuildBinaryOp(left, right, expr->op(),
+ expr->BinaryOperationFeedbackId());
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
}
@@ -1567,16 +2848,16 @@
op = javascript()->StrictNotEqual();
break;
case Token::LT:
- op = javascript()->LessThan();
+ op = javascript()->LessThan(language_mode());
break;
case Token::GT:
- op = javascript()->GreaterThan();
+ op = javascript()->GreaterThan(language_mode());
break;
case Token::LTE:
- op = javascript()->LessThanOrEqual();
+ op = javascript()->LessThanOrEqual(language_mode());
break;
case Token::GTE:
- op = javascript()->GreaterThanOrEqual();
+ op = javascript()->GreaterThanOrEqual(language_mode());
break;
case Token::INSTANCEOF:
op = javascript()->InstanceOf();
@@ -1585,31 +2866,55 @@
op = javascript()->HasProperty();
break;
default:
- op = NULL;
+ op = nullptr;
UNREACHABLE();
}
VisitForValue(expr->left());
VisitForValue(expr->right());
+ FrameStateBeforeAndAfter states(this, expr->right()->id());
Node* right = environment()->Pop();
Node* left = environment()->Pop();
Node* value = NewNode(op, left, right);
- PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
+ states.AddToNode(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
+void AstGraphBuilder::VisitSpread(Spread* expr) {
+ // Handled entirely by the parser itself.
+ UNREACHABLE();
+}
+
+
+void AstGraphBuilder::VisitEmptyParentheses(EmptyParentheses* expr) {
+ // Handled entirely by the parser itself.
+ UNREACHABLE();
+}
+
+
void AstGraphBuilder::VisitThisFunction(ThisFunction* expr) {
Node* value = GetFunctionClosure();
ast_context()->ProduceValue(value);
}
-void AstGraphBuilder::VisitSuperReference(SuperReference* expr) {
+void AstGraphBuilder::VisitSuperPropertyReference(
+ SuperPropertyReference* expr) {
+ Node* value = BuildThrowUnsupportedSuperError(expr->id());
+ ast_context()->ProduceValue(value);
+}
+
+
+void AstGraphBuilder::VisitSuperCallReference(SuperCallReference* expr) {
+ // Handled by VisitCall
UNREACHABLE();
}
-void AstGraphBuilder::VisitCaseClause(CaseClause* expr) { UNREACHABLE(); }
+void AstGraphBuilder::VisitCaseClause(CaseClause* expr) {
+ // Handled entirely in VisitSwitch.
+ UNREACHABLE();
+}
void AstGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) {
@@ -1622,24 +2927,36 @@
for (Handle<Object> obj : *globals()) data->set(array_index++, *obj);
int encoded_flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
DeclareGlobalsNativeFlag::encode(info()->is_native()) |
- DeclareGlobalsStrictMode::encode(strict_mode());
+ DeclareGlobalsLanguageMode::encode(language_mode());
Node* flags = jsgraph()->Constant(encoded_flags);
Node* pairs = jsgraph()->Constant(data);
- const Operator* op = javascript()->CallRuntime(Runtime::kDeclareGlobals, 3);
- NewNode(op, current_context(), pairs, flags);
+ const Operator* op = javascript()->CallRuntime(Runtime::kDeclareGlobals, 2);
+ Node* call = NewNode(op, pairs, flags);
+ PrepareFrameState(call, BailoutId::Declarations());
globals()->clear();
}
void AstGraphBuilder::VisitIfNotNull(Statement* stmt) {
- if (stmt == NULL) return;
+ if (stmt == nullptr) return;
+ Visit(stmt);
+}
+
+
+void AstGraphBuilder::VisitInScope(Statement* stmt, Scope* s, Node* context) {
+ ContextScope scope(this, s, context);
+ DCHECK(s->declarations()->is_empty());
Visit(stmt);
}
void AstGraphBuilder::VisitIterationBody(IterationStatement* stmt,
- LoopBuilder* loop, int drop_extra) {
- BreakableScope scope(this, stmt, loop, drop_extra);
+ LoopBuilder* loop) {
+ ControlScopeForIteration scope(this, stmt, loop);
+ if (FLAG_turbo_loop_stackcheck || !info()->shared_info()->asm_function()) {
+ Node* node = NewNode(javascript()->StackCheck());
+ PrepareFrameState(node, stmt->StackCheckId());
+ }
Visit(stmt->body());
}
@@ -1650,7 +2967,9 @@
// Delete of an unqualified identifier is only allowed in classic mode but
// deleting "this" is allowed in all language modes.
Variable* variable = expr->expression()->AsVariableProxy()->var();
- DCHECK(strict_mode() == SLOPPY || variable->is_this());
+ // Delete of an unqualified identifier is disallowed in strict mode but
+ // "delete this" is allowed.
+ DCHECK(is_sloppy(language_mode()) || variable->HasThisName(isolate()));
value = BuildVariableDelete(variable, expr->id(),
ast_context()->GetStateCombine());
} else if (expr->expression()->IsProperty()) {
@@ -1659,7 +2978,7 @@
VisitForValue(property->key());
Node* key = environment()->Pop();
Node* object = environment()->Pop();
- value = NewNode(javascript()->DeleteProperty(strict_mode()), object, key);
+ value = NewNode(javascript()->DeleteProperty(language_mode()), object, key);
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
} else {
VisitForEffect(expr->expression());
@@ -1683,8 +3002,10 @@
// perform a non-contextual load in case the operand is a variable proxy.
VariableProxy* proxy = expr->expression()->AsVariableProxy();
VectorSlotPair pair = CreateVectorSlotPair(proxy->VariableFeedbackSlot());
- operand = BuildVariableLoad(proxy->var(), expr->expression()->id(), pair,
- NOT_CONTEXTUAL);
+ FrameStateBeforeAndAfter states(this, BeforeId(proxy));
+ operand =
+ BuildVariableLoad(proxy->var(), expr->expression()->id(), states, pair,
+ OutputFrameStateCombine::Push(), INSIDE_TYPEOF);
} else {
VisitForValue(expr->expression());
operand = environment()->Pop();
@@ -1697,8 +3018,9 @@
void AstGraphBuilder::VisitNot(UnaryOperation* expr) {
VisitForValue(expr->expression());
Node* operand = environment()->Pop();
- // TODO(mstarzinger): Possible optimization when we are in effect context.
- Node* value = NewNode(javascript()->UnaryNot(), operand);
+ Node* input = BuildToBoolean(operand, expr->expression()->test_id());
+ Node* value = NewNode(common()->Select(MachineRepresentation::kTagged), input,
+ jsgraph()->FalseConstant(), jsgraph()->TrueConstant());
ast_context()->ProduceValue(value);
}
@@ -1715,13 +3037,15 @@
IfBuilder compare_if(this);
VisitForValue(expr->left());
Node* condition = environment()->Top();
- compare_if.If(BuildToBoolean(condition));
+ compare_if.If(BuildToBoolean(condition, expr->left()->test_id()));
compare_if.Then();
if (is_logical_and) {
environment()->Pop();
Visit(expr->right());
} else if (ast_context()->IsEffect()) {
environment()->Pop();
+ } else if (ast_context()->IsTest()) {
+ environment()->Poke(0, jsgraph()->TrueConstant());
}
compare_if.Else();
if (!is_logical_and) {
@@ -1729,23 +3053,76 @@
Visit(expr->right());
} else if (ast_context()->IsEffect()) {
environment()->Pop();
+ } else if (ast_context()->IsTest()) {
+ environment()->Poke(0, jsgraph()->FalseConstant());
}
compare_if.End();
ast_context()->ReplaceValue();
}
-StrictMode AstGraphBuilder::strict_mode() const {
- return info()->strict_mode();
+LanguageMode AstGraphBuilder::language_mode() const {
+ return info()->language_mode();
}
VectorSlotPair AstGraphBuilder::CreateVectorSlotPair(
- FeedbackVectorICSlot slot) const {
+ FeedbackVectorSlot slot) const {
return VectorSlotPair(handle(info()->shared_info()->feedback_vector()), slot);
}
+void AstGraphBuilder::VisitRewritableAssignmentExpression(
+ RewritableAssignmentExpression* node) {
+ Visit(node->expression());
+}
+
+
+namespace {
+
+// Limit of context chain length to which inline check is possible.
+const int kMaxCheckDepth = 30;
+
+// Sentinel for {TryLoadDynamicVariable} disabling inline checks.
+const uint32_t kFullCheckRequired = -1;
+
+} // namespace
+
+
+uint32_t AstGraphBuilder::ComputeBitsetForDynamicGlobal(Variable* variable) {
+ DCHECK_EQ(DYNAMIC_GLOBAL, variable->mode());
+ bool found_eval_scope = false;
+ uint32_t check_depths = 0;
+ for (Scope* s = current_scope(); s != nullptr; s = s->outer_scope()) {
+ if (s->num_heap_slots() <= 0) continue;
+ // TODO(mstarzinger): If we have reached an eval scope, we check all
+ // extensions from this point. Replicated from full-codegen, figure out
+ // whether this is still needed. If not, drop {found_eval_scope} below.
+ if (s->is_eval_scope()) found_eval_scope = true;
+ if (!s->calls_sloppy_eval() && !found_eval_scope) continue;
+ int depth = current_scope()->ContextChainLength(s);
+ if (depth > kMaxCheckDepth) return kFullCheckRequired;
+ check_depths |= 1 << depth;
+ }
+ return check_depths;
+}
+
+
+uint32_t AstGraphBuilder::ComputeBitsetForDynamicContext(Variable* variable) {
+ DCHECK_EQ(DYNAMIC_LOCAL, variable->mode());
+ uint32_t check_depths = 0;
+ for (Scope* s = current_scope(); s != nullptr; s = s->outer_scope()) {
+ if (s->num_heap_slots() <= 0) continue;
+ if (!s->calls_sloppy_eval() && s != variable->scope()) continue;
+ int depth = current_scope()->ContextChainLength(s);
+ if (depth > kMaxCheckDepth) return kFullCheckRequired;
+ check_depths |= 1 << depth;
+ if (s == variable->scope()) break;
+ }
+ return check_depths;
+}
+
+
Node* AstGraphBuilder::ProcessArguments(const Operator* op, int arity) {
DCHECK(environment()->stack_height() >= arity);
Node** all = info()->zone()->NewArray<Node*>(arity);
@@ -1757,52 +3134,31 @@
}
-Node* AstGraphBuilder::BuildPatchReceiverToGlobalProxy(Node* receiver) {
- // Sloppy mode functions and builtins need to replace the receiver with the
- // global proxy when called as functions (without an explicit receiver
- // object). Otherwise there is nothing left to do here.
- if (info()->strict_mode() != SLOPPY || info()->is_native()) return receiver;
-
- // There is no need to perform patching if the receiver is never used. Note
- // that scope predicates are purely syntactical, a call to eval might still
- // inspect the receiver value.
- if (!info()->scope()->uses_this() && !info()->scope()->inner_uses_this() &&
- !info()->scope()->calls_sloppy_eval()) {
- return receiver;
- }
-
- IfBuilder receiver_check(this);
- Node* undefined = jsgraph()->UndefinedConstant();
- Node* check = NewNode(javascript()->StrictEqual(), receiver, undefined);
- receiver_check.If(check);
- receiver_check.Then();
- environment()->Push(BuildLoadGlobalProxy());
- receiver_check.Else();
- environment()->Push(receiver);
- receiver_check.End();
- return environment()->Pop();
-}
-
-
-Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context, Node* closure) {
- int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots <= 0) return context;
+Node* AstGraphBuilder::BuildLocalActivationContext(Node* context) {
+ Scope* scope = info()->scope();
// Allocate a new local context.
- const Operator* op = javascript()->CreateFunctionContext();
- Node* local_context = NewNode(op, closure);
- set_current_context(local_context);
+ Node* local_context = scope->is_script_scope()
+ ? BuildLocalScriptContext(scope)
+ : BuildLocalFunctionContext(scope);
+
+ if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
+ Node* receiver = environment()->RawParameterLookup(0);
+ // Context variable (at bottom of the context chain).
+ Variable* variable = scope->receiver();
+ DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
+ const Operator* op = javascript()->StoreContext(0, variable->index());
+ NewNode(op, local_context, receiver);
+ }
// Copy parameters into context if necessary.
- int num_parameters = info()->scope()->num_parameters();
+ int num_parameters = scope->num_parameters();
for (int i = 0; i < num_parameters; i++) {
- Variable* variable = info()->scope()->parameter(i);
+ Variable* variable = scope->parameter(i);
if (!variable->IsContextSlot()) continue;
- // Temporary parameter node. The parameter indices are shifted by 1
- // (receiver is parameter index -1 but environment index 0).
- Node* parameter = NewNode(common()->Parameter(i + 1), graph()->start());
+ Node* parameter = environment()->RawParameterLookup(i + 1);
// Context variable (at bottom of the context chain).
- DCHECK_EQ(0, info()->scope()->ContextChainLength(variable->scope()));
+ DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
const Operator* op = javascript()->StoreContext(0, variable->index());
NewNode(op, local_context, parameter);
}
@@ -1811,25 +3167,145 @@
}
+Node* AstGraphBuilder::BuildLocalFunctionContext(Scope* scope) {
+ DCHECK(scope->is_function_scope());
+
+ // Allocate a new local context.
+ int slot_count = scope->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ const Operator* op = javascript()->CreateFunctionContext(slot_count);
+ Node* local_context = NewNode(op, GetFunctionClosure());
+
+ return local_context;
+}
+
+
+Node* AstGraphBuilder::BuildLocalScriptContext(Scope* scope) {
+ DCHECK(scope->is_script_scope());
+
+ // Allocate a new local context.
+ Handle<ScopeInfo> scope_info = scope->GetScopeInfo(isolate());
+ const Operator* op = javascript()->CreateScriptContext(scope_info);
+ Node* local_context = NewNode(op, GetFunctionClosure());
+ PrepareFrameState(local_context, BailoutId::ScriptContext(),
+ OutputFrameStateCombine::Push());
+
+ return local_context;
+}
+
+
+Node* AstGraphBuilder::BuildLocalBlockContext(Scope* scope) {
+ DCHECK(scope->is_block_scope());
+
+ // Allocate a new local context.
+ Handle<ScopeInfo> scope_info = scope->GetScopeInfo(isolate());
+ const Operator* op = javascript()->CreateBlockContext(scope_info);
+ Node* local_context = NewNode(op, GetFunctionClosureForContext());
+
+ return local_context;
+}
+
+
Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) {
- if (arguments == NULL) return NULL;
+ if (arguments == nullptr) return nullptr;
// Allocate and initialize a new arguments object.
- Node* callee = GetFunctionClosure();
- const Operator* op = javascript()->CallRuntime(Runtime::kNewArguments, 1);
- Node* object = NewNode(op, callee);
+ CreateArgumentsParameters::Type type =
+ is_strict(language_mode()) || !info()->has_simple_parameters()
+ ? CreateArgumentsParameters::kUnmappedArguments
+ : CreateArgumentsParameters::kMappedArguments;
+ const Operator* op = javascript()->CreateArguments(type, 0);
+ Node* object = NewNode(op, GetFunctionClosure());
+ PrepareFrameState(object, BailoutId::None());
- // Assign the object to the arguments variable.
+ // Assign the object to the {arguments} variable. This should never lazy
+ // deopt, so it is fine to send invalid bailout id.
DCHECK(arguments->IsContextSlot() || arguments->IsStackAllocated());
- // This should never lazy deopt, so it is fine to send invalid bailout id.
- BuildVariableAssignment(arguments, object, Token::ASSIGN, BailoutId::None());
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ BuildVariableAssignment(arguments, object, Token::ASSIGN, VectorSlotPair(),
+ BailoutId::None(), states);
+ return object;
+}
+
+Node* AstGraphBuilder::BuildRestArgumentsArray(Variable* rest, int index) {
+ if (rest == nullptr) return nullptr;
+
+ // Allocate and initialize a new arguments object.
+ CreateArgumentsParameters::Type type = CreateArgumentsParameters::kRestArray;
+ const Operator* op = javascript()->CreateArguments(type, index);
+ Node* object = NewNode(op, GetFunctionClosure());
+ PrepareFrameState(object, BailoutId::None());
+
+ // Assign the object to the {rest} variable. This should never lazy
+ // deopt, so it is fine to send invalid bailout id.
+ DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ BuildVariableAssignment(rest, object, Token::ASSIGN, VectorSlotPair(),
+ BailoutId::None(), states);
+ return object;
+}
+
+
+Node* AstGraphBuilder::BuildThisFunctionVariable(Variable* this_function_var) {
+ if (this_function_var == nullptr) return nullptr;
+
+ // Retrieve the closure we were called with.
+ Node* this_function = GetFunctionClosure();
+
+ // Assign the object to the {.this_function} variable. This should never lazy
+ // deopt, so it is fine to send invalid bailout id.
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ BuildVariableAssignment(this_function_var, this_function, Token::INIT,
+ VectorSlotPair(), BailoutId::None(), states);
+ return this_function;
+}
+
+
+Node* AstGraphBuilder::BuildNewTargetVariable(Variable* new_target_var) {
+ if (new_target_var == nullptr) return nullptr;
+
+ // Retrieve the new target we were called with.
+ Node* object = GetNewTarget();
+
+ // Assign the object to the {new.target} variable. This should never lazy
+ // deopt, so it is fine to send invalid bailout id.
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ BuildVariableAssignment(new_target_var, object, Token::INIT, VectorSlotPair(),
+ BailoutId::None(), states);
return object;
}
Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole,
Node* not_hole) {
+ Node* the_hole = jsgraph()->TheHoleConstant();
+ Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
+ return NewNode(
+ common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
+ check, for_hole, not_hole);
+}
+
+
+Node* AstGraphBuilder::BuildHoleCheckThenThrow(Node* value, Variable* variable,
+ Node* not_hole,
+ BailoutId bailout_id) {
+ IfBuilder hole_check(this);
+ Node* the_hole = jsgraph()->TheHoleConstant();
+ Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
+ hole_check.If(check);
+ hole_check.Then();
+ Node* error = BuildThrowReferenceError(variable, bailout_id);
+ environment()->Push(error);
+ hole_check.Else();
+ environment()->Push(not_hole);
+ hole_check.End();
+ return environment()->Pop();
+}
+
+
+Node* AstGraphBuilder::BuildHoleCheckElseThrow(Node* value, Variable* variable,
+ Node* for_hole,
+ BailoutId bailout_id) {
IfBuilder hole_check(this);
Node* the_hole = jsgraph()->TheHoleConstant();
Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
@@ -1837,47 +3313,50 @@
hole_check.Then();
environment()->Push(for_hole);
hole_check.Else();
- environment()->Push(not_hole);
+ Node* error = BuildThrowReferenceError(variable, bailout_id);
+ environment()->Push(error);
hole_check.End();
return environment()->Pop();
}
-Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable,
- Node* not_hole,
- BailoutId bailout_id) {
- IfBuilder hole_check(this);
- Node* the_hole = jsgraph()->TheHoleConstant();
- Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
- hole_check.If(check);
- hole_check.Then();
- environment()->Push(BuildThrowReferenceError(variable, bailout_id));
- hole_check.Else();
- environment()->Push(not_hole);
- hole_check.End();
+Node* AstGraphBuilder::BuildThrowIfStaticPrototype(Node* name,
+ BailoutId bailout_id) {
+ IfBuilder prototype_check(this);
+ Node* prototype_string =
+ jsgraph()->Constant(isolate()->factory()->prototype_string());
+ Node* check = NewNode(javascript()->StrictEqual(), name, prototype_string);
+ prototype_check.If(check);
+ prototype_check.Then();
+ Node* error = BuildThrowStaticPrototypeError(bailout_id);
+ environment()->Push(error);
+ prototype_check.Else();
+ environment()->Push(name);
+ prototype_check.End();
return environment()->Pop();
}
Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states,
const VectorSlotPair& feedback,
- ContextualMode contextual_mode) {
+ OutputFrameStateCombine combine,
+ TypeofMode typeof_mode) {
Node* the_hole = jsgraph()->TheHoleConstant();
VariableMode mode = variable->mode();
switch (variable->location()) {
- case Variable::UNALLOCATED: {
+ case VariableLocation::GLOBAL:
+ case VariableLocation::UNALLOCATED: {
// Global var, const, or let variable.
- Node* global = BuildLoadGlobalObject();
- Unique<Name> name = MakeUnique(variable->name());
- const Operator* op =
- javascript()->LoadNamed(name, feedback, contextual_mode);
- Node* node = NewNode(op, global);
- PrepareFrameState(node, bailout_id, OutputFrameStateCombine::Push());
- return node;
+ Handle<Name> name = variable->name();
+ if (Node* node = TryLoadGlobalConstant(name)) return node;
+ Node* value = BuildGlobalLoad(name, feedback, typeof_mode);
+ states.AddToNode(value, bailout_id, combine);
+ return value;
}
- case Variable::PARAMETER:
- case Variable::LOCAL: {
+ case VariableLocation::PARAMETER:
+ case VariableLocation::LOCAL: {
// Local var, const, or let variable.
Node* value = environment()->Lookup(variable);
if (mode == CONST_LEGACY) {
@@ -1893,12 +3372,12 @@
if (value->op() == the_hole->op()) {
value = BuildThrowReferenceError(variable, bailout_id);
} else if (value->opcode() == IrOpcode::kPhi) {
- value = BuildHoleCheckThrow(value, variable, value, bailout_id);
+ value = BuildHoleCheckThenThrow(value, variable, value, bailout_id);
}
}
return value;
}
- case Variable::CONTEXT: {
+ case VariableLocation::CONTEXT: {
// Context variable (potentially up the context chain).
int depth = current_scope()->ContextChainLength(variable->scope());
bool immutable = variable->maybe_assigned() == kNotAssigned;
@@ -1914,318 +3393,974 @@
value = BuildHoleCheckSilent(value, undefined, value);
} else if (mode == LET || mode == CONST) {
// Perform check for uninitialized let/const variables.
- value = BuildHoleCheckThrow(value, variable, value, bailout_id);
+ value = BuildHoleCheckThenThrow(value, variable, value, bailout_id);
}
return value;
}
- case Variable::LOOKUP: {
+ case VariableLocation::LOOKUP: {
// Dynamic lookup of context variable (anywhere in the chain).
- Node* name = jsgraph()->Constant(variable->name());
- Runtime::FunctionId function_id =
- (contextual_mode == CONTEXTUAL)
- ? Runtime::kLoadLookupSlot
- : Runtime::kLoadLookupSlotNoReferenceError;
- const Operator* op = javascript()->CallRuntime(function_id, 2);
- Node* pair = NewNode(op, current_context(), name);
- PrepareFrameState(pair, bailout_id, OutputFrameStateCombine::Push(1));
- return NewNode(common()->Projection(0), pair);
+ Handle<String> name = variable->name();
+ if (Node* node =
+ TryLoadDynamicVariable(variable, name, bailout_id, states,
+ feedback, combine, typeof_mode)) {
+ return node;
+ }
+ const Operator* op = javascript()->LoadDynamic(name, typeof_mode);
+ Node* value = NewNode(op, BuildLoadFeedbackVector(), current_context());
+ states.AddToNode(value, bailout_id, combine);
+ return value;
}
}
UNREACHABLE();
- return NULL;
+ return nullptr;
}
-Node* AstGraphBuilder::BuildVariableDelete(
- Variable* variable, BailoutId bailout_id,
- OutputFrameStateCombine state_combine) {
+Node* AstGraphBuilder::BuildVariableDelete(Variable* variable,
+ BailoutId bailout_id,
+ OutputFrameStateCombine combine) {
switch (variable->location()) {
- case Variable::UNALLOCATED: {
+ case VariableLocation::GLOBAL:
+ case VariableLocation::UNALLOCATED: {
// Global var, const, or let variable.
Node* global = BuildLoadGlobalObject();
Node* name = jsgraph()->Constant(variable->name());
- const Operator* op = javascript()->DeleteProperty(strict_mode());
+ const Operator* op = javascript()->DeleteProperty(language_mode());
Node* result = NewNode(op, global, name);
- PrepareFrameState(result, bailout_id, state_combine);
+ PrepareFrameState(result, bailout_id, combine);
return result;
}
- case Variable::PARAMETER:
- case Variable::LOCAL:
- case Variable::CONTEXT:
+ case VariableLocation::PARAMETER:
+ case VariableLocation::LOCAL:
+ case VariableLocation::CONTEXT: {
// Local var, const, or let variable or context variable.
- return jsgraph()->BooleanConstant(variable->is_this());
- case Variable::LOOKUP: {
+ return jsgraph()->BooleanConstant(variable->HasThisName(isolate()));
+ }
+ case VariableLocation::LOOKUP: {
// Dynamic lookup of context variable (anywhere in the chain).
Node* name = jsgraph()->Constant(variable->name());
const Operator* op =
javascript()->CallRuntime(Runtime::kDeleteLookupSlot, 2);
Node* result = NewNode(op, current_context(), name);
- PrepareFrameState(result, bailout_id, state_combine);
+ PrepareFrameState(result, bailout_id, combine);
return result;
}
}
UNREACHABLE();
- return NULL;
+ return nullptr;
}
Node* AstGraphBuilder::BuildVariableAssignment(
- Variable* variable, Node* value, Token::Value op, BailoutId bailout_id,
- OutputFrameStateCombine combine) {
+ Variable* variable, Node* value, Token::Value op,
+ const VectorSlotPair& feedback, BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states, OutputFrameStateCombine combine) {
Node* the_hole = jsgraph()->TheHoleConstant();
VariableMode mode = variable->mode();
switch (variable->location()) {
- case Variable::UNALLOCATED: {
+ case VariableLocation::GLOBAL:
+ case VariableLocation::UNALLOCATED: {
// Global var, const, or let variable.
- Node* global = BuildLoadGlobalObject();
- Unique<Name> name = MakeUnique(variable->name());
- const Operator* op = javascript()->StoreNamed(strict_mode(), name);
- Node* store = NewNode(op, global, value);
- PrepareFrameState(store, bailout_id, combine);
+ Handle<Name> name = variable->name();
+ Node* store = BuildGlobalStore(name, value, feedback);
+ states.AddToNode(store, bailout_id, combine);
return store;
}
- case Variable::PARAMETER:
- case Variable::LOCAL:
+ case VariableLocation::PARAMETER:
+ case VariableLocation::LOCAL:
// Local var, const, or let variable.
- if (mode == CONST_LEGACY && op == Token::INIT_CONST_LEGACY) {
+ if (mode == CONST_LEGACY && op == Token::INIT) {
// Perform an initialization check for legacy const variables.
Node* current = environment()->Lookup(variable);
if (current->op() != the_hole->op()) {
value = BuildHoleCheckSilent(current, value, current);
}
- } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) {
- // Non-initializing assignments to legacy const is
+ } else if (mode == CONST_LEGACY && op != Token::INIT) {
+ // Non-initializing assignment to legacy const is
// - exception in strict mode.
// - ignored in sloppy mode.
- if (strict_mode() == STRICT) {
+ if (is_strict(language_mode())) {
return BuildThrowConstAssignError(bailout_id);
}
return value;
- } else if (mode == LET && op != Token::INIT_LET) {
+ } else if (mode == LET && op == Token::INIT) {
+ // No initialization check needed because scoping guarantees it. Note
+ // that we still perform a lookup to keep the variable live, because
+ // baseline code might contain debug code that inspects the variable.
+ Node* current = environment()->Lookup(variable);
+ CHECK_NOT_NULL(current);
+ } else if (mode == LET && op != Token::INIT) {
// Perform an initialization check for let declared variables.
- // Also note that the dynamic hole-check is only done to ensure that
- // this does not break in the presence of do-expressions within the
- // temporal dead zone of a let declared variable.
Node* current = environment()->Lookup(variable);
if (current->op() == the_hole->op()) {
- value = BuildThrowReferenceError(variable, bailout_id);
- } else if (value->opcode() == IrOpcode::kPhi) {
- value = BuildHoleCheckThrow(current, variable, value, bailout_id);
+ return BuildThrowReferenceError(variable, bailout_id);
+ } else if (current->opcode() == IrOpcode::kPhi) {
+ BuildHoleCheckThenThrow(current, variable, value, bailout_id);
}
- } else if (mode == CONST && op != Token::INIT_CONST) {
- // Non-initializing assignments to const is exception in all modes.
+ } else if (mode == CONST && op == Token::INIT) {
+ // Perform an initialization check for const {this} variables.
+ // Note that the {this} variable is the only const variable being able
+ // to trigger bind operations outside the TDZ, via {super} calls.
+ Node* current = environment()->Lookup(variable);
+ if (current->op() != the_hole->op() && variable->is_this()) {
+ value = BuildHoleCheckElseThrow(current, variable, value, bailout_id);
+ }
+ } else if (mode == CONST && op != Token::INIT) {
+ // Assignment to const is exception in all modes.
+ Node* current = environment()->Lookup(variable);
+ if (current->op() == the_hole->op()) {
+ return BuildThrowReferenceError(variable, bailout_id);
+ } else if (current->opcode() == IrOpcode::kPhi) {
+ BuildHoleCheckThenThrow(current, variable, value, bailout_id);
+ }
return BuildThrowConstAssignError(bailout_id);
}
environment()->Bind(variable, value);
return value;
- case Variable::CONTEXT: {
+ case VariableLocation::CONTEXT: {
// Context variable (potentially up the context chain).
int depth = current_scope()->ContextChainLength(variable->scope());
- if (mode == CONST_LEGACY && op == Token::INIT_CONST_LEGACY) {
+ if (mode == CONST_LEGACY && op == Token::INIT) {
// Perform an initialization check for legacy const variables.
const Operator* op =
javascript()->LoadContext(depth, variable->index(), false);
Node* current = NewNode(op, current_context());
value = BuildHoleCheckSilent(current, value, current);
- } else if (mode == CONST_LEGACY && op != Token::INIT_CONST_LEGACY) {
- // Non-initializing assignments to legacy const is
+ } else if (mode == CONST_LEGACY && op != Token::INIT) {
+ // Non-initializing assignment to legacy const is
// - exception in strict mode.
// - ignored in sloppy mode.
- if (strict_mode() == STRICT) {
+ if (is_strict(language_mode())) {
return BuildThrowConstAssignError(bailout_id);
}
return value;
- } else if (mode == LET && op != Token::INIT_LET) {
+ } else if (mode == LET && op != Token::INIT) {
// Perform an initialization check for let declared variables.
const Operator* op =
javascript()->LoadContext(depth, variable->index(), false);
Node* current = NewNode(op, current_context());
- value = BuildHoleCheckThrow(current, variable, value, bailout_id);
- } else if (mode == CONST && op != Token::INIT_CONST) {
- // Non-initializing assignments to const is exception in all modes.
+ value = BuildHoleCheckThenThrow(current, variable, value, bailout_id);
+ } else if (mode == CONST && op == Token::INIT) {
+ // Perform an initialization check for const {this} variables.
+ // Note that the {this} variable is the only const variable being able
+ // to trigger bind operations outside the TDZ, via {super} calls.
+ if (variable->is_this()) {
+ const Operator* op =
+ javascript()->LoadContext(depth, variable->index(), false);
+ Node* current = NewNode(op, current_context());
+ value = BuildHoleCheckElseThrow(current, variable, value, bailout_id);
+ }
+ } else if (mode == CONST && op != Token::INIT) {
+ // Assignment to const is exception in all modes.
+ const Operator* op =
+ javascript()->LoadContext(depth, variable->index(), false);
+ Node* current = NewNode(op, current_context());
+ BuildHoleCheckThenThrow(current, variable, value, bailout_id);
return BuildThrowConstAssignError(bailout_id);
}
const Operator* op = javascript()->StoreContext(depth, variable->index());
return NewNode(op, current_context(), value);
}
- case Variable::LOOKUP: {
+ case VariableLocation::LOOKUP: {
// Dynamic lookup of context variable (anywhere in the chain).
Node* name = jsgraph()->Constant(variable->name());
- Node* strict = jsgraph()->Constant(strict_mode());
+ Node* language = jsgraph()->Constant(language_mode());
// TODO(mstarzinger): Use Runtime::kInitializeLegacyConstLookupSlot for
// initializations of const declarations.
const Operator* op =
javascript()->CallRuntime(Runtime::kStoreLookupSlot, 4);
- Node* store = NewNode(op, value, current_context(), name, strict);
+ Node* store = NewNode(op, value, current_context(), name, language);
PrepareFrameState(store, bailout_id, combine);
return store;
}
}
UNREACHABLE();
- return NULL;
+ return nullptr;
+}
+
+
+Node* AstGraphBuilder::BuildKeyedLoad(Node* object, Node* key,
+ const VectorSlotPair& feedback) {
+ const Operator* op = javascript()->LoadProperty(language_mode(), feedback);
+ Node* node = NewNode(op, object, key, BuildLoadFeedbackVector());
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildNamedLoad(Node* object, Handle<Name> name,
+ const VectorSlotPair& feedback) {
+ const Operator* op = javascript()->LoadNamed(language_mode(), name, feedback);
+ Node* node = NewNode(op, object, BuildLoadFeedbackVector());
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildKeyedStore(Node* object, Node* key, Node* value,
+ const VectorSlotPair& feedback) {
+ const Operator* op = javascript()->StoreProperty(language_mode(), feedback);
+ Node* node = NewNode(op, object, key, value, BuildLoadFeedbackVector());
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildNamedStore(Node* object, Handle<Name> name,
+ Node* value,
+ const VectorSlotPair& feedback) {
+ const Operator* op =
+ javascript()->StoreNamed(language_mode(), name, feedback);
+ Node* node = NewNode(op, object, value, BuildLoadFeedbackVector());
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildNamedSuperLoad(Node* receiver, Node* home_object,
+ Handle<Name> name,
+ const VectorSlotPair& feedback) {
+ Node* name_node = jsgraph()->Constant(name);
+ Node* language = jsgraph()->Constant(language_mode());
+ const Operator* op = javascript()->CallRuntime(Runtime::kLoadFromSuper, 4);
+ Node* node = NewNode(op, receiver, home_object, name_node, language);
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildKeyedSuperLoad(Node* receiver, Node* home_object,
+ Node* key,
+ const VectorSlotPair& feedback) {
+ Node* language = jsgraph()->Constant(language_mode());
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kLoadKeyedFromSuper, 4);
+ Node* node = NewNode(op, receiver, home_object, key, language);
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildKeyedSuperStore(Node* receiver, Node* home_object,
+ Node* key, Node* value) {
+ Runtime::FunctionId function_id = is_strict(language_mode())
+ ? Runtime::kStoreKeyedToSuper_Strict
+ : Runtime::kStoreKeyedToSuper_Sloppy;
+ const Operator* op = javascript()->CallRuntime(function_id, 4);
+ Node* node = NewNode(op, receiver, home_object, key, value);
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildNamedSuperStore(Node* receiver, Node* home_object,
+ Handle<Name> name, Node* value) {
+ Node* name_node = jsgraph()->Constant(name);
+ Runtime::FunctionId function_id = is_strict(language_mode())
+ ? Runtime::kStoreToSuper_Strict
+ : Runtime::kStoreToSuper_Sloppy;
+ const Operator* op = javascript()->CallRuntime(function_id, 4);
+ Node* node = NewNode(op, receiver, home_object, name_node, value);
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildGlobalLoad(Handle<Name> name,
+ const VectorSlotPair& feedback,
+ TypeofMode typeof_mode) {
+ const Operator* op = javascript()->LoadGlobal(name, feedback, typeof_mode);
+ Node* node = NewNode(op, BuildLoadFeedbackVector());
+ return node;
+}
+
+
+Node* AstGraphBuilder::BuildGlobalStore(Handle<Name> name, Node* value,
+ const VectorSlotPair& feedback) {
+ const Operator* op =
+ javascript()->StoreGlobal(language_mode(), name, feedback);
+ Node* node = NewNode(op, value, BuildLoadFeedbackVector());
+ return node;
}
Node* AstGraphBuilder::BuildLoadObjectField(Node* object, int offset) {
- Node* field_load = NewNode(jsgraph()->machine()->Load(kMachAnyTagged), object,
- jsgraph()->Int32Constant(offset - kHeapObjectTag));
- return field_load;
+ return NewNode(jsgraph()->machine()->Load(MachineType::AnyTagged()), object,
+ jsgraph()->IntPtrConstant(offset - kHeapObjectTag));
}
-Node* AstGraphBuilder::BuildLoadBuiltinsObject() {
- Node* global = BuildLoadGlobalObject();
- Node* builtins =
- BuildLoadObjectField(global, JSGlobalObject::kBuiltinsOffset);
- return builtins;
+Node* AstGraphBuilder::BuildLoadImmutableObjectField(Node* object, int offset) {
+ return graph()->NewNode(jsgraph()->machine()->Load(MachineType::AnyTagged()),
+ object,
+ jsgraph()->IntPtrConstant(offset - kHeapObjectTag),
+ graph()->start(), graph()->start());
}
Node* AstGraphBuilder::BuildLoadGlobalObject() {
- Node* context = GetFunctionContext();
- const Operator* load_op =
- javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true);
- return NewNode(load_op, context);
+ return BuildLoadNativeContextField(Context::EXTENSION_INDEX);
}
-Node* AstGraphBuilder::BuildLoadGlobalProxy() {
- Node* global = BuildLoadGlobalObject();
- Node* proxy =
- BuildLoadObjectField(global, JSGlobalObject::kGlobalProxyOffset);
- return proxy;
+Node* AstGraphBuilder::BuildLoadNativeContextField(int index) {
+ const Operator* op =
+ javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true);
+ Node* native_context = NewNode(op, current_context());
+ return NewNode(javascript()->LoadContext(0, index, true), native_context);
}
-Node* AstGraphBuilder::BuildToBoolean(Node* input) {
- // TODO(titzer): this should be in a JSOperatorReducer.
- switch (input->opcode()) {
- case IrOpcode::kInt32Constant:
- return jsgraph_->BooleanConstant(!Int32Matcher(input).Is(0));
- case IrOpcode::kFloat64Constant:
- return jsgraph_->BooleanConstant(!Float64Matcher(input).Is(0));
- case IrOpcode::kNumberConstant:
- return jsgraph_->BooleanConstant(!NumberMatcher(input).Is(0));
- case IrOpcode::kHeapConstant: {
- Handle<Object> object = HeapObjectMatcher<Object>(input).Value().handle();
- if (object->IsTrue()) return jsgraph_->TrueConstant();
- if (object->IsFalse()) return jsgraph_->FalseConstant();
- // TODO(turbofan): other constants.
- break;
- }
- default:
- break;
+Node* AstGraphBuilder::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);
}
- if (NodeProperties::IsTyped(input)) {
- Type* upper = NodeProperties::GetBounds(input).upper;
- if (upper->Is(Type::Boolean())) return input;
- }
+ return feedback_vector_.get();
+}
- return NewNode(javascript()->ToBoolean(), input);
+
+Node* AstGraphBuilder::BuildToBoolean(Node* input, TypeFeedbackId feedback_id) {
+ if (Node* node = TryFastToBoolean(input)) return node;
+ ToBooleanHints hints;
+ if (!type_hint_analysis_ ||
+ !type_hint_analysis_->GetToBooleanHints(feedback_id, &hints)) {
+ hints = ToBooleanHint::kAny;
+ }
+ return NewNode(javascript()->ToBoolean(hints), input);
+}
+
+
+Node* AstGraphBuilder::BuildToName(Node* input, BailoutId bailout_id) {
+ if (Node* node = TryFastToName(input)) return node;
+ Node* name = NewNode(javascript()->ToName(), input);
+ PrepareFrameState(name, bailout_id);
+ return name;
+}
+
+
+Node* AstGraphBuilder::BuildToObject(Node* input, BailoutId bailout_id) {
+ Node* object = NewNode(javascript()->ToObject(), input);
+ PrepareFrameState(object, bailout_id, OutputFrameStateCombine::Push());
+ return object;
+}
+
+
+Node* AstGraphBuilder::BuildSetHomeObject(Node* value, Node* home_object,
+ ObjectLiteralProperty* property,
+ int slot_number) {
+ Expression* expr = property->value();
+ if (!FunctionLiteral::NeedsHomeObject(expr)) return value;
+ Handle<Name> name = isolate()->factory()->home_object_symbol();
+ FrameStateBeforeAndAfter states(this, BailoutId::None());
+ VectorSlotPair feedback =
+ CreateVectorSlotPair(property->GetSlot(slot_number));
+ Node* store = BuildNamedStore(value, name, home_object, feedback);
+ states.AddToNode(store, BailoutId::None(), OutputFrameStateCombine::Ignore());
+ return store;
+}
+
+
+Node* AstGraphBuilder::BuildThrowError(Node* exception, BailoutId bailout_id) {
+ const Operator* op = javascript()->CallRuntime(Runtime::kThrow, 1);
+ Node* call = NewNode(op, exception);
+ PrepareFrameState(call, bailout_id);
+ Node* control = NewNode(common()->Throw(), call);
+ UpdateControlDependencyToLeaveFunction(control);
+ return call;
}
Node* AstGraphBuilder::BuildThrowReferenceError(Variable* variable,
BailoutId bailout_id) {
- // TODO(mstarzinger): Should be unified with the VisitThrow implementation.
Node* variable_name = jsgraph()->Constant(variable->name());
const Operator* op =
javascript()->CallRuntime(Runtime::kThrowReferenceError, 1);
Node* call = NewNode(op, variable_name);
PrepareFrameState(call, bailout_id);
+ Node* control = NewNode(common()->Throw(), call);
+ UpdateControlDependencyToLeaveFunction(control);
return call;
}
Node* AstGraphBuilder::BuildThrowConstAssignError(BailoutId bailout_id) {
- // TODO(mstarzinger): Should be unified with the VisitThrow implementation.
const Operator* op =
javascript()->CallRuntime(Runtime::kThrowConstAssignError, 0);
Node* call = NewNode(op);
PrepareFrameState(call, bailout_id);
+ Node* control = NewNode(common()->Throw(), call);
+ UpdateControlDependencyToLeaveFunction(control);
return call;
}
-Node* AstGraphBuilder::BuildBinaryOp(Node* left, Node* right, Token::Value op) {
+Node* AstGraphBuilder::BuildThrowStaticPrototypeError(BailoutId bailout_id) {
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kThrowStaticPrototypeError, 0);
+ Node* call = NewNode(op);
+ PrepareFrameState(call, bailout_id);
+ Node* control = NewNode(common()->Throw(), call);
+ UpdateControlDependencyToLeaveFunction(control);
+ return call;
+}
+
+
+Node* AstGraphBuilder::BuildThrowUnsupportedSuperError(BailoutId bailout_id) {
+ const Operator* op =
+ javascript()->CallRuntime(Runtime::kThrowUnsupportedSuperError, 0);
+ Node* call = NewNode(op);
+ PrepareFrameState(call, bailout_id);
+ Node* control = NewNode(common()->Throw(), call);
+ UpdateControlDependencyToLeaveFunction(control);
+ return call;
+}
+
+
+Node* AstGraphBuilder::BuildReturn(Node* return_value) {
+ Node* control = NewNode(common()->Return(), return_value);
+ UpdateControlDependencyToLeaveFunction(control);
+ return control;
+}
+
+
+Node* AstGraphBuilder::BuildThrow(Node* exception_value) {
+ NewNode(javascript()->CallRuntime(Runtime::kReThrow, 1), exception_value);
+ Node* control = NewNode(common()->Throw(), exception_value);
+ UpdateControlDependencyToLeaveFunction(control);
+ return control;
+}
+
+
+Node* AstGraphBuilder::BuildBinaryOp(Node* left, Node* right, Token::Value op,
+ TypeFeedbackId feedback_id) {
const Operator* js_op;
+ BinaryOperationHints hints;
+ if (!type_hint_analysis_ ||
+ !type_hint_analysis_->GetBinaryOperationHints(feedback_id, &hints)) {
+ hints = BinaryOperationHints::Any();
+ }
switch (op) {
case Token::BIT_OR:
- js_op = javascript()->BitwiseOr();
+ js_op = javascript()->BitwiseOr(language_mode(), hints);
break;
case Token::BIT_AND:
- js_op = javascript()->BitwiseAnd();
+ js_op = javascript()->BitwiseAnd(language_mode(), hints);
break;
case Token::BIT_XOR:
- js_op = javascript()->BitwiseXor();
+ js_op = javascript()->BitwiseXor(language_mode(), hints);
break;
case Token::SHL:
- js_op = javascript()->ShiftLeft();
+ js_op = javascript()->ShiftLeft(language_mode(), hints);
break;
case Token::SAR:
- js_op = javascript()->ShiftRight();
+ js_op = javascript()->ShiftRight(language_mode(), hints);
break;
case Token::SHR:
- js_op = javascript()->ShiftRightLogical();
+ js_op = javascript()->ShiftRightLogical(language_mode(), hints);
break;
case Token::ADD:
- js_op = javascript()->Add();
+ js_op = javascript()->Add(language_mode(), hints);
break;
case Token::SUB:
- js_op = javascript()->Subtract();
+ js_op = javascript()->Subtract(language_mode(), hints);
break;
case Token::MUL:
- js_op = javascript()->Multiply();
+ js_op = javascript()->Multiply(language_mode(), hints);
break;
case Token::DIV:
- js_op = javascript()->Divide();
+ js_op = javascript()->Divide(language_mode(), hints);
break;
case Token::MOD:
- js_op = javascript()->Modulus();
+ js_op = javascript()->Modulus(language_mode(), hints);
break;
default:
UNREACHABLE();
- js_op = NULL;
+ js_op = nullptr;
}
return NewNode(js_op, left, right);
}
-Node* AstGraphBuilder::BuildStackCheck() {
- IfBuilder stack_check(this);
- Node* limit =
- NewNode(jsgraph()->machine()->Load(kMachPtr),
- jsgraph()->ExternalConstant(
- ExternalReference::address_of_stack_limit(isolate())),
- jsgraph()->ZeroConstant());
- Node* stack = NewNode(jsgraph()->machine()->LoadStackPointer());
- Node* tag = NewNode(jsgraph()->machine()->UintLessThan(), limit, stack);
- stack_check.If(tag, BranchHint::kTrue);
- stack_check.Then();
- stack_check.Else();
- Node* guard = NewNode(javascript()->CallRuntime(Runtime::kStackGuard, 0));
- stack_check.End();
- return guard;
+Node* AstGraphBuilder::TryLoadGlobalConstant(Handle<Name> name) {
+ // Optimize global constants like "undefined", "Infinity", and "NaN".
+ Handle<Object> constant_value = isolate()->factory()->GlobalConstantFor(name);
+ if (!constant_value.is_null()) return jsgraph()->Constant(constant_value);
+ return nullptr;
+}
+
+
+Node* AstGraphBuilder::TryLoadDynamicVariable(
+ Variable* variable, Handle<String> name, BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states, const VectorSlotPair& feedback,
+ OutputFrameStateCombine combine, TypeofMode typeof_mode) {
+ VariableMode mode = variable->mode();
+
+ if (mode == DYNAMIC_GLOBAL) {
+ uint32_t bitset = ComputeBitsetForDynamicGlobal(variable);
+ if (bitset == kFullCheckRequired) return nullptr;
+
+ // We are using two blocks to model fast and slow cases.
+ BlockBuilder fast_block(this);
+ BlockBuilder slow_block(this);
+ environment()->Push(jsgraph()->TheHoleConstant());
+ slow_block.BeginBlock();
+ environment()->Pop();
+ fast_block.BeginBlock();
+
+ // Perform checks whether the fast mode applies, by looking for any
+ // extension object which might shadow the optimistic declaration.
+ for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
+ if ((bitset & 1) == 0) continue;
+ Node* load = NewNode(
+ javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
+ current_context());
+ Node* check = NewNode(javascript()->StrictEqual(), load,
+ jsgraph()->TheHoleConstant());
+ fast_block.BreakUnless(check, BranchHint::kTrue);
+ }
+
+ // Fast case, because variable is not shadowed. Perform global slot load.
+ Node* fast = BuildGlobalLoad(name, feedback, typeof_mode);
+ states.AddToNode(fast, bailout_id, combine);
+ environment()->Push(fast);
+ slow_block.Break();
+ environment()->Pop();
+ fast_block.EndBlock();
+
+ // Slow case, because variable potentially shadowed. Perform dynamic lookup.
+ const Operator* op = javascript()->LoadDynamic(name, typeof_mode);
+ Node* slow = NewNode(op, BuildLoadFeedbackVector(), current_context());
+ states.AddToNode(slow, bailout_id, combine);
+ environment()->Push(slow);
+ slow_block.EndBlock();
+
+ return environment()->Pop();
+ }
+
+ if (mode == DYNAMIC_LOCAL) {
+ uint32_t bitset = ComputeBitsetForDynamicContext(variable);
+ if (bitset == kFullCheckRequired) return nullptr;
+
+ // We are using two blocks to model fast and slow cases.
+ BlockBuilder fast_block(this);
+ BlockBuilder slow_block(this);
+ environment()->Push(jsgraph()->TheHoleConstant());
+ slow_block.BeginBlock();
+ environment()->Pop();
+ fast_block.BeginBlock();
+
+ // Perform checks whether the fast mode applies, by looking for any
+ // extension object which might shadow the optimistic declaration.
+ for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
+ if ((bitset & 1) == 0) continue;
+ Node* load = NewNode(
+ javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
+ current_context());
+ Node* check = NewNode(javascript()->StrictEqual(), load,
+ jsgraph()->TheHoleConstant());
+ fast_block.BreakUnless(check, BranchHint::kTrue);
+ }
+
+ // Fast case, because variable is not shadowed. Perform context slot load.
+ Variable* local = variable->local_if_not_shadowed();
+ DCHECK(local->location() == VariableLocation::CONTEXT); // Must be context.
+ Node* fast = BuildVariableLoad(local, bailout_id, states, feedback, combine,
+ typeof_mode);
+ environment()->Push(fast);
+ slow_block.Break();
+ environment()->Pop();
+ fast_block.EndBlock();
+
+ // Slow case, because variable potentially shadowed. Perform dynamic lookup.
+ const Operator* op = javascript()->LoadDynamic(name, typeof_mode);
+ Node* slow = NewNode(op, BuildLoadFeedbackVector(), current_context());
+ states.AddToNode(slow, bailout_id, combine);
+ environment()->Push(slow);
+ slow_block.EndBlock();
+
+ return environment()->Pop();
+ }
+
+ return nullptr;
+}
+
+
+Node* AstGraphBuilder::TryFastToBoolean(Node* input) {
+ switch (input->opcode()) {
+ case IrOpcode::kNumberConstant: {
+ NumberMatcher m(input);
+ return jsgraph_->BooleanConstant(!m.Is(0) && !m.IsNaN());
+ }
+ case IrOpcode::kHeapConstant: {
+ Handle<HeapObject> object = HeapObjectMatcher(input).Value();
+ return jsgraph_->BooleanConstant(object->BooleanValue());
+ }
+ case IrOpcode::kJSEqual:
+ case IrOpcode::kJSNotEqual:
+ case IrOpcode::kJSStrictEqual:
+ case IrOpcode::kJSStrictNotEqual:
+ case IrOpcode::kJSLessThan:
+ case IrOpcode::kJSLessThanOrEqual:
+ case IrOpcode::kJSGreaterThan:
+ case IrOpcode::kJSGreaterThanOrEqual:
+ case IrOpcode::kJSToBoolean:
+ case IrOpcode::kJSDeleteProperty:
+ case IrOpcode::kJSHasProperty:
+ case IrOpcode::kJSInstanceOf:
+ return input;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+
+Node* AstGraphBuilder::TryFastToName(Node* input) {
+ switch (input->opcode()) {
+ case IrOpcode::kHeapConstant: {
+ Handle<HeapObject> object = HeapObjectMatcher(input).Value();
+ if (object->IsName()) return input;
+ break;
+ }
+ case IrOpcode::kJSToString:
+ case IrOpcode::kJSToName:
+ case IrOpcode::kJSTypeOf:
+ return input;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+
+bool AstGraphBuilder::CheckOsrEntry(IterationStatement* stmt) {
+ if (info()->osr_ast_id() == stmt->OsrEntryId()) {
+ info()->set_osr_expr_stack_height(std::max(
+ environment()->stack_height(), info()->osr_expr_stack_height()));
+ return true;
+ }
+ return false;
}
void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id,
OutputFrameStateCombine combine) {
- if (OperatorProperties::HasFrameStateInput(node->op())) {
- DCHECK(NodeProperties::GetFrameStateInput(node)->opcode() ==
- IrOpcode::kDead);
+ if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
+ DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
+
+ DCHECK_EQ(IrOpcode::kDead,
+ NodeProperties::GetFrameStateInput(node, 0)->opcode());
NodeProperties::ReplaceFrameStateInput(
- node, environment()->Checkpoint(ast_id, combine));
+ node, 0, environment()->Checkpoint(ast_id, combine));
}
}
BitVector* AstGraphBuilder::GetVariablesAssignedInLoop(
IterationStatement* stmt) {
- if (loop_assignment_analysis_ == NULL) return NULL;
+ if (loop_assignment_analysis_ == nullptr) return nullptr;
return loop_assignment_analysis_->GetVariablesAssignedInLoop(stmt);
}
+
+Node** AstGraphBuilder::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_;
+}
+
+
+Node* AstGraphBuilder::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(op->ControlInputCount() < 2);
+ DCHECK(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 {
+ bool inside_try_scope = try_nesting_level_ > 0;
+ 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++ = current_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 exception continuation for throwing nodes.
+ if (!result->op()->HasProperty(Operator::kNoThrow) && inside_try_scope) {
+ // Conservative prediction whether caught locally.
+ IfExceptionHint hint = try_catch_nesting_level_ > 0
+ ? IfExceptionHint::kLocallyCaught
+ : IfExceptionHint::kLocallyUncaught;
+ // Copy the environment for the success continuation.
+ Environment* success_env = environment()->CopyForConditional();
+ const Operator* op = common()->IfException(hint);
+ Node* effect = environment()->GetEffectDependency();
+ Node* on_exception = graph()->NewNode(op, effect, result);
+ environment_->UpdateControlDependency(on_exception);
+ environment_->UpdateEffectDependency(on_exception);
+ execution_control()->ThrowValue(on_exception);
+ set_environment(success_env);
+ }
+ // Add implicit success continuation for throwing nodes.
+ if (!result->op()->HasProperty(Operator::kNoThrow)) {
+ const Operator* op = common()->IfSuccess();
+ Node* on_success = graph()->NewNode(op, result);
+ environment_->UpdateControlDependency(on_success);
+ }
+ }
+ }
+
+ return result;
+}
+
+
+void AstGraphBuilder::UpdateControlDependencyToLeaveFunction(Node* exit) {
+ if (environment()->IsMarkedAsUnreachable()) return;
+ environment()->MarkAsUnreachable();
+ exit_controls_.push_back(exit);
+}
+
+
+void AstGraphBuilder::Environment::Merge(Environment* other) {
+ DCHECK(values_.size() == other->values_.size());
+ DCHECK(contexts_.size() == other->contexts_.size());
+
+ // Nothing to do if the other environment is dead.
+ if (other->IsMarkedAsUnreachable()) return;
+
+ // Resurrect a dead environment by copying the contents of the other one and
+ // placing a singleton merge as the new control dependency.
+ if (this->IsMarkedAsUnreachable()) {
+ Node* other_control = other->control_dependency_;
+ Node* inputs[] = {other_control};
+ control_dependency_ =
+ graph()->NewNode(common()->Merge(1), arraysize(inputs), inputs, true);
+ effect_dependency_ = other->effect_dependency_;
+ values_ = other->values_;
+ contexts_ = other->contexts_;
+ if (IsLivenessAnalysisEnabled()) {
+ liveness_block_ =
+ builder_->liveness_analyzer()->NewBlock(other->liveness_block());
+ }
+ return;
+ }
+
+ // Record the merge for the local variable liveness calculation.
+ // For loops, we are connecting a back edge into the existing block;
+ // for merges, we create a new merged block.
+ if (IsLivenessAnalysisEnabled()) {
+ if (GetControlDependency()->opcode() != IrOpcode::kLoop) {
+ liveness_block_ =
+ builder_->liveness_analyzer()->NewBlock(liveness_block());
+ }
+ liveness_block()->AddPredecessor(other->liveness_block());
+ }
+
+ // Create a merge of the control dependencies of both environments and update
+ // the current environment's control dependency accordingly.
+ Node* control = builder_->MergeControl(this->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(this->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.
+ for (int i = 0; i < static_cast<int>(values_.size()); ++i) {
+ values_[i] = builder_->MergeValue(values_[i], other->values_[i], control);
+ }
+ for (int i = 0; i < static_cast<int>(contexts_.size()); ++i) {
+ contexts_[i] =
+ builder_->MergeValue(contexts_[i], other->contexts_[i], control);
+ }
+}
+
+
+void AstGraphBuilder::Environment::PrepareForLoop(BitVector* assigned,
+ bool is_osr) {
+ int size = static_cast<int>(values()->size());
+
+ Node* control = builder_->NewLoop();
+ if (assigned == nullptr) {
+ // Assume that everything is updated in the loop.
+ for (int i = 0; i < size; ++i) {
+ values()->at(i) = builder_->NewPhi(1, values()->at(i), control);
+ }
+ } else {
+ // Only build phis for those locals assigned in this loop.
+ for (int i = 0; i < size; ++i) {
+ if (i < assigned->length() && !assigned->Contains(i)) continue;
+ Node* phi = builder_->NewPhi(1, values()->at(i), control);
+ values()->at(i) = phi;
+ }
+ }
+ Node* effect = builder_->NewEffectPhi(1, GetEffectDependency(), control);
+ UpdateEffectDependency(effect);
+
+ // Connect the loop to end via Terminate if it's not marked as unreachable.
+ if (!IsMarkedAsUnreachable()) {
+ // Connect the Loop node to end via a Terminate node.
+ Node* terminate = builder_->graph()->NewNode(
+ builder_->common()->Terminate(), effect, control);
+ builder_->exit_controls_.push_back(terminate);
+ }
+
+ if (builder_->info()->is_osr()) {
+ // Introduce phis for all context values in the case of an OSR graph.
+ for (size_t i = 0; i < contexts()->size(); ++i) {
+ Node* context = contexts()->at(i);
+ contexts()->at(i) = builder_->NewPhi(1, context, control);
+ }
+ }
+
+ if (is_osr) {
+ // Merge OSR values as inputs to the phis of the loop.
+ Graph* graph = builder_->graph();
+ Node* osr_loop_entry = builder_->graph()->NewNode(
+ builder_->common()->OsrLoopEntry(), graph->start(), graph->start());
+
+ builder_->MergeControl(control, osr_loop_entry);
+ builder_->MergeEffect(effect, osr_loop_entry, control);
+
+ for (int i = 0; i < size; ++i) {
+ Node* value = values()->at(i);
+ Node* osr_value =
+ graph->NewNode(builder_->common()->OsrValue(i), osr_loop_entry);
+ values()->at(i) = builder_->MergeValue(value, osr_value, control);
+ }
+
+ // Rename all the contexts in the environment.
+ // The innermost context is the OSR value, and the outer contexts are
+ // reconstructed by dynamically walking up the context chain.
+ Node* osr_context = nullptr;
+ const Operator* op =
+ builder_->javascript()->LoadContext(0, Context::PREVIOUS_INDEX, true);
+ const Operator* op_inner =
+ builder_->common()->OsrValue(Linkage::kOsrContextSpillSlotIndex);
+ int last = static_cast<int>(contexts()->size() - 1);
+ for (int i = last; i >= 0; i--) {
+ Node* context = contexts()->at(i);
+ osr_context = (i == last) ? graph->NewNode(op_inner, osr_loop_entry)
+ : graph->NewNode(op, osr_context, osr_context,
+ osr_loop_entry);
+ contexts()->at(i) = builder_->MergeValue(context, osr_context, control);
+ }
+ }
+}
+
+
+Node* AstGraphBuilder::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);
+}
+
+
+// TODO(mstarzinger): Revisit this once we have proper effect states.
+Node* AstGraphBuilder::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* AstGraphBuilder::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* inputs[] = {control, other};
+ control = graph()->NewNode(op, arraysize(inputs), inputs, true);
+ }
+ return control;
+}
+
+
+Node* AstGraphBuilder::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* AstGraphBuilder::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;
+}
+
} // namespace compiler
} // namespace internal
} // namespace v8