File: GenXTidyControlFlow.cpp

package info (click to toggle)
intel-graphics-compiler 1.0.12504.6-1%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 83,912 kB
  • sloc: cpp: 910,147; lisp: 202,655; ansic: 15,197; python: 4,025; yacc: 2,241; lex: 1,570; pascal: 244; sh: 104; makefile: 25
file content (341 lines) | stat: -rw-r--r-- 12,952 bytes parent folder | download
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
/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

//
/// GenXTidyControlFlow
/// -------------------
///
/// This pass tidies the control flow in the following ways:
///
/// 1. It removes empty blocks (a block is empty if all it contains is an
///    unconditional branch), and thus reduces branch chains in the generated
///    code.  It is needed because often a block inserted by critical edge
///    splitting is not needed for any phi copies.
///
/// 2. It reorders blocks to increase fallthrough generally, and specifically
///    to ensure that SIMD CF goto and join have the required structure: the
///    "false" successor must be fallthrough and the "true" successor must be
///    forward. (The '"true" successor must be forward' requirement is a vISA
///    requirement, because vISA goto/join does not specify JIP, and the
///    finalizer reconstructs it on this assumption.)
///
/// 3. fixGotoOverBranch: The pass spots where there is a SIMD CF goto over an
///    unconditional branch, and turns the combination into a backwards goto.
///
///    After reordering blocks, we know that any simd goto has its "false"
///    successor as the following block. If all of the following are true:
///
///    a. its "true" successor just branches over that same block;
///
///    b. that block contains only an unconditional branch;
///
///    c. the UIP of the goto (the join whose RM it updates) is the same as the
///       "true" successor;
///
///    d. the goto condition is not constant 0 (this condition is because we
///       cannot represent a backwards simd goto with this, and it is too late
///       to allocate it a register);
///
///    then we have the end of a simd do..while loop, and we can optimize to a
///    backwards simd goto.
///
///    We represent a backwards simd goto in the IR by having the "true"
///    successor as the following block. GenXCisaBuilder can then spot that it
///    is a backwards simd goto, and it needs its condition inverting.
///
/// 4. Ensure that there is a single return block and it is the last block.
///    These are required by the vISA's structurizer.
///
//===----------------------------------------------------------------------===//
#include "GenX.h"
#include "GenXBaling.h"
#include "GenXGotoJoin.h"
#include "GenXLiveness.h"
#include "GenXModule.h"
#include "GenXNumbering.h"
#include "GenXSubtarget.h"
#include "GenXTargetMachine.h"
#include "GenXUtil.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/PassRegistry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvmWrapper/IR/IntrinsicInst.h"
#include "Probe/Assertion.h"

#define DEBUG_TYPE "GENX_TIDYCONTROLFLOW"

using namespace llvm;
using namespace genx;

/***********************************************************************
 * GenXTidyControlFlow pass declaration
 */
namespace {
  class GenXTidyControlFlow : public FunctionPass {
    const GenXSubtarget *ST = nullptr;
    GenXLiveness *Liveness = nullptr;
    bool Modified = false;
  public:
    static char ID;
    explicit GenXTidyControlFlow() : FunctionPass(ID), Modified(false) {}
    StringRef getPassName() const override { return "GenX tidy control flow"; }

    void getAnalysisUsage(AnalysisUsage &AU) const override {
      AU.addPreserved<GenXModule>();
      AU.addPreserved<GenXGroupBaling>();
      AU.addPreserved<GenXLivenessWrapper>();
      AU.addPreserved<GenXNumbering>();
      AU.addPreserved<FunctionGroupAnalysis>();
      AU.addRequired<FunctionGroupAnalysis>();
      AU.addRequired<GenXLivenessWrapper>();
      AU.addRequired<LoopInfoWrapperPass>();
      AU.addRequired<TargetPassConfig>();
    }

    bool runOnFunction(Function &F) override;
    // createPrinterPass : get a pass to print the IR, together with the GenX
    // specific analyses
    Pass *createPrinterPass(raw_ostream &O,
                            const std::string &Banner) const override {
      return createGenXPrinterPass(O, Banner);
    }

  private:
    void removeEmptyBlocks(Function *F);
    void reorderBlocks(Function *F);
    void fixGotoOverBranch(Function *F);
    void fixReturns(Function *F);
  };
} // end anonymous namespace.

char GenXTidyControlFlow::ID = 0;

namespace llvm {
void initializeGenXTidyControlFlowPass(PassRegistry &);
}

