|  | // 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. | 
|  |  | 
|  | #include "src/compiler/simplified-lowering.h" | 
|  |  | 
|  | #include <limits> | 
|  |  | 
|  | #include "include/v8-fast-api-calls.h" | 
|  | #include "src/base/bits.h" | 
|  | #include "src/base/small-vector.h" | 
|  | #include "src/codegen/code-factory.h" | 
|  | #include "src/codegen/machine-type.h" | 
|  | #include "src/codegen/tick-counter.h" | 
|  | #include "src/compiler/access-builder.h" | 
|  | #include "src/compiler/common-operator.h" | 
|  | #include "src/compiler/compiler-source-position-table.h" | 
|  | #include "src/compiler/diamond.h" | 
|  | #include "src/compiler/linkage.h" | 
|  | #include "src/compiler/node-matchers.h" | 
|  | #include "src/compiler/node-origin-table.h" | 
|  | #include "src/compiler/node-properties.h" | 
|  | #include "src/compiler/operation-typer.h" | 
|  | #include "src/compiler/operator-properties.h" | 
|  | #include "src/compiler/representation-change.h" | 
|  | #include "src/compiler/simplified-operator.h" | 
|  | #include "src/compiler/type-cache.h" | 
|  | #include "src/numbers/conversions-inl.h" | 
|  | #include "src/objects/objects.h" | 
|  | #include "src/utils/address-map.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  | namespace compiler { | 
|  |  | 
|  | // Macro for outputting trace information from representation inference. | 
|  | #define TRACE(...)                                      \ | 
|  | do {                                                  \ | 
|  | if (FLAG_trace_representation) PrintF(__VA_ARGS__); \ | 
|  | } while (false) | 
|  |  | 
|  | // Representation selection and lowering of {Simplified} operators to machine | 
|  | // operators are interwined. We use a fixpoint calculation to compute both the | 
|  | // output representation and the best possible lowering for {Simplified} nodes. | 
|  | // Representation change insertion ensures that all values are in the correct | 
|  | // machine representation after this phase, as dictated by the machine | 
|  | // operators themselves. | 
|  | enum Phase { | 
|  | // 1.) PROPAGATE: Traverse the graph from the end, pushing usage information | 
|  | //     backwards from uses to definitions, around cycles in phis, according | 
|  | //     to local rules for each operator. | 
|  | //     During this phase, the usage information for a node determines the best | 
|  | //     possible lowering for each operator so far, and that in turn determines | 
|  | //     the output representation. | 
|  | //     Therefore, to be correct, this phase must iterate to a fixpoint before | 
|  | //     the next phase can begin. | 
|  | PROPAGATE, | 
|  |  | 
|  | // 2.) RETYPE: Propagate types from type feedback forwards. | 
|  | RETYPE, | 
|  |  | 
|  | // 3.) LOWER: perform lowering for all {Simplified} nodes by replacing some | 
|  | //     operators for some nodes, expanding some nodes to multiple nodes, or | 
|  | //     removing some (redundant) nodes. | 
|  | //     During this phase, use the {RepresentationChanger} to insert | 
|  | //     representation changes between uses that demand a particular | 
|  | //     representation and nodes that produce a different representation. | 
|  | LOWER | 
|  | }; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | MachineRepresentation MachineRepresentationFromArrayType( | 
|  | ExternalArrayType array_type) { | 
|  | switch (array_type) { | 
|  | case kExternalUint8Array: | 
|  | case kExternalUint8ClampedArray: | 
|  | case kExternalInt8Array: | 
|  | return MachineRepresentation::kWord8; | 
|  | case kExternalUint16Array: | 
|  | case kExternalInt16Array: | 
|  | return MachineRepresentation::kWord16; | 
|  | case kExternalUint32Array: | 
|  | case kExternalInt32Array: | 
|  | return MachineRepresentation::kWord32; | 
|  | case kExternalFloat32Array: | 
|  | return MachineRepresentation::kFloat32; | 
|  | case kExternalFloat64Array: | 
|  | return MachineRepresentation::kFloat64; | 
|  | case kExternalBigInt64Array: | 
|  | case kExternalBigUint64Array: | 
|  | UNIMPLEMENTED(); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | UseInfo CheckedUseInfoAsWord32FromHint( | 
|  | NumberOperationHint hint, const FeedbackSource& feedback = FeedbackSource(), | 
|  | IdentifyZeros identify_zeros = kDistinguishZeros) { | 
|  | switch (hint) { | 
|  | case NumberOperationHint::kSignedSmall: | 
|  | case NumberOperationHint::kSignedSmallInputs: | 
|  | return UseInfo::CheckedSignedSmallAsWord32(identify_zeros, feedback); | 
|  | case NumberOperationHint::kSigned32: | 
|  | return UseInfo::CheckedSigned32AsWord32(identify_zeros, feedback); | 
|  | case NumberOperationHint::kNumber: | 
|  | return UseInfo::CheckedNumberAsWord32(feedback); | 
|  | case NumberOperationHint::kNumberOrBoolean: | 
|  | // Not used currently. | 
|  | UNREACHABLE(); | 
|  | case NumberOperationHint::kNumberOrOddball: | 
|  | return UseInfo::CheckedNumberOrOddballAsWord32(feedback); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | UseInfo CheckedUseInfoAsFloat64FromHint( | 
|  | NumberOperationHint hint, const FeedbackSource& feedback, | 
|  | IdentifyZeros identify_zeros = kDistinguishZeros) { | 
|  | switch (hint) { | 
|  | case NumberOperationHint::kSignedSmall: | 
|  | case NumberOperationHint::kSignedSmallInputs: | 
|  | case NumberOperationHint::kSigned32: | 
|  | // Not used currently. | 
|  | UNREACHABLE(); | 
|  | case NumberOperationHint::kNumber: | 
|  | return UseInfo::CheckedNumberAsFloat64(identify_zeros, feedback); | 
|  | case NumberOperationHint::kNumberOrBoolean: | 
|  | return UseInfo::CheckedNumberOrBooleanAsFloat64(identify_zeros, feedback); | 
|  | case NumberOperationHint::kNumberOrOddball: | 
|  | return UseInfo::CheckedNumberOrOddballAsFloat64(identify_zeros, feedback); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) { | 
|  | switch (rep) { | 
|  | case MachineRepresentation::kTaggedSigned: | 
|  | return UseInfo::TaggedSigned(); | 
|  | case MachineRepresentation::kTaggedPointer: | 
|  | case MachineRepresentation::kTagged: | 
|  | return UseInfo::AnyTagged(); | 
|  | case MachineRepresentation::kFloat64: | 
|  | return UseInfo::TruncatingFloat64(); | 
|  | case MachineRepresentation::kFloat32: | 
|  | return UseInfo::Float32(); | 
|  | case MachineRepresentation::kWord8: | 
|  | case MachineRepresentation::kWord16: | 
|  | case MachineRepresentation::kWord32: | 
|  | return UseInfo::TruncatingWord32(); | 
|  | case MachineRepresentation::kWord64: | 
|  | return UseInfo::Word64(); | 
|  | case MachineRepresentation::kBit: | 
|  | return UseInfo::Bool(); | 
|  | case MachineRepresentation::kCompressedPointer: | 
|  | case MachineRepresentation::kCompressed: | 
|  | case MachineRepresentation::kSimd128: | 
|  | case MachineRepresentation::kNone: | 
|  | break; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | UseInfo UseInfoForBasePointer(const FieldAccess& access) { | 
|  | return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::Word(); | 
|  | } | 
|  |  | 
|  | UseInfo UseInfoForBasePointer(const ElementAccess& access) { | 
|  | return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::Word(); | 
|  | } | 
|  |  | 
|  | void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) { | 
|  | for (Edge edge : node->use_edges()) { | 
|  | if (NodeProperties::IsControlEdge(edge)) { | 
|  | edge.UpdateTo(control); | 
|  | } else if (NodeProperties::IsEffectEdge(edge)) { | 
|  | edge.UpdateTo(effect); | 
|  | } else { | 
|  | DCHECK(NodeProperties::IsValueEdge(edge) || | 
|  | NodeProperties::IsContextEdge(edge)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CanOverflowSigned32(const Operator* op, Type left, Type right, | 
|  | TypeCache const* type_cache, Zone* type_zone) { | 
|  | // We assume the inputs are checked Signed32 (or known statically to be | 
|  | // Signed32). Technically, the inputs could also be minus zero, which we treat | 
|  | // as 0 for the purpose of this function. | 
|  | if (left.Maybe(Type::MinusZero())) { | 
|  | left = Type::Union(left, type_cache->kSingletonZero, type_zone); | 
|  | } | 
|  | if (right.Maybe(Type::MinusZero())) { | 
|  | right = Type::Union(right, type_cache->kSingletonZero, type_zone); | 
|  | } | 
|  | left = Type::Intersect(left, Type::Signed32(), type_zone); | 
|  | right = Type::Intersect(right, Type::Signed32(), type_zone); | 
|  | if (left.IsNone() || right.IsNone()) return false; | 
|  | switch (op->opcode()) { | 
|  | case IrOpcode::kSpeculativeSafeIntegerAdd: | 
|  | return (left.Max() + right.Max() > kMaxInt) || | 
|  | (left.Min() + right.Min() < kMinInt); | 
|  |  | 
|  | case IrOpcode::kSpeculativeSafeIntegerSubtract: | 
|  | return (left.Max() - right.Min() > kMaxInt) || | 
|  | (left.Min() - right.Max() < kMinInt); | 
|  |  | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IsSomePositiveOrderedNumber(Type type) { | 
|  | return type.Is(Type::OrderedNumber()) && !type.IsNone() && type.Min() > 0; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #ifdef DEBUG | 
|  | // Helpers for monotonicity checking. | 
|  | class InputUseInfos { | 
|  | public: | 
|  | explicit InputUseInfos(Zone* zone) : input_use_infos_(zone) {} | 
|  |  | 
|  | void SetAndCheckInput(Node* node, int index, UseInfo use_info) { | 
|  | if (input_use_infos_.empty()) { | 
|  | input_use_infos_.resize(node->InputCount(), UseInfo::None()); | 
|  | } | 
|  | // Check that the new use informatin is a super-type of the old | 
|  | // one. | 
|  | DCHECK(IsUseLessGeneral(input_use_infos_[index], use_info)); | 
|  | input_use_infos_[index] = use_info; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ZoneVector<UseInfo> input_use_infos_; | 
|  |  | 
|  | static bool IsUseLessGeneral(UseInfo use1, UseInfo use2) { | 
|  | return use1.truncation().IsLessGeneralThan(use2.truncation()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif  // DEBUG | 
|  |  | 
|  | class RepresentationSelector { | 
|  | public: | 
|  | // Information for each node tracked during the fixpoint. | 
|  | class NodeInfo final { | 
|  | public: | 
|  | // Adds new use to the node. Returns true if something has changed | 
|  | // and the node has to be requeued. | 
|  | bool AddUse(UseInfo info) { | 
|  | Truncation old_truncation = truncation_; | 
|  | truncation_ = Truncation::Generalize(truncation_, info.truncation()); | 
|  | return truncation_ != old_truncation; | 
|  | } | 
|  |  | 
|  | void set_queued() { state_ = kQueued; } | 
|  | void set_visited() { state_ = kVisited; } | 
|  | void set_pushed() { state_ = kPushed; } | 
|  | void reset_state() { state_ = kUnvisited; } | 
|  | bool visited() const { return state_ == kVisited; } | 
|  | bool queued() const { return state_ == kQueued; } | 
|  | bool pushed() const { return state_ == kPushed; } | 
|  | bool unvisited() const { return state_ == kUnvisited; } | 
|  | Truncation truncation() const { return truncation_; } | 
|  | void set_output(MachineRepresentation output) { representation_ = output; } | 
|  |  | 
|  | MachineRepresentation representation() const { return representation_; } | 
|  |  | 
|  | // Helpers for feedback typing. | 
|  | void set_feedback_type(Type type) { feedback_type_ = type; } | 
|  | Type feedback_type() const { return feedback_type_; } | 
|  | void set_weakened() { weakened_ = true; } | 
|  | bool weakened() const { return weakened_; } | 
|  | void set_restriction_type(Type type) { restriction_type_ = type; } | 
|  | Type restriction_type() const { return restriction_type_; } | 
|  |  | 
|  | private: | 
|  | enum State : uint8_t { kUnvisited, kPushed, kVisited, kQueued }; | 
|  | State state_ = kUnvisited; | 
|  | MachineRepresentation representation_ = | 
|  | MachineRepresentation::kNone;             // Output representation. | 
|  | Truncation truncation_ = Truncation::None();  // Information about uses. | 
|  |  | 
|  | Type restriction_type_ = Type::Any(); | 
|  | Type feedback_type_; | 
|  | bool weakened_ = false; | 
|  | }; | 
|  |  | 
|  | RepresentationSelector(JSGraph* jsgraph, JSHeapBroker* broker, Zone* zone, | 
|  | RepresentationChanger* changer, | 
|  | SourcePositionTable* source_positions, | 
|  | NodeOriginTable* node_origins, | 
|  | TickCounter* tick_counter, Linkage* linkage) | 
|  | : jsgraph_(jsgraph), | 
|  | zone_(zone), | 
|  | might_need_revisit_(zone), | 
|  | count_(jsgraph->graph()->NodeCount()), | 
|  | info_(count_, zone), | 
|  | #ifdef DEBUG | 
|  | node_input_use_infos_(count_, InputUseInfos(zone), zone), | 
|  | #endif | 
|  | replacements_(zone), | 
|  | changer_(changer), | 
|  | revisit_queue_(zone), | 
|  | traversal_nodes_(zone), | 
|  | source_positions_(source_positions), | 
|  | node_origins_(node_origins), | 
|  | type_cache_(TypeCache::Get()), | 
|  | op_typer_(broker, graph_zone()), | 
|  | tick_counter_(tick_counter), | 
|  | linkage_(linkage) { | 
|  | } | 
|  |  | 
|  | void ResetNodeInfoState() { | 
|  | // Clean up for the next phase. | 
|  | for (NodeInfo& info : info_) { | 
|  | info.reset_state(); | 
|  | } | 
|  | } | 
|  |  | 
|  | Type TypeOf(Node* node) { | 
|  | Type type = GetInfo(node)->feedback_type(); | 
|  | return type.IsInvalid() ? NodeProperties::GetType(node) : type; | 
|  | } | 
|  |  | 
|  | Type FeedbackTypeOf(Node* node) { | 
|  | Type type = GetInfo(node)->feedback_type(); | 
|  | return type.IsInvalid() ? Type::None() : type; | 
|  | } | 
|  |  | 
|  | Type TypePhi(Node* node) { | 
|  | int arity = node->op()->ValueInputCount(); | 
|  | Type type = FeedbackTypeOf(node->InputAt(0)); | 
|  | for (int i = 1; i < arity; ++i) { | 
|  | type = op_typer_.Merge(type, FeedbackTypeOf(node->InputAt(i))); | 
|  | } | 
|  | return type; | 
|  | } | 
|  |  | 
|  | Type TypeSelect(Node* node) { | 
|  | return op_typer_.Merge(FeedbackTypeOf(node->InputAt(1)), | 
|  | FeedbackTypeOf(node->InputAt(2))); | 
|  | } | 
|  |  | 
|  | bool UpdateFeedbackType(Node* node) { | 
|  | if (node->op()->ValueOutputCount() == 0) return false; | 
|  |  | 
|  | // For any non-phi node just wait until we get all inputs typed. We only | 
|  | // allow untyped inputs for phi nodes because phis are the only places | 
|  | // where cycles need to be broken. | 
|  | if (node->opcode() != IrOpcode::kPhi) { | 
|  | for (int i = 0; i < node->op()->ValueInputCount(); i++) { | 
|  | if (GetInfo(node->InputAt(i))->feedback_type().IsInvalid()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | NodeInfo* info = GetInfo(node); | 
|  | Type type = info->feedback_type(); | 
|  | Type new_type = NodeProperties::GetType(node); | 
|  |  | 
|  | // We preload these values here to avoid increasing the binary size too | 
|  | // much, which happens if we inline the calls into the macros below. | 
|  | Type input0_type; | 
|  | if (node->InputCount() > 0) input0_type = FeedbackTypeOf(node->InputAt(0)); | 
|  | Type input1_type; | 
|  | if (node->InputCount() > 1) input1_type = FeedbackTypeOf(node->InputAt(1)); | 
|  |  | 
|  | switch (node->opcode()) { | 
|  | #define DECLARE_CASE(Name)                               \ | 
|  | case IrOpcode::k##Name: {                              \ | 
|  | new_type = op_typer_.Name(input0_type, input1_type); \ | 
|  | break;                                               \ | 
|  | } | 
|  | SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE) | 
|  | DECLARE_CASE(SameValue) | 
|  | #undef DECLARE_CASE | 
|  |  | 
|  | #define DECLARE_CASE(Name)                                               \ | 
|  | case IrOpcode::k##Name: {                                              \ | 
|  | new_type = Type::Intersect(op_typer_.Name(input0_type, input1_type), \ | 
|  | info->restriction_type(), graph_zone());  \ | 
|  | break;                                                               \ | 
|  | } | 
|  | SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_CASE) | 
|  | SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_CASE) | 
|  | #undef DECLARE_CASE | 
|  |  | 
|  | #define DECLARE_CASE(Name)                  \ | 
|  | case IrOpcode::k##Name: {                 \ | 
|  | new_type = op_typer_.Name(input0_type); \ | 
|  | break;                                  \ | 
|  | } | 
|  | SIMPLIFIED_NUMBER_UNOP_LIST(DECLARE_CASE) | 
|  | #undef DECLARE_CASE | 
|  |  | 
|  | #define DECLARE_CASE(Name)                                              \ | 
|  | case IrOpcode::k##Name: {                                             \ | 
|  | new_type = Type::Intersect(op_typer_.Name(input0_type),             \ | 
|  | info->restriction_type(), graph_zone()); \ | 
|  | break;                                                              \ | 
|  | } | 
|  | SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(DECLARE_CASE) | 
|  | #undef DECLARE_CASE | 
|  |  | 
|  | case IrOpcode::kConvertReceiver: | 
|  | new_type = op_typer_.ConvertReceiver(input0_type); | 
|  | break; | 
|  |  | 
|  | case IrOpcode::kPlainPrimitiveToNumber: | 
|  | new_type = op_typer_.ToNumber(input0_type); | 
|  | break; | 
|  |  | 
|  | case IrOpcode::kCheckBounds: | 
|  | new_type = | 
|  | Type::Intersect(op_typer_.CheckBounds(input0_type, input1_type), | 
|  | info->restriction_type(), graph_zone()); | 
|  | break; | 
|  |  | 
|  | case IrOpcode::kCheckFloat64Hole: | 
|  | new_type = Type::Intersect(op_typer_.CheckFloat64Hole(input0_type), | 
|  | info->restriction_type(), graph_zone()); | 
|  | break; | 
|  |  | 
|  | case IrOpcode::kCheckNumber: | 
|  | new_type = Type::Intersect(op_typer_.CheckNumber(input0_type), | 
|  | info->restriction_type(), graph_zone()); | 
|  | break; | 
|  |  | 
|  | case IrOpcode::kPhi: { | 
|  | new_type = TypePhi(node); | 
|  | if (!type.IsInvalid()) { | 
|  | new_type = Weaken(node, type, new_type); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kConvertTaggedHoleToUndefined: | 
|  | new_type = op_typer_.ConvertTaggedHoleToUndefined( | 
|  | FeedbackTypeOf(node->InputAt(0))); | 
|  | break; | 
|  |  | 
|  | case IrOpcode::kTypeGuard: { | 
|  | new_type = op_typer_.TypeTypeGuard(node->op(), | 
|  | FeedbackTypeOf(node->InputAt(0))); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kSelect: { | 
|  | new_type = TypeSelect(node); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | // Shortcut for operations that we do not handle. | 
|  | if (type.IsInvalid()) { | 
|  | GetInfo(node)->set_feedback_type(NodeProperties::GetType(node)); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  | // We need to guarantee that the feedback type is a subtype of the upper | 
|  | // bound. Naively that should hold, but weakening can actually produce | 
|  | // a bigger type if we are unlucky with ordering of phi typing. To be | 
|  | // really sure, just intersect the upper bound with the feedback type. | 
|  | new_type = Type::Intersect(GetUpperBound(node), new_type, graph_zone()); | 
|  |  | 
|  | if (!type.IsInvalid() && new_type.Is(type)) return false; | 
|  | GetInfo(node)->set_feedback_type(new_type); | 
|  | if (FLAG_trace_representation) { | 
|  | PrintNodeFeedbackType(node); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void PrintNodeFeedbackType(Node* n) { | 
|  | StdoutStream os; | 
|  | os << "#" << n->id() << ":" << *n->op() << "("; | 
|  | int j = 0; | 
|  | for (Node* const i : n->inputs()) { | 
|  | if (j++ > 0) os << ", "; | 
|  | os << "#" << i->id() << ":" << i->op()->mnemonic(); | 
|  | } | 
|  | os << ")"; | 
|  | if (NodeProperties::IsTyped(n)) { | 
|  | Type static_type = NodeProperties::GetType(n); | 
|  | os << "  [Static type: " << static_type; | 
|  | Type feedback_type = GetInfo(n)->feedback_type(); | 
|  | if (!feedback_type.IsInvalid() && feedback_type != static_type) { | 
|  | os << ", Feedback type: " << feedback_type; | 
|  | } | 
|  | os << "]"; | 
|  | } | 
|  | os << std::endl; | 
|  | } | 
|  |  | 
|  | Type Weaken(Node* node, Type previous_type, Type current_type) { | 
|  | // If the types have nothing to do with integers, return the types. | 
|  | Type const integer = type_cache_->kInteger; | 
|  | if (!previous_type.Maybe(integer)) { | 
|  | return current_type; | 
|  | } | 
|  | DCHECK(current_type.Maybe(integer)); | 
|  |  | 
|  | Type current_integer = Type::Intersect(current_type, integer, graph_zone()); | 
|  | DCHECK(!current_integer.IsNone()); | 
|  | Type previous_integer = | 
|  | Type::Intersect(previous_type, integer, graph_zone()); | 
|  | DCHECK(!previous_integer.IsNone()); | 
|  |  | 
|  | // Once we start weakening a node, we should always weaken. | 
|  | if (!GetInfo(node)->weakened()) { | 
|  | // Only weaken if there is range involved; we should converge quickly | 
|  | // for all other types (the exception is a union of many constants, | 
|  | // but we currently do not increase the number of constants in unions). | 
|  | Type previous = previous_integer.GetRange(); | 
|  | Type current = current_integer.GetRange(); | 
|  | if (current.IsInvalid() || previous.IsInvalid()) { | 
|  | return current_type; | 
|  | } | 
|  | // Range is involved => we are weakening. | 
|  | GetInfo(node)->set_weakened(); | 
|  | } | 
|  |  | 
|  | return Type::Union(current_type, | 
|  | op_typer_.WeakenRange(previous_integer, current_integer), | 
|  | graph_zone()); | 
|  | } | 
|  |  | 
|  | // Generates a pre-order traversal of the nodes, starting with End. | 
|  | void GenerateTraversal() { | 
|  | ZoneStack<NodeState> stack(zone_); | 
|  |  | 
|  | stack.push({graph()->end(), 0}); | 
|  | GetInfo(graph()->end())->set_pushed(); | 
|  | while (!stack.empty()) { | 
|  | NodeState& current = stack.top(); | 
|  | Node* node = current.node; | 
|  |  | 
|  | // If there is an unvisited input, push it and continue with that node. | 
|  | bool pushed_unvisited = false; | 
|  | while (current.input_index < node->InputCount()) { | 
|  | Node* input = node->InputAt(current.input_index); | 
|  | NodeInfo* input_info = GetInfo(input); | 
|  | current.input_index++; | 
|  | if (input_info->unvisited()) { | 
|  | input_info->set_pushed(); | 
|  | stack.push({input, 0}); | 
|  | pushed_unvisited = true; | 
|  | break; | 
|  | } else if (input_info->pushed()) { | 
|  | // Optimization for the Retype phase. | 
|  | // If we had already pushed (and not visited) an input, it means that | 
|  | // the current node will be visited in the Retype phase before one of | 
|  | // its inputs. If this happens, the current node might need to be | 
|  | // revisited. | 
|  | MarkAsPossibleRevisit(node, input); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (pushed_unvisited) continue; | 
|  |  | 
|  | stack.pop(); | 
|  | NodeInfo* info = GetInfo(node); | 
|  | info->set_visited(); | 
|  |  | 
|  | // Generate the traversal | 
|  | traversal_nodes_.push_back(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PushNodeToRevisitIfVisited(Node* node) { | 
|  | NodeInfo* info = GetInfo(node); | 
|  | if (info->visited()) { | 
|  | TRACE(" QUEUEING #%d: %s\n", node->id(), node->op()->mnemonic()); | 
|  | info->set_queued(); | 
|  | revisit_queue_.push(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tries to update the feedback type of the node, as well as setting its | 
|  | // machine representation (in VisitNode). Returns true iff updating the | 
|  | // feedback type is successful. | 
|  | bool RetypeNode(Node* node) { | 
|  | NodeInfo* info = GetInfo(node); | 
|  | info->set_visited(); | 
|  | bool updated = UpdateFeedbackType(node); | 
|  | TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic()); | 
|  | VisitNode<RETYPE>(node, info->truncation(), nullptr); | 
|  | TRACE("  ==> output %s\n", MachineReprToString(info->representation())); | 
|  | return updated; | 
|  | } | 
|  |  | 
|  | // Visits the node and marks it as visited. Inside of VisitNode, we might | 
|  | // change the truncation of one of our inputs (see EnqueueInput<PROPAGATE> for | 
|  | // this). If we change the truncation of an already visited node, we will add | 
|  | // it to the revisit queue. | 
|  | void PropagateTruncation(Node* node) { | 
|  | NodeInfo* info = GetInfo(node); | 
|  | info->set_visited(); | 
|  | TRACE(" visit #%d: %s (trunc: %s)\n", node->id(), node->op()->mnemonic(), | 
|  | info->truncation().description()); | 
|  | VisitNode<PROPAGATE>(node, info->truncation(), nullptr); | 
|  | } | 
|  |  | 
|  | // Backward propagation of truncations to a fixpoint. | 
|  | void RunPropagatePhase() { | 
|  | TRACE("--{Propagate phase}--\n"); | 
|  | ResetNodeInfoState(); | 
|  | DCHECK(revisit_queue_.empty()); | 
|  |  | 
|  | // Process nodes in reverse post order, with End as the root. | 
|  | for (auto it = traversal_nodes_.crbegin(); it != traversal_nodes_.crend(); | 
|  | ++it) { | 
|  | PropagateTruncation(*it); | 
|  |  | 
|  | while (!revisit_queue_.empty()) { | 
|  | Node* node = revisit_queue_.front(); | 
|  | revisit_queue_.pop(); | 
|  | PropagateTruncation(node); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Forward propagation of types from type feedback to a fixpoint. | 
|  | void RunRetypePhase() { | 
|  | TRACE("--{Retype phase}--\n"); | 
|  | ResetNodeInfoState(); | 
|  | DCHECK(revisit_queue_.empty()); | 
|  |  | 
|  | for (auto it = traversal_nodes_.cbegin(); it != traversal_nodes_.cend(); | 
|  | ++it) { | 
|  | Node* node = *it; | 
|  | if (!RetypeNode(node)) continue; | 
|  |  | 
|  | auto revisit_it = might_need_revisit_.find(node); | 
|  | if (revisit_it == might_need_revisit_.end()) continue; | 
|  |  | 
|  | for (Node* const user : revisit_it->second) { | 
|  | PushNodeToRevisitIfVisited(user); | 
|  | } | 
|  |  | 
|  | // Process the revisit queue. | 
|  | while (!revisit_queue_.empty()) { | 
|  | Node* revisit_node = revisit_queue_.front(); | 
|  | revisit_queue_.pop(); | 
|  | if (!RetypeNode(revisit_node)) continue; | 
|  | // Here we need to check all uses since we can't easily know which | 
|  | // nodes will need to be revisited due to having an input which was | 
|  | // a revisited node. | 
|  | for (Node* const user : revisit_node->uses()) { | 
|  | PushNodeToRevisitIfVisited(user); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Lowering and change insertion phase. | 
|  | void RunLowerPhase(SimplifiedLowering* lowering) { | 
|  | TRACE("--{Lower phase}--\n"); | 
|  | for (auto it = traversal_nodes_.cbegin(); it != traversal_nodes_.cend(); | 
|  | ++it) { | 
|  | Node* node = *it; | 
|  | NodeInfo* info = GetInfo(node); | 
|  | TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic()); | 
|  | // Reuse {VisitNode()} so the representation rules are in one place. | 
|  | SourcePositionTable::Scope scope( | 
|  | source_positions_, source_positions_->GetSourcePosition(node)); | 
|  | NodeOriginTable::Scope origin_scope(node_origins_, "simplified lowering", | 
|  | node); | 
|  | VisitNode<LOWER>(node, info->truncation(), lowering); | 
|  | } | 
|  |  | 
|  | // Perform the final replacements. | 
|  | for (NodeVector::iterator i = replacements_.begin(); | 
|  | i != replacements_.end(); ++i) { | 
|  | Node* node = *i; | 
|  | Node* replacement = *(++i); | 
|  | node->ReplaceUses(replacement); | 
|  | node->Kill(); | 
|  | // We also need to replace the node in the rest of the vector. | 
|  | for (NodeVector::iterator j = i + 1; j != replacements_.end(); ++j) { | 
|  | ++j; | 
|  | if (*j == node) *j = replacement; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Run(SimplifiedLowering* lowering) { | 
|  | GenerateTraversal(); | 
|  | RunPropagatePhase(); | 
|  | RunRetypePhase(); | 
|  | RunLowerPhase(lowering); | 
|  | } | 
|  |  | 
|  | // Just assert for Retype and Lower. Propagate specialized below. | 
|  | template <Phase T> | 
|  | void EnqueueInput(Node* use_node, int index, | 
|  | UseInfo use_info = UseInfo::None()) { | 
|  | static_assert(retype<T>() || lower<T>(), | 
|  | "This version of ProcessRemainingInputs has to be called in " | 
|  | "the Retype or Lower phase."); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | static constexpr bool propagate() { | 
|  | return T == PROPAGATE; | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | static constexpr bool retype() { | 
|  | return T == RETYPE; | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | static constexpr bool lower() { | 
|  | return T == LOWER; | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void SetOutput(Node* node, MachineRepresentation representation, | 
|  | Type restriction_type = Type::Any()); | 
|  |  | 
|  | Type GetUpperBound(Node* node) { return NodeProperties::GetType(node); } | 
|  |  | 
|  | bool InputCannotBe(Node* node, Type type) { | 
|  | DCHECK_EQ(1, node->op()->ValueInputCount()); | 
|  | return !GetUpperBound(node->InputAt(0)).Maybe(type); | 
|  | } | 
|  |  | 
|  | bool InputIs(Node* node, Type type) { | 
|  | DCHECK_EQ(1, node->op()->ValueInputCount()); | 
|  | return GetUpperBound(node->InputAt(0)).Is(type); | 
|  | } | 
|  |  | 
|  | bool BothInputsAreSigned32(Node* node) { | 
|  | return BothInputsAre(node, Type::Signed32()); | 
|  | } | 
|  |  | 
|  | bool BothInputsAreUnsigned32(Node* node) { | 
|  | return BothInputsAre(node, Type::Unsigned32()); | 
|  | } | 
|  |  | 
|  | bool BothInputsAre(Node* node, Type type) { | 
|  | DCHECK_EQ(2, node->op()->ValueInputCount()); | 
|  | return GetUpperBound(node->InputAt(0)).Is(type) && | 
|  | GetUpperBound(node->InputAt(1)).Is(type); | 
|  | } | 
|  |  | 
|  | bool IsNodeRepresentationTagged(Node* node) { | 
|  | MachineRepresentation representation = GetInfo(node)->representation(); | 
|  | return IsAnyTagged(representation); | 
|  | } | 
|  |  | 
|  | bool OneInputCannotBe(Node* node, Type type) { | 
|  | DCHECK_EQ(2, node->op()->ValueInputCount()); | 
|  | return !GetUpperBound(node->InputAt(0)).Maybe(type) || | 
|  | !GetUpperBound(node->InputAt(1)).Maybe(type); | 
|  | } | 
|  |  | 
|  | void ChangeToDeadValue(Node* node, Node* effect, Node* control) { | 
|  | DCHECK(TypeOf(node).IsNone()); | 
|  | // If the node is unreachable, insert an Unreachable node and mark the | 
|  | // value dead. | 
|  | // TODO(jarin,tebbi) Find a way to unify/merge this insertion with | 
|  | // InsertUnreachableIfNecessary. | 
|  | Node* unreachable = effect = | 
|  | graph()->NewNode(jsgraph_->common()->Unreachable(), effect, control); | 
|  | const Operator* dead_value = | 
|  | jsgraph_->common()->DeadValue(GetInfo(node)->representation()); | 
|  | node->ReplaceInput(0, unreachable); | 
|  | node->TrimInputCount(dead_value->ValueInputCount()); | 
|  | ReplaceEffectControlUses(node, effect, control); | 
|  | NodeProperties::ChangeOp(node, dead_value); | 
|  | } | 
|  |  | 
|  | void ChangeToPureOp(Node* node, const Operator* new_op) { | 
|  | DCHECK(new_op->HasProperty(Operator::kPure)); | 
|  | DCHECK_EQ(new_op->ValueInputCount(), node->op()->ValueInputCount()); | 
|  | if (node->op()->EffectInputCount() > 0) { | 
|  | DCHECK_LT(0, node->op()->ControlInputCount()); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | if (TypeOf(node).IsNone()) { | 
|  | ChangeToDeadValue(node, effect, control); | 
|  | return; | 
|  | } | 
|  | // Rewire the effect and control chains. | 
|  | node->TrimInputCount(new_op->ValueInputCount()); | 
|  | ReplaceEffectControlUses(node, effect, control); | 
|  | } else { | 
|  | DCHECK_EQ(0, node->op()->ControlInputCount()); | 
|  | } | 
|  | NodeProperties::ChangeOp(node, new_op); | 
|  | } | 
|  |  | 
|  | void ChangeUnaryToPureBinaryOp(Node* node, const Operator* new_op, | 
|  | int new_input_index, Node* new_input) { | 
|  | DCHECK(new_op->HasProperty(Operator::kPure)); | 
|  | DCHECK_EQ(new_op->ValueInputCount(), 2); | 
|  | DCHECK_EQ(node->op()->ValueInputCount(), 1); | 
|  | DCHECK_LE(0, new_input_index); | 
|  | DCHECK_LE(new_input_index, 1); | 
|  | if (node->op()->EffectInputCount() > 0) { | 
|  | DCHECK_LT(0, node->op()->ControlInputCount()); | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | if (TypeOf(node).IsNone()) { | 
|  | ChangeToDeadValue(node, effect, control); | 
|  | return; | 
|  | } | 
|  | node->TrimInputCount(node->op()->ValueInputCount()); | 
|  | ReplaceEffectControlUses(node, effect, control); | 
|  | } else { | 
|  | DCHECK_EQ(0, node->op()->ControlInputCount()); | 
|  | } | 
|  | node->InsertInput(jsgraph_->zone(), new_input_index, new_input); | 
|  | NodeProperties::ChangeOp(node, new_op); | 
|  | } | 
|  |  | 
|  | // Converts input {index} of {node} according to given UseInfo {use}, | 
|  | // assuming the type of the input is {input_type}. If {input_type} is null, | 
|  | // it takes the input from the input node {TypeOf(node->InputAt(index))}. | 
|  | void ConvertInput(Node* node, int index, UseInfo use, | 
|  | Type input_type = Type::Invalid()) { | 
|  | // In the change phase, insert a change before the use if necessary. | 
|  | if (use.representation() == MachineRepresentation::kNone) | 
|  | return;  // No input requirement on the use. | 
|  | Node* input = node->InputAt(index); | 
|  | DCHECK_NOT_NULL(input); | 
|  | NodeInfo* input_info = GetInfo(input); | 
|  | MachineRepresentation input_rep = input_info->representation(); | 
|  | if (input_rep != use.representation() || | 
|  | use.type_check() != TypeCheckKind::kNone) { | 
|  | // Output representation doesn't match usage. | 
|  | TRACE("  change: #%d:%s(@%d #%d:%s) ", node->id(), node->op()->mnemonic(), | 
|  | index, input->id(), input->op()->mnemonic()); | 
|  | TRACE("from %s to %s:%s\n", | 
|  | MachineReprToString(input_info->representation()), | 
|  | MachineReprToString(use.representation()), | 
|  | use.truncation().description()); | 
|  | if (input_type.IsInvalid()) { | 
|  | input_type = TypeOf(input); | 
|  | } | 
|  | Node* n = changer_->GetRepresentationFor(input, input_rep, input_type, | 
|  | node, use); | 
|  | node->ReplaceInput(index, n); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void ProcessInput(Node* node, int index, UseInfo use); | 
|  |  | 
|  | // Just assert for Retype and Lower. Propagate specialized below. | 
|  | template <Phase T> | 
|  | void ProcessRemainingInputs(Node* node, int index) { | 
|  | static_assert(retype<T>() || lower<T>(), | 
|  | "This version of ProcessRemainingInputs has to be called in " | 
|  | "the Retype or Lower phase."); | 
|  | DCHECK_GE(index, NodeProperties::PastValueIndex(node)); | 
|  | DCHECK_GE(index, NodeProperties::PastContextIndex(node)); | 
|  | } | 
|  |  | 
|  | // Marks node as a possible revisit since it is a use of input that will be | 
|  | // visited before input is visited. | 
|  | void MarkAsPossibleRevisit(Node* node, Node* input) { | 
|  | auto it = might_need_revisit_.find(input); | 
|  | if (it == might_need_revisit_.end()) { | 
|  | it = might_need_revisit_.insert({input, ZoneVector<Node*>(zone())}).first; | 
|  | } | 
|  | it->second.push_back(node); | 
|  | TRACE(" Marking #%d: %s as needing revisit due to #%d: %s\n", node->id(), | 
|  | node->op()->mnemonic(), input->id(), input->op()->mnemonic()); | 
|  | } | 
|  |  | 
|  | // Just assert for Retype. Propagate and Lower specialized below. | 
|  | template <Phase T> | 
|  | void VisitInputs(Node* node) { | 
|  | static_assert( | 
|  | retype<T>(), | 
|  | "This version of VisitInputs has to be called in the Retype phase."); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitReturn(Node* node) { | 
|  | int first_effect_index = NodeProperties::FirstEffectIndex(node); | 
|  | // Visit integer slot count to pop | 
|  | ProcessInput<T>(node, 0, UseInfo::TruncatingWord32()); | 
|  |  | 
|  | // Visit value, context and frame state inputs as tagged. | 
|  | for (int i = 1; i < first_effect_index; i++) { | 
|  | ProcessInput<T>(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  | // Only enqueue other inputs (effects, control). | 
|  | for (int i = first_effect_index; i < node->InputCount(); i++) { | 
|  | EnqueueInput<T>(node, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Helper for an unused node. | 
|  | template <Phase T> | 
|  | void VisitUnused(Node* node) { | 
|  | int first_effect_index = NodeProperties::FirstEffectIndex(node); | 
|  | for (int i = 0; i < first_effect_index; i++) { | 
|  | ProcessInput<T>(node, i, UseInfo::None()); | 
|  | } | 
|  | ProcessRemainingInputs<T>(node, first_effect_index); | 
|  | if (lower<T>()) Kill(node); | 
|  | } | 
|  |  | 
|  | // Helper for no-op node. | 
|  | template <Phase T> | 
|  | void VisitNoop(Node* node, Truncation truncation) { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | MachineRepresentation representation = | 
|  | GetOutputInfoForPhi(node, TypeOf(node), truncation); | 
|  | VisitUnop<T>(node, UseInfo(representation, truncation), representation); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } | 
|  |  | 
|  | // Helper for binops of the R x L -> O variety. | 
|  | template <Phase T> | 
|  | void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use, | 
|  | MachineRepresentation output, | 
|  | Type restriction_type = Type::Any()) { | 
|  | DCHECK_EQ(2, node->op()->ValueInputCount()); | 
|  | ProcessInput<T>(node, 0, left_use); | 
|  | ProcessInput<T>(node, 1, right_use); | 
|  | for (int i = 2; i < node->InputCount(); i++) { | 
|  | EnqueueInput<T>(node, i); | 
|  | } | 
|  | SetOutput<T>(node, output, restriction_type); | 
|  | } | 
|  |  | 
|  | // Helper for binops of the I x I -> O variety. | 
|  | template <Phase T> | 
|  | void VisitBinop(Node* node, UseInfo input_use, MachineRepresentation output, | 
|  | Type restriction_type = Type::Any()) { | 
|  | VisitBinop<T>(node, input_use, input_use, output, restriction_type); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitSpeculativeInt32Binop(Node* node) { | 
|  | DCHECK_EQ(2, node->op()->ValueInputCount()); | 
|  | if (BothInputsAre(node, Type::NumberOrOddball())) { | 
|  | return VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | return VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  |  | 
|  | // Helper for unops of the I -> O variety. | 
|  | template <Phase T> | 
|  | void VisitUnop(Node* node, UseInfo input_use, MachineRepresentation output, | 
|  | Type restriction_type = Type::Any()) { | 
|  | DCHECK_EQ(1, node->op()->ValueInputCount()); | 
|  | ProcessInput<T>(node, 0, input_use); | 
|  | ProcessRemainingInputs<T>(node, 1); | 
|  | SetOutput<T>(node, output, restriction_type); | 
|  | } | 
|  |  | 
|  | // Helper for leaf nodes. | 
|  | template <Phase T> | 
|  | void VisitLeaf(Node* node, MachineRepresentation output) { | 
|  | DCHECK_EQ(0, node->InputCount()); | 
|  | SetOutput<T>(node, output); | 
|  | } | 
|  |  | 
|  | // Helpers for specific types of binops. | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitFloat64Binop(Node* node) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitInt64Binop(Node* node) { | 
|  | VisitBinop<T>(node, UseInfo::Word64(), MachineRepresentation::kWord64); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitWord32TruncatingBinop(Node* node) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  |  | 
|  | // Infer representation for phi-like nodes. | 
|  | // The {node} parameter is only used to decide on the int64 representation. | 
|  | // Once the type system supports an external pointer type, the {node} | 
|  | // parameter can be removed. | 
|  | MachineRepresentation GetOutputInfoForPhi(Node* node, Type type, | 
|  | Truncation use) { | 
|  | // Compute the representation. | 
|  | if (type.Is(Type::None())) { | 
|  | return MachineRepresentation::kNone; | 
|  | } else if (type.Is(Type::Signed32()) || type.Is(Type::Unsigned32())) { | 
|  | return MachineRepresentation::kWord32; | 
|  | } else if (type.Is(Type::NumberOrOddball()) && use.IsUsedAsWord32()) { | 
|  | return MachineRepresentation::kWord32; | 
|  | } else if (type.Is(Type::Boolean())) { | 
|  | return MachineRepresentation::kBit; | 
|  | } else if (type.Is(Type::NumberOrOddball()) && | 
|  | use.TruncatesOddballAndBigIntToNumber()) { | 
|  | return MachineRepresentation::kFloat64; | 
|  | } else if (type.Is(Type::Union(Type::SignedSmall(), Type::NaN(), zone()))) { | 
|  | // TODO(turbofan): For Phis that return either NaN or some Smi, it's | 
|  | // beneficial to not go all the way to double, unless the uses are | 
|  | // double uses. For tagging that just means some potentially expensive | 
|  | // allocation code; we might want to do the same for -0 as well? | 
|  | return MachineRepresentation::kTagged; | 
|  | } else if (type.Is(Type::Number())) { | 
|  | return MachineRepresentation::kFloat64; | 
|  | } else if (type.Is(Type::BigInt()) && use.IsUsedAsWord64()) { | 
|  | return MachineRepresentation::kWord64; | 
|  | } else if (type.Is(Type::ExternalPointer()) || | 
|  | type.Is(Type::SandboxedExternalPointer())) { | 
|  | return MachineType::PointerRepresentation(); | 
|  | } | 
|  | return MachineRepresentation::kTagged; | 
|  | } | 
|  |  | 
|  | // Helper for handling selects. | 
|  | template <Phase T> | 
|  | void VisitSelect(Node* node, Truncation truncation, | 
|  | SimplifiedLowering* lowering) { | 
|  | DCHECK(TypeOf(node->InputAt(0)).Is(Type::Boolean())); | 
|  | ProcessInput<T>(node, 0, UseInfo::Bool()); | 
|  |  | 
|  | MachineRepresentation output = | 
|  | GetOutputInfoForPhi(node, TypeOf(node), truncation); | 
|  | SetOutput<T>(node, output); | 
|  |  | 
|  | if (lower<T>()) { | 
|  | // Update the select operator. | 
|  | SelectParameters p = SelectParametersOf(node->op()); | 
|  | if (output != p.representation()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | lowering->common()->Select(output, p.hint())); | 
|  | } | 
|  | } | 
|  | // Convert inputs to the output representation of this phi, pass the | 
|  | // truncation truncation along. | 
|  | UseInfo input_use(output, truncation); | 
|  | ProcessInput<T>(node, 1, input_use); | 
|  | ProcessInput<T>(node, 2, input_use); | 
|  | } | 
|  |  | 
|  | // Helper for handling phis. | 
|  | template <Phase T> | 
|  | void VisitPhi(Node* node, Truncation truncation, | 
|  | SimplifiedLowering* lowering) { | 
|  | MachineRepresentation output = | 
|  | GetOutputInfoForPhi(node, TypeOf(node), truncation); | 
|  | // Only set the output representation if not running with type | 
|  | // feedback. (Feedback typing will set the representation.) | 
|  | SetOutput<T>(node, output); | 
|  |  | 
|  | int values = node->op()->ValueInputCount(); | 
|  | if (lower<T>()) { | 
|  | // Update the phi operator. | 
|  | if (output != PhiRepresentationOf(node->op())) { | 
|  | NodeProperties::ChangeOp(node, lowering->common()->Phi(output, values)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Convert inputs to the output representation of this phi, pass the | 
|  | // truncation along. | 
|  | UseInfo input_use(output, truncation); | 
|  | for (int i = 0; i < node->InputCount(); i++) { | 
|  | ProcessInput<T>(node, i, i < values ? input_use : UseInfo::None()); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitObjectIs(Node* node, Type type, SimplifiedLowering* lowering) { | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | if (input_type.Is(type)) { | 
|  | VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(1)); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | if (lower<T>() && !input_type.Maybe(type)) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitCheck(Node* node, Type type, SimplifiedLowering* lowering) { | 
|  | if (InputIs(node, type)) { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitUnop<T>(node, | 
|  | UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitCall(Node* node, SimplifiedLowering* lowering) { | 
|  | auto call_descriptor = CallDescriptorOf(node->op()); | 
|  | int params = static_cast<int>(call_descriptor->ParameterCount()); | 
|  | int value_input_count = node->op()->ValueInputCount(); | 
|  |  | 
|  | DCHECK_GT(value_input_count, 0); | 
|  | DCHECK_GE(value_input_count, params); | 
|  |  | 
|  | // The target of the call. | 
|  | ProcessInput<T>(node, 0, UseInfo::Any()); | 
|  |  | 
|  | // For the parameters (indexes [1, ..., params]), propagate representation | 
|  | // information from call descriptor. | 
|  | for (int i = 1; i <= params; i++) { | 
|  | ProcessInput<T>(node, i, | 
|  | TruncatingUseInfoFromRepresentation( | 
|  | call_descriptor->GetInputType(i).representation())); | 
|  | } | 
|  |  | 
|  | // Rest of the value inputs. | 
|  | for (int i = params + 1; i < value_input_count; i++) { | 
|  | ProcessInput<T>(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  |  | 
|  | // Effect and Control. | 
|  | ProcessRemainingInputs<T>(node, value_input_count); | 
|  |  | 
|  | if (call_descriptor->ReturnCount() > 0) { | 
|  | SetOutput<T>(node, call_descriptor->GetReturnType(0).representation()); | 
|  | } else { | 
|  | SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  | } | 
|  | } | 
|  |  | 
|  | void MaskShiftOperand(Node* node, Type rhs_type) { | 
|  | if (!rhs_type.Is(type_cache_->kZeroToThirtyOne)) { | 
|  | Node* const rhs = NodeProperties::GetValueInput(node, 1); | 
|  | node->ReplaceInput(1, | 
|  | graph()->NewNode(jsgraph_->machine()->Word32And(), rhs, | 
|  | jsgraph_->Int32Constant(0x1F))); | 
|  | } | 
|  | } | 
|  |  | 
|  | static MachineSemantic DeoptValueSemanticOf(Type type) { | 
|  | // We only need signedness to do deopt correctly. | 
|  | if (type.Is(Type::Signed32())) { | 
|  | return MachineSemantic::kInt32; | 
|  | } else if (type.Is(Type::Unsigned32())) { | 
|  | return MachineSemantic::kUint32; | 
|  | } else { | 
|  | return MachineSemantic::kAny; | 
|  | } | 
|  | } | 
|  |  | 
|  | static MachineType DeoptMachineTypeOf(MachineRepresentation rep, Type type) { | 
|  | if (type.IsNone()) { | 
|  | return MachineType::None(); | 
|  | } | 
|  | // Do not distinguish between various Tagged variations. | 
|  | if (IsAnyTagged(rep)) { | 
|  | return MachineType::AnyTagged(); | 
|  | } | 
|  | if (rep == MachineRepresentation::kWord64) { | 
|  | if (type.Is(Type::BigInt())) { | 
|  | return MachineType::AnyTagged(); | 
|  | } | 
|  |  | 
|  | DCHECK(type.Is(TypeCache::Get()->kSafeInteger)); | 
|  | return MachineType(rep, MachineSemantic::kInt64); | 
|  | } | 
|  | MachineType machine_type(rep, DeoptValueSemanticOf(type)); | 
|  | DCHECK(machine_type.representation() != MachineRepresentation::kWord32 || | 
|  | machine_type.semantic() == MachineSemantic::kInt32 || | 
|  | machine_type.semantic() == MachineSemantic::kUint32); | 
|  | DCHECK(machine_type.representation() != MachineRepresentation::kBit || | 
|  | type.Is(Type::Boolean())); | 
|  | return machine_type; | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitStateValues(Node* node) { | 
|  | if (propagate<T>()) { | 
|  | for (int i = 0; i < node->InputCount(); i++) { | 
|  | // When lowering 64 bit BigInts to Word64 representation, we have to | 
|  | // make sure they are rematerialized before deoptimization. By | 
|  | // propagating a AnyTagged use, the RepresentationChanger is going to | 
|  | // insert the necessary conversions. | 
|  | // TODO(nicohartmann): Remove, once the deoptimizer can rematerialize | 
|  | // truncated BigInts. | 
|  | if (TypeOf(node->InputAt(i)).Is(Type::BigInt())) { | 
|  | EnqueueInput<T>(node, i, UseInfo::AnyTagged()); | 
|  | } else { | 
|  | EnqueueInput<T>(node, i, UseInfo::Any()); | 
|  | } | 
|  | } | 
|  | } else if (lower<T>()) { | 
|  | Zone* zone = jsgraph_->zone(); | 
|  | ZoneVector<MachineType>* types = | 
|  | zone->New<ZoneVector<MachineType>>(node->InputCount(), zone); | 
|  | for (int i = 0; i < node->InputCount(); i++) { | 
|  | Node* input = node->InputAt(i); | 
|  | // TODO(nicohartmann): Remove, once the deoptimizer can rematerialize | 
|  | // truncated BigInts. | 
|  | if (TypeOf(input).Is(Type::BigInt())) { | 
|  | ConvertInput(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  |  | 
|  | (*types)[i] = | 
|  | DeoptMachineTypeOf(GetInfo(input)->representation(), TypeOf(input)); | 
|  | } | 
|  | SparseInputMask mask = SparseInputMaskOf(node->op()); | 
|  | NodeProperties::ChangeOp( | 
|  | node, jsgraph_->common()->TypedStateValues(types, mask)); | 
|  | } | 
|  | SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitFrameState(Node* node) { | 
|  | DCHECK_EQ(5, node->op()->ValueInputCount()); | 
|  | DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op())); | 
|  |  | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());  // Parameters. | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged());  // Registers. | 
|  |  | 
|  | // Accumulator is a special flower - we need to remember its type in | 
|  | // a singleton typed-state-values node (as if it was a singleton | 
|  | // state-values node). | 
|  | Node* accumulator = node->InputAt(2); | 
|  | if (propagate<T>()) { | 
|  | // TODO(nicohartmann): Remove, once the deoptimizer can rematerialize | 
|  | // truncated BigInts. | 
|  | if (TypeOf(accumulator).Is(Type::BigInt())) { | 
|  | EnqueueInput<T>(node, 2, UseInfo::AnyTagged()); | 
|  | } else { | 
|  | EnqueueInput<T>(node, 2, UseInfo::Any()); | 
|  | } | 
|  | } else if (lower<T>()) { | 
|  | // TODO(nicohartmann): Remove, once the deoptimizer can rematerialize | 
|  | // truncated BigInts. | 
|  | if (TypeOf(accumulator).Is(Type::BigInt())) { | 
|  | ConvertInput(node, 2, UseInfo::AnyTagged()); | 
|  | } | 
|  | Zone* zone = jsgraph_->zone(); | 
|  | if (accumulator == jsgraph_->OptimizedOutConstant()) { | 
|  | node->ReplaceInput(2, jsgraph_->SingleDeadTypedStateValues()); | 
|  | } else { | 
|  | ZoneVector<MachineType>* types = | 
|  | zone->New<ZoneVector<MachineType>>(1, zone); | 
|  | (*types)[0] = DeoptMachineTypeOf(GetInfo(accumulator)->representation(), | 
|  | TypeOf(accumulator)); | 
|  |  | 
|  | node->ReplaceInput( | 
|  | 2, jsgraph_->graph()->NewNode(jsgraph_->common()->TypedStateValues( | 
|  | types, SparseInputMask::Dense()), | 
|  | node->InputAt(2))); | 
|  | } | 
|  | } | 
|  |  | 
|  | ProcessInput<T>(node, 3, UseInfo::AnyTagged());  // Context. | 
|  | ProcessInput<T>(node, 4, UseInfo::AnyTagged());  // Closure. | 
|  | ProcessInput<T>(node, 5, UseInfo::AnyTagged());  // Outer frame state. | 
|  | return SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitObjectState(Node* node) { | 
|  | if (propagate<T>()) { | 
|  | for (int i = 0; i < node->InputCount(); i++) { | 
|  | // TODO(nicohartmann): Remove, once the deoptimizer can rematerialize | 
|  | // truncated BigInts. | 
|  | if (TypeOf(node->InputAt(i)).Is(Type::BigInt())) { | 
|  | EnqueueInput<T>(node, i, UseInfo::AnyTagged()); | 
|  | } else { | 
|  | EnqueueInput<T>(node, i, UseInfo::Any()); | 
|  | } | 
|  | } | 
|  | } else if (lower<T>()) { | 
|  | Zone* zone = jsgraph_->zone(); | 
|  | ZoneVector<MachineType>* types = | 
|  | zone->New<ZoneVector<MachineType>>(node->InputCount(), zone); | 
|  | for (int i = 0; i < node->InputCount(); i++) { | 
|  | Node* input = node->InputAt(i); | 
|  | (*types)[i] = | 
|  | DeoptMachineTypeOf(GetInfo(input)->representation(), TypeOf(input)); | 
|  | // TODO(nicohartmann): Remove, once the deoptimizer can rematerialize | 
|  | // truncated BigInts. | 
|  | if (TypeOf(node->InputAt(i)).Is(Type::BigInt())) { | 
|  | ConvertInput(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  | } | 
|  | NodeProperties::ChangeOp(node, jsgraph_->common()->TypedObjectState( | 
|  | ObjectIdOf(node->op()), types)); | 
|  | } | 
|  | SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  | } | 
|  |  | 
|  | const Operator* Int32Op(Node* node) { | 
|  | return changer_->Int32OperatorFor(node->opcode()); | 
|  | } | 
|  |  | 
|  | const Operator* Int32OverflowOp(Node* node) { | 
|  | return changer_->Int32OverflowOperatorFor(node->opcode()); | 
|  | } | 
|  |  | 
|  | const Operator* Int64Op(Node* node) { | 
|  | return changer_->Int64OperatorFor(node->opcode()); | 
|  | } | 
|  |  | 
|  | const Operator* Uint32Op(Node* node) { | 
|  | return changer_->Uint32OperatorFor(node->opcode()); | 
|  | } | 
|  |  | 
|  | const Operator* Uint32OverflowOp(Node* node) { | 
|  | return changer_->Uint32OverflowOperatorFor(node->opcode()); | 
|  | } | 
|  |  | 
|  | const Operator* Float64Op(Node* node) { | 
|  | return changer_->Float64OperatorFor(node->opcode()); | 
|  | } | 
|  |  | 
|  | WriteBarrierKind WriteBarrierKindFor( | 
|  | BaseTaggedness base_taggedness, | 
|  | MachineRepresentation field_representation, Type field_type, | 
|  | MachineRepresentation value_representation, Node* value) { | 
|  | if (base_taggedness == kTaggedBase && | 
|  | CanBeTaggedPointer(field_representation)) { | 
|  | Type value_type = NodeProperties::GetType(value); | 
|  | if (value_representation == MachineRepresentation::kTaggedSigned) { | 
|  | // Write barriers are only for stores of heap objects. | 
|  | return kNoWriteBarrier; | 
|  | } | 
|  | if (field_type.Is(Type::BooleanOrNullOrUndefined()) || | 
|  | value_type.Is(Type::BooleanOrNullOrUndefined())) { | 
|  | // Write barriers are not necessary when storing true, false, null or | 
|  | // undefined, because these special oddballs are always in the root set. | 
|  | return kNoWriteBarrier; | 
|  | } | 
|  | if (value_type.IsHeapConstant()) { | 
|  | RootIndex root_index; | 
|  | const RootsTable& roots_table = jsgraph_->isolate()->roots_table(); | 
|  | if (roots_table.IsRootHandle(value_type.AsHeapConstant()->Value(), | 
|  | &root_index)) { | 
|  | if (RootsTable::IsImmortalImmovable(root_index)) { | 
|  | // Write barriers are unnecessary for immortal immovable roots. | 
|  | return kNoWriteBarrier; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (field_representation == MachineRepresentation::kTaggedPointer || | 
|  | value_representation == MachineRepresentation::kTaggedPointer) { | 
|  | // Write barriers for heap objects are cheaper. | 
|  | return kPointerWriteBarrier; | 
|  | } | 
|  | NumberMatcher m(value); | 
|  | if (m.HasResolvedValue()) { | 
|  | if (IsSmiDouble(m.ResolvedValue())) { | 
|  | // Storing a smi doesn't need a write barrier. | 
|  | return kNoWriteBarrier; | 
|  | } | 
|  | // The NumberConstant will be represented as HeapNumber. | 
|  | return kPointerWriteBarrier; | 
|  | } | 
|  | return kFullWriteBarrier; | 
|  | } | 
|  | return kNoWriteBarrier; | 
|  | } | 
|  |  | 
|  | WriteBarrierKind WriteBarrierKindFor( | 
|  | BaseTaggedness base_taggedness, | 
|  | MachineRepresentation field_representation, int field_offset, | 
|  | Type field_type, MachineRepresentation value_representation, | 
|  | Node* value) { | 
|  | WriteBarrierKind write_barrier_kind = | 
|  | WriteBarrierKindFor(base_taggedness, field_representation, field_type, | 
|  | value_representation, value); | 
|  | if (write_barrier_kind != kNoWriteBarrier) { | 
|  | if (base_taggedness == kTaggedBase && | 
|  | field_offset == HeapObject::kMapOffset) { | 
|  | write_barrier_kind = kMapWriteBarrier; | 
|  | } | 
|  | } | 
|  | return write_barrier_kind; | 
|  | } | 
|  |  | 
|  | Graph* graph() const { return jsgraph_->graph(); } | 
|  | CommonOperatorBuilder* common() const { return jsgraph_->common(); } | 
|  | SimplifiedOperatorBuilder* simplified() const { | 
|  | return jsgraph_->simplified(); | 
|  | } | 
|  |  | 
|  | void LowerToCheckedInt32Mul(Node* node, Truncation truncation, | 
|  | Type input0_type, Type input1_type) { | 
|  | // If one of the inputs is positive and/or truncation is being applied, | 
|  | // there is no need to return -0. | 
|  | CheckForMinusZeroMode mz_mode = | 
|  | truncation.IdentifiesZeroAndMinusZero() || | 
|  | IsSomePositiveOrderedNumber(input0_type) || | 
|  | IsSomePositiveOrderedNumber(input1_type) | 
|  | ? CheckForMinusZeroMode::kDontCheckForMinusZero | 
|  | : CheckForMinusZeroMode::kCheckForMinusZero; | 
|  | NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode)); | 
|  | } | 
|  |  | 
|  | void ChangeToInt32OverflowOp(Node* node) { | 
|  | NodeProperties::ChangeOp(node, Int32OverflowOp(node)); | 
|  | } | 
|  |  | 
|  | void ChangeToUint32OverflowOp(Node* node) { | 
|  | NodeProperties::ChangeOp(node, Uint32OverflowOp(node)); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitSpeculativeIntegerAdditiveOp(Node* node, Truncation truncation, | 
|  | SimplifiedLowering* lowering) { | 
|  | Type left_upper = GetUpperBound(node->InputAt(0)); | 
|  | Type right_upper = GetUpperBound(node->InputAt(1)); | 
|  |  | 
|  | if (left_upper.Is(type_cache_->kAdditiveSafeIntegerOrMinusZero) && | 
|  | right_upper.Is(type_cache_->kAdditiveSafeIntegerOrMinusZero)) { | 
|  | // Only eliminate the node if its typing rule can be satisfied, namely | 
|  | // that a safe integer is produced. | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  |  | 
|  | // If we know how to interpret the result or if the users only care | 
|  | // about the low 32-bits, we can truncate to Word32 do a wrapping | 
|  | // addition. | 
|  | if (GetUpperBound(node).Is(Type::Signed32()) || | 
|  | GetUpperBound(node).Is(Type::Unsigned32()) || | 
|  | truncation.IsUsedAsWord32()) { | 
|  | // => Int32Add/Sub | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Try to use type feedback. | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | DCHECK(hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32); | 
|  |  | 
|  | Type left_feedback_type = TypeOf(node->InputAt(0)); | 
|  | Type right_feedback_type = TypeOf(node->InputAt(1)); | 
|  |  | 
|  | // Using Signed32 as restriction type amounts to promising there won't be | 
|  | // signed overflow. This is incompatible with relying on a Word32 | 
|  | // truncation in order to skip the overflow check. | 
|  | Type const restriction = | 
|  | truncation.IsUsedAsWord32() ? Type::Any() : Type::Signed32(); | 
|  |  | 
|  | // Handle the case when no int32 checks on inputs are necessary (but | 
|  | // an overflow check is needed on the output). Note that we do not | 
|  | // have to do any check if at most one side can be minus zero. For | 
|  | // subtraction we need to handle the case of -0 - 0 properly, since | 
|  | // that can produce -0. | 
|  | Type left_constraint_type = | 
|  | node->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd | 
|  | ? Type::Signed32OrMinusZero() | 
|  | : Type::Signed32(); | 
|  | if (left_upper.Is(left_constraint_type) && | 
|  | right_upper.Is(Type::Signed32OrMinusZero()) && | 
|  | (left_upper.Is(Type::Signed32()) || right_upper.Is(Type::Signed32()))) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, restriction); | 
|  | } else { | 
|  | // If the output's truncation is identify-zeros, we can pass it | 
|  | // along. Moreover, if the operation is addition and we know the | 
|  | // right-hand side is not minus zero, we do not have to distinguish | 
|  | // between 0 and -0. | 
|  | IdentifyZeros left_identify_zeros = truncation.identify_zeros(); | 
|  | if (node->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd && | 
|  | !right_feedback_type.Maybe(Type::MinusZero())) { | 
|  | left_identify_zeros = kIdentifyZeros; | 
|  | } | 
|  | UseInfo left_use = CheckedUseInfoAsWord32FromHint(hint, FeedbackSource(), | 
|  | left_identify_zeros); | 
|  | // For CheckedInt32Add and CheckedInt32Sub, we don't need to do | 
|  | // a minus zero check for the right hand side, since we already | 
|  | // know that the left hand side is a proper Signed32 value, | 
|  | // potentially guarded by a check. | 
|  | UseInfo right_use = CheckedUseInfoAsWord32FromHint(hint, FeedbackSource(), | 
|  | kIdentifyZeros); | 
|  | VisitBinop<T>(node, left_use, right_use, MachineRepresentation::kWord32, | 
|  | restriction); | 
|  | } | 
|  |  | 
|  | if (lower<T>()) { | 
|  | if (truncation.IsUsedAsWord32() || | 
|  | !CanOverflowSigned32(node->op(), left_feedback_type, | 
|  | right_feedback_type, type_cache_, | 
|  | graph_zone())) { | 
|  | ChangeToPureOp(node, Int32Op(node)); | 
|  |  | 
|  | } else { | 
|  | ChangeToInt32OverflowOp(node); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation, | 
|  | SimplifiedLowering* lowering) { | 
|  | if (BothInputsAre(node, type_cache_->kAdditiveSafeIntegerOrMinusZero) && | 
|  | (GetUpperBound(node).Is(Type::Signed32()) || | 
|  | GetUpperBound(node).Is(Type::Unsigned32()) || | 
|  | truncation.IsUsedAsWord32())) { | 
|  | // => Int32Add/Sub | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // default case => Float64Add/Sub | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedNumberOrOddballAsFloat64(kDistinguishZeros, | 
|  | FeedbackSource()), | 
|  | MachineRepresentation::kFloat64, Type::Number()); | 
|  | if (lower<T>()) { | 
|  | ChangeToPureOp(node, Float64Op(node)); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitSpeculativeNumberModulus(Node* node, Truncation truncation, | 
|  | SimplifiedLowering* lowering) { | 
|  | if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN()) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | NodeProperties::GetType(node).Is(Type::Unsigned32()))) { | 
|  | // => unsigned Uint32Mod | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Uint32Mod(node)); | 
|  | return; | 
|  | } | 
|  | if (BothInputsAre(node, Type::Signed32OrMinusZeroOrNaN()) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | NodeProperties::GetType(node).Is(Type::Signed32()))) { | 
|  | // => signed Int32Mod | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Mod(node)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Try to use type feedback. | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  |  | 
|  | // Handle the case when no uint32 checks on inputs are necessary | 
|  | // (but an overflow check is needed on the output). | 
|  | if (BothInputsAreUnsigned32(node)) { | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Unsigned32()); | 
|  | if (lower<T>()) ChangeToUint32OverflowOp(node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle the case when no int32 checks on inputs are necessary | 
|  | // (but an overflow check is needed on the output). | 
|  | if (BothInputsAre(node, Type::Signed32())) { | 
|  | // If both the inputs the feedback are int32, use the overflow op. | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) ChangeToInt32OverflowOp(node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | // If the result is truncated, we only need to check the inputs. | 
|  | // For the left hand side we just propagate the identify zeros | 
|  | // mode of the {truncation}; and for modulus the sign of the | 
|  | // right hand side doesn't matter anyways, so in particular there's | 
|  | // no observable difference between a 0 and a -0 then. | 
|  | UseInfo const lhs_use = CheckedUseInfoAsWord32FromHint( | 
|  | hint, FeedbackSource(), truncation.identify_zeros()); | 
|  | UseInfo const rhs_use = CheckedUseInfoAsWord32FromHint( | 
|  | hint, FeedbackSource(), kIdentifyZeros); | 
|  | if (truncation.IsUsedAsWord32()) { | 
|  | VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Mod(node)); | 
|  | } else if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN())) { | 
|  | VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kWord32, | 
|  | Type::Unsigned32()); | 
|  | if (lower<T>()) ChangeToUint32OverflowOp(node); | 
|  | } else { | 
|  | VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kWord32, | 
|  | Type::Signed32()); | 
|  | if (lower<T>()) ChangeToInt32OverflowOp(node); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (TypeOf(node->InputAt(0)).Is(Type::Unsigned32()) && | 
|  | TypeOf(node->InputAt(1)).Is(Type::Unsigned32()) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | NodeProperties::GetType(node).Is(Type::Unsigned32()))) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Number()); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Uint32Mod(node)); | 
|  | return; | 
|  | } | 
|  | if (TypeOf(node->InputAt(0)).Is(Type::Signed32()) && | 
|  | TypeOf(node->InputAt(1)).Is(Type::Signed32()) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | NodeProperties::GetType(node).Is(Type::Signed32()))) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Number()); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Mod(node)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // default case => Float64Mod | 
|  | // For the left hand side we just propagate the identify zeros | 
|  | // mode of the {truncation}; and for modulus the sign of the | 
|  | // right hand side doesn't matter anyways, so in particular there's | 
|  | // no observable difference between a 0 and a -0 then. | 
|  | UseInfo const lhs_use = UseInfo::CheckedNumberOrOddballAsFloat64( | 
|  | truncation.identify_zeros(), FeedbackSource()); | 
|  | UseInfo const rhs_use = UseInfo::CheckedNumberOrOddballAsFloat64( | 
|  | kIdentifyZeros, FeedbackSource()); | 
|  | VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kFloat64, | 
|  | Type::Number()); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Just assert for Propagate and Retype. Lower specialized below. | 
|  | template <Phase T> | 
|  | void InsertUnreachableIfNecessary(Node* node) { | 
|  | static_assert(propagate<T>() || retype<T>(), | 
|  | "This version of InsertUnreachableIfNecessary has to be " | 
|  | "called in the Propagate or Retype phase."); | 
|  | } | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitCheckBounds(Node* node, SimplifiedLowering* lowering) { | 
|  | CheckBoundsParameters const& p = CheckBoundsParametersOf(node->op()); | 
|  | FeedbackSource const& feedback = p.check_parameters().feedback(); | 
|  | Type const index_type = TypeOf(node->InputAt(0)); | 
|  | Type const length_type = TypeOf(node->InputAt(1)); | 
|  |  | 
|  | // Conversions, if requested and needed, will be handled by the | 
|  | // representation changer, not by the lower-level Checked*Bounds operators. | 
|  | CheckBoundsFlags new_flags = | 
|  | p.flags().without(CheckBoundsFlag::kConvertStringAndMinusZero); | 
|  |  | 
|  | if (length_type.Is(Type::Unsigned31())) { | 
|  | if (index_type.Is(Type::Integral32()) || | 
|  | (index_type.Is(Type::Integral32OrMinusZero()) && | 
|  | p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero)) { | 
|  | // Map the values in the [-2^31,-1] range to the [2^31,2^32-1] range, | 
|  | // which will be considered out-of-bounds because the {length_type} is | 
|  | // limited to Unsigned31. This also converts -0 to 0. | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | if (lowering->poisoning_level_ == | 
|  | PoisoningMitigationLevel::kDontPoison && | 
|  | (index_type.IsNone() || length_type.IsNone() || | 
|  | (index_type.Min() >= 0.0 && | 
|  | index_type.Max() < length_type.Min()))) { | 
|  | // The bounds check is redundant if we already know that | 
|  | // the index is within the bounds of [0.0, length[. | 
|  | // TODO(neis): Move this into TypedOptimization? | 
|  | new_flags |= CheckBoundsFlag::kAbortOnOutOfBounds; | 
|  | } | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->CheckedUint32Bounds(feedback, new_flags)); | 
|  | } | 
|  | } else if (p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero) { | 
|  | VisitBinop<T>(node, UseInfo::CheckedTaggedAsArrayIndex(feedback), | 
|  | UseInfo::Word(), MachineType::PointerRepresentation()); | 
|  | if (lower<T>()) { | 
|  | if (jsgraph_->machine()->Is64()) { | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->CheckedUint64Bounds(feedback, new_flags)); | 
|  | } else { | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->CheckedUint32Bounds(feedback, new_flags)); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>( | 
|  | node, UseInfo::CheckedSigned32AsWord32(kDistinguishZeros, feedback), | 
|  | UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->CheckedUint32Bounds(feedback, new_flags)); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | CHECK(length_type.Is(type_cache_->kPositiveSafeInteger)); | 
|  | IdentifyZeros zero_handling = | 
|  | (p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero) | 
|  | ? kIdentifyZeros | 
|  | : kDistinguishZeros; | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedSigned64AsWord64(zero_handling, feedback), | 
|  | UseInfo::Word64(), MachineRepresentation::kWord64); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->CheckedUint64Bounds(feedback, new_flags)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static MachineType MachineTypeFor(CTypeInfo::Type type) { | 
|  | switch (type) { | 
|  | case CTypeInfo::Type::kVoid: | 
|  | return MachineType::AnyTagged(); | 
|  | case CTypeInfo::Type::kBool: | 
|  | return MachineType::Bool(); | 
|  | case CTypeInfo::Type::kInt32: | 
|  | return MachineType::Int32(); | 
|  | case CTypeInfo::Type::kUint32: | 
|  | return MachineType::Uint32(); | 
|  | case CTypeInfo::Type::kInt64: | 
|  | return MachineType::Int64(); | 
|  | case CTypeInfo::Type::kUint64: | 
|  | return MachineType::Uint64(); | 
|  | case CTypeInfo::Type::kFloat32: | 
|  | return MachineType::Float32(); | 
|  | case CTypeInfo::Type::kFloat64: | 
|  | return MachineType::Float64(); | 
|  | case CTypeInfo::Type::kV8Value: | 
|  | return MachineType::AnyTagged(); | 
|  | } | 
|  | } | 
|  |  | 
|  | UseInfo UseInfoForFastApiCallArgument(CTypeInfo::Type type, | 
|  | FeedbackSource const& feedback) { | 
|  | switch (type) { | 
|  | case CTypeInfo::Type::kVoid: | 
|  | UNREACHABLE(); | 
|  | case CTypeInfo::Type::kBool: | 
|  | return UseInfo::Bool(); | 
|  | case CTypeInfo::Type::kInt32: | 
|  | case CTypeInfo::Type::kUint32: | 
|  | return UseInfo::CheckedNumberAsWord32(feedback); | 
|  | // TODO(mslekova): We deopt for unsafe integers, but ultimately we want | 
|  | // to make this less restrictive in order to stay on the fast path. | 
|  | case CTypeInfo::Type::kInt64: | 
|  | case CTypeInfo::Type::kUint64: | 
|  | return UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, feedback); | 
|  | case CTypeInfo::Type::kFloat32: | 
|  | case CTypeInfo::Type::kFloat64: | 
|  | return UseInfo::CheckedNumberAsFloat64(kDistinguishZeros, feedback); | 
|  | case CTypeInfo::Type::kV8Value: | 
|  | return UseInfo::AnyTagged(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static constexpr int kInitialArgumentsCount = 10; | 
|  |  | 
|  | template <Phase T> | 
|  | void VisitFastApiCall(Node* node, SimplifiedLowering* lowering) { | 
|  | FastApiCallParameters const& op_params = | 
|  | FastApiCallParametersOf(node->op()); | 
|  | const CFunctionInfo* c_signature = op_params.signature(); | 
|  | const int c_arg_count = c_signature->ArgumentCount(); | 
|  | CallDescriptor* call_descriptor = op_params.descriptor(); | 
|  | int js_arg_count = static_cast<int>(call_descriptor->ParameterCount()); | 
|  | const int value_input_count = node->op()->ValueInputCount(); | 
|  | CHECK_EQ(FastApiCallNode::ArityForArgc(c_arg_count, js_arg_count), | 
|  | value_input_count); | 
|  |  | 
|  | base::SmallVector<UseInfo, kInitialArgumentsCount> arg_use_info( | 
|  | c_arg_count); | 
|  | // The target of the fast call. | 
|  | ProcessInput<T>(node, 0, UseInfo::Word()); | 
|  | // Propagate representation information from TypeInfo. | 
|  | for (int i = 0; i < c_arg_count; i++) { | 
|  | arg_use_info[i] = UseInfoForFastApiCallArgument( | 
|  | c_signature->ArgumentInfo(i).GetType(), op_params.feedback()); | 
|  | ProcessInput<T>(node, i + FastApiCallNode::kFastTargetInputCount, | 
|  | arg_use_info[i]); | 
|  | } | 
|  |  | 
|  | // The call code for the slow call. | 
|  | ProcessInput<T>(node, c_arg_count + FastApiCallNode::kFastTargetInputCount, | 
|  | UseInfo::AnyTagged()); | 
|  | for (int i = 1; i <= js_arg_count; i++) { | 
|  | ProcessInput<T>(node, | 
|  | c_arg_count + FastApiCallNode::kFastTargetInputCount + i, | 
|  | TruncatingUseInfoFromRepresentation( | 
|  | call_descriptor->GetInputType(i).representation())); | 
|  | } | 
|  | for (int i = c_arg_count + FastApiCallNode::kFastTargetInputCount + | 
|  | js_arg_count; | 
|  | i < value_input_count; ++i) { | 
|  | ProcessInput<T>(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  | ProcessRemainingInputs<T>(node, value_input_count); | 
|  |  | 
|  | MachineType return_type = | 
|  | MachineTypeFor(c_signature->ReturnInfo().GetType()); | 
|  | SetOutput<T>(node, return_type.representation()); | 
|  | } | 
|  |  | 
|  | // Dispatching routine for visiting the node {node} with the usage {use}. | 
|  | // Depending on the operator, propagate new usage info to the inputs. | 
|  | template <Phase T> | 
|  | void VisitNode(Node* node, Truncation truncation, | 
|  | SimplifiedLowering* lowering) { | 
|  | tick_counter_->TickAndMaybeEnterSafepoint(); | 
|  |  | 
|  | // Unconditionally eliminate unused pure nodes (only relevant if there's | 
|  | // a pure operation in between two effectful ones, where the last one | 
|  | // is unused). | 
|  | // Note: We must not do this for constants, as they are cached and we | 
|  | // would thus kill the cached {node} during lowering (i.e. replace all | 
|  | // uses with Dead), but at that point some node lowering might have | 
|  | // already taken the constant {node} from the cache (while it was not | 
|  | // yet killed) and we would afterwards replace that use with Dead as well. | 
|  | if (node->op()->ValueInputCount() > 0 && | 
|  | node->op()->HasProperty(Operator::kPure) && truncation.IsUnused()) { | 
|  | return VisitUnused<T>(node); | 
|  | } | 
|  |  | 
|  | if (lower<T>()) InsertUnreachableIfNecessary<T>(node); | 
|  |  | 
|  | switch (node->opcode()) { | 
|  | //------------------------------------------------------------------ | 
|  | // Common operators. | 
|  | //------------------------------------------------------------------ | 
|  | case IrOpcode::kStart: | 
|  | // We use Start as a terminator for the frame state chain, so even | 
|  | // tho Start doesn't really produce a value, we have to say Tagged | 
|  | // here, otherwise the input conversion will fail. | 
|  | return VisitLeaf<T>(node, MachineRepresentation::kTagged); | 
|  | case IrOpcode::kParameter: | 
|  | return VisitUnop<T>(node, UseInfo::None(), | 
|  | linkage() | 
|  | ->GetParameterType(ParameterIndexOf(node->op())) | 
|  | .representation()); | 
|  | case IrOpcode::kInt32Constant: | 
|  | return VisitLeaf<T>(node, MachineRepresentation::kWord32); | 
|  | case IrOpcode::kInt64Constant: | 
|  | return VisitLeaf<T>(node, MachineRepresentation::kWord64); | 
|  | case IrOpcode::kExternalConstant: | 
|  | return VisitLeaf<T>(node, MachineType::PointerRepresentation()); | 
|  | case IrOpcode::kNumberConstant: { | 
|  | double const value = OpParameter<double>(node->op()); | 
|  | int value_as_int; | 
|  | if (DoubleToSmiInteger(value, &value_as_int)) { | 
|  | VisitLeaf<T>(node, MachineRepresentation::kTaggedSigned); | 
|  | if (lower<T>()) { | 
|  | intptr_t smi = bit_cast<intptr_t>(Smi::FromInt(value_as_int)); | 
|  | DeferReplacement(node, lowering->jsgraph()->IntPtrConstant(smi)); | 
|  | } | 
|  | return; | 
|  | } | 
|  | VisitLeaf<T>(node, MachineRepresentation::kTagged); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kHeapConstant: | 
|  | case IrOpcode::kDelayedStringConstant: | 
|  | return VisitLeaf<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | case IrOpcode::kPointerConstant: { | 
|  | VisitLeaf<T>(node, MachineType::PointerRepresentation()); | 
|  | if (lower<T>()) { | 
|  | intptr_t const value = OpParameter<intptr_t>(node->op()); | 
|  | DeferReplacement(node, lowering->jsgraph()->IntPtrConstant(value)); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kBranch: { | 
|  | DCHECK(TypeOf(node->InputAt(0)).Is(Type::Boolean())); | 
|  | ProcessInput<T>(node, 0, UseInfo::Bool()); | 
|  | EnqueueInput<T>(node, NodeProperties::FirstControlIndex(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSwitch: | 
|  | ProcessInput<T>(node, 0, UseInfo::TruncatingWord32()); | 
|  | EnqueueInput<T>(node, NodeProperties::FirstControlIndex(node)); | 
|  | return; | 
|  | case IrOpcode::kSelect: | 
|  | return VisitSelect<T>(node, truncation, lowering); | 
|  | case IrOpcode::kPhi: | 
|  | return VisitPhi<T>(node, truncation, lowering); | 
|  | case IrOpcode::kCall: | 
|  | return VisitCall<T>(node, lowering); | 
|  |  | 
|  | //------------------------------------------------------------------ | 
|  | // JavaScript operators. | 
|  | //------------------------------------------------------------------ | 
|  | case IrOpcode::kToBoolean: { | 
|  | if (truncation.IsUsedAsBool()) { | 
|  | ProcessInput<T>(node, 0, UseInfo::Bool()); | 
|  | SetOutput<T>(node, MachineRepresentation::kBit); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitInputs<T>(node); | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kJSToNumber: | 
|  | case IrOpcode::kJSToNumberConvertBigInt: | 
|  | case IrOpcode::kJSToNumeric: { | 
|  | DCHECK(NodeProperties::GetType(node).Is(Type::Union( | 
|  | Type::BigInt(), Type::NumberOrOddball(), graph()->zone()))); | 
|  | VisitInputs<T>(node); | 
|  | // TODO(bmeurer): Optimize somewhat based on input type? | 
|  | if (truncation.IsUsedAsWord32()) { | 
|  | SetOutput<T>(node, MachineRepresentation::kWord32); | 
|  | if (lower<T>()) | 
|  | lowering->DoJSToNumberOrNumericTruncatesToWord32(node, this); | 
|  | } else if (truncation.TruncatesOddballAndBigIntToNumber()) { | 
|  | SetOutput<T>(node, MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) | 
|  | lowering->DoJSToNumberOrNumericTruncatesToFloat64(node, this); | 
|  | } else { | 
|  | SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | //------------------------------------------------------------------ | 
|  | // Simplified operators. | 
|  | //------------------------------------------------------------------ | 
|  | case IrOpcode::kBooleanNot: { | 
|  | if (lower<T>()) { | 
|  | NodeInfo* input_info = GetInfo(node->InputAt(0)); | 
|  | if (input_info->representation() == MachineRepresentation::kBit) { | 
|  | // BooleanNot(x: kRepBit) => Word32Equal(x, #0) | 
|  | node->AppendInput(jsgraph_->zone(), jsgraph_->Int32Constant(0)); | 
|  | NodeProperties::ChangeOp(node, lowering->machine()->Word32Equal()); | 
|  | } else if (CanBeTaggedPointer(input_info->representation())) { | 
|  | // BooleanNot(x: kRepTagged) => WordEqual(x, #false) | 
|  | node->AppendInput(jsgraph_->zone(), jsgraph_->FalseConstant()); | 
|  | NodeProperties::ChangeOp(node, lowering->machine()->WordEqual()); | 
|  | } else { | 
|  | DCHECK(TypeOf(node->InputAt(0)).IsNone()); | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } else { | 
|  | // No input representation requirement; adapt during lowering. | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTruncatingToBool()); | 
|  | SetOutput<T>(node, MachineRepresentation::kBit); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberEqual: { | 
|  | Type const lhs_type = TypeOf(node->InputAt(0)); | 
|  | Type const rhs_type = TypeOf(node->InputAt(1)); | 
|  | // Regular number comparisons in JavaScript generally identify zeros, | 
|  | // so we always pass kIdentifyZeros for the inputs, and in addition | 
|  | // we can truncate -0 to 0 for otherwise Unsigned32 or Signed32 inputs. | 
|  | // For equality we also handle the case that one side is non-zero, in | 
|  | // which case we allow to truncate NaN to 0 on the other side. | 
|  | if ((lhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZero())) || | 
|  | (lhs_type.Is(Type::Unsigned32OrMinusZeroOrNaN()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZeroOrNaN()) && | 
|  | OneInputCannotBe(node, type_cache_->kZeroish))) { | 
|  | // => unsigned Int32Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node)); | 
|  | return; | 
|  | } | 
|  | if ((lhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZero())) || | 
|  | (lhs_type.Is(Type::Signed32OrMinusZeroOrNaN()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZeroOrNaN()) && | 
|  | OneInputCannotBe(node, type_cache_->kZeroish))) { | 
|  | // => signed Int32Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  | // => Float64Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberLessThan: | 
|  | case IrOpcode::kNumberLessThanOrEqual: { | 
|  | Type const lhs_type = TypeOf(node->InputAt(0)); | 
|  | Type const rhs_type = TypeOf(node->InputAt(1)); | 
|  | // Regular number comparisons in JavaScript generally identify zeros, | 
|  | // so we always pass kIdentifyZeros for the inputs, and in addition | 
|  | // we can truncate -0 to 0 for otherwise Unsigned32 or Signed32 inputs. | 
|  | if (lhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZero())) { | 
|  | // => unsigned Int32Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node)); | 
|  | } else if (lhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZero())) { | 
|  | // => signed Int32Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Int32Op(node)); | 
|  | } else { | 
|  | // => Float64Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kSpeculativeSafeIntegerAdd: | 
|  | case IrOpcode::kSpeculativeSafeIntegerSubtract: | 
|  | return VisitSpeculativeIntegerAdditiveOp<T>(node, truncation, lowering); | 
|  |  | 
|  | case IrOpcode::kSpeculativeNumberAdd: | 
|  | case IrOpcode::kSpeculativeNumberSubtract: | 
|  | return VisitSpeculativeAdditiveOp<T>(node, truncation, lowering); | 
|  |  | 
|  | case IrOpcode::kSpeculativeNumberLessThan: | 
|  | case IrOpcode::kSpeculativeNumberLessThanOrEqual: | 
|  | case IrOpcode::kSpeculativeNumberEqual: { | 
|  | Type const lhs_type = TypeOf(node->InputAt(0)); | 
|  | Type const rhs_type = TypeOf(node->InputAt(1)); | 
|  | // Regular number comparisons in JavaScript generally identify zeros, | 
|  | // so we always pass kIdentifyZeros for the inputs, and in addition | 
|  | // we can truncate -0 to 0 for otherwise Unsigned32 or Signed32 inputs. | 
|  | if (lhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZero())) { | 
|  | // => unsigned Int32Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) ChangeToPureOp(node, Uint32Op(node)); | 
|  | return; | 
|  | } else if (lhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZero())) { | 
|  | // => signed Int32Cmp | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  | // Try to use type feedback. | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | switch (hint) { | 
|  | case NumberOperationHint::kSigned32: | 
|  | case NumberOperationHint::kSignedSmall: | 
|  | if (propagate<T>()) { | 
|  | VisitBinop<T>(node, | 
|  | CheckedUseInfoAsWord32FromHint( | 
|  | hint, FeedbackSource(), kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | } else if (retype<T>()) { | 
|  | SetOutput<T>(node, MachineRepresentation::kBit, Type::Any()); | 
|  | } else { | 
|  | DCHECK(lower<T>()); | 
|  | Node* lhs = node->InputAt(0); | 
|  | Node* rhs = node->InputAt(1); | 
|  | if (IsNodeRepresentationTagged(lhs) && | 
|  | IsNodeRepresentationTagged(rhs)) { | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedSignedSmallAsTaggedSigned( | 
|  | FeedbackSource(), kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | ChangeToPureOp( | 
|  | node, changer_->TaggedSignedOperatorFor(node->opcode())); | 
|  |  | 
|  | } else { | 
|  | VisitBinop<T>(node, | 
|  | CheckedUseInfoAsWord32FromHint( | 
|  | hint, FeedbackSource(), kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | ChangeToPureOp(node, Int32Op(node)); | 
|  | } | 
|  | } | 
|  | return; | 
|  | case NumberOperationHint::kSignedSmallInputs: | 
|  | // This doesn't make sense for compare operations. | 
|  | UNREACHABLE(); | 
|  | case NumberOperationHint::kNumberOrOddball: | 
|  | // Abstract and strict equality don't perform ToNumber conversions | 
|  | // on Oddballs, so make sure we don't accidentially sneak in a | 
|  | // hint with Oddball feedback here. | 
|  | DCHECK_NE(IrOpcode::kSpeculativeNumberEqual, node->opcode()); | 
|  | V8_FALLTHROUGH; | 
|  | case NumberOperationHint::kNumberOrBoolean: | 
|  | case NumberOperationHint::kNumber: | 
|  | VisitBinop<T>(node, | 
|  | CheckedUseInfoAsFloat64FromHint( | 
|  | hint, FeedbackSource(), kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kNumberAdd: | 
|  | case IrOpcode::kNumberSubtract: { | 
|  | if (TypeOf(node->InputAt(0)) | 
|  | .Is(type_cache_->kAdditiveSafeIntegerOrMinusZero) && | 
|  | TypeOf(node->InputAt(1)) | 
|  | .Is(type_cache_->kAdditiveSafeIntegerOrMinusZero) && | 
|  | (TypeOf(node).Is(Type::Signed32()) || | 
|  | TypeOf(node).Is(Type::Unsigned32()) || | 
|  | truncation.IsUsedAsWord32())) { | 
|  | // => Int32Add/Sub | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int32Op(node)); | 
|  | } else if (jsgraph_->machine()->Is64() && | 
|  | BothInputsAre(node, type_cache_->kSafeInteger) && | 
|  | GetUpperBound(node).Is(type_cache_->kSafeInteger)) { | 
|  | // => Int64Add/Sub | 
|  | VisitInt64Binop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int64Op(node)); | 
|  | } else { | 
|  | // => Float64Add/Sub | 
|  | VisitFloat64Binop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberMultiply: { | 
|  | if (BothInputsAre(node, Type::Integral32()) && | 
|  | (NodeProperties::GetType(node).Is(Type::Signed32()) || | 
|  | NodeProperties::GetType(node).Is(Type::Unsigned32()) || | 
|  | (truncation.IsUsedAsWord32() && | 
|  | NodeProperties::GetType(node).Is( | 
|  | type_cache_->kSafeIntegerOrMinusZero)))) { | 
|  | // Multiply reduces to Int32Mul if the inputs are integers, and | 
|  | // (a) the output is either known to be Signed32, or | 
|  | // (b) the output is known to be Unsigned32, or | 
|  | // (c) the uses are truncating and the result is in the safe | 
|  | //     integer range. | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  | // Try to use type feedback. | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | Type input0_type = TypeOf(node->InputAt(0)); | 
|  | Type input1_type = TypeOf(node->InputAt(1)); | 
|  |  | 
|  | // Handle the case when no int32 checks on inputs are necessary | 
|  | // (but an overflow check is needed on the output). | 
|  | if (BothInputsAre(node, Type::Signed32())) { | 
|  | // If both inputs and feedback are int32, use the overflow op. | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) { | 
|  | LowerToCheckedInt32Mul(node, truncation, input0_type, | 
|  | input1_type); | 
|  | } | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) { | 
|  | LowerToCheckedInt32Mul(node, truncation, input0_type, input1_type); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Checked float64 x float64 => float64 | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedNumberOrOddballAsFloat64( | 
|  | kDistinguishZeros, FeedbackSource()), | 
|  | MachineRepresentation::kFloat64, Type::Number()); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberMultiply: { | 
|  | if (TypeOf(node->InputAt(0)).Is(Type::Integral32()) && | 
|  | TypeOf(node->InputAt(1)).Is(Type::Integral32()) && | 
|  | (TypeOf(node).Is(Type::Signed32()) || | 
|  | TypeOf(node).Is(Type::Unsigned32()) || | 
|  | (truncation.IsUsedAsWord32() && | 
|  | TypeOf(node).Is(type_cache_->kSafeIntegerOrMinusZero)))) { | 
|  | // Multiply reduces to Int32Mul if the inputs are integers, and | 
|  | // (a) the output is either known to be Signed32, or | 
|  | // (b) the output is known to be Unsigned32, or | 
|  | // (c) the uses are truncating and the result is in the safe | 
|  | //     integer range. | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  | // Number x Number => Float64Mul | 
|  | VisitFloat64Binop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberDivide: { | 
|  | if (BothInputsAreUnsigned32(node) && truncation.IsUsedAsWord32()) { | 
|  | // => unsigned Uint32Div | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Uint32Div(node)); | 
|  | return; | 
|  | } | 
|  | if (BothInputsAreSigned32(node)) { | 
|  | if (NodeProperties::GetType(node).Is(Type::Signed32())) { | 
|  | // => signed Int32Div | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Div(node)); | 
|  | return; | 
|  | } | 
|  | if (truncation.IsUsedAsWord32()) { | 
|  | // => signed Int32Div | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Div(node)); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Try to use type feedback. | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  |  | 
|  | // Handle the case when no uint32 checks on inputs are necessary | 
|  | // (but an overflow check is needed on the output). | 
|  | if (BothInputsAreUnsigned32(node)) { | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Unsigned32()); | 
|  | if (lower<T>()) ChangeToUint32OverflowOp(node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle the case when no int32 checks on inputs are necessary | 
|  | // (but an overflow check is needed on the output). | 
|  | if (BothInputsAreSigned32(node)) { | 
|  | // If both the inputs the feedback are int32, use the overflow op. | 
|  | if (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) ChangeToInt32OverflowOp(node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hint == NumberOperationHint::kSigned32 || | 
|  | hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSignedSmallInputs) { | 
|  | // If the result is truncated, we only need to check the inputs. | 
|  | if (truncation.IsUsedAsWord32()) { | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Div(node)); | 
|  | return; | 
|  | } else if (hint != NumberOperationHint::kSignedSmallInputs) { | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) ChangeToInt32OverflowOp(node); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // default case => Float64Div | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedNumberOrOddballAsFloat64( | 
|  | kDistinguishZeros, FeedbackSource()), | 
|  | MachineRepresentation::kFloat64, Type::Number()); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberDivide: { | 
|  | if (TypeOf(node->InputAt(0)).Is(Type::Unsigned32()) && | 
|  | TypeOf(node->InputAt(1)).Is(Type::Unsigned32()) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | TypeOf(node).Is(Type::Unsigned32()))) { | 
|  | // => unsigned Uint32Div | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Uint32Div(node)); | 
|  | return; | 
|  | } | 
|  | if (TypeOf(node->InputAt(0)).Is(Type::Signed32()) && | 
|  | TypeOf(node->InputAt(1)).Is(Type::Signed32()) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | TypeOf(node).Is(Type::Signed32()))) { | 
|  | // => signed Int32Div | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Div(node)); | 
|  | return; | 
|  | } | 
|  | // Number x Number => Float64Div | 
|  | VisitFloat64Binop<T>(node); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberModulus: | 
|  | return VisitSpeculativeNumberModulus<T>(node, truncation, lowering); | 
|  | case IrOpcode::kNumberModulus: { | 
|  | Type const lhs_type = TypeOf(node->InputAt(0)); | 
|  | Type const rhs_type = TypeOf(node->InputAt(1)); | 
|  | if ((lhs_type.Is(Type::Unsigned32OrMinusZeroOrNaN()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZeroOrNaN())) && | 
|  | (truncation.IsUsedAsWord32() || | 
|  | TypeOf(node).Is(Type::Unsigned32()))) { | 
|  | // => unsigned Uint32Mod | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Uint32Mod(node)); | 
|  | return; | 
|  | } | 
|  | if ((lhs_type.Is(Type::Signed32OrMinusZeroOrNaN()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZeroOrNaN())) && | 
|  | (truncation.IsUsedAsWord32() || TypeOf(node).Is(Type::Signed32()) || | 
|  | (truncation.IdentifiesZeroAndMinusZero() && | 
|  | TypeOf(node).Is(Type::Signed32OrMinusZero())))) { | 
|  | // => signed Int32Mod | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Mod(node)); | 
|  | return; | 
|  | } | 
|  | // => Float64Mod | 
|  | // For the left hand side we just propagate the identify zeros | 
|  | // mode of the {truncation}; and for modulus the sign of the | 
|  | // right hand side doesn't matter anyways, so in particular there's | 
|  | // no observable difference between a 0 and a -0 then. | 
|  | UseInfo const lhs_use = | 
|  | UseInfo::TruncatingFloat64(truncation.identify_zeros()); | 
|  | UseInfo const rhs_use = UseInfo::TruncatingFloat64(kIdentifyZeros); | 
|  | VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) ChangeToPureOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberBitwiseOr: | 
|  | case IrOpcode::kNumberBitwiseXor: | 
|  | case IrOpcode::kNumberBitwiseAnd: { | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Int32Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberBitwiseOr: | 
|  | case IrOpcode::kSpeculativeNumberBitwiseXor: | 
|  | case IrOpcode::kSpeculativeNumberBitwiseAnd: | 
|  | VisitSpeculativeInt32Binop<T>(node); | 
|  | if (lower<T>()) { | 
|  | ChangeToPureOp(node, Int32Op(node)); | 
|  | } | 
|  | return; | 
|  | case IrOpcode::kNumberShiftLeft: { | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Shl()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberShiftLeft: { | 
|  | if (BothInputsAre(node, Type::NumberOrOddball())) { | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Shl()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Shl()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberShiftRight: { | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Sar()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberShiftRight: { | 
|  | if (BothInputsAre(node, Type::NumberOrOddball())) { | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Sar()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Sar()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberShiftRightLogical: { | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Shr()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeNumberShiftRightLogical: { | 
|  | NumberOperationHint hint = NumberOperationHintOf(node->op()); | 
|  | Type rhs_type = GetUpperBound(node->InputAt(1)); | 
|  | if (rhs_type.Is(type_cache_->kZeroish) && | 
|  | (hint == NumberOperationHint::kSignedSmall || | 
|  | hint == NumberOperationHint::kSigned32) && | 
|  | !truncation.IsUsedAsWord32()) { | 
|  | // The SignedSmall or Signed32 feedback means that the results that we | 
|  | // have seen so far were of type Unsigned31.  We speculate that this | 
|  | // will continue to hold.  Moreover, since the RHS is 0, the result | 
|  | // will just be the (converted) LHS. | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32, Type::Unsigned31()); | 
|  | if (lower<T>()) { | 
|  | node->RemoveInput(1); | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->CheckedUint32ToInt32(FeedbackSource())); | 
|  | } | 
|  | return; | 
|  | } | 
|  | if (BothInputsAre(node, Type::NumberOrOddball())) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Shr()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint), | 
|  | MachineRepresentation::kWord32, Type::Unsigned32()); | 
|  | if (lower<T>()) { | 
|  | MaskShiftOperand(node, rhs_type); | 
|  | ChangeToPureOp(node, lowering->machine()->Word32Shr()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberAbs: { | 
|  | // NumberAbs maps both 0 and -0 to 0, so we can generally | 
|  | // pass the kIdentifyZeros truncation to its input, and | 
|  | // choose to ignore minus zero in all cases. | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | if (input_type.Is(Type::Unsigned32OrMinusZero())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (input_type.Is(Type::Signed32OrMinusZero())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Abs(node)); | 
|  | } else if (input_type.Is(type_cache_->kPositiveIntegerOrNaN)) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberClz32: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberImul: { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingWord32(), | 
|  | UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberFround: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat32); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberMax: { | 
|  | // It is safe to use the feedback types for left and right hand side | 
|  | // here, since we can only narrow those types and thus we can only | 
|  | // promise a more specific truncation. | 
|  | // For NumberMax we generally propagate whether the truncation | 
|  | // identifies zeros to the inputs, and we choose to ignore minus | 
|  | // zero in those cases. | 
|  | Type const lhs_type = TypeOf(node->InputAt(0)); | 
|  | Type const rhs_type = TypeOf(node->InputAt(1)); | 
|  | if ((lhs_type.Is(Type::Unsigned32()) && | 
|  | rhs_type.Is(Type::Unsigned32())) || | 
|  | (lhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | truncation.IdentifiesZeroAndMinusZero())) { | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) { | 
|  | lowering->DoMax(node, lowering->machine()->Uint32LessThan(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  | } else if ((lhs_type.Is(Type::Signed32()) && | 
|  | rhs_type.Is(Type::Signed32())) || | 
|  | (lhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | truncation.IdentifiesZeroAndMinusZero())) { | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) { | 
|  | lowering->DoMax(node, lowering->machine()->Int32LessThan(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  | } else if (jsgraph_->machine()->Is64() && | 
|  | lhs_type.Is(type_cache_->kSafeInteger) && | 
|  | rhs_type.Is(type_cache_->kSafeInteger)) { | 
|  | VisitInt64Binop<T>(node); | 
|  | if (lower<T>()) { | 
|  | lowering->DoMax(node, lowering->machine()->Int64LessThan(), | 
|  | MachineRepresentation::kWord64); | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::TruncatingFloat64(truncation.identify_zeros()), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) { | 
|  | // If the right hand side is not NaN, and the left hand side | 
|  | // is not NaN (or -0 if the difference between the zeros is | 
|  | // observed), we can do a simple floating point comparison here. | 
|  | if (lhs_type.Is(truncation.IdentifiesZeroAndMinusZero() | 
|  | ? Type::OrderedNumber() | 
|  | : Type::PlainNumber()) && | 
|  | rhs_type.Is(Type::OrderedNumber())) { | 
|  | lowering->DoMax(node, lowering->machine()->Float64LessThan(), | 
|  | MachineRepresentation::kFloat64); | 
|  | } else { | 
|  | NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | } | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberMin: { | 
|  | // It is safe to use the feedback types for left and right hand side | 
|  | // here, since we can only narrow those types and thus we can only | 
|  | // promise a more specific truncation. | 
|  | // For NumberMin we generally propagate whether the truncation | 
|  | // identifies zeros to the inputs, and we choose to ignore minus | 
|  | // zero in those cases. | 
|  | Type const lhs_type = TypeOf(node->InputAt(0)); | 
|  | Type const rhs_type = TypeOf(node->InputAt(1)); | 
|  | if ((lhs_type.Is(Type::Unsigned32()) && | 
|  | rhs_type.Is(Type::Unsigned32())) || | 
|  | (lhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Unsigned32OrMinusZero()) && | 
|  | truncation.IdentifiesZeroAndMinusZero())) { | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) { | 
|  | lowering->DoMin(node, lowering->machine()->Uint32LessThan(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  | } else if ((lhs_type.Is(Type::Signed32()) && | 
|  | rhs_type.Is(Type::Signed32())) || | 
|  | (lhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | rhs_type.Is(Type::Signed32OrMinusZero()) && | 
|  | truncation.IdentifiesZeroAndMinusZero())) { | 
|  | VisitWord32TruncatingBinop<T>(node); | 
|  | if (lower<T>()) { | 
|  | lowering->DoMin(node, lowering->machine()->Int32LessThan(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  | } else if (jsgraph_->machine()->Is64() && | 
|  | lhs_type.Is(type_cache_->kSafeInteger) && | 
|  | rhs_type.Is(type_cache_->kSafeInteger)) { | 
|  | VisitInt64Binop<T>(node); | 
|  | if (lower<T>()) { | 
|  | lowering->DoMin(node, lowering->machine()->Int64LessThan(), | 
|  | MachineRepresentation::kWord64); | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::TruncatingFloat64(truncation.identify_zeros()), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) { | 
|  | // If the left hand side is not NaN, and the right hand side | 
|  | // is not NaN (or -0 if the difference between the zeros is | 
|  | // observed), we can do a simple floating point comparison here. | 
|  | if (lhs_type.Is(Type::OrderedNumber()) && | 
|  | rhs_type.Is(truncation.IdentifiesZeroAndMinusZero() | 
|  | ? Type::OrderedNumber() | 
|  | : Type::PlainNumber())) { | 
|  | lowering->DoMin(node, | 
|  | lowering->machine()->Float64LessThanOrEqual(), | 
|  | MachineRepresentation::kFloat64); | 
|  | } else { | 
|  | NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | } | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberAtan2: | 
|  | case IrOpcode::kNumberPow: { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberCeil: | 
|  | case IrOpcode::kNumberFloor: | 
|  | case IrOpcode::kNumberRound: | 
|  | case IrOpcode::kNumberTrunc: { | 
|  | // For NumberCeil, NumberFloor, NumberRound and NumberTrunc we propagate | 
|  | // the zero identification part of the truncation, and we turn them into | 
|  | // no-ops if we figure out (late) that their input is already an | 
|  | // integer, NaN or -0. | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | VisitUnop<T>(node, | 
|  | UseInfo::TruncatingFloat64(truncation.identify_zeros()), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) { | 
|  | if (input_type.Is(type_cache_->kIntegerOrMinusZeroOrNaN)) { | 
|  | DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (node->opcode() == IrOpcode::kNumberRound) { | 
|  | DeferReplacement(node, lowering->Float64Round(node)); | 
|  | } else { | 
|  | NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckBigInt: { | 
|  | if (InputIs(node, Type::BigInt())) { | 
|  | VisitNoop<T>(node, truncation); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kBigIntAsUintN: { | 
|  | ProcessInput<T>(node, 0, UseInfo::TruncatingWord64()); | 
|  | SetOutput<T>(node, MachineRepresentation::kWord64, Type::BigInt()); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberAcos: | 
|  | case IrOpcode::kNumberAcosh: | 
|  | case IrOpcode::kNumberAsin: | 
|  | case IrOpcode::kNumberAsinh: | 
|  | case IrOpcode::kNumberAtan: | 
|  | case IrOpcode::kNumberAtanh: | 
|  | case IrOpcode::kNumberCos: | 
|  | case IrOpcode::kNumberCosh: | 
|  | case IrOpcode::kNumberExp: | 
|  | case IrOpcode::kNumberExpm1: | 
|  | case IrOpcode::kNumberLog: | 
|  | case IrOpcode::kNumberLog1p: | 
|  | case IrOpcode::kNumberLog2: | 
|  | case IrOpcode::kNumberLog10: | 
|  | case IrOpcode::kNumberCbrt: | 
|  | case IrOpcode::kNumberSin: | 
|  | case IrOpcode::kNumberSinh: | 
|  | case IrOpcode::kNumberTan: | 
|  | case IrOpcode::kNumberTanh: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberSign: { | 
|  | if (InputIs(node, Type::Signed32())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Int32Sign(node)); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) DeferReplacement(node, lowering->Float64Sign(node)); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberSilenceNaN: { | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | if (input_type.Is(Type::OrderedNumber())) { | 
|  | // No need to silence anything if the input cannot be NaN. | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberSqrt: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberToBoolean: { | 
|  | // For NumberToBoolean we don't care whether the input is 0 or | 
|  | // -0, since both of them are mapped to false anyways, so we | 
|  | // can generally pass kIdentifyZeros truncation. | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | if (input_type.Is(Type::Integral32OrMinusZeroOrNaN())) { | 
|  | // 0, -0 and NaN all map to false, so we can safely truncate | 
|  | // all of them to zero here. | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) lowering->DoIntegral32ToBit(node); | 
|  | } else if (input_type.Is(Type::OrderedNumber())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) lowering->DoOrderedNumberToBit(node); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) lowering->DoNumberToBit(node); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberToInt32: { | 
|  | // Just change representation if necessary. | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberToString: { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberToUint32: { | 
|  | // Just change representation if necessary. | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberToUint8Clamped: { | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | if (input_type.Is(type_cache_->kUint8OrMinusZeroOrNaN)) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (input_type.Is(Type::Unsigned32OrMinusZeroOrNaN())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) lowering->DoUnsigned32ToUint8Clamped(node); | 
|  | } else if (input_type.Is(Type::Signed32OrMinusZeroOrNaN())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) lowering->DoSigned32ToUint8Clamped(node); | 
|  | } else if (input_type.Is(type_cache_->kIntegerOrMinusZeroOrNaN)) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) lowering->DoIntegerToUint8Clamped(node); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) lowering->DoNumberToUint8Clamped(node); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kReferenceEqual: { | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | if (COMPRESS_POINTERS_BOOL) { | 
|  | NodeProperties::ChangeOp(node, lowering->machine()->Word32Equal()); | 
|  | } else { | 
|  | NodeProperties::ChangeOp(node, lowering->machine()->WordEqual()); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSameValueNumbersOnly: { | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSameValue: { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | if (BothInputsAre(node, Type::Number())) { | 
|  | VisitBinop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | lowering->simplified()->NumberSameValue()); | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kTypeOf: { | 
|  | return VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | case IrOpcode::kTierUpCheck: { | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged()); | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged()); | 
|  | ProcessInput<T>(node, 2, UseInfo::AnyTagged()); | 
|  | ProcessInput<T>(node, 3, UseInfo::TruncatingWord32()); | 
|  | ProcessInput<T>(node, 4, UseInfo::AnyTagged()); | 
|  | ProcessRemainingInputs<T>(node, 5); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kUpdateInterruptBudget: { | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged()); | 
|  | ProcessRemainingInputs<T>(node, 1); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNewConsString: { | 
|  | ProcessInput<T>(node, 0, UseInfo::TruncatingWord32());  // length | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged());         // first | 
|  | ProcessInput<T>(node, 2, UseInfo::AnyTagged());         // second | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeBigIntAdd: { | 
|  | // TODO(nicohartmann@, chromium:1073440): There should be special | 
|  | // handling for trunction.IsUnused() that correctly propagates deadness, | 
|  | // but preserves type checking which may throw exceptions. Until this | 
|  | // is fully supported, we lower to int64 operations but keep pushing | 
|  | // type constraints. | 
|  | if (truncation.IsUsedAsWord64()) { | 
|  | VisitBinop<T>( | 
|  | node, UseInfo::CheckedBigIntTruncatingWord64(FeedbackSource{}), | 
|  | MachineRepresentation::kWord64); | 
|  | if (lower<T>()) { | 
|  | ChangeToPureOp(node, lowering->machine()->Int64Add()); | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, lowering->simplified()->BigIntAdd()); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeBigIntSubtract: { | 
|  | if (truncation.IsUsedAsWord64()) { | 
|  | VisitBinop<T>( | 
|  | node, UseInfo::CheckedBigIntTruncatingWord64(FeedbackSource{}), | 
|  | MachineRepresentation::kWord64); | 
|  | if (lower<T>()) { | 
|  | ChangeToPureOp(node, lowering->machine()->Int64Sub()); | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>(node, | 
|  | UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | lowering->simplified()->BigIntSubtract()); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeBigIntNegate: { | 
|  | if (truncation.IsUsedAsWord64()) { | 
|  | VisitUnop<T>(node, | 
|  | UseInfo::CheckedBigIntTruncatingWord64(FeedbackSource{}), | 
|  | MachineRepresentation::kWord64); | 
|  | if (lower<T>()) { | 
|  | ChangeUnaryToPureBinaryOp(node, lowering->machine()->Int64Sub(), 0, | 
|  | jsgraph_->Int64Constant(0)); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, | 
|  | UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | if (lower<T>()) { | 
|  | ChangeToPureOp(node, lowering->simplified()->BigIntNegate()); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringConcat: { | 
|  | // TODO(turbofan): We currently depend on having this first length input | 
|  | // to make sure that the overflow check is properly scheduled before the | 
|  | // actual string concatenation. We should also use the length to pass it | 
|  | // to the builtin or decide in optimized code how to construct the | 
|  | // resulting string (i.e. cons string or sequential string). | 
|  | ProcessInput<T>(node, 0, UseInfo::TaggedSigned());  // length | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged());     // first | 
|  | ProcessInput<T>(node, 2, UseInfo::AnyTagged());     // second | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringEqual: | 
|  | case IrOpcode::kStringLessThan: | 
|  | case IrOpcode::kStringLessThanOrEqual: { | 
|  | return VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | case IrOpcode::kStringCharCodeAt: { | 
|  | return VisitBinop<T>(node, UseInfo::AnyTagged(), UseInfo::Word(), | 
|  | MachineRepresentation::kWord32); | 
|  | } | 
|  | case IrOpcode::kStringCodePointAt: { | 
|  | return VisitBinop<T>(node, UseInfo::AnyTagged(), UseInfo::Word(), | 
|  | MachineRepresentation::kTaggedSigned); | 
|  | } | 
|  | case IrOpcode::kStringFromSingleCharCode: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringFromSingleCodePoint: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringFromCodePointAt: { | 
|  | return VisitBinop<T>(node, UseInfo::AnyTagged(), UseInfo::Word(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | case IrOpcode::kStringIndexOf: { | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged()); | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged()); | 
|  | ProcessInput<T>(node, 2, UseInfo::TaggedSigned()); | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedSigned); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringLength: { | 
|  | // TODO(bmeurer): The input representation should be TaggedPointer. | 
|  | // Fix this once we have a dedicated StringConcat/JSStringAdd | 
|  | // operator, which marks it's output as TaggedPointer properly. | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kWord32); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringSubstring: { | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged()); | 
|  | ProcessInput<T>(node, 1, UseInfo::TruncatingWord32()); | 
|  | ProcessInput<T>(node, 2, UseInfo::TruncatingWord32()); | 
|  | ProcessRemainingInputs<T>(node, 3); | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStringToLowerCaseIntl: | 
|  | case IrOpcode::kStringToUpperCaseIntl: { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckBounds: | 
|  | return VisitCheckBounds<T>(node, lowering); | 
|  | case IrOpcode::kPoisonIndex: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckHeapObject: { | 
|  | if (InputCannotBe(node, Type::SignedSmall())) { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } else { | 
|  | VisitUnop<T>( | 
|  | node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckIf: { | 
|  | ProcessInput<T>(node, 0, UseInfo::Bool()); | 
|  | ProcessRemainingInputs<T>(node, 1); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckInternalizedString: { | 
|  | VisitCheck<T>(node, Type::InternalizedString(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckNumber: { | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | if (input_type.Is(Type::Number())) { | 
|  | VisitNoop<T>(node, truncation); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckReceiver: { | 
|  | VisitCheck<T>(node, Type::Receiver(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckReceiverOrNullOrUndefined: { | 
|  | VisitCheck<T>(node, Type::ReceiverOrNullOrUndefined(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckSmi: { | 
|  | const CheckParameters& params = CheckParametersOf(node->op()); | 
|  | if (SmiValuesAre32Bits() && truncation.IsUsedAsWord32()) { | 
|  | VisitUnop<T>(node, | 
|  | UseInfo::CheckedSignedSmallAsWord32(kDistinguishZeros, | 
|  | params.feedback()), | 
|  | MachineRepresentation::kWord32); | 
|  | } else { | 
|  | VisitUnop<T>( | 
|  | node, | 
|  | UseInfo::CheckedSignedSmallAsTaggedSigned(params.feedback()), | 
|  | MachineRepresentation::kTaggedSigned); | 
|  | } | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckString: { | 
|  | const CheckParameters& params = CheckParametersOf(node->op()); | 
|  | if (InputIs(node, Type::String())) { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitUnop<T>( | 
|  | node, | 
|  | UseInfo::CheckedHeapObjectAsTaggedPointer(params.feedback()), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckSymbol: { | 
|  | VisitCheck<T>(node, Type::Symbol(), lowering); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kAllocate: { | 
|  | ProcessInput<T>(node, 0, UseInfo::Word()); | 
|  | ProcessRemainingInputs<T>(node, 1); | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadMessage: { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | VisitUnop<T>(node, UseInfo::Word(), MachineRepresentation::kTagged); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStoreMessage: { | 
|  | ProcessInput<T>(node, 0, UseInfo::Word()); | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged()); | 
|  | ProcessRemainingInputs<T>(node, 2); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadFieldByIndex: { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kTagged); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadField: { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | FieldAccess access = FieldAccessOf(node->op()); | 
|  | MachineRepresentation const representation = | 
|  | access.machine_type.representation(); | 
|  | VisitUnop<T>(node, UseInfoForBasePointer(access), representation); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStoreField: { | 
|  | FieldAccess access = FieldAccessOf(node->op()); | 
|  | Node* value_node = node->InputAt(1); | 
|  | NodeInfo* input_info = GetInfo(value_node); | 
|  | MachineRepresentation field_representation = | 
|  | access.machine_type.representation(); | 
|  |  | 
|  | // Convert to Smi if possible, such that we can avoid a write barrier. | 
|  | if (field_representation == MachineRepresentation::kTagged && | 
|  | TypeOf(value_node).Is(Type::SignedSmall())) { | 
|  | field_representation = MachineRepresentation::kTaggedSigned; | 
|  | } | 
|  | WriteBarrierKind write_barrier_kind = WriteBarrierKindFor( | 
|  | access.base_is_tagged, field_representation, access.offset, | 
|  | access.type, input_info->representation(), value_node); | 
|  |  | 
|  | ProcessInput<T>(node, 0, UseInfoForBasePointer(access)); | 
|  | ProcessInput<T>( | 
|  | node, 1, TruncatingUseInfoFromRepresentation(field_representation)); | 
|  | ProcessRemainingInputs<T>(node, 2); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | if (lower<T>()) { | 
|  | if (write_barrier_kind < access.write_barrier_kind) { | 
|  | access.write_barrier_kind = write_barrier_kind; | 
|  | NodeProperties::ChangeOp( | 
|  | node, jsgraph_->simplified()->StoreField(access)); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadElement: { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | ElementAccess access = ElementAccessOf(node->op()); | 
|  | VisitBinop<T>(node, UseInfoForBasePointer(access), UseInfo::Word(), | 
|  | access.machine_type.representation()); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadStackArgument: { | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | VisitBinop<T>(node, UseInfo::Word(), MachineRepresentation::kTagged); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStoreElement: { | 
|  | ElementAccess access = ElementAccessOf(node->op()); | 
|  | Node* value_node = node->InputAt(2); | 
|  | NodeInfo* input_info = GetInfo(value_node); | 
|  | MachineRepresentation element_representation = | 
|  | access.machine_type.representation(); | 
|  |  | 
|  | // Convert to Smi if possible, such that we can avoid a write barrier. | 
|  | if (element_representation == MachineRepresentation::kTagged && | 
|  | TypeOf(value_node).Is(Type::SignedSmall())) { | 
|  | element_representation = MachineRepresentation::kTaggedSigned; | 
|  | } | 
|  | WriteBarrierKind write_barrier_kind = WriteBarrierKindFor( | 
|  | access.base_is_tagged, element_representation, access.type, | 
|  | input_info->representation(), value_node); | 
|  | ProcessInput<T>(node, 0, UseInfoForBasePointer(access));  // base | 
|  | ProcessInput<T>(node, 1, UseInfo::Word());                // index | 
|  | ProcessInput<T>(node, 2, | 
|  | TruncatingUseInfoFromRepresentation( | 
|  | element_representation));  // value | 
|  | ProcessRemainingInputs<T>(node, 3); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | if (lower<T>()) { | 
|  | if (write_barrier_kind < access.write_barrier_kind) { | 
|  | access.write_barrier_kind = write_barrier_kind; | 
|  | NodeProperties::ChangeOp( | 
|  | node, jsgraph_->simplified()->StoreElement(access)); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberIsFloat64Hole: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kTransitionAndStoreElement: { | 
|  | Type value_type = TypeOf(node->InputAt(2)); | 
|  |  | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());  // array | 
|  | ProcessInput<T>(node, 1, UseInfo::Word());       // index | 
|  |  | 
|  | if (value_type.Is(Type::SignedSmall())) { | 
|  | ProcessInput<T>(node, 2, UseInfo::TruncatingWord32());  // value | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | simplified()->StoreSignedSmallElement()); | 
|  | } | 
|  | } else if (value_type.Is(Type::Number())) { | 
|  | ProcessInput<T>(node, 2, UseInfo::TruncatingFloat64());  // value | 
|  | if (lower<T>()) { | 
|  | Handle<Map> double_map = DoubleMapParameterOf(node->op()); | 
|  | NodeProperties::ChangeOp( | 
|  | node, | 
|  | simplified()->TransitionAndStoreNumberElement(double_map)); | 
|  | } | 
|  | } else if (value_type.Is(Type::NonNumber())) { | 
|  | ProcessInput<T>(node, 2, UseInfo::AnyTagged());  // value | 
|  | if (lower<T>()) { | 
|  | Handle<Map> fast_map = FastMapParameterOf(node->op()); | 
|  | NodeProperties::ChangeOp( | 
|  | node, simplified()->TransitionAndStoreNonNumberElement( | 
|  | fast_map, value_type)); | 
|  | } | 
|  | } else { | 
|  | ProcessInput<T>(node, 2, UseInfo::AnyTagged());  // value | 
|  | } | 
|  |  | 
|  | ProcessRemainingInputs<T>(node, 3); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadTypedElement: { | 
|  | MachineRepresentation const rep = | 
|  | MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op())); | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());  // buffer | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged());  // base pointer | 
|  | ProcessInput<T>(node, 2, UseInfo::Word());       // external pointer | 
|  | ProcessInput<T>(node, 3, UseInfo::Word());       // index | 
|  | ProcessRemainingInputs<T>(node, 4); | 
|  | SetOutput<T>(node, rep); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kLoadDataViewElement: { | 
|  | MachineRepresentation const rep = | 
|  | MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op())); | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());  // object | 
|  | ProcessInput<T>(node, 1, UseInfo::Word());       // base | 
|  | ProcessInput<T>(node, 2, UseInfo::Word());       // index | 
|  | ProcessInput<T>(node, 3, UseInfo::Bool());       // little-endian | 
|  | ProcessRemainingInputs<T>(node, 4); | 
|  | SetOutput<T>(node, rep); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStoreTypedElement: { | 
|  | MachineRepresentation const rep = | 
|  | MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op())); | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());  // buffer | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged());  // base pointer | 
|  | ProcessInput<T>(node, 2, UseInfo::Word());       // external pointer | 
|  | ProcessInput<T>(node, 3, UseInfo::Word());       // index | 
|  | ProcessInput<T>(node, 4, | 
|  | TruncatingUseInfoFromRepresentation(rep));  // value | 
|  | ProcessRemainingInputs<T>(node, 5); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kStoreDataViewElement: { | 
|  | MachineRepresentation const rep = | 
|  | MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op())); | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());  // object | 
|  | ProcessInput<T>(node, 1, UseInfo::Word());       // base | 
|  | ProcessInput<T>(node, 2, UseInfo::Word());       // index | 
|  | ProcessInput<T>(node, 3, | 
|  | TruncatingUseInfoFromRepresentation(rep));  // value | 
|  | ProcessInput<T>(node, 4, UseInfo::Bool());  // little-endian | 
|  | ProcessRemainingInputs<T>(node, 5); | 
|  | SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kConvertReceiver: { | 
|  | Type input_type = TypeOf(node->InputAt(0)); | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | if (lower<T>()) { | 
|  | // Try to optimize the {node} based on the input type. | 
|  | if (input_type.Is(Type::Receiver())) { | 
|  | DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (input_type.Is(Type::NullOrUndefined())) { | 
|  | DeferReplacement(node, node->InputAt(1)); | 
|  | } else if (!input_type.Maybe(Type::NullOrUndefined())) { | 
|  | NodeProperties::ChangeOp( | 
|  | node, lowering->simplified()->ConvertReceiver( | 
|  | ConvertReceiverMode::kNotNullOrUndefined)); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kPlainPrimitiveToNumber: { | 
|  | if (InputIs(node, Type::Boolean())) { | 
|  | VisitUnop<T>(node, UseInfo::Bool(), MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (InputIs(node, Type::String())) { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, simplified()->StringToNumber()); | 
|  | } | 
|  | } else if (truncation.IsUsedAsWord32()) { | 
|  | if (InputIs(node, Type::NumberOrOddball())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | simplified()->PlainPrimitiveToWord32()); | 
|  | } | 
|  | } | 
|  | } else if (truncation.TruncatesOddballAndBigIntToNumber()) { | 
|  | if (InputIs(node, Type::NumberOrOddball())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | simplified()->PlainPrimitiveToFloat64()); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kSpeculativeToNumber: { | 
|  | NumberOperationParameters const& p = | 
|  | NumberOperationParametersOf(node->op()); | 
|  | switch (p.hint()) { | 
|  | case NumberOperationHint::kSigned32: | 
|  | case NumberOperationHint::kSignedSmall: | 
|  | case NumberOperationHint::kSignedSmallInputs: | 
|  | VisitUnop<T>(node, | 
|  | CheckedUseInfoAsWord32FromHint(p.hint(), p.feedback()), | 
|  | MachineRepresentation::kWord32, Type::Signed32()); | 
|  | break; | 
|  | case NumberOperationHint::kNumber: | 
|  | case NumberOperationHint::kNumberOrBoolean: | 
|  | case NumberOperationHint::kNumberOrOddball: | 
|  | VisitUnop<T>( | 
|  | node, CheckedUseInfoAsFloat64FromHint(p.hint(), p.feedback()), | 
|  | MachineRepresentation::kFloat64); | 
|  | break; | 
|  | } | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsArrayBufferView: { | 
|  | // TODO(turbofan): Introduce a Type::ArrayBufferView? | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsBigInt: { | 
|  | VisitObjectIs<T>(node, Type::BigInt(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsCallable: { | 
|  | VisitObjectIs<T>(node, Type::Callable(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsConstructor: { | 
|  | // TODO(turbofan): Introduce a Type::Constructor? | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsDetectableCallable: { | 
|  | VisitObjectIs<T>(node, Type::DetectableCallable(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsFiniteNumber: { | 
|  | Type const input_type = GetUpperBound(node->InputAt(0)); | 
|  | if (input_type.Is(type_cache_->kSafeInteger)) { | 
|  | VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(1)); | 
|  | } | 
|  | } else if (!input_type.Maybe(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } else if (input_type.Is(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | lowering->simplified()->NumberIsFinite()); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberIsFinite: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsSafeInteger: { | 
|  | Type const input_type = GetUpperBound(node->InputAt(0)); | 
|  | if (input_type.Is(type_cache_->kSafeInteger)) { | 
|  | VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(1)); | 
|  | } | 
|  | } else if (!input_type.Maybe(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } else if (input_type.Is(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp( | 
|  | node, lowering->simplified()->NumberIsSafeInteger()); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberIsSafeInteger: { | 
|  | UNREACHABLE(); | 
|  | } | 
|  | case IrOpcode::kObjectIsInteger: { | 
|  | Type const input_type = GetUpperBound(node->InputAt(0)); | 
|  | if (input_type.Is(type_cache_->kSafeInteger)) { | 
|  | VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(1)); | 
|  | } | 
|  | } else if (!input_type.Maybe(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } else if (input_type.Is(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, | 
|  | lowering->simplified()->NumberIsInteger()); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberIsInteger: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsMinusZero: { | 
|  | Type const input_type = GetUpperBound(node->InputAt(0)); | 
|  | if (input_type.Is(Type::MinusZero())) { | 
|  | VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(1)); | 
|  | } | 
|  | } else if (!input_type.Maybe(Type::MinusZero())) { | 
|  | VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } else if (input_type.Is(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, simplified()->NumberIsMinusZero()); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsNaN: { | 
|  | Type const input_type = GetUpperBound(node->InputAt(0)); | 
|  | if (input_type.Is(Type::NaN())) { | 
|  | VisitUnop<T>(node, UseInfo::None(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(1)); | 
|  | } | 
|  | } else if (!input_type.Maybe(Type::NaN())) { | 
|  | VisitUnop<T>(node, UseInfo::Any(), MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | DeferReplacement(node, lowering->jsgraph()->Int32Constant(0)); | 
|  | } | 
|  | } else if (input_type.Is(Type::Number())) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp(node, simplified()->NumberIsNaN()); | 
|  | } | 
|  | } else { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNumberIsNaN: { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsNonCallable: { | 
|  | VisitObjectIs<T>(node, Type::NonCallable(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsNumber: { | 
|  | VisitObjectIs<T>(node, Type::Number(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsReceiver: { | 
|  | VisitObjectIs<T>(node, Type::Receiver(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsSmi: { | 
|  | // TODO(turbofan): Optimize based on input representation. | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsString: { | 
|  | VisitObjectIs<T>(node, Type::String(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsSymbol: { | 
|  | VisitObjectIs<T>(node, Type::Symbol(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kObjectIsUndetectable: { | 
|  | VisitObjectIs<T>(node, Type::Undetectable(), lowering); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kArgumentsFrame: { | 
|  | SetOutput<T>(node, MachineType::PointerRepresentation()); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kArgumentsLength: | 
|  | case IrOpcode::kRestLength: { | 
|  | VisitUnop<T>(node, UseInfo::Word(), | 
|  | MachineRepresentation::kTaggedSigned); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNewDoubleElements: | 
|  | case IrOpcode::kNewSmiOrObjectElements: { | 
|  | VisitUnop<T>(node, UseInfo::Word(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kNewArgumentsElements: { | 
|  | VisitBinop<T>(node, UseInfo::Word(), UseInfo::TaggedSigned(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckFloat64Hole: { | 
|  | Type const input_type = TypeOf(node->InputAt(0)); | 
|  | CheckFloat64HoleMode mode = | 
|  | CheckFloat64HoleParametersOf(node->op()).mode(); | 
|  | if (mode == CheckFloat64HoleMode::kAllowReturnHole) { | 
|  | // If {mode} is allow-return-hole _and_ the {truncation} | 
|  | // identifies NaN and undefined, we can just pass along | 
|  | // the {truncation} and completely wipe the {node}. | 
|  | if (truncation.IsUnused()) return VisitUnused<T>(node); | 
|  | if (truncation.TruncatesOddballAndBigIntToNumber()) { | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | return; | 
|  | } | 
|  | } | 
|  | VisitUnop<T>( | 
|  | node, UseInfo(MachineRepresentation::kFloat64, Truncation::Any()), | 
|  | MachineRepresentation::kFloat64, Type::Number()); | 
|  | if (lower<T>() && input_type.Is(Type::Number())) { | 
|  | DeferReplacement(node, node->InputAt(0)); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckNotTaggedHole: { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckClosure: { | 
|  | VisitUnop<T>( | 
|  | node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kConvertTaggedHoleToUndefined: { | 
|  | if (InputIs(node, Type::NumberOrOddball()) && | 
|  | truncation.IsUsedAsWord32()) { | 
|  | // Propagate the Word32 truncation. | 
|  | VisitUnop<T>(node, UseInfo::TruncatingWord32(), | 
|  | MachineRepresentation::kWord32); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (InputIs(node, Type::NumberOrOddball()) && | 
|  | truncation.TruncatesOddballAndBigIntToNumber()) { | 
|  | // Propagate the Float64 truncation. | 
|  | VisitUnop<T>(node, UseInfo::TruncatingFloat64(), | 
|  | MachineRepresentation::kFloat64); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else if (InputIs(node, Type::NonInternal())) { | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | if (lower<T>()) DeferReplacement(node, node->InputAt(0)); | 
|  | } else { | 
|  | // TODO(turbofan): Add a (Tagged) truncation that identifies hole | 
|  | // and undefined, i.e. for a[i] === obj cases. | 
|  | VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case IrOpcode::kCheckEqualsSymbol: | 
|  | case IrOpcode::kCheckEqualsInternalizedString: | 
|  | return VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kNone); | 
|  | case IrOpcode::kMapGuard: | 
|  | // Eliminate MapGuard nodes here. | 
|  | return VisitUnused<T>(node); | 
|  | case IrOpcode::kCheckMaps: { | 
|  | CheckMapsParameters const& p = CheckMapsParametersOf(node->op()); | 
|  | return VisitUnop<T>( | 
|  | node, UseInfo::CheckedHeapObjectAsTaggedPointer(p.feedback()), | 
|  | MachineRepresentation::kNone); | 
|  | } | 
|  | case IrOpcode::kDynamicCheckMaps: { | 
|  | return VisitUnop<T>( | 
|  | node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()), | 
|  | MachineRepresentation::kNone); | 
|  | } | 
|  | case IrOpcode::kTransitionElementsKind: { | 
|  | return VisitUnop<T>( | 
|  | node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()), | 
|  | MachineRepresentation::kNone); | 
|  | } | 
|  | case IrOpcode::kCompareMaps: | 
|  | return VisitUnop<T>( | 
|  | node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()), | 
|  | MachineRepresentation::kBit); | 
|  | case IrOpcode::kEnsureWritableFastElements: | 
|  | return VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedPointer); | 
|  | case IrOpcode::kMaybeGrowFastElements: { | 
|  | ProcessInput<T>(node, 0, UseInfo::AnyTagged());         // object | 
|  | ProcessInput<T>(node, 1, UseInfo::AnyTagged());         // elements | 
|  | ProcessInput<T>(node, 2, UseInfo::TruncatingWord32());  // index | 
|  | ProcessInput<T>(node, 3, UseInfo::TruncatingWord32());  // length | 
|  | ProcessRemainingInputs<T>(node, 4); | 
|  | SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kDateNow: | 
|  | VisitInputs<T>(node); | 
|  | return SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  | case IrOpcode::kFrameState: | 
|  | return VisitFrameState<T>(node); | 
|  | case IrOpcode::kStateValues: | 
|  | return VisitStateValues<T>(node); | 
|  | case IrOpcode::kObjectState: | 
|  | return VisitObjectState<T>(node); | 
|  | case IrOpcode::kObjectId: | 
|  | return SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  |  | 
|  | case IrOpcode::kTypeGuard: { | 
|  | // We just get rid of the sigma here, choosing the best representation | 
|  | // for the sigma's type. | 
|  | Type type = TypeOf(node); | 
|  | MachineRepresentation representation = | 
|  | GetOutputInfoForPhi(node, type, truncation); | 
|  |  | 
|  | // Here we pretend that the input has the sigma's type for the | 
|  | // conversion. | 
|  | UseInfo use(representation, truncation); | 
|  | if (propagate<T>()) { | 
|  | EnqueueInput<T>(node, 0, use); | 
|  | } else if (lower<T>()) { | 
|  | ConvertInput(node, 0, use, type); | 
|  | } | 
|  | ProcessRemainingInputs<T>(node, 1); | 
|  | SetOutput<T>(node, representation); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kFoldConstant: | 
|  | VisitInputs<T>(node); | 
|  | return SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  |  | 
|  | case IrOpcode::kFinishRegion: | 
|  | VisitInputs<T>(node); | 
|  | // Assume the output is tagged pointer. | 
|  | return SetOutput<T>(node, MachineRepresentation::kTaggedPointer); | 
|  |  | 
|  | case IrOpcode::kReturn: | 
|  | VisitReturn<T>(node); | 
|  | // Assume the output is tagged. | 
|  | return SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  |  | 
|  | case IrOpcode::kFindOrderedHashMapEntry: { | 
|  | Type const key_type = TypeOf(node->InputAt(1)); | 
|  | if (key_type.Is(Type::Signed32OrMinusZero())) { | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(), | 
|  | MachineType::PointerRepresentation()); | 
|  | if (lower<T>()) { | 
|  | NodeProperties::ChangeOp( | 
|  | node, | 
|  | lowering->simplified()->FindOrderedHashMapEntryForInt32Key()); | 
|  | } | 
|  | } else { | 
|  | VisitBinop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTaggedSigned); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | case IrOpcode::kFastApiCall: { | 
|  | VisitFastApiCall<T>(node, lowering); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Operators with all inputs tagged and no or tagged output have uniform | 
|  | // handling. | 
|  | case IrOpcode::kEnd: | 
|  | case IrOpcode::kIfSuccess: | 
|  | case IrOpcode::kIfException: | 
|  | case IrOpcode::kIfTrue: | 
|  | case IrOpcode::kIfFalse: | 
|  | case IrOpcode::kIfValue: | 
|  | case IrOpcode::kIfDefault: | 
|  | case IrOpcode::kDeoptimize: | 
|  | case IrOpcode::kEffectPhi: | 
|  | case IrOpcode::kTerminate: | 
|  | case IrOpcode::kCheckpoint: | 
|  | case IrOpcode::kLoop: | 
|  | case IrOpcode::kMerge: | 
|  | case IrOpcode::kThrow: | 
|  | case IrOpcode::kBeginRegion: | 
|  | case IrOpcode::kProjection: | 
|  | case IrOpcode::kOsrValue: | 
|  | case IrOpcode::kArgumentsElementsState: | 
|  | case IrOpcode::kArgumentsLengthState: | 
|  | case IrOpcode::kUnreachable: | 
|  | case IrOpcode::kRuntimeAbort: | 
|  | // All JavaScript operators except JSToNumber have uniform handling. | 
|  | #define OPCODE_CASE(name, ...) case IrOpcode::k##name: | 
|  | JS_SIMPLE_BINOP_LIST(OPCODE_CASE) | 
|  | JS_OBJECT_OP_LIST(OPCODE_CASE) | 
|  | JS_CONTEXT_OP_LIST(OPCODE_CASE) | 
|  | JS_OTHER_OP_LIST(OPCODE_CASE) | 
|  | #undef OPCODE_CASE | 
|  | case IrOpcode::kJSBitwiseNot: | 
|  | case IrOpcode::kJSDecrement: | 
|  | case IrOpcode::kJSIncrement: | 
|  | case IrOpcode::kJSNegate: | 
|  | case IrOpcode::kJSToLength: | 
|  | case IrOpcode::kJSToName: | 
|  | case IrOpcode::kJSToObject: | 
|  | case IrOpcode::kJSToString: | 
|  | case IrOpcode::kJSParseInt: | 
|  | VisitInputs<T>(node); | 
|  | // Assume the output is tagged. | 
|  | return SetOutput<T>(node, MachineRepresentation::kTagged); | 
|  | case IrOpcode::kDeadValue: | 
|  | ProcessInput<T>(node, 0, UseInfo::Any()); | 
|  | return SetOutput<T>(node, MachineRepresentation::kNone); | 
|  | case IrOpcode::kStaticAssert: | 
|  | return VisitUnop<T>(node, UseInfo::Any(), | 
|  | MachineRepresentation::kTagged); | 
|  | case IrOpcode::kAssertType: | 
|  | return VisitUnop<T>(node, UseInfo::AnyTagged(), | 
|  | MachineRepresentation::kTagged); | 
|  | default: | 
|  | FATAL( | 
|  | "Representation inference: unsupported opcode %i (%s), node #%i\n.", | 
|  | node->opcode(), node->op()->mnemonic(), node->id()); | 
|  | break; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void DeferReplacement(Node* node, Node* replacement) { | 
|  | TRACE("defer replacement #%d:%s with #%d:%s\n", node->id(), | 
|  | node->op()->mnemonic(), replacement->id(), | 
|  | replacement->op()->mnemonic()); | 
|  |  | 
|  | // Disconnect the node from effect and control chains, if necessary. | 
|  | if (node->op()->EffectInputCount() > 0) { | 
|  | DCHECK_LT(0, node->op()->ControlInputCount()); | 
|  | // Disconnect the node from effect and control chains. | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | ReplaceEffectControlUses(node, effect, control); | 
|  | } | 
|  |  | 
|  | replacements_.push_back(node); | 
|  | replacements_.push_back(replacement); | 
|  |  | 
|  | node->NullAllInputs();  // Node is now dead. | 
|  | } | 
|  |  | 
|  | void Kill(Node* node) { | 
|  | TRACE("killing #%d:%s\n", node->id(), node->op()->mnemonic()); | 
|  |  | 
|  | if (node->op()->EffectInputCount() == 1) { | 
|  | DCHECK_LT(0, node->op()->ControlInputCount()); | 
|  | // Disconnect the node from effect and control chains. | 
|  | Node* control = NodeProperties::GetControlInput(node); | 
|  | Node* effect = NodeProperties::GetEffectInput(node); | 
|  | ReplaceEffectControlUses(node, effect, control); | 
|  | } else { | 
|  | DCHECK_EQ(0, node->op()->EffectInputCount()); | 
|  | DCHECK_EQ(0, node->op()->ControlOutputCount()); | 
|  | DCHECK_EQ(0, node->op()->EffectOutputCount()); | 
|  | } | 
|  |  | 
|  | node->ReplaceUses(jsgraph_->Dead()); | 
|  |  | 
|  | node->NullAllInputs();  // The {node} is now dead. | 
|  | } | 
|  |  | 
|  | private: | 
|  | JSGraph* jsgraph_; | 
|  | Zone* zone_;                      // Temporary zone. | 
|  | // Map from node to its uses that might need to be revisited. | 
|  | ZoneMap<Node*, ZoneVector<Node*>> might_need_revisit_; | 
|  | size_t const count_;              // number of nodes in the graph | 
|  | ZoneVector<NodeInfo> info_;       // node id -> usage information | 
|  | #ifdef DEBUG | 
|  | ZoneVector<InputUseInfos> node_input_use_infos_;  // Debug information about | 
|  | // requirements on inputs. | 
|  | #endif                                              // DEBUG | 
|  | NodeVector replacements_;         // replacements to be done after lowering | 
|  | RepresentationChanger* changer_;  // for inserting representation changes | 
|  | ZoneQueue<Node*> revisit_queue_;  // Queue for revisiting nodes. | 
|  |  | 
|  | struct NodeState { | 
|  | Node* node; | 
|  | int input_index; | 
|  | }; | 
|  | NodeVector traversal_nodes_;  // Order in which to traverse the nodes. | 
|  | // TODO(danno): RepresentationSelector shouldn't know anything about the | 
|  | // source positions table, but must for now since there currently is no other | 
|  | // way to pass down source position information to nodes created during | 
|  | // lowering. Once this phase becomes a vanilla reducer, it should get source | 
|  | // position information via the SourcePositionWrapper like all other reducers. | 
|  | SourcePositionTable* source_positions_; | 
|  | NodeOriginTable* node_origins_; | 
|  | TypeCache const* type_cache_; | 
|  | OperationTyper op_typer_;  // helper for the feedback typer | 
|  | TickCounter* const tick_counter_; | 
|  | Linkage* const linkage_; | 
|  |  | 
|  | NodeInfo* GetInfo(Node* node) { | 
|  | DCHECK(node->id() < count_); | 
|  | return &info_[node->id()]; | 
|  | } | 
|  | Zone* zone() { return zone_; } | 
|  | Zone* graph_zone() { return jsgraph_->zone(); } | 
|  | Linkage* linkage() { return linkage_; } | 
|  | }; | 
|  |  | 
|  | // Template specializations | 
|  |  | 
|  | // Enqueue {use_node}'s {index} input if the {use_info} contains new information | 
|  | // for that input node. | 
|  | template <> | 
|  | void RepresentationSelector::EnqueueInput<PROPAGATE>(Node* use_node, int index, | 
|  | UseInfo use_info) { | 
|  | Node* node = use_node->InputAt(index); | 
|  | NodeInfo* info = GetInfo(node); | 
|  | #ifdef DEBUG | 
|  | // Check monotonicity of input requirements. | 
|  | node_input_use_infos_[use_node->id()].SetAndCheckInput(use_node, index, | 
|  | use_info); | 
|  | #endif  // DEBUG | 
|  | if (info->unvisited()) { | 
|  | info->AddUse(use_info); | 
|  | TRACE("  initial #%i: %s\n", node->id(), info->truncation().description()); | 
|  | return; | 
|  | } | 
|  | TRACE("   queue #%i?: %s\n", node->id(), info->truncation().description()); | 
|  | if (info->AddUse(use_info)) { | 
|  | // New usage information for the node is available. | 
|  | if (!info->queued()) { | 
|  | DCHECK(info->visited()); | 
|  | revisit_queue_.push(node); | 
|  | info->set_queued(); | 
|  | TRACE("   added: %s\n", info->truncation().description()); | 
|  | } else { | 
|  | TRACE(" inqueue: %s\n", info->truncation().description()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::SetOutput<PROPAGATE>( | 
|  | Node* node, MachineRepresentation representation, Type restriction_type) { | 
|  | NodeInfo* const info = GetInfo(node); | 
|  | info->set_restriction_type(restriction_type); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::SetOutput<RETYPE>( | 
|  | Node* node, MachineRepresentation representation, Type restriction_type) { | 
|  | NodeInfo* const info = GetInfo(node); | 
|  | DCHECK(info->restriction_type().Is(restriction_type)); | 
|  | DCHECK(restriction_type.Is(info->restriction_type())); | 
|  | info->set_output(representation); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::SetOutput<LOWER>( | 
|  | Node* node, MachineRepresentation representation, Type restriction_type) { | 
|  | NodeInfo* const info = GetInfo(node); | 
|  | DCHECK_EQ(info->representation(), representation); | 
|  | DCHECK(info->restriction_type().Is(restriction_type)); | 
|  | DCHECK(restriction_type.Is(info->restriction_type())); | 
|  | USE(info); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::ProcessInput<PROPAGATE>(Node* node, int index, | 
|  | UseInfo use) { | 
|  | DCHECK_IMPLIES(use.type_check() != TypeCheckKind::kNone, | 
|  | !node->op()->HasProperty(Operator::kNoDeopt) && | 
|  | node->op()->EffectInputCount() > 0); | 
|  | EnqueueInput<PROPAGATE>(node, index, use); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::ProcessInput<RETYPE>(Node* node, int index, | 
|  | UseInfo use) { | 
|  | DCHECK_IMPLIES(use.type_check() != TypeCheckKind::kNone, | 
|  | !node->op()->HasProperty(Operator::kNoDeopt) && | 
|  | node->op()->EffectInputCount() > 0); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::ProcessInput<LOWER>(Node* node, int index, | 
|  | UseInfo use) { | 
|  | DCHECK_IMPLIES(use.type_check() != TypeCheckKind::kNone, | 
|  | !node->op()->HasProperty(Operator::kNoDeopt) && | 
|  | node->op()->EffectInputCount() > 0); | 
|  | ConvertInput(node, index, use); | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::ProcessRemainingInputs<PROPAGATE>(Node* node, | 
|  | int index) { | 
|  | DCHECK_GE(index, NodeProperties::PastContextIndex(node)); | 
|  |  | 
|  | // Enqueue other inputs (effects, control). | 
|  | for (int i = std::max(index, NodeProperties::FirstEffectIndex(node)); | 
|  | i < node->InputCount(); ++i) { | 
|  | EnqueueInput<PROPAGATE>(node, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | // The default, most general visitation case. For {node}, process all value, | 
|  | // context, frame state, effect, and control inputs, assuming that value | 
|  | // inputs should have {kRepTagged} representation and can observe all output | 
|  | // values {kTypeAny}. | 
|  | template <> | 
|  | void RepresentationSelector::VisitInputs<PROPAGATE>(Node* node) { | 
|  | int first_effect_index = NodeProperties::FirstEffectIndex(node); | 
|  | // Visit value, context and frame state inputs as tagged. | 
|  | for (int i = 0; i < first_effect_index; i++) { | 
|  | ProcessInput<PROPAGATE>(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  | // Only enqueue other inputs (effects, control). | 
|  | for (int i = first_effect_index; i < node->InputCount(); i++) { | 
|  | EnqueueInput<PROPAGATE>(node, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::VisitInputs<LOWER>(Node* node) { | 
|  | int first_effect_index = NodeProperties::FirstEffectIndex(node); | 
|  | // Visit value, context and frame state inputs as tagged. | 
|  | for (int i = 0; i < first_effect_index; i++) { | 
|  | ProcessInput<LOWER>(node, i, UseInfo::AnyTagged()); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <> | 
|  | void RepresentationSelector::InsertUnreachableIfNecessary<LOWER>(Node* node) { | 
|  | // If the node is effectful and it produces an impossible value, then we | 
|  | // insert Unreachable node after it. | 
|  | if (node->op()->ValueOutputCount() > 0 && | 
|  | node->op()->EffectOutputCount() > 0 && | 
|  | node->opcode() != IrOpcode::kUnreachable && TypeOf(node).IsNone()) { | 
|  | Node* control = (node->op()->ControlOutputCount() == 0) | 
|  | ? NodeProperties::GetControlInput(node, 0) | 
|  | : NodeProperties::FindSuccessfulControlProjection(node); | 
|  |  | 
|  | Node* unreachable = | 
|  | graph()->NewNode(common()->Unreachable(), node, control); | 
|  |  | 
|  | // Insert unreachable node and replace all the effect uses of the {node} | 
|  | // with the new unreachable node. | 
|  | for (Edge edge : node->use_edges()) { | 
|  | if (!NodeProperties::IsEffectEdge(edge)) continue; | 
|  | // Make sure to not overwrite the unreachable node's input. That would | 
|  | // create a cycle. | 
|  | if (edge.from() == unreachable) continue; | 
|  | // Avoid messing up the exceptional path. | 
|  | if (edge.from()->opcode() == IrOpcode::kIfException) { | 
|  | DCHECK(!node->op()->HasProperty(Operator::kNoThrow)); | 
|  | DCHECK_EQ(NodeProperties::GetControlInput(edge.from()), node); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | edge.UpdateTo(unreachable); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, JSHeapBroker* broker, | 
|  | Zone* zone, | 
|  | SourcePositionTable* source_positions, | 
|  | NodeOriginTable* node_origins, | 
|  | PoisoningMitigationLevel poisoning_level, | 
|  | TickCounter* tick_counter, | 
|  | Linkage* linkage) | 
|  | : jsgraph_(jsgraph), | 
|  | broker_(broker), | 
|  | zone_(zone), | 
|  | type_cache_(TypeCache::Get()), | 
|  | source_positions_(source_positions), | 
|  | node_origins_(node_origins), | 
|  | poisoning_level_(poisoning_level), | 
|  | tick_counter_(tick_counter), | 
|  | linkage_(linkage) {} | 
|  |  | 
|  | void SimplifiedLowering::LowerAllNodes() { | 
|  | RepresentationChanger changer(jsgraph(), broker_); | 
|  | RepresentationSelector selector(jsgraph(), broker_, zone_, &changer, | 
|  | source_positions_, node_origins_, | 
|  | tick_counter_, linkage_); | 
|  | selector.Run(this); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64( | 
|  | Node* node, RepresentationSelector* selector) { | 
|  | DCHECK(node->opcode() == IrOpcode::kJSToNumber || | 
|  | node->opcode() == IrOpcode::kJSToNumberConvertBigInt || | 
|  | node->opcode() == IrOpcode::kJSToNumeric); | 
|  | Node* value = node->InputAt(0); | 
|  | Node* context = node->InputAt(1); | 
|  | Node* frame_state = node->InputAt(2); | 
|  | Node* effect = node->InputAt(3); | 
|  | Node* control = node->InputAt(4); | 
|  |  | 
|  | Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value); | 
|  | Node* branch0 = | 
|  | graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); | 
|  |  | 
|  | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
|  | Node* etrue0 = effect; | 
|  | Node* vtrue0; | 
|  | { | 
|  | vtrue0 = graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), value); | 
|  | vtrue0 = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue0); | 
|  | } | 
|  |  | 
|  | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
|  | Node* efalse0 = effect; | 
|  | Node* vfalse0; | 
|  | { | 
|  | Operator const* op = | 
|  | node->opcode() == IrOpcode::kJSToNumber | 
|  | ? (node->opcode() == IrOpcode::kJSToNumberConvertBigInt | 
|  | ? ToNumberConvertBigIntOperator() | 
|  | : ToNumberOperator()) | 
|  | : ToNumericOperator(); | 
|  | Node* code = node->opcode() == IrOpcode::kJSToNumber | 
|  | ? ToNumberCode() | 
|  | : (node->opcode() == IrOpcode::kJSToNumberConvertBigInt | 
|  | ? ToNumberConvertBigIntCode() | 
|  | : ToNumericCode()); | 
|  | vfalse0 = efalse0 = if_false0 = graph()->NewNode( | 
|  | op, code, value, context, frame_state, efalse0, if_false0); | 
|  |  | 
|  | // Update potential {IfException} uses of {node} to point to the above | 
|  | // stub call node instead. | 
|  | Node* on_exception = nullptr; | 
|  | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { | 
|  | NodeProperties::ReplaceControlInput(on_exception, vfalse0); | 
|  | NodeProperties::ReplaceEffectInput(on_exception, efalse0); | 
|  | if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0); | 
|  | } | 
|  |  | 
|  | Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0); | 
|  | Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); | 
|  |  | 
|  | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
|  | Node* etrue1 = efalse0; | 
|  | Node* vtrue1; | 
|  | { | 
|  | vtrue1 = | 
|  | graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), vfalse0); | 
|  | vtrue1 = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue1); | 
|  | } | 
|  |  | 
|  | Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
|  | Node* efalse1 = efalse0; | 
|  | Node* vfalse1; | 
|  | { | 
|  | vfalse1 = efalse1 = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), efalse0, | 
|  | efalse1, if_false1); | 
|  | } | 
|  |  | 
|  | if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); | 
|  | efalse0 = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); | 
|  | vfalse0 = | 
|  | graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), | 
|  | vtrue1, vfalse1, if_false0); | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); | 
|  | effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); | 
|  | value = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), | 
|  | vtrue0, vfalse0, control); | 
|  |  | 
|  | // Replace effect and control uses appropriately. | 
|  | for (Edge edge : node->use_edges()) { | 
|  | if (NodeProperties::IsControlEdge(edge)) { | 
|  | if (edge.from()->opcode() == IrOpcode::kIfSuccess) { | 
|  | edge.from()->ReplaceUses(control); | 
|  | edge.from()->Kill(); | 
|  | } else { | 
|  | DCHECK_NE(IrOpcode::kIfException, edge.from()->opcode()); | 
|  | edge.UpdateTo(control); | 
|  | } | 
|  | } else if (NodeProperties::IsEffectEdge(edge)) { | 
|  | edge.UpdateTo(effect); | 
|  | } | 
|  | } | 
|  |  | 
|  | selector->DeferReplacement(node, value); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32( | 
|  | Node* node, RepresentationSelector* selector) { | 
|  | DCHECK(node->opcode() == IrOpcode::kJSToNumber || | 
|  | node->opcode() == IrOpcode::kJSToNumberConvertBigInt || | 
|  | node->opcode() == IrOpcode::kJSToNumeric); | 
|  | Node* value = node->InputAt(0); | 
|  | Node* context = node->InputAt(1); | 
|  | Node* frame_state = node->InputAt(2); | 
|  | Node* effect = node->InputAt(3); | 
|  | Node* control = node->InputAt(4); | 
|  |  | 
|  | Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value); | 
|  | Node* branch0 = | 
|  | graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); | 
|  |  | 
|  | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
|  | Node* etrue0 = effect; | 
|  | Node* vtrue0 = | 
|  | graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), value); | 
|  |  | 
|  | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
|  | Node* efalse0 = effect; | 
|  | Node* vfalse0; | 
|  | { | 
|  | Operator const* op = | 
|  | node->opcode() == IrOpcode::kJSToNumber | 
|  | ? (node->opcode() == IrOpcode::kJSToNumberConvertBigInt | 
|  | ? ToNumberConvertBigIntOperator() | 
|  | : ToNumberOperator()) | 
|  | : ToNumericOperator(); | 
|  | Node* code = node->opcode() == IrOpcode::kJSToNumber | 
|  | ? ToNumberCode() | 
|  | : (node->opcode() == IrOpcode::kJSToNumberConvertBigInt | 
|  | ? ToNumberConvertBigIntCode() | 
|  | : ToNumericCode()); | 
|  | vfalse0 = efalse0 = if_false0 = graph()->NewNode( | 
|  | op, code, value, context, frame_state, efalse0, if_false0); | 
|  |  | 
|  | // Update potential {IfException} uses of {node} to point to the above | 
|  | // stub call node instead. | 
|  | Node* on_exception = nullptr; | 
|  | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { | 
|  | NodeProperties::ReplaceControlInput(on_exception, vfalse0); | 
|  | NodeProperties::ReplaceEffectInput(on_exception, efalse0); | 
|  | if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0); | 
|  | } | 
|  |  | 
|  | Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0); | 
|  | Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); | 
|  |  | 
|  | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
|  | Node* etrue1 = efalse0; | 
|  | Node* vtrue1 = | 
|  | graph()->NewNode(simplified()->ChangeTaggedSignedToInt32(), vfalse0); | 
|  |  | 
|  | Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
|  | Node* efalse1 = efalse0; | 
|  | Node* vfalse1; | 
|  | { | 
|  | vfalse1 = efalse1 = graph()->NewNode( | 
|  | simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), efalse0, | 
|  | efalse1, if_false1); | 
|  | vfalse1 = graph()->NewNode(machine()->TruncateFloat64ToWord32(), vfalse1); | 
|  | } | 
|  |  | 
|  | if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); | 
|  | efalse0 = | 
|  | graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); | 
|  | vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), | 
|  | vtrue1, vfalse1, if_false0); | 
|  | } | 
|  |  | 
|  | control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); | 
|  | effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); | 
|  | value = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), | 
|  | vtrue0, vfalse0, control); | 
|  |  | 
|  | // Replace effect and control uses appropriately. | 
|  | for (Edge edge : node->use_edges()) { | 
|  | if (NodeProperties::IsControlEdge(edge)) { | 
|  | if (edge.from()->opcode() == IrOpcode::kIfSuccess) { | 
|  | edge.from()->ReplaceUses(control); | 
|  | edge.from()->Kill(); | 
|  | } else { | 
|  | DCHECK_NE(IrOpcode::kIfException, edge.from()->opcode()); | 
|  | edge.UpdateTo(control); | 
|  | } | 
|  | } else if (NodeProperties::IsEffectEdge(edge)) { | 
|  | edge.UpdateTo(effect); | 
|  | } | 
|  | } | 
|  |  | 
|  | selector->DeferReplacement(node, value); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Float64Round(Node* const node) { | 
|  | Node* const one = jsgraph()->Float64Constant(1.0); | 
|  | Node* const one_half = jsgraph()->Float64Constant(0.5); | 
|  | Node* const input = node->InputAt(0); | 
|  |  | 
|  | // Round up towards Infinity, and adjust if the difference exceeds 0.5. | 
|  | Node* result = graph()->NewNode(machine()->Float64RoundUp().placeholder(), | 
|  | node->InputAt(0)); | 
|  | return graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kFloat64), | 
|  | graph()->NewNode( | 
|  | machine()->Float64LessThanOrEqual(), | 
|  | graph()->NewNode(machine()->Float64Sub(), result, one_half), input), | 
|  | result, graph()->NewNode(machine()->Float64Sub(), result, one)); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Float64Sign(Node* const node) { | 
|  | Node* const minus_one = jsgraph()->Float64Constant(-1.0); | 
|  | Node* const zero = jsgraph()->Float64Constant(0.0); | 
|  | Node* const one = jsgraph()->Float64Constant(1.0); | 
|  |  | 
|  | Node* const input = node->InputAt(0); | 
|  |  | 
|  | return graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kFloat64), | 
|  | graph()->NewNode(machine()->Float64LessThan(), input, zero), minus_one, | 
|  | graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kFloat64), | 
|  | graph()->NewNode(machine()->Float64LessThan(), zero, input), one, | 
|  | input)); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Int32Abs(Node* const node) { | 
|  | Node* const input = node->InputAt(0); | 
|  |  | 
|  | // Generate case for absolute integer value. | 
|  | // | 
|  | //    let sign = input >> 31 in | 
|  | //    (input ^ sign) - sign | 
|  |  | 
|  | Node* sign = graph()->NewNode(machine()->Word32Sar(), input, | 
|  | jsgraph()->Int32Constant(31)); | 
|  | return graph()->NewNode(machine()->Int32Sub(), | 
|  | graph()->NewNode(machine()->Word32Xor(), input, sign), | 
|  | sign); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Int32Div(Node* const node) { | 
|  | Int32BinopMatcher m(node); | 
|  | Node* const zero = jsgraph()->Int32Constant(0); | 
|  | Node* const minus_one = jsgraph()->Int32Constant(-1); | 
|  | Node* const lhs = m.left().node(); | 
|  | Node* const rhs = m.right().node(); | 
|  |  | 
|  | if (m.right().Is(-1)) { | 
|  | return graph()->NewNode(machine()->Int32Sub(), zero, lhs); | 
|  | } else if (m.right().Is(0)) { | 
|  | return rhs; | 
|  | } else if (machine()->Int32DivIsSafe() || m.right().HasResolvedValue()) { | 
|  | return graph()->NewNode(machine()->Int32Div(), lhs, rhs, graph()->start()); | 
|  | } | 
|  |  | 
|  | // General case for signed integer division. | 
|  | // | 
|  | //    if 0 < rhs then | 
|  | //      lhs / rhs | 
|  | //    else | 
|  | //      if rhs < -1 then | 
|  | //        lhs / rhs | 
|  | //      else if rhs == 0 then | 
|  | //        0 | 
|  | //      else | 
|  | //        0 - lhs | 
|  | // | 
|  | // Note: We do not use the Diamond helper class here, because it really hurts | 
|  | // readability with nested diamonds. | 
|  | const Operator* const merge_op = common()->Merge(2); | 
|  | const Operator* const phi_op = | 
|  | common()->Phi(MachineRepresentation::kWord32, 2); | 
|  |  | 
|  | Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs); | 
|  | Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, | 
|  | graph()->start()); | 
|  |  | 
|  | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
|  | Node* true0 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_true0); | 
|  |  | 
|  | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
|  | Node* false0; | 
|  | { | 
|  | Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one); | 
|  | Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); | 
|  |  | 
|  | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
|  | Node* true1 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_true1); | 
|  |  | 
|  | Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
|  | Node* false1; | 
|  | { | 
|  | Node* check2 = graph()->NewNode(machine()->Word32Equal(), rhs, zero); | 
|  | Node* branch2 = graph()->NewNode(common()->Branch(), check2, if_false1); | 
|  |  | 
|  | Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); | 
|  | Node* true2 = zero; | 
|  |  | 
|  | Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); | 
|  | Node* false2 = graph()->NewNode(machine()->Int32Sub(), zero, lhs); | 
|  |  | 
|  | if_false1 = graph()->NewNode(merge_op, if_true2, if_false2); | 
|  | false1 = graph()->NewNode(phi_op, true2, false2, if_false1); | 
|  | } | 
|  |  | 
|  | if_false0 = graph()->NewNode(merge_op, if_true1, if_false1); | 
|  | false0 = graph()->NewNode(phi_op, true1, false1, if_false0); | 
|  | } | 
|  |  | 
|  | Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0); | 
|  | return graph()->NewNode(phi_op, true0, false0, merge0); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Int32Mod(Node* const node) { | 
|  | Int32BinopMatcher m(node); | 
|  | Node* const zero = jsgraph()->Int32Constant(0); | 
|  | Node* const minus_one = jsgraph()->Int32Constant(-1); | 
|  | Node* const lhs = m.left().node(); | 
|  | Node* const rhs = m.right().node(); | 
|  |  | 
|  | if (m.right().Is(-1) || m.right().Is(0)) { | 
|  | return zero; | 
|  | } else if (m.right().HasResolvedValue()) { | 
|  | return graph()->NewNode(machine()->Int32Mod(), lhs, rhs, graph()->start()); | 
|  | } | 
|  |  | 
|  | // General case for signed integer modulus, with optimization for (unknown) | 
|  | // power of 2 right hand side. | 
|  | // | 
|  | //   if 0 < rhs then | 
|  | //     msk = rhs - 1 | 
|  | //     if rhs & msk != 0 then | 
|  | //       lhs % rhs | 
|  | //     else | 
|  | //       if lhs < 0 then | 
|  | //         -(-lhs & msk) | 
|  | //       else | 
|  | //         lhs & msk | 
|  | //   else | 
|  | //     if rhs < -1 then | 
|  | //       lhs % rhs | 
|  | //     else | 
|  | //       zero | 
|  | // | 
|  | // Note: We do not use the Diamond helper class here, because it really hurts | 
|  | // readability with nested diamonds. | 
|  | const Operator* const merge_op = common()->Merge(2); | 
|  | const Operator* const phi_op = | 
|  | common()->Phi(MachineRepresentation::kWord32, 2); | 
|  |  | 
|  | Node* check0 = graph()->NewNode(machine()->Int32LessThan(), zero, rhs); | 
|  | Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, | 
|  | graph()->start()); | 
|  |  | 
|  | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
|  | Node* true0; | 
|  | { | 
|  | Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one); | 
|  |  | 
|  | Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk); | 
|  | Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0); | 
|  |  | 
|  | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
|  | Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1); | 
|  |  | 
|  | Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
|  | Node* false1; | 
|  | { | 
|  | Node* check2 = graph()->NewNode(machine()->Int32LessThan(), lhs, zero); | 
|  | Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse), | 
|  | check2, if_false1); | 
|  |  | 
|  | Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); | 
|  | Node* true2 = graph()->NewNode( | 
|  | machine()->Int32Sub(), zero, | 
|  | graph()->NewNode(machine()->Word32And(), | 
|  | graph()->NewNode(machine()->Int32Sub(), zero, lhs), | 
|  | msk)); | 
|  |  | 
|  | Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); | 
|  | Node* false2 = graph()->NewNode(machine()->Word32And(), lhs, msk); | 
|  |  | 
|  | if_false1 = graph()->NewNode(merge_op, if_true2, if_false2); | 
|  | false1 = graph()->NewNode(phi_op, true2, false2, if_false1); | 
|  | } | 
|  |  | 
|  | if_true0 = graph()->NewNode(merge_op, if_true1, if_false1); | 
|  | true0 = graph()->NewNode(phi_op, true1, false1, if_true0); | 
|  | } | 
|  |  | 
|  | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
|  | Node* false0; | 
|  | { | 
|  | Node* check1 = graph()->NewNode(machine()->Int32LessThan(), rhs, minus_one); | 
|  | Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 
|  | check1, if_false0); | 
|  |  | 
|  | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
|  | Node* true1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_true1); | 
|  |  | 
|  | Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
|  | Node* false1 = zero; | 
|  |  | 
|  | if_false0 = graph()->NewNode(merge_op, if_true1, if_false1); | 
|  | false0 = graph()->NewNode(phi_op, true1, false1, if_false0); | 
|  | } | 
|  |  | 
|  | Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0); | 
|  | return graph()->NewNode(phi_op, true0, false0, merge0); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Int32Sign(Node* const node) { | 
|  | Node* const minus_one = jsgraph()->Int32Constant(-1); | 
|  | Node* const zero = jsgraph()->Int32Constant(0); | 
|  | Node* const one = jsgraph()->Int32Constant(1); | 
|  |  | 
|  | Node* const input = node->InputAt(0); | 
|  |  | 
|  | return graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kWord32), | 
|  | graph()->NewNode(machine()->Int32LessThan(), input, zero), minus_one, | 
|  | graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kWord32), | 
|  | graph()->NewNode(machine()->Int32LessThan(), zero, input), one, | 
|  | zero)); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Uint32Div(Node* const node) { | 
|  | Uint32BinopMatcher m(node); | 
|  | Node* const zero = jsgraph()->Uint32Constant(0); | 
|  | Node* const lhs = m.left().node(); | 
|  | Node* const rhs = m.right().node(); | 
|  |  | 
|  | if (m.right().Is(0)) { | 
|  | return zero; | 
|  | } else if (machine()->Uint32DivIsSafe() || m.right().HasResolvedValue()) { | 
|  | return graph()->NewNode(machine()->Uint32Div(), lhs, rhs, graph()->start()); | 
|  | } | 
|  |  | 
|  | Node* check = graph()->NewNode(machine()->Word32Equal(), rhs, zero); | 
|  | Diamond d(graph(), common(), check, BranchHint::kFalse); | 
|  | Node* div = graph()->NewNode(machine()->Uint32Div(), lhs, rhs, d.if_false); | 
|  | return d.Phi(MachineRepresentation::kWord32, zero, div); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::Uint32Mod(Node* const node) { | 
|  | Uint32BinopMatcher m(node); | 
|  | Node* const minus_one = jsgraph()->Int32Constant(-1); | 
|  | Node* const zero = jsgraph()->Uint32Constant(0); | 
|  | Node* const lhs = m.left().node(); | 
|  | Node* const rhs = m.right().node(); | 
|  |  | 
|  | if (m.right().Is(0)) { | 
|  | return zero; | 
|  | } else if (m.right().HasResolvedValue()) { | 
|  | return graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, graph()->start()); | 
|  | } | 
|  |  | 
|  | // General case for unsigned integer modulus, with optimization for (unknown) | 
|  | // power of 2 right hand side. | 
|  | // | 
|  | //   if rhs == 0 then | 
|  | //     zero | 
|  | //   else | 
|  | //     msk = rhs - 1 | 
|  | //     if rhs & msk != 0 then | 
|  | //       lhs % rhs | 
|  | //     else | 
|  | //       lhs & msk | 
|  | // | 
|  | // Note: We do not use the Diamond helper class here, because it really hurts | 
|  | // readability with nested diamonds. | 
|  | const Operator* const merge_op = common()->Merge(2); | 
|  | const Operator* const phi_op = | 
|  | common()->Phi(MachineRepresentation::kWord32, 2); | 
|  |  | 
|  | Node* check0 = graph()->NewNode(machine()->Word32Equal(), rhs, zero); | 
|  | Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, | 
|  | graph()->start()); | 
|  |  | 
|  | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
|  | Node* true0 = zero; | 
|  |  | 
|  | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
|  | Node* false0; | 
|  | { | 
|  | Node* msk = graph()->NewNode(machine()->Int32Add(), rhs, minus_one); | 
|  |  | 
|  | Node* check1 = graph()->NewNode(machine()->Word32And(), rhs, msk); | 
|  | Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); | 
|  |  | 
|  | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
|  | Node* true1 = graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, if_true1); | 
|  |  | 
|  | Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
|  | Node* false1 = graph()->NewNode(machine()->Word32And(), lhs, msk); | 
|  |  | 
|  | if_false0 = graph()->NewNode(merge_op, if_true1, if_false1); | 
|  | false0 = graph()->NewNode(phi_op, true1, false1, if_false0); | 
|  | } | 
|  |  | 
|  | Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0); | 
|  | return graph()->NewNode(phi_op, true0, false0, merge0); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoMax(Node* node, Operator const* op, | 
|  | MachineRepresentation rep) { | 
|  | Node* const lhs = node->InputAt(0); | 
|  | Node* const rhs = node->InputAt(1); | 
|  |  | 
|  | node->ReplaceInput(0, graph()->NewNode(op, lhs, rhs)); | 
|  | DCHECK_EQ(rhs, node->InputAt(1)); | 
|  | node->AppendInput(graph()->zone(), lhs); | 
|  | NodeProperties::ChangeOp(node, common()->Select(rep)); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoMin(Node* node, Operator const* op, | 
|  | MachineRepresentation rep) { | 
|  | Node* const lhs = node->InputAt(0); | 
|  | Node* const rhs = node->InputAt(1); | 
|  |  | 
|  | node->InsertInput(graph()->zone(), 0, graph()->NewNode(op, lhs, rhs)); | 
|  | DCHECK_EQ(lhs, node->InputAt(1)); | 
|  | DCHECK_EQ(rhs, node->InputAt(2)); | 
|  | NodeProperties::ChangeOp(node, common()->Select(rep)); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoIntegral32ToBit(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  | Node* const zero = jsgraph()->Int32Constant(0); | 
|  | Operator const* const op = machine()->Word32Equal(); | 
|  |  | 
|  | node->ReplaceInput(0, graph()->NewNode(op, input, zero)); | 
|  | node->AppendInput(graph()->zone(), zero); | 
|  | NodeProperties::ChangeOp(node, op); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoOrderedNumberToBit(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  |  | 
|  | node->ReplaceInput(0, graph()->NewNode(machine()->Float64Equal(), input, | 
|  | jsgraph()->Float64Constant(0.0))); | 
|  | node->AppendInput(graph()->zone(), jsgraph()->Int32Constant(0)); | 
|  | NodeProperties::ChangeOp(node, machine()->Word32Equal()); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoNumberToBit(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  |  | 
|  | node->ReplaceInput(0, jsgraph()->Float64Constant(0.0)); | 
|  | node->AppendInput(graph()->zone(), | 
|  | graph()->NewNode(machine()->Float64Abs(), input)); | 
|  | NodeProperties::ChangeOp(node, machine()->Float64LessThan()); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoIntegerToUint8Clamped(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  | Node* const min = jsgraph()->Float64Constant(0.0); | 
|  | Node* const max = jsgraph()->Float64Constant(255.0); | 
|  |  | 
|  | node->ReplaceInput( | 
|  | 0, graph()->NewNode(machine()->Float64LessThan(), min, input)); | 
|  | node->AppendInput( | 
|  | graph()->zone(), | 
|  | graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kFloat64), | 
|  | graph()->NewNode(machine()->Float64LessThan(), input, max), input, | 
|  | max)); | 
|  | node->AppendInput(graph()->zone(), min); | 
|  | NodeProperties::ChangeOp(node, | 
|  | common()->Select(MachineRepresentation::kFloat64)); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoNumberToUint8Clamped(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  | Node* const min = jsgraph()->Float64Constant(0.0); | 
|  | Node* const max = jsgraph()->Float64Constant(255.0); | 
|  |  | 
|  | node->ReplaceInput( | 
|  | 0, graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kFloat64), | 
|  | graph()->NewNode(machine()->Float64LessThan(), min, input), | 
|  | graph()->NewNode( | 
|  | common()->Select(MachineRepresentation::kFloat64), | 
|  | graph()->NewNode(machine()->Float64LessThan(), input, max), | 
|  | input, max), | 
|  | min)); | 
|  | NodeProperties::ChangeOp(node, | 
|  | machine()->Float64RoundTiesEven().placeholder()); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoSigned32ToUint8Clamped(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  | Node* const min = jsgraph()->Int32Constant(0); | 
|  | Node* const max = jsgraph()->Int32Constant(255); | 
|  |  | 
|  | node->ReplaceInput( | 
|  | 0, graph()->NewNode(machine()->Int32LessThanOrEqual(), input, max)); | 
|  | node->AppendInput( | 
|  | graph()->zone(), | 
|  | graph()->NewNode(common()->Select(MachineRepresentation::kWord32), | 
|  | graph()->NewNode(machine()->Int32LessThan(), input, min), | 
|  | min, input)); | 
|  | node->AppendInput(graph()->zone(), max); | 
|  | NodeProperties::ChangeOp(node, | 
|  | common()->Select(MachineRepresentation::kWord32)); | 
|  | } | 
|  |  | 
|  | void SimplifiedLowering::DoUnsigned32ToUint8Clamped(Node* node) { | 
|  | Node* const input = node->InputAt(0); | 
|  | Node* const max = jsgraph()->Uint32Constant(255u); | 
|  |  | 
|  | node->ReplaceInput( | 
|  | 0, graph()->NewNode(machine()->Uint32LessThanOrEqual(), input, max)); | 
|  | node->AppendInput(graph()->zone(), input); | 
|  | node->AppendInput(graph()->zone(), max); | 
|  | NodeProperties::ChangeOp(node, | 
|  | common()->Select(MachineRepresentation::kWord32)); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::ToNumberCode() { | 
|  | if (!to_number_code_.is_set()) { | 
|  | Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumber); | 
|  | to_number_code_.set(jsgraph()->HeapConstant(callable.code())); | 
|  | } | 
|  | return to_number_code_.get(); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::ToNumberConvertBigIntCode() { | 
|  | if (!to_number_convert_big_int_code_.is_set()) { | 
|  | Callable callable = | 
|  | Builtins::CallableFor(isolate(), Builtins::kToNumberConvertBigInt); | 
|  | to_number_convert_big_int_code_.set( | 
|  | jsgraph()->HeapConstant(callable.code())); | 
|  | } | 
|  | return to_number_convert_big_int_code_.get(); | 
|  | } | 
|  |  | 
|  | Node* SimplifiedLowering::ToNumericCode() { | 
|  | if (!to_numeric_code_.is_set()) { | 
|  | Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumeric); | 
|  | to_numeric_code_.set(jsgraph()->HeapConstant(callable.code())); | 
|  | } | 
|  | return to_numeric_code_.get(); | 
|  | } | 
|  |  | 
|  | Operator const* SimplifiedLowering::ToNumberOperator() { | 
|  | if (!to_number_operator_.is_set()) { | 
|  | Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumber); | 
|  | CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; | 
|  | auto call_descriptor = Linkage::GetStubCallDescriptor( | 
|  | graph()->zone(), callable.descriptor(), | 
|  | callable.descriptor().GetStackParameterCount(), flags, | 
|  | Operator::kNoProperties); | 
|  | to_number_operator_.set(common()->Call(call_descriptor)); | 
|  | } | 
|  | return to_number_operator_.get(); | 
|  | } | 
|  |  | 
|  | Operator const* SimplifiedLowering::ToNumberConvertBigIntOperator() { | 
|  | if (!to_number_convert_big_int_operator_.is_set()) { | 
|  | Callable callable = | 
|  | Builtins::CallableFor(isolate(), Builtins::kToNumberConvertBigInt); | 
|  | CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; | 
|  | auto call_descriptor = Linkage::GetStubCallDescriptor( | 
|  | graph()->zone(), callable.descriptor(), | 
|  | callable.descriptor().GetStackParameterCount(), flags, | 
|  | Operator::kNoProperties); | 
|  | to_number_convert_big_int_operator_.set(common()->Call(call_descriptor)); | 
|  | } | 
|  | return to_number_convert_big_int_operator_.get(); | 
|  | } | 
|  |  | 
|  | Operator const* SimplifiedLowering::ToNumericOperator() { | 
|  | if (!to_numeric_operator_.is_set()) { | 
|  | Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumeric); | 
|  | CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; | 
|  | auto call_descriptor = Linkage::GetStubCallDescriptor( | 
|  | graph()->zone(), callable.descriptor(), | 
|  | callable.descriptor().GetStackParameterCount(), flags, | 
|  | Operator::kNoProperties); | 
|  | to_numeric_operator_.set(common()->Call(call_descriptor)); | 
|  | } | 
|  | return to_numeric_operator_.get(); | 
|  | } | 
|  |  | 
|  | #undef TRACE | 
|  |  | 
|  | }  // namespace compiler | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |