github.com/jonasi/go@v0.0.0-20150930005915-e78e654c1de0/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 preprocess(ctxt *obj.Link, cursym *obj.LSym) { 160 autosize := int32(0) 161 162 ctxt.Cursym = cursym 163 164 if cursym.Text == nil || cursym.Text.Link == nil { 165 return 166 } 167 168 softfloat(ctxt, cursym) 169 170 p := cursym.Text 171 autoffset := int32(p.To.Offset) 172 if autoffset < 0 { 173 autoffset = 0 174 } 175 cursym.Locals = autoffset 176 cursym.Args = p.To.Val.(int32) 177 178 /* 179 * find leaf subroutines 180 * strip NOPs 181 * expand RET 182 * expand BECOME pseudo 183 */ 184 var q1 *obj.Prog 185 var q *obj.Prog 186 for p := cursym.Text; p != nil; p = p.Link { 187 switch p.As { 188 case obj.ATEXT: 189 p.Mark |= LEAF 190 191 case obj.ARET: 192 break 193 194 case ADIV, ADIVU, AMOD, AMODU: 195 q = p 196 if ctxt.Sym_div == nil { 197 initdiv(ctxt) 198 } 199 cursym.Text.Mark &^= LEAF 200 continue 201 202 case obj.ANOP: 203 q1 = p.Link 204 q.Link = q1 /* q is non-nop */ 205 if q1 != nil { 206 q1.Mark |= p.Mark 207 } 208 continue 209 210 case ABL, 211 ABX, 212 obj.ADUFFZERO, 213 obj.ADUFFCOPY: 214 cursym.Text.Mark &^= LEAF 215 fallthrough 216 217 case AB, 218 ABEQ, 219 ABNE, 220 ABCS, 221 ABHS, 222 ABCC, 223 ABLO, 224 ABMI, 225 ABPL, 226 ABVS, 227 ABVC, 228 ABHI, 229 ABLS, 230 ABGE, 231 ABLT, 232 ABGT, 233 ABLE: 234 q1 = p.Pcond 235 if q1 != nil { 236 for q1.As == obj.ANOP { 237 q1 = q1.Link 238 p.Pcond = q1 239 } 240 } 241 } 242 243 q = p 244 } 245 246 var o int 247 var p1 *obj.Prog 248 var p2 *obj.Prog 249 var q2 *obj.Prog 250 for p := cursym.Text; p != nil; p = p.Link { 251 o = int(p.As) 252 switch o { 253 case obj.ATEXT: 254 autosize = int32(p.To.Offset + 4) 255 if autosize <= 4 { 256 if cursym.Text.Mark&LEAF != 0 { 257 p.To.Offset = -4 258 autosize = 0 259 } 260 } 261 262 if autosize == 0 && cursym.Text.Mark&LEAF == 0 { 263 if ctxt.Debugvlog != 0 { 264 fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) 265 ctxt.Bso.Flush() 266 } 267 268 cursym.Text.Mark |= LEAF 269 } 270 271 if cursym.Text.Mark&LEAF != 0 { 272 cursym.Leaf = 1 273 if autosize == 0 { 274 break 275 } 276 } 277 278 if p.From3.Offset&obj.NOSPLIT == 0 { 279 p = stacksplit(ctxt, p, autosize) // emit split check 280 } 281 282 // MOVW.W R14,$-autosize(SP) 283 p = obj.Appendp(ctxt, p) 284 285 p.As = AMOVW 286 p.Scond |= C_WBIT 287 p.From.Type = obj.TYPE_REG 288 p.From.Reg = REGLINK 289 p.To.Type = obj.TYPE_MEM 290 p.To.Offset = int64(-autosize) 291 p.To.Reg = REGSP 292 p.Spadj = autosize 293 294 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 295 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 296 // 297 // MOVW g_panic(g), R1 298 // CMP $0, R1 299 // B.EQ end 300 // MOVW panic_argp(R1), R2 301 // ADD $(autosize+4), R13, R3 302 // CMP R2, R3 303 // B.NE end 304 // ADD $4, R13, R4 305 // MOVW R4, panic_argp(R1) 306 // end: 307 // NOP 308 // 309 // The NOP is needed to give the jumps somewhere to land. 310 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. 311 312 p = obj.Appendp(ctxt, p) 313 314 p.As = AMOVW 315 p.From.Type = obj.TYPE_MEM 316 p.From.Reg = REGG 317 p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic 318 p.To.Type = obj.TYPE_REG 319 p.To.Reg = REG_R1 320 321 p = obj.Appendp(ctxt, p) 322 p.As = ACMP 323 p.From.Type = obj.TYPE_CONST 324 p.From.Offset = 0 325 p.Reg = REG_R1 326 327 p = obj.Appendp(ctxt, p) 328 p.As = ABEQ 329 p.To.Type = obj.TYPE_BRANCH 330 p1 = p 331 332 p = obj.Appendp(ctxt, p) 333 p.As = AMOVW 334 p.From.Type = obj.TYPE_MEM 335 p.From.Reg = REG_R1 336 p.From.Offset = 0 // Panic.argp 337 p.To.Type = obj.TYPE_REG 338 p.To.Reg = REG_R2 339 340 p = obj.Appendp(ctxt, p) 341 p.As = AADD 342 p.From.Type = obj.TYPE_CONST 343 p.From.Offset = int64(autosize) + 4 344 p.Reg = REG_R13 345 p.To.Type = obj.TYPE_REG 346 p.To.Reg = REG_R3 347 348 p = obj.Appendp(ctxt, p) 349 p.As = ACMP 350 p.From.Type = obj.TYPE_REG 351 p.From.Reg = REG_R2 352 p.Reg = REG_R3 353 354 p = obj.Appendp(ctxt, p) 355 p.As = ABNE 356 p.To.Type = obj.TYPE_BRANCH 357 p2 = p 358 359 p = obj.Appendp(ctxt, p) 360 p.As = AADD 361 p.From.Type = obj.TYPE_CONST 362 p.From.Offset = 4 363 p.Reg = REG_R13 364 p.To.Type = obj.TYPE_REG 365 p.To.Reg = REG_R4 366 367 p = obj.Appendp(ctxt, p) 368 p.As = AMOVW 369 p.From.Type = obj.TYPE_REG 370 p.From.Reg = REG_R4 371 p.To.Type = obj.TYPE_MEM 372 p.To.Reg = REG_R1 373 p.To.Offset = 0 // Panic.argp 374 375 p = obj.Appendp(ctxt, p) 376 377 p.As = obj.ANOP 378 p1.Pcond = p 379 p2.Pcond = p 380 } 381 382 case obj.ARET: 383 obj.Nocache(p) 384 if cursym.Text.Mark&LEAF != 0 { 385 if autosize == 0 { 386 p.As = AB 387 p.From = obj.Addr{} 388 if p.To.Sym != nil { // retjmp 389 p.To.Type = obj.TYPE_BRANCH 390 } else { 391 p.To.Type = obj.TYPE_MEM 392 p.To.Offset = 0 393 p.To.Reg = REGLINK 394 } 395 396 break 397 } 398 } 399 400 p.As = AMOVW 401 p.Scond |= C_PBIT 402 p.From.Type = obj.TYPE_MEM 403 p.From.Offset = int64(autosize) 404 p.From.Reg = REGSP 405 p.To.Type = obj.TYPE_REG 406 p.To.Reg = REGPC 407 408 // If there are instructions following 409 // this ARET, they come from a branch 410 // with the same stackframe, so no spadj. 411 if p.To.Sym != nil { // retjmp 412 p.To.Reg = REGLINK 413 q2 = obj.Appendp(ctxt, p) 414 q2.As = AB 415 q2.To.Type = obj.TYPE_BRANCH 416 q2.To.Sym = p.To.Sym 417 p.To.Sym = nil 418 p = q2 419 } 420 421 case AADD: 422 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 423 p.Spadj = int32(-p.From.Offset) 424 } 425 426 case ASUB: 427 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 428 p.Spadj = int32(p.From.Offset) 429 } 430 431 case ADIV, ADIVU, AMOD, AMODU: 432 if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { 433 ctxt.Diag("cannot divide in NOSPLIT function") 434 } 435 if ctxt.Debugdivmod != 0 { 436 break 437 } 438 if p.From.Type != obj.TYPE_REG { 439 break 440 } 441 if p.To.Type != obj.TYPE_REG { 442 break 443 } 444 445 // Make copy because we overwrite p below. 446 q1 := *p 447 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { 448 ctxt.Diag("div already using REGTMP: %v", p) 449 } 450 451 /* MOV m(g),REGTMP */ 452 p.As = AMOVW 453 p.Lineno = q1.Lineno 454 p.From.Type = obj.TYPE_MEM 455 p.From.Reg = REGG 456 p.From.Offset = 6 * 4 // offset of g.m 457 p.Reg = 0 458 p.To.Type = obj.TYPE_REG 459 p.To.Reg = REGTMP 460 461 /* MOV a,m_divmod(REGTMP) */ 462 p = obj.Appendp(ctxt, p) 463 p.As = AMOVW 464 p.Lineno = q1.Lineno 465 p.From.Type = obj.TYPE_REG 466 p.From.Reg = q1.From.Reg 467 p.To.Type = obj.TYPE_MEM 468 p.To.Reg = REGTMP 469 p.To.Offset = 8 * 4 // offset of m.divmod 470 471 /* MOV b,REGTMP */ 472 p = obj.Appendp(ctxt, p) 473 p.As = AMOVW 474 p.Lineno = q1.Lineno 475 p.From.Type = obj.TYPE_REG 476 p.From.Reg = q1.Reg 477 if q1.Reg == 0 { 478 p.From.Reg = q1.To.Reg 479 } 480 p.To.Type = obj.TYPE_REG 481 p.To.Reg = REGTMP 482 p.To.Offset = 0 483 484 /* CALL appropriate */ 485 p = obj.Appendp(ctxt, p) 486 p.As = ABL 487 p.Lineno = q1.Lineno 488 p.To.Type = obj.TYPE_BRANCH 489 switch o { 490 case ADIV: 491 p.To.Sym = ctxt.Sym_div 492 493 case ADIVU: 494 p.To.Sym = ctxt.Sym_divu 495 496 case AMOD: 497 p.To.Sym = ctxt.Sym_mod 498 499 case AMODU: 500 p.To.Sym = ctxt.Sym_modu 501 } 502 503 /* MOV REGTMP, b */ 504 p = obj.Appendp(ctxt, p) 505 p.As = AMOVW 506 p.Lineno = q1.Lineno 507 p.From.Type = obj.TYPE_REG 508 p.From.Reg = REGTMP 509 p.From.Offset = 0 510 p.To.Type = obj.TYPE_REG 511 p.To.Reg = q1.To.Reg 512 513 case AMOVW: 514 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 515 p.Spadj = int32(-p.To.Offset) 516 } 517 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { 518 p.Spadj = int32(-p.From.Offset) 519 } 520 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 521 p.Spadj = int32(-p.From.Offset) 522 } 523 } 524 } 525 } 526 527 func isfloatreg(a *obj.Addr) bool { 528 return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15 529 } 530 531 func softfloat(ctxt *obj.Link, cursym *obj.LSym) { 532 if ctxt.Goarm > 5 { 533 return 534 } 535 536 symsfloat := obj.Linklookup(ctxt, "_sfloat", 0) 537 538 wasfloat := 0 539 for p := cursym.Text; p != nil; p = p.Link { 540 if p.Pcond != nil { 541 p.Pcond.Mark |= LABEL 542 } 543 } 544 var next *obj.Prog 545 for p := cursym.Text; p != nil; p = p.Link { 546 switch p.As { 547 case AMOVW: 548 if isfloatreg(&p.To) || isfloatreg(&p.From) { 549 goto soft 550 } 551 goto notsoft 552 553 case AMOVWD, 554 AMOVWF, 555 AMOVDW, 556 AMOVFW, 557 AMOVFD, 558 AMOVDF, 559 AMOVF, 560 AMOVD, 561 ACMPF, 562 ACMPD, 563 AADDF, 564 AADDD, 565 ASUBF, 566 ASUBD, 567 AMULF, 568 AMULD, 569 ADIVF, 570 ADIVD, 571 ASQRTF, 572 ASQRTD, 573 AABSF, 574 AABSD: 575 goto soft 576 577 default: 578 goto notsoft 579 } 580 581 soft: 582 if wasfloat == 0 || (p.Mark&LABEL != 0) { 583 next = ctxt.NewProg() 584 *next = *p 585 586 // BL _sfloat(SB) 587 *p = obj.Prog{} 588 p.Ctxt = ctxt 589 p.Link = next 590 p.As = ABL 591 p.To.Type = obj.TYPE_BRANCH 592 p.To.Sym = symsfloat 593 p.Lineno = next.Lineno 594 595 p = next 596 wasfloat = 1 597 } 598 599 continue 600 601 notsoft: 602 wasfloat = 0 603 } 604 } 605 606 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 607 // MOVW g_stackguard(g), R1 608 p = obj.Appendp(ctxt, p) 609 610 p.As = AMOVW 611 p.From.Type = obj.TYPE_MEM 612 p.From.Reg = REGG 613 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 614 if ctxt.Cursym.Cfunc != 0 { 615 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 616 } 617 p.To.Type = obj.TYPE_REG 618 p.To.Reg = REG_R1 619 620 if framesize <= obj.StackSmall { 621 // small stack: SP < stackguard 622 // CMP stackguard, SP 623 p = obj.Appendp(ctxt, p) 624 625 p.As = ACMP 626 p.From.Type = obj.TYPE_REG 627 p.From.Reg = REG_R1 628 p.Reg = REGSP 629 } else if framesize <= obj.StackBig { 630 // large stack: SP-framesize < stackguard-StackSmall 631 // MOVW $-framesize(SP), R2 632 // CMP stackguard, R2 633 p = obj.Appendp(ctxt, p) 634 635 p.As = AMOVW 636 p.From.Type = obj.TYPE_ADDR 637 p.From.Reg = REGSP 638 p.From.Offset = int64(-framesize) 639 p.To.Type = obj.TYPE_REG 640 p.To.Reg = REG_R2 641 642 p = obj.Appendp(ctxt, p) 643 p.As = ACMP 644 p.From.Type = obj.TYPE_REG 645 p.From.Reg = REG_R1 646 p.Reg = REG_R2 647 } else { 648 // Such a large stack we need to protect against wraparound 649 // if SP is close to zero. 650 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 651 // The +StackGuard on both sides is required to keep the left side positive: 652 // SP is allowed to be slightly below stackguard. See stack.h. 653 // CMP $StackPreempt, R1 654 // MOVW.NE $StackGuard(SP), R2 655 // SUB.NE R1, R2 656 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 657 // CMP.NE R3, R2 658 p = obj.Appendp(ctxt, p) 659 660 p.As = ACMP 661 p.From.Type = obj.TYPE_CONST 662 p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 663 p.Reg = REG_R1 664 665 p = obj.Appendp(ctxt, p) 666 p.As = AMOVW 667 p.From.Type = obj.TYPE_ADDR 668 p.From.Reg = REGSP 669 p.From.Offset = obj.StackGuard 670 p.To.Type = obj.TYPE_REG 671 p.To.Reg = REG_R2 672 p.Scond = C_SCOND_NE 673 674 p = obj.Appendp(ctxt, p) 675 p.As = ASUB 676 p.From.Type = obj.TYPE_REG 677 p.From.Reg = REG_R1 678 p.To.Type = obj.TYPE_REG 679 p.To.Reg = REG_R2 680 p.Scond = C_SCOND_NE 681 682 p = obj.Appendp(ctxt, p) 683 p.As = AMOVW 684 p.From.Type = obj.TYPE_ADDR 685 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 686 p.To.Type = obj.TYPE_REG 687 p.To.Reg = REG_R3 688 p.Scond = C_SCOND_NE 689 690 p = obj.Appendp(ctxt, p) 691 p.As = ACMP 692 p.From.Type = obj.TYPE_REG 693 p.From.Reg = REG_R3 694 p.Reg = REG_R2 695 p.Scond = C_SCOND_NE 696 } 697 698 // BLS call-to-morestack 699 bls := obj.Appendp(ctxt, p) 700 bls.As = ABLS 701 bls.To.Type = obj.TYPE_BRANCH 702 703 var last *obj.Prog 704 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 705 } 706 707 // MOVW LR, R3 708 movw := obj.Appendp(ctxt, last) 709 movw.As = AMOVW 710 movw.From.Type = obj.TYPE_REG 711 movw.From.Reg = REGLINK 712 movw.To.Type = obj.TYPE_REG 713 movw.To.Reg = REG_R3 714 715 bls.Pcond = movw 716 717 // BL runtime.morestack 718 call := obj.Appendp(ctxt, movw) 719 call.As = obj.ACALL 720 call.To.Type = obj.TYPE_BRANCH 721 morestack := "runtime.morestack" 722 switch { 723 case ctxt.Cursym.Cfunc != 0: 724 morestack = "runtime.morestackc" 725 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 726 morestack = "runtime.morestack_noctxt" 727 } 728 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 729 730 // B start 731 b := obj.Appendp(ctxt, call) 732 b.As = obj.AJMP 733 b.To.Type = obj.TYPE_BRANCH 734 b.Pcond = ctxt.Cursym.Text.Link 735 736 return bls 737 } 738 739 func initdiv(ctxt *obj.Link) { 740 if ctxt.Sym_div != nil { 741 return 742 } 743 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) 744 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) 745 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) 746 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) 747 } 748 749 func follow(ctxt *obj.Link, s *obj.LSym) { 750 ctxt.Cursym = s 751 752 firstp := ctxt.NewProg() 753 lastp := firstp 754 xfol(ctxt, s.Text, &lastp) 755 lastp.Link = nil 756 s.Text = firstp.Link 757 } 758 759 func relinv(a int) int { 760 switch a { 761 case ABEQ: 762 return ABNE 763 case ABNE: 764 return ABEQ 765 case ABCS: 766 return ABCC 767 case ABHS: 768 return ABLO 769 case ABCC: 770 return ABCS 771 case ABLO: 772 return ABHS 773 case ABMI: 774 return ABPL 775 case ABPL: 776 return ABMI 777 case ABVS: 778 return ABVC 779 case ABVC: 780 return ABVS 781 case ABHI: 782 return ABLS 783 case ABLS: 784 return ABHI 785 case ABGE: 786 return ABLT 787 case ABLT: 788 return ABGE 789 case ABGT: 790 return ABLE 791 case ABLE: 792 return ABGT 793 } 794 795 log.Fatalf("unknown relation: %s", Anames[a]) 796 return 0 797 } 798 799 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 800 var q *obj.Prog 801 var r *obj.Prog 802 var a int 803 var i int 804 805 loop: 806 if p == nil { 807 return 808 } 809 a = int(p.As) 810 if a == AB { 811 q = p.Pcond 812 if q != nil && q.As != obj.ATEXT { 813 p.Mark |= FOLL 814 p = q 815 if p.Mark&FOLL == 0 { 816 goto loop 817 } 818 } 819 } 820 821 if p.Mark&FOLL != 0 { 822 i = 0 823 q = p 824 for ; i < 4; i, q = i+1, q.Link { 825 if q == *last || q == nil { 826 break 827 } 828 a = int(q.As) 829 if a == obj.ANOP { 830 i-- 831 continue 832 } 833 834 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 835 goto copy 836 } 837 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 838 continue 839 } 840 if a != ABEQ && a != ABNE { 841 continue 842 } 843 844 copy: 845 for { 846 r = ctxt.NewProg() 847 *r = *p 848 if r.Mark&FOLL == 0 { 849 fmt.Printf("can't happen 1\n") 850 } 851 r.Mark |= FOLL 852 if p != q { 853 p = p.Link 854 (*last).Link = r 855 *last = r 856 continue 857 } 858 859 (*last).Link = r 860 *last = r 861 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 862 return 863 } 864 r.As = ABNE 865 if a == ABNE { 866 r.As = ABEQ 867 } 868 r.Pcond = p.Link 869 r.Link = p.Pcond 870 if r.Link.Mark&FOLL == 0 { 871 xfol(ctxt, r.Link, last) 872 } 873 if r.Pcond.Mark&FOLL == 0 { 874 fmt.Printf("can't happen 2\n") 875 } 876 return 877 } 878 } 879 880 a = AB 881 q = ctxt.NewProg() 882 q.As = int16(a) 883 q.Lineno = p.Lineno 884 q.To.Type = obj.TYPE_BRANCH 885 q.To.Offset = p.Pc 886 q.Pcond = p 887 p = q 888 } 889 890 p.Mark |= FOLL 891 (*last).Link = p 892 *last = p 893 if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { 894 return 895 } 896 897 if p.Pcond != nil { 898 if a != ABL && a != ABX && p.Link != nil { 899 q = obj.Brchain(ctxt, p.Link) 900 if a != obj.ATEXT { 901 if q != nil && (q.Mark&FOLL != 0) { 902 p.As = int16(relinv(a)) 903 p.Link = p.Pcond 904 p.Pcond = q 905 } 906 } 907 908 xfol(ctxt, p.Link, last) 909 q = obj.Brchain(ctxt, p.Pcond) 910 if q == nil { 911 q = p.Pcond 912 } 913 if q.Mark&FOLL != 0 { 914 p.Pcond = q 915 return 916 } 917 918 p = q 919 goto loop 920 } 921 } 922 923 p = p.Link 924 goto loop 925 } 926 927 var unaryDst = map[int]bool{ 928 ASWI: true, 929 AWORD: true, 930 } 931 932 var Linkarm = obj.LinkArch{ 933 ByteOrder: binary.LittleEndian, 934 Name: "arm", 935 Thechar: '5', 936 Preprocess: preprocess, 937 Assemble: span5, 938 Follow: follow, 939 Progedit: progedit, 940 UnaryDst: unaryDst, 941 Minlc: 4, 942 Ptrsize: 4, 943 Regsize: 4, 944 }