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