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