Commit 9def087e authored by Benedikt Meurer's avatar Benedikt Meurer

[turbofan] Correctify JSToBoolean lowering.

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}
parent 17a18084
...@@ -490,124 +490,34 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) { ...@@ -490,124 +490,34 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
} }
Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) { Reduction JSTypedLowering::ReduceJSUnaryNot(Node* node) {
if (input->opcode() == IrOpcode::kJSToBoolean) { Node* input = node->InputAt(0);
// 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);
Type* input_type = NodeProperties::GetBounds(input).upper; Type* input_type = NodeProperties::GetBounds(input).upper;
if (input_type->Is(Type::Boolean())) { if (input_type->Is(Type::Boolean())) {
return Changed(input); // JSToBoolean(x:boolean) => x // JSUnaryNot(x:boolean,context) => BooleanNot(x)
} node->set_op(simplified()->BooleanNot());
if (input_type->Is(Type::Undefined())) { node->TrimInputCount(1);
// JSToBoolean(undefined) => #false return Changed(node);
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);
} }
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) { Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
// Try to reduce the input first. Node* input = node->InputAt(0);
Node* const input = node->InputAt(0); Type* input_type = NodeProperties::GetBounds(input).upper;
Reduction reduction = ReduceJSToBooleanInput(input); if (input_type->Is(Type::Boolean())) {
if (reduction.Changed()) return reduction; // JSToBoolean(x:boolean,context) => x
if (input->opcode() == IrOpcode::kPhi) { return Replace(input);
// 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);
} }
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) { ...@@ -972,18 +882,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceNumberBinop(node, simplified()->NumberDivide()); return ReduceNumberBinop(node, simplified()->NumberDivide());
case IrOpcode::kJSModulus: case IrOpcode::kJSModulus:
return ReduceNumberBinop(node, simplified()->NumberModulus()); return ReduceNumberBinop(node, simplified()->NumberModulus());
case IrOpcode::kJSUnaryNot: { case IrOpcode::kJSUnaryNot:
Reduction result = ReduceJSToBooleanInput(node->InputAt(0)); return ReduceJSUnaryNot(node);
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::kJSToBoolean: case IrOpcode::kJSToBoolean:
return ReduceJSToBoolean(node); return ReduceJSToBoolean(node);
case IrOpcode::kJSToNumber: case IrOpcode::kJSToNumber:
...@@ -1005,17 +905,6 @@ Reduction JSTypedLowering::Reduce(Node* node) { ...@@ -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) { Node* JSTypedLowering::ConvertToNumber(Node* input) {
DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive())); DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive()));
// Avoid inserting too many eager ToNumber() operations. // Avoid inserting too many eager ToNumber() operations.
...@@ -1043,8 +932,7 @@ Node* JSTypedLowering::FindConversion(Node* input) { ...@@ -1043,8 +932,7 @@ Node* JSTypedLowering::FindConversion(Node* input) {
void JSTypedLowering::InsertConversion(Node* conversion) { void JSTypedLowering::InsertConversion(Node* conversion) {
DCHECK(conversion->opcode() == IrOpcode::kJSToBoolean || DCHECK(conversion->opcode() == IrOpcode::kJSToNumber);
conversion->opcode() == IrOpcode::kJSToNumber);
size_t const input_id = conversion->InputAt(0)->id(); size_t const input_id = conversion->InputAt(0)->id();
if (input_id >= conversions_.size()) { if (input_id >= conversions_.size()) {
conversions_.resize(2 * input_id + 1); conversions_.resize(2 * input_id + 1);
......
...@@ -41,7 +41,7 @@ class JSTypedLowering FINAL : public Reducer { ...@@ -41,7 +41,7 @@ class JSTypedLowering FINAL : public Reducer {
Reduction ReduceJSStoreContext(Node* node); Reduction ReduceJSStoreContext(Node* node);
Reduction ReduceJSEqual(Node* node, bool invert); Reduction ReduceJSEqual(Node* node, bool invert);
Reduction ReduceJSStrictEqual(Node* node, bool invert); Reduction ReduceJSStrictEqual(Node* node, bool invert);
Reduction ReduceJSToBooleanInput(Node* input); Reduction ReduceJSUnaryNot(Node* node);
Reduction ReduceJSToBoolean(Node* node); Reduction ReduceJSToBoolean(Node* node);
Reduction ReduceJSToNumberInput(Node* input); Reduction ReduceJSToNumberInput(Node* input);
Reduction ReduceJSToNumber(Node* node); Reduction ReduceJSToNumber(Node* node);
...@@ -52,7 +52,6 @@ class JSTypedLowering FINAL : public Reducer { ...@@ -52,7 +52,6 @@ class JSTypedLowering FINAL : public Reducer {
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness, Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
const Operator* shift_op); const Operator* shift_op);
Node* ConvertToBoolean(Node* input);
Node* ConvertToNumber(Node* input); Node* ConvertToNumber(Node* input);
template <IrOpcode::Value> template <IrOpcode::Value>
Node* FindConversion(Node* input); Node* FindConversion(Node* input);
......
...@@ -132,6 +132,7 @@ ...@@ -132,6 +132,7 @@
// Opcodes for VirtuaMachine-level operators. // Opcodes for VirtuaMachine-level operators.
#define SIMPLIFIED_OP_LIST(V) \ #define SIMPLIFIED_OP_LIST(V) \
V(AnyToBoolean) \
V(BooleanNot) \ V(BooleanNot) \
V(BooleanToNumber) \ V(BooleanToNumber) \
V(NumberEqual) \ V(NumberEqual) \
......
...@@ -441,7 +441,7 @@ struct SimplifiedLoweringPhase { ...@@ -441,7 +441,7 @@ struct SimplifiedLoweringPhase {
void Run(PipelineData* data, Zone* temp_zone) { void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(), SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown()); SourcePosition::Unknown());
SimplifiedLowering lowering(data->jsgraph()); SimplifiedLowering lowering(data->jsgraph(), temp_zone);
lowering.LowerAllNodes(); lowering.LowerAllNodes();
ValueNumberingReducer vn_reducer(temp_zone); ValueNumberingReducer vn_reducer(temp_zone);
SimplifiedOperatorReducer simple_reducer(data->jsgraph()); SimplifiedOperatorReducer simple_reducer(data->jsgraph());
......
...@@ -77,6 +77,9 @@ class RepresentationSelector { ...@@ -77,6 +77,9 @@ class RepresentationSelector {
memset(info_, 0, sizeof(NodeInfo) * count_); memset(info_, 0, sizeof(NodeInfo) * count_);
Factory* f = zone->isolate()->factory(); 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_ = safe_int_additive_range_ =
Type::Range(f->NewNumber(-std::pow(2.0, 52.0)), Type::Range(f->NewNumber(-std::pow(2.0, 52.0)),
f->NewNumber(std::pow(2.0, 52.0)), zone); f->NewNumber(std::pow(2.0, 52.0)), zone);
...@@ -320,7 +323,7 @@ class RepresentationSelector { ...@@ -320,7 +323,7 @@ class RepresentationSelector {
} else { } else {
return kRepFloat64; return kRepFloat64;
} }
} else if (upper->Is(Type::Boolean())) { } else if (IsSafeBitOperand(node)) {
// multiple uses => pick kRepBit. // multiple uses => pick kRepBit.
return kRepBit; return kRepBit;
} else if (upper->Is(Type::Number())) { } else if (upper->Is(Type::Number())) {
...@@ -414,6 +417,11 @@ class RepresentationSelector { ...@@ -414,6 +417,11 @@ class RepresentationSelector {
return BothInputsAre(node, Type::Signed32()) && !CanObserveNonInt32(use); 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) { bool IsSafeIntAdditiveOperand(Node* node) {
Type* type = NodeProperties::GetBounds(node).upper; Type* type = NodeProperties::GetBounds(node).upper;
// TODO(jarin): Unfortunately, bitset types are not subtypes of larger // TODO(jarin): Unfortunately, bitset types are not subtypes of larger
...@@ -521,6 +529,28 @@ class RepresentationSelector { ...@@ -521,6 +529,28 @@ class RepresentationSelector {
//------------------------------------------------------------------ //------------------------------------------------------------------
// Simplified operators. // 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: { case IrOpcode::kBooleanNot: {
if (lower()) { if (lower()) {
MachineTypeUnion input = GetInfo(node->InputAt(0))->output; MachineTypeUnion input = GetInfo(node->InputAt(0))->output;
...@@ -1034,6 +1064,7 @@ class RepresentationSelector { ...@@ -1034,6 +1064,7 @@ class RepresentationSelector {
Phase phase_; // current phase of algorithm Phase phase_; // current phase of algorithm
RepresentationChanger* changer_; // for inserting representation changes RepresentationChanger* changer_; // for inserting representation changes
ZoneQueue<Node*> queue_; // queue for traversing the graph ZoneQueue<Node*> queue_; // queue for traversing the graph
Type* safe_bit_range_;
Type* safe_int_additive_range_; Type* safe_int_additive_range_;
NodeInfo* GetInfo(Node* node) { NodeInfo* GetInfo(Node* node) {
...@@ -1058,7 +1089,7 @@ void SimplifiedLowering::LowerAllNodes() { ...@@ -1058,7 +1089,7 @@ void SimplifiedLowering::LowerAllNodes() {
SimplifiedOperatorBuilder simplified(graph()->zone()); SimplifiedOperatorBuilder simplified(graph()->zone());
RepresentationChanger changer(jsgraph(), &simplified, RepresentationChanger changer(jsgraph(), &simplified,
graph()->zone()->isolate()); graph()->zone()->isolate());
RepresentationSelector selector(jsgraph(), zone(), &changer); RepresentationSelector selector(jsgraph(), zone_, &changer);
selector.Run(this); selector.Run(this);
} }
......
...@@ -20,7 +20,8 @@ class RepresentationChanger; ...@@ -20,7 +20,8 @@ class RepresentationChanger;
class SimplifiedLowering FINAL { class SimplifiedLowering FINAL {
public: public:
explicit SimplifiedLowering(JSGraph* jsgraph) : jsgraph_(jsgraph) {} SimplifiedLowering(JSGraph* jsgraph, Zone* zone)
: jsgraph_(jsgraph), zone_(zone) {}
~SimplifiedLowering() {} ~SimplifiedLowering() {}
void LowerAllNodes(); void LowerAllNodes();
...@@ -41,7 +42,8 @@ class SimplifiedLowering FINAL { ...@@ -41,7 +42,8 @@ class SimplifiedLowering FINAL {
void DoStringLessThanOrEqual(Node* node); void DoStringLessThanOrEqual(Node* node);
private: private:
JSGraph* jsgraph_; JSGraph* const jsgraph_;
Zone* const zone_;
Node* SmiTag(Node* node); Node* SmiTag(Node* node);
Node* IsTagged(Node* node); Node* IsTagged(Node* node);
......
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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/js-graph.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h" #include "src/compiler/node-matchers.h"
#include "src/compiler/simplified-operator-reducer.h" #include "src/compiler/node-properties-inl.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -20,6 +23,8 @@ SimplifiedOperatorReducer::~SimplifiedOperatorReducer() {} ...@@ -20,6 +23,8 @@ SimplifiedOperatorReducer::~SimplifiedOperatorReducer() {}
Reduction SimplifiedOperatorReducer::Reduce(Node* node) { Reduction SimplifiedOperatorReducer::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kAnyToBoolean:
return ReduceAnyToBoolean(node);
case IrOpcode::kBooleanNot: { case IrOpcode::kBooleanNot: {
HeapObjectMatcher<HeapObject> m(node->InputAt(0)); HeapObjectMatcher<HeapObject> m(node->InputAt(0));
if (m.Is(Unique<HeapObject>::CreateImmovable(factory()->false_value()))) { if (m.Is(Unique<HeapObject>::CreateImmovable(factory()->false_value()))) {
...@@ -105,8 +110,36 @@ Reduction SimplifiedOperatorReducer::Reduce(Node* node) { ...@@ -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, Reduction SimplifiedOperatorReducer::Change(Node* node, const Operator* op,
Node* a) { Node* a) {
DCHECK_EQ(node->InputCount(), OperatorProperties::GetTotalInputCount(op));
DCHECK_LE(1, node->InputCount());
node->set_op(op); node->set_op(op);
node->ReplaceInput(0, a); node->ReplaceInput(0, a);
return Changed(node); return Changed(node);
...@@ -141,6 +174,11 @@ Factory* SimplifiedOperatorReducer::factory() const { ...@@ -141,6 +174,11 @@ Factory* SimplifiedOperatorReducer::factory() const {
} }
CommonOperatorBuilder* SimplifiedOperatorReducer::common() const {
return jsgraph()->common();
}
MachineOperatorBuilder* SimplifiedOperatorReducer::machine() const { MachineOperatorBuilder* SimplifiedOperatorReducer::machine() const {
return jsgraph()->machine(); return jsgraph()->machine();
} }
......
...@@ -17,6 +17,7 @@ class Heap; ...@@ -17,6 +17,7 @@ class Heap;
namespace compiler { namespace compiler {
// Forward declarations. // Forward declarations.
class CommonOperatorBuilder;
class JSGraph; class JSGraph;
class MachineOperatorBuilder; class MachineOperatorBuilder;
...@@ -28,6 +29,8 @@ class SimplifiedOperatorReducer FINAL : public Reducer { ...@@ -28,6 +29,8 @@ class SimplifiedOperatorReducer FINAL : public Reducer {
Reduction Reduce(Node* node) FINAL; Reduction Reduce(Node* node) FINAL;
private: private:
Reduction ReduceAnyToBoolean(Node* node);
Reduction Change(Node* node, const Operator* op, Node* a); Reduction Change(Node* node, const Operator* op, Node* a);
Reduction ReplaceFloat64(double value); Reduction ReplaceFloat64(double value);
Reduction ReplaceInt32(int32_t value); Reduction ReplaceInt32(int32_t value);
...@@ -40,6 +43,7 @@ class SimplifiedOperatorReducer FINAL : public Reducer { ...@@ -40,6 +43,7 @@ class SimplifiedOperatorReducer FINAL : public Reducer {
Graph* graph() const; Graph* graph() const;
Factory* factory() const; Factory* factory() const;
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
CommonOperatorBuilder* common() const;
MachineOperatorBuilder* machine() const; MachineOperatorBuilder* machine() const;
SimplifiedOperatorBuilder* simplified() { return &simplified_; } SimplifiedOperatorBuilder* simplified() { return &simplified_; }
......
...@@ -158,6 +158,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) { ...@@ -158,6 +158,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
#define PURE_OP_LIST(V) \ #define PURE_OP_LIST(V) \
V(AnyToBoolean, Operator::kNoProperties, 1) \
V(BooleanNot, Operator::kNoProperties, 1) \ V(BooleanNot, Operator::kNoProperties, 1) \
V(BooleanToNumber, Operator::kNoProperties, 1) \ V(BooleanToNumber, Operator::kNoProperties, 1) \
V(NumberEqual, Operator::kCommutative, 2) \ V(NumberEqual, Operator::kCommutative, 2) \
......
...@@ -128,6 +128,8 @@ class SimplifiedOperatorBuilder FINAL { ...@@ -128,6 +128,8 @@ class SimplifiedOperatorBuilder FINAL {
public: public:
explicit SimplifiedOperatorBuilder(Zone* zone); explicit SimplifiedOperatorBuilder(Zone* zone);
const Operator* AnyToBoolean();
const Operator* BooleanNot(); const Operator* BooleanNot();
const Operator* BooleanToNumber(); const Operator* BooleanToNumber();
......
...@@ -1441,6 +1441,11 @@ Bounds Typer::Visitor::TypeJSDebugger(Node* node) { ...@@ -1441,6 +1441,11 @@ Bounds Typer::Visitor::TypeJSDebugger(Node* node) {
// Simplified operators. // Simplified operators.
Bounds Typer::Visitor::TypeAnyToBoolean(Node* node) {
return TypeUnaryOp(node, ToBoolean);
}
Bounds Typer::Visitor::TypeBooleanNot(Node* node) { Bounds Typer::Visitor::TypeBooleanNot(Node* node) {
return Bounds(Type::None(zone()), Type::Boolean(zone())); return Bounds(Type::None(zone()), Type::Boolean(zone()));
} }
......
...@@ -482,6 +482,10 @@ void Verifier::Visitor::Pre(Node* node) { ...@@ -482,6 +482,10 @@ void Verifier::Visitor::Pre(Node* node) {
// Simplified operators // Simplified operators
// ------------------------------- // -------------------------------
case IrOpcode::kAnyToBoolean:
// Type is Boolean.
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kBooleanNot: case IrOpcode::kBooleanNot:
// Boolean -> Boolean // Boolean -> Boolean
CheckValueInputIs(node, 0, Type::Boolean()); CheckValueInputIs(node, 0, Type::Boolean());
......
...@@ -507,24 +507,6 @@ TEST(JSToBoolean) { ...@@ -507,24 +507,6 @@ TEST(JSToBoolean) {
CHECK_EQ(IrOpcode::kParameter, r->opcode()); 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) { // ToBoolean(object)
Node* r = R.ReduceUnop(op, Type::DetectableObject()); Node* r = R.ReduceUnop(op, Type::DetectableObject());
R.CheckTrue(r); R.CheckTrue(r);
...@@ -537,30 +519,7 @@ TEST(JSToBoolean) { ...@@ -537,30 +519,7 @@ TEST(JSToBoolean) {
{ // ToBoolean(object) { // ToBoolean(object)
Node* r = R.ReduceUnop(op, Type::Object()); Node* r = R.ReduceUnop(op, Type::Object());
CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode()); CHECK_EQ(IrOpcode::kAnyToBoolean, 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());
}
} }
} }
......
...@@ -39,7 +39,7 @@ class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> { ...@@ -39,7 +39,7 @@ class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> {
typer(this->graph(), MaybeHandle<Context>()), typer(this->graph(), MaybeHandle<Context>()),
javascript(this->zone()), javascript(this->zone()),
jsgraph(this->graph(), this->common(), &javascript, this->machine()), jsgraph(this->graph(), this->common(), &javascript, this->machine()),
lowering(&jsgraph) {} lowering(&jsgraph, this->zone()) {}
Typer typer; Typer typer;
JSOperatorBuilder javascript; JSOperatorBuilder javascript;
...@@ -698,9 +698,7 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders { ...@@ -698,9 +698,7 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
CHECK_EQ(expected, node->opcode()); CHECK_EQ(expected, node->opcode());
} }
void Lower() { void Lower() { SimplifiedLowering(&jsgraph, jsgraph.zone()).LowerAllNodes(); }
SimplifiedLowering(&jsgraph).LowerAllNodes();
}
// Inserts the node as the return value of the graph. // Inserts the node as the return value of the graph.
Node* Return(Node* node) { Node* Return(Node* node) {
...@@ -789,6 +787,46 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders { ...@@ -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) { TEST(LowerBooleanNot_bit_bit) {
// BooleanNot(x: kRepBit) used as kRepBit // BooleanNot(x: kRepBit) used as kRepBit
TestingGraph t(Type::Boolean()); TestingGraph t(Type::Boolean());
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "src/compiler/js-typed-lowering.h" #include "src/compiler/js-typed-lowering.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties-inl.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/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h" #include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h" #include "test/unittests/compiler/node-test-utils.h"
...@@ -73,125 +72,165 @@ class JSTypedLoweringTest : public TypedGraphTest { ...@@ -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 = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_EQ(input, r.replacement()); EXPECT_THAT(r.replacement(), IsBooleanNot(input));
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithUndefined) { TEST_F(JSTypedLoweringTest, JSUnaryNotWithFalsish) {
Node* input = Parameter(Type::Undefined()); Handle<Object> zero = factory()->NewNumber(0);
Node* context = UndefinedConstant(); 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 = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant()); EXPECT_THAT(r.replacement(), IsFalseConstant());
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithNull) { TEST_F(JSTypedLoweringTest, JSUnaryNotWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Null()); Node* input = Parameter(
Node* context = UndefinedConstant(); Type::Range(factory()->NewNumber(1), factory()->NewNumber(42), zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant()); EXPECT_THAT(r.replacement(), IsFalseConstant());
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithDetectableReceiver) { TEST_F(JSTypedLoweringTest, JSUnaryNotWithAny) {
Node* input = Parameter(Type::DetectableReceiver()); Node* input = Parameter(Type::Any(), 0);
Node* context = UndefinedConstant(); Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed()); 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()); // JSToBoolean
Node* context = UndefinedConstant();
TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant()); EXPECT_EQ(input, r.replacement());
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) { TEST_F(JSTypedLoweringTest, JSToBooleanWithFalsish) {
Node* input = Parameter(Type::OrderedNumber()); Handle<Object> zero = factory()->NewNumber(0);
Node* context = UndefinedConstant(); 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 = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), EXPECT_THAT(r.replacement(), IsFalseConstant());
IsBooleanNot(IsNumberEqual(input, IsNumberConstant(BitEq(0.0)))));
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithString) { TEST_F(JSTypedLoweringTest, JSToBooleanWithTruish) {
Node* input = Parameter(Type::String()); Node* input = Parameter(
Node* context = UndefinedConstant(); 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 = Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context)); Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), EXPECT_THAT(r.replacement(), IsTrueConstant());
IsBooleanNot(IsNumberEqual(
IsLoadField(AccessBuilder::ForStringLength(), input,
graph()->start(), graph()->start()),
IsNumberConstant(BitEq(0.0)))));
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithPhi) { TEST_F(JSTypedLoweringTest, JSToBooleanWithNonZeroPlainNumber) {
Node* p0 = Parameter(Type::OrderedNumber(), 0); Node* input =
Node* p1 = Parameter(Type::Boolean(), 1); Parameter(Type::Range(factory()->NewNumber(1),
Node* context = UndefinedConstant(); factory()->NewNumber(V8_INFINITY), zone()),
Node* control = graph()->start(); 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode( Reduction r =
javascript()->ToBoolean(), Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
graph()->NewNode(common()->Phi(kMachAnyTagged, 2), p0, p1, control),
context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), EXPECT_THAT(r.replacement(), IsTrueConstant());
IsPhi(kMachAnyTagged, IsBooleanNot(IsNumberEqual(
p0, IsNumberConstant(BitEq(0.0)))),
p1, control));
} }
TEST_F(JSTypedLoweringTest, JSToBooleanWithSelect) { TEST_F(JSTypedLoweringTest, JSToBooleanWithAny) {
Node* p0 = Parameter(Type::Boolean(), 0); Node* input = Parameter(Type::Any(), 0);
Node* p1 = Parameter(Type::DetectableReceiver(), 1); Node* context = Parameter(Type::Any(), 1);
Node* p2 = Parameter(Type::OrderedNumber(), 2); Reduction r =
Node* context = UndefinedConstant(); Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(),
graph()->NewNode(common()->Select(kMachAnyTagged, BranchHint::kTrue), p0,
p1, p2),
context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT( EXPECT_THAT(r.replacement(), IsAnyToBoolean(input));
r.replacement(),
IsSelect(kMachAnyTagged, p0, IsTrueConstant(),
IsBooleanNot(IsNumberEqual(p2, IsNumberConstant(BitEq(0.0))))));
} }
......
...@@ -1290,6 +1290,7 @@ IS_BINOP_MATCHER(Float64Sub) ...@@ -1290,6 +1290,7 @@ IS_BINOP_MATCHER(Float64Sub)
Matcher<Node*> Is##Name(const Matcher<Node*>& input_matcher) { \ Matcher<Node*> Is##Name(const Matcher<Node*>& input_matcher) { \
return MakeMatcher(new IsUnopMatcher(IrOpcode::k##Name, input_matcher)); \ return MakeMatcher(new IsUnopMatcher(IrOpcode::k##Name, input_matcher)); \
} }
IS_UNOP_MATCHER(AnyToBoolean)
IS_UNOP_MATCHER(BooleanNot) IS_UNOP_MATCHER(BooleanNot)
IS_UNOP_MATCHER(ChangeFloat64ToInt32) IS_UNOP_MATCHER(ChangeFloat64ToInt32)
IS_UNOP_MATCHER(ChangeFloat64ToUint32) IS_UNOP_MATCHER(ChangeFloat64ToUint32)
......
...@@ -75,6 +75,7 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher, ...@@ -75,6 +75,7 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
const Matcher<Node*>& effect_matcher, const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher); const Matcher<Node*>& control_matcher);
Matcher<Node*> IsAnyToBoolean(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsBooleanNot(const Matcher<Node*>& value_matcher); Matcher<Node*> IsBooleanNot(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsNumberEqual(const Matcher<Node*>& lhs_matcher, Matcher<Node*> IsNumberEqual(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher); const Matcher<Node*>& rhs_matcher);
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.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.h"
#include "src/compiler/simplified-operator-reducer.h" #include "src/compiler/simplified-operator-reducer.h"
#include "src/conversions.h" #include "src/conversions.h"
...@@ -18,10 +20,10 @@ namespace v8 { ...@@ -18,10 +20,10 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
class SimplifiedOperatorReducerTest : public GraphTest { class SimplifiedOperatorReducerTest : public TypedGraphTest {
public: public:
explicit SimplifiedOperatorReducerTest(int num_parameters = 1) explicit SimplifiedOperatorReducerTest(int num_parameters = 1)
: GraphTest(num_parameters), simplified_(zone()) {} : TypedGraphTest(num_parameters), simplified_(zone()) {}
~SimplifiedOperatorReducerTest() OVERRIDE {} ~SimplifiedOperatorReducerTest() OVERRIDE {}
protected: protected:
...@@ -139,6 +141,7 @@ std::ostream& operator<<(std::ostream& os, const UnaryOperator& unop) { ...@@ -139,6 +141,7 @@ std::ostream& operator<<(std::ostream& os, const UnaryOperator& unop) {
static const UnaryOperator kUnaryOperators[] = { static const UnaryOperator kUnaryOperators[] = {
{&SimplifiedOperatorBuilder::AnyToBoolean, "AnyToBoolean"},
{&SimplifiedOperatorBuilder::BooleanNot, "BooleanNot"}, {&SimplifiedOperatorBuilder::BooleanNot, "BooleanNot"},
{&SimplifiedOperatorBuilder::ChangeBitToBool, "ChangeBitToBool"}, {&SimplifiedOperatorBuilder::ChangeBitToBool, "ChangeBitToBool"},
{&SimplifiedOperatorBuilder::ChangeBoolToBit, "ChangeBoolToBit"}, {&SimplifiedOperatorBuilder::ChangeBoolToBit, "ChangeBoolToBit"},
...@@ -160,8 +163,8 @@ typedef SimplifiedOperatorReducerTestWithParam<UnaryOperator> ...@@ -160,8 +163,8 @@ typedef SimplifiedOperatorReducerTestWithParam<UnaryOperator>
TEST_P(SimplifiedUnaryOperatorTest, Parameter) { TEST_P(SimplifiedUnaryOperatorTest, Parameter) {
const UnaryOperator& unop = GetParam(); const UnaryOperator& unop = GetParam();
Reduction reduction = Reduce( Reduction reduction = Reduce(graph()->NewNode(
graph()->NewNode((simplified()->*unop.constructor)(), Parameter(0))); (simplified()->*unop.constructor)(), Parameter(Type::Any())));
EXPECT_FALSE(reduction.Changed()); EXPECT_FALSE(reduction.Changed());
} }
...@@ -171,6 +174,39 @@ INSTANTIATE_TEST_CASE_P(SimplifiedOperatorReducerTest, ...@@ -171,6 +174,39 @@ INSTANTIATE_TEST_CASE_P(SimplifiedOperatorReducerTest,
::testing::ValuesIn(kUnaryOperators)); ::testing::ValuesIn(kUnaryOperators));
// -----------------------------------------------------------------------------
// 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 // BooleanNot
......
...@@ -38,6 +38,7 @@ const PureOperator kPureOperators[] = { ...@@ -38,6 +38,7 @@ const PureOperator kPureOperators[] = {
&SimplifiedOperatorBuilder::Name, IrOpcode::k##Name, \ &SimplifiedOperatorBuilder::Name, IrOpcode::k##Name, \
Operator::kPure | properties, input_count \ Operator::kPure | properties, input_count \
} }
PURE(AnyToBoolean, Operator::kNoProperties, 1),
PURE(BooleanNot, Operator::kNoProperties, 1), PURE(BooleanNot, Operator::kNoProperties, 1),
PURE(BooleanToNumber, Operator::kNoProperties, 1), PURE(BooleanToNumber, Operator::kNoProperties, 1),
PURE(NumberEqual, Operator::kCommutative, 2), PURE(NumberEqual, Operator::kCommutative, 2),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment