// Copyright 2016 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. #include "src/compiler/typed-optimization.h" #include "src/base/optional.h" #include "src/compiler/compilation-dependencies.h" #include "src/compiler/js-graph.h" #include "src/compiler/js-heap-broker.h" #include "src/compiler/node-matchers.h" #include "src/compiler/node-properties.h" #include "src/compiler/simplified-operator.h" #include "src/compiler/type-cache.h" #include "src/isolate-inl.h" namespace v8 { namespace internal { namespace compiler { TypedOptimization::TypedOptimization(Editor* editor, CompilationDependencies* dependencies, JSGraph* jsgraph, JSHeapBroker* js_heap_broker) : AdvancedReducer(editor), dependencies_(dependencies), jsgraph_(jsgraph), js_heap_broker_(js_heap_broker), true_type_(Type::HeapConstant(js_heap_broker, factory()->true_value(), graph()->zone())), false_type_(Type::HeapConstant(js_heap_broker, factory()->false_value(), graph()->zone())), type_cache_(TypeCache::Get()) {} TypedOptimization::~TypedOptimization() = default; Reduction TypedOptimization::Reduce(Node* node) { DisallowHeapAccess no_heap_access; switch (node->opcode()) { case IrOpcode::kConvertReceiver: return ReduceConvertReceiver(node); case IrOpcode::kCheckHeapObject: return ReduceCheckHeapObject(node); case IrOpcode::kCheckNotTaggedHole: return ReduceCheckNotTaggedHole(node); case IrOpcode::kCheckMaps: return ReduceCheckMaps(node); case IrOpcode::kCheckNumber: return ReduceCheckNumber(node); case IrOpcode::kCheckString: return ReduceCheckString(node); case IrOpcode::kCheckEqualsInternalizedString: return ReduceCheckEqualsInternalizedString(node); case IrOpcode::kCheckEqualsSymbol: return ReduceCheckEqualsSymbol(node); case IrOpcode::kLoadField: return ReduceLoadField(node); case IrOpcode::kNumberCeil: case IrOpcode::kNumberRound: case IrOpcode::kNumberTrunc: return ReduceNumberRoundop(node); case IrOpcode::kNumberFloor: return ReduceNumberFloor(node); case IrOpcode::kNumberSilenceNaN: return ReduceNumberSilenceNaN(node); case IrOpcode::kNumberToUint8Clamped: return ReduceNumberToUint8Clamped(node); case IrOpcode::kPhi: return ReducePhi(node); case IrOpcode::kReferenceEqual: return ReduceReferenceEqual(node); case IrOpcode::kStringEqual: case IrOpcode::kStringLessThan: case IrOpcode::kStringLessThanOrEqual: return ReduceStringComparison(node); case IrOpcode::kStringLength: return ReduceStringLength(node); case IrOpcode::kSameValue: return ReduceSameValue(node); case IrOpcode::kSelect: return ReduceSelect(node); case IrOpcode::kTypeOf: return ReduceTypeOf(node); case IrOpcode::kToBoolean: return ReduceToBoolean(node); case IrOpcode::kSpeculativeToNumber: return ReduceSpeculativeToNumber(node); default: break; } return NoChange(); } namespace { base::Optional<MapRef> GetStableMapFromObjectType(JSHeapBroker* js_heap_broker, Type object_type) { if (object_type.IsHeapConstant()) { HeapObjectRef object = object_type.AsHeapConstant()->Ref(); MapRef object_map = object.map(); if (object_map.is_stable()) return object_map; } return {}; } } // namespace Reduction TypedOptimization::ReduceConvertReceiver(Node* node) { Node* const value = NodeProperties::GetValueInput(node, 0); Type const value_type = NodeProperties::GetType(value); Node* const global_proxy = NodeProperties::GetValueInput(node, 1); if (value_type.Is(Type::Receiver())) { ReplaceWithValue(node, value); return Replace(value); } else if (value_type.Is(Type::NullOrUndefined())) { ReplaceWithValue(node, global_proxy); return Replace(global_proxy); } return NoChange(); } Reduction TypedOptimization::ReduceCheckHeapObject(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (!input_type.Maybe(Type::SignedSmall())) { ReplaceWithValue(node, input); return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceCheckNotTaggedHole(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (!input_type.Maybe(Type::Hole())) { ReplaceWithValue(node, input); return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceCheckMaps(Node* node) { // The CheckMaps(o, ...map...) can be eliminated if map is stable, // o has type Constant(object) and map == object->map, and either // (1) map cannot transition further, or // (2) we can add a code dependency on the stability of map // (to guard the Constant type information). Node* const object = NodeProperties::GetValueInput(node, 0); Type const object_type = NodeProperties::GetType(object); Node* const effect = NodeProperties::GetEffectInput(node); base::Optional<MapRef> object_map = GetStableMapFromObjectType(js_heap_broker(), object_type); if (object_map.has_value()) { for (int i = 1; i < node->op()->ValueInputCount(); ++i) { Node* const map = NodeProperties::GetValueInput(node, i); Type const map_type = NodeProperties::GetType(map); if (map_type.IsHeapConstant() && map_type.AsHeapConstant()->Ref().equals(*object_map)) { if (object_map->CanTransition()) { dependencies()->DependOnStableMap(*object_map); } return Replace(effect); } } } return NoChange(); } Reduction TypedOptimization::ReduceCheckNumber(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(Type::Number())) { ReplaceWithValue(node, input); return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceCheckString(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(Type::String())) { ReplaceWithValue(node, input); return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceCheckEqualsInternalizedString(Node* node) { Node* const exp = NodeProperties::GetValueInput(node, 0); Type const exp_type = NodeProperties::GetType(exp); Node* const val = NodeProperties::GetValueInput(node, 1); Type const val_type = NodeProperties::GetType(val); Node* const effect = NodeProperties::GetEffectInput(node); if (val_type.Is(exp_type)) return Replace(effect); // TODO(turbofan): Should we also try to optimize the // non-internalized String case for {val} here? return NoChange(); } Reduction TypedOptimization::ReduceCheckEqualsSymbol(Node* node) { Node* const exp = NodeProperties::GetValueInput(node, 0); Type const exp_type = NodeProperties::GetType(exp); Node* const val = NodeProperties::GetValueInput(node, 1); Type const val_type = NodeProperties::GetType(val); Node* const effect = NodeProperties::GetEffectInput(node); if (val_type.Is(exp_type)) return Replace(effect); return NoChange(); } Reduction TypedOptimization::ReduceLoadField(Node* node) { Node* const object = NodeProperties::GetValueInput(node, 0); Type const object_type = NodeProperties::GetType(object); FieldAccess const& access = FieldAccessOf(node->op()); if (access.base_is_tagged == kTaggedBase && access.offset == HeapObject::kMapOffset) { // We can replace LoadField[Map](o) with map if is stable, and // o has type Constant(object) and map == object->map, and either // (1) map cannot transition further, or // (2) deoptimization is enabled and we can add a code dependency on the // stability of map (to guard the Constant type information). base::Optional<MapRef> object_map = GetStableMapFromObjectType(js_heap_broker(), object_type); if (object_map.has_value()) { dependencies()->DependOnStableMap(*object_map); Node* const value = jsgraph()->Constant(*object_map); ReplaceWithValue(node, value); return Replace(value); } } return NoChange(); } Reduction TypedOptimization::ReduceNumberFloor(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(type_cache_.kIntegerOrMinusZeroOrNaN)) { return Replace(input); } if (input_type.Is(Type::PlainNumber()) && (input->opcode() == IrOpcode::kNumberDivide || input->opcode() == IrOpcode::kSpeculativeNumberDivide)) { Node* const lhs = NodeProperties::GetValueInput(input, 0); Type const lhs_type = NodeProperties::GetType(lhs); Node* const rhs = NodeProperties::GetValueInput(input, 1); Type const rhs_type = NodeProperties::GetType(rhs); if (lhs_type.Is(Type::Unsigned32()) && rhs_type.Is(Type::Unsigned32())) { // We can replace // // NumberFloor(NumberDivide(lhs: unsigned32, // rhs: unsigned32)): plain-number // // with // // NumberToUint32(NumberDivide(lhs, rhs)) // // and just smash the type [0...lhs.Max] on the {node}, // as the truncated result must be loewr than {lhs}'s maximum // value (note that {rhs} cannot be less than 1 due to the // plain-number type constraint on the {node}). NodeProperties::ChangeOp(node, simplified()->NumberToUint32()); NodeProperties::SetType(node, Type::Range(0, lhs_type.Max(), graph()->zone())); return Changed(node); } } return NoChange(); } Reduction TypedOptimization::ReduceNumberRoundop(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(type_cache_.kIntegerOrMinusZeroOrNaN)) { return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceNumberSilenceNaN(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(Type::OrderedNumber())) { return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceNumberToUint8Clamped(Node* node) { Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(type_cache_.kUint8)) { return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReducePhi(Node* node) { // Try to narrow the type of the Phi {node}, which might be more precise now // after lowering based on types, i.e. a SpeculativeNumberAdd has a more // precise type than the JSAdd that was in the graph when the Typer was run. DCHECK_EQ(IrOpcode::kPhi, node->opcode()); int arity = node->op()->ValueInputCount(); Type type = NodeProperties::GetType(node->InputAt(0)); for (int i = 1; i < arity; ++i) { type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)), graph()->zone()); } Type const node_type = NodeProperties::GetType(node); if (!node_type.Is(type)) { type = Type::Intersect(node_type, type, graph()->zone()); NodeProperties::SetType(node, type); return Changed(node); } return NoChange(); } Reduction TypedOptimization::ReduceReferenceEqual(Node* node) { DCHECK_EQ(IrOpcode::kReferenceEqual, node->opcode()); Node* const lhs = NodeProperties::GetValueInput(node, 0); Node* const rhs = NodeProperties::GetValueInput(node, 1); Type const lhs_type = NodeProperties::GetType(lhs); Type const rhs_type = NodeProperties::GetType(rhs); if (!lhs_type.Maybe(rhs_type)) { Node* replacement = jsgraph()->FalseConstant(); // Make sure we do not widen the type. if (NodeProperties::GetType(replacement) .Is(NodeProperties::GetType(node))) { return Replace(jsgraph()->FalseConstant()); } } return NoChange(); } const Operator* TypedOptimization::NumberComparisonFor(const Operator* op) { switch (op->opcode()) { case IrOpcode::kStringEqual: return simplified()->NumberEqual(); case IrOpcode::kStringLessThan: return simplified()->NumberLessThan(); case IrOpcode::kStringLessThanOrEqual: return simplified()->NumberLessThanOrEqual(); default: break; } UNREACHABLE(); } Reduction TypedOptimization:: TryReduceStringComparisonOfStringFromSingleCharCodeToConstant( Node* comparison, const StringRef& string, bool inverted) { switch (comparison->opcode()) { case IrOpcode::kStringEqual: if (string.length() != 1) { // String.fromCharCode(x) always has length 1. return Replace(jsgraph()->BooleanConstant(false)); } break; case IrOpcode::kStringLessThan: V8_FALLTHROUGH; case IrOpcode::kStringLessThanOrEqual: if (string.length() == 0) { // String.fromCharCode(x) <= "" is always false, // "" < String.fromCharCode(x) is always true. return Replace(jsgraph()->BooleanConstant(inverted)); } break; default: UNREACHABLE(); } return NoChange(); } // Try to reduces a string comparison of the form // String.fromCharCode(x) {comparison} {constant} if inverted is false, // and {constant} {comparison} String.fromCharCode(x) if inverted is true. Reduction TypedOptimization::TryReduceStringComparisonOfStringFromSingleCharCode( Node* comparison, Node* from_char_code, Type constant_type, bool inverted) { DCHECK_EQ(IrOpcode::kStringFromSingleCharCode, from_char_code->opcode()); if (!constant_type.IsHeapConstant()) return NoChange(); ObjectRef constant = constant_type.AsHeapConstant()->Ref(); if (!constant.IsString()) return NoChange(); StringRef string = constant.AsString(); // Check if comparison can be resolved statically. Reduction red = TryReduceStringComparisonOfStringFromSingleCharCodeToConstant( comparison, string, inverted); if (red.Changed()) return red; const Operator* comparison_op = NumberComparisonFor(comparison->op()); Node* from_char_code_repl = NodeProperties::GetValueInput(from_char_code, 0); Type from_char_code_repl_type = NodeProperties::GetType(from_char_code_repl); if (!from_char_code_repl_type.Is(type_cache_.kUint16)) { // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}. from_char_code_repl = graph()->NewNode(simplified()->NumberToInt32(), from_char_code_repl); from_char_code_repl = graph()->NewNode( simplified()->NumberBitwiseAnd(), from_char_code_repl, jsgraph()->Constant(std::numeric_limits<uint16_t>::max())); } Node* constant_repl = jsgraph()->Constant(string.GetFirstChar()); Node* number_comparison = nullptr; if (inverted) { // "x..." <= String.fromCharCode(z) is true if x < z. if (string.length() > 1 && comparison->opcode() == IrOpcode::kStringLessThanOrEqual) { comparison_op = simplified()->NumberLessThan(); } number_comparison = graph()->NewNode(comparison_op, constant_repl, from_char_code_repl); } else { // String.fromCharCode(z) < "x..." is true if z <= x. if (string.length() > 1 && comparison->opcode() == IrOpcode::kStringLessThan) { comparison_op = simplified()->NumberLessThanOrEqual(); } number_comparison = graph()->NewNode(comparison_op, from_char_code_repl, constant_repl); } ReplaceWithValue(comparison, number_comparison); return Replace(number_comparison); } Reduction TypedOptimization::ReduceStringComparison(Node* node) { DCHECK(IrOpcode::kStringEqual == node->opcode() || IrOpcode::kStringLessThan == node->opcode() || IrOpcode::kStringLessThanOrEqual == node->opcode()); Node* const lhs = NodeProperties::GetValueInput(node, 0); Node* const rhs = NodeProperties::GetValueInput(node, 1); Type lhs_type = NodeProperties::GetType(lhs); Type rhs_type = NodeProperties::GetType(rhs); if (lhs->opcode() == IrOpcode::kStringFromSingleCharCode) { if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) { Node* left = NodeProperties::GetValueInput(lhs, 0); Node* right = NodeProperties::GetValueInput(rhs, 0); Type left_type = NodeProperties::GetType(left); Type right_type = NodeProperties::GetType(right); if (!left_type.Is(type_cache_.kUint16)) { // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}. left = graph()->NewNode(simplified()->NumberToInt32(), left); left = graph()->NewNode( simplified()->NumberBitwiseAnd(), left, jsgraph()->Constant(std::numeric_limits<uint16_t>::max())); } if (!right_type.Is(type_cache_.kUint16)) { // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}. right = graph()->NewNode(simplified()->NumberToInt32(), right); right = graph()->NewNode( simplified()->NumberBitwiseAnd(), right, jsgraph()->Constant(std::numeric_limits<uint16_t>::max())); } Node* equal = graph()->NewNode(NumberComparisonFor(node->op()), left, right); ReplaceWithValue(node, equal); return Replace(equal); } else { return TryReduceStringComparisonOfStringFromSingleCharCode( node, lhs, rhs_type, false); } } else if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) { return TryReduceStringComparisonOfStringFromSingleCharCode(node, rhs, lhs_type, true); } return NoChange(); } Reduction TypedOptimization::ReduceStringLength(Node* node) { DCHECK_EQ(IrOpcode::kStringLength, node->opcode()); Node* const input = NodeProperties::GetValueInput(node, 0); switch (input->opcode()) { case IrOpcode::kHeapConstant: { // Constant-fold the String::length of the {input}. HeapObjectMatcher m(input); if (m.Ref(js_heap_broker()).IsString()) { uint32_t const length = m.Ref(js_heap_broker()).AsString().length(); Node* value = jsgraph()->Constant(length); return Replace(value); } break; } case IrOpcode::kStringConcat: { // The first value input to the {input} is the resulting length. return Replace(input->InputAt(0)); } default: break; } return NoChange(); } Reduction TypedOptimization::ReduceSameValue(Node* node) { DCHECK_EQ(IrOpcode::kSameValue, node->opcode()); Node* const lhs = NodeProperties::GetValueInput(node, 0); Node* const rhs = NodeProperties::GetValueInput(node, 1); Type const lhs_type = NodeProperties::GetType(lhs); Type const rhs_type = NodeProperties::GetType(rhs); if (lhs == rhs) { // SameValue(x,x) => #true return Replace(jsgraph()->TrueConstant()); } else if (lhs_type.Is(Type::Unique()) && rhs_type.Is(Type::Unique())) { // SameValue(x:unique,y:unique) => ReferenceEqual(x,y) NodeProperties::ChangeOp(node, simplified()->ReferenceEqual()); return Changed(node); } else if (lhs_type.Is(Type::String()) && rhs_type.Is(Type::String())) { // SameValue(x:string,y:string) => StringEqual(x,y) NodeProperties::ChangeOp(node, simplified()->StringEqual()); return Changed(node); } else if (lhs_type.Is(Type::MinusZero())) { // SameValue(x:minus-zero,y) => ObjectIsMinusZero(y) node->RemoveInput(0); NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero()); return Changed(node); } else if (rhs_type.Is(Type::MinusZero())) { // SameValue(x,y:minus-zero) => ObjectIsMinusZero(x) node->RemoveInput(1); NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero()); return Changed(node); } else if (lhs_type.Is(Type::NaN())) { // SameValue(x:nan,y) => ObjectIsNaN(y) node->RemoveInput(0); NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN()); return Changed(node); } else if (rhs_type.Is(Type::NaN())) { // SameValue(x,y:nan) => ObjectIsNaN(x) node->RemoveInput(1); NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN()); return Changed(node); } else if (lhs_type.Is(Type::PlainNumber()) && rhs_type.Is(Type::PlainNumber())) { // SameValue(x:plain-number,y:plain-number) => NumberEqual(x,y) NodeProperties::ChangeOp(node, simplified()->NumberEqual()); return Changed(node); } return NoChange(); } Reduction TypedOptimization::ReduceSelect(Node* node) { DCHECK_EQ(IrOpcode::kSelect, node->opcode()); Node* const condition = NodeProperties::GetValueInput(node, 0); Type const condition_type = NodeProperties::GetType(condition); Node* const vtrue = NodeProperties::GetValueInput(node, 1); Type const vtrue_type = NodeProperties::GetType(vtrue); Node* const vfalse = NodeProperties::GetValueInput(node, 2); Type const vfalse_type = NodeProperties::GetType(vfalse); if (condition_type.Is(true_type_)) { // Select(condition:true, vtrue, vfalse) => vtrue return Replace(vtrue); } if (condition_type.Is(false_type_)) { // Select(condition:false, vtrue, vfalse) => vfalse return Replace(vfalse); } if (vtrue_type.Is(true_type_) && vfalse_type.Is(false_type_)) { // Select(condition, vtrue:true, vfalse:false) => condition return Replace(condition); } if (vtrue_type.Is(false_type_) && vfalse_type.Is(true_type_)) { // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition) node->TrimInputCount(1); NodeProperties::ChangeOp(node, simplified()->BooleanNot()); return Changed(node); } // Try to narrow the type of the Select {node}, which might be more precise // now after lowering based on types. Type type = Type::Union(vtrue_type, vfalse_type, graph()->zone()); Type const node_type = NodeProperties::GetType(node); if (!node_type.Is(type)) { type = Type::Intersect(node_type, type, graph()->zone()); NodeProperties::SetType(node, type); return Changed(node); } return NoChange(); } Reduction TypedOptimization::ReduceSpeculativeToNumber(Node* node) { DCHECK_EQ(IrOpcode::kSpeculativeToNumber, node->opcode()); Node* const input = NodeProperties::GetValueInput(node, 0); Type const input_type = NodeProperties::GetType(input); if (input_type.Is(Type::Number())) { // SpeculativeToNumber(x:number) => x ReplaceWithValue(node, input); return Replace(input); } return NoChange(); } Reduction TypedOptimization::ReduceTypeOf(Node* node) { Node* const input = node->InputAt(0); Type const type = NodeProperties::GetType(input); Factory* const f = factory(); if (type.Is(Type::Boolean())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->boolean_string()))); } else if (type.Is(Type::Number())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->number_string()))); } else if (type.Is(Type::String())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->string_string()))); } else if (type.Is(Type::BigInt())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->bigint_string()))); } else if (type.Is(Type::Symbol())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->symbol_string()))); } else if (type.Is(Type::OtherUndetectableOrUndefined())) { return Replace(jsgraph()->Constant( ObjectRef(js_heap_broker(), f->undefined_string()))); } else if (type.Is(Type::NonCallableOrNull())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->object_string()))); } else if (type.Is(Type::Function())) { return Replace( jsgraph()->Constant(ObjectRef(js_heap_broker(), f->function_string()))); } 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(); } Isolate* TypedOptimization::isolate() const { return jsgraph()->isolate(); } SimplifiedOperatorBuilder* TypedOptimization::simplified() const { return jsgraph()->simplified(); } } // namespace compiler } // namespace internal } // namespace v8