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 5package riscv64 6 7import ( 8 "cmd/compile/internal/base" 9 "cmd/compile/internal/ir" 10 "cmd/compile/internal/objw" 11 "cmd/compile/internal/ssa" 12 "cmd/compile/internal/ssagen" 13 "cmd/compile/internal/types" 14 "cmd/internal/obj" 15 "cmd/internal/obj/riscv" 16) 17 18// ssaRegToReg maps ssa register numbers to obj register numbers. 19var ssaRegToReg = []int16{ 20 riscv.REG_X0, 21 // X1 (LR): unused 22 riscv.REG_X2, 23 riscv.REG_X3, 24 riscv.REG_X4, 25 riscv.REG_X5, 26 riscv.REG_X6, 27 riscv.REG_X7, 28 riscv.REG_X8, 29 riscv.REG_X9, 30 riscv.REG_X10, 31 riscv.REG_X11, 32 riscv.REG_X12, 33 riscv.REG_X13, 34 riscv.REG_X14, 35 riscv.REG_X15, 36 riscv.REG_X16, 37 riscv.REG_X17, 38 riscv.REG_X18, 39 riscv.REG_X19, 40 riscv.REG_X20, 41 riscv.REG_X21, 42 riscv.REG_X22, 43 riscv.REG_X23, 44 riscv.REG_X24, 45 riscv.REG_X25, 46 riscv.REG_X26, 47 riscv.REG_X27, 48 riscv.REG_X28, 49 riscv.REG_X29, 50 riscv.REG_X30, 51 riscv.REG_X31, 52 riscv.REG_F0, 53 riscv.REG_F1, 54 riscv.REG_F2, 55 riscv.REG_F3, 56 riscv.REG_F4, 57 riscv.REG_F5, 58 riscv.REG_F6, 59 riscv.REG_F7, 60 riscv.REG_F8, 61 riscv.REG_F9, 62 riscv.REG_F10, 63 riscv.REG_F11, 64 riscv.REG_F12, 65 riscv.REG_F13, 66 riscv.REG_F14, 67 riscv.REG_F15, 68 riscv.REG_F16, 69 riscv.REG_F17, 70 riscv.REG_F18, 71 riscv.REG_F19, 72 riscv.REG_F20, 73 riscv.REG_F21, 74 riscv.REG_F22, 75 riscv.REG_F23, 76 riscv.REG_F24, 77 riscv.REG_F25, 78 riscv.REG_F26, 79 riscv.REG_F27, 80 riscv.REG_F28, 81 riscv.REG_F29, 82 riscv.REG_F30, 83 riscv.REG_F31, 84 0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case. 85} 86 87func loadByType(t *types.Type) obj.As { 88 width := t.Size() 89 90 if t.IsFloat() { 91 switch width { 92 case 4: 93 return riscv.AMOVF 94 case 8: 95 return riscv.AMOVD 96 default: 97 base.Fatalf("unknown float width for load %d in type %v", width, t) 98 return 0 99 } 100 } 101 102 switch width { 103 case 1: 104 if t.IsSigned() { 105 return riscv.AMOVB 106 } else { 107 return riscv.AMOVBU 108 } 109 case 2: 110 if t.IsSigned() { 111 return riscv.AMOVH 112 } else { 113 return riscv.AMOVHU 114 } 115 case 4: 116 if t.IsSigned() { 117 return riscv.AMOVW 118 } else { 119 return riscv.AMOVWU 120 } 121 case 8: 122 return riscv.AMOV 123 default: 124 base.Fatalf("unknown width for load %d in type %v", width, t) 125 return 0 126 } 127} 128 129// storeByType returns the store instruction of the given type. 130func storeByType(t *types.Type) obj.As { 131 width := t.Size() 132 133 if t.IsFloat() { 134 switch width { 135 case 4: 136 return riscv.AMOVF 137 case 8: 138 return riscv.AMOVD 139 default: 140 base.Fatalf("unknown float width for store %d in type %v", width, t) 141 return 0 142 } 143 } 144 145 switch width { 146 case 1: 147 return riscv.AMOVB 148 case 2: 149 return riscv.AMOVH 150 case 4: 151 return riscv.AMOVW 152 case 8: 153 return riscv.AMOV 154 default: 155 base.Fatalf("unknown width for store %d in type %v", width, t) 156 return 0 157 } 158} 159 160// largestMove returns the largest move instruction possible and its size, 161// given the alignment of the total size of the move. 162// 163// e.g., a 16-byte move may use MOV, but an 11-byte move must use MOVB. 164// 165// Note that the moves may not be on naturally aligned addresses depending on 166// the source and destination. 167// 168// This matches the calculation in ssa.moveSize. 169func largestMove(alignment int64) (obj.As, int64) { 170 switch { 171 case alignment%8 == 0: 172 return riscv.AMOV, 8 173 case alignment%4 == 0: 174 return riscv.AMOVW, 4 175 case alignment%2 == 0: 176 return riscv.AMOVH, 2 177 default: 178 return riscv.AMOVB, 1 179 } 180} 181 182// ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. 183// RISC-V has no flags, so this is a no-op. 184func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {} 185 186func ssaGenValue(s *ssagen.State, v *ssa.Value) { 187 s.SetPos(v.Pos) 188 189 switch v.Op { 190 case ssa.OpInitMem: 191 // memory arg needs no code 192 case ssa.OpArg: 193 // input args need no code 194 case ssa.OpPhi: 195 ssagen.CheckLoweredPhi(v) 196 case ssa.OpCopy, ssa.OpRISCV64MOVDreg: 197 if v.Type.IsMemory() { 198 return 199 } 200 rs := v.Args[0].Reg() 201 rd := v.Reg() 202 if rs == rd { 203 return 204 } 205 as := riscv.AMOV 206 if v.Type.IsFloat() { 207 as = riscv.AMOVD 208 } 209 p := s.Prog(as) 210 p.From.Type = obj.TYPE_REG 211 p.From.Reg = rs 212 p.To.Type = obj.TYPE_REG 213 p.To.Reg = rd 214 case ssa.OpRISCV64MOVDnop: 215 // nothing to do 216 case ssa.OpLoadReg: 217 if v.Type.IsFlags() { 218 v.Fatalf("load flags not implemented: %v", v.LongString()) 219 return 220 } 221 p := s.Prog(loadByType(v.Type)) 222 ssagen.AddrAuto(&p.From, v.Args[0]) 223 p.To.Type = obj.TYPE_REG 224 p.To.Reg = v.Reg() 225 case ssa.OpStoreReg: 226 if v.Type.IsFlags() { 227 v.Fatalf("store flags not implemented: %v", v.LongString()) 228 return 229 } 230 p := s.Prog(storeByType(v.Type)) 231 p.From.Type = obj.TYPE_REG 232 p.From.Reg = v.Args[0].Reg() 233 ssagen.AddrAuto(&p.To, v) 234 case ssa.OpArgIntReg, ssa.OpArgFloatReg: 235 // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill 236 // The loop only runs once. 237 for _, a := range v.Block.Func.RegArgs { 238 // Pass the spill/unspill information along to the assembler, offset by size of 239 // the saved LR slot. 240 addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.Arch.FixedFrameSize) 241 s.FuncInfo().AddSpill( 242 obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)}) 243 } 244 v.Block.Func.RegArgs = nil 245 246 ssagen.CheckArgReg(v) 247 case ssa.OpSP, ssa.OpSB, ssa.OpGetG: 248 // nothing to do 249 case ssa.OpRISCV64MOVBreg, ssa.OpRISCV64MOVHreg, ssa.OpRISCV64MOVWreg, 250 ssa.OpRISCV64MOVBUreg, ssa.OpRISCV64MOVHUreg, ssa.OpRISCV64MOVWUreg: 251 a := v.Args[0] 252 for a.Op == ssa.OpCopy || a.Op == ssa.OpRISCV64MOVDreg { 253 a = a.Args[0] 254 } 255 as := v.Op.Asm() 256 rs := v.Args[0].Reg() 257 rd := v.Reg() 258 if a.Op == ssa.OpLoadReg { 259 t := a.Type 260 switch { 261 case v.Op == ssa.OpRISCV64MOVBreg && t.Size() == 1 && t.IsSigned(), 262 v.Op == ssa.OpRISCV64MOVHreg && t.Size() == 2 && t.IsSigned(), 263 v.Op == ssa.OpRISCV64MOVWreg && t.Size() == 4 && t.IsSigned(), 264 v.Op == ssa.OpRISCV64MOVBUreg && t.Size() == 1 && !t.IsSigned(), 265 v.Op == ssa.OpRISCV64MOVHUreg && t.Size() == 2 && !t.IsSigned(), 266 v.Op == ssa.OpRISCV64MOVWUreg && t.Size() == 4 && !t.IsSigned(): 267 // arg is a proper-typed load and already sign/zero-extended 268 if rs == rd { 269 return 270 } 271 as = riscv.AMOV 272 default: 273 } 274 } 275 p := s.Prog(as) 276 p.From.Type = obj.TYPE_REG 277 p.From.Reg = rs 278 p.To.Type = obj.TYPE_REG 279 p.To.Reg = rd 280 case ssa.OpRISCV64ADD, ssa.OpRISCV64SUB, ssa.OpRISCV64SUBW, ssa.OpRISCV64XOR, ssa.OpRISCV64OR, ssa.OpRISCV64AND, 281 ssa.OpRISCV64SLL, ssa.OpRISCV64SLLW, ssa.OpRISCV64SRA, ssa.OpRISCV64SRAW, ssa.OpRISCV64SRL, ssa.OpRISCV64SRLW, 282 ssa.OpRISCV64SLT, ssa.OpRISCV64SLTU, ssa.OpRISCV64MUL, ssa.OpRISCV64MULW, ssa.OpRISCV64MULH, 283 ssa.OpRISCV64MULHU, ssa.OpRISCV64DIV, ssa.OpRISCV64DIVU, ssa.OpRISCV64DIVW, 284 ssa.OpRISCV64DIVUW, ssa.OpRISCV64REM, ssa.OpRISCV64REMU, ssa.OpRISCV64REMW, 285 ssa.OpRISCV64REMUW, 286 ssa.OpRISCV64ROL, ssa.OpRISCV64ROLW, ssa.OpRISCV64ROR, ssa.OpRISCV64RORW, 287 ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS, 288 ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES, 289 ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD, 290 ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED, 291 ssa.OpRISCV64FSGNJD: 292 r := v.Reg() 293 r1 := v.Args[0].Reg() 294 r2 := v.Args[1].Reg() 295 p := s.Prog(v.Op.Asm()) 296 p.From.Type = obj.TYPE_REG 297 p.From.Reg = r2 298 p.Reg = r1 299 p.To.Type = obj.TYPE_REG 300 p.To.Reg = r 301 302 case ssa.OpRISCV64LoweredFMAXD, ssa.OpRISCV64LoweredFMIND, ssa.OpRISCV64LoweredFMAXS, ssa.OpRISCV64LoweredFMINS: 303 // Most of FMIN/FMAX result match Go's required behaviour, unless one of the 304 // inputs is a NaN. As such, we need to explicitly test for NaN 305 // before using FMIN/FMAX. 306 307 // FADD Rarg0, Rarg1, Rout // FADD is used to propagate a NaN to the result in these cases. 308 // FEQ Rarg0, Rarg0, Rtmp 309 // BEQZ Rtmp, end 310 // FEQ Rarg1, Rarg1, Rtmp 311 // BEQZ Rtmp, end 312 // F(MIN | MAX) 313 314 r0 := v.Args[0].Reg() 315 r1 := v.Args[1].Reg() 316 out := v.Reg() 317 add, feq := riscv.AFADDD, riscv.AFEQD 318 if v.Op == ssa.OpRISCV64LoweredFMAXS || v.Op == ssa.OpRISCV64LoweredFMINS { 319 add = riscv.AFADDS 320 feq = riscv.AFEQS 321 } 322 323 p1 := s.Prog(add) 324 p1.From.Type = obj.TYPE_REG 325 p1.From.Reg = r0 326 p1.Reg = r1 327 p1.To.Type = obj.TYPE_REG 328 p1.To.Reg = out 329 330 p2 := s.Prog(feq) 331 p2.From.Type = obj.TYPE_REG 332 p2.From.Reg = r0 333 p2.Reg = r0 334 p2.To.Type = obj.TYPE_REG 335 p2.To.Reg = riscv.REG_TMP 336 337 p3 := s.Prog(riscv.ABEQ) 338 p3.From.Type = obj.TYPE_REG 339 p3.From.Reg = riscv.REG_ZERO 340 p3.Reg = riscv.REG_TMP 341 p3.To.Type = obj.TYPE_BRANCH 342 343 p4 := s.Prog(feq) 344 p4.From.Type = obj.TYPE_REG 345 p4.From.Reg = r1 346 p4.Reg = r1 347 p4.To.Type = obj.TYPE_REG 348 p4.To.Reg = riscv.REG_TMP 349 350 p5 := s.Prog(riscv.ABEQ) 351 p5.From.Type = obj.TYPE_REG 352 p5.From.Reg = riscv.REG_ZERO 353 p5.Reg = riscv.REG_TMP 354 p5.To.Type = obj.TYPE_BRANCH 355 356 p6 := s.Prog(v.Op.Asm()) 357 p6.From.Type = obj.TYPE_REG 358 p6.From.Reg = r1 359 p6.Reg = r0 360 p6.To.Type = obj.TYPE_REG 361 p6.To.Reg = out 362 363 nop := s.Prog(obj.ANOP) 364 p3.To.SetTarget(nop) 365 p5.To.SetTarget(nop) 366 367 case ssa.OpRISCV64LoweredMuluhilo: 368 r0 := v.Args[0].Reg() 369 r1 := v.Args[1].Reg() 370 p := s.Prog(riscv.AMULHU) 371 p.From.Type = obj.TYPE_REG 372 p.From.Reg = r1 373 p.Reg = r0 374 p.To.Type = obj.TYPE_REG 375 p.To.Reg = v.Reg0() 376 p1 := s.Prog(riscv.AMUL) 377 p1.From.Type = obj.TYPE_REG 378 p1.From.Reg = r1 379 p1.Reg = r0 380 p1.To.Type = obj.TYPE_REG 381 p1.To.Reg = v.Reg1() 382 case ssa.OpRISCV64LoweredMuluover: 383 r0 := v.Args[0].Reg() 384 r1 := v.Args[1].Reg() 385 p := s.Prog(riscv.AMULHU) 386 p.From.Type = obj.TYPE_REG 387 p.From.Reg = r1 388 p.Reg = r0 389 p.To.Type = obj.TYPE_REG 390 p.To.Reg = v.Reg1() 391 p1 := s.Prog(riscv.AMUL) 392 p1.From.Type = obj.TYPE_REG 393 p1.From.Reg = r1 394 p1.Reg = r0 395 p1.To.Type = obj.TYPE_REG 396 p1.To.Reg = v.Reg0() 397 p2 := s.Prog(riscv.ASNEZ) 398 p2.From.Type = obj.TYPE_REG 399 p2.From.Reg = v.Reg1() 400 p2.To.Type = obj.TYPE_REG 401 p2.To.Reg = v.Reg1() 402 case ssa.OpRISCV64FMADDD, ssa.OpRISCV64FMSUBD, ssa.OpRISCV64FNMADDD, ssa.OpRISCV64FNMSUBD, 403 ssa.OpRISCV64FMADDS, ssa.OpRISCV64FMSUBS, ssa.OpRISCV64FNMADDS, ssa.OpRISCV64FNMSUBS: 404 r := v.Reg() 405 r1 := v.Args[0].Reg() 406 r2 := v.Args[1].Reg() 407 r3 := v.Args[2].Reg() 408 p := s.Prog(v.Op.Asm()) 409 p.From.Type = obj.TYPE_REG 410 p.From.Reg = r2 411 p.Reg = r1 412 p.AddRestSource(obj.Addr{Type: obj.TYPE_REG, Reg: r3}) 413 p.To.Type = obj.TYPE_REG 414 p.To.Reg = r 415 case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD, 416 ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX, 417 ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS, 418 ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD, 419 ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW: 420 p := s.Prog(v.Op.Asm()) 421 p.From.Type = obj.TYPE_REG 422 p.From.Reg = v.Args[0].Reg() 423 p.To.Type = obj.TYPE_REG 424 p.To.Reg = v.Reg() 425 case ssa.OpRISCV64ADDI, ssa.OpRISCV64ADDIW, ssa.OpRISCV64XORI, ssa.OpRISCV64ORI, ssa.OpRISCV64ANDI, 426 ssa.OpRISCV64SLLI, ssa.OpRISCV64SLLIW, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRAIW, 427 ssa.OpRISCV64SRLI, ssa.OpRISCV64SRLIW, ssa.OpRISCV64SLTI, ssa.OpRISCV64SLTIU, 428 ssa.OpRISCV64RORI, ssa.OpRISCV64RORIW: 429 p := s.Prog(v.Op.Asm()) 430 p.From.Type = obj.TYPE_CONST 431 p.From.Offset = v.AuxInt 432 p.Reg = v.Args[0].Reg() 433 p.To.Type = obj.TYPE_REG 434 p.To.Reg = v.Reg() 435 case ssa.OpRISCV64MOVDconst: 436 p := s.Prog(v.Op.Asm()) 437 p.From.Type = obj.TYPE_CONST 438 p.From.Offset = v.AuxInt 439 p.To.Type = obj.TYPE_REG 440 p.To.Reg = v.Reg() 441 case ssa.OpRISCV64MOVaddr: 442 p := s.Prog(v.Op.Asm()) 443 p.From.Type = obj.TYPE_ADDR 444 p.To.Type = obj.TYPE_REG 445 p.To.Reg = v.Reg() 446 447 var wantreg string 448 // MOVW $sym+off(base), R 449 switch v.Aux.(type) { 450 default: 451 v.Fatalf("aux is of unknown type %T", v.Aux) 452 case *obj.LSym: 453 wantreg = "SB" 454 ssagen.AddAux(&p.From, v) 455 case *ir.Name: 456 wantreg = "SP" 457 ssagen.AddAux(&p.From, v) 458 case nil: 459 // No sym, just MOVW $off(SP), R 460 wantreg = "SP" 461 p.From.Reg = riscv.REG_SP 462 p.From.Offset = v.AuxInt 463 } 464 if reg := v.Args[0].RegName(); reg != wantreg { 465 v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) 466 } 467 case ssa.OpRISCV64MOVBload, ssa.OpRISCV64MOVHload, ssa.OpRISCV64MOVWload, ssa.OpRISCV64MOVDload, 468 ssa.OpRISCV64MOVBUload, ssa.OpRISCV64MOVHUload, ssa.OpRISCV64MOVWUload, 469 ssa.OpRISCV64FMOVWload, ssa.OpRISCV64FMOVDload: 470 p := s.Prog(v.Op.Asm()) 471 p.From.Type = obj.TYPE_MEM 472 p.From.Reg = v.Args[0].Reg() 473 ssagen.AddAux(&p.From, v) 474 p.To.Type = obj.TYPE_REG 475 p.To.Reg = v.Reg() 476 case ssa.OpRISCV64MOVBstore, ssa.OpRISCV64MOVHstore, ssa.OpRISCV64MOVWstore, ssa.OpRISCV64MOVDstore, 477 ssa.OpRISCV64FMOVWstore, ssa.OpRISCV64FMOVDstore: 478 p := s.Prog(v.Op.Asm()) 479 p.From.Type = obj.TYPE_REG 480 p.From.Reg = v.Args[1].Reg() 481 p.To.Type = obj.TYPE_MEM 482 p.To.Reg = v.Args[0].Reg() 483 ssagen.AddAux(&p.To, v) 484 case ssa.OpRISCV64MOVBstorezero, ssa.OpRISCV64MOVHstorezero, ssa.OpRISCV64MOVWstorezero, ssa.OpRISCV64MOVDstorezero: 485 p := s.Prog(v.Op.Asm()) 486 p.From.Type = obj.TYPE_REG 487 p.From.Reg = riscv.REG_ZERO 488 p.To.Type = obj.TYPE_MEM 489 p.To.Reg = v.Args[0].Reg() 490 ssagen.AddAux(&p.To, v) 491 case ssa.OpRISCV64SEQZ, ssa.OpRISCV64SNEZ: 492 p := s.Prog(v.Op.Asm()) 493 p.From.Type = obj.TYPE_REG 494 p.From.Reg = v.Args[0].Reg() 495 p.To.Type = obj.TYPE_REG 496 p.To.Reg = v.Reg() 497 case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter: 498 s.Call(v) 499 case ssa.OpRISCV64CALLtail: 500 s.TailCall(v) 501 case ssa.OpRISCV64LoweredWB: 502 p := s.Prog(obj.ACALL) 503 p.To.Type = obj.TYPE_MEM 504 p.To.Name = obj.NAME_EXTERN 505 // AuxInt encodes how many buffer entries we need. 506 p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1] 507 case ssa.OpRISCV64LoweredPanicBoundsA, ssa.OpRISCV64LoweredPanicBoundsB, ssa.OpRISCV64LoweredPanicBoundsC: 508 p := s.Prog(obj.ACALL) 509 p.To.Type = obj.TYPE_MEM 510 p.To.Name = obj.NAME_EXTERN 511 p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] 512 s.UseArgs(16) // space used in callee args area by assembly stubs 513 514 case ssa.OpRISCV64LoweredAtomicLoad8: 515 s.Prog(riscv.AFENCE) 516 p := s.Prog(riscv.AMOVBU) 517 p.From.Type = obj.TYPE_MEM 518 p.From.Reg = v.Args[0].Reg() 519 p.To.Type = obj.TYPE_REG 520 p.To.Reg = v.Reg0() 521 s.Prog(riscv.AFENCE) 522 523 case ssa.OpRISCV64LoweredAtomicLoad32, ssa.OpRISCV64LoweredAtomicLoad64: 524 as := riscv.ALRW 525 if v.Op == ssa.OpRISCV64LoweredAtomicLoad64 { 526 as = riscv.ALRD 527 } 528 p := s.Prog(as) 529 p.From.Type = obj.TYPE_MEM 530 p.From.Reg = v.Args[0].Reg() 531 p.To.Type = obj.TYPE_REG 532 p.To.Reg = v.Reg0() 533 534 case ssa.OpRISCV64LoweredAtomicStore8: 535 s.Prog(riscv.AFENCE) 536 p := s.Prog(riscv.AMOVB) 537 p.From.Type = obj.TYPE_REG 538 p.From.Reg = v.Args[1].Reg() 539 p.To.Type = obj.TYPE_MEM 540 p.To.Reg = v.Args[0].Reg() 541 s.Prog(riscv.AFENCE) 542 543 case ssa.OpRISCV64LoweredAtomicStore32, ssa.OpRISCV64LoweredAtomicStore64: 544 as := riscv.AAMOSWAPW 545 if v.Op == ssa.OpRISCV64LoweredAtomicStore64 { 546 as = riscv.AAMOSWAPD 547 } 548 p := s.Prog(as) 549 p.From.Type = obj.TYPE_REG 550 p.From.Reg = v.Args[1].Reg() 551 p.To.Type = obj.TYPE_MEM 552 p.To.Reg = v.Args[0].Reg() 553 p.RegTo2 = riscv.REG_ZERO 554 555 case ssa.OpRISCV64LoweredAtomicAdd32, ssa.OpRISCV64LoweredAtomicAdd64: 556 as := riscv.AAMOADDW 557 if v.Op == ssa.OpRISCV64LoweredAtomicAdd64 { 558 as = riscv.AAMOADDD 559 } 560 p := s.Prog(as) 561 p.From.Type = obj.TYPE_REG 562 p.From.Reg = v.Args[1].Reg() 563 p.To.Type = obj.TYPE_MEM 564 p.To.Reg = v.Args[0].Reg() 565 p.RegTo2 = riscv.REG_TMP 566 567 p2 := s.Prog(riscv.AADD) 568 p2.From.Type = obj.TYPE_REG 569 p2.From.Reg = riscv.REG_TMP 570 p2.Reg = v.Args[1].Reg() 571 p2.To.Type = obj.TYPE_REG 572 p2.To.Reg = v.Reg0() 573 574 case ssa.OpRISCV64LoweredAtomicExchange32, ssa.OpRISCV64LoweredAtomicExchange64: 575 as := riscv.AAMOSWAPW 576 if v.Op == ssa.OpRISCV64LoweredAtomicExchange64 { 577 as = riscv.AAMOSWAPD 578 } 579 p := s.Prog(as) 580 p.From.Type = obj.TYPE_REG 581 p.From.Reg = v.Args[1].Reg() 582 p.To.Type = obj.TYPE_MEM 583 p.To.Reg = v.Args[0].Reg() 584 p.RegTo2 = v.Reg0() 585 586 case ssa.OpRISCV64LoweredAtomicCas32, ssa.OpRISCV64LoweredAtomicCas64: 587 // MOV ZERO, Rout 588 // LR (Rarg0), Rtmp 589 // BNE Rtmp, Rarg1, 3(PC) 590 // SC Rarg2, (Rarg0), Rtmp 591 // BNE Rtmp, ZERO, -3(PC) 592 // MOV $1, Rout 593 594 lr := riscv.ALRW 595 sc := riscv.ASCW 596 if v.Op == ssa.OpRISCV64LoweredAtomicCas64 { 597 lr = riscv.ALRD 598 sc = riscv.ASCD 599 } 600 601 r0 := v.Args[0].Reg() 602 r1 := v.Args[1].Reg() 603 r2 := v.Args[2].Reg() 604 out := v.Reg0() 605 606 p := s.Prog(riscv.AMOV) 607 p.From.Type = obj.TYPE_REG 608 p.From.Reg = riscv.REG_ZERO 609 p.To.Type = obj.TYPE_REG 610 p.To.Reg = out 611 612 p1 := s.Prog(lr) 613 p1.From.Type = obj.TYPE_MEM 614 p1.From.Reg = r0 615 p1.To.Type = obj.TYPE_REG 616 p1.To.Reg = riscv.REG_TMP 617 618 p2 := s.Prog(riscv.ABNE) 619 p2.From.Type = obj.TYPE_REG 620 p2.From.Reg = r1 621 p2.Reg = riscv.REG_TMP 622 p2.To.Type = obj.TYPE_BRANCH 623 624 p3 := s.Prog(sc) 625 p3.From.Type = obj.TYPE_REG 626 p3.From.Reg = r2 627 p3.To.Type = obj.TYPE_MEM 628 p3.To.Reg = r0 629 p3.RegTo2 = riscv.REG_TMP 630 631 p4 := s.Prog(riscv.ABNE) 632 p4.From.Type = obj.TYPE_REG 633 p4.From.Reg = riscv.REG_TMP 634 p4.Reg = riscv.REG_ZERO 635 p4.To.Type = obj.TYPE_BRANCH 636 p4.To.SetTarget(p1) 637 638 p5 := s.Prog(riscv.AMOV) 639 p5.From.Type = obj.TYPE_CONST 640 p5.From.Offset = 1 641 p5.To.Type = obj.TYPE_REG 642 p5.To.Reg = out 643 644 p6 := s.Prog(obj.ANOP) 645 p2.To.SetTarget(p6) 646 647 case ssa.OpRISCV64LoweredAtomicAnd32, ssa.OpRISCV64LoweredAtomicOr32: 648 p := s.Prog(v.Op.Asm()) 649 p.From.Type = obj.TYPE_REG 650 p.From.Reg = v.Args[1].Reg() 651 p.To.Type = obj.TYPE_MEM 652 p.To.Reg = v.Args[0].Reg() 653 p.RegTo2 = riscv.REG_ZERO 654 655 case ssa.OpRISCV64LoweredZero: 656 mov, sz := largestMove(v.AuxInt) 657 658 // mov ZERO, (Rarg0) 659 // ADD $sz, Rarg0 660 // BGEU Rarg1, Rarg0, -2(PC) 661 662 p := s.Prog(mov) 663 p.From.Type = obj.TYPE_REG 664 p.From.Reg = riscv.REG_ZERO 665 p.To.Type = obj.TYPE_MEM 666 p.To.Reg = v.Args[0].Reg() 667 668 p2 := s.Prog(riscv.AADD) 669 p2.From.Type = obj.TYPE_CONST 670 p2.From.Offset = sz 671 p2.To.Type = obj.TYPE_REG 672 p2.To.Reg = v.Args[0].Reg() 673 674 p3 := s.Prog(riscv.ABGEU) 675 p3.To.Type = obj.TYPE_BRANCH 676 p3.Reg = v.Args[0].Reg() 677 p3.From.Type = obj.TYPE_REG 678 p3.From.Reg = v.Args[1].Reg() 679 p3.To.SetTarget(p) 680 681 case ssa.OpRISCV64LoweredMove: 682 mov, sz := largestMove(v.AuxInt) 683 684 // mov (Rarg1), T2 685 // mov T2, (Rarg0) 686 // ADD $sz, Rarg0 687 // ADD $sz, Rarg1 688 // BGEU Rarg2, Rarg0, -4(PC) 689 690 p := s.Prog(mov) 691 p.From.Type = obj.TYPE_MEM 692 p.From.Reg = v.Args[1].Reg() 693 p.To.Type = obj.TYPE_REG 694 p.To.Reg = riscv.REG_T2 695 696 p2 := s.Prog(mov) 697 p2.From.Type = obj.TYPE_REG 698 p2.From.Reg = riscv.REG_T2 699 p2.To.Type = obj.TYPE_MEM 700 p2.To.Reg = v.Args[0].Reg() 701 702 p3 := s.Prog(riscv.AADD) 703 p3.From.Type = obj.TYPE_CONST 704 p3.From.Offset = sz 705 p3.To.Type = obj.TYPE_REG 706 p3.To.Reg = v.Args[0].Reg() 707 708 p4 := s.Prog(riscv.AADD) 709 p4.From.Type = obj.TYPE_CONST 710 p4.From.Offset = sz 711 p4.To.Type = obj.TYPE_REG 712 p4.To.Reg = v.Args[1].Reg() 713 714 p5 := s.Prog(riscv.ABGEU) 715 p5.To.Type = obj.TYPE_BRANCH 716 p5.Reg = v.Args[1].Reg() 717 p5.From.Type = obj.TYPE_REG 718 p5.From.Reg = v.Args[2].Reg() 719 p5.To.SetTarget(p) 720 721 case ssa.OpRISCV64LoweredNilCheck: 722 // Issue a load which will fault if arg is nil. 723 // TODO: optimizations. See arm and amd64 LoweredNilCheck. 724 p := s.Prog(riscv.AMOVB) 725 p.From.Type = obj.TYPE_MEM 726 p.From.Reg = v.Args[0].Reg() 727 ssagen.AddAux(&p.From, v) 728 p.To.Type = obj.TYPE_REG 729 p.To.Reg = riscv.REG_ZERO 730 if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers 731 base.WarnfAt(v.Pos, "generated nil check") 732 } 733 734 case ssa.OpRISCV64LoweredGetClosurePtr: 735 // Closure pointer is S10 (riscv.REG_CTXT). 736 ssagen.CheckLoweredGetClosurePtr(v) 737 738 case ssa.OpRISCV64LoweredGetCallerSP: 739 // caller's SP is FixedFrameSize below the address of the first arg 740 p := s.Prog(riscv.AMOV) 741 p.From.Type = obj.TYPE_ADDR 742 p.From.Offset = -base.Ctxt.Arch.FixedFrameSize 743 p.From.Name = obj.NAME_PARAM 744 p.To.Type = obj.TYPE_REG 745 p.To.Reg = v.Reg() 746 747 case ssa.OpRISCV64LoweredGetCallerPC: 748 p := s.Prog(obj.AGETCALLERPC) 749 p.To.Type = obj.TYPE_REG 750 p.To.Reg = v.Reg() 751 752 case ssa.OpRISCV64DUFFZERO: 753 p := s.Prog(obj.ADUFFZERO) 754 p.To.Type = obj.TYPE_MEM 755 p.To.Name = obj.NAME_EXTERN 756 p.To.Sym = ir.Syms.Duffzero 757 p.To.Offset = v.AuxInt 758 759 case ssa.OpRISCV64DUFFCOPY: 760 p := s.Prog(obj.ADUFFCOPY) 761 p.To.Type = obj.TYPE_MEM 762 p.To.Name = obj.NAME_EXTERN 763 p.To.Sym = ir.Syms.Duffcopy 764 p.To.Offset = v.AuxInt 765 766 case ssa.OpRISCV64LoweredPubBarrier: 767 // FENCE 768 s.Prog(v.Op.Asm()) 769 770 case ssa.OpRISCV64LoweredRound32F, ssa.OpRISCV64LoweredRound64F: 771 // input is already rounded 772 773 case ssa.OpClobber, ssa.OpClobberReg: 774 // TODO: implement for clobberdead experiment. Nop is ok for now. 775 776 default: 777 v.Fatalf("Unhandled op %v", v.Op) 778 } 779} 780 781var blockBranch = [...]obj.As{ 782 ssa.BlockRISCV64BEQ: riscv.ABEQ, 783 ssa.BlockRISCV64BEQZ: riscv.ABEQZ, 784 ssa.BlockRISCV64BGE: riscv.ABGE, 785 ssa.BlockRISCV64BGEU: riscv.ABGEU, 786 ssa.BlockRISCV64BGEZ: riscv.ABGEZ, 787 ssa.BlockRISCV64BGTZ: riscv.ABGTZ, 788 ssa.BlockRISCV64BLEZ: riscv.ABLEZ, 789 ssa.BlockRISCV64BLT: riscv.ABLT, 790 ssa.BlockRISCV64BLTU: riscv.ABLTU, 791 ssa.BlockRISCV64BLTZ: riscv.ABLTZ, 792 ssa.BlockRISCV64BNE: riscv.ABNE, 793 ssa.BlockRISCV64BNEZ: riscv.ABNEZ, 794} 795 796func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { 797 s.SetPos(b.Pos) 798 799 switch b.Kind { 800 case ssa.BlockDefer: 801 // defer returns in A0: 802 // 0 if we should continue executing 803 // 1 if we should jump to deferreturn call 804 p := s.Prog(riscv.ABNE) 805 p.To.Type = obj.TYPE_BRANCH 806 p.From.Type = obj.TYPE_REG 807 p.From.Reg = riscv.REG_ZERO 808 p.Reg = riscv.REG_A0 809 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()}) 810 if b.Succs[0].Block() != next { 811 p := s.Prog(obj.AJMP) 812 p.To.Type = obj.TYPE_BRANCH 813 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 814 } 815 case ssa.BlockPlain: 816 if b.Succs[0].Block() != next { 817 p := s.Prog(obj.AJMP) 818 p.To.Type = obj.TYPE_BRANCH 819 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 820 } 821 case ssa.BlockExit, ssa.BlockRetJmp: 822 case ssa.BlockRet: 823 s.Prog(obj.ARET) 824 case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ, 825 ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ, 826 ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU: 827 828 as := blockBranch[b.Kind] 829 invAs := riscv.InvertBranch(as) 830 831 var p *obj.Prog 832 switch next { 833 case b.Succs[0].Block(): 834 p = s.Br(invAs, b.Succs[1].Block()) 835 case b.Succs[1].Block(): 836 p = s.Br(as, b.Succs[0].Block()) 837 default: 838 if b.Likely != ssa.BranchUnlikely { 839 p = s.Br(as, b.Succs[0].Block()) 840 s.Br(obj.AJMP, b.Succs[1].Block()) 841 } else { 842 p = s.Br(invAs, b.Succs[1].Block()) 843 s.Br(obj.AJMP, b.Succs[0].Block()) 844 } 845 } 846 847 p.From.Type = obj.TYPE_REG 848 switch b.Kind { 849 case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU: 850 if b.NumControls() != 2 { 851 b.Fatalf("Unexpected number of controls (%d != 2): %s", b.NumControls(), b.LongString()) 852 } 853 p.From.Reg = b.Controls[0].Reg() 854 p.Reg = b.Controls[1].Reg() 855 856 case ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ: 857 if b.NumControls() != 1 { 858 b.Fatalf("Unexpected number of controls (%d != 1): %s", b.NumControls(), b.LongString()) 859 } 860 p.From.Reg = b.Controls[0].Reg() 861 } 862 863 default: 864 b.Fatalf("Unhandled block: %s", b.LongString()) 865 } 866} 867 868func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { 869 p := s.Prog(loadByType(t)) 870 p.From.Type = obj.TYPE_MEM 871 p.From.Name = obj.NAME_AUTO 872 p.From.Sym = n.Linksym() 873 p.From.Offset = n.FrameOffset() + off 874 p.To.Type = obj.TYPE_REG 875 p.To.Reg = reg 876 return p 877} 878 879func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { 880 p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) 881 p.To.Name = obj.NAME_PARAM 882 p.To.Sym = n.Linksym() 883 p.Pos = p.Pos.WithNotStmt() 884 return p 885} 886