Allow control intructions to have side effects.
authorolivf@chromium.org <olivf@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 23 Sep 2013 16:56:34 +0000 (16:56 +0000)
committerolivf@chromium.org <olivf@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 23 Sep 2013 16:56:34 +0000 (16:56 +0000)
As a first application convert HCompareGeneric to a control Instruction, thus avoid materializing a boolean result value.

BUG=
R=titzer@chromium.org

Review URL: https://codereview.chromium.org/23710070

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

14 files changed:
src/arm/lithium-arm.cc
src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/hydrogen-instructions.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/utils.h
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
src/x64/lithium-x64.h
test/mjsunit/compare-generic.js [new file with mode: 0644]

index f216a8e..22f5590 100644 (file)
@@ -630,12 +630,16 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
   instr = AssignPointerMap(instr);
 
   if (hinstr->HasObservableSideEffects()) {
-    ASSERT(hinstr->next()->IsSimulate());
-    HSimulate* sim = HSimulate::cast(hinstr->next());
     ASSERT(instruction_pending_deoptimization_environment_ == NULL);
     ASSERT(pending_deoptimization_ast_id_.IsNone());
+    if (!hinstr->IsControlInstruction()) {
+      ASSERT(hinstr->next()->IsSimulate());
+      HSimulate* sim = HSimulate::cast(hinstr->next());
+      pending_deoptimization_ast_id_ = sim->ast_id();
+    } else {
+      pending_deoptimization_ast_id_ = BailoutId::PendingMarker();
+    }
     instruction_pending_deoptimization_environment_ = instr;
-    pending_deoptimization_ast_id_ = sim->ast_id();
   }
 
   // If instruction does not have side-effects lazy deoptimization
@@ -1716,13 +1720,13 @@ LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
 }
 
 
-LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
+LInstruction* LChunkBuilder::DoCompareGenericAndBranch(
+    HCompareGenericAndBranch* instr) {
   ASSERT(instr->left()->representation().IsTagged());
   ASSERT(instr->right()->representation().IsTagged());
   LOperand* left = UseFixed(instr->left(), r1);
   LOperand* right = UseFixed(instr->right(), r0);
-  LCmpT* result = new(zone()) LCmpT(left, right);
-  return MarkAsCall(DefineFixed(result, r0), instr);
+  return MarkAsCall(new(zone()) LCompareGenericAndBranch(left, right), instr);
 }
 
 
@@ -2515,7 +2519,9 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
 
   // If there is an instruction pending deoptimization environment create a
   // lazy bailout instruction to capture the environment.
