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