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