[turbofan] Correctify JSToBoolean lowering.
authorBenedikt Meurer <bmeurer@chromium.org>
Mon, 5 Jan 2015 13:44:13 +0000 (14:44 +0100)
committerBenedikt Meurer <bmeurer@chromium.org>
Mon, 5 Jan 2015 13:44:31 +0000 (13:44 +0000)
Introduce a new AnyToBoolean simplified operator to handle the later
lowering of boolean conversions. Previously we tried to hack that with
the generic JSToBoolean, having its context set to zero, but that lead
to various problems/bugs and did not handle all cases.

TEST=cctest,unittests
R=jarin@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#25958}

19 files changed:
src/compiler/js-typed-lowering.cc
src/compiler/js-typed-lowering.h
src/compiler/opcodes.h
src/compiler/pipeline.cc
src/compiler/simplified-lowering.cc
src/compiler/simplified-lowering.h
src/compiler/simplified-operator-reducer.cc
src/compiler/simplified-operator-reducer.h
src/compiler/simplified-operator.cc
src/compiler/simplified-operator.h
src/compiler/typer.cc
src/compiler/verifier.cc
test/cctest/compiler/test-js-typed-lowering.cc
test/cctest/compiler/test-simplified-lowering.cc
test/unittests/compiler/js-typed-lowering-unittest.cc
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h
test/unittests/compiler/simplified-operator-reducer-unittest.cc
test/unittests/compiler/simplified-operator-unittest.cc

index 2338866..d9a627a 100644 (file)
@@ -490,124 +490,34 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
 }
 
 
-Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) {
-  if (input->opcode() == IrOpcode::kJSToBoolean) {
-    // Recursively try to reduce the input first.
-    Reduction result = ReduceJSToBoolean(input);
-    if (result.Changed()) return result;
-    return Changed(input);  // JSToBoolean(JSToBoolean(x)) => JSToBoolean(x)
-  }
-  // Check if we have a cached conversion.
-  Node* conversion = FindConversion<IrOpcode::kJSToBoolean>(input);
-  if (conversion) return Replace(conversion);
+Reduction JSTypedLowering::ReduceJSUnaryNot(Node* node) {
+  Node* input = node->InputAt(0);
   Type* input_type = NodeProperties::GetBounds(input).upper;
   if (input_type->Is(Type::Boolean())) {
-    return Changed(input);  // JSToBoolean(x:boolean) => x
-  }
-  if (input_type->Is(Type::Undefined())) {
-    // JSToBoolean(undefined) => #false
-    return Replace(jsgraph()->FalseConstant());
-  }
-  if (input_type->Is(Type::Null())) {
-    // JSToBoolean(null) => #false
-    return Replace(jsgraph()->FalseConstant());
-  }
-  if (input_type->Is(Type::DetectableReceiver())) {
-    // JSToBoolean(x:detectable) => #true
-    return Replace(jsgraph()->TrueConstant());
-  }
-  if (input_type->Is(Type::Undetectable())) {
-    // JSToBoolean(x:undetectable) => #false
-    return Replace(jsgraph()->FalseConstant());
-  }
-  if (input_type->Is(Type::OrderedNumber())) {
-    // JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
-    Node* cmp = graph()->NewNode(simplified()->NumberEqual(), input,
-                                 jsgraph()->ZeroConstant());
-    Node* inv = graph()->NewNode(simplified()->BooleanNot(), cmp);
-    return Replace(inv);
-  }
-  if (input_type->Is(Type::String())) {
-    // JSToBoolean(x:string) => BooleanNot(NumberEqual(x.length, #0))
-    FieldAccess access = AccessBuilder::ForStringLength();
-    Node* length = graph()->NewNode(simplified()->LoadField(access), input,
-                                    graph()->start(), graph()->start());
-    Node* cmp = graph()->NewNode(simplified()->NumberEqual(), length,
-                                 jsgraph()->ZeroConstant());
-    Node* inv = graph()->NewNode(simplified()->BooleanNot(), cmp);
-    return Replace(inv);
+    // JSUnaryNot(x:boolean,context) => BooleanNot(x)
+    node->set_op(simplified()->BooleanNot());
+    node->TrimInputCount(1);
+    return Changed(node);
   }
-  return NoChange();
+  // JSUnaryNot(x,context) => BooleanNot(AnyToBoolean(x))
+  node->set_op(simplified()->BooleanNot());
+  node->ReplaceInput(0, graph()->NewNode(simplified()->AnyToBoolean(), input));
+  node->TrimInputCount(1);
+  return Changed(node);
 }
 
 
 Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
-  // Try to reduce the input first.
-  Node* const input = node->InputAt(0);
-  Reduction reduction = ReduceJSToBooleanInput(input);
-  if (reduction.Changed()) return reduction;
-  if (input->opcode() == IrOpcode::kPhi) {
-    // JSToBoolean(phi(x1,...,xn,control),context)
-    //   => phi(JSToBoolean(x1,no-context),...,JSToBoolean(xn,no-context))
-    int const input_count = input->InputCount() - 1;
-    Node* const control = input->InputAt(input_count);
-    DCHECK_LE(0, input_count);
-    DCHECK(NodeProperties::IsControl(control));
-    DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Boolean()));
-    DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Boolean()));
-    node->set_op(common()->Phi(kMachAnyTagged, input_count));
-    for (int i = 0; i < input_count; ++i) {
-      // We must be very careful not to introduce cycles when pushing
-      // operations into phis. It is safe for {value}, since it appears
-      // as input to the phi that we are replacing, but it's not safe
-      // to simply reuse the context of the {node}. However, ToBoolean()
-      // does not require a context anyways, so it's safe to discard it
-      // here and pass the dummy context.
-      Node* const value = ConvertToBoolean(input->InputAt(i));
-      if (i < node->InputCount()) {
-        node->ReplaceInput(i, value);
-      } else {
-        node->AppendInput(graph()->zone(), value);
-      }
-    }
-    if (input_count < node->InputCount()) {
-      node->ReplaceInput(input_count, control);
-    } else {
-      node->AppendInput(graph()->zone(), control);
-    }
-    node->TrimInputCount(input_count + 1);
-    return Changed(node);
-  }
-  if (input->opcode() == IrOpcode::kSelect) {
-    // JSToBoolean(select(c,x1,x2),context)
-    //   => select(c,JSToBoolean(x1,no-context),...,JSToBoolean(x2,no-context))
-    int const input_count = input->InputCount();
-    BranchHint const input_hint = SelectParametersOf(input->op()).hint();
-    DCHECK_EQ(3, input_count);
-    DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Boolean()));
-    DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Boolean()));
-    node->set_op(common()->Select(kMachAnyTagged, input_hint));
-    node->InsertInput(graph()->zone(), 0, input->InputAt(0));
-    for (int i = 1; i < input_count; ++i) {
-      // We must be very careful not to introduce cycles when pushing
-      // operations into selects. It is safe for {value}, since it appears
-      // as input to the select that we are replacing, but it's not safe
-      // to simply reuse the context of the {node}. However, ToBoolean()
-      // does not require a context anyways, so it's safe to discard it
-      // here and pass the dummy context.
-      Node* const value = ConvertToBoolean(input->InputAt(i));
-      node->ReplaceInput(i, value);
-    }
-    DCHECK_EQ(3, node->InputCount());
-    return Changed(node);
-  }
-  InsertConversion(node);
-  if (node->InputAt(1) != jsgraph()->NoContextConstant()) {
-    // JSToBoolean(x,context) => JSToBoolean(x,no-context)
-    node->ReplaceInput(1, jsgraph()->NoContextConstant());
-    return Changed(node);
+  Node* input = node->InputAt(0);
+  Type* input_type = NodeProperties::GetBounds(input).upper;
+  if (input_type->Is(Type::Boolean())) {
+    // JSToBoolean(x:boolean,context) => x
+    return Replace(input);
   }
-  return NoChange();
+  // JSToBoolean(x,context) => AnyToBoolean(x)
+  node->set_op(simplified()->AnyToBoolean());
+  node->TrimInputCount(1);
+  return Changed(node);
 }
 
 
