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