Revision: 24802
Author:   [email protected]
Date:     Wed Oct 22 11:24:55 2014 UTC
Log:      [turbofan] Add support for deferred code.

Branch can now have an optional hint, when the condition is
likely true or false, and if such a hint is present the other
basic block will be marked as deferred and placed at the end
of the function.

We currently use this feature for tagging int32/uint32 in
change lowering, and for load/store bounds checks in simplified
lowering.

TEST=cctest,unittests
[email protected]

Review URL: https://codereview.chromium.org/642883003
https://code.google.com/p/v8/source/detail?r=24802

Modified:
 /branches/bleeding_edge/src/compiler/change-lowering.cc
 /branches/bleeding_edge/src/compiler/code-generator.cc
 /branches/bleeding_edge/src/compiler/code-generator.h
 /branches/bleeding_edge/src/compiler/common-operator.cc
 /branches/bleeding_edge/src/compiler/common-operator.h
 /branches/bleeding_edge/src/compiler/graph-visualizer.cc
 /branches/bleeding_edge/src/compiler/instruction-selector.cc
 /branches/bleeding_edge/src/compiler/instruction.cc
 /branches/bleeding_edge/src/compiler/instruction.h
 /branches/bleeding_edge/src/compiler/operator-properties-inl.h
 /branches/bleeding_edge/src/compiler/schedule.cc
 /branches/bleeding_edge/src/compiler/schedule.h
 /branches/bleeding_edge/src/compiler/scheduler.cc
 /branches/bleeding_edge/src/compiler/simplified-lowering.cc
 /branches/bleeding_edge/test/cctest/compiler/test-scheduler.cc
 /branches/bleeding_edge/test/unittests/compiler/common-operator-unittest.cc

=======================================
--- /branches/bleeding_edge/src/compiler/change-lowering.cc Wed Oct 1 11:08:37 2014 UTC +++ /branches/bleeding_edge/src/compiler/change-lowering.cc Wed Oct 22 11:24:55 2014 UTC
@@ -142,7 +142,8 @@
Node* add = graph()->NewNode(machine()->Int32AddWithOverflow(), val, val);
   Node* ovf = graph()->NewNode(common()->Projection(1), add);

