}
-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);
}
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:
}
-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.
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);
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);
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);
// Opcodes for VirtuaMachine-level operators.
#define SIMPLIFIED_OP_LIST(V) \
+ V(AnyToBoolean) \
V(BooleanNot) \
V(BooleanToNumber) \
V(NumberEqual) \
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());
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);
} 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())) {
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
//------------------------------------------------------------------
// 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;
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) {
SimplifiedOperatorBuilder simplified(graph()->zone());
RepresentationChanger changer(jsgraph(), &simplified,
graph()->zone()->isolate());
- RepresentationSelector selector(jsgraph(), zone(), &changer);
+ RepresentationSelector selector(jsgraph(), zone_, &changer);
selector.Run(this);
}
class SimplifiedLowering FINAL {
public:
- explicit SimplifiedLowering(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
+ SimplifiedLowering(JSGraph* jsgraph, Zone* zone)
+ : jsgraph_(jsgraph), zone_(zone) {}
~SimplifiedLowering() {}
void LowerAllNodes();
void DoStringLessThanOrEqual(Node* node);
private:
- JSGraph* jsgraph_;
+ JSGraph* const jsgraph_;
+ Zone* const zone_;
Node* SmiTag(Node* node);
Node* IsTagged(Node* node);
// 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 {
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()))) {
}
+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);
}
+CommonOperatorBuilder* SimplifiedOperatorReducer::common() const {
+ return jsgraph()->common();
+}
+
+
MachineOperatorBuilder* SimplifiedOperatorReducer::machine() const {
return jsgraph()->machine();
}
namespace compiler {
// Forward declarations.
+class CommonOperatorBuilder;
class JSGraph;
class MachineOperatorBuilder;
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);
Graph* graph() const;
Factory* factory() const;
JSGraph* jsgraph() const { return jsgraph_; }
+ CommonOperatorBuilder* common() const;
MachineOperatorBuilder* machine() const;
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
#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) \
public:
explicit SimplifiedOperatorBuilder(Zone* zone);
+ const Operator* AnyToBoolean();
+
const Operator* BooleanNot();
const Operator* BooleanToNumber();
// 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()));
}
// Simplified operators
// -------------------------------
+ case IrOpcode::kAnyToBoolean:
+ // Type is Boolean.
+ CheckUpperIs(node, Type::Boolean());
+ break;
case IrOpcode::kBooleanNot:
// Boolean -> Boolean
CheckValueInputIs(node, 0, Type::Boolean());
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);
{ // 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());
}
}
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;
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) {
};
+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());
#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"
// -----------------------------------------------------------------------------
-// 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));
}
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)
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);
// 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"
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:
static const UnaryOperator kUnaryOperators[] = {
+ {&SimplifiedOperatorBuilder::AnyToBoolean, "AnyToBoolean"},
{&SimplifiedOperatorBuilder::BooleanNot, "BooleanNot"},
{&SimplifiedOperatorBuilder::ChangeBitToBool, "ChangeBitToBool"},
{&SimplifiedOperatorBuilder::ChangeBoolToBit, "ChangeBoolToBit"},
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());
}
// -----------------------------------------------------------------------------
+// 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
&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),