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() { ...@@ -2297,7 +2297,7 @@ void BytecodeGraphBuilder::VisitLogicalNot() {
} }
void BytecodeGraphBuilder::VisitToBooleanLogicalNot() { void BytecodeGraphBuilder::VisitToBooleanLogicalNot() {
Node* value = NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), Node* value = NewNode(simplified()->ToBoolean(ToBooleanHint::kAny),
environment()->LookupAccumulator()); environment()->LookupAccumulator());
Node* node = NewNode(simplified()->BooleanNot(), value); Node* node = NewNode(simplified()->BooleanNot(), value);
environment()->BindAccumulator(node); environment()->BindAccumulator(node);
...@@ -2952,14 +2952,14 @@ void BytecodeGraphBuilder::BuildJumpIfTrue() { ...@@ -2952,14 +2952,14 @@ void BytecodeGraphBuilder::BuildJumpIfTrue() {
void BytecodeGraphBuilder::BuildJumpIfToBooleanTrue() { void BytecodeGraphBuilder::BuildJumpIfToBooleanTrue() {
Node* accumulator = environment()->LookupAccumulator(); Node* accumulator = environment()->LookupAccumulator();
Node* condition = Node* condition =
NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator); NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), accumulator);
BuildJumpIf(condition); BuildJumpIf(condition);
} }
void BytecodeGraphBuilder::BuildJumpIfToBooleanFalse() { void BytecodeGraphBuilder::BuildJumpIfToBooleanFalse() {
Node* accumulator = environment()->LookupAccumulator(); Node* accumulator = environment()->LookupAccumulator();
Node* condition = Node* condition =
NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator); NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), accumulator);
BuildJumpIfNot(condition); BuildJumpIfNot(condition);
} }
......
...@@ -828,6 +828,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -828,6 +828,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kArgumentsLength: case IrOpcode::kArgumentsLength:
result = LowerArgumentsLength(node); result = LowerArgumentsLength(node);
break; break;
case IrOpcode::kToBoolean:
result = LowerToBoolean(node);
break;
case IrOpcode::kTypeOf: case IrOpcode::kTypeOf:
result = LowerTypeOf(node); result = LowerTypeOf(node);
break; break;
...@@ -2271,6 +2274,18 @@ Node* EffectControlLinearizer::LowerTypeOf(Node* node) { ...@@ -2271,6 +2274,18 @@ Node* EffectControlLinearizer::LowerTypeOf(Node* node) {
__ NoContextConstant()); __ 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* EffectControlLinearizer::LowerArgumentsLength(Node* node) {
Node* arguments_frame = NodeProperties::GetValueInput(node, 0); Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
int formal_parameter_count = FormalParameterCountOf(node->op()); int formal_parameter_count = FormalParameterCountOf(node->op());
......
...@@ -118,6 +118,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -118,6 +118,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerCheckNotTaggedHole(Node* node, Node* frame_state); Node* LowerCheckNotTaggedHole(Node* node, Node* frame_state);
Node* LowerConvertTaggedHoleToUndefined(Node* node); Node* LowerConvertTaggedHoleToUndefined(Node* node);
Node* LowerTypeOf(Node* node); Node* LowerTypeOf(Node* node);
Node* LowerToBoolean(Node* node);
Node* LowerPlainPrimitiveToNumber(Node* node); Node* LowerPlainPrimitiveToNumber(Node* node);
Node* LowerPlainPrimitiveToWord32(Node* node); Node* LowerPlainPrimitiveToWord32(Node* node);
Node* LowerPlainPrimitiveToFloat64(Node* node); Node* LowerPlainPrimitiveToFloat64(Node* node);
......
...@@ -57,7 +57,7 @@ bool CanBePrimitive(Node* node) { ...@@ -57,7 +57,7 @@ bool CanBePrimitive(Node* node) {
bool CanBeNullOrUndefined(Node* node) { bool CanBeNullOrUndefined(Node* node) {
if (CanBePrimitive(node)) { if (CanBePrimitive(node)) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kJSToBoolean: case IrOpcode::kToBoolean:
case IrOpcode::kJSToInteger: case IrOpcode::kJSToInteger:
case IrOpcode::kJSToLength: case IrOpcode::kJSToLength:
case IrOpcode::kJSToName: case IrOpcode::kJSToName:
...@@ -137,13 +137,11 @@ Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) { ...@@ -137,13 +137,11 @@ Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op()); 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()); DCHECK_LE(2u, p.arity());
Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant() Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
: NodeProperties::GetValueInput(node, 2); : NodeProperties::GetValueInput(node, 2);
Node* context = NodeProperties::GetContextInput(node); value = graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), value);
value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), value,
context);
ReplaceWithValue(node, value); ReplaceWithValue(node, value);
return Replace(value); return Replace(value);
} }
...@@ -1321,8 +1319,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function, ...@@ -1321,8 +1319,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
// We have to coerce callback_value to boolean, and only store the element in // 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 // a if it's true. The checkpoint above protects against the case that
// growing {a} fails. // growing {a} fails.
to = DoFilterPostCallbackWork(kind, context, &control, &effect, a, to, to = DoFilterPostCallbackWork(kind, &control, &effect, a, to, element,
element, callback_value); callback_value);
k = next_k; k = next_k;
loop->ReplaceInput(1, control); loop->ReplaceInput(1, control);
...@@ -1345,12 +1343,12 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function, ...@@ -1345,12 +1343,12 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
return Replace(a); return Replace(a);
} }
Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node* context, Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
Node** control, Node** effect, Node** effect, Node* a, Node* to,
Node* a, Node* to, Node* element, Node* element,
Node* callback_value) { Node* callback_value) {
Node* boolean_result = graph()->NewNode( Node* boolean_result = graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), callback_value, context); simplified()->ToBoolean(ToBooleanHint::kAny), callback_value);
Node* check_boolean_result = Node* check_boolean_result =
graph()->NewNode(simplified()->ReferenceEqual(), boolean_result, graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
......
...@@ -89,9 +89,9 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -89,9 +89,9 @@ class JSCallReducer final : public AdvancedReducer {
// Returns the updated {to} node, and updates control and effect along the // Returns the updated {to} node, and updates control and effect along the
// way. // way.
Node* DoFilterPostCallbackWork(ElementsKind kind, Node* context, Node* DoFilterPostCallbackWork(ElementsKind kind, Node** control,
Node** control, Node** effect, Node* a, Node** effect, Node* a, Node* to,
Node* to, Node* element, Node* callback_value); Node* element, Node* callback_value);
// If {fncallback} is not callable, throw a TypeError. // If {fncallback} is not callable, throw a TypeError.
// {control} is altered, and new nodes {check_fail} and {check_throw} are // {control} is altered, and new nodes {check_fail} and {check_throw} are
......
...@@ -126,15 +126,6 @@ void JSGenericLowering::LowerJSStrictEqual(Node* node) { ...@@ -126,15 +126,6 @@ void JSGenericLowering::LowerJSStrictEqual(Node* node) {
Operator::kEliminatable); 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) { void JSGenericLowering::LowerJSClassOf(Node* node) {
// The %_ClassOf intrinsic doesn't need the current context. // The %_ClassOf intrinsic doesn't need the current context.
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant()); NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
......
// Copyright 2014 the V8 project authors. All rights reserved. // Copyright 2014 the V8 project authors. All rights reserved.
// 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.
#ifndef V8_COMPILER_JS_GENERIC_LOWERING_H_ #ifndef V8_COMPILER_JS_GENERIC_LOWERING_H_
#define V8_COMPILER_JS_GENERIC_LOWERING_H_ #define V8_COMPILER_JS_GENERIC_LOWERING_H_
......
...@@ -252,8 +252,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -252,8 +252,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
ConvertReceiverMode::kNotNullOrUndefined)); ConvertReceiverMode::kNotNullOrUndefined));
// Rewire the value uses of {node} to ToBoolean conversion of the result. // Rewire the value uses of {node} to ToBoolean conversion of the result.
Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), Node* value =
node, context); graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), node);
for (Edge edge : node->use_edges()) { for (Edge edge : node->use_edges()) {
if (NodeProperties::IsValueEdge(edge) && edge.from() != value) { if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
edge.UpdateTo(value); edge.UpdateTo(value);
......
...@@ -58,11 +58,6 @@ ConvertReceiverMode ConvertReceiverModeOf(Operator const* op) { ...@@ -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, std::ostream& operator<<(std::ostream& os,
ConstructForwardVarargsParameters const& p) { ConstructForwardVarargsParameters const& p) {
return os << p.arity() << ", " << p.start_index(); return os << p.arity() << ", " << p.start_index();
...@@ -757,15 +752,6 @@ const Operator* JSOperatorBuilder::StoreDataPropertyInLiteral( ...@@ -757,15 +752,6 @@ const Operator* JSOperatorBuilder::StoreDataPropertyInLiteral(
parameters); // parameter 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, const Operator* JSOperatorBuilder::CallForwardVarargs(size_t arity,
uint32_t start_index) { uint32_t start_index) {
CallForwardVarargsParameters parameters(arity, start_index); CallForwardVarargsParameters parameters(arity, start_index);
......
...@@ -89,9 +89,6 @@ size_t hash_value(VectorSlotPair const&); ...@@ -89,9 +89,6 @@ size_t hash_value(VectorSlotPair const&);
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op); 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 // Defines the flags for a JavaScript call forwarding parameters. This
// is used as parameter by JSConstructForwardVarargs operators. // is used as parameter by JSConstructForwardVarargs operators.
class ConstructForwardVarargsParameters final { class ConstructForwardVarargsParameters final {
...@@ -659,7 +656,6 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -659,7 +656,6 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* Divide(); const Operator* Divide();
const Operator* Modulus(); const Operator* Modulus();
const Operator* ToBoolean(ToBooleanHints hints);
const Operator* ToInteger(); const Operator* ToInteger();
const Operator* ToLength(); const Operator* ToLength();
const Operator* ToName(); const Operator* ToName();
......
...@@ -855,52 +855,6 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) { ...@@ -855,52 +855,6 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) {
return NoChange(); 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) { Reduction JSTypedLowering::ReduceJSToInteger(Node* node) {
Node* const input = NodeProperties::GetValueInput(node, 0); Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input); Type* const input_type = NodeProperties::GetType(input);
...@@ -2265,8 +2219,6 @@ Reduction JSTypedLowering::Reduce(Node* node) { ...@@ -2265,8 +2219,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSHasInPrototypeChain(node); return ReduceJSHasInPrototypeChain(node);
case IrOpcode::kJSOrdinaryHasInstance: case IrOpcode::kJSOrdinaryHasInstance:
return ReduceJSOrdinaryHasInstance(node); return ReduceJSOrdinaryHasInstance(node);
case IrOpcode::kJSToBoolean:
return ReduceJSToBoolean(node);
case IrOpcode::kJSToInteger: case IrOpcode::kJSToInteger:
return ReduceJSToInteger(node); return ReduceJSToInteger(node);
case IrOpcode::kJSToLength: case IrOpcode::kJSToLength:
......
...@@ -50,7 +50,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final ...@@ -50,7 +50,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSStoreModule(Node* node); Reduction ReduceJSStoreModule(Node* node);
Reduction ReduceJSEqual(Node* node); Reduction ReduceJSEqual(Node* node);
Reduction ReduceJSStrictEqual(Node* node); Reduction ReduceJSStrictEqual(Node* node);
Reduction ReduceJSToBoolean(Node* node);
Reduction ReduceJSToInteger(Node* node); Reduction ReduceJSToInteger(Node* node);
Reduction ReduceJSToLength(Node* node); Reduction ReduceJSToLength(Node* node);
Reduction ReduceJSToName(Node* node); Reduction ReduceJSToName(Node* node);
......
...@@ -116,7 +116,6 @@ ...@@ -116,7 +116,6 @@
V(JSOrdinaryHasInstance) V(JSOrdinaryHasInstance)
#define JS_CONVERSION_UNOP_LIST(V) \ #define JS_CONVERSION_UNOP_LIST(V) \
V(JSToBoolean) \
V(JSToInteger) \ V(JSToInteger) \
V(JSToLength) \ V(JSToLength) \
V(JSToName) \ V(JSToName) \
...@@ -357,6 +356,7 @@ ...@@ -357,6 +356,7 @@
V(TransitionAndStoreElement) \ V(TransitionAndStoreElement) \
V(TransitionAndStoreNumberElement) \ V(TransitionAndStoreNumberElement) \
V(TransitionAndStoreNonNumberElement) \ V(TransitionAndStoreNonNumberElement) \
V(ToBoolean) \
V(ObjectIsArrayBufferView) \ V(ObjectIsArrayBufferView) \
V(ObjectIsCallable) \ V(ObjectIsCallable) \
V(ObjectIsConstructor) \ V(ObjectIsConstructor) \
......
...@@ -1541,10 +1541,9 @@ class RepresentationSelector { ...@@ -1541,10 +1541,9 @@ class RepresentationSelector {
//------------------------------------------------------------------ //------------------------------------------------------------------
// JavaScript operators. // JavaScript operators.
//------------------------------------------------------------------ //------------------------------------------------------------------
case IrOpcode::kJSToBoolean: { case IrOpcode::kToBoolean: {
if (truncation.IsUsedAsBool()) { if (truncation.IsUsedAsBool()) {
ProcessInput(node, 0, UseInfo::Bool()); ProcessInput(node, 0, UseInfo::Bool());
ProcessInput(node, 1, UseInfo::None());
SetOutput(node, MachineRepresentation::kBit); SetOutput(node, MachineRepresentation::kBit);
if (lower()) DeferReplacement(node, node->InputAt(0)); if (lower()) DeferReplacement(node, node->InputAt(0));
} else { } else {
......
...@@ -102,6 +102,10 @@ std::ostream& operator<<(std::ostream& os, ElementAccess const& access) { ...@@ -102,6 +102,10 @@ std::ostream& operator<<(std::ostream& os, ElementAccess const& access) {
return os; return os;
} }
ToBooleanHints ToBooleanHintsOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kToBoolean, op->opcode());
return OpParameter<ToBooleanHints>(op);
}
const FieldAccess& FieldAccessOf(const Operator* op) { const FieldAccess& FieldAccessOf(const Operator* op) {
DCHECK_NOT_NULL(op); DCHECK_NOT_NULL(op);
...@@ -1029,6 +1033,15 @@ const Operator* SimplifiedOperatorBuilder::TransitionElementsKind( ...@@ -1029,6 +1033,15 @@ const Operator* SimplifiedOperatorBuilder::TransitionElementsKind(
transition); // parameter 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 { namespace {
struct ArgumentsLengthParameters { struct ArgumentsLengthParameters {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "src/handles.h" #include "src/handles.h"
#include "src/machine-type.h" #include "src/machine-type.h"
#include "src/objects.h" #include "src/objects.h"
#include "src/type-hints.h"
#include "src/zone/zone-handle-set.h" #include "src/zone/zone-handle-set.h"
namespace v8 { namespace v8 {
...@@ -173,6 +174,9 @@ std::ostream& operator<<(std::ostream&, GrowFastElementsMode); ...@@ -173,6 +174,9 @@ std::ostream& operator<<(std::ostream&, GrowFastElementsMode);
GrowFastElementsMode GrowFastElementsModeOf(const Operator*) WARN_UNUSED_RESULT; 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. // A descriptor for elements kind transitions.
class ElementsTransition final { class ElementsTransition final {
public: public:
...@@ -366,6 +370,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -366,6 +370,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* TypeOf(); const Operator* TypeOf();
const Operator* ToBoolean(ToBooleanHints hints);
const Operator* StringEqual(); const Operator* StringEqual();
const Operator* StringLessThan(); const Operator* StringLessThan();
const Operator* StringLessThanOrEqual(); const Operator* StringLessThanOrEqual();
......
...@@ -103,6 +103,8 @@ Reduction TypedOptimization::Reduce(Node* node) { ...@@ -103,6 +103,8 @@ Reduction TypedOptimization::Reduce(Node* node) {
return ReduceSelect(node); return ReduceSelect(node);
case IrOpcode::kTypeOf: case IrOpcode::kTypeOf:
return ReduceTypeOf(node); return ReduceTypeOf(node);
case IrOpcode::kToBoolean:
return ReduceToBoolean(node);
case IrOpcode::kSpeculativeToNumber: case IrOpcode::kSpeculativeToNumber:
return ReduceSpeculativeToNumber(node); return ReduceSpeculativeToNumber(node);
default: default:
...@@ -389,6 +391,52 @@ Reduction TypedOptimization::ReduceTypeOf(Node* node) { ...@@ -389,6 +391,52 @@ Reduction TypedOptimization::ReduceTypeOf(Node* node) {
return NoChange(); 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(); } Factory* TypedOptimization::factory() const { return isolate()->factory(); }
Graph* TypedOptimization::graph() const { return jsgraph()->graph(); } Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
......
...@@ -51,6 +51,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final ...@@ -51,6 +51,7 @@ class V8_EXPORT_PRIVATE TypedOptimization final
Reduction ReduceSpeculativeToNumber(Node* node); Reduction ReduceSpeculativeToNumber(Node* node);
Reduction ReduceCheckNotTaggedHole(Node* node); Reduction ReduceCheckNotTaggedHole(Node* node);
Reduction ReduceTypeOf(Node* node); Reduction ReduceTypeOf(Node* node);
Reduction ReduceToBoolean(Node* node);
CompilationDependencies* dependencies() const { return dependencies_; } CompilationDependencies* dependencies() const { return dependencies_; }
Factory* factory() const; Factory* factory() const;
......
...@@ -1094,8 +1094,7 @@ Type* Typer::Visitor::TypeTypeOf(Node* node) { ...@@ -1094,8 +1094,7 @@ Type* Typer::Visitor::TypeTypeOf(Node* node) {
// JS conversion operators. // JS conversion operators.
Type* Typer::Visitor::TypeToBoolean(Node* node) {
Type* Typer::Visitor::TypeJSToBoolean(Node* node) {
return TypeUnaryOp(node, ToBoolean); return TypeUnaryOp(node, ToBoolean);
} }
......
...@@ -563,7 +563,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -563,7 +563,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Number()); CheckTypeIs(node, Type::Number());
break; break;
case IrOpcode::kJSToBoolean: case IrOpcode::kToBoolean:
// Type is Boolean. // Type is Boolean.
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
break; break;
......
...@@ -65,77 +65,6 @@ class JSTypedLoweringTest : public TypedGraphTest { ...@@ -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 // JSToName
......
...@@ -62,14 +62,14 @@ const double kIntegerValues[] = {-V8_INFINITY, INT_MIN, -1000.0, -42.0, ...@@ -62,14 +62,14 @@ const double kIntegerValues[] = {-V8_INFINITY, INT_MIN, -1000.0, -42.0,
class TypedOptimizationTest : public TypedGraphTest { class TypedOptimizationTest : public TypedGraphTest {
public: public:
TypedOptimizationTest() TypedOptimizationTest()
: TypedGraphTest(3), javascript_(zone()), deps_(isolate(), zone()) {} : TypedGraphTest(3), simplified_(zone()), deps_(isolate(), zone()) {}
~TypedOptimizationTest() override {} ~TypedOptimizationTest() override {}
protected: protected:
Reduction Reduce(Node* node) { Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone()); MachineOperatorBuilder machine(zone());
SimplifiedOperatorBuilder simplified(zone()); JSOperatorBuilder javascript(zone());
JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified, JSGraph jsgraph(isolate(), graph(), common(), &javascript, simplified(),
&machine); &machine);
// TODO(titzer): mock the GraphReducer here for better unit testing. // TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph()); GraphReducer graph_reducer(zone(), graph());
...@@ -77,10 +77,10 @@ class TypedOptimizationTest : public TypedGraphTest { ...@@ -77,10 +77,10 @@ class TypedOptimizationTest : public TypedGraphTest {
return reducer.Reduce(node); return reducer.Reduce(node);
} }
JSOperatorBuilder* javascript() { return &javascript_; } SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private: private:
JSOperatorBuilder javascript_; SimplifiedOperatorBuilder simplified_;
CompilationDependencies deps_; CompilationDependencies deps_;
}; };
...@@ -169,7 +169,10 @@ TEST_F(TypedOptimizationTest, ParameterWithUndefined) { ...@@ -169,7 +169,10 @@ TEST_F(TypedOptimizationTest, ParameterWithUndefined) {
} }
} }
TEST_F(TypedOptimizationTest, JSToBooleanWithFalsish) { // -----------------------------------------------------------------------------
// ToBoolean
TEST_F(TypedOptimizationTest, ToBooleanWithFalsish) {
Node* input = Parameter( Node* input = Parameter(
Type::Union( Type::Union(
Type::MinusZero(), Type::MinusZero(),
...@@ -190,36 +193,92 @@ TEST_F(TypedOptimizationTest, JSToBooleanWithFalsish) { ...@@ -190,36 +193,92 @@ TEST_F(TypedOptimizationTest, JSToBooleanWithFalsish) {
zone()), zone()),
zone()), zone()),
0); 0);
Node* context = Parameter(Type::Any(), 1); Reduction r = Reduce(
Reduction r = Reduce(graph()->NewNode( graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant()); EXPECT_THAT(r.replacement(), IsFalseConstant());
} }
TEST_F(TypedOptimizationTest, JSToBooleanWithTruish) { TEST_F(TypedOptimizationTest, ToBooleanWithTruish) {
Node* input = Parameter( Node* input = Parameter(
Type::Union( Type::Union(
Type::NewConstant(factory()->true_value(), zone()), Type::NewConstant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()), Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()), zone()),
0); 0);
Node* context = Parameter(Type::Any(), 1); Reduction r = Reduce(
Reduction r = Reduce(graph()->NewNode( graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant()); EXPECT_THAT(r.replacement(), IsTrueConstant());
} }
TEST_F(TypedOptimizationTest, JSToBooleanWithNonZeroPlainNumber) { TEST_F(TypedOptimizationTest, ToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0); Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Node* context = Parameter(Type::Any(), 1); Reduction r = Reduce(
Reduction r = Reduce(graph()->NewNode( graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), input));
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant()); 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 typed_optimization_unittest
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
......
...@@ -433,14 +433,6 @@ TEST_MONOTONICITY(ToString) ...@@ -433,14 +433,6 @@ TEST_MONOTONICITY(ToString)
TEST_MONOTONICITY(ClassOf) TEST_MONOTONICITY(ClassOf)
#undef TEST_MONOTONICITY #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 // JS BINOPs with CompareOperationHint
#define TEST_MONOTONICITY(name) \ #define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \ TEST_F(TyperTest, Monotonicity_##name) { \
...@@ -498,6 +490,14 @@ TEST_MONOTONICITY(ObjectIsUndetectable) ...@@ -498,6 +490,14 @@ TEST_MONOTONICITY(ObjectIsUndetectable)
TEST_MONOTONICITY(TypeOf) TEST_MONOTONICITY(TypeOf)
#undef TEST_MONOTONICITY #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 // SIMPLIFIED BINOPs without hint, with Number input restriction
#define TEST_MONOTONICITY(name) \ #define TEST_MONOTONICITY(name) \
TEST_F(TyperTest, 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