github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/internal/obj/mips/obj0.go (about) 1 // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova. 2 // 3 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 4 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 5 // Portions Copyright © 1997-1999 Vita Nuova Limited 6 // Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) 7 // Portions Copyright © 2004,2006 Bruce Ellis 8 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 9 // Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others 10 // Portions Copyright © 2009 The Go Authors. All rights reserved. 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining a copy 13 // of this software and associated documentation files (the "Software"), to deal 14 // in the Software without restriction, including without limitation the rights 15 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 // copies of the Software, and to permit persons to whom the Software is 17 // furnished to do so, subject to the following conditions: 18 // 19 // The above copyright notice and this permission notice shall be included in 20 // all copies or substantial portions of the Software. 21 // 22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 // THE SOFTWARE. 29 30 package mips 31 32 import ( 33 "github.com/gagliardetto/golang-go/cmd/internal/obj" 34 "github.com/gagliardetto/golang-go/cmd/internal/objabi" 35 "github.com/gagliardetto/golang-go/cmd/internal/sys" 36 "encoding/binary" 37 "fmt" 38 "math" 39 ) 40 41 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 42 c := ctxt0{ctxt: ctxt, newprog: newprog} 43 44 p.From.Class = 0 45 p.To.Class = 0 46 47 // Rewrite JMP/JAL to symbol as TYPE_BRANCH. 48 switch p.As { 49 case AJMP, 50 AJAL, 51 ARET, 52 obj.ADUFFZERO, 53 obj.ADUFFCOPY: 54 if p.To.Sym != nil { 55 p.To.Type = obj.TYPE_BRANCH 56 } 57 } 58 59 // Rewrite float constants to values stored in memory. 60 switch p.As { 61 case AMOVF: 62 if p.From.Type == obj.TYPE_FCONST { 63 f32 := float32(p.From.Val.(float64)) 64 if math.Float32bits(f32) == 0 { 65 p.As = AMOVW 66 p.From.Type = obj.TYPE_REG 67 p.From.Reg = REGZERO 68 break 69 } 70 p.From.Type = obj.TYPE_MEM 71 p.From.Sym = ctxt.Float32Sym(f32) 72 p.From.Name = obj.NAME_EXTERN 73 p.From.Offset = 0 74 } 75 76 case AMOVD: 77 if p.From.Type == obj.TYPE_FCONST { 78 f64 := p.From.Val.(float64) 79 if math.Float64bits(f64) == 0 && c.ctxt.Arch.Family == sys.MIPS64 { 80 p.As = AMOVV 81 p.From.Type = obj.TYPE_REG 82 p.From.Reg = REGZERO 83 break 84 } 85 p.From.Type = obj.TYPE_MEM 86 p.From.Sym = ctxt.Float64Sym(f64) 87 p.From.Name = obj.NAME_EXTERN 88 p.From.Offset = 0 89 } 90 91 // Put >32-bit constants in memory and load them 92 case AMOVV: 93 if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset { 94 p.From.Type = obj.TYPE_MEM 95 p.From.Sym = ctxt.Int64Sym(p.From.Offset) 96 p.From.Name = obj.NAME_EXTERN 97 p.From.Offset = 0 98 } 99 } 100 101 // Rewrite SUB constants into ADD. 102 switch p.As { 103 case ASUB: 104 if p.From.Type == obj.TYPE_CONST { 105 p.From.Offset = -p.From.Offset 106 p.As = AADD 107 } 108 109 case ASUBU: 110 if p.From.Type == obj.TYPE_CONST { 111 p.From.Offset = -p.From.Offset 112 p.As = AADDU 113 } 114 115 case ASUBV: 116 if p.From.Type == obj.TYPE_CONST { 117 p.From.Offset = -p.From.Offset 118 p.As = AADDV 119 } 120 121 case ASUBVU: 122 if p.From.Type == obj.TYPE_CONST { 123 p.From.Offset = -p.From.Offset 124 p.As = AADDVU 125 } 126 } 127 } 128 129 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 130 // TODO(minux): add morestack short-cuts with small fixed frame-size. 131 c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym} 132 133 // a switch for enabling/disabling instruction scheduling 134 nosched := true 135 136 if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil { 137 return 138 } 139 140 p := c.cursym.Func.Text 141 textstksiz := p.To.Offset 142 if textstksiz == -ctxt.FixedFrameSize() { 143 // Historical way to mark NOFRAME. 144 p.From.Sym.Set(obj.AttrNoFrame, true) 145 textstksiz = 0 146 } 147 if textstksiz < 0 { 148 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz) 149 } 150 if p.From.Sym.NoFrame() { 151 if textstksiz != 0 { 152 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) 153 } 154 } 155 156 c.cursym.Func.Args = p.To.Val.(int32) 157 c.cursym.Func.Locals = int32(textstksiz) 158 159 /* 160 * find leaf subroutines 161 * strip NOPs 162 * expand RET 163 * expand BECOME pseudo 164 */ 165 166 var q *obj.Prog 167 var q1 *obj.Prog 168 for p := c.cursym.Func.Text; p != nil; p = p.Link { 169 switch p.As { 170 /* too hard, just leave alone */ 171 case obj.ATEXT: 172 q = p 173 174 p.Mark |= LABEL | LEAF | SYNC 175 if p.Link != nil { 176 p.Link.Mark |= LABEL 177 } 178 179 /* too hard, just leave alone */ 180 case AMOVW, 181 AMOVV: 182 q = p 183 if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL { 184 p.Mark |= LABEL | SYNC 185 break 186 } 187 if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL { 188 p.Mark |= LABEL | SYNC 189 } 190 191 /* too hard, just leave alone */ 192 case ASYSCALL, 193 AWORD, 194 ATLBWR, 195 ATLBWI, 196 ATLBP, 197 ATLBR: 198 q = p 199 p.Mark |= LABEL | SYNC 200 201 case ANOR: 202 q = p 203 if p.To.Type == obj.TYPE_REG { 204 if p.To.Reg == REGZERO { 205 p.Mark |= LABEL | SYNC 206 } 207 } 208 209 case ABGEZAL, 210 ABLTZAL, 211 AJAL, 212 obj.ADUFFZERO, 213 obj.ADUFFCOPY: 214 c.cursym.Func.Text.Mark &^= LEAF 215 fallthrough 216 217 case AJMP, 218 ABEQ, 219 ABGEZ, 220 ABGTZ, 221 ABLEZ, 222 ABLTZ, 223 ABNE, 224 ABFPT, ABFPF: 225 if p.As == ABFPT || p.As == ABFPF { 226 // We don't treat ABFPT and ABFPF as branches here, 227 // so that we will always fill nop (0x0) in their 228 // delay slot during assembly. 229 // This is to workaround a kernel FPU emulator bug 230 // where it uses the user stack to simulate the 231 // instruction in the delay slot if it's not 0x0, 232 // and somehow that leads to SIGSEGV when the kernel 233 // jump to the stack. 234 p.Mark |= SYNC 235 } else { 236 p.Mark |= BRANCH 237 } 238 q = p 239 q1 = p.Pcond 240 if q1 != nil { 241 for q1.As == obj.ANOP { 242 q1 = q1.Link 243 p.Pcond = q1 244 } 245 246 if q1.Mark&LEAF == 0 { 247 q1.Mark |= LABEL 248 } 249 } 250 //else { 251 // p.Mark |= LABEL 252 //} 253 q1 = p.Link 254 if q1 != nil { 255 q1.Mark |= LABEL 256 } 257 continue 258 259 case ARET: 260 q = p 261 if p.Link != nil { 262 p.Link.Mark |= LABEL 263 } 264 continue 265 266 case obj.ANOP: 267 q1 = p.Link 268 q.Link = q1 /* q is non-nop */ 269 q1.Mark |= p.Mark 270 continue 271 272 default: 273 q = p 274 continue 275 } 276 } 277 278 var mov, add obj.As 279 if c.ctxt.Arch.Family == sys.MIPS64 { 280 add = AADDV 281 mov = AMOVV 282 } else { 283 add = AADDU 284 mov = AMOVW 285 } 286 287 autosize := int32(0) 288 var p1 *obj.Prog 289 var p2 *obj.Prog 290 for p := c.cursym.Func.Text; p != nil; p = p.Link { 291 o := p.As 292 switch o { 293 case obj.ATEXT: 294 autosize = int32(textstksiz) 295 296 if p.Mark&LEAF != 0 && autosize == 0 { 297 // A leaf function with no locals has no frame. 298 p.From.Sym.Set(obj.AttrNoFrame, true) 299 } 300 301 if !p.From.Sym.NoFrame() { 302 // If there is a stack frame at all, it includes 303 // space to save the LR. 304 autosize += int32(c.ctxt.FixedFrameSize()) 305 } 306 307 if autosize&4 != 0 && c.ctxt.Arch.Family == sys.MIPS64 { 308 autosize += 4 309 } 310 311 if autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { 312 if c.cursym.Func.Text.From.Sym.NoSplit() { 313 if ctxt.Debugvlog { 314 ctxt.Logf("save suppressed in: %s\n", c.cursym.Name) 315 } 316 317 c.cursym.Func.Text.Mark |= LEAF 318 } 319 } 320 321 p.To.Offset = int64(autosize) - ctxt.FixedFrameSize() 322 323 if c.cursym.Func.Text.Mark&LEAF != 0 { 324 c.cursym.Set(obj.AttrLeaf, true) 325 if p.From.Sym.NoFrame() { 326 break 327 } 328 } 329 330 if !p.From.Sym.NoSplit() { 331 p = c.stacksplit(p, autosize) // emit split check 332 } 333 334 q = p 335 336 if autosize != 0 { 337 // Make sure to save link register for non-empty frame, even if 338 // it is a leaf function, so that traceback works. 339 // Store link register before decrement SP, so if a signal comes 340 // during the execution of the function prologue, the traceback 341 // code will not see a half-updated stack frame. 342 // This sequence is not async preemptible, as if we open a frame 343 // at the current SP, it will clobber the saved LR. 344 q = c.ctxt.StartUnsafePoint(q, c.newprog) 345 346 q = obj.Appendp(q, newprog) 347 q.As = mov 348 q.Pos = p.Pos 349 q.From.Type = obj.TYPE_REG 350 q.From.Reg = REGLINK 351 q.To.Type = obj.TYPE_MEM 352 q.To.Offset = int64(-autosize) 353 q.To.Reg = REGSP 354 355 q = obj.Appendp(q, newprog) 356 q.As = add 357 q.Pos = p.Pos 358 q.From.Type = obj.TYPE_CONST 359 q.From.Offset = int64(-autosize) 360 q.To.Type = obj.TYPE_REG 361 q.To.Reg = REGSP 362 q.Spadj = +autosize 363 364 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) 365 } 366 367 if c.cursym.Func.Text.From.Sym.Wrapper() && c.cursym.Func.Text.Mark&LEAF == 0 { 368 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 369 // 370 // MOV g_panic(g), R1 371 // BEQ R1, end 372 // MOV panic_argp(R1), R2 373 // ADD $(autosize+FIXED_FRAME), R29, R3 374 // BNE R2, R3, end 375 // ADD $FIXED_FRAME, R29, R2 376 // MOV R2, panic_argp(R1) 377 // end: 378 // NOP 379 // 380 // The NOP is needed to give the jumps somewhere to land. 381 // It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes. 382 // 383 // We don't generate this for leafs because that means the wrapped 384 // function was inlined into the wrapper. 385 386 q = obj.Appendp(q, newprog) 387 388 q.As = mov 389 q.From.Type = obj.TYPE_MEM 390 q.From.Reg = REGG 391 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 392 q.To.Type = obj.TYPE_REG 393 q.To.Reg = REG_R1 394 395 q = obj.Appendp(q, newprog) 396 q.As = ABEQ 397 q.From.Type = obj.TYPE_REG 398 q.From.Reg = REG_R1 399 q.To.Type = obj.TYPE_BRANCH 400 q.Mark |= BRANCH 401 p1 = q 402 403 q = obj.Appendp(q, newprog) 404 q.As = mov 405 q.From.Type = obj.TYPE_MEM 406 q.From.Reg = REG_R1 407 q.From.Offset = 0 // Panic.argp 408 q.To.Type = obj.TYPE_REG 409 q.To.Reg = REG_R2 410 411 q = obj.Appendp(q, newprog) 412 q.As = add 413 q.From.Type = obj.TYPE_CONST 414 q.From.Offset = int64(autosize) + ctxt.FixedFrameSize() 415 q.Reg = REGSP 416 q.To.Type = obj.TYPE_REG 417 q.To.Reg = REG_R3 418 419 q = obj.Appendp(q, newprog) 420 q.As = ABNE 421 q.From.Type = obj.TYPE_REG 422 q.From.Reg = REG_R2 423 q.Reg = REG_R3 424 q.To.Type = obj.TYPE_BRANCH 425 q.Mark |= BRANCH 426 p2 = q 427 428 q = obj.Appendp(q, newprog) 429 q.As = add 430 q.From.Type = obj.TYPE_CONST 431 q.From.Offset = ctxt.FixedFrameSize() 432 q.Reg = REGSP 433 q.To.Type = obj.TYPE_REG 434 q.To.Reg = REG_R2 435 436 q = obj.Appendp(q, newprog) 437 q.As = mov 438 q.From.Type = obj.TYPE_REG 439 q.From.Reg = REG_R2 440 q.To.Type = obj.TYPE_MEM 441 q.To.Reg = REG_R1 442 q.To.Offset = 0 // Panic.argp 443 444 q = obj.Appendp(q, newprog) 445 446 q.As = obj.ANOP 447 p1.Pcond = q 448 p2.Pcond = q 449 } 450 451 case ARET: 452 if p.From.Type == obj.TYPE_CONST { 453 ctxt.Diag("using BECOME (%v) is not supported!", p) 454 break 455 } 456 457 retSym := p.To.Sym 458 p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction 459 p.To.Sym = nil 460 461 if c.cursym.Func.Text.Mark&LEAF != 0 { 462 if autosize == 0 { 463 p.As = AJMP 464 p.From = obj.Addr{} 465 if retSym != nil { // retjmp 466 p.To.Type = obj.TYPE_BRANCH 467 p.To.Name = obj.NAME_EXTERN 468 p.To.Sym = retSym 469 } else { 470 p.To.Type = obj.TYPE_MEM 471 p.To.Reg = REGLINK 472 p.To.Offset = 0 473 } 474 p.Mark |= BRANCH 475 break 476 } 477 478 p.As = add 479 p.From.Type = obj.TYPE_CONST 480 p.From.Offset = int64(autosize) 481 p.To.Type = obj.TYPE_REG 482 p.To.Reg = REGSP 483 p.Spadj = -autosize 484 485 q = c.newprog() 486 q.As = AJMP 487 q.Pos = p.Pos 488 q.To.Type = obj.TYPE_MEM 489 q.To.Offset = 0 490 q.To.Reg = REGLINK 491 q.Mark |= BRANCH 492 q.Spadj = +autosize 493 494 q.Link = p.Link 495 p.Link = q 496 break 497 } 498 499 p.As = mov 500 p.From.Type = obj.TYPE_MEM 501 p.From.Offset = 0 502 p.From.Reg = REGSP 503 p.To.Type = obj.TYPE_REG 504 p.To.Reg = REGLINK 505 506 if autosize != 0 { 507 q = c.newprog() 508 q.As = add 509 q.Pos = p.Pos 510 q.From.Type = obj.TYPE_CONST 511 q.From.Offset = int64(autosize) 512 q.To.Type = obj.TYPE_REG 513 q.To.Reg = REGSP 514 q.Spadj = -autosize 515 516 q.Link = p.Link 517 p.Link = q 518 } 519 520 q1 = c.newprog() 521 q1.As = AJMP 522 q1.Pos = p.Pos 523 if retSym != nil { // retjmp 524 q1.To.Type = obj.TYPE_BRANCH 525 q1.To.Name = obj.NAME_EXTERN 526 q1.To.Sym = retSym 527 } else { 528 q1.To.Type = obj.TYPE_MEM 529 q1.To.Offset = 0 530 q1.To.Reg = REGLINK 531 } 532 q1.Mark |= BRANCH 533 q1.Spadj = +autosize 534 535 q1.Link = q.Link 536 q.Link = q1 537 538 case AADD, 539 AADDU, 540 AADDV, 541 AADDVU: 542 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 543 p.Spadj = int32(-p.From.Offset) 544 } 545 546 case obj.AGETCALLERPC: 547 if cursym.Leaf() { 548 /* MOV LR, Rd */ 549 p.As = mov 550 p.From.Type = obj.TYPE_REG 551 p.From.Reg = REGLINK 552 } else { 553 /* MOV (RSP), Rd */ 554 p.As = mov 555 p.From.Type = obj.TYPE_MEM 556 p.From.Reg = REGSP 557 } 558 } 559 } 560 561 if c.ctxt.Arch.Family == sys.MIPS { 562 // rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access 563 for p = c.cursym.Func.Text; p != nil; p = p1 { 564 p1 = p.Link 565 566 if p.As != AMOVD { 567 continue 568 } 569 if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM { 570 continue 571 } 572 573 p.As = AMOVF 574 q = c.newprog() 575 *q = *p 576 q.Link = p.Link 577 p.Link = q 578 p1 = q.Link 579 580 var addrOff int64 581 if c.ctxt.Arch.ByteOrder == binary.BigEndian { 582 addrOff = 4 // swap load/save order 583 } 584 if p.From.Type == obj.TYPE_MEM { 585 reg := REG_F0 + (p.To.Reg-REG_F0)&^1 586 p.To.Reg = reg 587 q.To.Reg = reg + 1 588 p.From.Offset += addrOff 589 q.From.Offset += 4 - addrOff 590 } else if p.To.Type == obj.TYPE_MEM { 591 reg := REG_F0 + (p.From.Reg-REG_F0)&^1 592 p.From.Reg = reg 593 q.From.Reg = reg + 1 594 p.To.Offset += addrOff 595 q.To.Offset += 4 - addrOff 596 } 597 } 598 } 599 600 if nosched { 601 // if we don't do instruction scheduling, simply add 602 // NOP after each branch instruction. 603 for p = c.cursym.Func.Text; p != nil; p = p.Link { 604 if p.Mark&BRANCH != 0 { 605 c.addnop(p) 606 } 607 } 608 return 609 } 610 611 // instruction scheduling 612 q = nil // p - 1 613 q1 = c.cursym.Func.Text // top of block 614 o := 0 // count of instructions 615 for p = c.cursym.Func.Text; p != nil; p = p1 { 616 p1 = p.Link 617 o++ 618 if p.Mark&NOSCHED != 0 { 619 if q1 != p { 620 c.sched(q1, q) 621 } 622 for ; p != nil; p = p.Link { 623 if p.Mark&NOSCHED == 0 { 624 break 625 } 626 q = p 627 } 628 p1 = p 629 q1 = p 630 o = 0 631 continue 632 } 633 if p.Mark&(LABEL|SYNC) != 0 { 634 if q1 != p { 635 c.sched(q1, q) 636 } 637 q1 = p 638 o = 1 639 } 640 if p.Mark&(BRANCH|SYNC) != 0 { 641 c.sched(q1, p) 642 q1 = p1 643 o = 0 644 } 645 if o >= NSCHED { 646 c.sched(q1, p) 647 q1 = p1 648 o = 0 649 } 650 q = p 651 } 652 } 653 654 func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { 655 var mov, add, sub obj.As 656 657 if c.ctxt.Arch.Family == sys.MIPS64 { 658 add = AADDV 659 mov = AMOVV 660 sub = ASUBVU 661 } else { 662 add = AADDU 663 mov = AMOVW 664 sub = ASUBU 665 } 666 667 // MOV g_stackguard(g), R1 668 p = obj.Appendp(p, c.newprog) 669 670 p.As = mov 671 p.From.Type = obj.TYPE_MEM 672 p.From.Reg = REGG 673 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 674 if c.cursym.CFunc() { 675 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 676 } 677 p.To.Type = obj.TYPE_REG 678 p.To.Reg = REG_R1 679 680 // Mark the stack bound check and morestack call async nonpreemptible. 681 // If we get preempted here, when resumed the preemption request is 682 // cleared, but we'll still call morestack, which will double the stack 683 // unnecessarily. See issue #35470. 684 p = c.ctxt.StartUnsafePoint(p, c.newprog) 685 686 var q *obj.Prog 687 if framesize <= objabi.StackSmall { 688 // small stack: SP < stackguard 689 // AGTU SP, stackguard, R1 690 p = obj.Appendp(p, c.newprog) 691 692 p.As = ASGTU 693 p.From.Type = obj.TYPE_REG 694 p.From.Reg = REGSP 695 p.Reg = REG_R1 696 p.To.Type = obj.TYPE_REG 697 p.To.Reg = REG_R1 698 } else if framesize <= objabi.StackBig { 699 // large stack: SP-framesize < stackguard-StackSmall 700 // ADD $-(framesize-StackSmall), SP, R2 701 // SGTU R2, stackguard, R1 702 p = obj.Appendp(p, c.newprog) 703 704 p.As = add 705 p.From.Type = obj.TYPE_CONST 706 p.From.Offset = -(int64(framesize) - objabi.StackSmall) 707 p.Reg = REGSP 708 p.To.Type = obj.TYPE_REG 709 p.To.Reg = REG_R2 710 711 p = obj.Appendp(p, c.newprog) 712 p.As = ASGTU 713 p.From.Type = obj.TYPE_REG 714 p.From.Reg = REG_R2 715 p.Reg = REG_R1 716 p.To.Type = obj.TYPE_REG 717 p.To.Reg = REG_R1 718 } else { 719 // Such a large stack we need to protect against wraparound. 720 // If SP is close to zero: 721 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 722 // The +StackGuard on both sides is required to keep the left side positive: 723 // SP is allowed to be slightly below stackguard. See stack.h. 724 // 725 // Preemption sets stackguard to StackPreempt, a very large value. 726 // That breaks the math above, so we have to check for that explicitly. 727 // // stackguard is R1 728 // MOV $StackPreempt, R2 729 // BEQ R1, R2, label-of-call-to-morestack 730 // ADD $StackGuard, SP, R2 731 // SUB R1, R2 732 // MOV $(framesize+(StackGuard-StackSmall)), R1 733 // SGTU R2, R1, R1 734 p = obj.Appendp(p, c.newprog) 735 736 p.As = mov 737 p.From.Type = obj.TYPE_CONST 738 p.From.Offset = objabi.StackPreempt 739 p.To.Type = obj.TYPE_REG 740 p.To.Reg = REG_R2 741 742 p = obj.Appendp(p, c.newprog) 743 q = p 744 p.As = ABEQ 745 p.From.Type = obj.TYPE_REG 746 p.From.Reg = REG_R1 747 p.Reg = REG_R2 748 p.To.Type = obj.TYPE_BRANCH 749 p.Mark |= BRANCH 750 751 p = obj.Appendp(p, c.newprog) 752 p.As = add 753 p.From.Type = obj.TYPE_CONST 754 p.From.Offset = int64(objabi.StackGuard) 755 p.Reg = REGSP 756 p.To.Type = obj.TYPE_REG 757 p.To.Reg = REG_R2 758 759 p = obj.Appendp(p, c.newprog) 760 p.As = sub 761 p.From.Type = obj.TYPE_REG 762 p.From.Reg = REG_R1 763 p.To.Type = obj.TYPE_REG 764 p.To.Reg = REG_R2 765 766 p = obj.Appendp(p, c.newprog) 767 p.As = mov 768 p.From.Type = obj.TYPE_CONST 769 p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall 770 p.To.Type = obj.TYPE_REG 771 p.To.Reg = REG_R1 772 773 p = obj.Appendp(p, c.newprog) 774 p.As = ASGTU 775 p.From.Type = obj.TYPE_REG 776 p.From.Reg = REG_R2 777 p.Reg = REG_R1 778 p.To.Type = obj.TYPE_REG 779 p.To.Reg = REG_R1 780 } 781 782 // q1: BNE R1, done 783 p = obj.Appendp(p, c.newprog) 784 q1 := p 785 786 p.As = ABNE 787 p.From.Type = obj.TYPE_REG 788 p.From.Reg = REG_R1 789 p.To.Type = obj.TYPE_BRANCH 790 p.Mark |= BRANCH 791 792 // MOV LINK, R3 793 p = obj.Appendp(p, c.newprog) 794 795 p.As = mov 796 p.From.Type = obj.TYPE_REG 797 p.From.Reg = REGLINK 798 p.To.Type = obj.TYPE_REG 799 p.To.Reg = REG_R3 800 if q != nil { 801 q.Pcond = p 802 p.Mark |= LABEL 803 } 804 805 p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog) 806 807 // JAL runtime.morestack(SB) 808 p = obj.Appendp(p, c.newprog) 809 810 p.As = AJAL 811 p.To.Type = obj.TYPE_BRANCH 812 if c.cursym.CFunc() { 813 p.To.Sym = c.ctxt.Lookup("runtime.morestackc") 814 } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { 815 p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") 816 } else { 817 p.To.Sym = c.ctxt.Lookup("runtime.morestack") 818 } 819 p.Mark |= BRANCH 820 821 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 822 823 // JMP start 824 p = obj.Appendp(p, c.newprog) 825 826 p.As = AJMP 827 p.To.Type = obj.TYPE_BRANCH 828 p.Pcond = c.cursym.Func.Text.Link 829 p.Mark |= BRANCH 830 831 // placeholder for q1's jump target 832 p = obj.Appendp(p, c.newprog) 833 834 p.As = obj.ANOP // zero-width place holder 835 q1.Pcond = p 836 837 return p 838 } 839 840 func (c *ctxt0) addnop(p *obj.Prog) { 841 q := c.newprog() 842 q.As = ANOOP 843 q.Pos = p.Pos 844 q.Link = p.Link 845 p.Link = q 846 } 847 848 const ( 849 E_HILO = 1 << 0 850 E_FCR = 1 << 1 851 E_MCR = 1 << 2 852 E_MEM = 1 << 3 853 E_MEMSP = 1 << 4 /* uses offset and size */ 854 E_MEMSB = 1 << 5 /* uses offset and size */ 855 ANYMEM = E_MEM | E_MEMSP | E_MEMSB 856 //DELAY = LOAD|BRANCH|FCMP 857 DELAY = BRANCH /* only schedule branch */ 858 ) 859 860 type Dep struct { 861 ireg uint32 862 freg uint32 863 cc uint32 864 } 865 866 type Sch struct { 867 p obj.Prog 868 set Dep 869 used Dep 870 soffset int32 871 size uint8 872 nop uint8 873 comp bool 874 } 875 876 func (c *ctxt0) sched(p0, pe *obj.Prog) { 877 var sch [NSCHED]Sch 878 879 /* 880 * build side structure 881 */ 882 s := sch[:] 883 for p := p0; ; p = p.Link { 884 s[0].p = *p 885 c.markregused(&s[0]) 886 if p == pe { 887 break 888 } 889 s = s[1:] 890 } 891 se := s 892 893 for i := cap(sch) - cap(se); i >= 0; i-- { 894 s = sch[i:] 895 if s[0].p.Mark&DELAY == 0 { 896 continue 897 } 898 if -cap(s) < -cap(se) { 899 if !conflict(&s[0], &s[1]) { 900 continue 901 } 902 } 903 904 var t []Sch 905 var j int 906 for j = cap(sch) - cap(s) - 1; j >= 0; j-- { 907 t = sch[j:] 908 if t[0].comp { 909 if s[0].p.Mark&BRANCH != 0 { 910 continue 911 } 912 } 913 if t[0].p.Mark&DELAY != 0 { 914 if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) { 915 continue 916 } 917 } 918 for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] { 919 if c.depend(&u[0], &t[0]) { 920 continue 921 } 922 } 923 goto out2 924 } 925 926 if s[0].p.Mark&BRANCH != 0 { 927 s[0].nop = 1 928 } 929 continue 930 931 out2: 932 // t[0] is the instruction being moved to fill the delay 933 stmp := t[0] 934 copy(t[:i-j], t[1:i-j+1]) 935 s[0] = stmp 936 937 if t[i-j-1].p.Mark&BRANCH != 0 { 938 // t[i-j] is being put into a branch delay slot 939 // combine its Spadj with the branch instruction 940 t[i-j-1].p.Spadj += t[i-j].p.Spadj 941 t[i-j].p.Spadj = 0 942 } 943 944 i-- 945 } 946 947 /* 948 * put it all back 949 */ 950 var p *obj.Prog 951 var q *obj.Prog 952 for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q { 953 q = p.Link 954 if q != s[0].p.Link { 955 *p = s[0].p 956 p.Link = q 957 } 958 for s[0].nop != 0 { 959 s[0].nop-- 960 c.addnop(p) 961 } 962 } 963 } 964 965 func (c *ctxt0) markregused(s *Sch) { 966 p := &s.p 967 s.comp = c.compound(p) 968 s.nop = 0 969 if s.comp { 970 s.set.ireg |= 1 << (REGTMP - REG_R0) 971 s.used.ireg |= 1 << (REGTMP - REG_R0) 972 } 973 974 ar := 0 /* dest is really reference */ 975 ad := 0 /* source/dest is really address */ 976 ld := 0 /* opcode is load instruction */ 977 sz := 20 /* size of load/store for overlap computation */ 978 979 /* 980 * flags based on opcode 981 */ 982 switch p.As { 983 case obj.ATEXT: 984 c.autosize = int32(p.To.Offset + 8) 985 ad = 1 986 987 case AJAL: 988 r := p.Reg 989 if r == 0 { 990 r = REGLINK 991 } 992 s.set.ireg |= 1 << uint(r-REG_R0) 993 ar = 1 994 ad = 1 995 996 case ABGEZAL, 997 ABLTZAL: 998 s.set.ireg |= 1 << (REGLINK - REG_R0) 999 fallthrough 1000 case ABEQ, 1001 ABGEZ, 1002 ABGTZ, 1003 ABLEZ, 1004 ABLTZ, 1005 ABNE: 1006 ar = 1 1007 ad = 1 1008 1009 case ABFPT, 1010 ABFPF: 1011 ad = 1 1012 s.used.cc |= E_FCR 1013 1014 case ACMPEQD, 1015 ACMPEQF, 1016 ACMPGED, 1017 ACMPGEF, 1018 ACMPGTD, 1019 ACMPGTF: 1020 ar = 1 1021 s.set.cc |= E_FCR 1022 p.Mark |= FCMP 1023 1024 case AJMP: 1025 ar = 1 1026 ad = 1 1027 1028 case AMOVB, 1029 AMOVBU: 1030 sz = 1 1031 ld = 1 1032 1033 case AMOVH, 1034 AMOVHU: 1035 sz = 2 1036 ld = 1 1037 1038 case AMOVF, 1039 AMOVW, 1040 AMOVWL, 1041 AMOVWR: 1042 sz = 4 1043 ld = 1 1044 1045 case AMOVD, 1046 AMOVV, 1047 AMOVVL, 1048 AMOVVR: 1049 sz = 8 1050 ld = 1 1051 1052 case ADIV, 1053 ADIVU, 1054 AMUL, 1055 AMULU, 1056 AREM, 1057 AREMU, 1058 ADIVV, 1059 ADIVVU, 1060 AMULV, 1061 AMULVU, 1062 AREMV, 1063 AREMVU: 1064 s.set.cc = E_HILO 1065 fallthrough 1066 case AADD, 1067 AADDU, 1068 AADDV, 1069 AADDVU, 1070 AAND, 1071 ANOR, 1072 AOR, 1073 ASGT, 1074 ASGTU, 1075 ASLL, 1076 ASRA, 1077 ASRL, 1078 ASLLV, 1079 ASRAV, 1080 ASRLV, 1081 ASUB, 1082 ASUBU, 1083 ASUBV, 1084 ASUBVU, 1085 AXOR, 1086 1087 AADDD, 1088 AADDF, 1089 AADDW, 1090 ASUBD, 1091 ASUBF, 1092 ASUBW, 1093 AMULF, 1094 AMULD, 1095 AMULW, 1096 ADIVF, 1097 ADIVD, 1098 ADIVW: 1099 if p.Reg == 0 { 1100 if p.To.Type == obj.TYPE_REG { 1101 p.Reg = p.To.Reg 1102 } 1103 //if(p->reg == NREG) 1104 // print("botch %P\n", p); 1105 } 1106 } 1107 1108 /* 1109 * flags based on 'to' field 1110 */ 1111 cls := int(p.To.Class) 1112 if cls == 0 { 1113 cls = c.aclass(&p.To) + 1 1114 p.To.Class = int8(cls) 1115 } 1116 cls-- 1117 switch cls { 1118 default: 1119 fmt.Printf("unknown class %d %v\n", cls, p) 1120 1121 case C_ZCON, 1122 C_SCON, 1123 C_ADD0CON, 1124 C_AND0CON, 1125 C_ADDCON, 1126 C_ANDCON, 1127 C_UCON, 1128 C_LCON, 1129 C_NONE, 1130 C_SBRA, 1131 C_LBRA, 1132 C_ADDR, 1133 C_TEXTSIZE: 1134 break 1135 1136 case C_HI, 1137 C_LO: 1138 s.set.cc |= E_HILO 1139 1140 case C_FCREG: 1141 s.set.cc |= E_FCR 1142 1143 case C_MREG: 1144 s.set.cc |= E_MCR 1145 1146 case C_ZOREG, 1147 C_SOREG, 1148 C_LOREG: 1149 cls = int(p.To.Reg) 1150 s.used.ireg |= 1 << uint(cls-REG_R0) 1151 if ad != 0 { 1152 break 1153 } 1154 s.size = uint8(sz) 1155 s.soffset = c.regoff(&p.To) 1156 1157 m := uint32(ANYMEM) 1158 if cls == REGSB { 1159 m = E_MEMSB 1160 } 1161 if cls == REGSP { 1162 m = E_MEMSP 1163 } 1164 1165 if ar != 0 { 1166 s.used.cc |= m 1167 } else { 1168 s.set.cc |= m 1169 } 1170 1171 case C_SACON, 1172 C_LACON: 1173 s.used.ireg |= 1 << (REGSP - REG_R0) 1174 1175 case C_SECON, 1176 C_LECON: 1177 s.used.ireg |= 1 << (REGSB - REG_R0) 1178 1179 case C_REG: 1180 if ar != 0 { 1181 s.used.ireg |= 1 << uint(p.To.Reg-REG_R0) 1182 } else { 1183 s.set.ireg |= 1 << uint(p.To.Reg-REG_R0) 1184 } 1185 1186 case C_FREG: 1187 if ar != 0 { 1188 s.used.freg |= 1 << uint(p.To.Reg-REG_F0) 1189 } else { 1190 s.set.freg |= 1 << uint(p.To.Reg-REG_F0) 1191 } 1192 if ld != 0 && p.From.Type == obj.TYPE_REG { 1193 p.Mark |= LOAD 1194 } 1195 1196 case C_SAUTO, 1197 C_LAUTO: 1198 s.used.ireg |= 1 << (REGSP - REG_R0) 1199 if ad != 0 { 1200 break 1201 } 1202 s.size = uint8(sz) 1203 s.soffset = c.regoff(&p.To) 1204 1205 if ar != 0 { 1206 s.used.cc |= E_MEMSP 1207 } else { 1208 s.set.cc |= E_MEMSP 1209 } 1210 1211 case C_SEXT, 1212 C_LEXT: 1213 s.used.ireg |= 1 << (REGSB - REG_R0) 1214 if ad != 0 { 1215 break 1216 } 1217 s.size = uint8(sz) 1218 s.soffset = c.regoff(&p.To) 1219 1220 if ar != 0 { 1221 s.used.cc |= E_MEMSB 1222 } else { 1223 s.set.cc |= E_MEMSB 1224 } 1225 } 1226 1227 /* 1228 * flags based on 'from' field 1229 */ 1230 cls = int(p.From.Class) 1231 if cls == 0 { 1232 cls = c.aclass(&p.From) + 1 1233 p.From.Class = int8(cls) 1234 } 1235 cls-- 1236 switch cls { 1237 default: 1238 fmt.Printf("unknown class %d %v\n", cls, p) 1239 1240 case C_ZCON, 1241 C_SCON, 1242 C_ADD0CON, 1243 C_AND0CON, 1244 C_ADDCON, 1245 C_ANDCON, 1246 C_UCON, 1247 C_LCON, 1248 C_NONE, 1249 C_SBRA, 1250 C_LBRA, 1251 C_ADDR, 1252 C_TEXTSIZE: 1253 break 1254 1255 case C_HI, 1256 C_LO: 1257 s.used.cc |= E_HILO 1258 1259 case C_FCREG: 1260 s.used.cc |= E_FCR 1261 1262 case C_MREG: 1263 s.used.cc |= E_MCR 1264 1265 case C_ZOREG, 1266 C_SOREG, 1267 C_LOREG: 1268 cls = int(p.From.Reg) 1269 s.used.ireg |= 1 << uint(cls-REG_R0) 1270 if ld != 0 { 1271 p.Mark |= LOAD 1272 } 1273 s.size = uint8(sz) 1274 s.soffset = c.regoff(&p.From) 1275 1276 m := uint32(ANYMEM) 1277 if cls == REGSB { 1278 m = E_MEMSB 1279 } 1280 if cls == REGSP { 1281 m = E_MEMSP 1282 } 1283 1284 s.used.cc |= m 1285 1286 case C_SACON, 1287 C_LACON: 1288 cls = int(p.From.Reg) 1289 if cls == 0 { 1290 cls = REGSP 1291 } 1292 s.used.ireg |= 1 << uint(cls-REG_R0) 1293 1294 case C_SECON, 1295 C_LECON: 1296 s.used.ireg |= 1 << (REGSB - REG_R0) 1297 1298 case C_REG: 1299 s.used.ireg |= 1 << uint(p.From.Reg-REG_R0) 1300 1301 case C_FREG: 1302 s.used.freg |= 1 << uint(p.From.Reg-REG_F0) 1303 if ld != 0 && p.To.Type == obj.TYPE_REG { 1304 p.Mark |= LOAD 1305 } 1306 1307 case C_SAUTO, 1308 C_LAUTO: 1309 s.used.ireg |= 1 << (REGSP - REG_R0) 1310 if ld != 0 { 1311 p.Mark |= LOAD 1312 } 1313 if ad != 0 { 1314 break 1315 } 1316 s.size = uint8(sz) 1317 s.soffset = c.regoff(&p.From) 1318 1319 s.used.cc |= E_MEMSP 1320 1321 case C_SEXT: 1322 case C_LEXT: 1323 s.used.ireg |= 1 << (REGSB - REG_R0) 1324 if ld != 0 { 1325 p.Mark |= LOAD 1326 } 1327 if ad != 0 { 1328 break 1329 } 1330 s.size = uint8(sz) 1331 s.soffset = c.regoff(&p.From) 1332 1333 s.used.cc |= E_MEMSB 1334 } 1335 1336 cls = int(p.Reg) 1337 if cls != 0 { 1338 if REG_F0 <= cls && cls <= REG_F31 { 1339 s.used.freg |= 1 << uint(cls-REG_F0) 1340 } else { 1341 s.used.ireg |= 1 << uint(cls-REG_R0) 1342 } 1343 } 1344 s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */ 1345 } 1346 1347 /* 1348 * test to see if two instructions can be 1349 * interchanged without changing semantics 1350 */ 1351 func (c *ctxt0) depend(sa, sb *Sch) bool { 1352 if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 { 1353 return true 1354 } 1355 if sb.set.ireg&sa.used.ireg != 0 { 1356 return true 1357 } 1358 1359 if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 { 1360 return true 1361 } 1362 if sb.set.freg&sa.used.freg != 0 { 1363 return true 1364 } 1365 1366 /* 1367 * special case. 1368 * loads from same address cannot pass. 1369 * this is for hardware fifo's and the like 1370 */ 1371 if sa.used.cc&sb.used.cc&E_MEM != 0 { 1372 if sa.p.Reg == sb.p.Reg { 1373 if c.regoff(&sa.p.From) == c.regoff(&sb.p.From) { 1374 return true 1375 } 1376 } 1377 } 1378 1379 x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc) 1380 if x != 0 { 1381 /* 1382 * allow SB and SP to pass each other. 1383 * allow SB to pass SB iff doffsets are ok 1384 * anything else conflicts 1385 */ 1386 if x != E_MEMSP && x != E_MEMSB { 1387 return true 1388 } 1389 x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc 1390 if x&E_MEM != 0 { 1391 return true 1392 } 1393 if offoverlap(sa, sb) { 1394 return true 1395 } 1396 } 1397 1398 return false 1399 } 1400 1401 func offoverlap(sa, sb *Sch) bool { 1402 if sa.soffset < sb.soffset { 1403 if sa.soffset+int32(sa.size) > sb.soffset { 1404 return true 1405 } 1406 return false 1407 } 1408 if sb.soffset+int32(sb.size) > sa.soffset { 1409 return true 1410 } 1411 return false 1412 } 1413 1414 /* 1415 * test 2 adjacent instructions 1416 * and find out if inserted instructions 1417 * are desired to prevent stalls. 1418 */ 1419 func conflict(sa, sb *Sch) bool { 1420 if sa.set.ireg&sb.used.ireg != 0 { 1421 return true 1422 } 1423 if sa.set.freg&sb.used.freg != 0 { 1424 return true 1425 } 1426 if sa.set.cc&sb.used.cc != 0 { 1427 return true 1428 } 1429 return false 1430 } 1431 1432 func (c *ctxt0) compound(p *obj.Prog) bool { 1433 o := c.oplook(p) 1434 if o.size != 4 { 1435 return true 1436 } 1437 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB { 1438 return true 1439 } 1440 return false 1441 } 1442 1443 var Linkmips64 = obj.LinkArch{ 1444 Arch: sys.ArchMIPS64, 1445 Init: buildop, 1446 Preprocess: preprocess, 1447 Assemble: span0, 1448 Progedit: progedit, 1449 DWARFRegisters: MIPSDWARFRegisters, 1450 } 1451 1452 var Linkmips64le = obj.LinkArch{ 1453 Arch: sys.ArchMIPS64LE, 1454 Init: buildop, 1455 Preprocess: preprocess, 1456 Assemble: span0, 1457 Progedit: progedit, 1458 DWARFRegisters: MIPSDWARFRegisters, 1459 } 1460 1461 var Linkmips = obj.LinkArch{ 1462 Arch: sys.ArchMIPS, 1463 Init: buildop, 1464 Preprocess: preprocess, 1465 Assemble: span0, 1466 Progedit: progedit, 1467 DWARFRegisters: MIPSDWARFRegisters, 1468 } 1469 1470 var Linkmipsle = obj.LinkArch{ 1471 Arch: sys.ArchMIPSLE, 1472 Init: buildop, 1473 Preprocess: preprocess, 1474 Assemble: span0, 1475 Progedit: progedit, 1476 DWARFRegisters: MIPSDWARFRegisters, 1477 }