github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/liblink/obj5.c (about) 1 // Derived from Inferno utils/5c/swt.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.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 #include <u.h> 32 #include <libc.h> 33 #include <bio.h> 34 #include <link.h> 35 #include "../cmd/5l/5.out.h" 36 #include "../pkg/runtime/stack.h" 37 38 static Prog zprg = { 39 .as = AGOK, 40 .scond = C_SCOND_NONE, 41 .reg = NREG, 42 .from = { 43 .name = D_NONE, 44 .type = D_NONE, 45 .reg = NREG, 46 }, 47 .to = { 48 .name = D_NONE, 49 .type = D_NONE, 50 .reg = NREG, 51 }, 52 }; 53 54 static int 55 symtype(Addr *a) 56 { 57 return a->name; 58 } 59 60 static int 61 isdata(Prog *p) 62 { 63 return p->as == ADATA || p->as == AGLOBL; 64 } 65 66 static int 67 iscall(Prog *p) 68 { 69 return p->as == ABL; 70 } 71 72 static int 73 datasize(Prog *p) 74 { 75 return p->reg; 76 } 77 78 static int 79 textflag(Prog *p) 80 { 81 return p->reg; 82 } 83 84 static void 85 settextflag(Prog *p, int f) 86 { 87 p->reg = f; 88 } 89 90 static void 91 progedit(Link *ctxt, Prog *p) 92 { 93 char literal[64]; 94 LSym *s; 95 LSym *tlsfallback; 96 97 p->from.class = 0; 98 p->to.class = 0; 99 100 // Rewrite B/BL to symbol as D_BRANCH. 101 switch(p->as) { 102 case AB: 103 case ABL: 104 case ADUFFZERO: 105 case ADUFFCOPY: 106 if(p->to.type == D_OREG && (p->to.name == D_EXTERN || p->to.name == D_STATIC) && p->to.sym != nil) 107 p->to.type = D_BRANCH; 108 break; 109 } 110 111 // Replace TLS register fetches on older ARM procesors. 112 switch(p->as) { 113 case AMRC: 114 // If the instruction matches MRC 15, 0, <reg>, C13, C0, 3, replace it. 115 if(ctxt->goarm < 7 && (p->to.offset & 0xffff0fff) == 0xee1d0f70) { 116 tlsfallback = linklookup(ctxt, "runtime.read_tls_fallback", 0); 117 118 // BL runtime.read_tls_fallback(SB) 119 p->as = ABL; 120 p->to.type = D_BRANCH; 121 p->to.sym = tlsfallback; 122 p->to.offset = 0; 123 } else { 124 // Otherwise, MRC/MCR instructions need no further treatment. 125 p->as = AWORD; 126 } 127 break; 128 } 129 130 // Rewrite float constants to values stored in memory. 131 switch(p->as) { 132 case AMOVF: 133 if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval) < 0 && 134 (chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { 135 int32 i32; 136 float32 f32; 137 f32 = p->from.u.dval; 138 memmove(&i32, &f32, 4); 139 sprint(literal, "$f32.%08ux", (uint32)i32); 140 s = linklookup(ctxt, literal, 0); 141 if(s->type == 0) { 142 s->type = SRODATA; 143 adduint32(ctxt, s, i32); 144 s->reachable = 0; 145 } 146 p->from.type = D_OREG; 147 p->from.sym = s; 148 p->from.name = D_EXTERN; 149 p->from.offset = 0; 150 } 151 break; 152 153 case AMOVD: 154 if(p->from.type == D_FCONST && chipfloat5(ctxt, p->from.u.dval) < 0 && 155 (chipzero5(ctxt, p->from.u.dval) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) { 156 int64 i64; 157 memmove(&i64, &p->from.u.dval, 8); 158 sprint(literal, "$f64.%016llux", (uvlong)i64); 159 s = linklookup(ctxt, literal, 0); 160 if(s->type == 0) { 161 s->type = SRODATA; 162 adduint64(ctxt, s, i64); 163 s->reachable = 0; 164 } 165 p->from.type = D_OREG; 166 p->from.sym = s; 167 p->from.name = D_EXTERN; 168 p->from.offset = 0; 169 } 170 break; 171 } 172 173 if(ctxt->flag_shared) { 174 // Shared libraries use R_ARM_TLS_IE32 instead of 175 // R_ARM_TLS_LE32, replacing the link time constant TLS offset in 176 // runtime.tlsgm with an address to a GOT entry containing the 177 // offset. Rewrite $runtime.tlsgm(SB) to runtime.tlsgm(SB) to 178 // compensate. 179 if(ctxt->gmsym == nil) 180 ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); 181 182 if(p->from.type == D_CONST && p->from.name == D_EXTERN && p->from.sym == ctxt->gmsym) 183 p->from.type = D_OREG; 184 if(p->to.type == D_CONST && p->to.name == D_EXTERN && p->to.sym == ctxt->gmsym) 185 p->to.type = D_OREG; 186 } 187 } 188 189 static Prog* 190 prg(void) 191 { 192 Prog *p; 193 194 p = emallocz(sizeof(*p)); 195 *p = zprg; 196 return p; 197 } 198 199 static Prog* stacksplit(Link*, Prog*, int32, int); 200 static void initdiv(Link*); 201 static void softfloat(Link*, LSym*); 202 203 // Prog.mark 204 enum 205 { 206 FOLL = 1<<0, 207 LABEL = 1<<1, 208 LEAF = 1<<2, 209 }; 210 211 static void 212 linkcase(Prog *casep) 213 { 214 Prog *p; 215 216 for(p = casep; p != nil; p = p->link){ 217 if(p->as == ABCASE) { 218 for(; p != nil && p->as == ABCASE; p = p->link) 219 p->pcrel = casep; 220 break; 221 } 222 } 223 } 224 225 static void 226 nocache(Prog *p) 227 { 228 p->optab = 0; 229 p->from.class = 0; 230 p->to.class = 0; 231 } 232 233 static void 234 addstacksplit(Link *ctxt, LSym *cursym) 235 { 236 Prog *p, *pl, *q, *q1, *q2; 237 int o; 238 int32 autosize, autoffset; 239 240 autosize = 0; 241 242 if(ctxt->symmorestack[0] == nil) { 243 ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0); 244 ctxt->symmorestack[1] = linklookup(ctxt, "runtime.morestack_noctxt", 0); 245 } 246 247 q = nil; 248 249 ctxt->cursym = cursym; 250 251 if(cursym->text == nil || cursym->text->link == nil) 252 return; 253 254 softfloat(ctxt, cursym); 255 256 p = cursym->text; 257 autoffset = p->to.offset; 258 if(autoffset < 0) 259 autoffset = 0; 260 cursym->locals = autoffset; 261 cursym->args = p->to.offset2; 262 263 if(ctxt->debugzerostack) { 264 if(autoffset && !(p->reg&NOSPLIT)) { 265 // MOVW $4(R13), R1 266 p = appendp(ctxt, p); 267 p->as = AMOVW; 268 p->from.type = D_CONST; 269 p->from.reg = 13; 270 p->from.offset = 4; 271 p->to.type = D_REG; 272 p->to.reg = 1; 273 274 // MOVW $n(R13), R2 275 p = appendp(ctxt, p); 276 p->as = AMOVW; 277 p->from.type = D_CONST; 278 p->from.reg = 13; 279 p->from.offset = 4 + autoffset; 280 p->to.type = D_REG; 281 p->to.reg = 2; 282 283 // MOVW $0, R3 284 p = appendp(ctxt, p); 285 p->as = AMOVW; 286 p->from.type = D_CONST; 287 p->from.offset = 0; 288 p->to.type = D_REG; 289 p->to.reg = 3; 290 291 // L: 292 // MOVW.nil R3, 0(R1) +4 293 // CMP R1, R2 294 // BNE L 295 p = pl = appendp(ctxt, p); 296 p->as = AMOVW; 297 p->from.type = D_REG; 298 p->from.reg = 3; 299 p->to.type = D_OREG; 300 p->to.reg = 1; 301 p->to.offset = 4; 302 p->scond |= C_PBIT; 303 304 p = appendp(ctxt, p); 305 p->as = ACMP; 306 p->from.type = D_REG; 307 p->from.reg = 1; 308 p->reg = 2; 309 310 p = appendp(ctxt, p); 311 p->as = ABNE; 312 p->to.type = D_BRANCH; 313 p->pcond = pl; 314 } 315 } 316 317 /* 318 * find leaf subroutines 319 * strip NOPs 320 * expand RET 321 * expand BECOME pseudo 322 */ 323 324 for(p = cursym->text; p != nil; p = p->link) { 325 switch(p->as) { 326 case ACASE: 327 if(ctxt->flag_shared) 328 linkcase(p); 329 break; 330 331 case ATEXT: 332 p->mark |= LEAF; 333 break; 334 335 case ARET: 336 break; 337 338 case ADIV: 339 case ADIVU: 340 case AMOD: 341 case AMODU: 342 q = p; 343 if(ctxt->sym_div == nil) 344 initdiv(ctxt); 345 cursym->text->mark &= ~LEAF; 346 continue; 347 348 case ANOP: 349 q1 = p->link; 350 q->link = q1; /* q is non-nop */ 351 if(q1 != nil) 352 q1->mark |= p->mark; 353 continue; 354 355 case ABL: 356 case ABX: 357 case ADUFFZERO: 358 case ADUFFCOPY: 359 cursym->text->mark &= ~LEAF; 360 361 case ABCASE: 362 case AB: 363 364 case ABEQ: 365 case ABNE: 366 case ABCS: 367 case ABHS: 368 case ABCC: 369 case ABLO: 370 case ABMI: 371 case ABPL: 372 case ABVS: 373 case ABVC: 374 case ABHI: 375 case ABLS: 376 case ABGE: 377 case ABLT: 378 case ABGT: 379 case ABLE: 380 q1 = p->pcond; 381 if(q1 != nil) { 382 while(q1->as == ANOP) { 383 q1 = q1->link; 384 p->pcond = q1; 385 } 386 } 387 break; 388 } 389 q = p; 390 } 391 392 for(p = cursym->text; p != nil; p = p->link) { 393 o = p->as; 394 switch(o) { 395 case ATEXT: 396 autosize = p->to.offset + 4; 397 if(autosize <= 4) 398 if(cursym->text->mark & LEAF) { 399 p->to.offset = -4; 400 autosize = 0; 401 } 402 403 if(!autosize && !(cursym->text->mark & LEAF)) { 404 if(ctxt->debugvlog) { 405 Bprint(ctxt->bso, "save suppressed in: %s\n", 406 cursym->name); 407 Bflush(ctxt->bso); 408 } 409 cursym->text->mark |= LEAF; 410 } 411 if(cursym->text->mark & LEAF) { 412 cursym->leaf = 1; 413 if(!autosize) 414 break; 415 } 416 417 if(!(p->reg & NOSPLIT)) 418 p = stacksplit(ctxt, p, autosize, !(cursym->text->reg&NEEDCTXT)); // emit split check 419 420 // MOVW.W R14,$-autosize(SP) 421 p = appendp(ctxt, p); 422 p->as = AMOVW; 423 p->scond |= C_WBIT; 424 p->from.type = D_REG; 425 p->from.reg = REGLINK; 426 p->to.type = D_OREG; 427 p->to.offset = -autosize; 428 p->to.reg = REGSP; 429 p->spadj = autosize; 430 431 if(cursym->text->reg & WRAPPER) { 432 // g->panicwrap += autosize; 433 // MOVW panicwrap_offset(g), R3 434 // ADD $autosize, R3 435 // MOVW R3 panicwrap_offset(g) 436 p = appendp(ctxt, p); 437 p->as = AMOVW; 438 p->from.type = D_OREG; 439 p->from.reg = REGG; 440 p->from.offset = 2*ctxt->arch->ptrsize; 441 p->to.type = D_REG; 442 p->to.reg = 3; 443 444 p = appendp(ctxt, p); 445 p->as = AADD; 446 p->from.type = D_CONST; 447 p->from.offset = autosize; 448 p->to.type = D_REG; 449 p->to.reg = 3; 450 451 p = appendp(ctxt, p); 452 p->as = AMOVW; 453 p->from.type = D_REG; 454 p->from.reg = 3; 455 p->to.type = D_OREG; 456 p->to.reg = REGG; 457 p->to.offset = 2*ctxt->arch->ptrsize; 458 } 459 break; 460 461 case ARET: 462 nocache(p); 463 if(cursym->text->mark & LEAF) { 464 if(!autosize) { 465 p->as = AB; 466 p->from = zprg.from; 467 if(p->to.sym) { // retjmp 468 p->to.type = D_BRANCH; 469 } else { 470 p->to.type = D_OREG; 471 p->to.offset = 0; 472 p->to.reg = REGLINK; 473 } 474 break; 475 } 476 } 477 478 if(cursym->text->reg & WRAPPER) { 479 int scond; 480 481 // Preserve original RET's cond, to allow RET.EQ 482 // in the implementation of reflect.call. 483 scond = p->scond; 484 p->scond = C_SCOND_NONE; 485 486 // g->panicwrap -= autosize; 487 // MOVW panicwrap_offset(g), R3 488 // SUB $autosize, R3 489 // MOVW R3 panicwrap_offset(g) 490 p->as = AMOVW; 491 p->from.type = D_OREG; 492 p->from.reg = REGG; 493 p->from.offset = 2*ctxt->arch->ptrsize; 494 p->to.type = D_REG; 495 p->to.reg = 3; 496 p = appendp(ctxt, p); 497 498 p->as = ASUB; 499 p->from.type = D_CONST; 500 p->from.offset = autosize; 501 p->to.type = D_REG; 502 p->to.reg = 3; 503 p = appendp(ctxt, p); 504 505 p->as = AMOVW; 506 p->from.type = D_REG; 507 p->from.reg = 3; 508 p->to.type = D_OREG; 509 p->to.reg = REGG; 510 p->to.offset = 2*ctxt->arch->ptrsize; 511 p = appendp(ctxt, p); 512 513 p->scond = scond; 514 } 515 516 p->as = AMOVW; 517 p->scond |= C_PBIT; 518 p->from.type = D_OREG; 519 p->from.offset = autosize; 520 p->from.reg = REGSP; 521 p->to.type = D_REG; 522 p->to.reg = REGPC; 523 // If there are instructions following 524 // this ARET, they come from a branch 525 // with the same stackframe, so no spadj. 526 527 if(p->to.sym) { // retjmp 528 p->to.reg = REGLINK; 529 q2 = appendp(ctxt, p); 530 q2->as = AB; 531 q2->to.type = D_BRANCH; 532 q2->to.sym = p->to.sym; 533 p->to.sym = nil; 534 p = q2; 535 } 536 break; 537 538 case AADD: 539 if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP) 540 p->spadj = -p->from.offset; 541 break; 542 543 case ASUB: 544 if(p->from.type == D_CONST && p->from.reg == NREG && p->to.type == D_REG && p->to.reg == REGSP) 545 p->spadj = p->from.offset; 546 break; 547 548 case ADIV: 549 case ADIVU: 550 case AMOD: 551 case AMODU: 552 if(ctxt->debugdivmod) 553 break; 554 if(p->from.type != D_REG) 555 break; 556 if(p->to.type != D_REG) 557 break; 558 q1 = p; 559 560 /* MOV a,4(SP) */ 561 p = appendp(ctxt, p); 562 p->as = AMOVW; 563 p->lineno = q1->lineno; 564 p->from.type = D_REG; 565 p->from.reg = q1->from.reg; 566 p->to.type = D_OREG; 567 p->to.reg = REGSP; 568 p->to.offset = 4; 569 570 /* MOV b,REGTMP */ 571 p = appendp(ctxt, p); 572 p->as = AMOVW; 573 p->lineno = q1->lineno; 574 p->from.type = D_REG; 575 p->from.reg = q1->reg; 576 if(q1->reg == NREG) 577 p->from.reg = q1->to.reg; 578 p->to.type = D_REG; 579 p->to.reg = REGTMP; 580 p->to.offset = 0; 581 582 /* CALL appropriate */ 583 p = appendp(ctxt, p); 584 p->as = ABL; 585 p->lineno = q1->lineno; 586 p->to.type = D_BRANCH; 587 switch(o) { 588 case ADIV: 589 p->to.sym = ctxt->sym_div; 590 break; 591 case ADIVU: 592 p->to.sym = ctxt->sym_divu; 593 break; 594 case AMOD: 595 p->to.sym = ctxt->sym_mod; 596 break; 597 case AMODU: 598 p->to.sym = ctxt->sym_modu; 599 break; 600 } 601 602 /* MOV REGTMP, b */ 603 p = appendp(ctxt, p); 604 p->as = AMOVW; 605 p->lineno = q1->lineno; 606 p->from.type = D_REG; 607 p->from.reg = REGTMP; 608 p->from.offset = 0; 609 p->to.type = D_REG; 610 p->to.reg = q1->to.reg; 611 612 /* ADD $8,SP */ 613 p = appendp(ctxt, p); 614 p->as = AADD; 615 p->lineno = q1->lineno; 616 p->from.type = D_CONST; 617 p->from.reg = NREG; 618 p->from.offset = 8; 619 p->reg = NREG; 620 p->to.type = D_REG; 621 p->to.reg = REGSP; 622 p->spadj = -8; 623 624 /* Keep saved LR at 0(SP) after SP change. */ 625 /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */ 626 /* TODO: Remove SP adjustments; see issue 6699. */ 627 q1->as = AMOVW; 628 q1->from.type = D_OREG; 629 q1->from.reg = REGSP; 630 q1->from.offset = 0; 631 q1->reg = NREG; 632 q1->to.type = D_REG; 633 q1->to.reg = REGTMP; 634 635 /* SUB $8,SP */ 636 q1 = appendp(ctxt, q1); 637 q1->as = AMOVW; 638 q1->from.type = D_REG; 639 q1->from.reg = REGTMP; 640 q1->reg = NREG; 641 q1->to.type = D_OREG; 642 q1->to.reg = REGSP; 643 q1->to.offset = -8; 644 q1->scond |= C_WBIT; 645 q1->spadj = 8; 646 647 break; 648 case AMOVW: 649 if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP) 650 p->spadj = -p->to.offset; 651 if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC) 652 p->spadj = -p->from.offset; 653 if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP) 654 p->spadj = -p->from.offset; 655 break; 656 } 657 } 658 } 659 660 static void 661 softfloat(Link *ctxt, LSym *cursym) 662 { 663 Prog *p, *next; 664 LSym *symsfloat; 665 int wasfloat; 666 667 if(ctxt->goarm > 5) 668 return; 669 670 symsfloat = linklookup(ctxt, "_sfloat", 0); 671 672 wasfloat = 0; 673 for(p = cursym->text; p != nil; p = p->link) 674 if(p->pcond != nil) 675 p->pcond->mark |= LABEL; 676 for(p = cursym->text; p != nil; p = p->link) { 677 switch(p->as) { 678 case AMOVW: 679 if(p->to.type == D_FREG || p->from.type == D_FREG) 680 goto soft; 681 goto notsoft; 682 683 case AMOVWD: 684 case AMOVWF: 685 case AMOVDW: 686 case AMOVFW: 687 case AMOVFD: 688 case AMOVDF: 689 case AMOVF: 690 case AMOVD: 691 692 case ACMPF: 693 case ACMPD: 694 case AADDF: 695 case AADDD: 696 case ASUBF: 697 case ASUBD: 698 case AMULF: 699 case AMULD: 700 case ADIVF: 701 case ADIVD: 702 case ASQRTF: 703 case ASQRTD: 704 case AABSF: 705 case AABSD: 706 goto soft; 707 708 default: 709 goto notsoft; 710 711 soft: 712 if (!wasfloat || (p->mark&LABEL)) { 713 next = ctxt->arch->prg(); 714 *next = *p; 715 716 // BL _sfloat(SB) 717 *p = zprg; 718 p->link = next; 719 p->as = ABL; 720 p->to.type = D_BRANCH; 721 p->to.sym = symsfloat; 722 p->lineno = next->lineno; 723 724 p = next; 725 wasfloat = 1; 726 } 727 break; 728 729 notsoft: 730 wasfloat = 0; 731 } 732 } 733 } 734 735 static Prog* 736 stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt) 737 { 738 int32 arg; 739 740 // MOVW g_stackguard(g), R1 741 p = appendp(ctxt, p); 742 p->as = AMOVW; 743 p->from.type = D_OREG; 744 p->from.reg = REGG; 745 p->to.type = D_REG; 746 p->to.reg = 1; 747 748 if(framesize <= StackSmall) { 749 // small stack: SP < stackguard 750 // CMP stackguard, SP 751 p = appendp(ctxt, p); 752 p->as = ACMP; 753 p->from.type = D_REG; 754 p->from.reg = 1; 755 p->reg = REGSP; 756 } else if(framesize <= StackBig) { 757 // large stack: SP-framesize < stackguard-StackSmall 758 // MOVW $-framesize(SP), R2 759 // CMP stackguard, R2 760 p = appendp(ctxt, p); 761 p->as = AMOVW; 762 p->from.type = D_CONST; 763 p->from.reg = REGSP; 764 p->from.offset = -framesize; 765 p->to.type = D_REG; 766 p->to.reg = 2; 767 768 p = appendp(ctxt, p); 769 p->as = ACMP; 770 p->from.type = D_REG; 771 p->from.reg = 1; 772 p->reg = 2; 773 } else { 774 // Such a large stack we need to protect against wraparound 775 // if SP is close to zero. 776 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 777 // The +StackGuard on both sides is required to keep the left side positive: 778 // SP is allowed to be slightly below stackguard. See stack.h. 779 // CMP $StackPreempt, R1 780 // MOVW.NE $StackGuard(SP), R2 781 // SUB.NE R1, R2 782 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 783 // CMP.NE R3, R2 784 p = appendp(ctxt, p); 785 p->as = ACMP; 786 p->from.type = D_CONST; 787 p->from.offset = (uint32)StackPreempt; 788 p->reg = 1; 789 790 p = appendp(ctxt, p); 791 p->as = AMOVW; 792 p->from.type = D_CONST; 793 p->from.reg = REGSP; 794 p->from.offset = StackGuard; 795 p->to.type = D_REG; 796 p->to.reg = 2; 797 p->scond = C_SCOND_NE; 798 799 p = appendp(ctxt, p); 800 p->as = ASUB; 801 p->from.type = D_REG; 802 p->from.reg = 1; 803 p->to.type = D_REG; 804 p->to.reg = 2; 805 p->scond = C_SCOND_NE; 806 807 p = appendp(ctxt, p); 808 p->as = AMOVW; 809 p->from.type = D_CONST; 810 p->from.offset = framesize + (StackGuard - StackSmall); 811 p->to.type = D_REG; 812 p->to.reg = 3; 813 p->scond = C_SCOND_NE; 814 815 p = appendp(ctxt, p); 816 p->as = ACMP; 817 p->from.type = D_REG; 818 p->from.reg = 3; 819 p->reg = 2; 820 p->scond = C_SCOND_NE; 821 } 822 823 // MOVW.LS $framesize, R1 824 p = appendp(ctxt, p); 825 p->as = AMOVW; 826 p->scond = C_SCOND_LS; 827 p->from.type = D_CONST; 828 p->from.offset = framesize; 829 p->to.type = D_REG; 830 p->to.reg = 1; 831 832 // MOVW.LS $args, R2 833 p = appendp(ctxt, p); 834 p->as = AMOVW; 835 p->scond = C_SCOND_LS; 836 p->from.type = D_CONST; 837 arg = ctxt->cursym->text->to.offset2; 838 if(arg == 1) // special marker for known 0 839 arg = 0; 840 if(arg&3) 841 ctxt->diag("misaligned argument size in stack split"); 842 p->from.offset = arg; 843 p->to.type = D_REG; 844 p->to.reg = 2; 845 846 // MOVW.LS R14, R3 847 p = appendp(ctxt, p); 848 p->as = AMOVW; 849 p->scond = C_SCOND_LS; 850 p->from.type = D_REG; 851 p->from.reg = REGLINK; 852 p->to.type = D_REG; 853 p->to.reg = 3; 854 855 // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted 856 p = appendp(ctxt, p); 857 p->as = ABL; 858 p->scond = C_SCOND_LS; 859 p->to.type = D_BRANCH; 860 p->to.sym = ctxt->symmorestack[noctxt]; 861 862 // BLS start 863 p = appendp(ctxt, p); 864 p->as = ABLS; 865 p->to.type = D_BRANCH; 866 p->pcond = ctxt->cursym->text->link; 867 868 return p; 869 } 870 871 static void 872 initdiv(Link *ctxt) 873 { 874 if(ctxt->sym_div != nil) 875 return; 876 ctxt->sym_div = linklookup(ctxt, "_div", 0); 877 ctxt->sym_divu = linklookup(ctxt, "_divu", 0); 878 ctxt->sym_mod = linklookup(ctxt, "_mod", 0); 879 ctxt->sym_modu = linklookup(ctxt, "_modu", 0); 880 } 881 882 static void xfol(Link*, Prog*, Prog**); 883 884 static void 885 follow(Link *ctxt, LSym *s) 886 { 887 Prog *firstp, *lastp; 888 889 ctxt->cursym = s; 890 891 firstp = ctxt->arch->prg(); 892 lastp = firstp; 893 xfol(ctxt, s->text, &lastp); 894 lastp->link = nil; 895 s->text = firstp->link; 896 } 897 898 static int 899 relinv(int a) 900 { 901 switch(a) { 902 case ABEQ: return ABNE; 903 case ABNE: return ABEQ; 904 case ABCS: return ABCC; 905 case ABHS: return ABLO; 906 case ABCC: return ABCS; 907 case ABLO: return ABHS; 908 case ABMI: return ABPL; 909 case ABPL: return ABMI; 910 case ABVS: return ABVC; 911 case ABVC: return ABVS; 912 case ABHI: return ABLS; 913 case ABLS: return ABHI; 914 case ABGE: return ABLT; 915 case ABLT: return ABGE; 916 case ABGT: return ABLE; 917 case ABLE: return ABGT; 918 } 919 sysfatal("unknown relation: %s", anames5[a]); 920 return 0; 921 } 922 923 static void 924 xfol(Link *ctxt, Prog *p, Prog **last) 925 { 926 Prog *q, *r; 927 int a, i; 928 929 loop: 930 if(p == nil) 931 return; 932 a = p->as; 933 if(a == AB) { 934 q = p->pcond; 935 if(q != nil && q->as != ATEXT) { 936 p->mark |= FOLL; 937 p = q; 938 if(!(p->mark & FOLL)) 939 goto loop; 940 } 941 } 942 if(p->mark & FOLL) { 943 for(i=0,q=p; i<4; i++,q=q->link) { 944 if(q == *last || q == nil) 945 break; 946 a = q->as; 947 if(a == ANOP) { 948 i--; 949 continue; 950 } 951 if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF) 952 goto copy; 953 if(q->pcond == nil || (q->pcond->mark&FOLL)) 954 continue; 955 if(a != ABEQ && a != ABNE) 956 continue; 957 copy: 958 for(;;) { 959 r = ctxt->arch->prg(); 960 *r = *p; 961 if(!(r->mark&FOLL)) 962 print("can't happen 1\n"); 963 r->mark |= FOLL; 964 if(p != q) { 965 p = p->link; 966 (*last)->link = r; 967 *last = r; 968 continue; 969 } 970 (*last)->link = r; 971 *last = r; 972 if(a == AB || (a == ARET && q->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF) 973 return; 974 r->as = ABNE; 975 if(a == ABNE) 976 r->as = ABEQ; 977 r->pcond = p->link; 978 r->link = p->pcond; 979 if(!(r->link->mark&FOLL)) 980 xfol(ctxt, r->link, last); 981 if(!(r->pcond->mark&FOLL)) 982 print("can't happen 2\n"); 983 return; 984 } 985 } 986 a = AB; 987 q = ctxt->arch->prg(); 988 q->as = a; 989 q->lineno = p->lineno; 990 q->to.type = D_BRANCH; 991 q->to.offset = p->pc; 992 q->pcond = p; 993 p = q; 994 } 995 p->mark |= FOLL; 996 (*last)->link = p; 997 *last = p; 998 if(a == AB || (a == ARET && p->scond == C_SCOND_NONE) || a == ARFE || a == AUNDEF){ 999 return; 1000 } 1001 if(p->pcond != nil) 1002 if(a != ABL && a != ABX && p->link != nil) { 1003 q = brchain(ctxt, p->link); 1004 if(a != ATEXT && a != ABCASE) 1005 if(q != nil && (q->mark&FOLL)) { 1006 p->as = relinv(a); 1007 p->link = p->pcond; 1008 p->pcond = q; 1009 } 1010 xfol(ctxt, p->link, last); 1011 q = brchain(ctxt, p->pcond); 1012 if(q == nil) 1013 q = p->pcond; 1014 if(q->mark&FOLL) { 1015 p->pcond = q; 1016 return; 1017 } 1018 p = q; 1019 goto loop; 1020 } 1021 p = p->link; 1022 goto loop; 1023 } 1024 1025 LinkArch linkarm = { 1026 .name = "arm", 1027 .thechar = '5', 1028 1029 .addstacksplit = addstacksplit, 1030 .assemble = span5, 1031 .datasize = datasize, 1032 .follow = follow, 1033 .iscall = iscall, 1034 .isdata = isdata, 1035 .prg = prg, 1036 .progedit = progedit, 1037 .settextflag = settextflag, 1038 .symtype = symtype, 1039 .textflag = textflag, 1040 1041 .minlc = 4, 1042 .ptrsize = 4, 1043 .regsize = 4, 1044 1045 .D_ADDR = D_ADDR, 1046 .D_AUTO = D_AUTO, 1047 .D_BRANCH = D_BRANCH, 1048 .D_CONST = D_CONST, 1049 .D_EXTERN = D_EXTERN, 1050 .D_FCONST = D_FCONST, 1051 .D_NONE = D_NONE, 1052 .D_PARAM = D_PARAM, 1053 .D_SCONST = D_SCONST, 1054 .D_STATIC = D_STATIC, 1055 1056 .ACALL = ABL, 1057 .ADATA = ADATA, 1058 .AEND = AEND, 1059 .AFUNCDATA = AFUNCDATA, 1060 .AGLOBL = AGLOBL, 1061 .AJMP = AB, 1062 .ANOP = ANOP, 1063 .APCDATA = APCDATA, 1064 .ARET = ARET, 1065 .ATEXT = ATEXT, 1066 .ATYPE = ATYPE, 1067 .AUSEFIELD = AUSEFIELD, 1068 };