github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/loong64/obj.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package loong64 6 7 import ( 8 "log" 9 "math" 10 11 "github.com/go-asm/go/abi" 12 "github.com/go-asm/go/cmd/obj" 13 "github.com/go-asm/go/cmd/objabi" 14 "github.com/go-asm/go/cmd/sys" 15 ) 16 17 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 18 // Rewrite JMP/JAL to symbol as TYPE_BRANCH. 19 switch p.As { 20 case AJMP, 21 AJAL, 22 ARET, 23 obj.ADUFFZERO, 24 obj.ADUFFCOPY: 25 if p.To.Sym != nil { 26 p.To.Type = obj.TYPE_BRANCH 27 } 28 } 29 30 // Rewrite float constants to values stored in memory. 31 switch p.As { 32 case AMOVF: 33 if p.From.Type == obj.TYPE_FCONST { 34 f32 := float32(p.From.Val.(float64)) 35 if math.Float32bits(f32) == 0 { 36 p.As = AMOVW 37 p.From.Type = obj.TYPE_REG 38 p.From.Reg = REGZERO 39 break 40 } 41 p.From.Type = obj.TYPE_MEM 42 p.From.Sym = ctxt.Float32Sym(f32) 43 p.From.Name = obj.NAME_EXTERN 44 p.From.Offset = 0 45 } 46 47 case AMOVD: 48 if p.From.Type == obj.TYPE_FCONST { 49 f64 := p.From.Val.(float64) 50 if math.Float64bits(f64) == 0 { 51 p.As = AMOVV 52 p.From.Type = obj.TYPE_REG 53 p.From.Reg = REGZERO 54 break 55 } 56 p.From.Type = obj.TYPE_MEM 57 p.From.Sym = ctxt.Float64Sym(f64) 58 p.From.Name = obj.NAME_EXTERN 59 p.From.Offset = 0 60 } 61 } 62 63 // Rewrite SUB constants into ADD. 64 switch p.As { 65 case ASUB: 66 if p.From.Type == obj.TYPE_CONST { 67 p.From.Offset = -p.From.Offset 68 p.As = AADD 69 } 70 71 case ASUBU: 72 if p.From.Type == obj.TYPE_CONST { 73 p.From.Offset = -p.From.Offset 74 p.As = AADDU 75 } 76 77 case ASUBV: 78 if p.From.Type == obj.TYPE_CONST { 79 p.From.Offset = -p.From.Offset 80 p.As = AADDV 81 } 82 83 case ASUBVU: 84 if p.From.Type == obj.TYPE_CONST { 85 p.From.Offset = -p.From.Offset 86 p.As = AADDVU 87 } 88 } 89 90 if ctxt.Flag_dynlink { 91 rewriteToUseGot(ctxt, p, newprog) 92 } 93 } 94 95 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 96 // ADUFFxxx $offset 97 // becomes 98 // MOVV runtime.duffxxx@GOT, REGTMP 99 // ADD $offset, REGTMP 100 // JAL REGTMP 101 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 102 var sym *obj.LSym 103 if p.As == obj.ADUFFZERO { 104 sym = ctxt.Lookup("runtime.duffzero") 105 } else { 106 sym = ctxt.Lookup("runtime.duffcopy") 107 } 108 offset := p.To.Offset 109 p.As = AMOVV 110 p.From.Type = obj.TYPE_MEM 111 p.From.Sym = sym 112 p.From.Name = obj.NAME_GOTREF 113 p.To.Type = obj.TYPE_REG 114 p.To.Reg = REGTMP 115 p.To.Name = obj.NAME_NONE 116 p.To.Offset = 0 117 p.To.Sym = nil 118 p1 := obj.Appendp(p, newprog) 119 p1.As = AADDV 120 p1.From.Type = obj.TYPE_CONST 121 p1.From.Offset = offset 122 p1.To.Type = obj.TYPE_REG 123 p1.To.Reg = REGTMP 124 p2 := obj.Appendp(p1, newprog) 125 p2.As = AJAL 126 p2.To.Type = obj.TYPE_MEM 127 p2.To.Reg = REGTMP 128 } 129 130 // We only care about global data: NAME_EXTERN means a global 131 // symbol in the Go sense, and p.Sym.Local is true for a few 132 // internally defined symbols. 133 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 134 // MOVV $sym, Rx becomes MOVV sym@GOT, Rx 135 // MOVV $sym+<off>, Rx becomes MOVV sym@GOT, Rx; ADD <off>, Rx 136 if p.As != AMOVV { 137 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -shared", p) 138 } 139 if p.To.Type != obj.TYPE_REG { 140 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -shared", p) 141 } 142 p.From.Type = obj.TYPE_MEM 143 p.From.Name = obj.NAME_GOTREF 144 if p.From.Offset != 0 { 145 q := obj.Appendp(p, newprog) 146 q.As = AADDV 147 q.From.Type = obj.TYPE_CONST 148 q.From.Offset = p.From.Offset 149 q.To = p.To 150 p.From.Offset = 0 151 } 152 } 153 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { 154 ctxt.Diag("don't know how to handle %v with -shared", p) 155 } 156 157 var source *obj.Addr 158 // MOVx sym, Ry becomes MOVV sym@GOT, REGTMP; MOVx (REGTMP), Ry 159 // MOVx Ry, sym becomes MOVV sym@GOT, REGTMP; MOVx Ry, (REGTMP) 160 // An addition may be inserted between the two MOVs if there is an offset. 161 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 162 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 163 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -shared", p) 164 } 165 source = &p.From 166 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 167 source = &p.To 168 } else { 169 return 170 } 171 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 172 return 173 } 174 if source.Sym.Type == objabi.STLSBSS { 175 return 176 } 177 if source.Type != obj.TYPE_MEM { 178 ctxt.Diag("don't know how to handle %v with -shared", p) 179 } 180 p1 := obj.Appendp(p, newprog) 181 p2 := obj.Appendp(p1, newprog) 182 p1.As = AMOVV 183 p1.From.Type = obj.TYPE_MEM 184 p1.From.Sym = source.Sym 185 p1.From.Name = obj.NAME_GOTREF 186 p1.To.Type = obj.TYPE_REG 187 p1.To.Reg = REGTMP 188 189 p2.As = p.As 190 p2.From = p.From 191 p2.To = p.To 192 if p.From.Name == obj.NAME_EXTERN { 193 p2.From.Reg = REGTMP 194 p2.From.Name = obj.NAME_NONE 195 p2.From.Sym = nil 196 } else if p.To.Name == obj.NAME_EXTERN { 197 p2.To.Reg = REGTMP 198 p2.To.Name = obj.NAME_NONE 199 p2.To.Sym = nil 200 } else { 201 return 202 } 203 204 obj.Nopout(p) 205 } 206 207 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 208 c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym} 209 210 p := c.cursym.Func().Text 211 textstksiz := p.To.Offset 212 213 if textstksiz < 0 { 214 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz) 215 } 216 if p.From.Sym.NoFrame() { 217 if textstksiz != 0 { 218 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) 219 } 220 } 221 222 c.cursym.Func().Args = p.To.Val.(int32) 223 c.cursym.Func().Locals = int32(textstksiz) 224 225 /* 226 * find leaf subroutines 227 * expand RET 228 */ 229 230 for p := c.cursym.Func().Text; p != nil; p = p.Link { 231 switch p.As { 232 case obj.ATEXT: 233 p.Mark |= LABEL | LEAF | SYNC 234 if p.Link != nil { 235 p.Link.Mark |= LABEL 236 } 237 238 case AMOVW, 239 AMOVV: 240 if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL { 241 p.Mark |= LABEL | SYNC 242 break 243 } 244 if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL { 245 p.Mark |= LABEL | SYNC 246 } 247 248 case ASYSCALL, 249 AWORD: 250 p.Mark |= LABEL | SYNC 251 252 case ANOR: 253 if p.To.Type == obj.TYPE_REG { 254 if p.To.Reg == REGZERO { 255 p.Mark |= LABEL | SYNC 256 } 257 } 258 259 case AJAL, 260 obj.ADUFFZERO, 261 obj.ADUFFCOPY: 262 c.cursym.Func().Text.Mark &^= LEAF 263 fallthrough 264 265 case AJMP, 266 ABEQ, 267 ABGEU, 268 ABLTU, 269 ABLTZ, 270 ABNE, 271 ABFPT, ABFPF: 272 p.Mark |= BRANCH 273 q1 := p.To.Target() 274 if q1 != nil { 275 for q1.As == obj.ANOP { 276 q1 = q1.Link 277 p.To.SetTarget(q1) 278 } 279 280 if q1.Mark&LEAF == 0 { 281 q1.Mark |= LABEL 282 } 283 } 284 q1 = p.Link 285 if q1 != nil { 286 q1.Mark |= LABEL 287 } 288 289 case ARET: 290 if p.Link != nil { 291 p.Link.Mark |= LABEL 292 } 293 } 294 } 295 296 var mov, add obj.As 297 298 add = AADDV 299 mov = AMOVV 300 301 var q *obj.Prog 302 var q1 *obj.Prog 303 autosize := int32(0) 304 var p1 *obj.Prog 305 var p2 *obj.Prog 306 for p := c.cursym.Func().Text; p != nil; p = p.Link { 307 o := p.As 308 switch o { 309 case obj.ATEXT: 310 autosize = int32(textstksiz) 311 312 if p.Mark&LEAF != 0 && autosize == 0 { 313 // A leaf function with no locals has no frame. 314 p.From.Sym.Set(obj.AttrNoFrame, true) 315 } 316 317 if !p.From.Sym.NoFrame() { 318 // If there is a stack frame at all, it includes 319 // space to save the LR. 320 autosize += int32(c.ctxt.Arch.FixedFrameSize) 321 } 322 323 if autosize&4 != 0 { 324 autosize += 4 325 } 326 327 if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 { 328 if c.cursym.Func().Text.From.Sym.NoSplit() { 329 if ctxt.Debugvlog { 330 ctxt.Logf("save suppressed in: %s\n", c.cursym.Name) 331 } 332 333 c.cursym.Func().Text.Mark |= LEAF 334 } 335 } 336 337 p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize 338 339 if c.cursym.Func().Text.Mark&LEAF != 0 { 340 c.cursym.Set(obj.AttrLeaf, true) 341 if p.From.Sym.NoFrame() { 342 break 343 } 344 } 345 346 if !p.From.Sym.NoSplit() { 347 p = c.stacksplit(p, autosize) // emit split check 348 } 349 350 q = p 351 352 if autosize != 0 { 353 // Make sure to save link register for non-empty frame, even if 354 // it is a leaf function, so that traceback works. 355 // Store link register before decrement SP, so if a signal comes 356 // during the execution of the function prologue, the traceback 357 // code will not see a half-updated stack frame. 358 // This sequence is not async preemptible, as if we open a frame 359 // at the current SP, it will clobber the saved LR. 360 q = c.ctxt.StartUnsafePoint(q, c.newprog) 361 362 q = obj.Appendp(q, newprog) 363 q.As = mov 364 q.Pos = p.Pos 365 q.From.Type = obj.TYPE_REG 366 q.From.Reg = REGLINK 367 q.To.Type = obj.TYPE_MEM 368 q.To.Offset = int64(-autosize) 369 q.To.Reg = REGSP 370 371 q = obj.Appendp(q, newprog) 372 q.As = add 373 q.Pos = p.Pos 374 q.From.Type = obj.TYPE_CONST 375 q.From.Offset = int64(-autosize) 376 q.To.Type = obj.TYPE_REG 377 q.To.Reg = REGSP 378 q.Spadj = +autosize 379 380 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) 381 382 // On Linux, in a cgo binary we may get a SIGSETXID signal early on 383 // before the signal stack is set, as glibc doesn't allow us to block 384 // SIGSETXID. So a signal may land on the current stack and clobber 385 // the content below the SP. We store the LR again after the SP is 386 // decremented. 387 q = obj.Appendp(q, newprog) 388 q.As = mov 389 q.Pos = p.Pos 390 q.From.Type = obj.TYPE_REG 391 q.From.Reg = REGLINK 392 q.To.Type = obj.TYPE_MEM 393 q.To.Offset = 0 394 q.To.Reg = REGSP 395 } 396 397 if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 { 398 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 399 // 400 // MOV g_panic(g), R20 401 // BEQ R20, end 402 // MOV panic_argp(R20), R24 403 // ADD $(autosize+FIXED_FRAME), R3, R30 404 // BNE R24, R30, end 405 // ADD $FIXED_FRAME, R3, R24 406 // MOV R24, panic_argp(R20) 407 // end: 408 // NOP 409 // 410 // The NOP is needed to give the jumps somewhere to land. 411 // It is a liblink NOP, not a hardware NOP: it encodes to 0 instruction bytes. 412 // 413 // We don't generate this for leafs because that means the wrapped 414 // function was inlined into the wrapper. 415 416 q = obj.Appendp(q, newprog) 417 418 q.As = mov 419 q.From.Type = obj.TYPE_MEM 420 q.From.Reg = REGG 421 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 422 q.To.Type = obj.TYPE_REG 423 q.To.Reg = REG_R20 424 425 q = obj.Appendp(q, newprog) 426 q.As = ABEQ 427 q.From.Type = obj.TYPE_REG 428 q.From.Reg = REG_R20 429 q.To.Type = obj.TYPE_BRANCH 430 q.Mark |= BRANCH 431 p1 = q 432 433 q = obj.Appendp(q, newprog) 434 q.As = mov 435 q.From.Type = obj.TYPE_MEM 436 q.From.Reg = REG_R20 437 q.From.Offset = 0 // Panic.argp 438 q.To.Type = obj.TYPE_REG 439 q.To.Reg = REG_R24 440 441 q = obj.Appendp(q, newprog) 442 q.As = add 443 q.From.Type = obj.TYPE_CONST 444 q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize 445 q.Reg = REGSP 446 q.To.Type = obj.TYPE_REG 447 q.To.Reg = REG_R30 448 449 q = obj.Appendp(q, newprog) 450 q.As = ABNE 451 q.From.Type = obj.TYPE_REG 452 q.From.Reg = REG_R24 453 q.Reg = REG_R30 454 q.To.Type = obj.TYPE_BRANCH 455 q.Mark |= BRANCH 456 p2 = q 457 458 q = obj.Appendp(q, newprog) 459 q.As = add 460 q.From.Type = obj.TYPE_CONST 461 q.From.Offset = ctxt.Arch.FixedFrameSize 462 q.Reg = REGSP 463 q.To.Type = obj.TYPE_REG 464 q.To.Reg = REG_R24 465 466 q = obj.Appendp(q, newprog) 467 q.As = mov 468 q.From.Type = obj.TYPE_REG 469 q.From.Reg = REG_R24 470 q.To.Type = obj.TYPE_MEM 471 q.To.Reg = REG_R20 472 q.To.Offset = 0 // Panic.argp 473 474 q = obj.Appendp(q, newprog) 475 476 q.As = obj.ANOP 477 p1.To.SetTarget(q) 478 p2.To.SetTarget(q) 479 } 480 481 case ARET: 482 if p.From.Type == obj.TYPE_CONST { 483 ctxt.Diag("using BECOME (%v) is not supported!", p) 484 break 485 } 486 487 retSym := p.To.Sym 488 p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction 489 p.To.Sym = nil 490 491 if c.cursym.Func().Text.Mark&LEAF != 0 { 492 if autosize == 0 { 493 p.As = AJMP 494 p.From = obj.Addr{} 495 if retSym != nil { // retjmp 496 p.To.Type = obj.TYPE_BRANCH 497 p.To.Name = obj.NAME_EXTERN 498 p.To.Sym = retSym 499 } else { 500 p.To.Type = obj.TYPE_MEM 501 p.To.Reg = REGLINK 502 p.To.Offset = 0 503 } 504 p.Mark |= BRANCH 505 break 506 } 507 508 p.As = add 509 p.From.Type = obj.TYPE_CONST 510 p.From.Offset = int64(autosize) 511 p.To.Type = obj.TYPE_REG 512 p.To.Reg = REGSP 513 p.Spadj = -autosize 514 515 q = c.newprog() 516 q.As = AJMP 517 q.Pos = p.Pos 518 if retSym != nil { // retjmp 519 q.To.Type = obj.TYPE_BRANCH 520 q.To.Name = obj.NAME_EXTERN 521 q.To.Sym = retSym 522 } else { 523 q.To.Type = obj.TYPE_MEM 524 q.To.Offset = 0 525 q.To.Reg = REGLINK 526 } 527 q.Mark |= BRANCH 528 q.Spadj = +autosize 529 530 q.Link = p.Link 531 p.Link = q 532 break 533 } 534 535 p.As = mov 536 p.From.Type = obj.TYPE_MEM 537 p.From.Offset = 0 538 p.From.Reg = REGSP 539 p.To.Type = obj.TYPE_REG 540 p.To.Reg = REGLINK 541 542 if autosize != 0 { 543 q = c.newprog() 544 q.As = add 545 q.Pos = p.Pos 546 q.From.Type = obj.TYPE_CONST 547 q.From.Offset = int64(autosize) 548 q.To.Type = obj.TYPE_REG 549 q.To.Reg = REGSP 550 q.Spadj = -autosize 551 552 q.Link = p.Link 553 p.Link = q 554 } 555 556 q1 = c.newprog() 557 q1.As = AJMP 558 q1.Pos = p.Pos 559 if retSym != nil { // retjmp 560 q1.To.Type = obj.TYPE_BRANCH 561 q1.To.Name = obj.NAME_EXTERN 562 q1.To.Sym = retSym 563 } else { 564 q1.To.Type = obj.TYPE_MEM 565 q1.To.Offset = 0 566 q1.To.Reg = REGLINK 567 } 568 q1.Mark |= BRANCH 569 q1.Spadj = +autosize 570 571 q1.Link = q.Link 572 q.Link = q1 573 574 case AADD, 575 AADDU, 576 AADDV, 577 AADDVU: 578 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 579 p.Spadj = int32(-p.From.Offset) 580 } 581 582 case obj.AGETCALLERPC: 583 if cursym.Leaf() { 584 // MOV LR, Rd 585 p.As = mov 586 p.From.Type = obj.TYPE_REG 587 p.From.Reg = REGLINK 588 } else { 589 // MOV (RSP), Rd 590 p.As = mov 591 p.From.Type = obj.TYPE_MEM 592 p.From.Reg = REGSP 593 } 594 } 595 596 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 { 597 f := c.cursym.Func() 598 if f.FuncFlag&abi.FuncFlagSPWrite == 0 { 599 c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite 600 if ctxt.Debugvlog || !ctxt.IsAsm { 601 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p) 602 if !ctxt.IsAsm { 603 ctxt.Diag("invalid auto-SPWRITE in non-assembly") 604 ctxt.DiagFlush() 605 log.Fatalf("bad SPWRITE") 606 } 607 } 608 } 609 } 610 } 611 } 612 613 func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { 614 var mov, add obj.As 615 616 add = AADDV 617 mov = AMOVV 618 if c.ctxt.Flag_maymorestack != "" { 619 // Save LR and REGCTXT. 620 frameSize := 2 * c.ctxt.Arch.PtrSize 621 622 p = c.ctxt.StartUnsafePoint(p, c.newprog) 623 624 // Spill Arguments. This has to happen before we open 625 // any more frame space. 626 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog) 627 628 // MOV REGLINK, -8/-16(SP) 629 p = obj.Appendp(p, c.newprog) 630 p.As = mov 631 p.From.Type = obj.TYPE_REG 632 p.From.Reg = REGLINK 633 p.To.Type = obj.TYPE_MEM 634 p.To.Offset = int64(-frameSize) 635 p.To.Reg = REGSP 636 637 // MOV REGCTXT, -4/-8(SP) 638 p = obj.Appendp(p, c.newprog) 639 p.As = mov 640 p.From.Type = obj.TYPE_REG 641 p.From.Reg = REGCTXT 642 p.To.Type = obj.TYPE_MEM 643 p.To.Offset = -int64(c.ctxt.Arch.PtrSize) 644 p.To.Reg = REGSP 645 646 // ADD $-8/$-16, SP 647 p = obj.Appendp(p, c.newprog) 648 p.As = add 649 p.From.Type = obj.TYPE_CONST 650 p.From.Offset = int64(-frameSize) 651 p.To.Type = obj.TYPE_REG 652 p.To.Reg = REGSP 653 p.Spadj = int32(frameSize) 654 655 // JAL maymorestack 656 p = obj.Appendp(p, c.newprog) 657 p.As = AJAL 658 p.To.Type = obj.TYPE_BRANCH 659 // See ../x86/obj6.go 660 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI()) 661 p.Mark |= BRANCH 662 663 // Restore LR and REGCTXT. 664 665 // MOV 0(SP), REGLINK 666 p = obj.Appendp(p, c.newprog) 667 p.As = mov 668 p.From.Type = obj.TYPE_MEM 669 p.From.Offset = 0 670 p.From.Reg = REGSP 671 p.To.Type = obj.TYPE_REG 672 p.To.Reg = REGLINK 673 674 // MOV 4/8(SP), REGCTXT 675 p = obj.Appendp(p, c.newprog) 676 p.As = mov 677 p.From.Type = obj.TYPE_MEM 678 p.From.Offset = int64(c.ctxt.Arch.PtrSize) 679 p.From.Reg = REGSP 680 p.To.Type = obj.TYPE_REG 681 p.To.Reg = REGCTXT 682 683 // ADD $8/$16, SP 684 p = obj.Appendp(p, c.newprog) 685 p.As = add 686 p.From.Type = obj.TYPE_CONST 687 p.From.Offset = int64(frameSize) 688 p.To.Type = obj.TYPE_REG 689 p.To.Reg = REGSP 690 p.Spadj = int32(-frameSize) 691 692 // Unspill arguments 693 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog) 694 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 695 } 696 697 // Jump back to here after morestack returns. 698 startPred := p 699 700 // MOV g_stackguard(g), R20 701 p = obj.Appendp(p, c.newprog) 702 703 p.As = mov 704 p.From.Type = obj.TYPE_MEM 705 p.From.Reg = REGG 706 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 707 if c.cursym.CFunc() { 708 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 709 } 710 p.To.Type = obj.TYPE_REG 711 p.To.Reg = REG_R20 712 713 // Mark the stack bound check and morestack call async nonpreemptible. 714 // If we get preempted here, when resumed the preemption request is 715 // cleared, but we'll still call morestack, which will double the stack 716 // unnecessarily. See issue #35470. 717 p = c.ctxt.StartUnsafePoint(p, c.newprog) 718 719 var q *obj.Prog 720 if framesize <= abi.StackSmall { 721 // small stack: SP < stackguard 722 // AGTU SP, stackguard, R20 723 p = obj.Appendp(p, c.newprog) 724 725 p.As = ASGTU 726 p.From.Type = obj.TYPE_REG 727 p.From.Reg = REGSP 728 p.Reg = REG_R20 729 p.To.Type = obj.TYPE_REG 730 p.To.Reg = REG_R20 731 } else { 732 // large stack: SP-framesize < stackguard-StackSmall 733 offset := int64(framesize) - abi.StackSmall 734 if framesize > abi.StackBig { 735 // Such a large stack we need to protect against underflow. 736 // The runtime guarantees SP > objabi.StackBig, but 737 // framesize is large enough that SP-framesize may 738 // underflow, causing a direct comparison with the 739 // stack guard to incorrectly succeed. We explicitly 740 // guard against underflow. 741 // 742 // SGTU $(framesize-StackSmall), SP, R24 743 // BNE R24, label-of-call-to-morestack 744 745 p = obj.Appendp(p, c.newprog) 746 p.As = ASGTU 747 p.From.Type = obj.TYPE_CONST 748 p.From.Offset = offset 749 p.Reg = REGSP 750 p.To.Type = obj.TYPE_REG 751 p.To.Reg = REG_R24 752 753 p = obj.Appendp(p, c.newprog) 754 q = p 755 p.As = ABNE 756 p.From.Type = obj.TYPE_REG 757 p.From.Reg = REG_R24 758 p.To.Type = obj.TYPE_BRANCH 759 p.Mark |= BRANCH 760 } 761 762 p = obj.Appendp(p, c.newprog) 763 764 p.As = add 765 p.From.Type = obj.TYPE_CONST 766 p.From.Offset = -offset 767 p.Reg = REGSP 768 p.To.Type = obj.TYPE_REG 769 p.To.Reg = REG_R24 770 771 p = obj.Appendp(p, c.newprog) 772 p.As = ASGTU 773 p.From.Type = obj.TYPE_REG 774 p.From.Reg = REG_R24 775 p.Reg = REG_R20 776 p.To.Type = obj.TYPE_REG 777 p.To.Reg = REG_R20 778 } 779 780 // q1: BNE R20, done 781 p = obj.Appendp(p, c.newprog) 782 q1 := p 783 784 p.As = ABNE 785 p.From.Type = obj.TYPE_REG 786 p.From.Reg = REG_R20 787 p.To.Type = obj.TYPE_BRANCH 788 p.Mark |= BRANCH 789 790 // MOV LINK, R31 791 p = obj.Appendp(p, c.newprog) 792 793 p.As = mov 794 p.From.Type = obj.TYPE_REG 795 p.From.Reg = REGLINK 796 p.To.Type = obj.TYPE_REG 797 p.To.Reg = REG_R31 798 if q != nil { 799 q.To.SetTarget(p) 800 p.Mark |= LABEL 801 } 802 803 p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog) 804 805 // Spill the register args that could be clobbered by the 806 // morestack code 807 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog) 808 809 // JAL runtime.morestack(SB) 810 p = obj.Appendp(p, c.newprog) 811 812 p.As = AJAL 813 p.To.Type = obj.TYPE_BRANCH 814 if c.cursym.CFunc() { 815 p.To.Sym = c.ctxt.Lookup("runtime.morestackc") 816 } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { 817 p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") 818 } else { 819 p.To.Sym = c.ctxt.Lookup("runtime.morestack") 820 } 821 p.Mark |= BRANCH 822 823 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog) 824 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 825 826 // JMP start 827 p = obj.Appendp(p, c.newprog) 828 829 p.As = AJMP 830 p.To.Type = obj.TYPE_BRANCH 831 p.To.SetTarget(startPred.Link) 832 startPred.Link.Mark |= LABEL 833 p.Mark |= BRANCH 834 835 // placeholder for q1's jump target 836 p = obj.Appendp(p, c.newprog) 837 838 p.As = obj.ANOP // zero-width place holder 839 q1.To.SetTarget(p) 840 841 return p 842 } 843 844 func (c *ctxt0) addnop(p *obj.Prog) { 845 q := c.newprog() 846 q.As = ANOOP 847 q.Pos = p.Pos 848 q.Link = p.Link 849 p.Link = q 850 } 851 852 var Linkloong64 = obj.LinkArch{ 853 Arch: sys.ArchLoong64, 854 Init: buildop, 855 Preprocess: preprocess, 856 Assemble: span0, 857 Progedit: progedit, 858 DWARFRegisters: LOONG64DWARFRegisters, 859 }