Chromium Code Reviews| Index: src/codegen-ia32.cc |
| =================================================================== |
| --- src/codegen-ia32.cc (revision 1391) |
| +++ src/codegen-ia32.cc (working copy) |
| @@ -2031,199 +2031,113 @@ |
| node->set_break_stack_height(break_stack_height_); |
| node->break_target()->Initialize(this); |
| + // Compile the switch value. |
| Load(node->tag()); |
| + |
| if (TryGenerateFastCaseSwitchStatement(node)) { |
| return; |
| } |
| - JumpTarget next_test(this); |
| - JumpTarget fall_through(this); |
| - JumpTarget default_entry(this); |
| - JumpTarget default_exit(this, JumpTarget::BIDIRECTIONAL); |
| ZoneList<CaseClause*>* cases = node->cases(); |
| int length = cases->length(); |
| CaseClause* default_clause = NULL; |
| - // Loop over the cases, compiling tests and bodies. Skip the |
| - // default if found and compile it at the end. Exit early if an |
| - // unconditionally true match occurs (which can happen, eg, in the |
| - // event the switch value is a compile-time constant). |
| - // |
| - // Bind the next_test target before entering the loop so we can use |
| - // its state to detect whether the switch value needs to be dropped |
| - // from the frame. |
| + JumpTarget next_test(this); |
| + // Compile the case label expressions and comparisons. Exit early |
| + // if a comparison is unconditionally true. The target next_test is |
| + // bound before the loop in order to indicate control flow to the |
| + // first comparison. |
| next_test.Bind(); |
| - int index = 0; |
| - for (; index < length; index++) { |
| - CaseClause* clause = cases->at(index); |
| + for (int i = 0; i < length && !next_test.is_unused(); i++) { |
| + CaseClause* clause = cases->at(i); |
| + clause->body_target()->Initialize(this); |
| + // The default is not a test, but remember it for later. |
| if (clause->is_default()) { |
| - // Remember the default clause and compile it at the end. |
| default_clause = clause; |
| continue; |
| } |
| - // Compile each non-default clause. |
| - Comment cmnt(masm_, "[ Case clause"); |
| - // Recycle the same target for each test. |
| - if (!next_test.is_unused()) { |
| - // The next test target may be linked (as the target of a |
| - // previous match failure) or bound (if the previous comparison |
| - // was unconditionally false or this is the first non-default |
| - // comparison). |
| - if (next_test.is_linked()) { |
| - next_test.Bind(); |
| - } |
| - next_test.Unuse(); |
| + Comment cmnt(masm_, "[ Case comparison"); |
| + // We recycle the same target next_test for each test. Bind it if |
| + // the previous test has not done so and then unuse it for the |
| + // loop. |
| + if (next_test.is_linked()) { |
| + next_test.Bind(); |
| } |
| + next_test.Unuse(); |
| // Duplicate the switch value. |
| frame_->Dup(); |
| - // Compile the clause's label expression. |
| + // Compile the label expression. |
| Load(clause->label()); |
| - // Compare and branch to the body if true and to the next test if |
| - // false. |
| - JumpTarget enter_body(this); |
| - ControlDestination dest(&enter_body, &next_test, true); |
| + // Compare and branch to the body if true or the next test if |
| + // false. Prefer the next test as a fall through. |
| + ControlDestination dest(clause->body_target(), &next_test, false); |
| Comparison(equal, true, &dest); |
| - bool previous_was_default = |
| - index > 0 && cases->at(index - 1)->is_default(); |
| - if (dest.false_was_fall_through()) { |
| - // The false target next_test was bound as the fall-through. |
| - // This may indicate that the comparison was unconditionally |
| - // false if there are no dangling jumps to enter_body. Even |
| - // then we may still need to compile the body if it is reachable |
| - // as a fall through. |
| - |
| - // We do not need to compile the body if control cannot reach |
| - // it. Control could reach the body (1) from the comparison by |
| - // a branch to enter_body, (2) as the fall through of some |
| - // previous case, or (3) possibly via a backward jump from the |
| - // default. |
| - if (!enter_body.is_linked() && |
| - !fall_through.is_linked() && |
| - !previous_was_default) { |
| - continue; |
| - } |
| - |
| - // We will compile the body and we have to jump around it on |
| - // this path where the comparison failed. |
| - next_test.Unuse(); |
| - next_test.Jump(); |
| - if (enter_body.is_linked()) { |
| - enter_body.Bind(); |
| - } |
| + // If the comparison fell through to the true target, jump to the |
| + // actual body. |
| + if (dest.true_was_fall_through()) { |
| + clause->body_target()->Unuse(); |
| + clause->body_target()->Jump(); |
| } |
| + } |
| - // The body entry target may have been bound, indicating control |
| - // flow can reach the body via the comparison. |
| - if (enter_body.is_bound()) { |
| - // The switch value is no longer needed. |
| - frame_->Drop(); |
| - } else { |
| - // The test was unconditionally false but we will compile the |
| - // body as a fall through. |
| - ASSERT(!has_valid_frame()); |
| + // If there was control flow to a next test from the last one |
| + // compiled, compile a jump to the default or break target. |
| + if (!next_test.is_unused()) { |
| + if (next_test.is_linked()) { |
| + next_test.Bind(); |
| } |
| - |
| - // Label the body if needed for fall through. |
| - if (previous_was_default) { |
| - // Because the default is compiled last, there is always a potential |
| - // backwards edge to here, falling through from the default. |
| - default_exit.Bind(); |
| + // Drop the switch value. |
| + frame_->Drop(); |
| + if (default_clause != NULL) { |
| + default_clause->body_target()->Jump(); |
| } else { |
| - // Recycle the same target for each fall through. |
| - fall_through.Bind(); |
| - fall_through.Unuse(); |
| + node->break_target()->Jump(); |
| } |
| - |
| - // Compile the body. |
| - ASSERT(has_valid_frame()); |
| - { Comment body_cmnt(masm_, "[ Case body"); |
| - VisitStatements(clause->statements()); |
| - } |
| - |
| - // The test may have been unconditionally true, which is indicated |
| - // by the absence of any control flow to the next_test target. In |
| - // that case, exit this loop and stop compiling both tests and |
| - // bodies (and begin compiling only bodies if necessary). |
| - |
| - // Otherwise, if control flow can fall off the end of the body |
| - // jump to the body of the next case as fall through unless this |
| - // is the last non-default case. |
| - if (!next_test.is_linked()) { |
| - index++; |
| - break; |
| - } else if (has_valid_frame()) { |
| - if (index < length - 2 && // There are at least two cases after this |
| - cases->at(index + 1)->is_default()) { // The next is the default. |
| - default_entry.Jump(); |
| - } else if (index < length - 1) { // This is not the last case. |
| - fall_through.Jump(); |
| - } |
| - } |
| } |
| - // If we did not compile all the cases then we must have hit one |
| - // that was unconditionally true. We do not need to compile any |
| - // more tests but we may have (and continue to have) fall through. |
| - for (; index < length && has_valid_frame(); index++) { |
| - Comment cmnt(masm_, "[ Case fall-through"); |
| - VisitStatements(cases->at(index)->statements()); |
| - } |
| + // Compile case bodies as needed. |
|
iposva
2009/03/02 10:16:20
ASSERT that you do not have a valid frame here and
|
| + for (int i = 0; i < length; i++) { |
| + CaseClause* clause = cases->at(i); |
| - // Complete the switch statement based on the compilation state of |
| - // the last case that was compiled. |
| - if (next_test.is_unused()) { |
| - // The last test compiled was unconditionally true. We still need |
| - // to compile the default if we found one and it can be targeted |
| - // by fall through. |
| - if (default_clause != NULL) { |
| - bool was_only_clause = length == 1 && cases->at(0) == default_clause; |
| - if (was_only_clause || default_entry.is_linked()) { |
| - Comment cmnt(masm_, "[ Default clause"); |
| - default_entry.Bind(); |
| - VisitStatements(default_clause->statements()); |
| - // If control flow can fall off the end of the default and there |
| - // was a case after it, jump to that case's body. |
| - if (has_valid_frame() && default_exit.is_bound()) { |
| - default_exit.Jump(); |
| - } |
| + // There are two ways to reach the body: from the corresponding |
| + // test or as the fall through of the previous body. |
| + if (!clause->body_target()->is_linked() && !has_valid_frame()) { |
| + // If we have neither, skip this body. |
| + continue; |
| + } else if (clause->body_target()->is_linked() && has_valid_frame()) { |
| + // If we have both, put a jump on the fall through path to avoid |
| + // the dropping of the switch value on the test path. The |
| + // exception is the default which has already had the switch |
| + // value dropped. |
| + if (clause->is_default()) { |
| + clause->body_target()->Bind(); |
| + } else { |
| + JumpTarget body(this); |
| + body.Jump(); |
| + clause->body_target()->Bind(); |
| + frame_->Drop(); |
| + body.Bind(); |
| } |
| - } |
| - } else { |
| - // The switch value is still on the frame. We have to drop it and |
| - // possibly compile a default case. |
| - if (next_test.is_linked()) { |
| - if (has_valid_frame()) { |
| - // We have fall through and thus need to jump around the code |
| - // to drop the switch value. |
| - fall_through.Jump(); |
| + } else if (clause->body_target()->is_linked()) { |
| + // No fall through to worry about. |
| + clause->body_target()->Bind(); |
| + if (!clause->is_default()) { |
| + frame_->Drop(); |
| } |
| - next_test.Bind(); |
| } |
|
iposva
2009/03/02 10:16:20
Add else case here for the only fall-through case
|
| - frame_->Drop(); |
| - |
| - // If there was a default clause, compile it now. |
| - if (default_clause != NULL) { |
| - Comment cmnt(masm_, "[ Default clause"); |
| - if (default_entry.is_linked()) { |
| - default_entry.Bind(); |
| - } |
| - VisitStatements(default_clause->statements()); |
| - // If control flow can fall off the end of the default and there |
| - // was a case after it, jump to that case's body. |
| - if (has_valid_frame() && default_exit.is_bound()) { |
| - default_exit.Jump(); |
| - } |
| - } |
| + // Otherwise, we have only fall through. In any case, we are |
| + // prepared to compile the body. |
| + Comment cmnt(masm_, "[ Case body"); |
| + VisitStatements(clause->statements()); |
| } |
| - if (fall_through.is_linked()) { |
| - fall_through.Bind(); |
| - } |
| + // We may not have a valid frame here so bind the break target only |
| + // if needed. |
| if (node->break_target()->is_linked()) { |
| node->break_target()->Bind(); |
| } |