github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/s390x/ssa.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package s390x 6 7 import ( 8 "math" 9 10 "github.com/go-asm/go/cmd/compile/base" 11 "github.com/go-asm/go/cmd/compile/ir" 12 "github.com/go-asm/go/cmd/compile/logopt" 13 "github.com/go-asm/go/cmd/compile/ssa" 14 "github.com/go-asm/go/cmd/compile/ssagen" 15 "github.com/go-asm/go/cmd/compile/types" 16 "github.com/go-asm/go/cmd/obj" 17 "github.com/go-asm/go/cmd/obj/s390x" 18 ) 19 20 // ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. 21 func ssaMarkMoves(s *ssagen.State, b *ssa.Block) { 22 flive := b.FlagsLiveAtEnd 23 for _, c := range b.ControlValues() { 24 flive = c.Type.IsFlags() || flive 25 } 26 for i := len(b.Values) - 1; i >= 0; i-- { 27 v := b.Values[i] 28 if flive && v.Op == ssa.OpS390XMOVDconst { 29 // The "mark" is any non-nil Aux value. 30 v.Aux = ssa.AuxMark 31 } 32 if v.Type.IsFlags() { 33 flive = false 34 } 35 for _, a := range v.Args { 36 if a.Type.IsFlags() { 37 flive = true 38 } 39 } 40 } 41 } 42 43 // loadByType returns the load instruction of the given type. 44 func loadByType(t *types.Type) obj.As { 45 if t.IsFloat() { 46 switch t.Size() { 47 case 4: 48 return s390x.AFMOVS 49 case 8: 50 return s390x.AFMOVD 51 } 52 } else { 53 switch t.Size() { 54 case 1: 55 if t.IsSigned() { 56 return s390x.AMOVB 57 } else { 58 return s390x.AMOVBZ 59 } 60 case 2: 61 if t.IsSigned() { 62 return s390x.AMOVH 63 } else { 64 return s390x.AMOVHZ 65 } 66 case 4: 67 if t.IsSigned() { 68 return s390x.AMOVW 69 } else { 70 return s390x.AMOVWZ 71 } 72 case 8: 73 return s390x.AMOVD 74 } 75 } 76 panic("bad load type") 77 } 78 79 // storeByType returns the store instruction of the given type. 80 func storeByType(t *types.Type) obj.As { 81 width := t.Size() 82 if t.IsFloat() { 83 switch width { 84 case 4: 85 return s390x.AFMOVS 86 case 8: 87 return s390x.AFMOVD 88 } 89 } else { 90 switch width { 91 case 1: 92 return s390x.AMOVB 93 case 2: 94 return s390x.AMOVH 95 case 4: 96 return s390x.AMOVW 97 case 8: 98 return s390x.AMOVD 99 } 100 } 101 panic("bad store type") 102 } 103 104 // moveByType returns the reg->reg move instruction of the given type. 105 func moveByType(t *types.Type) obj.As { 106 if t.IsFloat() { 107 return s390x.AFMOVD 108 } else { 109 switch t.Size() { 110 case 1: 111 if t.IsSigned() { 112 return s390x.AMOVB 113 } else { 114 return s390x.AMOVBZ 115 } 116 case 2: 117 if t.IsSigned() { 118 return s390x.AMOVH 119 } else { 120 return s390x.AMOVHZ 121 } 122 case 4: 123 if t.IsSigned() { 124 return s390x.AMOVW 125 } else { 126 return s390x.AMOVWZ 127 } 128 case 8: 129 return s390x.AMOVD 130 } 131 } 132 panic("bad load type") 133 } 134 135 // opregreg emits instructions for 136 // 137 // dest := dest(To) op src(From) 138 // 139 // and also returns the created obj.Prog so it 140 // may be further adjusted (offset, scale, etc). 141 func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog { 142 p := s.Prog(op) 143 p.From.Type = obj.TYPE_REG 144 p.To.Type = obj.TYPE_REG 145 p.To.Reg = dest 146 p.From.Reg = src 147 return p 148 } 149 150 // opregregimm emits instructions for 151 // 152 // dest := src(From) op off 153 // 154 // and also returns the created obj.Prog so it 155 // may be further adjusted (offset, scale, etc). 156 func opregregimm(s *ssagen.State, op obj.As, dest, src int16, off int64) *obj.Prog { 157 p := s.Prog(op) 158 p.From.Type = obj.TYPE_CONST 159 p.From.Offset = off 160 p.Reg = src 161 p.To.Reg = dest 162 p.To.Type = obj.TYPE_REG 163 return p 164 } 165 166 func ssaGenValue(s *ssagen.State, v *ssa.Value) { 167 switch v.Op { 168 case ssa.OpS390XSLD, ssa.OpS390XSLW, 169 ssa.OpS390XSRD, ssa.OpS390XSRW, 170 ssa.OpS390XSRAD, ssa.OpS390XSRAW, 171 ssa.OpS390XRLLG, ssa.OpS390XRLL: 172 r := v.Reg() 173 r1 := v.Args[0].Reg() 174 r2 := v.Args[1].Reg() 175 if r2 == s390x.REG_R0 { 176 v.Fatalf("cannot use R0 as shift value %s", v.LongString()) 177 } 178 p := opregreg(s, v.Op.Asm(), r, r2) 179 if r != r1 { 180 p.Reg = r1 181 } 182 case ssa.OpS390XRXSBG: 183 r2 := v.Args[1].Reg() 184 i := v.Aux.(s390x.RotateParams) 185 p := s.Prog(v.Op.Asm()) 186 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(i.Start)} 187 p.AddRestSourceArgs([]obj.Addr{ 188 {Type: obj.TYPE_CONST, Offset: int64(i.End)}, 189 {Type: obj.TYPE_CONST, Offset: int64(i.Amount)}, 190 {Type: obj.TYPE_REG, Reg: r2}, 191 }) 192 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: v.Reg()} 193 case ssa.OpS390XRISBGZ: 194 r1 := v.Reg() 195 r2 := v.Args[0].Reg() 196 i := v.Aux.(s390x.RotateParams) 197 p := s.Prog(v.Op.Asm()) 198 p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(i.Start)} 199 p.AddRestSourceArgs([]obj.Addr{ 200 {Type: obj.TYPE_CONST, Offset: int64(i.End)}, 201 {Type: obj.TYPE_CONST, Offset: int64(i.Amount)}, 202 {Type: obj.TYPE_REG, Reg: r2}, 203 }) 204 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: r1} 205 case ssa.OpS390XADD, ssa.OpS390XADDW, 206 ssa.OpS390XSUB, ssa.OpS390XSUBW, 207 ssa.OpS390XAND, ssa.OpS390XANDW, 208 ssa.OpS390XOR, ssa.OpS390XORW, 209 ssa.OpS390XXOR, ssa.OpS390XXORW: 210 r := v.Reg() 211 r1 := v.Args[0].Reg() 212 r2 := v.Args[1].Reg() 213 p := opregreg(s, v.Op.Asm(), r, r2) 214 if r != r1 { 215 p.Reg = r1 216 } 217 case ssa.OpS390XADDC: 218 r1 := v.Reg0() 219 r2 := v.Args[0].Reg() 220 r3 := v.Args[1].Reg() 221 if r1 == r2 { 222 r2, r3 = r3, r2 223 } 224 p := opregreg(s, v.Op.Asm(), r1, r2) 225 if r3 != r1 { 226 p.Reg = r3 227 } 228 case ssa.OpS390XSUBC: 229 r1 := v.Reg0() 230 r2 := v.Args[0].Reg() 231 r3 := v.Args[1].Reg() 232 p := opregreg(s, v.Op.Asm(), r1, r3) 233 if r1 != r2 { 234 p.Reg = r2 235 } 236 case ssa.OpS390XADDE, ssa.OpS390XSUBE: 237 r2 := v.Args[1].Reg() 238 opregreg(s, v.Op.Asm(), v.Reg0(), r2) 239 case ssa.OpS390XADDCconst: 240 r1 := v.Reg0() 241 r3 := v.Args[0].Reg() 242 i2 := int64(int16(v.AuxInt)) 243 opregregimm(s, v.Op.Asm(), r1, r3, i2) 244 // 2-address opcode arithmetic 245 case ssa.OpS390XMULLD, ssa.OpS390XMULLW, 246 ssa.OpS390XMULHD, ssa.OpS390XMULHDU, 247 ssa.OpS390XFMULS, ssa.OpS390XFMUL, ssa.OpS390XFDIVS, ssa.OpS390XFDIV: 248 opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg()) 249 case ssa.OpS390XFSUBS, ssa.OpS390XFSUB, 250 ssa.OpS390XFADDS, ssa.OpS390XFADD: 251 opregreg(s, v.Op.Asm(), v.Reg0(), v.Args[1].Reg()) 252 case ssa.OpS390XMLGR: 253 // MLGR Rx R3 -> R2:R3 254 r0 := v.Args[0].Reg() 255 r1 := v.Args[1].Reg() 256 if r1 != s390x.REG_R3 { 257 v.Fatalf("We require the multiplcand to be stored in R3 for MLGR %s", v.LongString()) 258 } 259 p := s.Prog(s390x.AMLGR) 260 p.From.Type = obj.TYPE_REG 261 p.From.Reg = r0 262 p.To.Reg = s390x.REG_R2 263 p.To.Type = obj.TYPE_REG 264 case ssa.OpS390XFMADD, ssa.OpS390XFMADDS, 265 ssa.OpS390XFMSUB, ssa.OpS390XFMSUBS: 266 r1 := v.Args[1].Reg() 267 r2 := v.Args[2].Reg() 268 p := s.Prog(v.Op.Asm()) 269 p.From.Type = obj.TYPE_REG 270 p.From.Reg = r1 271 p.Reg = r2 272 p.To.Type = obj.TYPE_REG 273 p.To.Reg = v.Reg() 274 case ssa.OpS390XFIDBR: 275 switch v.AuxInt { 276 case 0, 1, 3, 4, 5, 6, 7: 277 opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt) 278 default: 279 v.Fatalf("invalid FIDBR mask: %v", v.AuxInt) 280 } 281 case ssa.OpS390XCPSDR: 282 p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg()) 283 p.Reg = v.Args[0].Reg() 284 case ssa.OpS390XDIVD, ssa.OpS390XDIVW, 285 ssa.OpS390XDIVDU, ssa.OpS390XDIVWU, 286 ssa.OpS390XMODD, ssa.OpS390XMODW, 287 ssa.OpS390XMODDU, ssa.OpS390XMODWU: 288 289 // TODO(mundaym): use the temp registers every time like x86 does with AX? 290 dividend := v.Args[0].Reg() 291 divisor := v.Args[1].Reg() 292 293 // CPU faults upon signed overflow, which occurs when most 294 // negative int is divided by -1. 295 var j *obj.Prog 296 if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW || 297 v.Op == ssa.OpS390XMODD || v.Op == ssa.OpS390XMODW { 298 299 var c *obj.Prog 300 c = s.Prog(s390x.ACMP) 301 j = s.Prog(s390x.ABEQ) 302 303 c.From.Type = obj.TYPE_REG 304 c.From.Reg = divisor 305 c.To.Type = obj.TYPE_CONST 306 c.To.Offset = -1 307 308 j.To.Type = obj.TYPE_BRANCH 309 310 } 311 312 p := s.Prog(v.Op.Asm()) 313 p.From.Type = obj.TYPE_REG 314 p.From.Reg = divisor 315 p.Reg = 0 316 p.To.Type = obj.TYPE_REG 317 p.To.Reg = dividend 318 319 // signed division, rest of the check for -1 case 320 if j != nil { 321 j2 := s.Prog(s390x.ABR) 322 j2.To.Type = obj.TYPE_BRANCH 323 324 var n *obj.Prog 325 if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW { 326 // n * -1 = -n 327 n = s.Prog(s390x.ANEG) 328 n.To.Type = obj.TYPE_REG 329 n.To.Reg = dividend 330 } else { 331 // n % -1 == 0 332 n = s.Prog(s390x.AXOR) 333 n.From.Type = obj.TYPE_REG 334 n.From.Reg = dividend 335 n.To.Type = obj.TYPE_REG 336 n.To.Reg = dividend 337 } 338 339 j.To.SetTarget(n) 340 j2.To.SetTarget(s.Pc()) 341 } 342 case ssa.OpS390XADDconst, ssa.OpS390XADDWconst: 343 opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt) 344 case ssa.OpS390XMULLDconst, ssa.OpS390XMULLWconst, 345 ssa.OpS390XSUBconst, ssa.OpS390XSUBWconst, 346 ssa.OpS390XANDconst, ssa.OpS390XANDWconst, 347 ssa.OpS390XORconst, ssa.OpS390XORWconst, 348 ssa.OpS390XXORconst, ssa.OpS390XXORWconst: 349 p := s.Prog(v.Op.Asm()) 350 p.From.Type = obj.TYPE_CONST 351 p.From.Offset = v.AuxInt 352 p.To.Type = obj.TYPE_REG 353 p.To.Reg = v.Reg() 354 case ssa.OpS390XSLDconst, ssa.OpS390XSLWconst, 355 ssa.OpS390XSRDconst, ssa.OpS390XSRWconst, 356 ssa.OpS390XSRADconst, ssa.OpS390XSRAWconst, 357 ssa.OpS390XRLLconst: 358 p := s.Prog(v.Op.Asm()) 359 p.From.Type = obj.TYPE_CONST 360 p.From.Offset = v.AuxInt 361 r := v.Reg() 362 r1 := v.Args[0].Reg() 363 if r != r1 { 364 p.Reg = r1 365 } 366 p.To.Type = obj.TYPE_REG 367 p.To.Reg = r 368 case ssa.OpS390XMOVDaddridx: 369 r := v.Args[0].Reg() 370 i := v.Args[1].Reg() 371 p := s.Prog(s390x.AMOVD) 372 p.From.Scale = 1 373 if i == s390x.REGSP { 374 r, i = i, r 375 } 376 p.From.Type = obj.TYPE_ADDR 377 p.From.Reg = r 378 p.From.Index = i 379 ssagen.AddAux(&p.From, v) 380 p.To.Type = obj.TYPE_REG 381 p.To.Reg = v.Reg() 382 case ssa.OpS390XMOVDaddr: 383 p := s.Prog(s390x.AMOVD) 384 p.From.Type = obj.TYPE_ADDR 385 p.From.Reg = v.Args[0].Reg() 386 ssagen.AddAux(&p.From, v) 387 p.To.Type = obj.TYPE_REG 388 p.To.Reg = v.Reg() 389 case ssa.OpS390XCMP, ssa.OpS390XCMPW, ssa.OpS390XCMPU, ssa.OpS390XCMPWU: 390 opregreg(s, v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg()) 391 case ssa.OpS390XFCMPS, ssa.OpS390XFCMP: 392 opregreg(s, v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg()) 393 case ssa.OpS390XCMPconst, ssa.OpS390XCMPWconst: 394 p := s.Prog(v.Op.Asm()) 395 p.From.Type = obj.TYPE_REG 396 p.From.Reg = v.Args[0].Reg() 397 p.To.Type = obj.TYPE_CONST 398 p.To.Offset = v.AuxInt 399 case ssa.OpS390XCMPUconst, ssa.OpS390XCMPWUconst: 400 p := s.Prog(v.Op.Asm()) 401 p.From.Type = obj.TYPE_REG 402 p.From.Reg = v.Args[0].Reg() 403 p.To.Type = obj.TYPE_CONST 404 p.To.Offset = int64(uint32(v.AuxInt)) 405 case ssa.OpS390XMOVDconst: 406 x := v.Reg() 407 p := s.Prog(v.Op.Asm()) 408 p.From.Type = obj.TYPE_CONST 409 p.From.Offset = v.AuxInt 410 p.To.Type = obj.TYPE_REG 411 p.To.Reg = x 412 case ssa.OpS390XFMOVSconst, ssa.OpS390XFMOVDconst: 413 x := v.Reg() 414 p := s.Prog(v.Op.Asm()) 415 p.From.Type = obj.TYPE_FCONST 416 p.From.Val = math.Float64frombits(uint64(v.AuxInt)) 417 p.To.Type = obj.TYPE_REG 418 p.To.Reg = x 419 case ssa.OpS390XADDWload, ssa.OpS390XADDload, 420 ssa.OpS390XMULLWload, ssa.OpS390XMULLDload, 421 ssa.OpS390XSUBWload, ssa.OpS390XSUBload, 422 ssa.OpS390XANDWload, ssa.OpS390XANDload, 423 ssa.OpS390XORWload, ssa.OpS390XORload, 424 ssa.OpS390XXORWload, ssa.OpS390XXORload: 425 p := s.Prog(v.Op.Asm()) 426 p.From.Type = obj.TYPE_MEM 427 p.From.Reg = v.Args[1].Reg() 428 ssagen.AddAux(&p.From, v) 429 p.To.Type = obj.TYPE_REG 430 p.To.Reg = v.Reg() 431 case ssa.OpS390XMOVDload, 432 ssa.OpS390XMOVWZload, ssa.OpS390XMOVHZload, ssa.OpS390XMOVBZload, 433 ssa.OpS390XMOVDBRload, ssa.OpS390XMOVWBRload, ssa.OpS390XMOVHBRload, 434 ssa.OpS390XMOVBload, ssa.OpS390XMOVHload, ssa.OpS390XMOVWload, 435 ssa.OpS390XFMOVSload, ssa.OpS390XFMOVDload: 436 p := s.Prog(v.Op.Asm()) 437 p.From.Type = obj.TYPE_MEM 438 p.From.Reg = v.Args[0].Reg() 439 ssagen.AddAux(&p.From, v) 440 p.To.Type = obj.TYPE_REG 441 p.To.Reg = v.Reg() 442 case ssa.OpS390XMOVBZloadidx, ssa.OpS390XMOVHZloadidx, ssa.OpS390XMOVWZloadidx, 443 ssa.OpS390XMOVBloadidx, ssa.OpS390XMOVHloadidx, ssa.OpS390XMOVWloadidx, ssa.OpS390XMOVDloadidx, 444 ssa.OpS390XMOVHBRloadidx, ssa.OpS390XMOVWBRloadidx, ssa.OpS390XMOVDBRloadidx, 445 ssa.OpS390XFMOVSloadidx, ssa.OpS390XFMOVDloadidx: 446 r := v.Args[0].Reg() 447 i := v.Args[1].Reg() 448 if i == s390x.REGSP { 449 r, i = i, r 450 } 451 p := s.Prog(v.Op.Asm()) 452 p.From.Type = obj.TYPE_MEM 453 p.From.Reg = r 454 p.From.Scale = 1 455 p.From.Index = i 456 ssagen.AddAux(&p.From, v) 457 p.To.Type = obj.TYPE_REG 458 p.To.Reg = v.Reg() 459 case ssa.OpS390XMOVBstore, ssa.OpS390XMOVHstore, ssa.OpS390XMOVWstore, ssa.OpS390XMOVDstore, 460 ssa.OpS390XMOVHBRstore, ssa.OpS390XMOVWBRstore, ssa.OpS390XMOVDBRstore, 461 ssa.OpS390XFMOVSstore, ssa.OpS390XFMOVDstore: 462 p := s.Prog(v.Op.Asm()) 463 p.From.Type = obj.TYPE_REG 464 p.From.Reg = v.Args[1].Reg() 465 p.To.Type = obj.TYPE_MEM 466 p.To.Reg = v.Args[0].Reg() 467 ssagen.AddAux(&p.To, v) 468 case ssa.OpS390XMOVBstoreidx, ssa.OpS390XMOVHstoreidx, ssa.OpS390XMOVWstoreidx, ssa.OpS390XMOVDstoreidx, 469 ssa.OpS390XMOVHBRstoreidx, ssa.OpS390XMOVWBRstoreidx, ssa.OpS390XMOVDBRstoreidx, 470 ssa.OpS390XFMOVSstoreidx, ssa.OpS390XFMOVDstoreidx: 471 r := v.Args[0].Reg() 472 i := v.Args[1].Reg() 473 if i == s390x.REGSP { 474 r, i = i, r 475 } 476 p := s.Prog(v.Op.Asm()) 477 p.From.Type = obj.TYPE_REG 478 p.From.Reg = v.Args[2].Reg() 479 p.To.Type = obj.TYPE_MEM 480 p.To.Reg = r 481 p.To.Scale = 1 482 p.To.Index = i 483 ssagen.AddAux(&p.To, v) 484 case ssa.OpS390XMOVDstoreconst, ssa.OpS390XMOVWstoreconst, ssa.OpS390XMOVHstoreconst, ssa.OpS390XMOVBstoreconst: 485 p := s.Prog(v.Op.Asm()) 486 p.From.Type = obj.TYPE_CONST 487 sc := v.AuxValAndOff() 488 p.From.Offset = sc.Val64() 489 p.To.Type = obj.TYPE_MEM 490 p.To.Reg = v.Args[0].Reg() 491 ssagen.AddAux2(&p.To, v, sc.Off64()) 492 case ssa.OpS390XMOVBreg, ssa.OpS390XMOVHreg, ssa.OpS390XMOVWreg, 493 ssa.OpS390XMOVBZreg, ssa.OpS390XMOVHZreg, ssa.OpS390XMOVWZreg, 494 ssa.OpS390XLDGR, ssa.OpS390XLGDR, 495 ssa.OpS390XCEFBRA, ssa.OpS390XCDFBRA, ssa.OpS390XCEGBRA, ssa.OpS390XCDGBRA, 496 ssa.OpS390XCFEBRA, ssa.OpS390XCFDBRA, ssa.OpS390XCGEBRA, ssa.OpS390XCGDBRA, 497 ssa.OpS390XCELFBR, ssa.OpS390XCDLFBR, ssa.OpS390XCELGBR, ssa.OpS390XCDLGBR, 498 ssa.OpS390XCLFEBR, ssa.OpS390XCLFDBR, ssa.OpS390XCLGEBR, ssa.OpS390XCLGDBR, 499 ssa.OpS390XLDEBR, ssa.OpS390XLEDBR, 500 ssa.OpS390XFNEG, ssa.OpS390XFNEGS, 501 ssa.OpS390XLPDFR, ssa.OpS390XLNDFR: 502 opregreg(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg()) 503 case ssa.OpS390XCLEAR: 504 p := s.Prog(v.Op.Asm()) 505 p.From.Type = obj.TYPE_CONST 506 sc := v.AuxValAndOff() 507 p.From.Offset = sc.Val64() 508 p.To.Type = obj.TYPE_MEM 509 p.To.Reg = v.Args[0].Reg() 510 ssagen.AddAux2(&p.To, v, sc.Off64()) 511 case ssa.OpCopy: 512 if v.Type.IsMemory() { 513 return 514 } 515 x := v.Args[0].Reg() 516 y := v.Reg() 517 if x != y { 518 opregreg(s, moveByType(v.Type), y, x) 519 } 520 case ssa.OpLoadReg: 521 if v.Type.IsFlags() { 522 v.Fatalf("load flags not implemented: %v", v.LongString()) 523 return 524 } 525 p := s.Prog(loadByType(v.Type)) 526 ssagen.AddrAuto(&p.From, v.Args[0]) 527 p.To.Type = obj.TYPE_REG 528 p.To.Reg = v.Reg() 529 case ssa.OpStoreReg: 530 if v.Type.IsFlags() { 531 v.Fatalf("store flags not implemented: %v", v.LongString()) 532 return 533 } 534 p := s.Prog(storeByType(v.Type)) 535 p.From.Type = obj.TYPE_REG 536 p.From.Reg = v.Args[0].Reg() 537 ssagen.AddrAuto(&p.To, v) 538 case ssa.OpS390XLoweredGetClosurePtr: 539 // Closure pointer is R12 (already) 540 ssagen.CheckLoweredGetClosurePtr(v) 541 case ssa.OpS390XLoweredRound32F, ssa.OpS390XLoweredRound64F: 542 // input is already rounded 543 case ssa.OpS390XLoweredGetG: 544 r := v.Reg() 545 p := s.Prog(s390x.AMOVD) 546 p.From.Type = obj.TYPE_REG 547 p.From.Reg = s390x.REGG 548 p.To.Type = obj.TYPE_REG 549 p.To.Reg = r 550 case ssa.OpS390XLoweredGetCallerSP: 551 // caller's SP is FixedFrameSize below the address of the first arg 552 p := s.Prog(s390x.AMOVD) 553 p.From.Type = obj.TYPE_ADDR 554 p.From.Offset = -base.Ctxt.Arch.FixedFrameSize 555 p.From.Name = obj.NAME_PARAM 556 p.To.Type = obj.TYPE_REG 557 p.To.Reg = v.Reg() 558 case ssa.OpS390XLoweredGetCallerPC: 559 p := s.Prog(obj.AGETCALLERPC) 560 p.To.Type = obj.TYPE_REG 561 p.To.Reg = v.Reg() 562 case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter: 563 s.Call(v) 564 case ssa.OpS390XCALLtail: 565 s.TailCall(v) 566 case ssa.OpS390XLoweredWB: 567 p := s.Prog(obj.ACALL) 568 p.To.Type = obj.TYPE_MEM 569 p.To.Name = obj.NAME_EXTERN 570 // AuxInt encodes how many buffer entries we need. 571 p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1] 572 case ssa.OpS390XLoweredPanicBoundsA, ssa.OpS390XLoweredPanicBoundsB, ssa.OpS390XLoweredPanicBoundsC: 573 p := s.Prog(obj.ACALL) 574 p.To.Type = obj.TYPE_MEM 575 p.To.Name = obj.NAME_EXTERN 576 p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] 577 s.UseArgs(16) // space used in callee args area by assembly stubs 578 case ssa.OpS390XFLOGR, ssa.OpS390XPOPCNT, 579 ssa.OpS390XNEG, ssa.OpS390XNEGW, 580 ssa.OpS390XMOVWBR, ssa.OpS390XMOVDBR: 581 p := s.Prog(v.Op.Asm()) 582 p.From.Type = obj.TYPE_REG 583 p.From.Reg = v.Args[0].Reg() 584 p.To.Type = obj.TYPE_REG 585 p.To.Reg = v.Reg() 586 case ssa.OpS390XNOT, ssa.OpS390XNOTW: 587 v.Fatalf("NOT/NOTW generated %s", v.LongString()) 588 case ssa.OpS390XSumBytes2, ssa.OpS390XSumBytes4, ssa.OpS390XSumBytes8: 589 v.Fatalf("SumBytes generated %s", v.LongString()) 590 case ssa.OpS390XLOCGR: 591 p := s.Prog(v.Op.Asm()) 592 p.From.Type = obj.TYPE_CONST 593 p.From.Offset = int64(v.Aux.(s390x.CCMask)) 594 p.Reg = v.Args[1].Reg() 595 p.To.Type = obj.TYPE_REG 596 p.To.Reg = v.Reg() 597 case ssa.OpS390XFSQRTS, ssa.OpS390XFSQRT: 598 p := s.Prog(v.Op.Asm()) 599 p.From.Type = obj.TYPE_REG 600 p.From.Reg = v.Args[0].Reg() 601 p.To.Type = obj.TYPE_REG 602 p.To.Reg = v.Reg() 603 case ssa.OpS390XLTDBR, ssa.OpS390XLTEBR: 604 opregreg(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[0].Reg()) 605 case ssa.OpS390XInvertFlags: 606 v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) 607 case ssa.OpS390XFlagEQ, ssa.OpS390XFlagLT, ssa.OpS390XFlagGT, ssa.OpS390XFlagOV: 608 v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString()) 609 case ssa.OpS390XAddTupleFirst32, ssa.OpS390XAddTupleFirst64: 610 v.Fatalf("AddTupleFirst* should never make it to codegen %v", v.LongString()) 611 case ssa.OpS390XLoweredNilCheck: 612 // Issue a load which will fault if the input is nil. 613 p := s.Prog(s390x.AMOVBZ) 614 p.From.Type = obj.TYPE_MEM 615 p.From.Reg = v.Args[0].Reg() 616 ssagen.AddAux(&p.From, v) 617 p.To.Type = obj.TYPE_REG 618 p.To.Reg = s390x.REGTMP 619 if logopt.Enabled() { 620 logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name) 621 } 622 if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers 623 base.WarnfAt(v.Pos, "generated nil check") 624 } 625 case ssa.OpS390XMVC: 626 vo := v.AuxValAndOff() 627 p := s.Prog(s390x.AMVC) 628 p.From.Type = obj.TYPE_CONST 629 p.From.Offset = vo.Val64() 630 p.AddRestSource(obj.Addr{ 631 Type: obj.TYPE_MEM, 632 Reg: v.Args[1].Reg(), 633 Offset: vo.Off64(), 634 }) 635 p.To.Type = obj.TYPE_MEM 636 p.To.Reg = v.Args[0].Reg() 637 p.To.Offset = vo.Off64() 638 case ssa.OpS390XSTMG2, ssa.OpS390XSTMG3, ssa.OpS390XSTMG4, 639 ssa.OpS390XSTM2, ssa.OpS390XSTM3, ssa.OpS390XSTM4: 640 for i := 2; i < len(v.Args)-1; i++ { 641 if v.Args[i].Reg() != v.Args[i-1].Reg()+1 { 642 v.Fatalf("invalid store multiple %s", v.LongString()) 643 } 644 } 645 p := s.Prog(v.Op.Asm()) 646 p.From.Type = obj.TYPE_REG 647 p.From.Reg = v.Args[1].Reg() 648 p.Reg = v.Args[len(v.Args)-2].Reg() 649 p.To.Type = obj.TYPE_MEM 650 p.To.Reg = v.Args[0].Reg() 651 ssagen.AddAux(&p.To, v) 652 case ssa.OpS390XLoweredMove: 653 // Inputs must be valid pointers to memory, 654 // so adjust arg0 and arg1 as part of the expansion. 655 // arg2 should be src+size, 656 // 657 // mvc: MVC $256, 0(R2), 0(R1) 658 // MOVD $256(R1), R1 659 // MOVD $256(R2), R2 660 // CMP R2, Rarg2 661 // BNE mvc 662 // MVC $rem, 0(R2), 0(R1) // if rem > 0 663 // arg2 is the last address to move in the loop + 256 664 mvc := s.Prog(s390x.AMVC) 665 mvc.From.Type = obj.TYPE_CONST 666 mvc.From.Offset = 256 667 mvc.AddRestSource(obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[1].Reg()}) 668 mvc.To.Type = obj.TYPE_MEM 669 mvc.To.Reg = v.Args[0].Reg() 670 671 for i := 0; i < 2; i++ { 672 movd := s.Prog(s390x.AMOVD) 673 movd.From.Type = obj.TYPE_ADDR 674 movd.From.Reg = v.Args[i].Reg() 675 movd.From.Offset = 256 676 movd.To.Type = obj.TYPE_REG 677 movd.To.Reg = v.Args[i].Reg() 678 } 679 680 cmpu := s.Prog(s390x.ACMPU) 681 cmpu.From.Reg = v.Args[1].Reg() 682 cmpu.From.Type = obj.TYPE_REG 683 cmpu.To.Reg = v.Args[2].Reg() 684 cmpu.To.Type = obj.TYPE_REG 685 686 bne := s.Prog(s390x.ABLT) 687 bne.To.Type = obj.TYPE_BRANCH 688 bne.To.SetTarget(mvc) 689 690 if v.AuxInt > 0 { 691 mvc := s.Prog(s390x.AMVC) 692 mvc.From.Type = obj.TYPE_CONST 693 mvc.From.Offset = v.AuxInt 694 mvc.AddRestSource(obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[1].Reg()}) 695 mvc.To.Type = obj.TYPE_MEM 696 mvc.To.Reg = v.Args[0].Reg() 697 } 698 case ssa.OpS390XLoweredZero: 699 // Input must be valid pointers to memory, 700 // so adjust arg0 as part of the expansion. 701 // arg1 should be src+size, 702 // 703 // clear: CLEAR $256, 0(R1) 704 // MOVD $256(R1), R1 705 // CMP R1, Rarg1 706 // BNE clear 707 // CLEAR $rem, 0(R1) // if rem > 0 708 // arg1 is the last address to zero in the loop + 256 709 clear := s.Prog(s390x.ACLEAR) 710 clear.From.Type = obj.TYPE_CONST 711 clear.From.Offset = 256 712 clear.To.Type = obj.TYPE_MEM 713 clear.To.Reg = v.Args[0].Reg() 714 715 movd := s.Prog(s390x.AMOVD) 716 movd.From.Type = obj.TYPE_ADDR 717 movd.From.Reg = v.Args[0].Reg() 718 movd.From.Offset = 256 719 movd.To.Type = obj.TYPE_REG 720 movd.To.Reg = v.Args[0].Reg() 721 722 cmpu := s.Prog(s390x.ACMPU) 723 cmpu.From.Reg = v.Args[0].Reg() 724 cmpu.From.Type = obj.TYPE_REG 725 cmpu.To.Reg = v.Args[1].Reg() 726 cmpu.To.Type = obj.TYPE_REG 727 728 bne := s.Prog(s390x.ABLT) 729 bne.To.Type = obj.TYPE_BRANCH 730 bne.To.SetTarget(clear) 731 732 if v.AuxInt > 0 { 733 clear := s.Prog(s390x.ACLEAR) 734 clear.From.Type = obj.TYPE_CONST 735 clear.From.Offset = v.AuxInt 736 clear.To.Type = obj.TYPE_MEM 737 clear.To.Reg = v.Args[0].Reg() 738 } 739 case ssa.OpS390XMOVBZatomicload, ssa.OpS390XMOVWZatomicload, ssa.OpS390XMOVDatomicload: 740 p := s.Prog(v.Op.Asm()) 741 p.From.Type = obj.TYPE_MEM 742 p.From.Reg = v.Args[0].Reg() 743 ssagen.AddAux(&p.From, v) 744 p.To.Type = obj.TYPE_REG 745 p.To.Reg = v.Reg0() 746 case ssa.OpS390XMOVBatomicstore, ssa.OpS390XMOVWatomicstore, ssa.OpS390XMOVDatomicstore: 747 p := s.Prog(v.Op.Asm()) 748 p.From.Type = obj.TYPE_REG 749 p.From.Reg = v.Args[1].Reg() 750 p.To.Type = obj.TYPE_MEM 751 p.To.Reg = v.Args[0].Reg() 752 ssagen.AddAux(&p.To, v) 753 case ssa.OpS390XLAN, ssa.OpS390XLAO: 754 // LA(N|O) Ry, TMP, 0(Rx) 755 op := s.Prog(v.Op.Asm()) 756 op.From.Type = obj.TYPE_REG 757 op.From.Reg = v.Args[1].Reg() 758 op.Reg = s390x.REGTMP 759 op.To.Type = obj.TYPE_MEM 760 op.To.Reg = v.Args[0].Reg() 761 case ssa.OpS390XLANfloor, ssa.OpS390XLAOfloor: 762 r := v.Args[0].Reg() // clobbered, assumed R1 in comments 763 764 // Round ptr down to nearest multiple of 4. 765 // ANDW $~3, R1 766 ptr := s.Prog(s390x.AANDW) 767 ptr.From.Type = obj.TYPE_CONST 768 ptr.From.Offset = 0xfffffffc 769 ptr.To.Type = obj.TYPE_REG 770 ptr.To.Reg = r 771 772 // Redirect output of LA(N|O) into R1 since it is clobbered anyway. 773 // LA(N|O) Rx, R1, 0(R1) 774 op := s.Prog(v.Op.Asm()) 775 op.From.Type = obj.TYPE_REG 776 op.From.Reg = v.Args[1].Reg() 777 op.Reg = r 778 op.To.Type = obj.TYPE_MEM 779 op.To.Reg = r 780 case ssa.OpS390XLAA, ssa.OpS390XLAAG: 781 p := s.Prog(v.Op.Asm()) 782 p.Reg = v.Reg0() 783 p.From.Type = obj.TYPE_REG 784 p.From.Reg = v.Args[1].Reg() 785 p.To.Type = obj.TYPE_MEM 786 p.To.Reg = v.Args[0].Reg() 787 ssagen.AddAux(&p.To, v) 788 case ssa.OpS390XLoweredAtomicCas32, ssa.OpS390XLoweredAtomicCas64: 789 // Convert the flags output of CS{,G} into a bool. 790 // CS{,G} arg1, arg2, arg0 791 // MOVD $0, ret 792 // BNE 2(PC) 793 // MOVD $1, ret 794 // NOP (so the BNE has somewhere to land) 795 796 // CS{,G} arg1, arg2, arg0 797 cs := s.Prog(v.Op.Asm()) 798 cs.From.Type = obj.TYPE_REG 799 cs.From.Reg = v.Args[1].Reg() // old 800 cs.Reg = v.Args[2].Reg() // new 801 cs.To.Type = obj.TYPE_MEM 802 cs.To.Reg = v.Args[0].Reg() 803 ssagen.AddAux(&cs.To, v) 804 805 // MOVD $0, ret 806 movd := s.Prog(s390x.AMOVD) 807 movd.From.Type = obj.TYPE_CONST 808 movd.From.Offset = 0 809 movd.To.Type = obj.TYPE_REG 810 movd.To.Reg = v.Reg0() 811 812 // BNE 2(PC) 813 bne := s.Prog(s390x.ABNE) 814 bne.To.Type = obj.TYPE_BRANCH 815 816 // MOVD $1, ret 817 movd = s.Prog(s390x.AMOVD) 818 movd.From.Type = obj.TYPE_CONST 819 movd.From.Offset = 1 820 movd.To.Type = obj.TYPE_REG 821 movd.To.Reg = v.Reg0() 822 823 // NOP (so the BNE has somewhere to land) 824 nop := s.Prog(obj.ANOP) 825 bne.To.SetTarget(nop) 826 case ssa.OpS390XLoweredAtomicExchange32, ssa.OpS390XLoweredAtomicExchange64: 827 // Loop until the CS{,G} succeeds. 828 // MOV{WZ,D} arg0, ret 829 // cs: CS{,G} ret, arg1, arg0 830 // BNE cs 831 832 // MOV{WZ,D} arg0, ret 833 load := s.Prog(loadByType(v.Type.FieldType(0))) 834 load.From.Type = obj.TYPE_MEM 835 load.From.Reg = v.Args[0].Reg() 836 load.To.Type = obj.TYPE_REG 837 load.To.Reg = v.Reg0() 838 ssagen.AddAux(&load.From, v) 839 840 // CS{,G} ret, arg1, arg0 841 cs := s.Prog(v.Op.Asm()) 842 cs.From.Type = obj.TYPE_REG 843 cs.From.Reg = v.Reg0() // old 844 cs.Reg = v.Args[1].Reg() // new 845 cs.To.Type = obj.TYPE_MEM 846 cs.To.Reg = v.Args[0].Reg() 847 ssagen.AddAux(&cs.To, v) 848 849 // BNE cs 850 bne := s.Prog(s390x.ABNE) 851 bne.To.Type = obj.TYPE_BRANCH 852 bne.To.SetTarget(cs) 853 case ssa.OpS390XSYNC: 854 s.Prog(s390x.ASYNC) 855 case ssa.OpClobber, ssa.OpClobberReg: 856 // TODO: implement for clobberdead experiment. Nop is ok for now. 857 default: 858 v.Fatalf("genValue not implemented: %s", v.LongString()) 859 } 860 } 861 862 func blockAsm(b *ssa.Block) obj.As { 863 switch b.Kind { 864 case ssa.BlockS390XBRC: 865 return s390x.ABRC 866 case ssa.BlockS390XCRJ: 867 return s390x.ACRJ 868 case ssa.BlockS390XCGRJ: 869 return s390x.ACGRJ 870 case ssa.BlockS390XCLRJ: 871 return s390x.ACLRJ 872 case ssa.BlockS390XCLGRJ: 873 return s390x.ACLGRJ 874 case ssa.BlockS390XCIJ: 875 return s390x.ACIJ 876 case ssa.BlockS390XCGIJ: 877 return s390x.ACGIJ 878 case ssa.BlockS390XCLIJ: 879 return s390x.ACLIJ 880 case ssa.BlockS390XCLGIJ: 881 return s390x.ACLGIJ 882 } 883 b.Fatalf("blockAsm not implemented: %s", b.LongString()) 884 panic("unreachable") 885 } 886 887 func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { 888 // Handle generic blocks first. 889 switch b.Kind { 890 case ssa.BlockPlain: 891 if b.Succs[0].Block() != next { 892 p := s.Prog(s390x.ABR) 893 p.To.Type = obj.TYPE_BRANCH 894 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 895 } 896 return 897 case ssa.BlockDefer: 898 // defer returns in R3: 899 // 0 if we should continue executing 900 // 1 if we should jump to deferreturn call 901 p := s.Br(s390x.ACIJ, b.Succs[1].Block()) 902 p.From.Type = obj.TYPE_CONST 903 p.From.Offset = int64(s390x.NotEqual & s390x.NotUnordered) // unordered is not possible 904 p.Reg = s390x.REG_R3 905 p.AddRestSourceConst(0) 906 if b.Succs[0].Block() != next { 907 s.Br(s390x.ABR, b.Succs[0].Block()) 908 } 909 return 910 case ssa.BlockExit, ssa.BlockRetJmp: 911 return 912 case ssa.BlockRet: 913 s.Prog(obj.ARET) 914 return 915 } 916 917 // Handle s390x-specific blocks. These blocks all have a 918 // condition code mask in the Aux value and 2 successors. 919 succs := [...]*ssa.Block{b.Succs[0].Block(), b.Succs[1].Block()} 920 mask := b.Aux.(s390x.CCMask) 921 922 // TODO: take into account Likely property for forward/backward 923 // branches. We currently can't do this because we don't know 924 // whether a block has already been emitted. In general forward 925 // branches are assumed 'not taken' and backward branches are 926 // assumed 'taken'. 927 if next == succs[0] { 928 succs[0], succs[1] = succs[1], succs[0] 929 mask = mask.Inverse() 930 } 931 932 p := s.Br(blockAsm(b), succs[0]) 933 switch b.Kind { 934 case ssa.BlockS390XBRC: 935 p.From.Type = obj.TYPE_CONST 936 p.From.Offset = int64(mask) 937 case ssa.BlockS390XCGRJ, ssa.BlockS390XCRJ, 938 ssa.BlockS390XCLGRJ, ssa.BlockS390XCLRJ: 939 p.From.Type = obj.TYPE_CONST 940 p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible 941 p.Reg = b.Controls[0].Reg() 942 p.AddRestSourceReg(b.Controls[1].Reg()) 943 case ssa.BlockS390XCGIJ, ssa.BlockS390XCIJ: 944 p.From.Type = obj.TYPE_CONST 945 p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible 946 p.Reg = b.Controls[0].Reg() 947 p.AddRestSourceConst(int64(int8(b.AuxInt))) 948 case ssa.BlockS390XCLGIJ, ssa.BlockS390XCLIJ: 949 p.From.Type = obj.TYPE_CONST 950 p.From.Offset = int64(mask & s390x.NotUnordered) // unordered is not possible 951 p.Reg = b.Controls[0].Reg() 952 p.AddRestSourceConst(int64(uint8(b.AuxInt))) 953 default: 954 b.Fatalf("branch not implemented: %s", b.LongString()) 955 } 956 if next != succs[1] { 957 s.Br(s390x.ABR, succs[1]) 958 } 959 }