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() && newtarget.has_initial_map()) {
336 if (!newtarget.serialized()) {
337 TRACE_BROKER_MISSING(broker, "initial map on " << newtarget);
338 return base::nullopt;
339 }
340 MapRef initial_map = newtarget.initial_map();
341 if (initial_map.GetConstructor().equals(target)) {
342 DCHECK(target.AsJSFunction().map().is_constructor());
343 DCHECK(newtarget.map().is_constructor());
344 return initial_map;
345 }
346 }
347 }
348 return base::nullopt;
349 }
350
351 // static
InferMapsUnsafe(JSHeapBroker * broker,Node * receiver,Node * effect,ZoneHandleSet<Map> * maps_return)352 NodeProperties::InferMapsResult NodeProperties::InferMapsUnsafe(
353 JSHeapBroker* broker, Node* receiver, Node* effect,
354 ZoneHandleSet<Map>* maps_return) {
355 HeapObjectMatcher m(receiver);
356 if (m.HasResolvedValue()) {
357 HeapObjectRef receiver = m.Ref(broker);
358 // We don't use ICs for the Array.prototype and the Object.prototype
359 // because the runtime has to be able to intercept them properly, so
360 // we better make sure that TurboFan doesn't outsmart the system here
361 // by storing to elements of either prototype directly.
362 //
363 // TODO(bmeurer): This can be removed once the Array.prototype and
364 // Object.prototype have NO_ELEMENTS elements kind.
365 if (!receiver.IsJSObject() ||
366 !broker->IsArrayOrObjectPrototype(receiver.AsJSObject())) {
367 if (receiver.map().is_stable()) {
368 // The {receiver_map} is only reliable when we install a stability
369 // code dependency.
370 *maps_return = ZoneHandleSet<Map>(receiver.map().object());
371 return kUnreliableMaps;
372 }
373 }
374 }
375 InferMapsResult result = kReliableMaps;
376 while (true) {
377 switch (effect->opcode()) {
378 case IrOpcode::kMapGuard: {
379 Node* const object = GetValueInput(effect, 0);
380 if (IsSame(receiver, object)) {
381 *maps_return = MapGuardMapsOf(effect->op());
382 return result;
383 }
384 break;
385 }
386 case IrOpcode::kCheckMaps: {
387 Node* const object = GetValueInput(effect, 0);
388 if (IsSame(receiver, object)) {
389 *maps_return = CheckMapsParametersOf(effect->op()).maps();
390 return result;
391 }
392 break;
393 }
394 case IrOpcode::kJSCreate: {
395 if (IsSame(receiver, effect)) {
396 base::Optional<MapRef> initial_map = GetJSCreateMap(broker, receiver);
397 if (initial_map.has_value()) {
398 *maps_return = ZoneHandleSet<Map>(initial_map->object());
399 return result;
400 }
401 // We reached the allocation of the {receiver}.
402 return kNoMaps;
403 }
404 result = kUnreliableMaps; // JSCreate can have side-effect.
405 break;
406 }
407 case IrOpcode::kJSCreatePromise: {
408 if (IsSame(receiver, effect)) {
409 *maps_return = ZoneHandleSet<Map>(broker->target_native_context()
410 .promise_function()
411 .initial_map()
412 .object());
413 return result;
414 }
415 break;
416 }
417 case IrOpcode::kStoreField: {
418 // We only care about StoreField of maps.
419 Node* const object = GetValueInput(effect, 0);
420 FieldAccess const& access = FieldAccessOf(effect->op());
421 if (access.base_is_tagged == kTaggedBase &&
422 access.offset == HeapObject::kMapOffset) {
423 if (IsSame(receiver, object)) {
424 Node* const value = GetValueInput(effect, 1);
425 HeapObjectMatcher m(value);
426 if (m.HasResolvedValue()) {
427 *maps_return = ZoneHandleSet<Map>(m.Ref(broker).AsMap().object());
428 return result;
429 }
430 }
431 // Without alias analysis we cannot tell whether this
432 // StoreField[map] affects {receiver} or not.
433 result = kUnreliableMaps;
434 }
435 break;
436 }
437 case IrOpcode::kJSStoreMessage:
438 case IrOpcode::kJSStoreModule:
439 case IrOpcode::kStoreElement:
440 case IrOpcode::kStoreTypedElement: {
441 // These never change the map of objects.
442 break;
443 }
444 case IrOpcode::kFinishRegion: {
445 // FinishRegion renames the output of allocations, so we need
446 // to update the {receiver} that we are looking for, if the
447 // {receiver} matches the current {effect}.
448 if (IsSame(receiver, effect)) receiver = GetValueInput(effect, 0);
449 break;
450 }
451 case IrOpcode::kEffectPhi: {
452 Node* control = GetControlInput(effect);
453 if (control->opcode() != IrOpcode::kLoop) {
454 DCHECK(control->opcode() == IrOpcode::kDead ||
455 control->opcode() == IrOpcode::kMerge);
456 return kNoMaps;
457 }
458
459 // Continue search for receiver map outside the loop. Since operations
460 // inside the loop may change the map, the result is unreliable.
461 effect = GetEffectInput(effect, 0);
462 result = kUnreliableMaps;
463 continue;
464 }
465 default: {
466 DCHECK_EQ(1, effect->op()->EffectOutputCount());
467 if (effect->op()->EffectInputCount() != 1) {
468 // Didn't find any appropriate CheckMaps node.
469 return kNoMaps;
470 }
471 if (!effect->op()->HasProperty(Operator::kNoWrite)) {
472 // Without alias/escape analysis we cannot tell whether this
473 // {effect} affects {receiver} or not.
474 result = kUnreliableMaps;
475 }
476 break;
477 }
478 }
479
480 // Stop walking the effect chain once we hit the definition of
481 // the {receiver} along the {effect}s.
482 if (IsSame(receiver, effect)) return kNoMaps;
483
484 // Continue with the next {effect}.
485 DCHECK_EQ(1, effect->op()->EffectInputCount());
486 effect = NodeProperties::GetEffectInput(effect);
487 }
488 }
489
490 // static
NoObservableSideEffectBetween(Node * effect,Node * dominator)491 bool NodeProperties::NoObservableSideEffectBetween(Node* effect,
492 Node* dominator) {
493 while (effect != dominator) {
494 if (effect->op()->EffectInputCount() == 1 &&
495 effect->op()->properties() & Operator::kNoWrite) {
496 effect = NodeProperties::GetEffectInput(effect);
497 } else {
498 return false;
499 }
500 }
501 return true;
502 }
503
504 // static
CanBePrimitive(JSHeapBroker * broker,Node * receiver,Node * effect)505 bool NodeProperties::CanBePrimitive(JSHeapBroker* broker, Node* receiver,
506 Node* effect) {
507 switch (receiver->opcode()) {
508 #define CASE(Opcode) case IrOpcode::k##Opcode:
509 JS_CONSTRUCT_OP_LIST(CASE)
510 JS_CREATE_OP_LIST(CASE)
511 #undef CASE
512 case IrOpcode::kCheckReceiver:
513 case IrOpcode::kConvertReceiver:
514 case IrOpcode::kJSGetSuperConstructor:
515 case IrOpcode::kJSToObject:
516 return false;
517 case IrOpcode::kHeapConstant: {
518 HeapObjectRef value = HeapObjectMatcher(receiver).Ref(broker);
519 return value.map().IsPrimitiveMap();
520 }
521 default: {
522 MapInference inference(broker, receiver, effect);
523 return !inference.HaveMaps() ||
524 !inference.AllOfInstanceTypesAreJSReceiver();
525 }
526 }
527 }
528
529 // static
CanBeNullOrUndefined(JSHeapBroker * broker,Node * receiver,Node * effect)530 bool NodeProperties::CanBeNullOrUndefined(JSHeapBroker* broker, Node* receiver,
531 Node* effect) {
532 if (CanBePrimitive(broker, receiver, effect)) {
533 switch (receiver->opcode()) {
534 case IrOpcode::kCheckInternalizedString:
535 case IrOpcode::kCheckNumber:
536 case IrOpcode::kCheckSmi:
537 case IrOpcode::kCheckString:
538 case IrOpcode::kCheckSymbol:
539 case IrOpcode::kJSToLength:
540 case IrOpcode::kJSToName:
541 case IrOpcode::kJSToNumber:
542 case IrOpcode::kJSToNumberConvertBigInt:
543 case IrOpcode::kJSToNumeric:
544 case IrOpcode::kJSToString:
545 case IrOpcode::kToBoolean:
546 return false;
547 case IrOpcode::kHeapConstant: {
548 HeapObjectRef value = HeapObjectMatcher(receiver).Ref(broker);
549 OddballType type = value.map().oddball_type();
550 return type == OddballType::kNull || type == OddballType::kUndefined;
551 }
552 default:
553 return true;
554 }
555 }
556 return false;
557 }
558
559 // static
GetOuterContext(Node * node,size_t * depth)560 Node* NodeProperties::GetOuterContext(Node* node, size_t* depth) {
561 Node* context = NodeProperties::GetContextInput(node);
562 while (*depth > 0 &&
563 IrOpcode::IsContextChainExtendingOpcode(context->opcode())) {
564 context = NodeProperties::GetContextInput(context);
565 (*depth)--;
566 }
567 return context;
568 }
569
570 // static
GetTypeOrAny(Node * node)571 Type NodeProperties::GetTypeOrAny(Node* node) {
572 return IsTyped(node) ? node->type() : Type::Any();
573 }
574
575
576 // static
AllValueInputsAreTyped(Node * node)577 bool NodeProperties::AllValueInputsAreTyped(Node* node) {
578 int input_count = node->op()->ValueInputCount();
579 for (int index = 0; index < input_count; ++index) {
580 if (!IsTyped(GetValueInput(node, index))) return false;
581 }
582 return true;
583 }
584
585
586 // static
IsInputRange(Edge edge,int first,int num)587 bool NodeProperties::IsInputRange(Edge edge, int first, int num) {
588 if (num == 0) return false;
589 int const index = edge.index();
590 return first <= index && index < first + num;
591 }
592
593 // static
HashCode(Node * node)594 size_t NodeProperties::HashCode(Node* node) {
595 size_t h = base::hash_combine(node->op()->HashCode(), node->InputCount());
596 for (Node* input : node->inputs()) {
597 h = base::hash_combine(h, input->id());
598 }
599 return h;
600 }
601
602 // static
Equals(Node * a,Node * b)603 bool NodeProperties::Equals(Node* a, Node* b) {
604 DCHECK_NOT_NULL(a);
605 DCHECK_NOT_NULL(b);
606 DCHECK_NOT_NULL(a->op());
607 DCHECK_NOT_NULL(b->op());
608 if (!a->op()->Equals(b->op())) return false;
609 if (a->InputCount() != b->InputCount()) return false;
610 Node::Inputs aInputs = a->inputs();
611 Node::Inputs bInputs = b->inputs();
612
613 auto aIt = aInputs.begin();
614 auto bIt = bInputs.begin();
615 auto aEnd = aInputs.end();
616
617 for (; aIt != aEnd; ++aIt, ++bIt) {
618 DCHECK_NOT_NULL(*aIt);
619 DCHECK_NOT_NULL(*bIt);
620 if ((*aIt)->id() != (*bIt)->id()) return false;
621 }
622 return true;
623 }
624
625 } // namespace compiler
626 } // namespace internal
627 } // namespace v8
628