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