[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/725721 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Commit-Queue: Michael Stanton <mvstanton@chromium.org> Cr-Commit-Position: refs/heads/master@{#48727}
This commit is contained in:
parent
25b78853e2
commit
78fc6668f6
@ -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());
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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) { \
|
||||
|
Loading…
Reference in New Issue
Block a user