Author: [email protected]
Date: Wed Feb  4 02:59:12 2009
New Revision: 1221

Modified:
    branches/experimental/toiger/src/codegen-ia32.cc
    branches/experimental/toiger/src/codegen-ia32.h
    branches/experimental/toiger/src/codegen.cc
    branches/experimental/toiger/src/jump-target-ia32.cc
    branches/experimental/toiger/src/jump-target.h

Log:
Experimental: handle single-entry basic blocks as a special case.
Also, clean up fast switch jump tables.

If basic block entry labels are forward-only and they are bound with
exactly one incoming CFG edge (either a live frame and no jumps or
branches, or no live frame and a single jump or branch) then they can
be treated specially.

The body of the block is compiled with the single reaching frame, with
no extra code emitted.

The fast-case switch statements are cleaned up to use raw labels
rather than jump targets, because they do not fit the jump target
abstraction very well.
Review URL: http://codereview.chromium.org/21040

Modified: branches/experimental/toiger/src/codegen-ia32.cc
==============================================================================
--- branches/experimental/toiger/src/codegen-ia32.cc    (original)
+++ branches/experimental/toiger/src/codegen-ia32.cc    Wed Feb  4 02:59:12  
2009
@@ -1443,9 +1443,7 @@
    node->set_break_stack_height(break_stack_height_);
    node->break_target()->Initialize(this);
    VisitStatements(node->statements());
-  if (node->break_target()->is_linked()) {
-    node->break_target()->Bind();
-  }
+  node->break_target()->Bind();
  }


@@ -1602,9 +1600,7 @@
      }
    }

-  if (exit.is_linked()) {
-    exit.Bind();
-  }
+  exit.Bind();
  }


@@ -1755,9 +1751,9 @@
      SwitchStatement* node,
      int min_index,
      int range,
