[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}
This commit is contained in:
Benedikt Meurer 2015-01-05 14:44:13 +01:00
parent 17a180842e
commit 9def087efc
19 changed files with 329 additions and 279 deletions

View 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
// JSUnaryNot(x:boolean,context) => BooleanNot(x)
node->set_op(simplified()->BooleanNot());
node->TrimInputCount(1);
return Changed(node);
}
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);
}
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);
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);
}
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) {
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);

View 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);

View File

@ -132,6 +132,7 @@
// Opcodes for VirtuaMachine-level operators.
#define SIMPLIFIED_OP_LIST(V) \
V(AnyToBoolean) \
V(BooleanNot) \
V(BooleanToNumber) \
V(NumberEqual) \

View 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());

View 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);
}

View 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);

View 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();
}

View 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_; }

View 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) \

View File

@ -128,6 +128,8 @@ class SimplifiedOperatorBuilder FINAL {
public:
explicit SimplifiedOperatorBuilder(Zone* zone);
const Operator* AnyToBoolean();
const Operator* BooleanNot();
const Operator* BooleanToNumber();

View 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()));
}

View 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());

View 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());
}
}

View 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());

View 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"
@ -72,14 +71,94 @@ class JSTypedLoweringTest : public TypedGraphTest {
};
// -----------------------------------------------------------------------------
// JSUnaryNot
TEST_F(JSTypedLoweringTest, JSUnaryNotWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(input));
}
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()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
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()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(JSTypedLoweringTest, JSUnaryNotWithAny) {
Node* input = Parameter(Type::Any(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsAnyToBoolean(input)));
}
// -----------------------------------------------------------------------------
// JSToBoolean
TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean());
Node* context = UndefinedConstant();
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());
@ -87,10 +166,29 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithUndefined) {
Node* input = Parameter(Type::Undefined());
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());
@ -98,21 +196,14 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithUndefined) {
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithNull) {
Node* input = Parameter(Type::Null());
Node* context = UndefinedConstant();
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), 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, 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());
@ -120,78 +211,26 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithDetectableReceiver) {
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithUndetectable) {
Node* input = Parameter(Type::Undetectable());
Node* context = UndefinedConstant();
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(), IsFalseConstant());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) {
Node* input = Parameter(Type::OrderedNumber());
Node* context = UndefinedConstant();
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(),
IsBooleanNot(IsNumberEqual(input, IsNumberConstant(BitEq(0.0)))));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithString) {
Node* input = Parameter(Type::String());
Node* context = UndefinedConstant();
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)))));
}
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));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsPhi(kMachAnyTagged, IsBooleanNot(IsNumberEqual(
p0, IsNumberConstant(BitEq(0.0)))),
p1, control));
}
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));
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));
}

View 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)

View 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);

View 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());
}
@ -171,6 +174,39 @@ INSTANTIATE_TEST_CASE_P(SimplifiedOperatorReducerTest,
::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

View 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),