Reland "Make FrameStates recursive (to be used for inlining).".
authorsigurds@chromium.org <sigurds@chromium.org>
Wed, 3 Sep 2014 14:10:20 +0000 (14:10 +0000)
committersigurds@chromium.org <sigurds@chromium.org>
Wed, 3 Sep 2014 14:10:20 +0000 (14:10 +0000)
Reland fixes:
- Verifier is now aware of sentinel value for FrameState chains.

R=jarin@chromium.org

Review URL: https://codereview.chromium.org/534573002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23662 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

14 files changed:
src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/compiler/code-generator.cc
src/compiler/code-generator.h
src/compiler/graph-builder.cc
src/compiler/instruction-selector-impl.h
src/compiler/instruction-selector-unittest.cc
src/compiler/instruction-selector.cc
src/compiler/instruction.h
src/compiler/node.h
src/compiler/operator-properties-inl.h
src/compiler/raw-machine-assembler.h
src/compiler/verifier.cc
test/cctest/compiler/test-codegen-deopt.cc

index 5ad8269..611b050 100644 (file)
@@ -227,7 +227,8 @@ Node* AstGraphBuilder::Environment::Checkpoint(
   Operator* op = common()->FrameState(ast_id, combine);
 
   return graph()->NewNode(op, parameters_node_, locals_node_, stack_node_,
-                          GetContext());
+                          GetContext(),
+                          builder()->jsgraph()->UndefinedConstant());
 }
 
 
index 9792a18..65a46b6 100644 (file)
@@ -260,6 +260,12 @@ class AstGraphBuilder::Environment
   // further mutation of the environment will not affect checkpoints.
   Node* Checkpoint(BailoutId ast_id, OutputFrameStateCombine combine);
 
+ protected:
+  AstGraphBuilder* builder() const {
+    return reinterpret_cast<AstGraphBuilder*>(
+        StructuredGraphBuilder::Environment::builder());
+  }
+
  private:
   void UpdateStateValues(Node** state_values, int offset, int count);
 
index f85168e..8f034fc 100644 (file)
@@ -301,11 +301,16 @@ FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor(
 }
 
 
-int CodeGenerator::BuildTranslation(Instruction* instr, int frame_state_offset,
-                                    OutputFrameStateCombine state_combine) {
-  FrameStateDescriptor* descriptor =
-      GetFrameStateDescriptor(instr, frame_state_offset);
-  frame_state_offset++;
+void CodeGenerator::BuildTranslationForFrameStateDescriptor(
+    FrameStateDescriptor* descriptor, Instruction* instr,
+    Translation* translation, int frame_state_offset,
+    OutputFrameStateCombine state_combine) {
+  // Outer-most state must be added to translation first.
+  if (descriptor->outer_state() != NULL) {
+    BuildTranslationForFrameStateDescriptor(
+        descriptor->outer_state(), instr, translation,
+        frame_state_offset + descriptor->size(), kIgnoreOutput);
+  }
 
   int height = descriptor->size() - descriptor->parameters_count();
   switch (state_combine) {
@@ -316,24 +321,35 @@ int CodeGenerator::BuildTranslation(Instruction* instr, int frame_state_offset,
       break;
   }
 
-
-  Translation translation(&translations_, 1, 1, zone());
-  translation.BeginJSFrame(descriptor->bailout_id(),
-                           Translation::kSelfLiteralId, height);
+  translation->BeginJSFrame(descriptor->bailout_id(),
+                            Translation::kSelfLiteralId, height);
 
   for (int i = 0; i < descriptor->size(); i++) {
-    AddTranslationForOperand(&translation, instr,
+    AddTranslationForOperand(translation, instr,
                              instr->InputAt(i + frame_state_offset));
   }
 
   switch (state_combine) {
     case kPushOutput:
       DCHECK(instr->OutputCount() == 1);
-      AddTranslationForOperand(&translation, instr, instr->OutputAt(0));
+      AddTranslationForOperand(translation, instr, instr->OutputAt(0));
       break;
     case kIgnoreOutput:
       break;
   }
+}
+
+
+int CodeGenerator::BuildTranslation(Instruction* instr, int frame_state_offset,
+                                    OutputFrameStateCombine state_combine) {
+  FrameStateDescriptor* descriptor =
+      GetFrameStateDescriptor(instr, frame_state_offset);
+  frame_state_offset++;
+
+  int frame_count = descriptor->GetFrameCount();
+  Translation translation(&translations_, frame_count, frame_count, zone());
+  BuildTranslationForFrameStateDescriptor(descriptor, instr, &translation,
+                                          frame_state_offset, state_combine);
 
   int deoptimization_id = static_cast<int>(deoptimization_states_.size());
 
index 0badbca..066a630 100644 (file)
@@ -91,6 +91,10 @@ class CodeGenerator FINAL : public GapResolver::Assembler {
                                                 int frame_state_offset);
   int BuildTranslation(Instruction* instr, int frame_state_offset,
                        OutputFrameStateCombine state_combine);
+  void BuildTranslationForFrameStateDescriptor(
+      FrameStateDescriptor* descriptor, Instruction* instr,
+      Translation* translation, int frame_state_offset,
+      OutputFrameStateCombine state_combine);
   void AddTranslationForOperand(Translation* translation, Instruction* instr,
                                 InstructionOperand* op);
   void AddNopForSmiCodeInlining();
index c41fd6f..f763a6e 100644 (file)
@@ -41,7 +41,7 @@ Node* StructuredGraphBuilder::MakeNode(Operator* op, int value_input_count,
   DCHECK(OperatorProperties::GetEffectInputCount(op) < 2);
 
   Node* result = NULL;
-  if (!has_context && !has_control && !has_effect) {
+  if (!has_context && !has_framestate && !has_control && !has_effect) {
     result = graph()->NewNode(op, value_input_count, value_inputs);
   } else {
     int input_count_with_deps = value_input_count;
index efe5d83..2a96ba0 100644 (file)
@@ -348,7 +348,8 @@ struct CallBuffer {
   size_t frame_state_value_count() const {
     return (frame_state_descriptor == NULL)
                ? 0
-               : (frame_state_descriptor->size() + 1);
+               : (frame_state_descriptor->total_size() +
+                  1);  // Include deopt id.
   }
 };
 
index 5a70d67..7b775f8 100644 (file)
@@ -298,8 +298,9 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
   Node* stack = m.NewNode(m.common()->StateValues(0));
   Node* context_dummy = m.Int32Constant(0);
 
-  Node* state_node = m.NewNode(m.common()->FrameState(bailout_id, kPushOutput),
-                               parameters, locals, stack, context_dummy);
+  Node* state_node =
+      m.NewNode(m.common()->FrameState(bailout_id, kPushOutput), parameters,
+                locals, stack, context_dummy, m.UndefinedConstant());
   Node* call = m.CallJS0(function_node, receiver, context, state_node);
   m.Return(call);
 
@@ -335,10 +336,11 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) {
   Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(43));
   Node* locals = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44));
   Node* stack = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45));
+
   Node* context_sentinel = m.Int32Constant(0);
-  Node* frame_state_before =
-      m.NewNode(m.common()->FrameState(bailout_id_before, kPushOutput),
-                parameters, locals, stack, context_sentinel);
+  Node* frame_state_before = m.NewNode(
+      m.common()->FrameState(bailout_id_before, kPushOutput), parameters,
+      locals, stack, context_sentinel, m.UndefinedConstant());
 
   // Build the call.
   Node* call = m.CallFunctionStub0(function_node, receiver, context,
@@ -394,6 +396,96 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) {
   EXPECT_EQ(index, s.size());
 }
 
+
+TARGET_TEST_F(InstructionSelectorTest,
+              CallFunctionStubDeoptRecursiveFrameState) {
+  StreamBuilder m(this, kMachAnyTagged, kMachAnyTagged, kMachAnyTagged,
+                  kMachAnyTagged);
+
+  BailoutId bailout_id_before(42);
+  BailoutId bailout_id_parent(62);
+
+  // Some arguments for the call node.
+  Node* function_node = m.Parameter(0);
+  Node* receiver = m.Parameter(1);
+  Node* context = m.Int32Constant(66);
+
+  // Build frame state for the state before the call.
+  Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(63));
+  Node* locals = m.NewNode(m.common()->StateValues(1), m.Int32Constant(64));
+  Node* stack = m.NewNode(m.common()->StateValues(1), m.Int32Constant(65));
+  Node* frame_state_parent =
+      m.NewNode(m.common()->FrameState(bailout_id_parent, kIgnoreOutput),
+                parameters, locals, stack, context, m.UndefinedConstant());
+
+  Node* context2 = m.Int32Constant(46);
+  Node* parameters2 =
+      m.NewNode(m.common()->StateValues(1), m.Int32Constant(43));
+  Node* locals2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44));
+  Node* stack2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45));
+  Node* frame_state_before =
+      m.NewNode(m.common()->FrameState(bailout_id_before, kPushOutput),
+                parameters2, locals2, stack2, context2, frame_state_parent);
+
+  // Build the call.
+  Node* call = m.CallFunctionStub0(function_node, receiver, context2,
+                                   frame_state_before, CALL_AS_METHOD);
+
+  m.Return(call);
+
+  Stream s = m.Build(kAllExceptNopInstructions);
+
+  // Skip until kArchCallJSFunction.
+  size_t index = 0;
+  for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
+       index++) {
+  }
+  // Now we should have three instructions: call, return.
+  EXPECT_EQ(index + 2, s.size());
+
+  // Check the call instruction
+  const Instruction* call_instr = s[index++];
+  EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
+  size_t num_operands =
+      1 +  // Code object.
+      1 +  // Frame state deopt id
+      4 +  // One input for each value in frame state + context.
+      4 +  // One input for each value in the parent frame state + context.
+      1 +  // Function.
+      1;   // Context.
+  EXPECT_EQ(num_operands, call_instr->InputCount());
+  // Code object.
+  EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
+
+  // Deoptimization id.
+  int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1));
+  FrameStateDescriptor* desc_before =
+      s.GetFrameStateDescriptor(deopt_id_before);
+  EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
+  EXPECT_EQ(1, desc_before->parameters_count());
+  EXPECT_EQ(1, desc_before->locals_count());
+  EXPECT_EQ(1, desc_before->stack_count());
+  EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(2)));
+  // Context:
+  EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(3)));
+  EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(4)));
+  EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(5)));
+  // Values from parent environment should follow.
+  EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(6)));
+  EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(7)));
+  EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(8)));
+  EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(9)));
+
+  // Function.
+  EXPECT_EQ(function_node->id(), s.ToVreg(call_instr->InputAt(10)));
+  // Context.
+  EXPECT_EQ(context2->id(), s.ToVreg(call_instr->InputAt(11)));
+  // Continuation.
+
+  EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
+  EXPECT_EQ(index, s.size());
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index fea6141..4b5552d 100644 (file)
@@ -1006,15 +1006,21 @@ void InstructionSelector::VisitThrow(Node* value) {
 
 FrameStateDescriptor* InstructionSelector::GetFrameStateDescriptor(
     Node* state) {
-  DCHECK(state->op()->opcode() == IrOpcode::kFrameState);
+  DCHECK(state->opcode() == IrOpcode::kFrameState);
+  DCHECK_EQ(5, state->InputCount());
   FrameStateCallInfo state_info = OpParameter<FrameStateCallInfo>(state);
-  Node* parameters = state->InputAt(0);
-  Node* locals = state->InputAt(1);
-  Node* stack = state->InputAt(2);
+  int parameters = OpParameter<int>(state->InputAt(0));
+  int locals = OpParameter<int>(state->InputAt(1));
+  int stack = OpParameter<int>(state->InputAt(2));
+
+  FrameStateDescriptor* outer_state = NULL;
+  Node* outer_node = state->InputAt(4);
+  if (outer_node->opcode() == IrOpcode::kFrameState) {
+    outer_state = GetFrameStateDescriptor(outer_node);
+  }
 
   return new (instruction_zone())
-      FrameStateDescriptor(state_info, OpParameter<int>(parameters),
-                           OpParameter<int>(locals), OpParameter<int>(stack));
+      FrameStateDescriptor(state_info, parameters, locals, stack, outer_state);
 }
 
 
@@ -1036,6 +1042,10 @@ void InstructionSelector::AddFrameStateInputs(
     FrameStateDescriptor* descriptor) {
   DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode());
 
+  if (descriptor->outer_state() != NULL) {
+    AddFrameStateInputs(state->InputAt(4), inputs, descriptor->outer_state());
+  }
+
   Node* parameters = state->InputAt(0);
   Node* locals = state->InputAt(1);
   Node* stack = state->InputAt(2);
index 2b3a28e..ee868b1 100644 (file)
@@ -702,30 +702,55 @@ class Constant FINAL {
 class FrameStateDescriptor : public ZoneObject {
  public:
   FrameStateDescriptor(const FrameStateCallInfo& state_info,
-                       int parameters_count, int locals_count, int stack_count)
+                       int parameters_count, int locals_count, int stack_count,
+                       FrameStateDescriptor* outer_state = NULL)
       : bailout_id_(state_info.bailout_id()),
         frame_state_combine_(state_info.state_combine()),
         parameters_count_(parameters_count),
         locals_count_(locals_count),
-        stack_count_(stack_count) {}
+        stack_count_(stack_count),
+        outer_state_(outer_state) {}
 
   BailoutId bailout_id() const { return bailout_id_; }
   OutputFrameStateCombine state_combine() const { return frame_state_combine_; }
   int parameters_count() { return parameters_count_; }
   int locals_count() { return locals_count_; }
   int stack_count() { return stack_count_; }
+  FrameStateDescriptor* outer_state() { return outer_state_; }
+  void set_outer_state(FrameStateDescriptor* outer_state) {
+    outer_state_ = outer_state;
+  }
 
   int size() {
     return parameters_count_ + locals_count_ + stack_count_ +
            1;  // Includes context.
   }
 
+  int total_size() {
+    int total_size = 0;
+    for (FrameStateDescriptor* iter = this; iter != NULL;
+         iter = iter->outer_state_) {
+      total_size += iter->size();
+    }
+    return total_size;
+  }
+
+  int GetFrameCount() {
+    int count = 0;
+    for (FrameStateDescriptor* iter = this; iter != NULL;
+         iter = iter->outer_state_) {
+      ++count;
+    }
+    return count;
+  }
+
  private:
   BailoutId bailout_id_;
   OutputFrameStateCombine frame_state_combine_;
   int parameters_count_;
   int locals_count_;
   int stack_count_;
+  FrameStateDescriptor* outer_state_;
 };
 
 OStream& operator<<(OStream& os, const Constant& constant);
index a097452..714a27b 100644 (file)
@@ -77,10 +77,17 @@ typedef NodeVectorVector::reverse_iterator NodeVectorVectorRIter;
 typedef Node::Uses::iterator UseIter;
 typedef Node::Inputs::iterator InputIter;
 
+// Helper to extract parameters from Operator1<*> operator.
+template <typename T>
+static inline T OpParameter(Operator* op) {
+  return reinterpret_cast<Operator1<T>*>(op)->parameter();
+}
+
+
 // Helper to extract parameters from Operator1<*> nodes.
 template <typename T>
 static inline T OpParameter(Node* node) {
-  return reinterpret_cast<Operator1<T>*>(node->op())->parameter();
+  return OpParameter<T>(node->op());
 }
 }
 }
index db160a8..fa41b44 100644 (file)
@@ -37,6 +37,8 @@ inline bool OperatorProperties::HasFrameStateInput(Operator* op) {
   }
 
   switch (op->opcode()) {
+    case IrOpcode::kFrameState:
+      return true;
     case IrOpcode::kJSCallRuntime: {
       Runtime::FunctionId function =
           reinterpret_cast<Operator1<Runtime::FunctionId>*>(op)->parameter();
index 5e08d40..ce9e71e 100644 (file)
@@ -57,6 +57,12 @@ class RawMachineAssembler : public GraphBuilder,
   size_t parameter_count() const { return machine_sig_->parameter_count(); }
   MachineSignature* machine_sig() const { return machine_sig_; }
 
+  Node* UndefinedConstant() {
+    PrintableUnique<Object> unique = PrintableUnique<Object>::CreateImmovable(
+        zone(), isolate()->factory()->undefined_value());
+    return NewNode(common()->HeapConstant(unique));
+  }
+
   // Parameters.
   Node* Parameter(size_t index);
 
index d750f8d..04ad458 100644 (file)
@@ -76,7 +76,10 @@ GenericGraphVisit::Control Verifier::Visitor::Pre(Node* node) {
   // Verify that frame state has been inserted for the nodes that need it.
   if (OperatorProperties::HasFrameStateInput(node->op())) {
     Node* frame_state = NodeProperties::GetFrameStateInput(node);
-    CHECK(frame_state->opcode() == IrOpcode::kFrameState);
+    CHECK(frame_state->opcode() == IrOpcode::kFrameState ||
+          // kFrameState uses undefined as a sentinel.
+          (node->opcode() == IrOpcode::kFrameState &&
+           frame_state->opcode() == IrOpcode::kHeapConstant));
     CHECK(IsDefUseChainLinkPresent(frame_state, node));
     CHECK(IsUseDefChainLinkPresent(frame_state, node));
   }
index 21766e8..551cc96 100644 (file)
@@ -123,7 +123,6 @@ class TrivialDeoptCodegenTester : public DeoptCodegenTester {
   }
 
   Schedule* BuildGraphAndSchedule(Graph* graph) {
-    Isolate* isolate = info.isolate();
     CommonOperatorBuilder common(zone());
 
     // Manually construct a schedule for the function below:
@@ -134,34 +133,29 @@ class TrivialDeoptCodegenTester : public DeoptCodegenTester {
     CSignature1<Object*, Object*> sig;
     RawMachineAssembler m(graph, &sig);
 
-    Handle<Object> undef_object =
-        Handle<Object>(isolate->heap()->undefined_value(), isolate);
-    PrintableUnique<Object> undef_constant =
-        PrintableUnique<Object>::CreateUninitialized(zone(), undef_object);
-    Node* undef_node = m.NewNode(common.HeapConstant(undef_constant));
-
     Handle<JSFunction> deopt_function =
         NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
     PrintableUnique<Object> deopt_fun_constant =
         PrintableUnique<Object>::CreateUninitialized(zone(), deopt_function);
     Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
 
-    Handle<Context> context(deopt_function->context(), isolate);
+    Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
     PrintableUnique<Object> context_constant =
         PrintableUnique<Object>::CreateUninitialized(zone(), context);
     Node* context_node = m.NewNode(common.HeapConstant(context_constant));
 
     bailout_id = GetCallBailoutId();
-    Node* parameters = m.NewNode(common.StateValues(1), undef_node);
+    Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
     Node* locals = m.NewNode(common.StateValues(0));
     Node* stack = m.NewNode(common.StateValues(0));
 
-    Node* state_node = m.NewNode(common.FrameState(bailout_id, kIgnoreOutput),
-                                 parameters, locals, stack, undef_node);
+    Node* state_node =
+        m.NewNode(common.FrameState(bailout_id, kIgnoreOutput), parameters,
+                  locals, stack, m.UndefinedConstant(), m.UndefinedConstant());
 
-    m.CallJS0(deopt_fun_node, undef_node, context_node, state_node);
+    m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
 
-    m.Return(undef_node);
+    m.Return(m.UndefinedConstant());
 
     // Schedule the graph:
     Schedule* schedule = m.Export();
@@ -240,7 +234,6 @@ class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
   }
 
   Schedule* BuildGraphAndSchedule(Graph* graph) {
-    Isolate* isolate = info.isolate();
     CommonOperatorBuilder common(zone());
 
     // Manually construct a schedule for the function below:
@@ -251,33 +244,28 @@ class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
     CSignature1<Object*, Object*> sig;
     RawMachineAssembler m(graph, &sig);
 
-    Handle<Object> undef_object =
-        Handle<Object>(isolate->heap()->undefined_value(), isolate);
-    PrintableUnique<Object> undef_constant =
-        PrintableUnique<Object>::CreateUninitialized(zone(), undef_object);
-    Node* undef_node = m.NewNode(common.HeapConstant(undef_constant));
-
     PrintableUnique<Object> this_fun_constant =
         PrintableUnique<Object>::CreateUninitialized(zone(), function);
     Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
 
-    Handle<Context> context(function->context(), isolate);
+    Handle<Context> context(function->context(), CcTest::i_isolate());
     PrintableUnique<Object> context_constant =
         PrintableUnique<Object>::CreateUninitialized(zone(), context);
     Node* context_node = m.NewNode(common.HeapConstant(context_constant));
 
     bailout_id = GetCallBailoutId();
-    Node* parameters = m.NewNode(common.StateValues(1), undef_node);
+    Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
     Node* locals = m.NewNode(common.StateValues(0));
     Node* stack = m.NewNode(common.StateValues(0));
 
-    Node* state_node = m.NewNode(common.FrameState(bailout_id, kIgnoreOutput),
-                                 parameters, locals, stack, undef_node);
+    Node* state_node =
+        m.NewNode(common.FrameState(bailout_id, kIgnoreOutput), parameters,
+                  locals, stack, m.UndefinedConstant(), m.UndefinedConstant());
 
     m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
                    state_node);
 
-    m.Return(undef_node);
+    m.Return(m.UndefinedConstant());
 
     // Schedule the graph:
     Schedule* schedule = m.Export();