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