github.com/huandu/go@v0.0.0-20151114150818-04e615e41150/src/cmd/internal/obj/arm/obj5.go (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 package arm 32 33 import ( 34 "cmd/internal/obj" 35 "encoding/binary" 36 "fmt" 37 "log" 38 "math" 39 ) 40 41 var progedit_tlsfallback *obj.LSym 42 43 func progedit(ctxt *obj.Link, p *obj.Prog) { 44 p.From.Class = 0 45 p.To.Class = 0 46 47 // Rewrite B/BL to symbol as TYPE_BRANCH. 48 switch p.As { 49 case AB, 50 ABL, 51 obj.ADUFFZERO, 52 obj.ADUFFCOPY: 53 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { 54 p.To.Type = obj.TYPE_BRANCH 55 } 56 } 57 58 // Replace TLS register fetches on older ARM procesors. 59 switch p.As { 60 // Treat MRC 15, 0, <reg>, C13, C0, 3 specially. 61 case AMRC: 62 if p.To.Offset&0xffff0fff == 0xee1d0f70 { 63 // Because the instruction might be rewriten to a BL which returns in R0 64 // the register must be zero. 65 if p.To.Offset&0xf000 != 0 { 66 ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) 67 } 68 69 if ctxt.Goarm < 7 { 70 // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. 71 if progedit_tlsfallback == nil { 72 progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0) 73 } 74 75 // MOVW LR, R11 76 p.As = AMOVW 77 78 p.From.Type = obj.TYPE_REG 79 p.From.Reg = REGLINK 80 p.To.Type = obj.TYPE_REG 81 p.To.Reg = REGTMP 82 83 // BL runtime.read_tls_fallback(SB) 84 p = obj.Appendp(ctxt, p) 85 86 p.As = ABL 87 p.To.Type = obj.TYPE_BRANCH 88 p.To.Sym = progedit_tlsfallback 89 p.To.Offset = 0 90 91 // MOVW R11, LR 92 p = obj.Appendp(ctxt, p) 93 94 p.As = AMOVW 95 p.From.Type = obj.TYPE_REG 96 p.From.Reg = REGTMP 97 p.To.Type = obj.TYPE_REG 98 p.To.Reg = REGLINK 99 break 100 } 101 } 102 103 // Otherwise, MRC/MCR instructions need no further treatment. 104 p.As = AWORD 105 } 106 107 // Rewrite float constants to values stored in memory. 108 switch p.As { 109 case AMOVF: 110 if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { 111 f32 := float32(p.From.Val.(float64)) 112 i32 := math.Float32bits(f32) 113 literal := fmt.Sprintf("$f32.%08x", i32) 114 s := obj.Linklookup(ctxt, literal, 0) 115 p.From.Type = obj.TYPE_MEM 116 p.From.Sym = s 117 p.From.Name = obj.NAME_EXTERN 118 p.From.Offset = 0 119 } 120 121 case AMOVD: 122 if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { 123 i64 := math.Float64bits(p.From.Val.(float64)) 124 literal := fmt.Sprintf("$f64.%016x", i64) 125 s := obj.Linklookup(ctxt, literal, 0) 126 p.From.Type = obj.TYPE_MEM 127 p.From.Sym = s 128 p.From.Name = obj.NAME_EXTERN 129 p.From.Offset = 0 130 } 131 } 132 133 if ctxt.Flag_shared != 0 { 134 // Shared libraries use R_ARM_TLS_IE32 instead of 135 // R_ARM_TLS_LE32, replacing the link time constant TLS offset in 136 // runtime.tlsg with an address to a GOT entry containing the 137 // offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to 138 // compensate. 139 if ctxt.Tlsg == nil { 140 ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0) 141 } 142 143 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && p.From.Sym == ctxt.Tlsg { 144 p.From.Type = obj.TYPE_MEM 145 } 146 if p.To.Type == obj.TYPE_ADDR && p.To.Name == obj.NAME_EXTERN && p.To.Sym == ctxt.Tlsg { 147 p.To.Type = obj.TYPE_MEM 148 } 149 } 150 } 151 152 // Prog.mark 153 const ( 154 FOLL = 1 << 0 155 LABEL = 1 << 1 156 LEAF = 1 << 2 157 ) 158 159 func linkcase(casep *obj.Prog) { 160 for p := casep; p != nil; p = p.Link { 161 if p.As == ABCASE { 162 for ; p != nil && p.As == ABCASE; p = p.Link { 163 p.Rel = casep 164 } 165 break 166 } 167 } 168 } 169 170 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 171 autosize := int32(0) 172 173 ctxt.Cursym = cursym 174 175 if cursym.Text == nil || cursym.Text.Link == nil { 176 return 177 } 178 179 softfloat(ctxt, cursym) 180 181 p := cursym.Text 182 autoffset := int32(p.To.Offset) 183 if autoffset < 0 { 184 autoffset = 0 185 } 186 cursym.Locals = autoffset 187 cursym.Args = p.To.Val.(int32) 188 189 if ctxt.Debugzerostack != 0 { 190 if autoffset != 0 && p.From3.Offset&obj.NOSPLIT == 0 { 191 // MOVW $4(R13), R1 192 p = obj.Appendp(ctxt, p) 193 194 p.As = AMOVW 195 p.From.Type = obj.TYPE_ADDR 196 p.From.Reg = REG_R13 197 p.From.Offset = 4 198 p.To.Type = obj.TYPE_REG 199 p.To.Reg = REG_R1 200 201 // MOVW $n(R13), R2 202 p = obj.Appendp(ctxt, p) 203 204 p.As = AMOVW 205 p.From.Type = obj.TYPE_ADDR 206 p.From.Reg = REG_R13 207 p.From.Offset = 4 + int64(autoffset) 208 p.To.Type = obj.TYPE_REG 209 p.To.Reg = REG_R2 210 211 // MOVW $0, R3 212 p = obj.Appendp(ctxt, p) 213 214 p.As = AMOVW 215 p.From.Type = obj.TYPE_CONST 216 p.From.Offset = 0 217 p.To.Type = obj.TYPE_REG 218 p.To.Reg = REG_R3 219 220 // L: 221 // MOVW.nil R3, 0(R1) +4 222 // CMP R1, R2 223 // BNE L 224 pl := obj.Appendp(ctxt, p) 225 p := pl 226 227 p.As = AMOVW 228 p.From.Type = obj.TYPE_REG 229 p.From.Reg = REG_R3 230 p.To.Type = obj.TYPE_MEM 231 p.To.Reg = REG_R1 232 p.To.Offset = 4 233 p.Scond |= C_PBIT 234 235 p = obj.Appendp(ctxt, p) 236 p.As = ACMP 237 p.From.Type = obj.TYPE_REG 238 p.From.Reg = REG_R1 239 p.Reg = REG_R2 240 241 p = obj.Appendp(ctxt, p) 242 p.As = ABNE 243 p.To.Type = obj.TYPE_BRANCH 244 p.Pcond = pl 245 } 246 } 247 248 /* 249 * find leaf subroutines 250 * strip NOPs 251 * expand RET 252 * expand BECOME pseudo 253 */ 254 var q1 *obj.Prog 255 var q *obj.Prog 256 for p := cursym.Text; p != nil; p = p.Link { 257 switch p.As { 258 case ACASE: 259 if ctxt.Flag_shared != 0 { 260 linkcase(p) 261 } 262 263 case obj.ATEXT: 264 p.Mark |= LEAF 265 266 case obj.ARET: 267 break 268 269 case ADIV, ADIVU, AMOD, AMODU: 270 q = p 271 if ctxt.Sym_div == nil { 272 initdiv(ctxt) 273 } 274 cursym.Text.Mark &^= LEAF 275 continue 276 277 case obj.ANOP: 278 q1 = p.Link 279 q.Link = q1 /* q is non-nop */ 280 if q1 != nil { 281 q1.Mark |= p.Mark 282 } 283 continue 284 285 case ABL, 286 ABX, 287 obj.ADUFFZERO, 288 obj.ADUFFCOPY: 289 cursym.Text.Mark &^= LEAF 290 fallthrough 291 292 case ABCASE, 293 AB, 294 ABEQ, 295 ABNE, 296 ABCS, 297 ABHS, 298 ABCC, 299 ABLO, 300 ABMI, 301 ABPL, 302 ABVS, 303 ABVC, 304 ABHI, 305 ABLS, 306 ABGE, 307 ABLT, 308 ABGT, 309 ABLE: 310 q1 = p.Pcond 311 if q1 != nil { 312 for q1.As == obj.ANOP { 313 q1 = q1.Link 314 p.Pcond = q1 315 } 316 } 317 } 318 319 q = p 320 } 321 322 var o int 323 var p1 *obj.Prog 324 var p2 *obj.Prog 325 var q2 *obj.Prog 326 for p := cursym.Text; p != nil; p = p.Link { 327 o = int(p.As) 328 switch o { 329 case obj.ATEXT: 330 autosize = int32(p.To.Offset + 4) 331 if autosize <= 4 { 332 if cursym.Text.Mark&LEAF != 0 { 333 p.To.Offset = -4 334 autosize = 0 335 } 336 } 337 338 if autosize == 0 && cursym.Text.Mark&LEAF == 0 { 339 if ctxt.Debugvlog != 0 { 340 fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) 341 ctxt.Bso.Flush() 342 } 343 344 cursym.Text.Mark |= LEAF 345 } 346 347 if cursym.Text.Mark&LEAF != 0 { 348 cursym.Leaf = 1 349 if autosize == 0 { 350 break 351 } 352 } 353 354 if p.From3.Offset&obj.NOSPLIT == 0 { 355 p = stacksplit(ctxt, p, autosize) // emit split check 356 } 357 358 // MOVW.W R14,$-autosize(SP) 359 p = obj.Appendp(ctxt, p) 360 361 p.As = AMOVW 362 p.Scond |= C_WBIT 363 p.From.Type = obj.TYPE_REG 364 p.From.Reg = REGLINK 365 p.To.Type = obj.TYPE_MEM 366 p.To.Offset = int64(-autosize) 367 p.To.Reg = REGSP 368 p.Spadj = autosize 369 370 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 371 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 372 // 373 // MOVW g_panic(g), R1 374 // CMP $0, R1 375 // B.EQ end 376 // MOVW panic_argp(R1), R2 377 // ADD $(autosize+4), R13, R3 378 // CMP R2, R3 379 // B.NE end 380 // ADD $4, R13, R4 381 // MOVW R4, panic_argp(R1) 382 // end: 383 // NOP 384 // 385 // The NOP is needed to give the jumps somewhere to land. 386 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. 387 388 p = obj.Appendp(ctxt, p) 389 390 p.As = AMOVW 391 p.From.Type = obj.TYPE_MEM 392 p.From.Reg = REGG 393 p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic 394 p.To.Type = obj.TYPE_REG 395 p.To.Reg = REG_R1 396 397 p = obj.Appendp(ctxt, p) 398 p.As = ACMP 399 p.From.Type = obj.TYPE_CONST 400 p.From.Offset = 0 401 p.Reg = REG_R1 402 403 p = obj.Appendp(ctxt, p) 404 p.As = ABEQ 405 p.To.Type = obj.TYPE_BRANCH 406 p1 = p 407 408 p = obj.Appendp(ctxt, p) 409 p.As = AMOVW 410 p.From.Type = obj.TYPE_MEM 411 p.From.Reg = REG_R1 412 p.From.Offset = 0 // Panic.argp 413 p.To.Type = obj.TYPE_REG 414 p.To.Reg = REG_R2 415 416 p = obj.Appendp(ctxt, p) 417 p.As = AADD 418 p.From.Type = obj.TYPE_CONST 419 p.From.Offset = int64(autosize) + 4 420 p.Reg = REG_R13 421 p.To.Type = obj.TYPE_REG 422 p.To.Reg = REG_R3 423 424 p = obj.Appendp(ctxt, p) 425 p.As = ACMP 426 p.From.Type = obj.TYPE_REG 427 p.From.Reg = REG_R2 428 p.Reg = REG_R3 429 430 p = obj.Appendp(ctxt, p) 431 p.As = ABNE 432 p.To.Type = obj.TYPE_BRANCH 433 p2 = p 434 435 p = obj.Appendp(ctxt, p) 436 p.As = AADD 437 p.From.Type = obj.TYPE_CONST 438 p.From.Offset = 4 439 p.Reg = REG_R13 440 p.To.Type = obj.TYPE_REG 441 p.To.Reg = REG_R4 442 443 p = obj.Appendp(ctxt, p) 444 p.As = AMOVW 445 p.From.Type = obj.TYPE_REG 446 p.From.Reg = REG_R4 447 p.To.Type = obj.TYPE_MEM 448 p.To.Reg = REG_R1 449 p.To.Offset = 0 // Panic.argp 450 451 p = obj.Appendp(ctxt, p) 452 453 p.As = obj.ANOP 454 p1.Pcond = p 455 p2.Pcond = p 456 } 457 458 case obj.ARET: 459 obj.Nocache(p) 460 if cursym.Text.Mark&LEAF != 0 { 461 if autosize == 0 { 462 p.As = AB 463 p.From = obj.Addr{} 464 if p.To.Sym != nil { // retjmp 465 p.To.Type = obj.TYPE_BRANCH 466 } else { 467 p.To.Type = obj.TYPE_MEM 468 p.To.Offset = 0 469 p.To.Reg = REGLINK 470 } 471 472 break 473 } 474 } 475 476 p.As = AMOVW 477 p.Scond |= C_PBIT 478 p.From.Type = obj.TYPE_MEM 479 p.From.Offset = int64(autosize) 480 p.From.Reg = REGSP 481 p.To.Type = obj.TYPE_REG 482 p.To.Reg = REGPC 483 484 // If there are instructions following 485 // this ARET, they come from a branch 486 // with the same stackframe, so no spadj. 487 if p.To.Sym != nil { // retjmp 488 p.To.Reg = REGLINK 489 q2 = obj.Appendp(ctxt, p) 490 q2.As = AB 491 q2.To.Type = obj.TYPE_BRANCH 492 q2.To.Sym = p.To.Sym 493 p.To.Sym = nil 494 p = q2 495 } 496 497 case AADD: 498 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 499 p.Spadj = int32(-p.From.Offset) 500 } 501 502 case ASUB: 503 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 504 p.Spadj = int32(p.From.Offset) 505 } 506 507 case ADIV, ADIVU, AMOD, AMODU: 508 if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { 509 ctxt.Diag("cannot divide in NOSPLIT function") 510 } 511 if ctxt.Debugdivmod != 0 { 512 break 513 } 514 if p.From.Type != obj.TYPE_REG { 515 break 516 } 517 if p.To.Type != obj.TYPE_REG { 518 break 519 } 520 521 // Make copy because we overwrite p below. 522 q1 := *p 523 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { 524 ctxt.Diag("div already using REGTMP: %v", p) 525 } 526 527 /* MOV m(g),REGTMP */ 528 p.As = AMOVW 529 p.Lineno = q1.Lineno 530 p.From.Type = obj.TYPE_MEM 531 p.From.Reg = REGG 532 p.From.Offset = 6 * 4 // offset of g.m 533 p.Reg = 0 534 p.To.Type = obj.TYPE_REG 535 p.To.Reg = REGTMP 536 537 /* MOV a,m_divmod(REGTMP) */ 538 p = obj.Appendp(ctxt, p) 539 p.As = AMOVW 540 p.Lineno = q1.Lineno 541 p.From.Type = obj.TYPE_REG 542 p.From.Reg = q1.From.Reg 543 p.To.Type = obj.TYPE_MEM 544 p.To.Reg = REGTMP 545 p.To.Offset = 8 * 4 // offset of m.divmod 546 547 /* MOV b,REGTMP */ 548 p = obj.Appendp(ctxt, p) 549 p.As = AMOVW 550 p.Lineno = q1.Lineno 551 p.From.Type = obj.TYPE_REG 552 p.From.Reg = q1.Reg 553 if q1.Reg == 0 { 554 p.From.Reg = q1.To.Reg 555 } 556 p.To.Type = obj.TYPE_REG 557 p.To.Reg = REGTMP 558 p.To.Offset = 0 559 560 /* CALL appropriate */ 561 p = obj.Appendp(ctxt, p) 562 p.As = ABL 563 p.Lineno = q1.Lineno 564 p.To.Type = obj.TYPE_BRANCH 565 switch o { 566 case ADIV: 567 p.To.Sym = ctxt.Sym_div 568 569 case ADIVU: 570 p.To.Sym = ctxt.Sym_divu 571 572 case AMOD: 573 p.To.Sym = ctxt.Sym_mod 574 575 case AMODU: 576 p.To.Sym = ctxt.Sym_modu 577 } 578 579 /* MOV REGTMP, b */ 580 p = obj.Appendp(ctxt, p) 581 p.As = AMOVW 582 p.Lineno = q1.Lineno 583 p.From.Type = obj.TYPE_REG 584 p.From.Reg = REGTMP 585 p.From.Offset = 0 586 p.To.Type = obj.TYPE_REG 587 p.To.Reg = q1.To.Reg 588 589 case AMOVW: 590 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 591 p.Spadj = int32(-p.To.Offset) 592 } 593 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { 594 p.Spadj = int32(-p.From.Offset) 595 } 596 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 597 p.Spadj = int32(-p.From.Offset) 598 } 599 } 600 } 601 } 602 603 func isfloatreg(a *obj.Addr) bool { 604 return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15 605 } 606 607 func softfloat(ctxt *obj.Link, cursym *obj.LSym) { 608 if ctxt.Goarm > 5 { 609 return 610 } 611 612 symsfloat := obj.Linklookup(ctxt, "_sfloat", 0) 613 614 wasfloat := 0 615 for p := cursym.Text; p != nil; p = p.Link { 616 if p.Pcond != nil { 617 p.Pcond.Mark |= LABEL 618 } 619 } 620 var next *obj.Prog 621 for p := cursym.Text; p != nil; p = p.Link { 622 switch p.As { 623 case AMOVW: 624 if isfloatreg(&p.To) || isfloatreg(&p.From) { 625 goto soft 626 } 627 goto notsoft 628 629 case AMOVWD, 630 AMOVWF, 631 AMOVDW, 632 AMOVFW, 633 AMOVFD, 634 AMOVDF, 635 AMOVF, 636 AMOVD, 637 ACMPF, 638 ACMPD, 639 AADDF, 640 AADDD, 641 ASUBF, 642 ASUBD, 643 AMULF, 644 AMULD, 645 ADIVF, 646 ADIVD, 647 ASQRTF, 648 ASQRTD, 649 AABSF, 650 AABSD: 651 goto soft 652 653 default: 654 goto notsoft 655 } 656 657 soft: 658 if wasfloat == 0 || (p.Mark&LABEL != 0) { 659 next = ctxt.NewProg() 660 *next = *p 661 662 // BL _sfloat(SB) 663 *p = obj.Prog{} 664 p.Ctxt = ctxt 665 p.Link = next 666 p.As = ABL 667 p.To.Type = obj.TYPE_BRANCH 668 p.To.Sym = symsfloat 669 p.Lineno = next.Lineno 670 671 p = next 672 wasfloat = 1 673 } 674 675 continue 676 677 notsoft: 678 wasfloat = 0 679 } 680 } 681 682 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 683 // MOVW g_stackguard(g), R1 684 p = obj.Appendp(ctxt, p) 685 686 p.As = AMOVW 687 p.From.Type = obj.TYPE_MEM 688 p.From.Reg = REGG 689 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 690 if ctxt.Cursym.Cfunc != 0 { 691 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 692 } 693 p.To.Type = obj.TYPE_REG 694 p.To.Reg = REG_R1 695 696 if framesize <= obj.StackSmall { 697 // small stack: SP < stackguard 698 // CMP stackguard, SP 699 p = obj.Appendp(ctxt, p) 700 701 p.As = ACMP 702 p.From.Type = obj.TYPE_REG 703 p.From.Reg = REG_R1 704 p.Reg = REGSP 705 } else if framesize <= obj.StackBig { 706 // large stack: SP-framesize < stackguard-StackSmall 707 // MOVW $-framesize(SP), R2 708 // CMP stackguard, R2 709 p = obj.Appendp(ctxt, p) 710 711 p.As = AMOVW 712 p.From.Type = obj.TYPE_ADDR 713 p.From.Reg = REGSP 714 p.From.Offset = int64(-framesize) 715 p.To.Type = obj.TYPE_REG 716 p.To.Reg = REG_R2 717 718 p = obj.Appendp(ctxt, p) 719 p.As = ACMP 720 p.From.Type = obj.TYPE_REG 721 p.From.Reg = REG_R1 722 p.Reg = REG_R2 723 } else { 724 // Such a large stack we need to protect against wraparound 725 // if SP is close to zero. 726 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 727 // The +StackGuard on both sides is required to keep the left side positive: 728 // SP is allowed to be slightly below stackguard. See stack.h. 729 // CMP $StackPreempt, R1 730 // MOVW.NE $StackGuard(SP), R2 731 // SUB.NE R1, R2 732 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 733 // CMP.NE R3, R2 734 p = obj.Appendp(ctxt, p) 735 736 p.As = ACMP 737 p.From.Type = obj.TYPE_CONST 738 p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 739 p.Reg = REG_R1 740 741 p = obj.Appendp(ctxt, p) 742 p.As = AMOVW 743 p.From.Type = obj.TYPE_ADDR 744 p.From.Reg = REGSP 745 p.From.Offset = obj.StackGuard 746 p.To.Type = obj.TYPE_REG 747 p.To.Reg = REG_R2 748 p.Scond = C_SCOND_NE 749 750 p = obj.Appendp(ctxt, p) 751 p.As = ASUB 752 p.From.Type = obj.TYPE_REG 753 p.From.Reg = REG_R1 754 p.To.Type = obj.TYPE_REG 755 p.To.Reg = REG_R2 756 p.Scond = C_SCOND_NE 757 758 p = obj.Appendp(ctxt, p) 759 p.As = AMOVW 760 p.From.Type = obj.TYPE_ADDR 761 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 762 p.To.Type = obj.TYPE_REG 763 p.To.Reg = REG_R3 764 p.Scond = C_SCOND_NE 765 766 p = obj.Appendp(ctxt, p) 767 p.As = ACMP 768 p.From.Type = obj.TYPE_REG 769 p.From.Reg = REG_R3 770 p.Reg = REG_R2 771 p.Scond = C_SCOND_NE 772 } 773 774 // BLS call-to-morestack 775 bls := obj.Appendp(ctxt, p) 776 bls.As = ABLS 777 bls.To.Type = obj.TYPE_BRANCH 778 779 var last *obj.Prog 780 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 781 } 782 783 // MOVW LR, R3 784 movw := obj.Appendp(ctxt, last) 785 movw.As = AMOVW 786 movw.From.Type = obj.TYPE_REG 787 movw.From.Reg = REGLINK 788 movw.To.Type = obj.TYPE_REG 789 movw.To.Reg = REG_R3 790 791 bls.Pcond = movw 792 793 // BL runtime.morestack 794 call := obj.Appendp(ctxt, movw) 795 call.As = obj.ACALL 796 call.To.Type = obj.TYPE_BRANCH 797 morestack := "runtime.morestack" 798 switch { 799 case ctxt.Cursym.Cfunc != 0: 800 morestack = "runtime.morestackc" 801 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 802 morestack = "runtime.morestack_noctxt" 803 } 804 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 805 806 // B start 807 b := obj.Appendp(ctxt, call) 808 b.As = obj.AJMP 809 b.To.Type = obj.TYPE_BRANCH 810 b.Pcond = ctxt.Cursym.Text.Link 811 812 return bls 813 } 814 815 func initdiv(ctxt *obj.Link) { 816 if ctxt.Sym_div != nil { 817 return 818 } 819 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) 820 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) 821 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) 822 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) 823 } 824 825 func follow(ctxt *obj.Link, s *obj.LSym) { 826 ctxt.Cursym = s 827 828 firstp := ctxt.NewProg() 829 lastp := firstp 830 xfol(ctxt, s.Text, &lastp) 831 lastp.Link = nil 832 s.Text = firstp.Link 833 } 834 835 func relinv(a int) int { 836 switch a { 837 case ABEQ: 838 return ABNE 839 case ABNE: 840 return ABEQ 841 case ABCS: 842 return ABCC 843 case ABHS: 844 return ABLO 845 case ABCC: 846 return ABCS 847 case ABLO: 848 return ABHS 849 case ABMI: 850 return ABPL 851 case ABPL: 852 return ABMI 853 case ABVS: 854 return ABVC 855 case ABVC: 856 return ABVS 857 case ABHI: 858 return ABLS 859 case ABLS: 860 return ABHI 861 case ABGE: 862 return ABLT 863 case ABLT: 864 return ABGE 865 case ABGT: 866 return ABLE 867 case ABLE: 868 return ABGT 869 } 870 871 log.Fatalf("unknown relation: %s", Anames[a]) 872 return 0 873 } 874 875 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 876 var q *obj.Prog 877 var r *obj.Prog 878 var a int 879 var i int 880 881 loop: 882 if p == nil { 883 return 884 } 885 a = int(p.As) 886 if a == AB { 887 q = p.Pcond 888 if q != nil && q.As != obj.ATEXT { 889 p.Mark |= FOLL 890 p = q 891 if p.Mark&FOLL == 0 { 892 goto loop 893 } 894 } 895 } 896 897 if p.Mark&FOLL != 0 { 898 i = 0 899 q = p 900 for ; i < 4; i, q = i+1, q.Link { 901 if q == *last || q == nil { 902 break 903 } 904 a = int(q.As) 905 if a == obj.ANOP { 906 i-- 907 continue 908 } 909 910 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 911 goto copy 912 } 913 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 914 continue 915 } 916 if a != ABEQ && a != ABNE { 917 continue 918 } 919 920 copy: 921 for { 922 r = ctxt.NewProg() 923 *r = *p 924 if r.Mark&FOLL == 0 { 925 fmt.Printf("can't happen 1\n") 926 } 927 r.Mark |= FOLL 928 if p != q { 929 p = p.Link 930 (*last).Link = r 931 *last = r 932 continue 933 } 934 935 (*last).Link = r 936 *last = r 937 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 938 return 939 } 940 r.As = ABNE 941 if a == ABNE { 942 r.As = ABEQ 943 } 944 r.Pcond = p.Link 945 r.Link = p.Pcond 946 if r.Link.Mark&FOLL == 0 { 947 xfol(ctxt, r.Link, last) 948 } 949 if r.Pcond.Mark&FOLL == 0 { 950 fmt.Printf("can't happen 2\n") 951 } 952 return 953 } 954 } 955 956 a = AB 957 q = ctxt.NewProg() 958 q.As = int16(a) 959 q.Lineno = p.Lineno 960 q.To.Type = obj.TYPE_BRANCH 961 q.To.Offset = p.Pc 962 q.Pcond = p 963 p = q 964 } 965 966 p.Mark |= FOLL 967 (*last).Link = p 968 *last = p 969 if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 970 return 971 } 972 973 if p.Pcond != nil { 974 if a != ABL && a != ABX && p.Link != nil { 975 q = obj.Brchain(ctxt, p.Link) 976 if a != obj.ATEXT && a != ABCASE { 977 if q != nil && (q.Mark&FOLL != 0) { 978 p.As = int16(relinv(a)) 979 p.Link = p.Pcond 980 p.Pcond = q 981 } 982 } 983 984 xfol(ctxt, p.Link, last) 985 q = obj.Brchain(ctxt, p.Pcond) 986 if q == nil { 987 q = p.Pcond 988 } 989 if q.Mark&FOLL != 0 { 990 p.Pcond = q 991 return 992 } 993 994 p = q 995 goto loop 996 } 997 } 998 999 p = p.Link 1000 goto loop 1001 } 1002 1003 var unaryDst = map[int]bool{ 1004 ASWI: true, 1005 AWORD: true, 1006 } 1007 1008 var Linkarm = obj.LinkArch{ 1009 ByteOrder: binary.LittleEndian, 1010 Name: "arm", 1011 Thechar: '5', 1012 Preprocess: preprocess, 1013 Assemble: span5, 1014 Follow: follow, 1015 Progedit: progedit, 1016 UnaryDst: unaryDst, 1017 Minlc: 4, 1018 Ptrsize: 4, 1019 Regsize: 4, 1020 }