File: IncrementalRA.cpp

package info (click to toggle)
intel-graphics-compiler2 2.24.13-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 113,504 kB
  • sloc: cpp: 812,849; lisp: 288,219; ansic: 102,423; python: 4,010; yacc: 2,588; lex: 1,666; pascal: 318; sh: 162; makefile: 38
file content (482 lines) | stat: -rw-r--r-- 14,975 bytes parent folder | download | duplicates (2)
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
/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2023 Intel Corporation

SPDX-License-Identifier: MIT

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

#include "GraphColor.h"

#include "common/LLVMWarningsPush.hpp"
#include <llvm/ADT/StringMap.h>
#include "common/LLVMWarningsPop.hpp"

using namespace vISA;

IncrementalRA::IncrementalRA(GlobalRA &g)
    : gra(g), kernel(g.kernel), sparseMatrix(g.intfStorage.sparseMatrix),
      sparseIntf(g.intfStorage.sparseIntf) {
  level = kernel.getOptions()->getuInt32Option(vISA_IncrementalRA);
}

void IncrementalRA::reset() {
  selectedRF = G4_RegFileKind::G4_UndefinedRF;
  lrs.clear();
  needIntfUpdate.clear();
  maxDclId = kernel.Declares.size();
  sparseMatrix.clear();
  sparseIntf.clear();
  updateIntfForBB.clear();
  updateIntfForBBValid = false;

  if (isEnabledWithVerification()) {
    def_in.clear();
    def_out.clear();
    use_in.clear();
    use_out.clear();
    use_gen.clear();
    use_kill.clear();
    prevIterRefs.reset();
  }
}

void IncrementalRA::eraseLiveOutsFromIncrementalUpdate() {
  // BuiltInR0 is supposed to be live-out of program, so no need to ever
  // recompute its intf.
  needIntfUpdate.erase(kernel.fg.builder->getBuiltinR0()->getRootDeclare());
  vISA_ASSERT(kernel.fg.builder->getBuiltinR0()->isOutput(),
              "expecting live-out");
  {
    vISA_ASSERT(!kernel.fg.builder->hasScratchSurface() ||
                    kernel.fg.builder->getSpillSurfaceOffset(),
                "expecting valid SSO");
    if (kernel.fg.builder->hasScratchSurface()) {
      needIntfUpdate.erase(kernel.fg.builder->getSpillSurfaceOffset());
      vISA_ASSERT(kernel.fg.builder->getSpillSurfaceOffset()->isOutput(),
                  "expecting live-out");
    }

    vISA_ASSERT(!kernel.fg.builder->hasScratchSurface() ||
                    kernel.fg.builder->hasValidOldA0Dot2(),
                "expecting valid old a0dot2 temp");
    if (kernel.fg.builder->hasValidOldA0Dot2()) {
      needIntfUpdate.erase(kernel.fg.builder->getOldA0Dot2Temp());
      vISA_ASSERT(kernel.fg.builder->getOldA0Dot2Temp()->isOutput(),
                  "expecting live-out");
    }
  }

  vISA_ASSERT(kernel.fg.builder->hasValidSpillFillHeader(),
              "expecting valid spill fill header");
  needIntfUpdate.erase(kernel.fg.builder->getSpillFillHeader());
  vISA_ASSERT(kernel.fg.builder->getSpillFillHeader()->isOutput(),
              "expecting live-out");
}