-    JumpTarget* fail_label,
-    Vector<JumpTarget*> case_targets,
-    Vector<JumpTarget> case_labels) {
+    Label* default_label,
+    Vector<Label*> case_targets,
+    Vector<Label> case_labels) {
    // Notice: Internal references, used by both the jmp instruction and
    // the table entries, need to be relocated if the buffer grows. This
    // prevents the forward use of Labels, since a displacement cannot
@@ -1766,38 +1762,48 @@
    // placeholders, and fill in the addresses after the labels have been
    // bound.

-  Result switch_value = frame_->Pop();  // supposed Smi
-  // If value is not in range [0..length-1] then jump to the default/end  
label.
-  ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
-
-  // Test whether input is a HeapNumber that is really a Smi
+  JumpTarget setup_default(this);
    JumpTarget is_smi(this);
+
+  // A non-null default label pointer indicates a default case among
+  // the case labels.  Otherwise we use the break target as a
+  // "default".
+  JumpTarget* default_target =
+      (default_label == NULL) ? node->break_target() : &setup_default;
+
+  // Test whether input is a smi.
+  ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+  Result switch_value = frame_->Pop();
    switch_value.ToRegister();
    __ test(switch_value.reg(), Immediate(kSmiTagMask));
    is_smi.Branch(equal, &switch_value, taken);
-  // It's a heap object, not a Smi or a Failure
+
+  // It's a heap object, not a smi or a failure.  Check if it is a
+  // heap number.
    Result temp = allocator()->Allocate();
    ASSERT(temp.is_valid());
    __ mov(temp.reg(), FieldOperand(switch_value.reg(),  
HeapObject::kMapOffset));
    __ movzx_b(temp.reg(), FieldOperand(temp.reg(),  
Map::kInstanceTypeOffset));
    __ cmp(temp.reg(), HEAP_NUMBER_TYPE);
    temp.Unuse();
-  fail_label->Branch(not_equal);
-  // Result switch_value is a heap number.
+  default_target->Branch(not_equal);
+
+  // The switch value is a heap number.  Convert it to a smi.
    frame_->Push(&switch_value);
    Result smi_value = frame_->CallRuntime(Runtime::kNumberToSmi, 1);
+
    is_smi.Bind(&smi_value);
    smi_value.ToRegister();
-
+  // Convert the switch value to a 0-based table index.
    if (min_index != 0) {
      frame_->Spill(smi_value.reg());
      __ sub(Operand(smi_value.reg()), Immediate(min_index << kSmiTagSize));
    }
+  // Go to the default case if the table index is negative or not a smi.
    __ test(smi_value.reg(), Immediate(0x80000000 | kSmiTagMask));
-  // Go to slow case if adjusted index is negative or not a Smi.
-  fail_label->Branch(not_equal, not_taken);
+  default_target->Branch(not_equal, not_taken);
    __ cmp(smi_value.reg(), range << kSmiTagSize);
-  fail_label->Branch(greater_equal, not_taken);
+  default_target->Branch(greater_equal, not_taken);

    // 0 is placeholder.
    // Jump to the address at table_address + 2 * smi_value.reg().
@@ -1805,27 +1811,50 @@
    // The Smi encoding of smi_value.reg() is 2 * switch_value.
    __ jmp(Operand(smi_value.reg(), smi_value.reg(),
                   times_1, 0x0, RelocInfo::INTERNAL_REFERENCE));
+  smi_value.Unuse();
+
+  // The expected frame at all the case labels is the (mergable
+  // version of the) current one.  Keep a copy to restore at the start
+  // of every label.
+  frame_->MakeMergable();
+  VirtualFrame* start_frame = new VirtualFrame(frame_);
+
    // Calculate address to overwrite later with actual address of table.
    int32_t jump_table_ref = __ pc_offset() - sizeof(int32_t);
-
    __ Align(4);
-  JumpTarget table_start(this);
-  smi_value.Unuse();
-  table_start.Bind();
-  __ WriteInternalReference(jump_table_ref, *table_start.entry_label());
+  Label table_start;
+  __ bind(&table_start);
+  __ WriteInternalReference(jump_table_ref, table_start);

    for (int i = 0; i < range; i++) {
      // These are the table entries. 0x0 is the placeholder for case  
address.
      __ dd(0x0, RelocInfo::INTERNAL_REFERENCE);
    }

-  GenerateFastCaseSwitchCases(node, case_labels, &table_start);
+  GenerateFastCaseSwitchCases(node, case_labels, start_frame);

-  for (int i = 0, entry_pos = table_start.entry_label()->pos();
+  // If there was a default case, we need to emit the code to match it.
+  if (default_label != NULL) {
+    node->break_target()->Jump();
+    setup_default.Bind();
+    frame_->MergeTo(start_frame);
+    __ jmp(default_label);
+    DeleteFrame();
+  }
+  node->break_target()->Bind();
+
+  for (int i = 0, entry_pos = table_start.pos();
         i < range;
         i++, entry_pos += sizeof(uint32_t)) {
-    __ WriteInternalReference(entry_pos, *case_targets[i]->entry_label());
+    if (case_targets[i] == NULL) {
+      __ WriteInternalReference(entry_pos,
+                                *node->break_target()->entry_label());
+    } else {
+      __ WriteInternalReference(entry_pos, *case_targets[i]);
+    }
    }
+
+  delete start_frame;
  }


@@ -1837,7 +1866,6 @@
    node->break_target()->Initialize(this);

    Load(node->tag());
-
    if (TryGenerateFastCaseSwitchStatement(node)) {
      return;
    }
@@ -1918,13 +1946,8 @@
      }
    }

-  if (fall_through.is_linked()) {
-    fall_through.Bind();
-  }
-
-  if (node->break_target()->is_linked()) {
-    node->break_target()->Bind();
-  }
+  fall_through.Bind();
+  node->break_target()->Bind();
  }


