| // 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/base/bits.h" |
| #include "src/code-factory.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/operator-properties.h" |
| #include "src/compiler/representation-change.h" |
| #include "src/compiler/simplified-operator.h" |
| #include "src/compiler/source-position.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 { |
| |
| // The {UseInfo} class is used to describe a use of an input of a node. |
| // |
| // This information is used in two different ways, based on the phase: |
| // |
| // 1. During propagation, the use info is used to inform the input node |
| // about what part of the input is used (we call this truncation) and what |
| // is the preferred representation. |
| // |
| // 2. During lowering, the use info is used to properly convert the input |
| // to the preferred representation. The preferred representation might be |
| // insufficient to do the conversion (e.g. word32->float64 conv), so we also |
| // need the signedness information to produce the correct value. |
| class UseInfo { |
| public: |
| UseInfo(MachineRepresentation preferred, Truncation truncation) |
| : preferred_(preferred), truncation_(truncation) {} |
| static UseInfo TruncatingWord32() { |
| return UseInfo(MachineRepresentation::kWord32, Truncation::Word32()); |
| } |
| static UseInfo TruncatingWord64() { |
| return UseInfo(MachineRepresentation::kWord64, Truncation::Word64()); |
| } |
| static UseInfo Bool() { |
| return UseInfo(MachineRepresentation::kBit, Truncation::Bool()); |
| } |
| static UseInfo Float32() { |
| return UseInfo(MachineRepresentation::kFloat32, Truncation::Float32()); |
| } |
| static UseInfo Float64() { |
| return UseInfo(MachineRepresentation::kFloat64, Truncation::Float64()); |
| } |
| static UseInfo PointerInt() { |
| return kPointerSize == 4 ? TruncatingWord32() : TruncatingWord64(); |
| } |
| static UseInfo AnyTagged() { |
| return UseInfo(MachineRepresentation::kTagged, Truncation::Any()); |
| } |
| |
| // Undetermined representation. |
| static UseInfo Any() { |
| return UseInfo(MachineRepresentation::kNone, Truncation::Any()); |
| } |
| static UseInfo None() { |
| return UseInfo(MachineRepresentation::kNone, Truncation::None()); |
| } |
| |
| // Truncation to a representation that is smaller than the preferred |
| // one. |
| static UseInfo Float64TruncatingToWord32() { |
| return UseInfo(MachineRepresentation::kFloat64, Truncation::Word32()); |
| } |
| static UseInfo Word64TruncatingToWord32() { |
| return UseInfo(MachineRepresentation::kWord64, Truncation::Word32()); |
| } |
| static UseInfo AnyTruncatingToBool() { |
| return UseInfo(MachineRepresentation::kNone, Truncation::Bool()); |
| } |
| |
| MachineRepresentation preferred() const { return preferred_; } |
| Truncation truncation() const { return truncation_; } |
| |
| private: |
| MachineRepresentation preferred_; |
| Truncation truncation_; |
| }; |
| |
| |
| UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) { |
| switch (rep) { |
| case MachineRepresentation::kTagged: |
| return UseInfo::AnyTagged(); |
| case MachineRepresentation::kFloat64: |
| return UseInfo::Float64(); |
| case MachineRepresentation::kFloat32: |
| return UseInfo::Float32(); |
| 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::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::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.preferred(), use2.preferred()) && |
| use1.truncation().IsLessGeneralThan(use2.truncation()); |
| } |
| }; |
| |
| #endif // DEBUG |
| |
| } // namespace |
| |
| |
| class RepresentationSelector { |
| public: |
| // Information for each node tracked during the fixpoint. |
| class NodeOutputInfo { |
| public: |
| NodeOutputInfo(MachineRepresentation representation, Type* type) |
| : type_(type), representation_(representation) {} |
| NodeOutputInfo() |
| : type_(Type::None()), representation_(MachineRepresentation::kNone) {} |
| |
| MachineRepresentation representation() const { return representation_; } |
| Type* type() const { return type_; } |
| |
| static NodeOutputInfo None() { |
| return NodeOutputInfo(MachineRepresentation::kNone, Type::None()); |
| } |
| |
| static NodeOutputInfo Float32() { |
| return NodeOutputInfo(MachineRepresentation::kFloat32, Type::Number()); |
| } |
| |
| static NodeOutputInfo Float64() { |
| return NodeOutputInfo(MachineRepresentation::kFloat64, Type::Number()); |
| } |
| |
| static NodeOutputInfo NumberTruncatedToWord32() { |
| return NodeOutputInfo(MachineRepresentation::kWord32, Type::Number()); |
| } |
| |
| static NodeOutputInfo Int32() { |
| return NodeOutputInfo(MachineRepresentation::kWord32, Type::Signed32()); |
| } |
| |
| static NodeOutputInfo Uint32() { |
| return NodeOutputInfo(MachineRepresentation::kWord32, Type::Unsigned32()); |
| } |
| |
| static NodeOutputInfo Bool() { |
| return NodeOutputInfo(MachineRepresentation::kBit, Type::Boolean()); |
| } |
| |
| static NodeOutputInfo Int64() { |
| // TODO(jarin) Fix once we have a real int64 type. |
| return NodeOutputInfo(MachineRepresentation::kWord64, Type::Internal()); |
| } |
| |
| static NodeOutputInfo Uint64() { |
| // TODO(jarin) Fix once we have a real uint64 type. |
| return NodeOutputInfo(MachineRepresentation::kWord64, Type::Internal()); |
| } |
| |
| static NodeOutputInfo AnyTagged() { |
| return NodeOutputInfo(MachineRepresentation::kTagged, Type::Any()); |
| } |
| |
| static NodeOutputInfo NumberTagged() { |
| return NodeOutputInfo(MachineRepresentation::kTagged, Type::Number()); |
| } |
| |
| static NodeOutputInfo Pointer() { |
| return NodeOutputInfo(MachineType::PointerRepresentation(), Type::Any()); |
| } |
| |
| private: |
| Type* type_; |
| MachineRepresentation representation_; |
| }; |
| |
| 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(bool value) { queued_ = value; } |
| bool queued() const { return queued_; } |
| void set_visited() { visited_ = true; } |
| bool visited() const { return visited_; } |
| Truncation truncation() const { return truncation_; } |
| void set_output_type(NodeOutputInfo output) { output_ = output; } |
| |
| Type* output_type() const { return output_.type(); } |
| MachineRepresentation representation() const { |
| return output_.representation(); |
| } |
| |
| private: |
| bool queued_ = false; // Bookkeeping for the traversal. |
| bool visited_ = false; // Bookkeeping for the traversal. |
| NodeOutputInfo output_; // Output type and representation. |
| Truncation truncation_ = Truncation::None(); // Information about uses. |
| }; |
| |
| RepresentationSelector(JSGraph* jsgraph, Zone* zone, |
| RepresentationChanger* changer, |
| SourcePositionTable* source_positions) |
| : jsgraph_(jsgraph), |
| 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), |
| source_positions_(source_positions), |
| type_cache_(TypeCache::Get()) { |
| } |
| |
| void Run(SimplifiedLowering* lowering) { |
| // 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_queued(false); |
| TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic()); |
| VisitNode(node, info->truncation(), nullptr); |
| TRACE(" ==> output "); |
| PrintOutputInfo(info); |
| TRACE("\n"); |
| } |
| |
| // 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); |
| // 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_visited(); |
| info->set_queued(true); |
| 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->visited()) { |
| // First visit of this node. |
| info->set_visited(); |
| info->set_queued(true); |
| 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(true); |
| TRACE(" added: "); |
| } else { |
| TRACE(" inqueue: "); |
| } |
| PrintTruncation(info->truncation()); |
| } |
| } |
| |
| bool lower() { return phase_ == LOWER; } |
| |
| void EnqueueUses(Node* node) { |
| for (Edge edge : node->use_edges()) { |
| if (NodeProperties::IsValueEdge(edge)) { |
| Node* const user = edge.from(); |
| if (user->id() < count_) { |
| // New type information for the node is available. |
| NodeInfo* info = GetInfo(user); |
| // Enqueue the node only if we are sure it is reachable from |
| // the end and it has not been queued yet. |
| if (info->visited() && !info->queued()) { |
| queue_.push(user); |
| info->set_queued(true); |
| } |
| } |
| } |
| } |
| } |
| |
| void SetOutputFromMachineType(Node* node, MachineType machine_type) { |
| Type* type = Type::None(); |
| switch (machine_type.semantic()) { |
| case MachineSemantic::kNone: |
| type = Type::None(); |
| break; |
| case MachineSemantic::kBool: |
| type = Type::Boolean(); |
| break; |
| case MachineSemantic::kInt32: |
| type = Type::Signed32(); |
| break; |
| case MachineSemantic::kUint32: |
| type = Type::Unsigned32(); |
| break; |
| case MachineSemantic::kInt64: |
| // TODO(jarin) Fix once we have proper int64. |
| type = Type::Internal(); |
| break; |
| case MachineSemantic::kUint64: |
| // TODO(jarin) Fix once we have proper uint64. |
| type = Type::Internal(); |
| break; |
| case MachineSemantic::kNumber: |
| type = Type::Number(); |
| break; |
| case MachineSemantic::kAny: |
| type = Type::Any(); |
| break; |
| } |
| return SetOutput(node, NodeOutputInfo(machine_type.representation(), type)); |
| } |
| |
| void SetOutput(Node* node, NodeOutputInfo output_info) { |
| // Every node should have at most one output representation. Note that |
| // phis can have 0, if they have not been used in a representation-inducing |
| // instruction. |
| Type* output_type = output_info.type(); |
| if (NodeProperties::IsTyped(node)) { |
| output_type = Type::Intersect(NodeProperties::GetType(node), |
| output_info.type(), jsgraph_->zone()); |
| } |
| NodeInfo* info = GetInfo(node); |
| DCHECK(info->output_type()->Is(output_type)); |
| DCHECK(MachineRepresentationIsSubtype(info->representation(), |
| output_info.representation())); |
| if (!output_type->Is(info->output_type()) || |
| output_info.representation() != info->representation()) { |
| EnqueueUses(node); |
| } |
| info->set_output_type( |
| NodeOutputInfo(output_info.representation(), output_type)); |
| } |
| |
| bool BothInputsAreSigned32(Node* node) { |
| DCHECK_EQ(2, node->InputCount()); |
| return GetInfo(node->InputAt(0))->output_type()->Is(Type::Signed32()) && |
| GetInfo(node->InputAt(1))->output_type()->Is(Type::Signed32()); |
| } |
| |
| bool BothInputsAreUnsigned32(Node* node) { |
| DCHECK_EQ(2, node->InputCount()); |
| return GetInfo(node->InputAt(0))->output_type()->Is(Type::Unsigned32()) && |
| GetInfo(node->InputAt(1))->output_type()->Is(Type::Unsigned32()); |
| } |
| |
| bool BothInputsAre(Node* node, Type* type) { |
| DCHECK_EQ(2, node->InputCount()); |
| return GetInfo(node->InputAt(0))->output_type()->Is(type) && |
| GetInfo(node->InputAt(1))->output_type()->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.preferred() == MachineRepresentation::kNone) |
| return; // No input requirement on the use. |
| NodeInfo* input_info = GetInfo(input); |
| MachineRepresentation input_rep = input_info->representation(); |
| if (input_rep != use.preferred()) { |
| // 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(), input_info->output_type(), |
| use.preferred(), use.truncation()); |
| 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, |
| NodeOutputInfo output) { |
| 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); |
| } |
| |
| // Helper for binops of the I x I -> O variety. |
| void VisitBinop(Node* node, UseInfo input_use, NodeOutputInfo output) { |
| VisitBinop(node, input_use, input_use, output); |
| } |
| |
| // Helper for unops of the I -> O variety. |
| void VisitUnop(Node* node, UseInfo input_use, NodeOutputInfo output) { |
| DCHECK_EQ(1, node->InputCount()); |
| ProcessInput(node, 0, input_use); |
| SetOutput(node, output); |
| } |
| |
| // Helper for leaf nodes. |
| void VisitLeaf(Node* node, NodeOutputInfo output) { |
| DCHECK_EQ(0, node->InputCount()); |
| SetOutput(node, output); |
| } |
| |
| // Helpers for specific types of binops. |
| void VisitFloat64Binop(Node* node) { |
| VisitBinop(node, UseInfo::Float64(), NodeOutputInfo::Float64()); |
| } |
| void VisitInt32Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), NodeOutputInfo::Int32()); |
| } |
| void VisitWord32TruncatingBinop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo::NumberTruncatedToWord32()); |
| } |
| void VisitUint32Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), NodeOutputInfo::Uint32()); |
| } |
| void VisitInt64Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), NodeOutputInfo::Int64()); |
| } |
| void VisitUint64Binop(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), NodeOutputInfo::Uint64()); |
| } |
| void VisitFloat64Cmp(Node* node) { |
| VisitBinop(node, UseInfo::Float64(), NodeOutputInfo::Bool()); |
| } |
| void VisitInt32Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), NodeOutputInfo::Bool()); |
| } |
| void VisitUint32Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord32(), NodeOutputInfo::Bool()); |
| } |
| void VisitInt64Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), NodeOutputInfo::Bool()); |
| } |
| void VisitUint64Cmp(Node* node) { |
| VisitBinop(node, UseInfo::TruncatingWord64(), NodeOutputInfo::Bool()); |
| } |
| |
| // Infer representation for phi-like nodes. |
| NodeOutputInfo GetOutputInfoForPhi(Node* node, Truncation use) { |
| // Compute the type. |
| Type* type = GetInfo(node->InputAt(0))->output_type(); |
| for (int i = 1; i < node->op()->ValueInputCount(); ++i) { |
| type = Type::Union(type, GetInfo(node->InputAt(i))->output_type(), |
| jsgraph_->zone()); |
| } |
| |
| // Compute the representation. |
| MachineRepresentation rep = MachineRepresentation::kTagged; |
| if (type->Is(Type::None())) { |
| rep = MachineRepresentation::kNone; |
| } else if (type->Is(Type::Signed32()) || type->Is(Type::Unsigned32())) { |
| rep = MachineRepresentation::kWord32; |
| } else if (use.TruncatesToWord32()) { |
| rep = MachineRepresentation::kWord32; |
| } else if (type->Is(Type::Boolean())) { |
| rep = MachineRepresentation::kBit; |
| } else if (type->Is(Type::Number())) { |
| rep = 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. |
| for (int i = 1; i < node->op()->ValueInputCount(); i++) { |
| DCHECK_EQ(is_word64, GetInfo(node->InputAt(i))->representation() == |
| MachineRepresentation::kWord64); |
| } |
| #endif |
| rep = is_word64 ? MachineRepresentation::kWord64 |
| : MachineRepresentation::kTagged; |
| } |
| return NodeOutputInfo(rep, type); |
| } |
| |
| // Helper for handling selects. |
| void VisitSelect(Node* node, Truncation truncation, |
| SimplifiedLowering* lowering) { |
| ProcessInput(node, 0, UseInfo::Bool()); |
| |
| NodeOutputInfo output = GetOutputInfoForPhi(node, truncation); |
| SetOutput(node, output); |
| |
| if (lower()) { |
| // Update the select operator. |
| SelectParameters p = SelectParametersOf(node->op()); |
| if (output.representation() != p.representation()) { |
| NodeProperties::ChangeOp(node, lowering->common()->Select( |
| output.representation(), p.hint())); |
| } |
| } |
| // Convert inputs to the output representation of this phi, pass the |
| // truncation truncation along. |
| UseInfo input_use(output.representation(), truncation); |
| ProcessInput(node, 1, input_use); |
| ProcessInput(node, 2, input_use); |
| } |
| |
| // Helper for handling phis. |
| void VisitPhi(Node* node, Truncation truncation, |
| SimplifiedLowering* lowering) { |
| NodeOutputInfo output = GetOutputInfoForPhi(node, truncation); |
| SetOutput(node, output); |
| |
| int values = node->op()->ValueInputCount(); |
| if (lower()) { |
| // Update the phi operator. |
| if (output.representation() != PhiRepresentationOf(node->op())) { |
| NodeProperties::ChangeOp( |
| node, lowering->common()->Phi(output.representation(), values)); |
| } |
| } |
| |
| // Convert inputs to the output representation of this phi, pass the |
| // truncation truncation along. |
| UseInfo input_use(output.representation(), 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 = OpParameter<const CallDescriptor*>(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) { |
| SetOutputFromMachineType(node, desc->GetMachineSignature()->GetReturn()); |
| } else { |
| SetOutput(node, NodeOutputInfo::AnyTagged()); |
| } |
| } |
| |
| 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++) { |
| NodeInfo* input_info = GetInfo(node->InputAt(i)); |
| MachineType machine_type( |
| input_info->representation(), |
| DeoptValueSemanticOf(input_info->output_type())); |
| 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, NodeOutputInfo::AnyTagged()); |
| } |
| |
| const Operator* Int32Op(Node* node) { |
| return changer_->Int32OperatorFor(node->opcode()); |
| } |
| |
| const Operator* Uint32Op(Node* node) { |
| return changer_->Uint32OperatorFor(node->opcode()); |
| } |
| |
| const Operator* Float64Op(Node* node) { |
| return changer_->Float64OperatorFor(node->opcode()); |
| } |
| |
| // 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, NodeOutputInfo::None()); |
| case IrOpcode::kParameter: { |
| // TODO(titzer): use representation from linkage. |
| Type* type = NodeProperties::GetType(node); |
| ProcessInput(node, 0, UseInfo::None()); |
| SetOutput(node, NodeOutputInfo(MachineRepresentation::kTagged, type)); |
| return; |
| } |
| case IrOpcode::kInt32Constant: |
| return VisitLeaf(node, NodeOutputInfo::Int32()); |
| case IrOpcode::kInt64Constant: |
| return VisitLeaf(node, NodeOutputInfo::Int64()); |
| case IrOpcode::kFloat32Constant: |
| return VisitLeaf(node, NodeOutputInfo::Float32()); |
| case IrOpcode::kFloat64Constant: |
| return VisitLeaf(node, NodeOutputInfo::Float64()); |
| case IrOpcode::kExternalConstant: |
| return VisitLeaf(node, NodeOutputInfo::Pointer()); |
| case IrOpcode::kNumberConstant: |
| return VisitLeaf(node, NodeOutputInfo::NumberTagged()); |
| case IrOpcode::kHeapConstant: |
| return VisitLeaf(node, NodeOutputInfo::AnyTagged()); |
| |
| case IrOpcode::kBranch: |
| ProcessInput(node, 0, UseInfo::Bool()); |
| EnqueueInput(node, NodeProperties::FirstControlIndex(node)); |
| break; |
| case IrOpcode::kSwitch: |
| ProcessInput(node, 0, UseInfo::TruncatingWord32()); |
| EnqueueInput(node, NodeProperties::FirstControlIndex(node)); |
| break; |
| 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. |
| //------------------------------------------------------------------ |
| // For now, we assume that all JS operators were too complex to lower |
| // to Simplified and that they will always require tagged value inputs |
| // and produce tagged value outputs. |
| // TODO(turbofan): it might be possible to lower some JSOperators here, |
| // but that responsibility really lies in the typed lowering phase. |
| #define DEFINE_JS_CASE(x) case IrOpcode::k##x: |
| JS_OP_LIST(DEFINE_JS_CASE) |
| #undef DEFINE_JS_CASE |
| VisitInputs(node); |
| return SetOutput(node, NodeOutputInfo::AnyTagged()); |
| |
| //------------------------------------------------------------------ |
| // 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, NodeOutputInfo::Bool()); |
| } |
| break; |
| } |
| 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, NodeOutputInfo::Int32()); |
| } |
| break; |
| } |
| case IrOpcode::kNumberEqual: |
| case IrOpcode::kNumberLessThan: |
| case IrOpcode::kNumberLessThanOrEqual: { |
| // Number comparisons reduce to integer comparisons for integer inputs. |
| if (BothInputsAreSigned32(node)) { |
| // => signed Int32Cmp |
| VisitInt32Cmp(node); |
| if (lower()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| } else if (BothInputsAreUnsigned32(node)) { |
| // => unsigned Int32Cmp |
| VisitUint32Cmp(node); |
| if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node)); |
| } else { |
| // => Float64Cmp |
| VisitFloat64Cmp(node); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| } |
| break; |
| } |
| 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_.kAdditiveSafeInteger) && |
| 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)); |
| } |
| break; |
| } |
| 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()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| break; |
| } |
| if (truncation.TruncatesToWord32() && |
| NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger)) { |
| // 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()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| break; |
| } |
| } |
| // => Float64Mul |
| VisitFloat64Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| break; |
| } |
| case IrOpcode::kNumberDivide: { |
| if (BothInputsAreSigned32(node)) { |
| if (NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // => signed Int32Div |
| VisitInt32Binop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Div(node)); |
| break; |
| } |
| if (truncation.TruncatesToWord32()) { |
| // => signed Int32Div |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Div(node)); |
| break; |
| } |
| } |
| if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) { |
| // => unsigned Uint32Div |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Uint32Div(node)); |
| break; |
| } |
| // => Float64Div |
| VisitFloat64Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| break; |
| } |
| case IrOpcode::kNumberModulus: { |
| if (BothInputsAreSigned32(node)) { |
| if (NodeProperties::GetType(node)->Is(Type::Signed32())) { |
| // => signed Int32Mod |
| VisitInt32Binop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Mod(node)); |
| break; |
| } |
| if (truncation.TruncatesToWord32()) { |
| // => signed Int32Mod |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Int32Mod(node)); |
| break; |
| } |
| } |
| if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) { |
| // => unsigned Uint32Mod |
| VisitWord32TruncatingBinop(node); |
| if (lower()) DeferReplacement(node, lowering->Uint32Mod(node)); |
| break; |
| } |
| // => Float64Mod |
| VisitFloat64Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Float64Op(node)); |
| break; |
| } |
| case IrOpcode::kNumberBitwiseOr: |
| case IrOpcode::kNumberBitwiseXor: |
| case IrOpcode::kNumberBitwiseAnd: { |
| VisitInt32Binop(node); |
| if (lower()) NodeProperties::ChangeOp(node, Int32Op(node)); |
| break; |
| } |
| case IrOpcode::kNumberShiftLeft: { |
| Type* rhs_type = GetInfo(node->InputAt(1))->output_type(); |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), NodeOutputInfo::Int32()); |
| if (lower()) { |
| lowering->DoShift(node, lowering->machine()->Word32Shl(), rhs_type); |
| } |
| break; |
| } |
| case IrOpcode::kNumberShiftRight: { |
| Type* rhs_type = GetInfo(node->InputAt(1))->output_type(); |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), NodeOutputInfo::Int32()); |
| if (lower()) { |
| lowering->DoShift(node, lowering->machine()->Word32Sar(), rhs_type); |
| } |
| break; |
| } |
| case IrOpcode::kNumberShiftRightLogical: { |
| Type* rhs_type = GetInfo(node->InputAt(1))->output_type(); |
| VisitBinop(node, UseInfo::TruncatingWord32(), |
| UseInfo::TruncatingWord32(), NodeOutputInfo::Uint32()); |
| if (lower()) { |
| lowering->DoShift(node, lowering->machine()->Word32Shr(), rhs_type); |
| } |
| break; |
| } |
| case IrOpcode::kNumberToInt32: { |
| // Just change representation if necessary. |
| VisitUnop(node, UseInfo::TruncatingWord32(), NodeOutputInfo::Int32()); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| break; |
| } |
| case IrOpcode::kNumberToUint32: { |
| // Just change representation if necessary. |
| VisitUnop(node, UseInfo::TruncatingWord32(), NodeOutputInfo::Uint32()); |
| if (lower()) DeferReplacement(node, node->InputAt(0)); |
| break; |
| } |
| case IrOpcode::kNumberIsHoleNaN: { |
| VisitUnop(node, UseInfo::Float64(), NodeOutputInfo::Bool()); |
| if (lower()) { |
| // NumberIsHoleNaN(x) => Word32Equal(Float64ExtractLowWord32(x), |
| // #HoleNaNLower32) |
| node->ReplaceInput(0, |
| jsgraph_->graph()->NewNode( |
| lowering->machine()->Float64ExtractLowWord32(), |
| node->InputAt(0))); |
| node->AppendInput(jsgraph_->zone(), |
| jsgraph_->Int32Constant(kHoleNanLower32)); |
| NodeProperties::ChangeOp(node, jsgraph_->machine()->Word32Equal()); |
| } |
| break; |
| } |
| case IrOpcode::kPlainPrimitiveToNumber: { |
| VisitUnop(node, UseInfo::AnyTagged(), NodeOutputInfo::NumberTagged()); |
| if (lower()) { |
| // PlainPrimitiveToNumber(x) => Call(ToNumberStub, x, no-context) |
| Operator::Properties properties = node->op()->properties(); |
| Callable callable = CodeFactory::ToNumber(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()); |
| NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc)); |
| } |
| break; |
| } |
| case IrOpcode::kReferenceEqual: { |
| VisitBinop(node, UseInfo::AnyTagged(), NodeOutputInfo::Bool()); |
| if (lower()) { |
| NodeProperties::ChangeOp(node, lowering->machine()->WordEqual()); |
| } |
| break; |
| } |
| case IrOpcode::kStringEqual: { |
| VisitBinop(node, UseInfo::AnyTagged(), NodeOutputInfo::Bool()); |
| if (lower()) lowering->DoStringEqual(node); |
| break; |
| } |
| case IrOpcode::kStringLessThan: { |
| VisitBinop(node, UseInfo::AnyTagged(), NodeOutputInfo::Bool()); |
| if (lower()) lowering->DoStringLessThan(node); |
| break; |
| } |
| case IrOpcode::kStringLessThanOrEqual: { |
| VisitBinop(node, UseInfo::AnyTagged(), NodeOutputInfo::Bool()); |
| if (lower()) lowering->DoStringLessThanOrEqual(node); |
| break; |
| } |
| case IrOpcode::kAllocate: { |
| ProcessInput(node, 0, UseInfo::AnyTagged()); |
| ProcessRemainingInputs(node, 1); |
| SetOutput(node, NodeOutputInfo::AnyTagged()); |
| break; |
| } |
| case IrOpcode::kLoadField: { |
| FieldAccess access = FieldAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfoForBasePointer(access)); |
| ProcessRemainingInputs(node, 1); |
| SetOutputFromMachineType(node, access.machine_type); |
| break; |
| } |
| 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, NodeOutputInfo::None()); |
| break; |
| } |
| 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); |
| |
| NodeOutputInfo output_info; |
| if (truncation.TruncatesUndefinedToZeroOrNaN()) { |
| if (truncation.TruncatesNaNToZero()) { |
| // If undefined is truncated to a non-NaN number, we can use |
| // the load's representation. |
| output_info = NodeOutputInfo(access.machine_type().representation(), |
| NodeProperties::GetType(node)); |
| } 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_info = |
| NodeOutputInfo(access.machine_type().representation(), |
| NodeProperties::GetType(node)); |
| } else { |
| output_info = NodeOutputInfo::Float64(); |
| } |
| } |
| } else { |
| // If undefined is not truncated away, we need to have the tagged |
| // representation. |
| output_info = NodeOutputInfo::AnyTagged(); |
| } |
| SetOutput(node, output_info); |
| if (lower()) |
| lowering->DoLoadBuffer(node, output_info.representation(), changer_); |
| break; |
| } |
| 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, NodeOutputInfo::None()); |
| if (lower()) lowering->DoStoreBuffer(node); |
| break; |
| } |
| case IrOpcode::kLoadElement: { |
| ElementAccess access = ElementAccessOf(node->op()); |
| ProcessInput(node, 0, UseInfoForBasePointer(access)); // base |
| ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index |
| ProcessRemainingInputs(node, 2); |
| SetOutputFromMachineType(node, access.machine_type); |
| break; |
| } |
| 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, NodeOutputInfo::None()); |
| break; |
| } |
| case IrOpcode::kObjectIsNumber: { |
| ProcessInput(node, 0, UseInfo::AnyTagged()); |
| SetOutput(node, NodeOutputInfo::Bool()); |
| if (lower()) lowering->DoObjectIsNumber(node); |
| break; |
| } |
| case IrOpcode::kObjectIsSmi: { |
| ProcessInput(node, 0, UseInfo::AnyTagged()); |
| SetOutput(node, NodeOutputInfo::Bool()); |
| if (lower()) lowering->DoObjectIsSmi(node); |
| break; |
| } |
| |
| //------------------------------------------------------------------ |
| // 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); |
| SetOutputFromMachineType(node, rep); |
| break; |
| } |
| 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); |
| SetOutput(node, NodeOutputInfo::None()); |
| break; |
| } |
| case IrOpcode::kWord32Shr: |
| // We output unsigned int32 for shift right because JavaScript. |
| return VisitBinop(node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo::Uint32()); |
| 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(), |
| NodeOutputInfo::Int32()); |
| case IrOpcode::kWord32Equal: |
| return VisitBinop(node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo::Bool()); |
| |
| case IrOpcode::kWord32Clz: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo::Uint32()); |
| |
| 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(), |
| NodeOutputInfo::Int64()); |
| case IrOpcode::kWord64Equal: |
| return VisitBinop(node, UseInfo::TruncatingWord64(), |
| NodeOutputInfo::Bool()); |
| |
| case IrOpcode::kChangeInt32ToInt64: |
| return VisitUnop( |
| node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo(MachineRepresentation::kWord64, Type::Signed32())); |
| case IrOpcode::kChangeUint32ToUint64: |
| return VisitUnop( |
| node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo(MachineRepresentation::kWord64, Type::Unsigned32())); |
| case IrOpcode::kTruncateFloat64ToFloat32: |
| return VisitUnop(node, UseInfo::Float64(), NodeOutputInfo::Float32()); |
| case IrOpcode::kTruncateFloat64ToInt32: |
| return VisitUnop(node, UseInfo::Float64(), NodeOutputInfo::Int32()); |
| case IrOpcode::kTruncateInt64ToInt32: |
| // TODO(titzer): Is kTypeInt32 correct here? |
| return VisitUnop(node, UseInfo::Word64TruncatingToWord32(), |
| NodeOutputInfo::Int32()); |
| |
| case IrOpcode::kChangeFloat32ToFloat64: |
| return VisitUnop(node, UseInfo::Float32(), NodeOutputInfo::Float64()); |
| case IrOpcode::kChangeInt32ToFloat64: |
| return VisitUnop( |
| node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo(MachineRepresentation::kFloat64, Type::Signed32())); |
| case IrOpcode::kChangeUint32ToFloat64: |
| return VisitUnop(node, UseInfo::TruncatingWord32(), |
| NodeOutputInfo(MachineRepresentation::kFloat64, |
| Type::Unsigned32())); |
| case IrOpcode::kChangeFloat64ToInt32: |
| return VisitUnop(node, UseInfo::Float64TruncatingToWord32(), |
| NodeOutputInfo::Int32()); |
| case IrOpcode::kChangeFloat64ToUint32: |
| return VisitUnop(node, UseInfo::Float64TruncatingToWord32(), |
| NodeOutputInfo::Uint32()); |
| |
| 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: |
| return VisitUnop(node, UseInfo::Float64(), NodeOutputInfo::Float64()); |
| case IrOpcode::kFloat64Equal: |
| case IrOpcode::kFloat64LessThan: |
| case IrOpcode::kFloat64LessThanOrEqual: |
| return VisitFloat64Cmp(node); |
| case IrOpcode::kFloat64ExtractLowWord32: |
| case IrOpcode::kFloat64ExtractHighWord32: |
| return VisitUnop(node, UseInfo::Float64(), NodeOutputInfo::Int32()); |
| case IrOpcode::kFloat64InsertLowWord32: |
| case IrOpcode::kFloat64InsertHighWord32: |
| return VisitBinop(node, UseInfo::Float64(), UseInfo::TruncatingWord32(), |
| NodeOutputInfo::Float64()); |
| case IrOpcode::kLoadStackPointer: |
| case IrOpcode::kLoadFramePointer: |
| return VisitLeaf(node, NodeOutputInfo::Pointer()); |
| case IrOpcode::kStateValues: |
| VisitStateValues(node); |
| break; |
| default: |
| VisitInputs(node); |
| // Assume the output is tagged. |
| SetOutput(node, NodeOutputInfo::AnyTagged()); |
| break; |
| } |
| } |
| |
| 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()); |
| |
| if (replacement->id() < count_ && |
| GetInfo(node)->output_type()->Is(GetInfo(replacement)->output_type())) { |
| // 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() << " ("; |
| info->output_type()->PrintTo(os, Type::SEMANTIC_DIM); |
| os << ")"; |
| } |
| } |
| |
| 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(); |
| } |
| } |
| |
| void PrintUseInfo(UseInfo info) { |
| if (FLAG_trace_representation) { |
| OFStream os(stdout); |
| os << info.preferred() << ":" << info.truncation().description(); |
| } |
| } |
| |
| private: |
| JSGraph* jsgraph_; |
| 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 |
| // 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_; |
| |
| NodeInfo* GetInfo(Node* node) { |
| DCHECK(node->id() >= 0); |
| DCHECK(node->id() < count_); |
| return &info_[node->id()]; |
| } |
| }; |
| |
| |
| SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, Zone* zone, |
| SourcePositionTable* source_positions) |
| : jsgraph_(jsgraph), |
| zone_(zone), |
| type_cache_(TypeCache::Get()), |
| source_positions_(source_positions) {} |
| |
| |
| void SimplifiedLowering::LowerAllNodes() { |
| RepresentationChanger changer(jsgraph(), jsgraph()->isolate()); |
| RepresentationSelector selector(jsgraph(), zone_, &changer, |
| source_positions_); |
| selector.Run(this); |
| } |
| |
| |
| 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); |
| Node* vtrue = changer->GetRepresentationFor( |
| etrue, access_type.representation(), NodeProperties::GetType(node), |
| 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)); |
| } |
| |
| |
| void SimplifiedLowering::DoObjectIsNumber(Node* node) { |
| Node* input = NodeProperties::GetValueInput(node, 0); |
| // TODO(bmeurer): Optimize somewhat based on input type. |
| Node* check = |
| graph()->NewNode(machine()->WordEqual(), |
| graph()->NewNode(machine()->WordAnd(), input, |
| jsgraph()->IntPtrConstant(kSmiTagMask)), |
| jsgraph()->IntPtrConstant(kSmiTag)); |
| Node* branch = graph()->NewNode(common()->Branch(), check, graph()->start()); |
| Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| Node* vtrue = jsgraph()->Int32Constant(1); |
| Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
| Node* vfalse = graph()->NewNode( |
| machine()->WordEqual(), |
| graph()->NewNode( |
| machine()->Load(MachineType::AnyTagged()), input, |
| jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag), |
| graph()->start(), if_false), |
| jsgraph()->HeapConstant(isolate()->factory()->heap_number_map())); |
| Node* control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
| node->ReplaceInput(0, vtrue); |
| node->AppendInput(graph()->zone(), vfalse); |
| node->AppendInput(graph()->zone(), control); |
| NodeProperties::ChangeOp(node, common()->Phi(MachineRepresentation::kBit, 2)); |
| } |
| |
| |
| void SimplifiedLowering::DoObjectIsSmi(Node* node) { |
| node->ReplaceInput(0, |
| graph()->NewNode(machine()->WordAnd(), node->InputAt(0), |
| jsgraph()->IntPtrConstant(kSmiTagMask))); |
| node->AppendInput(graph()->zone(), jsgraph()->IntPtrConstant(kSmiTag)); |
| NodeProperties::ChangeOp(node, machine()->WordEqual()); |
| } |
| |
| |
| Node* SimplifiedLowering::StringComparison(Node* node) { |
| Operator::Properties properties = node->op()->properties(); |
| Callable callable = CodeFactory::StringCompare(isolate()); |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), zone(), callable.descriptor(), 0, flags, properties); |
| return graph()->NewNode( |
| common()->Call(desc), jsgraph()->HeapConstant(callable.code()), |
| NodeProperties::GetValueInput(node, 0), |
| NodeProperties::GetValueInput(node, 1), jsgraph()->NoContextConstant(), |
| NodeProperties::GetEffectInput(node), |
| NodeProperties::GetControlInput(node)); |
| } |
| |
| |
| 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); |
| } |
| |
| |
| namespace { |
| |
| void ReplaceEffectUses(Node* node, Node* replacement) { |
| // Requires distinguishing between value and effect edges. |
| DCHECK(replacement->op()->EffectOutputCount() > 0); |
| for (Edge edge : node->use_edges()) { |
| if (NodeProperties::IsEffectEdge(edge)) { |
| edge.UpdateTo(replacement); |
| } else { |
| DCHECK(NodeProperties::IsValueEdge(edge)); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| |
| void SimplifiedLowering::DoStringEqual(Node* node) { |
| Node* comparison = StringComparison(node); |
| ReplaceEffectUses(node, comparison); |
| node->ReplaceInput(0, comparison); |
| node->ReplaceInput(1, jsgraph()->SmiConstant(EQUAL)); |
| node->TrimInputCount(2); |
| NodeProperties::ChangeOp(node, machine()->WordEqual()); |
| } |
| |
| |
| void SimplifiedLowering::DoStringLessThan(Node* node) { |
| Node* comparison = StringComparison(node); |
| ReplaceEffectUses(node, comparison); |
| node->ReplaceInput(0, comparison); |
| node->ReplaceInput(1, jsgraph()->SmiConstant(EQUAL)); |
| node->TrimInputCount(2); |
| NodeProperties::ChangeOp(node, machine()->IntLessThan()); |
| } |
| |
| |
| void SimplifiedLowering::DoStringLessThanOrEqual(Node* node) { |
| Node* comparison = StringComparison(node); |
| ReplaceEffectUses(node, comparison); |
| node->ReplaceInput(0, comparison); |
| node->ReplaceInput(1, jsgraph()->SmiConstant(EQUAL)); |
| node->TrimInputCount(2); |
| NodeProperties::ChangeOp(node, machine()->IntLessThanOrEqual()); |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |