v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
node-matchers.h
Go to the documentation of this file.
1// Copyright 2014 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#ifndef V8_COMPILER_NODE_MATCHERS_H_
6#define V8_COMPILER_NODE_MATCHERS_H_
7
8#include <cmath>
9#include <limits>
10
11#include "src/base/bounds.h"
15#include "src/common/globals.h"
18#include "src/compiler/node.h"
22
23namespace v8::internal::compiler {
24
25class JSHeapBroker;
26
27// A pattern matcher for nodes.
29 explicit NodeMatcher(Node* node) : node_(node) {}
30
31 Node* node() const { return node_; }
32 const Operator* op() const { return node()->op(); }
33 IrOpcode::Value opcode() const { return node()->opcode(); }
34
35 bool HasProperty(Operator::Property property) const {
36 return op()->HasProperty(property);
37 }
38 Node* InputAt(int index) const { return node()->InputAt(index); }
39
40 bool Equals(const Node* node) const { return node_ == node; }
41
42 bool IsComparison() const;
43
44#define DEFINE_IS_OPCODE(Opcode, ...) \
45 bool Is##Opcode() const { return opcode() == IrOpcode::k##Opcode; }
47#undef DEFINE_IS_OPCODE
48
49 private:
51};
52
54 while (NodeProperties::IsValueIdentity(node, &node)) {
55 }
56 DCHECK_NOT_NULL(node);
57 return node;
58}
59
60// A pattern matcher for arbitrary value constants.
61//
62// Note that value identities on the input node are skipped when matching. The
63// resolved value may not be a parameter of the input node. The node() method
64// returns the unmodified input node. This is by design, as reducers may wish to
65// match value constants but delay reducing the node until a later phase.
66template <typename T, IrOpcode::Value kOpcode>
67struct ValueMatcher : public NodeMatcher {
68 // TODO(42203211): Although value matchers will work with if `T` is a direct
69 // handle type, the special instance for indirect handles uses handle location
70 // equality for performing the match. This is designed to work with canonical
71 // handles, used by the compiler. As of now, it is not clear if direct handles
72 // can replace such canonical handles, hence the following assertion.
73 static_assert(!is_direct_handle_v<T>);
74
75 using ValueType = T;
76
77 explicit ValueMatcher(Node* node)
79 node = SkipValueIdentities(node);
80 has_resolved_value_ = node->opcode() == kOpcode;
82 resolved_value_ = OpParameter<T>(node->op());
83 }
84 }
85
86 bool HasResolvedValue() const { return has_resolved_value_; }
87 const T& ResolvedValue() const {
89 return resolved_value_;
90 }
91
92 private:
95};
96
97template <>
99 Node* node)
100 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
101 node = SkipValueIdentities(node);
102 has_resolved_value_ = node->opcode() == IrOpcode::kInt32Constant;
104 resolved_value_ = static_cast<uint32_t>(OpParameter<int32_t>(node->op()));
105 }
106}
107
108template <>
110 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
111 node = SkipValueIdentities(node);
112 if (node->opcode() == IrOpcode::kInt32Constant) {
114 has_resolved_value_ = true;
115 } else if (node->opcode() == IrOpcode::kInt64Constant) {
117 has_resolved_value_ = true;
118 }
119}
120
121template <>
123 Node* node)
124 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
125 node = SkipValueIdentities(node);
126 if (node->opcode() == IrOpcode::kInt32Constant) {
127 resolved_value_ = static_cast<uint32_t>(OpParameter<int32_t>(node->op()));
128 has_resolved_value_ = true;
129 } else if (node->opcode() == IrOpcode::kInt64Constant) {
130 resolved_value_ = static_cast<uint64_t>(OpParameter<int64_t>(node->op()));
131 has_resolved_value_ = true;
132 }
133}
134
135// A pattern matcher for integer constants.
136template <typename T, IrOpcode::Value kOpcode>
137struct IntMatcher final : public ValueMatcher<T, kOpcode> {
138 explicit IntMatcher(Node* node) : ValueMatcher<T, kOpcode>(node) {}
139
140 bool Is(const T& value) const {
141 return this->HasResolvedValue() && this->ResolvedValue() == value;
142 }
143 bool IsInRange(const T& low, const T& high) const {
144 return this->HasResolvedValue() &&
145 base::IsInRange(this->ResolvedValue(), low, high);
146 }
147 bool IsMultipleOf(T n) const {
148 return this->HasResolvedValue() && (this->ResolvedValue() % n) == 0;
149 }
150 bool IsPowerOf2() const {
151 return this->HasResolvedValue() && this->ResolvedValue() > 0 &&
152 (this->ResolvedValue() & (this->ResolvedValue() - 1)) == 0;
153 }
154 bool IsNegativePowerOf2() const {
155 return this->HasResolvedValue() && this->ResolvedValue() < 0 &&
156 ((this->ResolvedValue() == std::numeric_limits<T>::min()) ||
157 (-this->ResolvedValue() & (-this->ResolvedValue() - 1)) == 0);
158 }
159 bool IsNegative() const {
160 return this->HasResolvedValue() && this->ResolvedValue() < 0;
161 }
162};
163
168#if V8_ENABLE_WEBASSEMBLY
169using V128ConstMatcher =
171#endif // V8_ENABLE_WEBASSEMBLY
172#if V8_HOST_ARCH_32_BIT
175#else
178#endif
179
180
181// A pattern matcher for floating point constants.
182template <typename T, IrOpcode::Value kOpcode>
183struct FloatMatcher final : public ValueMatcher<T, kOpcode> {
184 explicit FloatMatcher(Node* node) : ValueMatcher<T, kOpcode>(node) {}
185
186 bool Is(const T& value) const {
187 return this->HasResolvedValue() && this->ResolvedValue() == value;
188 }
189 bool IsInRange(const T& low, const T& high) const {
190 return this->HasResolvedValue() && low <= this->ResolvedValue() &&
191 this->ResolvedValue() <= high;
192 }
193 bool IsMinusZero() const {
194 return this->Is(0.0) && std::signbit(this->ResolvedValue());
195 }
196 bool IsNegative() const {
197 return this->HasResolvedValue() && this->ResolvedValue() < 0.0;
198 }
199 bool IsNaN() const {
200 return this->HasResolvedValue() && std::isnan(this->ResolvedValue());
201 }
202 bool IsZero() const {
203 return this->Is(0.0) && !std::signbit(this->ResolvedValue());
204 }
205 bool IsNormal() const {
206 return this->HasResolvedValue() && std::isnormal(this->ResolvedValue());
207 }
208 bool IsInteger() const {
209 return this->HasResolvedValue() &&
210 std::nearbyint(this->ResolvedValue()) == this->ResolvedValue();
211 }
213 if (!this->HasResolvedValue() || (this->ResolvedValue() == 0.0)) {
214 return false;
215 }
216 base::Double value = base::Double(this->ResolvedValue());
217 return !value.IsInfinite() && base::bits::IsPowerOfTwo(value.Significand());
218 }
219};
220
224
225// A pattern matcher for heap object constants.
226template <IrOpcode::Value kHeapConstantOpcode>
228 : public ValueMatcher<IndirectHandle<HeapObject>, kHeapConstantOpcode> {
230 : ValueMatcher<IndirectHandle<HeapObject>, kHeapConstantOpcode>(node) {}
231
232 bool Is(IndirectHandle<HeapObject> const& value) const {
233 return this->HasResolvedValue() &&
234 this->ResolvedValue().address() == value.address();
235 }
236
238 // TODO(jgruber,chromium:1209798): Using kAssumeMemoryFence works around
239 // the fact that the graph stores handles (and not refs). The assumption is
240 // that any handle inserted into the graph is safe to read; but we don't
241 // preserve the reason why it is safe to read. Thus we must over-approximate
242 // here and assume the existence of a memory fence. In the future, we should
243 // consider having the graph store ObjectRefs or ObjectData pointer instead,
244 // which would make new ref construction here unnecessary.
246 }
247};
248
252
253// A pattern matcher for external reference constants.
255 : public ValueMatcher<ExternalReference, IrOpcode::kExternalConstant> {
257 : ValueMatcher<ExternalReference, IrOpcode::kExternalConstant>(node) {}
258 bool Is(const ExternalReference& value) const {
259 return this->HasResolvedValue() && this->ResolvedValue() == value;
260 }
261};
262
263
264// For shorter pattern matching code, this struct matches the inputs to
265// machine-level load operations.
266template <typename Object>
267struct LoadMatcher : public NodeMatcher {
268 explicit LoadMatcher(Node* node)
269 : NodeMatcher(node), object_(InputAt(0)), index_(InputAt(1)) {}
270
272
273 Object const& object() const { return object_; }
274 IntPtrMatcher const& index() const { return index_; }
275
276 private:
279};
280
281
282// For shorter pattern matching code, this struct matches both the left and
283// right hand sides of a binary operation and can put constants on the right
284// if they appear on the left hand side of a commutative operation.
285template <typename Left, typename Right, MachineRepresentation rep>
286struct BinopMatcher : public NodeMatcher {
291 BinopMatcher(Node* node, bool allow_input_swap)
292 : NodeMatcher(node), left_(InputAt(0)), right_(InputAt(1)) {
293 if (allow_input_swap) PutConstantOnRight();
294 }
295
296 using LeftMatcher = Left;
297 using RightMatcher = Right;
298
299 static constexpr MachineRepresentation representation = rep;
300
301 const Left& left() const { return left_; }
302 const Right& right() const { return right_; }
303
304 bool IsFoldable() const {
305 return left().HasResolvedValue() && right().HasResolvedValue();
306 }
307 bool LeftEqualsRight() const { return left().node() == right().node(); }
308
309 bool OwnsInput(Node* input) {
310 for (Node* use : input->uses()) {
311 if (use != node()) {
312 return false;
313 }
314 }
315 return true;
316 }
317
318 protected:
319 void SwapInputs() {
320 std::swap(left_, right_);
321 // TODO(turbofan): This modification should notify the reducers using
322 // BinopMatcher. Alternatively, all reducers (especially value numbering)
323 // could ignore the ordering for commutative binops.
324 node()->ReplaceInput(0, left().node());
325 node()->ReplaceInput(1, right().node());
326 }
327
328 private:
330 if (left().HasResolvedValue() && !right().HasResolvedValue()) {
331 SwapInputs();
332 }
333 }
334
335 Left left_;
336 Right right_;
337};
338
363
364template <class BinopMatcher, IrOpcode::Value kMulOpcode,
365 IrOpcode::Value kShiftOpcode>
367 explicit ScaleMatcher(Node* node, bool allow_power_of_two_plus_one = false)
368 : scale_(-1), power_of_two_plus_one_(false) {
369 if (node->InputCount() < 2) return;
370 BinopMatcher m(node);
371 if (node->opcode() == kShiftOpcode) {
372 if (m.right().HasResolvedValue()) {
373 typename BinopMatcher::RightMatcher::ValueType value =
374 m.right().ResolvedValue();
375 if (value >= 0 && value <= 3) {
376 scale_ = static_cast<int>(value);
377 }
378 }
379 } else if (node->opcode() == kMulOpcode) {
380 if (m.right().HasResolvedValue()) {
381 typename BinopMatcher::RightMatcher::ValueType value =
382 m.right().ResolvedValue();
383 if (value == 1) {
384 scale_ = 0;
385 } else if (value == 2) {
386 scale_ = 1;
387 } else if (value == 4) {
388 scale_ = 2;
389 } else if (value == 8) {
390 scale_ = 3;
391 } else if (allow_power_of_two_plus_one) {
392 if (value == 3) {
393 scale_ = 1;
394 power_of_two_plus_one_ = true;
395 } else if (value == 5) {
396 scale_ = 2;
397 power_of_two_plus_one_ = true;
398 } else if (value == 9) {
399 scale_ = 3;
400 power_of_two_plus_one_ = true;
401 }
402 }
403 }
404 }
405 }
406
407 bool matches() const { return scale_ != -1; }
408 int scale() const { return scale_; }
409 bool power_of_two_plus_one() const { return power_of_two_plus_one_; }
410
411 private:
414};
415
420
421template <class BinopMatcher, IrOpcode::Value AddOpcode,
422 IrOpcode::Value SubOpcode, IrOpcode::Value kMulOpcode,
423 IrOpcode::Value kShiftOpcode>
424struct AddMatcher : public BinopMatcher {
425 static const IrOpcode::Value kAddOpcode = AddOpcode;
426 static const IrOpcode::Value kSubOpcode = SubOpcode;
428
429 AddMatcher(Node* node, bool allow_input_swap)
430 : BinopMatcher(node, allow_input_swap),
431 scale_(-1),
432 power_of_two_plus_one_(false) {
433 Initialize(node, allow_input_swap);
434 }
435 explicit AddMatcher(Node* node)
436 : BinopMatcher(node, node->op()->HasProperty(Operator::kCommutative)),
437 scale_(-1),
438 power_of_two_plus_one_(false) {
439 Initialize(node, node->op()->HasProperty(Operator::kCommutative));
440 }
441
442 bool HasIndexInput() const { return scale_ != -1; }
443 Node* IndexInput() const {
444 DCHECK(HasIndexInput());
445 return this->left().node()->InputAt(0);
446 }
447 int scale() const {
448 DCHECK(HasIndexInput());
449 return scale_;
450 }
451 bool power_of_two_plus_one() const { return power_of_two_plus_one_; }
452
453 private:
454 void Initialize(Node* node, bool allow_input_swap) {
455 Matcher left_matcher(this->left().node(), true);
456 if (left_matcher.matches()) {
457 scale_ = left_matcher.scale();
458 power_of_two_plus_one_ = left_matcher.power_of_two_plus_one();
459 return;
460 }
461
462 if (!allow_input_swap) {
463 return;
464 }
465
466 Matcher right_matcher(this->right().node(), true);
467 if (right_matcher.matches()) {
468 scale_ = right_matcher.scale();
469 power_of_two_plus_one_ = right_matcher.power_of_two_plus_one();
470 this->SwapInputs();
471 return;
472 }
473
474 if ((this->left().opcode() != kSubOpcode &&
475 this->left().opcode() != kAddOpcode) &&
476 (this->right().opcode() == kAddOpcode ||
477 this->right().opcode() == kSubOpcode)) {
478 this->SwapInputs();
479 }
480 }
481
484};
485
487 AddMatcher<Int32BinopMatcher, IrOpcode::kInt32Add, IrOpcode::kInt32Sub,
488 IrOpcode::kInt32Mul, IrOpcode::kWord32Shl>;
490 AddMatcher<Int64BinopMatcher, IrOpcode::kInt64Add, IrOpcode::kInt64Sub,
491 IrOpcode::kInt64Mul, IrOpcode::kWord64Shl>;
492
494
495enum class AddressOption : uint8_t {
496 kAllowNone = 0u,
497 kAllowInputSwap = 1u << 0,
498 kAllowScale = 1u << 1,
500};
501
504
505template <class AddMatcher>
508 : matches_(false),
509 index_(nullptr),
510 scale_(0),
511 base_(nullptr),
512 displacement_(nullptr),
513 displacement_mode_(kPositiveDisplacement) {
514 Initialize(node, options);
515 }
516
518 : matches_(false),
519 index_(nullptr),
520 scale_(0),
521 base_(nullptr),
522 displacement_(nullptr),
523 displacement_mode_(kPositiveDisplacement) {
524 Initialize(node, AddressOption::kAllowScale |
525 (node->op()->HasProperty(Operator::kCommutative)
526 ? AddressOption::kAllowInputSwap
527 : AddressOption::kAllowNone));
528 }
529
530 bool matches() const { return matches_; }
531 Node* index() const { return index_; }
532 int scale() const { return scale_; }
533 Node* base() const { return base_; }
534 Node* displacement() const { return displacement_; }
535 DisplacementMode displacement_mode() const { return displacement_mode_; }
536
537 private:
544
545 void Initialize(Node* node, AddressOptions options) {
546 // The BaseWithIndexAndDisplacementMatcher canonicalizes the order of
547 // displacements and scale factors that are used as inputs, so instead of
548 // enumerating all possible patterns by brute force, checking for node
549 // clusters using the following templates in the following order suffices to
550 // find all of the interesting cases (S = index * scale, B = base input, D =
551 // displacement input):
552 // (S + (B + D))
553 // (S + (B + B))
554 // (S + D)
555 // (S + B)
556 // ((S + D) + B)
557 // ((S + B) + D)
558 // ((B + D) + B)
559 // ((B + B) + D)
560 // (B + D)
561 // (B + B)
562 if (node->InputCount() < 2) return;
563 AddMatcher m(node, options & AddressOption::kAllowInputSwap);
564 Node* left = m.left().node();
565 Node* right = m.right().node();
566 Node* displacement = nullptr;
567 Node* base = nullptr;
568 Node* index = nullptr;
569 Node* scale_expression = nullptr;
570 bool power_of_two_plus_one = false;
572 int scale = 0;
573 if (m.HasIndexInput() && OwnedByAddressingOperand(left)) {
574 index = m.IndexInput();
575 scale = m.scale();
576 scale_expression = left;
577 power_of_two_plus_one = m.power_of_two_plus_one();
578 bool match_found = false;
579 if (right->opcode() == AddMatcher::kSubOpcode &&
580 OwnedByAddressingOperand(right)) {
581 AddMatcher right_matcher(right);
582 if (right_matcher.right().HasResolvedValue()) {
583 // (S + (B - D))
584 base = right_matcher.left().node();
585 displacement = right_matcher.right().node();
587 match_found = true;
588 }
589 }
590 if (!match_found) {
591 if (right->opcode() == AddMatcher::kAddOpcode &&
592 OwnedByAddressingOperand(right)) {
593 AddMatcher right_matcher(right);
594 if (right_matcher.right().HasResolvedValue()) {
595 // (S + (B + D))
596 base = right_matcher.left().node();
597 displacement = right_matcher.right().node();
598 } else {
599 // (S + (B + B))
600 base = right;
601 }
602 } else if (m.right().HasResolvedValue()) {
603 // (S + D)
604 displacement = right;
605 } else {
606 // (S + B)
607 base = right;
608 }
609 }
610 } else {
611 bool match_found = false;
612 if (left->opcode() == AddMatcher::kSubOpcode &&
613 OwnedByAddressingOperand(left)) {
614 AddMatcher left_matcher(left);
615 Node* left_left = left_matcher.left().node();
616 Node* left_right = left_matcher.right().node();
617 if (left_matcher.right().HasResolvedValue()) {
618 if (left_matcher.HasIndexInput() &&
619 OwnedByAddressingOperand(left_left)) {
620 // ((S - D) + B)
621 index = left_matcher.IndexInput();
622 scale = left_matcher.scale();
623 scale_expression = left_left;
624 power_of_two_plus_one = left_matcher.power_of_two_plus_one();
625 displacement = left_right;
627 base = right;
628 } else {
629 // ((B - D) + B)
630 index = left_left;
631 displacement = left_right;
633 base = right;
634 }
635 match_found = true;
636 }
637 }
638 if (!match_found) {
639 if (left->opcode() == AddMatcher::kAddOpcode &&
640 OwnedByAddressingOperand(left)) {
641 AddMatcher left_matcher(left);
642 Node* left_left = left_matcher.left().node();
643 Node* left_right = left_matcher.right().node();
644 if (left_matcher.HasIndexInput() &&
645 OwnedByAddressingOperand(left_left)) {
646 if (left_matcher.right().HasResolvedValue()) {
647 // ((S + D) + B)
648 index = left_matcher.IndexInput();
649 scale = left_matcher.scale();
650 scale_expression = left_left;
651 power_of_two_plus_one = left_matcher.power_of_two_plus_one();
652 displacement = left_right;
653 base = right;
654 } else if (m.right().HasResolvedValue()) {
655 if (left->OwnedBy(node)) {
656 // ((S + B) + D)
657 index = left_matcher.IndexInput();
658 scale = left_matcher.scale();
659 scale_expression = left_left;
660 power_of_two_plus_one = left_matcher.power_of_two_plus_one();
661 base = left_right;
662 displacement = right;
663 } else {
664 // (B + D)
665 base = left;
666 displacement = right;
667 }
668 } else {
669 // (B + B)
670 index = left;
671 base = right;
672 }
673 } else {
674 if (left_matcher.right().HasResolvedValue()) {
675 // ((B + D) + B)
676 index = left_left;
677 displacement = left_right;
678 base = right;
679 } else if (m.right().HasResolvedValue()) {
680 if (left->OwnedBy(node)) {
681 // ((B + B) + D)
682 index = left_left;
683 base = left_right;
684 displacement = right;
685 } else {
686 // (B + D)
687 base = left;
688 displacement = right;
689 }
690 } else {
691 // (B + B)
692 index = left;
693 base = right;
694 }
695 }
696 } else {
697 if (m.right().HasResolvedValue()) {
698 // (B + D)
699 base = left;
700 displacement = right;
701 } else {
702 // (B + B)
703 base = left;
704 index = right;
705 }
706 }
707 }
708 }
709 if (displacement != nullptr) {
710 if (displacement->opcode() == IrOpcode::kInt32Constant) {
711 if (OpParameter<int32_t>(displacement->op()) == 0) {
712 displacement = nullptr;
713 }
714 } else {
715 DCHECK_EQ(displacement->opcode(), IrOpcode::kInt64Constant);
716 if (OpParameter<int64_t>(displacement->op()) == 0) {
717 displacement = nullptr;
718 }
719 }
720 }
721 if (power_of_two_plus_one) {
722 if (base != nullptr) {
723 // If the scale requires explicitly using the index as the base, but a
724 // base is already part of the match, then the (1 << N + 1) scale factor
725 // can't be folded into the match and the entire index * scale
726 // calculation must be computed separately.
727 index = scale_expression;
728 scale = 0;
729 } else {
730 base = index;
731 }
732 }
733 if (!(options & AddressOption::kAllowScale) && scale != 0) {
734 index = scale_expression;
735 scale = 0;
736 }
737 base_ = base;
738 displacement_ = displacement;
739 displacement_mode_ = displacement_mode;
740 index_ = index;
741 scale_ = scale;
742 matches_ = true;
743 }
744
745 // Warning: When {node} is used by a Add/Sub instruction, this function does
746 // not guarantee the Add/Sub will be part of a addressing operand.
747 static bool OwnedByAddressingOperand(Node* node) {
748 for (auto use : node->use_edges()) {
749 Node* from = use.from();
750 switch (from->opcode()) {
751 case IrOpcode::kLoad:
752 case IrOpcode::kLoadImmutable:
753 case IrOpcode::kProtectedLoad:
754 case IrOpcode::kLoadTrapOnNull:
755 case IrOpcode::kInt32Add:
756 case IrOpcode::kInt64Add:
757 // Skip addressing uses.
758 break;
759 case IrOpcode::kInt32Sub:
760 // If the subtrahend is not a constant, it is not an addressing use.
761 if (from->InputAt(1)->opcode() != IrOpcode::kInt32Constant)
762 return false;
763 break;
764 case IrOpcode::kInt64Sub:
765 // If the subtrahend is not a constant, it is not an addressing use.
766 if (from->InputAt(1)->opcode() != IrOpcode::kInt64Constant)
767 return false;
768 break;
769 case IrOpcode::kStore:
770 case IrOpcode::kProtectedStore:
771 case IrOpcode::kStoreTrapOnNull:
772 // If the stored value is this node, it is not an addressing use.
773 if (from->InputAt(2) == node) return false;
774 // Otherwise it is used as an address and skipped.
775 break;
776 default:
777 // Non-addressing use found.
778 return false;
779 }
780 }
781 return true;
782 }
783};
784
789
791 explicit BranchMatcher(Node* branch);
792
793 bool Matched() const { return if_true_ && if_false_; }
794
795 Node* Branch() const { return node(); }
796 Node* IfTrue() const { return if_true_; }
797 Node* IfFalse() const { return if_false_; }
798
799 private:
802};
803
805 : public NON_EXPORTED_BASE(NodeMatcher) {
806 explicit DiamondMatcher(Node* merge);
807
808 bool Matched() const { return branch_; }
810 return if_true_->OwnedBy(node()) && if_false_->OwnedBy(node());
811 }
812
813 Node* Branch() const { return branch_; }
814 Node* IfTrue() const { return if_true_; }
815 Node* IfFalse() const { return if_false_; }
816 Node* Merge() const { return node(); }
817
818 Node* TrueInputOf(Node* phi) const {
819 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
820 DCHECK_EQ(3, phi->InputCount());
821 DCHECK_EQ(Merge(), phi->InputAt(2));
822 return phi->InputAt(if_true_ == Merge()->InputAt(0) ? 0 : 1);
823 }
824
825 Node* FalseInputOf(Node* phi) const {
826 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
827 DCHECK_EQ(3, phi->InputCount());
828 DCHECK_EQ(Merge(), phi->InputAt(2));
829 return phi->InputAt(if_true_ == Merge()->InputAt(0) ? 1 : 0);
830 }
831
832 private:
836};
837
838#if V8_ENABLE_WEBASSEMBLY
839struct LoadTransformMatcher
840 : ValueMatcher<LoadTransformParameters, IrOpcode::kLoadTransform> {
841 explicit LoadTransformMatcher(Node* node) : ValueMatcher(node) {}
842 bool Is(LoadTransformation t) {
843 return HasResolvedValue() && ResolvedValue().transformation == t;
844 }
845};
846#endif // V8_ENABLE_WEBASSEMBLY
847
848} // namespace v8::internal::compiler
849
850#endif // V8_COMPILER_NODE_MATCHERS_H_
#define DEFINE_OPERATORS_FOR_FLAGS(Type)
Definition flags.h:100
#define T
interpreter::OperandScale scale
Definition builtins.cc:44
constexpr bool IsInfinite() const
Definition double.h:114
V8_INLINE Address address() const
Definition handles.h:73
static constexpr MachineRepresentation PointerRepresentation()
static bool IsValueIdentity(Node *node, Node **out_value)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
const Operator * op() const
Definition node.h:50
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
bool OwnedBy(Node const *owner) const
Definition node.cc:325
Register const index_
JSHeapBroker * broker
int32_t displacement
OptionalOpIndex index
DisplacementMode displacement_mode
Node * node
int m
Definition mul-fft.cc:294
int n
Definition mul-fft.cc:296
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
constexpr bool IsInRange(T value, U lower_limit, U higher_limit)
Definition bounds.h:20
FloatMatcher< double, IrOpcode::kFloat64Constant > Float64Matcher
IntMatcher< uint32_t, IrOpcode::kInt32Constant > Uint32Matcher
Uint64Matcher UintPtrMatcher
IntMatcher< int32_t, IrOpcode::kInt32Constant > Int32Matcher
IntMatcher< uint64_t, IrOpcode::kInt64Constant > Uint64Matcher
FloatMatcher< float, IrOpcode::kFloat32Constant > Float32Matcher
ref_traits< T >::ref_type MakeRefAssumeMemoryFence(JSHeapBroker *broker, Tagged< T > object)
IntMatcher< int64_t, IrOpcode::kInt64Constant > Int64Matcher
T const & OpParameter(const Operator *op)
Definition operator.h:214
Node * SkipValueIdentities(Node *node)
HeapObjectMatcherImpl< IrOpcode::kCompressedHeapConstant > CompressedHeapObjectMatcher
HeapObjectMatcherImpl< IrOpcode::kHeapConstant > HeapObjectMatcher
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
return value
Definition map-inl.h:893
static constexpr bool is_direct_handle_v
Definition handles.h:391
#define DEFINE_IS_OPCODE(Opcode,...)
#define ALL_OP_LIST(V)
Definition opcodes.h:1328
#define NON_EXPORTED_BASE(code)
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
void Initialize(Node *node, bool allow_input_swap)
AddMatcher(Node *node, bool allow_input_swap)
BaseWithIndexAndDisplacementMatcher(Node *node, AddressOptions options)
void Initialize(Node *node, AddressOptions options)
BinopMatcher(Node *node, bool allow_input_swap)
static constexpr MachineRepresentation representation
bool Is(const ExternalReference &value) const
bool IsInRange(const T &low, const T &high) const
bool Is(const T &value) const
bool Is(IndirectHandle< HeapObject > const &value) const
HeapObjectRef Ref(JSHeapBroker *broker) const
bool Is(const T &value) const
bool IsInRange(const T &low, const T &high) const
IntPtrMatcher const & index() const
bool Equals(const Node *node) const
bool HasProperty(Operator::Property property) const
IrOpcode::Value opcode() const
const Operator * op() const
Node * InputAt(int index) const
ScaleMatcher(Node *node, bool allow_power_of_two_plus_one=false)