Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Unified Diff: src/codegen-ia32.cc

Issue 21211: Optimize comparisons with constant Smis. Evaluate comparisons of... (Closed) Base URL: http://v8.googlecode.com/svn/branches/experimental/toiger/
Patch Set: '' Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/codegen-ia32.cc
===================================================================
--- src/codegen-ia32.cc (revision 1247)
+++ src/codegen-ia32.cc (working copy)
@@ -1333,53 +1333,122 @@
right_side = frame_->Pop();
left_side = frame_->Pop();
}
- left_side.ToRegister();
- right_side.ToRegister();
- ASSERT(left_side.is_valid());
- ASSERT(right_side.is_valid());
- // Check for the smi case.
- JumpTarget is_smi(this);
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), left_side.reg());
- __ or_(temp.reg(), Operand(right_side.reg()));
- __ test(temp.reg(), Immediate(kSmiTagMask));
- temp.Unuse();
- is_smi.Branch(zero, &left_side, &right_side, taken);
+ // If either side is a constant smi, optimize the comparison.
+ bool left_side_constant_smi =
+ left_side.is_constant() && left_side.handle()->IsSmi();
+ bool right_side_constant_smi =
+ right_side.is_constant() && right_side.handle()->IsSmi();
- // When non-smi, call out to the compare stub. "parameters" setup by
- // calling code in edx and eax and "result" is returned in the flags.
- if (!left_side.reg().is(eax)) {
- right_side.ToRegister(eax);
- left_side.ToRegister(edx);
- } else if (!right_side.reg().is(edx)) {
- left_side.ToRegister(edx);
- right_side.ToRegister(eax);
- } else {
- frame_->Spill(eax); // Can be multiply referenced, even now.
- frame_->Spill(edx);
- __ xchg(eax, edx);
- // If left_side and right_side become real (non-dummy) arguments
- // to CallStub, they need to be swapped in this case.
+ if (left_side_constant_smi || right_side_constant_smi) {
+ if (left_side_constant_smi && right_side_constant_smi) {
+ // Trivial case, comparing two constants.
+ int left_value = Smi::cast(*left_side.handle())->value();
+ int right_value = Smi::cast(*right_side.handle())->value();
+ if (left_value < right_value &&
+ (cc == less || cc == less_equal || cc == not_equal) ||
+ left_value == right_value &&
+ (cc == less_equal || cc == equal || cc == greater_equal) ||
+ left_value > right_value &&
+ (cc == greater || cc == greater_equal || cc == not_equal)) {
+ // Comparison is unconditionally true.
+ if (true_target->is_bound()) {
+ true_target->Jump();
+ } else {
+ true_target->Bind();
+ }
+ } else { // Comparison is always false.
+ if (false_target->is_bound()) {
+ false_target->Jump();
+ } else {
+ false_target->Bind();
+ }
+ }
+ } else { // Only one side is a constant Smi.
+ // If left side is a constant Smi, reverse the operands.
+ // Since one side is a constant Smi, conversion order does not matter.
+ if (left_side_constant_smi) {
+ Result temp = left_side;
+ left_side = right_side;
+ right_side = temp;
+ cc = ReverseCondition(cc);
+ }
+ // Implement comparison against a constant Smi, inlining the case
+ // where both sides are Smis.
+ left_side.ToRegister();
+ ASSERT(left_side.is_valid());
+ JumpTarget is_smi(this);
+ __ test(left_side.reg(), Immediate(kSmiTagMask));
+ is_smi.Branch(zero, &left_side, &right_side, taken);
+
+ // Setup and call the compare stub, which expects arguments in edx
+ // and eax.
+ CompareStub stub(cc, strict);
+ left_side.ToRegister(eax);
+ right_side.ToRegister(edx);
+ Result result = frame_->CallStub(&stub, &left_side, &right_side, 0);
+ result.ToRegister();
+ __ cmp(result.reg(), 0);
+ result.Unuse();
+ true_target->Branch(cc);
+ false_target->Jump();
+
+ is_smi.Bind(&left_side, &right_side);
+ left_side.ToRegister();
+ // Test smi equality and comparison by signed int comparison.
+ __ cmp(Operand(left_side.reg()), Immediate(right_side.handle()));
+ left_side.Unuse();
+ right_side.Unuse();
+ Branch(cc, true_target, false_target, fall_through);
+ }
+ } else { // Neither side is a constant Smi, normal comparison operation.
+ left_side.ToRegister();
+ right_side.ToRegister();
+ ASSERT(left_side.is_valid());
+ ASSERT(right_side.is_valid());
+ // Check for the smi case.
+ JumpTarget is_smi(this);
+ Result temp = allocator_->Allocate();
+ ASSERT(temp.is_valid());
+ __ mov(temp.reg(), left_side.reg());
+ __ or_(temp.reg(), Operand(right_side.reg()));
+ __ test(temp.reg(), Immediate(kSmiTagMask));
+ temp.Unuse();
+ is_smi.Branch(zero, &left_side, &right_side, taken);
+
+ // When non-smi, call out to the compare stub. "parameters" setup by
+ // calling code in edx and eax and "result" is returned in the flags.
+ if (!left_side.reg().is(eax)) {
+ right_side.ToRegister(eax);
+ left_side.ToRegister(edx);
+ } else if (!right_side.reg().is(edx)) {
+ left_side.ToRegister(edx);
+ right_side.ToRegister(eax);
+ } else {
+ frame_->Spill(eax); // Can be multiply referenced, even now.
+ frame_->Spill(edx);
+ __ xchg(eax, edx);
+ // If left_side and right_side become real (non-dummy) arguments
+ // to CallStub, they need to be swapped in this case.
+ }
+ CompareStub stub(cc, strict);
+ Result answer = frame_->CallStub(&stub, &right_side, &left_side, 0);
+ if (cc == equal) {
+ __ test(answer.reg(), Operand(answer.reg()));
+ } else {
+ __ cmp(answer.reg(), 0);
+ }
+ answer.Unuse();
+ true_target->Branch(cc);
+ false_target->Jump();
+
+ is_smi.Bind(&left_side, &right_side);
+ left_side.ToRegister();
+ right_side.ToRegister();
+ __ cmp(left_side.reg(), Operand(right_side.reg()));
+ right_side.Unuse();
+ left_side.Unuse();
+ Branch(cc, true_target, false_target, fall_through);
}
- CompareStub stub(cc, strict);
- Result answer = frame_->CallStub(&stub, &right_side, &left_side, 0);
- if (cc == equal) {
- __ test(answer.reg(), Operand(answer.reg()));
- } else {
- __ cmp(answer.reg(), 0);
- }
- answer.Unuse();
- true_target->Branch(cc);
- false_target->Jump();
-
- is_smi.Bind(&left_side, &right_side);
- left_side.ToRegister();
- right_side.ToRegister();
- __ cmp(left_side.reg(), Operand(right_side.reg()));
- right_side.Unuse();
- left_side.Unuse();
- Branch(cc, true_target, false_target, fall_through);
}
@@ -1952,30 +2021,54 @@
// Compile each non-default clause.
Comment cmnt(masm_, "[ Case clause");
- // Label and compile the test.
if (next_test.is_linked()) {
// Recycle the same label for each test.
next_test.Bind();
next_test.Unuse();
}
- // Duplicate the switch value.
- frame_->Dup();
- Load(clause->label());
+
JumpTarget enter_body(this);
- Comparison(equal, true, &enter_body, &next_test, &enter_body);
+ // Control flow reaches this test if it is the first non-default case,
+ // or if a previous test links to this as the next test through the
+ // next_test JumpTarget. If so, then has_valid_frame() is true.
+ if (has_valid_frame()) {
+ // Duplicate the switch value.
+ frame_->Dup();
+ Load(clause->label());
+ Comparison(equal, true, &enter_body, &next_test, &enter_body);
+ }
- // Before entering the body from the test remove the switch value from
- // the frame.
- frame_->Drop();
+ bool previous_was_default = (i > 0 && cases->at(i - 1)->is_default());
+ // If the body is unreachable, don't compile it.
+ if (!enter_body.is_bound() &&
+ !previous_was_default &&
+ !fall_through.is_linked()) {
+ next_test.Unuse();
+ continue;
+ }
+ // Compile the body, since it is reachable from the test or a fall-through.
+ if (next_test.is_bound()) {
+ // The test unconditionally failed, we should go to the next test.
+ // We still need to compile the body for the fall-through cases.
+ // In this case, we need to jump over the body.
+ next_test.Unuse();
+ next_test.Jump();
+ } else if (enter_body.is_bound()) {
+ // Otherwise we got here by passing the test.
+ frame_->Drop(); // The switch value is no longer needed.
+ } else {
+ // The tests are being skipped due to an earlier unconditional match,
+ // and only fall-through to bodies is generating control flow.
+ ASSERT(!has_valid_frame());
+ }
// Label the body so that fall through is enabled.
- if (i > 0 && cases->at(i - 1)->is_default()) {
- // The previous case was the default. This will be the target of a
- // possible backward edge.
+ 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();
} else if (fall_through.is_linked()) {
- // Recycle the same label for each fall through except for the default
- // case.
+ // Recycle the same label for each fall through.
fall_through.Bind();
fall_through.Unuse();
}
@@ -1994,14 +2087,23 @@
}
// The block at the final "test" label removes the switch value.
- next_test.Bind();
- frame_->Drop();
-
- // If there is a default clause, compile it now.
+ if (next_test.is_linked()) {
+ // JumpTarget next_test could have been bound, rather than linked to,
+ // if the previous test was unconditionally false.
+ next_test.Bind();
+ }
+ if (has_valid_frame()) {
+ frame_->Drop();
+ }
+ // If there is a default clause, compile it now. If it is determined
+ // at compile time to be unreachable, then it has no valid entry frame,
+ // and it is not compiled.
if (default_clause != NULL) {
Comment cmnt(masm_, "[ Default clause");
default_entry.Bind();
- VisitStatements(default_clause->statements());
+ if (has_valid_frame()) {
+ VisitStatements(default_clause->statements());
+ }
// If control flow can fall out of the default and there is a case after
// it, jump to that case's body.
if (has_valid_frame() && default_exit.is_bound()) {
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698