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