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