@@ -972,18 +882,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
       return ReduceNumberBinop(node, simplified()->NumberDivide());
     case IrOpcode::kJSModulus:
       return ReduceNumberBinop(node, simplified()->NumberModulus());
-    case IrOpcode::kJSUnaryNot: {
-      Reduction result = ReduceJSToBooleanInput(node->InputAt(0));
-      if (result.Changed()) {
-        // JSUnaryNot(x:boolean) => BooleanNot(x)
-        node = result.replacement();
-      } else {
-        // JSUnaryNot(x) => BooleanNot(JSToBoolean(x))
-        node->set_op(javascript()->ToBoolean());
-      }
-      Node* value = graph()->NewNode(simplified()->BooleanNot(), node);
-      return Replace(value);
-    }
+    case IrOpcode::kJSUnaryNot:
+      return ReduceJSUnaryNot(node);
     case IrOpcode::kJSToBoolean:
       return ReduceJSToBoolean(node);
     case IrOpcode::kJSToNumber:
@@ -1005,17 +905,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
 }
 
 
-Node* JSTypedLowering::ConvertToBoolean(Node* input) {
-  // Avoid inserting too many eager ToBoolean() operations.
-  Reduction const reduction = ReduceJSToBooleanInput(input);
-  if (reduction.Changed()) return reduction.replacement();
-  Node* const conversion = graph()->NewNode(javascript()->ToBoolean(), input,
-                                            jsgraph()->NoContextConstant());
-  InsertConversion(conversion);
-  return conversion;
-}
-
-
 Node* JSTypedLowering::ConvertToNumber(Node* input) {
   DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive()));
   // Avoid inserting too many eager ToNumber() operations.
