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