INITIALIZE_PASS_BEGIN(GenXTidyControlFlow,
                      "GenXTidyControlFlow",
                      "GenXTidyControlFlow", false, false)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
INITIALIZE_PASS_DEPENDENCY(GenXLivenessWrapper)
INITIALIZE_PASS_END(GenXTidyControlFlow, "GenXTidyControlFlow",
                    "GenXTidyControlFlow", false, false)

FunctionPass *llvm::createGenXTidyControlFlowPass() {
  initializeGenXTidyControlFlowPass(*PassRegistry::getPassRegistry());
  return new GenXTidyControlFlow;
}

/***********************************************************************
 * moveDbgBeforeBlockRemoval : Move debug intrinsics from basic block
 *    before erasing it
 */
static void moveDbgBeforeBlockRemoval(BasicBlock *BB, Instruction *InsertBefore,
                                      bool MakeUndef = false) {

  while (auto *DBG = dyn_cast<llvm::DbgVariableIntrinsic>(BB->begin())) {
    DBG->moveBefore(InsertBefore);
    if (MakeUndef)
      IGCLLVM::setDbgVariableLocationToUndef(DBG);
  }
  IGC_ASSERT_MESSAGE(BB->front().isTerminator(), "Expected that only terminator instruction remains");
}

/***********************************************************************
 * GenXTidyControlFlow::runOnFunction : process a function
 */
bool GenXTidyControlFlow::runOnFunction(Function &F)
{
  ST = &getAnalysis<TargetPassConfig>()
            .getTM<GenXTargetMachine>()
            .getGenXSubtarget();
  auto *FGA = &getAnalysis<FunctionGroupAnalysis>();
  Liveness =
      &(getAnalysis<GenXLivenessWrapper>().getFGPassImpl(FGA->getAnyGroup(&F)));
  Modified = false;
  removeEmptyBlocks(&F);
  reorderBlocks(&F);
  fixGotoOverBranch(&F);
  fixReturns(&F);
  return Modified;
}

/***********************************************************************
 * removeEmptyBlocks
 */
void GenXTidyControlFlow::removeEmptyBlocks(Function *F)
{
  Function::iterator fi = F->begin(), fe = F->end();
  // Don't consider the entry block.
  for (++fi; fi != fe; ) {
    BasicBlock *BB = &*fi;
    // Increment iterator here as we may be removing this block.
    ++fi;
    // FIXME: By claiming preserving liveness, we cannot remove phi(s) in empty
    // blocks. Need to adjust the pass order if such phi(s) really need
    // eliminating.
    if (dyn_cast<PHINode>(&BB->front()))
      continue;
    BranchInst *BI = dyn_cast<BranchInst>(BB->getFirstNonPHIOrDbg());
    if (!BI || !BI->isUnconditional())
      continue;
    // Do not remove BB if it has more than one predecessor.
    if (!BB->hasOneUse())
      continue;
    // Check if this is a critical edge splitting block whose predecessor is
    // the "false" leg of a goto/join. In that case we do not remove the
    // block, as reorderBlocks below may rely on it to ensure that the "false"
    // successor of a goto/join can be made fallthrough.
    if (BB->hasOneUse()
        && BB->use_begin()->getOperandNo() == 1 /*false successor*/
        && GotoJoin::isBranchingGotoJoinBlock(cast<Instruction>(
            BB->use_begin()->getUser())->getParent())) {
      LLVM_DEBUG(dbgs() << "removeEmptyBlocks: not removing " << BB->getName() << "\n");
      continue;
    }
    // We are removing this block. First adjust phi nodes in the successor.
    auto Succ = BI->getSuccessor(0);
    bool HasOnePred = Succ->hasNPredecessors(1);
    adjustPhiNodesForBlockRemoval(Succ, BB);
    // Change all of BB's uses to use its successor instead.
    IGC_ASSERT_MESSAGE(BB->getSinglePredecessor() != BB, "self loop");
    BB->replaceAllUsesWith(BI->getSuccessor(0));
    moveDbgBeforeBlockRemoval(BB, Succ->getFirstNonPHI(), !HasOnePred);
    BI->eraseFromParent();
    BB->eraseFromParent();
    Modified = true;
  }
}

/***********************************************************************
 * reorderBlocks : reorder blocks to increase fallthrough, and specifically
 *    to satisfy the requirements of SIMD control flow
 */