// Invoked from ctor of latest GraphColor instance.
void IncrementalRA::registerNextIter(G4_RegFileKind rf,
                                     const LivenessAnalysis *liveness,
                                     const Interference *intf) {
  // If incremental RA is not enabled, reset state so we run
  // RA iteration with a clean slate.
  if (!level) {
    reset();
    return;
  }

  // Skip incremental RA for everything but GRF RA for now as we still need to
  // mark candidates in address, flag, scalar spill and cleanup.
  if (rf == G4_RegFileKind::G4_FLAG || rf == G4_RegFileKind::G4_ADDRESS ||
      rf == G4_RegFileKind::G4_SCALAR) {
    reset();
    return;
  }

  // TODO: Add support for dense intf matrix
  if (intf->useDenseMatrix()) {
    reset();
    return;
  }

  if (rf != selectedRF) {
    reset();
    selectedRF = rf;
  }

  eraseLiveOutsFromIncrementalUpdate();

  // Create live-ranges for new vars created in previous GraphColor instance
  if (kernel.Declares.size() > maxDclId) {
    unsigned int index = 0;
    for (auto dcl : kernel.Declares) {
      // No action needed for dcls already present or for alias dcls
      if (index++ < maxDclId || dcl->getAliasDeclare()) {
        continue;
      }

      // New dcl seen, so create live-range for it
      addNewRAVariable(dcl);
    }
  }

  // Reset several fields of LiveRange instances from previous iteration. Fields
  // that are recomputed are the ones reset here.
  for (auto lr : lrs) {
    // Copy over some bit-fields so we can re-initialize them correctly.
    auto isPartialDcl = lr->getIsPartialDcl();
    lr->resetPhyReg();
    lr->setSpilled(false);
    lr->setUnconstrained(false);
    lr->setDegree(0);
    lr->setRefCount(0);
    lr->setBitFieldUnionValue(0);
    lr->setCandidate(true);
    lr->setSpillCost(0);
    lr->resetForbidden();

    if (isPartialDcl)
      lr->setIsPartialDcl();

    lr->initialize();
  }

  collectBBs(liveness);

  resetEdges();

  if (isEnabledWithVerification())
    verify(liveness);

  maxDclId = kernel.Declares.size();

  if (isEnabledWithVerification()) {
    // copy over liveness sets
    copyLiveness(liveness);
    // force compute var refs
    prevIterRefs =
        std::unique_ptr<VarReferences>(new VarReferences(gra.kernel));
    prevIterRefs->setStale();
    prevIterRefs->recomputeIfStale();
  }
}

void IncrementalRA::resetEdges() {
  std::unordered_map<unsigned int, std::vector<unsigned int>> toReset;
  // Reset neighbor list of incremental candidates
  for (auto candidate : needIntfUpdate) {
    auto id = candidate->getRegVar()->getId();
    // Clear neighbor list.

    if (id < sparseMatrix.size()) {
      // Sparse matrix uses upper triangle representation. So that makes it
      // difficult to get list of all neighbors of a node. So we use
      // sparseIntf data structure here that uses lower and upper triangle
      // representation making is easy to remove edges efficiently.

      // First go to each neighbor of node and remove edge.
      for (auto neighbor : sparseIntf[id]) {
        if (neighbor < id) {
          toReset[neighbor].push_back(id);
        }
      }
      sparseMatrix[id].clear();
    }
  }

  for (const auto &item : toReset) {
    for (const auto &neighbor : item.second) {
      sparseMatrix[item.first].reset(neighbor);
    }
  }

  // Now there should be no edge from candidate to/from any other node.

  sparseIntf.clear();
}

void IncrementalRA::collectBBs(const LivenessAnalysis *liveness) {
  vISA_ASSERT(liveness, "expecting valid liveness set");
  VarReferences refs(kernel, true, false);
  updateIntfForBB.clear();

  // In first iteration, needIntfUpdate is empty.
  if (needIntfUpdate.size() == 0) {
    std::for_each(kernel.fg.getBBList().begin(), kernel.fg.getBBList().end(),
                  [&](G4_BB *bb) { updateIntfForBB.insert(bb); });
    updateIntfForBBValid = true;
    return;
  }

  std::unordered_map<unsigned int, G4_BB *> idToBBPtr;
  for (auto bb : kernel.fg.getBBList())
    idToBBPtr[bb->getId()] = bb;

  std::vector<unsigned int> intfCandidates;
  for (auto newVar : needIntfUpdate) {
    // Most spilled variables are locals. We know which BBs they appear in
    // because markBlockLocalVars() is run in RA loop. We take advantage of
    // this pre-computed information for early exit.
    if (gra.isBlockLocal(newVar)) {
      auto *bb = idToBBPtr.at(gra.getBBId(newVar));
      updateIntfForBB.insert(bb);
      continue;
    }

    // It's sufficient to remove spilled vars from
    // all its neighbors. There's no need to redo
    // intf for all BBs they were live in.
    if (newVar->isSpilled() || newVar->getRegVar()->getPhyReg())
      continue;
    intfCandidates.push_back(newVar->getRegVar()->getId());
  }

  for (auto bb : kernel.fg.getBBList()) {
    if (updateIntfForBB.count(bb) > 0)
      continue;
    auto bbId = bb->getId();
    auto liveSet = (liveness->use_in[bbId] & liveness->def_in[bbId]) |
                   (liveness->use_out[bbId] & liveness->def_out[bbId]) |
                   liveness->use_kill[bbId];
    std::vector<bool> liveSetVec;
    liveSetVec.resize(liveness->getNumSelectedVar());
    for (auto setBit : liveSet) {
      liveSetVec[setBit] = true;
    }

    for (auto id : intfCandidates) {
      vISA_ASSERT(lrs[id]->getVar()->isRegAllocPartaker(),
                  "expecting RA candidate");

      if (liveSetVec[id]) {
        updateIntfForBB.insert(bb);
        break;
      }
    }
  }

  updateIntfForBBValid = true;
}

void IncrementalRA::resetPartialDcls() {
  if (!isEnabled())
    return;

  // Partial dcls are removed from kernel.Declares list.
  // We want to remove any interference bits from those dcls here.
  for (auto dcl : kernel.Declares) {
    if (!dcl->getIsPartialDcl())
      continue;

    // Removed partial dcl
    auto id = dcl->getRegVar()->getId();

    if (id < sparseMatrix.size()) {
      // Clear partial dcl edge from all neighbors
      for (auto neighbor : sparseIntf[id]) {
        sparseMatrix[neighbor].reset(id);
      }

      // Clear all neighbors of partial dcl itself
      sparseMatrix[id].clear();
    }

    // Now there should be no edge from partial dcl to/from any other node.
  }
}


void IncrementalRA::copyLiveness(const LivenessAnalysis* liveness) {
  def_in = liveness->def_in;
  def_out = liveness->def_out;
  use_in = liveness->use_in;
  use_out = liveness->use_out;
  use_gen = liveness->use_gen;
  use_kill = liveness->use_kill;
}

std::pair<bool, unsigned int>
IncrementalRA::getIdFromPrevIter(G4_Declare *dcl) {
  unsigned int id = UNDEFINED_VAL;
  auto it = varIdx.find(dcl);
  if (it != varIdx.end())
    id = (*it).second;
  return std::make_pair(it != varIdx.end(), id);
}

void IncrementalRA::recordVarId(G4_Declare *dcl, unsigned int id) {
  varIdx[dcl] = id;
  maxVarIdx = std::max(maxVarIdx, id);
}

void IncrementalRA::addNewRAVariable(G4_Declare *dcl) {
  // Assume new dcl already has a valid dclId.
  //
  // 1. Create new RAVarInfo entry in GlobalRA
  // 2. Create new LiveRange* for dcl
  // 3. Mark variable as partaker in incremental RA

  if (!level || !dcl || dcl->getAliasDeclare())
    return;

  gra.addVarToRA(dcl);

  // This could happen when we're in flag RA and new GRF temps are
  // created for spill/fill.
  if (!LivenessAnalysis::livenessClass(dcl->getRegFile(), selectedRF))
    return;

  auto lr = LiveRange::createNewLiveRange(dcl, gra);
  if (lr) {
    vISA_ASSERT(lrs.size() == lr->getVar()->getId(),
                "mismatch in lr index and regvar id");
    lrs.push_back(lr);
    vISA_ASSERT(lr->getVar()->isRegAllocPartaker(), "expecting RA partaker");
  }

  needIntfUpdate.insert(dcl);
}

void IncrementalRA::markForIntfUpdate(G4_Declare *dcl) {
  if (!level || !dcl || dcl->getAliasDeclare())
    return;

  needIntfUpdate.insert(dcl);
}

void IncrementalRA::skipIncrementalRANextIter() {
  // For passes that rarely executed or for debugging purpose,
  // we can invoke this method to skip running incremental RA
  // in following iteration.
  reset();
}

