Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/node-properties.h"
      6 #include "src/compiler/common-operator.h"
      7 #include "src/compiler/graph.h"
      8 #include "src/compiler/js-operator.h"
      9 #include "src/compiler/linkage.h"
     10 #include "src/compiler/node-matchers.h"
     11 #include "src/compiler/operator-properties.h"
     12 #include "src/compiler/simplified-operator.h"
     13 #include "src/compiler/verifier.h"
     14 #include "src/handles-inl.h"
     15 #include "src/objects-inl.h"
     16 #include "src/zone/zone-handle-set.h"
     17 
     18 namespace v8 {
     19 namespace internal {
     20 namespace compiler {
     21 
     22 // static
     23 int NodeProperties::PastValueIndex(Node* node) {
     24   return FirstValueIndex(node) + node->op()->ValueInputCount();
     25 }
     26 
     27 
     28 // static
     29 int NodeProperties::PastContextIndex(Node* node) {
     30   return FirstContextIndex(node) +
     31          OperatorProperties::GetContextInputCount(node->op());
     32 }
     33 
     34 
     35 // static
     36 int NodeProperties::PastFrameStateIndex(Node* node) {
     37   return FirstFrameStateIndex(node) +
     38          OperatorProperties::GetFrameStateInputCount(node->op());
     39 }
     40 
     41 
     42 // static
     43 int NodeProperties::PastEffectIndex(Node* node) {
     44   return FirstEffectIndex(node) + node->op()->EffectInputCount();
     45 }
     46 
     47 
     48 // static
     49 int NodeProperties::PastControlIndex(Node* node) {
     50   return FirstControlIndex(node) + node->op()->ControlInputCount();
     51 }
     52 
     53 
     54 // static
     55 Node* NodeProperties::GetValueInput(Node* node, int index) {
     56   DCHECK(0 <= index && index < node->op()->ValueInputCount());
     57   return node->InputAt(FirstValueIndex(node) + index);
     58 }
     59 
     60 
     61 // static
     62 Node* NodeProperties::GetContextInput(Node* node) {
     63   DCHECK(OperatorProperties::HasContextInput(node->op()));
     64   return node->InputAt(FirstContextIndex(node));
     65 }
     66 
     67 
     68 // static
     69 Node* NodeProperties::GetFrameStateInput(Node* node) {
     70   DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
     71   return node->InputAt(FirstFrameStateIndex(node));
     72 }
     73 
     74 
     75 // static
     76 Node* NodeProperties::GetEffectInput(Node* node, int index) {
     77   DCHECK(0 <= index && index < node->op()->EffectInputCount());
     78   return node->InputAt(FirstEffectIndex(node) + index);
     79 }
     80 
     81 
     82 // static
     83 Node* NodeProperties::GetControlInput(Node* node, int index) {
     84   DCHECK(0 <= index && index < node->op()->ControlInputCount());
     85   return node->InputAt(FirstControlIndex(node) + index);
     86 }
     87 
     88 
     89 // static
     90 bool NodeProperties::IsValueEdge(Edge edge) {
     91   Node* const node = edge.from();
     92   return IsInputRange(edge, FirstValueIndex(node),
     93                       node->op()->ValueInputCount());
     94 }
     95 
     96 
     97 // static
     98 bool NodeProperties::IsContextEdge(Edge edge) {
     99   Node* const node = edge.from();
    100   return IsInputRange(edge, FirstContextIndex(node),
    101                       OperatorProperties::GetContextInputCount(node->op()));
    102 }
    103 
    104 
    105 // static
    106 bool NodeProperties::IsFrameStateEdge(Edge edge) {
    107   Node* const node = edge.from();
    108   return IsInputRange(edge, FirstFrameStateIndex(node),
    109                       OperatorProperties::GetFrameStateInputCount(node->op()));
    110 }
    111 
    112 
    113 // static
    114 bool NodeProperties::IsEffectEdge(Edge edge) {
    115   Node* const node = edge.from();
    116   return IsInputRange(edge, FirstEffectIndex(node),
    117                       node->op()->EffectInputCount());
    118 }
    119 
    120 
    121 // static
    122 bool NodeProperties::IsControlEdge(Edge edge) {
    123   Node* const node = edge.from();
    124   return IsInputRange(edge, FirstControlIndex(node),
    125                       node->op()->ControlInputCount());
    126 }
    127 
    128 
    129 // static
    130 bool NodeProperties::IsExceptionalCall(Node* node, Node** out_exception) {
    131   if (node->op()->HasProperty(Operator::kNoThrow)) return false;
    132   for (Edge const edge : node->use_edges()) {
    133     if (!NodeProperties::IsControlEdge(edge)) continue;
    134     if (edge.from()->opcode() == IrOpcode::kIfException) {
    135       if (out_exception != nullptr) *out_exception = edge.from();
    136       return true;
    137     }
    138   }
    139   return false;
    140 }
    141 
    142 // static
    143 Node* NodeProperties::FindSuccessfulControlProjection(Node* node) {
    144   DCHECK_GT(node->op()->ControlOutputCount(), 0);
    145   if (node->op()->HasProperty(Operator::kNoThrow)) return node;
    146   for (Edge const edge : node->use_edges()) {
    147     if (!NodeProperties::IsControlEdge(edge)) continue;
    148     if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
    149       return edge.from();
    150     }
    151   }
    152   return node;
    153 }
    154 
    155 // static
    156 void NodeProperties::ReplaceValueInput(Node* node, Node* value, int index) {
    157   DCHECK(index < node->op()->ValueInputCount());
    158   node->ReplaceInput(FirstValueIndex(node) + index, value);
    159 }
    160 
    161 
    162 // static
    163 void NodeProperties::ReplaceValueInputs(Node* node, Node* value) {
    164   int value_input_count = node->op()->ValueInputCount();
    165   DCHECK_LE(1, value_input_count);
    166   node->ReplaceInput(0, value);
    167   while (--value_input_count > 0) {
    168     node->RemoveInput(value_input_count);
    169   }
    170 }
    171 
    172 
    173 // static
    174 void NodeProperties::ReplaceContextInput(Node* node, Node* context) {
    175   node->ReplaceInput(FirstContextIndex(node), context);
    176 }
    177 
    178 
    179 // static
    180 void NodeProperties::ReplaceControlInput(Node* node, Node* control, int index) {
    181   DCHECK(index < node->op()->ControlInputCount());
    182   node->ReplaceInput(FirstControlIndex(node) + index, control);
    183 }
    184 
    185 
    186 // static
    187 void NodeProperties::ReplaceEffectInput(Node* node, Node* effect, int index) {
    188   DCHECK(index < node->op()->EffectInputCount());
    189   return node->ReplaceInput(FirstEffectIndex(node) + index, effect);
    190 }
    191 
    192 
    193 // static
    194 void NodeProperties::ReplaceFrameStateInput(Node* node, Node* frame_state) {
    195   DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
    196   node->ReplaceInput(FirstFrameStateIndex(node), frame_state);
    197 }
    198 
    199 
    200 // static
    201 void NodeProperties::RemoveNonValueInputs(Node* node) {
    202   node->TrimInputCount(node->op()->ValueInputCount());
    203 }
    204 
    205 
    206 // static
    207 void NodeProperties::RemoveValueInputs(Node* node) {
    208   int value_input_count = node->op()->ValueInputCount();
    209   while (--value_input_count >= 0) {
    210     node->RemoveInput(value_input_count);
    211   }
    212 }
    213 
    214 
    215 void NodeProperties::MergeControlToEnd(Graph* graph,
    216                                        CommonOperatorBuilder* common,
    217                                        Node* node) {
    218   graph->end()->AppendInput(graph->zone(), node);
    219   graph->end()->set_op(common->End(graph->end()->InputCount()));
    220 }
    221 
    222 
    223 // static
    224 void NodeProperties::ReplaceUses(Node* node, Node* value, Node* effect,
    225                                  Node* success, Node* exception) {
    226   // Requires distinguishing between value, effect and control edges.
    227   for (Edge edge : node->use_edges()) {
    228     if (IsControlEdge(edge)) {
    229       if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
    230         DCHECK_NOT_NULL(success);
    231         edge.UpdateTo(success);
    232       } else if (edge.from()->opcode() == IrOpcode::kIfException) {
    233         DCHECK_NOT_NULL(exception);
    234         edge.UpdateTo(exception);
    235       } else {
    236         DCHECK_NOT_NULL(success);
    237         edge.UpdateTo(success);
    238       }
    239     } else if (IsEffectEdge(edge)) {
    240       DCHECK_NOT_NULL(effect);
    241       edge.UpdateTo(effect);
    242     } else {
    243       DCHECK_NOT_NULL(value);
    244       edge.UpdateTo(value);
    245     }
    246   }
    247 }
    248 
    249 
    250 // static
    251 void NodeProperties::ChangeOp(Node* node, const Operator* new_op) {
    252   node->set_op(new_op);
    253   Verifier::VerifyNode(node);
    254 }
    255 
    256 
    257 // static
    258 Node* NodeProperties::FindFrameStateBefore(Node* node) {
    259   Node* effect = NodeProperties::GetEffectInput(node);
    260   while (effect->opcode() != IrOpcode::kCheckpoint) {
    261     if (effect->opcode() == IrOpcode::kDead) return effect;
    262     DCHECK_EQ(1, effect->op()->EffectInputCount());
    263     effect = NodeProperties::GetEffectInput(effect);
    264   }
    265   Node* frame_state = GetFrameStateInput(effect);
    266   return frame_state;
    267 }
    268 
    269 // static
    270 Node* NodeProperties::FindProjection(Node* node, size_t projection_index) {
    271   for (auto use : node->uses()) {
    272     if (use->opcode() == IrOpcode::kProjection &&
    273         ProjectionIndexOf(use->op()) == projection_index) {
    274       return use;
    275     }
    276   }
    277   return nullptr;
    278 }
    279 
    280 
    281 // static
    282 void NodeProperties::CollectValueProjections(Node* node, Node** projections,
    283                                              size_t projection_count) {
    284 #ifdef DEBUG
    285   for (size_t index = 0; index < projection_count; ++index) {
    286     DCHECK_NULL(projections[index]);
    287   }
    288 #endif
    289   for (Edge const edge : node->use_edges()) {
    290     if (!IsValueEdge(edge)) continue;
    291     Node* use = edge.from();
    292     DCHECK_EQ(IrOpcode::kProjection, use->opcode());
    293     projections[ProjectionIndexOf(use->op())] = use;
    294   }
    295 }
    296 
    297 
    298 // static
    299 void NodeProperties::CollectControlProjections(Node* node, Node** projections,
    300                                                size_t projection_count) {
    301 #ifdef DEBUG
    302   DCHECK_LE(static_cast<int>(projection_count), node->UseCount());
    303   std::memset(projections, 0, sizeof(*projections) * projection_count);
    304 #endif
    305   size_t if_value_index = 0;
    306   for (Edge const edge : node->use_edges()) {
    307     if (!IsControlEdge(edge)) continue;
    308     Node* use = edge.from();
    309     size_t index;
    310     switch (use->opcode()) {
    311       case IrOpcode::kIfTrue:
    312         DCHECK_EQ(IrOpcode::kBranch, node->opcode());
    313         index = 0;
    314         break;
    315       case IrOpcode::kIfFalse:
    316         DCHECK_EQ(IrOpcode::kBranch, node->opcode());
    317         index = 1;
    318         break;
    319       case IrOpcode::kIfSuccess:
    320         DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
    321         index = 0;
    322         break;
    323       case IrOpcode::kIfException:
    324         DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
    325         index = 1;
    326         break;
    327       case IrOpcode::kIfValue:
    328         DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
    329         index = if_value_index++;
    330         break;
    331       case IrOpcode::kIfDefault:
    332         DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
    333         index = projection_count - 1;
    334         break;
    335       default:
    336         continue;
    337     }
    338     DCHECK_LT(if_value_index, projection_count);
    339     DCHECK_LT(index, projection_count);
    340     DCHECK_NULL(projections[index]);
    341     projections[index] = use;
    342   }
    343 #ifdef DEBUG
    344   for (size_t index = 0; index < projection_count; ++index) {
    345     DCHECK_NOT_NULL(projections[index]);
    346   }
    347 #endif
    348 }
    349 
    350 // static
    351 bool NodeProperties::IsSame(Node* a, Node* b) {
    352   for (;;) {
    353     if (a->opcode() == IrOpcode::kCheckHeapObject) {
    354       a = GetValueInput(a, 0);
    355       continue;
    356     }
    357     if (b->opcode() == IrOpcode::kCheckHeapObject) {
    358       b = GetValueInput(b, 0);
    359       continue;
    360     }
    361     return a == b;
    362   }
    363 }
    364 
    365 // static
    366 NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMaps(
    367     Isolate* isolate, Node* receiver, Node* effect,
    368     ZoneHandleSet<Map>* maps_return) {
    369   HeapObjectMatcher m(receiver);
    370   if (m.HasValue()) {
    371     Handle<HeapObject> receiver = m.Value();
    372     // We don't use ICs for the Array.prototype and the Object.prototype
    373     // because the runtime has to be able to intercept them properly, so
    374     // we better make sure that TurboFan doesn't outsmart the system here
    375     // by storing to elements of either prototype directly.
    376     //
    377     // TODO(bmeurer): This can be removed once the Array.prototype and
    378     // Object.prototype have NO_ELEMENTS elements kind.
    379     if (!isolate->IsInAnyContext(*receiver,
    380                                  Context::INITIAL_ARRAY_PROTOTYPE_INDEX) &&
    381         !isolate->IsInAnyContext(*receiver,
    382                                  Context::INITIAL_OBJECT_PROTOTYPE_INDEX)) {
    383       Handle<Map> receiver_map(receiver->map(), isolate);
    384       if (receiver_map->is_stable()) {
    385         // The {receiver_map} is only reliable when we install a stability
    386         // code dependency.
    387         *maps_return = ZoneHandleSet<Map>(receiver_map);
    388         return kUnreliableReceiverMaps;
    389       }
    390     }
    391   }
    392   InferReceiverMapsResult result = kReliableReceiverMaps;
    393   while (true) {
    394     switch (effect->opcode()) {
    395       case IrOpcode::kMapGuard: {
    396         Node* const object = GetValueInput(effect, 0);
    397         if (IsSame(receiver, object)) {
    398           *maps_return = MapGuardMapsOf(effect->op()).maps();
    399           return result;
    400         }
    401         break;
    402       }
    403       case IrOpcode::kCheckMaps: {
    404         Node* const object = GetValueInput(effect, 0);
    405         if (IsSame(receiver, object)) {
    406           *maps_return = CheckMapsParametersOf(effect->op()).maps();
    407           return result;
    408         }
    409         break;
    410       }
    411       case IrOpcode::kJSCreate: {
    412         if (IsSame(receiver, effect)) {
    413           HeapObjectMatcher mtarget(GetValueInput(effect, 0));
    414           HeapObjectMatcher mnewtarget(GetValueInput(effect, 1));
    415           if (mtarget.HasValue() && mnewtarget.HasValue() &&
    416               mnewtarget.Value()->IsJSFunction()) {
    417             Handle<JSFunction> original_constructor =
    418                 Handle<JSFunction>::cast(mnewtarget.Value());
    419             if (original_constructor->has_initial_map()) {
    420               Handle<Map> initial_map(original_constructor->initial_map(),
    421                                       isolate);
    422               if (initial_map->constructor_or_backpointer() ==
    423                   *mtarget.Value()) {
    424                 *maps_return = ZoneHandleSet<Map>(initial_map);
    425                 return result;
    426               }
    427             }
    428           }
    429           // We reached the allocation of the {receiver}.
    430           return kNoReceiverMaps;
    431         }
    432         break;
    433       }
    434       case IrOpcode::kStoreField: {
    435         // We only care about StoreField of maps.
    436         Node* const object = GetValueInput(effect, 0);
    437         FieldAccess const& access = FieldAccessOf(effect->op());
    438         if (access.base_is_tagged == kTaggedBase &&
    439             access.offset == HeapObject::kMapOffset) {
    440           if (IsSame(receiver, object)) {
    441             Node* const value = GetValueInput(effect, 1);
    442             HeapObjectMatcher m(value);
    443             if (m.HasValue()) {
    444               *maps_return = ZoneHandleSet<Map>(Handle<Map>::cast(m.Value()));
    445               return result;
    446             }
    447           }
    448           // Without alias analysis we cannot tell whether this
    449           // StoreField[map] affects {receiver} or not.
    450           result = kUnreliableReceiverMaps;
    451         }
    452         break;
    453       }
    454       case IrOpcode::kJSStoreMessage:
    455       case IrOpcode::kJSStoreModule:
    456       case IrOpcode::kStoreElement:
    457       case IrOpcode::kStoreTypedElement: {
    458         // These never change the map of objects.
    459         break;
    460       }
    461       case IrOpcode::kFinishRegion: {
    462         // FinishRegion renames the output of allocations, so we need
    463         // to update the {receiver} that we are looking for, if the
    464         // {receiver} matches the current {effect}.
    465         if (IsSame(receiver, effect)) receiver = GetValueInput(effect, 0);
    466         break;
    467       }
    468       case IrOpcode::kEffectPhi: {
    469         Node* control = GetControlInput(effect);
    470         if (control->opcode() != IrOpcode::kLoop) {
    471           DCHECK(control->opcode() == IrOpcode::kDead ||
    472                  control->opcode() == IrOpcode::kMerge);
    473           return kNoReceiverMaps;
    474         }
    475 
    476         // Continue search for receiver map outside the loop. Since operations
    477         // inside the loop may change the map, the result is unreliable.
    478         effect = GetEffectInput(effect, 0);
    479         result = kUnreliableReceiverMaps;
    480         continue;
    481       }
    482       default: {
    483         DCHECK_EQ(1, effect->op()->EffectOutputCount());
    484         if (effect->op()->EffectInputCount() != 1) {
    485           // Didn't find any appropriate CheckMaps node.
    486           return kNoReceiverMaps;
    487         }
    488         if (!effect->op()->HasProperty(Operator::kNoWrite)) {
    489           // Without alias/escape analysis we cannot tell whether this
    490           // {effect} affects {receiver} or not.
    491           result = kUnreliableReceiverMaps;
    492         }
    493         break;
    494       }
    495     }
    496 
    497     // Stop walking the effect chain once we hit the definition of
    498     // the {receiver} along the {effect}s.
    499     if (IsSame(receiver, effect)) return kNoReceiverMaps;
    500 
    501     // Continue with the next {effect}.
    502     DCHECK_EQ(1, effect->op()->EffectInputCount());
    503     effect = NodeProperties::GetEffectInput(effect);
    504   }
    505 }
    506 
    507 // static
    508 MaybeHandle<Map> NodeProperties::GetMapWitness(Isolate* isolate, Node* node) {
    509   ZoneHandleSet<Map> maps;
    510   Node* receiver = NodeProperties::GetValueInput(node, 1);
    511   Node* effect = NodeProperties::GetEffectInput(node);
    512   NodeProperties::InferReceiverMapsResult result =
    513       NodeProperties::InferReceiverMaps(isolate, receiver, effect, &maps);
    514   if (result == NodeProperties::kReliableReceiverMaps && maps.size() == 1) {
    515     return maps[0];
    516   }
    517   return MaybeHandle<Map>();
    518 }
    519 
    520 // static
    521 bool NodeProperties::HasInstanceTypeWitness(Isolate* isolate, Node* receiver,
    522                                             Node* effect,
    523                                             InstanceType instance_type) {
    524   ZoneHandleSet<Map> receiver_maps;
    525   NodeProperties::InferReceiverMapsResult result =
    526       NodeProperties::InferReceiverMaps(isolate, receiver, effect,
    527                                         &receiver_maps);
    528   switch (result) {
    529     case NodeProperties::kUnreliableReceiverMaps:
    530     case NodeProperties::kReliableReceiverMaps:
    531       DCHECK_NE(0, receiver_maps.size());
    532       for (size_t i = 0; i < receiver_maps.size(); ++i) {
    533         if (receiver_maps[i]->instance_type() != instance_type) return false;
    534       }
    535       return true;
    536 
    537     case NodeProperties::kNoReceiverMaps:
    538       return false;
    539   }
    540   UNREACHABLE();
    541 }
    542 
    543 // static
    544 bool NodeProperties::NoObservableSideEffectBetween(Node* effect,
    545                                                    Node* dominator) {
    546   while (effect != dominator) {
    547     if (effect->op()->EffectInputCount() == 1 &&
    548         effect->op()->properties() & Operator::kNoWrite) {
    549       effect = NodeProperties::GetEffectInput(effect);
    550     } else {
    551       return false;
    552     }
    553   }
    554   return true;
    555 }
    556 
    557 // static
    558 bool NodeProperties::CanBePrimitive(Isolate* isolate, Node* receiver,
    559                                     Node* effect) {
    560   switch (receiver->opcode()) {
    561 #define CASE(Opcode) case IrOpcode::k##Opcode:
    562     JS_CONSTRUCT_OP_LIST(CASE)
    563     JS_CREATE_OP_LIST(CASE)
    564 #undef CASE
    565     case IrOpcode::kCheckReceiver:
    566     case IrOpcode::kConvertReceiver:
    567     case IrOpcode::kJSGetSuperConstructor:
    568     case IrOpcode::kJSToObject:
    569       return false;
    570     case IrOpcode::kHeapConstant: {
    571       Handle<HeapObject> value = HeapObjectMatcher(receiver).Value();
    572       return value->IsPrimitive();
    573     }
    574     default: {
    575       // We don't really care about the exact maps here,
    576       // just the instance types, which don't change
    577       // across potential side-effecting operations.
    578       ZoneHandleSet<Map> maps;
    579       if (InferReceiverMaps(isolate, receiver, effect, &maps) !=
    580           kNoReceiverMaps) {
    581         // Check if all {maps} are actually JSReceiver maps.
    582         for (size_t i = 0; i < maps.size(); ++i) {
    583           if (!maps[i]->IsJSReceiverMap()) return true;
    584         }
    585         return false;
    586       }
    587       return true;
    588     }
    589   }
    590 }
    591 
    592 // static
    593 bool NodeProperties::CanBeNullOrUndefined(Isolate* isolate, Node* receiver,
    594                                           Node* effect) {
    595   if (CanBePrimitive(isolate, receiver, effect)) {
    596     switch (receiver->opcode()) {
    597       case IrOpcode::kCheckInternalizedString:
    598       case IrOpcode::kCheckNumber:
    599       case IrOpcode::kCheckSmi:
    600       case IrOpcode::kCheckString:
    601       case IrOpcode::kCheckSymbol:
    602       case IrOpcode::kJSToInteger:
    603       case IrOpcode::kJSToLength:
    604       case IrOpcode::kJSToName:
    605       case IrOpcode::kJSToNumber:
    606       case IrOpcode::kJSToNumberConvertBigInt:
    607       case IrOpcode::kJSToNumeric:
    608       case IrOpcode::kJSToString:
    609       case IrOpcode::kToBoolean:
    610         return false;
    611       case IrOpcode::kHeapConstant: {
    612         Handle<HeapObject> value = HeapObjectMatcher(receiver).Value();
    613         return value->IsNullOrUndefined(isolate);
    614       }
    615       default:
    616         return true;
    617     }
    618   }
    619   return false;
    620 }
    621 
    622 // static
    623 Node* NodeProperties::GetOuterContext(Node* node, size_t* depth) {
    624   Node* context = NodeProperties::GetContextInput(node);
    625   while (*depth > 0 &&
    626          IrOpcode::IsContextChainExtendingOpcode(context->opcode())) {
    627     context = NodeProperties::GetContextInput(context);
    628     (*depth)--;
    629   }
    630   return context;
    631 }
    632 
    633 // static
    634 Type NodeProperties::GetTypeOrAny(Node* node) {
    635   return IsTyped(node) ? node->type() : Type::Any();
    636 }
    637 
    638 
    639 // static
    640 bool NodeProperties::AllValueInputsAreTyped(Node* node) {
    641   int input_count = node->op()->ValueInputCount();
    642   for (int index = 0; index < input_count; ++index) {
    643     if (!IsTyped(GetValueInput(node, index))) return false;
    644   }
    645   return true;
    646 }
    647 
    648 
    649 // static
    650 bool NodeProperties::IsInputRange(Edge edge, int first, int num) {
    651   if (num == 0) return false;
    652   int const index = edge.index();
    653   return first <= index && index < first + num;
    654 }
    655 
    656 // static
    657 size_t NodeProperties::HashCode(Node* node) {
    658   size_t h = base::hash_combine(node->op()->HashCode(), node->InputCount());
    659   for (Node* input : node->inputs()) {
    660     h = base::hash_combine(h, input->id());
    661   }
    662   return h;
    663 }
    664 
    665 // static
    666 bool NodeProperties::Equals(Node* a, Node* b) {
    667   DCHECK_NOT_NULL(a);
    668   DCHECK_NOT_NULL(b);
    669   DCHECK_NOT_NULL(a->op());
    670   DCHECK_NOT_NULL(b->op());
    671   if (!a->op()->Equals(b->op())) return false;
    672   if (a->InputCount() != b->InputCount()) return false;
    673   Node::Inputs aInputs = a->inputs();
    674   Node::Inputs bInputs = b->inputs();
    675 
    676   auto aIt = aInputs.begin();
    677   auto bIt = bInputs.begin();
    678   auto aEnd = aInputs.end();
    679 
    680   for (; aIt != aEnd; ++aIt, ++bIt) {
    681     DCHECK_NOT_NULL(*aIt);
    682     DCHECK_NOT_NULL(*bIt);
    683     if ((*aIt)->id() != (*bIt)->id()) return false;
    684   }
    685   return true;
    686 }
    687 
    688 }  // namespace compiler
    689 }  // namespace internal
    690 }  // namespace v8
    691