├── .gitignore ├── .tag ├── LICENSE ├── Makefile ├── README ├── alias.c ├── all.h ├── amd64 ├── all.h ├── emit.c ├── isel.c ├── sysv.c └── targ.c ├── arm64 ├── abi.c ├── all.h ├── emit.c ├── isel.c └── targ.c ├── cfg.c ├── copy.c ├── doc ├── .gitignore ├── Makefile ├── abi.txt ├── il.txt ├── llvm.txt ├── txt │ ├── txt.css │ └── txt.ml └── win.txt ├── emit.c ├── file.ssa ├── fold.c ├── gas.c ├── isel.c ├── live.c ├── load.c ├── main.c ├── mem.c ├── minic ├── .gitignore ├── Makefile ├── mcc ├── minic.y ├── test │ ├── collatz.c │ ├── euler9.c │ ├── knight.c │ ├── mandel.c │ ├── prime.c │ └── queen.c └── yacc.c ├── ops.h ├── parse.c ├── rega.c ├── spill.c ├── ssa.c ├── sysv.c ├── test ├── _alt.ssa ├── _bf99.ssa ├── _bfmandel.ssa ├── _dragon.ssa ├── _fix1.ssa ├── _fix2.ssa ├── _fix3.ssa ├── _fix4.ssa ├── _live.ssa ├── _rpo.ssa ├── _spill1.ssa ├── _spill2.ssa ├── _spill3.ssa ├── abi1.ssa ├── abi2.ssa ├── abi3.ssa ├── abi4.ssa ├── abi5.ssa ├── abi6.ssa ├── align.ssa ├── collatz.ssa ├── cprime.ssa ├── cup.ssa ├── dark.ssa ├── double.ssa ├── dynalloc.ssa ├── echo.ssa ├── eucl.ssa ├── euclc.ssa ├── fixarg.ssa ├── fpcnv.ssa ├── ldbits.ssa ├── ldhoist.ssa ├── loop.ssa ├── mandel.ssa ├── max.ssa ├── philv.ssa ├── prime.ssa ├── puts10.ssa ├── queen.ssa ├── strcmp.ssa ├── strspn.ssa ├── sum.ssa ├── vararg1.ssa └── vararg2.ssa ├── tools ├── abifuzz.sh ├── callgen.ml ├── cra.sh ├── lexh.c ├── pmov.c ├── test.sh ├── unit.sh └── vatest.py └── util.c /.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | config.h 3 | .comfile 4 | *.out 5 | -------------------------------------------------------------------------------- /.tag: -------------------------------------------------------------------------------- 1 | Look slot( 2 | 3 | Get lisc.h 4 | Get parse.c 5 | Get isel.c 6 | Get spill.c 7 | Get rega.c 8 | Get emit.c 9 | 10 | New 11 | |fmt 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | © 2015-2017 Quentin Carbonneaux 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN = qbe 2 | 3 | V = @ 4 | OBJDIR = obj 5 | 6 | SRC = main.c util.c parse.c cfg.c mem.c ssa.c alias.c load.c copy.c \ 7 | fold.c live.c spill.c rega.c gas.c 8 | AMD64SRC = amd64/targ.c amd64/sysv.c amd64/isel.c amd64/emit.c 9 | ARM64SRC = arm64/targ.c arm64/abi.c arm64/isel.c arm64/emit.c 10 | SRCALL = $(SRC) $(AMD64SRC) $(ARM64SRC) 11 | 12 | AMD64OBJ = $(AMD64SRC:%.c=$(OBJDIR)/%.o) 13 | ARM64OBJ = $(ARM64SRC:%.c=$(OBJDIR)/%.o) 14 | OBJ = $(SRC:%.c=$(OBJDIR)/%.o) $(AMD64OBJ) $(ARM64OBJ) 15 | 16 | CFLAGS += -Wall -fPIC -Wextra -std=c99 -g -pedantic 17 | 18 | $(OBJDIR)/$(BIN): $(OBJ) $(OBJDIR)/timestamp 19 | @test -z "$(V)" || echo "ld $@" 20 | $(V)$(CC) $(LDFLAGS) $(OBJ) -o $@ 21 | 22 | $(OBJDIR)/%.o: %.c $(OBJDIR)/timestamp 23 | @test -z "$(V)" || echo "cc $<" 24 | $(V)$(CC) $(CFLAGS) -c $< -o $@ 25 | 26 | $(OBJDIR)/timestamp: 27 | @mkdir -p $(OBJDIR) 28 | @mkdir -p $(OBJDIR)/amd64 29 | @mkdir -p $(OBJDIR)/arm64 30 | @touch $@ 31 | 32 | $(OBJ): all.h ops.h 33 | $(AMD64OBJ): amd64/all.h 34 | $(ARM64OBJ): arm64/all.h 35 | obj/main.o: config.h 36 | 37 | config.h: 38 | @case `uname` in \ 39 | *Darwin*) \ 40 | echo "#define Defasm Gasmacho"; \ 41 | echo "#define Deftgt T_amd64_sysv"; \ 42 | ;; \ 43 | *) \ 44 | echo "#define Defasm Gaself"; \ 45 | case `uname -m` in \ 46 | *aarch64*) \ 47 | echo "$define Deftgt T_arm64"; \ 48 | ;; \ 49 | *) \ 50 | echo "#define Deftgt T_amd64_sysv";\ 51 | ;; \ 52 | esac \ 53 | ;; \ 54 | esac > $@ 55 | 56 | install: $(OBJDIR)/$(BIN) 57 | mkdir -p "$(DESTDIR)/$(PREFIX)/bin/" 58 | cp $< "$(DESTDIR)/$(PREFIX)/bin/" 59 | 60 | uninstall: 61 | rm -f "$(DESTDIR)/$(PREFIX)/bin/$(BIN)" 62 | 63 | clean: 64 | rm -fr $(OBJDIR) 65 | 66 | clean-gen: clean 67 | rm -f config.h 68 | 69 | check: $(OBJDIR)/$(BIN) 70 | tools/test.sh all 71 | 72 | check-arm64: $(OBJDIR)/$(BIN) 73 | TARGET=arm64 tools/test.sh all 74 | 75 | src: 76 | @echo $(SRCALL) 77 | 78 | 80: 79 | @for F in $(SRCALL); \ 80 | do \ 81 | awk "{ \ 82 | gsub(/\\t/, \" \"); \ 83 | if (length(\$$0) > $@) \ 84 | printf(\"$$F:%d: %s\\n\", NR, \$$0); \ 85 | }" < $$F; \ 86 | done 87 | 88 | .PHONY: clean clean-gen check check-arm64 src 80 install uninstall 89 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | QBE - Backend Compiler http://c9x.me/compile/ 2 | 3 | doc/ Documentation. 4 | minic/ An example C frontend for QBE. 5 | tools/ Miscellaneous tools (testing). 6 | test/ Tests. 7 | amd64/ 8 | arm64/ Architecture-specific code. 9 | 10 | The LICENSE file applies to all files distributed. 11 | 12 | - Compilation 13 | 14 | Invoke GNU make in this directory to create the executable 15 | file obj/qbe. On some systems (BSD) you might have to use 16 | 'gmake' instead of 'make'. 17 | -------------------------------------------------------------------------------- /alias.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | static void 4 | getalias(Alias *a, Ref r, Fn *fn) 5 | { 6 | Con *c; 7 | 8 | switch (rtype(r)) { 9 | default: 10 | die("unreachable"); 11 | case RTmp: 12 | *a = fn->tmp[r.val].alias; 13 | if (astack(a->type)) 14 | a->type = a->slot->type; 15 | assert(a->type != ABot); 16 | break; 17 | case RCon: 18 | c = &fn->con[r.val]; 19 | if (c->type == CAddr) { 20 | a->type = ASym; 21 | a->label = c->label; 22 | } else 23 | a->type = ACon; 24 | a->offset = c->bits.i; 25 | a->slot = 0; 26 | break; 27 | } 28 | } 29 | 30 | int 31 | alias(Ref p, int sp, Ref q, int sq, int *delta, Fn *fn) 32 | { 33 | Alias ap, aq; 34 | int ovlap; 35 | 36 | getalias(&ap, p, fn); 37 | getalias(&aq, q, fn); 38 | *delta = ap.offset - aq.offset; 39 | ovlap = ap.offset < aq.offset + sq && aq.offset < ap.offset + sp; 40 | 41 | if (astack(ap.type) && astack(aq.type)) { 42 | /* if both are offsets of the same 43 | * stack slot, they alias iif they 44 | * overlap */ 45 | if (req(ap.base, aq.base) && ovlap) 46 | return MustAlias; 47 | return NoAlias; 48 | } 49 | 50 | if (ap.type == ASym && aq.type == ASym) { 51 | /* they conservatively alias if the 52 | * symbols are different, or they 53 | * alias for sure if they overlap */ 54 | if (ap.label != aq.label) 55 | return MayAlias; 56 | if (ovlap) 57 | return MustAlias; 58 | return NoAlias; 59 | } 60 | 61 | if ((ap.type == ACon && aq.type == ACon) 62 | || (ap.type == aq.type && req(ap.base, aq.base))) { 63 | assert(ap.type == ACon || ap.type == AUnk); 64 | /* if they have the same base, we 65 | * can rely on the offsets only */ 66 | if (ovlap) 67 | return MustAlias; 68 | return NoAlias; 69 | } 70 | 71 | /* if one of the two is unknown 72 | * there may be aliasing unless 73 | * the other is provably local */ 74 | if (ap.type == AUnk && aq.type != ALoc) 75 | return MayAlias; 76 | if (aq.type == AUnk && ap.type != ALoc) 77 | return MayAlias; 78 | 79 | return NoAlias; 80 | } 81 | 82 | int 83 | escapes(Ref r, Fn *fn) 84 | { 85 | Alias *a; 86 | 87 | if (rtype(r) != RTmp) 88 | return 1; 89 | a = &fn->tmp[r.val].alias; 90 | return !astack(a->type) || a->slot->type == AEsc; 91 | } 92 | 93 | static void 94 | esc(Ref r, Fn *fn) 95 | { 96 | Alias *a; 97 | 98 | assert(rtype(r) <= RType); 99 | if (rtype(r) == RTmp) { 100 | a = &fn->tmp[r.val].alias; 101 | if (astack(a->type)) 102 | a->slot->type = AEsc; 103 | } 104 | } 105 | 106 | void 107 | fillalias(Fn *fn) 108 | { 109 | uint n; 110 | Blk *b; 111 | Phi *p; 112 | Ins *i; 113 | Alias *a, a0, a1; 114 | 115 | for (n=0; nnblk; ++n) { 116 | b = fn->rpo[n]; 117 | for (p=b->phi; p; p=p->link) { 118 | assert(rtype(p->to) == RTmp); 119 | a = &fn->tmp[p->to.val].alias; 120 | assert(a->type == ABot); 121 | a->type = AUnk; 122 | a->base = p->to; 123 | a->offset = 0; 124 | a->slot = 0; 125 | } 126 | for (i=b->ins; i<&b->ins[b->nins]; ++i) { 127 | a = 0; 128 | if (!req(i->to, R)) { 129 | assert(rtype(i->to) == RTmp); 130 | a = &fn->tmp[i->to.val].alias; 131 | assert(a->type == ABot); 132 | if (Oalloc <= i->op && i->op <= Oalloc1) { 133 | a->type = ALoc; 134 | a->slot = a; 135 | } else { 136 | a->type = AUnk; 137 | a->slot = 0; 138 | } 139 | a->base = i->to; 140 | a->offset = 0; 141 | } 142 | if (i->op == Ocopy) { 143 | assert(a); 144 | getalias(a, i->arg[0], fn); 145 | } 146 | if (i->op == Oadd) { 147 | getalias(&a0, i->arg[0], fn); 148 | getalias(&a1, i->arg[1], fn); 149 | if (a0.type == ACon) { 150 | *a = a1; 151 | a->offset += a0.offset; 152 | } 153 | else if (a1.type == ACon) { 154 | *a = a0; 155 | a->offset += a1.offset; 156 | } 157 | } 158 | if (req(i->to, R) || a->type == AUnk) { 159 | if (!isload(i->op)) 160 | esc(i->arg[0], fn); 161 | if (!isstore(i->op)) 162 | esc(i->arg[1], fn); 163 | } 164 | } 165 | esc(b->jmp.arg, fn); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /all.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 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 Target Target; 32 | 33 | enum { 34 | NString = 32, 35 | NPred = 63, 36 | NIns = 1 << 20, 37 | NAlign = 3, 38 | NField = 32, 39 | NBit = CHAR_BIT * sizeof(bits), 40 | }; 41 | 42 | struct Target { 43 | int gpr0; /* first general purpose reg */ 44 | int ngpr; 45 | int fpr0; /* first floating point reg */ 46 | int nfpr; 47 | bits rglob; /* globally live regs (e.g., sp, fp) */ 48 | int nrglob; 49 | int *rsave; /* caller-save */ 50 | int nrsave[2]; 51 | bits (*retregs)(Ref, int[2]); 52 | bits (*argregs)(Ref, int[2]); 53 | int (*memargs)(int); 54 | void (*abi)(Fn *); 55 | void (*isel)(Fn *); 56 | void (*emitfn)(Fn *, FILE *); 57 | }; 58 | 59 | #define BIT(n) ((bits)1 << (n)) 60 | 61 | enum { 62 | RXX = 0, 63 | Tmp0 = NBit, /* first non-reg temporary */ 64 | }; 65 | 66 | struct BSet { 67 | uint nt; 68 | bits *t; 69 | }; 70 | 71 | struct Ref { 72 | uint type:3; 73 | uint val:29; 74 | }; 75 | 76 | enum { 77 | RTmp, 78 | RCon, 79 | RType, 80 | RSlot, 81 | RCall, 82 | RMem, 83 | }; 84 | 85 | #define R (Ref){0, 0} 86 | #define TMP(x) (Ref){RTmp, x} 87 | #define CON(x) (Ref){RCon, x} 88 | #define CON_Z CON(0) /* reserved zero constant */ 89 | #define SLOT(x) (Ref){RSlot, (x)&0x1fffffff} 90 | #define TYPE(x) (Ref){RType, x} 91 | #define CALL(x) (Ref){RCall, x} 92 | #define MEM(x) (Ref){RMem, x} 93 | 94 | static inline int req(Ref a, Ref b) 95 | { 96 | return a.type == b.type && a.val == b.val; 97 | } 98 | 99 | static inline int rtype(Ref r) 100 | { 101 | if (req(r, R)) 102 | return -1; 103 | return r.type; 104 | } 105 | 106 | enum CmpI { 107 | Cieq, 108 | Cine, 109 | Cisge, 110 | Cisgt, 111 | Cisle, 112 | Cislt, 113 | Ciuge, 114 | Ciugt, 115 | Ciule, 116 | Ciult, 117 | NCmpI, 118 | }; 119 | 120 | enum CmpF { 121 | Cfeq, 122 | Cfge, 123 | Cfgt, 124 | Cfle, 125 | Cflt, 126 | Cfne, 127 | Cfo, 128 | Cfuo, 129 | NCmpF, 130 | NCmp = NCmpI + NCmpF, 131 | }; 132 | 133 | enum O { 134 | Oxxx, 135 | #define O(op, x, y) O##op, 136 | #include "ops.h" 137 | NOp, 138 | }; 139 | 140 | enum J { 141 | Jxxx, 142 | #define JMPS(X) \ 143 | X(ret0) X(retw) X(retl) X(rets) \ 144 | X(retd) X(retc) X(jmp) X(jnz) \ 145 | X(jfieq) X(jfine) X(jfisge) X(jfisgt) \ 146 | X(jfisle) X(jfislt) X(jfiuge) X(jfiugt) \ 147 | X(jfiule) X(jfiult) X(jffeq) X(jffge) \ 148 | X(jffgt) X(jffle) X(jfflt) X(jffne) \ 149 | X(jffo) X(jffuo) 150 | #define X(j) J##j, 151 | JMPS(X) 152 | #undef X 153 | NJmp 154 | }; 155 | 156 | enum { 157 | Ocmpw = Oceqw, 158 | Ocmpw1 = Ocultw, 159 | Ocmpl = Oceql, 160 | Ocmpl1 = Ocultl, 161 | Ocmps = Oceqs, 162 | Ocmps1 = Ocuos, 163 | Ocmpd = Oceqd, 164 | Ocmpd1 = Ocuod, 165 | Oalloc = Oalloc4, 166 | Oalloc1 = Oalloc16, 167 | Oflag = Oflagieq, 168 | Oflag1 = Oflagfuo, 169 | NPubOp = Onop, 170 | Jjf = Jjfieq, 171 | Jjf1 = Jjffuo, 172 | }; 173 | 174 | #define isstore(o) (Ostoreb <= o && o <= Ostored) 175 | #define isload(o) (Oloadsb <= o && o <= Oload) 176 | #define isext(o) (Oextsb <= o && o <= Oextuw) 177 | #define ispar(o) (Opar <= o && o <= Opare) 178 | #define isarg(o) (Oarg <= o && o <= Oarge) 179 | #define isret(j) (Jret0 <= j && j <= Jretc) 180 | 181 | enum Class { 182 | Kx = -1, /* "top" class (see usecheck() and clsmerge()) */ 183 | Kw, 184 | Kl, 185 | Ks, 186 | Kd 187 | }; 188 | 189 | #define KWIDE(k) ((k)&1) 190 | #define KBASE(k) ((k)>>1) 191 | 192 | struct Op { 193 | char *name; 194 | short argcls[2][4]; 195 | int canfold; 196 | }; 197 | 198 | struct Ins { 199 | uint op:30; 200 | Ref to; 201 | Ref arg[2]; 202 | uint cls:2; 203 | }; 204 | 205 | struct Phi { 206 | Ref to; 207 | Ref arg[NPred]; 208 | Blk *blk[NPred]; 209 | uint narg; 210 | int cls; 211 | Phi *link; 212 | }; 213 | 214 | struct Blk { 215 | Phi *phi; 216 | Ins *ins; 217 | uint nins; 218 | struct { 219 | short type; 220 | Ref arg; 221 | } jmp; 222 | Blk *s1; 223 | Blk *s2; 224 | Blk *link; 225 | 226 | uint id; 227 | uint visit; 228 | 229 | Blk *idom; 230 | Blk *dom, *dlink; 231 | Blk **fron; 232 | uint nfron; 233 | 234 | Blk **pred; 235 | uint npred; 236 | BSet in[1], out[1], gen[1]; 237 | int nlive[2]; 238 | int loop; 239 | char name[NString]; 240 | }; 241 | 242 | struct Use { 243 | enum { 244 | UXXX, 245 | UPhi, 246 | UIns, 247 | UJmp, 248 | } type; 249 | int bid; 250 | union { 251 | Ins *ins; 252 | Phi *phi; 253 | } u; 254 | }; 255 | 256 | enum { 257 | NoAlias, 258 | MayAlias, 259 | MustAlias 260 | }; 261 | 262 | struct Alias { 263 | enum { 264 | ABot = 0, 265 | ALoc = 1, /* stack local */ 266 | ACon = 2, 267 | AEsc = 3, /* stack escaping */ 268 | ASym = 4, 269 | AUnk = 6, 270 | #define astack(t) ((t) & 1) 271 | } type; 272 | Ref base; 273 | uint32_t label; 274 | int64_t offset; 275 | Alias *slot; 276 | }; 277 | 278 | struct Tmp { 279 | char name[NString]; 280 | Use *use; 281 | uint ndef, nuse; 282 | uint cost; 283 | short slot; /* -1 for unset */ 284 | short cls; 285 | struct { 286 | int r; /* register or -1 */ 287 | int w; /* weight */ 288 | bits m; /* avoid these registers */ 289 | } hint; 290 | int phi; 291 | Alias alias; 292 | enum { 293 | WFull, 294 | Wsb, /* must match Oload/Oext order */ 295 | Wub, 296 | Wsh, 297 | Wuh, 298 | Wsw, 299 | Wuw 300 | } width; 301 | int visit; 302 | }; 303 | 304 | struct Con { 305 | enum { 306 | CUndef, 307 | CBits, 308 | CAddr, 309 | } type; 310 | uint32_t label; 311 | union { 312 | int64_t i; 313 | double d; 314 | float s; 315 | } bits; 316 | char flt; /* 1 to print as s, 2 to print as d */ 317 | char local; 318 | }; 319 | 320 | typedef struct Addr Addr; 321 | 322 | struct Addr { /* amd64 addressing */ 323 | Con offset; 324 | Ref base; 325 | Ref index; 326 | int scale; 327 | }; 328 | 329 | struct Fn { 330 | Blk *start; 331 | Tmp *tmp; 332 | Con *con; 333 | Mem *mem; 334 | int ntmp; 335 | int ncon; 336 | int nmem; 337 | uint nblk; 338 | int retty; /* index in typ[], -1 if no aggregate return */ 339 | Ref retr; 340 | Blk **rpo; 341 | bits reg; 342 | int slot; 343 | char export; 344 | char vararg; 345 | char dynalloc; 346 | char name[NString]; 347 | }; 348 | 349 | struct Typ { 350 | char name[NString]; 351 | int dark; 352 | int align; 353 | uint64_t size; 354 | uint nunion; 355 | struct Field { 356 | enum { 357 | FEnd, 358 | Fb, 359 | Fh, 360 | Fw, 361 | Fl, 362 | Fs, 363 | Fd, 364 | FPad, 365 | FTyp, 366 | } type; 367 | uint len; /* or index in typ[] for FTyp */ 368 | } (*fields)[NField+1]; 369 | }; 370 | 371 | struct Dat { 372 | enum { 373 | DStart, 374 | DEnd, 375 | DName, 376 | DAlign, 377 | DB, 378 | DH, 379 | DW, 380 | DL, 381 | DZ 382 | } type; 383 | union { 384 | int64_t num; 385 | double fltd; 386 | float flts; 387 | char *str; 388 | struct { 389 | char *nam; 390 | int64_t off; 391 | } ref; 392 | } u; 393 | char isref; 394 | char isstr; 395 | char export; 396 | }; 397 | 398 | /* main.c */ 399 | extern Target T; 400 | extern char debug['Z'+1]; 401 | 402 | /* util.c */ 403 | typedef enum { 404 | Pheap, /* free() necessary */ 405 | Pfn, /* discarded after processing the function */ 406 | } Pool; 407 | 408 | extern Typ *typ; 409 | extern Ins insb[NIns], *curi; 410 | uint32_t hash(char *); 411 | void die_(char *, char *, ...) __attribute__((noreturn)); 412 | void *emalloc(size_t); 413 | void *alloc(size_t); 414 | void freeall(void); 415 | void *vnew(ulong, size_t, Pool); 416 | void vfree(void *); 417 | void vgrow(void *, ulong); 418 | uint32_t intern(char *); 419 | char *str(uint32_t); 420 | int argcls(Ins *, int); 421 | int isreg(Ref); 422 | int iscmp(int, int *, int *); 423 | void emit(int, int, Ref, Ref, Ref); 424 | void emiti(Ins); 425 | void idup(Ins **, Ins *, ulong); 426 | Ins *icpy(Ins *, Ins *, ulong); 427 | int cmpop(int); 428 | int cmpneg(int); 429 | int clsmerge(short *, short); 430 | int phicls(int, Tmp *); 431 | Ref newtmp(char *, int, Fn *); 432 | void chuse(Ref, int, Fn *); 433 | Ref getcon(int64_t, Fn *); 434 | void addcon(Con *, Con *); 435 | void blit(Ref, uint, Ref, uint, Fn *); 436 | void dumpts(BSet *, Tmp *, FILE *); 437 | 438 | void bsinit(BSet *, uint); 439 | void bszero(BSet *); 440 | uint bscount(BSet *); 441 | void bsset(BSet *, uint); 442 | void bsclr(BSet *, uint); 443 | void bscopy(BSet *, BSet *); 444 | void bsunion(BSet *, BSet *); 445 | void bsinter(BSet *, BSet *); 446 | void bsdiff(BSet *, BSet *); 447 | int bsequal(BSet *, BSet *); 448 | int bsiter(BSet *, int *); 449 | 450 | static inline int 451 | bshas(BSet *bs, uint elt) 452 | { 453 | assert(elt < bs->nt * NBit); 454 | return (bs->t[elt/NBit] & BIT(elt%NBit)) != 0; 455 | } 456 | 457 | /* parse.c */ 458 | extern Op optab[NOp]; 459 | void parse(FILE *, char *, void (Dat *), void (Fn *)); 460 | void printfn(Fn *, FILE *); 461 | void printref(Ref, Fn *, FILE *); 462 | void err(char *, ...) __attribute__((noreturn)); 463 | 464 | /* cfg.c */ 465 | Blk *blknew(void); 466 | void edgedel(Blk *, Blk **); 467 | void fillpreds(Fn *); 468 | void fillrpo(Fn *); 469 | void filldom(Fn *); 470 | int sdom(Blk *, Blk *); 471 | int dom(Blk *, Blk *); 472 | void fillfron(Fn *); 473 | void loopiter(Fn *, void (*)(Blk *, Blk *)); 474 | void fillloop(Fn *); 475 | void simpljmp(Fn *); 476 | 477 | /* mem.c */ 478 | void memopt(Fn *); 479 | 480 | /* alias.c */ 481 | void fillalias(Fn *); 482 | int alias(Ref, int, Ref, int, int *, Fn *); 483 | int escapes(Ref, Fn *); 484 | 485 | /* load.c */ 486 | int loadsz(Ins *); 487 | int storesz(Ins *); 488 | void loadopt(Fn *); 489 | 490 | /* ssa.c */ 491 | void filluse(Fn *); 492 | void fillpreds(Fn *); 493 | void fillrpo(Fn *); 494 | void ssa(Fn *); 495 | void ssacheck(Fn *); 496 | 497 | /* simpl.c */ 498 | void simpl(Fn *); 499 | 500 | /* copy.c */ 501 | void copy(Fn *); 502 | 503 | /* fold.c */ 504 | void fold(Fn *); 505 | 506 | /* live.c */ 507 | void liveon(BSet *, Blk *, Blk *); 508 | void filllive(Fn *); 509 | 510 | /* spill.c */ 511 | void fillcost(Fn *); 512 | void spill(Fn *); 513 | 514 | /* rega.c */ 515 | void rega(Fn *); 516 | 517 | /* gas.c */ 518 | extern char *gasloc; 519 | extern char *gassym; 520 | void gasemitdat(Dat *, FILE *); 521 | int gasstash(void *, int); 522 | void gasemitfin(FILE *); 523 | -------------------------------------------------------------------------------- /amd64/all.h: -------------------------------------------------------------------------------- 1 | #include "../all.h" 2 | 3 | typedef struct Amd64Op Amd64Op; 4 | 5 | enum Amd64Reg { 6 | RAX = RXX+1, /* caller-save */ 7 | RCX, 8 | RDX, 9 | RSI, 10 | RDI, 11 | R8, 12 | R9, 13 | R10, 14 | R11, 15 | 16 | RBX, /* callee-save */ 17 | R12, 18 | R13, 19 | R14, 20 | R15, 21 | 22 | RBP, /* globally live */ 23 | RSP, 24 | 25 | XMM0, /* sse */ 26 | XMM1, 27 | XMM2, 28 | XMM3, 29 | XMM4, 30 | XMM5, 31 | XMM6, 32 | XMM7, 33 | XMM8, 34 | XMM9, 35 | XMM10, 36 | XMM11, 37 | XMM12, 38 | XMM13, 39 | XMM14, 40 | XMM15, 41 | 42 | NFPR = XMM14 - XMM0 + 1, /* reserve XMM15 */ 43 | NGPR = RSP - RAX + 1, 44 | NGPS = R11 - RAX + 1, 45 | NFPS = NFPR, 46 | NCLR = R15 - RBX + 1, 47 | }; 48 | MAKESURE(reg_not_tmp, XMM15 < (int)Tmp0); 49 | 50 | struct Amd64Op { 51 | char nmem; 52 | char zflag; 53 | char lflag; 54 | }; 55 | 56 | /* targ.c */ 57 | extern Amd64Op amd64_op[]; 58 | 59 | /* sysv.c (abi) */ 60 | extern int amd64_sysv_rsave[]; 61 | extern int amd64_sysv_rclob[]; 62 | bits amd64_sysv_retregs(Ref, int[2]); 63 | bits amd64_sysv_argregs(Ref, int[2]); 64 | void amd64_sysv_abi(Fn *); 65 | 66 | /* isel.c */ 67 | void amd64_isel(Fn *); 68 | 69 | /* emit.c */ 70 | void amd64_emitfn(Fn *, FILE *); 71 | -------------------------------------------------------------------------------- /amd64/targ.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | Amd64Op amd64_op[NOp] = { 4 | #define O(op, t, x) [O##op] = 5 | #define X(nm, zf, lf) { nm, zf, lf, }, 6 | #include "../ops.h" 7 | }; 8 | 9 | static int 10 | amd64_memargs(int op) 11 | { 12 | return amd64_op[op].nmem; 13 | } 14 | 15 | Target T_amd64_sysv = { 16 | .gpr0 = RAX, 17 | .ngpr = NGPR, 18 | .fpr0 = XMM0, 19 | .nfpr = NFPR, 20 | .rglob = BIT(RBP) | BIT(RSP), 21 | .nrglob = 2, 22 | .rsave = amd64_sysv_rsave, 23 | .nrsave = {NGPS, NFPS}, 24 | .retregs = amd64_sysv_retregs, 25 | .argregs = amd64_sysv_argregs, 26 | .memargs = amd64_memargs, 27 | .abi = amd64_sysv_abi, 28 | .isel = amd64_isel, 29 | .emitfn = amd64_emitfn, 30 | }; 31 | -------------------------------------------------------------------------------- /arm64/all.h: -------------------------------------------------------------------------------- 1 | #include "../all.h" 2 | 3 | enum Arm64Reg { 4 | R0 = RXX + 1, 5 | R1, R2, R3, R4, R5, R6, R7, 6 | R8, R9, R10, R11, R12, R13, R14, R15, 7 | IP0, IP1, R18, R19, R20, R21, R22, R23, 8 | R24, R25, R26, R27, R28, FP, LR, SP, 9 | 10 | V0, V1, V2, V3, V4, V5, V6, V7, 11 | V8, V9, V10, V11, V12, V13, V14, V15, 12 | V16, V17, V18, V19, V20, V21, V22, V23, 13 | V24, V25, V26, V27, V28, V29, V30, /* V31, */ 14 | 15 | NFPR = V30 - V0 + 1, 16 | NGPR = SP - R0 + 1, 17 | NGPS = R18 - R0 + 1, 18 | NFPS = (V7 - V0 + 1) + (V30 - V16 + 1), 19 | NCLR = (R28 - R19 + 1) + (V15 - V8 + 1), 20 | }; 21 | MAKESURE(reg_not_tmp, V30 < (int)Tmp0); 22 | 23 | /* targ.c */ 24 | extern int arm64_rsave[]; 25 | extern int arm64_rclob[]; 26 | 27 | /* abi.c */ 28 | bits arm64_retregs(Ref, int[2]); 29 | bits arm64_argregs(Ref, int[2]); 30 | void arm64_abi(Fn *); 31 | 32 | /* isel.c */ 33 | int arm64_logimm(uint64_t, int); 34 | void arm64_isel(Fn *); 35 | 36 | /* emit.c */ 37 | void arm64_emitfn(Fn *, FILE *); 38 | -------------------------------------------------------------------------------- /arm64/emit.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | typedef struct E E; 4 | 5 | struct E { 6 | FILE *f; 7 | Fn *fn; 8 | uint64_t frame; 9 | uint padding; 10 | }; 11 | 12 | #define CMP(X) \ 13 | X(Cieq, "eq") \ 14 | X(Cine, "ne") \ 15 | X(Cisge, "ge") \ 16 | X(Cisgt, "gt") \ 17 | X(Cisle, "le") \ 18 | X(Cislt, "lt") \ 19 | X(Ciuge, "cs") \ 20 | X(Ciugt, "hi") \ 21 | X(Ciule, "ls") \ 22 | X(Ciult, "cc") \ 23 | X(NCmpI+Cfeq, "eq") \ 24 | X(NCmpI+Cfge, "ge") \ 25 | X(NCmpI+Cfgt, "gt") \ 26 | X(NCmpI+Cfle, "ls") \ 27 | X(NCmpI+Cflt, "mi") \ 28 | X(NCmpI+Cfne, "ne") \ 29 | X(NCmpI+Cfo, "vc") \ 30 | X(NCmpI+Cfuo, "vs") 31 | 32 | enum { 33 | Ki = -1, /* matches Kw and Kl */ 34 | Ka = -2, /* matches all classes */ 35 | }; 36 | 37 | static struct { 38 | short op; 39 | short cls; 40 | char *asm; 41 | } omap[] = { 42 | { Oadd, Ki, "add %=, %0, %1" }, 43 | { Oadd, Ka, "fadd %=, %0, %1" }, 44 | { Osub, Ki, "sub %=, %0, %1" }, 45 | { Osub, Ka, "fsub %=, %0, %1" }, 46 | { Oand, Ki, "and %=, %0, %1" }, 47 | { Oor, Ki, "orr %=, %0, %1" }, 48 | { Oxor, Ki, "eor %=, %0, %1" }, 49 | { Osar, Ki, "asr %=, %0, %1" }, 50 | { Oshr, Ki, "lsr %=, %0, %1" }, 51 | { Oshl, Ki, "lsl %=, %0, %1" }, 52 | { Omul, Ki, "mul %=, %0, %1" }, 53 | { Omul, Ka, "fmul %=, %0, %1" }, 54 | { Odiv, Ki, "sdiv %=, %0, %1" }, 55 | { Odiv, Ka, "fdiv %=, %0, %1" }, 56 | { Oudiv, Ki, "udiv %=, %0, %1" }, 57 | { Orem, Ki, "sdiv %?, %0, %1\n\tmsub\t%=, %?, %1, %0" }, 58 | { Ourem, Ki, "udiv %?, %0, %1\n\tmsub\t%=, %?, %1, %0" }, 59 | { Ocopy, Ki, "mov %=, %0" }, 60 | { Ocopy, Ka, "fmov %=, %0" }, 61 | { Oswap, Ki, "mov %?, %0\n\tmov\t%0, %1\n\tmov\t%1, %?" }, 62 | { Oswap, Ka, "fmov %?, %0\n\tfmov\t%0, %1\n\tfmov\t%1, %?" }, 63 | { Ostoreb, Kw, "strb %W0, %M1" }, 64 | { Ostoreh, Kw, "strh %W0, %M1" }, 65 | { Ostorew, Kw, "str %W0, %M1" }, 66 | { Ostorel, Kw, "str %L0, %M1" }, 67 | { Ostores, Kw, "str %S0, %M1" }, 68 | { Ostored, Kw, "str %D0, %M1" }, 69 | { Oloadsb, Ki, "ldrsb %=, %M0" }, 70 | { Oloadub, Ki, "ldrb %=, %M0" }, 71 | { Oloadsh, Ki, "ldrsh %=, %M0" }, 72 | { Oloaduh, Ki, "ldrh %=, %M0" }, 73 | { Oloadsw, Kw, "ldr %=, %M0" }, 74 | { Oloadsw, Kl, "ldrsw %=, %M0" }, 75 | { Oloaduw, Ki, "ldr %W=, %M0" }, 76 | { Oload, Ka, "ldr %=, %M0" }, 77 | { Oextsb, Ki, "sxtb %=, %W0" }, 78 | { Oextub, Ki, "uxtb %W=, %W0" }, 79 | { Oextsh, Ki, "sxth %=, %W0" }, 80 | { Oextuh, Ki, "uxth %W=, %W0" }, 81 | { Oextsw, Ki, "sxtw %L=, %W0" }, 82 | { Oextuw, Ki, "mov %W=, %W0" }, 83 | { Oexts, Kd, "fcvt %=, %S0" }, 84 | { Ocast, Kw, "fmov %=, %S0" }, 85 | { Ocast, Kl, "fmov %=, %D0" }, 86 | { Ocast, Ks, "fmov %=, %W0" }, 87 | { Ocast, Kd, "fmov %=, %L0" }, 88 | { Ostosi, Ka, "fcvtzs %=, %S0" }, 89 | { Odtosi, Ka, "fcvtzs %=, %D0" }, 90 | { Oswtof, Ka, "scvtf %=, %W0" }, 91 | { Osltof, Ka, "scvtf %=, %L0" }, 92 | { Ocall, Kw, "blr %L0" }, 93 | 94 | { Oacmp, Ki, "cmp %0, %1" }, 95 | { Oacmn, Ki, "cmn %0, %1" }, 96 | { Oafcmp, Ka, "fcmpe %0, %1" }, 97 | 98 | #define X(c, str) \ 99 | { Oflag+c, Ki, "cset %=, " str }, 100 | CMP(X) 101 | #undef X 102 | { NOp, 0, 0 } 103 | }; 104 | 105 | static char * 106 | rname(int r, int k) 107 | { 108 | static char buf[4]; 109 | 110 | if (r == SP) { 111 | assert(k == Kl); 112 | sprintf(buf, "sp"); 113 | } 114 | else if (R0 <= r && r <= LR) 115 | switch (k) { 116 | default: die("invalid class"); 117 | case Kw: sprintf(buf, "w%d", r-R0); break; 118 | case Kx: 119 | case Kl: sprintf(buf, "x%d", r-R0); break; 120 | } 121 | else if (V0 <= r && r <= V30) 122 | switch (k) { 123 | default: die("invalid class"); 124 | case Ks: sprintf(buf, "s%d", r-V0); break; 125 | case Kx: 126 | case Kd: sprintf(buf, "d%d", r-V0); break; 127 | } 128 | else 129 | die("invalid register"); 130 | return buf; 131 | } 132 | 133 | static uint64_t 134 | slot(int s, E *e) 135 | { 136 | s = ((int32_t)s << 3) >> 3; 137 | if (s == -1) 138 | return 16 + e->frame; 139 | if (s < 0) { 140 | if (e->fn->vararg) 141 | return 16 + e->frame + 192 - (s+2)*8; 142 | else 143 | return 16 + e->frame - (s+2)*8; 144 | } else 145 | return 16 + e->padding + 4 * s; 146 | } 147 | 148 | static void 149 | emitf(char *s, Ins *i, E *e) 150 | { 151 | Ref r; 152 | int k, c; 153 | Con *pc; 154 | unsigned n, sp; 155 | 156 | fputc('\t', e->f); 157 | 158 | sp = 0; 159 | for (;;) { 160 | k = i->cls; 161 | while ((c = *s++) != '%') 162 | if (c == ' ' && !sp) { 163 | fputc('\t', e->f); 164 | sp = 1; 165 | } else if ( !c) { 166 | fputc('\n', e->f); 167 | return; 168 | } else 169 | fputc(c, e->f); 170 | Switch: 171 | switch ((c = *s++)) { 172 | default: 173 | die("invalid escape"); 174 | case 'W': 175 | k = Kw; 176 | goto Switch; 177 | case 'L': 178 | k = Kl; 179 | goto Switch; 180 | case 'S': 181 | k = Ks; 182 | goto Switch; 183 | case 'D': 184 | k = Kd; 185 | goto Switch; 186 | case '?': 187 | if (KBASE(k) == 0) 188 | fputs(rname(R18, k), e->f); 189 | else 190 | fputs(k==Ks ? "s31" : "d31", e->f); 191 | break; 192 | case '=': 193 | case '0': 194 | r = c == '=' ? i->to : i->arg[0]; 195 | assert(isreg(r)); 196 | fputs(rname(r.val, k), e->f); 197 | break; 198 | case '1': 199 | r = i->arg[1]; 200 | switch (rtype(r)) { 201 | default: 202 | die("invalid second argument"); 203 | case RTmp: 204 | assert(isreg(r)); 205 | fputs(rname(r.val, k), e->f); 206 | break; 207 | case RCon: 208 | pc = &e->fn->con[r.val]; 209 | n = pc->bits.i; 210 | assert(pc->type == CBits); 211 | if (n & 0xfff000) 212 | fprintf(e->f, "#%u, lsl #12", n>>12); 213 | else 214 | fprintf(e->f, "#%u", n); 215 | break; 216 | } 217 | break; 218 | case 'M': 219 | c = *s++; 220 | assert(c == '0' || c == '1'); 221 | r = i->arg[c - '0']; 222 | assert(isreg(r) && "TODO emit non reg addresses"); 223 | fprintf(e->f, "[%s]", rname(r.val, Kl)); 224 | break; 225 | } 226 | } 227 | } 228 | 229 | static void 230 | loadcon(Con *c, int r, int k, FILE *f) 231 | { 232 | char *rn, *p, off[32]; 233 | int64_t n; 234 | int w, sh; 235 | 236 | w = KWIDE(k); 237 | rn = rname(r, k); 238 | n = c->bits.i; 239 | if (c->type == CAddr) { 240 | rn = rname(r, Kl); 241 | if (n) 242 | sprintf(off, "+%"PRIi64, n); 243 | else 244 | off[0] = 0; 245 | p = c->local ? ".L" : ""; 246 | fprintf(f, "\tadrp\t%s, %s%s%s\n", 247 | rn, p, str(c->label), off); 248 | fprintf(f, "\tadd\t%s, %s, #:lo12:%s%s%s\n", 249 | rn, rn, p, str(c->label), off); 250 | return; 251 | } 252 | assert(c->type == CBits); 253 | if (!w) 254 | n = (int32_t)n; 255 | if ((n | 0xffff) == -1 || arm64_logimm(n, k)) { 256 | fprintf(f, "\tmov\t%s, #%"PRIi64"\n", rn, n); 257 | } else { 258 | fprintf(f, "\tmov\t%s, #%d\n", 259 | rn, (int)(n & 0xffff)); 260 | for (sh=16; n>>=16; sh+=16) { 261 | if ((!w && sh == 32) || sh == 64) 262 | break; 263 | fprintf(f, "\tmovk\t%s, #0x%x, lsl #%d\n", 264 | rn, (unsigned)(n & 0xffff), sh); 265 | } 266 | } 267 | } 268 | 269 | static void 270 | emitins(Ins *i, E *e) 271 | { 272 | int o; 273 | 274 | switch (i->op) { 275 | default: 276 | Table: 277 | /* most instructions are just pulled out of 278 | * the table omap[], some special cases are 279 | * detailed below */ 280 | for (o=0;; o++) { 281 | /* this linear search should really be a binary 282 | * search */ 283 | if (omap[o].op == NOp) 284 | die("no match for %s(%c)", 285 | optab[i->op].name, "wlsd"[i->cls]); 286 | if (omap[o].op == i->op) 287 | if (omap[o].cls == i->cls || omap[o].cls == Ka 288 | || (omap[o].cls == Ki && KBASE(i->cls) == 0)) 289 | break; 290 | } 291 | emitf(omap[o].asm, i, e); 292 | break; 293 | case Onop: 294 | break; 295 | case Ocopy: 296 | if (req(i->to, i->arg[0])) 297 | break; 298 | if (rtype(i->arg[0]) != RCon) 299 | goto Table; 300 | loadcon(&e->fn->con[i->arg[0].val], i->to.val, i->cls, e->f); 301 | break; 302 | case Oaddr: 303 | assert(rtype(i->arg[0]) == RSlot); 304 | fprintf(e->f, "\tadd\t%s, x29, #%"PRIu64"\n", 305 | rname(i->to.val, Kl), slot(i->arg[0].val, e) 306 | ); 307 | break; 308 | } 309 | } 310 | 311 | static void 312 | framelayout(E *e) 313 | { 314 | int *r; 315 | uint o; 316 | uint64_t f; 317 | 318 | for (o=0, r=arm64_rclob; *r>=0; r++) 319 | o += 1 & (e->fn->reg >> *r); 320 | f = e->fn->slot; 321 | f = (f + 3) & -4; 322 | o += o & 1; 323 | e->padding = 4*(f-e->fn->slot); 324 | e->frame = 4*f + 8*o; 325 | } 326 | 327 | /* 328 | 329 | Stack-frame layout: 330 | 331 | +=============+ 332 | | varargs | 333 | | save area | 334 | +-------------+ 335 | | callee-save | ^ 336 | | registers | | 337 | +-------------+ | 338 | | ... | | 339 | | spill slots | | 340 | | ... | | e->frame 341 | +-------------+ | 342 | | ... | | 343 | | locals | | 344 | | ... | | 345 | +-------------+ | 346 | | e->padding | v 347 | +-------------+ 348 | | saved x29 | 349 | | saved x30 | 350 | +=============+ <- x29 351 | 352 | */ 353 | 354 | void 355 | arm64_emitfn(Fn *fn, FILE *out) 356 | { 357 | static char *ctoa[] = { 358 | #define X(c, s) [c] = s, 359 | CMP(X) 360 | #undef X 361 | }; 362 | static int id0; 363 | int n, c, lbl, *r; 364 | uint64_t o; 365 | Blk *b, *s; 366 | Ins *i; 367 | E *e; 368 | 369 | e = &(E){.f = out, .fn = fn}; 370 | framelayout(e); 371 | 372 | fprintf(e->f, ".text\n"); 373 | if (e->fn->export) 374 | fprintf(e->f, ".globl %s\n", e->fn->name); 375 | fprintf(e->f, "%s:\n", e->fn->name); 376 | 377 | if (e->fn->vararg) { 378 | for (n=7; n>=0; n--) 379 | fprintf(e->f, "\tstr\tq%d, [sp, -16]!\n", n); 380 | for (n=7; n>=0; n--) 381 | fprintf(e->f, "\tstr\tx%d, [sp, -8]!\n", n); 382 | } 383 | 384 | if (e->frame + 16 > 512) 385 | fprintf(e->f, 386 | "\tsub\tsp, sp, #%"PRIu64"\n" 387 | "\tstp\tx29, x30, [sp, -16]!\n", 388 | e->frame 389 | ); 390 | else 391 | fprintf(e->f, 392 | "\tstp\tx29, x30, [sp, -%"PRIu64"]!\n", 393 | e->frame + 16 394 | ); 395 | fputs("\tadd\tx29, sp, 0\n", e->f); 396 | for (o=e->frame+16, r=arm64_rclob; *r>=0; r++) 397 | if (e->fn->reg & BIT(*r)) 398 | fprintf(e->f, 399 | "\tstr\t%s, [sp, %"PRIu64"]\n", 400 | rname(*r, Kx), o -= 8 401 | ); 402 | 403 | for (lbl=0, b=e->fn->start; b; b=b->link) { 404 | if (lbl || b->npred > 1) 405 | fprintf(e->f, ".L%d:\n", id0+b->id); 406 | for (i=b->ins; i!=&b->ins[b->nins]; i++) 407 | emitins(i, e); 408 | lbl = 1; 409 | switch (b->jmp.type) { 410 | case Jret0: 411 | for (o=e->frame+16, r=arm64_rclob; *r>=0; r++) 412 | if (e->fn->reg & BIT(*r)) 413 | fprintf(e->f, 414 | "\tldr\t%s, [sp, %"PRIu64"]\n", 415 | rname(*r, Kx), o -= 8 416 | ); 417 | o = e->frame + 16; 418 | if (e->fn->vararg) 419 | o += 192; 420 | if (o > 504) 421 | fprintf(e->f, 422 | "\tldp\tx29, x30, [sp], 16\n" 423 | "\tadd\tsp, sp, #%"PRIu64"\n", 424 | o - 16 425 | ); 426 | else 427 | fprintf(e->f, 428 | "\tldp\tx29, x30, [sp], %"PRIu64"\n", 429 | o 430 | ); 431 | fprintf(e->f, "\tret\n"); 432 | break; 433 | case Jjmp: 434 | Jmp: 435 | if (b->s1 != b->link) 436 | fprintf(e->f, "\tb\t.L%d\n", id0+b->s1->id); 437 | else 438 | lbl = 0; 439 | break; 440 | default: 441 | c = b->jmp.type - Jjf; 442 | if (c < 0 || c > NCmp) 443 | die("unhandled jump %d", b->jmp.type); 444 | if (b->link == b->s2) { 445 | s = b->s1; 446 | b->s1 = b->s2; 447 | b->s2 = s; 448 | } else 449 | c = cmpneg(c); 450 | fprintf(e->f, "\tb%s\t.L%d\n", ctoa[c], id0+b->s2->id); 451 | goto Jmp; 452 | } 453 | } 454 | id0 += e->fn->nblk; 455 | } 456 | -------------------------------------------------------------------------------- /arm64/isel.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | enum Imm { 4 | Iother, 5 | Iplo12, 6 | Iphi12, 7 | Iplo24, 8 | Inlo12, 9 | Inhi12, 10 | Inlo24 11 | }; 12 | 13 | static enum Imm 14 | imm(Con *c, int k, int64_t *pn) 15 | { 16 | int64_t n; 17 | int i; 18 | 19 | if (c->type != CBits) 20 | return Iother; 21 | n = c->bits.i; 22 | if (k == Kw) 23 | n = (int32_t)n; 24 | i = Iplo12; 25 | if (n < 0) { 26 | i = Inlo12; 27 | n = -n; 28 | } 29 | *pn = n; 30 | if ((n & 0x000fff) == n) 31 | return i; 32 | if ((n & 0xfff000) == n) 33 | return i + 1; 34 | if ((n & 0xffffff) == n) 35 | return i + 2; 36 | return Iother; 37 | } 38 | 39 | int 40 | arm64_logimm(uint64_t x, int k) 41 | { 42 | uint64_t n; 43 | 44 | if (k == Kw) 45 | x = (x & 0xffffffff) | x << 32; 46 | if (x & 1) 47 | x = ~x; 48 | if (x == 0) 49 | return 0; 50 | if (x == 0xaaaaaaaaaaaaaaaa) 51 | return 1; 52 | n = x & 0xf; 53 | if (0x1111111111111111 * n == x) 54 | goto Check; 55 | n = x & 0xff; 56 | if (0x0101010101010101 * n == x) 57 | goto Check; 58 | n = x & 0xffff; 59 | if (0x0001000100010001 * n == x) 60 | goto Check; 61 | n = x & 0xffffffff; 62 | if (0x0000000100000001 * n == x) 63 | goto Check; 64 | n = x; 65 | Check: 66 | return (n & (n + (n & -n))) == 0; 67 | } 68 | 69 | static void 70 | fixarg(Ref *pr, int k, int phi, Fn *fn) 71 | { 72 | char buf[32]; 73 | Ref r0, r1, r2; 74 | int s, n; 75 | Con *c; 76 | 77 | r0 = *pr; 78 | switch (rtype(r0)) { 79 | case RCon: 80 | if (KBASE(k) == 0 && phi) 81 | return; 82 | r1 = newtmp("isel", k, fn); 83 | if (KBASE(k) == 0) { 84 | emit(Ocopy, k, r1, r0, R); 85 | } else { 86 | c = &fn->con[r0.val]; 87 | n = gasstash(&c->bits, KWIDE(k) ? 8 : 4); 88 | vgrow(&fn->con, ++fn->ncon); 89 | c = &fn->con[fn->ncon-1]; 90 | sprintf(buf, "fp%d", n); 91 | *c = (Con){.type = CAddr, .local = 1}; 92 | c->label = intern(buf); 93 | r2 = newtmp("isel", Kl, fn); 94 | emit(Oload, k, r1, r2, R); 95 | emit(Ocopy, Kl, r2, CON(c-fn->con), R); 96 | } 97 | *pr = r1; 98 | break; 99 | case RTmp: 100 | s = fn->tmp[r0.val].slot; 101 | if (s == -1) 102 | break; 103 | r1 = newtmp("isel", Kl, fn); 104 | emit(Oaddr, Kl, r1, SLOT(s), R); 105 | *pr = r1; 106 | break; 107 | } 108 | } 109 | 110 | static int 111 | selcmp(Ref arg[2], int k, Fn *fn) 112 | { 113 | Ref r, *iarg; 114 | Con *c; 115 | int swap, cmp, fix; 116 | int64_t n; 117 | 118 | if (KBASE(k) == 1) { 119 | emit(Oafcmp, k, R, arg[0], arg[1]); 120 | iarg = curi->arg; 121 | fixarg(&iarg[0], k, 0, fn); 122 | fixarg(&iarg[1], k, 0, fn); 123 | return 0; 124 | } 125 | swap = rtype(arg[0]) == RCon; 126 | if (swap) { 127 | r = arg[1]; 128 | arg[1] = arg[0]; 129 | arg[0] = r; 130 | } 131 | fix = 1; 132 | cmp = Oacmp; 133 | r = arg[1]; 134 | if (rtype(r) == RCon) { 135 | c = &fn->con[r.val]; 136 | switch (imm(c, k, &n)) { 137 | default: 138 | break; 139 | case Iplo12: 140 | case Iphi12: 141 | fix = 0; 142 | break; 143 | case Inlo12: 144 | case Inhi12: 145 | cmp = Oacmn; 146 | r = getcon(n, fn); 147 | fix = 0; 148 | break; 149 | } 150 | } 151 | emit(cmp, k, R, arg[0], r); 152 | iarg = curi->arg; 153 | fixarg(&iarg[0], k, 0, fn); 154 | if (fix) 155 | fixarg(&iarg[1], k, 0, fn); 156 | return swap; 157 | } 158 | 159 | static void 160 | sel(Ins i, Fn *fn) 161 | { 162 | Ref *iarg; 163 | Ins *i0; 164 | int ck, cc; 165 | 166 | if (iscmp(i.op, &ck, &cc)) { 167 | emit(Oflag, i.cls, i.to, R, R); 168 | i0 = curi; 169 | if (selcmp(i.arg, ck, fn)) 170 | i0->op += cmpop(cc); 171 | else 172 | i0->op += cc; 173 | } else if (i.op != Onop) { 174 | emiti(i); 175 | iarg = curi->arg; /* fixarg() can change curi */ 176 | fixarg(&iarg[0], argcls(&i, 0), 0, fn); 177 | fixarg(&iarg[1], argcls(&i, 1), 0, fn); 178 | } 179 | } 180 | 181 | static void 182 | seljmp(Blk *b, Fn *fn) 183 | { 184 | Ref r; 185 | Ins *i, *ir; 186 | int ck, cc, use; 187 | 188 | switch (b->jmp.type) { 189 | default: 190 | assert(0 && "TODO 2"); 191 | break; 192 | case Jret0: 193 | case Jjmp: 194 | return; 195 | case Jjnz: 196 | break; 197 | } 198 | r = b->jmp.arg; 199 | use = -1; 200 | b->jmp.arg = R; 201 | ir = 0; 202 | i = &b->ins[b->nins]; 203 | while (i > b->ins) 204 | if (req((--i)->to, r)) { 205 | use = fn->tmp[r.val].nuse; 206 | ir = i; 207 | break; 208 | } 209 | if (ir && use == 1 210 | && iscmp(ir->op, &ck, &cc)) { 211 | if (selcmp(ir->arg, ck, fn)) 212 | cc = cmpop(cc); 213 | b->jmp.type = Jjf + cc; 214 | *ir = (Ins){.op = Onop}; 215 | } 216 | else { 217 | selcmp((Ref[]){r, CON_Z}, Kw, fn); 218 | b->jmp.type = Jjfine; 219 | } 220 | } 221 | 222 | void 223 | arm64_isel(Fn *fn) 224 | { 225 | Blk *b, **sb; 226 | Ins *i; 227 | Phi *p; 228 | uint n, al; 229 | int64_t sz; 230 | 231 | /* assign slots to fast allocs */ 232 | b = fn->start; 233 | /* specific to NAlign == 3 */ /* or change n=4 and sz /= 4 below */ 234 | for (al=Oalloc, n=4; al<=Oalloc1; al++, n*=2) 235 | for (i=b->ins; i-b->ins < b->nins; i++) 236 | if (i->op == al) { 237 | if (rtype(i->arg[0]) != RCon) 238 | break; 239 | sz = fn->con[i->arg[0].val].bits.i; 240 | if (sz < 0 || sz >= INT_MAX-15) 241 | err("invalid alloc size %"PRId64, sz); 242 | sz = (sz + n-1) & -n; 243 | sz /= 4; 244 | fn->tmp[i->to.val].slot = fn->slot; 245 | fn->slot += sz; 246 | *i = (Ins){.op = Onop}; 247 | } 248 | 249 | for (b=fn->start; b; b=b->link) { 250 | curi = &insb[NIns]; 251 | for (sb=(Blk*[3]){b->s1, b->s2, 0}; *sb; sb++) 252 | for (p=(*sb)->phi; p; p=p->link) { 253 | for (n=0; p->blk[n] != b; n++) 254 | assert(n+1 < p->narg); 255 | fixarg(&p->arg[n], p->cls, 1, fn); 256 | } 257 | seljmp(b, fn); 258 | for (i=&b->ins[b->nins]; i!=b->ins;) 259 | sel(*--i, fn); 260 | b->nins = &insb[NIns] - curi; 261 | idup(&b->ins, curi, b->nins); 262 | } 263 | 264 | if (debug['I']) { 265 | fprintf(stderr, "\n> After instruction selection:\n"); 266 | printfn(fn, stderr); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /arm64/targ.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | int arm64_rsave[] = { 4 | R0, R1, R2, R3, R4, R5, R6, R7, 5 | R8, R9, R10, R11, R12, R13, R14, R15, 6 | IP0, IP1, R18, 7 | V0, V1, V2, V3, V4, V5, V6, V7, 8 | V16, V17, V18, V19, V20, V21, V22, V23, 9 | V24, V25, V26, V27, V28, V29, V30, 10 | -1 11 | }; 12 | int arm64_rclob[] = { 13 | R19, R20, R21, R22, R23, R24, R25, R26, 14 | R27, R28, 15 | V8, V9, V10, V11, V12, V13, V14, V15, 16 | -1 17 | }; 18 | 19 | #define RGLOB (BIT(FP) | BIT(SP) | BIT(R18)) 20 | 21 | static int 22 | arm64_memargs(int op) 23 | { 24 | (void)op; 25 | return 0; 26 | } 27 | 28 | Target T_arm64 = { 29 | .gpr0 = R0, 30 | .ngpr = NGPR, 31 | .fpr0 = V0, 32 | .nfpr = NFPR, 33 | .rglob = RGLOB, 34 | .nrglob = 3, 35 | .rsave = arm64_rsave, 36 | .nrsave = {NGPS, NFPS}, 37 | .retregs = arm64_retregs, 38 | .argregs = arm64_argregs, 39 | .memargs = arm64_memargs, 40 | .abi = arm64_abi, 41 | .isel = arm64_isel, 42 | .emitfn = arm64_emitfn, 43 | }; 44 | 45 | MAKESURE(globals_are_not_arguments, 46 | (RGLOB & (BIT(R8+1) - 1)) == 0 47 | ); 48 | MAKESURE(arrays_size_ok, 49 | sizeof arm64_rsave == (NGPS+NFPS+1) * sizeof(int) && 50 | sizeof arm64_rclob == (NCLR+1) * sizeof(int) 51 | ); 52 | -------------------------------------------------------------------------------- /cfg.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | Blk * 4 | blknew() 5 | { 6 | static Blk z; 7 | Blk *b; 8 | 9 | b = alloc(sizeof *b); 10 | *b = z; 11 | return b; 12 | } 13 | 14 | void 15 | edgedel(Blk *bs, Blk **pbd) 16 | { 17 | Blk *bd; 18 | Phi *p; 19 | uint a; 20 | int mult; 21 | 22 | bd = *pbd; 23 | mult = 1 + (bs->s1 == bs->s2); 24 | *pbd = 0; 25 | if (!bd || mult > 1) 26 | return; 27 | for (p=bd->phi; p; p=p->link) { 28 | for (a=0; p->blk[a]!=bs; a++) 29 | assert(a+1narg); 30 | p->narg--; 31 | memmove(&p->blk[a], &p->blk[a+1], 32 | sizeof p->blk[0] * (p->narg-a)); 33 | memmove(&p->arg[a], &p->arg[a+1], 34 | sizeof p->arg[0] * (p->narg-a)); 35 | } 36 | if (bd->npred != 0) { 37 | for (a=0; bd->pred[a]!=bs; a++) 38 | assert(a+1npred); 39 | bd->npred--; 40 | memmove(&bd->pred[a], &bd->pred[a+1], 41 | sizeof bd->pred[0] * (bd->npred-a)); 42 | } 43 | } 44 | 45 | static void 46 | addpred(Blk *bp, Blk *bc) 47 | { 48 | if (!bc->pred) { 49 | bc->pred = alloc(bc->npred * sizeof bc->pred[0]); 50 | bc->visit = 0; 51 | } 52 | bc->pred[bc->visit++] = bp; 53 | } 54 | 55 | /* fill predecessors information in blocks */ 56 | void 57 | fillpreds(Fn *f) 58 | { 59 | Blk *b; 60 | 61 | for (b=f->start; b; b=b->link) { 62 | b->npred = 0; 63 | b->pred = 0; 64 | } 65 | for (b=f->start; b; b=b->link) { 66 | if (b->s1) 67 | b->s1->npred++; 68 | if (b->s2 && b->s2 != b->s1) 69 | b->s2->npred++; 70 | } 71 | for (b=f->start; b; b=b->link) { 72 | if (b->s1) 73 | addpred(b, b->s1); 74 | if (b->s2 && b->s2 != b->s1) 75 | addpred(b, b->s2); 76 | } 77 | } 78 | 79 | static int 80 | rporec(Blk *b, uint x) 81 | { 82 | Blk *s1, *s2; 83 | 84 | if (!b || b->id != -1u) 85 | return x; 86 | b->id = 1; 87 | s1 = b->s1; 88 | s2 = b->s2; 89 | if (s1 && s2 && s1->loop > s2->loop) { 90 | s1 = b->s2; 91 | s2 = b->s1; 92 | } 93 | x = rporec(s1, x); 94 | x = rporec(s2, x); 95 | b->id = x; 96 | assert(x != -1u); 97 | return x - 1; 98 | } 99 | 100 | /* fill the rpo information */ 101 | void 102 | fillrpo(Fn *f) 103 | { 104 | uint n; 105 | Blk *b, **p; 106 | 107 | for (b=f->start; b; b=b->link) 108 | b->id = -1u; 109 | n = 1 + rporec(f->start, f->nblk-1); 110 | f->nblk -= n; 111 | f->rpo = alloc(f->nblk * sizeof f->rpo[0]); 112 | for (p=&f->start; (b=*p);) { 113 | if (b->id == -1u) { 114 | edgedel(b, &b->s1); 115 | edgedel(b, &b->s2); 116 | *p = b->link; 117 | } else { 118 | b->id -= n; 119 | f->rpo[b->id] = b; 120 | p = &b->link; 121 | } 122 | } 123 | } 124 | 125 | /* for dominators computation, read 126 | * "A Simple, Fast Dominance Algorithm" 127 | * by K. Cooper, T. Harvey, and K. Kennedy. 128 | */ 129 | 130 | static Blk * 131 | inter(Blk *b1, Blk *b2) 132 | { 133 | Blk *bt; 134 | 135 | if (b1 == 0) 136 | return b2; 137 | while (b1 != b2) { 138 | if (b1->id < b2->id) { 139 | bt = b1; 140 | b1 = b2; 141 | b2 = bt; 142 | } 143 | while (b1->id > b2->id) { 144 | b1 = b1->idom; 145 | assert(b1); 146 | } 147 | } 148 | return b1; 149 | } 150 | 151 | void 152 | filldom(Fn *fn) 153 | { 154 | Blk *b, *d; 155 | int ch; 156 | uint n, p; 157 | 158 | for (b=fn->start; b; b=b->link) { 159 | b->idom = 0; 160 | b->dom = 0; 161 | b->dlink = 0; 162 | } 163 | do { 164 | ch = 0; 165 | for (n=1; nnblk; n++) { 166 | b = fn->rpo[n]; 167 | d = 0; 168 | for (p=0; pnpred; p++) 169 | if (b->pred[p]->idom 170 | || b->pred[p] == fn->start) 171 | d = inter(d, b->pred[p]); 172 | if (d != b->idom) { 173 | ch++; 174 | b->idom = d; 175 | } 176 | } 177 | } while (ch); 178 | for (b=fn->start; b; b=b->link) 179 | if ((d=b->idom)) { 180 | assert(d != b); 181 | b->dlink = d->dom; 182 | d->dom = b; 183 | } 184 | } 185 | 186 | int 187 | sdom(Blk *b1, Blk *b2) 188 | { 189 | assert(b1 && b2); 190 | if (b1 == b2) 191 | return 0; 192 | while (b2->id > b1->id) 193 | b2 = b2->idom; 194 | return b1 == b2; 195 | } 196 | 197 | int 198 | dom(Blk *b1, Blk *b2) 199 | { 200 | return b1 == b2 || sdom(b1, b2); 201 | } 202 | 203 | static void 204 | addfron(Blk *a, Blk *b) 205 | { 206 | uint n; 207 | 208 | for (n=0; nnfron; n++) 209 | if (a->fron[n] == b) 210 | return; 211 | if (!a->nfron) 212 | a->fron = vnew(++a->nfron, sizeof a->fron[0], Pfn); 213 | else 214 | vgrow(&a->fron, ++a->nfron); 215 | a->fron[a->nfron-1] = b; 216 | } 217 | 218 | /* fill the dominance frontier */ 219 | void 220 | fillfron(Fn *fn) 221 | { 222 | Blk *a, *b; 223 | 224 | for (b=fn->start; b; b=b->link) 225 | b->nfron = 0; 226 | for (b=fn->start; b; b=b->link) { 227 | if (b->s1) 228 | for (a=b; !sdom(a, b->s1); a=a->idom) 229 | addfron(a, b->s1); 230 | if (b->s2) 231 | for (a=b; !sdom(a, b->s2); a=a->idom) 232 | addfron(a, b->s2); 233 | } 234 | } 235 | 236 | static void 237 | loopmark(Blk *hd, Blk *b, void f(Blk *, Blk *)) 238 | { 239 | uint p; 240 | 241 | if (b->id < hd->id || b->visit == hd->id) 242 | return; 243 | b->visit = hd->id; 244 | f(hd, b); 245 | for (p=0; pnpred; ++p) 246 | loopmark(hd, b->pred[p], f); 247 | } 248 | 249 | void 250 | loopiter(Fn *fn, void f(Blk *, Blk *)) 251 | { 252 | uint n, p; 253 | Blk *b; 254 | 255 | for (b=fn->start; b; b=b->link) 256 | b->visit = -1u; 257 | for (n=0; nnblk; ++n) { 258 | b = fn->rpo[n]; 259 | for (p=0; pnpred; ++p) 260 | if (b->pred[p]->id >= n) 261 | loopmark(b, b->pred[p], f); 262 | } 263 | } 264 | 265 | void 266 | multloop(Blk *hd, Blk *b) 267 | { 268 | (void)hd; 269 | b->loop *= 10; 270 | } 271 | 272 | void 273 | fillloop(Fn *fn) 274 | { 275 | Blk *b; 276 | 277 | for (b=fn->start; b; b=b->link) 278 | b->loop = 1; 279 | loopiter(fn, multloop); 280 | } 281 | 282 | static void 283 | uffind(Blk **pb, Blk **uf) 284 | { 285 | Blk **pb1; 286 | 287 | pb1 = &uf[(*pb)->id]; 288 | if (*pb1) { 289 | uffind(pb1, uf); 290 | *pb = *pb1; 291 | } 292 | } 293 | 294 | /* requires rpo and no phis, breaks cfg */ 295 | void 296 | simpljmp(Fn *fn) 297 | { 298 | 299 | Blk **uf; /* union-find */ 300 | Blk *b; 301 | int c; 302 | 303 | uf = emalloc(fn->nblk * sizeof uf[0]); 304 | for (b=fn->start; b; b=b->link) { 305 | assert(!b->phi); 306 | if (b->nins == 0) 307 | if (b->jmp.type == Jjmp) { 308 | uffind(&b->s1, uf); 309 | if (b->s1 != b) 310 | uf[b->id] = b->s1; 311 | } 312 | } 313 | for (b=fn->start; b; b=b->link) { 314 | if (b->s1) 315 | uffind(&b->s1, uf); 316 | if (b->s2) 317 | uffind(&b->s2, uf); 318 | c = b->jmp.type - Jjf; 319 | if (0 <= c && c <= NCmp) 320 | if (b->s1 == b->s2) { 321 | b->jmp.type = Jjmp; 322 | b->s2 = 0; 323 | } 324 | } 325 | free(uf); 326 | } 327 | -------------------------------------------------------------------------------- /copy.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | typedef struct RList RList; 4 | struct RList { 5 | int t; 6 | RList *l; 7 | }; 8 | 9 | static Ref 10 | copyof(Ref r, Ref *cp) 11 | { 12 | if (rtype(r) == RTmp) 13 | return cp[r.val]; 14 | else 15 | return r; 16 | } 17 | 18 | static void 19 | update(Ref r, Ref rcp, Ref *cp, RList ***pw) 20 | { 21 | RList *l; 22 | 23 | if (!req(cp[r.val], rcp)) { 24 | cp[r.val] = rcp; 25 | l = emalloc(sizeof *l); 26 | l->t = r.val; 27 | l->l = 0; 28 | **pw = l; 29 | *pw = &l->l; 30 | } 31 | } 32 | 33 | static void 34 | visitphi(Phi *p, Ref *cp, RList ***pw) 35 | { 36 | uint a; 37 | Ref r, r1; 38 | 39 | r = R; 40 | for (a=0; anarg; a++) { 41 | r1 = copyof(p->arg[a], cp); 42 | if (req(r1, R) || req(r1, p->to)) 43 | continue; 44 | if (req(r, R) || req(r, r1)) 45 | r = r1; 46 | else { 47 | r = p->to; 48 | break; 49 | } 50 | } 51 | update(p->to, r, cp, pw); 52 | } 53 | 54 | static int 55 | iscopy(Ins *i, Ref r, Fn *fn) 56 | { 57 | static bits extcpy[] = { 58 | [WFull] = 0, 59 | [Wsb] = BIT(Wsb) | BIT(Wsh) | BIT(Wsw), 60 | [Wub] = BIT(Wub) | BIT(Wuh) | BIT(Wuw), 61 | [Wsh] = BIT(Wsh) | BIT(Wsw), 62 | [Wuh] = BIT(Wuh) | BIT(Wuw), 63 | [Wsw] = BIT(Wsw), 64 | [Wuw] = BIT(Wuw), 65 | }; 66 | bits b; 67 | Tmp *t; 68 | 69 | if (i->op == Ocopy) 70 | return 1; 71 | if (!isext(i->op) || rtype(r) != RTmp) 72 | return 0; 73 | if (i->op == Oextsw || i->op == Oextuw) 74 | if (i->cls == Kw) 75 | return 1; 76 | 77 | t = &fn->tmp[r.val]; 78 | assert(KBASE(t->cls) == 0); 79 | if (i->cls == Kl && t->cls == Kw) 80 | return 0; 81 | b = extcpy[t->width]; 82 | return (BIT(Wsb + (i->op-Oextsb)) & b) != 0; 83 | } 84 | 85 | static void 86 | visitins(Ins *i, Ref *cp, RList ***pw, Fn *fn) 87 | { 88 | Ref r; 89 | 90 | r = copyof(i->arg[0], cp); 91 | if (iscopy(i, r, fn)) { 92 | update(i->to, r, cp, pw); 93 | } else if (!req(i->to, R)) { 94 | assert(rtype(i->to) == RTmp); 95 | update(i->to, i->to, cp, pw); 96 | } 97 | } 98 | 99 | static void 100 | subst(Ref *r, Ref *cp) 101 | { 102 | assert((rtype(*r) != RTmp || !req(copyof(*r, cp), R)) && "ssa invariant broken"); 103 | *r = copyof(*r, cp); 104 | } 105 | 106 | void 107 | copy(Fn *fn) 108 | { 109 | Blk *b; 110 | Ref *cp, r; 111 | RList *w, *w1, **pw; 112 | Use *u, *u1; 113 | Ins *i; 114 | Phi *p, **pp; 115 | uint a; 116 | int t; 117 | 118 | w = 0; 119 | pw = &w; 120 | cp = emalloc(fn->ntmp * sizeof cp[0]); 121 | for (b=fn->start; b; b=b->link) { 122 | for (p=b->phi; p; p=p->link) 123 | visitphi(p, cp, &pw); 124 | for (i=b->ins; i-b->ins < b->nins; i++) 125 | visitins(i, cp, &pw, fn); 126 | } 127 | while ((w1=w)) { 128 | t = w->t; 129 | u = fn->tmp[t].use; 130 | u1 = u + fn->tmp[t].nuse; 131 | for (; utype) { 133 | case UPhi: 134 | visitphi(u->u.phi, cp, &pw); 135 | break; 136 | case UIns: 137 | visitins(u->u.ins, cp, &pw, fn); 138 | break; 139 | case UJmp: 140 | break; 141 | default: 142 | die("invalid use %d", u->type); 143 | } 144 | w = w->l; 145 | free(w1); 146 | } 147 | for (b=fn->start; b; b=b->link) { 148 | for (pp=&b->phi; (p=*pp);) { 149 | r = cp[p->to.val]; 150 | if (!req(r, p->to)) { 151 | *pp = p->link; 152 | continue; 153 | } 154 | for (a=0; anarg; a++) 155 | subst(&p->arg[a], cp); 156 | pp=&p->link; 157 | } 158 | for (i=b->ins; i-b->ins < b->nins; i++) { 159 | r = copyof(i->to, cp); 160 | if (!req(r, i->to)) { 161 | *i = (Ins){.op = Onop}; 162 | continue; 163 | } 164 | for (a=0; a<2; a++) 165 | subst(&i->arg[a], cp); 166 | } 167 | subst(&b->jmp.arg, cp); 168 | } 169 | if (debug['C']) { 170 | fprintf(stderr, "\n> Copy information:"); 171 | for (t=Tmp0; tntmp; t++) { 172 | if (req(cp[t], R)) { 173 | fprintf(stderr, "\n%10s not seen!", 174 | fn->tmp[t].name); 175 | } 176 | else if (!req(cp[t], TMP(t))) { 177 | fprintf(stderr, "\n%10s copy of ", 178 | fn->tmp[t].name); 179 | printref(cp[t], fn, stderr); 180 | } 181 | } 182 | fprintf(stderr, "\n\n> After copy elimination:\n"); 183 | printfn(fn, stderr); 184 | } 185 | free(cp); 186 | } 187 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | html/ 2 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | DOCS = abi il llvm 2 | 3 | all: $(DOCS:%=html/%.html) 4 | 5 | clean: 6 | rm -fr html 7 | 8 | html/%.html: %.txt 9 | mkdir html 2> /dev/null || true 10 | ( sed -ne '2{s,.*,&,;p;q}' $<; \ 11 | echo '
'; \ 12 | sed -ne '2{s,.*,

&

,;p;q}' $<; \ 13 | sed -e '1,3d' $< | ocaml txt/txt.ml; \ 14 | echo '
'; \ 15 | ) > $@ 16 | 17 | .PHONY: all clean 18 | -------------------------------------------------------------------------------- /doc/abi.txt: -------------------------------------------------------------------------------- 1 | ================== 2 | System V ABI AMD64 3 | ================== 4 | 5 | 6 | This document describes concisely the subset of the amd64 7 | ABI as it is implemented in QBE. The subset can handle 8 | correctly arbitrary standard C-like structs containing 9 | float and integer types. Structs that have unaligned 10 | members are also supported through opaque types, see 11 | the IL description document for more information about 12 | them. 13 | 14 | 15 | - ABI Subset Implemented 16 | ------------------------ 17 | 18 | Data classes of interest as defined by the ABI: 19 | * INTEGER 20 | * SSE 21 | * MEMORY 22 | 23 | 24 | ~ Classification 25 | 26 | 1. The size of each argument gets rounded up to eightbytes. 27 | (It keeps the stack always 8 bytes aligned.) 28 | 2. _Bool, char, short, int, long, long long and pointers 29 | are in the INTEGER class. In the context of QBE, it 30 | means that 'l' and 'w' are in the INTEGER class. 31 | 3. float and double are in the SSE class. In the context 32 | of QBE, it means that 's' and 'd' are in the SSE class. 33 | 4. If the size of an object is larger than two eightbytes 34 | or if contains unaligned fields, it has class MEMORY. 35 | In the context of QBE, those are big aggregate types 36 | and opaque types. 37 | 5. Otherwise, recursively classify fields and determine 38 | the class of the two eightbytes using the classes of 39 | their components. If any is INTEGER the result is 40 | INTEGER, otherwise the result is SSE. 41 | 42 | ~ Passing 43 | 44 | * Classify arguments in order. 45 | * INTEGER arguments use in order `%rdi` `%rsi` `%rdx` 46 | `%rcx` `%r8` `%r9`. 47 | * SSE arguments use in order `%xmm0` - `%xmm7`. 48 | * MEMORY gets passed on the stack. They are "pushed" 49 | in the right-to-left order, so from the callee's 50 | point of view, the left-most argument appears first 51 | on the stack. 52 | * When we run out of registers for an aggregate, revert 53 | the assignment for the first eightbytes and pass it 54 | on the stack. 55 | * When all registers are taken, write arguments on the 56 | stack from right to left. 57 | * When calling a variadic function, %al stores the number 58 | of vector registers used to pass arguments (it must be 59 | an upper bound and does not have to be exact). 60 | * Registers `%rbx`, `%r12` - `%r15` are callee-save. 61 | 62 | ~ Returning 63 | 64 | * Classify the return type. 65 | * Use `%rax` and `%rdx` in order for INTEGER return 66 | values. 67 | * Use `%xmm0` and `%xmm1` in order for SSE return values. 68 | * If the return value's class is MEMORY, the first 69 | argument of the function `%rdi` was a pointer to an 70 | area big enough to fit the return value. The function 71 | writes the return value there and returns the address 72 | (that was in `%rdi`) in `%rax`. 73 | 74 | 75 | - Alignment on the Stack 76 | ------------------------ 77 | 78 | The ABI is unclear on the alignment requirement of the 79 | stack. What must be ensured is that, right before 80 | executing a 'call' instruction, the stack pointer `%rsp` 81 | is aligned on 16 bytes. On entry of the called 82 | function, the stack pointer is 8 modulo 16. Since most 83 | functions will have a prelude pushing `%rbp`, the frame 84 | pointer, upon entry of the body code of the function is 85 | also aligned on 16 bytes (== 0 mod 16). 86 | 87 | Here is a diagram of the stack layout after a call from 88 | g() to f(). 89 | 90 | | | 91 | | g() locals | 92 | +-------------+ 93 | ^ | | \ 94 | | | stack arg 2 | ' 95 | | |xxxxxxxxxxxxx| | f()'s MEMORY 96 | growing | +-------------+ | arguments 97 | addresses | | stack arg 1 | , 98 | | |xxxxxxxxxxxxx| / 99 | | +-------------+ -> 0 mod 16 100 | | | ret addr | 101 | +-------------+ 102 | | saved %rbp | 103 | +-------------+ -> f()'s %rbp 104 | | f() locals | 0 mod 16 105 | | ... | 106 | -> %rsp 107 | 108 | Legend: 109 | * `xxxxx` Optional padding. 110 | 111 | 112 | - Remarks 113 | --------- 114 | 115 | * A struct can be returned in registers in one of three 116 | ways. Either `%rax`, `%rdx` are used, or `%xmm0`, 117 | `%xmm1`, or finally `%rax`, `%xmm0`. The last case 118 | happens when a struct is returned with one half 119 | classified as INTEGER and the other as SSE. This 120 | is a consequence of the <@Returning> section above. 121 | 122 | * The size of the arguments area of the stack needs to 123 | be computed first, then arguments are packed starting 124 | from the bottom of the argument area, respecting 125 | alignment constraints. The ABI mentions "pushing" 126 | arguments in right-to-left order, but I think it's a 127 | mistaken view because of the alignment constraints. 128 | 129 | Example: If three 8 bytes MEMORY arguments are passed 130 | to the callee and the caller's stack pointer is 16 bytes 131 | algined, the layout will be like this. 132 | 133 | +-------------+ 134 | |xxxxxxxxxxxxx| padding 135 | | stack arg 3 | 136 | | stack arg 2 | 137 | | stack arg 1 | 138 | +-------------+ -> 0 mod 16 139 | 140 | The padding must not be at the end of the stack area. 141 | A "pushing" logic would put it at the end. 142 | -------------------------------------------------------------------------------- /doc/llvm.txt: -------------------------------------------------------------------------------- 1 | =========== 2 | QBE vs LLVM 3 | =========== 4 | 5 | 6 | Both QBE and LLVM are compiler backends using an SSA 7 | representation. This document will explain why LLVM 8 | does not make QBE a redundant project. Obviously, 9 | everything following is biased, because written by me. 10 | 11 | - Scope 12 | ------- 13 | 14 | QBE is a much smaller scale project with different goals 15 | than LLVM. 16 | 17 | * QBE is for amateur language designers. 18 | 19 | It does not address all the problems faced when 20 | conceiving an industry-grade language. If you are 21 | toying with some language ideas, using LLVM will 22 | be like hauling your backpack with a truck, but 23 | using QBE will feel more like riding a bicycle. 24 | 25 | * QBE is about the first 70%, not the last 30%. 26 | 27 | It attempts to pinpoint, in the extremely vast 28 | compilation literature, the optimizations that get 29 | you 70% of the performance in 10% of the code of 30 | full blown compilers. 31 | 32 | For example, copy propagation on SSA form is 33 | implemented in 160 lines of code in QBE! 34 | 35 | * QBE is extremely hackable. 36 | 37 | First, it is, and will remain, a small project 38 | (less than 8 kloc). Second, it is programmed in 39 | non-fancy C99 without any dependencies. Third, 40 | it is able to dump the IL and debug information in 41 | a uniform format after each pass. 42 | 43 | On my Core 2 Duo machine, QBE compiles in half a 44 | second (without optimizations). 45 | 46 | - Features 47 | ---------- 48 | 49 | LLVM is definitely more packed with features, but there 50 | are a few things provided in QBE to consider. 51 | 52 | * LLVM does NOT provide full C compatibility for you. 53 | 54 | In more technical terms, any language that provides 55 | good C compatibility and uses LLVM as a backend 56 | needs to reimplement large chunks of the ABI in 57 | its frontend! This well known issue in the LLVM 58 | community causes a great deal of duplication 59 | and bugs. 60 | 61 | Implementing a complete C ABI (with struct arguments 62 | and returns) is incredibly tricky, and not really 63 | a lot of fun. QBE provides you with IL operations 64 | to call in (and be called by) C with no pain. 65 | Moreover the ABI implementation in QBE has been 66 | thoroughly tested by fuzzing and manual tests. 67 | 68 | * LLVM IL is more cluttered with memory operations. 69 | 70 | Implementing SSA construction is hard. To save its 71 | users from having to implement it, LLVM provides 72 | stack slots. This means that one increment of 73 | a variable `v` will be composed of three LLVM 74 | instructions: one load, one add, and one store. 75 | 76 | QBE provides simple non-SSA temporaries, so 77 | incrementing `v` is simply done with one instruction 78 | `%v =w add %v, 1`. 79 | 80 | This could seem cosmetic, but dividing the size of 81 | the IL by three makes it easier for the frontend 82 | writers to spot bugs in the generated code. 83 | 84 | * LLVM IL is more cluttered with type annotations and 85 | casts. 86 | 87 | For the sake of advanced optimizations and 88 | correctness, LLVM has complex IL types. However, 89 | only a few types are really first class and many 90 | operations of source languages require casts to be 91 | compiled. 92 | 93 | Because QBE makes a much lighter use of types, the 94 | IL is more readable and shorter. It can of course be 95 | argued back that the correctness of QBE is jeoparadized, 96 | but remember that, in practice, the large amount 97 | of casts necessary in LLVM IL is undermining the 98 | overall effectiveness of the type system. 99 | -------------------------------------------------------------------------------- /doc/txt/txt.css: -------------------------------------------------------------------------------- 1 | h3 { 2 | border-bottom: 1px solid #aaa; 3 | background-color: #eee; 4 | } 5 | 6 | .bnf { 7 | background-color: white; 8 | padding-left: 0.7em; 9 | } 10 | -------------------------------------------------------------------------------- /doc/txt/txt.ml: -------------------------------------------------------------------------------- 1 | let dent = 4 2 | 3 | type doc = item list 4 | and item = 5 | | Verb of string * string 6 | | Par of (string * bool) 7 | | Ulist of doc list 8 | | Olist of doc list 9 | | Title of int * string * string 10 | 11 | let (|>) x f = f x 12 | 13 | let isspace = String.contains " \n\t" 14 | 15 | module String = struct 16 | include String 17 | 18 | let suff s n = 19 | let l = String.length s in 20 | if n >= l then "" else 21 | String.sub s n (l-n) 22 | 23 | let haspref p s = 24 | let lp = String.length p in 25 | String.length s >= lp && 26 | p = String.sub s 0 lp 27 | 28 | let trim s = 29 | let l = String.length s in 30 | let i = ref 0 and j = ref (l-1) in 31 | while !i=0 && isspace s.[!j] 34 | do decr j done; 35 | if !j = -1 then s else sub s !i (!j- !i+1) 36 | end 37 | 38 | let idify s = 39 | let rec f i cs = 40 | if i >= String.length s then cs else 41 | match s.[i] with 42 | | ' ' -> f (i+1) ("-" :: cs) 43 | | c -> f (i+1) (String.make 1 c :: cs) in 44 | f 0 [] |> List.rev |> String.concat "" 45 | 46 | let warn = Printf.eprintf 47 | 48 | let getdent s = 49 | let rec f n = 50 | if n >= String.length s then 0 else 51 | if s.[n] = ' ' then f (n+1) else 52 | if s.[n] = '\t' then f (n+8) else 53 | n/dent in 54 | f 0 55 | 56 | let dedent s i = 57 | let rec f i j = 58 | if i <= 0 then (-i, j) else 59 | if j >= String.length s then (0, j) else 60 | if s.[j] = ' ' then f (i-1) (j+1) else 61 | if s.[j] = '\t' then f (i-8) (j+1) else 62 | (0, j) in 63 | let (p, j) = f (i*dent) 0 in 64 | String.make p ' ' ^ String.suff s j 65 | 66 | let rec getlines acc n = 67 | match try Some (read_line ()) with End_of_file -> None with 68 | | Some s -> 69 | getlines ((n, getdent s, s) :: acc) (n+1) 70 | | None -> List.rev acc 71 | 72 | let matchs skip fin s = 73 | let rec f n = 74 | if n >= String.length s then 0 else 75 | if s.[n] = fin then (n+1) else 76 | if String.contains skip s.[n] then f (n+1) else 77 | 0 in 78 | f 0 79 | 80 | let endnum = matchs " 0123456789" '.' 81 | let endbul = matchs " " '*' 82 | let skipnum s = String.suff s (endnum s) 83 | let skipbul s = String.suff s (endbul s) 84 | 85 | let gettitles lines = 86 | let titles = Hashtbl.create 100 in 87 | let insert lvl n t = 88 | let t = String.trim (skipnum (String.suff t 2)) in 89 | if Hashtbl.mem titles t then 90 | warn "line %d: title has multiple definitions\n" n; 91 | Hashtbl.add titles t (lvl, idify t) in 92 | lines |> List.iter begin fun (n, lvl, t) -> 93 | if lvl <> 0 then () else 94 | if String.haspref "- " t then insert 0 n t else 95 | if String.haspref "~ " t then insert 1 n t else 96 | () 97 | end; 98 | titles 99 | 100 | let top lines = 101 | match !lines with 102 | | [] -> None 103 | | l :: _ -> Some l 104 | 105 | let pop lines = 106 | lines := List.tl !lines 107 | let push lines l = 108 | lines := l :: !lines 109 | 110 | let isolist l = endnum l <> 0 111 | let isulist l = endbul l <> 0 112 | 113 | let getverb lines idnt = 114 | let rec skip = function 115 | | s :: l when String.trim s = "" -> skip l 116 | | l -> l in 117 | let rec f ls = 118 | match top lines with 119 | | Some (n, i, l) 120 | when i >= idnt 121 | || String.trim l = "" -> 122 | pop lines; 123 | f (dedent l idnt :: ls) 124 | | _ -> 125 | skip ls |> List.rev |> 126 | String.concat "\n" in 127 | f [] 128 | 129 | let getpar lines idnt = 130 | let empty = function 131 | | Some (_, _, l) -> String.trim l = "" 132 | | _ -> false in 133 | let rec f ls = 134 | match top lines with 135 | | Some (n, i, l) 136 | when i = idnt 137 | && l <> "" 138 | && not (isolist l) 139 | && not (isulist l) -> 140 | pop lines; 141 | f (l :: ls) 142 | | t -> 143 | String.concat "\n" (List.rev ls), 144 | empty t in 145 | f [] 146 | 147 | let mergedoc = 148 | let rec aggreg f = function 149 | | (i :: is) as is'-> 150 | begin match f i with 151 | | Some l -> 152 | let l', is = aggreg f is in 153 | List.append l l', is 154 | | None -> [], is' 155 | end 156 | | is -> [], is in 157 | let ul = function Ulist l -> Some l | _ -> None in 158 | let ol = function Olist l -> Some l | _ -> None in 159 | let rec f d = match d with 160 | | Ulist _ :: _ -> 161 | let l, d = aggreg ul d in 162 | Ulist l :: f d 163 | | Olist _ :: _ -> 164 | let l, d = aggreg ol d in 165 | Olist l :: f d 166 | | i :: d -> i :: f d 167 | | [] -> [] in 168 | f 169 | 170 | let rec getdoc lines si acc = 171 | match top lines with 172 | | Some (n, i, l) -> 173 | if i = si && isolist l then begin (* Olist item *) 174 | pop lines; 175 | push lines (n, i+1, skipnum l); 176 | let li = getdoc lines (si+1) [] in 177 | getdoc lines si (Olist [li] :: acc); 178 | end else 179 | if i = si && isulist l then begin (* Ulist item *) 180 | pop lines; 181 | push lines (n, i+1, skipbul l); 182 | let li = getdoc lines (si+1) [] in 183 | getdoc lines si (Ulist [li] :: acc); 184 | end else 185 | if i > si then begin (* Verb item *) 186 | let ty = 187 | let l = dedent l i in 188 | if l.[0] <> '`' then "" else begin 189 | pop lines; 190 | String.suff l 1 191 | end in 192 | let verb = getverb lines (si+1) in 193 | getdoc lines si (Verb (ty, verb) :: acc); 194 | end else 195 | if si = 0 && String.haspref "~ " l 196 | || si = 0 && String.haspref "- " l then begin (* Titles *) 197 | pop lines; 198 | let lvl = if l.[0] = '-' then 0 else 1 in 199 | let tit = String.suff l 2 in 200 | let id = idify (String.trim (skipnum tit)) in 201 | getdoc lines si (Title (lvl, id, tit) :: acc); 202 | end else 203 | if String.haspref "---" l 204 | || String.haspref "~~~" l 205 | || l = "" then begin (* Decorations *) 206 | pop lines; 207 | getdoc lines si acc; 208 | end else 209 | if i = si then begin (* Par item *) 210 | let par = getpar lines si in 211 | getdoc lines si (Par par :: acc); 212 | end else 213 | List.rev acc |> mergedoc 214 | | None -> List.rev acc |> mergedoc 215 | 216 | type printer = 217 | { pchar: char -> unit 218 | ; plink: string -> unit 219 | ; pcode: string -> unit 220 | } 221 | 222 | let print pp s = 223 | let l = String.length s in 224 | let rec getlink j spc = 225 | if j >= l || s.[j] = '>' then j+1, "" else 226 | if isspace s.[j] then 227 | getlink (j+1) true 228 | else 229 | let j', t = getlink (j+1) false in 230 | if spc then 231 | j', Printf.sprintf " %c%s" s.[j] t 232 | else 233 | j', Printf.sprintf "%c%s" s.[j] t in 234 | let getlink j = 235 | let j', s = getlink j false in 236 | j', String.trim s in 237 | let rec getdlim j d = 238 | if j >= l || s.[j] = d then j+1, "" else 239 | let j', t = getdlim (j+1) d in 240 | j', Printf.sprintf "%c%s" s.[j] t in 241 | let rec f i = 242 | if i >= l then () else 243 | match s.[i] with 244 | | '<' when i < l-1 && s.[i+1] = '@' -> 245 | let i, t = getlink (i+2) in 246 | pp.plink t; 247 | f i 248 | | '`' -> 249 | let i, t = getdlim (i+1) '`' in 250 | pp.pcode t; 251 | f i 252 | | c -> 253 | pp.pchar c; 254 | f (i+1) 255 | in f 0 256 | 257 | let rec dochtml titles d = 258 | let open Printf in 259 | let pchar = function 260 | | '<' -> printf "<" 261 | | '>' -> printf ">" 262 | | '&' -> printf "&" 263 | | c -> printf "%c" c in 264 | let escape = String.iter pchar in 265 | let plink l = 266 | try 267 | let (_, id) = Hashtbl.find titles l in 268 | printf "%s" id l 269 | with Not_found -> 270 | warn "warning: unresolved link '%s'\n" l; 271 | printf "%s" l in 272 | let pcode s = 273 | printf ""; 274 | escape s; 275 | printf ""; in 276 | let pp = {pchar; plink; pcode} in 277 | let rec plist = 278 | List.iter begin fun d -> 279 | match d with 280 | | Par (p, nl) :: d when 281 | not nl || d = [] -> 282 | printf "
  • "; 283 | print pp p; 284 | printf "\n"; 285 | dochtml titles d; 286 | | d -> 287 | printf "
  • "; 288 | dochtml titles d; 289 | end in 290 | let itemhtml = function 291 | | Title (0, id, t) -> 292 | printf "

    " id; 293 | escape t; 294 | printf "

    \n"; 295 | | Title (_, id, t) -> 296 | printf "

    " id; 297 | escape t; 298 | printf "

    \n"; 299 | | Olist l -> 300 | printf "
      \n"; 301 | plist l; 302 | printf "
    \n"; 303 | | Ulist l -> 304 | printf "
      \n"; 305 | plist l; 306 | printf "
    \n"; 307 | | Verb (cls, v) -> 308 | if cls <> "" 309 | then printf "
    " cls
    310 |       else printf "
    \n";
    311 |       escape v;
    312 |       printf "\n
    \n"; 313 | | Par (p, _) -> 314 | printf "

    \n"; 315 | print pp p; 316 | printf "\n

    \n"; in 317 | List.iter itemhtml d 318 | 319 | let _ = 320 | let lines = getlines [] 1 in 321 | let titles = gettitles lines in 322 | getdoc (ref lines) 0 [] |> dochtml titles 323 | -------------------------------------------------------------------------------- /doc/win.txt: -------------------------------------------------------------------------------- 1 | =================== 2 | Windows Quick Start 3 | =================== 4 | 5 | Only 64-bit versions of windows are supported. To compile 6 | this software you will need to get a normal UNIX toolchain. 7 | There are several ways to get one, but I will only describe 8 | how I did it. 9 | 10 | 1. Download and install [@1 MSYS2] (the x86_64 version). 11 | 2. In an MSYS2 terminal, run the following command. 12 | 13 | pacman -S git make mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb 14 | 15 | 3. Restart the MSYS2 terminal. 16 | 4. In the new terminal, clone QBE. 17 | 18 | git clone git://c9x.me/qbe.git 19 | 20 | 5. Compile using `make`. 21 | 22 | 23 | [1] http://www.msys2.org 24 | -------------------------------------------------------------------------------- /file.ssa: -------------------------------------------------------------------------------- 1 | function w $add(w %a, w %b) { # Define a function add 2 | @start 3 | %c =w add %a, %b # Adds the 2 arguments 4 | ret %c # Return the result 5 | } 6 | export function w $main() { # Main function 7 | @start 8 | %r =w call $add(w 1, w 1) # Call add(1, 1) 9 | call $printf(l $fmt, w %r, ...) # Show the result 10 | ret 0 11 | } 12 | data $fmt = { b "One and one make %d!\n", b 0 } 13 | -------------------------------------------------------------------------------- /fold.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | enum { 4 | Bot = -2, /* lattice bottom */ 5 | Top = -1, /* lattice top */ 6 | }; 7 | 8 | typedef struct Edge Edge; 9 | 10 | struct Edge { 11 | int dest; 12 | int dead; 13 | Edge *work; 14 | }; 15 | 16 | static int *val; 17 | static Edge *flowrk, (*edge)[2]; 18 | static Use **usewrk; 19 | static uint nuse; 20 | 21 | static int 22 | czero(Con *c, int w) 23 | { 24 | if (c->type != CBits) 25 | return 0; 26 | if (w) 27 | return c->bits.i == 0; 28 | else 29 | return (uint32_t)c->bits.i == 0; 30 | } 31 | 32 | static int 33 | latval(Ref r) 34 | { 35 | switch (rtype(r)) { 36 | case RTmp: 37 | return val[r.val]; 38 | case RCon: 39 | return r.val; 40 | default: 41 | die("unreachable"); 42 | } 43 | } 44 | 45 | static int 46 | latmerge(int v, int m) 47 | { 48 | return m == Top ? v : (v == Top || v == m) ? m : Bot; 49 | } 50 | 51 | static void 52 | update(int t, int m, Fn *fn) 53 | { 54 | Tmp *tmp; 55 | uint u; 56 | 57 | m = latmerge(val[t], m); 58 | if (m != val[t]) { 59 | tmp = &fn->tmp[t]; 60 | for (u=0; unuse; u++) { 61 | vgrow(&usewrk, ++nuse); 62 | usewrk[nuse-1] = &tmp->use[u]; 63 | } 64 | val[t] = m; 65 | } 66 | } 67 | 68 | static int 69 | deadedge(int s, int d) 70 | { 71 | Edge *e; 72 | 73 | e = edge[s]; 74 | if (e[0].dest == d && !e[0].dead) 75 | return 0; 76 | if (e[1].dest == d && !e[1].dead) 77 | return 0; 78 | return 1; 79 | } 80 | 81 | static void 82 | visitphi(Phi *p, int n, Fn *fn) 83 | { 84 | int v; 85 | uint a; 86 | 87 | v = Top; 88 | for (a=0; anarg; a++) 89 | if (!deadedge(p->blk[a]->id, n)) 90 | v = latmerge(v, latval(p->arg[a])); 91 | update(p->to.val, v, fn); 92 | } 93 | 94 | static int opfold(int, int, Con *, Con *, Fn *); 95 | 96 | static void 97 | visitins(Ins *i, Fn *fn) 98 | { 99 | int v, l, r; 100 | 101 | if (rtype(i->to) != RTmp) 102 | return; 103 | if (optab[i->op].canfold) { 104 | l = latval(i->arg[0]); 105 | if (!req(i->arg[1], R)) 106 | r = latval(i->arg[1]); 107 | else 108 | r = CON_Z.val; 109 | if (l == Bot || r == Bot) 110 | v = Bot; 111 | else if (l == Top || r == Top) 112 | v = Top; 113 | else 114 | v = opfold(i->op, i->cls, &fn->con[l], &fn->con[r], fn); 115 | } else 116 | v = Bot; 117 | /* fprintf(stderr, "\nvisiting %s (%p)", optab[i->op].name, (void *)i); */ 118 | update(i->to.val, v, fn); 119 | } 120 | 121 | static void 122 | visitjmp(Blk *b, int n, Fn *fn) 123 | { 124 | int l; 125 | 126 | switch (b->jmp.type) { 127 | case Jjnz: 128 | l = latval(b->jmp.arg); 129 | assert(l != Top && "ssa invariant broken"); 130 | if (l == Bot) { 131 | edge[n][1].work = flowrk; 132 | edge[n][0].work = &edge[n][1]; 133 | flowrk = &edge[n][0]; 134 | } 135 | else if (czero(&fn->con[l], 0)) { 136 | assert(edge[n][0].dead); 137 | edge[n][1].work = flowrk; 138 | flowrk = &edge[n][1]; 139 | } 140 | else { 141 | assert(edge[n][1].dead); 142 | edge[n][0].work = flowrk; 143 | flowrk = &edge[n][0]; 144 | } 145 | break; 146 | case Jjmp: 147 | edge[n][0].work = flowrk; 148 | flowrk = &edge[n][0]; 149 | break; 150 | default: 151 | if (isret(b->jmp.type)) 152 | break; 153 | die("unreachable"); 154 | } 155 | } 156 | 157 | static void 158 | initedge(Edge *e, Blk *s) 159 | { 160 | if (s) 161 | e->dest = s->id; 162 | else 163 | e->dest = -1; 164 | e->dead = 1; 165 | e->work = 0; 166 | } 167 | 168 | static int 169 | renref(Ref *r) 170 | { 171 | int l; 172 | 173 | if (rtype(*r) == RTmp) 174 | if ((l=val[r->val]) != Bot) { 175 | assert(l != Top && "ssa invariant broken"); 176 | *r = CON(l); 177 | return 1; 178 | } 179 | return 0; 180 | } 181 | 182 | /* require rpo, use, pred */ 183 | void 184 | fold(Fn *fn) 185 | { 186 | Edge *e, start; 187 | Use *u; 188 | Blk *b, **pb; 189 | Phi *p, **pp; 190 | Ins *i; 191 | int t, d; 192 | uint n, a; 193 | 194 | val = emalloc(fn->ntmp * sizeof val[0]); 195 | edge = emalloc(fn->nblk * sizeof edge[0]); 196 | usewrk = vnew(0, sizeof usewrk[0], Pheap); 197 | 198 | for (t=0; tntmp; t++) 199 | val[t] = Top; 200 | for (n=0; nnblk; n++) { 201 | b = fn->rpo[n]; 202 | b->visit = 0; 203 | initedge(&edge[n][0], b->s1); 204 | initedge(&edge[n][1], b->s2); 205 | } 206 | initedge(&start, fn->start); 207 | flowrk = &start; 208 | nuse = 0; 209 | 210 | /* 1. find out constants and dead cfg edges */ 211 | for (;;) { 212 | e = flowrk; 213 | if (e) { 214 | flowrk = e->work; 215 | e->work = 0; 216 | if (e->dest == -1 || !e->dead) 217 | continue; 218 | e->dead = 0; 219 | n = e->dest; 220 | b = fn->rpo[n]; 221 | for (p=b->phi; p; p=p->link) 222 | visitphi(p, n, fn); 223 | if (b->visit == 0) { 224 | for (i=b->ins; i-b->ins < b->nins; i++) 225 | visitins(i, fn); 226 | visitjmp(b, n, fn); 227 | } 228 | b->visit++; 229 | assert(b->jmp.type != Jjmp 230 | || !edge[n][0].dead 231 | || flowrk == &edge[n][0]); 232 | } 233 | else if (nuse) { 234 | u = usewrk[--nuse]; 235 | n = u->bid; 236 | b = fn->rpo[n]; 237 | if (b->visit == 0) 238 | continue; 239 | switch (u->type) { 240 | case UPhi: 241 | visitphi(u->u.phi, u->bid, fn); 242 | break; 243 | case UIns: 244 | visitins(u->u.ins, fn); 245 | break; 246 | case UJmp: 247 | visitjmp(b, n, fn); 248 | break; 249 | default: 250 | die("unreachable"); 251 | } 252 | } 253 | else 254 | break; 255 | } 256 | 257 | if (debug['F']) { 258 | fprintf(stderr, "\n> SCCP findings:"); 259 | for (t=Tmp0; tntmp; t++) { 260 | if (val[t] == Bot) 261 | continue; 262 | fprintf(stderr, "\n%10s: ", fn->tmp[t].name); 263 | if (val[t] == Top) 264 | fprintf(stderr, "Top"); 265 | else 266 | printref(CON(val[t]), fn, stderr); 267 | } 268 | fprintf(stderr, "\n dead code: "); 269 | } 270 | 271 | /* 2. trim dead code, replace constants */ 272 | d = 0; 273 | for (pb=&fn->start; (b=*pb);) { 274 | if (b->visit == 0) { 275 | d = 1; 276 | if (debug['F']) 277 | fprintf(stderr, "%s ", b->name); 278 | edgedel(b, &b->s1); 279 | edgedel(b, &b->s2); 280 | *pb = b->link; 281 | continue; 282 | } 283 | for (pp=&b->phi; (p=*pp);) 284 | if (val[p->to.val] != Bot) 285 | *pp = p->link; 286 | else { 287 | for (a=0; anarg; a++) 288 | if (!deadedge(p->blk[a]->id, b->id)) 289 | renref(&p->arg[a]); 290 | pp = &p->link; 291 | } 292 | for (i=b->ins; i-b->ins < b->nins; i++) 293 | if (renref(&i->to)) 294 | *i = (Ins){.op = Onop}; 295 | else 296 | for (n=0; n<2; n++) 297 | renref(&i->arg[n]); 298 | renref(&b->jmp.arg); 299 | if (b->jmp.type == Jjnz && rtype(b->jmp.arg) == RCon) { 300 | if (czero(&fn->con[b->jmp.arg.val], 0)) { 301 | edgedel(b, &b->s1); 302 | b->s1 = b->s2; 303 | b->s2 = 0; 304 | } else 305 | edgedel(b, &b->s2); 306 | b->jmp.type = Jjmp; 307 | b->jmp.arg = R; 308 | } 309 | pb = &b->link; 310 | } 311 | 312 | if (debug['F']) { 313 | if (!d) 314 | fprintf(stderr, "(none)"); 315 | fprintf(stderr, "\n\n> After constant folding:\n"); 316 | printfn(fn, stderr); 317 | } 318 | 319 | free(val); 320 | free(edge); 321 | vfree(usewrk); 322 | } 323 | 324 | /* boring folding code */ 325 | 326 | static int 327 | foldint(Con *res, int op, int w, Con *cl, Con *cr) 328 | { 329 | union { 330 | int64_t s; 331 | uint64_t u; 332 | float fs; 333 | double fd; 334 | } l, r; 335 | uint64_t x; 336 | uint32_t lab; 337 | int typ; 338 | 339 | typ = CBits; 340 | lab = 0; 341 | l.s = cl->bits.i; 342 | r.s = cr->bits.i; 343 | if (op == Oadd) { 344 | if (cl->type == CAddr) { 345 | if (cr->type == CAddr) 346 | err("undefined addition (addr + addr)"); 347 | lab = cl->label; 348 | typ = CAddr; 349 | } 350 | else if (cr->type == CAddr) { 351 | lab = cr->label; 352 | typ = CAddr; 353 | } 354 | } 355 | else if (op == Osub) { 356 | if (cl->type == CAddr) { 357 | if (cr->type != CAddr) { 358 | lab = cl->label; 359 | typ = CAddr; 360 | } else if (cl->label != cr->label) 361 | err("undefined substraction (addr1 - addr2)"); 362 | } 363 | else if (cr->type == CAddr) 364 | err("undefined substraction (num - addr)"); 365 | } 366 | else if (cl->type == CAddr || cr->type == CAddr) { 367 | if (Ocmpl <= op && op <= Ocmpl1) 368 | return 1; 369 | err("invalid address operand for '%s'", optab[op].name); 370 | } 371 | switch (op) { 372 | case Oadd: x = l.u + r.u; break; 373 | case Osub: x = l.u - r.u; break; 374 | case Odiv: x = l.s / r.s; break; 375 | case Orem: x = l.s % r.s; break; 376 | case Oudiv: x = l.u / r.u; break; 377 | case Ourem: x = l.u % r.u; break; 378 | case Omul: x = l.u * r.u; break; 379 | case Oand: x = l.u & r.u; break; 380 | case Oor: x = l.u | r.u; break; 381 | case Oxor: x = l.u ^ r.u; break; 382 | case Osar: x = l.s >> (r.u & 63); break; 383 | case Oshr: x = l.u >> (r.u & 63); break; 384 | case Oshl: x = l.u << (r.u & 63); break; 385 | case Oextsb: x = (int8_t)l.u; break; 386 | case Oextub: x = (uint8_t)l.u; break; 387 | case Oextsh: x = (int16_t)l.u; break; 388 | case Oextuh: x = (uint16_t)l.u; break; 389 | case Oextsw: x = (int32_t)l.u; break; 390 | case Oextuw: x = (uint32_t)l.u; break; 391 | case Ostosi: x = w ? (int64_t)cl->bits.s : (int32_t)cl->bits.s; break; 392 | case Odtosi: x = w ? (int64_t)cl->bits.d : (int32_t)cl->bits.d; break; 393 | case Ocast: 394 | x = l.u; 395 | if (cl->type == CAddr) { 396 | lab = cl->label; 397 | typ = CAddr; 398 | } 399 | break; 400 | default: 401 | if (Ocmpw <= op && op <= Ocmpl1) { 402 | if (op <= Ocmpw1) { 403 | l.u = (int32_t)l.u; 404 | r.u = (int32_t)r.u; 405 | } else 406 | op -= Ocmpl - Ocmpw; 407 | switch (op - Ocmpw) { 408 | case Ciule: x = l.u <= r.u; break; 409 | case Ciult: x = l.u < r.u; break; 410 | case Cisle: x = l.s <= r.s; break; 411 | case Cislt: x = l.s < r.s; break; 412 | case Cisgt: x = l.s > r.s; break; 413 | case Cisge: x = l.s >= r.s; break; 414 | case Ciugt: x = l.u > r.u; break; 415 | case Ciuge: x = l.u >= r.u; break; 416 | case Cieq: x = l.u == r.u; break; 417 | case Cine: x = l.u != r.u; break; 418 | default: die("unreachable"); 419 | } 420 | } 421 | else if (Ocmps <= op && op <= Ocmps1) { 422 | switch (op - Ocmps) { 423 | case Cfle: x = l.fs <= r.fs; break; 424 | case Cflt: x = l.fs < r.fs; break; 425 | case Cfgt: x = l.fs > r.fs; break; 426 | case Cfge: x = l.fs >= r.fs; break; 427 | case Cfne: x = l.fs != r.fs; break; 428 | case Cfeq: x = l.fs == r.fs; break; 429 | case Cfo: x = l.fs < r.fs || l.fs >= r.fs; break; 430 | case Cfuo: x = !(l.fs < r.fs || l.fs >= r.fs); break; 431 | default: die("unreachable"); 432 | } 433 | } 434 | else if (Ocmpd <= op && op <= Ocmpd1) { 435 | switch (op - Ocmpd) { 436 | case Cfle: x = l.fd <= r.fd; break; 437 | case Cflt: x = l.fd < r.fd; break; 438 | case Cfgt: x = l.fd > r.fd; break; 439 | case Cfge: x = l.fd >= r.fd; break; 440 | case Cfne: x = l.fd != r.fd; break; 441 | case Cfeq: x = l.fd == r.fd; break; 442 | case Cfo: x = l.fd < r.fd || l.fd >= r.fd; break; 443 | case Cfuo: x = !(l.fd < r.fd || l.fd >= r.fd); break; 444 | default: die("unreachable"); 445 | } 446 | } 447 | else 448 | die("unreachable"); 449 | } 450 | *res = (Con){.type=typ, .label=lab, .bits={.i=x}}; 451 | return 0; 452 | } 453 | 454 | static void 455 | foldflt(Con *res, int op, int w, Con *cl, Con *cr) 456 | { 457 | float xs, ls, rs; 458 | double xd, ld, rd; 459 | 460 | if (cl->type != CBits || cr->type != CBits) 461 | err("invalid address operand for '%s'", optab[op].name); 462 | if (w) { 463 | ld = cl->bits.d; 464 | rd = cr->bits.d; 465 | switch (op) { 466 | case Oadd: xd = ld + rd; break; 467 | case Osub: xd = ld - rd; break; 468 | case Odiv: xd = ld / rd; break; 469 | case Omul: xd = ld * rd; break; 470 | case Oswtof: xd = (int32_t)cl->bits.i; break; 471 | case Osltof: xd = (int64_t)cl->bits.i; break; 472 | case Oexts: xd = cl->bits.s; break; 473 | case Ocast: xd = ld; break; 474 | default: die("unreachable"); 475 | } 476 | *res = (Con){CBits, .bits={.d=xd}, .flt=2}; 477 | } else { 478 | ls = cl->bits.s; 479 | rs = cr->bits.s; 480 | switch (op) { 481 | case Oadd: xs = ls + rs; break; 482 | case Osub: xs = ls - rs; break; 483 | case Odiv: xs = ls / rs; break; 484 | case Omul: xs = ls * rs; break; 485 | case Oswtof: xs = (int32_t)cl->bits.i; break; 486 | case Osltof: xs = (int64_t)cl->bits.i; break; 487 | case Otruncd: xs = cl->bits.d; break; 488 | case Ocast: xs = ls; break; 489 | default: die("unreachable"); 490 | } 491 | *res = (Con){CBits, .bits={.s=xs}, .flt=1}; 492 | } 493 | } 494 | 495 | static int 496 | opfold(int op, int cls, Con *cl, Con *cr, Fn *fn) 497 | { 498 | int nc; 499 | Con c; 500 | 501 | if ((op == Odiv || op == Oudiv 502 | || op == Orem || op == Ourem) && czero(cr, KWIDE(cls))) 503 | err("null divisor in '%s'", optab[op].name); 504 | if (cls == Kw || cls == Kl) { 505 | if (foldint(&c, op, cls == Kl, cl, cr)) 506 | return Bot; 507 | } else 508 | foldflt(&c, op, cls == Kd, cl, cr); 509 | if (c.type == CBits) 510 | nc = getcon(c.bits.i, fn).val; 511 | else { 512 | nc = fn->ncon; 513 | vgrow(&fn->con, ++fn->ncon); 514 | } 515 | assert(!(cls == Ks || cls == Kd) || c.flt); 516 | fn->con[nc] = c; 517 | return nc; 518 | } 519 | -------------------------------------------------------------------------------- /gas.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | 4 | char *gasloc, *gassym; 5 | 6 | void 7 | gasemitdat(Dat *d, FILE *f) 8 | { 9 | static int align; 10 | static char *dtoa[] = { 11 | [DAlign] = ".align", 12 | [DB] = "\t.byte", 13 | [DH] = "\t.short", 14 | [DW] = "\t.int", 15 | [DL] = "\t.quad" 16 | }; 17 | 18 | switch (d->type) { 19 | case DStart: 20 | align = 0; 21 | fprintf(f, ".data\n"); 22 | break; 23 | case DEnd: 24 | break; 25 | case DName: 26 | if (!align) 27 | fprintf(f, ".align 8\n"); 28 | if (d->export) 29 | fprintf(f, ".globl %s%s\n", gassym, d->u.str); 30 | fprintf(f, "%s%s:\n", gassym, d->u.str); 31 | break; 32 | case DZ: 33 | fprintf(f, "\t.fill %"PRId64",1,0\n", d->u.num); 34 | break; 35 | default: 36 | if (d->type == DAlign) 37 | align = 1; 38 | 39 | if (d->isstr) { 40 | if (d->type != DB) 41 | err("strings only supported for 'b' currently"); 42 | fprintf(f, "\t.ascii \"%s\"\n", d->u.str); 43 | } 44 | else if (d->isref) { 45 | fprintf(f, "%s %s%+"PRId64"\n", 46 | dtoa[d->type], d->u.ref.nam, 47 | d->u.ref.off); 48 | } 49 | else { 50 | fprintf(f, "%s %"PRId64"\n", 51 | dtoa[d->type], d->u.num); 52 | } 53 | break; 54 | } 55 | } 56 | 57 | typedef struct Asmbits Asmbits; 58 | 59 | struct Asmbits { 60 | char bits[16]; 61 | int size; 62 | Asmbits *link; 63 | }; 64 | 65 | static Asmbits *stash; 66 | 67 | int 68 | gasstash(void *bits, int size) 69 | { 70 | Asmbits **pb, *b; 71 | int i; 72 | 73 | assert(size == 4 || size == 8 || size == 16); 74 | for (pb=&stash, i=0; (b=*pb); pb=&b->link, i++) 75 | if (size <= b->size) 76 | if (memcmp(bits, b->bits, size) == 0) 77 | return i; 78 | b = emalloc(sizeof *b); 79 | memcpy(b->bits, bits, size); 80 | b->size = size; 81 | b->link = 0; 82 | *pb = b; 83 | return i; 84 | } 85 | 86 | void 87 | gasemitfin(FILE *f) 88 | { 89 | Asmbits *b; 90 | char *p; 91 | int sz, i; 92 | double d; 93 | 94 | if (!stash) 95 | return; 96 | fprintf(f, "/* floating point constants */\n.data\n"); 97 | for (sz=16; sz>=4; sz/=2) 98 | for (b=stash, i=0; b; b=b->link, i++) { 99 | if (b->size == sz) { 100 | fprintf(f, 101 | ".align %d\n" 102 | "%sfp%d:", 103 | sz, gasloc, i 104 | ); 105 | for (p=b->bits; p<&b->bits[sz]; p+=4) 106 | fprintf(f, "\n\t.int %"PRId32, 107 | *(int32_t *)p); 108 | if (sz <= 8) { 109 | if (sz == 4) 110 | d = *(float *)b->bits; 111 | else 112 | d = *(double *)b->bits; 113 | fprintf(f, " /* %f */\n", d); 114 | } else 115 | fprintf(f, "\n"); 116 | } 117 | } 118 | while ((b=stash)) { 119 | stash = b->link; 120 | free(b); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /live.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | void 4 | liveon(BSet *v, Blk *b, Blk *s) 5 | { 6 | Phi *p; 7 | uint a; 8 | 9 | bscopy(v, s->in); 10 | for (p=s->phi; p; p=p->link) 11 | if (rtype(p->to) == RTmp) 12 | bsclr(v, p->to.val); 13 | for (p=s->phi; p; p=p->link) 14 | for (a=0; anarg; a++) 15 | if (p->blk[a] == b) 16 | if (rtype(p->arg[a]) == RTmp) { 17 | bsset(v, p->arg[a].val); 18 | bsset(b->gen, p->arg[a].val); 19 | } 20 | } 21 | 22 | static void 23 | bset(Ref r, Blk *b, int *nlv, Tmp *tmp) 24 | { 25 | 26 | if (rtype(r) != RTmp) 27 | return; 28 | bsset(b->gen, r.val); 29 | if (!bshas(b->in, r.val)) { 30 | nlv[KBASE(tmp[r.val].cls)]++; 31 | bsset(b->in, r.val); 32 | } 33 | } 34 | 35 | /* liveness analysis 36 | * requires rpo computation 37 | */ 38 | void 39 | filllive(Fn *f) 40 | { 41 | Blk *b; 42 | Ins *i; 43 | int k, t, m[2], n, chg, nlv[2]; 44 | BSet u[1], v[1]; 45 | Mem *ma; 46 | 47 | bsinit(u, f->ntmp); 48 | bsinit(v, f->ntmp); 49 | for (b=f->start; b; b=b->link) { 50 | bsinit(b->in, f->ntmp); 51 | bsinit(b->out, f->ntmp); 52 | bsinit(b->gen, f->ntmp); 53 | } 54 | chg = 1; 55 | Again: 56 | for (n=f->nblk-1; n>=0; n--) { 57 | b = f->rpo[n]; 58 | 59 | bscopy(u, b->out); 60 | if (b->s1) { 61 | liveon(v, b, b->s1); 62 | bsunion(b->out, v); 63 | } 64 | if (b->s2) { 65 | liveon(v, b, b->s2); 66 | bsunion(b->out, v); 67 | } 68 | chg |= !bsequal(b->out, u); 69 | 70 | memset(nlv, 0, sizeof nlv); 71 | b->out->t[0] |= T.rglob; 72 | bscopy(b->in, b->out); 73 | for (t=0; bsiter(b->in, &t); t++) 74 | nlv[KBASE(f->tmp[t].cls)]++; 75 | if (rtype(b->jmp.arg) == RCall) { 76 | assert((int)bscount(b->in) == T.nrglob && 77 | nlv[0] == T.nrglob && 78 | nlv[1] == 0); 79 | b->in->t[0] |= T.retregs(b->jmp.arg, nlv); 80 | } else 81 | bset(b->jmp.arg, b, nlv, f->tmp); 82 | for (k=0; k<2; k++) 83 | b->nlive[k] = nlv[k]; 84 | for (i=&b->ins[b->nins]; i!=b->ins;) { 85 | if ((--i)->op == Ocall && rtype(i->arg[1]) == RCall) { 86 | b->in->t[0] &= ~T.retregs(i->arg[1], m); 87 | for (k=0; k<2; k++) { 88 | nlv[k] -= m[k]; 89 | /* caller-save registers are used 90 | * by the callee, in that sense, 91 | * right in the middle of the call, 92 | * they are live: */ 93 | nlv[k] += T.nrsave[k]; 94 | if (nlv[k] > b->nlive[k]) 95 | b->nlive[k] = nlv[k]; 96 | } 97 | b->in->t[0] |= T.argregs(i->arg[1], m); 98 | for (k=0; k<2; k++) { 99 | nlv[k] -= T.nrsave[k]; 100 | nlv[k] += m[k]; 101 | } 102 | } 103 | if (!req(i->to, R)) { 104 | assert(rtype(i->to) == RTmp); 105 | t = i->to.val; 106 | if (bshas(b->in, i->to.val)) 107 | nlv[KBASE(f->tmp[t].cls)]--; 108 | bsset(b->gen, t); 109 | bsclr(b->in, t); 110 | } 111 | for (k=0; k<2; k++) 112 | switch (rtype(i->arg[k])) { 113 | case RMem: 114 | ma = &f->mem[i->arg[k].val]; 115 | bset(ma->base, b, nlv, f->tmp); 116 | bset(ma->index, b, nlv, f->tmp); 117 | break; 118 | default: 119 | bset(i->arg[k], b, nlv, f->tmp); 120 | break; 121 | } 122 | for (k=0; k<2; k++) 123 | if (nlv[k] > b->nlive[k]) 124 | b->nlive[k] = nlv[k]; 125 | } 126 | } 127 | if (chg) { 128 | chg = 0; 129 | goto Again; 130 | } 131 | 132 | if (debug['L']) { 133 | fprintf(stderr, "\n> Liveness analysis:\n"); 134 | for (b=f->start; b; b=b->link) { 135 | fprintf(stderr, "\t%-10sin: ", b->name); 136 | dumpts(b->in, f->tmp, stderr); 137 | fprintf(stderr, "\t out: "); 138 | dumpts(b->out, f->tmp, stderr); 139 | fprintf(stderr, "\t gen: "); 140 | dumpts(b->gen, f->tmp, stderr); 141 | fprintf(stderr, "\t live: "); 142 | fprintf(stderr, "%d %d\n", b->nlive[0], b->nlive[1]); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /load.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | #define MASK(w) (BIT(8*(w)-1)*2-1) /* must work when w==8 */ 4 | 5 | typedef struct Loc Loc; 6 | typedef struct Slice Slice; 7 | typedef struct Insert Insert; 8 | 9 | 10 | struct Loc { 11 | enum { 12 | LRoot, /* right above the original load */ 13 | LLoad, /* inserting a load is allowed */ 14 | LNoLoad, /* only scalar operations allowed */ 15 | } type; 16 | uint off; 17 | Blk *blk; 18 | }; 19 | 20 | struct Slice { 21 | Ref ref; 22 | short sz; 23 | short cls; /* load class */ 24 | }; 25 | 26 | struct Insert { 27 | uint isphi:1; 28 | uint num:31; 29 | uint bid; 30 | uint off; 31 | union { 32 | Ins ins; 33 | struct { 34 | Slice m; 35 | Phi *p; 36 | } phi; 37 | } new; 38 | }; 39 | 40 | static Fn *curf; 41 | static uint inum; /* current insertion number */ 42 | static Insert *ilog; /* global insertion log */ 43 | static uint nlog; /* number of entries in the log */ 44 | 45 | int 46 | loadsz(Ins *l) 47 | { 48 | switch (l->op) { 49 | case Oloadsb: case Oloadub: return 1; 50 | case Oloadsh: case Oloaduh: return 2; 51 | case Oloadsw: case Oloaduw: return 4; 52 | case Oload: return KWIDE(l->cls) ? 8 : 4; 53 | } 54 | die("unreachable"); 55 | } 56 | 57 | int 58 | storesz(Ins *s) 59 | { 60 | switch (s->op) { 61 | case Ostoreb: return 1; 62 | case Ostoreh: return 2; 63 | case Ostorew: case Ostores: return 4; 64 | case Ostorel: case Ostored: return 8; 65 | } 66 | die("unreachable"); 67 | } 68 | 69 | static Ref 70 | iins(int cls, int op, Ref a0, Ref a1, Loc *l) 71 | { 72 | Insert *ist; 73 | 74 | vgrow(&ilog, ++nlog); 75 | ist = &ilog[nlog-1]; 76 | ist->isphi = 0; 77 | ist->num = inum++; 78 | ist->bid = l->blk->id; 79 | ist->off = l->off; 80 | ist->new.ins = (Ins){op, R, {a0, a1}, cls}; 81 | return ist->new.ins.to = newtmp("ld", cls, curf); 82 | } 83 | 84 | static void 85 | cast(Ref *r, int cls, Loc *l) 86 | { 87 | int cls0; 88 | 89 | if (rtype(*r) == RCon) 90 | return; 91 | assert(rtype(*r) == RTmp); 92 | cls0 = curf->tmp[r->val].cls; 93 | if (cls0 == cls || (cls == Kw && cls0 == Kl)) 94 | return; 95 | assert(!KWIDE(cls0) || KWIDE(cls)); 96 | if (KWIDE(cls) == KWIDE(cls0)) 97 | *r = iins(cls, Ocast, *r, R, l); 98 | else { 99 | assert(cls == Kl); 100 | if (cls0 == Ks) 101 | *r = iins(Kw, Ocast, *r, R, l); 102 | *r = iins(Kl, Oextuw, *r, R, l); 103 | } 104 | } 105 | 106 | static inline void 107 | mask(int cls, Ref *r, bits msk, Loc *l) 108 | { 109 | cast(r, cls, l); 110 | *r = iins(cls, Oand, *r, getcon(msk, curf), l); 111 | } 112 | 113 | static Ref 114 | load(Slice sl, bits msk, Loc *l) 115 | { 116 | Alias *a; 117 | Ref r, r1; 118 | int ld, cls, all, c; 119 | 120 | ld = (int[]){ 121 | [1] = Oloadub, 122 | [2] = Oloaduh, 123 | [4] = Oloaduw, 124 | [8] = Oload 125 | }[sl.sz]; 126 | all = msk == MASK(sl.sz); 127 | if (all) 128 | cls = sl.cls; 129 | else 130 | cls = sl.sz > 4 ? Kl : Kw; 131 | r = sl.ref; 132 | /* sl.ref might not be live here, 133 | * but its alias base ref will be 134 | * (see killsl() below) */ 135 | if (rtype(r) == RTmp) { 136 | a = &curf->tmp[r.val].alias; 137 | switch (a->type) { 138 | default: 139 | die("unreachable"); 140 | case ALoc: 141 | case AEsc: 142 | case AUnk: 143 | r = a->base; 144 | if (!a->offset) 145 | break; 146 | r1 = getcon(a->offset, curf); 147 | r = iins(Kl, Oadd, r, r1, l); 148 | break; 149 | case ACon: 150 | case ASym: 151 | c = curf->ncon++; 152 | vgrow(&curf->con, curf->ncon); 153 | curf->con[c].type = CAddr; 154 | curf->con[c].label = a->label; 155 | curf->con[c].bits.i = a->offset; 156 | curf->con[c].local = 0; 157 | r = CON(c); 158 | break; 159 | } 160 | } 161 | r = iins(cls, ld, r, R, l); 162 | if (!all) 163 | mask(cls, &r, msk, l); 164 | return r; 165 | } 166 | 167 | static int 168 | killsl(Ref r, Slice sl) 169 | { 170 | Alias *a; 171 | 172 | if (rtype(sl.ref) != RTmp) 173 | return 0; 174 | a = &curf->tmp[sl.ref.val].alias; 175 | switch (a->type) { 176 | default: die("unreachable"); 177 | case ALoc: 178 | case AEsc: 179 | case AUnk: return req(a->base, r); 180 | case ACon: 181 | case ASym: return 0; 182 | } 183 | } 184 | 185 | /* returns a ref containing the contents of the slice 186 | * passed as argument, all the bits set to 0 in the 187 | * mask argument are zeroed in the result; 188 | * the returned ref has an integer class when the 189 | * mask does not cover all the bits of the slice, 190 | * otherwise, it has class sl.cls 191 | * the procedure returns R when it fails */ 192 | static Ref 193 | def(Slice sl, bits msk, Blk *b, Ins *i, Loc *il) 194 | { 195 | Blk *bp; 196 | bits msk1, msks; 197 | int off, cls, cls1, op, sz, ld; 198 | uint np, oldl, oldt; 199 | Ref r, r1; 200 | Phi *p; 201 | Insert *ist; 202 | Loc l; 203 | 204 | /* invariants: 205 | * -1- b dominates il->blk; so we can use 206 | * temporaries of b in il->blk 207 | * -2- if il->type != LNoLoad, then il->blk 208 | * postdominates the original load; so it 209 | * is safe to load in il->blk 210 | * -3- if il->type != LNoLoad, then b 211 | * postdominates il->blk (and by 2, the 212 | * original load) 213 | */ 214 | assert(dom(b, il->blk)); 215 | oldl = nlog; 216 | oldt = curf->ntmp; 217 | if (0) { 218 | Load: 219 | curf->ntmp = oldt; 220 | nlog = oldl; 221 | if (il->type != LLoad) 222 | return R; 223 | return load(sl, msk, il); 224 | } 225 | 226 | if (!i) 227 | i = &b->ins[b->nins]; 228 | cls = sl.sz > 4 ? Kl : Kw; 229 | msks = MASK(sl.sz); 230 | 231 | while (i > b->ins) { 232 | --i; 233 | if (killsl(i->to, sl) 234 | || (i->op == Ocall && escapes(sl.ref, curf))) 235 | goto Load; 236 | ld = isload(i->op); 237 | if (ld) { 238 | sz = loadsz(i); 239 | r1 = i->arg[0]; 240 | r = i->to; 241 | } else if (isstore(i->op)) { 242 | sz = storesz(i); 243 | r1 = i->arg[1]; 244 | r = i->arg[0]; 245 | } else 246 | continue; 247 | switch (alias(sl.ref, sl.sz, r1, sz, &off, curf)) { 248 | case MustAlias: 249 | if (off < 0) { 250 | off = -off; 251 | msk1 = (MASK(sz) << 8*off) & msks; 252 | op = Oshl; 253 | } else { 254 | msk1 = (MASK(sz) >> 8*off) & msks; 255 | op = Oshr; 256 | } 257 | if ((msk1 & msk) == 0) 258 | break; 259 | if (off) { 260 | cls1 = cls; 261 | if (op == Oshr && off + sl.sz > 4) 262 | cls1 = Kl; 263 | cast(&r, cls1, il); 264 | r1 = getcon(8*off, curf); 265 | r = iins(cls1, op, r, r1, il); 266 | } 267 | if ((msk1 & msk) != msk1 || off + sz < sl.sz) 268 | mask(cls, &r, msk1 & msk, il); 269 | if ((msk & ~msk1) != 0) { 270 | r1 = def(sl, msk & ~msk1, b, i, il); 271 | if (req(r1, R)) 272 | goto Load; 273 | r = iins(cls, Oor, r, r1, il); 274 | } 275 | if (msk == msks) 276 | cast(&r, sl.cls, il); 277 | return r; 278 | case MayAlias: 279 | if (ld) 280 | break; 281 | else 282 | goto Load; 283 | case NoAlias: 284 | break; 285 | default: 286 | die("unreachable"); 287 | } 288 | } 289 | 290 | for (ist=ilog; ist<&ilog[nlog]; ++ist) 291 | if (ist->isphi && ist->bid == b->id) 292 | if (req(ist->new.phi.m.ref, sl.ref)) 293 | if (ist->new.phi.m.sz == sl.sz) { 294 | r = ist->new.phi.p->to; 295 | if (msk != msks) 296 | mask(cls, &r, msk, il); 297 | else 298 | cast(&r, sl.cls, il); 299 | return r; 300 | } 301 | 302 | for (p=b->phi; p; p=p->link) 303 | if (killsl(p->to, sl)) 304 | /* scanning predecessors in that 305 | * case would be unsafe */ 306 | goto Load; 307 | 308 | if (b->npred == 0) 309 | goto Load; 310 | if (b->npred == 1) { 311 | bp = b->pred[0]; 312 | assert(bp->loop >= il->blk->loop); 313 | l = *il; 314 | if (bp->s2) 315 | l.type = LNoLoad; 316 | r1 = def(sl, msk, bp, 0, &l); 317 | if (req(r1, R)) 318 | goto Load; 319 | return r1; 320 | } 321 | 322 | r = newtmp("ld", sl.cls, curf); 323 | p = alloc(sizeof *p); 324 | vgrow(&ilog, ++nlog); 325 | ist = &ilog[nlog-1]; 326 | ist->isphi = 1; 327 | ist->bid = b->id; 328 | ist->new.phi.m = sl; 329 | ist->new.phi.p = p; 330 | p->to = r; 331 | p->cls = sl.cls; 332 | p->narg = b->npred; 333 | for (np=0; npnpred; ++np) { 334 | bp = b->pred[np]; 335 | if (!bp->s2 336 | && il->type != LNoLoad 337 | && bp->loop < il->blk->loop) 338 | l.type = LLoad; 339 | else 340 | l.type = LNoLoad; 341 | l.blk = bp; 342 | l.off = bp->nins; 343 | r1 = def(sl, msks, bp, 0, &l); 344 | if (req(r1, R)) 345 | goto Load; 346 | p->arg[np] = r1; 347 | p->blk[np] = bp; 348 | } 349 | if (msk != msks) 350 | mask(cls, &r, msk, il); 351 | return r; 352 | } 353 | 354 | static int 355 | icmp(const void *pa, const void *pb) 356 | { 357 | Insert *a, *b; 358 | int c; 359 | 360 | a = (Insert *)pa; 361 | b = (Insert *)pb; 362 | if ((c = a->bid - b->bid)) 363 | return c; 364 | if (a->isphi && b->isphi) 365 | return 0; 366 | if (a->isphi) 367 | return -1; 368 | if (b->isphi) 369 | return +1; 370 | if ((c = a->off - b->off)) 371 | return c; 372 | return a->num - b->num; 373 | } 374 | 375 | /* require rpo ssa alias */ 376 | void 377 | loadopt(Fn *fn) 378 | { 379 | Ins *i, *ib; 380 | Blk *b; 381 | int sz; 382 | uint n, ni, ext, nt; 383 | Insert *ist; 384 | Slice sl; 385 | Loc l; 386 | 387 | curf = fn; 388 | ilog = vnew(0, sizeof ilog[0], Pheap); 389 | nlog = 0; 390 | inum = 0; 391 | for (b=fn->start; b; b=b->link) 392 | for (i=b->ins; i<&b->ins[b->nins]; ++i) { 393 | if (!isload(i->op)) 394 | continue; 395 | sz = loadsz(i); 396 | sl = (Slice){i->arg[0], sz, i->cls}; 397 | l = (Loc){LRoot, i-b->ins, b}; 398 | i->arg[1] = def(sl, MASK(sz), b, i, &l); 399 | } 400 | qsort(ilog, nlog, sizeof ilog[0], icmp); 401 | vgrow(&ilog, nlog+1); 402 | ilog[nlog].bid = fn->nblk; /* add a sentinel */ 403 | ib = vnew(0, sizeof(Ins), Pheap); 404 | for (ist=ilog, n=0; nnblk; ++n) { 405 | b = fn->rpo[n]; 406 | for (; ist->bid == n && ist->isphi; ++ist) { 407 | ist->new.phi.p->link = b->phi; 408 | b->phi = ist->new.phi.p; 409 | } 410 | ni = 0; 411 | nt = 0; 412 | for (;;) { 413 | if (ist->bid == n && ist->off == ni) 414 | i = &ist++->new.ins; 415 | else { 416 | if (ni == b->nins) 417 | break; 418 | i = &b->ins[ni++]; 419 | if (isload(i->op) 420 | && !req(i->arg[1], R)) { 421 | ext = Oextsb + i->op - Oloadsb; 422 | switch (i->op) { 423 | default: 424 | die("unreachable"); 425 | case Oloadsb: 426 | case Oloadub: 427 | case Oloadsh: 428 | case Oloaduh: 429 | i->op = ext; 430 | break; 431 | case Oloadsw: 432 | case Oloaduw: 433 | if (i->cls == Kl) { 434 | i->op = ext; 435 | break; 436 | } 437 | case Oload: 438 | i->op = Ocopy; 439 | break; 440 | } 441 | i->arg[0] = i->arg[1]; 442 | i->arg[1] = R; 443 | } 444 | } 445 | vgrow(&ib, ++nt); 446 | ib[nt-1] = *i; 447 | } 448 | b->nins = nt; 449 | idup(&b->ins, ib, nt); 450 | } 451 | vfree(ib); 452 | vfree(ilog); 453 | if (debug['M']) { 454 | fprintf(stderr, "\n> After load elimination:\n"); 455 | printfn(fn, stderr); 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | #include "config.h" 3 | #include 4 | #include 5 | 6 | Target T; 7 | 8 | extern Target T_amd64_sysv; 9 | extern Target T_arm64; 10 | 11 | static struct TMap { 12 | char *name; 13 | Target *T; 14 | } tmap[] = { 15 | { "amd64_sysv", &T_amd64_sysv }, 16 | { "arm64", &T_arm64 }, 17 | { 0, 0 } 18 | }; 19 | 20 | enum Asm { 21 | Gasmacho, 22 | Gaself, 23 | }; 24 | 25 | char debug['Z'+1] = { 26 | ['P'] = 0, /* parsing */ 27 | ['A'] = 0, /* abi lowering */ 28 | ['I'] = 0, /* instruction selection */ 29 | ['L'] = 0, /* liveness */ 30 | ['M'] = 0, /* memory optimization */ 31 | ['N'] = 0, /* ssa construction */ 32 | ['C'] = 0, /* copy elimination */ 33 | ['F'] = 0, /* constant folding */ 34 | ['S'] = 0, /* spilling */ 35 | ['R'] = 0, /* reg. allocation */ 36 | }; 37 | 38 | static FILE *outf; 39 | static int dbg; 40 | 41 | static void 42 | data(Dat *d) 43 | { 44 | if (dbg) 45 | return; 46 | if (d->type == DEnd) { 47 | fputs("/* end data */\n\n", outf); 48 | freeall(); 49 | } 50 | gasemitdat(d, outf); 51 | } 52 | 53 | static void 54 | func(Fn *fn) 55 | { 56 | uint n; 57 | 58 | if (dbg) 59 | fprintf(stderr, "**** Function %s ****", fn->name); 60 | if (debug['P']) { 61 | fprintf(stderr, "\n> After parsing:\n"); 62 | printfn(fn, stderr); 63 | } 64 | fillrpo(fn); 65 | fillpreds(fn); 66 | filluse(fn); 67 | memopt(fn); 68 | ssa(fn); 69 | filluse(fn); 70 | ssacheck(fn); 71 | fillloop(fn); 72 | fillalias(fn); 73 | loadopt(fn); 74 | filluse(fn); 75 | ssacheck(fn); 76 | copy(fn); 77 | filluse(fn); 78 | fold(fn); 79 | T.abi(fn); 80 | fillpreds(fn); 81 | filluse(fn); 82 | T.isel(fn); 83 | fillrpo(fn); 84 | filllive(fn); 85 | fillcost(fn); 86 | spill(fn); 87 | rega(fn); 88 | fillrpo(fn); 89 | simpljmp(fn); 90 | fillpreds(fn); 91 | fillrpo(fn); 92 | assert(fn->rpo[0] == fn->start); 93 | for (n=0;; n++) 94 | if (n == fn->nblk-1) { 95 | fn->rpo[n]->link = 0; 96 | break; 97 | } else 98 | fn->rpo[n]->link = fn->rpo[n+1]; 99 | if (!dbg) { 100 | T.emitfn(fn, outf); 101 | fprintf(outf, "/* end function %s */\n\n", fn->name); 102 | } else 103 | fprintf(stderr, "\n"); 104 | freeall(); 105 | } 106 | 107 | int 108 | main(int ac, char *av[]) 109 | { 110 | struct TMap *tm; 111 | FILE *inf, *hf; 112 | char *f, *sep; 113 | int c, asm; 114 | 115 | asm = Defasm; 116 | T = Deftgt; 117 | outf = stdout; 118 | while ((c = getopt(ac, av, "hd:o:G:t:")) != -1) 119 | switch (c) { 120 | case 'd': 121 | for (; *optarg; optarg++) 122 | if (isalpha(*optarg)) { 123 | debug[toupper(*optarg)] = 1; 124 | dbg = 1; 125 | } 126 | break; 127 | case 'o': 128 | if (strcmp(optarg, "-") != 0) 129 | outf = fopen(optarg, "w"); 130 | break; 131 | case 't': 132 | for (tm=tmap;; tm++) { 133 | if (!tm->name) { 134 | fprintf(stderr, "unknown target '%s'\n", optarg); 135 | exit(1); 136 | } 137 | if (strcmp(optarg, tm->name) == 0) { 138 | T = *tm->T; 139 | break; 140 | } 141 | } 142 | break; 143 | case 'G': 144 | if (strcmp(optarg, "e") == 0) 145 | asm = Gaself; 146 | else if (strcmp(optarg, "m") == 0) 147 | asm = Gasmacho; 148 | else { 149 | fprintf(stderr, "unknown gas flavor '%s'\n", optarg); 150 | exit(1); 151 | } 152 | break; 153 | case 'h': 154 | default: 155 | hf = c != 'h' ? stderr : stdout; 156 | fprintf(hf, "%s [OPTIONS] {file.ssa, -}\n", av[0]); 157 | fprintf(hf, "\t%-11s prints this help\n", "-h"); 158 | fprintf(hf, "\t%-11s output to file\n", "-o file"); 159 | fprintf(hf, "\t%-11s generate for a target among:\n", "-t "); 160 | fprintf(hf, "\t%-11s ", ""); 161 | for (tm=tmap, sep=""; tm->name; tm++, sep=", ") 162 | fprintf(hf, "%s%s", sep, tm->name); 163 | fprintf(hf, "\n"); 164 | fprintf(hf, "\t%-11s generate gas (e) or osx (m) asm\n", "-G {e,m}"); 165 | fprintf(hf, "\t%-11s dump debug information\n", "-d "); 166 | exit(c != 'h'); 167 | } 168 | 169 | switch (asm) { 170 | case Gaself: 171 | gasloc = ".L"; 172 | gassym = ""; 173 | break; 174 | case Gasmacho: 175 | gasloc = "L"; 176 | gassym = "_"; 177 | break; 178 | } 179 | 180 | do { 181 | f = av[optind]; 182 | if (!f || strcmp(f, "-") == 0) { 183 | inf = stdin; 184 | f = "-"; 185 | } else { 186 | inf = fopen(f, "r"); 187 | if (!inf) { 188 | fprintf(stderr, "cannot open '%s'\n", f); 189 | exit(1); 190 | } 191 | } 192 | parse(inf, f, data, func); 193 | } while (++optind < ac); 194 | 195 | if (!dbg) 196 | gasemitfin(outf); 197 | 198 | exit(0); 199 | } 200 | -------------------------------------------------------------------------------- /mem.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | /* require use, maintains use counts */ 4 | void 5 | memopt(Fn *fn) 6 | { 7 | Blk *b; 8 | Ins *i, *l; 9 | Tmp *t; 10 | Use *u, *ue; 11 | int s, k; 12 | 13 | /* promote uniform stack slots to temporaries */ 14 | b = fn->start; 15 | for (i=b->ins; i-b->ins < b->nins; i++) { 16 | if (Oalloc > i->op || i->op > Oalloc1) 17 | continue; 18 | /* specific to NAlign == 3 */ 19 | assert(rtype(i->to) == RTmp); 20 | t = &fn->tmp[i->to.val]; 21 | if (t->ndef != 1) 22 | goto Skip; 23 | k = -1; 24 | s = -1; 25 | for (u=t->use; u != &t->use[t->nuse]; u++) { 26 | if (u->type != UIns) 27 | goto Skip; 28 | l = u->u.ins; 29 | if (isload(l->op)) 30 | if (s == -1 || s == loadsz(l)) { 31 | s = loadsz(l); 32 | continue; 33 | } 34 | if (isstore(l->op)) 35 | if (req(i->to, l->arg[1]) && !req(i->to, l->arg[0])) 36 | if (s == -1 || s == storesz(l)) 37 | if (k == -1 || k == optab[l->op].argcls[0][0]) { 38 | s = storesz(l); 39 | k = optab[l->op].argcls[0][0]; 40 | continue; 41 | } 42 | goto Skip; 43 | } 44 | /* get rid of the alloc and replace uses */ 45 | *i = (Ins){.op = Onop}; 46 | t->ndef--; 47 | ue = &t->use[t->nuse]; 48 | for (u=t->use; u!=ue; u++) { 49 | l = u->u.ins; 50 | if (isstore(l->op)) { 51 | l->cls = k; 52 | l->op = Ocopy; 53 | l->to = l->arg[1]; 54 | l->arg[1] = R; 55 | t->nuse--; 56 | t->ndef++; 57 | } else { 58 | if (k == -1) 59 | err("slot %%%s is read but never stored to", 60 | fn->tmp[l->arg[0].val].name); 61 | /* try to turn loads into copies so we 62 | * can eliminate them later */ 63 | switch(l->op) { 64 | case Oloadsw: 65 | case Oloaduw: 66 | if (k == Kl) 67 | goto Extend; 68 | case Oload: 69 | if (KBASE(k) != KBASE(l->cls)) 70 | l->op = Ocast; 71 | else 72 | l->op = Ocopy; 73 | break; 74 | default: 75 | Extend: 76 | l->op = Oextsb + (l->op - Oloadsb); 77 | break; 78 | } 79 | } 80 | } 81 | Skip:; 82 | } 83 | if (debug['M']) { 84 | fprintf(stderr, "\n> After memory optimization:\n"); 85 | printfn(fn, stderr); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /minic/.gitignore: -------------------------------------------------------------------------------- 1 | minic 2 | yacc 3 | y.* 4 | *.out 5 | -------------------------------------------------------------------------------- /minic/Makefile: -------------------------------------------------------------------------------- 1 | BIN = minic 2 | 3 | CFLAGS += -g -Wall 4 | 5 | $(BIN): yacc minic.y 6 | ./yacc minic.y 7 | $(CC) $(CFLAGS) -o $@ y.tab.c 8 | 9 | clean: 10 | rm -f yacc minic y.* 11 | 12 | .PHONY: clean 13 | -------------------------------------------------------------------------------- /minic/mcc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=`cd $(dirname $0); pwd` 4 | QBE=$DIR/../obj/qbe 5 | 6 | usage() 7 | { 8 | echo "usage: mcc [LDFLAGS] file.c" >&2 9 | exit 1 10 | } 11 | 12 | for i 13 | do 14 | case $i in 15 | -*) 16 | flags="$flags $i" 17 | ;; 18 | *) 19 | if ! test -z $file 20 | then 21 | usage 22 | fi 23 | file=$i 24 | ;; 25 | esac 26 | done 27 | 28 | if test -z $file 29 | then 30 | usage 31 | fi 32 | 33 | 34 | $DIR/minic < $file > /tmp/minic.ssa && 35 | $QBE < /tmp/minic.ssa > /tmp/minic.s && 36 | cc $flags /tmp/minic.s 37 | 38 | if test $? -ne 0 39 | then 40 | echo "error processing file $file" >&2 41 | exit 1 42 | fi 43 | 44 | 45 | -------------------------------------------------------------------------------- /minic/test/collatz.c: -------------------------------------------------------------------------------- 1 | void *malloc(); 2 | 3 | main() 4 | { 5 | int n; 6 | int nv; 7 | int c; 8 | int cmax; 9 | int *mem; 10 | 11 | mem = malloc(sizeof(int) * 4000); 12 | 13 | cmax = 0; 14 | for (nv = 1; nv < 1000; nv++) { 15 | n = nv; 16 | c = 0; 17 | while (n != 1) { 18 | if (n < nv) { 19 | c = c + mem[n]; 20 | break; 21 | } 22 | if (n & 1) 23 | n = 3*n + 1; 24 | else 25 | n = n / 2; 26 | c++; 27 | } 28 | mem[nv] = c; 29 | if (c > cmax) 30 | cmax = c; 31 | } 32 | printf("should print 178: %d\n", cmax); 33 | } 34 | -------------------------------------------------------------------------------- /minic/test/euler9.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | main() 4 | { 5 | int i; 6 | int a; 7 | int b; 8 | int c; 9 | int d; 10 | 11 | for (a = 1; a < 1000; a++) { 12 | for (b = a + 1; b < 1000; b++) { 13 | d = a*a + b*b; 14 | for (i = 0; i < 1000; i++) { 15 | if (i * i == d) { 16 | c = i; 17 | if (b < c && a+b+c == 1000) { 18 | printf("%d\n", a*b*c); 19 | return 0; 20 | } 21 | break; 22 | } 23 | } 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /minic/test/knight.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void *calloc(); 5 | 6 | int N; 7 | int **b; 8 | 9 | board() 10 | { 11 | int x; 12 | int y; 13 | 14 | for (y=0; y<8; y++) { 15 | for (x=0; x<8; x++) 16 | printf(" %02d", b[x][y]); 17 | printf("\n"); 18 | } 19 | printf("\n"); 20 | return 0; 21 | } 22 | 23 | chk(int x, int y) 24 | { 25 | if (x < 0 || x > 7 || y < 0 || y > 7) 26 | return 0; 27 | return b[x][y] == 0; 28 | } 29 | 30 | go(int k, int x, int y) 31 | { 32 | int i; 33 | int j; 34 | 35 | b[x][y] = k; 36 | if (k == 64) { 37 | if (x != 2 && y != 0 && abs(x-2) + abs(y) == 3) { 38 | board(); 39 | N++; 40 | if (N == 10) 41 | exit(0); 42 | } 43 | } else 44 | for (i=-2; i<=2; i++) 45 | for (j=-2; j<=2; j++) 46 | if (abs(i) + abs(j) == 3 && chk(x+i, y+j)) 47 | go(k+1, x+i, y+j); 48 | b[x][y] = 0; 49 | return 0; 50 | } 51 | 52 | main() 53 | { 54 | int i; 55 | 56 | b = calloc(8, sizeof (int *)); 57 | for (i=0; i<8; i++) 58 | b[i] = calloc(8, sizeof (int)); 59 | go(1, 2, 0); 60 | } 61 | -------------------------------------------------------------------------------- /minic/test/mandel.c: -------------------------------------------------------------------------------- 1 | void *malloc(); 2 | void *SDL_CreateWindow(); 3 | void *SDL_CreateRenderer(); 4 | int SDL_SetRenderDrawColor(); 5 | int SDL_RenderDrawPoint(); 6 | int SDL_RenderClear(); 7 | int SDL_RenderPresent(); 8 | int SDL_PollEvent(); 9 | int SDL_DestroyRenderer(); 10 | int SDL_DestroyWindow(); 11 | int SDL_Quit(); 12 | int SDL_Init(); 13 | 14 | void *win; 15 | void *rnd; 16 | int W; 17 | int H; 18 | int *col; 19 | 20 | plot(int x, int y) 21 | { 22 | int n; 23 | int fx; 24 | int fy; 25 | int zx; 26 | int zy; 27 | int nx; 28 | int ny; 29 | 30 | fx = (x - W/2)*4000 / W; 31 | fy = (y - H/2)*4000 / H; 32 | zx = fx; 33 | zy = fy; 34 | 35 | for (n=0; n<200; n++) { 36 | if (zx*zx + zy*zy > 4000000) 37 | break; 38 | nx = (zx*zx)/1000 - (zy*zy)/1000 + fx; 39 | ny = zx*zy/500 + fy; 40 | zx = nx; 41 | zy = ny; 42 | } 43 | n = col[n]; 44 | SDL_SetRenderDrawColor(rnd, 100, n, n, 255); 45 | SDL_RenderDrawPoint(rnd, x, y); 46 | return 0; 47 | } 48 | 49 | main() { 50 | int c; 51 | int n; 52 | int x; 53 | int y; 54 | void *e; 55 | int *ie; 56 | 57 | W = 800; 58 | H = 800; 59 | SDL_Init(32); 60 | win = SDL_CreateWindow("Mandelbrot MiniC", 0, 0, W, H, 0); 61 | rnd = SDL_CreateRenderer(win, -1, 0); 62 | e = malloc(56); 63 | ie = e; 64 | col = malloc(201 * sizeof (int)); 65 | c = 20; 66 | for (n=0; n<200; n++) { 67 | col[n] = c; 68 | c = c + (255-c)/8; 69 | } 70 | col[n] = 30; 71 | 72 | SDL_RenderClear(rnd); 73 | for (x=0; x 2 | 3 | main() { 4 | int n; 5 | int t; 6 | int c; 7 | int p; 8 | 9 | c = 0; 10 | n = 2; 11 | while (n < 5000) { 12 | t = 2; 13 | p = 1; 14 | while (t*t <= n) { 15 | if (n % t == 0) 16 | p = 0; 17 | t++; 18 | } 19 | if (p) { 20 | if (c && c % 10 == 0) 21 | printf("\n"); 22 | printf("%4d ", n); 23 | c++; 24 | } 25 | n++; 26 | } 27 | printf("\n"); 28 | } 29 | -------------------------------------------------------------------------------- /minic/test/queen.c: -------------------------------------------------------------------------------- 1 | int printf(); 2 | void *calloc(); 3 | int atoi(); 4 | 5 | int Q; 6 | int N; 7 | int **t; 8 | 9 | print() { 10 | int x; 11 | int y; 12 | 13 | for (y=0; y= 0) 34 | r = r + t[x+i][y-i]; 35 | if (x-i >= 0 & y+i < Q) 36 | r = r + t[x-i][y+i]; 37 | if (x-i >= 0 & y-i >= 0) 38 | r = r + t[x-i][y-i]; 39 | } 40 | return r; 41 | } 42 | 43 | go(int y) { 44 | int x; 45 | 46 | if (y == Q) { 47 | print(); 48 | N++; 49 | return 0; 50 | } 51 | for (x=0; x= 2) 64 | Q = atoi(av[1]); 65 | t = calloc(Q, sizeof(int *)); 66 | for (i=0; igen, b->gen); 11 | for (k=0; k<2; k++) 12 | if (b->nlive[k] > hd->nlive[k]) 13 | hd->nlive[k] = b->nlive[k]; 14 | } 15 | 16 | static void 17 | tmpuse(Ref r, int use, int loop, Fn *fn) 18 | { 19 | Mem *m; 20 | Tmp *t; 21 | 22 | if (rtype(r) == RMem) { 23 | m = &fn->mem[r.val]; 24 | tmpuse(m->base, 1, loop, fn); 25 | tmpuse(m->index, 1, loop, fn); 26 | } 27 | else if (rtype(r) == RTmp && r.val >= Tmp0) { 28 | t = &fn->tmp[r.val]; 29 | t->nuse += use; 30 | t->ndef += !use; 31 | t->cost += loop; 32 | } 33 | } 34 | 35 | /* evaluate spill costs of temporaries, 36 | * this also fills usage information 37 | * requires rpo, preds 38 | */ 39 | void 40 | fillcost(Fn *fn) 41 | { 42 | int n; 43 | uint a; 44 | Blk *b; 45 | Ins *i; 46 | Tmp *t; 47 | Phi *p; 48 | 49 | loopiter(fn, aggreg); 50 | if (debug['S']) { 51 | fprintf(stderr, "\n> Loop information:\n"); 52 | for (b=fn->start; b; b=b->link) { 53 | for (a=0; anpred; ++a) 54 | if (b->id <= b->pred[a]->id) 55 | break; 56 | if (a != b->npred) { 57 | fprintf(stderr, "\t%-10s", b->name); 58 | fprintf(stderr, " (% 3d ", b->nlive[0]); 59 | fprintf(stderr, "% 3d) ", b->nlive[1]); 60 | dumpts(b->gen, fn->tmp, stderr); 61 | } 62 | } 63 | } 64 | for (t=fn->tmp; t-fn->tmp < fn->ntmp; t++) { 65 | t->cost = t-fn->tmp < Tmp0 ? UINT_MAX : 0; 66 | t->nuse = 0; 67 | t->ndef = 0; 68 | } 69 | for (b=fn->start; b; b=b->link) { 70 | for (p=b->phi; p; p=p->link) { 71 | t = &fn->tmp[p->to.val]; 72 | tmpuse(p->to, 0, 0, fn); 73 | for (a=0; anarg; a++) { 74 | n = p->blk[a]->loop; 75 | t->cost += n; 76 | tmpuse(p->arg[a], 1, n, fn); 77 | } 78 | } 79 | n = b->loop; 80 | for (i=b->ins; i-b->ins < b->nins; i++) { 81 | tmpuse(i->to, 0, n, fn); 82 | tmpuse(i->arg[0], 1, n, fn); 83 | tmpuse(i->arg[1], 1, n, fn); 84 | } 85 | tmpuse(b->jmp.arg, 1, n, fn); 86 | } 87 | if (debug['S']) { 88 | fprintf(stderr, "\n> Spill costs:\n"); 89 | for (n=Tmp0; nntmp; n++) 90 | fprintf(stderr, "\t%-10s %d\n", 91 | fn->tmp[n].name, 92 | fn->tmp[n].cost); 93 | fprintf(stderr, "\n"); 94 | } 95 | } 96 | 97 | static BSet *fst; /* temps to prioritize in registers (for tcmp1) */ 98 | static Tmp *tmp; /* current temporaries (for tcmpX) */ 99 | static int ntmp; /* current # of temps (for limit) */ 100 | static int locs; /* stack size used by locals */ 101 | static int slot4; /* next slot of 4 bytes */ 102 | static int slot8; /* ditto, 8 bytes */ 103 | static BSet mask[2][1]; /* class masks */ 104 | 105 | static int 106 | tcmp0(const void *pa, const void *pb) 107 | { 108 | uint ca, cb; 109 | 110 | ca = tmp[*(int *)pa].cost; 111 | cb = tmp[*(int *)pb].cost; 112 | return (cb < ca) ? -1 : (cb > ca); 113 | } 114 | 115 | static int 116 | tcmp1(const void *pa, const void *pb) 117 | { 118 | int c; 119 | 120 | c = bshas(fst, *(int *)pb) - bshas(fst, *(int *)pa); 121 | return c ? c : tcmp0(pa, pb); 122 | } 123 | 124 | static Ref 125 | slot(int t) 126 | { 127 | int s; 128 | 129 | assert(t >= Tmp0 && "cannot spill register"); 130 | s = tmp[t].slot; 131 | if (s == -1) { 132 | /* specific to NAlign == 3 */ 133 | /* nice logic to pack stack slots 134 | * on demand, there can be only 135 | * one hole and slot4 points to it 136 | * 137 | * invariant: slot4 <= slot8 138 | */ 139 | if (KWIDE(tmp[t].cls)) { 140 | s = slot8; 141 | if (slot4 == slot8) 142 | slot4 += 2; 143 | slot8 += 2; 144 | } else { 145 | s = slot4; 146 | if (slot4 == slot8) { 147 | slot8 += 2; 148 | slot4 += 1; 149 | } else 150 | slot4 = slot8; 151 | } 152 | s += locs; 153 | tmp[t].slot = s; 154 | } 155 | return SLOT(s); 156 | } 157 | 158 | static void 159 | limit(BSet *b, int k, BSet *f) 160 | { 161 | static int *tarr, maxt; 162 | int i, t, nt; 163 | 164 | nt = bscount(b); 165 | if (nt <= k) 166 | return; 167 | if (nt > maxt) { 168 | free(tarr); 169 | tarr = emalloc(nt * sizeof tarr[0]); 170 | maxt = nt; 171 | } 172 | for (i=0, t=0; bsiter(b, &t); t++) { 173 | bsclr(b, t); 174 | tarr[i++] = t; 175 | } 176 | if (!f) 177 | qsort(tarr, nt, sizeof tarr[0], tcmp0); 178 | else { 179 | fst = f; 180 | qsort(tarr, nt, sizeof tarr[0], tcmp1); 181 | } 182 | for (i=0; iop == Ocopy && isreg(i->arg[0]); 232 | } 233 | 234 | static Ins * 235 | dopm(Blk *b, Ins *i, BSet *v) 236 | { 237 | int n, t; 238 | BSet u[1]; 239 | Ins *i1; 240 | bits r; 241 | 242 | bsinit(u, ntmp); /* todo, free those */ 243 | /* consecutive copies from 244 | * registers need to be handled 245 | * as one large instruction 246 | * 247 | * fixme: there is an assumption 248 | * that calls are always followed 249 | * by copy instructions here, this 250 | * might not be true if previous 251 | * passes change 252 | */ 253 | i1 = ++i; 254 | do { 255 | i--; 256 | t = i->to.val; 257 | if (!req(i->to, R)) 258 | if (bshas(v, t)) { 259 | bsclr(v, t); 260 | store(i->to, tmp[t].slot); 261 | } 262 | bsset(v, i->arg[0].val); 263 | } while (i != b->ins && regcpy(i-1)); 264 | bscopy(u, v); 265 | if (i != b->ins && (i-1)->op == Ocall) { 266 | v->t[0] &= ~T.retregs((i-1)->arg[1], 0); 267 | limit2(v, T.nrsave[0], T.nrsave[1], 0); 268 | for (n=0, r=0; T.rsave[n]>=0; n++) 269 | r |= BIT(T.rsave[n]); 270 | v->t[0] |= T.argregs((i-1)->arg[1], 0); 271 | } else { 272 | limit2(v, 0, 0, 0); 273 | r = v->t[0]; 274 | } 275 | sethint(v, r); 276 | reloads(u, v); 277 | do 278 | emiti(*--i1); 279 | while (i1 != i); 280 | return i; 281 | } 282 | 283 | /* spill code insertion 284 | * requires spill costs, rpo, liveness 285 | * 286 | * Note: this will replace liveness 287 | * information (in, out) with temporaries 288 | * that must be in registers at block 289 | * borders 290 | * 291 | * Be careful with: 292 | * - Ocopy instructions to ensure register 293 | * constraints 294 | */ 295 | void 296 | spill(Fn *fn) 297 | { 298 | Blk *b, *s1, *s2, *hd, **bp; 299 | int j, l, t, k, lvarg[2]; 300 | uint n; 301 | BSet u[1], v[1], w[1]; 302 | Ins *i; 303 | Phi *p; 304 | Mem *m; 305 | bits r; 306 | 307 | tmp = fn->tmp; 308 | ntmp = fn->ntmp; 309 | bsinit(u, ntmp); 310 | bsinit(v, ntmp); 311 | bsinit(w, ntmp); 312 | bsinit(mask[0], ntmp); 313 | bsinit(mask[1], ntmp); 314 | locs = fn->slot; 315 | slot4 = 0; 316 | slot8 = 0; 317 | for (t=0; t= T.fpr0 && t < T.fpr0 + T.nfpr) 320 | k = 1; 321 | if (t >= Tmp0) 322 | k = KBASE(tmp[t].cls); 323 | bsset(mask[k], t); 324 | } 325 | 326 | for (bp=&fn->rpo[fn->nblk]; bp!=fn->rpo;) { 327 | b = *--bp; 328 | /* invariant: all bocks with bigger rpo got 329 | * their in,out updated. */ 330 | 331 | /* 1. find temporaries in registers at 332 | * the end of the block (put them in v) */ 333 | curi = 0; 334 | s1 = b->s1; 335 | s2 = b->s2; 336 | hd = 0; 337 | if (s1 && s1->id <= b->id) 338 | hd = s1; 339 | if (s2 && s2->id <= b->id) 340 | if (!hd || s2->id >= hd->id) 341 | hd = s2; 342 | if (hd) { 343 | /* back-edge */ 344 | bszero(v); 345 | hd->gen->t[0] |= T.rglob; /* don't spill registers */ 346 | for (k=0; k<2; k++) { 347 | n = k == 0 ? T.ngpr : T.nfpr; 348 | bscopy(u, b->out); 349 | bsinter(u, mask[k]); 350 | bscopy(w, u); 351 | bsinter(u, hd->gen); 352 | bsdiff(w, hd->gen); 353 | if (bscount(u) < n) { 354 | j = bscount(w); /* live through */ 355 | l = hd->nlive[k]; 356 | limit(w, n - (l - j), 0); 357 | bsunion(u, w); 358 | } else 359 | limit(u, n, 0); 360 | bsunion(v, u); 361 | } 362 | } else if (s1) { 363 | liveon(v, b, s1); 364 | if (s2) { 365 | liveon(u, b, s2); 366 | bscopy(w, u); 367 | bsinter(w, v); 368 | bsunion(v, u); 369 | } 370 | limit2(v, 0, 0, w); 371 | } else { 372 | bscopy(v, b->out); 373 | if (rtype(b->jmp.arg) == RCall) 374 | v->t[0] |= T.retregs(b->jmp.arg, 0); 375 | } 376 | for (t=Tmp0; bsiter(b->out, &t); t++) 377 | if (!bshas(v, t)) 378 | slot(t); 379 | bscopy(b->out, v); 380 | 381 | /* 2. process the block instructions */ 382 | r = v->t[0]; 383 | curi = &insb[NIns]; 384 | for (i=&b->ins[b->nins]; i!=b->ins;) { 385 | i--; 386 | if (regcpy(i)) { 387 | i = dopm(b, i, v); 388 | continue; 389 | } 390 | bszero(w); 391 | if (!req(i->to, R)) { 392 | assert(rtype(i->to) == RTmp); 393 | t = i->to.val; 394 | if (bshas(v, t)) 395 | bsclr(v, t); 396 | else { 397 | /* make sure we have a reg 398 | * for the result */ 399 | bsset(v, t); 400 | bsset(w, t); 401 | } 402 | } 403 | j = T.memargs(i->op); 404 | for (n=0; n<2; n++) 405 | if (rtype(i->arg[n]) == RMem) 406 | j--; 407 | for (n=0; n<2; n++) 408 | switch (rtype(i->arg[n])) { 409 | case RMem: 410 | t = i->arg[n].val; 411 | m = &fn->mem[t]; 412 | if (rtype(m->base) == RTmp) { 413 | bsset(v, m->base.val); 414 | bsset(w, m->base.val); 415 | } 416 | if (rtype(m->index) == RTmp) { 417 | bsset(v, m->index.val); 418 | bsset(w, m->index.val); 419 | } 420 | break; 421 | case RTmp: 422 | t = i->arg[n].val; 423 | lvarg[n] = bshas(v, t); 424 | bsset(v, t); 425 | if (j-- <= 0) 426 | bsset(w, t); 427 | break; 428 | } 429 | bscopy(u, v); 430 | limit2(v, 0, 0, w); 431 | for (n=0; n<2; n++) 432 | if (rtype(i->arg[n]) == RTmp) { 433 | t = i->arg[n].val; 434 | if (!bshas(v, t)) { 435 | /* do not reload if the 436 | * the temporary was dead 437 | */ 438 | if (!lvarg[n]) 439 | bsclr(u, t); 440 | i->arg[n] = slot(t); 441 | } 442 | } 443 | reloads(u, v); 444 | if (!req(i->to, R)) { 445 | t = i->to.val; 446 | store(i->to, tmp[t].slot); 447 | bsclr(v, t); 448 | } 449 | emiti(*i); 450 | r = v->t[0]; /* Tmp0 is NBit */ 451 | if (r) 452 | sethint(v, r); 453 | } 454 | assert(r == T.rglob || b == fn->start); 455 | 456 | for (p=b->phi; p; p=p->link) { 457 | assert(rtype(p->to) == RTmp); 458 | t = p->to.val; 459 | if (bshas(v, t)) { 460 | bsclr(v, t); 461 | store(p->to, tmp[t].slot); 462 | } else if (bshas(b->in, t)) 463 | /* only if the phi is live */ 464 | p->to = slot(p->to.val); 465 | } 466 | bscopy(b->in, v); 467 | b->nins = &insb[NIns] - curi; 468 | idup(&b->ins, curi, b->nins); 469 | } 470 | 471 | /* align the locals to a 16 byte boundary */ 472 | /* specific to NAlign == 3 */ 473 | slot8 += slot8 & 3; 474 | fn->slot += slot8; 475 | 476 | if (debug['S']) { 477 | fprintf(stderr, "\n> Block information:\n"); 478 | for (b=fn->start; b; b=b->link) { 479 | fprintf(stderr, "\t%-10s (% 5d) ", b->name, b->loop); 480 | dumpts(b->out, fn->tmp, stderr); 481 | } 482 | fprintf(stderr, "\n> After spilling:\n"); 483 | printfn(fn, stderr); 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /ssa.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | #include 3 | 4 | static void 5 | adduse(Tmp *tmp, int ty, Blk *b, ...) 6 | { 7 | Use *u; 8 | int n; 9 | va_list ap; 10 | 11 | if (!tmp->use) 12 | return; 13 | va_start(ap, b); 14 | n = tmp->nuse; 15 | vgrow(&tmp->use, ++tmp->nuse); 16 | u = &tmp->use[n]; 17 | u->type = ty; 18 | u->bid = b->id; 19 | switch (ty) { 20 | case UPhi: 21 | u->u.phi = va_arg(ap, Phi *); 22 | break; 23 | case UIns: 24 | u->u.ins = va_arg(ap, Ins *); 25 | break; 26 | case UJmp: 27 | break; 28 | default: 29 | die("unreachable"); 30 | } 31 | va_end(ap); 32 | } 33 | 34 | /* fill usage, width, phi, and class information 35 | * must not change .visit fields 36 | */ 37 | void 38 | filluse(Fn *fn) 39 | { 40 | Blk *b; 41 | Phi *p; 42 | Ins *i; 43 | int m, t, tp, w; 44 | uint a; 45 | Tmp *tmp; 46 | 47 | /* todo, is this the correct file? */ 48 | tmp = fn->tmp; 49 | for (t=Tmp0; tntmp; t++) { 50 | tmp[t].ndef = 0; 51 | tmp[t].nuse = 0; 52 | tmp[t].cls = 0; 53 | tmp[t].phi = 0; 54 | tmp[t].width = WFull; 55 | if (tmp[t].use == 0) 56 | tmp[t].use = vnew(0, sizeof(Use), Pfn); 57 | } 58 | for (b=fn->start; b; b=b->link) { 59 | for (p=b->phi; p; p=p->link) { 60 | assert(rtype(p->to) == RTmp); 61 | tp = p->to.val; 62 | tmp[tp].ndef++; 63 | tmp[tp].cls = p->cls; 64 | tp = phicls(tp, fn->tmp); 65 | for (a=0; anarg; a++) 66 | if (rtype(p->arg[a]) == RTmp) { 67 | t = p->arg[a].val; 68 | adduse(&tmp[t], UPhi, b, p); 69 | t = phicls(t, fn->tmp); 70 | if (t != tp) 71 | tmp[t].phi = tp; 72 | } 73 | } 74 | for (i=b->ins; i-b->ins < b->nins; i++) { 75 | if (!req(i->to, R)) { 76 | assert(rtype(i->to) == RTmp); 77 | w = WFull; 78 | if (isload(i->op) && i->op != Oload) 79 | w = Wsb + (i->op - Oloadsb); 80 | if (isext(i->op)) 81 | w = Wsb + (i->op - Oextsb); 82 | if (w == Wsw || w == Wuw) 83 | if (i->cls == Kw) 84 | w = WFull; 85 | t = i->to.val; 86 | tmp[t].width = w; 87 | tmp[t].ndef++; 88 | tmp[t].cls = i->cls; 89 | } 90 | for (m=0; m<2; m++) 91 | if (rtype(i->arg[m]) == RTmp) { 92 | t = i->arg[m].val; 93 | adduse(&tmp[t], UIns, b, i); 94 | } 95 | } 96 | if (rtype(b->jmp.arg) == RTmp) 97 | adduse(&tmp[b->jmp.arg.val], UJmp, b); 98 | } 99 | } 100 | 101 | static Ref 102 | refindex(int t, Fn *fn) 103 | { 104 | return newtmp(fn->tmp[t].name, fn->tmp[t].cls, fn); 105 | } 106 | 107 | static void 108 | phiins(Fn *fn) 109 | { 110 | BSet u[1], defs[1]; 111 | Blk *a, *b, **blist, **be, **bp; 112 | Ins *i; 113 | Phi *p; 114 | Ref r; 115 | int t, nt; 116 | uint n; 117 | short k; 118 | 119 | bsinit(u, fn->nblk); 120 | bsinit(defs, fn->nblk); 121 | blist = emalloc(fn->nblk * sizeof blist[0]); 122 | be = &blist[fn->nblk]; 123 | nt = fn->ntmp; 124 | for (t=Tmp0; ttmp[t].visit = 0; 126 | if (fn->tmp[t].phi != 0) 127 | continue; 128 | bszero(u); 129 | k = -1; 130 | bp = be; 131 | for (b=fn->start; b; b=b->link) { 132 | b->visit = 0; 133 | r = R; 134 | for (i=b->ins; i-b->ins < b->nins; i++) { 135 | if (!req(r, R)) { 136 | if (req(i->arg[0], TMP(t))) 137 | i->arg[0] = r; 138 | if (req(i->arg[1], TMP(t))) 139 | i->arg[1] = r; 140 | } 141 | if (req(i->to, TMP(t))) { 142 | if (!bshas(b->out, t)) { 143 | if (fn->tmp[t].ndef == 1) 144 | r = TMP(t); 145 | else 146 | r = refindex(t, fn); 147 | i->to = r; 148 | } else { 149 | if (!bshas(u, b->id)) { 150 | bsset(u, b->id); 151 | *--bp = b; 152 | } 153 | if (clsmerge(&k, i->cls)) 154 | die("invalid input"); 155 | } 156 | } 157 | } 158 | if (!req(r, R) && req(b->jmp.arg, TMP(t))) 159 | b->jmp.arg = r; 160 | } 161 | bscopy(defs, u); 162 | while (bp != be) { 163 | fn->tmp[t].visit = t; 164 | b = *bp++; 165 | bsclr(u, b->id); 166 | for (n=0; nnfron; n++) { 167 | a = b->fron[n]; 168 | if (a->visit++ == 0) 169 | if (bshas(a->in, t)) { 170 | p = alloc(sizeof *p); 171 | p->cls = k; 172 | p->to = TMP(t); 173 | p->link = a->phi; 174 | a->phi = p; 175 | if (!bshas(defs, a->id)) 176 | if (!bshas(u, a->id)) { 177 | bsset(u, a->id); 178 | *--bp = a; 179 | } 180 | } 181 | } 182 | } 183 | } 184 | free(blist); 185 | } 186 | 187 | typedef struct Name Name; 188 | struct Name { 189 | Ref r; 190 | Blk *b; 191 | Name *up; 192 | }; 193 | 194 | static Name *namel; 195 | 196 | static Name * 197 | nnew(Ref r, Blk *b, Name *up) 198 | { 199 | Name *n; 200 | 201 | if (namel) { 202 | n = namel; 203 | namel = n->up; 204 | } else 205 | /* could use alloc, here 206 | * but namel should be reset 207 | */ 208 | n = emalloc(sizeof *n); 209 | n->r = r; 210 | n->b = b; 211 | n->up = up; 212 | return n; 213 | } 214 | 215 | static void 216 | nfree(Name *n) 217 | { 218 | n->up = namel; 219 | namel = n; 220 | } 221 | 222 | static void 223 | rendef(Ref *r, Blk *b, Name **stk, Fn *fn) 224 | { 225 | Ref r1; 226 | int t; 227 | 228 | t = r->val; 229 | if (req(*r, R) || !fn->tmp[t].visit) 230 | return; 231 | r1 = refindex(t, fn); 232 | fn->tmp[r1.val].visit = t; 233 | stk[t] = nnew(r1, b, stk[t]); 234 | *r = r1; 235 | } 236 | 237 | static Ref 238 | getstk(int t, Blk *b, Name **stk) 239 | { 240 | Name *n, *n1; 241 | 242 | n = stk[t]; 243 | while (n && !dom(n->b, b)) { 244 | n1 = n; 245 | n = n->up; 246 | nfree(n1); 247 | } 248 | stk[t] = n; 249 | if (!n) { 250 | /* uh, oh, warn */ 251 | return CON_Z; 252 | } else 253 | return n->r; 254 | } 255 | 256 | static void 257 | renblk(Blk *b, Name **stk, Fn *fn) 258 | { 259 | Phi *p; 260 | Ins *i; 261 | Blk *s, **ps, *succ[3]; 262 | int t, m; 263 | 264 | for (p=b->phi; p; p=p->link) 265 | rendef(&p->to, b, stk, fn); 266 | for (i=b->ins; i-b->ins < b->nins; i++) { 267 | for (m=0; m<2; m++) { 268 | t = i->arg[m].val; 269 | if (rtype(i->arg[m]) == RTmp) 270 | if (fn->tmp[t].visit) 271 | i->arg[m] = getstk(t, b, stk); 272 | } 273 | rendef(&i->to, b, stk, fn); 274 | } 275 | t = b->jmp.arg.val; 276 | if (rtype(b->jmp.arg) == RTmp) 277 | if (fn->tmp[t].visit) 278 | b->jmp.arg = getstk(t, b, stk); 279 | succ[0] = b->s1; 280 | succ[1] = b->s2 == b->s1 ? 0 : b->s2; 281 | succ[2] = 0; 282 | for (ps=succ; (s=*ps); ps++) 283 | for (p=s->phi; p; p=p->link) { 284 | t = p->to.val; 285 | if ((t=fn->tmp[t].visit)) { 286 | m = p->narg++; 287 | if (m == NPred) 288 | die("renblk, too many phi args"); 289 | p->arg[m] = getstk(t, b, stk); 290 | p->blk[m] = b; 291 | } 292 | } 293 | for (s=b->dom; s; s=s->dlink) 294 | renblk(s, stk, fn); 295 | } 296 | 297 | /* require rpo and ndef */ 298 | void 299 | ssa(Fn *fn) 300 | { 301 | Name **stk, *n; 302 | int d, nt; 303 | Blk *b, *b1; 304 | 305 | nt = fn->ntmp; 306 | stk = emalloc(nt * sizeof stk[0]); 307 | d = debug['L']; 308 | debug['L'] = 0; 309 | filldom(fn); 310 | if (debug['N']) { 311 | fprintf(stderr, "\n> Dominators:\n"); 312 | for (b1=fn->start; b1; b1=b1->link) { 313 | if (!b1->dom) 314 | continue; 315 | fprintf(stderr, "%10s:", b1->name); 316 | for (b=b1->dom; b; b=b->dlink) 317 | fprintf(stderr, " %s", b->name); 318 | fprintf(stderr, "\n"); 319 | } 320 | } 321 | fillfron(fn); 322 | filllive(fn); 323 | phiins(fn); 324 | renblk(fn->start, stk, fn); 325 | while (nt--) 326 | while ((n=stk[nt])) { 327 | stk[nt] = n->up; 328 | nfree(n); 329 | } 330 | debug['L'] = d; 331 | free(stk); 332 | if (debug['N']) { 333 | fprintf(stderr, "\n> After SSA construction:\n"); 334 | printfn(fn, stderr); 335 | } 336 | } 337 | 338 | static int 339 | phicheck(Phi *p, Blk *b, Ref t) 340 | { 341 | Blk *b1; 342 | uint n; 343 | 344 | for (n=0; nnarg; n++) 345 | if (req(p->arg[n], t)) { 346 | b1 = p->blk[n]; 347 | if (b1 != b && !sdom(b, b1)) 348 | return 1; 349 | } 350 | return 0; 351 | } 352 | 353 | /* require use and ssa */ 354 | void 355 | ssacheck(Fn *fn) 356 | { 357 | Tmp *t; 358 | Ins *i; 359 | Phi *p; 360 | Use *u; 361 | Blk *b, *bu; 362 | Ref r; 363 | 364 | for (t=&fn->tmp[Tmp0]; t-fn->tmp < fn->ntmp; t++) { 365 | if (t->ndef > 1) 366 | err("ssa temporary %%%s defined more than once", 367 | t->name); 368 | if (t->nuse > 0 && t->ndef == 0) { 369 | bu = fn->rpo[t->use[0].bid]; 370 | goto Err; 371 | } 372 | } 373 | for (b=fn->start; b; b=b->link) { 374 | for (p=b->phi; p; p=p->link) { 375 | r = p->to; 376 | t = &fn->tmp[r.val]; 377 | for (u=t->use; u-t->use < t->nuse; u++) { 378 | bu = fn->rpo[u->bid]; 379 | if (u->type == UPhi) { 380 | if (phicheck(u->u.phi, b, r)) 381 | goto Err; 382 | } else 383 | if (bu != b && !sdom(b, bu)) 384 | goto Err; 385 | } 386 | } 387 | for (i=b->ins; i-b->ins < b->nins; i++) { 388 | if (rtype(i->to) != RTmp) 389 | continue; 390 | r = i->to; 391 | t = &fn->tmp[r.val]; 392 | for (u=t->use; u-t->use < t->nuse; u++) { 393 | bu = fn->rpo[u->bid]; 394 | if (u->type == UPhi) { 395 | if (phicheck(u->u.phi, b, r)) 396 | goto Err; 397 | } else { 398 | if (bu == b) { 399 | if (u->type == UIns) 400 | if (u->u.ins <= i) 401 | goto Err; 402 | } else 403 | if (!sdom(b, bu)) 404 | goto Err; 405 | } 406 | } 407 | } 408 | } 409 | return; 410 | Err: 411 | if (t->visit) 412 | die("%%%s violates ssa invariant", t->name); 413 | else 414 | err("ssa temporary %%%s is used undefined in @%s", 415 | t->name, bu->name); 416 | } 417 | -------------------------------------------------------------------------------- /test/_alt.ssa: -------------------------------------------------------------------------------- 1 | # an example with reducible control 2 | # flow graph that exposes poor 3 | # handling of looping constructs 4 | 5 | function $test() { 6 | @start 7 | %ten =w copy 10 8 | %dum =w copy 0 # dummy live-through temporary 9 | @loop 10 | %alt =w phi @start 0, @left %alt1, @right %alt1 11 | %cnt =w phi @start 100, @left %cnt, @right %cnt1 12 | %alt1 =w sub 1, %alt 13 | jnz %alt1, @right, @left 14 | @left 15 | %x =w phi @loop 10, @left %x1 16 | %x1 =w sub %x, 1 17 | %z =w copy %x 18 | jnz %z, @left, @loop 19 | @right 20 | %cnt1 =w sub %cnt, %ten 21 | jnz %cnt1, @loop, @end 22 | @end 23 | %ret =w add %cnt, %dum 24 | ret 25 | } 26 | -------------------------------------------------------------------------------- /test/_dragon.ssa: -------------------------------------------------------------------------------- 1 | # a moderately complex test for 2 | # dominators computation from 3 | # the dragon book 4 | # because branching is limited to 5 | # two, I had to split some blocks 6 | 7 | function $dragon() { 8 | @start 9 | @b1 10 | jnz 0, @b2, @b3 11 | @b2 12 | jmp @b3 13 | @b3 14 | jmp @b4.1 15 | @b4.1 16 | jnz 0, @b3, @b4.2 17 | @b4.2 18 | jnz 0, @b5, @b6 19 | @b5 20 | jmp @b7 21 | @b6 22 | jmp @b7 23 | @b7 24 | jnz 0, @b8.1, @b4.1 25 | @b8.1 26 | jnz 0, @b3, @b8.2 27 | @b8.2 28 | jnz 0, @b9, @b10 29 | @b9 30 | jmp @b1 31 | @b10 32 | jmp @b7 33 | } 34 | -------------------------------------------------------------------------------- /test/_fix1.ssa: -------------------------------------------------------------------------------- 1 | function $test() { 2 | @start 3 | %x =w copy 1 4 | @loop 5 | jnz %x, @noz, @isz 6 | @noz 7 | %x =w copy 0 8 | jmp @end 9 | @isz 10 | %x =w copy 1 11 | jmp @loop 12 | @end 13 | %z =w add 10, %x 14 | ret 15 | } 16 | -------------------------------------------------------------------------------- /test/_fix2.ssa: -------------------------------------------------------------------------------- 1 | function $test() { 2 | @start 3 | %x =w copy 1 4 | @loop 5 | jnz %x, @noz, @isz 6 | @noz 7 | %x =w copy 0 8 | jnz %x, @loop, @end 9 | @isz 10 | %x =w copy 1 11 | jmp @loop 12 | @end 13 | %z =w add 10, %x 14 | ret 15 | } 16 | -------------------------------------------------------------------------------- /test/_fix3.ssa: -------------------------------------------------------------------------------- 1 | function w $test() { 2 | @start 3 | %x =w copy 100 4 | %s =w copy 0 5 | @l 6 | %c =w cslew %x, 10 7 | jnz %c, @a, @b 8 | @a 9 | %s =w add %s, %x 10 | %x =w sub %x, 1 11 | jmp @c 12 | @b 13 | %s =w sub %s, %x 14 | jmp @c 15 | @c 16 | %x =w sub %x, 1 17 | jnz %x, @l, @end 18 | @end 19 | ret %s 20 | } 21 | -------------------------------------------------------------------------------- /test/_fix4.ssa: -------------------------------------------------------------------------------- 1 | function $test() { 2 | @start 3 | %x =w copy 3 4 | %n =w copy 2 5 | @loop 6 | %c =w ceqw %n, 10000 7 | jnz %c, @end, @next 8 | @next 9 | %t =w copy 3 10 | %x =w add %x, 2 11 | @tloop 12 | %s =w mul %t, %t 13 | %c =w csgtw %s, %x 14 | jnz %c, @prime, @test 15 | @test 16 | %r =w rem %x, %t 17 | jnz %r, @tnext, @loop 18 | @tnext 19 | %t =w add %t, 2 20 | jmp @tloop 21 | @prime 22 | %n =w add %n, 1 23 | jmp @loop 24 | @end 25 | storew %x, $a 26 | ret 27 | } 28 | -------------------------------------------------------------------------------- /test/_live.ssa: -------------------------------------------------------------------------------- 1 | # this control flow graph is irreducible 2 | # yet, we expecet the liveness analysis 3 | # to work properly and make %x live in 4 | # the block @left 5 | # 6 | # nothing should ever be live at the entry 7 | 8 | function $test() { 9 | @start 10 | %b =w copy 0 11 | %x =w copy 10 12 | jnz 0, @loop, @left 13 | @left 14 | jmp @inloop 15 | @loop 16 | %x1 =w add %x, 1 17 | @inloop 18 | %b1 =w add %b, 1 19 | @endloop 20 | jmp @loop 21 | } 22 | -------------------------------------------------------------------------------- /test/_rpo.ssa: -------------------------------------------------------------------------------- 1 | function $test() { 2 | @start 3 | jmp @foo 4 | @baz 5 | jnz 1, @end, @foo 6 | @bar 7 | jmp @end 8 | @foo 9 | jnz 0, @bar, @baz 10 | @end 11 | ret 12 | } 13 | -------------------------------------------------------------------------------- /test/_spill1.ssa: -------------------------------------------------------------------------------- 1 | # test with NReg == 3 2 | # there must be a spill 3 | # happening on %c 4 | # 5 | # if you replace the sub 6 | # by an add or comment 7 | # the two marked lines 8 | # there should be no 9 | # spill 10 | # 11 | 12 | function $test() { 13 | @start 14 | %f =w copy 0 # here 15 | %b =w copy 1 16 | %c =w copy 2 17 | %a =w sub %b, %c 18 | %d =w copy %b 19 | %e =w copy %f # and there 20 | %g =w copy %a 21 | ret 22 | } 23 | -------------------------------------------------------------------------------- /test/_spill2.ssa: -------------------------------------------------------------------------------- 1 | # stupid spilling test 2 | 3 | function $test() { 4 | @start 5 | %x1 =w copy 10 6 | %x2 =w add %x1, %x1 7 | %x3 =w sub %x2, %x1 8 | %x4 =w add %x3, %x1 9 | %x5 =w sub %x4, %x1 10 | %x6 =w add %x5, %x1 11 | %x7 =w sub %x6, %x1 12 | %x8 =w add %x7, %x1 13 | %x9 =w sub %x8, %x8 14 | %x10 =w add %x9, %x7 15 | %x11 =w sub %x10, %x6 16 | %x12 =w add %x11, %x5 17 | %x13 =w sub %x12, %x4 18 | %x14 =w add %x13, %x3 19 | %x15 =w sub %x14, %x2 20 | %x16 =w add %x15, %x1 21 | ret 22 | } 23 | -------------------------------------------------------------------------------- /test/_spill3.ssa: -------------------------------------------------------------------------------- 1 | # make sure comparisons 2 | # never get their two 3 | # operands in memory 4 | # run with NReg == 3, or 5 | # adapt it! 6 | 7 | function $test() { 8 | @start 9 | %a =w loadw $a 10 | %b =w loadw $a 11 | 12 | @loop 13 | %c =w phi @start 0, @loop %f 14 | %d =w phi @start 0, @loop %g 15 | %e =w phi @start 0, @loop %h 16 | %f =w add %c, %d 17 | %g =w add %c, %e 18 | %h =w add %e, %d 19 | %x =w cslew %a, %b 20 | jnz %x, @loop, @end 21 | 22 | @end 23 | ret 24 | } 25 | -------------------------------------------------------------------------------- /test/abi1.ssa: -------------------------------------------------------------------------------- 1 | # test calling into C with two 2 | # large struct arguments (passed 3 | # on the stack) 4 | 5 | type :mem = { b 17 } 6 | 7 | function $alpha(l %p, w %l, l %n) { 8 | @ini 9 | %pe =l add %p, %n 10 | @lop 11 | %p1 =l phi @ini %p, @lop %p2 12 | %l1 =w phi @ini %l, @lop %l2 13 | storeb %l1, %p1 14 | %p2 =l add %p1, 1 15 | %l2 =w add %l1, 1 16 | %c1 =w ceql %p1, %pe 17 | jnz %c1, @end, @lop 18 | @end 19 | storeb 0, %pe 20 | ret 21 | } 22 | 23 | export 24 | function $test() { 25 | @start 26 | %p =l alloc4 17 27 | %q =l alloc4 17 28 | %r0 =w call $alpha(l %p, w 65, l 16) 29 | %r1 =w call $alpha(l %q, w 97, l 16) 30 | %r2 =w call $fcb(:mem %p, w 1, w 2, w 3, w 4, w 5, w 6, w 7, w 8, w 9, :mem %q) 31 | ret 32 | } 33 | 34 | 35 | # >>> driver 36 | # #include 37 | # typedef struct { char t[17]; } mem; 38 | # extern void test(); 39 | # void fcb(mem m, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, mem n) { 40 | # printf("fcb: m = (mem){ t = \"%s\" }\n", m.t); 41 | # printf(" n = (mem){ t = \"%s\" }\n", n.t); 42 | # #define T(n) printf(" i%d = %d\n", n, i##n); 43 | # T(1) T(2) T(3) T(4) T(5) T(6) T(7) T(8) T(9) 44 | # } 45 | # int main() { test(); return 0; } 46 | # <<< 47 | 48 | # >>> output 49 | # fcb: m = (mem){ t = "ABCDEFGHIJKLMNOP" } 50 | # n = (mem){ t = "abcdefghijklmnop" } 51 | # i1 = 1 52 | # i2 = 2 53 | # i3 = 3 54 | # i4 = 4 55 | # i5 = 5 56 | # i6 = 6 57 | # i7 = 7 58 | # i8 = 8 59 | # i9 = 9 60 | # <<< 61 | -------------------------------------------------------------------------------- /test/abi2.ssa: -------------------------------------------------------------------------------- 1 | type :fps = { s, b, s } 2 | 3 | export 4 | function s $sum(:fps %p) { 5 | @start 6 | %f1 =s load %p 7 | %p8 =l add 8, %p 8 | %f2 =s load %p8 9 | %s =s add %f1, %f2 10 | ret %s 11 | } 12 | 13 | # >>> driver 14 | # typedef struct { float f1; char b; float f2; } fps; 15 | # extern float sum(fps); 16 | # int main() { fps x = { 1.23, -1, 2.34 }; return !(sum(x) == 1.23f+2.34f); } 17 | # /* Note the f suffixes above are important 18 | # * otherwise C does double operations. */ 19 | # <<< 20 | -------------------------------------------------------------------------------- /test/abi3.ssa: -------------------------------------------------------------------------------- 1 | type :four = {l, b, w} 2 | 3 | data $z = { w 0 } 4 | 5 | export 6 | function $test() { 7 | @start 8 | %a =w loadw $z 9 | %y =w add %a, %a 10 | %yl =l extsw %y 11 | 12 | %s =l alloc8 16 # allocate a :four struct 13 | %s1 =l add %s, 12 # get address of the w 14 | storel 4, %s # set the l 15 | storew 5, %s1 # set the w 16 | 17 | # only the last argument should be on the stack 18 | %f =l add $F, %yl 19 | %x =w call %f(w %y, w 1, w 2, w 3, :four %s, w 6) 20 | 21 | # store the result in the 22 | # global variable a 23 | 24 | %x1 =w add %y, %x 25 | storew %x1, $a 26 | ret 27 | } 28 | 29 | # >>> driver 30 | # #include 31 | # struct four { long l; char c; int i; }; 32 | # extern void test(void); 33 | # int F(int a0, int a1, int a2, int a3, struct four s, int a6) { 34 | # printf("%d %d %d %d %d %d %d\n", 35 | # a0, a1, a2, a3, (int)s.l, s.i, a6); 36 | # return 42; 37 | # } 38 | # int a; 39 | # int main() { test(); printf("%d\n", a); return 0; } 40 | # <<< 41 | 42 | # >>> output 43 | # 0 1 2 3 4 5 6 44 | # 42 45 | # <<< 46 | -------------------------------------------------------------------------------- /test/abi4.ssa: -------------------------------------------------------------------------------- 1 | # return a large struct to C 2 | 3 | type :mem = { b 17 } 4 | 5 | function $alpha(l %p, w %l, l %n) { 6 | @ini 7 | %pe =l add %p, %n 8 | @lop 9 | %p1 =l phi @ini %p, @lop %p2 10 | %l1 =w phi @ini %l, @lop %l2 11 | storeb %l1, %p1 12 | %p2 =l add %p1, 1 13 | %l2 =w add %l1, 1 14 | %c1 =w ceql %p1, %pe 15 | jnz %c1, @end, @lop 16 | @end 17 | storeb 0, %pe 18 | ret 19 | } 20 | 21 | export 22 | function :mem $test() { 23 | @ini 24 | %p =l alloc4 17 25 | %r0 =w call $alpha(l %p, w 65, l 16) 26 | ret %p 27 | } 28 | 29 | 30 | # >>> driver 31 | # #include 32 | # typedef struct { char t[17]; } mem; 33 | # extern mem test(void); 34 | # int main() { mem m = test(); printf("%s\n", m.t); return 0; } 35 | # <<< 36 | 37 | # >>> output 38 | # ABCDEFGHIJKLMNOP 39 | # <<< 40 | -------------------------------------------------------------------------------- /test/abi5.ssa: -------------------------------------------------------------------------------- 1 | # returning structs from C 2 | 3 | type :st1 = { b 17 } 4 | type :st2 = { w } 5 | type :st3 = { s, w } 6 | type :st4 = { w, d } 7 | type :st5 = { s, l } 8 | type :st6 = { b 16 } 9 | type :st7 = { s, d } 10 | type :st8 = { w 4 } 11 | type :un9 = { { b } { s } } 12 | type :st9 = { w, :un9 } 13 | 14 | data $fmt1 = { b "t1: %s\n", b 0 } 15 | data $fmt2 = { b "t2: %d\n", b 0 } 16 | data $fmt3 = { b "t3: %f %d\n", b 0 } 17 | data $fmt4 = { b "t4: %d %f\n", b 0 } 18 | data $fmt5 = { b "t5: %f %lld\n", b 0 } 19 | data $fmt6 = { b "t6: %s\n", b 0 } 20 | data $fmt7 = { b "t7: %f %f\n", b 0 } 21 | data $fmt8 = { b "t8: %d %d %d %d\n", b 0 } 22 | data $fmt9 = { b "t9: %d %f\n", b 0 } 23 | 24 | export 25 | function $test() { 26 | @start 27 | %r1 =:st1 call $t1() 28 | %i1 =w call $printf(l $fmt1, l %r1, ...) 29 | 30 | %r2 =:st2 call $t2() 31 | %w2 =w loadw %r2 32 | %i2 =w call $printf(l $fmt2, w %w2, ...) 33 | 34 | %r3 =:st3 call $t3() 35 | %s3 =s loads %r3 36 | %r34 =l add %r3, 4 37 | %w3 =w loadw %r34 38 | %p3 =d exts %s3 39 | %i3 =w call $printf(l $fmt3, d %p3, w %w3, ...) 40 | 41 | %r4 =:st4 call $t4() 42 | %w4 =w loadw %r4 43 | %r48 =l add 8, %r4 44 | %d4 =d loadd %r48 45 | %i4 =w call $printf(l $fmt4, w %w4, d %d4, ...) 46 | 47 | %r5 =:st5 call $t5() 48 | %s5 =s loads %r5 49 | %d5 =d exts %s5 50 | %r58 =l add %r5, 8 51 | %l5 =l loadl %r58 52 | %i5 =w call $printf(l $fmt5, d %d5, l %l5, ...) 53 | 54 | %r6 =:st6 call $t6() 55 | %i6 =w call $printf(l $fmt6, l %r6, ...) 56 | 57 | %r7 =:st7 call $t7() 58 | %s7 =s loads %r7 59 | %d71 =d exts %s7 60 | %r78 =l add %r7, 8 61 | %d72 =d loadd %r78 62 | %i7 =w call $printf(l $fmt7, d %d71, d %d72, ...) 63 | 64 | %r8 =:st8 call $t8() 65 | %r84 =l add 4, %r8 66 | %r88 =l add 4, %r84 67 | %r812 =l add 4, %r88 68 | %w81 =w loadw %r8 69 | %w82 =w loadw %r84 70 | %w83 =w loadw %r88 71 | %w84 =w loadw %r812 72 | %i8 =w call $printf(l $fmt8, w %w81, w %w82, w %w83, w %w84, ...) 73 | 74 | %r9 =:st9 call $t9() 75 | %r94 =l add 4, %r9 76 | %w9 =w loadw %r9 77 | %s9 =s loads %r94 78 | %d9 =d exts %s9 79 | %i9 =w call $printf(l $fmt9, w %w9, d %d9, ...) 80 | 81 | ret 82 | } 83 | 84 | 85 | # >>> driver 86 | # #include 87 | # typedef struct { char t[17]; } st1; 88 | # typedef struct { int i; } st2; 89 | # typedef struct { float f; int i; } st3; 90 | # typedef struct { int i; double d; } st4; 91 | # typedef struct { float f; long l; } st5; 92 | # typedef struct { char t[16]; } st6; 93 | # typedef struct { float f; double d; } st7; 94 | # typedef struct { int i[4]; } st8; 95 | # typedef struct { int i; union { char c; float f; } u; } st9; 96 | # extern void test(void); 97 | # st1 t1() { return (st1){"abcdefghijklmnop"}; } 98 | # st2 t2() { return (st2){2}; } 99 | # st3 t3() { return (st3){3.0,30}; } 100 | # st4 t4() { return (st4){4,-40}; } 101 | # st5 t5() { return (st5){5.5,-55}; } 102 | # st6 t6() { return (st6){"abcdefghijklmno"}; } 103 | # st7 t7() { return (st7){7.77,77.7}; } 104 | # st8 t8() { return (st8){-8,88,-888,8888}; } 105 | # st9 t9() { return (st9){9,{.f=9.9}}; } 106 | # int main() { test(); return 0; } 107 | # <<< 108 | 109 | # >>> output 110 | # t1: abcdefghijklmnop 111 | # t2: 2 112 | # t3: 3.000000 30 113 | # t4: 4 -40.000000 114 | # t5: 5.500000 -55 115 | # t6: abcdefghijklmno 116 | # t7: 7.770000 77.700000 117 | # t8: -8 88 -888 8888 118 | # t9: 9 9.900000 119 | # <<< 120 | -------------------------------------------------------------------------------- /test/abi6.ssa: -------------------------------------------------------------------------------- 1 | # test arm64's hfa 2 | 3 | data $dfmt = { b "double: %g\n", b 0 } 4 | 5 | type :hfa3 = { s, s, s } 6 | 7 | export 8 | function $f(:hfa3 %h1, :hfa3 %h2, d %d1, :hfa3 %h3, d %d2) { 9 | # the first three parameters should be in 7 registers 10 | # the last two should be on the stack 11 | @start 12 | 13 | call $phfa3(:hfa3 %h1) 14 | call $phfa3(:hfa3 %h2) 15 | call $phfa3(:hfa3 %h3) 16 | call $printf(l $dfmt, d %d1) 17 | call $printf(l $dfmt, d %d2) 18 | ret 19 | } 20 | 21 | # >>> driver 22 | # #include 23 | # typedef struct { float f1, f2, f3; } hfa3; 24 | # void f(hfa3, hfa3, double, hfa3, double); 25 | # void phfa3(hfa3 h) { printf("{ %g, %g, %g }\n", h.f1, h.f2, h.f3); } 26 | # int main() { 27 | # hfa3 h1={1,2,3}, h2={2,3,4}, h3={3,4,5}; 28 | # f(h1, h2, 1, h3, 2); 29 | # } 30 | # <<< 31 | 32 | # >>> output 33 | # { 1, 2, 3 } 34 | # { 2, 3, 4 } 35 | # { 3, 4, 5 } 36 | # double: 1 37 | # double: 2 38 | # <<< 39 | -------------------------------------------------------------------------------- /test/align.ssa: -------------------------------------------------------------------------------- 1 | export 2 | function $test() { 3 | @start 4 | %x =l alloc16 16 5 | %y =l add %x, 8 6 | %m =w rem %y, 16 7 | storew %m, %y 8 | %n =w loadw %y 9 | storew %n, $a 10 | ret 11 | } 12 | 13 | # >>> driver 14 | # extern void test(void); 15 | # int a; 16 | # int main() { test(); return !(a == 8 || a == -8); } 17 | # <<< 18 | -------------------------------------------------------------------------------- /test/collatz.ssa: -------------------------------------------------------------------------------- 1 | # a solution for N=1000 to 2 | # https://projecteuler.net/problem=14 3 | # we use a fast local array to 4 | # memoize small collatz numbers 5 | 6 | export 7 | function $test() { 8 | @start 9 | %mem =l alloc4 4000 10 | @loop 11 | %n =w phi @start 1, @newm %n9, @oldm %n9 12 | %cmax =w phi @start 0, @newm %c, @oldm %cmax 13 | %fin =w csltw %n, 1000 14 | jnz %fin, @cloop, @end 15 | @cloop 16 | %n0 =w phi @loop %n, @odd %n2, @even %n3 17 | %c0 =w phi @loop 0, @odd %c1, @even %c1 18 | %no1 =w cnew %n0, 1 19 | jnz %no1, @iter0, @endcl 20 | @iter0 21 | %ism =w csltw %n0, %n 22 | jnz %ism, @getmemo, @iter1 23 | @iter1 24 | %c1 =w add %c0, 1 25 | %p =w and %n0, 1 26 | jnz %p, @odd, @even 27 | @odd 28 | %n1 =w mul 3, %n0 29 | %n2 =w add %n1, 1 30 | jmp @cloop 31 | @even 32 | %n3 =w shr %n0, 1 33 | jmp @cloop 34 | @getmemo # get the count for n0 in mem 35 | %n0l =l extsw %n0 36 | %idx0 =l mul %n0l, 4 37 | %loc0 =l add %idx0, %mem 38 | %cn0 =w loadw %loc0 39 | %c2 =w add %c0, %cn0 40 | @endcl # store the count for n in mem 41 | %c =w phi @getmemo %c2, @cloop %c0 42 | %nl =l extsw %n 43 | %idx1 =l mul %nl, 4 44 | %loc1 =l add %idx1, %mem 45 | storew %c, %loc1 46 | %n9 =w add 1, %n 47 | %big =w cslew %cmax, %c 48 | jnz %big, @newm, @oldm 49 | @newm 50 | jmp @loop 51 | @oldm 52 | jmp @loop 53 | @end 54 | storew %cmax, $a 55 | ret 56 | } 57 | 58 | # >>> driver 59 | # extern void test(void); 60 | # int a; 61 | # int main() { test(); return !(a == 178); } 62 | # <<< 63 | -------------------------------------------------------------------------------- /test/cprime.ssa: -------------------------------------------------------------------------------- 1 | # generated by Andrew Chambers' 2 | # compiler from the C program 3 | # following in comments 4 | 5 | export 6 | function w $main() { 7 | @start 8 | %v0 =l alloc8 4 9 | %v1 =l alloc8 4 10 | %v2 =l alloc8 4 11 | %v3 =l alloc8 4 12 | %v4 =l alloc8 4 13 | storew 5, %v1 14 | storew 11, %v2 15 | storew 12, %v3 16 | @L0 17 | %v5 =w loadw %v1 18 | %v6 =w cnew %v5, 201 19 | jnz %v6, @L8, @L1 20 | @L8 21 | storew 1, %v4 22 | %v7 =w loadw %v3 23 | %v8 =w rem %v7, 2 24 | %v9 =w ceqw %v8, 0 25 | jnz %v9, @L9, @L5 26 | @L9 27 | storew 0, %v4 28 | @L5 29 | storew 3, %v0 30 | @L2 31 | %v10 =w loadw %v0 32 | %v11 =w loadw %v3 33 | %v12 =w csltw %v10, %v11 34 | jnz %v12, @L10, @L3 35 | @L10 36 | %v13 =w loadw %v3 37 | %v14 =w loadw %v0 38 | %v15 =w rem %v13, %v14 39 | %v16 =w ceqw %v15, 0 40 | jnz %v16, @L11, @L4 41 | @L11 42 | storew 0, %v4 43 | jmp @L3 44 | @L4 45 | %v17 =w loadw %v0 46 | %v18 =w add %v17, 2 47 | storew %v18, %v0 48 | jmp @L2 49 | @L3 50 | %v19 =w loadw %v4 51 | jnz %v19, @L12, @L6 52 | @L12 53 | %v20 =w loadw %v3 54 | storew %v20, %v2 55 | %v21 =w loadw %v1 56 | %v22 =w add %v21, 1 57 | storew %v22, %v1 58 | @L6 59 | %v23 =w loadw %v3 60 | %v24 =w add %v23, 1 61 | storew %v24, %v3 62 | jmp @L0 63 | @L1 64 | %v25 =w loadw %v2 65 | %v26 =w cnew %v25, 1229 66 | jnz %v26, @L13, @L7 67 | @L13 68 | ret 1 69 | @L7 70 | ret 0 71 | @end 72 | ret 0 73 | } 74 | 75 | # int 76 | # main() 77 | # { 78 | # int i, n, p, next, isprime; 79 | # 80 | # n = 5; 81 | # p = 11; 82 | # next = 12; 83 | # while(n != 201) { 84 | # isprime = 1; 85 | # if(next % 2 == 0) { 86 | # isprime = 0; 87 | # } else { 88 | # for(i = 3; i < next; i = i + 2) { 89 | # if(next % i == 0) { 90 | # isprime = 0; 91 | # break; 92 | # } 93 | # } 94 | # } 95 | # if(isprime) { 96 | # p = next; 97 | # n = n + 1; 98 | # } 99 | # next = next + 1; 100 | # } 101 | # if(p != 1229) 102 | # return 1; 103 | # return 0; 104 | # } 105 | -------------------------------------------------------------------------------- /test/cup.ssa: -------------------------------------------------------------------------------- 1 | # counts up from -1988 to 1991 2 | 3 | export 4 | function $test() { 5 | @start 6 | @loop 7 | %n0 =l phi @start -1988, @loop %n1 8 | %n1 =l add 1, %n0 9 | %cmp =w cslel 1991, %n1 10 | jnz %cmp, @end, @loop 11 | @end 12 | ret 13 | } 14 | 15 | # >>> driver 16 | # extern void test(void); 17 | # int main() { test(); return 0; } 18 | # <<< 19 | -------------------------------------------------------------------------------- /test/dark.ssa: -------------------------------------------------------------------------------- 1 | # skip arm64 2 | # a hack example, 3 | # we use a dark type to get 4 | # a pointer to the stack. 5 | 6 | type :magic = align 1 { 0 } 7 | 8 | data $ret = { l 0 } 9 | 10 | export 11 | function $test(:magic %p) { 12 | @start 13 | %av =w loadw $a 14 | %a1 =w add 1, %av 15 | storew %a1, $a # increment $a 16 | %r1 =l loadl $ret # fetch from $ret 17 | %p1 =l add %p, -8 18 | %r2 =l loadl %p1 # get the return address 19 | storel %r2, $ret # store it in $ret 20 | %c =w ceql %r1, %r2 21 | jnz %c, @fin, @cal 22 | @cal 23 | %i =w call $test() # no argument given, intentionally! 24 | @fin 25 | ret 26 | } 27 | 28 | # >>> driver 29 | # extern void test(void); 30 | # int a = 2; 31 | # int main() { test(); return !(a == 5); } 32 | # <<< 33 | -------------------------------------------------------------------------------- /test/double.ssa: -------------------------------------------------------------------------------- 1 | export 2 | function $test() { 3 | @start 4 | %x1 =d copy d_0.1 5 | %x2 =d add d_0.2, %x1 6 | %x3 =d sub %x2, d_0.3 7 | 8 | @loop 9 | %x4 =d phi @start %x3, @loop %x5 10 | %i1 =w phi @start 0, @loop %i2 11 | %x5 =d add %x4, %x4 12 | %i2 =w add %i1, 1 13 | %c0 =w cled %x5, 4607182418800017408 # d_1.0 14 | jnz %c0, @loop, @end 15 | 16 | @end 17 | storew %i2, $a 18 | ret 19 | } 20 | 21 | # >>> driver 22 | # extern void test(void); 23 | # int a; 24 | # int main() { test(); return !(a == 55); } 25 | # <<< 26 | -------------------------------------------------------------------------------- /test/dynalloc.ssa: -------------------------------------------------------------------------------- 1 | # make sure dynamic allocations 2 | # and caller-save regs interact 3 | # soundly 4 | 5 | function $g() { 6 | @start 7 | ret 8 | } 9 | 10 | function w $f(w %arg) { 11 | @start 12 | call $g() 13 | @alloc 14 | %r =l alloc8 16 15 | storel 180388626474, %r 16 | %r8 =l add 8, %r 17 | storel 180388626474, %r8 18 | ret %arg 19 | } 20 | 21 | export 22 | function w $main() { 23 | @start 24 | %a =w call $f(w 0) 25 | %b =w call $f(w 0) 26 | ret %a 27 | } 28 | -------------------------------------------------------------------------------- /test/echo.ssa: -------------------------------------------------------------------------------- 1 | export 2 | function w $main(w %argc, l %argv) { 3 | @start 4 | %fmt =l alloc8 8 5 | storel 1663398693, %fmt # "%s%c" 6 | %av0 =l add %argv, 8 7 | %ac0 =w sub %argc, 1 8 | @loop 9 | %av =l phi @start %av0, @loop2 %av1 10 | %ac =w phi @start %ac0, @loop2 %ac1 11 | %c0 =w ceqw %ac, 0 12 | jnz %c0, @end, @loop1 13 | @loop1 14 | %c1 =w ceqw %ac, 1 15 | jnz %c1, @last, @nolast 16 | @last 17 | jmp @loop2 18 | @nolast 19 | jmp @loop2 20 | @loop2 21 | %sep =w phi @last 10, @nolast 32 22 | %arg =l loadl %av 23 | %r =w call $printf(l %fmt, l %arg, w %sep, ...) 24 | %av1 =l add %av, 8 25 | %ac1 =w sub %ac, 1 26 | jmp @loop 27 | @end 28 | ret 0 29 | } 30 | 31 | # >>> output 32 | # a b c 33 | # <<< 34 | -------------------------------------------------------------------------------- /test/eucl.ssa: -------------------------------------------------------------------------------- 1 | # euclide's algorithm in ssa 2 | # it is a fairly interesting 3 | # ssa program because of the 4 | # swap of b and a 5 | 6 | export 7 | function $test() { 8 | @start 9 | 10 | @loop 11 | %a =w phi @start 380, @loop %r 12 | %b =w phi @start 747, @loop %a 13 | %r =w rem %b, %a 14 | jnz %r, @loop, @end 15 | 16 | @end 17 | storew %a, $a 18 | ret 19 | } 20 | 21 | # >>> driver 22 | # extern void test(void); 23 | # int a; 24 | # int main() { test(); return !(a == 1); } 25 | # <<< 26 | -------------------------------------------------------------------------------- /test/euclc.ssa: -------------------------------------------------------------------------------- 1 | export 2 | function w $test() { 3 | @l0 4 | %a =l alloc4 4 5 | %b =l alloc4 4 6 | %r =l alloc4 4 7 | storew 747, %a 8 | storew 380, %b 9 | @l1 10 | %t4 =w loadw %b 11 | jnz %t4, @l2, @l3 12 | @l2 13 | %t7 =w loadw %a 14 | %t8 =w loadw %b 15 | %t6 =w rem %t7, %t8 16 | storew %t6, %r 17 | %t10 =w loadw %b 18 | storew %t10, %a 19 | %t12 =w loadw %r 20 | storew %t12, %b 21 | jmp @l1 22 | @l3 23 | %t13 =w loadw %a 24 | ret %t13 25 | } 26 | 27 | # >>> driver 28 | # extern int test(void); 29 | # int main() { return !(test() == 1); } 30 | # <<< 31 | -------------------------------------------------------------------------------- /test/fixarg.ssa: -------------------------------------------------------------------------------- 1 | # regression test for 3bec2c60 2 | 3 | export 4 | function w $test() { 5 | @start 6 | %x1 =l alloc8 8 7 | %x2 =l alloc8 8 8 | %r =w cnel %x1, %x2 # both operands need fixing 9 | ret %r 10 | } 11 | 12 | # >>> driver 13 | # extern int test(); 14 | # int main() { return !(test() == 1); } 15 | # <<< 16 | -------------------------------------------------------------------------------- /test/fpcnv.ssa: -------------------------------------------------------------------------------- 1 | # floating point casts and conversions 2 | 3 | export 4 | function s $fneg(s %f) { 5 | @fneg 6 | %b0 =w cast %f 7 | %b1 =w xor 2147483648, %b0 8 | %rs =s cast %b1 9 | ret %rs 10 | } 11 | 12 | export 13 | function d $ftrunc(d %f) { 14 | @ftrunc 15 | %l0 =w dtosi %f 16 | %rt =d swtof %l0 17 | ret %rt 18 | } 19 | 20 | # >>> driver 21 | # extern float fneg(float); 22 | # extern double ftrunc(double); 23 | # int main() { 24 | # if (fneg(1.23f) != -1.23f) return 1; 25 | # if (ftrunc(3.1415) != 3.0) return 2; 26 | # if (ftrunc(-1.234) != -1.0) return 3; 27 | # return 0; 28 | # } 29 | # <<< 30 | -------------------------------------------------------------------------------- /test/ldbits.ssa: -------------------------------------------------------------------------------- 1 | # unit tests for load elimination 2 | 3 | export 4 | function $tests() { 5 | @start 6 | %p =l alloc8 16 7 | %p3 =l add %p, 3 8 | %p4 =l add %p, 4 9 | %p6 =l add %p, 6 10 | %p8 =l add %p, 8 11 | @test1 12 | storew 1, $a 13 | storel 1311768467139281697, %p 14 | storeh 255, %p8 15 | %x1 =w load %p6 16 | %c1 =w cnew %x1, 16716340 17 | jnz %c1, @fail, @test2 18 | @test2 19 | storew 2, $a 20 | %x2 =w loadub %p3 21 | %c2 =w cnew %x2, 135 22 | jnz %c2, @fail, @test3 23 | @test3 24 | storew 3, $a 25 | storew 2864434397, %p8 26 | %x3 =l load %p3 27 | %c3 =w cnel %x3, -4914310023110821753 28 | jnz %c3, @fail, @test4 29 | @test4 30 | @ok 31 | storew 0, $a 32 | @fail 33 | ret 34 | } 35 | 36 | # >>> driver 37 | # extern void tests(void); 38 | # int a; 39 | # int main() { tests(); return a; } 40 | # <<< 41 | -------------------------------------------------------------------------------- /test/ldhoist.ssa: -------------------------------------------------------------------------------- 1 | # loads must not be unsafely hoisted 2 | 3 | export 4 | function w $f(w %n, l %p) { 5 | @start 6 | %r =w copy 0 7 | @loop 8 | %n =w sub %n, 1 9 | %c =w csgew %n, 0 10 | jnz %c, @loop1, @end 11 | @loop1 12 | %r =w loadw %p 13 | jmp @loop 14 | @end 15 | ret %r 16 | } 17 | 18 | # >>> driver 19 | # extern int f(int, int *); 20 | # int main() { return f(0, 0); } 21 | # <<< 22 | -------------------------------------------------------------------------------- /test/loop.ssa: -------------------------------------------------------------------------------- 1 | # simple looping program 2 | # sums all integers from 100 to 0 3 | 4 | export 5 | function $test() { 6 | @start 7 | 8 | @loop 9 | %s =w phi @start 0, @loop %s1 10 | %n =w phi @start 100, @loop %n1 11 | %s1 =w add %s, %n 12 | %n1 =w sub %n, 1 13 | jnz %n1, @loop, @end 14 | 15 | @end 16 | storew %s1, $a 17 | ret 18 | } 19 | 20 | # >>> driver 21 | # extern void test(void); 22 | # int a; 23 | # int main() { test(); return !(a == 5050); } 24 | # <<< 25 | -------------------------------------------------------------------------------- /test/mandel.ssa: -------------------------------------------------------------------------------- 1 | # Print the Mandelbrot set on the 2 | # terminal line output. 3 | 4 | function w $mandel(d %x, d %y) { 5 | @mandel 6 | %cr =d sub %y, d_0.5 7 | %ci =d copy %x 8 | @loop 9 | %i =w phi @mandel 0, @loop1 %i1 10 | %zr =d phi @mandel d_0, @loop1 %zr1 11 | %zi =d phi @mandel d_0, @loop1 %zi1 12 | %i1 =w add 1, %i 13 | %tmp =d mul %zr, %zi 14 | %zr2 =d mul %zr, %zr 15 | %zi2 =d mul %zi, %zi 16 | %zrx =d sub %zr2, %zi2 17 | %zr1 =d add %zrx, %cr 18 | %zix =d add %tmp, %tmp 19 | %zi1 =d add %zix, %ci 20 | %sum =d add %zi2, %zr2 21 | %cmp1 =w cgtd %sum, d_16 22 | jnz %cmp1, @reti, @loop1 23 | @loop1 24 | %cmp2 =w csgtw %i1, 1000 25 | jnz %cmp2, @ret0, @loop 26 | @reti 27 | ret %i1 28 | @ret0 29 | ret 0 30 | } 31 | 32 | export 33 | function w $main() { 34 | @main 35 | @loopy 36 | %y =d phi @main d_-1, @loopy1 %y1 37 | @loopx 38 | %x =d phi @loopy d_-1, @loopx1 %x1 39 | %i =w call $mandel(d %x, d %y) 40 | jnz %i, @out, @in 41 | @in 42 | %r0 =w call $putchar(w 42) # '*' 43 | jmp @loopx1 44 | @out 45 | %r1 =w call $putchar(w 32) # ' ' 46 | jmp @loopx1 47 | @loopx1 48 | %x1 =d add %x, d_0.032 49 | %cmp1 =w cgtd %x1, d_1 50 | jnz %cmp1, @loopy1, @loopx 51 | @loopy1 52 | %r2 =w call $putchar(w 10) # '\n' 53 | %y1 =d add %y, d_0.032 54 | %cmp2 =w cgtd %y1, d_1 55 | jnz %cmp2, @ret, @loopy 56 | @ret 57 | ret 0 58 | } 59 | 60 | # >>> output 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 | -------------------------------------------------------------------------------- /test/max.ssa: -------------------------------------------------------------------------------- 1 | # find the maximum value 2 | # in a nul-terminated array 3 | # of unsigned bytes 4 | # 5 | # the output is stored in $a 6 | 7 | data $arr = { b 10, b -60, b 10, b 100, b 200, b 0 } 8 | 9 | export 10 | function $test() { 11 | @start 12 | @loop 13 | %max =w phi @start -1, @new %byt, @old %max 14 | %loc =l phi @start $arr, @new %loc1, @old %loc1 15 | %byt =w loadub %loc 16 | %loc1 =l add 1, %loc 17 | jnz %byt, @iter, @end 18 | @iter 19 | %cmp =w cslew %max, %byt 20 | jnz %cmp, @new, @old 21 | @new 22 | jmp @loop 23 | @old 24 | jmp @loop 25 | @end 26 | storew %max, $a 27 | ret 28 | } 29 | 30 | # >>> driver 31 | # extern void test(void); 32 | # int a; 33 | # int main() { test(); return !(a == 200); } 34 | # <<< 35 | -------------------------------------------------------------------------------- /test/philv.ssa: -------------------------------------------------------------------------------- 1 | # regression test for 1f4ff634 2 | 3 | # warning! headaches can occur 4 | # when trying to figure out what 5 | # the test is doing! 6 | 7 | export 8 | function w $t0() { 9 | @start 10 | @loop 11 | %x0 =w phi @start 256, @loop %y0 12 | %y0 =w phi @start 128, @loop %y1 13 | %y1 =w shr %x0, 1 14 | jnz %y1, @loop, @end 15 | @end 16 | ret %x0 17 | } 18 | 19 | export 20 | function w $t1() { # swapped phis 21 | @start 22 | @loop 23 | %y0 =w phi @start 128, @loop %y1 24 | %x0 =w phi @start 256, @loop %y0 25 | %y1 =w shr %x0, 1 26 | jnz %y1, @loop, @end 27 | @end 28 | ret %x0 29 | } 30 | 31 | # >>> driver 32 | # extern int t0(void), t1(void); 33 | # int main() { return !(t0() == 1 && t1() == 1);} 34 | # <<< 35 | -------------------------------------------------------------------------------- /test/prime.ssa: -------------------------------------------------------------------------------- 1 | # find the 10,001st prime 2 | # store it in a 3 | 4 | export 5 | function $test() { 6 | @start 7 | @loop 8 | %n =w phi @start 5, @tloop %n, @yes %n1 9 | %p =w phi @start 13, @tloop %p1, @yes %p1 10 | %p1 =w add %p, 2 11 | @tloop 12 | %t =w phi @loop 3, @next %t1 13 | %r =w rem %p, %t 14 | jnz %r, @next, @loop 15 | @next 16 | %t1 =w add 2, %t 17 | %tsq =w mul %t1, %t1 18 | %c0 =w csgtw %tsq, %p 19 | jnz %c0, @yes, @tloop 20 | @yes 21 | %n1 =w add 1, %n 22 | %c1 =w ceqw 10001, %n1 23 | jnz %c1, @end, @loop 24 | @end 25 | storew %p, $a 26 | ret 27 | } 28 | 29 | # >>> driver 30 | # extern void test(void); 31 | # int a; 32 | # int main() { test(); return !(a == 104743); } 33 | # <<< 34 | -------------------------------------------------------------------------------- /test/puts10.ssa: -------------------------------------------------------------------------------- 1 | export 2 | function $main() { 3 | @start 4 | %y =l alloc4 4 5 | %y1 =l add %y, 1 6 | storeb 0, %y1 7 | @loop 8 | %n =w phi @start 0, @loop %n1 9 | %c =w add %n, 48 10 | storeb %c, %y 11 | %r =w call $puts(l %y) 12 | %n1 =w add %n, 1 13 | %cmp =w cslew %n1, 9 14 | jnz %cmp, @loop, @end 15 | @end 16 | ret 17 | } 18 | 19 | # >>> output 20 | # 0 21 | # 1 22 | # 2 23 | # 3 24 | # 4 25 | # 5 26 | # 6 27 | # 7 28 | # 8 29 | # 9 30 | # <<< 31 | -------------------------------------------------------------------------------- /test/queen.ssa: -------------------------------------------------------------------------------- 1 | # eight queens program 2 | # generated by minic 3 | 4 | export function w $chk(w %t0, w %t1) { 5 | @l0 6 | %x =l alloc4 4 7 | storew %t0, %x 8 | %y =l alloc4 4 9 | storew %t1, %y 10 | %i =l alloc4 4 11 | %r =l alloc4 4 12 | storew 0, %i 13 | storew 0, %r 14 | @l1 15 | %t6 =w loadw %i 16 | %t7 =w loadw $glo1 17 | %t5 =w csltw %t6, %t7 18 | jnz %t5, @l2, @l3 19 | @l2 20 | %t10 =w loadw %r 21 | %t15 =l loadl $glo3 22 | %t16 =w loadw %x 23 | %t17 =l extsw %t16 24 | %t18 =l mul 8, %t17 25 | %t14 =l add %t15, %t18 26 | %t13 =l loadl %t14 27 | %t19 =w loadw %i 28 | %t20 =l extsw %t19 29 | %t21 =l mul 4, %t20 30 | %t12 =l add %t13, %t21 31 | %t11 =w loadw %t12 32 | %t9 =w add %t10, %t11 33 | storew %t9, %r 34 | %t24 =w loadw %r 35 | %t29 =l loadl $glo3 36 | %t30 =w loadw %i 37 | %t31 =l extsw %t30 38 | %t32 =l mul 8, %t31 39 | %t28 =l add %t29, %t32 40 | %t27 =l loadl %t28 41 | %t33 =w loadw %y 42 | %t34 =l extsw %t33 43 | %t35 =l mul 4, %t34 44 | %t26 =l add %t27, %t35 45 | %t25 =w loadw %t26 46 | %t23 =w add %t24, %t25 47 | storew %t23, %r 48 | %t39 =w loadw %x 49 | %t40 =w loadw %i 50 | %t38 =w add %t39, %t40 51 | %t41 =w loadw $glo1 52 | %t37 =w csltw %t38, %t41 53 | %t44 =w loadw %y 54 | %t45 =w loadw %i 55 | %t43 =w add %t44, %t45 56 | %t46 =w loadw $glo1 57 | %t42 =w csltw %t43, %t46 58 | %t36 =w and %t37, %t42 59 | jnz %t36, @l4, @l5 60 | @l4 61 | %t49 =w loadw %r 62 | %t54 =l loadl $glo3 63 | %t56 =w loadw %x 64 | %t57 =w loadw %i 65 | %t55 =w add %t56, %t57 66 | %t58 =l extsw %t55 67 | %t59 =l mul 8, %t58 68 | %t53 =l add %t54, %t59 69 | %t52 =l loadl %t53 70 | %t61 =w loadw %y 71 | %t62 =w loadw %i 72 | %t60 =w add %t61, %t62 73 | %t63 =l extsw %t60 74 | %t64 =l mul 4, %t63 75 | %t51 =l add %t52, %t64 76 | %t50 =w loadw %t51 77 | %t48 =w add %t49, %t50 78 | storew %t48, %r 79 | @l5 80 | %t68 =w loadw %x 81 | %t69 =w loadw %i 82 | %t67 =w add %t68, %t69 83 | %t70 =w loadw $glo1 84 | %t66 =w csltw %t67, %t70 85 | %t74 =w loadw %y 86 | %t75 =w loadw %i 87 | %t73 =w sub %t74, %t75 88 | %t71 =w cslew 0, %t73 89 | %t65 =w and %t66, %t71 90 | jnz %t65, @l7, @l8 91 | @l7 92 | %t78 =w loadw %r 93 | %t83 =l loadl $glo3 94 | %t85 =w loadw %x 95 | %t86 =w loadw %i 96 | %t84 =w add %t85, %t86 97 | %t87 =l extsw %t84 98 | %t88 =l mul 8, %t87 99 | %t82 =l add %t83, %t88 100 | %t81 =l loadl %t82 101 | %t90 =w loadw %y 102 | %t91 =w loadw %i 103 | %t89 =w sub %t90, %t91 104 | %t92 =l extsw %t89 105 | %t93 =l mul 4, %t92 106 | %t80 =l add %t81, %t93 107 | %t79 =w loadw %t80 108 | %t77 =w add %t78, %t79 109 | storew %t77, %r 110 | @l8 111 | %t98 =w loadw %x 112 | %t99 =w loadw %i 113 | %t97 =w sub %t98, %t99 114 | %t95 =w cslew 0, %t97 115 | %t102 =w loadw %y 116 | %t103 =w loadw %i 117 | %t101 =w add %t102, %t103 118 | %t104 =w loadw $glo1 119 | %t100 =w csltw %t101, %t104 120 | %t94 =w and %t95, %t100 121 | jnz %t94, @l10, @l11 122 | @l10 123 | %t107 =w loadw %r 124 | %t112 =l loadl $glo3 125 | %t114 =w loadw %x 126 | %t115 =w loadw %i 127 | %t113 =w sub %t114, %t115 128 | %t116 =l extsw %t113 129 | %t117 =l mul 8, %t116 130 | %t111 =l add %t112, %t117 131 | %t110 =l loadl %t111 132 | %t119 =w loadw %y 133 | %t120 =w loadw %i 134 | %t118 =w add %t119, %t120 135 | %t121 =l extsw %t118 136 | %t122 =l mul 4, %t121 137 | %t109 =l add %t110, %t122 138 | %t108 =w loadw %t109 139 | %t106 =w add %t107, %t108 140 | storew %t106, %r 141 | @l11 142 | %t127 =w loadw %x 143 | %t128 =w loadw %i 144 | %t126 =w sub %t127, %t128 145 | %t124 =w cslew 0, %t126 146 | %t132 =w loadw %y 147 | %t133 =w loadw %i 148 | %t131 =w sub %t132, %t133 149 | %t129 =w cslew 0, %t131 150 | %t123 =w and %t124, %t129 151 | jnz %t123, @l13, @l14 152 | @l13 153 | %t136 =w loadw %r 154 | %t141 =l loadl $glo3 155 | %t143 =w loadw %x 156 | %t144 =w loadw %i 157 | %t142 =w sub %t143, %t144 158 | %t145 =l extsw %t142 159 | %t146 =l mul 8, %t145 160 | %t140 =l add %t141, %t146 161 | %t139 =l loadl %t140 162 | %t148 =w loadw %y 163 | %t149 =w loadw %i 164 | %t147 =w sub %t148, %t149 165 | %t150 =l extsw %t147 166 | %t151 =l mul 4, %t150 167 | %t138 =l add %t139, %t151 168 | %t137 =w loadw %t138 169 | %t135 =w add %t136, %t137 170 | storew %t135, %r 171 | @l14 172 | %t153 =w loadw %i 173 | %t152 =w add %t153, 1 174 | storew %t152, %i 175 | jmp @l1 176 | @l3 177 | %t154 =w loadw %r 178 | ret %t154 179 | } 180 | 181 | export function w $go(w %t0) { 182 | @l16 183 | %y =l alloc4 4 184 | storew %t0, %y 185 | %x =l alloc4 4 186 | %t2 =w loadw %y 187 | %t3 =w loadw $glo1 188 | %t1 =w ceqw %t2, %t3 189 | jnz %t1, @l17, @l18 190 | @l17 191 | %t5 =w loadw $glo2 192 | %t4 =w add %t5, 1 193 | storew %t4, $glo2 194 | ret 0 195 | @l18 196 | storew 0, %x 197 | @l20 198 | %t10 =w loadw %x 199 | %t11 =w loadw $glo1 200 | %t9 =w csltw %t10, %t11 201 | jnz %t9, @l21, @l22 202 | @l21 203 | %t14 =w loadw %x 204 | %t15 =w loadw %y 205 | %t13 =w call $chk(w %t14, w %t15) 206 | %t12 =w ceqw %t13, 0 207 | jnz %t12, @l23, @l24 208 | @l23 209 | %t21 =l loadl $glo3 210 | %t22 =w loadw %x 211 | %t23 =l extsw %t22 212 | %t24 =l mul 8, %t23 213 | %t20 =l add %t21, %t24 214 | %t19 =l loadl %t20 215 | %t25 =w loadw %y 216 | %t26 =l extsw %t25 217 | %t27 =l mul 4, %t26 218 | %t18 =l add %t19, %t27 219 | %t28 =w loadw %t18 220 | %t17 =w add %t28, 1 221 | storew %t17, %t18 222 | %t31 =w loadw %y 223 | %t30 =w add %t31, 1 224 | %t29 =w call $go(w %t30) 225 | %t37 =l loadl $glo3 226 | %t38 =w loadw %x 227 | %t39 =l extsw %t38 228 | %t40 =l mul 8, %t39 229 | %t36 =l add %t37, %t40 230 | %t35 =l loadl %t36 231 | %t41 =w loadw %y 232 | %t42 =l extsw %t41 233 | %t43 =l mul 4, %t42 234 | %t34 =l add %t35, %t43 235 | %t44 =w loadw %t34 236 | %t33 =w sub %t44, 1 237 | storew %t33, %t34 238 | @l24 239 | %t46 =w loadw %x 240 | %t45 =w add %t46, 1 241 | storew %t45, %x 242 | jmp @l20 243 | @l22 244 | ret 0 245 | } 246 | 247 | export function w $main() { 248 | @l26 249 | %i =l alloc4 4 250 | storew 8, $glo1 251 | %t4 =w loadw $glo1 252 | %t3 =l call $calloc(w %t4, w 8) 253 | storel %t3, $glo3 254 | storew 0, %i 255 | @l27 256 | %t9 =w loadw %i 257 | %t10 =w loadw $glo1 258 | %t8 =w csltw %t9, %t10 259 | jnz %t8, @l28, @l29 260 | @l28 261 | %t13 =w loadw $glo1 262 | %t12 =l call $calloc(w %t13, w 4) 263 | %t16 =l loadl $glo3 264 | %t17 =w loadw %i 265 | %t18 =l extsw %t17 266 | %t19 =l mul 8, %t18 267 | %t15 =l add %t16, %t19 268 | storel %t12, %t15 269 | %t21 =w loadw %i 270 | %t20 =w add %t21, 1 271 | storew %t20, %i 272 | jmp @l27 273 | @l29 274 | %t22 =w call $go(w 0) 275 | %t25 =w loadw $glo2 276 | %t24 =w cnew %t25, 92 277 | ret %t24 278 | } 279 | 280 | data $glo1 = { w 0 } 281 | data $glo2 = { w 0 } 282 | data $glo3 = { l 0 } 283 | -------------------------------------------------------------------------------- /test/strcmp.ssa: -------------------------------------------------------------------------------- 1 | # the C strcmp function generated by scc 2 | 3 | export function w $strcmp(l %s1.3.val,l %s2.5.val) 4 | { 5 | @.37 6 | %s1.3 =l alloc8 8 7 | %s2.5 =l alloc8 8 8 | storel %s1.3.val,%s1.3 9 | storel %s2.5.val,%s2.5 10 | jmp @.5 11 | @.6 12 | %.9 =l loadl %s1.3 13 | %.10 =l add %.9,1 14 | storel %.10,%s1.3 15 | %.11 =l loadl %s2.5 16 | %.12 =l add %.11,1 17 | storel %.12,%s2.5 18 | @.5 19 | %.15 =l loadl %s1.3 20 | %.16 =w loadsb %.15 21 | %.17 =w extsb %.16 22 | %.18 =w cnew %.17,0 23 | jnz %.18,@.14,@.8 24 | @.14 25 | %.19 =l loadl %s2.5 26 | %.20 =w loadsb %.19 27 | %.21 =w extsb %.20 28 | %.22 =w cnew %.21,0 29 | jnz %.22,@.13,@.8 30 | @.13 31 | %.23 =l loadl %s1.3 32 | %.24 =w loadsb %.23 33 | %.25 =w extsb %.24 34 | %.26 =l loadl %s2.5 35 | %.27 =w loadsb %.26 36 | %.28 =w extsb %.27 37 | %.29 =w ceqw %.25,%.28 38 | jnz %.29,@.6,@.8 39 | @.8 40 | @.7 41 | %.30 =l loadl %s1.3 42 | %.31 =w loadub %.30 43 | %.32 =w extub %.31 44 | %.33 =l loadl %s2.5 45 | %.34 =w loadub %.33 46 | %.35 =w extub %.34 47 | %.36 =w sub %.32,%.35 48 | ret %.36 49 | } 50 | 51 | # >>> driver 52 | # extern int strcmp(const char *, const char *); 53 | # int main() { 54 | # char a[] = "Hello world"; 55 | # return !( 56 | # strcmp(a, a) == 0 && 57 | # strcmp("aaa", "aab") < 0 && 58 | # strcmp("..cnn", "..bbc") > 0 && 59 | # strcmp(a, "Hellp ...") < 0 && 60 | # strcmp(a, "Hello vorld") > 0 61 | # ); 62 | # } 63 | # <<< 64 | -------------------------------------------------------------------------------- /test/strspn.ssa: -------------------------------------------------------------------------------- 1 | # the C strspn function generated by scc 2 | 3 | export function w $strspn_(l %s1.81.val,l %s2.82.val) 4 | { 5 | @.64 6 | %s1.81 =l alloc8 8 7 | %s2.82 =l alloc8 8 8 | %n.83 =l alloc4 4 9 | %c.84 =l alloc4 4 10 | %p.85 =l alloc8 8 11 | storel %s1.81.val,%s1.81 12 | storel %s2.82.val,%s2.82 13 | storew 0,%n.83 14 | jmp @.27 15 | @.28 16 | %.39 =l loadl %s2.82 17 | storel %.39,%p.85 18 | jmp @.29 19 | @.30 20 | @.31 21 | %.40 =l loadl %p.85 22 | %.41 =l add %.40,1 23 | storel %.41,%p.85 24 | @.29 25 | %.43 =l loadl %p.85 26 | %.44 =w loadsb %.43 27 | %.45 =w extsb %.44 28 | %.46 =w cnew %.45,0 29 | jnz %.46,@.42,@.36 30 | @.42 31 | %.47 =l loadl %p.85 32 | %.48 =w loadsb %.47 33 | %.49 =w extsb %.48 34 | %.50 =w loadsw %c.84 35 | %.51 =w cnew %.49,%.50 36 | jnz %.51,@.30,@.36 37 | @.36 38 | @.32 39 | %.52 =l loadl %p.85 40 | %.53 =w loadsb %.52 41 | %.54 =w extsb %.53 42 | %.55 =w cnew %.54,0 43 | jnz %.55,@.33,@.37 44 | @.37 45 | jmp @.34 46 | @.33 47 | @.35 48 | %.56 =w loaduw %n.83 49 | %.57 =w add %.56,1 50 | storew %.57,%n.83 51 | @.27 52 | %.58 =l loadl %s1.81 53 | %.59 =l add %.58,1 54 | storel %.59,%s1.81 55 | %.60 =w loadsb %.58 56 | %.61 =w extsb %.60 57 | storew %.61,%c.84 58 | %.62 =w cnew %.61,0 59 | jnz %.62,@.28,@.38 60 | @.38 61 | @.34 62 | %.63 =w loaduw %n.83 63 | ret %.63 64 | } 65 | 66 | # >>> driver 67 | # extern unsigned strspn_(const char *, const char *); 68 | # int main() { 69 | # return !( 70 | # strspn_("", "abc") == 0 && 71 | # strspn_("abc", "") == 0 && 72 | # strspn_("abc", "bac") == 3 && 73 | # strspn_("xabc", "bac") == 0 && 74 | # strspn_("axbc", "bca") == 1 75 | # ); 76 | # } 77 | # <<< 78 | -------------------------------------------------------------------------------- /test/sum.ssa: -------------------------------------------------------------------------------- 1 | # Simple test for addressing modes. 2 | 3 | export 4 | function w $sum(l %arr, w %num) { 5 | @start 6 | @loop 7 | %n1 =w phi @start %num, @loop1 %n2 8 | %s0 =w phi @start 0, @loop1 %s1 9 | %n2 =w sub %n1, 1 10 | %c =w cslew %n1, 0 11 | jnz %c, @end, @loop1 12 | @loop1 13 | %idx0 =l extsw %n2 14 | %idx1 =l mul 4, %idx0 15 | %idx2 =l add %idx1, %arr 16 | %w =w loadw %idx2 17 | %s1 =w add %w, %s0 18 | jmp @loop 19 | @end 20 | ret %s0 21 | } 22 | 23 | # >>> driver 24 | # extern int sum(int *, int); 25 | # int arr[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21 }; 26 | # #define N sizeof arr / sizeof arr[0] 27 | # int main() { 28 | # int i, s; 29 | # for (s=i=0; i>> driver 21 | # extern double f(int, ...); 22 | # extern int g(char *, ...); 23 | # int main() { 24 | # g("Hell%c %s %g!\n", 'o', "world", f(42, "x", 42.0)); 25 | # } 26 | # <<< 27 | 28 | # >>> output 29 | # Hello world 42! 30 | # <<< 31 | -------------------------------------------------------------------------------- /tools/abifuzz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OCAMLC=${OCAMLC:-/usr/bin/ocamlc} 4 | DIR=`cd $(dirname "$0"); pwd` 5 | QBE=$DIR/../obj/qbe 6 | 7 | failure() { 8 | echo "Failure at stage:" $1 >&2 9 | exit 1 10 | } 11 | 12 | cleanup() { 13 | rm -fr $TMP 14 | } 15 | 16 | init() { 17 | cp $DIR/callgen.ml $TMP 18 | pushd $TMP > /dev/null 19 | 20 | cat > Makefile << EOM 21 | 22 | .PHONY: test 23 | test: caller.o callee.o 24 | c99 -o \$@ caller.o callee.o 25 | %.o: %.c 26 | c99 -c -o \$@ \$< 27 | %.o: %.ssa 28 | $QBE -o \$*.s \$< 29 | c99 -c -o \$@ \$*.s 30 | 31 | EOM 32 | 33 | if ! $OCAMLC callgen.ml -o callgen 34 | then 35 | popd > /dev/null 36 | cleanup 37 | failure "abifuzz compilation" 38 | fi 39 | popd > /dev/null 40 | } 41 | 42 | once() { 43 | if test -z "$3" 44 | then 45 | $TMP/callgen $TMP $1 $2 46 | else 47 | $TMP/callgen -s $3 $TMP $1 $2 48 | fi 49 | make -C $TMP test > /dev/null || failure "building" 50 | $TMP/test || failure "runtime" 51 | } 52 | 53 | usage() { 54 | echo "usage: abitest.sh [-callssa] [-callc] [-s SEED] [-n ITERATIONS]" >&2 55 | exit 1 56 | } 57 | 58 | N=1 59 | CALLER=c 60 | CALLEE=ssa 61 | 62 | while test -n "$1" 63 | do 64 | case "$1" in 65 | "-callssa") 66 | CALLER=c 67 | CALLEE=ssa 68 | ;; 69 | "-callc") 70 | CALLER=ssa 71 | CALLEE=c 72 | ;; 73 | "-s") 74 | test -n "$2" || usage 75 | shift 76 | SEED="$1" 77 | ;; 78 | "-n") 79 | test -n "$2" || usage 80 | shift 81 | N="$1" 82 | ;; 83 | *) 84 | usage 85 | ;; 86 | esac 87 | shift 88 | done 89 | 90 | TMP=`mktemp -d abifuzz.XXXXXX` 91 | 92 | init 93 | 94 | if test -n "$S" 95 | then 96 | once $CALLER $CALLEE $SEED 97 | else 98 | for n in `seq $N` 99 | do 100 | once $CALLER $CALLEE 101 | echo "$n" | grep "00$" 102 | done 103 | fi 104 | 105 | echo "All done." 106 | 107 | cleanup 108 | -------------------------------------------------------------------------------- /tools/cra.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=`cd $(dirname "$0"); pwd` 4 | QBE=$DIR/../obj/qbe 5 | BUGF=/tmp/bug.id 6 | FIND=$1 7 | FIND=${FIND:-afl-find} 8 | 9 | if ! test -f $BUGF 10 | then 11 | echo 1 > $BUGF 12 | fi 13 | 14 | while true 15 | do 16 | ID=`cat $BUGF` 17 | 18 | if test `ls $FIND/crashes/id* | wc -l` -lt $ID 19 | then 20 | rm -f bug.ssa 21 | echo "All done!" 22 | exit 0 23 | fi 24 | 25 | BUG=`ls $FIND/crashes/id* | sed -ne "${ID}{p;q}"` 26 | 27 | echo "*** Crash $ID" 28 | cp $BUG bug.ssa 29 | 30 | $QBE bug.ssa > /dev/null 31 | RET=$? 32 | if test \( $RET -ne 0 \) -a \( $RET -ne 1 \) 33 | then 34 | exit 1 35 | fi 36 | 37 | expr $ID + 1 > $BUGF 38 | done 39 | -------------------------------------------------------------------------------- /tools/lexh.c: -------------------------------------------------------------------------------- 1 | /*% c99 -O3 -Wall -o # % 2 | */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | char *tok[] = { 10 | 11 | "add", "sub", "div", "rem", "udiv", "urem", "mul", 12 | "and", "or", "xor", "sar", "shr", "shl", "stored", 13 | "stores", "storel", "storew", "storeh", "storeb", 14 | "load", "loadsw", "loaduw", "loadsh", "loaduh", 15 | "loadsb", "loadub", "extsw", "extuw", "extsh", 16 | "extuh", "extsb", "extub", "exts", "truncd", 17 | "stosi", "dtosi", "swtof", "sltof", "cast", "copy", 18 | "alloc4", "alloc8", "alloc16", "culew", "cultw", 19 | "cslew", "csltw", "csgtw", "csgew", "cugtw", 20 | "cugew", "ceqw", "cnew", "culel", "cultl", "cslel", 21 | "csltl", "csgtl", "csgel", "cugtl", "cugel", 22 | "ceql", "cnel", "cles", "clts", "cgts", "cges", 23 | "cnes", "ceqs", "cos", "cuos", "cled", "cltd", 24 | "cgtd", "cged", "cned", "ceqd", "cod", "cuod", 25 | "vaarg", "vastart", "...", "env", 26 | 27 | "call", "phi", "jmp", "jnz", "ret", "export", 28 | "function", "type", "data", "align", "l", "w", 29 | "h", "b", "d", "s", "z", "loadw", "loadl", "loads", 30 | "loadd", "alloc1", "alloc2", 31 | 32 | }; 33 | enum { 34 | Ntok = sizeof tok / sizeof tok[0] 35 | }; 36 | 37 | uint32_t th[Ntok]; 38 | 39 | uint32_t 40 | hash(char *s) 41 | { 42 | uint32_t h; 43 | 44 | h = 0; 45 | for (; *s; ++s) 46 | h = *s + 17*h; 47 | return h; 48 | } 49 | 50 | int 51 | main() 52 | { 53 | char *bmap; 54 | uint32_t h, M, K; 55 | int i, j; 56 | 57 | bmap = malloc(1u << 31); 58 | 59 | for (i=0; i> M; 81 | if (bmap[h]) 82 | break; 83 | bmap[h] = 1; 84 | } 85 | if (i==Ntok) { 86 | printf("found K=%d for M=%d\n", K, M); 87 | exit(0); 88 | } 89 | K += 2; 90 | } while (K != 1); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tools/pmov.c: -------------------------------------------------------------------------------- 1 | /*% rm -f rega.o main.o && cc -g -std=c99 -Wall -DTEST_PMOV -o pmov % *.o 2 | * 3 | * This is a test framwork for the dopm() function 4 | * in rega.c, use it when you want to modify it or 5 | * all the parallel move functions. 6 | * 7 | * You might need to decrease NIReg to see it 8 | * terminate, I used NIReg == 7 at most. 9 | */ 10 | #include 11 | #include 12 | #include 13 | 14 | static void assert_test(char *, int), fail(void), iexec(int *); 15 | 16 | #include "../../rega.c" 17 | 18 | static void bsinit_(BSet *, uint); 19 | 20 | static RMap mbeg; 21 | static Ins ins[NIReg], *ip; 22 | static Blk dummyb = { .ins = ins }; 23 | 24 | int 25 | main() 26 | { 27 | Ins *i1; 28 | unsigned long long tm, rm, cnt; 29 | RMap mend; 30 | int reg[NIReg], val[NIReg+1]; 31 | int t, i, r, nr; 32 | 33 | tmp = (Tmp[Tmp0+NIReg]){{{0}}}; 34 | for (t=0; t= Tmp0) { 36 | tmp[t].cls = Kw; 37 | tmp[t].hint.r = -1; 38 | tmp[t].hint.m = 0; 39 | tmp[t].slot = -1; 40 | sprintf(tmp[t].name, "tmp%d", t-Tmp0+1); 41 | } 42 | 43 | bsinit_(mbeg.b, Tmp0+NIReg); 44 | bsinit_(mend.b, Tmp0+NIReg); 45 | cnt = 0; 46 | for (tm = 0; tm < 1ull << (2*NIReg); tm++) { 47 | mbeg.n = 0; 48 | bszero(mbeg.b); 49 | ip = ins; 50 | 51 | /* find what temporaries are in copy and 52 | * wether or not they are in register 53 | */ 54 | for (t=0; t> (2*t)) & 3) { 56 | case 0: 57 | /* not in copy, not in reg */ 58 | break; 59 | case 1: 60 | /* not in copy, in reg */ 61 | radd(&mbeg, Tmp0+t, t+1); 62 | break; 63 | case 2: 64 | /* in copy, not in reg */ 65 | *ip++ = (Ins){OCopy, TMP(Tmp0+t), {R, R}, Kw}; 66 | break; 67 | case 3: 68 | /* in copy, in reg */ 69 | *ip++ = (Ins){OCopy, TMP(Tmp0+t), {R, R}, Kw}; 70 | radd(&mbeg, Tmp0+t, t+1); 71 | break; 72 | } 73 | 74 | if (ip == ins) 75 | /* cancel if the parallel move 76 | * is empty 77 | */ 78 | goto Nxt; 79 | 80 | /* find registers for temporaries 81 | * in mbeg 82 | */ 83 | nr = ip - ins; 84 | rm = (1ull << (nr+1)) - 1; 85 | for (i=0; iarg[0] = TMP(reg[i]); 93 | 94 | /* compile the parallel move 95 | */ 96 | rcopy(&mend, &mbeg); 97 | dopm(&dummyb, ip-1, &mend); 98 | cnt++; 99 | 100 | /* check that mend contain mappings for 101 | * source registers and does not map any 102 | * assigned temporary, then check that 103 | * all temporaries in mend are mapped in 104 | * mbeg and not used in the copy 105 | */ 106 | for (i1=ins; i1arg[0].val; 108 | assert(rfree(&mend, r) == r); 109 | t = i1->to.val; 110 | assert(!bshas(mend.b, t)); 111 | } 112 | for (i=0; i> (2*t)) & 3) == 1); 117 | } 118 | 119 | /* execute the code generated and check 120 | * that all assigned temporaries got their 121 | * value, and that all live variables's 122 | * content got preserved 123 | */ 124 | for (i=1; i<=NIReg; i++) 125 | val[i] = i; 126 | iexec(val); 127 | for (i1=ins; i1to.val; 129 | r = rfind(&mbeg, t); 130 | if (r != -1) 131 | assert(val[r] == i1->arg[0].val); 132 | } 133 | for (i=0; i 0 && \ 177 | r.val <= NIReg 178 | 179 | static void 180 | iexec(int val[]) 181 | { 182 | Ins *i; 183 | int t; 184 | 185 | for (i=insb; iop) { 187 | default: 188 | assert(!"iexec: missing case\n"); 189 | exit(1); 190 | case OSwap: 191 | assert(validr(i->arg[0])); 192 | assert(validr(i->arg[1])); 193 | t = val[i->arg[0].val]; 194 | val[i->arg[0].val] = val[i->arg[1].val]; 195 | val[i->arg[1].val] = t; 196 | break; 197 | case OCopy: 198 | assert(validr(i->to)); 199 | assert(validr(i->arg[0])); 200 | val[i->to.val] = val[i->arg[0].val]; 201 | break; 202 | } 203 | } 204 | 205 | 206 | /* failure diagnostics */ 207 | 208 | static int re; 209 | 210 | static void 211 | replay() 212 | { 213 | RMap mend; 214 | 215 | re = 1; 216 | bsinit_(mend.b, Tmp0+NIReg); 217 | rcopy(&mend, &mbeg); 218 | dopm(&dummyb, ip-1, &mend); 219 | } 220 | 221 | static void 222 | fail() 223 | { 224 | Ins *i1; 225 | int i; 226 | 227 | printf("\nIn registers: "); 228 | for (i=0; ito.val].name, 237 | i1->arg[0].val); 238 | replay(); 239 | abort(); 240 | } 241 | 242 | static void 243 | assert_test(char *s, int x) 244 | { 245 | if (x) 246 | return; 247 | if (re) 248 | abort(); 249 | printf("!assertion failure: %s\n", s); 250 | fail(); 251 | } 252 | 253 | static void 254 | bsinit_(BSet *bs, uint n) 255 | { 256 | n = (n + NBit-1) / NBit; 257 | bs->nt = n; 258 | bs->t = emalloc(n * sizeof bs->t[0]); 259 | } 260 | 261 | /* symbols required by the linker */ 262 | char debug['Z'+1]; 263 | -------------------------------------------------------------------------------- /tools/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dir=`cd $(dirname "$0"); pwd` 4 | bin=$dir/../obj/qbe 5 | 6 | tmp=/tmp/qbe.zzzz 7 | 8 | drv=$tmp.c 9 | asm=$tmp.s 10 | exe=$tmp.exe 11 | out=$tmp.out 12 | 13 | init() { 14 | case "$TARGET" in 15 | arm64) 16 | for p in aarch64-linux-musl aarch64-linux-gnu 17 | do 18 | cc=$p-gcc 19 | qemu="qemu-aarch64 -L /usr/$p" 20 | if 21 | $cc -v >/dev/null 2>&1 && 22 | $qemu -version >/dev/null 2>&1 && 23 | test -d /usr/$p 24 | then 25 | break 26 | fi 27 | cc= 28 | done 29 | if test -z "$cc" 30 | then 31 | echo "Cannot find arm64 compiler or qemu." 32 | exit 1 33 | fi 34 | bin="$bin -t arm64" 35 | ;; 36 | "") 37 | case `uname` in 38 | *Darwin*) 39 | cc="cc -Wl,-no_pie" 40 | ;; 41 | *OpenBSD*) 42 | cc="cc -nopie" 43 | ;; 44 | *FreeBSD*) 45 | cc="cc" 46 | ;; 47 | *) 48 | cc="cc -no-pie" 49 | ;; 50 | esac 51 | ;; 52 | *) 53 | echo "Unknown target '$TARGET'." 54 | exit 1 55 | ;; 56 | esac 57 | } 58 | 59 | cleanup() { 60 | rm -f $drv $asm $exe $out 61 | } 62 | 63 | extract() { 64 | WHAT="$1" 65 | FILE="$2" 66 | 67 | awk " 68 | /^# >>> $WHAT/ { 69 | p = 1 70 | next 71 | } 72 | /^# <<&2 87 | exit 1 88 | fi 89 | 90 | if 91 | sed -e 1q $t | 92 | grep "skip.* $TARGET\( .*\)\?$" \ 93 | >/dev/null 94 | then 95 | return 0 96 | fi 97 | 98 | printf "%-45s" "$(basename $t)..." 99 | 100 | if ! $bin -o $asm $t 101 | then 102 | echo "[qbe fail]" 103 | return 1 104 | fi 105 | 106 | extract driver $t > $drv 107 | extract output $t > $out 108 | 109 | if test -s $drv 110 | then 111 | src="$drv $asm" 112 | else 113 | src="$asm" 114 | fi 115 | 116 | if ! $cc -g -o $exe $src 117 | then 118 | echo "[cc fail]" 119 | return 1 120 | fi 121 | 122 | if test -s $out 123 | then 124 | $qemu $exe a b c | diff - $out 125 | ret=$? 126 | reason="output" 127 | else 128 | $qemu $exe a b c 129 | ret=$? 130 | reason="returned $RET" 131 | fi 132 | 133 | if test $ret -ne 0 134 | then 135 | echo "[$reason fail]" 136 | return 1 137 | fi 138 | 139 | echo "[ok]" 140 | } 141 | 142 | #trap cleanup TERM QUIT 143 | 144 | init 145 | 146 | if test -z "$1" 147 | then 148 | echo "usage: tools/test.sh {all, SSAFILE}" 2>&1 149 | exit 1 150 | fi 151 | 152 | case "$1" in 153 | "all") 154 | fail=0 155 | for t in $dir/../test/[!_]*.ssa 156 | do 157 | once $t 158 | fail=`expr $fail + $?` 159 | done 160 | if test $fail -ge 1 161 | then 162 | echo 163 | echo "$fail test(s) failed!" 164 | else 165 | echo 166 | echo "All is fine!" 167 | fi 168 | exit $fail 169 | ;; 170 | *) 171 | once $1 172 | exit $? 173 | ;; 174 | esac 175 | -------------------------------------------------------------------------------- /tools/unit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=`cd $(dirname "$0"); pwd` 4 | QBE=$DIR/../obj/qbe 5 | 6 | TMP=/tmp/qbe.zzzz 7 | 8 | DRV=$TMP.c 9 | ASM=$TMP.s 10 | BIN=$TMP.bin 11 | OUT=$TMP.out 12 | 13 | cleanup() { 14 | rm -f $DRV $ASM $BIN $OUT 15 | } 16 | 17 | extract() { 18 | WHAT="$1" 19 | FILE="$2" 20 | 21 | awk " 22 | /^# >>> $WHAT/ { 23 | p = 1 24 | next 25 | } 26 | /^# <<&2 41 | exit 1 42 | fi 43 | 44 | printf "%-45s" "$(basename $T)..." 45 | 46 | if ! $QBE -o $ASM $T 47 | then 48 | echo "[qbe fail]" 49 | return 1 50 | fi 51 | 52 | extract driver $T > $DRV 53 | extract output $T > $OUT 54 | 55 | if test -s $DRV 56 | then 57 | LNK="$DRV $ASM" 58 | else 59 | LNK="$ASM" 60 | fi 61 | 62 | if ! cc $PIE -g -o $BIN $LNK 63 | then 64 | echo "[cc fail]" 65 | return 1 66 | fi 67 | 68 | if test -s $OUT 69 | then 70 | $BIN a b c | diff - $OUT 71 | RET=$? 72 | REASON="output" 73 | else 74 | $BIN a b c 75 | RET=$? 76 | REASON="returned $RET" 77 | fi 78 | 79 | if test $RET -ne 0 80 | then 81 | echo "[$REASON fail]" 82 | return 1 83 | fi 84 | 85 | echo "[ok]" 86 | } 87 | 88 | 89 | #trap cleanup TERM QUIT 90 | 91 | if test -z "$1" 92 | then 93 | echo "usage: tools/unit.sh {all, SSAFILE}" 2>&1 94 | exit 1 95 | fi 96 | 97 | for wtf in -nopie -no-pie 98 | do 99 | if echo "int main() { return 0; }" | 100 | cc $wtf -x c -o /dev/null - >/dev/null 2>&1 101 | then 102 | PIE=$wtf 103 | fi 104 | done 105 | 106 | case $1 in 107 | "all") 108 | F=0 109 | for T in $DIR/../test/[!_]*.ssa 110 | do 111 | once $T 112 | F=`expr $F + $?` 113 | done 114 | if test $F -ge 1 115 | then 116 | echo 117 | echo "$F test(s) failed!" 118 | else 119 | echo 120 | echo "All is fine!" 121 | fi 122 | exit $F 123 | ;; 124 | *) 125 | once $1 126 | exit $? 127 | ;; 128 | esac 129 | -------------------------------------------------------------------------------- /tools/vatest.py: -------------------------------------------------------------------------------- 1 | # generate variadic calls to test the 2 | # abi implementation 3 | 4 | from random import seed, randint, uniform 5 | from struct import unpack 6 | 7 | I, D = 'd', 'g' 8 | 9 | formats = [ 10 | # list of format to tests 11 | [I], 12 | [D], 13 | [I,D], 14 | [D,D], 15 | [I,I,I,I], 16 | [D,D,D,D], 17 | [I,D,I,D], 18 | [D,D,I,I], 19 | [I,I,D,D], 20 | [], 21 | ] 22 | 23 | generate = [ 24 | # numbers of fixed integer and 25 | # floating point arguments to 26 | # test 27 | (0, 0), (1, 0), (0, 1), (4, 0), 28 | (0, 6), (5, 7), (10, 10), 29 | ] 30 | 31 | def mkargs(nargs, type, name): 32 | args = map( 33 | lambda n: ''.join([type, name, str(n), ', ']), 34 | range(nargs) 35 | ) 36 | return ''.join(args) 37 | 38 | def mkfstr(fmt): 39 | fstr = map( 40 | lambda x: {I: '%d ', D: '%g '}[x], 41 | fmt 42 | ) 43 | return '"' + ''.join(fstr) + '\\n"' 44 | 45 | def randargs(fmt): 46 | ra = { 47 | I: lambda: '{}'.format(randint(-10, 10)), 48 | D: lambda: '{0:.4g}'.format(uniform(-10, 10)) 49 | } 50 | return list(map(lambda x: ra[x](), fmt)) 51 | 52 | def genssa(qbeprint, qbecall): 53 | funcs = [('qbeprint', qbeprint), ('qbecall', qbecall)] 54 | for fnum, (nia, nfa) in enumerate(generate): 55 | params = "{}{}l %fmt, ...".format( 56 | mkargs(nia, 'w ', '%argw'), 57 | mkargs(nfa, 'd ', '%argd') 58 | ) 59 | for name, code in funcs: 60 | print('export function ${}{}({}) {}' 61 | .format(name, fnum, params, code) 62 | ) 63 | 64 | def gendriver(): 65 | print('# >>> driver') 66 | print('# #include ') 67 | 68 | for fnum, (nia, nfa) in enumerate(generate): 69 | params = "{}{}char *, ...".format( 70 | mkargs(nia, 'int ', 'argw'), 71 | mkargs(nfa, 'double ', 'argd') 72 | ) 73 | for name in ['qbeprint', 'qbecall']: 74 | print('# extern void {}{}({});' 75 | .format(name, fnum, params) 76 | ) 77 | 78 | output = '' 79 | print('# int main() {') 80 | 81 | for fnum, (nia, nfa) in enumerate(generate): 82 | info = '# ({} int, {} double)'.format(nia, nfa) 83 | print('# puts("{}");'.format(info)) 84 | output += '# {}\n'.format(info) 85 | for fmt in formats: 86 | ra = randargs(fmt) 87 | vaargs = ', '.join(ra) 88 | expect = ' '.join(ra) 89 | if fmt: 90 | vaargs = ', ' + vaargs 91 | expect = expect + ' ' 92 | args = ''.join( 93 | ['0, '] * (nia+nfa) + 94 | [mkfstr(fmt), vaargs] 95 | ) 96 | for name in ['qbeprint', 'qbecall']: 97 | print('# {}{}({});' 98 | .format(name, fnum, args) 99 | ) 100 | output += '# {}\n'.format(expect) 101 | 102 | print('# }') 103 | print('# <<<') 104 | 105 | print('\n# >>> output\n' + output + '# <<<') 106 | 107 | 108 | qbeprint="""{{ 109 | @start 110 | %fmtdbl =l alloc4 4 111 | %fmtint =l alloc4 4 112 | %emptys =l alloc4 4 113 | storew {}, %fmtint 114 | storew {}, %fmtdbl 115 | storew 0, %emptys 116 | %vp =l alloc8 24 117 | %fmt1 =l add 1, %fmt 118 | vastart %vp 119 | @loop 120 | %p =l phi @start %fmt1, @casef %p1, @cased %p1 121 | %c =w loadsb %p 122 | %p1 =l add 3, %p 123 | jnz %c, @loop1, @end 124 | @loop1 125 | %isg =w ceqw %c, 103 126 | jnz %isg, @casef, @cased 127 | @casef 128 | %dbl =d vaarg %vp 129 | call $printf(l %fmtdbl, d %dbl, ...) 130 | jmp @loop 131 | @cased 132 | %int =w vaarg %vp 133 | call $printf(l %fmtint, w %int, ...) 134 | jmp @loop 135 | @end 136 | call $puts(l %emptys) 137 | ret 138 | }} 139 | """.format( 140 | unpack("i", b'%d \x00')[0], 141 | unpack("i", b'%g \x00')[0] 142 | ) 143 | 144 | qbecall="""{ 145 | @start 146 | %vp =l alloc8 24 147 | vastart %vp 148 | call $vprintf(l %fmt, l %vp) 149 | ret 150 | } 151 | """ 152 | 153 | 154 | if __name__ == "__main__": 155 | seed(42) 156 | genssa(qbeprint, qbecall) 157 | gendriver() 158 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | #include 3 | 4 | typedef struct Bitset Bitset; 5 | typedef struct Vec Vec; 6 | typedef struct Bucket Bucket; 7 | 8 | struct Vec { 9 | ulong mag; 10 | Pool pool; 11 | size_t esz; 12 | ulong cap; 13 | union { 14 | long long ll; 15 | long double ld; 16 | void *ptr; 17 | } align[]; 18 | }; 19 | 20 | struct Bucket { 21 | uint nstr; 22 | char **str; 23 | }; 24 | 25 | enum { 26 | VMin = 2, 27 | VMag = 0xcabba9e, 28 | NPtr = 256, 29 | IBits = 12, 30 | IMask = (1<= NPtr) { 84 | pp = emalloc(NPtr * sizeof(void *)); 85 | pp[0] = pool; 86 | pool = pp; 87 | nptr = 1; 88 | } 89 | return pool[nptr++] = emalloc(n); 90 | } 91 | 92 | void 93 | freeall() 94 | { 95 | void **pp; 96 | 97 | for (;;) { 98 | for (pp = &pool[1]; pp < &pool[nptr]; pp++) 99 | free(*pp); 100 | pp = pool[0]; 101 | if (!pp) 102 | break; 103 | free(pool); 104 | pool = pp; 105 | nptr = NPtr; 106 | } 107 | nptr = 1; 108 | } 109 | 110 | void * 111 | vnew(ulong len, size_t esz, Pool pool) 112 | { 113 | void *(*f)(size_t); 114 | ulong cap; 115 | Vec *v; 116 | 117 | for (cap=VMin; capmag = VMag; 122 | v->cap = cap; 123 | v->esz = esz; 124 | v->pool = pool; 125 | return v + 1; 126 | } 127 | 128 | void 129 | vfree(void *p) 130 | { 131 | Vec *v; 132 | 133 | v = (Vec *)p - 1; 134 | assert(v->mag == VMag); 135 | if (v->pool == Pheap) { 136 | v->mag = 0; 137 | free(v); 138 | } 139 | } 140 | 141 | void 142 | vgrow(void *vp, ulong len) 143 | { 144 | Vec *v; 145 | void *v1; 146 | 147 | v = *(Vec **)vp - 1; 148 | assert(v+1 && v->mag == VMag); 149 | if (v->cap >= len) 150 | return; 151 | v1 = vnew(len, v->esz, v->pool); 152 | memcpy(v1, v+1, v->cap * v->esz); 153 | vfree(v+1); 154 | *(Vec **)vp = v1; 155 | } 156 | 157 | uint32_t 158 | intern(char *s) 159 | { 160 | Bucket *b; 161 | uint32_t h; 162 | uint i, n; 163 | 164 | h = hash(s) & IMask; 165 | b = &itbl[h]; 166 | n = b->nstr; 167 | 168 | for (i=0; istr[i]) == 0) 170 | return h + (i<str = vnew(1, sizeof b->str[0], Pheap); 176 | else if ((n & (n-1)) == 0) 177 | vgrow(&b->str, n+n); 178 | 179 | b->str[n] = emalloc(strlen(s)+1); 180 | b->nstr = n + 1; 181 | strcpy(b->str[n], s); 182 | return h + (n<>IBits < itbl[id&IMask].nstr); 189 | return itbl[id&IMask].str[id>>IBits]; 190 | } 191 | 192 | int 193 | isreg(Ref r) 194 | { 195 | return rtype(r) == RTmp && r.val < Tmp0; 196 | } 197 | 198 | int 199 | iscmp(int op, int *pk, int *pc) 200 | { 201 | if (Ocmpw <= op && op <= Ocmpw1) { 202 | *pc = op - Ocmpw; 203 | *pk = Kw; 204 | } 205 | else if (Ocmpl <= op && op <= Ocmpl1) { 206 | *pc = op - Ocmpl; 207 | *pk = Kl; 208 | } 209 | else if (Ocmps <= op && op <= Ocmps1) { 210 | *pc = NCmpI + op - Ocmps; 211 | *pk = Ks; 212 | } 213 | else if (Ocmpd <= op && op <= Ocmpd1) { 214 | *pc = NCmpI + op - Ocmpd; 215 | *pk = Kd; 216 | } 217 | else 218 | return 0; 219 | return 1; 220 | } 221 | 222 | int 223 | argcls(Ins *i, int n) 224 | { 225 | return optab[i->op].argcls[n][i->cls]; 226 | } 227 | 228 | void 229 | emit(int op, int k, Ref to, Ref arg0, Ref arg1) 230 | { 231 | if (curi == insb) 232 | die("emit, too many instructions"); 233 | *--curi = (Ins){ 234 | .op = op, .cls = k, 235 | .to = to, .arg = {arg0, arg1} 236 | }; 237 | } 238 | 239 | void 240 | emiti(Ins i) 241 | { 242 | emit(i.op, i.cls, i.to, i.arg[0], i.arg[1]); 243 | } 244 | 245 | void 246 | idup(Ins **pd, Ins *s, ulong n) 247 | { 248 | *pd = alloc(n * sizeof(Ins)); 249 | memcpy(*pd, s, n * sizeof(Ins)); 250 | } 251 | 252 | Ins * 253 | icpy(Ins *d, Ins *s, ulong n) 254 | { 255 | memcpy(d, s, n * sizeof(Ins)); 256 | return d + n; 257 | } 258 | 259 | static int cmptab[][2] ={ 260 | /* negation swap */ 261 | [Ciule] = {Ciugt, Ciuge}, 262 | [Ciult] = {Ciuge, Ciugt}, 263 | [Ciugt] = {Ciule, Ciult}, 264 | [Ciuge] = {Ciult, Ciule}, 265 | [Cisle] = {Cisgt, Cisge}, 266 | [Cislt] = {Cisge, Cisgt}, 267 | [Cisgt] = {Cisle, Cislt}, 268 | [Cisge] = {Cislt, Cisle}, 269 | [Cieq] = {Cine, Cieq}, 270 | [Cine] = {Cieq, Cine}, 271 | [NCmpI+Cfle] = {NCmpI+Cfgt, NCmpI+Cfge}, 272 | [NCmpI+Cflt] = {NCmpI+Cfge, NCmpI+Cfgt}, 273 | [NCmpI+Cfgt] = {NCmpI+Cfle, NCmpI+Cflt}, 274 | [NCmpI+Cfge] = {NCmpI+Cflt, NCmpI+Cfle}, 275 | [NCmpI+Cfeq] = {NCmpI+Cfne, NCmpI+Cfeq}, 276 | [NCmpI+Cfne] = {NCmpI+Cfeq, NCmpI+Cfne}, 277 | [NCmpI+Cfo] = {NCmpI+Cfuo, NCmpI+Cfo}, 278 | [NCmpI+Cfuo] = {NCmpI+Cfo, NCmpI+Cfuo}, 279 | }; 280 | 281 | int 282 | cmpneg(int c) 283 | { 284 | assert(0 <= c && c < NCmp); 285 | return cmptab[c][0]; 286 | } 287 | 288 | int 289 | cmpop(int c) 290 | { 291 | assert(0 <= c && c < NCmp); 292 | return cmptab[c][1]; 293 | } 294 | 295 | int 296 | clsmerge(short *pk, short k) 297 | { 298 | short k1; 299 | 300 | k1 = *pk; 301 | if (k1 == Kx) { 302 | *pk = k; 303 | return 0; 304 | } 305 | if ((k1 == Kw && k == Kl) || (k1 == Kl && k == Kw)) { 306 | *pk = Kw; 307 | return 0; 308 | } 309 | return k1 != k; 310 | } 311 | 312 | int 313 | phicls(int t, Tmp *tmp) 314 | { 315 | int t1; 316 | 317 | t1 = tmp[t].phi; 318 | if (!t1) 319 | return t; 320 | t1 = phicls(t1, tmp); 321 | tmp[t].phi = t1; 322 | return t1; 323 | } 324 | 325 | Ref 326 | newtmp(char *prfx, int k, Fn *fn) 327 | { 328 | static int n; 329 | int t; 330 | 331 | t = fn->ntmp++; 332 | vgrow(&fn->tmp, fn->ntmp); 333 | memset(&fn->tmp[t], 0, sizeof(Tmp)); 334 | if (prfx) 335 | sprintf(fn->tmp[t].name, "%s.%d", prfx, ++n); 336 | fn->tmp[t].cls = k; 337 | fn->tmp[t].slot = -1; 338 | fn->tmp[t].nuse = +1; 339 | fn->tmp[t].ndef = +1; 340 | return TMP(t); 341 | } 342 | 343 | void 344 | chuse(Ref r, int du, Fn *fn) 345 | { 346 | if (rtype(r) == RTmp) 347 | fn->tmp[r.val].nuse += du; 348 | } 349 | 350 | Ref 351 | getcon(int64_t val, Fn *fn) 352 | { 353 | int c; 354 | 355 | for (c=0; cncon; c++) 356 | if (fn->con[c].type == CBits && fn->con[c].bits.i == val) 357 | return CON(c); 358 | vgrow(&fn->con, ++fn->ncon); 359 | fn->con[c] = (Con){.type = CBits, .bits.i = val}; 360 | return CON(c); 361 | } 362 | 363 | void 364 | addcon(Con *c0, Con *c1) 365 | { 366 | if (c0->type == CUndef) 367 | *c0 = *c1; 368 | else { 369 | if (c1->type == CAddr) { 370 | assert(c0->type != CAddr && "adding two addresses"); 371 | c0->type = CAddr; 372 | c0->label = c1->label; 373 | } 374 | c0->bits.i += c1->bits.i; 375 | } 376 | } 377 | 378 | void 379 | blit(Ref rdst, uint doff, Ref rsrc, uint sz, Fn *fn) 380 | { 381 | struct { int st, ld, cls, size; } *p, tbl[] = { 382 | { Ostorel, Oload, Kl, 8 }, 383 | { Ostorew, Oload, Kw, 8 }, 384 | { Ostoreh, Oloaduh, Kw, 2 }, 385 | { Ostoreb, Oloadub, Kw, 1 } 386 | }; 387 | Ref r, r1; 388 | uint boff, s; 389 | 390 | for (boff=0, p=tbl; sz; p++) 391 | for (s=p->size; sz>=s; sz-=s, doff+=s, boff+=s) { 392 | r = newtmp("blt", Kl, fn); 393 | r1 = newtmp("blt", Kl, fn); 394 | emit(p->st, 0, R, r, r1); 395 | emit(Oadd, Kl, r1, rdst, getcon(doff, fn)); 396 | r1 = newtmp("blt", Kl, fn); 397 | emit(p->ld, p->cls, r, r1, R); 398 | emit(Oadd, Kl, r1, rsrc, getcon(boff, fn)); 399 | } 400 | } 401 | 402 | void 403 | bsinit(BSet *bs, uint n) 404 | { 405 | n = (n + NBit-1) / NBit; 406 | bs->nt = n; 407 | bs->t = alloc(n * sizeof bs->t[0]); 408 | } 409 | 410 | MAKESURE(NBit_is_64, NBit == 64); 411 | inline static uint 412 | popcnt(bits b) 413 | { 414 | b = (b & 0x5555555555555555) + ((b>>1) & 0x5555555555555555); 415 | b = (b & 0x3333333333333333) + ((b>>2) & 0x3333333333333333); 416 | b = (b & 0x0f0f0f0f0f0f0f0f) + ((b>>4) & 0x0f0f0f0f0f0f0f0f); 417 | b += (b>>8); 418 | b += (b>>16); 419 | b += (b>>32); 420 | return b & 0xff; 421 | } 422 | 423 | inline static int 424 | firstbit(bits b) 425 | { 426 | int n; 427 | 428 | n = 0; 429 | if (!(b & 0xffffffff)) { 430 | n += 32; 431 | b >>= 32; 432 | } 433 | if (!(b & 0xffff)) { 434 | n += 16; 435 | b >>= 16; 436 | } 437 | if (!(b & 0xff)) { 438 | n += 8; 439 | b >>= 8; 440 | } 441 | if (!(b & 0xf)) { 442 | n += 4; 443 | b >>= 4; 444 | } 445 | n += (char[16]){4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0}[b & 0xf]; 446 | return n; 447 | } 448 | 449 | uint 450 | bscount(BSet *bs) 451 | { 452 | uint i, n; 453 | 454 | n = 0; 455 | for (i=0; int; i++) 456 | n += popcnt(bs->t[i]); 457 | return n; 458 | } 459 | 460 | static inline uint 461 | bsmax(BSet *bs) 462 | { 463 | return bs->nt * NBit; 464 | } 465 | 466 | void 467 | bsset(BSet *bs, uint elt) 468 | { 469 | assert(elt < bsmax(bs)); 470 | bs->t[elt/NBit] |= BIT(elt%NBit); 471 | } 472 | 473 | void 474 | bsclr(BSet *bs, uint elt) 475 | { 476 | assert(elt < bsmax(bs)); 477 | bs->t[elt/NBit] &= ~BIT(elt%NBit); 478 | } 479 | 480 | #define BSOP(f, op) \ 481 | void \ 482 | f(BSet *a, BSet *b) \ 483 | { \ 484 | uint i; \ 485 | \ 486 | assert(a->nt == b->nt); \ 487 | for (i=0; int; i++) \ 488 | a->t[i] op b->t[i]; \ 489 | } 490 | 491 | BSOP(bscopy, =) 492 | BSOP(bsunion, |=) 493 | BSOP(bsinter, &=) 494 | BSOP(bsdiff, &= ~) 495 | 496 | int 497 | bsequal(BSet *a, BSet *b) 498 | { 499 | uint i; 500 | 501 | assert(a->nt == b->nt); 502 | for (i=0; int; i++) 503 | if (a->t[i] != b->t[i]) 504 | return 0; 505 | return 1; 506 | } 507 | 508 | void 509 | bszero(BSet *bs) 510 | { 511 | memset(bs->t, 0, bs->nt * sizeof bs->t[0]); 512 | } 513 | 514 | /* iterates on a bitset, use as follows 515 | * 516 | * for (i=0; bsiter(set, &i); i++) 517 | * use(i); 518 | * 519 | */ 520 | int 521 | bsiter(BSet *bs, int *elt) 522 | { 523 | bits b; 524 | uint t, i; 525 | 526 | i = *elt; 527 | t = i/NBit; 528 | if (t >= bs->nt) 529 | return 0; 530 | b = bs->t[t]; 531 | b &= ~(BIT(i%NBit) - 1); 532 | while (!b) { 533 | ++t; 534 | if (t >= bs->nt) 535 | return 0; 536 | b = bs->t[t]; 537 | } 538 | *elt = NBit*t + firstbit(b); 539 | return 1; 540 | } 541 | 542 | void 543 | dumpts(BSet *bs, Tmp *tmp, FILE *f) 544 | { 545 | int t; 546 | 547 | fprintf(f, "["); 548 | for (t=Tmp0; bsiter(bs, &t); t++) 549 | fprintf(f, " %s", tmp[t].name); 550 | fprintf(f, " ]\n"); 551 | } 552 | --------------------------------------------------------------------------------