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