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