-  Node* branch = graph()->NewNode(common()->Branch(), ovf, control);
+  Node* branch =
+      graph()->NewNode(common()->Branch(BranchHint::kTrue), ovf, control);

   Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
   Node* heap_number = AllocateHeapNumberWithValue(
@@ -215,7 +216,8 @@

   Node* cmp = graph()->NewNode(machine()->Uint32LessThanOrEqual(), val,
                                SmiMaxValueConstant());
-  Node* branch = graph()->NewNode(common()->Branch(), cmp, control);
+  Node* branch =
+      graph()->NewNode(common()->Branch(BranchHint::kTrue), cmp, control);

   Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
   Node* smi = graph()->NewNode(
=======================================
--- /branches/bleeding_edge/src/compiler/code-generator.cc Tue Oct 21 06:59:50 2014 UTC +++ /branches/bleeding_edge/src/compiler/code-generator.cc Wed Oct 22 11:24:55 2014 UTC
@@ -44,10 +44,20 @@
   info->set_prologue_offset(masm()->pc_offset());
   AssemblePrologue();

-  // Assemble all instructions.
-  for (InstructionSequence::const_iterator i = code()->begin();
-       i != code()->end(); ++i) {
-    AssembleInstruction(*i);
+  // Assemble all non-deferred instructions.
+  for (auto const block : code()->instruction_blocks()) {
+    if (block->IsDeferred()) continue;
+    for (int i = block->code_start(); i < block->code_end(); ++i) {
+      AssembleInstruction(code()->InstructionAt(i));
+    }
+  }
+
+  // Assemble all deferred instructions.
+  for (auto const block : code()->instruction_blocks()) {
+    if (!block->IsDeferred()) continue;
+    for (int i = block->code_start(); i < block->code_end(); ++i) {
+      AssembleInstruction(code()->InstructionAt(i));
+    }
   }

   FinishCode(masm());
@@ -81,6 +91,12 @@

   return result;
 }
+
+
+bool CodeGenerator::IsNextInAssemblyOrder(BasicBlock::RpoNumber block) const {
+  return code()->InstructionBlockAt(current_block_)->ao_number().IsNext(
+      code()->InstructionBlockAt(block)->ao_number());
+}


void CodeGenerator::RecordSafepoint(PointerMap* pointers, Safepoint::Kind kind,
=======================================
--- /branches/bleeding_edge/src/compiler/code-generator.h Tue Oct 21 06:59:50 2014 UTC +++ /branches/bleeding_edge/src/compiler/code-generator.h Wed Oct 22 11:24:55 2014 UTC
@@ -39,9 +39,7 @@

   // Checks if {block} will appear directly after {current_block_} when
   // assembling code, in which case, a fall-through can be used.
-  bool IsNextInAssemblyOrder(BasicBlock::RpoNumber block) const {
-    return current_block_.IsNext(block);
-  }
+  bool IsNextInAssemblyOrder(BasicBlock::RpoNumber block) const;

   // Record a safepoint with the given pointer map.
   void RecordSafepoint(PointerMap* pointers, Safepoint::Kind kind,
=======================================
--- /branches/bleeding_edge/src/compiler/common-operator.cc Wed Oct 8 07:32:07 2014 UTC +++ /branches/bleeding_edge/src/compiler/common-operator.cc Wed Oct 22 11:24:55 2014 UTC
@@ -30,6 +30,26 @@
 }  // namespace


+std::ostream& operator<<(std::ostream& os, BranchHint hint) {
+  switch (hint) {
+    case BranchHint::kNone:
+      return os << "None";
+    case BranchHint::kTrue:
+      return os << "True";
+    case BranchHint::kFalse:
+      return os << "False";
+  }
+  UNREACHABLE();
+  return os;
+}
+
+
+BranchHint BranchHintOf(const Operator* const op) {
+  DCHECK_EQ(IrOpcode::kBranch, op->opcode());
+  return OpParameter<BranchHint>(op);
+}
+
+
 size_t hash_value(OutputFrameStateCombine const& sc) {
   return base::hash_combine(sc.kind_, sc.parameter_);
 }
@@ -74,7 +94,6 @@
 #define SHARED_OP_LIST(V)               \
   V(Dead, Operator::kFoldable, 0, 0)    \
   V(End, Operator::kFoldable, 0, 1)     \
-  V(Branch, Operator::kFoldable, 1, 1)  \
   V(IfTrue, Operator::kFoldable, 0, 1)  \
   V(IfFalse, Operator::kFoldable, 0, 1) \
   V(Throw, Operator::kFoldable, 1, 1)   \
@@ -110,6 +129,12 @@
 #undef SHARED


+const Operator* CommonOperatorBuilder::Branch(BranchHint hint) {
+  return new (zone()) Operator1<BranchHint>(
+      IrOpcode::kBranch, Operator::kFoldable, 1, 0, "Branch", hint);
+}
+
+
 const Operator* CommonOperatorBuilder::Start(int num_formal_parameters) {
   // Outputs are formal parameters, plus context, receiver, and JSFunction.
   const int value_output_count = num_formal_parameters + 3;
=======================================
--- /branches/bleeding_edge/src/compiler/common-operator.h Tue Oct 7 13:30:28 2014 UTC +++ /branches/bleeding_edge/src/compiler/common-operator.h Wed Oct 22 11:24:55 2014 UTC
@@ -23,6 +23,16 @@
 class Operator;


+// Prediction hint for branches.
+enum class BranchHint : uint8_t { kNone, kTrue, kFalse };
+
+inline size_t hash_value(BranchHint hint) { return static_cast<size_t>(hint); }
+
+std::ostream& operator<<(std::ostream&, BranchHint);
+
+BranchHint BranchHintOf(const Operator* const);
+
+
 // Flag that describes how to combine the current environment with
 // the output of a node to obtain a framestate for lazy bailout.
 class OutputFrameStateCombine {
@@ -123,7 +133,7 @@

   const Operator* Dead();
   const Operator* End();
-  const Operator* Branch();
+  const Operator* Branch(BranchHint = BranchHint::kNone);
   const Operator* IfTrue();
   const Operator* IfFalse();
   const Operator* Throw();
=======================================
--- /branches/bleeding_edge/src/compiler/graph-visualizer.cc Mon Oct 20 10:19:15 2014 UTC +++ /branches/bleeding_edge/src/compiler/graph-visualizer.cc Wed Oct 22 11:24:55 2014 UTC
@@ -562,6 +562,7 @@
     BasicBlock* current = (*rpo)[i];
     Tag block_tag(this, "block");
     PrintBlockProperty("name", current->id());
+ PrintStringProperty("deferred", current->deferred() ? "true" : "false");
     PrintIntProperty("from_bci", -1);
     PrintIntProperty("to_bci", -1);

=======================================
--- /branches/bleeding_edge/src/compiler/instruction-selector.cc Tue Oct 21 14:44:50 2014 UTC +++ /branches/bleeding_edge/src/compiler/instruction-selector.cc Wed Oct 22 11:24:55 2014 UTC
@@ -147,7 +147,7 @@


bool InstructionSelector::IsNextInAssemblyOrder(const BasicBlock* block) const {
-  return current_block_->GetRpoNumber().IsNext(block->GetRpoNumber());
+  return current_block_->GetAoNumber().IsNext(block->GetAoNumber());
 }


=======================================
--- /branches/bleeding_edge/src/compiler/instruction.cc Tue Oct 21 06:59:50 2014 UTC +++ /branches/bleeding_edge/src/compiler/instruction.cc Wed Oct 22 11:24:55 2014 UTC
@@ -336,11 +336,13 @@
                     BasicBlock::RpoNumber::Invalid(), zone),
       phis_(zone),
       id_(block->id()),
+      ao_number_(block->GetAoNumber()),
       rpo_number_(block->GetRpoNumber()),
       loop_header_(GetRpo(block->loop_header())),
       loop_end_(GetLoopEndRpo(block)),
       code_start_(-1),
-      code_end_(-1) {
+      code_end_(-1),
+      deferred_(block->deferred()) {
   // Map successors and precessors
   size_t index = 0;
for (BasicBlock::Successors::const_iterator it = block->successors_begin();
@@ -604,7 +606,10 @@
     const InstructionBlock* block = code.InstructionBlockAt(rpo);
     CHECK(block->rpo_number() == rpo);

-    os << "RPO#" << block->rpo_number() << ": B" << block->id();
+    os << "RPO#" << block->rpo_number();
+    os << ": AO#" << block->ao_number();
+    os << ": B" << block->id();
+    if (block->IsDeferred()) os << " (deferred)";
     if (block->IsLoopHeader()) {
       os << " loop blocks: [" << block->rpo_number() << ", "
          << block->loop_end() << ")";
=======================================
--- /branches/bleeding_edge/src/compiler/instruction.h Tue Oct 21 14:41:54 2014 UTC +++ /branches/bleeding_edge/src/compiler/instruction.h Wed Oct 22 11:24:55 2014 UTC
@@ -794,8 +794,11 @@

   int32_t code_end() const { return code_end_; }
   void set_code_end(int32_t end) { code_end_ = end; }
+
+  bool IsDeferred() const { return deferred_; }

   BasicBlock::Id id() const { return id_; }
+  BasicBlock::RpoNumber ao_number() const { return ao_number_; }
   BasicBlock::RpoNumber rpo_number() const { return rpo_number_; }
   BasicBlock::RpoNumber loop_header() const { return loop_header_; }
   BasicBlock::RpoNumber loop_end() const {
@@ -822,12 +825,14 @@
   Predecessors predecessors_;
   PhiInstructions phis_;
   BasicBlock::Id id_;
+  BasicBlock::RpoNumber ao_number_;  // Assembly order number.
   // TODO(dcarney): probably dont't need this.
   BasicBlock::RpoNumber rpo_number_;
   BasicBlock::RpoNumber loop_header_;
   BasicBlock::RpoNumber loop_end_;
-  int32_t code_start_;  // start index of arch-specific code.
-  int32_t code_end_;    // end index of arch-specific code.
+  int32_t code_start_;   // start index of arch-specific code.
+  int32_t code_end_;     // end index of arch-specific code.
+  const bool deferred_;  // Block contains deferred code.
 };

 typedef ZoneDeque<Constant> ConstantDeque;
@@ -853,6 +858,10 @@
   int VirtualRegisterCount() const { return next_virtual_register_; }

   int node_count() const { return static_cast<int>(node_map_.size()); }
+
+  const InstructionBlocks& instruction_blocks() const {
+    return instruction_blocks_;
+  }

   int InstructionBlockCount() const {
     return static_cast<int>(instruction_blocks_.size());
=======================================
--- /branches/bleeding_edge/src/compiler/operator-properties-inl.h Tue Oct 14 08:59:27 2014 UTC +++ /branches/bleeding_edge/src/compiler/operator-properties-inl.h Wed Oct 22 11:24:55 2014 UTC
@@ -123,6 +123,8 @@
 #define OPCODE_CASE(x) case IrOpcode::k##x:
       CONTROL_OP_LIST(OPCODE_CASE)
 #undef OPCODE_CASE
+      // Branch operator is special
+      if (op->opcode() == IrOpcode::kBranch) return 1;
       // Control operators are Operator1<int>.
       return OpParameter<int>(op);
     default:
=======================================
--- /branches/bleeding_edge/src/compiler/schedule.cc Wed Oct 22 09:14:48 2014 UTC +++ /branches/bleeding_edge/src/compiler/schedule.cc Wed Oct 22 11:24:55 2014 UTC
@@ -13,7 +13,9 @@
 namespace compiler {

 BasicBlock::BasicBlock(Zone* zone, Id id)
-    : rpo_number_(-1),
+    : ao_number_(-1),
+      rpo_number_(-1),
+      deferred_(false),
       dominator_(NULL),
       loop_header_(NULL),
       loop_depth_(0),
@@ -240,6 +242,7 @@
   for (BasicBlockVectorIter i = rpo->begin(); i != rpo->end(); ++i) {
     BasicBlock* block = *i;
     os << "--- BLOCK B" << block->id();
+    if (block->deferred()) os << " (deferred)";
     if (block->PredecessorCount() != 0) os << " <- ";
     bool comma = false;
for (BasicBlock::Predecessors::iterator j = block->predecessors_begin();
=======================================
--- /branches/bleeding_edge/src/compiler/schedule.h Mon Oct 20 10:19:15 2014 UTC +++ /branches/bleeding_edge/src/compiler/schedule.h Wed Oct 22 11:24:55 2014 UTC
@@ -139,6 +139,9 @@

   Node* control_input() const { return control_input_; }
   void set_control_input(Node* control_input);
+
+  bool deferred() const { return deferred_; }
+  void set_deferred(bool deferred) { deferred_ = deferred; }

   BasicBlock* dominator() const { return dominator_; }
   void set_dominator(BasicBlock* dominator);
@@ -151,6 +154,10 @@

   int32_t loop_end() const { return loop_end_; }
   void set_loop_end(int32_t loop_end);
+
+  RpoNumber GetAoNumber() const { return RpoNumber::FromInt(ao_number_); }
+  int32_t ao_number() const { return ao_number_; }
+  void set_ao_number(int32_t ao_number) { ao_number_ = ao_number; }

RpoNumber GetRpoNumber() const { return RpoNumber::FromInt(rpo_number_); }
   int32_t rpo_number() const { return rpo_number_; }
@@ -161,7 +168,9 @@
   bool LoopContains(BasicBlock* block) const;

  private:
+  int32_t ao_number_;        // assembly order number of the block.
   int32_t rpo_number_;       // special RPO number of the block.
+  bool deferred_;            // true if the block contains deferred code.
   BasicBlock* dominator_;    // Immediate dominator of the block.
BasicBlock* loop_header_; // Pointer to dominating loop header basic block, // NULL if none. For loop headers, this points to
=======================================
--- /branches/bleeding_edge/src/compiler/scheduler.cc Tue Oct 21 16:31:51 2014 UTC +++ /branches/bleeding_edge/src/compiler/scheduler.cc Wed Oct 22 11:24:55 2014 UTC
@@ -277,6 +277,20 @@

     TraceConnect(branch, branch_block, successor_blocks[0]);
     TraceConnect(branch, branch_block, successor_blocks[1]);
+
+    // Consider branch hints.
+ // TODO(turbofan): Propagate the deferred flag to all blocks dominated by
+    // this IfTrue/IfFalse later.
+    switch (BranchHintOf(branch->op())) {
+      case BranchHint::kNone:
+        break;
+      case BranchHint::kTrue:
+        successor_blocks[1]->set_deferred(true);
+        break;
+      case BranchHint::kFalse:
+        successor_blocks[0]->set_deferred(true);
+        break;
+    }

     schedule_->AddBranch(branch_block, branch, successor_blocks[0],
                          successor_blocks[1]);
@@ -1194,6 +1208,18 @@
             current->loop_header()->id().ToInt(), current->loop_depth());
     }
   }
+
+  // Compute the assembly order (non-deferred code first, deferred code
+  // afterwards).
+  int32_t number = 0;
+  for (auto block : *final_order) {
+    if (block->deferred()) continue;
+    block->set_ao_number(number++);
+  }
+  for (auto block : *final_order) {
+    if (!block->deferred()) continue;
+    block->set_ao_number(number++);
+  }

 #if DEBUG
   if (FLAG_trace_turbo_scheduler) PrintRPO(num_loops, loops, final_order);
=======================================
--- /branches/bleeding_edge/src/compiler/simplified-lowering.cc Mon Oct 20 07:56:50 2014 UTC +++ /branches/bleeding_edge/src/compiler/simplified-lowering.cc Wed Oct 22 11:24:55 2014 UTC
@@ -1027,7 +1027,8 @@
     Node* control = node->InputAt(4);

Node* check = graph()->NewNode(machine()->Uint32LessThan(), key, length);
-    Node* branch = graph()->NewNode(common()->Branch(), check, control);
+    Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

     Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
     Node* load = graph()->NewNode(op, base, index, effect, if_true);
@@ -1098,7 +1099,8 @@
     Node* control = node->InputAt(5);

Node* check = graph()->NewNode(machine()->Uint32LessThan(), key, length);
-    Node* branch = graph()->NewNode(common()->Branch(), check, control);
+    Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

     Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* store = graph()->NewNode(op, base, index, value, effect, if_true);
=======================================
--- /branches/bleeding_edge/test/cctest/compiler/test-scheduler.cc Tue Oct 21 12:38:46 2014 UTC +++ /branches/bleeding_edge/test/cctest/compiler/test-scheduler.cc Wed Oct 22 11:24:55 2014 UTC
@@ -1831,5 +1831,61 @@

   ComputeAndVerifySchedule(24, &graph);
 }
+
+
+TEST(BranchHintTrue) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+
+  Node* start = graph.NewNode(common.Start(1));
+  graph.SetStart(start);
+
+  Node* p0 = graph.NewNode(common.Parameter(0), start);
+  Node* tv = graph.NewNode(common.Int32Constant(6));
+  Node* fv = graph.NewNode(common.Int32Constant(7));
+  Node* br = graph.NewNode(common.Branch(BranchHint::kTrue), p0, start);
+  Node* t = graph.NewNode(common.IfTrue(), br);
+  Node* f = graph.NewNode(common.IfFalse(), br);
+  Node* m = graph.NewNode(common.Merge(2), t, f);
+  Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), tv, fv, m);
+  Node* ret = graph.NewNode(common.Return(), phi, start, start);
+  Node* end = graph.NewNode(common.End(), ret, start);
+
+  graph.SetEnd(end);
+
+  Schedule* schedule = ComputeAndVerifySchedule(13, &graph);
+  // Make sure the false block is marked as deferred.
+  CHECK(!schedule->block(t)->deferred());
+  CHECK(schedule->block(f)->deferred());
+}
+
+
+TEST(BranchHintFalse) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+
+  Node* start = graph.NewNode(common.Start(1));
+  graph.SetStart(start);
+
+  Node* p0 = graph.NewNode(common.Parameter(0), start);
+  Node* tv = graph.NewNode(common.Int32Constant(6));
+  Node* fv = graph.NewNode(common.Int32Constant(7));
+  Node* br = graph.NewNode(common.Branch(BranchHint::kFalse), p0, start);
+  Node* t = graph.NewNode(common.IfTrue(), br);
+  Node* f = graph.NewNode(common.IfFalse(), br);
+  Node* m = graph.NewNode(common.Merge(2), t, f);
+  Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), tv, fv, m);
+  Node* ret = graph.NewNode(common.Return(), phi, start, start);
+  Node* end = graph.NewNode(common.End(), ret, start);
+
+  graph.SetEnd(end);
+
+  Schedule* schedule = ComputeAndVerifySchedule(13, &graph);
+  // Make sure the true block is marked as deferred.
+  CHECK(schedule->block(t)->deferred());
+  CHECK(!schedule->block(f)->deferred());
+}

 #endif
=======================================
--- /branches/bleeding_edge/test/unittests/compiler/common-operator-unittest.cc Wed Oct 8 07:32:07 2014 UTC +++ /branches/bleeding_edge/test/unittests/compiler/common-operator-unittest.cc Wed Oct 22 11:24:55 2014 UTC
@@ -47,7 +47,6 @@
   }
     SHARED(Dead, Operator::kFoldable, 0, 0, 0, 0, 1),
     SHARED(End, Operator::kFoldable, 0, 0, 1, 0, 0),
-    SHARED(Branch, Operator::kFoldable, 1, 0, 1, 0, 2),
     SHARED(IfTrue, Operator::kFoldable, 0, 0, 1, 0, 1),
     SHARED(IfFalse, Operator::kFoldable, 0, 0, 1, 0, 1),
     SHARED(Throw, Operator::kFoldable, 1, 0, 1, 0, 1),
@@ -160,6 +159,24 @@
 }  // namespace