@@ -1984,16 +2007,12 @@
          }
        } else if (info == ALWAYS_FALSE) {
          // If we had a continue in the body we have to bind its jump  
target.
-        if (node->continue_target()->is_linked()) {
-          node->continue_target()->Bind();
-        }
+        node->continue_target()->Bind();
        } else {
          ASSERT(info == DONT_KNOW);
          // We have to compile the test expression if it can be reached by
          // control flow falling out of the body or via continue.
-        if (node->continue_target()->is_linked()) {
-          node->continue_target()->Bind();
-        }
+        node->continue_target()->Bind();
          if (has_valid_frame()) {
            LoadCondition(node->cond(), NOT_INSIDE_TYPEOF,
                          &body, node->break_target(), true);
@@ -2021,9 +2040,7 @@
          JumpTarget body(this);
          LoadCondition(node->cond(), NOT_INSIDE_TYPEOF,
                        &body, node->break_target(), true);
-        if (body.is_linked()) {
-          body.Bind();
-        }
+        body.Bind();
        }

        if (has_valid_frame()) {
@@ -2066,9 +2083,7 @@
          JumpTarget body(this);
          LoadCondition(node->cond(), NOT_INSIDE_TYPEOF,
                        &body, node->break_target(), true);
-        if (body.is_linked()) {
-          body.Bind();
-        }
+        body.Bind();
        }

        if (has_valid_frame()) {
@@ -2085,9 +2100,7 @@
            // If there is an update statement and control flow can reach it
            // via falling out of the body of the loop or continuing, we
            // compile the update statement.
-          if (node->continue_target()->is_linked()) {
-            node->continue_target()->Bind();
-          }
+          node->continue_target()->Bind();
            if (has_valid_frame()) {
              // Record source position of the statement as this code which  
is
              // after the code for the body actually belongs to the loop
@@ -2104,9 +2117,7 @@
    }

    DecrementLoopNesting();
-  if (node->break_target()->is_linked()) {
-    node->break_target()->Bind();
-  }
+  node->break_target()->Bind();
  }


@@ -2130,7 +2141,6 @@
    JumpTarget fixed_array(this);
    JumpTarget entry(this, JumpTarget::BIDIRECTIONAL);
    JumpTarget end_del_check(this);
-  JumpTarget cleanup(this);
    JumpTarget exit(this);

    // Get the object to enumerate over (converted to JSObject).
@@ -2216,7 +2226,7 @@
    entry.Bind();
    __ mov(eax, frame_->ElementAt(0));  // load the current count
    __ cmp(eax, frame_->ElementAt(1));  // compare to the array length
-  cleanup.Branch(above_equal);
+  node->break_target()->Branch(above_equal);

    // Get the i'th entry of the array.
    __ mov(edx, frame_->ElementAt(2));
@@ -2291,7 +2301,6 @@
    entry.Jump();

    // Cleanup.
-  cleanup.Bind();
    node->break_target()->Bind();
    frame_->Drop(5);

@@ -2661,9 +2670,7 @@
      Load(node->else_expression(), typeof_state());
    }

-  if (exit.is_linked()) {
-    exit.Bind();
-  }
+  exit.Bind();
  }


@@ -2801,12 +2808,7 @@
        // scope.
      }

-    // If we definitely did not jump over the assignment, we do not need
-    // to bind the exit label.  Doing so can defeat peephole
-    // optimization.
-    if (exit.is_linked()) {
-      exit.Bind();
-    }
+    exit.Bind();
    }
  }


