| 1 | #include <assert.h> |
| 2 | #include <inttypes.h> |
| 3 | #include <limits.h> |
| 4 | #include <stdio.h> |
| 5 | #include <stdlib.h> |
| 6 | #include <string.h> |
| 7 | |
| 8 | #define MAKESURE(what, x) typedef char make_sure_##what[(x)?1:-1] |
| 9 | #define die(...) die_(__FILE__, __VA_ARGS__) |
| 10 | |
| 11 | typedef unsigned char uchar; |
| 12 | typedef unsigned int uint; |
| 13 | typedef unsigned long ulong; |
| 14 | typedef unsigned long long bits; |
| 15 | |
| 16 | typedef struct BSet BSet; |
| 17 | typedef struct Ref Ref; |
| 18 | typedef struct Op Op; |
| 19 | typedef struct Ins Ins; |
| 20 | typedef struct Phi Phi; |
| 21 | typedef struct Blk Blk; |
| 22 | typedef struct Use Use; |
| 23 | typedef struct Alias Alias; |
| 24 | typedef struct Tmp Tmp; |
| 25 | typedef struct Con Con; |
| 26 | typedef struct Addr Mem; |
| 27 | typedef struct Fn Fn; |
| 28 | typedef struct Typ Typ; |
| 29 | typedef struct Field Field; |
| 30 | typedef struct Dat Dat; |
| 31 | typedef struct Lnk Lnk; |
| 32 | typedef struct Target Target; |
| 33 | |
| 34 | enum { |
| 35 | NString = 72, |
| 36 | NIns = 1 << 20, |
| 37 | NAlign = 3, |
| 38 | NField = 32, |
| 39 | NBit = CHAR_BIT * sizeof(bits), |
| 40 | }; |
| 41 | |
| 42 | struct Target { |
| 43 | char name[16]; |
| 44 | int gpr0; /* first general purpose reg */ |
| 45 | int ngpr; |
| 46 | int fpr0; /* first floating point reg */ |
| 47 | int nfpr; |
| 48 | bits rglob; /* globally live regs (e.g., sp, fp) */ |
| 49 | int nrglob; |
| 50 | int *rsave; /* caller-save */ |
| 51 | int nrsave[2]; |
| 52 | bits (*retregs)(Ref, int[2]); |
| 53 | bits (*argregs)(Ref, int[2]); |
| 54 | int (*memargs)(int); |
| 55 | void (*abi)(Fn *); |
| 56 | void (*isel)(Fn *); |
| 57 | void (*emitfn)(Fn *, FILE *); |
| 58 | }; |
| 59 | |
| 60 | #define BIT(n) ((bits)1 << (n)) |
| 61 | |
| 62 | enum { |
| 63 | RXX = 0, |
| 64 | Tmp0 = NBit, /* first non-reg temporary */ |
| 65 | }; |
| 66 | |
| 67 | struct BSet { |
| 68 | uint nt; |
| 69 | bits *t; |
| 70 | }; |
| 71 | |
| 72 | struct Ref { |
| 73 | uint type:3; |
| 74 | uint val:29; |
| 75 | }; |
| 76 | |
| 77 | enum { |
| 78 | RTmp, |
| 79 | RCon, |
| 80 | RType, |
| 81 | RSlot, |
| 82 | RCall, |
| 83 | RMem, |
| 84 | }; |
| 85 | |
| 86 | #define R (Ref){0, 0} |
| 87 | #define TMP(x) (Ref){RTmp, x} |
| 88 | #define CON(x) (Ref){RCon, x} |
| 89 | #define CON_Z CON(0) /* reserved zero constant */ |
| 90 | #define SLOT(x) (Ref){RSlot, (x)&0x1fffffff} |
| 91 | #define TYPE(x) (Ref){RType, x} |
| 92 | #define CALL(x) (Ref){RCall, x} |
| 93 | #define MEM(x) (Ref){RMem, x} |
| 94 | |
| 95 | static inline int req(Ref a, Ref b) |
| 96 | { |
| 97 | return a.type == b.type && a.val == b.val; |
| 98 | } |
| 99 | |
| 100 | static inline int rtype(Ref r) |
| 101 | { |
| 102 | if (req(r, R)) |
| 103 | return -1; |
| 104 | return r.type; |
| 105 | } |
| 106 | |
| 107 | enum CmpI { |
| 108 | Cieq, |
| 109 | Cine, |
| 110 | Cisge, |
| 111 | Cisgt, |
| 112 | Cisle, |
| 113 | Cislt, |
| 114 | Ciuge, |
| 115 | Ciugt, |
| 116 | Ciule, |
| 117 | Ciult, |
| 118 | NCmpI, |
| 119 | }; |
| 120 | |
| 121 | enum CmpF { |
| 122 | Cfeq, |
| 123 | Cfge, |
| 124 | Cfgt, |
| 125 | Cfle, |
| 126 | Cflt, |
| 127 | Cfne, |
| 128 | Cfo, |
| 129 | Cfuo, |
| 130 | NCmpF, |
| 131 | NCmp = NCmpI + NCmpF, |
| 132 | }; |
| 133 | |
| 134 | enum O { |
| 135 | Oxxx, |
| 136 | #define O(op, x, y) O##op, |
| 137 | #include "ops.h" |
| 138 | NOp, |
| 139 | }; |
| 140 | |
| 141 | enum J { |
| 142 | Jxxx, |
| 143 | #define JMPS(X) \ |
| 144 | X(ret0) X(retw) X(retl) X(rets) \ |
| 145 | X(retd) X(retc) X(jmp) X(jnz) \ |
| 146 | X(jfieq) X(jfine) X(jfisge) X(jfisgt) \ |
| 147 | X(jfisle) X(jfislt) X(jfiuge) X(jfiugt) \ |
| 148 | X(jfiule) X(jfiult) X(jffeq) X(jffge) \ |
| 149 | X(jffgt) X(jffle) X(jfflt) X(jffne) \ |
| 150 | X(jffo) X(jffuo) |
| 151 | #define X(j) J##j, |
| 152 | JMPS(X) |
| 153 | #undef X |
| 154 | NJmp |
| 155 | }; |
| 156 | |
| 157 | enum { |
| 158 | Ocmpw = Oceqw, |
| 159 | Ocmpw1 = Ocultw, |
| 160 | Ocmpl = Oceql, |
| 161 | Ocmpl1 = Ocultl, |
| 162 | Ocmps = Oceqs, |
| 163 | Ocmps1 = Ocuos, |
| 164 | Ocmpd = Oceqd, |
| 165 | Ocmpd1 = Ocuod, |
| 166 | Oalloc = Oalloc4, |
| 167 | Oalloc1 = Oalloc16, |
| 168 | Oflag = Oflagieq, |
| 169 | Oflag1 = Oflagfuo, |
| 170 | NPubOp = Onop, |
| 171 | Jjf = Jjfieq, |
| 172 | Jjf1 = Jjffuo, |
| 173 | }; |
| 174 | |
| 175 | #define INRANGE(x, l, u) ((unsigned)(x) - l <= u - l) /* linear in x */ |
| 176 | #define isstore(o) INRANGE(o, Ostoreb, Ostored) |
| 177 | #define isload(o) INRANGE(o, Oloadsb, Oload) |
| 178 | #define isext(o) INRANGE(o, Oextsb, Oextuw) |
| 179 | #define ispar(o) INRANGE(o, Opar, Opare) |
| 180 | #define isarg(o) INRANGE(o, Oarg, Oargv) |
| 181 | #define isret(j) INRANGE(j, Jret0, Jretc) |
| 182 | |
| 183 | enum { |
| 184 | Kx = -1, /* "top" class (see usecheck() and clsmerge()) */ |
| 185 | Kw, |
| 186 | Kl, |
| 187 | Ks, |
| 188 | Kd |
| 189 | }; |
| 190 | |
| 191 | #define KWIDE(k) ((k)&1) |
| 192 | #define KBASE(k) ((k)>>1) |
| 193 | |
| 194 | struct Op { |
| 195 | char *name; |
| 196 | short argcls[2][4]; |
| 197 | int canfold; |
| 198 | }; |
| 199 | |
| 200 | struct Ins { |
| 201 | uint op:30; |
| 202 | uint cls:2; |
| 203 | Ref to; |
| 204 | Ref arg[2]; |
| 205 | }; |
| 206 | |
| 207 | struct Phi { |
| 208 | Ref to; |
| 209 | Ref *arg; |
| 210 | Blk **blk; |
| 211 | uint narg; |
| 212 | int cls; |
| 213 | Phi *link; |
| 214 | }; |
| 215 | |
| 216 | struct Blk { |
| 217 | Phi *phi; |
| 218 | Ins *ins; |
| 219 | uint nins; |
| 220 | struct { |
| 221 | short type; |
| 222 | Ref arg; |
| 223 | } jmp; |
| 224 | Blk *s1; |
| 225 | Blk *s2; |
| 226 | Blk *link; |
| 227 | |
| 228 | uint id; |
| 229 | uint visit; |
| 230 | |
| 231 | Blk *idom; |
| 232 | Blk *dom, *dlink; |
| 233 | Blk **fron; |
| 234 | uint nfron; |
| 235 | |
| 236 | Blk **pred; |
| 237 | uint npred; |
| 238 | BSet in[1], out[1], gen[1]; |
| 239 | int nlive[2]; |
| 240 | int loop; |
| 241 | char name[NString]; |
| 242 | }; |
| 243 | |
| 244 | struct Use { |
| 245 | enum { |
| 246 | UXXX, |
| 247 | UPhi, |
| 248 | UIns, |
| 249 | UJmp, |
| 250 | } type; |
| 251 | uint bid; |
| 252 | union { |
| 253 | Ins *ins; |
| 254 | Phi *phi; |
| 255 | } u; |
| 256 | }; |
| 257 | |
| 258 | enum { |
| 259 | NoAlias, |
| 260 | MayAlias, |
| 261 | MustAlias |
| 262 | }; |
| 263 | |
| 264 | struct Alias { |
| 265 | enum { |
| 266 | ABot = 0, |
| 267 | ALoc = 1, /* stack local */ |
| 268 | ACon = 2, |
| 269 | AEsc = 3, /* stack escaping */ |
| 270 | ASym = 4, |
| 271 | AUnk = 6, |
| 272 | #define astack(t) ((t) & 1) |
| 273 | } type; |
| 274 | Ref base; |
| 275 | uint32_t label; |
| 276 | int64_t offset; |
| 277 | Alias *slot; |
| 278 | }; |
| 279 | |
| 280 | struct Tmp { |
| 281 | char name[NString]; |
| 282 | Use *use; |
| 283 | uint ndef, nuse; |
| 284 | uint bid; /* id of a defining block */ |
| 285 | uint cost; |
| 286 | int slot; /* -1 for unset */ |
| 287 | short cls; |
| 288 | struct { |
| 289 | int r; /* register or -1 */ |
| 290 | int w; /* weight */ |
| 291 | bits m; /* avoid these registers */ |
| 292 | } hint; |
| 293 | int phi; |
| 294 | Alias alias; |
| 295 | enum { |
| 296 | WFull, |
| 297 | Wsb, /* must match Oload/Oext order */ |
| 298 | Wub, |
| 299 | Wsh, |
| 300 | Wuh, |
| 301 | Wsw, |
| 302 | Wuw |
| 303 | } width; |
| 304 | int visit; |
| 305 | }; |
| 306 | |
| 307 | struct Con { |
| 308 | enum { |
| 309 | CUndef, |
| 310 | CBits, |
| 311 | CAddr, |
| 312 | } type; |
| 313 | uint32_t label; |
| 314 | union { |
| 315 | int64_t i; |
| 316 | double d; |
| 317 | float s; |
| 318 | } bits; |
| 319 | char flt; /* 1 to print as s, 2 to print as d */ |
| 320 | char local; |
| 321 | }; |
| 322 | |
| 323 | typedef struct Addr Addr; |
| 324 | |
| 325 | struct Addr { /* amd64 addressing */ |
| 326 | Con offset; |
| 327 | Ref base; |
| 328 | Ref index; |
| 329 | int scale; |
| 330 | }; |
| 331 | |
| 332 | struct Lnk { |
| 333 | char export; |
| 334 | char align; |
| 335 | char *sec; |
| 336 | char *secf; |
| 337 | }; |
| 338 | |
| 339 | struct Fn { |
| 340 | Blk *start; |
| 341 | Tmp *tmp; |
| 342 | Con *con; |
| 343 | Mem *mem; |
| 344 | int ntmp; |
| 345 | int ncon; |
| 346 | int nmem; |
| 347 | uint nblk; |
| 348 | int retty; /* index in typ[], -1 if no aggregate return */ |
| 349 | Ref retr; |
| 350 | Blk **rpo; |
| 351 | bits reg; |
| 352 | int slot; |
| 353 | char vararg; |
| 354 | char dynalloc; |
| 355 | char name[NString]; |
| 356 | Lnk lnk; |
| 357 | }; |
| 358 | |
| 359 | struct Typ { |
| 360 | char name[NString]; |
| 361 | char isdark; |
| 362 | char isunion; |
| 363 | int align; |
| 364 | uint64_t size; |
| 365 | uint nunion; |
| 366 | struct Field { |
| 367 | enum { |
| 368 | FEnd, |
| 369 | Fb, |
| 370 | Fh, |
| 371 | Fw, |
| 372 | Fl, |
| 373 | Fs, |
| 374 | Fd, |
| 375 | FPad, |
| 376 | FTyp, |
| 377 | } type; |
| 378 | uint len; /* or index in typ[] for FTyp */ |
| 379 | } (*fields)[NField+1]; |
| 380 | }; |
| 381 | |
| 382 | struct Dat { |
| 383 | enum { |
| 384 | DStart, |
| 385 | DEnd, |
| 386 | DB, |
| 387 | DH, |
| 388 | DW, |
| 389 | DL, |
| 390 | DZ |
| 391 | } type; |
| 392 | char *name; |
| 393 | Lnk *lnk; |
| 394 | union { |
| 395 | int64_t num; |
| 396 | double fltd; |
| 397 | float flts; |
| 398 | char *str; |
| 399 | struct { |
| 400 | char *name; |
| 401 | int64_t off; |
| 402 | } ref; |
| 403 | } u; |
| 404 | char isref; |
| 405 | char isstr; |
| 406 | }; |
| 407 | |
| 408 | /* main.c */ |
| 409 | extern Target T; |
| 410 | extern char debug['Z'+1]; |
| 411 | |
| 412 | /* util.c */ |
| 413 | typedef enum { |
| 414 | Pheap, /* free() necessary */ |
| 415 | Pfn, /* discarded after processing the function */ |
| 416 | } Pool; |
| 417 | |
| 418 | extern Typ *typ; |
| 419 | extern Ins insb[NIns], *curi; |
| 420 | uint32_t hash(char *); |
| 421 | void die_(char *, char *, ...) __attribute__((noreturn)); |
| 422 | void *emalloc(size_t); |
| 423 | void *alloc(size_t); |
| 424 | void freeall(void); |
| 425 | void *vnew(ulong, size_t, Pool); |
| 426 | void vfree(void *); |
| 427 | void vgrow(void *, ulong); |
| 428 | uint32_t intern(char *); |
| 429 | char *str(uint32_t); |
| 430 | int argcls(Ins *, int); |
| 431 | int isreg(Ref); |
| 432 | int iscmp(int, int *, int *); |
| 433 | void emit(int, int, Ref, Ref, Ref); |
| 434 | void emiti(Ins); |
| 435 | void idup(Ins **, Ins *, ulong); |
| 436 | Ins *icpy(Ins *, Ins *, ulong); |
| 437 | int cmpop(int); |
| 438 | int cmpneg(int); |
| 439 | int clsmerge(short *, short); |
| 440 | int phicls(int, Tmp *); |
| 441 | Ref newtmp(char *, int, Fn *); |
| 442 | void chuse(Ref, int, Fn *); |
| 443 | Ref newcon(Con *, Fn *); |
| 444 | Ref getcon(int64_t, Fn *); |
| 445 | int addcon(Con *, Con *); |
| 446 | void blit(Ref, uint, Ref, uint, uint, Fn *); |
| 447 | void blit0(Ref, Ref, uint, Fn *); |
| 448 | void salloc(Ref, Ref, Fn *); |
| 449 | void dumpts(BSet *, Tmp *, FILE *); |
| 450 | |
| 451 | void bsinit(BSet *, uint); |
| 452 | void bszero(BSet *); |
| 453 | uint bscount(BSet *); |
| 454 | void bsset(BSet *, uint); |
| 455 | void bsclr(BSet *, uint); |
| 456 | void bscopy(BSet *, BSet *); |
| 457 | void bsunion(BSet *, BSet *); |
| 458 | void bsinter(BSet *, BSet *); |
| 459 | void bsdiff(BSet *, BSet *); |
| 460 | int bsequal(BSet *, BSet *); |
| 461 | int bsiter(BSet *, int *); |
| 462 | |
| 463 | static inline int |
| 464 | bshas(BSet *bs, uint elt) |
| 465 | { |
| 466 | assert(elt < bs->nt * NBit); |
| 467 | return (bs->t[elt/NBit] & BIT(elt%NBit)) != 0; |
| 468 | } |
| 469 | |
| 470 | /* parse.c */ |
| 471 | extern Op optab[NOp]; |
| 472 | void parse(FILE *, char *, void (Dat *), void (Fn *)); |
| 473 | void printfn(Fn *, FILE *); |
| 474 | void printref(Ref, Fn *, FILE *); |
| 475 | void err(char *, ...) __attribute__((noreturn)); |
| 476 | |
| 477 | /* cfg.c */ |
| 478 | Blk *blknew(void); |
| 479 | void edgedel(Blk *, Blk **); |
| 480 | void fillpreds(Fn *); |
| 481 | void fillrpo(Fn *); |
| 482 | void filldom(Fn *); |
| 483 | int sdom(Blk *, Blk *); |
| 484 | int dom(Blk *, Blk *); |
| 485 | void fillfron(Fn *); |
| 486 | void loopiter(Fn *, void (*)(Blk *, Blk *)); |
| 487 | void fillloop(Fn *); |
| 488 | void simpljmp(Fn *); |
| 489 | |
| 490 | /* mem.c */ |
| 491 | void memopt(Fn *); |
| 492 | |
| 493 | /* alias.c */ |
| 494 | void fillalias(Fn *); |
| 495 | int alias(Ref, int, Ref, int, int *, Fn *); |
| 496 | int escapes(Ref, Fn *); |
| 497 | |
| 498 | /* load.c */ |
| 499 | int loadsz(Ins *); |
| 500 | int storesz(Ins *); |
| 501 | void loadopt(Fn *); |
| 502 | |
| 503 | /* ssa.c */ |
| 504 | void filluse(Fn *); |
| 505 | void fillpreds(Fn *); |
| 506 | void fillrpo(Fn *); |
| 507 | void ssa(Fn *); |
| 508 | void ssacheck(Fn *); |
| 509 | |
| 510 | /* copy.c */ |
| 511 | void copy(Fn *); |
| 512 | |
| 513 | /* fold.c */ |
| 514 | void fold(Fn *); |
| 515 | |
| 516 | /* live.c */ |
| 517 | void liveon(BSet *, Blk *, Blk *); |
| 518 | void filllive(Fn *); |
| 519 | |
| 520 | /* spill.c */ |
| 521 | void fillcost(Fn *); |
| 522 | void spill(Fn *); |
| 523 | |
| 524 | /* rega.c */ |
| 525 | void rega(Fn *); |
| 526 | |
| 527 | /* gas.c */ |
| 528 | enum Asm { |
| 529 | Gasmacho, |
| 530 | Gaself, |
| 531 | }; |
| 532 | extern char *gasloc; |
| 533 | extern char *gassym; |
| 534 | void gasinit(enum Asm); |
| 535 | void gasemitlnk(char *, Lnk *, char *, FILE *); |
| 536 | void gasemitfntail(char *, FILE *); |
| 537 | void gasemitdat(Dat *, FILE *); |
| 538 | int gasstash(void *, int); |
| 539 | void gasemitfin(FILE *); |
| 540 | |