github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 "github.com/gagliardetto/golang-go/cmd/internal/obj" 35 "github.com/gagliardetto/golang-go/cmd/internal/objabi" 36 "github.com/gagliardetto/golang-go/cmd/internal/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 * strip NOPs 280 * expand RET 281 * expand BECOME pseudo 282 */ 283 var q1 *obj.Prog 284 var q *obj.Prog 285 for p := cursym.Func.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 cursym.Func.Text.Mark &^= LEAF 296 continue 297 298 case obj.ANOP: 299 q1 = p.Link 300 q.Link = q1 /* q is non-nop */ 301 if q1 != nil { 302 q1.Mark |= p.Mark 303 } 304 continue 305 306 case ABL, 307 ABX, 308 obj.ADUFFZERO, 309 obj.ADUFFCOPY: 310 cursym.Func.Text.Mark &^= LEAF 311 fallthrough 312 313 case AB, 314 ABEQ, 315 ABNE, 316 ABCS, 317 ABHS, 318 ABCC, 319 ABLO, 320 ABMI, 321 ABPL, 322 ABVS, 323 ABVC, 324 ABHI, 325 ABLS, 326 ABGE, 327 ABLT, 328 ABGT, 329 ABLE: 330 q1 = p.Pcond 331 if q1 != nil { 332 for q1.As == obj.ANOP { 333 q1 = q1.Link 334 p.Pcond = q1 335 } 336 } 337 } 338 339 q = p 340 } 341 342 var q2 *obj.Prog 343 for p := cursym.Func.Text; p != nil; p = p.Link { 344 o := p.As 345 switch o { 346 case obj.ATEXT: 347 autosize = autoffset 348 349 if p.Mark&LEAF != 0 && autosize == 0 { 350 // A leaf function with no locals has no frame. 351 p.From.Sym.Set(obj.AttrNoFrame, true) 352 } 353 354 if !p.From.Sym.NoFrame() { 355 // If there is a stack frame at all, it includes 356 // space to save the LR. 357 autosize += 4 358 } 359 360 if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 { 361 // A very few functions that do not return to their caller 362 // are not identified as leaves but still have no frame. 363 if ctxt.Debugvlog { 364 ctxt.Logf("save suppressed in: %s\n", cursym.Name) 365 } 366 367 cursym.Func.Text.Mark |= LEAF 368 } 369 370 // FP offsets need an updated p.To.Offset. 371 p.To.Offset = int64(autosize) - 4 372 373 if cursym.Func.Text.Mark&LEAF != 0 { 374 cursym.Set(obj.AttrLeaf, true) 375 if p.From.Sym.NoFrame() { 376 break 377 } 378 } 379 380 if !p.From.Sym.NoSplit() { 381 p = c.stacksplit(p, autosize) // emit split check 382 } 383 384 // MOVW.W R14,$-autosize(SP) 385 p = obj.Appendp(p, c.newprog) 386 387 p.As = AMOVW 388 p.Scond |= C_WBIT 389 p.From.Type = obj.TYPE_REG 390 p.From.Reg = REGLINK 391 p.To.Type = obj.TYPE_MEM 392 p.To.Offset = int64(-autosize) 393 p.To.Reg = REGSP 394 p.Spadj = autosize 395 396 if cursym.Func.Text.From.Sym.Wrapper() { 397 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 398 // 399 // MOVW g_panic(g), R1 400 // CMP $0, R1 401 // B.NE checkargp 402 // end: 403 // NOP 404 // ... function ... 405 // checkargp: 406 // MOVW panic_argp(R1), R2 407 // ADD $(autosize+4), R13, R3 408 // CMP R2, R3 409 // B.NE end 410 // ADD $4, R13, R4 411 // MOVW R4, panic_argp(R1) 412 // B end 413 // 414 // The NOP is needed to give the jumps somewhere to land. 415 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. 416 417 p = obj.Appendp(p, newprog) 418 p.As = AMOVW 419 p.From.Type = obj.TYPE_MEM 420 p.From.Reg = REGG 421 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 422 p.To.Type = obj.TYPE_REG 423 p.To.Reg = REG_R1 424 425 p = obj.Appendp(p, newprog) 426 p.As = ACMP 427 p.From.Type = obj.TYPE_CONST 428 p.From.Offset = 0 429 p.Reg = REG_R1 430 431 // B.NE checkargp 432 bne := obj.Appendp(p, newprog) 433 bne.As = ABNE 434 bne.To.Type = obj.TYPE_BRANCH 435 436 // end: NOP 437 end := obj.Appendp(bne, newprog) 438 end.As = obj.ANOP 439 440 // find end of function 441 var last *obj.Prog 442 for last = end; last.Link != nil; last = last.Link { 443 } 444 445 // MOVW panic_argp(R1), R2 446 mov := obj.Appendp(last, newprog) 447 mov.As = AMOVW 448 mov.From.Type = obj.TYPE_MEM 449 mov.From.Reg = REG_R1 450 mov.From.Offset = 0 // Panic.argp 451 mov.To.Type = obj.TYPE_REG 452 mov.To.Reg = REG_R2 453 454 // B.NE branch target is MOVW above 455 bne.Pcond = mov 456 457 // ADD $(autosize+4), R13, R3 458 p = obj.Appendp(mov, newprog) 459 p.As = AADD 460 p.From.Type = obj.TYPE_CONST 461 p.From.Offset = int64(autosize) + 4 462 p.Reg = REG_R13 463 p.To.Type = obj.TYPE_REG 464 p.To.Reg = REG_R3 465 466 // CMP R2, R3 467 p = obj.Appendp(p, newprog) 468 p.As = ACMP 469 p.From.Type = obj.TYPE_REG 470 p.From.Reg = REG_R2 471 p.Reg = REG_R3 472 473 // B.NE end 474 p = obj.Appendp(p, newprog) 475 p.As = ABNE 476 p.To.Type = obj.TYPE_BRANCH 477 p.Pcond = end 478 479 // ADD $4, R13, R4 480 p = obj.Appendp(p, newprog) 481 p.As = AADD 482 p.From.Type = obj.TYPE_CONST 483 p.From.Offset = 4 484 p.Reg = REG_R13 485 p.To.Type = obj.TYPE_REG 486 p.To.Reg = REG_R4 487 488 // MOVW R4, panic_argp(R1) 489 p = obj.Appendp(p, newprog) 490 p.As = AMOVW 491 p.From.Type = obj.TYPE_REG 492 p.From.Reg = REG_R4 493 p.To.Type = obj.TYPE_MEM 494 p.To.Reg = REG_R1 495 p.To.Offset = 0 // Panic.argp 496 497 // B end 498 p = obj.Appendp(p, newprog) 499 p.As = AB 500 p.To.Type = obj.TYPE_BRANCH 501 p.Pcond = end 502 503 // reset for subsequent passes 504 p = end 505 } 506 507 case obj.ARET: 508 nocache(p) 509 if cursym.Func.Text.Mark&LEAF != 0 { 510 if autosize == 0 { 511 p.As = AB 512 p.From = obj.Addr{} 513 if p.To.Sym != nil { // retjmp 514 p.To.Type = obj.TYPE_BRANCH 515 } else { 516 p.To.Type = obj.TYPE_MEM 517 p.To.Offset = 0 518 p.To.Reg = REGLINK 519 } 520 521 break 522 } 523 } 524 525 p.As = AMOVW 526 p.Scond |= C_PBIT 527 p.From.Type = obj.TYPE_MEM 528 p.From.Offset = int64(autosize) 529 p.From.Reg = REGSP 530 p.To.Type = obj.TYPE_REG 531 p.To.Reg = REGPC 532 533 // If there are instructions following 534 // this ARET, they come from a branch 535 // with the same stackframe, so no spadj. 536 if p.To.Sym != nil { // retjmp 537 p.To.Reg = REGLINK 538 q2 = obj.Appendp(p, newprog) 539 q2.As = AB 540 q2.To.Type = obj.TYPE_BRANCH 541 q2.To.Sym = p.To.Sym 542 p.To.Sym = nil 543 p = q2 544 } 545 546 case AADD: 547 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 548 p.Spadj = int32(-p.From.Offset) 549 } 550 551 case ASUB: 552 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 553 p.Spadj = int32(p.From.Offset) 554 } 555 556 case ADIV, ADIVU, AMOD, AMODU: 557 if cursym.Func.Text.From.Sym.NoSplit() { 558 ctxt.Diag("cannot divide in NOSPLIT function") 559 } 560 const debugdivmod = false 561 if debugdivmod { 562 break 563 } 564 if p.From.Type != obj.TYPE_REG { 565 break 566 } 567 if p.To.Type != obj.TYPE_REG { 568 break 569 } 570 571 // Make copy because we overwrite p below. 572 q1 := *p 573 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { 574 ctxt.Diag("div already using REGTMP: %v", p) 575 } 576 577 /* MOV m(g),REGTMP */ 578 p.As = AMOVW 579 p.Pos = q1.Pos 580 p.From.Type = obj.TYPE_MEM 581 p.From.Reg = REGG 582 p.From.Offset = 6 * 4 // offset of g.m 583 p.Reg = 0 584 p.To.Type = obj.TYPE_REG 585 p.To.Reg = REGTMP 586 587 /* MOV a,m_divmod(REGTMP) */ 588 p = obj.Appendp(p, newprog) 589 p.As = AMOVW 590 p.Pos = q1.Pos 591 p.From.Type = obj.TYPE_REG 592 p.From.Reg = q1.From.Reg 593 p.To.Type = obj.TYPE_MEM 594 p.To.Reg = REGTMP 595 p.To.Offset = 8 * 4 // offset of m.divmod 596 597 /* MOV b, R8 */ 598 p = obj.Appendp(p, newprog) 599 p.As = AMOVW 600 p.Pos = q1.Pos 601 p.From.Type = obj.TYPE_REG 602 p.From.Reg = q1.Reg 603 if q1.Reg == 0 { 604 p.From.Reg = q1.To.Reg 605 } 606 p.To.Type = obj.TYPE_REG 607 p.To.Reg = REG_R8 608 p.To.Offset = 0 609 610 /* CALL appropriate */ 611 p = obj.Appendp(p, newprog) 612 p.As = ABL 613 p.Pos = q1.Pos 614 p.To.Type = obj.TYPE_BRANCH 615 switch o { 616 case ADIV: 617 p.To.Sym = symdiv 618 case ADIVU: 619 p.To.Sym = symdivu 620 case AMOD: 621 p.To.Sym = symmod 622 case AMODU: 623 p.To.Sym = symmodu 624 } 625 626 /* MOV REGTMP, b */ 627 p = obj.Appendp(p, newprog) 628 p.As = AMOVW 629 p.Pos = q1.Pos 630 p.From.Type = obj.TYPE_REG 631 p.From.Reg = REGTMP 632 p.From.Offset = 0 633 p.To.Type = obj.TYPE_REG 634 p.To.Reg = q1.To.Reg 635 636 case AMOVW: 637 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 638 p.Spadj = int32(-p.To.Offset) 639 } 640 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { 641 p.Spadj = int32(-p.From.Offset) 642 } 643 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { 644 p.Spadj = int32(-p.From.Offset) 645 } 646 647 case obj.AGETCALLERPC: 648 if cursym.Leaf() { 649 /* MOVW LR, Rd */ 650 p.As = AMOVW 651 p.From.Type = obj.TYPE_REG 652 p.From.Reg = REGLINK 653 } else { 654 /* MOVW (RSP), Rd */ 655 p.As = AMOVW 656 p.From.Type = obj.TYPE_MEM 657 p.From.Reg = REGSP 658 } 659 } 660 } 661 } 662 663 func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { 664 // MOVW g_stackguard(g), R1 665 p = obj.Appendp(p, c.newprog) 666 667 p.As = AMOVW 668 p.From.Type = obj.TYPE_MEM 669 p.From.Reg = REGG 670 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 671 if c.cursym.CFunc() { 672 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 673 } 674 p.To.Type = obj.TYPE_REG 675 p.To.Reg = REG_R1 676 677 // Mark the stack bound check and morestack call async nonpreemptible. 678 // If we get preempted here, when resumed the preemption request is 679 // cleared, but we'll still call morestack, which will double the stack 680 // unnecessarily. See issue #35470. 681 p = c.ctxt.StartUnsafePoint(p, c.newprog) 682 683 if framesize <= objabi.StackSmall { 684 // small stack: SP < stackguard 685 // CMP stackguard, SP 686 p = obj.Appendp(p, c.newprog) 687 688 p.As = ACMP 689 p.From.Type = obj.TYPE_REG 690 p.From.Reg = REG_R1 691 p.Reg = REGSP 692 } else if framesize <= objabi.StackBig { 693 // large stack: SP-framesize < stackguard-StackSmall 694 // MOVW $-(framesize-StackSmall)(SP), R2 695 // CMP stackguard, R2 696 p = obj.Appendp(p, c.newprog) 697 698 p.As = AMOVW 699 p.From.Type = obj.TYPE_ADDR 700 p.From.Reg = REGSP 701 p.From.Offset = -(int64(framesize) - objabi.StackSmall) 702 p.To.Type = obj.TYPE_REG 703 p.To.Reg = REG_R2 704 705 p = obj.Appendp(p, c.newprog) 706 p.As = ACMP 707 p.From.Type = obj.TYPE_REG 708 p.From.Reg = REG_R1 709 p.Reg = REG_R2 710 } else { 711 // Such a large stack we need to protect against wraparound 712 // if SP is close to zero. 713 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 714 // The +StackGuard on both sides is required to keep the left side positive: 715 // SP is allowed to be slightly below stackguard. See stack.h. 716 // CMP $StackPreempt, R1 717 // MOVW.NE $StackGuard(SP), R2 718 // SUB.NE R1, R2 719 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3 720 // CMP.NE R3, R2 721 p = obj.Appendp(p, c.newprog) 722 723 p.As = ACMP 724 p.From.Type = obj.TYPE_CONST 725 p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1))) 726 p.Reg = REG_R1 727 728 p = obj.Appendp(p, c.newprog) 729 p.As = AMOVW 730 p.From.Type = obj.TYPE_ADDR 731 p.From.Reg = REGSP 732 p.From.Offset = int64(objabi.StackGuard) 733 p.To.Type = obj.TYPE_REG 734 p.To.Reg = REG_R2 735 p.Scond = C_SCOND_NE 736 737 p = obj.Appendp(p, c.newprog) 738 p.As = ASUB 739 p.From.Type = obj.TYPE_REG 740 p.From.Reg = REG_R1 741 p.To.Type = obj.TYPE_REG 742 p.To.Reg = REG_R2 743 p.Scond = C_SCOND_NE 744 745 p = obj.Appendp(p, c.newprog) 746 p.As = AMOVW 747 p.From.Type = obj.TYPE_ADDR 748 p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall) 749 p.To.Type = obj.TYPE_REG 750 p.To.Reg = REG_R3 751 p.Scond = C_SCOND_NE 752 753 p = obj.Appendp(p, c.newprog) 754 p.As = ACMP 755 p.From.Type = obj.TYPE_REG 756 p.From.Reg = REG_R3 757 p.Reg = REG_R2 758 p.Scond = C_SCOND_NE 759 } 760 761 // BLS call-to-morestack 762 bls := obj.Appendp(p, c.newprog) 763 bls.As = ABLS 764 bls.To.Type = obj.TYPE_BRANCH 765 766 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1) 767 768 var last *obj.Prog 769 for last = c.cursym.Func.Text; last.Link != nil; last = last.Link { 770 } 771 772 // Now we are at the end of the function, but logically 773 // we are still in function prologue. We need to fix the 774 // SP data and PCDATA. 775 spfix := obj.Appendp(last, c.newprog) 776 spfix.As = obj.ANOP 777 spfix.Spadj = -framesize 778 779 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog) 780 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog) 781 782 // MOVW LR, R3 783 movw := obj.Appendp(pcdata, c.newprog) 784 movw.As = AMOVW 785 movw.From.Type = obj.TYPE_REG 786 movw.From.Reg = REGLINK 787 movw.To.Type = obj.TYPE_REG 788 movw.To.Reg = REG_R3 789 790 bls.Pcond = movw 791 792 // BL runtime.morestack 793 call := obj.Appendp(movw, c.newprog) 794 call.As = obj.ACALL 795 call.To.Type = obj.TYPE_BRANCH 796 morestack := "runtime.morestack" 797 switch { 798 case c.cursym.CFunc(): 799 morestack = "runtime.morestackc" 800 case !c.cursym.Func.Text.From.Sym.NeedCtxt(): 801 morestack = "runtime.morestack_noctxt" 802 } 803 call.To.Sym = c.ctxt.Lookup(morestack) 804 805 pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1) 806 807 // B start 808 b := obj.Appendp(pcdata, c.newprog) 809 b.As = obj.AJMP 810 b.To.Type = obj.TYPE_BRANCH 811 b.Pcond = c.cursym.Func.Text.Link 812 b.Spadj = +framesize 813 814 return end 815 } 816 817 var unaryDst = map[obj.As]bool{ 818 ASWI: true, 819 AWORD: true, 820 } 821 822 var Linkarm = obj.LinkArch{ 823 Arch: sys.ArchARM, 824 Init: buildop, 825 Preprocess: preprocess, 826 Assemble: span5, 827 Progedit: progedit, 828 UnaryDst: unaryDst, 829 DWARFRegisters: ARMDWARFRegisters, 830 }