Use untagged int32 values in evaluation of side-effect free expressions.
authorwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 16 Mar 2010 16:07:19 +0000 (16:07 +0000)
committerwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 16 Mar 2010 16:07:19 +0000 (16:07 +0000)
Review URL: http://codereview.chromium.org/975001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4148 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

13 files changed:
src/ast.h
src/flag-definitions.h
src/frame-element.h
src/ia32/assembler-ia32-inl.h
src/ia32/assembler-ia32.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/ia32/register-allocator-ia32.cc
src/ia32/virtual-frame-ia32.cc
src/ia32/virtual-frame-ia32.h
src/register-allocator.h
src/rewriter.cc
src/virtual-frame.cc

index ac39cab..262bf17 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -269,8 +269,18 @@ class Expression: public AstNode {
     bitfields_ |= ToInt32Field::encode(to_int32);
   }
 
+  // How many bitwise logical or shift operators are used in this expression?
+  int num_bit_ops() { return NumBitOpsField::decode(bitfields_); }
+  void set_num_bit_ops(int num_bit_ops) {
+    bitfields_ &= ~NumBitOpsField::mask();
+    num_bit_ops = Min(num_bit_ops, kMaxNumBitOps);
+    bitfields_ |= NumBitOpsField::encode(num_bit_ops);
+  }
+
 
  private:
+  static const int kMaxNumBitOps = (1 << 5) - 1;
+
   uint32_t bitfields_;
   StaticType type_;
 
@@ -281,6 +291,7 @@ class Expression: public AstNode {
   class SideEffectFreeField : public BitField<bool, 0, 1> {};
   class NoNegativeZeroField : public BitField<bool, 1, 1> {};
   class ToInt32Field : public BitField<bool, 2, 1> {};
+  class NumBitOpsField : public BitField<int, 3, 5> {};
 };
 
 
index 573d893..021362d 100644 (file)
@@ -153,7 +153,7 @@ DEFINE_bool(always_fast_compiler, false,
             "try to use the speculative optimizing backend for all code")
 DEFINE_bool(trace_bailout, false,
             "print reasons for falling back to using the classic V8 backend")
