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