Modified: branches/experimental/toiger/src/codegen-ia32.h
==============================================================================
--- branches/experimental/toiger/src/codegen-ia32.h     (original)
+++ branches/experimental/toiger/src/codegen-ia32.h     Wed Feb  4 02:59:12 2009
@@ -438,15 +438,15 @@
    void GenerateFastCaseSwitchJumpTable(SwitchStatement* node,
                                         int min_index,
                                         int range,
-                                       JumpTarget* fail_label,
-                                       Vector<JumpTarget*> case_targets,
-                                       Vector<JumpTarget> case_labels);
+                                       Label* fail_label,
+                                       Vector<Label*> case_targets,
+                                       Vector<Label> case_labels);

    // Generate the code for cases for the fast case switch.
    // Called by GenerateFastCaseSwitchJumpTable.
    void GenerateFastCaseSwitchCases(SwitchStatement* node,
-                                   Vector<JumpTarget> case_labels,
-                                   JumpTarget* table_start);
+                                   Vector<Label> case_labels,
+                                   VirtualFrame* start_frame);

    // Fast support for constant-Smi switches.
    void GenerateFastCaseSwitchStatement(SwitchStatement* node,

Modified: branches/experimental/toiger/src/codegen.cc
==============================================================================
--- branches/experimental/toiger/src/codegen.cc (original)
+++ branches/experimental/toiger/src/codegen.cc Wed Feb  4 02:59:12 2009
@@ -379,17 +379,14 @@
    ZoneList<CaseClause*>* cases = node->cases();
    int length = cases->length();

-  // JumpTarget pointer per number in range.
-  SmartPointer<JumpTarget*> case_targets(NewArray<JumpTarget*>(range));
+  // Label pointer per number in range.
+  SmartPointer<Label*> case_targets(NewArray<Label*>(range));

-  // JumpTarget per switch case.
-  SmartPointer<JumpTarget> case_labels(NewArray<JumpTarget>(length));
-  for (int i = 0; i < length; i++) {
-    case_labels[i].Initialize(this);
-  }
+  // Label per switch case.
+  SmartPointer<Label> case_labels(NewArray<Label>(length));

-  JumpTarget* fail_label = default_index >= 0 ?  
&(case_labels[default_index])
-                                              : node->break_target();
+  Label* fail_label =
+      default_index >= 0 ? &(case_labels[default_index]) : NULL;

    // Populate array of label pointers for each number in the range.
    // Initally put the failure label everywhere.
@@ -400,7 +397,7 @@
    // Overwrite with label of a case for the number value of that case.
    // (In reverse order, so that if the same label occurs twice, the
    // first one wins).
-  for (int i = length-1; i >= 0 ; i--) {
+  for (int i = length - 1; i >= 0 ; i--) {
      CaseClause* clause = cases->at(i);
      if (!clause->is_default()) {
        Object* label_value = *(clause->label()->AsLiteral()->handle());
@@ -413,35 +410,33 @@
                                    min_index,
                                    range,
                                    fail_label,
-                                  Vector<JumpTarget*>(*case_targets,  
range),
-                                  Vector<JumpTarget>(*case_labels,  
length));
+                                  Vector<Label*>(*case_targets, range),
+                                  Vector<Label>(*case_labels, length));
  }


  void CodeGenerator::GenerateFastCaseSwitchCases(
      SwitchStatement* node,
-    Vector<JumpTarget> case_labels,
-    JumpTarget* table_start) {
+    Vector<Label> case_labels,
+    VirtualFrame* start_frame) {
    ZoneList<CaseClause*>* cases = node->cases();
    int length = cases->length();

    for (int i = 0; i < length; i++) {
      Comment cmnt(masm(), "[ Case clause");
-    case_labels[i].Bind();
+    masm()->bind(&case_labels[i]);
      VisitStatements(cases->at(i)->statements());

-    // All of the labels match the same expected frame as the table start.
-    // If control flow cannot fall off the end of the case statement, we
-    // pick up the expected frame from the table start.  Otherwise we have
-    // to generate merge code to match the next label.
+    // If control flow did not fall off the end of the case statement,
+    // we restore the expected frame for the next iteration (or exit
+    // of the loop).  Otherwise we have to generate merge code to
+    // expectation at the next case.
      if (frame_ == NULL) {
-      frame_ = new VirtualFrame(table_start->expected_frame());
+      frame_ = new VirtualFrame(start_frame);
      } else {
-      frame_->MergeTo(table_start->expected_frame());
+      frame_->MergeTo(start_frame);
      }
    }
-
-  node->break_target()->Bind();
  }



Modified: branches/experimental/toiger/src/jump-target-ia32.cc
==============================================================================
--- branches/experimental/toiger/src/jump-target-ia32.cc        (original)
+++ branches/experimental/toiger/src/jump-target-ia32.cc        Wed Feb  4  
02:59:12 2009
@@ -42,7 +42,9 @@
        direction_(direction),
        reaching_frames_(0),
        merge_labels_(0),
-      expected_frame_(NULL) {
+      expected_frame_(NULL),
+      is_bound_(false),
+      is_linked_(false) {
    ASSERT(cgen_ != NULL);
    masm_ = cgen_->masm();
  }
@@ -54,7 +56,9 @@
        direction_(FORWARD_ONLY),
        reaching_frames_(0),
        merge_labels_(0),
-      expected_frame_(NULL) {
+      expected_frame_(NULL),
+      is_bound_(false),
+      is_linked_(false) {
  }


@@ -67,6 +71,26 @@
  }


+void JumpTarget::Unuse() {
+  ASSERT(!is_linked());
+  entry_label_.Unuse();
+  delete expected_frame_;
+  expected_frame_ = NULL;
+  is_bound_ = false;
+  is_linked_ = false;
+}
+
+
+void JumpTarget::Reset() {
+  reaching_frames_.Clear();
+  merge_labels_.Clear();
+  expected_frame_ = NULL;
+  entry_label_.Unuse();
+  is_bound_ = false;
+  is_linked_ = false;
+}
+
+
  void JumpTarget::Jump() {
    ASSERT(cgen_ != NULL);
    ASSERT(cgen_->has_valid_frame());
@@ -90,6 +114,8 @@
      cgen_->SetFrame(NULL, &empty);
      __ jmp(&merge_labels_.last());
    }
+
+  is_linked_ = !is_bound_;
  }


@@ -160,6 +186,8 @@
      AddReachingFrame(new VirtualFrame(cgen_->frame()));
      __ j(cc, &merge_labels_.last(), hint);
    }