-DEFINE_bool(safe_int32_compiler, false,
+DEFINE_bool(safe_int32_compiler, true,
             "enable optimized side-effect-free int32 expressions.")
 DEFINE_bool(use_flow_graph, false, "perform flow-graph based optimizations")
 
index 3c5a389..2bbc288 100644 (file)
@@ -145,6 +145,16 @@ class FrameElement BASE_EMBEDDED {
   void set_copied() { value_ = value_ | CopiedField::encode(true); }
   void clear_copied() { value_ = value_ & ~CopiedField::mask(); }
 
+  // An untagged int32 FrameElement represents a signed int32
+  // on the stack.  These are only allowed in a side-effect-free
+  // int32 calculation, and if a non-int32 input shows up or an overflow
+  // occurs, we bail out and drop all the int32 values.
+  void set_untagged_int32(bool value) {
+    value_ &= ~UntaggedInt32Field::mask();
+    value_ |= UntaggedInt32Field::encode(value);
+  }
+  bool is_untagged_int32() const { return UntaggedInt32Field::decode(value_); }
+
   Register reg() const {
     ASSERT(is_register());
     uint32_t reg = DataField::decode(value_);
@@ -255,8 +265,9 @@ class FrameElement BASE_EMBEDDED {
   class TypeField: public BitField<Type, 0, 3> {};
   class CopiedField: public BitField<bool, 3, 1> {};
   class SyncedField: public BitField<bool, 4, 1> {};
-  class NumberInfoField: public BitField<int, 5, 4> {};
-  class DataField: public BitField<uint32_t, 9, 32 - 9> {};
+  class UntaggedInt32Field: public BitField<bool, 5, 1> {};
+  class NumberInfoField: public BitField<int, 6, 4> {};
+  class DataField: public BitField<uint32_t, 10, 32 - 10> {};
 
   friend class VirtualFrame;
 };
index 69f2a8d..6dc584e 100644 (file)
@@ -314,6 +314,12 @@ Operand::Operand(Register reg) {
 }
 
 
+Operand::Operand(XMMRegister xmm_reg) {
+  Register reg = { xmm_reg.code() };
+  set_modrm(3, reg);
+}
+
+
 Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
   // [disp/r]
   set_modrm(0, ebp);
index d8cd57a..6a7effd 100644 (file)
@@ -241,6 +241,9 @@ class Operand BASE_EMBEDDED {
   // reg
   INLINE(explicit Operand(Register reg));
 
+  // XMM reg
+  INLINE(explicit Operand(XMMRegister xmm_reg));
+
   // [disp/r]
   INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode));
   // disp only must always be relocated
@@ -709,6 +712,7 @@ class Assembler : public Malloced {
   void fistp_s(const Operand& adr);
   void fistp_d(const Operand& adr);
 
+  // The fisttp instructions require SSE3.
   void fisttp_s(const Operand& adr);
   void fisttp_d(const Operand& adr);
 
index b2e64cf..3cfabf3 100644 (file)
@@ -112,6 +112,8 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm)
       allocator_(NULL),
       state_(NULL),
       loop_nesting_(0),
+      in_safe_int32_mode_(false),
+      safe_int32_mode_enabled_(true),
       function_return_is_shadowed_(false),
       in_spilled_code_(false) {
 }
@@ -437,14 +439,14 @@ Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
 // frame. If the expression is boolean-valued it may be compiled (or
 // partially compiled) into control flow to the control destination.
 // If force_control is true, control flow is forced.
-void CodeGenerator::LoadCondition(Expression* x,
+void CodeGenerator::LoadCondition(Expression* expr,
                                   ControlDestination* dest,
                                   bool force_control) {
   ASSERT(!in_spilled_code());
   int original_height = frame_->height();
 
   { CodeGenState new_state(this, dest);
-    Visit(x);
+    Visit(expr);
 
     // If we hit a stack overflow, we may not have actually visited
     // the expression.  In that case, we ensure that we have a
@@ -481,64 +483,157 @@ void CodeGenerator::LoadAndSpill(Expression* expression) {
 }
 
 
+void CodeGenerator::LoadInSafeInt32Mode(Expression* expr,
+                                         BreakTarget* unsafe_bailout) {
+  set_unsafe_bailout(unsafe_bailout);
+  set_in_safe_int32_mode(true);
+  Load(expr);
+  Result value = frame_->Pop();
+  ASSERT(frame_->HasNoUntaggedInt32Elements());
+  ConvertInt32ResultToNumber(&value);
+  set_in_safe_int32_mode(false);
+  set_unsafe_bailout(NULL);
+  frame_->Push(&value);
+}
+
+
+void CodeGenerator::LoadWithSafeInt32ModeDisabled(Expression* expr) {
+  set_safe_int32_mode_enabled(false);
+  Load(expr);
+  set_safe_int32_mode_enabled(true);
+}
+
+
+void CodeGenerator::ConvertInt32ResultToNumber(Result* value) {
+  ASSERT(value->is_untagged_int32());
+  if (value->is_register()) {
+    Register val = value->reg();
+    JumpTarget done;
+    __ add(val, Operand(val));
+    done.Branch(no_overflow, value);
+    __ sar(val, 1);
+    // If there was an overflow, bits 30 and 31 of the original number disagree.
+    __ xor_(val, 0x80000000u);
+    if (CpuFeatures::IsSupported(SSE2)) {
+      CpuFeatures::Scope fscope(SSE2);
+      __ cvtsi2sd(xmm0, Operand(val));
+    } else {
+      // Move val to ST[0] in the FPU
+      // Push and pop are safe with respect to the virtual frame because
+      // all synced elements are below the actual stack pointer.
+      __ push(val);
+      __ fild_s(Operand(esp, 0));
+      __ pop(val);
+    }
+    Result scratch = allocator_->Allocate();
+    ASSERT(scratch.is_register());
+    Label allocation_failed;
+    __ AllocateHeapNumber(val, scratch.reg(),
+                          no_reg, &allocation_failed);
+    VirtualFrame* clone = new VirtualFrame(frame_);
+    scratch.Unuse();
+    if (CpuFeatures::IsSupported(SSE2)) {
+      CpuFeatures::Scope fscope(SSE2);
+      __ movdbl(FieldOperand(val, HeapNumber::kValueOffset), xmm0);
+    } else {
+      __ fstp_d(FieldOperand(val, HeapNumber::kValueOffset));
+    }
+    done.Jump(value);
+
+    // Establish the virtual frame, cloned from where AllocateHeapNumber
+    // jumped to allocation_failed.
+    RegisterFile empty_regs;
+    SetFrame(clone, &empty_regs);
+    __ bind(&allocation_failed);
+    unsafe_bailout_->Jump();
+
+    done.Bind(value);
+  } else {
+    ASSERT(value->is_constant());
+  }
+  value->set_untagged_int32(false);
+}
+
+
 void CodeGenerator::Load(Expression* expr) {
 #ifdef DEBUG
   int original_height = frame_->height();
 #endif
   ASSERT(!in_spilled_code());
-  JumpTarget true_target;
-  JumpTarget false_target;
-  ControlDestination dest(&true_target, &false_target, true);
-  LoadCondition(expr, &dest, false);
+  JumpTarget done;
 
-  if (dest.false_was_fall_through()) {
-    // The false target was just bound.
-    JumpTarget loaded;
-    frame_->Push(Factory::false_value());
-    // There may be dangling jumps to the true target.
-    if (true_target.is_linked()) {
-      loaded.Jump();
-      true_target.Bind();
-      frame_->Push(Factory::true_value());
-      loaded.Bind();
+  // If the expression should be a side-effect-free 32-bit int computation,
+  // compile that SafeInt32 path, and a bailout path.
+  if (!in_safe_int32_mode() &&
+      safe_int32_mode_enabled() &&
+      expr->side_effect_free() &&
+      expr->num_bit_ops() > 2 &&
+      CpuFeatures::IsSupported(SSE2)) {
+    BreakTarget unsafe_bailout;
+    unsafe_bailout.set_expected_height(frame_->height());
+    LoadInSafeInt32Mode(expr, &unsafe_bailout);
+    done.Jump();
+
+    if (unsafe_bailout.is_linked()) {
+      unsafe_bailout.Bind();
+      LoadWithSafeInt32ModeDisabled(expr);
     }
+  } else {
+    JumpTarget true_target;
+    JumpTarget false_target;
 
-  } else if (dest.is_used()) {
-    // There is true, and possibly false, control flow (with true as
-    // the fall through).
-    JumpTarget loaded;
-    frame_->Push(Factory::true_value());
-    if (false_target.is_linked()) {
-      loaded.Jump();
-      false_target.Bind();
-      frame_->Push(Factory::false_value());
-      loaded.Bind();
-    }
+    ControlDestination dest(&true_target, &false_target, true);
+    LoadCondition(expr, &dest, false);
 
-  } else {
-    // We have a valid value on top of the frame, but we still may
-    // have dangling jumps to the true and false targets from nested
-    // subexpressions (eg, the left subexpressions of the
-    // short-circuited boolean operators).
-    ASSERT(has_valid_frame());
-    if (true_target.is_linked() || false_target.is_linked()) {
+    if (dest.false_was_fall_through()) {
+      // The false target was just bound.
       JumpTarget loaded;
-      loaded.Jump();  // Don't lose the current TOS.
+      frame_->Push(Factory::false_value());
+      // There may be dangling jumps to the true target.
       if (true_target.is_linked()) {
+        loaded.Jump();
         true_target.Bind();
         frame_->Push(Factory::true_value());
-        if (false_target.is_linked()) {
-          loaded.Jump();
-        }
+        loaded.Bind();
       }
+
+    } else if (dest.is_used()) {
+      // There is true, and possibly false, control flow (with true as
+      // the fall through).
+      JumpTarget loaded;
+      frame_->Push(Factory::true_value());
       if (false_target.is_linked()) {
+        loaded.Jump();
         false_target.Bind();
         frame_->Push(Factory::false_value());
+        loaded.Bind();
+      }
+
+    } else {
+      // We have a valid value on top of the frame, but we still may
+      // have dangling jumps to the true and false targets from nested
+      // subexpressions (eg, the left subexpressions of the
+      // short-circuited boolean operators).
+      ASSERT(has_valid_frame());
+      if (true_target.is_linked() || false_target.is_linked()) {
+        JumpTarget loaded;
+        loaded.Jump();  // Don't lose the current TOS.
+        if (true_target.is_linked()) {
+          true_target.Bind();
+          frame_->Push(Factory::true_value());
+          if (false_target.is_linked()) {
+            loaded.Jump();
+          }
+        }
+        if (false_target.is_linked()) {
+          false_target.Bind();
+          frame_->Push(Factory::false_value());
+        }
+        loaded.Bind();
       }
-      loaded.Bind();
     }
   }
-
+  done.Bind();
   ASSERT(has_valid_frame());
   ASSERT(frame_->height() == original_height + 1);
 }
@@ -4312,7 +4407,7 @@ Result CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
 
 void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
   Comment cmnt(masm_, "[ FunctionLiteral");
-
+  ASSERT(!in_safe_int32_mode());
   // Build the function boilerplate and instantiate it.
   Handle<JSFunction> boilerplate =
       Compiler::BuildBoilerplate(node, script(), this);
@@ -4325,6 +4420,7 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
 
 void CodeGenerator::VisitFunctionBoilerplateLiteral(
     FunctionBoilerplateLiteral* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
   Result result = InstantiateBoilerplate(node->boilerplate());
   frame()->Push(&result);
@@ -4333,6 +4429,7 @@ void CodeGenerator::VisitFunctionBoilerplateLiteral(
 
 void CodeGenerator::VisitConditional(Conditional* node) {
   Comment cmnt(masm_, "[ Conditional");
+  ASSERT(!in_safe_int32_mode());
   JumpTarget then;
   JumpTarget else_;
   JumpTarget exit;
@@ -4503,6 +4600,7 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
     Slot* slot,
     TypeofState typeof_state,
     JumpTarget* slow) {
+  ASSERT(!in_safe_int32_mode());
   // Check that no extension objects have been created by calls to
   // eval from the current scope to the global scope.
   Register context = esi;
@@ -4671,10 +4769,20 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
 }
 
 
-void CodeGenerator::VisitSlot(Slot* node) {
+void CodeGenerator::VisitSlot(Slot* slot) {
   Comment cmnt(masm_, "[ Slot");
-  Result result = LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
-  frame()->Push(&result);
+  if (in_safe_int32_mode()) {
+    if ((slot->type() == Slot::LOCAL  && !slot->is_arguments())) {
+      frame()->UntaggedPushLocalAt(slot->index());
+    } else if (slot->type() == Slot::PARAMETER) {
+      frame()->UntaggedPushParameterAt(slot->index());
+    } else {
+      UNREACHABLE();
+    }
+  } else {
+    Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
+    frame()->Push(&result);
+  }
 }
 
 
@@ -4686,6 +4794,7 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
     Visit(expr);
   } else {
     ASSERT(var->is_global());
+    ASSERT(!in_safe_int32_mode());
     Reference ref(this, node);
     ref.GetValue();
   }
@@ -4694,7 +4803,11 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
 
 void CodeGenerator::VisitLiteral(Literal* node) {
   Comment cmnt(masm_, "[ Literal");
-  frame_->Push(node->handle());
+  if (in_safe_int32_mode()) {
+    frame_->PushUntaggedElement(node->handle());
+  } else {
+    frame_->Push(node->handle());
+  }
 }
 
 
@@ -4768,6 +4881,7 @@ void DeferredRegExpLiteral::Generate() {
 
 
 void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ RegExp Literal");
 
   // Retrieve the literals array and check the allocated entry.  Begin
@@ -4804,6 +4918,7 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
 
 
 void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ ObjectLiteral");
 
   // Load a writable copy of the function of this activation in a
@@ -4888,6 +5003,7 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
 
 
 void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ ArrayLiteral");
 
   // Load a writable copy of the function of this activation in a
@@ -4959,6 +5075,7 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
 
 
 void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
+  ASSERT(!in_safe_int32_mode());
   ASSERT(!in_spilled_code());
   // Call runtime routine to allocate the catch extension object and
   // assign the exception value to the catch variable.
@@ -5178,6 +5295,7 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
 
 
 void CodeGenerator::VisitAssignment(Assignment* node) {
+  ASSERT(!in_safe_int32_mode());
 #ifdef DEBUG
   int original_height = frame()->height();
 #endif
@@ -5213,6 +5331,7 @@ void CodeGenerator::VisitAssignment(Assignment* node) {
 
 
 void CodeGenerator::VisitThrow(Throw* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ Throw");
   Load(node->exception());
   Result result = frame_->CallRuntime(Runtime::kThrow, 1);
@@ -5221,6 +5340,7 @@ void CodeGenerator::VisitThrow(Throw* node) {
 
 
 void CodeGenerator::VisitProperty(Property* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ Property");
   Reference property(this, node);
   property.GetValue();
@@ -5228,6 +5348,7 @@ void CodeGenerator::VisitProperty(Property* node) {
 
 
 void CodeGenerator::VisitCall(Call* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ Call");
 
   Expression* function = node->expression();
@@ -5443,6 +5564,7 @@ void CodeGenerator::VisitCall(Call* node) {
 
 
 void CodeGenerator::VisitCallNew(CallNew* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ CallNew");
 
   // According to ECMA-262, section 11.2.2, page 44, the function
@@ -6370,6 +6492,7 @@ void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
 
 
 void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
+  ASSERT(!in_safe_int32_mode());
   if (CheckForInlineRuntimeCall(node)) {
     return;
   }
@@ -6496,64 +6619,100 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
     }
 
   } else {
-    Load(node->expression());
-    bool overwrite =
-        (node->expression()->AsBinaryOperation() != NULL &&
-         node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
-    switch (op) {
-      case Token::SUB: {
-        GenericUnaryOpStub stub(Token::SUB, overwrite);
-        Result operand = frame_->Pop();
-        Result answer = frame_->CallStub(&stub, &operand);
-        frame_->Push(&answer);
-        break;
+    if (in_safe_int32_mode()) {
+      Visit(node->expression());
+      Result value = frame_->Pop();
+      ASSERT(value.is_untagged_int32());
+      // Registers containing an int32 value are not multiply used.
+      ASSERT(!value.is_register() || !frame_->is_used(value.reg()));
+      value.ToRegister();
+      switch (op) {
+        case Token::SUB: {
+          __ neg(value.reg());
+          if (node->no_negative_zero()) {
+            // -MIN_INT is MIN_INT with the overflow flag set.
+            unsafe_bailout_->Branch(overflow);
+          } else {
+            // MIN_INT and 0 both have bad negations.  They both have 31 zeros.
+            __ test(value.reg(), Immediate(0x7FFFFFFF));
+            unsafe_bailout_->Branch(zero);
+          }
+          break;
+        }
+        case Token::BIT_NOT: {
+          __ not_(value.reg());
+          break;
+        }
+        case Token::ADD: {
+          // Unary plus has no effect on int32 values.
+          break;
+        }
+        default:
+          UNREACHABLE();
+          break;
       }
+      frame_->Push(&value);
 
-      case Token::BIT_NOT: {
-        // Smi check.
-        JumpTarget smi_label;
-        JumpTarget continue_label;
-        Result operand = frame_->Pop();
-        operand.ToRegister();
-        __ test(operand.reg(), Immediate(kSmiTagMask));
-        smi_label.Branch(zero, &operand, taken);
-
-        GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
-        Result answer = frame_->CallStub(&stub, &operand);
-        continue_label.Jump(&answer);
-
-        smi_label.Bind(&answer);
-        answer.ToRegister();
-        frame_->Spill(answer.reg());
-        __ not_(answer.reg());
-        __ and_(answer.reg(), ~kSmiTagMask);  // Remove inverted smi-tag.
+    } else {
+      Load(node->expression());
+      bool overwrite =
+          (node->expression()->AsBinaryOperation() != NULL &&
+           node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+      switch (op) {
+        case Token::SUB: {
+          GenericUnaryOpStub stub(Token::SUB, overwrite);
+          Result operand = frame_->Pop();
+          Result answer = frame_->CallStub(&stub, &operand);
+          frame_->Push(&answer);
+          break;
+        }
 
-        continue_label.Bind(&answer);
-        frame_->Push(&answer);
-        break;
-      }
+        case Token::BIT_NOT: {
+          // Smi check.
+          JumpTarget smi_label;
+          JumpTarget continue_label;
+          Result operand = frame_->Pop();
+          operand.ToRegister();
+          __ test(operand.reg(), Immediate(kSmiTagMask));
+          smi_label.Branch(zero, &operand, taken);
+
+          GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
+          Result answer = frame_->CallStub(&stub, &operand);
+          continue_label.Jump(&answer);
+
+          smi_label.Bind(&answer);
+          answer.ToRegister();
+          frame_->Spill(answer.reg());
+          __ not_(answer.reg());
+          __ and_(answer.reg(), ~kSmiTagMask);  // Remove inverted smi-tag.
+
+          continue_label.Bind(&answer);
+          frame_->Push(&answer);
+          break;
+        }
 
-      case Token::ADD: {
-        // Smi check.
-        JumpTarget continue_label;
-        Result operand = frame_->Pop();
-        operand.ToRegister();
-        __ test(operand.reg(), Immediate(kSmiTagMask));
-        continue_label.Branch(zero, &operand, taken);
+        case Token::ADD: {
+          // Smi check.
+          JumpTarget continue_label;
+          Result operand = frame_->Pop();
+          operand.ToRegister();
+          __ test(operand.reg(), Immediate(kSmiTagMask));
+          continue_label.Branch(zero, &operand, taken);
 
-        frame_->Push(&operand);
-        Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
+          frame_->Push(&operand);
+          Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
                                               CALL_FUNCTION, 1);
 
-        continue_label.Bind(&answer);
-        frame_->Push(&answer);
-        break;
-      }
+          continue_label.Bind(&answer);
+          frame_->Push(&answer);
+          break;
+        }
 
-      default:
-        // NOT, DELETE, TYPEOF, and VOID are handled outside the
-        // switch.
-        UNREACHABLE();
+        default:
+          // NOT, DELETE, TYPEOF, and VOID are handled outside the
+          // switch.
+          UNREACHABLE();
+      }
     }
   }
 }
@@ -6646,6 +6805,7 @@ void DeferredPostfixCountOperation::Generate() {
 
 
 void CodeGenerator::VisitCountOperation(CountOperation* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ CountOperation");
 
   bool is_postfix = node->is_postfix();
@@ -6759,6 +6919,166 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
 }
 
 
+void CodeGenerator::Int32BinaryOperation(BinaryOperation* node) {
+  Token::Value op = node->op();
+  Comment cmnt(masm_, "[ Int32BinaryOperation");
+  ASSERT(in_safe_int32_mode());
+  ASSERT(safe_int32_mode_enabled());
+  ASSERT(FLAG_safe_int32_compiler);
+
+  if (op == Token::COMMA) {
+    // Discard left value.
+    frame_->Nip(1);
+    return;
+  }
+
+  Result right = frame_->Pop();
+  Result left = frame_->Pop();
+
+  ASSERT(right.is_untagged_int32());
+  ASSERT(left.is_untagged_int32());
+  // Registers containing an int32 value are not multiply used.
+  ASSERT(!left.is_register() || !frame_->is_used(left.reg()));
+  ASSERT(!right.is_register() || !frame_->is_used(right.reg()));
+
+  switch (op) {
+    case Token::COMMA:
+    case Token::OR:
+    case Token::AND:
+      UNREACHABLE();
+      break;
+    case Token::BIT_OR:
+    case Token::BIT_XOR:
+    case Token::BIT_AND:
+      left.ToRegister();
+      right.ToRegister();
+      if (op == Token::BIT_OR) {
+        __ or_(left.reg(), Operand(right.reg()));
+      } else if (op == Token::BIT_XOR) {
+        __ xor_(left.reg(), Operand(right.reg()));
+      } else {
+        ASSERT(op == Token::BIT_AND);
+        __ and_(left.reg(), Operand(right.reg()));
+      }
+      frame_->Push(&left);
+      right.Unuse();
+      break;
+    case Token::SAR:
+    case Token::SHL:
+    case Token::SHR: {
+      bool test_shr_overflow = false;
+      left.ToRegister();
+      if (right.is_constant()) {
+        ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
+        int shift_amount = NumberToInt32(*right.handle()) & 0x1F;
+        if (op == Token::SAR) {
+          __ sar(left.reg(), shift_amount);
+        } else if (op == Token::SHL) {
+          __ shl(left.reg(), shift_amount);
+        } else {
+          ASSERT(op == Token::SHR);
+          __ shr(left.reg(), shift_amount);
+          if (shift_amount == 0) test_shr_overflow = true;
+        }
+      } else {
+        // Move right to ecx
+        if (left.is_register() && left.reg().is(ecx)) {
+          right.ToRegister();
+          __ xchg(left.reg(), right.reg());
+          left = right;  // Left is unused here, copy of right unused by Push.
+        } else {
+          right.ToRegister(ecx);
+          left.ToRegister();
+        }
+        if (op == Token::SAR) {
+          __ sar_cl(left.reg());
+        } else if (op == Token::SHL) {
+          __ shl_cl(left.reg());
+        } else {
+          ASSERT(op == Token::SHR);
+          __ shr_cl(left.reg());
+          test_shr_overflow = true;
+        }
+      }
+      {
+        Register left_reg = left.reg();
+        frame_->Push(&left);
+        right.Unuse();
+        if (test_shr_overflow && !node->to_int32()) {
+          // Uint32 results with top bit set are not Int32 values.
+          // If they will be forced to Int32, skip the test.
+          // Test is needed because shr with shift amount 0 does not set flags.
+          __ test(left_reg, Operand(left_reg));
+          unsafe_bailout_->Branch(sign);
+        }
+      }
+      break;
+    }
+    case Token::ADD:
+    case Token::SUB:
+    case Token::MUL:
+      left.ToRegister();
+      right.ToRegister();
+      if (op == Token::ADD) {
+        __ add(left.reg(), Operand(right.reg()));
+      } else if (op == Token::SUB) {
+        __ sub(left.reg(), Operand(right.reg()));
+      } else {
+        ASSERT(op == Token::MUL);
+        // We have statically verified that a negative zero can be ignored.
+        __ imul(left.reg(), Operand(right.reg()));
+      }
+      right.Unuse();
+      frame_->Push(&left);
+      if (!node->to_int32()) {
+        // If ToInt32 is called on the result of ADD, SUB, or MUL, we don't
+        // care about overflows.
+        unsafe_bailout_->Branch(overflow);
+      }
+      break;
+    case Token::DIV:
+    case Token::MOD: {
+      if (right.is_register() && (right.reg().is(eax) || right.reg().is(edx))) {
+        if (left.is_register() && left.reg().is(edi)) {
+          right.ToRegister(ebx);
+        } else {
+          right.ToRegister(edi);
+        }
+      }
+      left.ToRegister(eax);
+      Result edx_reg = allocator_->Allocate(edx);
+      right.ToRegister();
+      // The results are unused here because BreakTarget::Branch cannot handle
+      // live results.
+      Register right_reg = right.reg();
+      left.Unuse();
+      right.Unuse();
+      edx_reg.Unuse();
+      __ cmp(right_reg, 0);
+      // Ensure divisor is positive: no chance of non-int32 or -0 result.
+      unsafe_bailout_->Branch(less_equal);
+      __ cdq();  // Sign-extend eax into edx:eax
+      __ idiv(right_reg);
+      if (op == Token::MOD) {
+        Result edx_result(edx, NumberInfo::Integer32());
+        edx_result.set_untagged_int32(true);
+        frame_->Push(&edx_result);
+      } else {
+        ASSERT(op == Token::DIV);
+        __ test(edx, Operand(edx));
+        unsafe_bailout_->Branch(not_equal);
+        Result eax_result(eax, NumberInfo::Integer32());
+        eax_result.set_untagged_int32(true);
+        frame_->Push(&eax_result);
+      }
+      break;
+    }
+    default:
+      UNREACHABLE();
+      break;
+  }
+}
+
 void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
   Comment cmnt(masm_, "[ BinaryOperation");
   Token::Value op = node->op();
@@ -6773,6 +7093,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
   // is necessary because we assume that if we get control flow on the
   // last path out of an expression we got it on all paths.
   if (op == Token::AND) {
+    ASSERT(!in_safe_int32_mode());
     JumpTarget is_true;
     ControlDestination dest(&is_true, destination()->false_target(), true);
     LoadCondition(node->left(), &dest, false);
@@ -6836,6 +7157,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
     }
 
   } else if (op == Token::OR) {
+    ASSERT(!in_safe_int32_mode());
     JumpTarget is_false;
     ControlDestination dest(destination()->true_target(), &is_false, false);
     LoadCondition(node->left(), &dest, false);
@@ -6897,6 +7219,10 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
       exit.Bind();
     }
 
+  } else if (in_safe_int32_mode()) {
+    Visit(node->left());
+    Visit(node->right());
+    Int32BinaryOperation(node);
   } else {
     // NOTE: The code below assumes that the slow cases (calls to runtime)
     // never return a constant/immutable object.
@@ -6925,11 +7251,13 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
 
 
 void CodeGenerator::VisitThisFunction(ThisFunction* node) {
+  ASSERT(!in_safe_int32_mode());
   frame_->PushFunction();
 }
 
 
 void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
+  ASSERT(!in_safe_int32_mode());
   Comment cmnt(masm_, "[ CompareOperation");
 
   bool left_already_loaded = false;
index 5f474d2..89318ce 100644 (file)
@@ -357,6 +357,23 @@ class CodeGenerator: public AstVisitor {
   // State
   ControlDestination* destination() const { return state_->destination(); }
 
+  // Control of side-effect-free int32 expression compilation.
+  bool in_safe_int32_mode() { return in_safe_int32_mode_; }
+  void set_in_safe_int32_mode(bool value) { in_safe_int32_mode_ = value; }
+  bool safe_int32_mode_enabled() {
+    return FLAG_safe_int32_compiler && safe_int32_mode_enabled_;
+  }
+  void set_safe_int32_mode_enabled(bool value) {
+    safe_int32_mode_enabled_ = value;
+  }
+  void set_unsafe_bailout(BreakTarget* unsafe_bailout) {
+    unsafe_bailout_ = unsafe_bailout;
+  }
+
+  // Take the Result that is an untagged int32, and convert it to a tagged
+  // Smi or HeapNumber.  Remove the untagged_int32 flag from the result.
+  void ConvertInt32ResultToNumber(Result* value);
+
   // Track loop nesting level.
   int loop_nesting() const { return loop_nesting_; }
   void IncrementLoopNesting() { loop_nesting_++; }
@@ -413,7 +430,7 @@ class CodeGenerator: public AstVisitor {
     return ContextOperand(esi, Context::GLOBAL_INDEX);
   }
 
-  void LoadCondition(Expression* x,
+  void LoadCondition(Expression* expr,
                      ControlDestination* destination,
                      bool force_control);
   void Load(Expression* expr);
@@ -425,6 +442,11 @@ class CodeGenerator: public AstVisitor {
   // temporarily while the code generator is being transformed.
   void LoadAndSpill(Expression* expression);
 
+  // Evaluate an expression and place its value on top of the frame,
+  // using, or not using, the side-effect-free expression compiler.
+  void LoadInSafeInt32Mode(Expression* expr, BreakTarget* unsafe_bailout);
+  void LoadWithSafeInt32ModeDisabled(Expression* expr);
+
   // Read a value from a slot and leave it on top of the expression stack.
   Result LoadFromSlot(Slot* slot, TypeofState typeof_state);
   Result LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
@@ -496,6 +518,12 @@ class CodeGenerator: public AstVisitor {
                                   OverwriteMode overwrite_mode,
                                   bool no_negative_zero);
 
+
+  // Emit code to perform a binary operation on two untagged int32 values.
+  // The values are on top of the frame, and the result is pushed on the frame.
+  void Int32BinaryOperation(BinaryOperation* node);
+
+
   void Comparison(AstNode* node,
                   Condition cc,
                   bool strict,
@@ -642,10 +670,14 @@ class CodeGenerator: public AstVisitor {
   RegisterAllocator* allocator_;
   CodeGenState* state_;
   int loop_nesting_;
+  bool in_safe_int32_mode_;
+  bool safe_int32_mode_enabled_;
 
   // Jump targets.
   // The target of the return from the function.
   BreakTarget function_return_;
+  // The target of the bailout from a side-effect-free int32 subexpression.
+  BreakTarget* unsafe_bailout_;
 
   // True if the function return is shadowed (ie, jumping to the target
   // function_return_ does not jump to the true function return, but rather
index 3ccfe8c..f04390e 100644 (file)
@@ -42,7 +42,33 @@ void Result::ToRegister() {
   if (is_constant()) {
     Result fresh = CodeGeneratorScope::Current()->allocator()->Allocate();
     ASSERT(fresh.is_valid());
-    if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
+    if (is_untagged_int32()) {
+      fresh.set_untagged_int32(true);
+      if (handle()->IsSmi()) {
+      CodeGeneratorScope::Current()->masm()->Set(
+          fresh.reg(),
+          Immediate(Smi::cast(*handle())->value()));
+      } else if (handle()->IsHeapNumber()) {
+        double double_value = HeapNumber::cast(*handle())->value();
+        int32_t value = DoubleToInt32(double_value);
+        if (double_value == 0 && signbit(double_value)) {
+          // Negative zero must not be converted to an int32 unless
+          // the context allows it.
+          CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+          CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+        } else if (double_value == value) {
+          CodeGeneratorScope::Current()->masm()->Set(
+              fresh.reg(), Immediate(value));
+        } else {
+          CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+          CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+        }
+      } else {
+        // Constant is not a number.  This was not predicted by AST analysis.
+        CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+        CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+      }
+    } else if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
       CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
     } else {
       CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
@@ -65,13 +91,38 @@ void Result::ToRegister(Register target) {
       CodeGeneratorScope::Current()->masm()->mov(fresh.reg(), reg());
     } else {
       ASSERT(is_constant());
-      if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
-        CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
+      if (is_untagged_int32()) {
+        if (handle()->IsSmi()) {
+          CodeGeneratorScope::Current()->masm()->Set(
+              fresh.reg(),
+              Immediate(Smi::cast(*handle())->value()));
+        } else {
+          ASSERT(handle()->IsHeapNumber());
+          double double_value = HeapNumber::cast(*handle())->value();
+          int32_t value = DoubleToInt32(double_value);
+          if (double_value == 0 && signbit(double_value)) {
+            // Negative zero must not be converted to an int32 unless
+            // the context allows it.
+            CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+            CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+          } else if (double_value == value) {
+            CodeGeneratorScope::Current()->masm()->Set(
+                fresh.reg(), Immediate(value));
+          } else {
+            CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+            CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+          }
+        }
       } else {
-        CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
-                                                   Immediate(handle()));
+        if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
+          CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
+        } else {
+          CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
+                                                     Immediate(handle()));
+        }
       }
     }
+    fresh.set_untagged_int32(is_untagged_int32());
     *this = fresh;
   } else if (is_register() && reg().is(target)) {
     ASSERT(CodeGeneratorScope::Current()->has_valid_frame());
index a749c78..de12b64 100644 (file)
@@ -775,6 +775,89 @@ void VirtualFrame::StoreToFrameSlotAt(int index) {
 }
 
 
+void VirtualFrame::UntaggedPushFrameSlotAt(int index) {
+  ASSERT(index >= 0);
+  ASSERT(index <= element_count());
+  FrameElement original = elements_[index];
+  if (original.is_copy()) {
+    original = elements_[original.index()];
+    index = original.index();
+  }
+
+  switch (original.type()) {
+    case FrameElement::MEMORY:
+    case FrameElement::REGISTER:  {
+      Label done;
+      // Emit code to load the original element's data into a register.
+      // Push that register as a FrameElement on top of the frame.
+      Result fresh = cgen()->allocator()->Allocate();
+      ASSERT(fresh.is_valid());
+      Register fresh_reg = fresh.reg();
+      FrameElement new_element =
+          FrameElement::RegisterElement(fresh_reg,
+                                        FrameElement::NOT_SYNCED,
+                                        original.number_info());
+      new_element.set_untagged_int32(true);
+      Use(fresh_reg, element_count());
+      fresh.Unuse();  // BreakTarget does not handle a live Result well.
+      elements_.Add(new_element);
+      if (original.is_register()) {
+        __ mov(fresh_reg, original.reg());
+      } else {
+        ASSERT(original.is_memory());
+        __ mov(fresh_reg, Operand(ebp, fp_relative(index)));
+      }
+      // Now convert the value to int32, or bail out.
+      if (original.number_info().IsSmi()) {
+        __ SmiUntag(fresh_reg);
+        // Pushing the element is completely done.
+      } else {
+        __ test(fresh_reg, Immediate(kSmiTagMask));
+        Label not_smi;
+        __ j(not_zero, &not_smi);
+        __ SmiUntag(fresh_reg);
+        __ jmp(&done);
+
+        __ bind(&not_smi);
+        if (!original.number_info().IsNumber()) {
+          __ cmp(FieldOperand(fresh_reg, HeapObject::kMapOffset),
+                 Factory::heap_number_map());
+          cgen()->unsafe_bailout_->Branch(not_equal);
+        }
+
+        if (!CpuFeatures::IsSupported(SSE2)) {
+          UNREACHABLE();
+        } else {
+          CpuFeatures::Scope use_sse2(SSE2);
+          __ movdbl(xmm0, FieldOperand(fresh_reg, HeapNumber::kValueOffset));
+          __ cvttsd2si(fresh_reg, Operand(xmm0));
+          __ cvtsi2sd(xmm1, Operand(fresh_reg));
+          __ ucomisd(xmm0, xmm1);
+          cgen()->unsafe_bailout_->Branch(not_equal);
+          cgen()->unsafe_bailout_->Branch(parity_even);  // NaN.
+          // Test for negative zero.
+          __ test(fresh_reg, Operand(fresh_reg));
+          __ j(not_zero, &done);
+          __ movmskpd(fresh_reg, xmm0);
+          __ and_(fresh_reg, 0x1);
+          cgen()->unsafe_bailout_->Branch(not_equal);
+        }
+        __ bind(&done);
+      }
+      break;
+    }
+    case FrameElement::CONSTANT:
+      elements_.Add(CopyElementAt(index));
+      elements_[element_count() - 1].set_untagged_int32(true);
+      break;
+    case FrameElement::COPY:
+    case FrameElement::INVALID:
+      UNREACHABLE();
+      break;
+  }
+}
+
+
 void VirtualFrame::PushTryHandler(HandlerType type) {
   ASSERT(cgen()->HasValidEntryRegisters());
   // Grow the expression stack by handler size less one (the return
@@ -1060,6 +1143,7 @@ Result VirtualFrame::Pop() {
   FrameElement element = elements_.RemoveLast();
   int index = element_count();
   ASSERT(element.is_valid());
+  ASSERT(element.is_untagged_int32() == cgen()->in_safe_int32_mode());
 
   // Get number type information of the result.
   NumberInfo info;
@@ -1077,6 +1161,7 @@ Result VirtualFrame::Pop() {
       ASSERT(temp.is_valid());
       __ pop(temp.reg());
       temp.set_number_info(info);
+      temp.set_untagged_int32(element.is_untagged_int32());
       return temp;
     }
 
@@ -1089,6 +1174,7 @@ Result VirtualFrame::Pop() {
   if (element.is_register()) {
     Unuse(element.reg());
   } else if (element.is_copy()) {
+    ASSERT(!element.is_untagged_int32());
     ASSERT(element.index() < index);
     index = element.index();
     element = elements_[index];
@@ -1100,6 +1186,7 @@ Result VirtualFrame::Pop() {
     // Memory elements could only be the backing store of a copy.
     // Allocate the original to a register.
     ASSERT(index <= stack_pointer_);
+    ASSERT(!element.is_untagged_int32());
     Result temp = cgen()->allocator()->Allocate();
     ASSERT(temp.is_valid());
     Use(temp.reg(), index);
@@ -1113,10 +1200,14 @@ Result VirtualFrame::Pop() {
     __ mov(temp.reg(), Operand(ebp, fp_relative(index)));
     return Result(temp.reg(), info);
   } else if (element.is_register()) {
-    return Result(element.reg(), info);
+    Result return_value(element.reg(), info);
+    return_value.set_untagged_int32(element.is_untagged_int32());
+    return return_value;
   } else {
     ASSERT(element.is_constant());
-    return Result(element.handle());
+    Result return_value(element.handle());
+    return_value.set_untagged_int32(element.is_untagged_int32());
+    return return_value;
   }
 }
 
@@ -1161,6 +1252,12 @@ void VirtualFrame::EmitPush(Immediate immediate, NumberInfo info) {
 }
 
 
+void VirtualFrame::PushUntaggedElement(Handle<Object> value) {
+  elements_.Add(FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED));
+  elements_[element_count() - 1].set_untagged_int32(true);
+}
+
+
 void VirtualFrame::Push(Expression* expr) {
   ASSERT(expr->IsTrivial());
 
index 042f9b7..69c3e47 100644 (file)
@@ -242,6 +242,11 @@ class VirtualFrame: public ZoneObject {
     PushFrameSlotAt(local0_index() + index);
   }
 
+  // Push a copy of the value of a local frame slot on top of the frame.
+  void UntaggedPushLocalAt(int index) {
+    UntaggedPushFrameSlotAt(local0_index() + index);
+  }
+
   // Push the value of a local frame slot on top of the frame and invalidate
   // the local slot.  The slot should be written to before trying to read
   // from it again.
@@ -282,6 +287,11 @@ class VirtualFrame: public ZoneObject {
     PushFrameSlotAt(param0_index() + index);
   }
 
+  // Push a copy of the value of a parameter frame slot on top of the frame.
+  void UntaggedPushParameterAt(int index) {
+    UntaggedPushFrameSlotAt(param0_index() + index);
+  }
+
   // Push the value of a paramter frame slot on top of the frame and
   // invalidate the parameter slot.  The slot should be written to before
   // trying to read from it again.
@@ -399,6 +409,8 @@ class VirtualFrame: public ZoneObject {
   inline void Push(Handle<Object> value);
   inline void Push(Smi* value);
 
+  void PushUntaggedElement(Handle<Object> value);
+
   // Pushing a result invalidates it (its contents become owned by the
   // frame).
   void Push(Result* result) {
@@ -410,6 +422,10 @@ class VirtualFrame: public ZoneObject {
       ASSERT(result->is_constant());
       Push(result->handle());
     }
+    if (cgen()->in_safe_int32_mode()) {
+      ASSERT(result->is_untagged_int32());
+      elements_[element_count() - 1].set_untagged_int32(true);
+    }
     result->Unuse();
   }
 
@@ -422,6 +438,14 @@ class VirtualFrame: public ZoneObject {
   // the frame.  Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
   inline void Nip(int num_dropped);
 
+  // Check that the frame has no elements containing untagged int32 elements.
+  bool HasNoUntaggedInt32Elements() {
+    for (int i = 0; i < element_count(); ++i) {
+      if (elements_[i].is_untagged_int32()) return false;
+    }
+    return true;
+  }
+
   // Update the type information of a local variable frame element directly.
   inline void SetTypeForLocalAt(int index, NumberInfo info);
 
@@ -533,6 +557,11 @@ class VirtualFrame: public ZoneObject {
   // the frame.
   inline void PushFrameSlotAt(int index);
 
+  // Push a copy of a frame slot (typically a local or parameter) on top of
+  // the frame, at an untagged int32 value.  Bails out if the value is not
+  // an int32.
+  void UntaggedPushFrameSlotAt(int index);
+
   // Push a the value of a frame slot (typically a local or parameter) on
   // top of the frame and invalidate the slot.
   void TakeFrameSlotAt(int index);
index 2a7b820..8bc8ede 100644 (file)
@@ -71,6 +71,7 @@ class Result BASE_EMBEDDED {
   explicit Result(Handle<Object> value) {
     value_ = TypeField::encode(CONSTANT)
         | NumberInfoField::encode(NumberInfo::Uninitialized().ToInt())
+        | IsUntaggedInt32Field::encode(false)
         | DataField::encode(ConstantList()->length());
     ConstantList()->Add(value);
   }
@@ -112,6 +113,19 @@ class Result BASE_EMBEDDED {
   bool is_register() const { return type() == REGISTER; }
   bool is_constant() const { return type() == CONSTANT; }
 
+  // An untagged int32 Result contains a signed int32 in a register
+  // or as a constant.  These are only allowed in a side-effect-free
+  // int32 calculation, and if a non-int32 input shows up or an overflow
+  // occurs, we bail out and drop all the int32 values.  Constants are
+  // not converted to int32 until they are loaded into a register.
+  bool is_untagged_int32() const {
+    return IsUntaggedInt32Field::decode(value_);
+  }
+  void set_untagged_int32(bool value) {
+    value_ &= ~IsUntaggedInt32Field::mask();
+    value_ |= IsUntaggedInt32Field::encode(value);
+  }
+
   Register reg() const {
     ASSERT(is_register());
     uint32_t reg = DataField::decode(value_);
@@ -140,7 +154,8 @@ class Result BASE_EMBEDDED {
 
   class TypeField: public BitField<Type, 0, 2> {};
   class NumberInfoField : public BitField<int, 2, 4> {};
-  class DataField: public BitField<uint32_t, 6, 32 - 6> {};
+  class IsUntaggedInt32Field : public BitField<bool, 6, 1> {};
+  class DataField: public BitField<uint32_t, 7, 32 - 7> {};
 
   inline void CopyTo(Result* destination) const;
 
index 0f40623..b05b045 100644 (file)
@@ -266,7 +266,14 @@ void AstOptimizer::VisitLiteral(Literal* node) {
       func_name_inferrer_.PushName(lit_str);
     }
   } else if (literal->IsHeapNumber()) {
-    node->set_side_effect_free(true);
+    if (node->to_int32()) {
+      // Any HeapNumber has an int32 value if it is the input to a bit op.
+      node->set_side_effect_free(true);
+    } else {
+      double double_value = HeapNumber::cast(*literal)->value();
+      int32_t int32_value = DoubleToInt32(double_value);
+      node->set_side_effect_free(double_value == int32_value);
+    }
   }
 }
 
@@ -320,6 +327,7 @@ void AstOptimizer::VisitAssignment(Assignment* node) {
       node->type()->SetAsLikelySmiIfUnknown();
       node->target()->type()->SetAsLikelySmiIfUnknown();
       node->value()->type()->SetAsLikelySmiIfUnknown();
+      node->value()->set_to_int32(true);
       node->value()->set_no_negative_zero(true);
       break;
     case Token::ASSIGN_ADD:
@@ -438,9 +446,9 @@ void AstOptimizer::VisitUnaryOperation(UnaryOperation* node) {
         // Fall through.
       case Token::ADD:
       case Token::SUB:
-      case Token::NOT:
         node->set_side_effect_free(node->expression()->side_effect_free());
         break;
+      case Token::NOT:
       case Token::DELETE:
       case Token::TYPEOF:
       case Token::VOID:
@@ -553,6 +561,9 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
       case Token::SHL:
       case Token::SAR:
       case Token::SHR:
+        // Add one to the number of bit operations in this expression.
+        node->set_num_bit_ops(1);
+        // Fall through.
       case Token::ADD:
       case Token::SUB:
       case Token::MUL:
@@ -560,6 +571,12 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
       case Token::MOD:
         node->set_side_effect_free(node->left()->side_effect_free() &&
                                    node->right()->side_effect_free());
+        node->set_num_bit_ops(node->num_bit_ops() +
+                                  node->left()->num_bit_ops() +
+                                  node->right()->num_bit_ops());
+        if (!node->no_negative_zero() && node->op() == Token::MUL) {
+          node->set_side_effect_free(false);
+        }
         break;
       default:
         UNREACHABLE();
index 8c13a6a..070424c 100644 (file)
@@ -163,6 +163,9 @@ void VirtualFrame::SpillElementAt(int index) {
   if (elements_[index].is_copied()) {
     new_element.set_copied();
   }
+  if (elements_[index].is_untagged_int32()) {
+    new_element.set_untagged_int32(true);
+  }
   if (elements_[index].is_register()) {
     Unuse(elements_[index].reg());
   }