github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/compile/internal/arm64/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 arm64 6 7 import ( 8 "math" 9 10 "cmd/compile/internal/gc" 11 "cmd/compile/internal/ssa" 12 "cmd/internal/obj" 13 "cmd/internal/obj/arm64" 14 ) 15 16 // loadByType returns the load instruction of the given type. 17 func loadByType(t ssa.Type) obj.As { 18 if t.IsFloat() { 19 switch t.Size() { 20 case 4: 21 return arm64.AFMOVS 22 case 8: 23 return arm64.AFMOVD 24 } 25 } else { 26 switch t.Size() { 27 case 1: 28 if t.IsSigned() { 29 return arm64.AMOVB 30 } else { 31 return arm64.AMOVBU 32 } 33 case 2: 34 if t.IsSigned() { 35 return arm64.AMOVH 36 } else { 37 return arm64.AMOVHU 38 } 39 case 4: 40 if t.IsSigned() { 41 return arm64.AMOVW 42 } else { 43 return arm64.AMOVWU 44 } 45 case 8: 46 return arm64.AMOVD 47 } 48 } 49 panic("bad load type") 50 } 51 52 // storeByType returns the store instruction of the given type. 53 func storeByType(t ssa.Type) obj.As { 54 if t.IsFloat() { 55 switch t.Size() { 56 case 4: 57 return arm64.AFMOVS 58 case 8: 59 return arm64.AFMOVD 60 } 61 } else { 62 switch t.Size() { 63 case 1: 64 return arm64.AMOVB 65 case 2: 66 return arm64.AMOVH 67 case 4: 68 return arm64.AMOVW 69 case 8: 70 return arm64.AMOVD 71 } 72 } 73 panic("bad store type") 74 } 75 76 // makeshift encodes a register shifted by a constant, used as an Offset in Prog 77 func makeshift(reg int16, typ int64, s int64) int64 { 78 return int64(reg&31)<<16 | typ | (s&63)<<10 79 } 80 81 // genshift generates a Prog for r = r0 op (r1 shifted by n) 82 func genshift(s *gc.SSAGenState, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { 83 p := s.Prog(as) 84 p.From.Type = obj.TYPE_SHIFT 85 p.From.Offset = makeshift(r1, typ, n) 86 p.Reg = r0 87 if r != 0 { 88 p.To.Type = obj.TYPE_REG 89 p.To.Reg = r 90 } 91 return p 92 } 93 94 func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { 95 switch v.Op { 96 case ssa.OpCopy, ssa.OpARM64MOVDconvert, ssa.OpARM64MOVDreg: 97 if v.Type.IsMemory() { 98 return 99 } 100 x := v.Args[0].Reg() 101 y := v.Reg() 102 if x == y { 103 return 104 } 105 as := arm64.AMOVD 106 if v.Type.IsFloat() { 107 switch v.Type.Size() { 108 case 4: 109 as = arm64.AFMOVS 110 case 8: 111 as = arm64.AFMOVD 112 default: 113 panic("bad float size") 114 } 115 } 116 p := s.Prog(as) 117 p.From.Type = obj.TYPE_REG 118 p.From.Reg = x 119 p.To.Type = obj.TYPE_REG 120 p.To.Reg = y 121 case ssa.OpARM64MOVDnop: 122 if v.Reg() != v.Args[0].Reg() { 123 v.Fatalf("input[0] and output not in same register %s", v.LongString()) 124 } 125 // nothing to do 126 case ssa.OpLoadReg: 127 if v.Type.IsFlags() { 128 v.Fatalf("load flags not implemented: %v", v.LongString()) 129 return 130 } 131 p := s.Prog(loadByType(v.Type)) 132 gc.AddrAuto(&p.From, v.Args[0]) 133 p.To.Type = obj.TYPE_REG 134 p.To.Reg = v.Reg() 135 case ssa.OpStoreReg: 136 if v.Type.IsFlags() { 137 v.Fatalf("store flags not implemented: %v", v.LongString()) 138 return 139 } 140 p := s.Prog(storeByType(v.Type)) 141 p.From.Type = obj.TYPE_REG 142 p.From.Reg = v.Args[0].Reg() 143 gc.AddrAuto(&p.To, v) 144 case ssa.OpARM64ADD, 145 ssa.OpARM64SUB, 146 ssa.OpARM64AND, 147 ssa.OpARM64OR, 148 ssa.OpARM64XOR, 149 ssa.OpARM64BIC, 150 ssa.OpARM64MUL, 151 ssa.OpARM64MULW, 152 ssa.OpARM64MULH, 153 ssa.OpARM64UMULH, 154 ssa.OpARM64MULL, 155 ssa.OpARM64UMULL, 156 ssa.OpARM64DIV, 157 ssa.OpARM64UDIV, 158 ssa.OpARM64DIVW, 159 ssa.OpARM64UDIVW, 160 ssa.OpARM64MOD, 161 ssa.OpARM64UMOD, 162 ssa.OpARM64MODW, 163 ssa.OpARM64UMODW, 164 ssa.OpARM64SLL, 165 ssa.OpARM64SRL, 166 ssa.OpARM64SRA, 167 ssa.OpARM64FADDS, 168 ssa.OpARM64FADDD, 169 ssa.OpARM64FSUBS, 170 ssa.OpARM64FSUBD, 171 ssa.OpARM64FMULS, 172 ssa.OpARM64FMULD, 173 ssa.OpARM64FDIVS, 174 ssa.OpARM64FDIVD: 175 r := v.Reg() 176 r1 := v.Args[0].Reg() 177 r2 := v.Args[1].Reg() 178 p := s.Prog(v.Op.Asm()) 179 p.From.Type = obj.TYPE_REG 180 p.From.Reg = r2 181 p.Reg = r1 182 p.To.Type = obj.TYPE_REG 183 p.To.Reg = r 184 case ssa.OpARM64ADDconst, 185 ssa.OpARM64SUBconst, 186 ssa.OpARM64ANDconst, 187 ssa.OpARM64ORconst, 188 ssa.OpARM64XORconst, 189 ssa.OpARM64BICconst, 190 ssa.OpARM64SLLconst, 191 ssa.OpARM64SRLconst, 192 ssa.OpARM64SRAconst, 193 ssa.OpARM64RORconst, 194 ssa.OpARM64RORWconst: 195 p := s.Prog(v.Op.Asm()) 196 p.From.Type = obj.TYPE_CONST 197 p.From.Offset = v.AuxInt 198 p.Reg = v.Args[0].Reg() 199 p.To.Type = obj.TYPE_REG 200 p.To.Reg = v.Reg() 201 case ssa.OpARM64ADDshiftLL, 202 ssa.OpARM64SUBshiftLL, 203 ssa.OpARM64ANDshiftLL, 204 ssa.OpARM64ORshiftLL, 205 ssa.OpARM64XORshiftLL, 206 ssa.OpARM64BICshiftLL: 207 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) 208 case ssa.OpARM64ADDshiftRL, 209 ssa.OpARM64SUBshiftRL, 210 ssa.OpARM64ANDshiftRL, 211 ssa.OpARM64ORshiftRL, 212 ssa.OpARM64XORshiftRL, 213 ssa.OpARM64BICshiftRL: 214 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) 215 case ssa.OpARM64ADDshiftRA, 216 ssa.OpARM64SUBshiftRA, 217 ssa.OpARM64ANDshiftRA, 218 ssa.OpARM64ORshiftRA, 219 ssa.OpARM64XORshiftRA, 220 ssa.OpARM64BICshiftRA: 221 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) 222 case ssa.OpARM64MOVDconst: 223 p := s.Prog(v.Op.Asm()) 224 p.From.Type = obj.TYPE_CONST 225 p.From.Offset = v.AuxInt 226 p.To.Type = obj.TYPE_REG 227 p.To.Reg = v.Reg() 228 case ssa.OpARM64FMOVSconst, 229 ssa.OpARM64FMOVDconst: 230 p := s.Prog(v.Op.Asm()) 231 p.From.Type = obj.TYPE_FCONST 232 p.From.Val = math.Float64frombits(uint64(v.AuxInt)) 233 p.To.Type = obj.TYPE_REG 234 p.To.Reg = v.Reg() 235 case ssa.OpARM64CMP, 236 ssa.OpARM64CMPW, 237 ssa.OpARM64CMN, 238 ssa.OpARM64CMNW, 239 ssa.OpARM64FCMPS, 240 ssa.OpARM64FCMPD: 241 p := s.Prog(v.Op.Asm()) 242 p.From.Type = obj.TYPE_REG 243 p.From.Reg = v.Args[1].Reg() 244 p.Reg = v.Args[0].Reg() 245 case ssa.OpARM64CMPconst, 246 ssa.OpARM64CMPWconst, 247 ssa.OpARM64CMNconst, 248 ssa.OpARM64CMNWconst: 249 p := s.Prog(v.Op.Asm()) 250 p.From.Type = obj.TYPE_CONST 251 p.From.Offset = v.AuxInt 252 p.Reg = v.Args[0].Reg() 253 case ssa.OpARM64CMPshiftLL: 254 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt) 255 case ssa.OpARM64CMPshiftRL: 256 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt) 257 case ssa.OpARM64CMPshiftRA: 258 genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt) 259 case ssa.OpARM64MOVDaddr: 260 p := s.Prog(arm64.AMOVD) 261 p.From.Type = obj.TYPE_ADDR 262 p.To.Type = obj.TYPE_REG 263 p.To.Reg = v.Reg() 264 265 var wantreg string 266 // MOVD $sym+off(base), R 267 // the assembler expands it as the following: 268 // - base is SP: add constant offset to SP (R13) 269 // when constant is large, tmp register (R11) may be used 270 // - base is SB: load external address from constant pool (use relocation) 271 switch v.Aux.(type) { 272 default: 273 v.Fatalf("aux is of unknown type %T", v.Aux) 274 case *ssa.ExternSymbol: 275 wantreg = "SB" 276 gc.AddAux(&p.From, v) 277 case *ssa.ArgSymbol, *ssa.AutoSymbol: 278 wantreg = "SP" 279 gc.AddAux(&p.From, v) 280 case nil: 281 // No sym, just MOVD $off(SP), R 282 wantreg = "SP" 283 p.From.Reg = arm64.REGSP 284 p.From.Offset = v.AuxInt 285 } 286 if reg := v.Args[0].RegName(); reg != wantreg { 287 v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) 288 } 289 case ssa.OpARM64MOVBload, 290 ssa.OpARM64MOVBUload, 291 ssa.OpARM64MOVHload, 292 ssa.OpARM64MOVHUload, 293 ssa.OpARM64MOVWload, 294 ssa.OpARM64MOVWUload, 295 ssa.OpARM64MOVDload, 296 ssa.OpARM64FMOVSload, 297 ssa.OpARM64FMOVDload: 298 p := s.Prog(v.Op.Asm()) 299 p.From.Type = obj.TYPE_MEM 300 p.From.Reg = v.Args[0].Reg() 301 gc.AddAux(&p.From, v) 302 p.To.Type = obj.TYPE_REG 303 p.To.Reg = v.Reg() 304 case ssa.OpARM64LDAR, 305 ssa.OpARM64LDARW: 306 p := s.Prog(v.Op.Asm()) 307 p.From.Type = obj.TYPE_MEM 308 p.From.Reg = v.Args[0].Reg() 309 gc.AddAux(&p.From, v) 310 p.To.Type = obj.TYPE_REG 311 p.To.Reg = v.Reg0() 312 case ssa.OpARM64MOVBstore, 313 ssa.OpARM64MOVHstore, 314 ssa.OpARM64MOVWstore, 315 ssa.OpARM64MOVDstore, 316 ssa.OpARM64FMOVSstore, 317 ssa.OpARM64FMOVDstore, 318 ssa.OpARM64STLR, 319 ssa.OpARM64STLRW: 320 p := s.Prog(v.Op.Asm()) 321 p.From.Type = obj.TYPE_REG 322 p.From.Reg = v.Args[1].Reg() 323 p.To.Type = obj.TYPE_MEM 324 p.To.Reg = v.Args[0].Reg() 325 gc.AddAux(&p.To, v) 326 case ssa.OpARM64MOVBstorezero, 327 ssa.OpARM64MOVHstorezero, 328 ssa.OpARM64MOVWstorezero, 329 ssa.OpARM64MOVDstorezero: 330 p := s.Prog(v.Op.Asm()) 331 p.From.Type = obj.TYPE_REG 332 p.From.Reg = arm64.REGZERO 333 p.To.Type = obj.TYPE_MEM 334 p.To.Reg = v.Args[0].Reg() 335 gc.AddAux(&p.To, v) 336 case ssa.OpARM64LoweredAtomicExchange64, 337 ssa.OpARM64LoweredAtomicExchange32: 338 // LDAXR (Rarg0), Rout 339 // STLXR Rarg1, (Rarg0), Rtmp 340 // CBNZ Rtmp, -2(PC) 341 ld := arm64.ALDAXR 342 st := arm64.ASTLXR 343 if v.Op == ssa.OpARM64LoweredAtomicExchange32 { 344 ld = arm64.ALDAXRW 345 st = arm64.ASTLXRW 346 } 347 r0 := v.Args[0].Reg() 348 r1 := v.Args[1].Reg() 349 out := v.Reg0() 350 p := s.Prog(ld) 351 p.From.Type = obj.TYPE_MEM 352 p.From.Reg = r0 353 p.To.Type = obj.TYPE_REG 354 p.To.Reg = out 355 p1 := s.Prog(st) 356 p1.From.Type = obj.TYPE_REG 357 p1.From.Reg = r1 358 p1.To.Type = obj.TYPE_MEM 359 p1.To.Reg = r0 360 p1.RegTo2 = arm64.REGTMP 361 p2 := s.Prog(arm64.ACBNZ) 362 p2.From.Type = obj.TYPE_REG 363 p2.From.Reg = arm64.REGTMP 364 p2.To.Type = obj.TYPE_BRANCH 365 gc.Patch(p2, p) 366 case ssa.OpARM64LoweredAtomicAdd64, 367 ssa.OpARM64LoweredAtomicAdd32: 368 // LDAXR (Rarg0), Rout 369 // ADD Rarg1, Rout 370 // STLXR Rout, (Rarg0), Rtmp 371 // CBNZ Rtmp, -3(PC) 372 ld := arm64.ALDAXR 373 st := arm64.ASTLXR 374 if v.Op == ssa.OpARM64LoweredAtomicAdd32 { 375 ld = arm64.ALDAXRW 376 st = arm64.ASTLXRW 377 } 378 r0 := v.Args[0].Reg() 379 r1 := v.Args[1].Reg() 380 out := v.Reg0() 381 p := s.Prog(ld) 382 p.From.Type = obj.TYPE_MEM 383 p.From.Reg = r0 384 p.To.Type = obj.TYPE_REG 385 p.To.Reg = out 386 p1 := s.Prog(arm64.AADD) 387 p1.From.Type = obj.TYPE_REG 388 p1.From.Reg = r1 389 p1.To.Type = obj.TYPE_REG 390 p1.To.Reg = out 391 p2 := s.Prog(st) 392 p2.From.Type = obj.TYPE_REG 393 p2.From.Reg = out 394 p2.To.Type = obj.TYPE_MEM 395 p2.To.Reg = r0 396 p2.RegTo2 = arm64.REGTMP 397 p3 := s.Prog(arm64.ACBNZ) 398 p3.From.Type = obj.TYPE_REG 399 p3.From.Reg = arm64.REGTMP 400 p3.To.Type = obj.TYPE_BRANCH 401 gc.Patch(p3, p) 402 case ssa.OpARM64LoweredAtomicCas64, 403 ssa.OpARM64LoweredAtomicCas32: 404 // LDAXR (Rarg0), Rtmp 405 // CMP Rarg1, Rtmp 406 // BNE 3(PC) 407 // STLXR Rarg2, (Rarg0), Rtmp 408 // CBNZ Rtmp, -4(PC) 409 // CSET EQ, Rout 410 ld := arm64.ALDAXR 411 st := arm64.ASTLXR 412 cmp := arm64.ACMP 413 if v.Op == ssa.OpARM64LoweredAtomicCas32 { 414 ld = arm64.ALDAXRW 415 st = arm64.ASTLXRW 416 cmp = arm64.ACMPW 417 } 418 r0 := v.Args[0].Reg() 419 r1 := v.Args[1].Reg() 420 r2 := v.Args[2].Reg() 421 out := v.Reg0() 422 p := s.Prog(ld) 423 p.From.Type = obj.TYPE_MEM 424 p.From.Reg = r0 425 p.To.Type = obj.TYPE_REG 426 p.To.Reg = arm64.REGTMP 427 p1 := s.Prog(cmp) 428 p1.From.Type = obj.TYPE_REG 429 p1.From.Reg = r1 430 p1.Reg = arm64.REGTMP 431 p2 := s.Prog(arm64.ABNE) 432 p2.To.Type = obj.TYPE_BRANCH 433 p3 := s.Prog(st) 434 p3.From.Type = obj.TYPE_REG 435 p3.From.Reg = r2 436 p3.To.Type = obj.TYPE_MEM 437 p3.To.Reg = r0 438 p3.RegTo2 = arm64.REGTMP 439 p4 := s.Prog(arm64.ACBNZ) 440 p4.From.Type = obj.TYPE_REG 441 p4.From.Reg = arm64.REGTMP 442 p4.To.Type = obj.TYPE_BRANCH 443 gc.Patch(p4, p) 444 p5 := s.Prog(arm64.ACSET) 445 p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg 446 p5.From.Reg = arm64.COND_EQ 447 p5.To.Type = obj.TYPE_REG 448 p5.To.Reg = out 449 gc.Patch(p2, p5) 450 case ssa.OpARM64LoweredAtomicAnd8, 451 ssa.OpARM64LoweredAtomicOr8: 452 // LDAXRB (Rarg0), Rtmp 453 // AND/OR Rarg1, Rtmp 454 // STLXRB Rtmp, (Rarg0), Rtmp 455 // CBNZ Rtmp, -3(PC) 456 r0 := v.Args[0].Reg() 457 r1 := v.Args[1].Reg() 458 p := s.Prog(arm64.ALDAXRB) 459 p.From.Type = obj.TYPE_MEM 460 p.From.Reg = r0 461 p.To.Type = obj.TYPE_REG 462 p.To.Reg = arm64.REGTMP 463 p1 := s.Prog(v.Op.Asm()) 464 p1.From.Type = obj.TYPE_REG 465 p1.From.Reg = r1 466 p1.To.Type = obj.TYPE_REG 467 p1.To.Reg = arm64.REGTMP 468 p2 := s.Prog(arm64.ASTLXRB) 469 p2.From.Type = obj.TYPE_REG 470 p2.From.Reg = arm64.REGTMP 471 p2.To.Type = obj.TYPE_MEM 472 p2.To.Reg = r0 473 p2.RegTo2 = arm64.REGTMP 474 p3 := s.Prog(arm64.ACBNZ) 475 p3.From.Type = obj.TYPE_REG 476 p3.From.Reg = arm64.REGTMP 477 p3.To.Type = obj.TYPE_BRANCH 478 gc.Patch(p3, p) 479 case ssa.OpARM64MOVBreg, 480 ssa.OpARM64MOVBUreg, 481 ssa.OpARM64MOVHreg, 482 ssa.OpARM64MOVHUreg, 483 ssa.OpARM64MOVWreg, 484 ssa.OpARM64MOVWUreg: 485 a := v.Args[0] 486 for a.Op == ssa.OpCopy || a.Op == ssa.OpARM64MOVDreg { 487 a = a.Args[0] 488 } 489 if a.Op == ssa.OpLoadReg { 490 t := a.Type 491 switch { 492 case v.Op == ssa.OpARM64MOVBreg && t.Size() == 1 && t.IsSigned(), 493 v.Op == ssa.OpARM64MOVBUreg && t.Size() == 1 && !t.IsSigned(), 494 v.Op == ssa.OpARM64MOVHreg && t.Size() == 2 && t.IsSigned(), 495 v.Op == ssa.OpARM64MOVHUreg && t.Size() == 2 && !t.IsSigned(), 496 v.Op == ssa.OpARM64MOVWreg && t.Size() == 4 && t.IsSigned(), 497 v.Op == ssa.OpARM64MOVWUreg && t.Size() == 4 && !t.IsSigned(): 498 // arg is a proper-typed load, already zero/sign-extended, don't extend again 499 if v.Reg() == v.Args[0].Reg() { 500 return 501 } 502 p := s.Prog(arm64.AMOVD) 503 p.From.Type = obj.TYPE_REG 504 p.From.Reg = v.Args[0].Reg() 505 p.To.Type = obj.TYPE_REG 506 p.To.Reg = v.Reg() 507 return 508 default: 509 } 510 } 511 fallthrough 512 case ssa.OpARM64MVN, 513 ssa.OpARM64NEG, 514 ssa.OpARM64FNEGS, 515 ssa.OpARM64FNEGD, 516 ssa.OpARM64FSQRTD, 517 ssa.OpARM64FCVTZSSW, 518 ssa.OpARM64FCVTZSDW, 519 ssa.OpARM64FCVTZUSW, 520 ssa.OpARM64FCVTZUDW, 521 ssa.OpARM64FCVTZSS, 522 ssa.OpARM64FCVTZSD, 523 ssa.OpARM64FCVTZUS, 524 ssa.OpARM64FCVTZUD, 525 ssa.OpARM64SCVTFWS, 526 ssa.OpARM64SCVTFWD, 527 ssa.OpARM64SCVTFS, 528 ssa.OpARM64SCVTFD, 529 ssa.OpARM64UCVTFWS, 530 ssa.OpARM64UCVTFWD, 531 ssa.OpARM64UCVTFS, 532 ssa.OpARM64UCVTFD, 533 ssa.OpARM64FCVTSD, 534 ssa.OpARM64FCVTDS, 535 ssa.OpARM64REV, 536 ssa.OpARM64REVW, 537 ssa.OpARM64REV16W, 538 ssa.OpARM64RBIT, 539 ssa.OpARM64RBITW, 540 ssa.OpARM64CLZ, 541 ssa.OpARM64CLZW: 542 p := s.Prog(v.Op.Asm()) 543 p.From.Type = obj.TYPE_REG 544 p.From.Reg = v.Args[0].Reg() 545 p.To.Type = obj.TYPE_REG 546 p.To.Reg = v.Reg() 547 case ssa.OpARM64CSELULT, 548 ssa.OpARM64CSELULT0: 549 r1 := int16(arm64.REGZERO) 550 if v.Op == ssa.OpARM64CSELULT { 551 r1 = v.Args[1].Reg() 552 } 553 p := s.Prog(v.Op.Asm()) 554 p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg 555 p.From.Reg = arm64.COND_LO 556 p.Reg = v.Args[0].Reg() 557 p.From3 = &obj.Addr{Type: obj.TYPE_REG, Reg: r1} 558 p.To.Type = obj.TYPE_REG 559 p.To.Reg = v.Reg() 560 case ssa.OpARM64DUFFZERO: 561 // runtime.duffzero expects start address - 8 in R16 562 p := s.Prog(arm64.ASUB) 563 p.From.Type = obj.TYPE_CONST 564 p.From.Offset = 8 565 p.Reg = v.Args[0].Reg() 566 p.To.Type = obj.TYPE_REG 567 p.To.Reg = arm64.REG_R16 568 p = s.Prog(obj.ADUFFZERO) 569 p.To.Type = obj.TYPE_MEM 570 p.To.Name = obj.NAME_EXTERN 571 p.To.Sym = gc.Duffzero 572 p.To.Offset = v.AuxInt 573 case ssa.OpARM64LoweredZero: 574 // MOVD.P ZR, 8(R16) 575 // CMP Rarg1, R16 576 // BLE -2(PC) 577 // arg1 is the address of the last element to zero 578 p := s.Prog(arm64.AMOVD) 579 p.Scond = arm64.C_XPOST 580 p.From.Type = obj.TYPE_REG 581 p.From.Reg = arm64.REGZERO 582 p.To.Type = obj.TYPE_MEM 583 p.To.Reg = arm64.REG_R16 584 p.To.Offset = 8 585 p2 := s.Prog(arm64.ACMP) 586 p2.From.Type = obj.TYPE_REG 587 p2.From.Reg = v.Args[1].Reg() 588 p2.Reg = arm64.REG_R16 589 p3 := s.Prog(arm64.ABLE) 590 p3.To.Type = obj.TYPE_BRANCH 591 gc.Patch(p3, p) 592 case ssa.OpARM64DUFFCOPY: 593 p := s.Prog(obj.ADUFFCOPY) 594 p.To.Type = obj.TYPE_MEM 595 p.To.Name = obj.NAME_EXTERN 596 p.To.Sym = gc.Duffcopy 597 p.To.Offset = v.AuxInt 598 case ssa.OpARM64LoweredMove: 599 // MOVD.P 8(R16), Rtmp 600 // MOVD.P Rtmp, 8(R17) 601 // CMP Rarg2, R16 602 // BLE -3(PC) 603 // arg2 is the address of the last element of src 604 p := s.Prog(arm64.AMOVD) 605 p.Scond = arm64.C_XPOST 606 p.From.Type = obj.TYPE_MEM 607 p.From.Reg = arm64.REG_R16 608 p.From.Offset = 8 609 p.To.Type = obj.TYPE_REG 610 p.To.Reg = arm64.REGTMP 611 p2 := s.Prog(arm64.AMOVD) 612 p2.Scond = arm64.C_XPOST 613 p2.From.Type = obj.TYPE_REG 614 p2.From.Reg = arm64.REGTMP 615 p2.To.Type = obj.TYPE_MEM 616 p2.To.Reg = arm64.REG_R17 617 p2.To.Offset = 8 618 p3 := s.Prog(arm64.ACMP) 619 p3.From.Type = obj.TYPE_REG 620 p3.From.Reg = v.Args[2].Reg() 621 p3.Reg = arm64.REG_R16 622 p4 := s.Prog(arm64.ABLE) 623 p4.To.Type = obj.TYPE_BRANCH 624 gc.Patch(p4, p) 625 case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter: 626 s.Call(v) 627 case ssa.OpARM64LoweredNilCheck: 628 // Issue a load which will fault if arg is nil. 629 p := s.Prog(arm64.AMOVB) 630 p.From.Type = obj.TYPE_MEM 631 p.From.Reg = v.Args[0].Reg() 632 gc.AddAux(&p.From, v) 633 p.To.Type = obj.TYPE_REG 634 p.To.Reg = arm64.REGTMP 635 if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Line==1 in generated wrappers 636 gc.Warnl(v.Pos, "generated nil check") 637 } 638 case ssa.OpARM64Equal, 639 ssa.OpARM64NotEqual, 640 ssa.OpARM64LessThan, 641 ssa.OpARM64LessEqual, 642 ssa.OpARM64GreaterThan, 643 ssa.OpARM64GreaterEqual, 644 ssa.OpARM64LessThanU, 645 ssa.OpARM64LessEqualU, 646 ssa.OpARM64GreaterThanU, 647 ssa.OpARM64GreaterEqualU: 648 // generate boolean values using CSET 649 p := s.Prog(arm64.ACSET) 650 p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg 651 p.From.Reg = condBits[v.Op] 652 p.To.Type = obj.TYPE_REG 653 p.To.Reg = v.Reg() 654 case ssa.OpARM64LoweredGetClosurePtr: 655 // Closure pointer is R26 (arm64.REGCTXT). 656 gc.CheckLoweredGetClosurePtr(v) 657 case ssa.OpARM64FlagEQ, 658 ssa.OpARM64FlagLT_ULT, 659 ssa.OpARM64FlagLT_UGT, 660 ssa.OpARM64FlagGT_ULT, 661 ssa.OpARM64FlagGT_UGT: 662 v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString()) 663 case ssa.OpARM64InvertFlags: 664 v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) 665 case ssa.OpClobber: 666 // TODO: implement for clobberdead experiment. Nop is ok for now. 667 default: 668 v.Fatalf("genValue not implemented: %s", v.LongString()) 669 } 670 } 671 672 var condBits = map[ssa.Op]int16{ 673 ssa.OpARM64Equal: arm64.COND_EQ, 674 ssa.OpARM64NotEqual: arm64.COND_NE, 675 ssa.OpARM64LessThan: arm64.COND_LT, 676 ssa.OpARM64LessThanU: arm64.COND_LO, 677 ssa.OpARM64LessEqual: arm64.COND_LE, 678 ssa.OpARM64LessEqualU: arm64.COND_LS, 679 ssa.OpARM64GreaterThan: arm64.COND_GT, 680 ssa.OpARM64GreaterThanU: arm64.COND_HI, 681 ssa.OpARM64GreaterEqual: arm64.COND_GE, 682 ssa.OpARM64GreaterEqualU: arm64.COND_HS, 683 } 684 685 var blockJump = map[ssa.BlockKind]struct { 686 asm, invasm obj.As 687 }{ 688 ssa.BlockARM64EQ: {arm64.ABEQ, arm64.ABNE}, 689 ssa.BlockARM64NE: {arm64.ABNE, arm64.ABEQ}, 690 ssa.BlockARM64LT: {arm64.ABLT, arm64.ABGE}, 691 ssa.BlockARM64GE: {arm64.ABGE, arm64.ABLT}, 692 ssa.BlockARM64LE: {arm64.ABLE, arm64.ABGT}, 693 ssa.BlockARM64GT: {arm64.ABGT, arm64.ABLE}, 694 ssa.BlockARM64ULT: {arm64.ABLO, arm64.ABHS}, 695 ssa.BlockARM64UGE: {arm64.ABHS, arm64.ABLO}, 696 ssa.BlockARM64UGT: {arm64.ABHI, arm64.ABLS}, 697 ssa.BlockARM64ULE: {arm64.ABLS, arm64.ABHI}, 698 ssa.BlockARM64Z: {arm64.ACBZ, arm64.ACBNZ}, 699 ssa.BlockARM64NZ: {arm64.ACBNZ, arm64.ACBZ}, 700 ssa.BlockARM64ZW: {arm64.ACBZW, arm64.ACBNZW}, 701 ssa.BlockARM64NZW: {arm64.ACBNZW, arm64.ACBZW}, 702 } 703 704 func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { 705 switch b.Kind { 706 case ssa.BlockPlain: 707 if b.Succs[0].Block() != next { 708 p := s.Prog(obj.AJMP) 709 p.To.Type = obj.TYPE_BRANCH 710 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 711 } 712 713 case ssa.BlockDefer: 714 // defer returns in R0: 715 // 0 if we should continue executing 716 // 1 if we should jump to deferreturn call 717 p := s.Prog(arm64.ACMP) 718 p.From.Type = obj.TYPE_CONST 719 p.From.Offset = 0 720 p.Reg = arm64.REG_R0 721 p = s.Prog(arm64.ABNE) 722 p.To.Type = obj.TYPE_BRANCH 723 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()}) 724 if b.Succs[0].Block() != next { 725 p := s.Prog(obj.AJMP) 726 p.To.Type = obj.TYPE_BRANCH 727 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 728 } 729 730 case ssa.BlockExit: 731 s.Prog(obj.AUNDEF) // tell plive.go that we never reach here 732 733 case ssa.BlockRet: 734 s.Prog(obj.ARET) 735 736 case ssa.BlockRetJmp: 737 p := s.Prog(obj.ARET) 738 p.To.Type = obj.TYPE_MEM 739 p.To.Name = obj.NAME_EXTERN 740 p.To.Sym = b.Aux.(*obj.LSym) 741 742 case ssa.BlockARM64EQ, ssa.BlockARM64NE, 743 ssa.BlockARM64LT, ssa.BlockARM64GE, 744 ssa.BlockARM64LE, ssa.BlockARM64GT, 745 ssa.BlockARM64ULT, ssa.BlockARM64UGT, 746 ssa.BlockARM64ULE, ssa.BlockARM64UGE, 747 ssa.BlockARM64Z, ssa.BlockARM64NZ, 748 ssa.BlockARM64ZW, ssa.BlockARM64NZW: 749 jmp := blockJump[b.Kind] 750 var p *obj.Prog 751 switch next { 752 case b.Succs[0].Block(): 753 p = s.Prog(jmp.invasm) 754 p.To.Type = obj.TYPE_BRANCH 755 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()}) 756 case b.Succs[1].Block(): 757 p = s.Prog(jmp.asm) 758 p.To.Type = obj.TYPE_BRANCH 759 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 760 default: 761 p = s.Prog(jmp.asm) 762 p.To.Type = obj.TYPE_BRANCH 763 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 764 q := s.Prog(obj.AJMP) 765 q.To.Type = obj.TYPE_BRANCH 766 s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()}) 767 } 768 if !b.Control.Type.IsFlags() { 769 p.From.Type = obj.TYPE_REG 770 p.From.Reg = b.Control.Reg() 771 } 772 773 default: 774 b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString()) 775 } 776 }