+
+  is_linked_ = !is_bound_;
  }


@@ -298,12 +326,13 @@
    target_frame->Adjust(1);
    AddReachingFrame(target_frame);
    __ call(&merge_labels_.last());
+
+  is_linked_ = !is_bound_;
  }


  void JumpTarget::Bind() {
    ASSERT(cgen_ != NULL);
-  ASSERT(is_linked() || cgen_->has_valid_frame());
    ASSERT(!is_bound());

    if (is_linked()) {
@@ -311,46 +340,71 @@
      // the frames reaching the block via forward jumps are merged to it.
      ASSERT(reaching_frames_.length() == merge_labels_.length());

-    // Choose a frame as the basis of the expected frame, and make it
-    // mergable.  If there is a current frame use it, otherwise use the
-    // first in the list (there will be at least one).
-    int start_index = 0;
-    if (cgen_->has_valid_frame()) {
-      // Live non-frame registers are not allowed at the start of a labeled
-      // basic block.
-      ASSERT(cgen_->HasValidEntryRegisters());
-    } else {
+    // A special case is that there was only one jump to the block so
+    // far, no fall-through, and there cannot be another entry because
+    // the block is forward only.  In that case, simply use the single
+    // frame.
+    bool single_entry = (direction_ == FORWARD_ONLY) &&
+                        !cgen_->has_valid_frame() &&
+                        (reaching_frames_.length() == 1);
+    if (single_entry) {
+      // Pick up the only forward reaching frame and bind its merge
+      // label.  No merge code is emitted.
        RegisterFile reserved_registers = RegisterAllocator::Reserved();
-      cgen_->SetFrame(reaching_frames_[start_index], &reserved_registers);
-      __ bind(&merge_labels_[start_index++]);
-    }
-    cgen_->frame()->MakeMergable();
-    expected_frame_ = new VirtualFrame(cgen_->frame());
-
-    for (int i = start_index; i < reaching_frames_.length(); i++) {
-      cgen_->DeleteFrame();
-      __ jmp(&entry_label_);
+      cgen_->SetFrame(reaching_frames_[0], &reserved_registers);
+      __ bind(&merge_labels_[0]);
+    } else {
+      // Otherwise, choose a frame as the basis of the expected frame,
+      // and make it mergable.  If there is a current frame use it,
+      // otherwise use the first in the list (there will be at least
+      // one).
+      int start_index = 0;
+      if (cgen_->has_valid_frame()) {
+        // Live non-frame registers are not allowed at the start of a
+        // labeled basic block.
+        ASSERT(cgen_->HasValidEntryRegisters());
+      } else {
+        RegisterFile reserved_registers = RegisterAllocator::Reserved();
+        cgen_->SetFrame(reaching_frames_[start_index],  
&reserved_registers);
+        __ bind(&merge_labels_[start_index++]);
+      }
+      cgen_->frame()->MakeMergable();
+      expected_frame_ = new VirtualFrame(cgen_->frame());
+
+      for (int i = start_index; i < reaching_frames_.length(); i++) {
+        cgen_->DeleteFrame();
+        __ jmp(&entry_label_);
+
+        RegisterFile reserved_registers = RegisterAllocator::Reserved();
+        cgen_->SetFrame(reaching_frames_[i], &reserved_registers);
+        __ bind(&merge_labels_[i]);

-      RegisterFile reserved_registers = RegisterAllocator::Reserved();
-      cgen_->SetFrame(reaching_frames_[i], &reserved_registers);
-      __ bind(&merge_labels_[i]);
+        cgen_->frame()->MergeTo(expected_frame_);
+      }

-      cgen_->frame()->MergeTo(expected_frame_);
+      __ bind(&entry_label_);
      }
-    __ bind(&entry_label_);

      // All but the last reaching virtual frame have been deleted, and
      // the last one is the current frame.
      reaching_frames_.Clear();
      merge_labels_.Clear();
+
    } else {
-    // There were no forward jumps.  There must be a current frame,
-    // which is made mergable and used as the expected frame.
-    ASSERT(cgen_->HasValidEntryRegisters());
-    cgen_->frame()->MakeMergable();
-    expected_frame_ = new VirtualFrame(cgen_->frame());
-    __ bind(&entry_label_);
+    // There were no forward jumps.  If this jump target is not
+    // bidirectional, there is no need to do anything.  For
+    // bidirectional jump targets, the current frame is made mergable
+    // and used for the expected frame.
+    if (direction_ == BIDIRECTIONAL) {
+      ASSERT(cgen_->HasValidEntryRegisters());
+      cgen_->frame()->MakeMergable();
+      expected_frame_ = new VirtualFrame(cgen_->frame());
+      __ bind(&entry_label_);
+    }
    }
+
+  is_linked_ = false;
+  is_bound_ = true;
  }


@@ -424,6 +478,8 @@
    }
    destination->expected_frame_ = expected_frame_;
    destination->entry_label_ = entry_label_;