bool IncrementalRA::verify(const LivenessAnalysis *curLiveness) const {
  // Verify whether candidate set contains:
  // 1. Variables added in previous iteration (eg, spill temp, remat temp),
  // 2. Variables with modified liveness (eg, due to remat)
  //
  // If any candidate is missing then return false. Otherwise return true.
  bool status = true;
  llvm::StringMap<std::string> errorMsgs;

  // If candidate set is empty it means full RA will be run and there was no
  // previous iteration to perform incremental RA.
  if (needIntfUpdate.empty())
    return status;

  // Verify that id of G4_RegVar matches with index in lrs
  for (unsigned int i = 0; i != lrs.size(); ++i) {
    if (i != lrs[i]->getVar()->getId())
      errorMsgs.insert(std::make_pair(lrs[i]->getDcl()->getName(),
                                      "mismatch in lrs index and regvar id"));
  }

  // Verify newly added variables are RA candidates
  unsigned int idx = 0;
  for (auto dcl : kernel.Declares) {
    if (idx++ <= maxDclId)
      continue;

    if (dcl->getAliasDeclare() ||
        !LivenessAnalysis::livenessClass(dcl->getRegFile(), selectedRF))
      continue;

    if (needIntfUpdate.count(dcl) == 0) {
      errorMsgs.insert(std::make_pair(
          dcl->getName(), "Didn't find new variable in candidate list"));
      status = false;
    }
  }

  auto compare = [&](const std::vector<SparseBitVector> &curLivenessSet,
                     const std::vector<SparseBitVector> &oldLivenessSet,
                     std::string name) {
    for (unsigned int bb = 0; bb != kernel.fg.getBBList().size(); ++bb) {
      for (unsigned int i = 0, cnt = oldLivenessSet[bb].count(); i != cnt;
           ++i) {
        bool diff = curLivenessSet[bb].test(i) ^ oldLivenessSet[bb].test(i);
        if (diff && needIntfUpdate.count(lrs[i]->getDcl()) == 0) {
          errorMsgs.insert(std::make_pair(
              lrs[i]->getDcl()->getName(),
              "Variable liveness changed but not found in candidates set"));
          status = false;
        }
      }
    }
    return;
  };

  // Verify liveness delta between current liveness (parameter curLiveness) and
  // liveness data from previous iteration.
  if (def_in.empty() && def_out.empty() && use_in.empty() &&
      use_out.empty() && use_gen.empty() && use_kill.empty())
    return status;

  compare(curLiveness->def_in, def_in, "def_in");
  compare(curLiveness->def_out, def_out, "def_out");
  compare(curLiveness->use_in, use_in, "use_in");
  compare(curLiveness->use_out, use_out, "use_out");
  compare(curLiveness->use_gen, use_gen, "use_gen");
  compare(curLiveness->use_kill, use_kill, "use_kill");

  // Check whether opnds still appear in same instruction as previous iteration
  VarReferences refs(gra.kernel);
  refs.setStale();
  refs.recomputeIfStale();

  for (auto dcl : kernel.Declares) {
    if (!LivenessAnalysis::livenessClass(dcl->getRegFile(), selectedRF))
      continue;

    if (dcl->getAliasDeclare())
      continue;

    if (needIntfUpdate.count(dcl) > 0)
      continue;

    auto oldDefs = prevIterRefs->getDefs(dcl);
    auto oldUses = prevIterRefs->getUses(dcl);
    auto newDefs = refs.getDefs(dcl);
    auto newUses = refs.getUses(dcl);

    if ((oldDefs || newDefs) &&
        ((oldDefs && !newDefs) || (!oldDefs && newDefs) ||
         (*oldDefs != *newDefs))) {
      errorMsgs.insert(std::make_pair(
          dcl->getName(),
          "Variable appears in different defs but it isn't in candidate list"));
      status = false;
    }

    if ((oldUses || newUses) &&
        ((oldUses && !newUses) || (!oldUses && newUses) ||
         (*oldUses != *newUses))) {
      errorMsgs.insert(std::make_pair(
          dcl->getName(),
          "Variable appears in different uses but it isn't in candidate list"));
      status = false;
    }
  }

  for (auto& error : errorMsgs) {
    std::cerr << error.first().str() << " : " << error.second << "\n";
  }

  return status;
}

void IncrementalRA::computeLeftOverUnassigned(const LiveRangeVec &sorted,
                                    const LivenessAnalysis &liveAnalysis) {
  std::unordered_set<G4_Declare *> sortedSet;
  std::unordered_set<G4_Declare *> leftOver;
  for (auto lr : sorted)
    sortedSet.insert(lr->getDcl());

  for (auto dcl : sortedSet) {
    if (unassignedVars.count(dcl) > 0) {
      unassignedVars.erase(dcl);
    } else
      leftOver.insert(dcl);
  }
}