Crashing loads of uninitialized memory proved to be a problem when implementing unions using qbe. This patch introduces a new UNDEF Ref to represent data that is known to be uninitialized. Optimization passes can make use of it to eliminate some code. In the last compilation stages, UNDEF is treated as the constant 0xdeaddead.
Unified · Split
| @@ -90,10 +90,11 @@ enum { · 89 unmodified lines | |||
| 90 | RMem, | 90 | RMem, |
| 91 | }; | 91 | }; |
| 92 | 92 | ||
| 93 | #define R (Ref){0, 0} | 93 | #define R (Ref){RTmp, 0} |
| 94 | #define UNDEF (Ref){RCon, 0} /* represents uninitialized data */ | ||
| 95 | #define CON_Z (Ref){RCon, 1} | ||
| 94 | #define TMP(x) (Ref){RTmp, x} | 96 | #define TMP(x) (Ref){RTmp, x} |
| 95 | #define CON(x) (Ref){RCon, x} | 97 | #define CON(x) (Ref){RCon, x} |
| 96 | #define CON_Z CON(0) /* reserved zero constant */ | ||
| 97 | #define SLOT(x) (Ref){RSlot, (x)&0x1fffffff} | 98 | #define SLOT(x) (Ref){RSlot, (x)&0x1fffffff} |
| 98 | #define TYPE(x) (Ref){RType, x} | 99 | #define TYPE(x) (Ref){RType, x} |
| 99 | #define CALL(x) (Ref){RCall, x} | 100 | #define CALL(x) (Ref){RCall, x} |
| @@ -482,7 +482,7 @@ seljmp(Blk *b, Fn *fn) · 481 unmodified lines | |||
| 482 | } | 482 | } |
| 483 | fi = flagi(b->ins, &b->ins[b->nins]); | 483 | fi = flagi(b->ins, &b->ins[b->nins]); |
| 484 | if (!fi || !req(fi->to, r)) { | 484 | if (!fi || !req(fi->to, r)) { |
| 485 | selcmp((Ref[2]){r, CON_Z}, Kw, 0, fn); /* todo, long jnz */ | 485 | selcmp((Ref[2]){r, CON_Z}, Kw, 0, fn); |
| 486 | b->jmp.type = Jjf + Cine; | 486 | b->jmp.type = Jjf + Cine; |
| 487 | } | 487 | } |
| 488 | else if (iscmp(fi->op, &k, &c) | 488 | else if (iscmp(fi->op, &k, &c) |
| @@ -125,7 +125,7 @@ subst(Ref *pr, Ref *cpy) · 124 unmodified lines | |||
| 125 | *pr = copyof(*pr, cpy); | 125 | *pr = copyof(*pr, cpy); |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | /* requires use and rpo, breaks use */ | 128 | /* requires use and dom, breaks use */ |
| 129 | void | 129 | void |
| 130 | copy(Fn *fn) | 130 | copy(Fn *fn) |
| 131 | { | 131 | { |
| @@ -155,12 +155,16 @@ copy(Fn *fn) · 23 unmodified lines | |||
| 155 | for (a=0; a<p->narg; a++) | 155 | for (a=0; a<p->narg; a++) |
| 156 | if (p->blk[a]->id < n) { | 156 | if (p->blk[a]->id < n) { |
| 157 | r1 = copyof(p->arg[a], cpy); | 157 | r1 = copyof(p->arg[a], cpy); |
| 158 | if (req(r, R) || req(r1, r)) | 158 | if (req(r, R) || req(r, UNDEF)) |
| 159 | r = r1; | ||
| 160 | if (req(r1, r) || req(r1, UNDEF)) | ||
| 159 | eq++; | 161 | eq++; |
| 160 | r = r1; | ||
| 161 | } | 162 | } |
| 162 | assert(!req(r, R)); | 163 | assert(!req(r, R)); |
| 163 | if (eq == p->narg) | 164 | if (rtype(r) == RTmp |
| 165 | && !dom(fn->rpo[fn->tmp[r.val].bid], b)) | ||
| 166 | cpy[p->to.val] = p->to; | ||
| 167 | else if (eq == p->narg) | ||
| 164 | cpy[p->to.val] = r; | 168 | cpy[p->to.val] = r; |
| 165 | else { | 169 | else { |
| 166 | cpy[p->to.val] = p->to; | 170 | cpy[p->to.val] = p->to; |
| @@ -1,8 +1,8 @@ | |||
| 1 | #include "all.h" | 1 | #include "all.h" |
| 2 | 2 | ||
| 3 | enum { | 3 | enum { |
| 4 | Bot = -2, /* lattice bottom */ | 4 | Bot = -1, /* lattice bottom */ |
| 5 | Top = -1, /* lattice top */ | 5 | Top = 0, /* lattice top (matches UNDEF) */ |
| 6 | }; | 6 | }; |
| 7 | 7 | ||
| 8 | typedef struct Edge Edge; | 8 | typedef struct Edge Edge; |
| @@ -126,7 +126,6 @@ visitjmp(Blk *b, int n, Fn *fn) · 117 unmodified lines | |||
| 126 | switch (b->jmp.type) { | 126 | switch (b->jmp.type) { |
| 127 | case Jjnz: | 127 | case Jjnz: |
| 128 | l = latval(b->jmp.arg); | 128 | l = latval(b->jmp.arg); |
| 129 | assert(l != Top && "ssa invariant broken"); | ||
| 130 | if (l == Bot) { | 129 | if (l == Bot) { |
| 131 | edge[n][1].work = flowrk; | 130 | edge[n][1].work = flowrk; |
| 132 | edge[n][0].work = &edge[n][1]; | 131 | edge[n][0].work = &edge[n][1]; |
| @@ -174,7 +173,6 @@ renref(Ref *r) · 41 unmodified lines | |||
| 174 | 173 | ||
| 175 | if (rtype(*r) == RTmp) | 174 | if (rtype(*r) == RTmp) |
| 176 | if ((l=val[r->val]) != Bot) { | 175 | if ((l=val[r->val]) != Bot) { |
| 177 | assert(l != Top && "ssa invariant broken"); | ||
| 178 | *r = CON(l); | 176 | *r = CON(l); |
| 179 | return 1; | 177 | return 1; |
| 180 | } | 178 | } |
| @@ -294,9 +292,13 @@ fold(Fn *fn) · 113 unmodified lines | |||
| 294 | for (i=b->ins; i<&b->ins[b->nins]; i++) | 292 | for (i=b->ins; i<&b->ins[b->nins]; i++) |
| 295 | if (renref(&i->to)) | 293 | if (renref(&i->to)) |
| 296 | *i = (Ins){.op = Onop}; | 294 | *i = (Ins){.op = Onop}; |
| 297 | else | 295 | else { |
| 298 | for (n=0; n<2; n++) | 296 | for (n=0; n<2; n++) |
| 299 | renref(&i->arg[n]); | 297 | renref(&i->arg[n]); |
| 298 | if (isstore(i->op)) | ||
| 299 | if (req(i->arg[0], UNDEF)) | ||
| 300 | *i = (Ins){.op = Onop}; | ||
| 301 | } | ||
| 300 | renref(&b->jmp.arg); | 302 | renref(&b->jmp.arg); |
| 301 | if (b->jmp.type == Jjnz && rtype(b->jmp.arg) == RCon) { | 303 | if (b->jmp.type == Jjnz && rtype(b->jmp.arg) == RCon) { |
| 302 | if (iscon(&fn->con[b->jmp.arg.val], 0, 0)) { | 304 | if (iscon(&fn->con[b->jmp.arg.val], 0, 0)) { |
| @@ -201,7 +201,7 @@ coalesce(Fn *fn) · 200 unmodified lines | |||
| 201 | Ref *arg; | 201 | Ref *arg; |
| 202 | bits x; | 202 | bits x; |
| 203 | int64_t off0, off1; | 203 | int64_t off0, off1; |
| 204 | int n, m, sz, nsl, nbl, ip, *stk; | 204 | int n, m, ip, sz, nsl, nbl, *stk; |
| 205 | uint total, freed, fused; | 205 | uint total, freed, fused; |
| 206 | 206 | ||
| 207 | /* minimize the stack usage | 207 | /* minimize the stack usage |
| @@ -317,26 +317,31 @@ coalesce(Fn *fn) · 109 unmodified lines | |||
| 317 | while (n--) { | 317 | while (n--) { |
| 318 | t = &fn->tmp[stk[n]]; | 318 | t = &fn->tmp[stk[n]]; |
| 319 | assert(t->ndef == 1 && t->def); | 319 | assert(t->ndef == 1 && t->def); |
| 320 | *t->def = (Ins){.op = Onop}; | 320 | i = t->def; |
| 321 | if (isload(i->op)) { | ||
| 322 | i->op = Ocopy; | ||
| 323 | i->arg[0] = UNDEF; | ||
| 324 | continue; | ||
| 325 | } | ||
| 326 | *i = (Ins){.op = Onop}; | ||
| 321 | for (u=t->use; u<&t->use[t->nuse]; u++) { | 327 | for (u=t->use; u<&t->use[t->nuse]; u++) { |
| 322 | if (u->type == UJmp) { | 328 | if (u->type == UJmp) { |
| 323 | b = fn->rpo[u->bid]; | 329 | b = fn->rpo[u->bid]; |
| 324 | b->jmp.arg = CON_Z; | 330 | assert(isret(b->jmp.type)); |
| 331 | b->jmp.type = Jret0; | ||
| 332 | b->jmp.arg = R; | ||
| 325 | continue; | 333 | continue; |
| 326 | } | 334 | } |
| 327 | assert(u->type == UIns); | 335 | assert(u->type == UIns); |
| 328 | i = u->u.ins; | 336 | i = u->u.ins; |
| 329 | /* make loads crash */ | 337 | if (!req(i->to, R)) { |
| 330 | if (isload(i->op)) | ||
| 331 | i->arg[0] = CON_Z; | ||
| 332 | else if (i->op == Oargc) | ||
| 333 | i->arg[1] = CON_Z; | ||
| 334 | else if (!req(i->to, R)) { | ||
| 335 | assert(rtype(i->to) == RTmp); | 338 | assert(rtype(i->to) == RTmp); |
| 336 | vgrow(&stk, ++n); | 339 | vgrow(&stk, ++n); |
| 337 | stk[n-1] = i->to.val; | 340 | stk[n-1] = i->to.val; |
| 341 | } else if (isarg(i->op)) { | ||
| 342 | assert(i->op == Oargc); | ||
| 343 | i->arg[1] = CON_Z; /* crash */ | ||
| 338 | } else { | 344 | } else { |
| 339 | assert(!isarg(i->op)); | ||
| 340 | if (i->op == Oblit0) | 345 | if (i->op == Oblit0) |
| 341 | *(i+1) = (Ins){.op = Onop}; | 346 | *(i+1) = (Ins){.op = Onop}; |
| 342 | *i = (Ins){.op = Onop}; | 347 | *i = (Ins){.op = Onop}; |
| @@ -867,7 +867,7 @@ parsefn(Lnk *lnk) · 866 unmodified lines | |||
| 867 | curi = insb; | 867 | curi = insb; |
| 868 | curf = alloc(sizeof *curf); | 868 | curf = alloc(sizeof *curf); |
| 869 | curf->ntmp = 0; | 869 | curf->ntmp = 0; |
| 870 | curf->ncon = 1; /* first constant must be 0 */ | 870 | curf->ncon = 2; |
| 871 | curf->tmp = vnew(curf->ntmp, sizeof curf->tmp[0], PFn); | 871 | curf->tmp = vnew(curf->ntmp, sizeof curf->tmp[0], PFn); |
| 872 | curf->con = vnew(curf->ncon, sizeof curf->con[0], PFn); | 872 | curf->con = vnew(curf->ncon, sizeof curf->con[0], PFn); |
| 873 | for (i=0; i<Tmp0; ++i) | 873 | for (i=0; i<Tmp0; ++i) |
| @@ -876,6 +876,8 @@ parsefn(Lnk *lnk) · 2 unmodified lines | |||
| 876 | else | 876 | else |
| 877 | newtmp(0, Kl, curf); | 877 | newtmp(0, Kl, curf); |
| 878 | curf->con[0].type = CBits; | 878 | curf->con[0].type = CBits; |
| 879 | curf->con[0].bits.i = 0xdeaddead; /* UNDEF */ | ||
| 880 | curf->con[1].type = CBits; | ||
| 879 | curf->lnk = *lnk; | 881 | curf->lnk = *lnk; |
| 880 | blink = &curf->start; | 882 | blink = &curf->start; |
| 881 | curf->retty = Kx; | 883 | curf->retty = Kx; |
| @@ -1230,7 +1232,10 @@ printref(Ref r, Fn *fn, FILE *f) · 348 unmodified lines | |||
| 1230 | fprintf(f, "%%%s", fn->tmp[r.val].name); | 1232 | fprintf(f, "%%%s", fn->tmp[r.val].name); |
| 1231 | break; | 1233 | break; |
| 1232 | case RCon: | 1234 | case RCon: |
| 1233 | printcon(&fn->con[r.val], f); | 1235 | if (req(r, UNDEF)) |
| 1236 | fprintf(f, "UNDEF"); | ||
| 1237 | else | ||
| 1238 | printcon(&fn->con[r.val], f); | ||
| 1234 | break; | 1239 | break; |
| 1235 | case RSlot: | 1240 | case RSlot: |
| 1236 | fprintf(f, "S%d", rsval(r)); | 1241 | fprintf(f, "S%d", rsval(r)); |
| @@ -264,7 +264,7 @@ getstk(int t, Blk *b, Name **stk) · 263 unmodified lines | |||
| 264 | stk[t] = n; | 264 | stk[t] = n; |
| 265 | if (!n) { | 265 | if (!n) { |
| 266 | /* uh, oh, warn */ | 266 | /* uh, oh, warn */ |
| 267 | return CON_Z; | 267 | return UNDEF; |
| 268 | } else | 268 | } else |
| 269 | return n->r; | 269 | return n->r; |
| 270 | } | 270 | } |
| @@ -361,7 +361,7 @@ newcon(Con *c0, Fn *fn) · 360 unmodified lines | |||
| 361 | Con *c1; | 361 | Con *c1; |
| 362 | int i; | 362 | int i; |
| 363 | 363 | ||
| 364 | for (i=0; i<fn->ncon; i++) { | 364 | for (i=1; i<fn->ncon; i++) { |
| 365 | c1 = &fn->con[i]; | 365 | c1 = &fn->con[i]; |
| 366 | if (c0->type == c1->type | 366 | if (c0->type == c1->type |
| 367 | && symeq(c0->sym, c1->sym) | 367 | && symeq(c0->sym, c1->sym) |
| @@ -378,8 +378,9 @@ getcon(int64_t val, Fn *fn) · 10 unmodified lines | |||
| 378 | { | 378 | { |
| 379 | int c; | 379 | int c; |
| 380 | 380 | ||
| 381 | for (c=0; c<fn->ncon; c++) | 381 | for (c=1; c<fn->ncon; c++) |
| 382 | if (fn->con[c].type == CBits && fn->con[c].bits.i == val) | 382 | if (fn->con[c].type == CBits |
| 383 | && fn->con[c].bits.i == val) | ||
| 383 | return CON(c); | 384 | return CON(c); |
| 384 | vgrow(&fn->con, ++fn->ncon); | 385 | vgrow(&fn->con, ++fn->ncon); |
| 385 | fn->con[c] = (Con){.type = CBits, .bits.i = val}; | 386 | fn->con[c] = (Con){.type = CBits, .bits.i = val}; |