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/compiler-specific.h"
12 #include "src/codegen/external-reference.h"
13 #include "src/common/globals.h"
14 #include "src/compiler/common-operator.h"
15 #include "src/compiler/node.h"
16 #include "src/compiler/operator.h"
17 #include "src/numbers/double.h"
18 #include "src/objects/heap-object.h"
19
20 namespace v8 {
21 namespace internal {
22 namespace compiler {
23
24 class JSHeapBroker;
25
26 // A pattern matcher for nodes.
27 struct NodeMatcher {
NodeMatcherNodeMatcher28 explicit NodeMatcher(Node* node) : node_(node) {}
29
nodeNodeMatcher30 Node* node() const { return node_; }
opNodeMatcher31 const Operator* op() const { return node()->op(); }
opcodeNodeMatcher32 IrOpcode::Value opcode() const { return node()->opcode(); }
33
HasPropertyNodeMatcher34 bool HasProperty(Operator::Property property) const {
35 return op()->HasProperty(property);
36 }
InputAtNodeMatcher37 Node* InputAt(int index) const { return node()->InputAt(index); }
38
EqualsNodeMatcher39 bool Equals(const Node* node) const { return node_ == node; }
40
41 bool IsComparison() const;
42
43 #define DEFINE_IS_OPCODE(Opcode, ...) \
44 bool Is##Opcode() const { return opcode() == IrOpcode::k##Opcode; }
45 ALL_OP_LIST(DEFINE_IS_OPCODE)
46 #undef DEFINE_IS_OPCODE
47
48 private:
49 Node* node_;
50 };
51
SkipValueIdentities(Node * node)52 inline Node* SkipValueIdentities(Node* node) {
53 #ifdef DEBUG
54 bool seen_fold_constant = false;
55 #endif
56 do {
57 #ifdef DEBUG
58 if (node->opcode() == IrOpcode::kFoldConstant) {
59 DCHECK(!seen_fold_constant);
60 seen_fold_constant = true;
61 }
62 #endif
63 } while (NodeProperties::IsValueIdentity(node, &node));
64 DCHECK_NOT_NULL(node);
65 return node;
66 }
67
68 // A pattern matcher for abitrary value constants.
69 //
70 // Note that value identities on the input node are skipped when matching. The
71 // resolved value may not be a parameter of the input node. The node() method
72 // returns the unmodified input node. This is by design, as reducers may wish to
73 // match value constants but delay reducing the node until a later phase. For
74 // example, binary operator reducers may opt to keep FoldConstant operands while
75 // applying a reduction that match on the constant value of the FoldConstant.
76 template <typename T, IrOpcode::Value kOpcode>
77 struct ValueMatcher : public NodeMatcher {
78 using ValueType = T;
79
ValueMatcherValueMatcher80 explicit ValueMatcher(Node* node)
81 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
82 node = SkipValueIdentities(node);
83 has_resolved_value_ = node->opcode() == kOpcode;
84 if (has_resolved_value_) {
85 resolved_value_ = OpParameter<T>(node->op());
86 }
87 }
88
HasResolvedValueValueMatcher89 bool HasResolvedValue() const { return has_resolved_value_; }
ResolvedValueValueMatcher90 const T& ResolvedValue() const {
91 CHECK(HasResolvedValue());
92 return resolved_value_;
93 }
94
95 private:
96 T resolved_value_;
97 bool has_resolved_value_;
98 };
99
100 template <>
ValueMatcher(Node * node)101 inline ValueMatcher<uint32_t, IrOpcode::kInt32Constant>::ValueMatcher(
102 Node* node)
103 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
104 node = SkipValueIdentities(node);
105 has_resolved_value_ = node->opcode() == IrOpcode::kInt32Constant;
106 if (has_resolved_value_) {
107 resolved_value_ = static_cast<uint32_t>(OpParameter<int32_t>(node->op()));
108 }
109 }
110
111 template <>
ValueMatcher(Node * node)112 inline ValueMatcher<int64_t, IrOpcode::kInt64Constant>::ValueMatcher(Node* node)
113 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
114 node = SkipValueIdentities(node);
115 if (node->opcode() == IrOpcode::kInt32Constant) {
116 resolved_value_ = OpParameter<int32_t>(node->op());
117 has_resolved_value_ = true;
118 } else if (node->opcode() == IrOpcode::kInt64Constant) {
119 resolved_value_ = OpParameter<int64_t>(node->op());
120 has_resolved_value_ = true;
121 }
122 }
123
124 template <>
ValueMatcher(Node * node)125 inline ValueMatcher<uint64_t, IrOpcode::kInt64Constant>::ValueMatcher(
126 Node* node)
127 : NodeMatcher(node), resolved_value_(), has_resolved_value_(false) {
128 node = SkipValueIdentities(node);
129 if (node->opcode() == IrOpcode::kInt32Constant) {
130 resolved_value_ = static_cast<uint32_t>(OpParameter<int32_t>(node->op()));
131 has_resolved_value_ = true;
132 } else if (node->opcode() == IrOpcode::kInt64Constant) {
133 resolved_value_ = static_cast<uint64_t>(OpParameter<int64_t>(node->op()));
134 has_resolved_value_ = true;
135 }
136 }
137
138 // A pattern matcher for integer constants.
139 template <typename T, IrOpcode::Value kOpcode>
140 struct IntMatcher final : public ValueMatcher<T, kOpcode> {
IntMatcherfinal141 explicit IntMatcher(Node* node) : ValueMatcher<T, kOpcode>(node) {}
142
Isfinal143 bool Is(const T& value) const {
144 return this->HasResolvedValue() && this->ResolvedValue() == value;
145 }
IsInRangefinal146 bool IsInRange(const T& low, const T& high) const {
147 return this->HasResolvedValue() && low <= this->ResolvedValue() &&
148 this->ResolvedValue() <= high;
149 }
IsMultipleOffinal150 bool IsMultipleOf(T n) const {
151 return this->HasResolvedValue() && (this->ResolvedValue() % n) == 0;
152 }
IsPowerOf2final153 bool IsPowerOf2() const {
154 return this->HasResolvedValue() && this->ResolvedValue() > 0 &&
155 (this->ResolvedValue() & (this->ResolvedValue() - 1)) == 0;
156 }
IsNegativePowerOf2final157 bool IsNegativePowerOf2() const {
158 return this->HasResolvedValue() && this->ResolvedValue() < 0 &&
159 ((this->ResolvedValue() == std::numeric_limits<T>::min()) ||
160 (-this->ResolvedValue() & (-this->ResolvedValue() - 1)) == 0);
161 }
IsNegativefinal162 bool IsNegative() const {
163 return this->HasResolvedValue() && this->ResolvedValue() < 0;
164 }
165 };
166
167 using Int32Matcher = IntMatcher<int32_t, IrOpcode::kInt32Constant>;
168 using Uint32Matcher = IntMatcher<uint32_t, IrOpcode::kInt32Constant>;
169 using Int64Matcher = IntMatcher<int64_t, IrOpcode::kInt64Constant>;
170 using Uint64Matcher = IntMatcher<uint64_t, IrOpcode::kInt64Constant>;
171 #if V8_HOST_ARCH_32_BIT
172 using IntPtrMatcher = Int32Matcher;
173 using UintPtrMatcher = Uint32Matcher;
174 #else
175 using IntPtrMatcher = Int64Matcher;
176 using UintPtrMatcher = Uint64Matcher;
177 #endif
178
179
180 // A pattern matcher for floating point constants.
181 template <typename T, IrOpcode::Value kOpcode>
182 struct FloatMatcher final : public ValueMatcher<T, kOpcode> {
FloatMatcherfinal183 explicit FloatMatcher(Node* node) : ValueMatcher<T, kOpcode>(node) {}
184
Isfinal185 bool Is(const T& value) const {
186 return this->HasResolvedValue() && this->ResolvedValue() == value;
187 }
IsInRangefinal188 bool IsInRange(const T& low, const T& high) const {
189 return this->HasResolvedValue() && low <= this->ResolvedValue() &&
190 this->ResolvedValue() <= high;
191 }
IsMinusZerofinal192 bool IsMinusZero() const {
193 return this->Is(0.0) && std::signbit(this->ResolvedValue());
194 }
IsNegativefinal195 bool IsNegative() const {
196 return this->HasResolvedValue() && this->ResolvedValue() < 0.0;
197 }
IsNaNfinal198 bool IsNaN() const {
199 return this->HasResolvedValue() && std::isnan(this->ResolvedValue());
200 }
IsZerofinal201 bool IsZero() const {
202 return this->Is(0.0) && !std::signbit(this->ResolvedValue());
203 }
IsNormalfinal204 bool IsNormal() const {
205 return this->HasResolvedValue() && std::isnormal(this->ResolvedValue());
206 }
IsIntegerfinal207 bool IsInteger() const {
208 return this->HasResolvedValue() &&
209 std::nearbyint(this->ResolvedValue()) == this->ResolvedValue();
210 }
IsPositiveOrNegativePowerOf2final211 bool IsPositiveOrNegativePowerOf2() const {
212 if (!this->HasResolvedValue() || (this->ResolvedValue() == 0.0)) {
213 return false;
214 }
215 Double value = Double(this->ResolvedValue());
216 return !value.IsInfinite() && base::bits::IsPowerOfTwo(value.Significand());
217 }
218 };
219
220 using Float32Matcher = FloatMatcher<float, IrOpcode::kFloat32Constant>;
221 using Float64Matcher = FloatMatcher<double, IrOpcode::kFloat64Constant>;
222 using NumberMatcher = FloatMatcher<double, IrOpcode::kNumberConstant>;
223
224 // A pattern matcher for heap object constants.
225 template <IrOpcode::Value kHeapConstantOpcode>
226 struct HeapObjectMatcherImpl final
227 : public ValueMatcher<Handle<HeapObject>, kHeapConstantOpcode> {
HeapObjectMatcherImplfinal228 explicit HeapObjectMatcherImpl(Node* node)
229 : ValueMatcher<Handle<HeapObject>, kHeapConstantOpcode>(node) {}
230
Isfinal231 bool Is(Handle<HeapObject> const& value) const {
232 return this->HasResolvedValue() &&
233 this->ResolvedValue().address() == value.address();
234 }
235
Reffinal236 HeapObjectRef Ref(JSHeapBroker* broker) const {
237 return HeapObjectRef(broker, this->ResolvedValue());
238 }
239 };
240
241 using HeapObjectMatcher = HeapObjectMatcherImpl<IrOpcode::kHeapConstant>;
242 using CompressedHeapObjectMatcher =
243 HeapObjectMatcherImpl<IrOpcode::kCompressedHeapConstant>;
244
245 // A pattern matcher for external reference constants.
246 struct ExternalReferenceMatcher final
247 : public ValueMatcher<ExternalReference, IrOpcode::kExternalConstant> {
ExternalReferenceMatcherfinal248 explicit ExternalReferenceMatcher(Node* node)
249 : ValueMatcher<ExternalReference, IrOpcode::kExternalConstant>(node) {}
Isfinal250 bool Is(const ExternalReference& value) const {
251 return this->HasResolvedValue() && this->ResolvedValue() == value;
252 }
253 };
254
255
256 // For shorter pattern matching code, this struct matches the inputs to
257 // machine-level load operations.
258 template <typename Object>
259 struct LoadMatcher : public NodeMatcher {
LoadMatcherLoadMatcher260 explicit LoadMatcher(Node* node)
261 : NodeMatcher(node), object_(InputAt(0)), index_(InputAt(1)) {}
262
263 using ObjectMatcher = Object;
264
objectLoadMatcher265 Object const& object() const { return object_; }
indexLoadMatcher266 IntPtrMatcher const& index() const { return index_; }
267
268 private:
269 Object const object_;
270 IntPtrMatcher const index_;
271 };
272
273
274 // For shorter pattern matching code, this struct matches both the left and
275 // right hand sides of a binary operation and can put constants on the right
276 // if they appear on the left hand side of a commutative operation.
277 template <typename Left, typename Right>
278 struct BinopMatcher : public NodeMatcher {
BinopMatcherBinopMatcher279 explicit BinopMatcher(Node* node)
280 : NodeMatcher(node), left_(InputAt(0)), right_(InputAt(1)) {
281 if (HasProperty(Operator::kCommutative)) PutConstantOnRight();
282 }
BinopMatcherBinopMatcher283 BinopMatcher(Node* node, bool allow_input_swap)
284 : NodeMatcher(node), left_(InputAt(0)), right_(InputAt(1)) {
285 if (allow_input_swap) PutConstantOnRight();
286 }
287
288 using LeftMatcher = Left;
289 using RightMatcher = Right;
290
leftBinopMatcher291 const Left& left() const { return left_; }
rightBinopMatcher292 const Right& right() const { return right_; }
293
IsFoldableBinopMatcher294 bool IsFoldable() const {
295 return left().HasResolvedValue() && right().HasResolvedValue();
296 }
LeftEqualsRightBinopMatcher297 bool LeftEqualsRight() const { return left().node() == right().node(); }
298
OwnsInputBinopMatcher299 bool OwnsInput(Node* input) {
300 for (Node* use : input->uses()) {
301 if (use != node()) {
302 return false;
303 }
304 }
305 return true;
306 }
307
308 protected:
SwapInputsBinopMatcher309 void SwapInputs() {
310 std::swap(left_, right_);
311 // TODO(tebbi): This modification should notify the reducers using
312 // BinopMatcher. Alternatively, all reducers (especially value numbering)
313 // could ignore the ordering for commutative binops.
314 node()->ReplaceInput(0, left().node());
315 node()->ReplaceInput(1, right().node());
316 }
317
318 private:
PutConstantOnRightBinopMatcher319 void PutConstantOnRight() {
320 if (left().HasResolvedValue() && !right().HasResolvedValue()) {
321 SwapInputs();
322 }
323 }
324
325 Left left_;
326 Right right_;
327 };
328
329 using Int32BinopMatcher = BinopMatcher<Int32Matcher, Int32Matcher>;
330 using Uint32BinopMatcher = BinopMatcher<Uint32Matcher, Uint32Matcher>;
331 using Int64BinopMatcher = BinopMatcher<Int64Matcher, Int64Matcher>;
332 using Uint64BinopMatcher = BinopMatcher<Uint64Matcher, Uint64Matcher>;
333 using IntPtrBinopMatcher = BinopMatcher<IntPtrMatcher, IntPtrMatcher>;
334 using UintPtrBinopMatcher = BinopMatcher<UintPtrMatcher, UintPtrMatcher>;
335 using Float32BinopMatcher = BinopMatcher<Float32Matcher, Float32Matcher>;
336 using Float64BinopMatcher = BinopMatcher<Float64Matcher, Float64Matcher>;
337 using NumberBinopMatcher = BinopMatcher<NumberMatcher, NumberMatcher>;
338 using HeapObjectBinopMatcher =
339 BinopMatcher<HeapObjectMatcher, HeapObjectMatcher>;
340 using CompressedHeapObjectBinopMatcher =
341 BinopMatcher<CompressedHeapObjectMatcher, CompressedHeapObjectMatcher>;
342
343 template <class BinopMatcher, IrOpcode::Value kMulOpcode,
344 IrOpcode::Value kShiftOpcode>
345 struct ScaleMatcher {
346 explicit ScaleMatcher(Node* node, bool allow_power_of_two_plus_one = false)
347 : scale_(-1), power_of_two_plus_one_(false) {
348 if (node->InputCount() < 2) return;
349 BinopMatcher m(node);
350 if (node->opcode() == kShiftOpcode) {
351 if (m.right().HasResolvedValue()) {
352 typename BinopMatcher::RightMatcher::ValueType value =
353 m.right().ResolvedValue();
354 if (value >= 0 && value <= 3) {
355 scale_ = static_cast<int>(value);
356 }
357 }
358 } else if (node->opcode() == kMulOpcode) {
359 if (m.right().HasResolvedValue()) {
360 typename BinopMatcher::RightMatcher::ValueType value =
361 m.right().ResolvedValue();
362 if (value == 1) {
363 scale_ = 0;
364 } else if (value == 2) {
365 scale_ = 1;
366 } else if (value == 4) {
367 scale_ = 2;
368 } else if (value == 8) {
369 scale_ = 3;
370 } else if (allow_power_of_two_plus_one) {
371 if (value == 3) {
372 scale_ = 1;
373 power_of_two_plus_one_ = true;
374 } else if (value == 5) {
375 scale_ = 2;
376 power_of_two_plus_one_ = true;
377 } else if (value == 9) {
378 scale_ = 3;
379 power_of_two_plus_one_ = true;
380 }
381 }
382 }
383 }
384 }
385
matchesScaleMatcher386 bool matches() const { return scale_ != -1; }
scaleScaleMatcher387 int scale() const { return scale_; }
power_of_two_plus_oneScaleMatcher388 bool power_of_two_plus_one() const { return power_of_two_plus_one_; }
389
390 private:
391 int scale_;
392 bool power_of_two_plus_one_;
393 };
394
395 using Int32ScaleMatcher =
396 ScaleMatcher<Int32BinopMatcher, IrOpcode::kInt32Mul, IrOpcode::kWord32Shl>;
397 using Int64ScaleMatcher =
398 ScaleMatcher<Int64BinopMatcher, IrOpcode::kInt64Mul, IrOpcode::kWord64Shl>;
399
400 template <class BinopMatcher, IrOpcode::Value AddOpcode,
401 IrOpcode::Value SubOpcode, IrOpcode::Value kMulOpcode,
402 IrOpcode::Value kShiftOpcode>
403 struct AddMatcher : public BinopMatcher {
404 static const IrOpcode::Value kAddOpcode = AddOpcode;
405 static const IrOpcode::Value kSubOpcode = SubOpcode;
406 using Matcher = ScaleMatcher<BinopMatcher, kMulOpcode, kShiftOpcode>;
407
AddMatcherAddMatcher408 AddMatcher(Node* node, bool allow_input_swap)
409 : BinopMatcher(node, allow_input_swap),
410 scale_(-1),
411 power_of_two_plus_one_(false) {
412 Initialize(node, allow_input_swap);
413 }
AddMatcherAddMatcher414 explicit AddMatcher(Node* node)
415 : BinopMatcher(node, node->op()->HasProperty(Operator::kCommutative)),
416 scale_(-1),
417 power_of_two_plus_one_(false) {
418 Initialize(node, node->op()->HasProperty(Operator::kCommutative));
419 }
420
HasIndexInputAddMatcher421 bool HasIndexInput() const { return scale_ != -1; }
IndexInputAddMatcher422 Node* IndexInput() const {
423 DCHECK(HasIndexInput());
424 return this->left().node()->InputAt(0);
425 }
scaleAddMatcher426 int scale() const {
427 DCHECK(HasIndexInput());
428 return scale_;
429 }
power_of_two_plus_oneAddMatcher430 bool power_of_two_plus_one() const { return power_of_two_plus_one_; }
431
432 private:
InitializeAddMatcher433 void Initialize(Node* node, bool allow_input_swap) {
434 Matcher left_matcher(this->left().node(), true);
435 if (left_matcher.matches()) {
436 scale_ = left_matcher.scale();
437 power_of_two_plus_one_ = left_matcher.power_of_two_plus_one();
438 return;
439 }
440
441 if (!allow_input_swap) {
442 return;
443 }
444
445 Matcher right_matcher(this->right().node(), true);
446 if (right_matcher.matches()) {
447 scale_ = right_matcher.scale();
448 power_of_two_plus_one_ = right_matcher.power_of_two_plus_one();
449 this->SwapInputs();
450 return;
451 }
452
453 if ((this->left().opcode() != kSubOpcode &&
454 this->left().opcode() != kAddOpcode) &&
455 (this->right().opcode() == kAddOpcode ||
456 this->right().opcode() == kSubOpcode)) {
457 this->SwapInputs();
458 }
459 }
460
461 int scale_;
462 bool power_of_two_plus_one_;
463 };
464
465 using Int32AddMatcher =
466 AddMatcher<Int32BinopMatcher, IrOpcode::kInt32Add, IrOpcode::kInt32Sub,
467 IrOpcode::kInt32Mul, IrOpcode::kWord32Shl>;
468 using Int64AddMatcher =
469 AddMatcher<Int64BinopMatcher, IrOpcode::kInt64Add, IrOpcode::kInt64Sub,
470 IrOpcode::kInt64Mul, IrOpcode::kWord64Shl>;
471
472 enum DisplacementMode { kPositiveDisplacement, kNegativeDisplacement };
473
474 enum class AddressOption : uint8_t {
475 kAllowNone = 0u,
476 kAllowInputSwap = 1u << 0,
477 kAllowScale = 1u << 1,
478 kAllowAll = kAllowInputSwap | kAllowScale
479 };
480
481 using AddressOptions = base::Flags<AddressOption, uint8_t>;
482 DEFINE_OPERATORS_FOR_FLAGS(AddressOptions)
483
484 template <class AddMatcher>
485 struct BaseWithIndexAndDisplacementMatcher {
BaseWithIndexAndDisplacementMatcherBaseWithIndexAndDisplacementMatcher486 BaseWithIndexAndDisplacementMatcher(Node* node, AddressOptions options)
487 : matches_(false),
488 index_(nullptr),
489 scale_(0),
490 base_(nullptr),
491 displacement_(nullptr),
492 displacement_mode_(kPositiveDisplacement) {
493 Initialize(node, options);
494 }
495
BaseWithIndexAndDisplacementMatcherBaseWithIndexAndDisplacementMatcher496 explicit BaseWithIndexAndDisplacementMatcher(Node* node)
497 : matches_(false),
498 index_(nullptr),
499 scale_(0),
500 base_(nullptr),
501 displacement_(nullptr),
502 displacement_mode_(kPositiveDisplacement) {
503 Initialize(node, AddressOption::kAllowScale |
504 (node->op()->HasProperty(Operator::kCommutative)
505 ? AddressOption::kAllowInputSwap
506 : AddressOption::kAllowNone));
507 }
508
matchesBaseWithIndexAndDisplacementMatcher509 bool matches() const { return matches_; }
indexBaseWithIndexAndDisplacementMatcher510 Node* index() const { return index_; }
scaleBaseWithIndexAndDisplacementMatcher511 int scale() const { return scale_; }
baseBaseWithIndexAndDisplacementMatcher512 Node* base() const { return base_; }
displacementBaseWithIndexAndDisplacementMatcher513 Node* displacement() const { return displacement_; }
displacement_modeBaseWithIndexAndDisplacementMatcher514 DisplacementMode displacement_mode() const { return displacement_mode_; }
515
516 private:
517 bool matches_;
518 Node* index_;
519 int scale_;
520 Node* base_;
521 Node* displacement_;
522 DisplacementMode displacement_mode_;
523
InitializeBaseWithIndexAndDisplacementMatcher524 void Initialize(Node* node, AddressOptions options) {
525 // The BaseWithIndexAndDisplacementMatcher canonicalizes the order of
526 // displacements and scale factors that are used as inputs, so instead of
527 // enumerating all possible patterns by brute force, checking for node
528 // clusters using the following templates in the following order suffices to
529 // find all of the interesting cases (S = index * scale, B = base input, D =
530 // displacement input):
531 // (S + (B + D))
532 // (S + (B + B))
533 // (S + D)
534 // (S + B)
535 // ((S + D) + B)
536 // ((S + B) + D)
537 // ((B + D) + B)
538 // ((B + B) + D)
539 // (B + D)
540 // (B + B)
541 if (node->InputCount() < 2) return;
542 AddMatcher m(node, options & AddressOption::kAllowInputSwap);
543 Node* left = m.left().node();
544 Node* right = m.right().node();
545 Node* displacement = nullptr;
546 Node* base = nullptr;
547 Node* index = nullptr;
548 Node* scale_expression = nullptr;
549 bool power_of_two_plus_one = false;
550 DisplacementMode displacement_mode = kPositiveDisplacement;
551 int scale = 0;
552 if (m.HasIndexInput() && OwnedByAddressingOperand(left)) {
553 index = m.IndexInput();
554 scale = m.scale();
555 scale_expression = left;
556 power_of_two_plus_one = m.power_of_two_plus_one();
557 bool match_found = false;
558 if (right->opcode() == AddMatcher::kSubOpcode &&
559 OwnedByAddressingOperand(right)) {
560 AddMatcher right_matcher(right);
561 if (right_matcher.right().HasResolvedValue()) {
562 // (S + (B - D))
563 base = right_matcher.left().node();
564 displacement = right_matcher.right().node();
565 displacement_mode = kNegativeDisplacement;
566 match_found = true;
567 }
568 }
569 if (!match_found) {
570 if (right->opcode() == AddMatcher::kAddOpcode &&
571 OwnedByAddressingOperand(right)) {
572 AddMatcher right_matcher(right);
573 if (right_matcher.right().HasResolvedValue()) {
574 // (S + (B + D))
575 base = right_matcher.left().node();
576 displacement = right_matcher.right().node();
577 } else {
578 // (S + (B + B))
579 base = right;
580 }
581 } else if (m.right().HasResolvedValue()) {
582 // (S + D)
583 displacement = right;
584 } else {
585 // (S + B)
586 base = right;
587 }
588 }
589 } else {
590 bool match_found = false;
591 if (left->opcode() == AddMatcher::kSubOpcode &&
592 OwnedByAddressingOperand(left)) {
593 AddMatcher left_matcher(left);
594 Node* left_left = left_matcher.left().node();
595 Node* left_right = left_matcher.right().node();
596 if (left_matcher.right().HasResolvedValue()) {
597 if (left_matcher.HasIndexInput() && left_left->OwnedBy(left)) {
598 // ((S - D) + B)
599 index = left_matcher.IndexInput();
600 scale = left_matcher.scale();
601 scale_expression = left_left;
602 power_of_two_plus_one = left_matcher.power_of_two_plus_one();
603 displacement = left_right;
604 displacement_mode = kNegativeDisplacement;
605 base = right;
606 } else {
607 // ((B - D) + B)
608 index = left_left;
609 displacement = left_right;
610 displacement_mode = kNegativeDisplacement;
611 base = right;
612 }
613 match_found = true;
614 }
615 }
616 if (!match_found) {
617 if (left->opcode() == AddMatcher::kAddOpcode &&
618 OwnedByAddressingOperand(left)) {
619 AddMatcher left_matcher(left);
620 Node* left_left = left_matcher.left().node();
621 Node* left_right = left_matcher.right().node();
622 if (left_matcher.HasIndexInput() && left_left->OwnedBy(left)) {
623 if (left_matcher.right().HasResolvedValue()) {
624 // ((S + D) + B)
625 index = left_matcher.IndexInput();
626 scale = left_matcher.scale();
627 scale_expression = left_left;
628 power_of_two_plus_one = left_matcher.power_of_two_plus_one();
629 displacement = left_right;
630 base = right;
631 } else if (m.right().HasResolvedValue()) {
632 if (left->OwnedBy(node)) {
633 // ((S + B) + D)
634 index = left_matcher.IndexInput();
635 scale = left_matcher.scale();
636 scale_expression = left_left;
637 power_of_two_plus_one = left_matcher.power_of_two_plus_one();
638 base = left_right;
639 displacement = right;
640 } else {
641 // (B + D)
642 base = left;
643 displacement = right;
644 }
645 } else {
646 // (B + B)
647 index = left;
648 base = right;
649 }
650 } else {
651 if (left_matcher.right().HasResolvedValue()) {
652 // ((B + D) + B)
653 index = left_left;
654 displacement = left_right;
655 base = right;
656 } else if (m.right().HasResolvedValue()) {
657 if (left->OwnedBy(node)) {
658 // ((B + B) + D)
659 index = left_left;
660 base = left_right;
661 displacement = right;
662 } else {
663 // (B + D)
664 base = left;
665 displacement = right;
666 }
667 } else {
668 // (B + B)
669 index = left;
670 base = right;
671 }
672 }
673 } else {
674 if (m.right().HasResolvedValue()) {
675 // (B + D)
676 base = left;
677 displacement = right;
678 } else {
679 // (B + B)
680 base = left;
681 index = right;
682 }
683 }
684 }
685 }
686 int64_t value = 0;
687 if (displacement != nullptr) {
688 switch (displacement->opcode()) {
689 case IrOpcode::kInt32Constant: {
690 value = OpParameter<int32_t>(displacement->op());
691 break;
692 }
693 case IrOpcode::kInt64Constant: {
694 value = OpParameter<int64_t>(displacement->op());
695 break;
696 }
697 default:
698 UNREACHABLE();
699 break;
700 }
701 if (value == 0) {
702 displacement = nullptr;
703 }
704 }
705 if (power_of_two_plus_one) {
706 if (base != nullptr) {
707 // If the scale requires explicitly using the index as the base, but a
708 // base is already part of the match, then the (1 << N + 1) scale factor
709 // can't be folded into the match and the entire index * scale
710 // calculation must be computed separately.
711 index = scale_expression;
712 scale = 0;
713 } else {
714 base = index;
715 }
716 }
717 if (!(options & AddressOption::kAllowScale) && scale != 0) {
718 index = scale_expression;
719 scale = 0;
720 }
721 base_ = base;
722 displacement_ = displacement;
723 displacement_mode_ = displacement_mode;
724 index_ = index;
725 scale_ = scale;
726 matches_ = true;
727 }
728
OwnedByAddressingOperandBaseWithIndexAndDisplacementMatcher729 static bool OwnedByAddressingOperand(Node* node) {
730 for (auto use : node->use_edges()) {
731 Node* from = use.from();
732 switch (from->opcode()) {
733 case IrOpcode::kLoad:
734 case IrOpcode::kPoisonedLoad:
735 case IrOpcode::kProtectedLoad:
736 case IrOpcode::kInt32Add:
737 case IrOpcode::kInt64Add:
738 // Skip addressing uses.
739 break;
740 case IrOpcode::kStore:
741 case IrOpcode::kProtectedStore:
742 // If the stored value is this node, it is not an addressing use.
743 if (from->InputAt(2) == node) return false;
744 // Otherwise it is used as an address and skipped.
745 break;
746 default:
747 // Non-addressing use found.
748 return false;
749 }
750 }
751 return true;
752 }
753 };
754
755 using BaseWithIndexAndDisplacement32Matcher =
756 BaseWithIndexAndDisplacementMatcher<Int32AddMatcher>;
757 using BaseWithIndexAndDisplacement64Matcher =
758 BaseWithIndexAndDisplacementMatcher<Int64AddMatcher>;
759
760 struct V8_EXPORT_PRIVATE BranchMatcher : public NON_EXPORTED_BASE(NodeMatcher) {
761 explicit BranchMatcher(Node* branch);
762
MatchedBranchMatcher763 bool Matched() const { return if_true_ && if_false_; }
764
BranchBranchMatcher765 Node* Branch() const { return node(); }
IfTrueBranchMatcher766 Node* IfTrue() const { return if_true_; }
IfFalseBranchMatcher767 Node* IfFalse() const { return if_false_; }
768
769 private:
770 Node* if_true_;
771 Node* if_false_;
772 };
773
774 struct V8_EXPORT_PRIVATE DiamondMatcher
775 : public NON_EXPORTED_BASE(NodeMatcher) {
776 explicit DiamondMatcher(Node* merge);
777
MatchedDiamondMatcher778 bool Matched() const { return branch_; }
IfProjectionsAreOwnedDiamondMatcher779 bool IfProjectionsAreOwned() const {
780 return if_true_->OwnedBy(node()) && if_false_->OwnedBy(node());
781 }
782
BranchDiamondMatcher783 Node* Branch() const { return branch_; }
IfTrueDiamondMatcher784 Node* IfTrue() const { return if_true_; }
IfFalseDiamondMatcher785 Node* IfFalse() const { return if_false_; }
MergeDiamondMatcher786 Node* Merge() const { return node(); }
787
TrueInputOfDiamondMatcher788 Node* TrueInputOf(Node* phi) const {
789 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
790 DCHECK_EQ(3, phi->InputCount());
791 DCHECK_EQ(Merge(), phi->InputAt(2));
792 return phi->InputAt(if_true_ == Merge()->InputAt(0) ? 0 : 1);
793 }
794
FalseInputOfDiamondMatcher795 Node* FalseInputOf(Node* phi) const {
796 DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
797 DCHECK_EQ(3, phi->InputCount());
798 DCHECK_EQ(Merge(), phi->InputAt(2));
799 return phi->InputAt(if_true_ == Merge()->InputAt(0) ? 1 : 0);
800 }
801
802 private:
803 Node* branch_;
804 Node* if_true_;
805 Node* if_false_;
806 };
807
808 } // namespace compiler
809 } // namespace internal
810 } // namespace v8
811
812 #endif // V8_COMPILER_NODE_MATCHERS_H_
813