| // 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 "src/address-map.h" |
| #include "src/base/bits.h" |
| #include "src/code-factory.h" |
| #include "src/compiler/access-builder.h" |
| #include "src/compiler/common-operator.h" |
| #include "src/compiler/diamond.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/node-matchers.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/source-position.h" |
| #include "src/conversions-inl.h" |
| #include "src/objects.h" |
| #include "src/type-cache.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.) 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 { |
| |
| |
| UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) { |
| switch (rep) { |
| case MachineRepresentation::kTagged: |
| return UseInfo::AnyTagged(); |
| case MachineRepresentation::kFloat64: |
| return UseInfo::TruncatingFloat64(); |
| case MachineRepresentation::kFloat32: |
| return UseInfo::TruncatingFloat32(); |
| case MachineRepresentation::kWord64: |
| return UseInfo::TruncatingWord64(); |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord32: |
| return UseInfo::TruncatingWord32(); |
| case MachineRepresentation::kBit: |
| return UseInfo::Bool(); |
| case MachineRepresentation::kSimd128: // Fall through. |
| case MachineRepresentation::kNone: |
| break; |
| } |
| UNREACHABLE(); |
| return UseInfo::None(); |
| } |
| |
| |
| UseInfo UseInfoForBasePointer(const FieldAccess& access) { |
| return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::PointerInt(); |
| } |
| |
| |
| UseInfo UseInfoForBasePointer(const ElementAccess& access) { |
| return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::PointerInt(); |
| } |
| |
| |
| #ifdef DEBUG |
| // Helpers for monotonicity checking. |
| bool MachineRepresentationIsSubtype(MachineRepresentation r1, |
| MachineRepresentation r2) { |
| switch (r1) { |
| case MachineRepresentation::kNone: |
| return true; |
| case MachineRepresentation::kBit: |
| return r2 == MachineRepresentation::kBit || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kWord8: |
| return r2 == MachineRepresentation::kWord8 || |
| r2 == MachineRepresentation::kWord16 || |
| r2 == MachineRepresentation::kWord32 || |
| r2 == MachineRepresentation::kWord64 || |
| r2 == MachineRepresentation::kFloat32 || |
| r2 == MachineRepresentation::kFloat64 || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kWord16: |
| return r2 == MachineRepresentation::kWord16 || |
| r2 == MachineRepresentation::kWord32 || |
| r2 == MachineRepresentation::kWord64 || |
| r2 == MachineRepresentation::kFloat32 || |
| r2 == MachineRepresentation::kFloat64 || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kWord32: |
| return r2 == MachineRepresentation::kWord32 || |
| r2 == MachineRepresentation::kWord64 || |
| r2 == MachineRepresentation::kFloat64 || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kWord64: |
| return r2 == MachineRepresentation::kWord64; |
| case MachineRepresentation::kFloat32: |
| return r2 == MachineRepresentation::kFloat32 || |
| r2 == MachineRepresentation::kFloat64 || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kFloat64: |
| return r2 == MachineRepresentation::kFloat64 || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kSimd128: |
| return r2 == MachineRepresentation::kSimd128 || |
| r2 == MachineRepresentation::kTagged; |
| case MachineRepresentation::kTagged: |
| return r2 == MachineRepresentation::kTagged; |
| } |
| UNREACHABLE(); |
| return false; |
| } |
| |
| |
| 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. |
| CHECK(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 MachineRepresentationIsSubtype(use1.representation(), |
| use2.representation()) && |
| use1.truncation().IsLessGeneralThan(use2.truncation()); |
| } |
| }; |
| |
| #endif // DEBUG |
| |
| } // namespace |
| |
| |
| class RepresentationSelector { |
| public: |
| // Information for each node tracked during the fixpoint. |
| class NodeInfo { |
| 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 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() { return feedback_type_; } |
| void set_weakened() { weakened_ = true; } |
| bool weakened() { return weakened_; } |
| TypeCheckKind type_check() { return type_check_; } |
| void set_type_check(TypeCheckKind type_check) { type_check_ = type_check; } |
| |
| 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. |
| TypeCheckKind type_check_ = TypeCheckKind::kNone; // Runtime check kind. |
| |
| Type* feedback_type_ = nullptr; |
| bool weakened_ = false; |
| }; |
| |
| RepresentationSelector(JSGraph* jsgraph, Zone* zone, |
| RepresentationChanger* changer, |
| SourcePositionTable* source_positions) |
| : jsgraph_(jsgraph), |
| zone_(zone), |
| count_(jsgraph->graph()->NodeCount()), |
| info_(count_, zone), |
| #ifdef DEBUG |
| node_input_use_infos_(count_, InputUseInfos(zone), zone), |
| #endif |
| nodes_(zone), |
| replacements_(zone), |
| phase_(PROPAGATE), |
| changer_(changer), |
| queue_(zone), |
| typing_stack_(zone), |
| source_positions_(source_positions), |
| type_cache_(TypeCache::Get()), |
| op_typer_(jsgraph->isolate(), graph_zone()) { |
| } |
| |
| // Forward propagation of types from type feedback. |
| void RunTypePropagationPhase() { |
| DCHECK(typing_stack_.empty()); |
| |
| typing_stack_.push({graph()->end(), 0}); |
| GetInfo(graph()->end())->set_pushed(); |
| while (!typing_stack_.empty()) { |
| NodeState& current = typing_stack_.top(); |
| |
| // If there is an unvisited input, push it and continue. |
| bool pushed_unvisited = false; |
| while (current.input_index < current.node->InputCount()) { |
| Node* input = current.node->InputAt(current.input_index); |
| NodeInfo* input_info = GetInfo(input); |
| current.input_index++; |
| if (input_info->unvisited()) { |
| input_info->set_pushed(); |
| typing_stack_.push({input, 0}); |
| pushed_unvisited = true; |
| break; |
| } |
| } |
| if (pushed_unvisited) continue; |
| |
| // Process the top of the stack. |
| Node* node = current.node; |
| typing_stack_.pop(); |
| NodeInfo* info = GetInfo(node); |
| info->set_visited(); |
| bool updated = UpdateFeedbackType(node); |
| if (updated) { |
| for (Node* const user : node->uses()) { |
| if (GetInfo(user)->visited()) { |
| GetInfo(user)->set_queued(); |
| queue_.push(user); |
| } |
| } |
| } |
| } |
| |
| // Process the revisit queue. |
| while (!queue_.empty()) { |
| Node* node = queue_.front(); |
| queue_.pop(); |
| NodeInfo* info = GetInfo(node); |
| info->set_visited(); |
| bool updated = UpdateFeedbackType(node); |
| if (updated) { |
| for (Node* const user : node->uses()) { |
| if (GetInfo(user)->visited()) { |
| GetInfo(user)->set_queued(); |
| queue_.push(user); |
| } |
| } |
| } |
| } |
| } |
| |
| 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 == nullptr ? NodeProperties::GetType(node) : type; |
| } |
| |
| Type* FeedbackTypeOf(Node* node) { |
| Type* type = GetInfo(node)->feedback_type(); |
| return type == nullptr ? 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))); |
| } |
| |
| static Type* TypeOfSpeculativeOp(TypeCheckKind type_check) { |
| switch (type_check) { |
| case TypeCheckKind::kNone: |
| return Type::Any(); |
| case TypeCheckKind::kSigned32: |
| return Type::Signed32(); |
| case TypeCheckKind::kNumber: |
| return Type::Number(); |
| // Unexpected cases. |
| case TypeCheckKind::kNumberOrUndefined: |
| FATAL("Unexpected checked type."); |
| break; |
| } |
| UNREACHABLE(); |
| return nullptr; |
| } |
| |
| bool UpdateFeedbackType(Node* node) { |
| if (node->op()->ValueOutputCount() == 0) return false; |
| |
| NodeInfo* info = GetInfo(node); |
| Type* type = info->feedback_type(); |
| Type* new_type = type; |
| |
| switch (node->opcode()) { |
| case IrOpcode::kSpeculativeNumberAdd: { |
| Type* lhs = FeedbackTypeOf(node->InputAt(0)); |
| Type* rhs = FeedbackTypeOf(node->InputAt(1)); |
| if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false; |
| // TODO(jarin) The ToNumber conversion is too conservative here, |
| // e.g. it will treat true as 1 even though the number check will |
| // fail on a boolean. OperationTyper should have a function that |
| // computes a more precise type. |
| lhs = op_typer_.ToNumber(lhs); |
| rhs = op_typer_.ToNumber(rhs); |
| Type* static_type = op_typer_.NumericAdd(lhs, rhs); |
| if (info->type_check() == TypeCheckKind::kNone) { |
| new_type = static_type; |
| } else { |
| Type* feedback_type = TypeOfSpeculativeOp(info->type_check()); |
| new_type = Type::Intersect(static_type, feedback_type, graph_zone()); |
| } |
| break; |
| } |
| |
| case IrOpcode::kSpeculativeNumberSubtract: { |
| Type* lhs = FeedbackTypeOf(node->InputAt(0)); |
| Type* rhs = FeedbackTypeOf(node->InputAt(1)); |
| if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false; |
| // TODO(jarin) The ToNumber conversion is too conservative here, |
| // e.g. it will treat true as 1 even though the number check will |
| // fail on a boolean. OperationTyper should have a function that |
| // computes a more precise type. |
| lhs = op_typer_.ToNumber(lhs); |
| rhs = op_typer_.ToNumber(rhs); |
| Type* static_type = op_typer_.NumericSubtract(lhs, rhs); |
| if (info->type_check() == TypeCheckKind::kNone) { |
| new_type = static_type; |
| } else { |
| Type* feedback_type = TypeOfSpeculativeOp(info->type_check()); |
| new_type = Type::Intersect(static_type, feedback_type, graph_zone()); |
| } |
| break; |
| } |
| |
| case IrOpcode::kSpeculativeNumberMultiply: { |
| Type* lhs = FeedbackTypeOf(node->InputAt(0)); |
| Type* rhs = FeedbackTypeOf(node->InputAt(1)); |
| if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false; |
| // TODO(jarin) The ToNumber conversion is too conservative here, |
| // e.g. it will treat true as 1 even though the number check will |
| // fail on a boolean. OperationTyper should have a function that |
| // computes a more precise type. |
| lhs = op_typer_.ToNumber(lhs); |
| rhs = op_typer_.ToNumber(rhs); |
| Type* static_type = op_typer_.NumericMultiply(lhs, rhs); |
| if (info->type_check() == TypeCheckKind::kNone) { |
| new_type = static_type; |
| } else { |
| Type* feedback_type = TypeOfSpeculativeOp(info->type_check()); |
| new_type = Type::Intersect(static_type, feedback_type, graph_zone()); |
| } |
| break; |
| } |
| |
| case IrOpcode::kSpeculativeNumberDivide: { |
| Type* lhs = FeedbackTypeOf(node->InputAt(0)); |
| Type* rhs = FeedbackTypeOf(node->InputAt(1)); |
| if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false; |
| // TODO(jarin) The ToNumber conversion is too conservative here, |
| // e.g. it will treat true as 1 even though the number check will |
| // fail on a boolean. OperationTyper should have a function that |
| // computes a more precise type. |
| lhs = op_typer_.ToNumber(lhs); |
| rhs = op_typer_.ToNumber(rhs); |
| Type* static_type = op_typer_.NumericDivide(lhs, rhs); |
| if (info->type_check() == TypeCheckKind::kNone) { |
| new_type = static_type; |
| } else { |
| Type* feedback_type = TypeOfSpeculativeOp(info->type_check()); |
| new_type = Type::Intersect(static_type, feedback_type, graph_zone()); |
| } |
| break; |
| } |
| |
| case IrOpcode::kSpeculativeNumberModulus: { |
| Type* lhs = FeedbackTypeOf(node->InputAt(0)); |
| Type* rhs = FeedbackTypeOf(node->InputAt(1)); |
| if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false; |
| // TODO(jarin) The ToNumber conversion is too conservative here, |
| // e.g. it will treat true as 1 even though the number check will |
| // fail on a boolean. OperationTyper should have a function that |
| // computes a more precise type. |
| lhs = op_typer_.ToNumber(lhs); |
| rhs = op_typer_.ToNumber(rhs); |
| Type* static_type = op_typer_.NumericModulus(lhs, rhs); |
| if (info->type_check() == TypeCheckKind::kNone) { |
| new_type = static_type; |
| } else { |
| Type* feedback_type = TypeOfSpeculativeOp(info->type_check()); |
| new_type = Type::Intersect(static_type, feedback_type, graph_zone()); |
| } |
| break; |
| } |
| |
| case IrOpcode::kPhi: { |
| new_type = TypePhi(node); |
| if (type != nullptr) { |
| new_type = Weaken(node, type, new_type); |
| } |
| // Recompute the phi representation based on the new type. |
| MachineRepresentation output = |
| GetOutputInfoForPhi(node, GetInfo(node)->truncation(), new_type); |
| ResetOutput(node, output); |
| break; |
| } |
| |
| case IrOpcode::kSelect: { |
| new_type = TypeSelect(node); |
| // Recompute representation based on the new type. |
| MachineRepresentation output = |
| GetOutputInfoForPhi(node, GetInfo(node)->truncation(), new_type); |
| ResetOutput(node, output); |
| break; |
| } |
| |
| default: |
| // Shortcut for operations that we do not handle. |
| if (type == nullptr) { |
| GetInfo(node)->set_feedback_type(NodeProperties::GetType(node)); |
| return true; |
| } |
| return false; |
| } |
| if (type != nullptr && 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) { |
| OFStream os(stdout); |
| 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)) { |
| os << " [Static type: "; |
| Type* static_type = NodeProperties::GetType(n); |
| static_type->PrintTo(os); |
| Type* feedback_type = GetInfo(n)->feedback_type(); |
| if (feedback_type != nullptr && feedback_type != static_type) { |
| os << ", Feedback type: "; |
| feedback_type->PrintTo(os); |
| } |
| 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()); |
| Type* previous_integer = |
| Type::Intersect(previous_type, integer, graph_zone()); |
| |
| // 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 == nullptr || previous == nullptr) { |
| 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()); |
| } |
| |
| // Backward propagation of truncations. |
| void RunTruncationPropagationPhase() { |
| // Run propagation phase to a fixpoint. |
| TRACE("--{Propagation phase}--\n"); |
| phase_ = PROPAGATE; |
| EnqueueInitial(jsgraph_->graph()->end()); |
| // Process nodes from the queue until it is empty. |
| while (!queue_.empty()) { |
| Node* node = queue_.front(); |
| NodeInfo* info = GetInfo(node); |
| queue_.pop(); |
| info->set_visited(); |
| TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic()); |
| VisitNode(node, info->truncation(), nullptr); |
| TRACE(" ==> output "); |
| PrintOutputInfo(info); |
| TRACE("\n"); |
| } |
| } |
| |
| void Run(SimplifiedLowering* lowering) { |
| RunTruncationPropagationPhase(); |
| |
| if (lowering->flags() & SimplifiedLowering::kTypeFeedbackEnabled) { |
| ResetNodeInfoState(); |
| RunTypePropagationPhase(); |
| } |
| |
| // Run lowering and change insertion phase. |
| TRACE("--{Simplified lowering phase}--\n"); |
| phase_ = LOWER; |
| // Process nodes from the collected {nodes_} vector. |
| for (NodeVector::iterator i = nodes_.begin(); i != nodes_.end(); ++i) { |
| Node* node = *i; |
| 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)); |
| VisitNode(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 EnqueueInitial(Node* node) { |
| NodeInfo* info = GetInfo(node); |
| info->set_queued(); |
| nodes_.push_back(node); |
| queue_.push(node); |
| } |
| |
| // Enqueue {use_node}'s {index} input if the {use} contains new information |
| // for that input node. Add the input to {nodes_} if this is the first time |
| // it's been visited. |
| void EnqueueInput(Node* use_node, int index, |
| UseInfo use_info = UseInfo::None()) { |
| Node* node = use_node->InputAt(index); |
| if (phase_ != PROPAGATE) return; |
| 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()) { |
| // First visit of this node. |
| info->set_queued(); |
| nodes_.push_back(node); |
| queue_.push(node); |
| TRACE(" initial: "); |
| info->AddUse(use_info); |
| PrintTruncation(info->truncation()); |
| return; |
| } |
| TRACE(" queue?: "); |
| PrintTruncation(info->truncation()); |
| if (info->AddUse(use_info)) { |
| // New usage information for the node is available. |
| if (!info->queued()) { |
| queue_.push(node); |
| info->set_queued(); |
| TRACE(" added: "); |
| } else { |
| TRACE(" inqueue: "); |
| } |
| PrintTruncation(info->truncation()); |
| } |
| } |
| |
| bool lower() { return phase_ == LOWER; } |
| bool propagate() { return phase_ == PROPAGATE; } |
| |
| void SetOutput(Node* node, MachineRepresentation representation, |
| TypeCheckKind type_check = TypeCheckKind::kNone) { |
| DCHECK(MachineRepresentationIsSubtype(GetInfo(node)->representation(), |
| representation)); |
| ResetOutput(node, representation, type_check); |
| } |
| |
| void ResetOutput(Node* node, MachineRepresentation representation, |
| TypeCheckKind type_check = TypeCheckKind::kNone) { |
| NodeInfo* info = GetInfo(node); |
| info->set_output(representation); |
| info->set_type_check(type_check); |
| } |
| |
| Type* GetUpperBound(Node* node) { return NodeProperties::GetType(node); } |
| |
| 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); |
| } |
| |
| void ConvertInput(Node* node, int index, UseInfo use) { |
| Node* input = node->InputAt(index); |
| // In the change phase, insert a change before the use if necessary. |
| if (use.representation() == MachineRepresentation::kNone) |
| return; // No input requirement on the use. |
| 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 "); |
| PrintOutputInfo(input_info); |
| TRACE(" to "); |
| PrintUseInfo(use); |
| TRACE("\n"); |
| Node* n = changer_->GetRepresentationFor( |
| input, input_info->representation(), TypeOf(input), node, use); |
| node->ReplaceInput(index, n); |
| } |
| } |
| |
| void ProcessInput(Node* node, int index, UseInfo use) { |
| if (phase_ == PROPAGATE) { |
| EnqueueInput(node, index, use); |
| } else { |
| ConvertInput(node, index, use); |
| } |
| } |
| |
| void ProcessRemainingInputs(Node* node, int index) { |
| DCHECK_GE(index, NodeProperties::PastValueIndex(node)); |
| DCHECK_GE(index, NodeProperties::PastContextIndex(node)); |
| for (int i = std::max(index, NodeProperties::FirstEffectIndex(node)); |
| i < NodeProperties::PastEffectIndex(node); ++i) { |
| EnqueueInput(node, i); // Effect inputs: just visit |
| } |
| for (int i = std::max(index, NodeProperties::FirstControlIndex(node)); |
| i < NodeProperties::PastControlIndex(node); ++i) { |
| EnqueueInput(node, i); // Control inputs: just visit |
| } |
| } |
| |
| // 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}. |
| void VisitInputs(Node* node) { |
| int tagged_count = node->op()->ValueInputCount() + |
| OperatorProperties::GetContextInputCount(node->op()); |
| // Visit value and context inputs as tagged. |
| for (int i = 0; i < tagged_count; i++) { |
| ProcessInput(node, i, UseInfo::AnyTagged()); |
| } |
| // Only enqueue other inputs (framestates, effects, control). |
| for (int i = tagged_count; i < node->InputCount(); i++) { |
| EnqueueInput(node, i); |
| } |
| } |
| |
| // Helper for binops of the R x L -> O variety. |
| void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use, |
| MachineRepresentation output, |
| TypeCheckKind type_check = TypeCheckKind::kNone) { |
| DCHECK_EQ(2, node->op()->ValueInputCount()); |
| ProcessInput(node, 0, left_use); |
| ProcessInput(node, 1, right_use); |
| for (int i = 2; i < node->InputCount(); i++) { |
| EnqueueInput(node, i); |
| } |
| SetOutput(node, output, type_check); |
| } |
| |
| // Helper for binops of the I x I -> O variety. |
| void VisitBinop(Node* node, UseInfo input_use, MachineRepresentation output, |
| TypeCheckKind type_check = TypeCheckKind::kNone) { |
| VisitBinop(node, input_use, input_use, output, type_check); |
| } |
| |
| // Helper for unops of the I -> O variety. |
| void VisitUnop(Node* node, UseInfo input_use, MachineRepresentation output) { |
| DCHECK_EQ(1, node->op()->ValueInputCount()); |
| ProcessInput(node, 0, input_use); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, output); |
| } |
| |
| // Helper for leaf nodes. |
| void VisitLeaf(Node* node, MachineRepresentation output) { |
| DCHECK_EQ(0, node->InputCount()); |
| SetOutput(node, output); |
| } |
| |
| // Helpers for specific types of binops. |
| void VisitFloat64Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| } |
| void VisitInt32Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| } |
| void VisitWord32TruncatingBinop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| } |
| void VisitUint32Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| } |
| void VisitInt64Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), |
| MachineRepresentation::kWord64); |
| } |
| void VisitUint64Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), |
| MachineRepresentation::kWord64); |
| } |
| void VisitFloat64Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingFloat64(), MachineRepresentation::kBit); |
| } |
| void VisitInt32Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), MachineRepresentation::kBit); |
| } |
| void VisitUint32Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), MachineRepresentation::kBit); |
| } |
| void VisitInt64Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), MachineRepresentation::kBit); |
| } |
| void VisitUint64Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), MachineRepresentation::kBit); |
| } |
| |
| // Infer representation for phi-like nodes. |
| MachineRepresentation GetOutputInfoForPhi(Node* node, Truncation use, |
| Type* type = nullptr) { |
| // Compute the representation. |
| if (type == nullptr) { |
| type = TypeOf(node); |
| } |
| if (type->Is(Type::None())) { |
| return MachineRepresentation::kNone; |
| } else if (type->Is(Type::Signed32()) || type->Is(Type::Unsigned32())) { |
| return MachineRepresentation::kWord32; |
| } else if (use.TruncatesToWord32()) { |
| return MachineRepresentation::kWord32; |
| } else if (type->Is(Type::Boolean())) { |
| return MachineRepresentation::kBit; |
| } else if (type->Is(Type::Number())) { |
| return MachineRepresentation::kFloat64; |
| } else if (use.TruncatesToFloat64()) { |
| return MachineRepresentation::kFloat64; |
| } else if (type->Is(Type::Internal())) { |
| // We mark (u)int64 as Type::Internal. |
| // TODO(jarin) This is a workaround for our lack of (u)int64 |
| // types. This can be removed once we can represent (u)int64 |
| // unambiguously. (At the moment internal objects, such as the hole, |
| // are also Type::Internal()). |
| bool is_word64 = GetInfo(node->InputAt(0))->representation() == |
| MachineRepresentation::kWord64; |
| #ifdef DEBUG |
| // Check that all the inputs agree on being Word64. |
| DCHECK_EQ(IrOpcode::kPhi, node->opcode()); // This only works for phis. |
| for (int i = 1; i < node->op()->ValueInputCount(); i++) { |
| DCHECK_EQ(is_word64, GetInfo(node->InputAt(i))->representation() == |
| MachineRepresentation::kWord64); |
| } |
| #endif |
| return is_word64 ? MachineRepresentation::kWord64 |
| : MachineRepresentation::kTagged; |
| } |
| return MachineRepresentation::kTagged; |
| } |
| |
| // Helper for handling selects. |
| void VisitSelect(Node* node, Truncation truncation, |
| SimplifiedLowering* lowering) { |
| ProcessInput(node, 0, UseInfo::Bool()); |
| |
| MachineRepresentation output = GetOutputInfoForPhi(node, truncation); |
| SetOutput(node, output); |
| |
| if (lower()) { |
| // 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(node, 1, input_use); |
| ProcessInput(node, 2, input_use); |
| } |
| |
| // Helper for handling phis. |
| void VisitPhi(Node* node, Truncation truncation, |
| SimplifiedLowering* lowering) { |
| MachineRepresentation output = GetOutputInfoForPhi(node, truncation); |
| // Only set the output representation if not running with type |
| // feedback. (Feedback typing will set the representation.) |
| SetOutput(node, output); |
| |
| int values = node->op()->ValueInputCount(); |
| if (lower()) { |
| // 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 truncation along. |
| UseInfo input_use(output, truncation); |
| for (int i = 0; i < node->InputCount(); i++) { |
| ProcessInput(node, i, i < values ? input_use : UseInfo::None()); |
| } |
| } |
| |
| void VisitCall(Node* node, SimplifiedLowering* lowering) { |
| const CallDescriptor* desc = CallDescriptorOf(node->op()); |
| const MachineSignature* sig = desc->GetMachineSignature(); |
| int params = static_cast<int>(sig->parameter_count()); |
| // Propagate representation information from call descriptor. |
| for (int i = 0; i < node->InputCount(); i++) { |
| if (i == 0) { |
| // The target of the call. |
| ProcessInput(node, i, UseInfo::None()); |
| } else if ((i - 1) < params) { |
| ProcessInput(node, i, TruncatingUseInfoFromRepresentation( |
| sig->GetParam(i - 1).representation())); |
| } else { |
| ProcessInput(node, i, UseInfo::None()); |
| } |
| } |
| |
| if (sig->return_count() > 0) { |
| SetOutput(node, |
| desc->GetMachineSignature()->GetReturn().representation()); |
| } else { |
| SetOutput(node, MachineRepresentation::kTagged); |
| } |
| } |
| |
| MachineSemantic DeoptValueSemanticOf(Type* type) { |
| CHECK(!type->Is(Type::None())); |
| // 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; |
| } |
| } |
| |
| void VisitStateValues(Node* node) { |
| if (phase_ == PROPAGATE) { |
| for (int i = 0; i < node->InputCount(); i++) { |
| EnqueueInput(node, i, UseInfo::Any()); |
| } |
| } else { |
| Zone* zone = jsgraph_->zone(); |
| ZoneVector<MachineType>* types = |
| new (zone->New(sizeof(ZoneVector<MachineType>))) |
| ZoneVector<MachineType>(node->InputCount(), zone); |
| for (int i = 0; i < node->InputCount(); i++) { |
| Node* input = node->InputAt(i); |
| NodeInfo* input_info = GetInfo(input); |
| MachineType machine_type(input_info->representation(), |
| DeoptValueSemanticOf(TypeOf(input))); |
| DCHECK(machine_type.representation() != |
| MachineRepresentation::kWord32 || |
| machine_type.semantic() == MachineSemantic::kInt32 || |
| machine_type.semantic() == MachineSemantic::kUint32); |
| (*types)[i] = machine_type; |
| } |
| NodeProperties::ChangeOp(node, |
| jsgraph_->common()->TypedStateValues(types)); |
| } |
| SetOutput(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* Uint32Op(Node* node) { |
| return changer_->Uint32OperatorFor(node->opcode()); |
| } |
| |
| const Operator* Float64Op(Node* node) { |
| return changer_->Float64OperatorFor(node->opcode()); |
| } |
| |
| WriteBarrierKind WriteBarrierKindFor( |
| BaseTaggedness base_taggedness, |
| MachineRepresentation field_representation, Type* field_type, |
| Node* value) { |
| if (base_taggedness == kTaggedBase && |
| field_representation == MachineRepresentation::kTagged) { |
| Type* value_type = NodeProperties::GetType(value); |
| if (field_type->Is(Type::TaggedSigned()) || |
| value_type->Is(Type::TaggedSigned())) { |
| // 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->IsConstant() && |
| value_type->AsConstant()->Value()->IsHeapObject()) { |
| Handle<HeapObject> value_object = |
| Handle<HeapObject>::cast(value_type->AsConstant()->Value()); |
| RootIndexMap root_index_map(jsgraph_->isolate()); |
| int root_index = root_index_map.Lookup(*value_object); |
| if (root_index != RootIndexMap::kInvalidRootIndex && |
| jsgraph_->isolate()->heap()->RootIsImmortalImmovable(root_index)) { |
| // Write barriers are unnecessary for immortal immovable roots. |
| return kNoWriteBarrier; |
| } |
| if (value_object->IsMap()) { |
| // Write barriers for storing maps are cheaper. |
| return kMapWriteBarrier; |
| } |
| } |
| if (field_type->Is(Type::TaggedPointer()) || |
| value_type->Is(Type::TaggedPointer())) { |
| // Write barriers for heap objects are cheaper. |
| return kPointerWriteBarrier; |
| } |
| NumberMatcher m(value); |
| if (m.HasValue()) { |
| if (IsSmiDouble(m.Value())) { |
| // 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, Node* value) { |
| if (base_taggedness == kTaggedBase && |
| field_offset == HeapObject::kMapOffset) { |
| return kMapWriteBarrier; |
| } |
| return WriteBarrierKindFor(base_taggedness, field_representation, |
| field_type, value); |
| } |
| |
| Graph* graph() const { return jsgraph_->graph(); } |
| CommonOperatorBuilder* common() const { return jsgraph_->common(); } |
| SimplifiedOperatorBuilder* simplified() const { |
| return jsgraph_->simplified(); |
| } |
| |
| 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)); |
| } |
| } |
| } |
| |
| void ChangeToPureOp(Node* node, const Operator* new_op) { |
| 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); |
| node->TrimInputCount(new_op->ValueInputCount()); |
| } else { |
| DCHECK_EQ(0, node->op()->ControlInputCount()); |
| } |
| |
| NodeProperties::ChangeOp(node, new_op); |
| } |
| |
| void ChangeToInt32OverflowOp(Node* node, const Operator* new_op) { |
| NodeProperties::ChangeOp(node, new_op); |
| } |
| |
| void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation, |
| SimplifiedLowering* lowering) { |
| if (BothInputsAre(node, type_cache_.kSigned32OrMinusZero) && |
| NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // int32 + int32 = int32 ==> signed Int32Add/Sub |
| VisitInt32Binop(node); |
| if (lower()) ChangeToPureOp(node, Int32Op(node)); |
| return; |
| } |
| |
| // Use truncation if available. |
| if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) && |
| truncation.TruncatesToWord32()) { |
| // safe-int + safe-int = x (truncated to int32) |
| // => signed Int32Add/Sub (truncated) |
| VisitWord32TruncatingBinop(node); |
| if (lower()) ChangeToPureOp(node, Int32Op(node)); |
| return; |
| } |
| |
| // Try to use type feedback. |
| BinaryOperationHints::Hint hint = BinaryOperationHintOf(node->op()); |
| |
| // 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()) || |
| (BothInputsAre(node, type_cache_.kSigned32OrMinusZero) && |
| NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger))) { |
| // If both the inputs the feedback are int32, use the overflow op. |
| if (hint == BinaryOperationHints::kSignedSmall || |
| hint == BinaryOperationHints::kSigned32) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32, TypeCheckKind::kSigned32); |
| if (lower()) { |
| ChangeToInt32OverflowOp(node, Int32OverflowOp(node)); |
| } |
| return; |
| } |
| } |
| |
| if (hint == BinaryOperationHints::kSignedSmall || |
| hint == BinaryOperationHints::kSigned32) { |
| VisitBinop(node, UseInfo::CheckedSigned32AsWord32(), |
| MachineRepresentation::kWord32, TypeCheckKind::kSigned32); |
| if (lower()) { |
| ChangeToInt32OverflowOp(node, Int32OverflowOp(node)); |
| } |
| return; |
| } |
| |
| // default case => Float64Add/Sub |
| VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(), |
| MachineRepresentation::kFloat64, TypeCheckKind::kNumber); |
| if (lower()) { |
| ChangeToPureOp(node, Float64Op(node)); |
| } |
| return; |
| } |
| |
| // Dispatching routine for visiting the node {node} with the usage {use}. |
| // Depending on the operator, propagate new usage info to the inputs. |
| void VisitNode(Node* node, Truncation truncation, |
| SimplifiedLowering* lowering) { |
| switch (node->opcode()) { |
| //------------------------------------------------------------------ |
| // Common operators. |
| //------------------------------------------------------------------ |
| case IrOpcode::kStart: |
| case IrOpcode::kDead: |
| return VisitLeaf(node, MachineRepresentation::kNone); |
| case IrOpcode::kParameter: { |
| // TODO(titzer): use representation from linkage. |
| ProcessInput(node, 0, UseInfo::None()); |
| SetOutput(node, MachineRepresentation::kTagged); |
| return; |
| } |
| case IrOpcode::kInt32Constant: |
| return VisitLeaf(node, MachineRepresentation::kWord32); |
| case IrOpcode::kInt64Constant: |
| return VisitLeaf(node, MachineRepresentation::kWord64); |
| case IrOpcode::kFloat32Constant: |
| return VisitLeaf(node, MachineRepresentation::kFloat32); |
| case IrOpcode::kFloat64Constant: |
| return VisitLeaf(node, MachineRepresentation::kFloat64); |
| case IrOpcode::kExternalConstant: |
| return VisitLeaf(node, MachineType::PointerRepresentation()); |
| case IrOpcode::kNumberConstant: |
| return VisitLeaf(node, MachineRepresentation::kTagged); |
| case IrOpcode::kHeapConstant: |
| return VisitLeaf(node, MachineRepresentation::kTagged); |
| |
| case IrOpcode::kDeoptimizeIf: |
| case IrOpcode::kDeoptimizeUnless: |
| ProcessInput(node, 0, UseInfo::Bool()); |
| ProcessInput(node, 1, UseInfo::AnyTagged()); |
| ProcessRemainingInputs(node, 2); |
| return; |
| case IrOpcode::kBranch: |
| ProcessInput(node, 0, UseInfo::Bool()); |
| EnqueueInput(node, NodeProperties::FirstControlIndex(node)); |
| return; |
| case IrOpcode::kSwitch: |
| ProcessInput(node, 0, UseInfo::TruncatingWord32()); |
| EnqueueInput(node, NodeProperties::FirstControlIndex(node)); |
| return; |
| case IrOpcode::kSelect: |
| return VisitSelect(node, truncation, lowering); |
| case IrOpcode::kPhi: |
| return VisitPhi(node, truncation, lowering); |
| case IrOpcode::kCall: |
| return VisitCall(node, lowering); |
| |
| //------------------------------------------------------------------ |
| // JavaScript operators. |
| //------------------------------------------------------------------ |
| case IrOpcode::kJSToNumber: { |
| VisitInputs(node); |
| // TODO(bmeurer): Optimize somewhat based on input type? |
| if (truncation.TruncatesToWord32()) { |
| SetOutput(node, MachineRepresentation::kWord32); |
| if (lower()) lowering->DoJSToNumberTruncatesToWord32(node, this); |
| } else if (truncation.TruncatesToFloat64()) { |
| SetOutput(node, MachineRepresentation::kFloat64); |
| if (lower()) lowering->DoJSToNumberTruncatesToFloat64(node, this); |
| } else { |
| SetOutput(node, MachineRepresentation::kTagged); |
| } |
| return; |
| } |
| |
| //------------------------------------------------------------------ |
| // Simplified operators. |
| //------------------------------------------------------------------ |
| case IrOpcode::kBooleanNot: { |
| if (lower()) { |
| 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 { |
| // BooleanNot(x: kRepTagged) => WordEqual(x, #false) |
| node->AppendInput(jsgraph_->zone(), jsgraph_->FalseConstant()); |
| NodeProperties::ChangeOp(node, lowering->machine()->WordEqual()); |
| } |
| } else { |
| // No input representation requirement; adapt during lowering. |
| ProcessInput(node, 0, UseInfo::AnyTruncatingToBool()); |
| SetOutput(node, MachineRepresentation::kBit); |
| } |
| return; |
| } |
| case IrOpcode::kBooleanToNumber: { |
| if (lower()) { |
| NodeInfo* input_info = GetInfo(node->InputAt(0)); |
| if (input_info->representation() == MachineRepresentation::kBit) { |
| // BooleanToNumber(x: kRepBit) => x |
| DeferReplacement(node, node->InputAt(0)); |
| } else { |
| // BooleanToNumber(x: kRepTagged) => WordEqual(x, #true) |
| node->AppendInput(jsgraph_->zone(), jsgraph_->TrueConstant()); |
| NodeProperties::ChangeOp(node, lowering->machine()->WordEqual()); |
| } |
| } else { |
| // No input representation requirement; adapt during lowering. |
| ProcessInput(node, 0, UseInfo::AnyTruncatingToBool()); |
| SetOutput(node, MachineRepresentation::kWord32); |
| } |
| return; |
| } |
| case IrOpcode::kNumberEqual: |
| case IrOpcode::kNumberLessThan: |
| case IrOpcode::kNumberLessThanOrEqual: { |
| // Number comparisons reduce to integer comparisons for integer inputs. |
| if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) && |
| TypeOf(node->InputAt(1))->Is(Type::Signed32())) { |
| // => signed Int32Cmp |
| VisitInt32Cmp(node); |
| if (lower()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| } else if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) && |
| TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) { |
| // => unsigned Int32Cmp |
| VisitUint32Cmp(node); |
| if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node)); |
| } else { |
| // => Float64Cmp |
| VisitFloat64Cmp(node); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| } |
| return; |
| } |
| |
| case IrOpcode::kSpeculativeNumberAdd: |
| case IrOpcode::kSpeculativeNumberSubtract: |
| return VisitSpeculativeAdditiveOp(node, truncation, lowering); |
| |
| case IrOpcode::kSpeculativeNumberLessThan: |
| case IrOpcode::kSpeculativeNumberLessThanOrEqual: |
| case IrOpcode::kSpeculativeNumberEqual: { |
| // Number comparisons reduce to integer comparisons for integer inputs. |
| if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) && |
| TypeOf(node->InputAt(1))->Is(Type::Signed32())) { |
| // => signed Int32Cmp |
| VisitInt32Cmp(node); |
| if (lower()) ChangeToPureOp(node, Int32Op(node)); |
| return; |
| } else if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) && |
| TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) { |
| // => unsigned Int32Cmp |
| VisitUint32Cmp(node); |
| if (lower()) ChangeToPureOp(node, Uint32Op(node)); |
| return; |
| } |
| // Try to use type feedback. |
| CompareOperationHints::Hint hint = CompareOperationHintOf(node->op()); |
| |
| if (hint == CompareOperationHints::kSignedSmall) { |
| VisitBinop(node, UseInfo::CheckedSigned32AsWord32(), |
| MachineRepresentation::kBit); |
| if (lower()) ChangeToPureOp(node, Int32Op(node)); |
| return; |
| } |
| DCHECK_EQ(CompareOperationHints::kNumber, hint); |
| // default case => Float64 comparison |
| VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(), |
| MachineRepresentation::kBit); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| |
| case IrOpcode::kNumberAdd: |
| case IrOpcode::kNumberSubtract: { |
| if (BothInputsAre(node, Type::Signed32()) && |
| NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // int32 + int32 = int32 |
| // => signed Int32Add/Sub |
| VisitInt32Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| } else if (BothInputsAre(node, |
| type_cache_.kAdditiveSafeIntegerOrMinusZero) && |
| truncation.TruncatesToWord32()) { |
| // safe-int + safe-int = x (truncated to int32) |
| // => signed Int32Add/Sub (truncated) |
| VisitWord32TruncatingBinop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| } else { |
| // => Float64Add/Sub |
| VisitFloat64Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| } |
| return; |
| } |
| case IrOpcode::kSpeculativeNumberMultiply: |
| case IrOpcode::kNumberMultiply: { |
| if (BothInputsAreSigned32(node)) { |
| if (NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // Multiply reduces to Int32Mul if the inputs and the output |
| // are integers. |
| VisitInt32Binop(node); |
| if (lower()) ChangeToPureOp(node, Int32Op(node)); |
| return; |
| } |
| if (truncation.TruncatesToWord32() && |
| NodeProperties::GetType(node)->Is( |
| type_cache_.kSafeIntegerOrMinusZero)) { |
| // Multiply reduces to Int32Mul if the inputs are integers, |
| // the uses are truncating and the result is in the safe |
| // integer range. |
| VisitWord32TruncatingBinop(node); |
| if (lower()) ChangeToPureOp(node, Int32Op(node)); |
| return; |
| } |
| } |
| // Number x Number => Float64Mul |
| if (BothInputsAre(node, Type::NumberOrUndefined())) { |
| VisitFloat64Binop(node); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| // Checked float64 x float64 => float64 |
| DCHECK_EQ(IrOpcode::kSpeculativeNumberMultiply, node->opcode()); |
| VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(), |
| MachineRepresentation::kFloat64, TypeCheckKind::kNumber); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kSpeculativeNumberDivide: |
| case IrOpcode::kNumberDivide: { |
| if (BothInputsAreSigned32(node)) { |
| if (NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // => signed Int32Div |
| VisitInt32Binop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Div(node)); |
| return; |
| } |
| if (truncation.TruncatesToWord32()) { |
| // => signed Int32Div |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Div(node)); |
| return; |
| } |
| } |
| if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) { |
| // => unsigned Uint32Div |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Uint32Div(node)); |
| return; |
| } |
| // Number x Number => Float64Div |
| if (BothInputsAre(node, Type::NumberOrUndefined())) { |
| VisitFloat64Binop(node); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| // Checked float64 x float64 => float64 |
| DCHECK_EQ(IrOpcode::kSpeculativeNumberDivide, node->opcode()); |
| VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(), |
| MachineRepresentation::kFloat64, TypeCheckKind::kNumber); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kSpeculativeNumberModulus: |
| case IrOpcode::kNumberModulus: { |
| if (BothInputsAreSigned32(node)) { |
| if (NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // => signed Int32Mod |
| VisitInt32Binop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Mod(node)); |
| return; |
| } |
| if (truncation.TruncatesToWord32()) { |
| // => signed Int32Mod |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Mod(node)); |
| return; |
| } |
| } |
| if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) { |
| // => unsigned Uint32Mod |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Uint32Mod(node)); |
| return; |
| } |
| // Number x Number => Float64Mod |
| if (BothInputsAre(node, Type::NumberOrUndefined())) { |
| // => Float64Mod |
| VisitFloat64Binop(node); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| // Checked float64 x float64 => float64 |
| DCHECK_EQ(IrOpcode::kSpeculativeNumberModulus, node->opcode()); |
| VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(), |
| MachineRepresentation::kFloat64, TypeCheckKind::kNumber); |
| if (lower()) ChangeToPureOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberBitwiseOr: |
| case IrOpcode::kNumberBitwiseXor: |
| case IrOpcode::kNumberBitwiseAnd: { |
| VisitInt32Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberShiftLeft: { |
| Type* rhs_type = GetUpperBound(node->InputAt(1)); |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); |
| if (lower()) { |
| lowering->DoShift(node, lowering->machine()->Word32Shl(), rhs_type); |
| } |
| return; |
| } |
| case IrOpcode::kNumberShiftRight: { |
| Type* rhs_type = GetUpperBound(node->InputAt(1)); |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); |
| if (lower()) { |
| lowering->DoShift(node, lowering->machine()->Word32Sar(), rhs_type); |
| } |
| return; |
| } |
| case IrOpcode::kNumberShiftRightLogical: { |
| Type* rhs_type = GetUpperBound(node->InputAt(1)); |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); |
| if (lower()) { |
| lowering->DoShift(node, lowering->machine()->Word32Shr(), rhs_type); |
| } |
| return; |
| } |
| case IrOpcode::kNumberAbs: { |
| if (InputIs(node, Type::Unsigned32())) { |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } else if (InputIs(node, type_cache_.kSafeSigned32)) { |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, lowering->Int32Abs(node)); |
| } else if (InputIs(node, |
| type_cache_.kPositiveIntegerOrMinusZeroOrNaN)) { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } else { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| } |
| return; |
| } |
| case IrOpcode::kNumberClz32: { |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberImul: { |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); |
| if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberCeil: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) DeferReplacement(node, lowering->Float64Ceil(node)); |
| return; |
| } |
| case IrOpcode::kNumberFloor: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) DeferReplacement(node, lowering->Float64Floor(node)); |
| return; |
| } |
| case IrOpcode::kNumberFround: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat32); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberAtan2: { |
| VisitBinop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberAtan: |
| case IrOpcode::kNumberAtanh: |
| case IrOpcode::kNumberCos: |
| 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::kNumberTan: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberRound: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) DeferReplacement(node, lowering->Float64Round(node)); |
| return; |
| } |
| case IrOpcode::kNumberSqrt: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| return; |
| } |
| case IrOpcode::kNumberTrunc: { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) DeferReplacement(node, lowering->Float64Trunc(node)); |
| return; |
| } |
| case IrOpcode::kNumberToInt32: { |
| // Just change representation if necessary. |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| return; |
| } |
| case IrOpcode::kNumberToUint32: { |
| // Just change representation if necessary. |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| return; |
| } |
| case IrOpcode::kReferenceEqual: { |
| VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); |
| if (lower()) { |
| NodeProperties::ChangeOp(node, lowering->machine()->WordEqual()); |
| } |
| return; |
| } |
| case IrOpcode::kStringEqual: { |
| VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| if (lower()) { |
| // StringEqual(x, y) => Call(StringEqualStub, x, y, no-context) |
| Operator::Properties properties = |
| Operator::kCommutative | Operator::kEliminatable; |
| Callable callable = CodeFactory::StringEqual(jsgraph_->isolate()); |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| jsgraph_->isolate(), jsgraph_->zone(), callable.descriptor(), 0, |
| flags, properties); |
| node->InsertInput(jsgraph_->zone(), 0, |
| jsgraph_->HeapConstant(callable.code())); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->NoContextConstant()); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start()); |
| NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc)); |
| } |
| return; |
| } |
| case IrOpcode::kStringLessThan: { |
| VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| if (lower()) { |
| // StringLessThan(x, y) => Call(StringLessThanStub, x, y, no-context) |
| Operator::Properties properties = Operator::kEliminatable; |
| Callable callable = CodeFactory::StringLessThan(jsgraph_->isolate()); |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| jsgraph_->isolate(), jsgraph_->zone(), callable.descriptor(), 0, |
| flags, properties); |
| node->InsertInput(jsgraph_->zone(), 0, |
| jsgraph_->HeapConstant(callable.code())); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->NoContextConstant()); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start()); |
| NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc)); |
| } |
| return; |
| } |
| case IrOpcode::kStringLessThanOrEqual: { |
| VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| if (lower()) { |
| // StringLessThanOrEqual(x, y) |
| // => Call(StringLessThanOrEqualStub, x, y, no-context) |
| Operator::Properties properties = Operator::kEliminatable; |
| Callable callable = |
| CodeFactory::StringLessThanOrEqual(jsgraph_->isolate()); |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| jsgraph_->isolate(), jsgraph_->zone(), callable.descriptor(), 0, |
| flags, properties); |
| node->InsertInput(jsgraph_->zone(), 0, |
| jsgraph_->HeapConstant(callable.code())); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->NoContextConstant()); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start()); |
| NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc)); |
| } |
| return; |
| } |
| case IrOpcode::kStringFromCharCode: { |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kTagged); |
| return; |
| } |
| case IrOpcode::kStringToNumber: { |
| VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| if (lower()) { |
| // StringToNumber(x) => Call(StringToNumber, x, no-context) |
| Operator::Properties properties = Operator::kEliminatable; |
| Callable callable = CodeFactory::StringToNumber(jsgraph_->isolate()); |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| jsgraph_->isolate(), jsgraph_->zone(), callable.descriptor(), 0, |
| flags, properties); |
| node->InsertInput(jsgraph_->zone(), 0, |
| jsgraph_->HeapConstant(callable.code())); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->NoContextConstant()); |
| node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start()); |
| NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc)); |
| } |
| return; |
| } |
| |
| case IrOpcode::kCheckBounds: { |
| VisitBinop(node, UseInfo::CheckedSigned32AsWord32(), |
| UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); |
| return; |
| } |
| case IrOpcode::kCheckTaggedPointer: { |
| VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| if (lower()) { |
| if (InputIs(node, Type::TaggedPointer())) { |
| DeferReplacement(node, node->InputAt(0)); |
| } |
| } |
| return; |
| } |
| case IrOpcode::kCheckTaggedSigned: { |
| if (SmiValuesAre32Bits() && truncation.TruncatesToWord32()) { |
| // TODO(jarin,bmeurer): Add CheckedSignedSmallAsWord32? |
| VisitUnop(node, UseInfo::CheckedSigned32AsWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } else { |
| VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| if (lower()) { |
| if (InputIs(node, Type::TaggedSigned())) { |
| DeferReplacement(node, node->InputAt(0)); |
| } |
| } |
| } |
| return; |
| } |
| |
| case IrOpcode::kAllocate: { |
| ProcessInput(node, 0, UseInfo::TruncatingWord32()); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, MachineRepresentation::kTagged); |
| return; |
| } |
| case IrOpcode::kLoadField: { |
| FieldAccess access = FieldAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfoForBasePointer(access)); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, access.machine_type.representation()); |
| return; |
| } |
| case IrOpcode::kStoreField: { |
| FieldAccess access = FieldAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfoForBasePointer(access)); |
| ProcessInput(node, 1, TruncatingUseInfoFromRepresentation( |
| access.machine_type.representation())); |
| ProcessRemainingInputs(node, 2); |
| SetOutput(node, MachineRepresentation::kNone); |
| if (lower()) { |
| WriteBarrierKind write_barrier_kind = WriteBarrierKindFor( |
| access.base_is_tagged, access.machine_type.representation(), |
| access.offset, access.type, node->InputAt(1)); |
| 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::kLoadBuffer: { |
| BufferAccess access = BufferAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfo::PointerInt()); // buffer |
| ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset |
| ProcessInput(node, 2, UseInfo::TruncatingWord32()); // length |
| ProcessRemainingInputs(node, 3); |
| |
| MachineRepresentation output; |
| if (truncation.TruncatesUndefinedToZeroOrNaN()) { |
| if (truncation.TruncatesNaNToZero()) { |
| // If undefined is truncated to a non-NaN number, we can use |
| // the load's representation. |
| output = access.machine_type().representation(); |
| } else { |
| // If undefined is truncated to a number, but the use can |
| // observe NaN, we need to output at least the float32 |
| // representation. |
| if (access.machine_type().representation() == |
| MachineRepresentation::kFloat32) { |
| output = access.machine_type().representation(); |
| } else { |
| output = MachineRepresentation::kFloat64; |
| } |
| } |
| } else { |
| // If undefined is not truncated away, we need to have the tagged |
| // representation. |
| output = MachineRepresentation::kTagged; |
| } |
| SetOutput(node, output); |
| if (lower()) lowering->DoLoadBuffer(node, output, changer_); |
| return; |
| } |
| case IrOpcode::kStoreBuffer: { |
| BufferAccess access = BufferAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfo::PointerInt()); // buffer |
| ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset |
| ProcessInput(node, 2, UseInfo::TruncatingWord32()); // length |
| ProcessInput(node, 3, |
| TruncatingUseInfoFromRepresentation( |
| access.machine_type().representation())); // value |
| ProcessRemainingInputs(node, 4); |
| SetOutput(node, MachineRepresentation::kNone); |
| if (lower()) lowering->DoStoreBuffer(node); |
| return; |
| } |
| case IrOpcode::kLoadElement: { |
| ElementAccess access = ElementAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfoForBasePointer(access)); // base |
| ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index |
| ProcessRemainingInputs(node, 2); |
| SetOutput(node, access.machine_type.representation()); |
| return; |
| } |
| case IrOpcode::kStoreElement: { |
| ElementAccess access = ElementAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfoForBasePointer(access)); // base |
| ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index |
| ProcessInput(node, 2, |
| TruncatingUseInfoFromRepresentation( |
| access.machine_type.representation())); // value |
| ProcessRemainingInputs(node, 3); |
| SetOutput(node, MachineRepresentation::kNone); |
| if (lower()) { |
| WriteBarrierKind write_barrier_kind = WriteBarrierKindFor( |
| access.base_is_tagged, access.machine_type.representation(), |
| access.type, node->InputAt(2)); |
| 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::kPlainPrimitiveToNumber: |
| if (truncation.TruncatesToWord32()) { |
| // TODO(jarin): Extend this to Number \/ Oddball |
| if (InputIs(node, Type::NumberOrUndefined())) { |
| VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } else { |
| VisitUnop(node, UseInfo::AnyTagged(), |
| MachineRepresentation::kWord32); |
| if (lower()) { |
| NodeProperties::ChangeOp(node, |
| simplified()->PlainPrimitiveToWord32()); |
| } |
| } |
| } else if (truncation.TruncatesToFloat64()) { |
| // TODO(jarin): Extend this to Number \/ Oddball |
| if (InputIs(node, Type::NumberOrUndefined())) { |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } else { |
| VisitUnop(node, UseInfo::AnyTagged(), |
| MachineRepresentation::kFloat64); |
| if (lower()) { |
| NodeProperties::ChangeOp(node, |
| simplified()->PlainPrimitiveToFloat64()); |
| } |
| } |
| } else { |
| VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged); |
| } |
| return; |
| case IrOpcode::kObjectIsCallable: |
| case IrOpcode::kObjectIsNumber: |
| case IrOpcode::kObjectIsReceiver: |
| case IrOpcode::kObjectIsSmi: |
| case IrOpcode::kObjectIsString: |
| case IrOpcode::kObjectIsUndetectable: { |
| ProcessInput(node, 0, UseInfo::AnyTagged()); |
| SetOutput(node, MachineRepresentation::kBit); |
| return; |
| } |
| case IrOpcode::kCheckFloat64Hole: { |
| CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op()); |
| ProcessInput(node, 0, UseInfo::TruncatingFloat64()); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, MachineRepresentation::kFloat64); |
| if (truncation.TruncatesToFloat64() && |
| mode == CheckFloat64HoleMode::kAllowReturnHole) { |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } |
| return; |
| } |
| case IrOpcode::kCheckTaggedHole: { |
| CheckTaggedHoleMode mode = CheckTaggedHoleModeOf(node->op()); |
| if (truncation.TruncatesToWord32() && |
| mode == CheckTaggedHoleMode::kConvertHoleToUndefined) { |
| ProcessInput(node, 0, UseInfo::CheckedSigned32AsWord32()); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, MachineRepresentation::kWord32); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| } else { |
| ProcessInput(node, 0, UseInfo::AnyTagged()); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, MachineRepresentation::kTagged); |
| } |
| return; |
| } |
| |
| //------------------------------------------------------------------ |
| // Machine-level operators. |
| //------------------------------------------------------------------ |
| case IrOpcode::kLoad: { |
| // TODO(jarin) Eventually, we should get rid of all machine stores |
| // from the high-level phases, then this becomes UNREACHABLE. |
| LoadRepresentation rep = LoadRepresentationOf(node->op()); |
| ProcessInput(node, 0, UseInfo::AnyTagged()); // tagged pointer |
| ProcessInput(node, 1, UseInfo::PointerInt()); // index |
| ProcessRemainingInputs(node, 2); |
| return SetOutput(node, rep.representation()); |
| } |
| case IrOpcode::kStore: { |
| // TODO(jarin) Eventually, we should get rid of all machine stores |
| // from the high-level phases, then this becomes UNREACHABLE. |
| StoreRepresentation rep = StoreRepresentationOf(node->op()); |
| ProcessInput(node, 0, UseInfo::AnyTagged()); // tagged pointer |
| ProcessInput(node, 1, UseInfo::PointerInt()); // index |
| ProcessInput(node, 2, |
| TruncatingUseInfoFromRepresentation(rep.representation())); |
| ProcessRemainingInputs(node, 3); |
| return SetOutput(node, MachineRepresentation::kNone); |
| } |
| case IrOpcode::kWord32Shr: |
| // We output unsigned int32 for shift right because JavaScript. |
| return VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| case IrOpcode::kWord32And: |
| case IrOpcode::kWord32Or: |
| case IrOpcode::kWord32Xor: |
| case IrOpcode::kWord32Shl: |
| case IrOpcode::kWord32Sar: |
| // We use signed int32 as the output type for these word32 operations, |
| // though the machine bits are the same for either signed or unsigned, |
| // because JavaScript considers the result from these operations signed. |
| return VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| case IrOpcode::kWord32Equal: |
| return VisitBinop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kBit); |
| |
| case IrOpcode::kWord32Clz: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord32); |
| |
| case IrOpcode::kInt32Add: |
| case IrOpcode::kInt32Sub: |
| case IrOpcode::kInt32Mul: |
| case IrOpcode::kInt32MulHigh: |
| case IrOpcode::kInt32Div: |
| case IrOpcode::kInt32Mod: |
| return VisitInt32Binop(node); |
| case IrOpcode::kUint32Div: |
| case IrOpcode::kUint32Mod: |
| case IrOpcode::kUint32MulHigh: |
| return VisitUint32Binop(node); |
| case IrOpcode::kInt32LessThan: |
| case IrOpcode::kInt32LessThanOrEqual: |
| return VisitInt32Cmp(node); |
| |
| case IrOpcode::kUint32LessThan: |
| case IrOpcode::kUint32LessThanOrEqual: |
| return VisitUint32Cmp(node); |
| |
| case IrOpcode::kInt64Add: |
| case IrOpcode::kInt64Sub: |
| case IrOpcode::kInt64Mul: |
| case IrOpcode::kInt64Div: |
| case IrOpcode::kInt64Mod: |
| return VisitInt64Binop(node); |
| case IrOpcode::kInt64LessThan: |
| case IrOpcode::kInt64LessThanOrEqual: |
| return VisitInt64Cmp(node); |
| |
| case IrOpcode::kUint64LessThan: |
| return VisitUint64Cmp(node); |
| |
| case IrOpcode::kUint64Div: |
| case IrOpcode::kUint64Mod: |
| return VisitUint64Binop(node); |
| |
| case IrOpcode::kWord64And: |
| case IrOpcode::kWord64Or: |
| case IrOpcode::kWord64Xor: |
| case IrOpcode::kWord64Shl: |
| case IrOpcode::kWord64Shr: |
| case IrOpcode::kWord64Sar: |
| return VisitBinop(node, UseInfo::TruncatingWord64(), |
| MachineRepresentation::kWord64); |
| case IrOpcode::kWord64Equal: |
| return VisitBinop(node, UseInfo::TruncatingWord64(), |
| MachineRepresentation::kBit); |
| |
| case IrOpcode::kChangeInt32ToInt64: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord64); |
| case IrOpcode::kChangeUint32ToUint64: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kWord64); |
| case IrOpcode::kTruncateFloat64ToFloat32: |
| return VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat32); |
| case IrOpcode::kTruncateFloat64ToWord32: |
| return VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kWord32); |
| |
| case IrOpcode::kChangeInt32ToFloat64: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kFloat64); |
| case IrOpcode::kChangeUint32ToFloat64: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| MachineRepresentation::kFloat64); |
| case IrOpcode::kFloat64Add: |
| case IrOpcode::kFloat64Sub: |
| case IrOpcode::kFloat64Mul: |
| case IrOpcode::kFloat64Div: |
| case IrOpcode::kFloat64Mod: |
| case IrOpcode::kFloat64Min: |
| return VisitFloat64Binop(node); |
| case IrOpcode::kFloat64Abs: |
| case IrOpcode::kFloat64Sqrt: |
| case IrOpcode::kFloat64RoundDown: |
| case IrOpcode::kFloat64RoundTruncate: |
| case IrOpcode::kFloat64RoundTiesAway: |
| case IrOpcode::kFloat64RoundUp: |
| return VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| case IrOpcode::kFloat64SilenceNaN: |
| return VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| case IrOpcode::kFloat64Equal: |
| case IrOpcode::kFloat64LessThan: |
| case IrOpcode::kFloat64LessThanOrEqual: |
| return VisitFloat64Cmp(node); |
| case IrOpcode::kFloat64ExtractLowWord32: |
| case IrOpcode::kFloat64ExtractHighWord32: |
| return VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kWord32); |
| case IrOpcode::kFloat64InsertLowWord32: |
| case IrOpcode::kFloat64InsertHighWord32: |
| return VisitBinop(node, UseInfo::TruncatingFloat64(), |
| UseInfo::TruncatingWord32(), |
| MachineRepresentation::kFloat64); |
| case IrOpcode::kNumberSilenceNaN: |
| VisitUnop(node, UseInfo::TruncatingFloat64(), |
| MachineRepresentation::kFloat64); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| return; |
| case IrOpcode::kLoadStackPointer: |
| case IrOpcode::kLoadFramePointer: |
| case IrOpcode::kLoadParentFramePointer: |
| return VisitLeaf(node, MachineType::PointerRepresentation()); |
| case IrOpcode::kStateValues: |
| return VisitStateValues(node); |
| |
| // The following opcodes are not produced before representation |
| // inference runs, so we do not have any real test coverage. |
| // Simply fail here. |
| case IrOpcode::kChangeFloat64ToInt32: |
| case IrOpcode::kChangeFloat64ToUint32: |
| case IrOpcode::kTruncateInt64ToInt32: |
| case IrOpcode::kChangeFloat32ToFloat64: |
| case IrOpcode::kCheckedInt32Add: |
| case IrOpcode::kCheckedInt32Sub: |
| case IrOpcode::kCheckedUint32ToInt32: |
| case IrOpcode::kCheckedFloat64ToInt32: |
| case IrOpcode::kCheckedTaggedToInt32: |
| case IrOpcode::kCheckedTaggedToFloat64: |
| case IrOpcode::kPlainPrimitiveToWord32: |
| case IrOpcode::kPlainPrimitiveToFloat64: |
| FATAL("Representation inference: unsupported opcodes."); |
| break; |
| |
| default: |
| VisitInputs(node); |
| // Assume the output is tagged. |
| return SetOutput(node, MachineRepresentation::kTagged); |
| } |
| 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); |
| } else { |
| DCHECK_EQ(0, node->op()->ControlInputCount()); |
| } |
| |
| if (replacement->id() < count_ && |
| GetUpperBound(node)->Is(GetUpperBound(replacement)) && |
| TypeOf(node)->Is(TypeOf(replacement))) { |
| // Replace with a previously existing node eagerly only if the type is the |
| // same. |
| node->ReplaceUses(replacement); |
| } else { |
| // Otherwise, we are replacing a node with a representation change. |
| // Such a substitution must be done after all lowering is done, because |
| // changing the type could confuse the representation change |
| // insertion for uses of the node. |
| replacements_.push_back(node); |
| replacements_.push_back(replacement); |
| } |
| node->NullAllInputs(); // Node is now dead. |
| } |
| |
| void PrintOutputInfo(NodeInfo* info) { |
| if (FLAG_trace_representation) { |
| OFStream os(stdout); |
| os << info->representation(); |
| } |
| } |
| |
| void PrintRepresentation(MachineRepresentation rep) { |
| if (FLAG_trace_representation) { |
| OFStream os(stdout); |
| os << rep; |
| } |
| } |
| |
| void PrintTruncation(Truncation truncation) { |
| if (FLAG_trace_representation) { |
| OFStream os(stdout); |
| os << truncation.description() << std::endl; |
| } |
| } |
| |
| void PrintUseInfo(UseInfo info) { |
| if (FLAG_trace_representation) { |
| OFStream os(stdout); |
| os << info.representation() << ":" << info.truncation().description(); |
| } |
| } |
| |
| private: |
| JSGraph* jsgraph_; |
| Zone* zone_; // Temporary zone. |
| 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 nodes_; // collected nodes |
| NodeVector replacements_; // replacements to be done after lowering |
| Phase phase_; // current phase of algorithm |
| RepresentationChanger* changer_; // for inserting representation changes |
| ZoneQueue<Node*> queue_; // queue for traversing the graph |
| |
| struct NodeState { |
| Node* node; |
| int input_index; |
| }; |
| ZoneStack<NodeState> typing_stack_; // stack for graph typing. |
| // 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_; |
| TypeCache const& type_cache_; |
| OperationTyper op_typer_; // helper for the feedback typer |
| |
| NodeInfo* GetInfo(Node* node) { |
| DCHECK(node->id() >= 0); |
| DCHECK(node->id() < count_); |
| return &info_[node->id()]; |
| } |
| Zone* zone() { return zone_; } |
| Zone* graph_zone() { return jsgraph_->zone(); } |
| }; |
| |
| SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, Zone* zone, |
| SourcePositionTable* source_positions, |
| Flags flags) |
| : jsgraph_(jsgraph), |
| zone_(zone), |
| type_cache_(TypeCache::Get()), |
| flags_(flags), |
| source_positions_(source_positions) {} |
| |
| void SimplifiedLowering::LowerAllNodes() { |
| RepresentationChanger changer(jsgraph(), jsgraph()->isolate()); |
| RepresentationSelector selector(jsgraph(), zone_, &changer, |
| source_positions_); |
| selector.Run(this); |
| } |
| |
| void SimplifiedLowering::DoJSToNumberTruncatesToFloat64( |
| Node* node, RepresentationSelector* selector) { |
| DCHECK_EQ(IrOpcode::kJSToNumber, node->opcode()); |
| 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* throwing; |
| |
| 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; |
| { |
| throwing = vfalse0 = efalse0 = |
| graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context, |
| frame_state, efalse0, if_false0); |
| if_false0 = graph()->NewNode(common()->IfSuccess(), throwing); |
| |
| 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 if (edge.from()->opcode() == IrOpcode::kIfException) { |
| edge.UpdateTo(throwing); |
| } else { |
| UNREACHABLE(); |
| } |
| } else if (NodeProperties::IsEffectEdge(edge)) { |
| edge.UpdateTo(effect); |
| } |
| } |
| |
| selector->DeferReplacement(node, value); |
| } |
| |
| void SimplifiedLowering::DoJSToNumberTruncatesToWord32( |
| Node* node, RepresentationSelector* selector) { |
| DCHECK_EQ(IrOpcode::kJSToNumber, node->opcode()); |
| 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* throwing; |
| |
| 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; |
| { |
| throwing = vfalse0 = efalse0 = |
| graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context, |
| frame_state, efalse0, if_false0); |
| if_false0 = graph()->NewNode(common()->IfSuccess(), throwing); |
| |
| 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 if (edge.from()->opcode() == IrOpcode::kIfException) { |
| edge.UpdateTo(throwing); |
| } else { |
| UNREACHABLE(); |
| } |
| } else if (NodeProperties::IsEffectEdge(edge)) { |
| edge.UpdateTo(effect); |
| } |
| } |
| |
| selector->DeferReplacement(node, value); |
| } |
| |
| void SimplifiedLowering::DoLoadBuffer(Node* node, |
| MachineRepresentation output_rep, |
| RepresentationChanger* changer) { |
| DCHECK_EQ(IrOpcode::kLoadBuffer, node->opcode()); |
| DCHECK_NE(MachineRepresentation::kNone, output_rep); |
| MachineType const access_type = BufferAccessOf(node->op()).machine_type(); |
| if (output_rep != access_type.representation()) { |
| Node* const buffer = node->InputAt(0); |
| Node* const offset = node->InputAt(1); |
| Node* const length = node->InputAt(2); |
| Node* const effect = node->InputAt(3); |
| Node* const control = node->InputAt(4); |
| Node* const index = |
| machine()->Is64() |
| ? graph()->NewNode(machine()->ChangeUint32ToUint64(), offset) |
| : offset; |
| |
| Node* check = graph()->NewNode(machine()->Uint32LessThan(), offset, length); |
| Node* branch = |
| graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
| |
| Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| Node* etrue = graph()->NewNode(machine()->Load(access_type), buffer, index, |
| effect, if_true); |
| Type* element_type = |
| Type::Intersect(NodeProperties::GetType(node), Type::Number(), zone()); |
| Node* vtrue = changer->GetRepresentationFor( |
| etrue, access_type.representation(), element_type, node, |
| UseInfo(output_rep, Truncation::None())); |
| |
| Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
| Node* efalse = effect; |
| Node* vfalse; |
| if (output_rep == MachineRepresentation::kTagged) { |
| vfalse = jsgraph()->UndefinedConstant(); |
| } else if (output_rep == MachineRepresentation::kFloat64) { |
| vfalse = |
| jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN()); |
| } else if (output_rep == MachineRepresentation::kFloat32) { |
| vfalse = |
| jsgraph()->Float32Constant(std::numeric_limits<float>::quiet_NaN()); |
| } else { |
| vfalse = jsgraph()->Int32Constant(0); |
| } |
| |
| Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); |
| Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); |
| |
| // Replace effect uses of {node} with the {ephi}. |
| NodeProperties::ReplaceUses(node, node, ephi); |
| |
| // Turn the {node} into a Phi. |
| node->ReplaceInput(0, vtrue); |
| node->ReplaceInput(1, vfalse); |
| node->ReplaceInput(2, merge); |
| node->TrimInputCount(3); |
| NodeProperties::ChangeOp(node, common()->Phi(output_rep, 2)); |
| } else { |
| NodeProperties::ChangeOp(node, machine()->CheckedLoad(access_type)); |
| } |
| } |
| |
| |
| void SimplifiedLowering::DoStoreBuffer(Node* node) { |
| DCHECK_EQ(IrOpcode::kStoreBuffer, node->opcode()); |
| MachineRepresentation const rep = |
| BufferAccessOf(node->op()).machine_type().representation(); |
| NodeProperties::ChangeOp(node, machine()->CheckedStore(rep)); |
| } |
| |
| Node* SimplifiedLowering::Float64Ceil(Node* const node) { |
| Node* const one = jsgraph()->Float64Constant(1.0); |
| Node* const zero = jsgraph()->Float64Constant(0.0); |
| Node* const minus_zero = jsgraph()->Float64Constant(-0.0); |
| Node* const two_52 = jsgraph()->Float64Constant(4503599627370496.0E0); |
| Node* const minus_two_52 = jsgraph()->Float64Constant(-4503599627370496.0E0); |
| Node* const input = node->InputAt(0); |
| |
| // Use fast hardware instruction if available. |
| if (machine()->Float64RoundUp().IsSupported()) { |
| return graph()->NewNode(machine()->Float64RoundUp().op(), input); |
| } |
| |
| // General case for ceil. |
| // |
| // if 0.0 < input then |
| // if 2^52 <= input then |
| // input |
| // else |
| // let temp1 = (2^52 + input) - 2^52 in |
| // if temp1 < input then |
| // temp1 + 1 |
| // else |
| // temp1 |
| // else |
| // if input == 0 then |
| // input |
| // else |
| // if input <= -2^52 then |
| // input |
| // else |
| // let temp1 = -0 - input in |
| // let temp2 = (2^52 + temp1) - 2^52 in |
| // let temp3 = (if temp1 < temp2 then temp2 - 1 else temp2) in |
| // -0 - temp3 |
| // |
| // Note: We do not use the Diamond helper class here, because it really hurts |
| // readability with nested diamonds. |
| |
| Node* check0 = graph()->NewNode(machine()->Float64LessThan(), zero, input); |
| Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, |
| graph()->start()); |
| |
| Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| Node* vtrue0; |
| { |
| Node* check1 = |
| graph()->NewNode(machine()->Float64LessThanOrEqual(), two_52, input); |
| Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* vtrue1 = input; |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* vfalse1; |
| { |
| Node* temp1 = graph()->NewNode( |
| machine()->Float64Sub(), |
| graph()->NewNode(machine()->Float64Add(), two_52, input), two_52); |
| vfalse1 = graph()->NewNode( |
| common()->Select(MachineRepresentation::kFloat64), |
| graph()->NewNode(machine()->Float64LessThan(), temp1, input), |
| graph()->NewNode(machine()->Float64Add(), temp1, one), temp1); |
| } |
| |
| if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue1, vfalse1, if_true0); |
| } |
| |
| Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| Node* vfalse0; |
| { |
| Node* check1 = graph()->NewNode(machine()->Float64Equal(), input, zero); |
| Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check1, if_false0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* vtrue1 = input; |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* vfalse1; |
| { |
| Node* check2 = graph()->NewNode(machine()->Float64LessThanOrEqual(), |
| input, minus_two_52); |
| Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check2, if_false1); |
| |
| Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); |
| Node* vtrue2 = input; |
| |
| Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); |
| Node* vfalse2; |
| { |
| Node* temp1 = |
| graph()->NewNode(machine()->Float64Sub(), minus_zero, input); |
| Node* temp2 = graph()->NewNode( |
| machine()->Float64Sub(), |
| graph()->NewNode(machine()->Float64Add(), two_52, temp1), two_52); |
| Node* temp3 = graph()->NewNode( |
| common()->Select(MachineRepresentation::kFloat64), |
| graph()->NewNode(machine()->Float64LessThan(), temp1, temp2), |
| graph()->NewNode(machine()->Float64Sub(), temp2, one), temp2); |
| vfalse2 = graph()->NewNode(machine()->Float64Sub(), minus_zero, temp3); |
| } |
| |
| if_false1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); |
| vfalse1 = |
| graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue2, vfalse2, if_false1); |
| } |
| |
| if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| vfalse0 = |
| graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue1, vfalse1, if_false0); |
| } |
| |
| Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| return graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue0, vfalse0, merge0); |
| } |
| |
| Node* SimplifiedLowering::Float64Floor(Node* const node) { |
| Node* const one = jsgraph()->Float64Constant(1.0); |
| Node* const zero = jsgraph()->Float64Constant(0.0); |
| Node* const minus_one = jsgraph()->Float64Constant(-1.0); |
| Node* const minus_zero = jsgraph()->Float64Constant(-0.0); |
| Node* const two_52 = jsgraph()->Float64Constant(4503599627370496.0E0); |
| Node* const minus_two_52 = jsgraph()->Float64Constant(-4503599627370496.0E0); |
| Node* const input = node->InputAt(0); |
| |
| // Use fast hardware instruction if available. |
| if (machine()->Float64RoundDown().IsSupported()) { |
| return graph()->NewNode(machine()->Float64RoundDown().op(), input); |
| } |
| |
| // General case for floor. |
| // |
| // if 0.0 < input then |
| // if 2^52 <= input then |
| // input |
| // else |
| // let temp1 = (2^52 + input) - 2^52 in |
| // if input < temp1 then |
| // temp1 - 1 |
| // else |
| // temp1 |
| // else |
| // if input == 0 then |
| // input |
| // else |
| // if input <= -2^52 then |
| // input |
| // else |
| // let temp1 = -0 - input in |
| // let temp2 = (2^52 + temp1) - 2^52 in |
| // if temp2 < temp1 then |
| // -1 - temp2 |
| // else |
| // -0 - temp2 |
| // |
| // Note: We do not use the Diamond helper class here, because it really hurts |
| // readability with nested diamonds. |
| |
| Node* check0 = graph()->NewNode(machine()->Float64LessThan(), zero, input); |
| Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, |
| graph()->start()); |
| |
| Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| Node* vtrue0; |
| { |
| Node* check1 = |
| graph()->NewNode(machine()->Float64LessThanOrEqual(), two_52, input); |
| Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* vtrue1 = input; |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* vfalse1; |
| { |
| Node* temp1 = graph()->NewNode( |
| machine()->Float64Sub(), |
| graph()->NewNode(machine()->Float64Add(), two_52, input), two_52); |
| vfalse1 = graph()->NewNode( |
| common()->Select(MachineRepresentation::kFloat64), |
| graph()->NewNode(machine()->Float64LessThan(), input, temp1), |
| graph()->NewNode(machine()->Float64Sub(), temp1, one), temp1); |
| } |
| |
| if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue1, vfalse1, if_true0); |
| } |
| |
| Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| Node* vfalse0; |
| { |
| Node* check1 = graph()->NewNode(machine()->Float64Equal(), input, zero); |
| Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check1, if_false0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* vtrue1 = input; |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* vfalse1; |
| { |
| Node* check2 = graph()->NewNode(machine()->Float64LessThanOrEqual(), |
| input, minus_two_52); |
| Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check2, if_false1); |
| |
| Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); |
| Node* vtrue2 = input; |
| |
| Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); |
| Node* vfalse2; |
| { |
| Node* temp1 = |
| graph()->NewNode(machine()->Float64Sub(), minus_zero, input); |
| Node* temp2 = graph()->NewNode( |
| machine()->Float64Sub(), |
| graph()->NewNode(machine()->Float64Add(), two_52, temp1), two_52); |
| vfalse2 = graph()->NewNode( |
| common()->Select(MachineRepresentation::kFloat64), |
| graph()->NewNode(machine()->Float64LessThan(), temp2, temp1), |
| graph()->NewNode(machine()->Float64Sub(), minus_one, temp2), |
| graph()->NewNode(machine()->Float64Sub(), minus_zero, temp2)); |
| } |
| |
| if_false1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); |
| vfalse1 = |
| graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue2, vfalse2, if_false1); |
| } |
| |
| if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| vfalse0 = |
| graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue1, vfalse1, if_false0); |
| } |
| |
| Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| return graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue0, vfalse0, merge0); |
| } |
| |
| 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 = Float64Ceil(node); |
| 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::Float64Trunc(Node* const node) { |
| Node* const one = jsgraph()->Float64Constant(1.0); |
| Node* const zero = jsgraph()->Float64Constant(0.0); |
| Node* const minus_zero = jsgraph()->Float64Constant(-0.0); |
| Node* const two_52 = jsgraph()->Float64Constant(4503599627370496.0E0); |
| Node* const minus_two_52 = jsgraph()->Float64Constant(-4503599627370496.0E0); |
| Node* const input = node->InputAt(0); |
| |
| // Use fast hardware instruction if available. |
| if (machine()->Float64RoundTruncate().IsSupported()) { |
| return graph()->NewNode(machine()->Float64RoundTruncate().op(), input); |
| } |
| |
| // General case for trunc. |
| // |
| // if 0.0 < input then |
| // if 2^52 <= input then |
| // input |
| // else |
| // let temp1 = (2^52 + input) - 2^52 in |
| // if input < temp1 then |
| // temp1 - 1 |
| // else |
| // temp1 |
| // else |
| // if input == 0 then |
| // input |
| // else |
| // if input <= -2^52 then |
| // input |
| // else |
| // let temp1 = -0 - input in |
| // let temp2 = (2^52 + temp1) - 2^52 in |
| // let temp3 = (if temp1 < temp2 then temp2 - 1 else temp2) in |
| // -0 - temp3 |
| // |
| // Note: We do not use the Diamond helper class here, because it really hurts |
| // readability with nested diamonds. |
| |
| Node* check0 = graph()->NewNode(machine()->Float64LessThan(), zero, input); |
| Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, |
| graph()->start()); |
| |
| Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| Node* vtrue0; |
| { |
| Node* check1 = |
| graph()->NewNode(machine()->Float64LessThanOrEqual(), two_52, input); |
| Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* vtrue1 = input; |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* vfalse1; |
| { |
| Node* temp1 = graph()->NewNode( |
| machine()->Float64Sub(), |
| graph()->NewNode(machine()->Float64Add(), two_52, input), two_52); |
| vfalse1 = graph()->NewNode( |
| common()->Select(MachineRepresentation::kFloat64), |
| graph()->NewNode(machine()->Float64LessThan(), input, temp1), |
| graph()->NewNode(machine()->Float64Sub(), temp1, one), temp1); |
| } |
| |
| if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue1, vfalse1, if_true0); |
| } |
| |
| Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| Node* vfalse0; |
| { |
| Node* check1 = graph()->NewNode(machine()->Float64Equal(), input, zero); |
| Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check1, if_false0); |
| |
| Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| Node* vtrue1 = input; |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* vfalse1; |
| { |
| Node* check2 = graph()->NewNode(machine()->Float64LessThanOrEqual(), |
| input, minus_two_52); |
| Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| check2, if_false1); |
| |
| Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); |
| Node* vtrue2 = input; |
| |
| Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); |
| Node* vfalse2; |
| { |
| Node* temp1 = |
| graph()->NewNode(machine()->Float64Sub(), minus_zero, input); |
| Node* temp2 = graph()->NewNode( |
| machine()->Float64Sub(), |
| graph()->NewNode(machine()->Float64Add(), two_52, temp1), two_52); |
| Node* temp3 = graph()->NewNode( |
| common()->Select(MachineRepresentation::kFloat64), |
| graph()->NewNode(machine()->Float64LessThan(), temp1, temp2), |
| graph()->NewNode(machine()->Float64Sub(), temp2, one), temp2); |
| vfalse2 = graph()->NewNode(machine()->Float64Sub(), minus_zero, temp3); |
| } |
| |
| if_false1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); |
| vfalse1 = |
| graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue2, vfalse2, if_false1); |
| } |
| |
| if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| vfalse0 = |
| graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue1, vfalse1, if_false0); |
| } |
| |
| Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| return graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), |
| vtrue0, vfalse0, merge0); |
| } |
| |
| Node* SimplifiedLowering::Int32Abs(Node* const node) { |
| Node* const zero = jsgraph()->Int32Constant(0); |
| Node* const input = node->InputAt(0); |
| |
| // if 0 < input then input else 0 - input |
| return graph()->NewNode( |
| common()->Select(MachineRepresentation::kWord32, BranchHint::kTrue), |
| graph()->NewNode(machine()->Int32LessThan(), zero, input), input, |
| graph()->NewNode(machine()->Int32Sub(), zero, input)); |
| } |
| |
| 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().HasValue()) { |
| 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().HasValue()) { |
| 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::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().HasValue()) { |
| 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().HasValue()) { |
| 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 then |
| // msk = rhs - 1 |
| // if rhs & msk != 0 then |
| // lhs % rhs |
| // else |
| // lhs & msk |
| // 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* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), rhs, |
| 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()->Uint32Mod(), lhs, rhs, if_true1); |
| |
| Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| Node* false1 = graph()->NewNode(machine()->Word32And(), lhs, msk); |
| |
| 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 = zero; |
| |
| Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0); |
| return graph()->NewNode(phi_op, true0, false0, merge0); |
| } |
| |
| |
| void SimplifiedLowering::DoShift(Node* node, Operator const* op, |
| Type* rhs_type) { |
| Node* const rhs = NodeProperties::GetValueInput(node, 1); |
| if (!rhs_type->Is(type_cache_.kZeroToThirtyOne)) { |
| node->ReplaceInput(1, graph()->NewNode(machine()->Word32And(), rhs, |
| jsgraph()->Int32Constant(0x1f))); |
| } |
| NodeProperties::ChangeOp(node, op); |
| } |
| |
| Node* SimplifiedLowering::ToNumberCode() { |
| if (!to_number_code_.is_set()) { |
| Callable callable = CodeFactory::ToNumber(isolate()); |
| to_number_code_.set(jsgraph()->HeapConstant(callable.code())); |
| } |
| return to_number_code_.get(); |
| } |
| |
| Operator const* SimplifiedLowering::ToNumberOperator() { |
| if (!to_number_operator_.is_set()) { |
| Callable callable = CodeFactory::ToNumber(isolate()); |
| CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, |
| Operator::kNoProperties); |
| to_number_operator_.set(common()->Call(desc)); |
| } |
| return to_number_operator_.get(); |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |