nv50/ir: only unspill once ahead of a group of instructions

We already semi-did this but the list of uses as unsorted, so it was
unreliable. Sort the uses by bb and serial, and don't unspill for each
instruction in a sequence. (And also don't unspill multiple times for a
single instruction that uses the value in question multiple times.)

This causes a minor reduction in generated instructions for shader-db
(as few programs spill) but more importantly it brings determinism to
each run's output.

On SM10:

total instructions in shared programs : 6387945 -> 6379359 (-0.13%)
total gprs used in shared programs    : 728544 -> 728544 (0.00%)
total local used in shared programs   : 9904 -> 9904 (0.00%)

                local        gpr       inst      bytes
    helped           0           0         322         322
      hurt           0           0           0           0

Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu>
This commit is contained in:
Ilia Mirkin 2015-12-06 23:25:54 -05:00
parent 0f647bd65b
commit 99581ca393
1 changed files with 20 additions and 5 deletions

View File

@ -23,6 +23,7 @@
#include "codegen/nv50_ir.h"
#include "codegen/nv50_ir_target.h"
#include <algorithm>
#include <stack>
#include <limits>
#if __cplusplus >= 201103L
@ -1649,6 +1650,13 @@ SpillCodeInserter::unspill(Instruction *usei, LValue *lval, Value *slot)
return lval;
}
static bool
value_cmp(ValueRef *a, ValueRef *b) {
Instruction *ai = a->getInsn(), *bi = b->getInsn();
if (ai->bb != bi->bb)
return ai->bb->getId() < bi->bb->getId();
return ai->serial < bi->serial;
}
// For each value that is to be spilled, go through all its definitions.
// A value can have multiple definitions if it has been coalesced before.
@ -1682,18 +1690,25 @@ SpillCodeInserter::run(const std::list<ValuePair>& lst)
LValue *dval = (*d)->get()->asLValue();
Instruction *defi = (*d)->getInsn();
// Sort all the uses by BB/instruction so that we don't unspill
// multiple times in a row, and also remove a source of
// non-determinism.
std::vector<ValueRef *> refs(dval->uses.begin(), dval->uses.end());
std::sort(refs.begin(), refs.end(), value_cmp);
// Unspill at each use *before* inserting spill instructions,
// we don't want to have the spill instructions in the use list here.
while (!dval->uses.empty()) {
ValueRef *u = *dval->uses.begin();
for (std::vector<ValueRef*>::const_iterator it = refs.begin();
it != refs.end(); ++it) {
ValueRef *u = *it;
Instruction *usei = u->getInsn();
assert(usei);
if (usei->isPseudo()) {
tmp = (slot->reg.file == FILE_MEMORY_LOCAL) ? NULL : slot;
last = NULL;
} else
if (!last || usei != last->next) { // TODO: sort uses
tmp = unspill(usei, dval, slot);
} else {
if (!last || (usei != last->next && usei != last))
tmp = unspill(usei, dval, slot);
last = usei;
}
u->set(tmp);