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