github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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 "math" 38 ) 39 40 var progedit_tlsfallback *obj.LSym 41 42 func progedit(ctxt *obj.Link, p *obj.Prog) { 43 p.From.Class = 0 44 p.To.Class = 0 45 46 // Rewrite B/BL to symbol as TYPE_BRANCH. 47 switch p.As { 48 case AB, 49 ABL, 50 obj.ADUFFZERO, 51 obj.ADUFFCOPY: 52 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { 53 p.To.Type = obj.TYPE_BRANCH 54 } 55 } 56 57 // Replace TLS register fetches on older ARM processors. 58 switch p.As { 59 // Treat MRC 15, 0, <reg>, C13, C0, 3 specially. 60 case AMRC: 61 if p.To.Offset&0xffff0fff == 0xee1d0f70 { 62 // Because the instruction might be rewritten to a BL which returns in R0 63 // the register must be zero. 64 if p.To.Offset&0xf000 != 0 { 65 ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) 66 } 67 68 if obj.GOARM < 7 { 69 // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. 70 if progedit_tlsfallback == nil { 71 progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0) 72 } 73 74 // MOVW LR, R11 75 p.As = AMOVW 76 77 p.From.Type = obj.TYPE_REG 78 p.From.Reg = REGLINK 79 p.To.Type = obj.TYPE_REG 80 p.To.Reg = REGTMP 81 82 // BL runtime.read_tls_fallback(SB) 83 p = obj.Appendp(ctxt, p) 84 85 p.As = ABL 86 p.To.Type = obj.TYPE_BRANCH 87 p.To.Sym = progedit_tlsfallback 88 p.To.Offset = 0 89 90 // MOVW R11, LR 91 p = obj.Appendp(ctxt, p) 92 93 p.As = AMOVW 94 p.From.Type = obj.TYPE_REG 95 p.From.Reg = REGTMP 96 p.To.Type = obj.TYPE_REG 97 p.To.Reg = REGLINK 98 break 99 } 100 } 101 102 // Otherwise, MRC/MCR instructions need no further treatment. 103 p.As = AWORD 104 } 105 106 // Rewrite float constants to values stored in memory. 107 switch p.As { 108 case AMOVF: 109 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) { 110 f32 := float32(p.From.Val.(float64)) 111 i32 := math.Float32bits(f32) 112 literal := fmt.Sprintf("$f32.%08x", i32) 113 s := obj.Linklookup(ctxt, literal, 0) 114 p.From.Type = obj.TYPE_MEM 115 p.From.Sym = s 116 p.From.Name = obj.NAME_EXTERN 117 p.From.Offset = 0 118 } 119 120 case AMOVD: 121 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) { 122 i64 := math.Float64bits(p.From.Val.(float64)) 123 literal := fmt.Sprintf("$f64.%016x", i64) 124 s := obj.Linklookup(ctxt, literal, 0) 125 p.From.Type = obj.TYPE_MEM 126 p.From.Sym = s 127 p.From.Name = obj.NAME_EXTERN 128 p.From.Offset = 0 129 } 130 } 131 132 if ctxt.Flag_dynlink { 133 rewriteToUseGot(ctxt, p) 134 } 135 } 136 137 // Rewrite p, if necessary, to access global data via the global offset table. 138 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { 139 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 140 // ADUFFxxx $offset 141 // becomes 142 // MOVW runtime.duffxxx@GOT, R9 143 // ADD $offset, R9 144 // CALL (R9) 145 var sym *obj.LSym 146 if p.As == obj.ADUFFZERO { 147 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 148 } else { 149 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 150 } 151 offset := p.To.Offset 152 p.As = AMOVW 153 p.From.Type = obj.TYPE_MEM 154 p.From.Name = obj.NAME_GOTREF 155 p.From.Sym = sym 156 p.To.Type = obj.TYPE_REG 157 p.To.Reg = REG_R9 158 p.To.Name = obj.NAME_NONE 159 p.To.Offset = 0 160 p.To.Sym = nil 161 p1 := obj.Appendp(ctxt, p) 162 p1.As = AADD 163 p1.From.Type = obj.TYPE_CONST 164 p1.From.Offset = offset 165 p1.To.Type = obj.TYPE_REG 166 p1.To.Reg = REG_R9 167 p2 := obj.Appendp(ctxt, p1) 168 p2.As = obj.ACALL 169 p2.To.Type = obj.TYPE_MEM 170 p2.To.Reg = REG_R9 171 return 172 } 173 174 // We only care about global data: NAME_EXTERN means a global 175 // symbol in the Go sense, and p.Sym.Local is true for a few 176 // internally defined symbols. 177 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 178 // MOVW $sym, Rx becomes MOVW sym@GOT, Rx 179 // MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx 180 if p.As != AMOVW { 181 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 182 } 183 if p.To.Type != obj.TYPE_REG { 184 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 185 } 186 p.From.Type = obj.TYPE_MEM 187 p.From.Name = obj.NAME_GOTREF 188 if p.From.Offset != 0 { 189 q := obj.Appendp(ctxt, p) 190 q.As = AADD 191 q.From.Type = obj.TYPE_CONST 192 q.From.Offset = p.From.Offset 193 q.To = p.To 194 p.From.Offset = 0 195 } 196 } 197 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 198 ctxt.Diag("don't know how to handle %v with -dynlink", p) 199 } 200 var source *obj.Addr 201 // MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry 202 // MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9) 203 // An addition may be inserted between the two MOVs if there is an offset. 204 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 205 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 206 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 207 } 208 source = &p.From 209 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 210 source = &p.To 211 } else { 212 return 213 } 214 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 215 return 216 } 217 if source.Sym.Type == obj.STLSBSS { 218 return 219 } 220 if source.Type != obj.TYPE_MEM { 221 ctxt.Diag("don't know how to handle %v with -dynlink", p) 222 } 223 p1 := obj.Appendp(ctxt, p) 224 p2 := obj.Appendp(ctxt, p1) 225 226 p1.As = AMOVW 227 p1.From.Type = obj.TYPE_MEM 228 p1.From.Sym = source.Sym 229 p1.From.Name = obj.NAME_GOTREF 230 p1.To.Type = obj.TYPE_REG 231 p1.To.Reg = REG_R9 232 233 p2.As = p.As 234 p2.From = p.From 235 p2.To = p.To 236 if p.From.Name == obj.NAME_EXTERN { 237 p2.From.Reg = REG_R9 238 p2.From.Name = obj.NAME_NONE 239 p2.From.Sym = nil 240 } else if p.To.Name == obj.NAME_EXTERN { 241 p2.To.Reg = REG_R9 242 p2.To.Name = obj.NAME_NONE 243 p2.To.Sym = nil 244 } else { 245 return 246 } 247 obj.Nopout(p) 248 } 249 250 // Prog.mark 251 const ( 252 FOLL = 1 << 0 253 LABEL = 1 << 1 254 LEAF = 1 << 2 255 ) 256 257 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 258 autosize := int32(0) 259 260 ctxt.Cursym = cursym 261 262 if cursym.Text == nil || cursym.Text.Link == nil { 263 return 264 } 265 266 softfloat(ctxt, cursym) 267 268 p := cursym.Text 269 autoffset := int32(p.To.Offset) 270 if autoffset < 0 { 271 autoffset = 0 272 } 273 cursym.Locals = autoffset 274 cursym.Args = p.To.Val.(int32) 275 276 /* 277 * find leaf subroutines 278 * strip NOPs 279 * expand RET 280 * expand BECOME pseudo 281 */ 282 var q1 *obj.Prog 283 var q *obj.Prog 284 for p := cursym.Text; p != nil; p = p.Link { 285 switch p.As { 286 case obj.ATEXT: 287 p.Mark |= LEAF 288 289 case obj.ARET: 290 break 291 292 case ADIV, ADIVU, AMOD, AMODU: 293 q = p 294 if ctxt.Sym_div == nil { 295 initdiv(ctxt) 296 } 297 cursym.Text.Mark &^= LEAF 298 continue 299 300 case obj.ANOP: 301 q1 = p.Link 302 q.Link = q1 /* q is non-nop */ 303 if q1 != nil { 304 q1.Mark |= p.Mark 305 } 306 continue 307 308 case ABL, 309 ABX, 310 obj.ADUFFZERO, 311 obj.ADUFFCOPY: 312 cursym.Text.Mark &^= LEAF 313 fallthrough 314 315 case AB, 316 ABEQ, 317 ABNE, 318 ABCS, 319 ABHS, 320 ABCC, 321 ABLO, 322 ABMI, 323 ABPL, 324 ABVS, 325 ABVC, 326 ABHI, 327 ABLS, 328 ABGE, 329 ABLT, 330 ABGT, 331 ABLE: 332 q1 = p.Pcond 333 if q1 != nil { 334 for q1.As == obj.ANOP { 335 q1 = q1.Link 336 p.Pcond = q1 337 } 338 } 339 } 340 341 q = p 342 } 343 344 var p1 *obj.Prog 345 var p2 *obj.Prog 346 var q2 *obj.Prog 347 for p := cursym.Text; p != nil; p = p.Link { 348 o := p.As 349 switch o { 350 case obj.ATEXT: 351 autosize = int32(p.To.Offset + 4) 352 if autosize <= 4 { 353 if cursym.Text.Mark&LEAF != 0 { 354 p.To.Offset = -4 355 autosize = 0 356 } 357 } 358 359 if autosize == 0 && cursym.Text.Mark&LEAF == 0 { 360 if ctxt.Debugvlog != 0 { 361 ctxt.Logf("save suppressed in: %s\n", cursym.Name) 362 } 363 364 cursym.Text.Mark |= LEAF 365 } 366 367 if cursym.Text.Mark&LEAF != 0 { 368 cursym.Set(obj.AttrLeaf, true) 369 if autosize == 0 { 370 break 371 } 372 } 373 374 if p.From3.Offset&obj.NOSPLIT == 0 { 375 p = stacksplit(ctxt, p, autosize) // emit split check 376 } 377 378 // MOVW.W R14,$-autosize(SP) 379 p = obj.Appendp(ctxt, p) 380 381 p.As = AMOVW 382 p.Scond |= C_WBIT 383 p.From.Type = obj.TYPE_REG 384 p.From.Reg = REGLINK 385 p.To.Type = obj.TYPE_MEM 386 p.To.Offset = int64(-autosize) 387 p.To.Reg = REGSP 388 p.Spadj = autosize 389 390 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 391 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 392 // 393 // MOVW g_panic(g), R1 394 // CMP $0, R1 395 // B.EQ end 396 // MOVW panic_argp(R1), R2 397 // ADD $(autosize+4), R13, R3 398 // CMP R2, R3 399 // B.NE end 400 // ADD $4, R13, R4 401 // MOVW R4, panic_argp(R1) 402 // end: 403 // NOP 404 // 405 // The NOP is needed to give the jumps somewhere to land. 406 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. 407 408 p = obj.Appendp(ctxt, p) 409 410 p.As = AMOVW 411 p.From.Type = obj.TYPE_MEM 412 p.From.Reg = REGG 413 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 414 p.To.Type = obj.TYPE_REG 415 p.To.Reg = REG_R1 416 417 p = obj.Appendp(ctxt, p) 418 p.As = ACMP 419 p.From.Type = obj.TYPE_CONST 420 p.From.Offset = 0 421 p.Reg = REG_R1 422 423 p = obj.Appendp(ctxt, p) 424 p.As = ABEQ 425 p.To.Type = obj.TYPE_BRANCH 426 p1 = p 427 428 p = obj.Appendp(ctxt, p) 429 p.As = AMOVW 430 p.From.Type = obj.TYPE_MEM 431 p.From.Reg = REG_R1 432 p.From.Offset = 0 // Panic.argp 433 p.To.Type = obj.TYPE_REG 434 p.To.Reg = REG_R2 435 436 p = obj.Appendp(ctxt, p) 437 p.As = AADD 438 p.From.Type = obj.TYPE_CONST 439 p.From.Offset = int64(autosize) + 4 440 p.Reg = REG_R13 441 p.To.Type = obj.TYPE_REG 442 p.To.Reg = REG_R3 443 444 p = obj.Appendp(ctxt, p) 445 p.As = ACMP 446 p.From.Type = obj.TYPE_REG 447 p.From.Reg = REG_R2 448 p.Reg = REG_R3 449 450 p = obj.Appendp(ctxt, p) 451 p.As = ABNE 452 p.To.Type = obj.TYPE_BRANCH 453 p2 = p 454 455 p = obj.Appendp(ctxt, p) 456 p.As = AADD 457 p.From.Type = obj.TYPE_CONST 458 p.From.Offset = 4 459 p.Reg = REG_R13 460 p.To.Type = obj.TYPE_REG 461 p.To.Reg = REG_R4 462 463 p = obj.Appendp(ctxt, p) 464 p.As = AMOVW 465 p.From.Type = obj.TYPE_REG 466 p.From.Reg = REG_R4 467 p.To.Type = obj.TYPE_MEM 468 p.To.Reg = REG_R1 469 p.To.Offset = 0 // Panic.argp 470 471 p = obj.Appendp(ctxt, p) 472 473 p.As = obj.ANOP 474 p1.Pcond = p 475 p2.Pcond = p 476 } 477 478 case obj.ARET: 479 nocache(p) 480 if cursym.Text.Mark&LEAF != 0 { 481 if autosize == 0 { 482 p.As = AB 483 p.From = obj.Addr{} 484 if p.To.Sym != nil { // retjmp 485 p.To.Type = obj.TYPE_BRANCH 486 } else { 487 p.To.Type = obj.TYPE_MEM 488 p.To.Offset = 0 489 p.To.Reg = REGLINK 490 } 491 492 break 493 } 494 } 495 496 p.As = AMOVW 497 p.Scond |= C_PBIT 498 p.From.Type = obj.TYPE_MEM 499 p.From.Offset = int64(autosize) 500 p.From.Reg = REGSP 501 p.To.Type = obj.TYPE_REG 502 p.To.Reg = REGPC 503 504 // If there are instructions following 505 // this ARET, they come from a branch 506 // with the same stackframe, so no spadj. 507 if p.To.Sym != nil { // retjmp 508 p.To.Reg = REGLINK 509 q2 = obj.Appendp(ctxt, p) 510 q2.As = AB 511 q2.To.Type = obj.TYPE_BRANCH 512 q2.To.Sym = p.To.Sym 513 p.To.Sym = nil 514 p = q2 515 } 516 517 case AADD: 518 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 519 p.Spadj = int32(-p.From.Offset) 520 } 521 522 case ASUB: 523 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 524 p.Spadj = int32(p.From.Offset) 525 } 526 527 case ADIV, ADIVU, AMOD, AMODU: 528 if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { 529 ctxt.Diag("cannot divide in NOSPLIT function") 530 } 531 if ctxt.Debugdivmod != 0 { 532 break 533 } 534 if p.From.Type != obj.TYPE_REG { 535 break 536 } 537 if p.To.Type != obj.TYPE_REG { 538 break 539 } 540 541 // Make copy because we overwrite p below. 542 q1 := *p 543 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { 544 ctxt.Diag("div already using REGTMP: %v", p) 545 } 546 547 /* MOV m(g),REGTMP */ 548 p.As = AMOVW 549 p.Pos = q1.Pos 550 p.From.Type = obj.TYPE_MEM 551 p.From.Reg = REGG 552 p.From.Offset = 6 * 4 // offset of g.m 553 p.Reg = 0 554 p.To.Type = obj.TYPE_REG 555 p.To.Reg = REGTMP 556 557 /* MOV a,m_divmod(REGTMP) */ 558 p = obj.Appendp(ctxt, p) 559 p.As = AMOVW 560 p.Pos = q1.Pos 561 p.From.Type = obj.TYPE_REG 562 p.From.Reg = q1.From.Reg 563 p.To.Type = obj.TYPE_MEM 564 p.To.Reg = REGTMP 565 p.To.Offset = 8 * 4 // offset of m.divmod 566 567 /* MOV b, R8 */ 568 p = obj.Appendp(ctxt, p) 569 p.As = AMOVW 570 p.Pos = q1.Pos 571 p.From.Type = obj.TYPE_REG 572 p.From.Reg = q1.Reg 573 if q1.Reg == 0 { 574 p.From.Reg = q1.To.Reg 575 } 576 p.To.Type = obj.TYPE_REG 577 p.To.Reg = REG_R8 578 p.To.Offset = 0 579 580 /* CALL appropriate */ 581 p = obj.Appendp(ctxt, p) 582 p.As = ABL 583 p.Pos = q1.Pos 584 p.To.Type = obj.TYPE_BRANCH 585 switch o { 586 case ADIV: 587 p.To.Sym = ctxt.Sym_div 588 589 case ADIVU: 590 p.To.Sym = ctxt.Sym_divu 591 592 case AMOD: 593 p.To.Sym = ctxt.Sym_mod 594 595 case AMODU: 596 p.To.Sym = ctxt.Sym_modu 597 } 598 599 /* MOV REGTMP, b */ 600 p = obj.Appendp(ctxt, p) 601 p.As = AMOVW 602 p.Pos = q1.Pos 603 p.From.Type = obj.TYPE_REG 604 p.From.Reg = REGTMP 605 p.From.Offset = 0 606 p.To.Type = obj.TYPE_REG 607 p.To.Reg = q1.To.Reg 608 609 case AMOVW: 610 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 611 p.Spadj = int32(-p.To.Offset) 612 } 613 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { 614 p.Spadj = int32(-p.From.Offset) 615 } 616 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 617 p.Spadj = int32(-p.From.Offset) 618 } 619 } 620 } 621 } 622 623 func isfloatreg(a *obj.Addr) bool { 624 return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15 625 } 626 627 func softfloat(ctxt *obj.Link, cursym *obj.LSym) { 628 if obj.GOARM > 5 { 629 return 630 } 631 632 symsfloat := obj.Linklookup(ctxt, "_sfloat", 0) 633 634 wasfloat := 0 635 for p := cursym.Text; p != nil; p = p.Link { 636 if p.Pcond != nil { 637 p.Pcond.Mark |= LABEL 638 } 639 } 640 var next *obj.Prog 641 for p := cursym.Text; p != nil; p = p.Link { 642 switch p.As { 643 case AMOVW: 644 if isfloatreg(&p.To) || isfloatreg(&p.From) { 645 goto soft 646 } 647 goto notsoft 648 649 case AMOVWD, 650 AMOVWF, 651 AMOVDW, 652 AMOVFW, 653 AMOVFD, 654 AMOVDF, 655 AMOVF, 656 AMOVD, 657 ACMPF, 658 ACMPD, 659 AADDF, 660 AADDD, 661 ASUBF, 662 ASUBD, 663 AMULF, 664 AMULD, 665 ADIVF, 666 ADIVD, 667 ASQRTF, 668 ASQRTD, 669 AABSF, 670 AABSD, 671 ANEGF, 672 ANEGD: 673 goto soft 674 675 default: 676 goto notsoft 677 } 678 679 soft: 680 if wasfloat == 0 || (p.Mark&LABEL != 0) { 681 next = ctxt.NewProg() 682 *next = *p 683 684 // BL _sfloat(SB) 685 *p = obj.Prog{} 686 p.Ctxt = ctxt 687 p.Link = next 688 p.As = ABL 689 p.To.Type = obj.TYPE_BRANCH 690 p.To.Sym = symsfloat 691 p.Pos = next.Pos 692 693 p = next 694 wasfloat = 1 695 } 696 697 continue 698 699 notsoft: 700 wasfloat = 0 701 } 702 } 703 704 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 705 // MOVW g_stackguard(g), R1 706 p = obj.Appendp(ctxt, p) 707 708 p.As = AMOVW 709 p.From.Type = obj.TYPE_MEM 710 p.From.Reg = REGG 711 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 712 if ctxt.Cursym.CFunc() { 713 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 714 } 715 p.To.Type = obj.TYPE_REG 716 p.To.Reg = REG_R1 717 718 if framesize <= obj.StackSmall { 719 // small stack: SP < stackguard 720 // CMP stackguard, SP 721 p = obj.Appendp(ctxt, p) 722 723 p.As = ACMP 724 p.From.Type = obj.TYPE_REG 725 p.From.Reg = REG_R1 726 p.Reg = REGSP 727 } else if framesize <= obj.StackBig { 728 // large stack: SP-framesize < stackguard-StackSmall 729 // MOVW $-framesize(SP), R2 730 // CMP stackguard, R2 731 p = obj.Appendp(ctxt, p) 732 733 p.As = AMOVW 734 p.From.Type = obj.TYPE_ADDR 735 p.From.Reg = REGSP 736 p.From.Offset = int64(-framesize) 737 p.To.Type = obj.TYPE_REG 738 p.To.Reg = REG_R2 739 740 p = obj.Appendp(ctxt, p) 741 p.As = ACMP 742 p.From.Type = obj.TYPE_REG 743 p.From.Reg = REG_R1 744 p.Reg = REG_R2 745 } else { 746 // Such a large stack we need to protect against wraparound 747 // if SP is close to zero. 748 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 749 // The +StackGuard on both sides is required to keep the left side positive: 750 // SP is allowed to be slightly below stackguard. See stack.h. 751 // CMP $StackPreempt, R1 752 // MOVW.NE $StackGuard(SP), R2 753 // SUB.NE R1, R2 754 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 755 // CMP.NE R3, R2 756 p = obj.Appendp(ctxt, p) 757 758 p.As = ACMP 759 p.From.Type = obj.TYPE_CONST 760 p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 761 p.Reg = REG_R1 762 763 p = obj.Appendp(ctxt, p) 764 p.As = AMOVW 765 p.From.Type = obj.TYPE_ADDR 766 p.From.Reg = REGSP 767 p.From.Offset = obj.StackGuard 768 p.To.Type = obj.TYPE_REG 769 p.To.Reg = REG_R2 770 p.Scond = C_SCOND_NE 771 772 p = obj.Appendp(ctxt, p) 773 p.As = ASUB 774 p.From.Type = obj.TYPE_REG 775 p.From.Reg = REG_R1 776 p.To.Type = obj.TYPE_REG 777 p.To.Reg = REG_R2 778 p.Scond = C_SCOND_NE 779 780 p = obj.Appendp(ctxt, p) 781 p.As = AMOVW 782 p.From.Type = obj.TYPE_ADDR 783 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 784 p.To.Type = obj.TYPE_REG 785 p.To.Reg = REG_R3 786 p.Scond = C_SCOND_NE 787 788 p = obj.Appendp(ctxt, p) 789 p.As = ACMP 790 p.From.Type = obj.TYPE_REG 791 p.From.Reg = REG_R3 792 p.Reg = REG_R2 793 p.Scond = C_SCOND_NE 794 } 795 796 // BLS call-to-morestack 797 bls := obj.Appendp(ctxt, p) 798 bls.As = ABLS 799 bls.To.Type = obj.TYPE_BRANCH 800 801 var last *obj.Prog 802 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 803 } 804 805 // Now we are at the end of the function, but logically 806 // we are still in function prologue. We need to fix the 807 // SP data and PCDATA. 808 spfix := obj.Appendp(ctxt, last) 809 spfix.As = obj.ANOP 810 spfix.Spadj = -framesize 811 812 pcdata := obj.Appendp(ctxt, spfix) 813 pcdata.Pos = ctxt.Cursym.Text.Pos 814 pcdata.Mode = ctxt.Cursym.Text.Mode 815 pcdata.As = obj.APCDATA 816 pcdata.From.Type = obj.TYPE_CONST 817 pcdata.From.Offset = obj.PCDATA_StackMapIndex 818 pcdata.To.Type = obj.TYPE_CONST 819 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 820 821 // MOVW LR, R3 822 movw := obj.Appendp(ctxt, pcdata) 823 movw.As = AMOVW 824 movw.From.Type = obj.TYPE_REG 825 movw.From.Reg = REGLINK 826 movw.To.Type = obj.TYPE_REG 827 movw.To.Reg = REG_R3 828 829 bls.Pcond = movw 830 831 // BL runtime.morestack 832 call := obj.Appendp(ctxt, movw) 833 call.As = obj.ACALL 834 call.To.Type = obj.TYPE_BRANCH 835 morestack := "runtime.morestack" 836 switch { 837 case ctxt.Cursym.CFunc(): 838 morestack = "runtime.morestackc" 839 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 840 morestack = "runtime.morestack_noctxt" 841 } 842 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 843 844 // B start 845 b := obj.Appendp(ctxt, call) 846 b.As = obj.AJMP 847 b.To.Type = obj.TYPE_BRANCH 848 b.Pcond = ctxt.Cursym.Text.Link 849 b.Spadj = +framesize 850 851 return bls 852 } 853 854 func initdiv(ctxt *obj.Link) { 855 if ctxt.Sym_div != nil { 856 return 857 } 858 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) 859 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) 860 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) 861 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) 862 } 863 864 var unaryDst = map[obj.As]bool{ 865 ASWI: true, 866 AWORD: true, 867 } 868 869 var Linkarm = obj.LinkArch{ 870 Arch: sys.ArchARM, 871 Preprocess: preprocess, 872 Assemble: span5, 873 Progedit: progedit, 874 UnaryDst: unaryDst, 875 }