Commit 78fc6668 authored by Mike Stanton's avatar Mike Stanton Committed by Commit Bot

[Turbofan] Model JSToBoolean as a simplified operator

Because the toboolean operator may lower to a builtin call (which is
effectful in turbofan parlance after effect control linearization),
it really should be encoded as a simplified operator, which can
be optimized with respect for the effect chain in linearization.

No new functionality here, rather a furniture rearrangement in
the TurboFan node structure.

Bug: v8:6929
Change-Id: I371fd22941397d5c28d13bded2738161d8da8275
Reviewed-on: https://chromium-review.googlesource.com/725721Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48727}
parent 25b78853
......@@ -2297,7 +2297,7 @@ void BytecodeGraphBuilder::VisitLogicalNot() {
}
void BytecodeGraphBuilder::VisitToBooleanLogicalNot() {
Node* value = NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
Node* value = NewNode(simplified()->ToBoolean(ToBooleanHint::kAny),
environment()->LookupAccumulator());
Node* node = NewNode(simplified()->BooleanNot(), value);
environment()->BindAccumulator(node);
......@@ -2952,14 +2952,14 @@ void BytecodeGraphBuilder::BuildJumpIfTrue() {
void BytecodeGraphBuilder::BuildJumpIfToBooleanTrue() {
Node* accumulator = environment()->LookupAccumulator();
Node* condition =
NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator);
NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), accumulator);
BuildJumpIf(condition);
}
void BytecodeGraphBuilder::BuildJumpIfToBooleanFalse() {
Node* accumulator = environment()->LookupAccumulator();
Node* condition =
NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator);
NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), accumulator);
BuildJumpIfNot(condition);
}
......
......@@ -828,6 +828,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kArgumentsLength:
result = LowerArgumentsLength(node);
break;
case IrOpcode::kToBoolean:
result = LowerToBoolean(node);
break;
case IrOpcode::kTypeOf:
result = LowerTypeOf(node);
break;
......@@ -2271,6 +2274,18 @@ Node* EffectControlLinearizer::LowerTypeOf(Node* node) {
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerToBoolean(Node* node) {
Node* obj = node->InputAt(0);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kToBoolean);
Operator::Properties const properties = Operator::kEliminatable;
CallDescriptor::Flags const flags = CallDescriptor::kNoAllocate;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), obj,
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerArgumentsLength(Node* node) {
Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
int formal_parameter_count = FormalParameterCountOf(node->op());
......
......@@ -118,6 +118,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerCheckNotTaggedHole(Node* node, Node* frame_state);
Node* LowerConvertTaggedHoleToUndefined(Node* node);
Node* LowerTypeOf(Node* node);
Node* LowerToBoolean(Node* node);
Node* LowerPlainPrimitiveToNumber(Node* node);
Node* LowerPlainPrimitiveToWord32(Node* node);
Node* LowerPlainPrimitiveToFloat64(Node* node);
......
......@@ -57,7 +57,7 @@ bool CanBePrimitive(Node* node) {
bool CanBeNullOrUndefined(Node* node) {
if (CanBePrimitive(node)) {
switch (node->opcode()) {
case IrOpcode::kJSToBoolean:
case IrOpcode::kToBoolean:
case IrOpcode::kJSToInteger:
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
......@@ -137,13 +137,11 @@ Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
// Replace the {node} with a proper {JSToBoolean} operator.
// Replace the {node} with a proper {ToBoolean} operator.
DCHECK_LE(2u, p.arity());
Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
: NodeProperties::GetValueInput(node, 2);
Node* context = NodeProperties::GetContextInput(node);
value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), value,
context);
value = graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), value);
ReplaceWithValue(node, value);
return Replace(value);
}
......@@ -1321,8 +1319,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
// We have to coerce callback_value to boolean, and only store the element in
// a if it's true. The checkpoint above protects against the case that
// growing {a} fails.
to = DoFilterPostCallbackWork(kind, context, &control, &effect, a, to,
element, callback_value);
to = DoFilterPostCallbackWork(kind, &control, &effect, a, to, element,
callback_value);
k = next_k;
loop->ReplaceInput(1, control);
......@@ -1345,12 +1343,12 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
return Replace(a);
}
Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node* context,
Node** control, Node** effect,
Node* a, Node* to, Node* element,
Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
Node** effect, Node* a, Node* to,
Node* element,
Node* callback_value) {
Node* boolean_result = graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), callback_value, context);
simplified()->ToBoolean(ToBooleanHint::kAny), callback_value);
Node* check_boolean_result =
graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
......
......@@ -89,9 +89,9 @@ class JSCallReducer final : public AdvancedReducer {
// Returns the updated {to} node, and updates control and effect along the
// way.
Node* DoFilterPostCallbackWork(ElementsKind kind, Node* context,
Node** control, Node** effect, Node* a,
Node* to, Node* element, Node* callback_value);
Node* DoFilterPostCallbackWork(ElementsKind kind, Node** control,
Node** effect, Node* a, Node* to,
Node* element, Node* callback_value);
// If {fncallback} is not callable, throw a TypeError.
// {control} is altered, and new nodes {check_fail} and {check_throw} are
......
......@@ -126,15 +126,6 @@ void JSGenericLowering::LowerJSStrictEqual(Node* node) {
Operator::kEliminatable);
}
void JSGenericLowering::LowerJSToBoolean(Node* node) {
// The ToBoolean conversion doesn't need the current context.
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToBoolean);
node->AppendInput(zone(), graph()->start());
ReplaceWithStubCall(node, callable, CallDescriptor::kNoAllocate,
Operator::kEliminatable);
}
void JSGenericLowering::LowerJSClassOf(Node* node) {
// The %_ClassOf intrinsic doesn't need the current context.
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_COMPILER_JS_GENERIC_LOWERING_H_
#define V8_COMPILER_JS_GENERIC_LOWERING_H_
......
......@@ -252,8 +252,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
ConvertReceiverMode::kNotNullOrUndefined));
// Rewire the value uses of {node} to ToBoolean conversion of the result.
Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
node, context);
Node* value =
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), node);
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
edge.UpdateTo(value);
......
......@@ -58,11 +58,6 @@ ConvertReceiverMode ConvertReceiverModeOf(Operator const* op) {
}
ToBooleanHints ToBooleanHintsOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kJSToBoolean, op->opcode());
return OpParameter<ToBooleanHints>(op);
}
std::ostream& operator<<(std::ostream& os,
ConstructForwardVarargsParameters const& p) {
return os << p.arity() << ", " << p.start_index();
......@@ -757,15 +752,6 @@ const Operator* JSOperatorBuilder::StoreDataPropertyInLiteral(
parameters); // parameter
}
const Operator* JSOperatorBuilder::ToBoolean(ToBooleanHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<ToBooleanHints>( //--
IrOpcode::kJSToBoolean, Operator::kPure, // opcode
"JSToBoolean", // name
1, 0, 0, 1, 0, 0, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::CallForwardVarargs(size_t arity,
uint32_t start_index) {
CallForwardVarargsParameters parameters(arity, start_index);
......
......@@ -89,9 +89,6 @@ size_t hash_value(VectorSlotPair const&);
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op);
// The ToBooleanHints are used as parameter by JSToBoolean operators.
ToBooleanHints ToBooleanHintsOf(Operator const* op);
// Defines the flags for a JavaScript call forwarding parameters. This
// is used as parameter by JSConstructForwardVarargs operators.
class ConstructForwardVarargsParameters final {
......@@ -659,7 +656,6 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* Divide();
const Operator* Modulus();
const Operator* ToBoolean(ToBooleanHints hints);
const Operator* ToInteger();
const Operator* ToLength();
const Operator* ToName();
......
......@@ -855,52 +855,6 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) {
return NoChange();
}
Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
Node* const input = node->InputAt(0);
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::Boolean())) {
// JSToBoolean(x:boolean) => x
return Replace(input);
} else if (input_type->Is(Type::OrderedNumber())) {
// JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
jsgraph()->ZeroConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::Number())) {
// JSToBoolean(x:number) => NumberToBoolean(x)
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->NumberToBoolean());
return Changed(node);
} else if (input_type->Is(Type::DetectableReceiverOrNull())) {
// JSToBoolean(x:detectable receiver \/ null)
// => BooleanNot(ReferenceEqual(x,#null))
node->ReplaceInput(0, graph()->NewNode(simplified()->ReferenceEqual(),
input, jsgraph()->NullConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::ReceiverOrNullOrUndefined())) {
// JSToBoolean(x:receiver \/ null \/ undefined)
// => BooleanNot(ObjectIsUndetectable(x))
node->ReplaceInput(
0, graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::String())) {
// JSToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
node->ReplaceInput(0,
graph()->NewNode(simplified()->ReferenceEqual(), input,
jsgraph()->EmptyStringConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSToInteger(Node* node) {
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input);
......@@ -2265,8 +2219,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSHasInPrototypeChain(node);
case IrOpcode::kJSOrdinaryHasInstance:
return ReduceJSOrdinaryHasInstance(node);
case IrOpcode::kJSToBoolean:
return ReduceJSToBoolean(node);
case IrOpcode::kJSToInteger:
return ReduceJSToInteger(node);
case IrOpcode::kJSToLength:
......
......@@ -50,7 +50,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSStoreModule(Node* node);
Reduction ReduceJSEqual(Node* node);
Reduction ReduceJSStrictEqual(Node* node);
Reduction ReduceJSToBoolean(Node* node);
Reduction ReduceJSToInteger(Node* node);
Reduction ReduceJSToLength(Node* node);
Reduction ReduceJSToName(Node* node);
......
......@@ -116,7 +116,6 @@
V(JSOrdinaryHasInstance)
#define JS_CONVERSION_UNOP_LIST(V) \
V(JSToBoolean) \
V(JSToInteger) \
V(JSToLength) \
V(JSToName) \
......@@ -357,6 +356,7 @@
V(TransitionAndStoreElement) \
V(TransitionAndStoreNumberElement) \
V(TransitionAndStoreNonNumberElement) \
V(ToBoolean) \
V(ObjectIsArrayBufferView) \
V(ObjectIsCallable) \
V(ObjectIsConstructor) \
......
......@@ -1541,10 +1541,9 @@ class RepresentationSelector {
//------------------------------------------------------------------
// JavaScript operators.
//------------------------------------------------------------------
case IrOpcode::kJSToBoolean: {
case IrOpcode::kToBoolean: {
if (truncation.IsUsedAsBool()) {
ProcessInput(node, 0, UseInfo::Bool());
ProcessInput(node, 1, UseInfo::None());
SetOutput(node, MachineRepresentation::kBit);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else {
......
......@@ -102,6 +102,10 @@ std::ostream& operator<<(std::ostream& os, ElementAccess const& access) {
return os;
}
ToBooleanHints ToBooleanHintsOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kToBoolean, op->opcode());
return OpParameter<ToBooleanHints>(op);
}
const FieldAccess& FieldAccessOf(const Operator* op) {
DCHECK_NOT_NULL(op);
......@@ -1029,6 +1033,15 @@ const Operator* SimplifiedOperatorBuilder::TransitionElementsKind(
transition); // parameter
}
const Operator* SimplifiedOperatorBuilder::ToBoolean(ToBooleanHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<ToBooleanHints>( //--
IrOpcode::kToBoolean, Operator::kPure, // opcode
"ToBoolean", // name
1, 0, 0, 1, 0, 0, // inputs/outputs
hints); // parameter
}
namespace {
struct ArgumentsLengthParameters {
......
......@@ -15,6 +15,7 @@
#include "src/handles.h"
#include "src/machine-type.h"
#include "src/objects.h"
#include "src/type-hints.h"
#include "src/zone/zone-handle-set.h"
namespace v8 {
......@@ -173,6 +174,9 @@ std::ostream& operator<<(std::ostream&, GrowFastElementsMode);
GrowFastElementsMode GrowFastElementsModeOf(const Operator*) WARN_UNUSED_RESULT;
// The ToBooleanHints are used as parameter by ToBoolean operators.
ToBooleanHints ToBooleanHintsOf(Operator const* op);
// A descriptor for elements kind transitions.
class ElementsTransition final {
public:
......@@ -366,6 +370,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* TypeOf();
const Operator* ToBoolean(ToBooleanHints hints);
const Operator* StringEqual();
const Operator* StringLessThan();
const Operator* StringLessThanOrEqual();
......
......@@ -103,6 +103,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
return ReduceSelect(node);
case IrOpcode::kTypeOf:
return ReduceTypeOf(node);
case IrOpcode::kToBoolean:
return ReduceToBoolean(node);
case IrOpcode::kSpeculativeToNumber:
return ReduceSpeculativeToNumber(node);
default:
......@@ -389,6 +391,52 @@ Reduction TypedOptimization::ReduceTypeOf(Node* node) {
return NoChange();
}
Reduction TypedOptimization::ReduceToBoolean(Node* node) {
Node* const input = node->InputAt(0);
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::Boolean())) {
// ToBoolean(x:boolean) => x
return Replace(input);
} else if (input_type->Is(Type::OrderedNumber())) {
// SToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
jsgraph()->ZeroConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::Number())) {
// ToBoolean(x:number) => NumberToBoolean(x)
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->NumberToBoolean());
return Changed(node);
} else if (input_type->Is(Type::DetectableReceiverOrNull())) {
// ToBoolean(x:detectable receiver \/ null)
// => BooleanNot(ReferenceEqual(x,#null))
node->ReplaceInput(0, graph()->NewNode(simplified()->ReferenceEqual(),
input, jsgraph()->NullConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::ReceiverOrNullOrUndefined())) {
// ToBoolean(x:receiver \/ null \/ undefined)
// => BooleanNot(ObjectIsUndetectable(x))
node->ReplaceInput(
0, graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::String())) {
// ToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
node->ReplaceInput(0,
graph()->NewNode(simplified()->ReferenceEqual(), input,
jsgraph()->EmptyStringConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
}
return NoChange();
}
Factory* TypedOptimization::factory() const { return isolate()->factory(); }
Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
......
......@@ -51,6 +51,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
Reduction ReduceSpeculativeToNumber(Node* node);
Reduction ReduceCheckNotTaggedHole(Node* node);
Reduction ReduceTypeOf(Node* node);
Reduction ReduceToBoolean(Node* node);
CompilationDependencies* dependencies() const { return dependencies_; }
Factory* factory() const;
......
......@@ -1094,8 +1094,7 @@ Type* Typer::Visitor::TypeTypeOf(Node* node) {
// JS conversion operators.
Type* Typer::Visitor::TypeJSToBoolean(Node* node) {
Type* Typer::Visitor::TypeToBoolean(Node* node) {
return TypeUnaryOp(node, ToBoolean);
}
......
......@@ -563,7 +563,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Number());
break;
case IrOpcode::kJSToBoolean:
case IrOpcode::kToBoolean:
// Type is Boolean.
CheckTypeIs(node, Type::Boolean());
break;
......
......@@ -65,77 +65,6 @@ class JSTypedLoweringTest : public TypedGraphTest {
};
// -----------------------------------------------------------------------------
// JSToBoolean
TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(input, r.replacement());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) {
Node* input = Parameter(Type::OrderedNumber(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsNumberEqual(input, IsNumberConstant(0.0))));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithNumber) {
Node* input = Parameter(Type::Number(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberToBoolean(input));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithDetectableReceiverOrNull) {
Node* input = Parameter(Type::DetectableReceiverOrNull(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsReferenceEqual(input, IsNullConstant())));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithReceiverOrNullOrUndefined) {
Node* input = Parameter(Type::ReceiverOrNullOrUndefined(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsObjectIsUndetectable(input)));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithString) {
Node* input = Parameter(Type::String(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsReferenceEqual(
input, IsHeapConstant(factory()->empty_string()))));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithAny) {
Node* input = Parameter(Type::Any(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_FALSE(r.Changed());
}
// -----------------------------------------------------------------------------
// JSToName
......
......@@ -62,14 +62,14 @@ const double kIntegerValues[] = {-V8_INFINITY, INT_MIN, -1000.0, -42.0,
class TypedOptimizationTest : public TypedGraphTest {
public:
TypedOptimizationTest()
: TypedGraphTest(3), javascript_(zone()), deps_(isolate(), zone()) {}
: TypedGraphTest(3), simplified_(zone()), deps_(isolate(), zone()) {}
~TypedOptimizationTest() override {}
protected:
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone());
SimplifiedOperatorBuilder simplified(zone());
JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified,
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, simplified(),
&machine);
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph());
......@@ -77,10 +77,10 @@ class TypedOptimizationTest : public TypedGraphTest {
return reducer.Reduce(node);
}
JSOperatorBuilder* javascript() { return &javascript_; }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private:
JSOperatorBuilder javascript_;
SimplifiedOperatorBuilder simplified_;
CompilationDependencies deps_;
};
......@@ -169,7 +169,10 @@ TEST_F(TypedOptimizationTest, ParameterWithUndefined) {
}
}
TEST_F(TypedOptimizationTest, JSToBooleanWithFalsish) {
// -----------------------------------------------------------------------------
// ToBoolean
TEST_F(TypedOptimizationTest, ToBooleanWithFalsish) {
Node* input = Parameter(
Type::Union(
Type::MinusZero(),
......@@ -190,36 +193,92 @@ TEST_F(TypedOptimizationTest, JSToBooleanWithFalsish) {
zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(TypedOptimizationTest, JSToBooleanWithTruish) {
TEST_F(TypedOptimizationTest, ToBooleanWithTruish) {
Node* input = Parameter(
Type::Union(
Type::NewConstant(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(ToBooleanHint::kAny), input, context));
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(TypedOptimizationTest, JSToBooleanWithNonZeroPlainNumber) {
TEST_F(TypedOptimizationTest, ToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(TypedOptimizationTest, ToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(input, r.replacement());
}
TEST_F(TypedOptimizationTest, ToBooleanWithOrderedNumber) {
Node* input = Parameter(Type::OrderedNumber(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsNumberEqual(input, IsNumberConstant(0.0))));
}
TEST_F(TypedOptimizationTest, ToBooleanWithNumber) {
Node* input = Parameter(Type::Number(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberToBoolean(input));
}
TEST_F(TypedOptimizationTest, ToBooleanWithDetectableReceiverOrNull) {
Node* input = Parameter(Type::DetectableReceiverOrNull(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsReferenceEqual(input, IsNullConstant())));
}
TEST_F(TypedOptimizationTest, ToBooleanWithReceiverOrNullOrUndefined) {
Node* input = Parameter(Type::ReceiverOrNullOrUndefined(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsObjectIsUndetectable(input)));
}
TEST_F(TypedOptimizationTest, ToBooleanWithString) {
Node* input = Parameter(Type::String(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsReferenceEqual(
input, IsHeapConstant(factory()->empty_string()))));
}
TEST_F(TypedOptimizationTest, ToBooleanWithAny) {
Node* input = Parameter(Type::Any(), 0);
Reduction r = Reduce(
graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
ASSERT_FALSE(r.Changed());
}
} // namespace typed_optimization_unittest
} // namespace compiler
} // namespace internal
......
......@@ -433,14 +433,6 @@ TEST_MONOTONICITY(ToString)
TEST_MONOTONICITY(ClassOf)
#undef TEST_MONOTONICITY
// JS UNOPs with ToBooleanHint
#define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestUnaryMonotonicity(javascript_.name(ToBooleanHint())); \
}
TEST_MONOTONICITY(ToBoolean)
#undef TEST_MONOTONICITY
// JS BINOPs with CompareOperationHint
#define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
......@@ -498,6 +490,14 @@ TEST_MONOTONICITY(ObjectIsUndetectable)
TEST_MONOTONICITY(TypeOf)
#undef TEST_MONOTONICITY
// SIMPLIFIED UNOPs with ToBooleanHint
#define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestUnaryMonotonicity(simplified_.name(ToBooleanHint())); \
}
TEST_MONOTONICITY(ToBoolean)
#undef TEST_MONOTONICITY
// SIMPLIFIED BINOPs without hint, with Number input restriction
#define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
......
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