Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 1 | // Copyright 2009 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | #undef EXTERN |
| 6 | #define EXTERN |
Russ Cox | 61f84a2 | 2011-08-25 16:25:10 -0400 | [diff] [blame] | 7 | #include <u.h> |
| 8 | #include <libc.h> |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 9 | #include "gg.h" |
Ken Thompson | 0eb2a79 | 2009-11-06 16:51:49 -0800 | [diff] [blame] | 10 | #include "opt.h" |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 11 | |
| 12 | void |
Luuk van Dijk | e59aa8e | 2011-06-02 18:48:17 +0200 | [diff] [blame] | 13 | defframe(Prog *ptxt) |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 14 | { |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 15 | // fill in argument size |
Russ Cox | e7a0f67 | 2010-12-13 11:57:41 -0500 | [diff] [blame] | 16 | ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 17 | |
| 18 | // fill in final stack size |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 19 | if(stksize > maxstksize) |
| 20 | maxstksize = stksize; |
Russ Cox | e7a0f67 | 2010-12-13 11:57:41 -0500 | [diff] [blame] | 21 | ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 22 | maxstksize = 0; |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 23 | } |
| 24 | |
Luuk van Dijk | 2ad42a8 | 2011-06-14 17:03:37 +0200 | [diff] [blame] | 25 | // Sweep the prog list to mark any used nodes. |
| 26 | void |
| 27 | markautoused(Prog* p) |
| 28 | { |
| 29 | for (; p; p = p->link) { |
| 30 | if (p->from.type == D_AUTO && p->from.node) |
Rémy Oudompheng | f2ad374 | 2012-02-21 16:38:01 +1100 | [diff] [blame] | 31 | p->from.node->used = 1; |
Luuk van Dijk | 2ad42a8 | 2011-06-14 17:03:37 +0200 | [diff] [blame] | 32 | |
| 33 | if (p->to.type == D_AUTO && p->to.node) |
Rémy Oudompheng | f2ad374 | 2012-02-21 16:38:01 +1100 | [diff] [blame] | 34 | p->to.node->used = 1; |
Luuk van Dijk | 2ad42a8 | 2011-06-14 17:03:37 +0200 | [diff] [blame] | 35 | } |
| 36 | } |
| 37 | |
| 38 | // Fixup instructions after compactframe has moved all autos around. |
| 39 | void |
| 40 | fixautoused(Prog* p) |
| 41 | { |
| 42 | for (; p; p = p->link) { |
| 43 | if (p->from.type == D_AUTO && p->from.node) |
| 44 | p->from.offset += p->from.node->stkdelta; |
| 45 | |
| 46 | if (p->to.type == D_AUTO && p->to.node) |
| 47 | p->to.offset += p->to.node->stkdelta; |
| 48 | } |
| 49 | } |
| 50 | |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 51 | void |
| 52 | clearfat(Node *nl) |
| 53 | { |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 54 | uint32 w, c, q; |
| 55 | Node n1; |
| 56 | |
| 57 | /* clear a fat object */ |
| 58 | if(debug['g']) |
| 59 | dump("\nclearfat", nl); |
| 60 | |
| 61 | w = nl->type->width; |
| 62 | c = w % 4; // bytes |
| 63 | q = w / 4; // quads |
| 64 | |
| 65 | gconreg(AMOVL, 0, D_AX); |
| 66 | nodreg(&n1, types[tptr], D_DI); |
| 67 | agen(nl, &n1); |
| 68 | |
| 69 | if(q >= 4) { |
| 70 | gconreg(AMOVL, q, D_CX); |
| 71 | gins(AREP, N, N); // repeat |
| 72 | gins(ASTOSL, N, N); // STOL AL,*(DI)+ |
| 73 | } else |
| 74 | while(q > 0) { |
| 75 | gins(ASTOSL, N, N); // STOL AL,*(DI)+ |
| 76 | q--; |
| 77 | } |
| 78 | |
| 79 | if(c >= 4) { |
| 80 | gconreg(AMOVL, c, D_CX); |
| 81 | gins(AREP, N, N); // repeat |
| 82 | gins(ASTOSB, N, N); // STOB AL,*(DI)+ |
| 83 | } else |
| 84 | while(c > 0) { |
| 85 | gins(ASTOSB, N, N); // STOB AL,*(DI)+ |
| 86 | c--; |
| 87 | } |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | /* |
| 91 | * generate: |
| 92 | * call f |
| 93 | * proc=0 normal call |
| 94 | * proc=1 goroutine run in new proc |
| 95 | * proc=2 defer call save away stack |
| 96 | */ |
| 97 | void |
| 98 | ginscall(Node *f, int proc) |
| 99 | { |
| 100 | Prog *p; |
| 101 | Node reg, con; |
| 102 | |
| 103 | switch(proc) { |
| 104 | default: |
| 105 | fatal("ginscall: bad proc %d", proc); |
| 106 | break; |
| 107 | |
| 108 | case 0: // normal call |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 109 | case -1: // normal call but no return |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 110 | p = gins(ACALL, N, f); |
| 111 | afunclit(&p->to); |
Luuk van Dijk | 40af78c | 2012-06-02 22:50:57 -0400 | [diff] [blame] | 112 | if(proc == -1 || noreturn(p)) |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 113 | gins(AUNDEF, N, N); |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 114 | break; |
| 115 | |
| 116 | case 1: // call in new proc (go) |
Russ Cox | 9b1507b | 2010-03-31 11:46:01 -0700 | [diff] [blame] | 117 | case 2: // deferred call (defer) |
| 118 | nodreg(®, types[TINT32], D_CX); |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 119 | gins(APUSHL, f, N); |
| 120 | nodconst(&con, types[TINT32], argsize(f->type)); |
| 121 | gins(APUSHL, &con, N); |
| 122 | if(proc == 1) |
| 123 | ginscall(newproc, 0); |
| 124 | else |
| 125 | ginscall(deferproc, 0); |
| 126 | gins(APOPL, N, ®); |
| 127 | gins(APOPL, N, ®); |
Russ Cox | 9b1507b | 2010-03-31 11:46:01 -0700 | [diff] [blame] | 128 | if(proc == 2) { |
| 129 | nodreg(®, types[TINT64], D_AX); |
| 130 | gins(ATESTL, ®, ®); |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 131 | patch(gbranch(AJNE, T, -1), retpc); |
Russ Cox | 9b1507b | 2010-03-31 11:46:01 -0700 | [diff] [blame] | 132 | } |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 133 | break; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 | * n is call to interface method. |
| 139 | * generate res = n. |
| 140 | */ |
| 141 | void |
| 142 | cgen_callinter(Node *n, Node *res, int proc) |
| 143 | { |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 144 | Node *i, *f; |
| 145 | Node tmpi, nodo, nodr, nodsp; |
| 146 | |
| 147 | i = n->left; |
| 148 | if(i->op != ODOTINTER) |
| 149 | fatal("cgen_callinter: not ODOTINTER %O", i->op); |
| 150 | |
| 151 | f = i->right; // field |
| 152 | if(f->op != ONAME) |
| 153 | fatal("cgen_callinter: not ONAME %O", f->op); |
| 154 | |
| 155 | i = i->left; // interface |
| 156 | |
| 157 | if(!i->addable) { |
Russ Cox | 69c0edd | 2009-12-02 18:31:29 -0800 | [diff] [blame] | 158 | tempname(&tmpi, i->type); |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 159 | cgen(i, &tmpi); |
| 160 | i = &tmpi; |
| 161 | } |
| 162 | |
Russ Cox | e52e9ca | 2009-07-17 01:00:44 -0700 | [diff] [blame] | 163 | genlist(n->list); // assign the args |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 164 | |
| 165 | // Can regalloc now; i is known to be addable, |
| 166 | // so the agen will be easy. |
| 167 | regalloc(&nodr, types[tptr], res); |
| 168 | regalloc(&nodo, types[tptr], &nodr); |
| 169 | nodo.op = OINDREG; |
| 170 | |
| 171 | agen(i, &nodr); // REG = &inter |
| 172 | |
| 173 | nodindreg(&nodsp, types[tptr], D_SP); |
| 174 | nodo.xoffset += widthptr; |
Anthony Martin | 028f74f | 2011-06-20 14:49:29 -0400 | [diff] [blame] | 175 | cgen(&nodo, &nodsp); // 0(SP) = 4(REG) -- i.data |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 176 | |
| 177 | nodo.xoffset -= widthptr; |
Anthony Martin | 028f74f | 2011-06-20 14:49:29 -0400 | [diff] [blame] | 178 | cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 179 | |
Russ Cox | 4589c34 | 2010-02-18 18:31:13 -0800 | [diff] [blame] | 180 | if(n->left->xoffset == BADWIDTH) |
| 181 | fatal("cgen_callinter: badwidth"); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 182 | nodo.xoffset = n->left->xoffset + 3*widthptr + 8; |
Anthony Martin | 028f74f | 2011-06-20 14:49:29 -0400 | [diff] [blame] | 183 | cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 184 | |
| 185 | // BOTCH nodr.type = fntype; |
| 186 | nodr.type = n->left->type; |
| 187 | ginscall(&nodr, proc); |
| 188 | |
| 189 | regfree(&nodr); |
| 190 | regfree(&nodo); |
| 191 | |
| 192 | setmaxarg(n->left->type); |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | /* |
| 196 | * generate function call; |
| 197 | * proc=0 normal call |
| 198 | * proc=1 goroutine run in new proc |
| 199 | * proc=2 defer call save away stack |
| 200 | */ |
| 201 | void |
| 202 | cgen_call(Node *n, int proc) |
| 203 | { |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 204 | Type *t; |
| 205 | Node nod, afun; |
| 206 | |
| 207 | if(n == N) |
| 208 | return; |
| 209 | |
| 210 | if(n->left->ullman >= UINF) { |
| 211 | // if name involves a fn call |
| 212 | // precompute the address of the fn |
Russ Cox | 69c0edd | 2009-12-02 18:31:29 -0800 | [diff] [blame] | 213 | tempname(&afun, types[tptr]); |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 214 | cgen(n->left, &afun); |
| 215 | } |
| 216 | |
Russ Cox | e52e9ca | 2009-07-17 01:00:44 -0700 | [diff] [blame] | 217 | genlist(n->list); // assign the args |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 218 | t = n->left->type; |
| 219 | |
| 220 | setmaxarg(t); |
| 221 | |
| 222 | // call tempname pointer |
| 223 | if(n->left->ullman >= UINF) { |
| 224 | regalloc(&nod, types[tptr], N); |
| 225 | cgen_as(&nod, &afun); |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 226 | nod.type = t; |
| 227 | ginscall(&nod, proc); |
| 228 | regfree(&nod); |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | // call pointer |
| 233 | if(n->left->op != ONAME || n->left->class != PFUNC) { |
| 234 | regalloc(&nod, types[tptr], N); |
| 235 | cgen_as(&nod, n->left); |
| 236 | nod.type = t; |
| 237 | ginscall(&nod, proc); |
| 238 | regfree(&nod); |
| 239 | return; |
| 240 | } |
| 241 | |
| 242 | // call direct |
| 243 | n->left->method = 1; |
| 244 | ginscall(n->left, proc); |
| 245 | } |
| 246 | |
| 247 | /* |
| 248 | * call to n has already been generated. |
| 249 | * generate: |
| 250 | * res = return value from call. |
| 251 | */ |
| 252 | void |
| 253 | cgen_callret(Node *n, Node *res) |
| 254 | { |
| 255 | Node nod; |
| 256 | Type *fp, *t; |
| 257 | Iter flist; |
| 258 | |
| 259 | t = n->left->type; |
| 260 | if(t->etype == TPTR32 || t->etype == TPTR64) |
| 261 | t = t->type; |
| 262 | |
| 263 | fp = structfirst(&flist, getoutarg(t)); |
| 264 | if(fp == T) |
| 265 | fatal("cgen_callret: nil"); |
| 266 | |
| 267 | memset(&nod, 0, sizeof(nod)); |
| 268 | nod.op = OINDREG; |
| 269 | nod.val.u.reg = D_SP; |
| 270 | nod.addable = 1; |
| 271 | |
| 272 | nod.xoffset = fp->width; |
| 273 | nod.type = fp->type; |
| 274 | cgen_as(res, &nod); |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 | * call to n has already been generated. |
| 279 | * generate: |
| 280 | * res = &return value from call. |
| 281 | */ |
| 282 | void |
| 283 | cgen_aret(Node *n, Node *res) |
| 284 | { |
| 285 | Node nod1, nod2; |
| 286 | Type *fp, *t; |
| 287 | Iter flist; |
| 288 | |
| 289 | t = n->left->type; |
| 290 | if(isptr[t->etype]) |
| 291 | t = t->type; |
| 292 | |
| 293 | fp = structfirst(&flist, getoutarg(t)); |
| 294 | if(fp == T) |
| 295 | fatal("cgen_aret: nil"); |
| 296 | |
| 297 | memset(&nod1, 0, sizeof(nod1)); |
| 298 | nod1.op = OINDREG; |
| 299 | nod1.val.u.reg = D_SP; |
| 300 | nod1.addable = 1; |
| 301 | |
| 302 | nod1.xoffset = fp->width; |
| 303 | nod1.type = fp->type; |
| 304 | |
| 305 | if(res->op != OREGISTER) { |
| 306 | regalloc(&nod2, types[tptr], res); |
| 307 | gins(ALEAL, &nod1, &nod2); |
| 308 | gins(AMOVL, &nod2, res); |
| 309 | regfree(&nod2); |
| 310 | } else |
| 311 | gins(ALEAL, &nod1, res); |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 312 | } |
| 313 | |
| 314 | /* |
| 315 | * generate return. |
| 316 | * n->left is assignments to return values. |
| 317 | */ |
| 318 | void |
| 319 | cgen_ret(Node *n) |
| 320 | { |
Russ Cox | e52e9ca | 2009-07-17 01:00:44 -0700 | [diff] [blame] | 321 | genlist(n->list); // copy out args |
Luuk van Dijk | e59aa8e | 2011-06-02 18:48:17 +0200 | [diff] [blame] | 322 | if(retpc) |
| 323 | gjmp(retpc); |
Russ Cox | 97d0e8f | 2010-03-26 18:01:02 -0700 | [diff] [blame] | 324 | else |
| 325 | gins(ARET, N, N); |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 326 | } |
| 327 | |
| 328 | /* |
| 329 | * generate += *= etc. |
| 330 | */ |
| 331 | void |
| 332 | cgen_asop(Node *n) |
| 333 | { |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 334 | Node n1, n2, n3, n4; |
| 335 | Node *nl, *nr; |
| 336 | Prog *p1; |
| 337 | Addr addr; |
| 338 | int a; |
| 339 | |
| 340 | nl = n->left; |
| 341 | nr = n->right; |
| 342 | |
| 343 | if(nr->ullman >= UINF && nl->ullman >= UINF) { |
Russ Cox | 69c0edd | 2009-12-02 18:31:29 -0800 | [diff] [blame] | 344 | tempname(&n1, nr->type); |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 345 | cgen(nr, &n1); |
| 346 | n2 = *n; |
| 347 | n2.right = &n1; |
| 348 | cgen_asop(&n2); |
| 349 | goto ret; |
| 350 | } |
| 351 | |
| 352 | if(!isint[nl->type->etype]) |
| 353 | goto hard; |
| 354 | if(!isint[nr->type->etype]) |
| 355 | goto hard; |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 356 | if(is64(nl->type) || is64(nr->type)) |
| 357 | goto hard; |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 358 | |
| 359 | switch(n->etype) { |
| 360 | case OADD: |
| 361 | if(smallintconst(nr)) |
| 362 | if(mpgetfix(nr->val.u.xval) == 1) { |
| 363 | a = optoas(OINC, nl->type); |
| 364 | if(nl->addable) { |
| 365 | gins(a, N, nl); |
| 366 | goto ret; |
| 367 | } |
| 368 | if(sudoaddable(a, nl, &addr)) { |
| 369 | p1 = gins(a, N, N); |
| 370 | p1->to = addr; |
| 371 | sudoclean(); |
| 372 | goto ret; |
| 373 | } |
| 374 | } |
| 375 | break; |
| 376 | |
| 377 | case OSUB: |
| 378 | if(smallintconst(nr)) |
| 379 | if(mpgetfix(nr->val.u.xval) == 1) { |
| 380 | a = optoas(ODEC, nl->type); |
| 381 | if(nl->addable) { |
| 382 | gins(a, N, nl); |
| 383 | goto ret; |
| 384 | } |
| 385 | if(sudoaddable(a, nl, &addr)) { |
| 386 | p1 = gins(a, N, N); |
| 387 | p1->to = addr; |
| 388 | sudoclean(); |
| 389 | goto ret; |
| 390 | } |
| 391 | } |
| 392 | break; |
| 393 | } |
| 394 | |
| 395 | switch(n->etype) { |
| 396 | case OADD: |
| 397 | case OSUB: |
| 398 | case OXOR: |
| 399 | case OAND: |
| 400 | case OOR: |
| 401 | a = optoas(n->etype, nl->type); |
| 402 | if(nl->addable) { |
| 403 | if(smallintconst(nr)) { |
| 404 | gins(a, nr, nl); |
| 405 | goto ret; |
| 406 | } |
| 407 | regalloc(&n2, nr->type, N); |
| 408 | cgen(nr, &n2); |
| 409 | gins(a, &n2, nl); |
| 410 | regfree(&n2); |
| 411 | goto ret; |
| 412 | } |
| 413 | if(nr->ullman < UINF) |
| 414 | if(sudoaddable(a, nl, &addr)) { |
| 415 | if(smallintconst(nr)) { |
| 416 | p1 = gins(a, nr, N); |
| 417 | p1->to = addr; |
| 418 | sudoclean(); |
| 419 | goto ret; |
| 420 | } |
| 421 | regalloc(&n2, nr->type, N); |
| 422 | cgen(nr, &n2); |
| 423 | p1 = gins(a, &n2, N); |
| 424 | p1->to = addr; |
| 425 | regfree(&n2); |
| 426 | sudoclean(); |
| 427 | goto ret; |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | hard: |
Russ Cox | ac499ed | 2010-03-05 15:35:09 -0800 | [diff] [blame] | 432 | n2.op = 0; |
| 433 | n1.op = 0; |
| 434 | if(nr->ullman >= nl->ullman || nl->addable) { |
| 435 | mgen(nr, &n2, N); |
| 436 | nr = &n2; |
Russ Cox | ac499ed | 2010-03-05 15:35:09 -0800 | [diff] [blame] | 437 | } else { |
| 438 | tempname(&n2, nr->type); |
| 439 | cgen(nr, &n2); |
| 440 | nr = &n2; |
| 441 | } |
| 442 | if(!nl->addable) { |
| 443 | igen(nl, &n1, N); |
| 444 | nl = &n1; |
| 445 | } |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 446 | |
| 447 | n3 = *n; |
Russ Cox | ac499ed | 2010-03-05 15:35:09 -0800 | [diff] [blame] | 448 | n3.left = nl; |
| 449 | n3.right = nr; |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 450 | n3.op = n->etype; |
| 451 | |
Russ Cox | ac499ed | 2010-03-05 15:35:09 -0800 | [diff] [blame] | 452 | mgen(&n3, &n4, N); |
| 453 | gmove(&n4, nl); |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 454 | |
Russ Cox | ac499ed | 2010-03-05 15:35:09 -0800 | [diff] [blame] | 455 | if(n1.op) |
| 456 | regfree(&n1); |
| 457 | mfree(&n2); |
| 458 | mfree(&n4); |
Russ Cox | 5ecd010 | 2009-05-26 16:23:54 -0700 | [diff] [blame] | 459 | |
| 460 | ret: |
| 461 | ; |
Russ Cox | dc5b467 | 2009-03-31 00:22:59 -0700 | [diff] [blame] | 462 | } |
| 463 | |
Russ Cox | 76a763e | 2009-08-07 12:57:44 -0700 | [diff] [blame] | 464 | int |
| 465 | samereg(Node *a, Node *b) |
| 466 | { |
| 467 | if(a->op != OREGISTER) |
| 468 | return 0; |
| 469 | if(b->op != OREGISTER) |
| 470 | return 0; |
| 471 | if(a->val.u.reg != b->val.u.reg) |
| 472 | return 0; |
| 473 | return 1; |
| 474 | } |
| 475 | |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 476 | /* |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 477 | * generate division. |
| 478 | * caller must set: |
| 479 | * ax = allocated AX register |
| 480 | * dx = allocated DX register |
| 481 | * generates one of: |
| 482 | * res = nl / nr |
| 483 | * res = nl % nr |
| 484 | * according to op. |
| 485 | */ |
| 486 | void |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 487 | dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 488 | { |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 489 | int check; |
Russ Cox | 4fb3c4f | 2011-08-30 08:47:28 -0400 | [diff] [blame] | 490 | Node n1, t1, t2, t3, t4, n4, nz; |
| 491 | Type *t, *t0; |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 492 | Prog *p1, *p2, *p3; |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 493 | |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 494 | // Have to be careful about handling |
| 495 | // most negative int divided by -1 correctly. |
| 496 | // The hardware will trap. |
| 497 | // Also the byte divide instruction needs AH, |
| 498 | // which we otherwise don't have to deal with. |
| 499 | // Easiest way to avoid for int8, int16: use int32. |
| 500 | // For int32 and int64, use explicit test. |
| 501 | // Could use int64 hw for int32. |
| 502 | t = nl->type; |
Russ Cox | 4fb3c4f | 2011-08-30 08:47:28 -0400 | [diff] [blame] | 503 | t0 = t; |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 504 | check = 0; |
| 505 | if(issigned[t->etype]) { |
| 506 | check = 1; |
| 507 | if(isconst(nl, CTINT) && mpgetfix(nl->val.u.xval) != -1LL<<(t->width*8-1)) |
| 508 | check = 0; |
| 509 | else if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) != -1) |
| 510 | check = 0; |
| 511 | } |
| 512 | if(t->width < 4) { |
| 513 | if(issigned[t->etype]) |
| 514 | t = types[TINT32]; |
| 515 | else |
| 516 | t = types[TUINT32]; |
| 517 | check = 0; |
| 518 | } |
| 519 | |
| 520 | tempname(&t1, t); |
| 521 | tempname(&t2, t); |
Russ Cox | 4fb3c4f | 2011-08-30 08:47:28 -0400 | [diff] [blame] | 522 | if(t0 != t) { |
| 523 | tempname(&t3, t0); |
| 524 | tempname(&t4, t0); |
| 525 | cgen(nl, &t3); |
| 526 | cgen(nr, &t4); |
| 527 | // Convert. |
| 528 | gmove(&t3, &t1); |
| 529 | gmove(&t4, &t2); |
| 530 | } else { |
| 531 | cgen(nl, &t1); |
| 532 | cgen(nr, &t2); |
| 533 | } |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 534 | |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 535 | if(!samereg(ax, res) && !samereg(dx, res)) |
| 536 | regalloc(&n1, t, res); |
| 537 | else |
| 538 | regalloc(&n1, t, N); |
| 539 | gmove(&t2, &n1); |
| 540 | gmove(&t1, ax); |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 541 | p3 = P; |
| 542 | if(check) { |
| 543 | nodconst(&n4, t, -1); |
| 544 | gins(optoas(OCMP, t), &n1, &n4); |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 545 | p1 = gbranch(optoas(ONE, t), T, +1); |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 546 | nodconst(&n4, t, -1LL<<(t->width*8-1)); |
| 547 | gins(optoas(OCMP, t), ax, &n4); |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 548 | p2 = gbranch(optoas(ONE, t), T, +1); |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 549 | if(op == ODIV) |
| 550 | gmove(&n4, res); |
| 551 | if(op == OMOD) { |
| 552 | nodconst(&n4, t, 0); |
| 553 | gmove(&n4, res); |
| 554 | } |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 555 | p3 = gbranch(AJMP, T, 0); |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 556 | patch(p1, pc); |
| 557 | patch(p2, pc); |
| 558 | } |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 559 | if(!issigned[t->etype]) { |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 560 | nodconst(&nz, t, 0); |
| 561 | gmove(&nz, dx); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 562 | } else |
| 563 | gins(optoas(OEXTEND, t), N, N); |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 564 | gins(optoas(op, t), &n1, N); |
| 565 | regfree(&n1); |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 566 | |
| 567 | if(op == ODIV) |
| 568 | gmove(ax, res); |
| 569 | else |
| 570 | gmove(dx, res); |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 571 | if(check) |
| 572 | patch(p3, pc); |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 573 | } |
| 574 | |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 575 | static void |
| 576 | savex(int dr, Node *x, Node *oldx, Node *res, Type *t) |
| 577 | { |
| 578 | int r; |
| 579 | |
| 580 | r = reg[dr]; |
| 581 | nodreg(x, types[TINT32], dr); |
| 582 | |
| 583 | // save current ax and dx if they are live |
| 584 | // and not the destination |
| 585 | memset(oldx, 0, sizeof *oldx); |
| 586 | if(r > 0 && !samereg(x, res)) { |
Russ Cox | 69c0edd | 2009-12-02 18:31:29 -0800 | [diff] [blame] | 587 | tempname(oldx, types[TINT32]); |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 588 | gmove(x, oldx); |
| 589 | } |
| 590 | |
| 591 | regalloc(x, t, x); |
| 592 | } |
| 593 | |
| 594 | static void |
| 595 | restx(Node *x, Node *oldx) |
| 596 | { |
| 597 | regfree(x); |
| 598 | |
| 599 | if(oldx->op != 0) { |
| 600 | x->type = types[TINT32]; |
| 601 | gmove(oldx, x); |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 602 | } |
| 603 | } |
| 604 | |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 605 | /* |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 606 | * generate division according to op, one of: |
| 607 | * res = nl / nr |
| 608 | * res = nl % nr |
| 609 | */ |
| 610 | void |
| 611 | cgen_div(int op, Node *nl, Node *nr, Node *res) |
| 612 | { |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 613 | Node ax, dx, oldax, olddx; |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 614 | Type *t; |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 615 | |
Russ Cox | a8e4ed6 | 2009-05-26 21:07:26 -0700 | [diff] [blame] | 616 | if(is64(nl->type)) |
| 617 | fatal("cgen_div %T", nl->type); |
| 618 | |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 619 | if(issigned[nl->type->etype]) |
| 620 | t = types[TINT32]; |
| 621 | else |
| 622 | t = types[TUINT32]; |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 623 | savex(D_AX, &ax, &oldax, res, t); |
| 624 | savex(D_DX, &dx, &olddx, res, t); |
Russ Cox | 08bfb39 | 2011-07-28 14:18:22 -0400 | [diff] [blame] | 625 | dodiv(op, nl, nr, res, &ax, &dx); |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 626 | restx(&dx, &olddx); |
| 627 | restx(&ax, &oldax); |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 628 | } |
| 629 | |
| 630 | /* |
| 631 | * generate shift according to op, one of: |
| 632 | * res = nl << nr |
| 633 | * res = nl >> nr |
| 634 | */ |
| 635 | void |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 636 | cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 637 | { |
Russ Cox | 28a2367 | 2011-07-28 18:22:12 -0400 | [diff] [blame] | 638 | Node n1, n2, nt, cx, oldcx, hi, lo; |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 639 | int a, w; |
Russ Cox | 28a2367 | 2011-07-28 18:22:12 -0400 | [diff] [blame] | 640 | Prog *p1, *p2; |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 641 | uvlong sc; |
| 642 | |
| 643 | if(nl->type->width > 4) |
Kai Backman | 58ee1f5 | 2009-10-27 22:38:45 -0700 | [diff] [blame] | 644 | fatal("cgen_shift %T", nl->type); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 645 | |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 646 | w = nl->type->width * 8; |
| 647 | |
| 648 | a = optoas(op, nl->type); |
| 649 | |
| 650 | if(nr->op == OLITERAL) { |
Russ Cox | 7c9ed79 | 2010-06-30 20:45:50 -0700 | [diff] [blame] | 651 | tempname(&n2, nl->type); |
| 652 | cgen(nl, &n2); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 653 | regalloc(&n1, nl->type, res); |
Russ Cox | 7c9ed79 | 2010-06-30 20:45:50 -0700 | [diff] [blame] | 654 | gmove(&n2, &n1); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 655 | sc = mpgetfix(nr->val.u.xval); |
| 656 | if(sc >= nl->type->width*8) { |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 657 | // large shift gets 2 shifts by width-1 |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 658 | gins(a, ncon(w-1), &n1); |
| 659 | gins(a, ncon(w-1), &n1); |
| 660 | } else |
| 661 | gins(a, nr, &n1); |
| 662 | gmove(&n1, res); |
| 663 | regfree(&n1); |
| 664 | return; |
| 665 | } |
| 666 | |
Russ Cox | 76a763e | 2009-08-07 12:57:44 -0700 | [diff] [blame] | 667 | memset(&oldcx, 0, sizeof oldcx); |
| 668 | nodreg(&cx, types[TUINT32], D_CX); |
Russ Cox | b71c484 | 2009-09-30 08:56:01 -0700 | [diff] [blame] | 669 | if(reg[D_CX] > 1 && !samereg(&cx, res)) { |
Russ Cox | 69c0edd | 2009-12-02 18:31:29 -0800 | [diff] [blame] | 670 | tempname(&oldcx, types[TUINT32]); |
Russ Cox | 76a763e | 2009-08-07 12:57:44 -0700 | [diff] [blame] | 671 | gmove(&cx, &oldcx); |
| 672 | } |
| 673 | |
Russ Cox | 28a2367 | 2011-07-28 18:22:12 -0400 | [diff] [blame] | 674 | if(nr->type->width > 4) { |
| 675 | tempname(&nt, nr->type); |
| 676 | n1 = nt; |
| 677 | } else { |
| 678 | nodreg(&n1, types[TUINT32], D_CX); |
| 679 | regalloc(&n1, nr->type, &n1); // to hold the shift type in CX |
| 680 | } |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 681 | |
Russ Cox | 76a763e | 2009-08-07 12:57:44 -0700 | [diff] [blame] | 682 | if(samereg(&cx, res)) |
| 683 | regalloc(&n2, nl->type, N); |
| 684 | else |
| 685 | regalloc(&n2, nl->type, res); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 686 | if(nl->ullman >= nr->ullman) { |
| 687 | cgen(nl, &n2); |
| 688 | cgen(nr, &n1); |
| 689 | } else { |
| 690 | cgen(nr, &n1); |
| 691 | cgen(nl, &n2); |
| 692 | } |
| 693 | |
| 694 | // test and fix up large shifts |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 695 | if(bounded) { |
| 696 | if(nr->type->width > 4) { |
| 697 | // delayed reg alloc |
| 698 | nodreg(&n1, types[TUINT32], D_CX); |
| 699 | regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX |
| 700 | split64(&nt, &lo, &hi); |
| 701 | gmove(&lo, &n1); |
| 702 | } |
Russ Cox | 28a2367 | 2011-07-28 18:22:12 -0400 | [diff] [blame] | 703 | } else { |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 704 | if(nr->type->width > 4) { |
| 705 | // delayed reg alloc |
| 706 | nodreg(&n1, types[TUINT32], D_CX); |
| 707 | regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX |
| 708 | split64(&nt, &lo, &hi); |
| 709 | gmove(&lo, &n1); |
| 710 | gins(optoas(OCMP, types[TUINT32]), &hi, ncon(0)); |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 711 | p2 = gbranch(optoas(ONE, types[TUINT32]), T, +1); |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 712 | gins(optoas(OCMP, types[TUINT32]), &n1, ncon(w)); |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 713 | p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 714 | patch(p2, pc); |
| 715 | } else { |
| 716 | gins(optoas(OCMP, nr->type), &n1, ncon(w)); |
Russ Cox | 001b75c | 2012-05-30 18:07:39 -0400 | [diff] [blame] | 717 | p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); |
Russ Cox | c6ce448 | 2012-05-24 17:20:07 -0400 | [diff] [blame] | 718 | } |
| 719 | if(op == ORSH && issigned[nl->type->etype]) { |
| 720 | gins(a, ncon(w-1), &n2); |
| 721 | } else { |
| 722 | gmove(ncon(0), &n2); |
| 723 | } |
| 724 | patch(p1, pc); |
Russ Cox | 28a2367 | 2011-07-28 18:22:12 -0400 | [diff] [blame] | 725 | } |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 726 | gins(a, &n1, &n2); |
Russ Cox | dd5f323 | 2009-08-12 13:18:27 -0700 | [diff] [blame] | 727 | |
Russ Cox | 69c0edd | 2009-12-02 18:31:29 -0800 | [diff] [blame] | 728 | if(oldcx.op != 0) |
Russ Cox | 76a763e | 2009-08-07 12:57:44 -0700 | [diff] [blame] | 729 | gmove(&oldcx, &cx); |
Russ Cox | 9a9ffb2 | 2009-06-04 15:24:01 -0700 | [diff] [blame] | 730 | |
| 731 | gmove(&n2, res); |
| 732 | |
| 733 | regfree(&n1); |
| 734 | regfree(&n2); |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 735 | } |
| 736 | |
| 737 | /* |
| 738 | * generate byte multiply: |
| 739 | * res = nl * nr |
| 740 | * no byte multiply instruction so have to do |
| 741 | * 16-bit multiply and take bottom half. |
| 742 | */ |
| 743 | void |
| 744 | cgen_bmul(int op, Node *nl, Node *nr, Node *res) |
| 745 | { |
Russ Cox | 3c06bd6 | 2009-06-06 19:28:16 -0700 | [diff] [blame] | 746 | Node n1b, n2b, n1w, n2w; |
| 747 | Type *t; |
| 748 | int a; |
| 749 | |
| 750 | if(nl->ullman >= nr->ullman) { |
| 751 | regalloc(&n1b, nl->type, res); |
| 752 | cgen(nl, &n1b); |
| 753 | regalloc(&n2b, nr->type, N); |
| 754 | cgen(nr, &n2b); |
| 755 | } else { |
| 756 | regalloc(&n2b, nr->type, N); |
| 757 | cgen(nr, &n2b); |
| 758 | regalloc(&n1b, nl->type, res); |
| 759 | cgen(nl, &n1b); |
| 760 | } |
| 761 | |
| 762 | // copy from byte to short registers |
| 763 | t = types[TUINT16]; |
| 764 | if(issigned[nl->type->etype]) |
| 765 | t = types[TINT16]; |
| 766 | |
| 767 | regalloc(&n2w, t, &n2b); |
| 768 | cgen(&n2b, &n2w); |
| 769 | |
| 770 | regalloc(&n1w, t, &n1b); |
| 771 | cgen(&n1b, &n1w); |
| 772 | |
| 773 | a = optoas(op, t); |
| 774 | gins(a, &n2w, &n1w); |
| 775 | cgen(&n1w, &n1b); |
| 776 | cgen(&n1b, res); |
| 777 | |
| 778 | regfree(&n1w); |
| 779 | regfree(&n2w); |
| 780 | regfree(&n1b); |
| 781 | regfree(&n2b); |
Russ Cox | 6b07021 | 2009-04-02 16:48:06 -0700 | [diff] [blame] | 782 | } |