| Index: src/codegen-ia32.cc
|
| ===================================================================
|
| --- src/codegen-ia32.cc (revision 1441)
|
| +++ src/codegen-ia32.cc (working copy)
|
| @@ -154,18 +154,14 @@
|
| // Get outer context and create a new context based on it.
|
| frame_->PushFunction();
|
| Result context = frame_->CallRuntime(Runtime::kNewContext, 1);
|
| +
|
| // Update context local.
|
| frame_->SaveContextRegister();
|
|
|
| - if (kDebug) {
|
| - JumpTarget verified_true(this);
|
| - // Verify eax and esi are the same in debug mode
|
| + // Verify that the runtime call result and esi agree.
|
| + if (FLAG_debug_code) {
|
| __ cmp(context.reg(), Operand(esi));
|
| - context.Unuse();
|
| - verified_true.Branch(equal);
|
| - frame_->SpillAll();
|
| - __ int3();
|
| - verified_true.Bind();
|
| + __ Assert(equal, "Runtime::NewContext should end up in esi");
|
| }
|
| }
|
|
|
| @@ -736,17 +732,17 @@
|
|
|
| const char* GenericBinaryOpStub::GetName() {
|
| switch (op_) {
|
| - case Token::ADD: return "GenericBinaryOpStub_ADD";
|
| - case Token::SUB: return "GenericBinaryOpStub_SUB";
|
| - case Token::MUL: return "GenericBinaryOpStub_MUL";
|
| - case Token::DIV: return "GenericBinaryOpStub_DIV";
|
| - case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
|
| - case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
|
| - case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
|
| - case Token::SAR: return "GenericBinaryOpStub_SAR";
|
| - case Token::SHL: return "GenericBinaryOpStub_SHL";
|
| - case Token::SHR: return "GenericBinaryOpStub_SHR";
|
| - default: return "GenericBinaryOpStub";
|
| + case Token::ADD: return "GenericBinaryOpStub_ADD";
|
| + case Token::SUB: return "GenericBinaryOpStub_SUB";
|
| + case Token::MUL: return "GenericBinaryOpStub_MUL";
|
| + case Token::DIV: return "GenericBinaryOpStub_DIV";
|
| + case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
|
| + case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
|
| + case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
|
| + case Token::SAR: return "GenericBinaryOpStub_SAR";
|
| + case Token::SHL: return "GenericBinaryOpStub_SHL";
|
| + case Token::SHR: return "GenericBinaryOpStub_SHR";
|
| + default: return "GenericBinaryOpStub";
|
| }
|
| }
|
|
|
| @@ -840,9 +836,9 @@
|
| class DeferredInlineSmiOperation: public DeferredCode {
|
| public:
|
| DeferredInlineSmiOperation(CodeGenerator* generator,
|
| - Token::Value op,
|
| - Smi* value,
|
| - OverwriteMode overwrite_mode)
|
| + Token::Value op,
|
| + Smi* value,
|
| + OverwriteMode overwrite_mode)
|
| : DeferredCode(generator),
|
| op_(op),
|
| value_(value),
|
| @@ -1123,8 +1119,8 @@
|
| // In the slow case, this masking is done inside the runtime call.
|
| int shift_value = int_value & 0x1f;
|
| DeferredCode* deferred =
|
| - new DeferredInlineSmiOperation(this, Token::SAR, smi_value,
|
| - overwrite_mode);
|
| + new DeferredInlineSmiOperation(this, Token::SAR, smi_value,
|
| + overwrite_mode);
|
| Result result = frame_->Pop();
|
| result.ToRegister();
|
| __ test(result.reg(), Immediate(kSmiTagMask));
|
| @@ -1149,8 +1145,8 @@
|
| // In the slow case, this masking is done inside the runtime call.
|
| int shift_value = int_value & 0x1f;
|
| DeferredCode* deferred =
|
| - new DeferredInlineSmiOperation(this, Token::SHR, smi_value,
|
| - overwrite_mode);
|
| + new DeferredInlineSmiOperation(this, Token::SHR, smi_value,
|
| + overwrite_mode);
|
| Result operand = frame_->Pop();
|
| operand.ToRegister();
|
| __ test(operand.reg(), Immediate(kSmiTagMask));
|
| @@ -1186,8 +1182,8 @@
|
| // In the slow case, this masking is done inside the runtime call.
|
| int shift_value = int_value & 0x1f;
|
| DeferredCode* deferred =
|
| - new DeferredInlineSmiOperation(this, Token::SHL, smi_value,
|
| - overwrite_mode);
|
| + new DeferredInlineSmiOperation(this, Token::SHL, smi_value,
|
| + overwrite_mode);
|
| Result operand = frame_->Pop();
|
| operand.ToRegister();
|
| __ test(operand.reg(), Immediate(kSmiTagMask));
|
| @@ -1310,6 +1306,8 @@
|
| right_side = frame_->Pop();
|
| left_side = frame_->Pop();
|
| }
|
| + ASSERT(cc == less || cc == equal || cc == greater_equal);
|
| +
|
| // If either side is a constant smi, optimize the comparison.
|
| bool left_side_constant_smi =
|
| left_side.is_constant() && left_side.handle()->IsSmi();
|
| @@ -1325,17 +1323,18 @@
|
| // 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)) {
|
| - // The comparison is unconditionally true.
|
| - dest->Goto(true);
|
| - } else {
|
| - // The comparison is unconditionally false.
|
| - dest->Goto(false);
|
| + switch (cc) {
|
| + case less:
|
| + dest->Goto(left_value < right_value);
|
| + break;
|
| + case equal:
|
| + dest->Goto(left_value == right_value);
|
| + break;
|
| + case greater_equal:
|
| + dest->Goto(left_value >= right_value);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| }
|
| } else { // Only one side is a constant Smi.
|
| // If left side is a constant Smi, reverse the operands.
|
| @@ -1376,7 +1375,7 @@
|
| ASSERT(right_side.is_valid());
|
| __ cmp(left_side.reg(), Operand(right_side.reg()));
|
| } else {
|
| - __ cmp(Operand(left_side.reg()), Immediate(right_side.handle()));
|
| + __ cmp(Operand(left_side.reg()), Immediate(right_side.handle()));
|
| }
|
| left_side.Unuse();
|
| right_side.Unuse();
|
| @@ -1421,8 +1420,8 @@
|
| } else { // Neither side is a constant Smi or null.
|
| // If either side is a non-smi constant, skip the smi check.
|
| bool known_non_smi =
|
| - left_side.is_constant() && !left_side.handle()->IsSmi() ||
|
| - right_side.is_constant() && !right_side.handle()->IsSmi();
|
| + (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
|
| + (right_side.is_constant() && !right_side.handle()->IsSmi());
|
| left_side.ToRegister();
|
| right_side.ToRegister();
|
| JumpTarget is_smi(this);
|
| @@ -1870,16 +1869,10 @@
|
| // Update context local.
|
| frame_->SaveContextRegister();
|
|
|
| - if (kDebug) {
|
| - JumpTarget verified_true(this);
|
| - // Verify that the result of the runtime call and the esi register are
|
| - // the same in debug mode.
|
| + // Verify that the runtime call result and esi agree.
|
| + if (FLAG_debug_code) {
|
| __ cmp(context.reg(), Operand(esi));
|
| - context.Unuse();
|
| - verified_true.Branch(equal);
|
| - frame_->SpillAll();
|
| - __ int3();
|
| - verified_true.Bind();
|
| + __ Assert(equal, "Runtime::NewContext should end up in esi");
|
| }
|
| }
|
|
|
| @@ -2031,199 +2024,121 @@
|
| 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());
|
| - }
|
|
|
| - // 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();
|
| - }
|
| - }
|
| - }
|
| - } 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();
|
| - }
|
| - next_test.Bind();
|
| - }
|
| - frame_->Drop();
|
| + // The last instruction emitted was a jump, either to the default
|
| + // clause or the break target, or else to a case body from the loop
|
| + // that compiles the tests.
|
| + ASSERT(!has_valid_frame());
|
| + // Compile case bodies as needed.
|
| + for (int i = 0; i < length; i++) {
|
| + CaseClause* clause = cases->at(i);
|
|
|
| - // 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();
|
| + // 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();
|
| }
|
| - 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();
|
| + } else if (clause->body_target()->is_linked()) {
|
| + // No fall through to worry about.
|
| + clause->body_target()->Bind();
|
| + if (!clause->is_default()) {
|
| + frame_->Drop();
|
| }
|
| + } else {
|
| + // Otherwise, we have only fall through.
|
| + ASSERT(has_valid_frame());
|
| }
|
| - }
|
|
|
| - if (fall_through.is_linked()) {
|
| - fall_through.Bind();
|
| + // We are now prepared to compile the body.
|
| + Comment cmnt(masm_, "[ Case body");
|
| + VisitStatements(clause->statements());
|
| }
|
| +
|
| + // 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();
|
| }
|
| @@ -2915,23 +2830,30 @@
|
| }
|
|
|
| // Unlink from try chain; be careful not to destroy the TOS.
|
| - unlink.Bind();
|
| - // Reload sp from the top handler, because some statements that we
|
| - // break from (eg, for...in) may have left stuff on the stack.
|
| - // Preserve the TOS in a register across stack manipulation.
|
| - frame_->EmitPop(eax);
|
| - ExternalReference handler_address(Top::k_handler_address);
|
| - __ mov(edx, Operand::StaticVariable(handler_address));
|
| - const int kNextOffset = StackHandlerConstants::kNextOffset +
|
| - StackHandlerConstants::kAddressDisplacement;
|
| - __ lea(esp, Operand(edx, kNextOffset));
|
| - frame_->Forget(frame_->height() - handler_height);
|
| + if (unlink.is_linked()) {
|
| + unlink.Bind();
|
| + }
|
|
|
| - frame_->EmitPop(Operand::StaticVariable(handler_address));
|
| - frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
|
| - // Next_sp popped.
|
| - frame_->EmitPush(eax);
|
| + // Control can reach here via a jump to unlink or by falling off the
|
| + // end of the try block (with no unlinks).
|
| + if (has_valid_frame()) {
|
| + // Reload sp from the top handler, because some statements that we
|
| + // break from (eg, for...in) may have left stuff on the stack.
|
| + // Preserve the TOS in a register across stack manipulation.
|
| + frame_->EmitPop(eax);
|
| + ExternalReference handler_address(Top::k_handler_address);
|
| + __ mov(edx, Operand::StaticVariable(handler_address));
|
| + const int kNextOffset = StackHandlerConstants::kNextOffset +
|
| + StackHandlerConstants::kAddressDisplacement;
|
| + __ lea(esp, Operand(edx, kNextOffset));
|
| + frame_->Forget(frame_->height() - handler_height);
|
|
|
| + frame_->EmitPop(Operand::StaticVariable(handler_address));
|
| + frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
|
| + // Next_sp popped.
|
| + frame_->EmitPush(eax);
|
| + }
|
| +
|
| // --- Finally block ---
|
| finally_block.Bind();
|
|
|
| @@ -3517,7 +3439,10 @@
|
| for (int i = 0; i < node->properties()->length(); i++) {
|
| ObjectLiteral::Property* property = node->properties()->at(i);
|
| switch (property->kind()) {
|
| - case ObjectLiteral::Property::CONSTANT: break;
|
| + case ObjectLiteral::Property::CONSTANT:
|
| + break;
|
| + case ObjectLiteral::Property::OBJECT_LITERAL:
|
| + if (property->value()->AsObjectLiteral()->is_simple()) break;
|
| case ObjectLiteral::Property::COMPUTED: {
|
| Handle<Object> key(property->key()->handle());
|
| Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
|
|
|