github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/cmd/5l/noop.c (about) 1 // Inferno utils/5l/noop.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/5l/noop.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 // Code transformations. 32 33 #include "l.h" 34 #include "../ld/lib.h" 35 #include "../../pkg/runtime/stack.h" 36 37 static Sym* sym_div; 38 static Sym* sym_divu; 39 static Sym* sym_mod; 40 static Sym* sym_modu; 41 42 static void 43 linkcase(Prog *casep) 44 { 45 Prog *p; 46 47 for(p = casep; p != P; p = p->link){ 48 if(p->as == ABCASE) { 49 for(; p != P && p->as == ABCASE; p = p->link) 50 p->pcrel = casep; 51 break; 52 } 53 } 54 } 55 56 void 57 noops(void) 58 { 59 Prog *p, *q, *q1, *q2; 60 int o; 61 int32 arg; 62 Prog *pmorestack; 63 Sym *symmorestack, *tlsfallback, *gmsym; 64 65 /* 66 * find leaf subroutines 67 * strip NOPs 68 * expand RET 69 * expand BECOME pseudo 70 * fixup TLS 71 */ 72 73 if(debug['v']) 74 Bprint(&bso, "%5.2f noops\n", cputime()); 75 Bflush(&bso); 76 77 symmorestack = lookup("runtime.morestack", 0); 78 if(symmorestack->type != STEXT) { 79 diag("runtime·morestack not defined"); 80 errorexit(); 81 } 82 pmorestack = symmorestack->text; 83 pmorestack->reg |= NOSPLIT; 84 85 tlsfallback = lookup("runtime.read_tls_fallback", 0); 86 gmsym = S; 87 if(linkmode == LinkExternal) 88 gmsym = lookup("runtime.tlsgm", 0); 89 q = P; 90 for(cursym = textp; cursym != nil; cursym = cursym->next) { 91 for(p = cursym->text; p != P; p = p->link) { 92 switch(p->as) { 93 case ACASE: 94 if(flag_shared) 95 linkcase(p); 96 break; 97 98 case ATEXT: 99 p->mark |= LEAF; 100 break; 101 102 case ARET: 103 break; 104 105 case ADIV: 106 case ADIVU: 107 case AMOD: 108 case AMODU: 109 q = p; 110 if(prog_div == P) 111 initdiv(); 112 cursym->text->mark &= ~LEAF; 113 continue; 114 115 case ANOP: 116 q1 = p->link; 117 q->link = q1; /* q is non-nop */ 118 if(q1 != P) 119 q1->mark |= p->mark; 120 continue; 121 122 case ABL: 123 case ABX: 124 cursym->text->mark &= ~LEAF; 125 126 case ABCASE: 127 case AB: 128 129 case ABEQ: 130 case ABNE: 131 case ABCS: 132 case ABHS: 133 case ABCC: 134 case ABLO: 135 case ABMI: 136 case ABPL: 137 case ABVS: 138 case ABVC: 139 case ABHI: 140 case ABLS: 141 case ABGE: 142 case ABLT: 143 case ABGT: 144 case ABLE: 145 q1 = p->cond; 146 if(q1 != P) { 147 while(q1->as == ANOP) { 148 q1 = q1->link; 149 p->cond = q1; 150 } 151 } 152 break; 153 case AWORD: 154 // Rewrite TLS register fetch: MRC 15, 0, <reg>, C13, C0, 3 155 if((p->to.offset & 0xffff0fff) == 0xee1d0f70) { 156 if(HEADTYPE == Hopenbsd) { 157 p->as = ARET; 158 } else if(goarm < 7) { 159 if(tlsfallback->type != STEXT) { 160 diag("runtime·read_tls_fallback not defined"); 161 errorexit(); 162 } 163 // BL runtime.read_tls_fallback(SB) 164 p->as = ABL; 165 p->to.type = D_BRANCH; 166 p->to.sym = tlsfallback; 167 p->cond = tlsfallback->text; 168 p->to.offset = 0; 169 cursym->text->mark &= ~LEAF; 170 } 171 if(linkmode == LinkExternal) { 172 // runtime.tlsgm is relocated with R_ARM_TLS_LE32 173 // and $runtime.tlsgm will contain the TLS offset. 174 // 175 // MOV $runtime.tlsgm+tlsoffset(SB), REGTMP 176 // ADD REGTMP, <reg> 177 // 178 // In shared mode, runtime.tlsgm is relocated with 179 // R_ARM_TLS_IE32 and runtime.tlsgm(SB) will point 180 // to the GOT entry containing the TLS offset. 181 // 182 // MOV runtime.tlsgm(SB), REGTMP 183 // ADD REGTMP, <reg> 184 // SUB -tlsoffset, <reg> 185 // 186 // The SUB compensates for tlsoffset 187 // used in runtime.save_gm and runtime.load_gm. 188 q = p; 189 p = appendp(p); 190 p->as = AMOVW; 191 p->scond = 14; 192 p->reg = NREG; 193 if(flag_shared) { 194 p->from.type = D_OREG; 195 p->from.offset = 0; 196 } else { 197 p->from.type = D_CONST; 198 p->from.offset = tlsoffset; 199 } 200 p->from.sym = gmsym; 201 p->from.name = D_EXTERN; 202 p->to.type = D_REG; 203 p->to.reg = REGTMP; 204 p->to.offset = 0; 205 206 p = appendp(p); 207 p->as = AADD; 208 p->scond = 14; 209 p->reg = NREG; 210 p->from.type = D_REG; 211 p->from.reg = REGTMP; 212 p->to.type = D_REG; 213 p->to.reg = (q->to.offset & 0xf000) >> 12; 214 p->to.offset = 0; 215 216 if(flag_shared) { 217 p = appendp(p); 218 p->as = ASUB; 219 p->scond = 14; 220 p->reg = NREG; 221 p->from.type = D_CONST; 222 p->from.offset = -tlsoffset; 223 p->to.type = D_REG; 224 p->to.reg = (q->to.offset & 0xf000) >> 12; 225 p->to.offset = 0; 226 } 227 } 228 } 229 } 230 q = p; 231 } 232 } 233 234 for(cursym = textp; cursym != nil; cursym = cursym->next) { 235 for(p = cursym->text; p != P; p = p->link) { 236 o = p->as; 237 switch(o) { 238 case ATEXT: 239 autosize = p->to.offset + 4; 240 if(autosize <= 4) 241 if(cursym->text->mark & LEAF) { 242 p->to.offset = -4; 243 autosize = 0; 244 } 245 246 if(!autosize && !(cursym->text->mark & LEAF)) { 247 if(debug['v']) 248 Bprint(&bso, "save suppressed in: %s\n", 249 cursym->name); 250 Bflush(&bso); 251 cursym->text->mark |= LEAF; 252 } 253 if(cursym->text->mark & LEAF) { 254 cursym->leaf = 1; 255 if(!autosize) 256 break; 257 } 258 259 if(!(p->reg & NOSPLIT)) { 260 // MOVW g_stackguard(g), R1 261 p = appendp(p); 262 p->as = AMOVW; 263 p->from.type = D_OREG; 264 p->from.reg = REGG; 265 p->to.type = D_REG; 266 p->to.reg = 1; 267 268 if(autosize <= StackSmall) { 269 // small stack: SP < stackguard 270 // CMP stackguard, SP 271 p = appendp(p); 272 p->as = ACMP; 273 p->from.type = D_REG; 274 p->from.reg = 1; 275 p->reg = REGSP; 276 } else if(autosize <= StackBig) { 277 // large stack: SP-framesize < stackguard-StackSmall 278 // MOVW $-autosize(SP), R2 279 // CMP stackguard, R2 280 p = appendp(p); 281 p->as = AMOVW; 282 p->from.type = D_CONST; 283 p->from.reg = REGSP; 284 p->from.offset = -autosize; 285 p->to.type = D_REG; 286 p->to.reg = 2; 287 288 p = appendp(p); 289 p->as = ACMP; 290 p->from.type = D_REG; 291 p->from.reg = 1; 292 p->reg = 2; 293 } else { 294 // Such a large stack we need to protect against wraparound 295 // if SP is close to zero. 296 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 297 // The +StackGuard on both sides is required to keep the left side positive: 298 // SP is allowed to be slightly below stackguard. See stack.h. 299 // CMP $StackPreempt, R1 300 // MOVW.NE $StackGuard(SP), R2 301 // SUB.NE R1, R2 302 // MOVW.NE $(autosize+(StackGuard-StackSmall)), R3 303 // CMP.NE R3, R2 304 p = appendp(p); 305 p->as = ACMP; 306 p->from.type = D_CONST; 307 p->from.offset = (uint32)StackPreempt; 308 p->reg = 1; 309 310 p = appendp(p); 311 p->as = AMOVW; 312 p->from.type = D_CONST; 313 p->from.reg = REGSP; 314 p->from.offset = StackGuard; 315 p->to.type = D_REG; 316 p->to.reg = 2; 317 p->scond = C_SCOND_NE; 318 319 p = appendp(p); 320 p->as = ASUB; 321 p->from.type = D_REG; 322 p->from.reg = 1; 323 p->to.type = D_REG; 324 p->to.reg = 2; 325 p->scond = C_SCOND_NE; 326 327 p = appendp(p); 328 p->as = AMOVW; 329 p->from.type = D_CONST; 330 p->from.offset = autosize + (StackGuard - StackSmall); 331 p->to.type = D_REG; 332 p->to.reg = 3; 333 p->scond = C_SCOND_NE; 334 335 p = appendp(p); 336 p->as = ACMP; 337 p->from.type = D_REG; 338 p->from.reg = 3; 339 p->reg = 2; 340 p->scond = C_SCOND_NE; 341 } 342 343 // MOVW.LS $autosize, R1 344 p = appendp(p); 345 p->as = AMOVW; 346 p->scond = C_SCOND_LS; 347 p->from.type = D_CONST; 348 p->from.offset = autosize; 349 p->to.type = D_REG; 350 p->to.reg = 1; 351 352 // MOVW.LS $args, R2 353 p = appendp(p); 354 p->as = AMOVW; 355 p->scond = C_SCOND_LS; 356 p->from.type = D_CONST; 357 arg = cursym->text->to.offset2; 358 if(arg == 1) // special marker for known 0 359 arg = 0; 360 if(arg&3) 361 diag("misaligned argument size in stack split"); 362 p->from.offset = arg; 363 p->to.type = D_REG; 364 p->to.reg = 2; 365 366 // MOVW.LS R14, R3 367 p = appendp(p); 368 p->as = AMOVW; 369 p->scond = C_SCOND_LS; 370 p->from.type = D_REG; 371 p->from.reg = REGLINK; 372 p->to.type = D_REG; 373 p->to.reg = 3; 374 375 // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted 376 p = appendp(p); 377 p->as = ABL; 378 p->scond = C_SCOND_LS; 379 p->to.type = D_BRANCH; 380 p->to.sym = symmorestack; 381 p->cond = pmorestack; 382 383 // BLS start 384 p = appendp(p); 385 p->as = ABLS; 386 p->to.type = D_BRANCH; 387 p->cond = cursym->text->link; 388 } 389 390 // MOVW.W R14,$-autosize(SP) 391 p = appendp(p); 392 p->as = AMOVW; 393 p->scond |= C_WBIT; 394 p->from.type = D_REG; 395 p->from.reg = REGLINK; 396 p->to.type = D_OREG; 397 p->to.offset = -autosize; 398 p->to.reg = REGSP; 399 p->spadj = autosize; 400 break; 401 402 case ARET: 403 nocache(p); 404 if(cursym->text->mark & LEAF) { 405 if(!autosize) { 406 p->as = AB; 407 p->from = zprg.from; 408 if(p->to.sym) { // retjmp 409 p->to.type = D_BRANCH; 410 p->cond = p->to.sym->text; 411 } else { 412 p->to.type = D_OREG; 413 p->to.offset = 0; 414 p->to.reg = REGLINK; 415 } 416 break; 417 } 418 } 419 p->as = AMOVW; 420 p->scond |= C_PBIT; 421 p->from.type = D_OREG; 422 p->from.offset = autosize; 423 p->from.reg = REGSP; 424 p->to.type = D_REG; 425 p->to.reg = REGPC; 426 // If there are instructions following 427 // this ARET, they come from a branch 428 // with the same stackframe, so no spadj. 429 430 if(p->to.sym) { // retjmp 431 p->to.reg = REGLINK; 432 q2 = appendp(p); 433 q2->as = AB; 434 q2->to.type = D_BRANCH; 435 q2->to.sym = p->to.sym; 436 q2->cond = p->to.sym->text; 437 p->to.sym = nil; 438 p = q2; 439 } 440 break; 441 442 case AADD: 443 if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP) 444 p->spadj = -p->from.offset; 445 break; 446 447 case ASUB: 448 if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP) 449 p->spadj = p->from.offset; 450 break; 451 452 case ADIV: 453 case ADIVU: 454 case AMOD: 455 case AMODU: 456 if(debug['M']) 457 break; 458 if(p->from.type != D_REG) 459 break; 460 if(p->to.type != D_REG) 461 break; 462 q1 = p; 463 464 /* MOV a,4(SP) */ 465 p = appendp(p); 466 p->as = AMOVW; 467 p->line = q1->line; 468 p->from.type = D_REG; 469 p->from.reg = q1->from.reg; 470 p->to.type = D_OREG; 471 p->to.reg = REGSP; 472 p->to.offset = 4; 473 474 /* MOV b,REGTMP */ 475 p = appendp(p); 476 p->as = AMOVW; 477 p->line = q1->line; 478 p->from.type = D_REG; 479 p->from.reg = q1->reg; 480 if(q1->reg == NREG) 481 p->from.reg = q1->to.reg; 482 p->to.type = D_REG; 483 p->to.reg = REGTMP; 484 p->to.offset = 0; 485 486 /* CALL appropriate */ 487 p = appendp(p); 488 p->as = ABL; 489 p->line = q1->line; 490 p->to.type = D_BRANCH; 491 p->cond = p; 492 switch(o) { 493 case ADIV: 494 p->cond = prog_div; 495 p->to.sym = sym_div; 496 break; 497 case ADIVU: 498 p->cond = prog_divu; 499 p->to.sym = sym_divu; 500 break; 501 case AMOD: 502 p->cond = prog_mod; 503 p->to.sym = sym_mod; 504 break; 505 case AMODU: 506 p->cond = prog_modu; 507 p->to.sym = sym_modu; 508 break; 509 } 510 511 /* MOV REGTMP, b */ 512 p = appendp(p); 513 p->as = AMOVW; 514 p->line = q1->line; 515 p->from.type = D_REG; 516 p->from.reg = REGTMP; 517 p->from.offset = 0; 518 p->to.type = D_REG; 519 p->to.reg = q1->to.reg; 520 521 /* ADD $8,SP */ 522 p = appendp(p); 523 p->as = AADD; 524 p->line = q1->line; 525 p->from.type = D_CONST; 526 p->from.reg = NREG; 527 p->from.offset = 8; 528 p->reg = NREG; 529 p->to.type = D_REG; 530 p->to.reg = REGSP; 531 p->spadj = -8; 532 533 /* SUB $8,SP */ 534 q1->as = ASUB; 535 q1->from.type = D_CONST; 536 q1->from.offset = 8; 537 q1->from.reg = NREG; 538 q1->reg = NREG; 539 q1->to.type = D_REG; 540 q1->to.reg = REGSP; 541 q1->spadj = 8; 542 543 break; 544 case AMOVW: 545 if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP) 546 p->spadj = -p->to.offset; 547 if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC) 548 p->spadj = -p->from.offset; 549 if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP) 550 p->spadj = -p->from.offset; 551 break; 552 } 553 } 554 } 555 } 556 557 static void 558 sigdiv(char *n) 559 { 560 Sym *s; 561 562 s = lookup(n, 0); 563 if(s->type == STEXT) 564 if(s->sig == 0) 565 s->sig = SIGNINTERN; 566 } 567 568 void 569 divsig(void) 570 { 571 sigdiv("_div"); 572 sigdiv("_divu"); 573 sigdiv("_mod"); 574 sigdiv("_modu"); 575 } 576 577 void 578 initdiv(void) 579 { 580 Sym *s2, *s3, *s4, *s5; 581 582 if(prog_div != P) 583 return; 584 sym_div = s2 = lookup("_div", 0); 585 sym_divu = s3 = lookup("_divu", 0); 586 sym_mod = s4 = lookup("_mod", 0); 587 sym_modu = s5 = lookup("_modu", 0); 588 prog_div = s2->text; 589 prog_divu = s3->text; 590 prog_mod = s4->text; 591 prog_modu = s5->text; 592 if(prog_div == P) { 593 diag("undefined: %s", s2->name); 594 prog_div = cursym->text; 595 } 596 if(prog_divu == P) { 597 diag("undefined: %s", s3->name); 598 prog_divu = cursym->text; 599 } 600 if(prog_mod == P) { 601 diag("undefined: %s", s4->name); 602 prog_mod = cursym->text; 603 } 604 if(prog_modu == P) { 605 diag("undefined: %s", s5->name); 606 prog_modu = cursym->text; 607 } 608 } 609 610 void 611 nocache(Prog *p) 612 { 613 p->optab = 0; 614 p->from.class = 0; 615 p->to.class = 0; 616 }