-
Notifications
You must be signed in to change notification settings - Fork 10.7k
/
SDPatternMatch.h
775 lines (641 loc) · 25.1 KB
/
SDPatternMatch.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
//==--------------- llvm/CodeGen/SDPatternMatch.h ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// Contains matchers for matching SelectionDAG nodes and values.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_SDPATTERNMATCH_H
#define LLVM_CODEGEN_SDPATTERNMATCH_H
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/CodeGen/TargetLowering.h"
namespace llvm {
namespace SDPatternMatch {
/// MatchContext can repurpose existing patterns to behave differently under
/// a certain context. For instance, `m_Opc(ISD::ADD)` matches plain ADD nodes
/// in normal circumstances, but matches VP_ADD nodes under a custom
/// VPMatchContext. This design is meant to facilitate code / pattern reusing.
class BasicMatchContext {
const SelectionDAG *DAG;
const TargetLowering *TLI;
public:
explicit BasicMatchContext(const SelectionDAG *DAG)
: DAG(DAG), TLI(DAG ? &DAG->getTargetLoweringInfo() : nullptr) {}
explicit BasicMatchContext(const TargetLowering *TLI)
: DAG(nullptr), TLI(TLI) {}
// A valid MatchContext has to implement the following functions.
const SelectionDAG *getDAG() const { return DAG; }
const TargetLowering *getTLI() const { return TLI; }
/// Return true if N effectively has opcode Opcode.
bool match(SDValue N, unsigned Opcode) const {
return N->getOpcode() == Opcode;
}
};
template <typename Pattern, typename MatchContext>
[[nodiscard]] bool sd_context_match(SDValue N, const MatchContext &Ctx,
Pattern &&P) {
return P.match(Ctx, N);
}
template <typename Pattern, typename MatchContext>
[[nodiscard]] bool sd_context_match(SDNode *N, const MatchContext &Ctx,
Pattern &&P) {
return sd_context_match(SDValue(N, 0), Ctx, P);
}
template <typename Pattern>
[[nodiscard]] bool sd_match(SDNode *N, const SelectionDAG *DAG, Pattern &&P) {
return sd_context_match(N, BasicMatchContext(DAG), P);
}
template <typename Pattern>
[[nodiscard]] bool sd_match(SDValue N, const SelectionDAG *DAG, Pattern &&P) {
return sd_context_match(N, BasicMatchContext(DAG), P);
}
template <typename Pattern>
[[nodiscard]] bool sd_match(SDNode *N, Pattern &&P) {
return sd_match(N, nullptr, P);
}
template <typename Pattern>
[[nodiscard]] bool sd_match(SDValue N, Pattern &&P) {
return sd_match(N, nullptr, P);
}
// === Utilities ===
struct Value_match {
SDValue MatchVal;
Value_match() = default;
explicit Value_match(SDValue Match) : MatchVal(Match) {}
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
if (MatchVal)
return MatchVal == N;
return N.getNode();
}
};
/// Match any valid SDValue.
inline Value_match m_Value() { return Value_match(); }
inline Value_match m_Specific(SDValue N) {
assert(N);
return Value_match(N);
}
struct DeferredValue_match {
SDValue &MatchVal;
explicit DeferredValue_match(SDValue &Match) : MatchVal(Match) {}
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
return N == MatchVal;
}
};
/// Similar to m_Specific, but the specific value to match is determined by
/// another sub-pattern in the same sd_match() expression. For instance,
/// We cannot match `(add V, V)` with `m_Add(m_Value(X), m_Specific(X))` since
/// `X` is not initialized at the time it got copied into `m_Specific`. Instead,
/// we should use `m_Add(m_Value(X), m_Deferred(X))`.
inline DeferredValue_match m_Deferred(SDValue &V) {
return DeferredValue_match(V);
}
struct Opcode_match {
unsigned Opcode;
explicit Opcode_match(unsigned Opc) : Opcode(Opc) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
return Ctx.match(N, Opcode);
}
};
inline Opcode_match m_Opc(unsigned Opcode) { return Opcode_match(Opcode); }
template <unsigned NumUses, typename Pattern> struct NUses_match {
Pattern P;
explicit NUses_match(const Pattern &P) : P(P) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
// SDNode::hasNUsesOfValue is pretty expensive when the SDNode produces
// multiple results, hence we check the subsequent pattern here before
// checking the number of value users.
return P.match(Ctx, N) && N->hasNUsesOfValue(NumUses, N.getResNo());
}
};
template <typename Pattern>
inline NUses_match<1, Pattern> m_OneUse(const Pattern &P) {
return NUses_match<1, Pattern>(P);
}
template <unsigned N, typename Pattern>
inline NUses_match<N, Pattern> m_NUses(const Pattern &P) {
return NUses_match<N, Pattern>(P);
}
inline NUses_match<1, Value_match> m_OneUse() {
return NUses_match<1, Value_match>(m_Value());
}
template <unsigned N> inline NUses_match<N, Value_match> m_NUses() {
return NUses_match<N, Value_match>(m_Value());
}
struct Value_bind {
SDValue &BindVal;
explicit Value_bind(SDValue &N) : BindVal(N) {}
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
BindVal = N;
return true;
}
};
inline Value_bind m_Value(SDValue &N) { return Value_bind(N); }
template <typename Pattern, typename PredFuncT> struct TLI_pred_match {
Pattern P;
PredFuncT PredFunc;
TLI_pred_match(const PredFuncT &Pred, const Pattern &P)
: P(P), PredFunc(Pred) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
assert(Ctx.getTLI() && "TargetLowering is required for this pattern.");
return PredFunc(*Ctx.getTLI(), N) && P.match(Ctx, N);
}
};
// Explicit deduction guide.
template <typename PredFuncT, typename Pattern>
TLI_pred_match(const PredFuncT &Pred, const Pattern &P)
-> TLI_pred_match<Pattern, PredFuncT>;
/// Match legal SDNodes based on the information provided by TargetLowering.
template <typename Pattern> inline auto m_LegalOp(const Pattern &P) {
return TLI_pred_match{[](const TargetLowering &TLI, SDValue N) {
return TLI.isOperationLegal(N->getOpcode(),
N.getValueType());
},
P};
}
/// Switch to a different MatchContext for subsequent patterns.
template <typename NewMatchContext, typename Pattern> struct SwitchContext {
const NewMatchContext &Ctx;
Pattern P;
template <typename OrigMatchContext>
bool match(const OrigMatchContext &, SDValue N) {
return P.match(Ctx, N);
}
};
template <typename MatchContext, typename Pattern>
inline SwitchContext<MatchContext, Pattern> m_Context(const MatchContext &Ctx,
Pattern &&P) {
return SwitchContext<MatchContext, Pattern>{Ctx, std::move(P)};
}
// === Value type ===
struct ValueType_bind {
EVT &BindVT;
explicit ValueType_bind(EVT &Bind) : BindVT(Bind) {}
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
BindVT = N.getValueType();
return true;
}
};
/// Retreive the ValueType of the current SDValue.
inline ValueType_bind m_VT(EVT &VT) { return ValueType_bind(VT); }
template <typename Pattern, typename PredFuncT> struct ValueType_match {
PredFuncT PredFunc;
Pattern P;
ValueType_match(const PredFuncT &Pred, const Pattern &P)
: PredFunc(Pred), P(P) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
return PredFunc(N.getValueType()) && P.match(Ctx, N);
}
};
// Explicit deduction guide.
template <typename PredFuncT, typename Pattern>
ValueType_match(const PredFuncT &Pred, const Pattern &P)
-> ValueType_match<Pattern, PredFuncT>;
/// Match a specific ValueType.
template <typename Pattern>
inline auto m_SpecificVT(EVT RefVT, const Pattern &P) {
return ValueType_match{[=](EVT VT) { return VT == RefVT; }, P};
}
inline auto m_SpecificVT(EVT RefVT) {
return ValueType_match{[=](EVT VT) { return VT == RefVT; }, m_Value()};
}
inline auto m_Glue() { return m_SpecificVT(MVT::Glue); }
inline auto m_OtherVT() { return m_SpecificVT(MVT::Other); }
/// Match any integer ValueTypes.
template <typename Pattern> inline auto m_IntegerVT(const Pattern &P) {
return ValueType_match{[](EVT VT) { return VT.isInteger(); }, P};
}
inline auto m_IntegerVT() {
return ValueType_match{[](EVT VT) { return VT.isInteger(); }, m_Value()};
}
/// Match any floating point ValueTypes.
template <typename Pattern> inline auto m_FloatingPointVT(const Pattern &P) {
return ValueType_match{[](EVT VT) { return VT.isFloatingPoint(); }, P};
}
inline auto m_FloatingPointVT() {
return ValueType_match{[](EVT VT) { return VT.isFloatingPoint(); },
m_Value()};
}
/// Match any vector ValueTypes.
template <typename Pattern> inline auto m_VectorVT(const Pattern &P) {
return ValueType_match{[](EVT VT) { return VT.isVector(); }, P};
}
inline auto m_VectorVT() {
return ValueType_match{[](EVT VT) { return VT.isVector(); }, m_Value()};
}
/// Match fixed-length vector ValueTypes.
template <typename Pattern> inline auto m_FixedVectorVT(const Pattern &P) {
return ValueType_match{[](EVT VT) { return VT.isFixedLengthVector(); }, P};
}
inline auto m_FixedVectorVT() {
return ValueType_match{[](EVT VT) { return VT.isFixedLengthVector(); },
m_Value()};
}
/// Match scalable vector ValueTypes.
template <typename Pattern> inline auto m_ScalableVectorVT(const Pattern &P) {
return ValueType_match{[](EVT VT) { return VT.isScalableVector(); }, P};
}
inline auto m_ScalableVectorVT() {
return ValueType_match{[](EVT VT) { return VT.isScalableVector(); },
m_Value()};
}
/// Match legal ValueTypes based on the information provided by TargetLowering.
template <typename Pattern> inline auto m_LegalType(const Pattern &P) {
return TLI_pred_match{[](const TargetLowering &TLI, SDValue N) {
return TLI.isTypeLegal(N.getValueType());
},
P};
}
// === Patterns combinators ===
template <typename... Preds> struct And {
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
return true;
}
};
template <typename Pred, typename... Preds>
struct And<Pred, Preds...> : And<Preds...> {
Pred P;
And(Pred &&p, Preds &&...preds)
: And<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {
}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
return P.match(Ctx, N) && And<Preds...>::match(Ctx, N);
}
};
template <typename... Preds> struct Or {
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
return false;
}
};
template <typename Pred, typename... Preds>
struct Or<Pred, Preds...> : Or<Preds...> {
Pred P;
Or(Pred &&p, Preds &&...preds)
: Or<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
return P.match(Ctx, N) || Or<Preds...>::match(Ctx, N);
}
};
template <typename... Preds> And<Preds...> m_AllOf(Preds &&...preds) {
return And<Preds...>(std::forward<Preds>(preds)...);
}
template <typename... Preds> Or<Preds...> m_AnyOf(Preds &&...preds) {
return Or<Preds...>(std::forward<Preds>(preds)...);
}
// === Generic node matching ===
template <unsigned OpIdx, typename... OpndPreds> struct Operands_match {
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
// Returns false if there are more operands than predicates;
return N->getNumOperands() == OpIdx;
}
};
template <unsigned OpIdx, typename OpndPred, typename... OpndPreds>
struct Operands_match<OpIdx, OpndPred, OpndPreds...>
: Operands_match<OpIdx + 1, OpndPreds...> {
OpndPred P;
Operands_match(OpndPred &&p, OpndPreds &&...preds)
: Operands_match<OpIdx + 1, OpndPreds...>(
std::forward<OpndPreds>(preds)...),
P(std::forward<OpndPred>(p)) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
if (OpIdx < N->getNumOperands())
return P.match(Ctx, N->getOperand(OpIdx)) &&
Operands_match<OpIdx + 1, OpndPreds...>::match(Ctx, N);
// This is the case where there are more predicates than operands.
return false;
}
};
template <typename... OpndPreds>
auto m_Node(unsigned Opcode, OpndPreds &&...preds) {
return m_AllOf(m_Opc(Opcode), Operands_match<0, OpndPreds...>(
std::forward<OpndPreds>(preds)...));
}
/// Provide number of operands that are not chain or glue, as well as the first
/// index of such operand.
template <bool ExcludeChain> struct EffectiveOperands {
unsigned Size = 0;
unsigned FirstIndex = 0;
explicit EffectiveOperands(SDValue N) {
const unsigned TotalNumOps = N->getNumOperands();
FirstIndex = TotalNumOps;
for (unsigned I = 0; I < TotalNumOps; ++I) {
// Count the number of non-chain and non-glue nodes (we ignore chain
// and glue by default) and retreive the operand index offset.
EVT VT = N->getOperand(I).getValueType();
if (VT != MVT::Glue && VT != MVT::Other) {
++Size;
if (FirstIndex == TotalNumOps)
FirstIndex = I;
}
}
}
};
template <> struct EffectiveOperands<false> {
unsigned Size = 0;
unsigned FirstIndex = 0;
explicit EffectiveOperands(SDValue N) : Size(N->getNumOperands()) {}
};
// === Binary operations ===
template <typename LHS_P, typename RHS_P, bool Commutable = false,
bool ExcludeChain = false>
struct BinaryOpc_match {
unsigned Opcode;
LHS_P LHS;
RHS_P RHS;
BinaryOpc_match(unsigned Opc, const LHS_P &L, const RHS_P &R)
: Opcode(Opc), LHS(L), RHS(R) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
if (sd_context_match(N, Ctx, m_Opc(Opcode))) {
EffectiveOperands<ExcludeChain> EO(N);
assert(EO.Size == 2);
return (LHS.match(Ctx, N->getOperand(EO.FirstIndex)) &&
RHS.match(Ctx, N->getOperand(EO.FirstIndex + 1))) ||
(Commutable && LHS.match(Ctx, N->getOperand(EO.FirstIndex + 1)) &&
RHS.match(Ctx, N->getOperand(EO.FirstIndex)));
}
return false;
}
};
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_BinOp(unsigned Opc, const LHS &L,
const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(Opc, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_c_BinOp(unsigned Opc, const LHS &L,
const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(Opc, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false, true>
m_ChainedBinOp(unsigned Opc, const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false, true>(Opc, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true, true>
m_c_ChainedBinOp(unsigned Opc, const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true, true>(Opc, L, R);
}
// Common binary operations
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_Add(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::ADD, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_Sub(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::SUB, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_Mul(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::MUL, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_And(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::AND, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_Or(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::OR, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_Xor(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::XOR, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_SMin(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::SMIN, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_SMax(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::SMAX, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_UMin(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::UMIN, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_UMax(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::UMAX, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_UDiv(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::UDIV, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_SDiv(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::SDIV, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_URem(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::UREM, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_SRem(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::SREM, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_Shl(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::SHL, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_Sra(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::SRA, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_Srl(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::SRL, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_FAdd(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::FADD, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_FSub(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::FSUB, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, true> m_FMul(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, true>(ISD::FMUL, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_FDiv(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::FDIV, L, R);
}
template <typename LHS, typename RHS>
inline BinaryOpc_match<LHS, RHS, false> m_FRem(const LHS &L, const RHS &R) {
return BinaryOpc_match<LHS, RHS, false>(ISD::FREM, L, R);
}
// === Unary operations ===
template <typename Opnd_P, bool ExcludeChain = false> struct UnaryOpc_match {
unsigned Opcode;
Opnd_P Opnd;
UnaryOpc_match(unsigned Opc, const Opnd_P &Op) : Opcode(Opc), Opnd(Op) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
if (sd_context_match(N, Ctx, m_Opc(Opcode))) {
EffectiveOperands<ExcludeChain> EO(N);
assert(EO.Size == 1);
return Opnd.match(Ctx, N->getOperand(EO.FirstIndex));
}
return false;
}
};
template <typename Opnd>
inline UnaryOpc_match<Opnd> m_UnaryOp(unsigned Opc, const Opnd &Op) {
return UnaryOpc_match<Opnd>(Opc, Op);
}
template <typename Opnd>
inline UnaryOpc_match<Opnd, true> m_ChainedUnaryOp(unsigned Opc,
const Opnd &Op) {
return UnaryOpc_match<Opnd, true>(Opc, Op);
}
template <typename Opnd> inline UnaryOpc_match<Opnd> m_ZExt(const Opnd &Op) {
return UnaryOpc_match<Opnd>(ISD::ZERO_EXTEND, Op);
}
template <typename Opnd> inline UnaryOpc_match<Opnd> m_SExt(const Opnd &Op) {
return UnaryOpc_match<Opnd>(ISD::SIGN_EXTEND, Op);
}
template <typename Opnd> inline UnaryOpc_match<Opnd> m_AnyExt(const Opnd &Op) {
return UnaryOpc_match<Opnd>(ISD::ANY_EXTEND, Op);
}
template <typename Opnd> inline UnaryOpc_match<Opnd> m_Trunc(const Opnd &Op) {
return UnaryOpc_match<Opnd>(ISD::TRUNCATE, Op);
}
// === Constants ===
struct ConstantInt_match {
APInt *BindVal;
explicit ConstantInt_match(APInt *V) : BindVal(V) {}
template <typename MatchContext> bool match(const MatchContext &, SDValue N) {
// The logics here are similar to that in
// SelectionDAG::isConstantIntBuildVectorOrConstantInt, but the latter also
// treats GlobalAddressSDNode as a constant, which is difficult to turn into
// APInt.
if (auto *C = dyn_cast_or_null<ConstantSDNode>(N.getNode())) {
if (BindVal)
*BindVal = C->getAPIntValue();
return true;
}
APInt Discard;
return ISD::isConstantSplatVector(N.getNode(),
BindVal ? *BindVal : Discard);
}
};
/// Match any interger constants or splat of an integer constant.
inline ConstantInt_match m_ConstInt() { return ConstantInt_match(nullptr); }
/// Match any interger constants or splat of an integer constant; return the
/// specific constant or constant splat value.
inline ConstantInt_match m_ConstInt(APInt &V) { return ConstantInt_match(&V); }
struct SpecificInt_match {
APInt IntVal;
explicit SpecificInt_match(APInt APV) : IntVal(std::move(APV)) {}
template <typename MatchContext>
bool match(const MatchContext &Ctx, SDValue N) {
APInt ConstInt;
if (sd_context_match(N, Ctx, m_ConstInt(ConstInt)))
return APInt::isSameValue(IntVal, ConstInt);
return false;
}
};
/// Match a specific integer constant or constant splat value.
inline SpecificInt_match m_SpecificInt(APInt V) {
return SpecificInt_match(std::move(V));
}
inline SpecificInt_match m_SpecificInt(uint64_t V) {
return SpecificInt_match(APInt(64, V));
}
inline SpecificInt_match m_Zero() { return m_SpecificInt(0U); }
inline SpecificInt_match m_One() { return m_SpecificInt(1U); }
inline SpecificInt_match m_AllOnes() { return m_SpecificInt(~0U); }
/// Match true boolean value based on the information provided by
/// TargetLowering.
inline auto m_True() {
return TLI_pred_match{
[](const TargetLowering &TLI, SDValue N) {
APInt ConstVal;
if (sd_match(N, m_ConstInt(ConstVal)))
switch (TLI.getBooleanContents(N.getValueType())) {
case TargetLowering::ZeroOrOneBooleanContent:
return ConstVal.isOne();
case TargetLowering::ZeroOrNegativeOneBooleanContent:
return ConstVal.isAllOnes();
case TargetLowering::UndefinedBooleanContent:
return (ConstVal & 0x01) == 1;
}
return false;
},
m_Value()};
}
/// Match false boolean value based on the information provided by
/// TargetLowering.
inline auto m_False() {
return TLI_pred_match{
[](const TargetLowering &TLI, SDValue N) {
APInt ConstVal;
if (sd_match(N, m_ConstInt(ConstVal)))
switch (TLI.getBooleanContents(N.getValueType())) {
case TargetLowering::ZeroOrOneBooleanContent:
case TargetLowering::ZeroOrNegativeOneBooleanContent:
return ConstVal.isZero();
case TargetLowering::UndefinedBooleanContent:
return (ConstVal & 0x01) == 0;
}
return false;
},
m_Value()};
}
/// Match a negate as a sub(0, v)
template <typename ValTy>
inline BinaryOpc_match<SpecificInt_match, ValTy> m_Neg(const ValTy &V) {
return m_Sub(m_Zero(), V);
}
/// Match a Not as a xor(v, -1) or xor(-1, v)
template <typename ValTy>
inline BinaryOpc_match<ValTy, SpecificInt_match, true> m_Not(const ValTy &V) {
return m_Xor(V, m_AllOnes());
}
/// Match a zext or identity
/// Allows to peek through optional extensions
template <typename Opnd>
inline Or<UnaryOpc_match<Opnd>, Opnd> m_ZExtOrSelf(Opnd &&Op) {
return Or<UnaryOpc_match<Opnd>, Opnd>(m_ZExt(std::forward<Opnd>(Op)),
std::forward<Opnd>(Op));
}
/// Match a sext or identity
/// Allows to peek through optional extensions
template <typename Opnd>
inline Or<UnaryOpc_match<Opnd>, Opnd> m_SExtOrSelf(Opnd &&Op) {
return Or<UnaryOpc_match<Opnd>, Opnd>(m_SExt(std::forward<Opnd>(Op)),
std::forward<Opnd>(Op));
}
/// Match a aext or identity
/// Allows to peek through optional extensions
template <typename Opnd>
inline Or<UnaryOpc_match<Opnd>, Opnd> m_AExtOrSelf(Opnd &&Op) {
return Or<UnaryOpc_match<Opnd>, Opnd>(m_AnyExt(std::forward<Opnd>(Op)),
std::forward<Opnd>(Op));
}
/// Match a trunc or identity
/// Allows to peek through optional truncations
template <typename Opnd>
inline Or<UnaryOpc_match<Opnd>, Opnd> m_TruncOrSelf(Opnd &&Op) {
return Or<UnaryOpc_match<Opnd>, Opnd>(m_Trunc(std::forward<Opnd>(Op)),
std::forward<Opnd>(Op));
}
} // namespace SDPatternMatch
} // namespace llvm
#endif