void GenXTidyControlFlow::reorderBlocks(Function *F)
{
  LoopInfo& LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
  if (LI.empty())
    LayoutBlocks(*F);
  else
    LayoutBlocks(*F, LI);
  Modified = true;
}

/***********************************************************************
 * fixGotoOverBranch : fix a (simd) goto over a branch into a backwards goto
 *
 * See the comment at the top of the file.
 */
void GenXTidyControlFlow::fixGotoOverBranch(Function *F)
{
  for (auto fi = F->begin(), fe = F->end(); fi != fe; ++fi) {
    BasicBlock *BB = &*fi;
    auto Goto = GotoJoin::isGotoBlock(BB);
    if (!Goto)
      continue;
    auto Br = cast<BranchInst>(BB->getTerminator());
    if (!Br->isConditional())
      continue;
    // We have a block ending with a conditional branch that is a goto.
    // Now check whether it branches over an unconditional branch.
    auto Succ = BB->getNextNode();
    if (!Succ || !Succ->hasOneUse())
      continue;
    if (Br->getSuccessor(0)->getPrevNode() != Succ)
      continue;
    auto SuccBr = dyn_cast<BranchInst>(Succ->getFirstNonPHIOrDbg());
    if (!SuccBr || SuccBr->isConditional())
      continue;
    // The goto branches over just an unconditional branch.
    // Check whether its UIP is the same as the branch target.
    auto Join = GotoJoin::findJoin(Goto);
    if (!Join || Join->getParent() != Br->getSuccessor(0))
      continue;
    // Check that the goto condition is not constant.
    if (isa<Constant>(Goto->getOperand(2)))
      continue;
    // Change the goto's "false" successor to the target of the unconditional
    // branch, and remove Succ so the goto's "true" successor becomes
    // fallthrough. This then represents a backward goto.
    adjustPhiNodesForBlockRemoval(SuccBr->getSuccessor(0), Succ);
    Br->setSuccessor(1, SuccBr->getSuccessor(0));
    moveDbgBeforeBlockRemoval(Succ, Br);
    Succ->eraseFromParent();
    Modified = true;
  }
}

/******************************************************************************
 * fixReturns : only keep a single return block and ensure it is the last block
 * of a function.
 */
void GenXTidyControlFlow::fixReturns(Function *F) {
  // Loop over all of the blocks in a function, tracking all of the blocks
  // that return.
  SmallVector<BasicBlock *, 16> ReturningBlocks;
  for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I)
    if (isa<ReturnInst>(I->getTerminator()))
      ReturningBlocks.push_back(&*I);

  // We need to insert a new basic block into the function,
  // add a PHI nodes (if the function returns values), and convert
  // all of the return instructions into unconditional branches.
  //
  if (ReturningBlocks.size() == 1) {
    BasicBlock *RetBlock = ReturningBlocks.front();
    BasicBlock *LastBlock = &F->back();
    if (LastBlock != RetBlock) {
      RetBlock->moveAfter(LastBlock);
      Modified = true;
    }
  } else if (ReturningBlocks.size() > 1) {
    BasicBlock *NewRetBlock =
        BasicBlock::Create(F->getContext(), "UnifiedReturnBlock", F);
    PHINode *PN = nullptr;
    if (F->getReturnType()->isVoidTy())
      ReturnInst::Create(F->getContext(), nullptr, NewRetBlock);
    else {
      // If the function doesn't return void, add a PHI node to the block.
      PN = PHINode::Create(F->getReturnType(), ReturningBlocks.size(),
                           "UnifiedRetVal");
      NewRetBlock->getInstList().push_back(PN);
      ReturnInst::Create(F->getContext(), PN, NewRetBlock);
    }

    if (PN) {
      auto *TermVal = ReturningBlocks.front()->getTerminator()->getOperand(0);
      if (GenXIntrinsic::isReadWritePredefReg(TermVal))
        TermVal = cast<Instruction>(TermVal)->getOperand(1);
      Liveness->setLiveRange(SimpleValue(PN), Liveness->getLiveRange(TermVal));
    }
    // Loop over all of the blocks, replacing the return instruction with an
    // unconditional branch.
    for (auto BB : ReturningBlocks) {
      // Add an incoming element to the PHI node for every return instruction
      // that is merging into this new block.
      if (PN)
        PN->addIncoming(BB->getTerminator()->getOperand(0), BB);

      BB->getInstList().pop_back(); // Remove the return inst.
      BranchInst::Create(NewRetBlock, BB);
    }
    Modified = true;
  }
}