 Chromium Code Reviews
 Chromium Code Reviews Issue 165230:
  Eliminate most of the jump target jumping, branching, and binding...  (Closed) 
  Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
    
  
    Issue 165230:
  Eliminate most of the jump target jumping, branching, and binding...  (Closed) 
  Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/| Index: src/arm/jump-target-arm.cc | 
| =================================================================== | 
| --- src/arm/jump-target-arm.cc (revision 2654) | 
| +++ src/arm/jump-target-arm.cc (working copy) | 
| @@ -47,23 +47,29 @@ | 
| ASSERT(cgen()->HasValidEntryRegisters()); | 
| if (is_bound()) { | 
| - // Backward jump. There is an expected frame to merge to. | 
| + // Backward jump. There already a frame expectation at the target. | 
| ASSERT(direction_ == BIDIRECTIONAL); | 
| - cgen()->frame()->PrepareMergeTo(entry_frame_); | 
| cgen()->frame()->MergeTo(entry_frame_); | 
| cgen()->DeleteFrame(); | 
| - __ jmp(&entry_label_); | 
| } else { | 
| - // Preconfigured entry frame is not used on ARM. | 
| - ASSERT(entry_frame_ == NULL); | 
| - // Forward jump. The current frame is added to the end of the list | 
| - // of frames reaching the target block and a jump to the merge code | 
| - // is emitted. | 
| - AddReachingFrame(cgen()->frame()); | 
| - RegisterFile empty; | 
| - cgen()->SetFrame(NULL, &empty); | 
| - __ jmp(&merge_labels_.last()); | 
| + // Use the current frame as the expected one at the target if necessary. | 
| + if (entry_frame_ == NULL) { | 
| + entry_frame_ = cgen()->frame(); | 
| + RegisterFile empty; | 
| + cgen()->SetFrame(NULL, &empty); | 
| + } else { | 
| + cgen()->frame()->MergeTo(entry_frame_); | 
| + cgen()->DeleteFrame(); | 
| + } | 
| + | 
| + // The predicate is_linked() should now be true. The implementation | 
| 
Erik Corry
2009/08/10 10:56:45
This comment doesn't explain itself very well.
 
Kevin Millikin (Chromium)
2009/08/10 11:11:31
Reworded:
"The predicate is_linked() should be ma
 | 
| + // uses the presence of a frame pointer in the reaching_frames_ list. | 
| + if (!is_linked()) { | 
| + reaching_frames_.Add(NULL); | 
| + ASSERT(is_linked()); | 
| + } | 
| } | 
| + __ jmp(&entry_label_); | 
| } | 
| @@ -74,56 +80,21 @@ | 
| ASSERT(direction_ == BIDIRECTIONAL); | 
| // Backward branch. We have an expected frame to merge to on the | 
| // backward edge. | 
| - | 
| - // Swap the current frame for a copy (we do the swapping to get | 
| - // the off-frame registers off the fall through) to use for the | 
| - // branch. | 
| - VirtualFrame* fall_through_frame = cgen()->frame(); | 
| - VirtualFrame* branch_frame = new VirtualFrame(fall_through_frame); | 
| - RegisterFile non_frame_registers; | 
| - cgen()->SetFrame(branch_frame, &non_frame_registers); | 
| - | 
| - // Check if we can avoid merge code. | 
| - cgen()->frame()->PrepareMergeTo(entry_frame_); | 
| - if (cgen()->frame()->Equals(entry_frame_)) { | 
| - // Branch right in to the block. | 
| - cgen()->DeleteFrame(); | 
| - __ b(cc, &entry_label_); | 
| - cgen()->SetFrame(fall_through_frame, &non_frame_registers); | 
| - return; | 
| - } | 
| - | 
| - // Check if we can reuse existing merge code. | 
| - for (int i = 0; i < reaching_frames_.length(); i++) { | 
| - if (reaching_frames_[i] != NULL && | 
| - cgen()->frame()->Equals(reaching_frames_[i])) { | 
| - // Branch to the merge code. | 
| - cgen()->DeleteFrame(); | 
| - __ b(cc, &merge_labels_[i]); | 
| - cgen()->SetFrame(fall_through_frame, &non_frame_registers); | 
| - return; | 
| - } | 
| - } | 
| - | 
| - // To emit the merge code here, we negate the condition and branch | 
| - // around the merge code on the fall through path. | 
| - Label original_fall_through; | 
| - __ b(NegateCondition(cc), &original_fall_through); | 
| cgen()->frame()->MergeTo(entry_frame_); | 
| - cgen()->DeleteFrame(); | 
| - __ b(&entry_label_); | 
| - cgen()->SetFrame(fall_through_frame, &non_frame_registers); | 
| - __ bind(&original_fall_through); | 
| - | 
| } else { | 
| - // Preconfigured entry frame is not used on ARM. | 
| - ASSERT(entry_frame_ == NULL); | 
| - // Forward branch. A copy of the current frame is added to the end | 
| - // of the list of frames reaching the target block and a branch to | 
| - // the merge code is emitted. | 
| - AddReachingFrame(new VirtualFrame(cgen()->frame())); | 
| - __ b(cc, &merge_labels_.last()); | 
| + // Clone the current frame to use as the expected one at the target if | 
| + // necessary. | 
| + if (entry_frame_ == NULL) { | 
| + entry_frame_ = new VirtualFrame(cgen()->frame()); | 
| + } | 
| + // The predicate is_linked() should now be true. The implementation | 
| + // uses the presence of a frame pointer in the reaching_frames_ list. | 
| + if (!is_linked()) { | 
| + reaching_frames_.Add(NULL); | 
| + ASSERT(is_linked()); | 
| + } | 
| } | 
| + __ b(cc, &entry_label_); | 
| } | 
| @@ -139,13 +110,19 @@ | 
| ASSERT(cgen()->HasValidEntryRegisters()); | 
| ASSERT(!is_linked()); | 
| - cgen()->frame()->SpillAll(); | 
| + // Calls are always 'forward' so we use a copy of the current frame (plus | 
| + // one for a return address) as the expected frame. | 
| + ASSERT(entry_frame_ == NULL); | 
| VirtualFrame* target_frame = new VirtualFrame(cgen()->frame()); | 
| target_frame->Adjust(1); | 
| - // We do not expect a call with a preconfigured entry frame. | 
| - ASSERT(entry_frame_ == NULL); | 
| - AddReachingFrame(target_frame); | 
| - __ bl(&merge_labels_.last()); | 
| + entry_frame_ = target_frame; | 
| + | 
| + // is_linked() should now be true. The JumpTarget class uses the presence | 
| + // of a frame pointer in the reaching_frames_ list. | 
| + reaching_frames_.Add(NULL); | 
| + ASSERT(is_linked()); | 
| + | 
| + __ bl(&entry_label_); | 
| } | 
| @@ -156,168 +133,103 @@ | 
| // block. | 
| ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters()); | 
| - if (direction_ == FORWARD_ONLY) { | 
| - // A simple case: no forward jumps and no possible backward jumps. | 
| - if (!is_linked()) { | 
| - // The stack pointer can be floating above the top of the | 
| - // virtual frame before the bind. Afterward, it should not. | 
| - ASSERT(cgen()->has_valid_frame()); | 
| - VirtualFrame* frame = cgen()->frame(); | 
| - int difference = frame->stack_pointer_ - (frame->element_count() - 1); | 
| - if (difference > 0) { | 
| - frame->stack_pointer_ -= difference; | 
| - __ add(sp, sp, Operand(difference * kPointerSize)); | 
| - } | 
| - __ bind(&entry_label_); | 
| - return; | 
| - } | 
| - | 
| - // Another simple case: no fall through, a single forward jump, | 
| - // and no possible backward jumps. | 
| - if (!cgen()->has_valid_frame() && reaching_frames_.length() == 1) { | 
| - // Pick up the only reaching frame, take ownership of it, and | 
| - // use it for the block about to be emitted. | 
| - VirtualFrame* frame = reaching_frames_[0]; | 
| - RegisterFile empty; | 
| - cgen()->SetFrame(frame, &empty); | 
| - reaching_frames_[0] = NULL; | 
| - __ bind(&merge_labels_[0]); | 
| - | 
| - // The stack pointer can be floating above the top of the | 
| - // virtual frame before the bind. Afterward, it should not. | 
| - int difference = frame->stack_pointer_ - (frame->element_count() - 1); | 
| - if (difference > 0) { | 
| - frame->stack_pointer_ -= difference; | 
| - __ add(sp, sp, Operand(difference * kPointerSize)); | 
| - } | 
| - __ bind(&entry_label_); | 
| - return; | 
| - } | 
| - } | 
| - | 
| - // If there is a current frame, record it as the fall-through. It | 
| - // is owned by the reaching frames for now. | 
| - bool had_fall_through = false; | 
| if (cgen()->has_valid_frame()) { | 
| - had_fall_through = true; | 
| - AddReachingFrame(cgen()->frame()); // Return value ignored. | 
| + // If there is a current frame we can use it on the fall through. | 
| + if (entry_frame_ == NULL) { | 
| + entry_frame_ = new VirtualFrame(cgen()->frame()); | 
| + } else { | 
| + ASSERT(cgen()->frame()->Equals(entry_frame_)); | 
| + } | 
| + } else { | 
| + // If there is no current frame we must have an entry frame which we can | 
| + // copy. | 
| + ASSERT(entry_frame_ != NULL); | 
| RegisterFile empty; | 
| - cgen()->SetFrame(NULL, &empty); | 
| + cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); | 
| } | 
| - // Compute the frame to use for entry to the block. | 
| - if (entry_frame_ == NULL) { | 
| - ComputeEntryFrame(); | 
| + // is_linked() should now be false. We may have inserted a bogus reaching | 
| + // frame to make it true, remove it now. | 
| + if (is_linked()) { | 
| + reaching_frames_.Clear(); | 
| } | 
| - // Some moves required to merge to an expected frame require purely | 
| - // frame state changes, and do not require any code generation. | 
| - // Perform those first to increase the possibility of finding equal | 
| - // frames below. | 
| - for (int i = 0; i < reaching_frames_.length(); i++) { | 
| - if (reaching_frames_[i] != NULL) { | 
| - reaching_frames_[i]->PrepareMergeTo(entry_frame_); | 
| - } | 
| - } | 
| + __ bind(&entry_label_); | 
| +} | 
| - if (is_linked()) { | 
| - // There were forward jumps. Handle merging the reaching frames | 
| - // and possible fall through to the entry frame. | 
| - // Loop over the (non-null) reaching frames and process any that | 
| - // need merge code. Iterate backwards through the list to handle | 
| - // the fall-through frame first. Set frames that will be | 
| - // processed after 'i' to NULL if we want to avoid processing | 
| - // them. | 
| - for (int i = reaching_frames_.length() - 1; i >= 0; i--) { | 
| - VirtualFrame* frame = reaching_frames_[i]; | 
| +void BreakTarget::Jump() { | 
| + // On ARM we do not currently emit merge code for jumps, so we need to do | 
| + // it explicitly here. The only merging necessary is to drop extra | 
| + // statement state from the stack. | 
| + ASSERT(cgen()->has_valid_frame()); | 
| + int count = cgen()->frame()->height() - expected_height_; | 
| 
Erik Corry
2009/08/10 10:56:45
Should we assert that count is positive?
 
Kevin Millikin (Chromium)
2009/08/10 11:11:31
Yea verily.  Even better it should go in VirtualFr
 | 
| + cgen()->frame()->Drop(count); | 
| + DoJump(); | 
| +} | 
| - if (frame != NULL) { | 
| - // Does the frame (probably) need merge code? | 
| - if (!frame->Equals(entry_frame_)) { | 
| - // We could have a valid frame as the fall through to the | 
| - // binding site or as the fall through from a previous merge | 
| - // code block. Jump around the code we are about to | 
| - // generate. | 
| - if (cgen()->has_valid_frame()) { | 
| - cgen()->DeleteFrame(); | 
| - __ b(&entry_label_); | 
| - } | 
| - // Pick up the frame for this block. Assume ownership if | 
| - // there cannot be backward jumps. | 
| - RegisterFile empty; | 
| - if (direction_ == BIDIRECTIONAL) { | 
| - cgen()->SetFrame(new VirtualFrame(frame), &empty); | 
| - } else { | 
| - cgen()->SetFrame(frame, &empty); | 
| - reaching_frames_[i] = NULL; | 
| - } | 
| - __ bind(&merge_labels_[i]); | 
| - // Loop over the remaining (non-null) reaching frames, | 
| - // looking for any that can share merge code with this one. | 
| - for (int j = 0; j < i; j++) { | 
| - VirtualFrame* other = reaching_frames_[j]; | 
| - if (other != NULL && other->Equals(cgen()->frame())) { | 
| - // Set the reaching frame element to null to avoid | 
| - // processing it later, and then bind its entry label. | 
| - reaching_frames_[j] = NULL; | 
| - __ bind(&merge_labels_[j]); | 
| - } | 
| - } | 
| +void BreakTarget::Jump(Result* arg) { | 
| + // On ARM we do not currently emit merge code for jumps, so we need to do | 
| + // it explicitly here. The only merging necessary is to drop extra | 
| + // statement state from the stack. | 
| + ASSERT(cgen()->has_valid_frame()); | 
| + int count = cgen()->frame()->height() - expected_height_; | 
| + cgen()->frame()->Drop(count); | 
| + cgen()->frame()->Push(arg); | 
| + DoJump(); | 
| +} | 
| - // Emit the merge code. | 
| - cgen()->frame()->MergeTo(entry_frame_); | 
| - } else if (i == reaching_frames_.length() - 1 && had_fall_through) { | 
| - // If this is the fall through, and it didn't need merge | 
| - // code, we need to pick up the frame so we can jump around | 
| - // subsequent merge blocks if necessary. | 
| - RegisterFile empty; | 
| - cgen()->SetFrame(frame, &empty); | 
| - reaching_frames_[i] = NULL; | 
| - } | 
| - } | 
| - } | 
| - // The code generator may not have a current frame if there was no | 
| - // fall through and none of the reaching frames needed merging. | 
| - // In that case, clone the entry frame as the current frame. | 
| - if (!cgen()->has_valid_frame()) { | 
| - RegisterFile empty; | 
| - cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); | 
| - } | 
| +void BreakTarget::Bind() { | 
| +#ifdef DEBUG | 
| + // All the forward-reaching frames should have been adjusted at the | 
| + // jumps to this target. | 
| + for (int i = 0; i < reaching_frames_.length(); i++) { | 
| + ASSERT(reaching_frames_[i] == NULL || | 
| + reaching_frames_[i]->height() == expected_height_); | 
| + } | 
| +#endif | 
| + // Drop leftover statement state from the frame before merging, even | 
| + // on the fall through. This is so we can bind the return target | 
| + // with state on the frame. | 
| + if (cgen()->has_valid_frame()) { | 
| + int count = cgen()->frame()->height() - expected_height_; | 
| + // On ARM we do not currently emit merge code at binding sites, so we need | 
| + // to do it explicitly here. The only merging necessary is to drop extra | 
| + // statement state from the stack. | 
| + cgen()->frame()->Drop(count); | 
| + } | 
| - // There may be unprocessed reaching frames that did not need | 
| - // merge code. They will have unbound merge labels. Bind their | 
| - // merge labels to be the same as the entry label and deallocate | 
| - // them. | 
| - for (int i = 0; i < reaching_frames_.length(); i++) { | 
| - if (!merge_labels_[i].is_bound()) { | 
| - reaching_frames_[i] = NULL; | 
| - __ bind(&merge_labels_[i]); | 
| - } | 
| - } | 
| + DoBind(); | 
| +} | 
| - // There are non-NULL reaching frames with bound labels for each | 
| - // merge block, but only on backward targets. | 
| - } else { | 
| - // There were no forward jumps. There must be a current frame and | 
| - // this must be a bidirectional target. | 
| - ASSERT(reaching_frames_.length() == 1); | 
| - ASSERT(reaching_frames_[0] != NULL); | 
| - ASSERT(direction_ == BIDIRECTIONAL); | 
| - // Use a copy of the reaching frame so the original can be saved | 
| - // for possible reuse as a backward merge block. | 
| - RegisterFile empty; | 
| - cgen()->SetFrame(new VirtualFrame(reaching_frames_[0]), &empty); | 
| - __ bind(&merge_labels_[0]); | 
| - cgen()->frame()->MergeTo(entry_frame_); | 
| +void BreakTarget::Bind(Result* arg) { | 
| +#ifdef DEBUG | 
| + // All the forward-reaching frames should have been adjusted at the | 
| + // jumps to this target. | 
| + for (int i = 0; i < reaching_frames_.length(); i++) { | 
| + ASSERT(reaching_frames_[i] == NULL || | 
| + reaching_frames_[i]->height() == expected_height_ + 1); | 
| } | 
| - | 
| - __ bind(&entry_label_); | 
| +#endif | 
| + // Drop leftover statement state from the frame before merging, even | 
| + // on the fall through. This is so we can bind the return target | 
| + // with state on the frame. | 
| + if (cgen()->has_valid_frame()) { | 
| + int count = cgen()->frame()->height() - expected_height_; | 
| + // On ARM we do not currently emit merge code at binding sites, so we need | 
| + // to do it explicitly here. The only merging necessary is to drop extra | 
| + // statement state from the stack. | 
| + cgen()->frame()->ForgetElements(count); | 
| + cgen()->frame()->Push(arg); | 
| + } | 
| + DoBind(); | 
| + *arg = cgen()->frame()->Pop(); | 
| } | 
| + | 
| #undef __ |