@@ -1043,8 +932,7 @@ Node* JSTypedLowering::FindConversion(Node* input) {
 
 
 void JSTypedLowering::InsertConversion(Node* conversion) {
-  DCHECK(conversion->opcode() == IrOpcode::kJSToBoolean ||
-         conversion->opcode() == IrOpcode::kJSToNumber);
+  DCHECK(conversion->opcode() == IrOpcode::kJSToNumber);
   size_t const input_id = conversion->InputAt(0)->id();
   if (input_id >= conversions_.size()) {
     conversions_.resize(2 * input_id + 1);
index aa7510b..838085e 100644 (file)
@@ -41,7 +41,7 @@ class JSTypedLowering FINAL : public Reducer {
   Reduction ReduceJSStoreContext(Node* node);
   Reduction ReduceJSEqual(Node* node, bool invert);
   Reduction ReduceJSStrictEqual(Node* node, bool invert);
-  Reduction ReduceJSToBooleanInput(Node* input);
+  Reduction ReduceJSUnaryNot(Node* node);
   Reduction ReduceJSToBoolean(Node* node);
   Reduction ReduceJSToNumberInput(Node* input);
   Reduction ReduceJSToNumber(Node* node);
@@ -52,7 +52,6 @@ class JSTypedLowering FINAL : public Reducer {
   Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
                             const Operator* shift_op);
 
-  Node* ConvertToBoolean(Node* input);
   Node* ConvertToNumber(Node* input);
   template <IrOpcode::Value>
   Node* FindConversion(Node* input);
index 3f00e6a..d229b6d 100644 (file)
 
 // Opcodes for VirtuaMachine-level operators.
 #define SIMPLIFIED_OP_LIST(V) \
+  V(AnyToBoolean)             \
   V(BooleanNot)               \
   V(BooleanToNumber)          \
   V(NumberEqual)              \
index 0e80623..c7432c6 100644 (file)
@@ -441,7 +441,7 @@ struct SimplifiedLoweringPhase {
   void Run(PipelineData* data, Zone* temp_zone) {
     SourcePositionTable::Scope pos(data->source_positions(),
                                    SourcePosition::Unknown());
-    SimplifiedLowering lowering(data->jsgraph());
+    SimplifiedLowering lowering(data->jsgraph(), temp_zone);
     lowering.LowerAllNodes();
     ValueNumberingReducer vn_reducer(temp_zone);
     SimplifiedOperatorReducer simple_reducer(data->jsgraph());
index bb57614..5f9a406 100644 (file)
@@ -77,6 +77,9 @@ class RepresentationSelector {
     memset(info_, 0, sizeof(NodeInfo) * count_);
 
     Factory* f = zone->isolate()->factory();
+    safe_bit_range_ =
+        Type::Union(Type::Boolean(),
+                    Type::Range(f->NewNumber(0), f->NewNumber(1), zone), zone);
     safe_int_additive_range_ =
         Type::Range(f->NewNumber(-std::pow(2.0, 52.0)),
                     f->NewNumber(std::pow(2.0, 52.0)), zone);
@@ -320,7 +323,7 @@ class RepresentationSelector {
       } else {
         return kRepFloat64;
       }
-    } else if (upper->Is(Type::Boolean())) {
+    } else if (IsSafeBitOperand(node)) {
       // multiple uses => pick kRepBit.
       return kRepBit;
     } else if (upper->Is(Type::Number())) {
@@ -414,6 +417,11 @@ class RepresentationSelector {
     return BothInputsAre(node, Type::Signed32()) && !CanObserveNonInt32(use);
   }
 
+  bool IsSafeBitOperand(Node* node) {
+    Type* type = NodeProperties::GetBounds(node).upper;
+    return type->Is(safe_bit_range_);
+  }
+
   bool IsSafeIntAdditiveOperand(Node* node) {
     Type* type = NodeProperties::GetBounds(node).upper;
     // TODO(jarin): Unfortunately, bitset types are not subtypes of larger
@@ -521,6 +529,28 @@ class RepresentationSelector {
       //------------------------------------------------------------------
       // Simplified operators.
       //------------------------------------------------------------------
+      case IrOpcode::kAnyToBoolean: {
+        if (IsSafeBitOperand(node->InputAt(0))) {
+          VisitUnop(node, kRepBit, kRepBit);
+          if (lower()) DeferReplacement(node, node->InputAt(0));
+        } else {
+          VisitUnop(node, kMachAnyTagged, kTypeBool | kRepTagged);
+          if (lower()) {
+            // AnyToBoolean(x) => Call(ToBooleanStub, x, no-context)
+            Operator::Properties properties = node->op()->properties();
+            Callable callable = CodeFactory::ToBoolean(
+                jsgraph_->isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
+            CallDescriptor::Flags flags = CallDescriptor::kPatchableCallSite;
+            CallDescriptor* desc = Linkage::GetStubCallDescriptor(
+                callable.descriptor(), 0, flags, properties, jsgraph_->zone());
+            node->set_op(jsgraph_->common()->Call(desc));
+            node->InsertInput(jsgraph_->zone(), 0,
+                              jsgraph_->HeapConstant(callable.code()));
+            node->AppendInput(jsgraph_->zone(), jsgraph_->NoContextConstant());
+          }
+        }
+        break;
+      }
       case IrOpcode::kBooleanNot: {
         if (lower()) {
           MachineTypeUnion input = GetInfo(node->InputAt(0))->output;
@@ -1034,6 +1064,7 @@ class RepresentationSelector {
   Phase phase_;                     // current phase of algorithm
   RepresentationChanger* changer_;  // for inserting representation changes
   ZoneQueue<Node*> queue_;          // queue for traversing the graph
+  Type* safe_bit_range_;
   Type* safe_int_additive_range_;
 
   NodeInfo* GetInfo(Node* node) {
@@ -1058,7 +1089,7 @@ void SimplifiedLowering::LowerAllNodes() {
   SimplifiedOperatorBuilder simplified(graph()->zone());
   RepresentationChanger changer(jsgraph(), &simplified,
                                 graph()->zone()->isolate());
-  RepresentationSelector selector(jsgraph(), zone(), &changer);
+  RepresentationSelector selector(jsgraph(), zone_, &changer);
   selector.Run(this);
 }
 
index 852ac7e..b21cf21 100644 (file)
@@ -20,7 +20,8 @@ class RepresentationChanger;
 
 class SimplifiedLowering FINAL {
  public:
-  explicit SimplifiedLowering(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
+  SimplifiedLowering(JSGraph* jsgraph, Zone* zone)
+      : jsgraph_(jsgraph), zone_(zone) {}
   ~SimplifiedLowering() {}
 
   void LowerAllNodes();
@@ -41,7 +42,8 @@ class SimplifiedLowering FINAL {
   void DoStringLessThanOrEqual(Node* node);
 
  private:
-  JSGraph* jsgraph_;
+  JSGraph* const jsgraph_;
+  Zone* const zone_;
 
   Node* SmiTag(Node* node);
   Node* IsTagged(Node* node);
index 0868cab..9d45e5b 100644 (file)
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "src/compiler/simplified-operator-reducer.h"
+
+#include "src/compiler/access-builder.h"
 #include "src/compiler/js-graph.h"
 #include "src/compiler/machine-operator.h"
 #include "src/compiler/node-matchers.h"
-#include "src/compiler/simplified-operator-reducer.h"
+#include "src/compiler/node-properties-inl.h"
 
 namespace v8 {
 namespace internal {
@@ -20,6 +23,8 @@ SimplifiedOperatorReducer::~SimplifiedOperatorReducer() {}
 
 Reduction SimplifiedOperatorReducer::Reduce(Node* node) {
   switch (node->opcode()) {
+    case IrOpcode::kAnyToBoolean:
+      return ReduceAnyToBoolean(node);
     case IrOpcode::kBooleanNot: {
       HeapObjectMatcher<HeapObject> m(node->InputAt(0));
       if (m.Is(Unique<HeapObject>::CreateImmovable(factory()->false_value()))) {
@@ -105,8 +110,36 @@ Reduction SimplifiedOperatorReducer::Reduce(Node* node) {
 }
 
 
+Reduction SimplifiedOperatorReducer::ReduceAnyToBoolean(Node* node) {
+  Node* const input = NodeProperties::GetValueInput(node, 0);
+  Type* const input_type = NodeProperties::GetBounds(input).upper;
+  if (input_type->Is(Type::Boolean())) {
+    // AnyToBoolean(x:boolean) => x
+    return Replace(input);
+  }
+  if (input_type->Is(Type::OrderedNumber())) {
+    // AnyToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
+    Node* compare = graph()->NewNode(simplified()->NumberEqual(), input,
+                                     jsgraph()->ZeroConstant());
+    return Change(node, simplified()->BooleanNot(), compare);
+  }
+  if (input_type->Is(Type::String())) {
+    // AnyToBoolean(x:string) => BooleanNot(NumberEqual(x.length, #0))
+    FieldAccess const access = AccessBuilder::ForStringLength();
+    Node* length = graph()->NewNode(simplified()->LoadField(access), input,
+                                    graph()->start(), graph()->start());
+    Node* compare = graph()->NewNode(simplified()->NumberEqual(), length,
+                                     jsgraph()->ZeroConstant());
+    return Change(node, simplified()->BooleanNot(), compare);
+  }
+  return NoChange();
+}
+
+
 Reduction SimplifiedOperatorReducer::Change(Node* node, const Operator* op,
                                             Node* a) {
+  DCHECK_EQ(node->InputCount(), OperatorProperties::GetTotalInputCount(op));
+  DCHECK_LE(1, node->InputCount());
   node->set_op(op);
   node->ReplaceInput(0, a);
   return Changed(node);
@@ -141,6 +174,11 @@ Factory* SimplifiedOperatorReducer::factory() const {
 }
 
 
+CommonOperatorBuilder* SimplifiedOperatorReducer::common() const {
+  return jsgraph()->common();
+}
+
+
 MachineOperatorBuilder* SimplifiedOperatorReducer::machine() const {
   return jsgraph()->machine();
 }
index 8f6c9aa..1e565b8 100644 (file)
@@ -17,6 +17,7 @@ class Heap;
 namespace compiler {
 
 // Forward declarations.
+class CommonOperatorBuilder;
 class JSGraph;
 class MachineOperatorBuilder;
 
@@ -28,6 +29,8 @@ class SimplifiedOperatorReducer FINAL : public Reducer {
   Reduction Reduce(Node* node) FINAL;
 
  private:
+  Reduction ReduceAnyToBoolean(Node* node);
+
   Reduction Change(Node* node, const Operator* op, Node* a);
   Reduction ReplaceFloat64(double value);
   Reduction ReplaceInt32(int32_t value);
@@ -40,6 +43,7 @@ class SimplifiedOperatorReducer FINAL : public Reducer {
   Graph* graph() const;
   Factory* factory() const;
   JSGraph* jsgraph() const { return jsgraph_; }
+  CommonOperatorBuilder* common() const;
   MachineOperatorBuilder* machine() const;
   SimplifiedOperatorBuilder* simplified() { return &simplified_; }
 
index e082a4b..9d88d12 100644 (file)
@@ -158,6 +158,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
 
 
 #define PURE_OP_LIST(V)                                \
+  V(AnyToBoolean, Operator::kNoProperties, 1)          \
   V(BooleanNot, Operator::kNoProperties, 1)            \
   V(BooleanToNumber, Operator::kNoProperties, 1)       \
   V(NumberEqual, Operator::kCommutative, 2)            \
index 72608ee..22664fa 100644 (file)
@@ -128,6 +128,8 @@ class SimplifiedOperatorBuilder FINAL {
  public:
   explicit SimplifiedOperatorBuilder(Zone* zone);
 
+  const Operator* AnyToBoolean();
+
   const Operator* BooleanNot();
   const Operator* BooleanToNumber();
 
index 2caeb9b..137829e 100644 (file)
@@ -1441,6 +1441,11 @@ Bounds Typer::Visitor::TypeJSDebugger(Node* node) {
 // Simplified operators.
 
 
+Bounds Typer::Visitor::TypeAnyToBoolean(Node* node) {
+  return TypeUnaryOp(node, ToBoolean);
+}
+
+
 Bounds Typer::Visitor::TypeBooleanNot(Node* node) {
   return Bounds(Type::None(zone()), Type::Boolean(zone()));
 }
index 84b060f..693b414 100644 (file)
@@ -482,6 +482,10 @@ void Verifier::Visitor::Pre(Node* node) {
 
     // Simplified operators
     // -------------------------------
+    case IrOpcode::kAnyToBoolean:
+      // Type is Boolean.
+      CheckUpperIs(node, Type::Boolean());
+      break;
     case IrOpcode::kBooleanNot:
       // Boolean -> Boolean
       CheckValueInputIs(node, 0, Type::Boolean());
index 70b1312..3023837 100644 (file)
@@ -507,24 +507,6 @@ TEST(JSToBoolean) {
     CHECK_EQ(IrOpcode::kParameter, r->opcode());
   }
 
-  {  // ToBoolean(ordered-number)
-    Node* r = R.ReduceUnop(op, Type::OrderedNumber());
-    CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
-    Node* i = r->InputAt(0);
-    CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
-    // ToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
-  }
-
-  {  // ToBoolean(string)
-    Node* r = R.ReduceUnop(op, Type::String());
-    CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
-    Node* i = r->InputAt(0);
-    CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
-    Node* j = i->InputAt(0);
-    CHECK_EQ(IrOpcode::kLoadField, j->opcode());
-    // ToBoolean(x:string) => BooleanNot(NumberEqual(x.length, #0))
-  }
-
   {  // ToBoolean(object)
     Node* r = R.ReduceUnop(op, Type::DetectableObject());
     R.CheckTrue(r);
@@ -537,30 +519,7 @@ TEST(JSToBoolean) {
 
   {  // ToBoolean(object)
     Node* r = R.ReduceUnop(op, Type::Object());
-    CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
-  }
-}
-
-
-TEST(JSToBoolean_replacement) {
-  JSTypedLoweringTester R;
-
-  Type* types[] = {Type::Null(),             Type::Undefined(),
-                   Type::Boolean(),          Type::OrderedNumber(),
-                   Type::DetectableObject(), Type::Undetectable()};
-
-  for (size_t i = 0; i < arraysize(types); i++) {
-    Node* n = R.Parameter(types[i]);
-    Node* c = R.graph.NewNode(R.javascript.ToBoolean(), n, R.context());
-    Node* r = R.reduce(c);
-
-    if (types[i]->Is(Type::Boolean())) {
-      CHECK_EQ(n, r);
-    } else if (types[i]->Is(Type::OrderedNumber())) {
-      CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
-    } else {
-      CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
-    }
+    CHECK_EQ(IrOpcode::kAnyToBoolean, r->opcode());
   }
 }
 
index d463997..ca1c873 100644 (file)
@@ -39,7 +39,7 @@ class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> {
         typer(this->graph(), MaybeHandle<Context>()),
         javascript(this->zone()),
         jsgraph(this->graph(), this->common(), &javascript, this->machine()),
-        lowering(&jsgraph) {}
+        lowering(&jsgraph, this->zone()) {}
 
   Typer typer;
   JSOperatorBuilder javascript;
@@ -698,9 +698,7 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
     CHECK_EQ(expected, node->opcode());
   }
 
-  void Lower() {
-    SimplifiedLowering(&jsgraph).LowerAllNodes();
-  }
+  void Lower() { SimplifiedLowering(&jsgraph, jsgraph.zone()).LowerAllNodes(); }
 
   // Inserts the node as the return value of the graph.
   Node* Return(Node* node) {
@@ -789,6 +787,46 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
 };
 
 
+TEST(LowerAnyToBoolean_bit_bit) {
+  // AnyToBoolean(x: kRepBit) used as kRepBit
+  HandleAndZoneScope scope;
+  Factory* f = scope.main_zone()->isolate()->factory();
+  Handle<Object> zero = f->NewNumber(0);
+  Handle<Object> one = f->NewNumber(1);
+  Type* singleton_zero = Type::Constant(zero, scope.main_zone());
+  Type* singleton_one = Type::Constant(one, scope.main_zone());
+  Type* zero_one_range = Type::Range(zero, one, scope.main_zone());
+  static Type* kTypes[] = {
+      singleton_zero, singleton_one, zero_one_range, Type::Boolean(),
+      Type::Union(Type::Boolean(), singleton_zero, scope.main_zone()),
+      Type::Union(Type::Boolean(), singleton_one, scope.main_zone()),
+      Type::Union(Type::Boolean(), zero_one_range, scope.main_zone())};
+  for (Type* type : kTypes) {
+    TestingGraph t(type);
+    Node* x = t.ExampleWithTypeAndRep(type, kRepBit);
+    Node* cnv = t.graph()->NewNode(t.simplified()->AnyToBoolean(), x);
+    Node* use = t.Branch(cnv);
+    t.Lower();
+    CHECK_EQ(x, use->InputAt(0));
+  }
+}
+
+
+TEST(LowerAnyToBoolean_tagged_tagged) {
+  // AnyToBoolean(x: kRepTagged) used as kRepTagged
+  TestingGraph t(Type::Any());
+  Node* x = t.p0;
+  Node* cnv = t.graph()->NewNode(t.simplified()->AnyToBoolean(), x);
+  Node* use = t.Use(cnv, kRepTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kCall, cnv->opcode());
+  CHECK_EQ(IrOpcode::kHeapConstant, cnv->InputAt(0)->opcode());
+  CHECK_EQ(x, cnv->InputAt(1));
+  CHECK_EQ(t.jsgraph.NoContextConstant(), cnv->InputAt(2));
+}
+
+
 TEST(LowerBooleanNot_bit_bit) {
   // BooleanNot(x: kRepBit) used as kRepBit
   TestingGraph t(Type::Boolean());
index 322d0cb..f0e141a 100644 (file)
@@ -8,7 +8,6 @@
 #include "src/compiler/js-typed-lowering.h"
 #include "src/compiler/machine-operator.h"
 #include "src/compiler/node-properties-inl.h"
-#include "src/compiler/typer.h"
 #include "test/unittests/compiler/compiler-test-utils.h"
 #include "test/unittests/compiler/graph-unittest.h"
 #include "test/unittests/compiler/node-test-utils.h"
@@ -73,125 +72,165 @@ class JSTypedLoweringTest : public TypedGraphTest {
 
 
 // -----------------------------------------------------------------------------
-// JSToBoolean
-
+// JSUnaryNot
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
-  Node* input = Parameter(Type::Boolean());
-  Node* context = UndefinedConstant();
 
+TEST_F(JSTypedLoweringTest, JSUnaryNotWithBoolean) {
+  Node* input = Parameter(Type::Boolean(), 0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
-      Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
+      Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_EQ(input, r.replacement());
+  EXPECT_THAT(r.replacement(), IsBooleanNot(input));
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithUndefined) {
-  Node* input = Parameter(Type::Undefined());
-  Node* context = UndefinedConstant();
+TEST_F(JSTypedLoweringTest, JSUnaryNotWithFalsish) {
+  Handle<Object> zero = factory()->NewNumber(0);
+  Node* input = Parameter(
+      Type::Union(
+          Type::MinusZero(),
+          Type::Union(
+              Type::NaN(),
+              Type::Union(
+                  Type::Null(),
+                  Type::Union(
+                      Type::Undefined(),
+                      Type::Union(
+                          Type::Undetectable(),
+                          Type::Union(
+                              Type::Constant(factory()->false_value(), zone()),
+                              Type::Range(zero, zero, zone()), zone()),
+                          zone()),
+                      zone()),
+                  zone()),
+              zone()),
+          zone()),
+      0);
+  Node* context = Parameter(Type::Any(), 1);
+  Reduction r =
+      Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
+  ASSERT_TRUE(r.Changed());
+  EXPECT_THAT(r.replacement(), IsTrueConstant());
+}
+
 
+TEST_F(JSTypedLoweringTest, JSUnaryNotWithTruish) {
+  Node* input = Parameter(
+      Type::Union(
+          Type::Constant(factory()->true_value(), zone()),
+          Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
+          zone()),
+      0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
-      Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
+      Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
   ASSERT_TRUE(r.Changed());
   EXPECT_THAT(r.replacement(), IsFalseConstant());
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithNull) {
-  Node* input = Parameter(Type::Null());
-  Node* context = UndefinedConstant();
-
+TEST_F(JSTypedLoweringTest, JSUnaryNotWithNonZeroPlainNumber) {
+  Node* input = Parameter(
+      Type::Range(factory()->NewNumber(1), factory()->NewNumber(42), zone()),
+      0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
-      Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
+      Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
   ASSERT_TRUE(r.Changed());
   EXPECT_THAT(r.replacement(), IsFalseConstant());
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithDetectableReceiver) {
-  Node* input = Parameter(Type::DetectableReceiver());
-  Node* context = UndefinedConstant();
-
+TEST_F(JSTypedLoweringTest, JSUnaryNotWithAny) {
+  Node* input = Parameter(Type::Any(), 0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
-      Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
+      Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_THAT(r.replacement(), IsTrueConstant());
+  EXPECT_THAT(r.replacement(), IsBooleanNot(IsAnyToBoolean(input)));
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithUndetectable) {
-  Node* input = Parameter(Type::Undetectable());
-  Node* context = UndefinedConstant();
+// -----------------------------------------------------------------------------
+// JSToBoolean
+
 
+TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
+  Node* input = Parameter(Type::Boolean(), 0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
       Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_THAT(r.replacement(), IsFalseConstant());
+  EXPECT_EQ(input, r.replacement());
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) {
-  Node* input = Parameter(Type::OrderedNumber());
-  Node* context = UndefinedConstant();
-
+TEST_F(JSTypedLoweringTest, JSToBooleanWithFalsish) {
+  Handle<Object> zero = factory()->NewNumber(0);
+  Node* input = Parameter(
+      Type::Union(
+          Type::MinusZero(),
+          Type::Union(
+              Type::NaN(),
+              Type::Union(
+                  Type::Null(),
+                  Type::Union(
+                      Type::Undefined(),
+                      Type::Union(
+                          Type::Undetectable(),
+                          Type::Union(
+                              Type::Constant(factory()->false_value(), zone()),
+                              Type::Range(zero, zero, zone()), zone()),
+                          zone()),
+                      zone()),
+                  zone()),
+              zone()),
+          zone()),
+      0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
       Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_THAT(r.replacement(),
-              IsBooleanNot(IsNumberEqual(input, IsNumberConstant(BitEq(0.0)))));
+  EXPECT_THAT(r.replacement(), IsFalseConstant());
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithString) {
-  Node* input = Parameter(Type::String());
-  Node* context = UndefinedConstant();
-
+TEST_F(JSTypedLoweringTest, JSToBooleanWithTruish) {
+  Node* input = Parameter(
+      Type::Union(
+          Type::Constant(factory()->true_value(), zone()),
+          Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
+          zone()),
+      0);
+  Node* context = Parameter(Type::Any(), 1);
   Reduction r =
       Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_THAT(r.replacement(),
-              IsBooleanNot(IsNumberEqual(
-                  IsLoadField(AccessBuilder::ForStringLength(), input,
-                              graph()->start(), graph()->start()),
-                  IsNumberConstant(BitEq(0.0)))));
+  EXPECT_THAT(r.replacement(), IsTrueConstant());
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithPhi) {
-  Node* p0 = Parameter(Type::OrderedNumber(), 0);
-  Node* p1 = Parameter(Type::Boolean(), 1);
-  Node* context = UndefinedConstant();
-  Node* control = graph()->start();
-
-  Reduction r = Reduce(graph()->NewNode(
-      javascript()->ToBoolean(),
-      graph()->NewNode(common()->Phi(kMachAnyTagged, 2), p0, p1, control),
-      context));
+TEST_F(JSTypedLoweringTest, JSToBooleanWithNonZeroPlainNumber) {
+  Node* input =
+      Parameter(Type::Range(factory()->NewNumber(1),
+                            factory()->NewNumber(V8_INFINITY), zone()),
+                0);
+  Node* context = Parameter(Type::Any(), 1);
+  Reduction r =
+      Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_THAT(r.replacement(),
-              IsPhi(kMachAnyTagged, IsBooleanNot(IsNumberEqual(
-                                        p0, IsNumberConstant(BitEq(0.0)))),
-                    p1, control));
+  EXPECT_THAT(r.replacement(), IsTrueConstant());
 }
 
 
-TEST_F(JSTypedLoweringTest, JSToBooleanWithSelect) {
-  Node* p0 = Parameter(Type::Boolean(), 0);
-  Node* p1 = Parameter(Type::DetectableReceiver(), 1);
-  Node* p2 = Parameter(Type::OrderedNumber(), 2);
-  Node* context = UndefinedConstant();
-
-  Reduction r = Reduce(graph()->NewNode(
-      javascript()->ToBoolean(),
-      graph()->NewNode(common()->Select(kMachAnyTagged, BranchHint::kTrue), p0,
-                       p1, p2),
-      context));
+TEST_F(JSTypedLoweringTest, JSToBooleanWithAny) {
+  Node* input = Parameter(Type::Any(), 0);
+  Node* context = Parameter(Type::Any(), 1);
+  Reduction r =
+      Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
   ASSERT_TRUE(r.Changed());
-  EXPECT_THAT(
-      r.replacement(),
-      IsSelect(kMachAnyTagged, p0, IsTrueConstant(),
-               IsBooleanNot(IsNumberEqual(p2, IsNumberConstant(BitEq(0.0))))));
+  EXPECT_THAT(r.replacement(), IsAnyToBoolean(input));
 }
 
 
index 3162c54..74afda9 100644 (file)
@@ -1290,6 +1290,7 @@ IS_BINOP_MATCHER(Float64Sub)
   Matcher<Node*> Is##Name(const Matcher<Node*>& input_matcher) {             \
     return MakeMatcher(new IsUnopMatcher(IrOpcode::k##Name, input_matcher)); \
   }
+IS_UNOP_MATCHER(AnyToBoolean)
 IS_UNOP_MATCHER(BooleanNot)
 IS_UNOP_MATCHER(ChangeFloat64ToInt32)
 IS_UNOP_MATCHER(ChangeFloat64ToUint32)
index f1f20cf..02b6e43 100644 (file)
@@ -75,6 +75,7 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
                       const Matcher<Node*>& effect_matcher,
                       const Matcher<Node*>& control_matcher);
 
+Matcher<Node*> IsAnyToBoolean(const Matcher<Node*>& value_matcher);
 Matcher<Node*> IsBooleanNot(const Matcher<Node*>& value_matcher);
 Matcher<Node*> IsNumberEqual(const Matcher<Node*>& lhs_matcher,
                              const Matcher<Node*>& rhs_matcher);
index 18f5e64..e5f46c0 100644 (file)
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "src/compiler/access-builder.h"
 #include "src/compiler/js-graph.h"
+#include "src/compiler/node-properties-inl.h"
 #include "src/compiler/simplified-operator.h"
 #include "src/compiler/simplified-operator-reducer.h"
 #include "src/conversions.h"
@@ -18,10 +20,10 @@ namespace v8 {
 namespace internal {
 namespace compiler {
 
-class SimplifiedOperatorReducerTest : public GraphTest {
+class SimplifiedOperatorReducerTest : public TypedGraphTest {
  public:
   explicit SimplifiedOperatorReducerTest(int num_parameters = 1)
-      : GraphTest(num_parameters), simplified_(zone()) {}
+      : TypedGraphTest(num_parameters), simplified_(zone()) {}
   ~SimplifiedOperatorReducerTest() OVERRIDE {}
 
  protected:
@@ -139,6 +141,7 @@ std::ostream& operator<<(std::ostream& os, const UnaryOperator& unop) {
 
 
 static const UnaryOperator kUnaryOperators[] = {
+    {&SimplifiedOperatorBuilder::AnyToBoolean, "AnyToBoolean"},
     {&SimplifiedOperatorBuilder::BooleanNot, "BooleanNot"},
     {&SimplifiedOperatorBuilder::ChangeBitToBool, "ChangeBitToBool"},
     {&SimplifiedOperatorBuilder::ChangeBoolToBit, "ChangeBoolToBit"},
@@ -160,8 +163,8 @@ typedef SimplifiedOperatorReducerTestWithParam<UnaryOperator>
 
 TEST_P(SimplifiedUnaryOperatorTest, Parameter) {
   const UnaryOperator& unop = GetParam();
-  Reduction reduction = Reduce(
-      graph()->NewNode((simplified()->*unop.constructor)(), Parameter(0)));
+  Reduction reduction = Reduce(graph()->NewNode(
+      (simplified()->*unop.constructor)(), Parameter(Type::Any())));
   EXPECT_FALSE(reduction.Changed());
 }
 
@@ -172,6 +175,39 @@ INSTANTIATE_TEST_CASE_P(SimplifiedOperatorReducerTest,
 
 
 // -----------------------------------------------------------------------------
+// AnyToBoolean
+
+
+TEST_F(SimplifiedOperatorReducerTest, AnyToBooleanWithBoolean) {
+  Node* p = Parameter(Type::Boolean());
+  Reduction r = Reduce(graph()->NewNode(simplified()->AnyToBoolean(), p));
+  ASSERT_TRUE(r.Changed());
+  EXPECT_EQ(p, r.replacement());
+}
+
+
+TEST_F(SimplifiedOperatorReducerTest, AnyToBooleanWithOrderedNumber) {
+  Node* p = Parameter(Type::OrderedNumber());
+  Reduction r = Reduce(graph()->NewNode(simplified()->AnyToBoolean(), p));
+  ASSERT_TRUE(r.Changed());
+  EXPECT_THAT(r.replacement(),
+              IsBooleanNot(IsNumberEqual(p, IsNumberConstant(0))));
+}
+
+
+TEST_F(SimplifiedOperatorReducerTest, AnyToBooleanWithString) {
+  Node* p = Parameter(Type::String());
+  Reduction r = Reduce(graph()->NewNode(simplified()->AnyToBoolean(), p));
+  ASSERT_TRUE(r.Changed());
+  EXPECT_THAT(r.replacement(),
+              IsBooleanNot(
+                  IsNumberEqual(IsLoadField(AccessBuilder::ForStringLength(), p,
+                                            graph()->start(), graph()->start()),
+                                IsNumberConstant(0))));
+}
+
+
+// -----------------------------------------------------------------------------
 // BooleanNot
 
 
index e7fceba..bc537fd 100644 (file)
@@ -38,6 +38,7 @@ const PureOperator kPureOperators[] = {
     &SimplifiedOperatorBuilder::Name, IrOpcode::k##Name, \
         Operator::kPure | properties, input_count        \
   }
+    PURE(AnyToBoolean, Operator::kNoProperties, 1),
     PURE(BooleanNot, Operator::kNoProperties, 1),
     PURE(BooleanToNumber, Operator::kNoProperties, 1),
     PURE(NumberEqual, Operator::kCommutative, 2),