mirror of
git://c9x.me/qbe.git
synced 2026-04-05 09:59:47 +00:00
More or less as proposed in its ninth iteration with the
addition of a gcmmove() functionality to restore coherent
local schedules.
Changes since RFC 8:
Features:
- generalization of phi 1/0 detection
- collapse linear jmp chains before GVN; simplifies if-graph
detection used in 0/non-0 value inference and if-elim...
- infer 0/non-0 values from dominating blk jnz; eliminates
redundant cmp eq/ne 0 and associated jnz/blocks, for example
redundant null pointer checks (hare codebase likes this)
- remove (emergent) empty if-then-else graphlets between GVN and
GCM; improves GCM instruction placement, particularly cmps.
- merge %addr =l add %addr1, N sequences - reduces tmp count,
register pressure.
- squash consecutive associative ops with constant args, e.g.
t1 = add t, N ... t2 = add t2, M -> t2 = add t, N+M
Bug Fixes:
- remove "cmp eq/ne of non-identical RCon's " in copyref().
RCon's are not guaranteed to be dedup'ed, and symbols can
alias.
Codebase:
- moved some stuff into cfg.c including blkmerge()
- some refactoring in gvn.c
- simplification of reassoc.c - always reassoc all cmp ops
and Kl add %t, N. Better on coremark, smaller codebase.
- minor simplification of movins() - use vins
Testing - standard QBE, cproc, hare, harec, coremark
[still have Rust build issues with latest roland]
Benchmark
- coremark is ~15%+ faster than master
- hare "HARETEST_INCLUDE='slow' make check" ~8% faster
(crypto::sha1::sha1_1gb is biggest obvious win - ~25% faster)
Changes since RFC 7:
Bug fixes:
- remove isbad4gcm() in GVN/GCM - it is unsound due to different state
at GVN vs GCM time; replace with "reassociation" pass after GCM
- fix intra-blk use-before-def after GCM
- prevent GVN from deduping trapping instructions cos GCM will not
move them
- remove cmp eq/ne identical arg copy detection for floating point, it
is not valid for NaN
- fix cges/cged flagged as commutative in ops.h instead of cnes/cned
respectively; just a typo
Minor features:
- copy detection handles cmp le/lt/ge/gt with identical args
- treat (integer) div/rem by non-zero constant as non-trapping
- eliminate add N/sub N pairs in copy detection
- maintain accurate tmp use in GVN; not strictly necessary but enables
interim global state sanity checking
- "reassociation" of trivial constant offset load/store addresses, and
cmp ops with point-of-use in pass after GCM
- normalise commutative op arg order - e.g. op con, tmp -> op tmp, con
to simplify copy detection and GVN instruction dedup
Codebase:
- split out core copy detection and constant folding (back) out into
copy.c, fold.c respectively; gvn.c was getting monolithic
- generic support for instruction moving in ins.c - used by GCM and
reassoc
- new reassociation pass in reassoc.c
- other minor clean-up/refactor
Changes since RFC 6:
- More ext elimination in GVN by examination of def and use bit width
- elimination of redundant and mask by bit width examination
- Incorporation of Song's patch
Changes since RFC 5:
- avoidance of "bad" candidates for GVN/GCM - trivial address offset
calculations, and comparisons
- more copy detection mostly around boolean values
- allow elimination of unused load, alloc, trapping instructions
- detection of trivial boolean v ? 1 : 0 phi patterns
- bug fix for (removal of) "chg" optimisation in ins recreation - it
was missing removal of unused instructions in some cases
ifelim() between GVN and GCM; deeper nopunused()
102 lines
2.0 KiB
C
102 lines
2.0 KiB
C
#include "all.h"
|
|
|
|
static void
|
|
ireassoc(Fn *fn, Blk *b, Ins *i, uint *pnim, InsMov **pim)
|
|
{
|
|
Blk *b2;
|
|
Tmp *t;
|
|
Use *u;
|
|
int64_t v;
|
|
int x;
|
|
|
|
assert(b->ins <= i && i < &b->ins[b->nins]);
|
|
if (!iscmp(i->op, &x, &x))
|
|
if (!iskladdcon(fn, i, &v))
|
|
return;
|
|
|
|
assert(rtype(i->to) == RTmp);
|
|
t = &fn->tmp[i->to.val];
|
|
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
|
if (u->type == UPhi)
|
|
continue;
|
|
if (u->type == UIns)
|
|
if (INRANGE(u->u.ins->op, Oarg, Ocall))
|
|
continue;
|
|
b2 = fn->rpo[u->bid];
|
|
vgrow(pim, ++(*pnim));
|
|
(*pim)[(*pnim)-1] = (InsMov){
|
|
.from = {.bid = b->id, .insn = i-b->ins},
|
|
.to = {
|
|
.bid = u->bid,
|
|
.insn = u->type == UJmp ? b2->nins : u->u.ins-b2->ins
|
|
}};
|
|
}
|
|
}
|
|
|
|
static void
|
|
trealloc(Fn *fn, Blk *b, Ins *i)
|
|
{
|
|
Ins *i2;
|
|
Ref r;
|
|
|
|
assert(b->ins <= i && i < &b->ins[b->nins]);
|
|
r = newtmp("rea", i->cls, fn);
|
|
if (i < &b->ins[b->nins-1]) {
|
|
i2 = i+1;
|
|
/* special case of both args of target instruction reassociated */
|
|
if (!req(i->to, i2->arg[0]) && !req(i->to, i2->arg[1])) {
|
|
assert(i < &b->ins[b->nins-2]);
|
|
i2 = i+2;
|
|
}
|
|
assert(req(i->to, i2->arg[0]) || req(i->to, i2->arg[1]));
|
|
if (req(i->to, i2->arg[0]))
|
|
i2->arg[0] = r;
|
|
if (req(i->to, i2->arg[1]))
|
|
i2->arg[1] = r;
|
|
} else {
|
|
assert(req(i->to, b->jmp.arg));
|
|
b->jmp.arg = r;
|
|
}
|
|
i->to = r;
|
|
}
|
|
|
|
/* Redistribute trivial ops to point of use. */
|
|
/* Reduces register pressure. */
|
|
/* needs rpo, use; breaks use */
|
|
void
|
|
reassoc(Fn *fn)
|
|
{
|
|
Blk *b;
|
|
Ins *i;
|
|
InsMov *im;
|
|
uint n, nim;
|
|
|
|
nim = 0;
|
|
im = vnew(nim, sizeof im[0], PHeap);
|
|
|
|
/* identify trivial ins */
|
|
for (b = fn->start; b; b = b->link) {
|
|
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
ireassoc(fn, b, i, &nim, &im);
|
|
}
|
|
|
|
/* duplicate trivial ins */
|
|
movins(fn, im, nim, 0/*!del*/);
|
|
|
|
/* create new tmps for dups */
|
|
for (n = 0; n < nim; n++) {
|
|
b = fn->rpo[im[n].to.bid];
|
|
i = &b->ins[im[n].to.insn];
|
|
trealloc(fn, b, i);
|
|
}
|
|
|
|
/* delete (now) unused ins */
|
|
filluse(fn);
|
|
nopunused(fn);
|
|
|
|
if (debug['G']) {
|
|
fprintf(stderr, "\n> After Reassociation:\n");
|
|
printfn(fn, stderr);
|
|
}
|
|
}
|