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