+TEST_F(CommonOperatorTest, Branch) {
+  static const BranchHint kHints[] = {BranchHint::kNone, BranchHint::kTrue,
+                                      BranchHint::kFalse};
+  TRACED_FOREACH(BranchHint, hint, kHints) {
+    const Operator* const op = common()->Branch(hint);
+    EXPECT_EQ(IrOpcode::kBranch, op->opcode());
+    EXPECT_EQ(Operator::kFoldable, op->properties());
+    EXPECT_EQ(1, OperatorProperties::GetValueInputCount(op));
+    EXPECT_EQ(0, OperatorProperties::GetEffectInputCount(op));
+    EXPECT_EQ(1, OperatorProperties::GetControlInputCount(op));
+    EXPECT_EQ(2, OperatorProperties::GetTotalInputCount(op));
+    EXPECT_EQ(0, OperatorProperties::GetValueOutputCount(op));
+    EXPECT_EQ(0, OperatorProperties::GetEffectOutputCount(op));
+    EXPECT_EQ(2, OperatorProperties::GetControlOutputCount(op));
+  }
+}
+
+
 TEST_F(CommonOperatorTest, Float32Constant) {
   TRACED_FOREACH(float, value, kFloatValues) {
     const Operator* op = common()->Float32Constant(value);

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to