-  if (pending_deoptimization_ast_id_ == instr->ast_id()) {
+  if (!pending_deoptimization_ast_id_.IsNone()) {
+    ASSERT(pending_deoptimization_ast_id_ == instr->ast_id() ||
+           pending_deoptimization_ast_id_.IsPendingMarker());
     LInstruction* result = new(zone()) LLazyBailout;
     result = AssignEnvironment(result);
     // Store the lazy deopt environment with the instruction if needed. Right
index 3902b4c..ddd3a2b 100644 (file)
@@ -76,7 +76,7 @@ class LCodeGen;
   V(CmpObjectEqAndBranch)                       \
   V(CmpHoleAndBranch)                           \
   V(CmpMapAndBranch)                            \
-  V(CmpT)                                       \
+  V(CompareGenericAndBranch)                    \
   V(ConstantD)                                  \
   V(ConstantE)                                  \
   V(ConstantI)                                  \
@@ -1073,9 +1073,9 @@ class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 1> {
 };
 
 
-class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
+class LCompareGenericAndBranch V8_FINAL : public LControlInstruction<2, 0> {
  public:
-  LCmpT(LOperand* left, LOperand* right) {
+  LCompareGenericAndBranch(LOperand* left, LOperand* right) {
     inputs_[0] = left;
     inputs_[1] = right;
   }
@@ -1083,8 +1083,9 @@ class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
   LOperand* left() { return inputs_[0]; }
   LOperand* right() { return inputs_[1]; }
 
-  DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
-  DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
+  DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch,
+                               "compare-generic-and-branch")
+  DECLARE_HYDROGEN_ACCESSOR(CompareGenericAndBranch)
 
   Token::Value op() const { return hydrogen()->token(); }
 };
index 0c5b973..316a5df 100644 (file)
@@ -2888,7 +2888,7 @@ void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
 }
 
 
-void LCodeGen::DoCmpT(LCmpT* instr) {
+void LCodeGen::DoCompareGenericAndBranch(LCompareGenericAndBranch* instr) {
   Token::Value op = instr->op();
 
   Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
@@ -2897,12 +2897,7 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
   __ cmp(r0, Operand::Zero());
 
   Condition condition = ComputeCompareCondition(op);
-  __ LoadRoot(ToRegister(instr->result()),
-              Heap::kTrueValueRootIndex,
-              condition);
-  __ LoadRoot(ToRegister(instr->result()),
-              Heap::kFalseValueRootIndex,
-              NegateCondition(condition));
+  EmitBranch(instr, condition);
 }
 
 
index a685198..377dbf5 100644 (file)
@@ -794,7 +794,7 @@ void HInstruction::Verify() {
 
   // Verify that instructions that may have side-effects are followed
   // by a simulate instruction.
-  if (HasObservableSideEffects() && !IsOsrEntry()) {
+  if (HasObservableSideEffects() && !IsOsrEntry() && !IsControlInstruction()) {
     ASSERT(next()->IsSimulate());
   }
 
@@ -1011,6 +1011,21 @@ void HControlInstruction::PrintDataTo(StringStream* stream) {
 }
 
 
+#ifdef DEBUG
+void HControlInstruction::Verify() {
+  HInstruction::Verify();
+  if (!HasObservableSideEffects()) return;
+  for (HSuccessorIterator it(this); !it.Done(); it.Advance()) {
+    // For ControlInstructions we need to verify that the successors all start
+    // with a Simulate.
+    HInstruction* first = it.Current()->first()->next();
+    ASSERT(first->IsSimulate() ||
+           (first->IsLeaveInlined() && first->next()->IsSimulate()));
+  }
+}
+#endif
+
+
 void HUnaryControlInstruction::PrintDataTo(StringStream* stream) {
   value()->PrintNameTo(stream);
   HControlInstruction::PrintDataTo(stream);
@@ -2823,10 +2838,14 @@ Range* HLoadKeyed::InferRange(Zone* zone) {
 }
 
 
-void HCompareGeneric::PrintDataTo(StringStream* stream) {
+void HCompareGenericAndBranch::PrintDataTo(StringStream* stream) {
   stream->Add(Token::Name(token()));
   stream->Add(" ");
-  HBinaryOperation::PrintDataTo(stream);
+  left()->PrintNameTo(stream);
+  stream->Add(" ");
+  right()->PrintNameTo(stream);
+  if (CheckFlag(kCanOverflow)) stream->Add(" !");
+  if (CheckFlag(kBailoutOnMinusZero)) stream->Add(" -0?");
 }
 
 
index 8cb2f59..66e3973 100644 (file)
@@ -98,7 +98,7 @@ class LChunkBuilder;
   V(ClassOfTestAndBranch)                      \
   V(CompareNumericAndBranch)                   \
   V(CompareHoleAndBranch)                      \
-  V(CompareGeneric)                            \
+  V(CompareGenericAndBranch)                   \
   V(CompareObjectEqAndBranch)                  \
   V(CompareMap)                                \
   V(Constant)                                  \
@@ -1069,26 +1069,56 @@ class HValue : public ZoneObject {
 };
 
 
+#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P0(I)                         \
+  static I* New(Zone* zone, HValue* context) {                                 \
+    return new(zone) I(context);                                               \
+}
+
 #define DECLARE_INSTRUCTION_FACTORY_P0(I)                                      \
   static I* New(Zone* zone, HValue* context) {                                 \
     return new(zone) I();                                                      \
 }
 
+#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(I, P1)                     \
+  static I* New(Zone* zone, HValue* context, P1 p1) {                          \
+    return new(zone) I(context, p1);                                           \
+  }
+
 #define DECLARE_INSTRUCTION_FACTORY_P1(I, P1)                                  \
   static I* New(Zone* zone, HValue* context, P1 p1) {                          \
     return new(zone) I(p1);                                                    \
   }
 
+#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(I, P1, P2)                 \
+  static I* New(Zone* zone, HValue* context, P1 p1, P2 p2) {                   \
+    return new(zone) I(context, p1, p2);                                       \
+  }
+
 #define DECLARE_INSTRUCTION_FACTORY_P2(I, P1, P2)                              \
   static I* New(Zone* zone, HValue* context, P1 p1, P2 p2) {                   \
     return new(zone) I(p1, p2);                                                \
   }
 
+#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(I, P1, P2, P3)             \
+  static I* New(Zone* zone, HValue* context, P1 p1, P2 p2, P3 p3) {            \
+    return new(zone) I(context, p1, p2, p3);                                   \
+  }
+
 #define DECLARE_INSTRUCTION_FACTORY_P3(I, P1, P2, P3)                          \
   static I* New(Zone* zone, HValue* context, P1 p1, P2 p2, P3 p3) {            \
     return new(zone) I(p1, p2, p3);                                            \
   }
 
+#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(I, P1, P2, P3, P4)         \
+  static I* New(Zone* zone,                                                    \
+                HValue* context,                                               \
+                P1 p1,                                                         \
+                P2 p2,                                                         \
+                P3 p3,                                                         \
+                P4 p4) {                                                       \
+    return new(zone) I(context, p1, p2, p3, p4);                               \
+  }
+
 #define DECLARE_INSTRUCTION_FACTORY_P4(I, P1, P2, P3, P4)                      \
   static I* New(Zone* zone,                                                    \
                 HValue* context,                                               \
@@ -1099,6 +1129,17 @@ class HValue : public ZoneObject {
     return new(zone) I(p1, p2, p3, p4);                                        \
   }
 
+#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(I, P1, P2, P3, P4, P5)     \
+  static I* New(Zone* zone,                                                    \
+                HValue* context,                                               \
+                P1 p1,                                                         \
+                P2 p2,                                                         \
+                P3 p3,                                                         \
+                P4 p4,                                                         \
+                P5 p5) {                                                       \
+    return new(zone) I(context, p1, p2, p3, p4, p5);                           \
+  }
+
 #define DECLARE_INSTRUCTION_FACTORY_P5(I, P1, P2, P3, P4, P5)                  \
   static I* New(Zone* zone,                                                    \
                 HValue* context,                                               \
@@ -1213,6 +1254,10 @@ class HControlInstruction : public HInstruction {
     SetSuccessorAt(1, swap);
   }
 
+#ifdef DEBUG
+  virtual void Verify() V8_OVERRIDE;
+#endif
+
   DECLARE_ABSTRACT_INSTRUCTION(ControlInstruction)
 };
 
@@ -4008,18 +4053,18 @@ class HArithmeticBinaryOperation : public HBinaryOperation {
 };
 
 
-class HCompareGeneric V8_FINAL : public HBinaryOperation {
+class HCompareGenericAndBranch V8_FINAL
+    : public HTemplateControlInstruction<2, 3> {
  public:
-  HCompareGeneric(HValue* context,
-                  HValue* left,
-                  HValue* right,
-                  Token::Value token)
-      : HBinaryOperation(context, left, right, HType::Boolean()),
-        token_(token) {
-    ASSERT(Token::IsCompareOp(token));
-    set_representation(Representation::Tagged());
-    SetAllSideEffects();
-  }
+  DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(HCompareGenericAndBranch,
+                                              HValue*, HValue*, Token::Value);
+  DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(HCompareGenericAndBranch,
+                                              HValue*, HValue*, Token::Value,
+                                              HBasicBlock*, HBasicBlock*);
+  HValue* context() { return OperandAt(0); }
+  HValue* left() { return OperandAt(1); }
+  HValue* right() { return OperandAt(2); }
+  Token::Value token() const { return token_; }
 
   virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
     return index == 0
@@ -4027,12 +4072,34 @@ class HCompareGeneric V8_FINAL : public HBinaryOperation {
         : representation();
   }
 
-  Token::Value token() const { return token_; }
+  void set_observed_input_representation(Representation left,
+                                         Representation right) {
+      observed_input_representation_[0] = left;
+      observed_input_representation_[1] = right;
+  }
+
   virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
 
-  DECLARE_CONCRETE_INSTRUCTION(CompareGeneric)
+  DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch)
 
  private:
+  HCompareGenericAndBranch(HValue* context,
+                           HValue* left,
+                           HValue* right,
+                           Token::Value token,
+                           HBasicBlock* true_target = NULL,
+                           HBasicBlock* false_target = NULL)
+      : token_(token) {
+    set_representation(Representation::Tagged());
+    SetAllSideEffects();
+    SetOperandAt(0, context);
+    SetOperandAt(1, left);
+    SetOperandAt(2, right);
+    SetSuccessorAt(0, true_target);
+    SetSuccessorAt(1, false_target);
+  }
+
+  Representation observed_input_representation_[2];
   Token::Value token_;
 };
 
@@ -4234,9 +4301,9 @@ class HIsUndetectableAndBranch V8_FINAL : public HUnaryControlInstruction {
 class HStringCompareAndBranch : public HTemplateControlInstruction<2, 3> {
  public:
   HStringCompareAndBranch(HValue* context,
-                           HValue* left,
-                           HValue* right,
-                           Token::Value token)
+                          HValue* left,
+                          HValue* right,
+                          Token::Value token)
       : token_(token) {
     ASSERT(Token::IsCompareOp(token));
     SetOperandAt(0, context);
index 50882a8..253abd1 100644 (file)
@@ -2655,7 +2655,6 @@ void EffectContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
 
 void EffectContext::ReturnControl(HControlInstruction* instr,
                                   BailoutId ast_id) {
-  ASSERT(!instr->HasObservableSideEffects());
   HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
   HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
   instr->SetSuccessorAt(0, empty_true);
@@ -2696,7 +2695,6 @@ void ValueContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
 
 
 void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
-  ASSERT(!instr->HasObservableSideEffects());
   if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
     return owner()->Bailout(kBadValueContextForArgumentsObjectValue);
   }
@@ -2754,7 +2752,9 @@ void TestContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
 
 
 void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
-  ASSERT(!instr->HasObservableSideEffects());
+  // We can ignore ObservableSideEffects here since both HGoto instructions
+  // insert a different Simulate, thus we will directly deoptimize into the
+  // correct branch.
   HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
   HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
   instr->SetSuccessorAt(0, empty_true);
@@ -8201,12 +8201,11 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
     return ast_context()->ReturnControl(result, expr->id());
   } else {
     if (combined_rep.IsTagged() || combined_rep.IsNone()) {
-      HCompareGeneric* result =
-          new(zone()) HCompareGeneric(context, left, right, op);
-      result->set_observed_input_representation(1, left_rep);
-      result->set_observed_input_representation(2, right_rep);
+      HCompareGenericAndBranch* result =
+          New<HCompareGenericAndBranch>(left, right, op);
+      result->set_observed_input_representation(left_rep, right_rep);
       result->set_position(expr->position());
-      return ast_context()->ReturnInstruction(result, expr->id());
+      return ast_context()->ReturnControl(result, expr->id());
     } else {
       HCompareNumericAndBranch* result =
           New<HCompareNumericAndBranch>(left, right, op);
index 98a049b..1ea97ca 100644 (file)
@@ -3044,21 +3044,15 @@ void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
 }
 
 
-void LCodeGen::DoCmpT(LCmpT* instr) {
+void LCodeGen::DoCompareGenericAndBranch(LCompareGenericAndBranch* instr) {
   Token::Value op = instr->op();
 
   Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
   CallCode(ic, RelocInfo::CODE_TARGET, instr);
 
   Condition condition = ComputeCompareCondition(op);
-  Label true_value, done;
   __ test(eax, Operand(eax));
-  __ j(condition, &true_value, Label::kNear);
-  __ mov(ToRegister(instr->result()), factory()->false_value());
-  __ jmp(&done, Label::kNear);
-  __ bind(&true_value);
-  __ mov(ToRegister(instr->result()), factory()->true_value());
-  __ bind(&done);
+  EmitBranch(instr, condition);
 }
 
 
index c73d073..4714c7d 100644 (file)
@@ -682,12 +682,16 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
   instr = AssignPointerMap(instr);
 
   if (hinstr->HasObservableSideEffects()) {
-    ASSERT(hinstr->next()->IsSimulate());
-    HSimulate* sim = HSimulate::cast(hinstr->next());
     ASSERT(instruction_pending_deoptimization_environment_ == NULL);
     ASSERT(pending_deoptimization_ast_id_.IsNone());
+    if (!hinstr->IsControlInstruction()) {
+      ASSERT(hinstr->next()->IsSimulate());
+      HSimulate* sim = HSimulate::cast(hinstr->next());
+      pending_deoptimization_ast_id_ = sim->ast_id();
+    } else {
+      pending_deoptimization_ast_id_ = BailoutId::PendingMarker();
+    }
     instruction_pending_deoptimization_environment_ = instr;
-    pending_deoptimization_ast_id_ = sim->ast_id();
   }
 
   // If instruction does not have side-effects lazy deoptimization
@@ -1700,14 +1704,15 @@ LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
 }
 
 
-LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
+LInstruction* LChunkBuilder::DoCompareGenericAndBranch(
+    HCompareGenericAndBranch* instr) {
   ASSERT(instr->left()->representation().IsSmiOrTagged());
   ASSERT(instr->right()->representation().IsSmiOrTagged());
   LOperand* context = UseFixed(instr->context(), esi);
   LOperand* left = UseFixed(instr->left(), edx);
   LOperand* right = UseFixed(instr->right(), eax);
-  LCmpT* result = new(zone()) LCmpT(context, left, right);
-  return MarkAsCall(DefineFixed(result, eax), instr);
+  return MarkAsCall(new(zone()) LCompareGenericAndBranch(context, left, right),
+                    instr);
 }
 
 
@@ -2626,7 +2631,8 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
   // If there is an instruction pending deoptimization environment create a
   // lazy bailout instruction to capture the environment.
   if (!pending_deoptimization_ast_id_.IsNone()) {
-    ASSERT(pending_deoptimization_ast_id_ == instr->ast_id());
+    ASSERT(pending_deoptimization_ast_id_ == instr->ast_id() ||
+           pending_deoptimization_ast_id_.IsPendingMarker());
     LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
     LInstruction* result = AssignEnvironment(lazy_bailout);
     // Store the lazy deopt environment with the instruction if needed. Right
index 379d64b..d914126 100644 (file)
@@ -78,7 +78,7 @@ class LCodeGen;
   V(CmpObjectEqAndBranch)                       \
   V(CmpHoleAndBranch)                           \
   V(CmpMapAndBranch)                            \
-  V(CmpT)                                       \
+  V(CompareGenericAndBranch)                    \
   V(ConstantD)                                  \
   V(ConstantE)                                  \
   V(ConstantI)                                  \
@@ -1073,16 +1073,17 @@ class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 2> {
 };
 
 
-class LCmpT V8_FINAL : public LTemplateInstruction<1, 3, 0> {
+class LCompareGenericAndBranch V8_FINAL : public LControlInstruction<3, 0> {
  public:
-  LCmpT(LOperand* context, LOperand* left, LOperand* right) {
+  LCompareGenericAndBranch(LOperand* context, LOperand* left, LOperand* right) {
     inputs_[0] = context;
     inputs_[1] = left;
     inputs_[2] = right;
   }
 
-  DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
-  DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
+  DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch,
+                               "compare-generic-and-branch")
+  DECLARE_HYDROGEN_ACCESSOR(CompareGenericAndBranch)
 
   Token::Value op() const { return hydrogen()->token(); }
 };
index 4a08319..50311cf 100644 (file)
@@ -1115,8 +1115,10 @@ class BailoutId {
   static BailoutId Declarations() { return BailoutId(kDeclarationsId); }
   static BailoutId FirstUsable() { return BailoutId(kFirstUsableId); }
   static BailoutId StubEntry() { return BailoutId(kStubEntryId); }
+  static BailoutId PendingMarker() { return BailoutId(kPendingMarkerId); }
 
   bool IsNone() const { return id_ == kNoneId; }
+  bool IsPendingMarker() const { return id_ == kPendingMarkerId; }
   bool operator==(const BailoutId& other) const { return id_ == other.id_; }
 
  private:
@@ -1136,6 +1138,10 @@ class BailoutId {
   // Every compiled stub starts with this id.
   static const int kStubEntryId = 5;
 
+  // For Control instructions we cannot verify the ast_id, since there is no
+  // 1:1 mapping but it corresponds to two simulates for each branch.
+  static const int kPendingMarkerId = 6;
+
   int id_;
 };
 
index 83a8cb2..4ee430b 100644 (file)
@@ -2604,7 +2604,7 @@ void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
 }
 
 
-void LCodeGen::DoCmpT(LCmpT* instr) {
+void LCodeGen::DoCompareGenericAndBranch(LCompareGenericAndBranch* instr) {
   Token::Value op = instr->op();
 
   Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
@@ -2613,12 +2613,7 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
   Condition condition = TokenToCondition(op, false);
   Label true_value, done;
   __ testq(rax, rax);
-  __ j(condition, &true_value, Label::kNear);
-  __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
-  __ jmp(&done, Label::kNear);
-  __ bind(&true_value);
-  __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
-  __ bind(&done);
+  EmitBranch(instr, condition);
 }
 
 
index ddaae82..9815e87 100644 (file)
@@ -639,12 +639,18 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
   instr = AssignPointerMap(instr);
 
   if (hinstr->HasObservableSideEffects()) {
-    ASSERT(hinstr->next()->IsSimulate());
-    HSimulate* sim = HSimulate::cast(hinstr->next());
     ASSERT(instruction_pending_deoptimization_environment_ == NULL);
     ASSERT(pending_deoptimization_ast_id_.IsNone());
+    // For Control instructions we cannot verify the ast_id, since there is no
+    // 1:1 mapping but it corresponds to two simulates for each branch.
+    if (!hinstr->IsControlInstruction()) {
+      ASSERT(hinstr->next()->IsSimulate());
+      HSimulate* sim = HSimulate::cast(hinstr->next());
+      pending_deoptimization_ast_id_ = sim->ast_id();
+    } else {
+      pending_deoptimization_ast_id_ = BailoutId::PendingMarker();
+    }
     instruction_pending_deoptimization_environment_ = instr;
-    pending_deoptimization_ast_id_ = sim->ast_id();
   }
 
   // If instruction does not have side-effects lazy deoptimization
@@ -1600,13 +1606,13 @@ LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
 }
 
 
-LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
+LInstruction* LChunkBuilder::DoCompareGenericAndBranch(
+    HCompareGenericAndBranch* instr) {
   ASSERT(instr->left()->representation().IsTagged());
   ASSERT(instr->right()->representation().IsTagged());
   LOperand* left = UseFixed(instr->left(), rdx);
   LOperand* right = UseFixed(instr->right(), rax);
-  LCmpT* result = new(zone()) LCmpT(left, right);
-  return MarkAsCall(DefineFixed(result, rax), instr);
+  return MarkAsCall(new(zone()) LCompareGenericAndBranch(left, right), instr);
 }
 
 
@@ -2434,7 +2440,9 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
 
   // If there is an instruction pending deoptimization environment create a
   // lazy bailout instruction to capture the environment.
-  if (pending_deoptimization_ast_id_ == instr->ast_id()) {
+  if (!pending_deoptimization_ast_id_.IsNone()) {
+    ASSERT(pending_deoptimization_ast_id_ == instr->ast_id() ||
+           pending_deoptimization_ast_id_.IsPendingMarker());
     LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
     LInstruction* result = AssignEnvironment(lazy_bailout);
     // Store the lazy deopt environment with the instruction if needed. Right
index 9f45f97..cb8173d 100644 (file)
@@ -76,7 +76,7 @@ class LCodeGen;
   V(CmpObjectEqAndBranch)                       \
   V(CmpHoleAndBranch)                           \
   V(CmpMapAndBranch)                            \
-  V(CmpT)                                       \
+  V(CompareGenericAndBranch)                    \
   V(ConstantD)                                  \
   V(ConstantE)                                  \
   V(ConstantI)                                  \
@@ -1019,9 +1019,9 @@ class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 2> {
 };
 
 
-class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
+class LCompareGenericAndBranch V8_FINAL : public LControlInstruction<2, 0> {
  public:
-  LCmpT(LOperand* left, LOperand* right) {
+  LCompareGenericAndBranch(LOperand* left, LOperand* right) {
     inputs_[0] = left;
     inputs_[1] = right;
   }
@@ -1029,8 +1029,9 @@ class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
   LOperand* left() { return inputs_[0]; }
   LOperand* right() { return inputs_[1]; }
 
-  DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
-  DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
+  DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch,
+                               "compare-generic-and-branch")
+  DECLARE_HYDROGEN_ACCESSOR(CompareGenericAndBranch)
 
   Token::Value op() const { return hydrogen()->token(); }
 };
diff --git a/test/mjsunit/compare-generic.js b/test/mjsunit/compare-generic.js
new file mode 100644 (file)
index 0000000..1d76aa2
--- /dev/null
@@ -0,0 +1,226 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax
+
+// Test the correct placement of the simulates after HCompareGenericAndBranch:
+function Checker() {
+  this.str = "1";
+  var toStringCalled = 0;
+  var toStringExpected = 0;
+  this.toString = function() {
+    toStringCalled++;
+    return this.str;
+  };
+  this.check = function() {
+    toStringExpected++;
+    assertEquals(toStringExpected, toStringCalled);
+  };
+};
+var left = new Checker();
+var right = new Checker();
+
+// This test compares a < b against x < y where
+// x/y are objects providing a/b as toString. In the end we
+// check if the observable side effects match our
+// expectations, thus we make sure that we deopted to a
+// simulate after the comparison was done.
+function test(a,b) {
+  left.str = a;
+  right.str = b;
+  if (left >= right) {
+    assertTrue(a >= b);
+  } else {
+    assertFalse(a >= b);
+  }
+  left.check();
+  right.check();
+}
+
+test("ab","abc");
+test("ab","a");
+%OptimizeFunctionOnNextCall(test);
+test("a","ab");
+test(1,"a");
+test("a","ab");
+%OptimizeFunctionOnNextCall(test);
+test("a","ab");
+test("a",1);
+test("ab","a");
+
+
+// Use generic compare in value, effect and test contexts
+
+function Checker2() {
+  var valueOfCalled = 0;
+  this.valueOf = function() {
+    return valueOfCalled++;
+  }
+  this.valueOfCalled = function() {
+    return valueOfCalled;
+  }
+}
+
+var x = new Checker2();
+var y = new Checker2();
+
+if (x < y || y < x || x <= y) {
+  assertEquals(3, x.valueOfCalled());
+  assertEquals(3, y.valueOfCalled());
+  assertEquals(1, (x < y) + (y < x) + (x <= y))
+  assertEquals(6, x.valueOfCalled());
+  assertEquals(6, y.valueOfCalled());
+  x < y;
+  assertEquals(7, x.valueOfCalled());
+  assertEquals(7, y.valueOfCalled());
+  x < y;
+  assertEquals(8, x.valueOfCalled());
+  assertEquals(8, y.valueOfCalled());
+  var res;
+  if (x <= y) {
+    res = 1+(x > {});
+  } else {
+    assertTrue(false);
+    res = y <= {};
+  }
+  assertEquals(10, x.valueOfCalled());
+  assertEquals(9, y.valueOfCalled());
+  assertEquals(1, res);
+  assertFalse(x < y);
+
+  var tb = 0, fb = 0;
+  var val = 0;
+  for (var i = 1; i < 10; i++) {
+    var res = 0;
+    // uses x,y in control context
+    if (x <= y) {
+      res += val;
+      assertTrue(x <= y);
+      // adds 1 + 0, uses x in value context
+      res += 1+(x > {});
+      tb++;
+      assertEquals(fb, tb);
+    } else {
+      res += val;
+      assertFalse(x < y);
+      // adds 1, uses y in value context, increments 2
+      res += (y <= y);
+      // use x in value context, increments x once to make it equal to y again
+      x + 2;
+      assertEquals(fb, tb);
+      fb++;
+    }
+    assertEquals(11+(2*i)+tb+fb, x.valueOfCalled());
+    assertEquals(10+(2*i)+(2*fb), y.valueOfCalled());
+    assertEquals(1 + val, res);
+    // Triggers deopt inside branch.
+    if (i%5 == 0) val += 0.5;
+  }
+} else {
+  assertTrue(false);
+}
+
+
+function t(a,b) { return (b < a) - (a < b); };
+function f() {
+  x = new Checker2();
+  y = new Checker2();
+  var tb = 0, fb = 0;
+  var val = 0;
+  for (var i = 1; i < 10; i++) {
+    var res = 0;
+    if ((x < y) + (y < x)) {
+      res += val;
+      res += x<0;
+      fb++;
+    } else {
+      res += val;
+      res += y<0;
+      tb++;
+    }
+    assertEquals(0, res + 1 - res - 1);
+    assertEquals((2*i)+fb, x.valueOfCalled());
+    assertEquals((2*i)+tb, y.valueOfCalled());
+    assertEquals(val, res);
+    if (i%4 == 0) val += 0.5;
+  }
+}
+
+f();
+%OptimizeFunctionOnNextCall(f);
+f();
+
+var a = {valueOf: function(){this.conv++; return 1;}};
+var b = {valueOf: function(){this.conv++; return 2;}};
+
+a.conv = 0;
+b.conv = 0;
+
+function f2(a,b,d1,d2) {
+  var runs = 0;
+  if ((a < b) + (a < b)) {
+    if (d2) { d2 += 0.2; }
+    runs++;
+  } else {
+    assertUnreachable();
+  }
+  assertEquals(1, runs);
+  if (a > b) {
+    assertUnreachable();
+  } else {
+    if (d1) { d1 += 0.2; }
+    runs++;
+  }
+  assertEquals(2, runs);
+}
+
+f2(a,b);
+f2(a,b);
+
+%OptimizeFunctionOnNextCall(f2);
+f2(a,b);
+f2(a,b);
+
+f2(a,b,true);
+f2(a,b);
+
+%OptimizeFunctionOnNextCall(f2);
+f2(a,b);
+f2(a,b);
+
+f2(a,b,false,true);
+f2(a,b);
+
+assertEquals(30, a.conv);
+assertEquals(30, b.conv);
+
+b.valueOf = function(){ return {}; }
+try {
+  f2(a,b);
+} catch(e) {
+  res = e.stack;
+}