+  destination->is_bound_ = is_bound_;
+  destination->is_linked_ = is_linked_;
  }



Modified: branches/experimental/toiger/src/jump-target.h
==============================================================================
--- branches/experimental/toiger/src/jump-target.h      (original)
+++ branches/experimental/toiger/src/jump-target.h      Wed Feb  4 02:59:12 2009
@@ -83,29 +83,19 @@
    }

    // Predicates testing the state of the encapsulated label.
-  bool is_bound() const { return expected_frame_ != NULL; }
-  bool is_linked() const { return reaching_frames_.length() > 0; }
+  bool is_bound() const { return is_bound_; }
+  bool is_linked() const { return is_linked_; }
    bool is_unused() const { return !is_bound() && !is_linked(); }

    // Treat the jump target as a fresh one.  The expected frame if any
    // will be deallocated and there should be no dangling jumps to the
    // target (thus no reaching frames).
-  void Unuse() {
-    ASSERT(!is_linked());
-    entry_label_.Unuse();
-    delete expected_frame_;
-    expected_frame_ = NULL;
-  }
+  void Unuse();

    // Reset the internal state of this jump target.  Pointed-to virtual
    // frames are not deallocated and dangling jumps to the target are
    // left dangling.
-  void Reset() {
-    reaching_frames_.Clear();
-    merge_labels_.Clear();
-    expected_frame_ = NULL;
-    entry_label_.Unuse();
-  }
+  void Reset();

    // Copy the state of this jump target to the destination.  The lists
    // of forward-reaching frames and merge-point labels are copied.
@@ -177,6 +167,12 @@

    // The actual entry label of the block.
    Label entry_label_;
+
+  // A target is bound if its Bind member function has been called.
+  // It is linked if it is not bound but its Jump, Branch, or Call
+  // member functions have been called.
+  bool is_bound_;
+  bool is_linked_;

    // Add a virtual frame reaching this labeled block via a forward
    // jump, and a fresh label for its merge code.

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to