github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/compile/internal/s390x/peep.go (about) 1 // Derived from Inferno utils/6c/peep.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package s390x 32 33 import ( 34 "cmd/compile/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/s390x" 37 "fmt" 38 ) 39 40 type usage int 41 42 const ( 43 _None usage = iota // no usage found 44 _Read // only read from 45 _ReadWriteSame // both read from and written to in a single operand 46 _Write // only written to 47 _ReadWriteDiff // both read from and written to in different operands 48 ) 49 50 var gactive uint32 51 52 func peep(firstp *obj.Prog) { 53 g := gc.Flowstart(firstp, nil) 54 if g == nil { 55 return 56 } 57 gactive = 0 58 59 run := func(name string, pass func(r *gc.Flow) int) int { 60 n := pass(g.Start) 61 if gc.Debug['P'] != 0 { 62 fmt.Println(name, ":", n) 63 } 64 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 65 gc.Dumpit(name, g.Start, 0) 66 } 67 return n 68 } 69 70 for { 71 n := 0 72 n += run("constant propagation", constantPropagation) 73 n += run("copy propagation", copyPropagation) 74 n += run("cast propagation", castPropagation) 75 n += run("remove load-hit-stores", removeLoadHitStores) 76 n += run("dead code elimination", deadCodeElimination) 77 if n == 0 { 78 break 79 } 80 } 81 run("fuse op moves", fuseOpMoves) 82 run("fuse clears", fuseClear) 83 run("load pipelining", loadPipelining) 84 run("fuse compare branch", fuseCompareBranch) 85 run("simplify ops", simplifyOps) 86 run("dead code elimination", deadCodeElimination) 87 88 // TODO(mundaym): load/store multiple aren't currently handled by copyu 89 // so this pass must be last. 90 run("fuse multiple", fuseMultiple) 91 92 gc.Flowend(g) 93 } 94 95 func pushback(r0 *gc.Flow) { 96 var r *gc.Flow 97 98 var b *gc.Flow 99 p0 := r0.Prog 100 for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { 101 p := r.Prog 102 if p.As != obj.ANOP { 103 if !(isReg(&p.From) || isConst(&p.From)) || !isReg(&p.To) { 104 break 105 } 106 if copyu(p, &p0.To, nil) != _None || copyu(p0, &p.To, nil) != _None { 107 break 108 } 109 } 110 111 if p.As == obj.ACALL { 112 break 113 } 114 b = r 115 } 116 117 if b == nil { 118 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 119 fmt.Printf("no pushback: %v\n", r0.Prog) 120 if r != nil { 121 fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) 122 } 123 } 124 125 return 126 } 127 128 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 129 fmt.Printf("pushback\n") 130 for r := b; ; r = r.Link { 131 fmt.Printf("\t%v\n", r.Prog) 132 if r == r0 { 133 break 134 } 135 } 136 } 137 138 t := *r0.Prog 139 for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { 140 p0 = r.Link.Prog 141 p := r.Prog 142 p0.As = p.As 143 p0.Lineno = p.Lineno 144 p0.From = p.From 145 p0.To = p.To 146 p0.From3 = p.From3 147 p0.Reg = p.Reg 148 p0.RegTo2 = p.RegTo2 149 if r == b { 150 break 151 } 152 } 153 154 p0 = r.Prog 155 p0.As = t.As 156 p0.Lineno = t.Lineno 157 p0.From = t.From 158 p0.To = t.To 159 p0.From3 = t.From3 160 p0.Reg = t.Reg 161 p0.RegTo2 = t.RegTo2 162 163 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 164 fmt.Printf("\tafter\n") 165 for r := b; ; r = r.Link { 166 fmt.Printf("\t%v\n", r.Prog) 167 if r == r0 { 168 break 169 } 170 } 171 } 172 } 173 174 // excise replaces the given instruction with a NOP and clears 175 // its operands. 176 func excise(r *gc.Flow) { 177 p := r.Prog 178 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 179 fmt.Printf("%v ===delete===\n", p) 180 } 181 obj.Nopout(p) 182 gc.Ostats.Ndelmov++ 183 } 184 185 // isZero returns true if a is either the constant 0 or the register 186 // REGZERO. 187 func isZero(a *obj.Addr) bool { 188 if a.Type == obj.TYPE_CONST && a.Offset == 0 { 189 return true 190 } 191 if a.Type == obj.TYPE_REG && a.Reg == s390x.REGZERO { 192 return true 193 } 194 return false 195 } 196 197 // isReg returns true if a is a general purpose or floating point 198 // register (GPR or FPR). 199 // 200 // TODO(mundaym): currently this excludes REGZER0, but not other 201 // special registers. 202 func isReg(a *obj.Addr) bool { 203 return a.Type == obj.TYPE_REG && 204 s390x.REG_R0 <= a.Reg && 205 a.Reg <= s390x.REG_F15 && 206 a.Reg != s390x.REGZERO 207 } 208 209 // isGPR returns true if a is a general purpose register (GPR). 210 // REGZERO is treated as a GPR. 211 func isGPR(a *obj.Addr) bool { 212 return a.Type == obj.TYPE_REG && 213 s390x.REG_R0 <= a.Reg && 214 a.Reg <= s390x.REG_R15 215 } 216 217 // isFPR returns true if a is a floating point register (FPR). 218 func isFPR(a *obj.Addr) bool { 219 return a.Type == obj.TYPE_REG && 220 s390x.REG_F0 <= a.Reg && 221 a.Reg <= s390x.REG_F15 222 } 223 224 // isConst returns true if a refers to a constant (integer or 225 // floating point, not string currently). 226 func isConst(a *obj.Addr) bool { 227 return a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_FCONST 228 } 229 230 // isBDMem returns true if a refers to a memory location addressable by a 231 // base register (B) and a displacement (D), such as: 232 // x+8(R1) 233 // and 234 // 0(R10) 235 // It returns false if the address contains an index register (X) such as: 236 // 16(R1)(R2*1) 237 // or if a relocation is required. 238 func isBDMem(a *obj.Addr) bool { 239 return a.Type == obj.TYPE_MEM && 240 a.Index == 0 && 241 (a.Name == obj.NAME_NONE || a.Name == obj.NAME_AUTO || a.Name == obj.NAME_PARAM) 242 } 243 244 // the idea is to substitute 245 // one register for another 246 // from one MOV to another 247 // MOV a, R1 248 // ADD b, R1 / no use of R2 249 // MOV R1, R2 250 // would be converted to 251 // MOV a, R2 252 // ADD b, R2 253 // MOV R2, R1 254 // hopefully, then the former or latter MOV 255 // will be eliminated by copy propagation. 256 // 257 // r0 (the argument, not the register) is the MOV at the end of the 258 // above sequences. subprop returns true if it modified any instructions. 259 func subprop(r0 *gc.Flow) bool { 260 p := r0.Prog 261 v1 := &p.From 262 if !isReg(v1) { 263 return false 264 } 265 v2 := &p.To 266 if !isReg(v2) { 267 return false 268 } 269 cast := false 270 switch p.As { 271 case s390x.AMOVW, s390x.AMOVWZ, 272 s390x.AMOVH, s390x.AMOVHZ, 273 s390x.AMOVB, s390x.AMOVBZ: 274 cast = true 275 } 276 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 277 if gc.Uniqs(r) == nil { 278 break 279 } 280 p = r.Prog 281 switch copyu(p, v1, nil) { 282 case _Write, _ReadWriteDiff: 283 if p.As == obj.ACALL { 284 return false 285 } 286 if (!cast || p.As == r0.Prog.As) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { 287 copysub(&p.To, v1, v2) 288 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 289 p = r.Prog 290 copysub(&p.From, v1, v2) 291 copysub1(p, v1, v2) 292 copysub(&p.To, v1, v2) 293 } 294 v1.Reg, v2.Reg = v2.Reg, v1.Reg 295 return true 296 } 297 if cast { 298 return false 299 } 300 case _ReadWriteSame: 301 if cast { 302 return false 303 } 304 } 305 if copyu(p, v2, nil) != _None { 306 return false 307 } 308 } 309 return false 310 } 311 312 // The idea is to remove redundant copies. 313 // v1->v2 F=0 314 // (use v2 s/v2/v1/)* 315 // set v1 F=1 316 // use v2 return fail (v1->v2 move must remain) 317 // ----------------- 318 // v1->v2 F=0 319 // (use v2 s/v2/v1/)* 320 // set v1 F=1 321 // set v2 return success (caller can remove v1->v2 move) 322 func copyprop(r *gc.Flow) bool { 323 p := r.Prog 324 325 canSub := false 326 switch p.As { 327 case s390x.AFMOVS, s390x.AFMOVD, s390x.AMOVD: 328 canSub = true 329 default: 330 for rr := gc.Uniqp(r); rr != nil; rr = gc.Uniqp(rr) { 331 if gc.Uniqs(rr) == nil { 332 break 333 } 334 switch copyu(rr.Prog, &p.From, nil) { 335 case _Read, _None: 336 continue 337 } 338 // write 339 if rr.Prog.As == p.As { 340 canSub = true 341 } 342 break 343 } 344 } 345 if !canSub { 346 return false 347 } 348 if copyas(&p.From, &p.To) { 349 return true 350 } 351 352 gactive++ 353 return copy1(&p.From, &p.To, r.S1, 0) 354 } 355 356 // copy1 replaces uses of v2 with v1 starting at r and returns true if 357 // all uses were rewritten. 358 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { 359 if uint32(r.Active) == gactive { 360 return true 361 } 362 r.Active = int32(gactive) 363 for ; r != nil; r = r.S1 { 364 p := r.Prog 365 if f == 0 && gc.Uniqp(r) == nil { 366 // Multiple predecessors; conservatively 367 // assume v1 was set on other path 368 f = 1 369 } 370 t := copyu(p, v2, nil) 371 switch t { 372 case _ReadWriteSame: 373 return false 374 case _Write: 375 return true 376 case _Read, _ReadWriteDiff: 377 if f != 0 { 378 return false 379 } 380 if copyu(p, v2, v1) != 0 { 381 return false 382 } 383 if t == _ReadWriteDiff { 384 return true 385 } 386 } 387 if f == 0 { 388 switch copyu(p, v1, nil) { 389 case _ReadWriteSame, _ReadWriteDiff, _Write: 390 f = 1 391 } 392 } 393 if r.S2 != nil { 394 if !copy1(v1, v2, r.S2, f) { 395 return false 396 } 397 } 398 } 399 return true 400 } 401 402 // If s==nil, copyu returns the set/use of v in p; otherwise, it 403 // modifies p to replace reads of v with reads of s and returns 0 for 404 // success or non-zero for failure. 405 // 406 // If s==nil, copy returns one of the following values: 407 // _Read if v only used 408 // _ReadWriteSame if v is set and used in one address (read-alter-rewrite; 409 // can't substitute) 410 // _Write if v is only set 411 // _ReadWriteDiff if v is set in one address and used in another (so addresses 412 // can be rewritten independently) 413 // _None otherwise (not touched) 414 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) usage { 415 if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST { 416 // Currently we never generate a From3 with anything other than a constant in it. 417 fmt.Printf("copyu: From3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) 418 } 419 420 switch p.As { 421 default: 422 fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As)) 423 return _ReadWriteSame 424 425 case // read p.From, write p.To 426 s390x.AMOVH, 427 s390x.AMOVHZ, 428 s390x.AMOVB, 429 s390x.AMOVBZ, 430 s390x.AMOVW, 431 s390x.AMOVWZ, 432 s390x.AMOVD, 433 s390x.ANEG, 434 s390x.AADDME, 435 s390x.AADDZE, 436 s390x.ASUBME, 437 s390x.ASUBZE, 438 s390x.AFMOVS, 439 s390x.AFMOVD, 440 s390x.ALEDBR, 441 s390x.AFNEG, 442 s390x.ALDEBR, 443 s390x.ACLFEBR, 444 s390x.ACLGEBR, 445 s390x.ACLFDBR, 446 s390x.ACLGDBR, 447 s390x.ACFEBRA, 448 s390x.ACGEBRA, 449 s390x.ACFDBRA, 450 s390x.ACGDBRA, 451 s390x.ACELFBR, 452 s390x.ACELGBR, 453 s390x.ACDLFBR, 454 s390x.ACDLGBR, 455 s390x.ACEFBRA, 456 s390x.ACEGBRA, 457 s390x.ACDFBRA, 458 s390x.ACDGBRA, 459 s390x.AFSQRT: 460 461 if s != nil { 462 copysub(&p.From, v, s) 463 464 // Update only indirect uses of v in p.To 465 if !copyas(&p.To, v) { 466 copysub(&p.To, v, s) 467 } 468 return _None 469 } 470 471 if copyas(&p.To, v) { 472 // Fix up implicit from 473 if p.From.Type == obj.TYPE_NONE { 474 p.From = p.To 475 } 476 if copyau(&p.From, v) { 477 return _ReadWriteDiff 478 } 479 return _Write 480 } 481 482 if copyau(&p.From, v) { 483 return _Read 484 } 485 if copyau(&p.To, v) { 486 // p.To only indirectly uses v 487 return _Read 488 } 489 490 return _None 491 492 // read p.From, read p.Reg, write p.To 493 case s390x.AADD, 494 s390x.AADDC, 495 s390x.AADDE, 496 s390x.ASUB, 497 s390x.ASLW, 498 s390x.ASRW, 499 s390x.ASRAW, 500 s390x.ASLD, 501 s390x.ASRD, 502 s390x.ASRAD, 503 s390x.ARLL, 504 s390x.ARLLG, 505 s390x.AOR, 506 s390x.AORN, 507 s390x.AAND, 508 s390x.AANDN, 509 s390x.ANAND, 510 s390x.ANOR, 511 s390x.AXOR, 512 s390x.AMULLW, 513 s390x.AMULLD, 514 s390x.AMULHD, 515 s390x.AMULHDU, 516 s390x.ADIVW, 517 s390x.ADIVD, 518 s390x.ADIVWU, 519 s390x.ADIVDU, 520 s390x.AFADDS, 521 s390x.AFADD, 522 s390x.AFSUBS, 523 s390x.AFSUB, 524 s390x.AFMULS, 525 s390x.AFMUL, 526 s390x.AFDIVS, 527 s390x.AFDIV: 528 if s != nil { 529 copysub(&p.From, v, s) 530 copysub1(p, v, s) 531 532 // Update only indirect uses of v in p.To 533 if !copyas(&p.To, v) { 534 copysub(&p.To, v, s) 535 } 536 } 537 538 if copyas(&p.To, v) { 539 if p.Reg == 0 { 540 p.Reg = p.To.Reg 541 } 542 if copyau(&p.From, v) || copyau1(p, v) { 543 return _ReadWriteDiff 544 } 545 return _Write 546 } 547 548 if copyau(&p.From, v) { 549 return _Read 550 } 551 if copyau1(p, v) { 552 return _Read 553 } 554 if copyau(&p.To, v) { 555 return _Read 556 } 557 return _None 558 559 case s390x.ABEQ, 560 s390x.ABGT, 561 s390x.ABGE, 562 s390x.ABLT, 563 s390x.ABLE, 564 s390x.ABNE, 565 s390x.ABVC, 566 s390x.ABVS: 567 return _None 568 569 case obj.ACHECKNIL, // read p.From 570 s390x.ACMP, // read p.From, read p.To 571 s390x.ACMPU, 572 s390x.ACMPW, 573 s390x.ACMPWU, 574 s390x.AFCMPO, 575 s390x.AFCMPU, 576 s390x.ACEBR, 577 s390x.AMVC, 578 s390x.ACLC, 579 s390x.AXC, 580 s390x.AOC, 581 s390x.ANC: 582 if s != nil { 583 copysub(&p.From, v, s) 584 copysub(&p.To, v, s) 585 return _None 586 } 587 588 if copyau(&p.From, v) { 589 return _Read 590 } 591 if copyau(&p.To, v) { 592 return _Read 593 } 594 return _None 595 596 case s390x.ACMPBNE, s390x.ACMPBEQ, 597 s390x.ACMPBLT, s390x.ACMPBLE, 598 s390x.ACMPBGT, s390x.ACMPBGE, 599 s390x.ACMPUBNE, s390x.ACMPUBEQ, 600 s390x.ACMPUBLT, s390x.ACMPUBLE, 601 s390x.ACMPUBGT, s390x.ACMPUBGE: 602 if s != nil { 603 copysub(&p.From, v, s) 604 copysub1(p, v, s) 605 return _None 606 } 607 if copyau(&p.From, v) { 608 return _Read 609 } 610 if copyau1(p, v) { 611 return _Read 612 } 613 return _None 614 615 case s390x.ACLEAR: 616 if s != nil { 617 copysub(&p.To, v, s) 618 return _None 619 } 620 if copyau(&p.To, v) { 621 return _Read 622 } 623 return _None 624 625 // go never generates a branch to a GPR 626 // read p.To 627 case s390x.ABR: 628 if s != nil { 629 copysub(&p.To, v, s) 630 return _None 631 } 632 633 if copyau(&p.To, v) { 634 return _Read 635 } 636 return _None 637 638 case obj.ARET, obj.AUNDEF: 639 if s != nil { 640 return _None 641 } 642 643 // All registers die at this point, so claim 644 // everything is set (and not used). 645 return _Write 646 647 case s390x.ABL: 648 if v.Type == obj.TYPE_REG { 649 if s390x.REGARG != -1 && v.Reg == s390x.REGARG { 650 return _ReadWriteSame 651 } 652 if p.From.Type == obj.TYPE_REG && p.From.Reg == v.Reg { 653 return _ReadWriteSame 654 } 655 if v.Reg == s390x.REGZERO { 656 // Deliberately inserted nops set R0. 657 return _ReadWriteSame 658 } 659 if v.Reg == s390x.REGCTXT { 660 // Context register for closures. 661 // TODO(mundaym): not sure if we need to exclude this. 662 return _ReadWriteSame 663 } 664 } 665 if s != nil { 666 copysub(&p.To, v, s) 667 return _None 668 } 669 if copyau(&p.To, v) { 670 return _ReadWriteDiff 671 } 672 return _Write 673 674 case obj.ATEXT: 675 if v.Type == obj.TYPE_REG { 676 if v.Reg == s390x.REGARG { 677 return _Write 678 } 679 } 680 return _None 681 682 case obj.APCDATA, 683 obj.AFUNCDATA, 684 obj.AVARDEF, 685 obj.AVARKILL, 686 obj.AVARLIVE, 687 obj.AUSEFIELD, 688 obj.ANOP: 689 return _None 690 } 691 } 692 693 // copyas returns 1 if a and v address the same register. 694 // 695 // If a is the from operand, this means this operation reads the 696 // register in v. If a is the to operand, this means this operation 697 // writes the register in v. 698 func copyas(a *obj.Addr, v *obj.Addr) bool { 699 if isReg(v) { 700 if a.Type == v.Type { 701 if a.Reg == v.Reg { 702 return true 703 } 704 } 705 } 706 return false 707 } 708 709 // copyau returns 1 if a either directly or indirectly addresses the 710 // same register as v. 711 // 712 // If a is the from operand, this means this operation reads the 713 // register in v. If a is the to operand, this means the operation 714 // either reads or writes the register in v (if !copyas(a, v), then 715 // the operation reads the register in v). 716 func copyau(a *obj.Addr, v *obj.Addr) bool { 717 if copyas(a, v) { 718 return true 719 } 720 if v.Type == obj.TYPE_REG { 721 if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { 722 if v.Reg == a.Reg { 723 return true 724 } 725 } 726 } 727 return false 728 } 729 730 // copyau1 returns 1 if p.Reg references the same register as v and v 731 // is a direct reference. 732 func copyau1(p *obj.Prog, v *obj.Addr) bool { 733 if isReg(v) && v.Reg != 0 { 734 if p.Reg == v.Reg { 735 return true 736 } 737 } 738 return false 739 } 740 741 // copysub replaces v.Reg with s.Reg if a.Reg and v.Reg are direct 742 // references to the same register. 743 func copysub(a, v, s *obj.Addr) { 744 if copyau(a, v) { 745 a.Reg = s.Reg 746 } 747 } 748 749 // copysub1 replaces p.Reg with s.Reg if p.Reg and v.Reg are direct 750 // references to the same register. 751 func copysub1(p *obj.Prog, v, s *obj.Addr) { 752 if copyau1(p, v) { 753 p.Reg = s.Reg 754 } 755 } 756 757 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 758 if a.Type != v.Type { 759 return false 760 } 761 if isReg(v) && a.Reg == v.Reg { 762 return true 763 } 764 if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { 765 // TODO(mundaym): is the offset enough here? Node? 766 if v.Offset == a.Offset { 767 return true 768 } 769 } 770 return false 771 } 772 773 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 774 return reg.Type == obj.TYPE_REG && 775 a.Type == obj.TYPE_MEM && 776 a.Reg == reg.Reg && 777 0 <= a.Offset && a.Offset < 4096 778 } 779 780 func stackaddr(a *obj.Addr) bool { 781 // TODO(mundaym): the name implies this should check 782 // for TYPE_ADDR with a base register REGSP. 783 return a.Type == obj.TYPE_REG && a.Reg == s390x.REGSP 784 } 785 786 // isMove returns true if p is a move. Moves may imply 787 // sign/zero extension. 788 func isMove(p *obj.Prog) bool { 789 switch p.As { 790 case s390x.AMOVD, 791 s390x.AMOVW, s390x.AMOVWZ, 792 s390x.AMOVH, s390x.AMOVHZ, 793 s390x.AMOVB, s390x.AMOVBZ, 794 s390x.AFMOVD, s390x.AFMOVS: 795 return true 796 } 797 return false 798 } 799 800 // isLoad returns true if p is a move from memory to a register. 801 func isLoad(p *obj.Prog) bool { 802 if !isMove(p) { 803 return false 804 } 805 if !(isGPR(&p.To) || isFPR(&p.To)) { 806 return false 807 } 808 if p.From.Type != obj.TYPE_MEM { 809 return false 810 } 811 return true 812 } 813 814 // isStore returns true if p is a move from a register to memory. 815 func isStore(p *obj.Prog) bool { 816 if !isMove(p) { 817 return false 818 } 819 if !(isGPR(&p.From) || isFPR(&p.From) || isConst(&p.From)) { 820 return false 821 } 822 if p.To.Type != obj.TYPE_MEM { 823 return false 824 } 825 return true 826 } 827 828 // sameStackMem returns true if a and b are both memory operands 829 // and address the same location which must reside on the stack. 830 func sameStackMem(a, b *obj.Addr) bool { 831 if a.Type != obj.TYPE_MEM || 832 b.Type != obj.TYPE_MEM || 833 a.Name != b.Name || 834 a.Sym != b.Sym || 835 a.Node != b.Node || 836 a.Reg != b.Reg || 837 a.Index != b.Index || 838 a.Offset != b.Offset { 839 return false 840 } 841 switch a.Name { 842 case obj.NAME_NONE: 843 return a.Reg == s390x.REGSP 844 case obj.NAME_PARAM, obj.NAME_AUTO: 845 // params and autos are always on the stack 846 return true 847 } 848 return false 849 } 850 851 // removeLoadHitStores trys to remove loads that take place 852 // immediately after a store to the same location. Returns 853 // true if load-hit-stores were removed. 854 // 855 // For example: 856 // MOVD R1, 0(R15) 857 // MOVD 0(R15), R2 858 // Would become: 859 // MOVD R1, 0(R15) 860 // MOVD R1, R2 861 func removeLoadHitStores(r *gc.Flow) int { 862 n := 0 863 for ; r != nil; r = r.Link { 864 p := r.Prog 865 if !isStore(p) { 866 continue 867 } 868 for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { 869 pp := rr.Prog 870 if gc.Uniqp(rr) == nil { 871 break 872 } 873 if pp.As == obj.ANOP { 874 continue 875 } 876 if isLoad(pp) && sameStackMem(&p.To, &pp.From) { 877 if size(p.As) >= size(pp.As) && isGPR(&p.From) == isGPR(&pp.To) { 878 pp.From = p.From 879 } 880 } 881 if !isMove(pp) || isStore(pp) { 882 break 883 } 884 if copyau(&p.From, &pp.To) { 885 break 886 } 887 } 888 } 889 return n 890 } 891 892 // size returns the width of the given move. 893 func size(as obj.As) int { 894 switch as { 895 case s390x.AMOVD, s390x.AFMOVD: 896 return 8 897 case s390x.AMOVW, s390x.AMOVWZ, s390x.AFMOVS: 898 return 4 899 case s390x.AMOVH, s390x.AMOVHZ: 900 return 2 901 case s390x.AMOVB, s390x.AMOVBZ: 902 return 1 903 } 904 return -1 905 } 906 907 // castPropagation tries to eliminate unecessary casts. 908 // 909 // For example: 910 // MOVHZ R1, R2 // uint16 911 // MOVB R2, 0(R15) // int8 912 // Can be simplified to: 913 // MOVB R1, 0(R15) 914 func castPropagation(r *gc.Flow) int { 915 n := 0 916 for ; r != nil; r = r.Link { 917 p := r.Prog 918 if !isMove(p) || !isGPR(&p.To) { 919 continue 920 } 921 922 // r is a move with a destination register 923 var move *gc.Flow 924 for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { 925 if gc.Uniqp(rr) == nil { 926 // branch target: leave alone 927 break 928 } 929 pp := rr.Prog 930 if isMove(pp) && copyas(&pp.From, &p.To) { 931 if pp.To.Type == obj.TYPE_MEM { 932 if p.From.Type == obj.TYPE_MEM || 933 p.From.Type == obj.TYPE_ADDR { 934 break 935 } 936 if p.From.Type == obj.TYPE_CONST && 937 int64(int16(p.From.Offset)) != p.From.Offset { 938 break 939 } 940 } 941 move = rr 942 break 943 } 944 if pp.As == obj.ANOP { 945 continue 946 } 947 break 948 } 949 if move == nil { 950 continue 951 } 952 953 // we have a move that reads from our destination reg, check if any future 954 // instructions also read from the reg 955 mp := move.Prog 956 if !copyas(&mp.From, &mp.To) { 957 safe := false 958 for rr := gc.Uniqs(move); rr != nil; rr = gc.Uniqs(rr) { 959 if gc.Uniqp(rr) == nil { 960 break 961 } 962 switch copyu(rr.Prog, &p.To, nil) { 963 case _None: 964 continue 965 case _Write: 966 safe = true 967 } 968 break 969 } 970 if !safe { 971 continue 972 } 973 } 974 975 // at this point we have something like: 976 // MOV* const/mem/reg, reg 977 // MOV* reg, reg/mem 978 // now check if this is a cast that cannot be forward propagated 979 execute := false 980 if p.As == mp.As || isZero(&p.From) || size(p.As) == size(mp.As) { 981 execute = true 982 } else if isGPR(&p.From) && size(p.As) >= size(mp.As) { 983 execute = true 984 } 985 986 if execute { 987 mp.From = p.From 988 excise(r) 989 n++ 990 } 991 } 992 return n 993 } 994 995 // fuseClear merges memory clear operations. 996 // 997 // Looks for this pattern (sequence of clears): 998 // MOVD R0, n(R15) 999 // MOVD R0, n+8(R15) 1000 // MOVD R0, n+16(R15) 1001 // Replaces with: 1002 // CLEAR $24, n(R15) 1003 func fuseClear(r *gc.Flow) int { 1004 n := 0 1005 var align int64 1006 var clear *obj.Prog 1007 for ; r != nil; r = r.Link { 1008 // If there is a branch into the instruction stream then 1009 // we can't fuse into previous instructions. 1010 if gc.Uniqp(r) == nil { 1011 clear = nil 1012 } 1013 1014 p := r.Prog 1015 if p.As == obj.ANOP { 1016 continue 1017 } 1018 if p.As == s390x.AXC { 1019 if p.From.Reg == p.To.Reg && p.From.Offset == p.To.Offset { 1020 // TODO(mundaym): merge clears? 1021 p.As = s390x.ACLEAR 1022 p.From.Offset = p.From3.Offset 1023 p.From3 = nil 1024 p.From.Type = obj.TYPE_CONST 1025 p.From.Reg = 0 1026 clear = p 1027 } else { 1028 clear = nil 1029 } 1030 continue 1031 } 1032 1033 // Is our source a constant zero? 1034 if !isZero(&p.From) { 1035 clear = nil 1036 continue 1037 } 1038 1039 // Are we moving to memory? 1040 if p.To.Type != obj.TYPE_MEM || 1041 p.To.Index != 0 || 1042 p.To.Offset >= 4096 || 1043 !(p.To.Name == obj.NAME_NONE || p.To.Name == obj.NAME_AUTO || p.To.Name == obj.NAME_PARAM) { 1044 clear = nil 1045 continue 1046 } 1047 1048 size := int64(0) 1049 switch p.As { 1050 default: 1051 clear = nil 1052 continue 1053 case s390x.AMOVB, s390x.AMOVBZ: 1054 size = 1 1055 case s390x.AMOVH, s390x.AMOVHZ: 1056 size = 2 1057 case s390x.AMOVW, s390x.AMOVWZ: 1058 size = 4 1059 case s390x.AMOVD: 1060 size = 8 1061 } 1062 1063 // doubleword aligned clears should be kept doubleword 1064 // aligned 1065 if (size == 8 && align != 8) || (size != 8 && align == 8) { 1066 clear = nil 1067 } 1068 1069 if clear != nil && 1070 clear.To.Reg == p.To.Reg && 1071 clear.To.Name == p.To.Name && 1072 clear.To.Node == p.To.Node && 1073 clear.To.Sym == p.To.Sym { 1074 1075 min := clear.To.Offset 1076 max := clear.To.Offset + clear.From.Offset 1077 1078 // previous clear is already clearing this region 1079 if min <= p.To.Offset && max >= p.To.Offset+size { 1080 excise(r) 1081 n++ 1082 continue 1083 } 1084 1085 // merge forwards 1086 if max == p.To.Offset { 1087 clear.From.Offset += size 1088 excise(r) 1089 n++ 1090 continue 1091 } 1092 1093 // merge backwards 1094 if min-size == p.To.Offset { 1095 clear.From.Offset += size 1096 clear.To.Offset -= size 1097 excise(r) 1098 n++ 1099 continue 1100 } 1101 } 1102 1103 // transform into clear 1104 p.From.Type = obj.TYPE_CONST 1105 p.From.Offset = size 1106 p.From.Reg = 0 1107 p.As = s390x.ACLEAR 1108 clear = p 1109 align = size 1110 } 1111 return n 1112 } 1113 1114 // fuseMultiple merges memory loads and stores into load multiple and 1115 // store multiple operations. 1116 // 1117 // Looks for this pattern (sequence of loads or stores): 1118 // MOVD R1, 0(R15) 1119 // MOVD R2, 8(R15) 1120 // MOVD R3, 16(R15) 1121 // Replaces with: 1122 // STMG R1, R3, 0(R15) 1123 func fuseMultiple(r *gc.Flow) int { 1124 n := 0 1125 var fused *obj.Prog 1126 for ; r != nil; r = r.Link { 1127 // If there is a branch into the instruction stream then 1128 // we can't fuse into previous instructions. 1129 if gc.Uniqp(r) == nil { 1130 fused = nil 1131 } 1132 1133 p := r.Prog 1134 1135 isStore := isGPR(&p.From) && isBDMem(&p.To) 1136 isLoad := isGPR(&p.To) && isBDMem(&p.From) 1137 1138 // are we a candidate? 1139 size := int64(0) 1140 switch p.As { 1141 default: 1142 fused = nil 1143 continue 1144 case obj.ANOP: 1145 // skip over nops 1146 continue 1147 case s390x.AMOVW, s390x.AMOVWZ: 1148 size = 4 1149 // TODO(mundaym): 32-bit load multiple is currently not supported 1150 // as it requires sign/zero extension. 1151 if !isStore { 1152 fused = nil 1153 continue 1154 } 1155 case s390x.AMOVD: 1156 size = 8 1157 if !isLoad && !isStore { 1158 fused = nil 1159 continue 1160 } 1161 } 1162 1163 // If we merge two loads/stores with different source/destination Nodes 1164 // then we will lose a reference the second Node which means that the 1165 // compiler might mark the Node as unused and free its slot on the stack. 1166 // TODO(mundaym): allow this by adding a dummy reference to the Node. 1167 if fused == nil || 1168 fused.From.Node != p.From.Node || 1169 fused.From.Type != p.From.Type || 1170 fused.To.Node != p.To.Node || 1171 fused.To.Type != p.To.Type { 1172 fused = p 1173 continue 1174 } 1175 1176 // check two addresses 1177 ca := func(a, b *obj.Addr, offset int64) bool { 1178 return a.Reg == b.Reg && a.Offset+offset == b.Offset && 1179 a.Sym == b.Sym && a.Name == b.Name 1180 } 1181 1182 switch fused.As { 1183 default: 1184 fused = p 1185 case s390x.AMOVW, s390x.AMOVWZ: 1186 if size == 4 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 4) { 1187 fused.As = s390x.ASTMY 1188 fused.Reg = p.From.Reg 1189 excise(r) 1190 n++ 1191 } else { 1192 fused = p 1193 } 1194 case s390x.AMOVD: 1195 if size == 8 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 8) { 1196 fused.As = s390x.ASTMG 1197 fused.Reg = p.From.Reg 1198 excise(r) 1199 n++ 1200 } else if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, 8) { 1201 fused.As = s390x.ALMG 1202 fused.Reg = fused.To.Reg 1203 fused.To.Reg = p.To.Reg 1204 excise(r) 1205 n++ 1206 } else { 1207 fused = p 1208 } 1209 case s390x.ASTMG, s390x.ASTMY: 1210 if (fused.As == s390x.ASTMY && size != 4) || 1211 (fused.As == s390x.ASTMG && size != 8) { 1212 fused = p 1213 continue 1214 } 1215 offset := size * int64(fused.Reg-fused.From.Reg+1) 1216 if fused.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, offset) { 1217 fused.Reg = p.From.Reg 1218 excise(r) 1219 n++ 1220 } else { 1221 fused = p 1222 } 1223 case s390x.ALMG: 1224 offset := 8 * int64(fused.To.Reg-fused.Reg+1) 1225 if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, offset) { 1226 fused.To.Reg = p.To.Reg 1227 excise(r) 1228 n++ 1229 } else { 1230 fused = p 1231 } 1232 } 1233 } 1234 return n 1235 } 1236 1237 // simplifyOps looks for side-effect free ops that can be removed or 1238 // replaced with moves. 1239 // 1240 // For example: 1241 // XOR $0, R1 => NOP 1242 // ADD $0, R1, R2 => MOVD R1, R2 1243 func simplifyOps(r *gc.Flow) int { 1244 n := 0 1245 for ; r != nil; r = r.Link { 1246 p := r.Prog 1247 1248 // if the target is R0 then this is a required NOP 1249 if isGPR(&p.To) && p.To.Reg == s390x.REGZERO { 1250 continue 1251 } 1252 1253 switch p.As { 1254 case s390x.AADD, s390x.ASUB, 1255 s390x.AOR, s390x.AXOR, 1256 s390x.ASLW, s390x.ASRW, s390x.ASRAW, 1257 s390x.ASLD, s390x.ASRD, s390x.ASRAD, 1258 s390x.ARLL, s390x.ARLLG: 1259 if isZero(&p.From) && isGPR(&p.To) { 1260 if p.Reg == 0 || p.Reg == p.To.Reg { 1261 excise(r) 1262 n++ 1263 } else { 1264 p.As = s390x.AMOVD 1265 p.From.Type = obj.TYPE_REG 1266 p.From.Reg = p.Reg 1267 p.Reg = 0 1268 } 1269 } 1270 case s390x.AMULLW, s390x.AAND: 1271 if isZero(&p.From) && isGPR(&p.To) { 1272 p.As = s390x.AMOVD 1273 p.From.Type = obj.TYPE_REG 1274 p.From.Reg = s390x.REGZERO 1275 p.Reg = 0 1276 } 1277 } 1278 } 1279 return n 1280 } 1281 1282 // fuseOpMoves looks for moves following 2-operand operations and trys to merge them into 1283 // a 3-operand operation. 1284 // 1285 // For example: 1286 // ADD R1, R2 1287 // MOVD R2, R3 1288 // might become 1289 // ADD R1, R2, R3 1290 func fuseOpMoves(r *gc.Flow) int { 1291 n := 0 1292 for ; r != nil; r = r.Link { 1293 p := r.Prog 1294 switch p.As { 1295 case s390x.AADD: 1296 case s390x.ASUB: 1297 if isConst(&p.From) && int64(int16(p.From.Offset)) != p.From.Offset { 1298 continue 1299 } 1300 case s390x.ASLW, 1301 s390x.ASRW, 1302 s390x.ASRAW, 1303 s390x.ASLD, 1304 s390x.ASRD, 1305 s390x.ASRAD, 1306 s390x.ARLL, 1307 s390x.ARLLG: 1308 // ok - p.From will be a reg or a constant 1309 case s390x.AOR, 1310 s390x.AORN, 1311 s390x.AAND, 1312 s390x.AANDN, 1313 s390x.ANAND, 1314 s390x.ANOR, 1315 s390x.AXOR, 1316 s390x.AMULLW, 1317 s390x.AMULLD: 1318 if isConst(&p.From) { 1319 // these instructions can either use 3 register form 1320 // or have an immediate but not both 1321 continue 1322 } 1323 default: 1324 continue 1325 } 1326 1327 if p.Reg != 0 && p.Reg != p.To.Reg { 1328 continue 1329 } 1330 1331 var move *gc.Flow 1332 rr := gc.Uniqs(r) 1333 for { 1334 if rr == nil || gc.Uniqp(rr) == nil || rr == r { 1335 break 1336 } 1337 pp := rr.Prog 1338 switch copyu(pp, &p.To, nil) { 1339 case _None: 1340 rr = gc.Uniqs(rr) 1341 continue 1342 case _Read: 1343 if move == nil && pp.As == s390x.AMOVD && isGPR(&pp.From) && isGPR(&pp.To) { 1344 move = rr 1345 rr = gc.Uniqs(rr) 1346 continue 1347 } 1348 case _Write: 1349 if move == nil { 1350 // dead code 1351 excise(r) 1352 n++ 1353 } else { 1354 for prev := gc.Uniqp(move); prev != r; prev = gc.Uniqp(prev) { 1355 if copyu(prev.Prog, &move.Prog.To, nil) != 0 { 1356 move = nil 1357 break 1358 } 1359 } 1360 if move == nil { 1361 break 1362 } 1363 p.Reg, p.To.Reg = p.To.Reg, move.Prog.To.Reg 1364 excise(move) 1365 n++ 1366 1367 // clean up 1368 if p.From.Reg == p.To.Reg && isCommutative(p.As) { 1369 p.From.Reg, p.Reg = p.Reg, 0 1370 } 1371 if p.To.Reg == p.Reg { 1372 p.Reg = 0 1373 } 1374 // we could try again if p has become a 2-operand op 1375 // but in testing nothing extra was extracted 1376 } 1377 } 1378 break 1379 } 1380 } 1381 return n 1382 } 1383 1384 // isCommutative returns true if the order of input operands 1385 // does not affect the result. For example: 1386 // x + y == y + x so ADD is commutative 1387 // x ^ y == y ^ x so XOR is commutative 1388 func isCommutative(as obj.As) bool { 1389 switch as { 1390 case s390x.AADD, 1391 s390x.AOR, 1392 s390x.AAND, 1393 s390x.AXOR, 1394 s390x.AMULLW, 1395 s390x.AMULLD: 1396 return true 1397 } 1398 return false 1399 } 1400 1401 // applyCast applies the cast implied by the given move 1402 // instruction to v and returns the result. 1403 func applyCast(cast obj.As, v int64) int64 { 1404 switch cast { 1405 case s390x.AMOVWZ: 1406 return int64(uint32(v)) 1407 case s390x.AMOVHZ: 1408 return int64(uint16(v)) 1409 case s390x.AMOVBZ: 1410 return int64(uint8(v)) 1411 case s390x.AMOVW: 1412 return int64(int32(v)) 1413 case s390x.AMOVH: 1414 return int64(int16(v)) 1415 case s390x.AMOVB: 1416 return int64(int8(v)) 1417 } 1418 return v 1419 } 1420 1421 // constantPropagation removes redundant constant copies. 1422 func constantPropagation(r *gc.Flow) int { 1423 n := 0 1424 // find MOV $con,R followed by 1425 // another MOV $con,R without 1426 // setting R in the interim 1427 for ; r != nil; r = r.Link { 1428 p := r.Prog 1429 if isMove(p) { 1430 if !isReg(&p.To) { 1431 continue 1432 } 1433 if !isConst(&p.From) { 1434 continue 1435 } 1436 } else { 1437 continue 1438 } 1439 1440 rr := r 1441 for { 1442 rr = gc.Uniqs(rr) 1443 if rr == nil || rr == r { 1444 break 1445 } 1446 if gc.Uniqp(rr) == nil { 1447 break 1448 } 1449 1450 pp := rr.Prog 1451 t := copyu(pp, &p.To, nil) 1452 switch t { 1453 case _None: 1454 continue 1455 case _Read: 1456 if !isGPR(&pp.From) || !isMove(pp) { 1457 continue 1458 } 1459 if p.From.Type == obj.TYPE_CONST { 1460 v := applyCast(p.As, p.From.Offset) 1461 if isGPR(&pp.To) { 1462 if int64(int32(v)) == v || ((v>>32)<<32) == v { 1463 pp.From.Reg = 0 1464 pp.From.Offset = v 1465 pp.From.Type = obj.TYPE_CONST 1466 n++ 1467 } 1468 } else if int64(int16(v)) == v { 1469 pp.From.Reg = 0 1470 pp.From.Offset = v 1471 pp.From.Type = obj.TYPE_CONST 1472 n++ 1473 } 1474 } 1475 continue 1476 case _Write: 1477 if p.As != pp.As || p.From.Type != pp.From.Type { 1478 break 1479 } 1480 if p.From.Type == obj.TYPE_CONST && p.From.Offset == pp.From.Offset { 1481 excise(rr) 1482 n++ 1483 continue 1484 } else if p.From.Type == obj.TYPE_FCONST { 1485 if p.From.Val.(float64) == pp.From.Val.(float64) { 1486 excise(rr) 1487 n++ 1488 continue 1489 } 1490 } 1491 } 1492 break 1493 } 1494 } 1495 return n 1496 } 1497 1498 // copyPropagation tries to eliminate register-to-register moves. 1499 func copyPropagation(r *gc.Flow) int { 1500 n := 0 1501 for ; r != nil; r = r.Link { 1502 p := r.Prog 1503 if isMove(p) && isReg(&p.To) { 1504 // Convert uses to $0 to uses of R0 and 1505 // propagate R0 1506 if isGPR(&p.To) && isZero(&p.From) { 1507 p.From.Type = obj.TYPE_REG 1508 p.From.Reg = s390x.REGZERO 1509 } 1510 1511 // Try to eliminate reg->reg moves 1512 if isGPR(&p.From) || isFPR(&p.From) { 1513 if copyprop(r) || (subprop(r) && copyprop(r)) { 1514 excise(r) 1515 n++ 1516 } 1517 } 1518 } 1519 } 1520 return n 1521 } 1522 1523 // loadPipelining pushes any load from memory as early as possible. 1524 func loadPipelining(r *gc.Flow) int { 1525 for ; r != nil; r = r.Link { 1526 p := r.Prog 1527 if isLoad(p) { 1528 pushback(r) 1529 } 1530 } 1531 return 0 1532 } 1533 1534 // fuseCompareBranch finds comparisons followed by a branch and converts 1535 // them into a compare-and-branch instruction (which avoid setting the 1536 // condition code). 1537 func fuseCompareBranch(r *gc.Flow) int { 1538 n := 0 1539 for ; r != nil; r = r.Link { 1540 p := r.Prog 1541 r1 := gc.Uniqs(r) 1542 if r1 == nil { 1543 continue 1544 } 1545 p1 := r1.Prog 1546 1547 var ins obj.As 1548 switch p.As { 1549 case s390x.ACMP: 1550 switch p1.As { 1551 case s390x.ABCL, s390x.ABC: 1552 continue 1553 case s390x.ABEQ: 1554 ins = s390x.ACMPBEQ 1555 case s390x.ABGE: 1556 ins = s390x.ACMPBGE 1557 case s390x.ABGT: 1558 ins = s390x.ACMPBGT 1559 case s390x.ABLE: 1560 ins = s390x.ACMPBLE 1561 case s390x.ABLT: 1562 ins = s390x.ACMPBLT 1563 case s390x.ABNE: 1564 ins = s390x.ACMPBNE 1565 default: 1566 continue 1567 } 1568 1569 case s390x.ACMPU: 1570 switch p1.As { 1571 case s390x.ABCL, s390x.ABC: 1572 continue 1573 case s390x.ABEQ: 1574 ins = s390x.ACMPUBEQ 1575 case s390x.ABGE: 1576 ins = s390x.ACMPUBGE 1577 case s390x.ABGT: 1578 ins = s390x.ACMPUBGT 1579 case s390x.ABLE: 1580 ins = s390x.ACMPUBLE 1581 case s390x.ABLT: 1582 ins = s390x.ACMPUBLT 1583 case s390x.ABNE: 1584 ins = s390x.ACMPUBNE 1585 default: 1586 continue 1587 } 1588 1589 case s390x.ACMPW, s390x.ACMPWU: 1590 continue 1591 1592 default: 1593 continue 1594 } 1595 1596 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 1597 fmt.Printf("cnb %v; %v ", p, p1) 1598 } 1599 1600 if p1.To.Sym != nil { 1601 continue 1602 } 1603 1604 if p.To.Type == obj.TYPE_REG { 1605 p1.As = ins 1606 p1.From = p.From 1607 p1.Reg = p.To.Reg 1608 p1.From3 = nil 1609 } else if p.To.Type == obj.TYPE_CONST { 1610 switch p.As { 1611 case s390x.ACMP, s390x.ACMPW: 1612 if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) { 1613 continue 1614 } 1615 case s390x.ACMPU, s390x.ACMPWU: 1616 if p.To.Offset >= (1 << 8) { 1617 continue 1618 } 1619 default: 1620 } 1621 p1.As = ins 1622 p1.From = p.From 1623 p1.Reg = 0 1624 p1.From3 = new(obj.Addr) 1625 *(p1.From3) = p.To 1626 } else { 1627 continue 1628 } 1629 1630 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 1631 fmt.Printf("%v\n", p1) 1632 } 1633 excise(r) 1634 n++ 1635 } 1636 return n 1637 } 1638 1639 // deadCodeElimination removes writes to registers which are written 1640 // to again before they are next read. 1641 func deadCodeElimination(r *gc.Flow) int { 1642 n := 0 1643 for ; r != nil; r = r.Link { 1644 p := r.Prog 1645 // Currently there are no instructions which write to multiple 1646 // registers in copyu. This check will need to change if there 1647 // ever are. 1648 if !(isGPR(&p.To) || isFPR(&p.To)) || copyu(p, &p.To, nil) != _Write { 1649 continue 1650 } 1651 for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { 1652 t := copyu(rr.Prog, &p.To, nil) 1653 if t == _None { 1654 continue 1655 } 1656 if t == _Write { 1657 excise(r) 1658 n++ 1659 } 1660 break 1661 } 1662 } 1663 return n 1664 }