1 /*========================== begin_copyright_notice ============================
2 
3 Copyright (C) 2017-2021 Intel Corporation
4 
5 SPDX-License-Identifier: MIT
6 
7 ============================= end_copyright_notice ===========================*/
8 
9 #include <tuple>
10 
11 #include "ifcvt.h"
12 #include "common.h"
13 #include "BuildIR.h"
14 
15 //#define DEBUG_VERBOSE_ON
16 #if defined(DEBUG_VERBOSE_ON)
17 #define DEBUG(X) do { X; } while (0)
18 #else
19 #define DEBUG(X) do { } while (0)
20 #endif
21 
22 using namespace vISA;
23 
24 namespace {
25 
26     const unsigned FullyConvertibleMaxInsts = 5;
27     const unsigned PartialConvertibleMaxInsts = 3;
28 
29     enum IfConvertKind {
30         FullConvert,
31         // Both 'if' and 'else' (if present) branches could be predicated.
32         PartialIfConvert,
33         // If both 'if' and 'else' branches are present, but only 'if' branch
34         // could be predicated.
35         PartialElseConvert
36         // If both 'if' and 'else' branches are present, but only 'else' branch
37         // could be predicated.
38 
39         // For the later two cases, it's potentially beneficial to convert the
40         // original 'if-else-fi' into 'if-fi' following predicated the other
41         // part. For example,
42         //
43         //  if (pred) {
44         //      BB1;
45         //  } else {
46         //      BB2;
47         //  } endif
48         //
49         // If BB2 cannot be converted (PartialIfConverted), we could convert it
50         // into
51         //
52         //  (pred) BB1;
53         //  if (-pred) {
54         //      BB2;
55         //  } endif
56         //
57         // Ofc, BB1 may need to be very smaller to really improve the final
58         // performance.
59     };
60 
61     // If-convertible.
62     struct IfConvertible {
63         IfConvertKind kind;
64         G4_Predicate *pred;
65         G4_BB *head;
66         G4_BB *succIf;
67         G4_BB *succElse;
68         G4_BB *tail;
69 
IfConvertible__anon71d72e1c0111::IfConvertible70         IfConvertible(IfConvertKind k, G4_Predicate *p, G4_BB *h,
71                       G4_BB *s0, G4_BB *s1, G4_BB *t)
72             : kind(k), pred(p), head(h), succIf(s0), succElse(s1), tail(t) {}
73     };
74 
75     // Trivial if-conversion.
76     class IfConverter {
77         FlowGraph &fg;
78 
79         /// getSinglePredecessor - Get the single predecessor or null
80         /// otherwise.
getSinglePredecessor(G4_BB * BB,G4_BB * If) const81         G4_BB *getSinglePredecessor(G4_BB *BB, G4_BB *If) const {
82             if (BB->Preds.size() != 1) {
83                 if (BB->Preds.size() == 2) {
84                     if (BB->Preds.front() == If)
85                         return BB->Preds.back();
86                     if (BB->Preds.back() == If)
87                         return BB->Preds.front();
88                 }
89                 return nullptr;
90             }
91             return BB->Preds.front();
92         }
93 
94         /// getSingleSuccessor - Get the single successor or null
95         /// otherwise.
getSingleSuccessor(G4_BB * BB,G4_BB * Else) const96         G4_BB *getSingleSuccessor(G4_BB *BB, G4_BB *Else) const {
97             if (BB->Succs.size() != 1) {
98                 if (BB->Succs.size() == 2) {
99                     if (BB->Succs.front() == Else)
100                         return BB->Succs.back();
101                     if (BB->Succs.back() == Else)
102                         return BB->Succs.front();
103                 }
104                 return nullptr;
105             }
106             return BB->Succs.front();
107         }
108 
109         /// getEMaskBits() -
getEMaskBits(unsigned maskOffset,unsigned execSize) const110         unsigned getEMaskBits(unsigned maskOffset, unsigned execSize) const
111         {
112             uint64_t Val = ((uint64_t)1 << execSize) - 1;
113             return (uint32_t) (Val << maskOffset);
114         }
115 
116         /// getnnerMostIf - If the given BB is the head of an innermost IF
117         /// block, return its condition, 'if' branch and 'else' branch (if any)
118         /// and tail, i.e.,
119         ///
120         ///         H                  H
121         ///        / \                / |
122         ///     'if' 'else'   or   'if' |
123         ///        \ /                \ |
124         ///         T                  T
125         /// Otherwise, return all null pointers.
126         ///
127         /// TODO: Add 'goto' support as CFG structurization is currently all or
128         /// nothing.
129         std::tuple<G4_INST * /* last instruction in head, i.e. 'if' */,
130                    G4_BB * /* if */, G4_BB * /* else */, G4_BB * /* tail */>
getInnermostIfBlock(G4_BB * BB) const131         getInnermostIfBlock(G4_BB *BB) const {
132             // Such BB should already be recognized as structural IF statement.
133             if (BB->empty())
134                 return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
135 
136             G4_INST *last = BB->back();
137 
138             // Skip if there's 'NoMask' on that (possible) conditional branch.
139             if (last->getMaskOption() & InstOpt_WriteEnable)
140                 return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
141 
142             // Check whether it's 'if' or 'goto'.
143             G4_opcode op = last->opcode();
144             if (op != G4_if) {
145                 if (op != G4_goto)
146                     return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
147 
148                 // Extra checks for 'goto'.
149 
150                 // Skip if there's no predicate.
151                 if (!last->getPredicate())
152                     return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
153 
154                 // Skip backward goto.
155                 if (last->isFlowControl() && last->asCFInst()->isBackward())
156                     return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
157 
158                 // Skip if there's no exactly 2 successors.
159                 if (BB->Succs.size() != 2)
160                     return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
161             }
162 
163             ASSERT_USER(BB->Succs.size() == 2,
164                         "'if' should have exactly two successors!");
165             ASSERT_USER(last->getPredicate(),
166                         "'if' or 'goto' should be conditional!");
167 
168             G4_BB *s0 = BB->Succs.front();  // if-block
169             G4_BB *s1 = BB->Succs.back();   // else-block
170 
171             G4_BB *t0 = getSingleSuccessor(s0, s1);
172             if (!t0) {
173                 // The innermost 'if' branch should have only one
174                 // successor.
175                 return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
176             }
177             // It also needs to have a single predecessor.
178             if (!getSinglePredecessor(s0, s1))
179                 return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
180 
181             if (t0 == s1) {
182                 // 'if-fi'
183                 DEBUG(std::cerr << "Found an innermost if-fi block at"
184                                 << " BB" << BB->getId()
185                                 << " with branch BB" << s0->getId()
186                                 << " and tail BB" << t0->getId() << '\n');
187                 return std::make_tuple(last, s0, nullptr, t0);
188             }
189 
190             G4_BB *t1 = getSingleSuccessor(s1, s0);
191             if (!t1 || t0 != t1) {
192                 // The innermost 'else' branch should have only one common
193                 // successor from the 'if' branch.
194                 return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
195             }
196             // It also needs to have a single predecessor.
197             if (!getSinglePredecessor(s1, s0))
198                 return std::make_tuple(nullptr, nullptr, nullptr, nullptr);
199 
200             DEBUG(std::cerr << "Found an innermost if-else-fi block at"
201                             << " BB" << BB->getId()
202                             << " with branches {"
203                             << "BB" << s0->getId() << ", BB" << s1->getId()
204                             << "} and tail BB" << t0->getId() << '\n');
205 
206             // 'if-else-fi'
207             return std::make_tuple(last, s0, s1, t0);
208         }
209 
210         /// isPredictable - Check whether the given instruction 'I' could be
211         /// predicated using the predicate from the specified 'if' instruction.
isPredictable(G4_INST * I,G4_INST * ifInst) const212         bool isPredictable(G4_INST *I, G4_INST *ifInst) const {
213             // Already predicated.
214             if (I->getPredicate()) {
215                 // NOTE: It's not the responsibility of this routine to check
216                 // special cases where an already predicated instruction could be
217                 // predicated again.
218                 return false;
219             }
220 
221             // With cond modifier.
222             if (I->getCondMod()) {
223                 // When condition modifier is present, we cannot predicate due to
224                 // 1) the flag register is used as both predicate and condmod flag;
225                 // 2) the update to flag register is guarded by EMask only. The
226                 //    behavior is different from regular predicated insts.
227                 return false;
228             }
229 
230             G4_opcode op = I->opcode();
231             switch (G4_Inst_Table[op].instType) {
232             case InstTypeMov:
233                 switch (op) {
234                 case G4_mov:
235                 case G4_movi:
236                 case G4_smov:
237                     break;
238                 case G4_sel:
239                 case G4_csel:
240                 default:
241                     return false;
242                 }
243                 break;
244             case InstTypeArith:
245             case InstTypeLogic:
246             case InstTypeVector:
247                 break;
248             case InstTypeCompare:
249             case InstTypeFlow:
250             case InstTypeMisc:
251                 // TODO: G4_send, G4_sendc, G4_sends, and G4_sendsc need
252                 // investigating whether they are profitable to be predicated.
253             case InstTypePseudoLogic:
254             case InstTypeReserved:
255             default:
256                 return false;
257             }
258 
259             unsigned maskOpt = I->getMaskOption();
260 
261             // Skip 'NoMask' so far as it requires further investigation.
262             // TODO: When 'NoMask' is present, we could convert them to
263             // (+P.any) or (-P.all) depending on 'if' or 'else' branch. E.g.,
264             //
265             //  (+P) if (16)                    (-P) if (16)
266             //      mov (1) V0 V1 {NoMask}  or      mov (1) V0 V1 {NoMask}
267             //  endif (16)                      endif (16)
268             //
269             //
270             // could be converted into
271             //
272             //  (+P.any) mov (1) V0 V1 {NoMask} or
273             //  (-P.all) mov (1) V0 V1 {NoMask}
274             if (maskOpt & InstOpt_WriteEnable)
275                 return false;
276 
277             unsigned ifMaskOpt = ifInst->getMaskOption();
278             ASSERT_USER((ifMaskOpt & InstOpt_WriteEnable) == 0,
279                         "Unexpected 'NoMask' in 'if' emask.");
280 
281             unsigned maskBits =
282                 getEMaskBits(I->getMaskOffset(), I->getExecSize());
283             unsigned ifMaskBits =
284                 getEMaskBits(ifInst->getMaskOffset(), ifInst->getExecSize());
285             // Skip if emask bits in 'if' cannot cover the one from the given
286             // instruction.
287             if ((~ifMaskBits) & maskBits)
288                 return false;
289 
290             return true;
291         }
292 
293         // isFlagClearingFollowedByGoto - Check if the current instruction is
294         // the flag clearing instruction followed by a goto using that flag.
isFlagClearingFollowedByGoto(G4_INST * I,G4_BB * BB) const295         bool isFlagClearingFollowedByGoto(G4_INST *I, G4_BB *BB) const {
296             // Skip if it's not the second to the last instruction, which
297             // should be a 'goto' with predicate.
298             if (BB->size() <= 1)
299                 return false;
300             auto iter = BB->rbegin();
301             G4_INST *last = *iter++;
302             if (I != *iter)
303                 return false;
304             if (last->opcode() != G4_goto)
305                 return false;
306             G4_Predicate *pred = last->getPredicate();
307             if (!pred)
308                 return false;
309 
310             // Skip non-mov instructions.
311             if (I->opcode() != G4_mov)
312                 return false;
313 
314             // Source should be zero.
315             G4_Operand *src = I->getSrc(0);
316             if (!src->isImm() || !src->asImm()->isZero())
317                 return false;
318 
319             // Dst should be a flag register.
320             G4_Operand *dst = I->getDst();
321             if (!dst->isFlag())
322                 return false;
323 
324             if (dst->getTopDcl() != pred->getTopDcl())
325                 return false;
326 
327             // Dst should be used in that goto instruction as the predicate.
328             return true;
329         }
330 
331         /// getPredictableInsts - Return the total number of instructions if
332         /// all instruction in the given BB is predictable. Otherwise, return
333         /// 0.
getPredictableInsts(G4_BB * BB,G4_INST * ifInst) const334         unsigned getPredictableInsts(G4_BB *BB, G4_INST *ifInst) const {
335             ASSERT_USER(ifInst->opcode() == G4_if ||
336                         ifInst->opcode() == G4_goto,
337                         "Either 'if' or 'goto' is expected!");
338 
339             bool isGoto = (ifInst->opcode() == G4_goto);
340             unsigned sum = 0;
341 
342             for (auto *I : *BB) {
343                 G4_opcode op = I->opcode();
344                 // Ignore G4_label
345                 if (op == G4_label) {
346                     ASSERT_USER(I == BB->front(),
347                                 "'label' should be the first instruction!");
348                     continue;
349                 }
350                 // Ignore G4_else
351                 if (isGoto) {
352                     if (op == G4_join) {
353                         ASSERT_USER(BB->size() > 1 &&
354                                     I == (*++BB->begin()),
355                                     "'join' should be the second instruction!");
356                         continue;
357                     }
358                     if (op == G4_goto) {
359                         ASSERT_USER(I == BB->back(),
360                                     "'goto' should be the last instruction!");
361                         continue;
362                     }
363                     if (isFlagClearingFollowedByGoto(I, BB)) {
364                         ASSERT_USER(BB->size() > 1 &&
365                                     I == (*++BB->rbegin()),
366                                     "flag clearing should be the second to last"
367                                     " instruction!");
368                         continue;
369                     }
370                 } else {
371                     if (op == G4_else) {
372                         ASSERT_USER(I == BB->back(),
373                                     "'else' should be the last instruction!");
374                         continue;
375                     }
376                 }
377                 if (!isPredictable(I, ifInst)) {
378                     return 0;
379                 }
380                 ++sum;
381             }
382 
383             return sum;
384         }
385 
386         /// reversePredicate - Reverse the predicate state.
reversePredicate(G4_Predicate * pred) const387         void reversePredicate(G4_Predicate *pred) const {
388             G4_PredState state = pred->getState();
389             switch (state) {
390             case PredState_Plus:
391                 state = PredState_Minus;
392                 break;
393             case PredState_Minus:
394                 state = PredState_Plus;
395                 break;
396             default:
397                 break;
398             }
399             pred->setState(state);
400         }
401 
402         /// An alternative to c++11 standard 'std::to_string' but does not
403         /// require c++11. 'T' should be restricted to integer types by using
404         /// 'enable_if' to avoid breaking broken c++ support on some platforms.
405         template<typename T>
toString(T v) const406         std::string toString(T v) const {
407             std::ostringstream oss;
408             oss << v;
409             return oss.str();
410         }
411 
412         /// markEmptyBB - Mark the given BB as empty.
markEmptyBB(IR_Builder * IRB,G4_BB * BB) const413         void markEmptyBB(IR_Builder *IRB, G4_BB *BB) const {
414             ASSERT_USER(BB->empty(),
415                         "BB to be marked empty is not empty!");
416 
417             std::string id = "LABEL__EMPTYBB__" + toString(BB->getId());
418             G4_Label *label = IRB->createLabel(id, LABEL_BLOCK);
419             G4_INST *inst =
420                 IRB->createLabelInst(label, false);
421             BB->push_back(inst);
422         }
423 
424         void fullConvert(IfConvertible &);
425         void partialConvert(IfConvertible &);
426 
427     public:
IfConverter(FlowGraph & g)428         IfConverter(FlowGraph &g) : fg(g) {}
429 
430         void analyze(std::vector<IfConvertible> &);
431 
convert(IfConvertible & IC)432         void convert(IfConvertible &IC) {
433             switch (IC.kind) {
434             case FullConvert:
435                 fullConvert(IC);
436                 break;
437             default:
438                 partialConvert(IC);
439                 break;
440             }
441         }
442     };
443 
444 } // End anonymous namespace
445 
analyze(std::vector<IfConvertible> & list)446 void IfConverter::analyze(std::vector<IfConvertible> &list) {
447     for (auto *BB : fg) {
448         G4_INST *ifInst;
449         G4_BB *s0, *s1, *t;
450         std::tie(ifInst, s0, s1, t) = getInnermostIfBlock(BB);
451 
452         if (!ifInst) {
453             // Skip non-innermost if.
454             continue;
455         }
456 
457         if (t && (t->isEndWithCall() || (t->getLastOpcode() ==  G4_return)))
458         {
459             continue;
460         }
461 
462         G4_Predicate *pred = ifInst->getPredicate();
463 
464         unsigned n0 = getPredictableInsts(s0, ifInst);
465         unsigned n1 = s1 ? getPredictableInsts(s1, ifInst) : 0;
466 
467         if (s0 && s1) {
468             if (((n0 > 0) && (n0 < FullyConvertibleMaxInsts)) &&
469                 ((n1 > 0) && (n1 < FullyConvertibleMaxInsts))) {
470                 // Both 'if' and 'else' are profitable to be if-converted.
471                 list.push_back(
472                     IfConvertible(FullConvert, pred, BB, s0, s1, t));
473             } else if ((n0 > 0) && (n0 < PartialConvertibleMaxInsts)) {
474                 // Only 'if' is profitable to be converted.
475                 list.push_back(
476                     IfConvertible(PartialIfConvert, pred, BB, s0, s1, t));
477             } else if ((n1 > 0) && (n1 < PartialConvertibleMaxInsts)) {
478                 // Only 'else' is profitable to be converted.
479                 list.push_back(
480                     IfConvertible(PartialElseConvert, pred, BB, s0, s1, t));
481             }
482         } else if ((n0 > 0) && (n0 < FullyConvertibleMaxInsts)) {
483             list.push_back(
484                 IfConvertible(FullConvert, pred, BB, s0, nullptr, t));
485         }
486     }
487 }
488 
489 // Combining GCC 4.9.0 and libcxx 11.01, incorrect code is generated on two
490 // consecutive `pop_front()` on std::list. Add `volatile` to prevent incorrect
491 // code generation during over optimization.
492 #if (defined(ANDROID) && !(defined(__clang__) || defined(__INTEL_COMPILER)) && \
493      (defined(__GNUC__) || defined(__GNUG__)) && \
494      __GNUC__ == 4 && __GNUC_MINOR__ == 9 && __GNUC_PATCHLEVEL__ == 0 && \
495      defined(_LIBCPP_VERSION) && _LIBCPP_VERSION == 1101)
496 #define ANDROID_WORKAROUND volatile
497 #else
498 #define ANDROID_WORKAROUND
499 #endif
500 
fullConvert(IfConvertible & IC)501 void IfConverter::fullConvert(IfConvertible &IC) {
502     G4_Predicate &pred = *IC.pred;
503     G4_BB *head = IC.head;
504     G4_BB * ANDROID_WORKAROUND tail = IC.tail;
505     G4_BB *s0 = IC.succIf;
506     G4_BB *s1 = IC.succElse;
507 
508     INST_LIST_ITER pos = std::prev(head->end());
509     G4_opcode op = (*pos)->opcode();
510     ASSERT_USER(op == G4_if || op == G4_goto,
511                 "Convertible if is not started with 'if' or 'goto'!");
512     bool isGoto = (op == G4_goto);
513 
514     // Skip tail merging if tail has other incoming edge(s).
515     bool doTailMerging = (tail->Preds.size() == 2);
516 
517     // forward goto's behavior is platform dependent
518     bool needReversePredicateForGoto = (isGoto && fg.builder->gotoJumpOnTrue());
519     // Merge predicated 'if' into header.
520     for (/* EMPTY */; !s0->empty(); s0->pop_front()) {
521         auto I = s0->front();
522         G4_opcode op = I->opcode();
523         if (op == G4_label)
524             continue;
525         if (isGoto && s1) {
526             // Have both s0 and s1, goto in s0 can be
527             // removed always.
528             if (op == G4_goto)
529                 continue;
530             if (isFlagClearingFollowedByGoto(I, s0))
531                 continue;
532         } else {
533             if (op == G4_else)
534                 continue;
535             // If there is a goto, its target must be tail.
536             // If merging is done, we must remove goto as its
537             // target is gone.
538             if (doTailMerging && op == G4_goto)
539                 continue;
540         }
541         /* Predicate instructions if it's not goto-style or it's not
542          * neither goto nor its flag clearing instruction */
543         if (!isGoto ||
544             !(op == G4_goto || isFlagClearingFollowedByGoto(I, s0))) {
545             // Negative predicate instructions if needed.
546             if (needReversePredicateForGoto) {
547                 G4_Predicate *negPred = fg.builder->createPredicate(pred);
548                 reversePredicate(negPred);
549                 I->setPredicate(negPred);
550             } else {
551                 I->setPredicate(fg.builder->createPredicate(pred));
552             }
553         }
554         head->insertBefore(pos, I);
555     }
556     markEmptyBB(fg.builder, s0);
557     // Merge predicated 'else' into header.
558     if (s1) {
559         // Reverse the flag controling whether the predicate needs reversing.
560         needReversePredicateForGoto = !needReversePredicateForGoto;
561         for (/* EMPTY */; !s1->empty(); s1->pop_front()) {
562             auto I = s1->front();
563             G4_opcode op = I->opcode();
564             if (op == G4_label)
565                 continue;
566             if (op == G4_join)
567                 continue;
568             // If there is a goto, its target must be tail.
569             // If merging is done, we must remove goto as its
570             // target is gone.
571             if (doTailMerging && op == G4_goto)
572                 continue;
573             /* Predicate instructions if it's not goto-style or it's not
574              * neither goto nor its flag clearing instruction */
575             if (!isGoto ||
576                 !(op == G4_goto || isFlagClearingFollowedByGoto(I, s1))) {
577                 // Negative predicate instructions if needed.
578                 if (needReversePredicateForGoto) {
579                     G4_Predicate *negPred = fg.builder->createPredicate(pred);
580                     reversePredicate(negPred);
581                     I->setPredicate(negPred);
582                 } else {
583                     I->setPredicate(fg.builder->createPredicate(pred));
584                 }
585             }
586             head->insertBefore(pos, I);
587         }
588         markEmptyBB(fg.builder, s1);
589     }
590 
591     // Remove 'if' instruction in head.
592     head->erase(pos);
593 
594     if (!doTailMerging)
595         return;
596 
597     // Remove 'label' and 'endif'/'join' instructions in tail.
598     ASSERT_USER(tail->front()->opcode() == G4_label,
599                 "BB is not started with 'label'!");
600     tail->pop_front();
601     ASSERT_USER(tail->front()->opcode() == G4_endif ||
602                 tail->front()->opcode() == G4_join,
603                 "Convertible if is not ended with 'endif'!");
604     tail->pop_front();
605     // Merge head and tail to get more code scheduling chance.
606     head->splice(head->end(), tail);
607     markEmptyBB(fg.builder, tail);
608 }
609 
partialConvert(IfConvertible & IC)610 void IfConverter::partialConvert(IfConvertible &IC) {
611     // TODO: Add partial if-conversion support.
612 }
613 
runIfCvt(FlowGraph & fg)614 void runIfCvt(FlowGraph &fg) {
615     IfConverter converter(fg);
616 
617     std::vector<IfConvertible> ifList;
618     converter.analyze(ifList);
619 
620     // FIXME: The convertible 'if's are traversed with assumption that BBs are
621     // already ordered in topological order so that, once we merge head & tail
622     // blocks, we won't break the remaining convertible 'if's to be converted.
623     for (auto II = ifList.rbegin(), IE = ifList.rend(); II != IE; ++II) {
624         converter.convert(*II);
625     }
626 
627     // Run additional transforms from 'sel' to 'mov' if one of the source
628     // operands is equal to the destination.
629     for (G4_BB *BB: fg) {
630         for (auto BI = BB->begin(), BE = BB->end(); BI != BE; ++BI) {
631             G4_INST *I = *BI;
632             if (I->opcode() != G4_sel || !I->getPredicate() || I->getCondMod())
633                 continue;
634 
635             auto compareOperand =
636                 [](G4_DstRegRegion* A, G4_Operand* B, unsigned ExecSize)
637                 -> G4_CmpRelation {
638                 G4_CmpRelation Res = A->compareOperand(B);
639                 if ((A->isAreg() && A->isFlag()) || (B->isAreg() && B->isFlag()) ||
640                     (A->isAreg() && A->isAccReg()) || (B->isAreg() && B->isAccReg()))
641                 {
642                     // compareOperand() not working for flag physical registers.
643                     return Rel_disjoint;
644                 }
645                 if (Res != Rel_interfere)
646                     return Res;
647                 if (A->getRegAccess() != IndirGRF ||
648                     B->getRegAccess() != IndirGRF)
649                     return Res;
650                 if (A->getHorzStride() != 1)
651                     return Res;
652                 // Extra check if both are indirect register accesses.
653                 G4_VarBase *BaseA = A->getBase();
654                 G4_VarBase *BaseB = B->getBase();
655                 if (!BaseA || !BaseB || BaseA != BaseB || !BaseA->isRegVar())
656                     return Res;
657                 if (!B->isSrcRegRegion())
658                     return Res;
659                 G4_SrcRegRegion *S = B->asSrcRegRegion();
660                 if (!S->getRegion()->isContiguous(ExecSize))
661                     return Res;
662                 if (A->getRegOff() != S->getRegOff() ||
663                     A->getSubRegOff() != S->getSubRegOff())
664                     return Res;
665                 if (A->getAddrImm() != S->getAddrImm())
666                     return Res;
667                 return Rel_eq;
668             };
669 
670             unsigned ExSz = I->getExecSize();
671             G4_DstRegRegion *Dst = I->getDst();
672             G4_Operand *Src0 = I->getSrc(0);
673             G4_Operand *Src1 = I->getSrc(1);
674             int OpndIdx = -1;
675             if (compareOperand(Dst, Src0, ExSz) == Rel_eq &&
676                 Src0->isSrcRegRegion() &&
677                 Src0->asSrcRegRegion()->getModifier() == Mod_src_undef)
678                 OpndIdx = 0;
679             else if (compareOperand(Dst, Src1, ExSz) == Rel_eq &&
680                      Src1->isSrcRegRegion() &&
681                      Src1->asSrcRegRegion()->getModifier() == Mod_src_undef)
682                 OpndIdx = 1;
683             if (OpndIdx >= 0) {
684                 // If dst is equal to one of operands of 'sel', that
685                 // 'sel' could be transformed into a predicated 'mov',
686                 // i.e.,
687                 //
688                 // transforms
689                 //
690                 //  (+p) sel dst, src0, src1
691                 //
692                 // into
693                 //
694                 //  (+p) mov dst, src0   if dst == src1
695                 //
696                 // or
697                 //
698                 //  (-p) mov dst, src1   if dst == src0
699                 //
700                 if (OpndIdx == 0) {
701                     // Inverse predicate.
702                     G4_Predicate *Pred = I->getPredicate();
703                     G4_PredState State = Pred->getState();
704                     State = (State == PredState_Plus) ? PredState_Minus
705                                                       : PredState_Plus;
706                     Pred->setState(State);
707                     // Swap source operands.
708                     I->setSrc(Src1, 0);
709                     I->setSrc(Src0, 1);
710                 }
711                 I->setOpcode(G4_mov);
712                 I->setSrc(nullptr, 1);
713             }
714         }
715     }
716 }
717 
718 // vim:ts=4:sw=4:et:
719