github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/ppc64/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 ppc64 6 7 import ( 8 "cmd/compile/internal/gc" 9 "cmd/compile/internal/ssa" 10 "cmd/internal/obj" 11 "cmd/internal/obj/ppc64" 12 "math" 13 ) 14 15 var condOps = map[ssa.Op]obj.As{ 16 ssa.OpPPC64Equal: ppc64.ABEQ, 17 ssa.OpPPC64NotEqual: ppc64.ABNE, 18 ssa.OpPPC64LessThan: ppc64.ABLT, 19 ssa.OpPPC64GreaterEqual: ppc64.ABGE, 20 ssa.OpPPC64GreaterThan: ppc64.ABGT, 21 ssa.OpPPC64LessEqual: ppc64.ABLE, 22 23 ssa.OpPPC64FLessThan: ppc64.ABLT, // 1 branch for FCMP 24 ssa.OpPPC64FGreaterThan: ppc64.ABGT, // 1 branch for FCMP 25 ssa.OpPPC64FLessEqual: ppc64.ABLT, // 2 branches for FCMP <=, second is BEQ 26 ssa.OpPPC64FGreaterEqual: ppc64.ABGT, // 2 branches for FCMP >=, second is BEQ 27 } 28 29 // iselOp encodes mapping of comparison operations onto ISEL operands 30 type iselOp struct { 31 cond int64 32 valueIfCond int // if cond is true, the value to return (0 or 1) 33 } 34 35 // Input registers to ISEL used for comparison. Index 0 is zero, 1 is (will be) 1 36 var iselRegs = [2]int16{ppc64.REG_R0, ppc64.REGTMP} 37 38 var iselOps = map[ssa.Op]iselOp{ 39 ssa.OpPPC64Equal: iselOp{cond: ppc64.C_COND_EQ, valueIfCond: 1}, 40 ssa.OpPPC64NotEqual: iselOp{cond: ppc64.C_COND_EQ, valueIfCond: 0}, 41 ssa.OpPPC64LessThan: iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1}, 42 ssa.OpPPC64GreaterEqual: iselOp{cond: ppc64.C_COND_LT, valueIfCond: 0}, 43 ssa.OpPPC64GreaterThan: iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1}, 44 ssa.OpPPC64LessEqual: iselOp{cond: ppc64.C_COND_GT, valueIfCond: 0}, 45 ssa.OpPPC64FLessThan: iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1}, 46 ssa.OpPPC64FGreaterThan: iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1}, 47 ssa.OpPPC64FLessEqual: iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ 48 ssa.OpPPC64FGreaterEqual: iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ 49 } 50 51 // markMoves marks any MOVXconst ops that need to avoid clobbering flags. 52 func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) { 53 // flive := b.FlagsLiveAtEnd 54 // if b.Control != nil && b.Control.Type.IsFlags() { 55 // flive = true 56 // } 57 // for i := len(b.Values) - 1; i >= 0; i-- { 58 // v := b.Values[i] 59 // if flive && (v.Op == v.Op == ssa.OpPPC64MOVDconst) { 60 // // The "mark" is any non-nil Aux value. 61 // v.Aux = v 62 // } 63 // if v.Type.IsFlags() { 64 // flive = false 65 // } 66 // for _, a := range v.Args { 67 // if a.Type.IsFlags() { 68 // flive = true 69 // } 70 // } 71 // } 72 } 73 74 // loadByType returns the load instruction of the given type. 75 func loadByType(t ssa.Type) obj.As { 76 if t.IsFloat() { 77 switch t.Size() { 78 case 4: 79 return ppc64.AFMOVS 80 case 8: 81 return ppc64.AFMOVD 82 } 83 } else { 84 switch t.Size() { 85 case 1: 86 if t.IsSigned() { 87 return ppc64.AMOVB 88 } else { 89 return ppc64.AMOVBZ 90 } 91 case 2: 92 if t.IsSigned() { 93 return ppc64.AMOVH 94 } else { 95 return ppc64.AMOVHZ 96 } 97 case 4: 98 if t.IsSigned() { 99 return ppc64.AMOVW 100 } else { 101 return ppc64.AMOVWZ 102 } 103 case 8: 104 return ppc64.AMOVD 105 } 106 } 107 panic("bad load type") 108 } 109 110 // storeByType returns the store instruction of the given type. 111 func storeByType(t ssa.Type) obj.As { 112 if t.IsFloat() { 113 switch t.Size() { 114 case 4: 115 return ppc64.AFMOVS 116 case 8: 117 return ppc64.AFMOVD 118 } 119 } else { 120 switch t.Size() { 121 case 1: 122 return ppc64.AMOVB 123 case 2: 124 return ppc64.AMOVH 125 case 4: 126 return ppc64.AMOVW 127 case 8: 128 return ppc64.AMOVD 129 } 130 } 131 panic("bad store type") 132 } 133 134 func ssaGenISEL(v *ssa.Value, cr int64, r1, r2 int16) { 135 r := v.Reg() 136 p := gc.Prog(ppc64.AISEL) 137 p.To.Type = obj.TYPE_REG 138 p.To.Reg = r 139 p.Reg = r1 140 p.From3 = &obj.Addr{Type: obj.TYPE_REG, Reg: r2} 141 p.From.Type = obj.TYPE_CONST 142 p.From.Offset = cr 143 } 144 145 func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { 146 s.SetPos(v.Pos) 147 switch v.Op { 148 case ssa.OpInitMem: 149 // memory arg needs no code 150 case ssa.OpArg: 151 // input args need no code 152 case ssa.OpSP, ssa.OpSB, ssa.OpGetG: 153 // nothing to do 154 155 case ssa.OpCopy, ssa.OpPPC64MOVDconvert: 156 t := v.Type 157 if t.IsMemory() { 158 return 159 } 160 x := v.Args[0].Reg() 161 y := v.Reg() 162 if x != y { 163 rt := obj.TYPE_REG 164 op := ppc64.AMOVD 165 166 if t.IsFloat() { 167 op = ppc64.AFMOVD 168 } 169 p := gc.Prog(op) 170 p.From.Type = rt 171 p.From.Reg = x 172 p.To.Type = rt 173 p.To.Reg = y 174 } 175 176 case ssa.OpPPC64Xf2i64: 177 { 178 x := v.Args[0].Reg() 179 y := v.Reg() 180 p := gc.Prog(ppc64.AFMOVD) 181 p.From.Type = obj.TYPE_REG 182 p.From.Reg = x 183 s.AddrScratch(&p.To) 184 p = gc.Prog(ppc64.AMOVD) 185 p.To.Type = obj.TYPE_REG 186 p.To.Reg = y 187 s.AddrScratch(&p.From) 188 } 189 case ssa.OpPPC64Xi2f64: 190 { 191 x := v.Args[0].Reg() 192 y := v.Reg() 193 p := gc.Prog(ppc64.AMOVD) 194 p.From.Type = obj.TYPE_REG 195 p.From.Reg = x 196 s.AddrScratch(&p.To) 197 p = gc.Prog(ppc64.AFMOVD) 198 p.To.Type = obj.TYPE_REG 199 p.To.Reg = y 200 s.AddrScratch(&p.From) 201 } 202 203 case ssa.OpPPC64LoweredGetClosurePtr: 204 // Closure pointer is R11 (already) 205 gc.CheckLoweredGetClosurePtr(v) 206 207 case ssa.OpLoadReg: 208 loadOp := loadByType(v.Type) 209 p := gc.Prog(loadOp) 210 gc.AddrAuto(&p.From, v.Args[0]) 211 p.To.Type = obj.TYPE_REG 212 p.To.Reg = v.Reg() 213 214 case ssa.OpStoreReg: 215 storeOp := storeByType(v.Type) 216 p := gc.Prog(storeOp) 217 p.From.Type = obj.TYPE_REG 218 p.From.Reg = v.Args[0].Reg() 219 gc.AddrAuto(&p.To, v) 220 221 case ssa.OpPPC64DIVD: 222 // For now, 223 // 224 // cmp arg1, -1 225 // be ahead 226 // v = arg0 / arg1 227 // b over 228 // ahead: v = - arg0 229 // over: nop 230 r := v.Reg() 231 r0 := v.Args[0].Reg() 232 r1 := v.Args[1].Reg() 233 234 p := gc.Prog(ppc64.ACMP) 235 p.From.Type = obj.TYPE_REG 236 p.From.Reg = r1 237 p.To.Type = obj.TYPE_CONST 238 p.To.Offset = -1 239 240 pbahead := gc.Prog(ppc64.ABEQ) 241 pbahead.To.Type = obj.TYPE_BRANCH 242 243 p = gc.Prog(v.Op.Asm()) 244 p.From.Type = obj.TYPE_REG 245 p.From.Reg = r1 246 p.Reg = r0 247 p.To.Type = obj.TYPE_REG 248 p.To.Reg = r 249 250 pbover := gc.Prog(obj.AJMP) 251 pbover.To.Type = obj.TYPE_BRANCH 252 253 p = gc.Prog(ppc64.ANEG) 254 p.To.Type = obj.TYPE_REG 255 p.To.Reg = r 256 p.From.Type = obj.TYPE_REG 257 p.From.Reg = r0 258 gc.Patch(pbahead, p) 259 260 p = gc.Prog(obj.ANOP) 261 gc.Patch(pbover, p) 262 263 case ssa.OpPPC64DIVW: 264 // word-width version of above 265 r := v.Reg() 266 r0 := v.Args[0].Reg() 267 r1 := v.Args[1].Reg() 268 269 p := gc.Prog(ppc64.ACMPW) 270 p.From.Type = obj.TYPE_REG 271 p.From.Reg = r1 272 p.To.Type = obj.TYPE_CONST 273 p.To.Offset = -1 274 275 pbahead := gc.Prog(ppc64.ABEQ) 276 pbahead.To.Type = obj.TYPE_BRANCH 277 278 p = gc.Prog(v.Op.Asm()) 279 p.From.Type = obj.TYPE_REG 280 p.From.Reg = r1 281 p.Reg = r0 282 p.To.Type = obj.TYPE_REG 283 p.To.Reg = r 284 285 pbover := gc.Prog(obj.AJMP) 286 pbover.To.Type = obj.TYPE_BRANCH 287 288 p = gc.Prog(ppc64.ANEG) 289 p.To.Type = obj.TYPE_REG 290 p.To.Reg = r 291 p.From.Type = obj.TYPE_REG 292 p.From.Reg = r0 293 gc.Patch(pbahead, p) 294 295 p = gc.Prog(obj.ANOP) 296 gc.Patch(pbover, p) 297 298 case ssa.OpPPC64ADD, ssa.OpPPC64FADD, ssa.OpPPC64FADDS, ssa.OpPPC64SUB, ssa.OpPPC64FSUB, ssa.OpPPC64FSUBS, 299 ssa.OpPPC64MULLD, ssa.OpPPC64MULLW, ssa.OpPPC64DIVDU, ssa.OpPPC64DIVWU, 300 ssa.OpPPC64SRAD, ssa.OpPPC64SRAW, ssa.OpPPC64SRD, ssa.OpPPC64SRW, ssa.OpPPC64SLD, ssa.OpPPC64SLW, 301 ssa.OpPPC64MULHD, ssa.OpPPC64MULHW, ssa.OpPPC64MULHDU, ssa.OpPPC64MULHWU, 302 ssa.OpPPC64FMUL, ssa.OpPPC64FMULS, ssa.OpPPC64FDIV, ssa.OpPPC64FDIVS, 303 ssa.OpPPC64AND, ssa.OpPPC64OR, ssa.OpPPC64ANDN, ssa.OpPPC64ORN, ssa.OpPPC64NOR, ssa.OpPPC64XOR, ssa.OpPPC64EQV: 304 r := v.Reg() 305 r1 := v.Args[0].Reg() 306 r2 := v.Args[1].Reg() 307 p := gc.Prog(v.Op.Asm()) 308 p.From.Type = obj.TYPE_REG 309 p.From.Reg = r2 310 p.Reg = r1 311 p.To.Type = obj.TYPE_REG 312 p.To.Reg = r 313 314 case ssa.OpPPC64MaskIfNotCarry: 315 r := v.Reg() 316 p := gc.Prog(v.Op.Asm()) 317 p.From.Type = obj.TYPE_REG 318 p.From.Reg = ppc64.REGZERO 319 p.To.Type = obj.TYPE_REG 320 p.To.Reg = r 321 322 case ssa.OpPPC64ADDconstForCarry: 323 r1 := v.Args[0].Reg() 324 p := gc.Prog(v.Op.Asm()) 325 p.Reg = r1 326 p.From.Type = obj.TYPE_CONST 327 p.From.Offset = v.AuxInt 328 p.To.Type = obj.TYPE_REG 329 p.To.Reg = ppc64.REGTMP // Ignored; this is for the carry effect. 330 331 case ssa.OpPPC64NEG, ssa.OpPPC64FNEG, ssa.OpPPC64FSQRT, ssa.OpPPC64FSQRTS, ssa.OpPPC64FCTIDZ, ssa.OpPPC64FCTIWZ, ssa.OpPPC64FCFID, ssa.OpPPC64FRSP: 332 r := v.Reg() 333 p := gc.Prog(v.Op.Asm()) 334 p.To.Type = obj.TYPE_REG 335 p.To.Reg = r 336 p.From.Type = obj.TYPE_REG 337 p.From.Reg = v.Args[0].Reg() 338 339 case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst, 340 ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst: 341 p := gc.Prog(v.Op.Asm()) 342 p.Reg = v.Args[0].Reg() 343 344 if v.Aux != nil { 345 p.From.Type = obj.TYPE_CONST 346 p.From.Offset = gc.AuxOffset(v) 347 } else { 348 p.From.Type = obj.TYPE_CONST 349 p.From.Offset = v.AuxInt 350 } 351 352 p.To.Type = obj.TYPE_REG 353 p.To.Reg = v.Reg() 354 355 case ssa.OpPPC64ANDCCconst: 356 p := gc.Prog(v.Op.Asm()) 357 p.Reg = v.Args[0].Reg() 358 359 if v.Aux != nil { 360 p.From.Type = obj.TYPE_CONST 361 p.From.Offset = gc.AuxOffset(v) 362 } else { 363 p.From.Type = obj.TYPE_CONST 364 p.From.Offset = v.AuxInt 365 } 366 367 p.To.Type = obj.TYPE_REG 368 p.To.Reg = ppc64.REGTMP // discard result 369 370 case ssa.OpPPC64MOVDaddr: 371 p := gc.Prog(ppc64.AMOVD) 372 p.From.Type = obj.TYPE_ADDR 373 p.To.Type = obj.TYPE_REG 374 p.To.Reg = v.Reg() 375 376 var wantreg string 377 // Suspect comment, copied from ARM code 378 // MOVD $sym+off(base), R 379 // the assembler expands it as the following: 380 // - base is SP: add constant offset to SP 381 // when constant is large, tmp register (R11) may be used 382 // - base is SB: load external address from constant pool (use relocation) 383 switch v.Aux.(type) { 384 default: 385 v.Fatalf("aux is of unknown type %T", v.Aux) 386 case *ssa.ExternSymbol: 387 wantreg = "SB" 388 gc.AddAux(&p.From, v) 389 case *ssa.ArgSymbol, *ssa.AutoSymbol: 390 wantreg = "SP" 391 gc.AddAux(&p.From, v) 392 case nil: 393 // No sym, just MOVD $off(SP), R 394 wantreg = "SP" 395 p.From.Reg = ppc64.REGSP 396 p.From.Offset = v.AuxInt 397 } 398 if reg := v.Args[0].RegName(); reg != wantreg { 399 v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) 400 } 401 402 case ssa.OpPPC64MOVDconst: 403 p := gc.Prog(v.Op.Asm()) 404 p.From.Type = obj.TYPE_CONST 405 p.From.Offset = v.AuxInt 406 p.To.Type = obj.TYPE_REG 407 p.To.Reg = v.Reg() 408 409 case ssa.OpPPC64FMOVDconst, ssa.OpPPC64FMOVSconst: 410 p := gc.Prog(v.Op.Asm()) 411 p.From.Type = obj.TYPE_FCONST 412 p.From.Val = math.Float64frombits(uint64(v.AuxInt)) 413 p.To.Type = obj.TYPE_REG 414 p.To.Reg = v.Reg() 415 416 case ssa.OpPPC64FCMPU, ssa.OpPPC64CMP, ssa.OpPPC64CMPW, ssa.OpPPC64CMPU, ssa.OpPPC64CMPWU: 417 p := gc.Prog(v.Op.Asm()) 418 p.From.Type = obj.TYPE_REG 419 p.From.Reg = v.Args[0].Reg() 420 p.To.Type = obj.TYPE_REG 421 p.To.Reg = v.Args[1].Reg() 422 423 case ssa.OpPPC64CMPconst, ssa.OpPPC64CMPUconst, ssa.OpPPC64CMPWconst, ssa.OpPPC64CMPWUconst: 424 p := gc.Prog(v.Op.Asm()) 425 p.From.Type = obj.TYPE_REG 426 p.From.Reg = v.Args[0].Reg() 427 p.To.Type = obj.TYPE_CONST 428 p.To.Offset = v.AuxInt 429 430 case ssa.OpPPC64MOVBreg, ssa.OpPPC64MOVBZreg, ssa.OpPPC64MOVHreg, ssa.OpPPC64MOVHZreg, ssa.OpPPC64MOVWreg, ssa.OpPPC64MOVWZreg: 431 // Shift in register to required size 432 p := gc.Prog(v.Op.Asm()) 433 p.From.Type = obj.TYPE_REG 434 p.From.Reg = v.Args[0].Reg() 435 p.To.Reg = v.Reg() 436 p.To.Type = obj.TYPE_REG 437 438 case ssa.OpPPC64MOVDload, ssa.OpPPC64MOVWload, ssa.OpPPC64MOVHload, ssa.OpPPC64MOVWZload, ssa.OpPPC64MOVBZload, ssa.OpPPC64MOVHZload: 439 p := gc.Prog(v.Op.Asm()) 440 p.From.Type = obj.TYPE_MEM 441 p.From.Reg = v.Args[0].Reg() 442 gc.AddAux(&p.From, v) 443 p.To.Type = obj.TYPE_REG 444 p.To.Reg = v.Reg() 445 446 case ssa.OpPPC64FMOVDload, ssa.OpPPC64FMOVSload: 447 p := gc.Prog(v.Op.Asm()) 448 p.From.Type = obj.TYPE_MEM 449 p.From.Reg = v.Args[0].Reg() 450 gc.AddAux(&p.From, v) 451 p.To.Type = obj.TYPE_REG 452 p.To.Reg = v.Reg() 453 454 case ssa.OpPPC64MOVDstorezero, ssa.OpPPC64MOVWstorezero, ssa.OpPPC64MOVHstorezero, ssa.OpPPC64MOVBstorezero: 455 p := gc.Prog(v.Op.Asm()) 456 p.From.Type = obj.TYPE_REG 457 p.From.Reg = ppc64.REGZERO 458 p.To.Type = obj.TYPE_MEM 459 p.To.Reg = v.Args[0].Reg() 460 gc.AddAux(&p.To, v) 461 462 case ssa.OpPPC64MOVDstore, ssa.OpPPC64MOVWstore, ssa.OpPPC64MOVHstore, ssa.OpPPC64MOVBstore: 463 p := gc.Prog(v.Op.Asm()) 464 p.From.Type = obj.TYPE_REG 465 p.From.Reg = v.Args[1].Reg() 466 p.To.Type = obj.TYPE_MEM 467 p.To.Reg = v.Args[0].Reg() 468 gc.AddAux(&p.To, v) 469 case ssa.OpPPC64FMOVDstore, ssa.OpPPC64FMOVSstore: 470 p := gc.Prog(v.Op.Asm()) 471 p.From.Type = obj.TYPE_REG 472 p.From.Reg = v.Args[1].Reg() 473 p.To.Type = obj.TYPE_MEM 474 p.To.Reg = v.Args[0].Reg() 475 gc.AddAux(&p.To, v) 476 477 case ssa.OpPPC64Equal, 478 ssa.OpPPC64NotEqual, 479 ssa.OpPPC64LessThan, 480 ssa.OpPPC64FLessThan, 481 ssa.OpPPC64LessEqual, 482 ssa.OpPPC64GreaterThan, 483 ssa.OpPPC64FGreaterThan, 484 ssa.OpPPC64GreaterEqual: 485 486 // On Power7 or later, can use isel instruction: 487 // for a < b, a > b, a = b: 488 // rtmp := 1 489 // isel rt,rtmp,r0,cond // rt is target in ppc asm 490 491 // for a >= b, a <= b, a != b: 492 // rtmp := 1 493 // isel rt,0,rtmp,!cond // rt is target in ppc asm 494 495 if v.Block.Func.Config.OldArch { 496 p := gc.Prog(ppc64.AMOVD) 497 p.From.Type = obj.TYPE_CONST 498 p.From.Offset = 1 499 p.To.Type = obj.TYPE_REG 500 p.To.Reg = v.Reg() 501 502 pb := gc.Prog(condOps[v.Op]) 503 pb.To.Type = obj.TYPE_BRANCH 504 505 p = gc.Prog(ppc64.AMOVD) 506 p.From.Type = obj.TYPE_CONST 507 p.From.Offset = 0 508 p.To.Type = obj.TYPE_REG 509 p.To.Reg = v.Reg() 510 511 p = gc.Prog(obj.ANOP) 512 gc.Patch(pb, p) 513 break 514 } 515 // Modern PPC uses ISEL 516 p := gc.Prog(ppc64.AMOVD) 517 p.From.Type = obj.TYPE_CONST 518 p.From.Offset = 1 519 p.To.Type = obj.TYPE_REG 520 p.To.Reg = iselRegs[1] 521 iop := iselOps[v.Op] 522 ssaGenISEL(v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond]) 523 524 case ssa.OpPPC64FLessEqual, // These include a second branch for EQ -- dealing with NaN prevents REL= to !REL conversion 525 ssa.OpPPC64FGreaterEqual: 526 527 if v.Block.Func.Config.OldArch { 528 p := gc.Prog(ppc64.AMOVW) 529 p.From.Type = obj.TYPE_CONST 530 p.From.Offset = 1 531 p.To.Type = obj.TYPE_REG 532 p.To.Reg = v.Reg() 533 534 pb0 := gc.Prog(condOps[v.Op]) 535 pb0.To.Type = obj.TYPE_BRANCH 536 pb1 := gc.Prog(ppc64.ABEQ) 537 pb1.To.Type = obj.TYPE_BRANCH 538 539 p = gc.Prog(ppc64.AMOVW) 540 p.From.Type = obj.TYPE_CONST 541 p.From.Offset = 0 542 p.To.Type = obj.TYPE_REG 543 p.To.Reg = v.Reg() 544 545 p = gc.Prog(obj.ANOP) 546 gc.Patch(pb0, p) 547 gc.Patch(pb1, p) 548 break 549 } 550 // Modern PPC uses ISEL 551 p := gc.Prog(ppc64.AMOVD) 552 p.From.Type = obj.TYPE_CONST 553 p.From.Offset = 1 554 p.To.Type = obj.TYPE_REG 555 p.To.Reg = iselRegs[1] 556 iop := iselOps[v.Op] 557 ssaGenISEL(v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond]) 558 ssaGenISEL(v, ppc64.C_COND_EQ, iselRegs[1], v.Reg()) 559 560 case ssa.OpPPC64LoweredZero: 561 // Similar to how this is done on ARM, 562 // except that PPC MOVDU x,off(y) is *(y+off) = x; y=y+off 563 // not store-and-increment. 564 // Therefore R3 should be dest-align 565 // and arg1 should be dest+size-align 566 // HOWEVER, the input dest address cannot be dest-align because 567 // that does not necessarily address valid memory and it's not 568 // known how that might be optimized. Therefore, correct it in 569 // in the expansion: 570 // 571 // ADD -8,R3,R3 572 // MOVDU R0, 8(R3) 573 // CMP R3, Rarg1 574 // BL -2(PC) 575 // arg1 is the address of the last element to zero 576 // auxint is alignment 577 var sz int64 578 var movu obj.As 579 switch { 580 case v.AuxInt%8 == 0: 581 sz = 8 582 movu = ppc64.AMOVDU 583 case v.AuxInt%4 == 0: 584 sz = 4 585 movu = ppc64.AMOVWZU // MOVWU instruction not implemented 586 case v.AuxInt%2 == 0: 587 sz = 2 588 movu = ppc64.AMOVHU 589 default: 590 sz = 1 591 movu = ppc64.AMOVBU 592 } 593 594 p := gc.Prog(ppc64.AADD) 595 p.Reg = v.Args[0].Reg() 596 p.From.Type = obj.TYPE_CONST 597 p.From.Offset = -sz 598 p.To.Type = obj.TYPE_REG 599 p.To.Reg = v.Args[0].Reg() 600 601 p = gc.Prog(movu) 602 p.From.Type = obj.TYPE_REG 603 p.From.Reg = ppc64.REG_R0 604 p.To.Type = obj.TYPE_MEM 605 p.To.Reg = v.Args[0].Reg() 606 p.To.Offset = sz 607 608 p2 := gc.Prog(ppc64.ACMPU) 609 p2.From.Type = obj.TYPE_REG 610 p2.From.Reg = v.Args[0].Reg() 611 p2.To.Reg = v.Args[1].Reg() 612 p2.To.Type = obj.TYPE_REG 613 614 p3 := gc.Prog(ppc64.ABLT) 615 p3.To.Type = obj.TYPE_BRANCH 616 gc.Patch(p3, p) 617 618 case ssa.OpPPC64LoweredMove: 619 // Similar to how this is done on ARM, 620 // except that PPC MOVDU x,off(y) is *(y+off) = x; y=y+off, 621 // not store-and-increment. 622 // Inputs must be valid pointers to memory, 623 // so adjust arg0 and arg1 as part of the expansion. 624 // arg2 should be src+size-align, 625 // 626 // ADD -8,R3,R3 627 // ADD -8,R4,R4 628 // MOVDU 8(R4), Rtmp 629 // MOVDU Rtmp, 8(R3) 630 // CMP R4, Rarg2 631 // BL -3(PC) 632 // arg2 is the address of the last element of src 633 // auxint is alignment 634 var sz int64 635 var movu obj.As 636 switch { 637 case v.AuxInt%8 == 0: 638 sz = 8 639 movu = ppc64.AMOVDU 640 case v.AuxInt%4 == 0: 641 sz = 4 642 movu = ppc64.AMOVWZU // MOVWU instruction not implemented 643 case v.AuxInt%2 == 0: 644 sz = 2 645 movu = ppc64.AMOVHU 646 default: 647 sz = 1 648 movu = ppc64.AMOVBU 649 } 650 651 p := gc.Prog(ppc64.AADD) 652 p.Reg = v.Args[0].Reg() 653 p.From.Type = obj.TYPE_CONST 654 p.From.Offset = -sz 655 p.To.Type = obj.TYPE_REG 656 p.To.Reg = v.Args[0].Reg() 657 658 p = gc.Prog(ppc64.AADD) 659 p.Reg = v.Args[1].Reg() 660 p.From.Type = obj.TYPE_CONST 661 p.From.Offset = -sz 662 p.To.Type = obj.TYPE_REG 663 p.To.Reg = v.Args[1].Reg() 664 665 p = gc.Prog(movu) 666 p.From.Type = obj.TYPE_MEM 667 p.From.Reg = v.Args[1].Reg() 668 p.From.Offset = sz 669 p.To.Type = obj.TYPE_REG 670 p.To.Reg = ppc64.REGTMP 671 672 p2 := gc.Prog(movu) 673 p2.From.Type = obj.TYPE_REG 674 p2.From.Reg = ppc64.REGTMP 675 p2.To.Type = obj.TYPE_MEM 676 p2.To.Reg = v.Args[0].Reg() 677 p2.To.Offset = sz 678 679 p3 := gc.Prog(ppc64.ACMPU) 680 p3.From.Reg = v.Args[1].Reg() 681 p3.From.Type = obj.TYPE_REG 682 p3.To.Reg = v.Args[2].Reg() 683 p3.To.Type = obj.TYPE_REG 684 685 p4 := gc.Prog(ppc64.ABLT) 686 p4.To.Type = obj.TYPE_BRANCH 687 gc.Patch(p4, p) 688 689 case ssa.OpPPC64CALLstatic: 690 if v.Aux.(*obj.LSym) == gc.Deferreturn { 691 // Deferred calls will appear to be returning to 692 // the CALL deferreturn(SB) that we are about to emit. 693 // However, the stack trace code will show the line 694 // of the instruction byte before the return PC. 695 // To avoid that being an unrelated instruction, 696 // insert two actual hardware NOPs that will have the right line number. 697 // This is different from obj.ANOP, which is a virtual no-op 698 // that doesn't make it into the instruction stream. 699 // PPC64 is unusual because TWO nops are required 700 // (see gc/cgen.go, gc/plive.go -- copy of comment below) 701 // 702 // On ppc64, when compiling Go into position 703 // independent code on ppc64le we insert an 704 // instruction to reload the TOC pointer from the 705 // stack as well. See the long comment near 706 // jmpdefer in runtime/asm_ppc64.s for why. 707 // If the MOVD is not needed, insert a hardware NOP 708 // so that the same number of instructions are used 709 // on ppc64 in both shared and non-shared modes. 710 ginsnop() 711 if gc.Ctxt.Flag_shared { 712 p := gc.Prog(ppc64.AMOVD) 713 p.From.Type = obj.TYPE_MEM 714 p.From.Offset = 24 715 p.From.Reg = ppc64.REGSP 716 p.To.Type = obj.TYPE_REG 717 p.To.Reg = ppc64.REG_R2 718 } else { 719 ginsnop() 720 } 721 } 722 p := gc.Prog(obj.ACALL) 723 p.To.Type = obj.TYPE_MEM 724 p.To.Name = obj.NAME_EXTERN 725 p.To.Sym = v.Aux.(*obj.LSym) 726 if gc.Maxarg < v.AuxInt { 727 gc.Maxarg = v.AuxInt 728 } 729 730 case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter: 731 p := gc.Prog(ppc64.AMOVD) 732 p.From.Type = obj.TYPE_REG 733 p.From.Reg = v.Args[0].Reg() 734 p.To.Type = obj.TYPE_REG 735 p.To.Reg = ppc64.REG_CTR 736 737 if gc.Ctxt.Flag_shared && p.From.Reg != ppc64.REG_R12 { 738 // Make sure function pointer is in R12 as well when 739 // compiling Go into PIC. 740 // TODO(mwhudson): it would obviously be better to 741 // change the register allocation to put the value in 742 // R12 already, but I don't know how to do that. 743 // TODO: We have the technology now to implement TODO above. 744 q := gc.Prog(ppc64.AMOVD) 745 q.From = p.From 746 q.To.Type = obj.TYPE_REG 747 q.To.Reg = ppc64.REG_R12 748 } 749 750 pp := gc.Prog(obj.ACALL) 751 pp.To.Type = obj.TYPE_REG 752 pp.To.Reg = ppc64.REG_CTR 753 754 if gc.Ctxt.Flag_shared { 755 // When compiling Go into PIC, the function we just 756 // called via pointer might have been implemented in 757 // a separate module and so overwritten the TOC 758 // pointer in R2; reload it. 759 q := gc.Prog(ppc64.AMOVD) 760 q.From.Type = obj.TYPE_MEM 761 q.From.Offset = 24 762 q.From.Reg = ppc64.REGSP 763 q.To.Type = obj.TYPE_REG 764 q.To.Reg = ppc64.REG_R2 765 } 766 767 if gc.Maxarg < v.AuxInt { 768 gc.Maxarg = v.AuxInt 769 } 770 771 case ssa.OpPPC64CALLdefer: 772 p := gc.Prog(obj.ACALL) 773 p.To.Type = obj.TYPE_MEM 774 p.To.Name = obj.NAME_EXTERN 775 p.To.Sym = gc.Deferproc 776 if gc.Maxarg < v.AuxInt { 777 gc.Maxarg = v.AuxInt 778 } 779 case ssa.OpPPC64CALLgo: 780 p := gc.Prog(obj.ACALL) 781 p.To.Type = obj.TYPE_MEM 782 p.To.Name = obj.NAME_EXTERN 783 p.To.Sym = gc.Newproc 784 if gc.Maxarg < v.AuxInt { 785 gc.Maxarg = v.AuxInt 786 } 787 case ssa.OpVarDef: 788 gc.Gvardef(v.Aux.(*gc.Node)) 789 case ssa.OpVarKill: 790 gc.Gvarkill(v.Aux.(*gc.Node)) 791 case ssa.OpVarLive: 792 gc.Gvarlive(v.Aux.(*gc.Node)) 793 case ssa.OpKeepAlive: 794 gc.KeepAlive(v) 795 case ssa.OpPhi: 796 gc.CheckLoweredPhi(v) 797 798 case ssa.OpPPC64LoweredNilCheck: 799 // Issue a load which will fault if arg is nil. 800 p := gc.Prog(ppc64.AMOVBZ) 801 p.From.Type = obj.TYPE_MEM 802 p.From.Reg = v.Args[0].Reg() 803 gc.AddAux(&p.From, v) 804 p.To.Type = obj.TYPE_REG 805 p.To.Reg = ppc64.REGTMP 806 if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers 807 gc.Warnl(v.Pos, "generated nil check") 808 } 809 810 case ssa.OpPPC64InvertFlags: 811 v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) 812 case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT: 813 v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString()) 814 815 default: 816 v.Fatalf("genValue not implemented: %s", v.LongString()) 817 } 818 } 819 820 var blockJump = [...]struct { 821 asm, invasm obj.As 822 asmeq, invasmun bool 823 }{ 824 ssa.BlockPPC64EQ: {ppc64.ABEQ, ppc64.ABNE, false, false}, 825 ssa.BlockPPC64NE: {ppc64.ABNE, ppc64.ABEQ, false, false}, 826 827 ssa.BlockPPC64LT: {ppc64.ABLT, ppc64.ABGE, false, false}, 828 ssa.BlockPPC64GE: {ppc64.ABGE, ppc64.ABLT, false, false}, 829 ssa.BlockPPC64LE: {ppc64.ABLE, ppc64.ABGT, false, false}, 830 ssa.BlockPPC64GT: {ppc64.ABGT, ppc64.ABLE, false, false}, 831 832 // TODO: need to work FP comparisons into block jumps 833 ssa.BlockPPC64FLT: {ppc64.ABLT, ppc64.ABGE, false, false}, 834 ssa.BlockPPC64FGE: {ppc64.ABGT, ppc64.ABLT, true, true}, // GE = GT or EQ; !GE = LT or UN 835 ssa.BlockPPC64FLE: {ppc64.ABLT, ppc64.ABGT, true, true}, // LE = LT or EQ; !LE = GT or UN 836 ssa.BlockPPC64FGT: {ppc64.ABGT, ppc64.ABLE, false, false}, 837 } 838 839 func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { 840 s.SetPos(b.Pos) 841 842 switch b.Kind { 843 844 case ssa.BlockDefer: 845 // defer returns in R3: 846 // 0 if we should continue executing 847 // 1 if we should jump to deferreturn call 848 p := gc.Prog(ppc64.ACMP) 849 p.From.Type = obj.TYPE_REG 850 p.From.Reg = ppc64.REG_R3 851 p.To.Type = obj.TYPE_REG 852 p.To.Reg = ppc64.REG_R0 853 854 p = gc.Prog(ppc64.ABNE) 855 p.To.Type = obj.TYPE_BRANCH 856 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()}) 857 if b.Succs[0].Block() != next { 858 p := gc.Prog(obj.AJMP) 859 p.To.Type = obj.TYPE_BRANCH 860 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 861 } 862 863 case ssa.BlockPlain: 864 if b.Succs[0].Block() != next { 865 p := gc.Prog(obj.AJMP) 866 p.To.Type = obj.TYPE_BRANCH 867 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 868 } 869 case ssa.BlockExit: 870 gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here 871 case ssa.BlockRet: 872 gc.Prog(obj.ARET) 873 case ssa.BlockRetJmp: 874 p := gc.Prog(obj.AJMP) 875 p.To.Type = obj.TYPE_MEM 876 p.To.Name = obj.NAME_EXTERN 877 p.To.Sym = b.Aux.(*obj.LSym) 878 879 case ssa.BlockPPC64EQ, ssa.BlockPPC64NE, 880 ssa.BlockPPC64LT, ssa.BlockPPC64GE, 881 ssa.BlockPPC64LE, ssa.BlockPPC64GT, 882 ssa.BlockPPC64FLT, ssa.BlockPPC64FGE, 883 ssa.BlockPPC64FLE, ssa.BlockPPC64FGT: 884 jmp := blockJump[b.Kind] 885 likely := b.Likely 886 var p *obj.Prog 887 switch next { 888 case b.Succs[0].Block(): 889 p = gc.Prog(jmp.invasm) 890 likely *= -1 891 p.To.Type = obj.TYPE_BRANCH 892 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()}) 893 if jmp.invasmun { 894 // TODO: The second branch is probably predict-not-taken since it is for FP unordered 895 q := gc.Prog(ppc64.ABVS) 896 q.To.Type = obj.TYPE_BRANCH 897 s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()}) 898 } 899 case b.Succs[1].Block(): 900 p = gc.Prog(jmp.asm) 901 p.To.Type = obj.TYPE_BRANCH 902 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 903 if jmp.asmeq { 904 q := gc.Prog(ppc64.ABEQ) 905 q.To.Type = obj.TYPE_BRANCH 906 s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[0].Block()}) 907 } 908 default: 909 p = gc.Prog(jmp.asm) 910 p.To.Type = obj.TYPE_BRANCH 911 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 912 if jmp.asmeq { 913 q := gc.Prog(ppc64.ABEQ) 914 q.To.Type = obj.TYPE_BRANCH 915 s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[0].Block()}) 916 } 917 q := gc.Prog(obj.AJMP) 918 q.To.Type = obj.TYPE_BRANCH 919 s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()}) 920 } 921 922 // liblink reorders the instruction stream as it sees fit. 923 // Pass along what we know so liblink can make use of it. 924 // TODO: Once we've fully switched to SSA, 925 // make liblink leave our output alone. 926 //switch likely { 927 //case ssa.BranchUnlikely: 928 // p.From.Type = obj.TYPE_CONST 929 // p.From.Offset = 0 930 //case ssa.BranchLikely: 931 // p.From.Type = obj.TYPE_CONST 932 // p.From.Offset = 1 933 //} 934 935 default: 936 b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString()) 937 } 938 }