| 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 (INRANGE(i.op, Oalloc, Oalloc1)) { |
| 167 | i0 = curi - 1; |
| 168 | salloc(i.to, i.arg[0], fn); |
| 169 | fixarg(&i0->arg[0], Kl, 0, fn); |
| 170 | return; |
| 171 | } |
| 172 | if (iscmp(i.op, &ck, &cc)) { |
| 173 | emit(Oflag, i.cls, i.to, R, R); |
| 174 | i0 = curi; |
| 175 | if (selcmp(i.arg, ck, fn)) |
| 176 | i0->op += cmpop(cc); |
| 177 | else |
| 178 | i0->op += cc; |
| 179 | return; |
| 180 | } |
| 181 | if (i.op != Onop) { |
| 182 | emiti(i); |
| 183 | iarg = curi->arg; /* fixarg() can change curi */ |
| 184 | fixarg(&iarg[0], argcls(&i, 0), 0, fn); |
| 185 | fixarg(&iarg[1], argcls(&i, 1), 0, fn); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | static void |
| 190 | seljmp(Blk *b, Fn *fn) |
| 191 | { |
| 192 | Ref r; |
| 193 | Ins *i, *ir; |
| 194 | int ck, cc, use; |
| 195 | |
| 196 | switch (b->jmp.type) { |
| 197 | default: |
| 198 | assert(0 && "TODO 2" ); |
| 199 | break; |
| 200 | case Jret0: |
| 201 | case Jjmp: |
| 202 | return; |
| 203 | case Jjnz: |
| 204 | break; |
| 205 | } |
| 206 | r = b->jmp.arg; |
| 207 | use = -1; |
| 208 | b->jmp.arg = R; |
| 209 | ir = 0; |
| 210 | i = &b->ins[b->nins]; |
| 211 | while (i > b->ins) |
| 212 | if (req((--i)->to, r)) { |
| 213 | use = fn->tmp[r.val].nuse; |
| 214 | ir = i; |
| 215 | break; |
| 216 | } |
| 217 | if (ir && use == 1 |
| 218 | && iscmp(ir->op, &ck, &cc)) { |
| 219 | if (selcmp(ir->arg, ck, fn)) |
| 220 | cc = cmpop(cc); |
| 221 | b->jmp.type = Jjf + cc; |
| 222 | *ir = (Ins){.op = Onop}; |
| 223 | } |
| 224 | else { |
| 225 | selcmp((Ref[]){r, CON_Z}, Kw, fn); |
| 226 | b->jmp.type = Jjfine; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | void |
| 231 | arm64_isel(Fn *fn) |
| 232 | { |
| 233 | Blk *b, **sb; |
| 234 | Ins *i; |
| 235 | Phi *p; |
| 236 | uint n, al; |
| 237 | int64_t sz; |
| 238 | |
| 239 | /* assign slots to fast allocs */ |
| 240 | b = fn->start; |
| 241 | /* specific to NAlign == 3 */ /* or change n=4 and sz /= 4 below */ |
| 242 | for (al=Oalloc, n=4; al<=Oalloc1; al++, n*=2) |
| 243 | for (i=b->ins; i<&b->ins[b->nins]; i++) |
| 244 | if (i->op == al) { |
| 245 | if (rtype(i->arg[0]) != RCon) |
| 246 | break; |
| 247 | sz = fn->con[i->arg[0].val].bits.i; |
| 248 | if (sz < 0 || sz >= INT_MAX-15) |
| 249 | err("invalid alloc size %" PRId64, sz); |
| 250 | sz = (sz + n-1) & -n; |
| 251 | sz /= 4; |
| 252 | fn->tmp[i->to.val].slot = fn->slot; |
| 253 | fn->slot += sz; |
| 254 | *i = (Ins){.op = Onop}; |
| 255 | } |
| 256 | |
| 257 | for (b=fn->start; b; b=b->link) { |
| 258 | curi = &insb[NIns]; |
| 259 | for (sb=(Blk*[3]){b->s1, b->s2, 0}; *sb; sb++) |
| 260 | for (p=(*sb)->phi; p; p=p->link) { |
| 261 | for (n=0; p->blk[n] != b; n++) |
| 262 | assert(n+1 < p->narg); |
| 263 | fixarg(&p->arg[n], p->cls, 1, fn); |
| 264 | } |
| 265 | seljmp(b, fn); |
| 266 | for (i=&b->ins[b->nins]; i!=b->ins;) |
| 267 | sel(*--i, fn); |
| 268 | b->nins = &insb[NIns] - curi; |
| 269 | idup(&b->ins, curi, b->nins); |
| 270 | } |
| 271 | |
| 272 | if (debug['I']) { |
| 273 | fprintf(stderr, "\n> After instruction selection:\n" ); |
| 274 | printfn(fn, stderr); |
| 275 | } |
| 276 | } |
| 277 | |