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