github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/loong64/ssa.go (about) 1 // Copyright 2022 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 loong64 6 7 import ( 8 "math" 9 10 "github.com/go-asm/go/cmd/compile/base" 11 "github.com/go-asm/go/cmd/compile/ir" 12 "github.com/go-asm/go/cmd/compile/logopt" 13 "github.com/go-asm/go/cmd/compile/objw" 14 "github.com/go-asm/go/cmd/compile/ssa" 15 "github.com/go-asm/go/cmd/compile/ssagen" 16 "github.com/go-asm/go/cmd/compile/types" 17 "github.com/go-asm/go/cmd/obj" 18 "github.com/go-asm/go/cmd/obj/loong64" 19 ) 20 21 // isFPreg reports whether r is an FP register. 22 func isFPreg(r int16) bool { 23 return loong64.REG_F0 <= r && r <= loong64.REG_F31 24 } 25 26 // loadByType returns the load instruction of the given type. 27 func loadByType(t *types.Type, r int16) obj.As { 28 if isFPreg(r) { 29 if t.Size() == 4 { 30 return loong64.AMOVF 31 } else { 32 return loong64.AMOVD 33 } 34 } else { 35 switch t.Size() { 36 case 1: 37 if t.IsSigned() { 38 return loong64.AMOVB 39 } else { 40 return loong64.AMOVBU 41 } 42 case 2: 43 if t.IsSigned() { 44 return loong64.AMOVH 45 } else { 46 return loong64.AMOVHU 47 } 48 case 4: 49 if t.IsSigned() { 50 return loong64.AMOVW 51 } else { 52 return loong64.AMOVWU 53 } 54 case 8: 55 return loong64.AMOVV 56 } 57 } 58 panic("bad load type") 59 } 60 61 // storeByType returns the store instruction of the given type. 62 func storeByType(t *types.Type, r int16) obj.As { 63 if isFPreg(r) { 64 if t.Size() == 4 { 65 return loong64.AMOVF 66 } else { 67 return loong64.AMOVD 68 } 69 } else { 70 switch t.Size() { 71 case 1: 72 return loong64.AMOVB 73 case 2: 74 return loong64.AMOVH 75 case 4: 76 return loong64.AMOVW 77 case 8: 78 return loong64.AMOVV 79 } 80 } 81 panic("bad store type") 82 } 83 84 // largestMove returns the largest move instruction possible and its size, 85 // given the alignment of the total size of the move. 86 // 87 // e.g., a 16-byte move may use MOVV, but an 11-byte move must use MOVB. 88 // 89 // Note that the moves may not be on naturally aligned addresses depending on 90 // the source and destination. 91 // 92 // This matches the calculation in ssa.moveSize. 93 func largestMove(alignment int64) (obj.As, int64) { 94 switch { 95 case alignment%8 == 0: 96 return loong64.AMOVV, 8 97 case alignment%4 == 0: 98 return loong64.AMOVW, 4 99 case alignment%2 == 0: 100 return loong64.AMOVH, 2 101 default: 102 return loong64.AMOVB, 1 103 } 104 } 105 106 func ssaGenValue(s *ssagen.State, v *ssa.Value) { 107 switch v.Op { 108 case ssa.OpCopy, ssa.OpLOONG64MOVVreg: 109 if v.Type.IsMemory() { 110 return 111 } 112 x := v.Args[0].Reg() 113 y := v.Reg() 114 if x == y { 115 return 116 } 117 as := loong64.AMOVV 118 if isFPreg(x) && isFPreg(y) { 119 as = loong64.AMOVD 120 } 121 p := s.Prog(as) 122 p.From.Type = obj.TYPE_REG 123 p.From.Reg = x 124 p.To.Type = obj.TYPE_REG 125 p.To.Reg = y 126 case ssa.OpLOONG64MOVVnop: 127 // nothing to do 128 case ssa.OpLoadReg: 129 if v.Type.IsFlags() { 130 v.Fatalf("load flags not implemented: %v", v.LongString()) 131 return 132 } 133 r := v.Reg() 134 p := s.Prog(loadByType(v.Type, r)) 135 ssagen.AddrAuto(&p.From, v.Args[0]) 136 p.To.Type = obj.TYPE_REG 137 p.To.Reg = r 138 case ssa.OpStoreReg: 139 if v.Type.IsFlags() { 140 v.Fatalf("store flags not implemented: %v", v.LongString()) 141 return 142 } 143 r := v.Args[0].Reg() 144 p := s.Prog(storeByType(v.Type, r)) 145 p.From.Type = obj.TYPE_REG 146 p.From.Reg = r 147 ssagen.AddrAuto(&p.To, v) 148 case ssa.OpArgIntReg, ssa.OpArgFloatReg: 149 // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill 150 // The loop only runs once. 151 for _, a := range v.Block.Func.RegArgs { 152 // Pass the spill/unspill information along to the assembler, offset by size of 153 // the saved LR slot. 154 addr := ssagen.SpillSlotAddr(a, loong64.REGSP, base.Ctxt.Arch.FixedFrameSize) 155 s.FuncInfo().AddSpill( 156 obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type, a.Reg), Spill: storeByType(a.Type, a.Reg)}) 157 } 158 v.Block.Func.RegArgs = nil 159 ssagen.CheckArgReg(v) 160 case ssa.OpLOONG64ADDV, 161 ssa.OpLOONG64SUBV, 162 ssa.OpLOONG64AND, 163 ssa.OpLOONG64OR, 164 ssa.OpLOONG64XOR, 165 ssa.OpLOONG64NOR, 166 ssa.OpLOONG64SLLV, 167 ssa.OpLOONG64SRLV, 168 ssa.OpLOONG64SRAV, 169 ssa.OpLOONG64ROTR, 170 ssa.OpLOONG64ROTRV, 171 ssa.OpLOONG64ADDF, 172 ssa.OpLOONG64ADDD, 173 ssa.OpLOONG64SUBF, 174 ssa.OpLOONG64SUBD, 175 ssa.OpLOONG64MULF, 176 ssa.OpLOONG64MULD, 177 ssa.OpLOONG64DIVF, 178 ssa.OpLOONG64DIVD, 179 ssa.OpLOONG64MULV, ssa.OpLOONG64MULHV, ssa.OpLOONG64MULHVU, 180 ssa.OpLOONG64DIVV, ssa.OpLOONG64REMV, ssa.OpLOONG64DIVVU, ssa.OpLOONG64REMVU: 181 p := s.Prog(v.Op.Asm()) 182 p.From.Type = obj.TYPE_REG 183 p.From.Reg = v.Args[1].Reg() 184 p.Reg = v.Args[0].Reg() 185 p.To.Type = obj.TYPE_REG 186 p.To.Reg = v.Reg() 187 case ssa.OpLOONG64SGT, 188 ssa.OpLOONG64SGTU: 189 p := s.Prog(v.Op.Asm()) 190 p.From.Type = obj.TYPE_REG 191 p.From.Reg = v.Args[0].Reg() 192 p.Reg = v.Args[1].Reg() 193 p.To.Type = obj.TYPE_REG 194 p.To.Reg = v.Reg() 195 case ssa.OpLOONG64ADDVconst, 196 ssa.OpLOONG64SUBVconst, 197 ssa.OpLOONG64ANDconst, 198 ssa.OpLOONG64ORconst, 199 ssa.OpLOONG64XORconst, 200 ssa.OpLOONG64NORconst, 201 ssa.OpLOONG64SLLVconst, 202 ssa.OpLOONG64SRLVconst, 203 ssa.OpLOONG64SRAVconst, 204 ssa.OpLOONG64ROTRconst, 205 ssa.OpLOONG64ROTRVconst, 206 ssa.OpLOONG64SGTconst, 207 ssa.OpLOONG64SGTUconst: 208 p := s.Prog(v.Op.Asm()) 209 p.From.Type = obj.TYPE_CONST 210 p.From.Offset = v.AuxInt 211 p.Reg = v.Args[0].Reg() 212 p.To.Type = obj.TYPE_REG 213 p.To.Reg = v.Reg() 214 case ssa.OpLOONG64MOVVconst: 215 r := v.Reg() 216 p := s.Prog(v.Op.Asm()) 217 p.From.Type = obj.TYPE_CONST 218 p.From.Offset = v.AuxInt 219 p.To.Type = obj.TYPE_REG 220 p.To.Reg = r 221 if isFPreg(r) { 222 // cannot move into FP or special registers, use TMP as intermediate 223 p.To.Reg = loong64.REGTMP 224 p = s.Prog(loong64.AMOVV) 225 p.From.Type = obj.TYPE_REG 226 p.From.Reg = loong64.REGTMP 227 p.To.Type = obj.TYPE_REG 228 p.To.Reg = r 229 } 230 case ssa.OpLOONG64MOVFconst, 231 ssa.OpLOONG64MOVDconst: 232 p := s.Prog(v.Op.Asm()) 233 p.From.Type = obj.TYPE_FCONST 234 p.From.Val = math.Float64frombits(uint64(v.AuxInt)) 235 p.To.Type = obj.TYPE_REG 236 p.To.Reg = v.Reg() 237 case ssa.OpLOONG64CMPEQF, 238 ssa.OpLOONG64CMPEQD, 239 ssa.OpLOONG64CMPGEF, 240 ssa.OpLOONG64CMPGED, 241 ssa.OpLOONG64CMPGTF, 242 ssa.OpLOONG64CMPGTD: 243 p := s.Prog(v.Op.Asm()) 244 p.From.Type = obj.TYPE_REG 245 p.From.Reg = v.Args[0].Reg() 246 p.Reg = v.Args[1].Reg() 247 case ssa.OpLOONG64MOVVaddr: 248 p := s.Prog(loong64.AMOVV) 249 p.From.Type = obj.TYPE_ADDR 250 p.From.Reg = v.Args[0].Reg() 251 var wantreg string 252 // MOVV $sym+off(base), R 253 // the assembler expands it as the following: 254 // - base is SP: add constant offset to SP (R3) 255 // when constant is large, tmp register (R30) may be used 256 // - base is SB: load external address with relocation 257 switch v.Aux.(type) { 258 default: 259 v.Fatalf("aux is of unknown type %T", v.Aux) 260 case *obj.LSym: 261 wantreg = "SB" 262 ssagen.AddAux(&p.From, v) 263 case *ir.Name: 264 wantreg = "SP" 265 ssagen.AddAux(&p.From, v) 266 case nil: 267 // No sym, just MOVV $off(SP), R 268 wantreg = "SP" 269 p.From.Offset = v.AuxInt 270 } 271 if reg := v.Args[0].RegName(); reg != wantreg { 272 v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) 273 } 274 p.To.Type = obj.TYPE_REG 275 p.To.Reg = v.Reg() 276 case ssa.OpLOONG64MOVBload, 277 ssa.OpLOONG64MOVBUload, 278 ssa.OpLOONG64MOVHload, 279 ssa.OpLOONG64MOVHUload, 280 ssa.OpLOONG64MOVWload, 281 ssa.OpLOONG64MOVWUload, 282 ssa.OpLOONG64MOVVload, 283 ssa.OpLOONG64MOVFload, 284 ssa.OpLOONG64MOVDload: 285 p := s.Prog(v.Op.Asm()) 286 p.From.Type = obj.TYPE_MEM 287 p.From.Reg = v.Args[0].Reg() 288 ssagen.AddAux(&p.From, v) 289 p.To.Type = obj.TYPE_REG 290 p.To.Reg = v.Reg() 291 case ssa.OpLOONG64MOVBstore, 292 ssa.OpLOONG64MOVHstore, 293 ssa.OpLOONG64MOVWstore, 294 ssa.OpLOONG64MOVVstore, 295 ssa.OpLOONG64MOVFstore, 296 ssa.OpLOONG64MOVDstore: 297 p := s.Prog(v.Op.Asm()) 298 p.From.Type = obj.TYPE_REG 299 p.From.Reg = v.Args[1].Reg() 300 p.To.Type = obj.TYPE_MEM 301 p.To.Reg = v.Args[0].Reg() 302 ssagen.AddAux(&p.To, v) 303 case ssa.OpLOONG64MOVBstorezero, 304 ssa.OpLOONG64MOVHstorezero, 305 ssa.OpLOONG64MOVWstorezero, 306 ssa.OpLOONG64MOVVstorezero: 307 p := s.Prog(v.Op.Asm()) 308 p.From.Type = obj.TYPE_REG 309 p.From.Reg = loong64.REGZERO 310 p.To.Type = obj.TYPE_MEM 311 p.To.Reg = v.Args[0].Reg() 312 ssagen.AddAux(&p.To, v) 313 case ssa.OpLOONG64MOVBreg, 314 ssa.OpLOONG64MOVBUreg, 315 ssa.OpLOONG64MOVHreg, 316 ssa.OpLOONG64MOVHUreg, 317 ssa.OpLOONG64MOVWreg, 318 ssa.OpLOONG64MOVWUreg: 319 a := v.Args[0] 320 for a.Op == ssa.OpCopy || a.Op == ssa.OpLOONG64MOVVreg { 321 a = a.Args[0] 322 } 323 if a.Op == ssa.OpLoadReg && loong64.REG_R0 <= a.Reg() && a.Reg() <= loong64.REG_R31 { 324 // LoadReg from a narrower type does an extension, except loading 325 // to a floating point register. So only eliminate the extension 326 // if it is loaded to an integer register. 327 328 t := a.Type 329 switch { 330 case v.Op == ssa.OpLOONG64MOVBreg && t.Size() == 1 && t.IsSigned(), 331 v.Op == ssa.OpLOONG64MOVBUreg && t.Size() == 1 && !t.IsSigned(), 332 v.Op == ssa.OpLOONG64MOVHreg && t.Size() == 2 && t.IsSigned(), 333 v.Op == ssa.OpLOONG64MOVHUreg && t.Size() == 2 && !t.IsSigned(), 334 v.Op == ssa.OpLOONG64MOVWreg && t.Size() == 4 && t.IsSigned(), 335 v.Op == ssa.OpLOONG64MOVWUreg && t.Size() == 4 && !t.IsSigned(): 336 // arg is a proper-typed load, already zero/sign-extended, don't extend again 337 if v.Reg() == v.Args[0].Reg() { 338 return 339 } 340 p := s.Prog(loong64.AMOVV) 341 p.From.Type = obj.TYPE_REG 342 p.From.Reg = v.Args[0].Reg() 343 p.To.Type = obj.TYPE_REG 344 p.To.Reg = v.Reg() 345 return 346 default: 347 } 348 } 349 fallthrough 350 case ssa.OpLOONG64MOVWF, 351 ssa.OpLOONG64MOVWD, 352 ssa.OpLOONG64TRUNCFW, 353 ssa.OpLOONG64TRUNCDW, 354 ssa.OpLOONG64MOVVF, 355 ssa.OpLOONG64MOVVD, 356 ssa.OpLOONG64TRUNCFV, 357 ssa.OpLOONG64TRUNCDV, 358 ssa.OpLOONG64MOVFD, 359 ssa.OpLOONG64MOVDF, 360 ssa.OpLOONG64NEGF, 361 ssa.OpLOONG64NEGD, 362 ssa.OpLOONG64SQRTD, 363 ssa.OpLOONG64SQRTF: 364 p := s.Prog(v.Op.Asm()) 365 p.From.Type = obj.TYPE_REG 366 p.From.Reg = v.Args[0].Reg() 367 p.To.Type = obj.TYPE_REG 368 p.To.Reg = v.Reg() 369 case ssa.OpLOONG64NEGV: 370 // SUB from REGZERO 371 p := s.Prog(loong64.ASUBVU) 372 p.From.Type = obj.TYPE_REG 373 p.From.Reg = v.Args[0].Reg() 374 p.Reg = loong64.REGZERO 375 p.To.Type = obj.TYPE_REG 376 p.To.Reg = v.Reg() 377 case ssa.OpLOONG64DUFFZERO: 378 // runtime.duffzero expects start address in R20 379 p := s.Prog(obj.ADUFFZERO) 380 p.To.Type = obj.TYPE_MEM 381 p.To.Name = obj.NAME_EXTERN 382 p.To.Sym = ir.Syms.Duffzero 383 p.To.Offset = v.AuxInt 384 case ssa.OpLOONG64LoweredZero: 385 // MOVx R0, (Rarg0) 386 // ADDV $sz, Rarg0 387 // BGEU Rarg1, Rarg0, -2(PC) 388 mov, sz := largestMove(v.AuxInt) 389 p := s.Prog(mov) 390 p.From.Type = obj.TYPE_REG 391 p.From.Reg = loong64.REGZERO 392 p.To.Type = obj.TYPE_MEM 393 p.To.Reg = v.Args[0].Reg() 394 395 p2 := s.Prog(loong64.AADDVU) 396 p2.From.Type = obj.TYPE_CONST 397 p2.From.Offset = sz 398 p2.To.Type = obj.TYPE_REG 399 p2.To.Reg = v.Args[0].Reg() 400 401 p3 := s.Prog(loong64.ABGEU) 402 p3.From.Type = obj.TYPE_REG 403 p3.From.Reg = v.Args[1].Reg() 404 p3.Reg = v.Args[0].Reg() 405 p3.To.Type = obj.TYPE_BRANCH 406 p3.To.SetTarget(p) 407 408 case ssa.OpLOONG64DUFFCOPY: 409 p := s.Prog(obj.ADUFFCOPY) 410 p.To.Type = obj.TYPE_MEM 411 p.To.Name = obj.NAME_EXTERN 412 p.To.Sym = ir.Syms.Duffcopy 413 p.To.Offset = v.AuxInt 414 case ssa.OpLOONG64LoweredMove: 415 // MOVx (Rarg1), Rtmp 416 // MOVx Rtmp, (Rarg0) 417 // ADDV $sz, Rarg1 418 // ADDV $sz, Rarg0 419 // BGEU Rarg2, Rarg0, -4(PC) 420 mov, sz := largestMove(v.AuxInt) 421 p := s.Prog(mov) 422 p.From.Type = obj.TYPE_MEM 423 p.From.Reg = v.Args[1].Reg() 424 p.To.Type = obj.TYPE_REG 425 p.To.Reg = loong64.REGTMP 426 427 p2 := s.Prog(mov) 428 p2.From.Type = obj.TYPE_REG 429 p2.From.Reg = loong64.REGTMP 430 p2.To.Type = obj.TYPE_MEM 431 p2.To.Reg = v.Args[0].Reg() 432 433 p3 := s.Prog(loong64.AADDVU) 434 p3.From.Type = obj.TYPE_CONST 435 p3.From.Offset = sz 436 p3.To.Type = obj.TYPE_REG 437 p3.To.Reg = v.Args[1].Reg() 438 439 p4 := s.Prog(loong64.AADDVU) 440 p4.From.Type = obj.TYPE_CONST 441 p4.From.Offset = sz 442 p4.To.Type = obj.TYPE_REG 443 p4.To.Reg = v.Args[0].Reg() 444 445 p5 := s.Prog(loong64.ABGEU) 446 p5.From.Type = obj.TYPE_REG 447 p5.From.Reg = v.Args[2].Reg() 448 p5.Reg = v.Args[1].Reg() 449 p5.To.Type = obj.TYPE_BRANCH 450 p5.To.SetTarget(p) 451 452 case ssa.OpLOONG64CALLstatic, ssa.OpLOONG64CALLclosure, ssa.OpLOONG64CALLinter: 453 s.Call(v) 454 case ssa.OpLOONG64CALLtail: 455 s.TailCall(v) 456 case ssa.OpLOONG64LoweredWB: 457 p := s.Prog(obj.ACALL) 458 p.To.Type = obj.TYPE_MEM 459 p.To.Name = obj.NAME_EXTERN 460 // AuxInt encodes how many buffer entries we need. 461 p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1] 462 case ssa.OpLOONG64LoweredPanicBoundsA, ssa.OpLOONG64LoweredPanicBoundsB, ssa.OpLOONG64LoweredPanicBoundsC: 463 p := s.Prog(obj.ACALL) 464 p.To.Type = obj.TYPE_MEM 465 p.To.Name = obj.NAME_EXTERN 466 p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] 467 s.UseArgs(16) // space used in callee args area by assembly stubs 468 case ssa.OpLOONG64LoweredAtomicLoad8, ssa.OpLOONG64LoweredAtomicLoad32, ssa.OpLOONG64LoweredAtomicLoad64: 469 as := loong64.AMOVV 470 switch v.Op { 471 case ssa.OpLOONG64LoweredAtomicLoad8: 472 as = loong64.AMOVB 473 case ssa.OpLOONG64LoweredAtomicLoad32: 474 as = loong64.AMOVW 475 } 476 s.Prog(loong64.ADBAR) 477 p := s.Prog(as) 478 p.From.Type = obj.TYPE_MEM 479 p.From.Reg = v.Args[0].Reg() 480 p.To.Type = obj.TYPE_REG 481 p.To.Reg = v.Reg0() 482 s.Prog(loong64.ADBAR) 483 case ssa.OpLOONG64LoweredAtomicStore8, ssa.OpLOONG64LoweredAtomicStore32, ssa.OpLOONG64LoweredAtomicStore64: 484 as := loong64.AMOVV 485 switch v.Op { 486 case ssa.OpLOONG64LoweredAtomicStore8: 487 as = loong64.AMOVB 488 case ssa.OpLOONG64LoweredAtomicStore32: 489 as = loong64.AMOVW 490 } 491 s.Prog(loong64.ADBAR) 492 p := s.Prog(as) 493 p.From.Type = obj.TYPE_REG 494 p.From.Reg = v.Args[1].Reg() 495 p.To.Type = obj.TYPE_MEM 496 p.To.Reg = v.Args[0].Reg() 497 s.Prog(loong64.ADBAR) 498 case ssa.OpLOONG64LoweredAtomicStorezero32, ssa.OpLOONG64LoweredAtomicStorezero64: 499 as := loong64.AMOVV 500 if v.Op == ssa.OpLOONG64LoweredAtomicStorezero32 { 501 as = loong64.AMOVW 502 } 503 s.Prog(loong64.ADBAR) 504 p := s.Prog(as) 505 p.From.Type = obj.TYPE_REG 506 p.From.Reg = loong64.REGZERO 507 p.To.Type = obj.TYPE_MEM 508 p.To.Reg = v.Args[0].Reg() 509 s.Prog(loong64.ADBAR) 510 case ssa.OpLOONG64LoweredAtomicExchange32, ssa.OpLOONG64LoweredAtomicExchange64: 511 // DBAR 512 // MOVV Rarg1, Rtmp 513 // LL (Rarg0), Rout 514 // SC Rtmp, (Rarg0) 515 // BEQ Rtmp, -3(PC) 516 // DBAR 517 ll := loong64.ALLV 518 sc := loong64.ASCV 519 if v.Op == ssa.OpLOONG64LoweredAtomicExchange32 { 520 ll = loong64.ALL 521 sc = loong64.ASC 522 } 523 s.Prog(loong64.ADBAR) 524 p := s.Prog(loong64.AMOVV) 525 p.From.Type = obj.TYPE_REG 526 p.From.Reg = v.Args[1].Reg() 527 p.To.Type = obj.TYPE_REG 528 p.To.Reg = loong64.REGTMP 529 p1 := s.Prog(ll) 530 p1.From.Type = obj.TYPE_MEM 531 p1.From.Reg = v.Args[0].Reg() 532 p1.To.Type = obj.TYPE_REG 533 p1.To.Reg = v.Reg0() 534 p2 := s.Prog(sc) 535 p2.From.Type = obj.TYPE_REG 536 p2.From.Reg = loong64.REGTMP 537 p2.To.Type = obj.TYPE_MEM 538 p2.To.Reg = v.Args[0].Reg() 539 p3 := s.Prog(loong64.ABEQ) 540 p3.From.Type = obj.TYPE_REG 541 p3.From.Reg = loong64.REGTMP 542 p3.To.Type = obj.TYPE_BRANCH 543 p3.To.SetTarget(p) 544 s.Prog(loong64.ADBAR) 545 case ssa.OpLOONG64LoweredAtomicAdd32, ssa.OpLOONG64LoweredAtomicAdd64: 546 // DBAR 547 // LL (Rarg0), Rout 548 // ADDV Rarg1, Rout, Rtmp 549 // SC Rtmp, (Rarg0) 550 // BEQ Rtmp, -3(PC) 551 // DBAR 552 // ADDV Rarg1, Rout 553 ll := loong64.ALLV 554 sc := loong64.ASCV 555 if v.Op == ssa.OpLOONG64LoweredAtomicAdd32 { 556 ll = loong64.ALL 557 sc = loong64.ASC 558 } 559 s.Prog(loong64.ADBAR) 560 p := s.Prog(ll) 561 p.From.Type = obj.TYPE_MEM 562 p.From.Reg = v.Args[0].Reg() 563 p.To.Type = obj.TYPE_REG 564 p.To.Reg = v.Reg0() 565 p1 := s.Prog(loong64.AADDVU) 566 p1.From.Type = obj.TYPE_REG 567 p1.From.Reg = v.Args[1].Reg() 568 p1.Reg = v.Reg0() 569 p1.To.Type = obj.TYPE_REG 570 p1.To.Reg = loong64.REGTMP 571 p2 := s.Prog(sc) 572 p2.From.Type = obj.TYPE_REG 573 p2.From.Reg = loong64.REGTMP 574 p2.To.Type = obj.TYPE_MEM 575 p2.To.Reg = v.Args[0].Reg() 576 p3 := s.Prog(loong64.ABEQ) 577 p3.From.Type = obj.TYPE_REG 578 p3.From.Reg = loong64.REGTMP 579 p3.To.Type = obj.TYPE_BRANCH 580 p3.To.SetTarget(p) 581 s.Prog(loong64.ADBAR) 582 p4 := s.Prog(loong64.AADDVU) 583 p4.From.Type = obj.TYPE_REG 584 p4.From.Reg = v.Args[1].Reg() 585 p4.Reg = v.Reg0() 586 p4.To.Type = obj.TYPE_REG 587 p4.To.Reg = v.Reg0() 588 case ssa.OpLOONG64LoweredAtomicAddconst32, ssa.OpLOONG64LoweredAtomicAddconst64: 589 // DBAR 590 // LL (Rarg0), Rout 591 // ADDV $auxint, Rout, Rtmp 592 // SC Rtmp, (Rarg0) 593 // BEQ Rtmp, -3(PC) 594 // DBAR 595 // ADDV $auxint, Rout 596 ll := loong64.ALLV 597 sc := loong64.ASCV 598 if v.Op == ssa.OpLOONG64LoweredAtomicAddconst32 { 599 ll = loong64.ALL 600 sc = loong64.ASC 601 } 602 s.Prog(loong64.ADBAR) 603 p := s.Prog(ll) 604 p.From.Type = obj.TYPE_MEM 605 p.From.Reg = v.Args[0].Reg() 606 p.To.Type = obj.TYPE_REG 607 p.To.Reg = v.Reg0() 608 p1 := s.Prog(loong64.AADDVU) 609 p1.From.Type = obj.TYPE_CONST 610 p1.From.Offset = v.AuxInt 611 p1.Reg = v.Reg0() 612 p1.To.Type = obj.TYPE_REG 613 p1.To.Reg = loong64.REGTMP 614 p2 := s.Prog(sc) 615 p2.From.Type = obj.TYPE_REG 616 p2.From.Reg = loong64.REGTMP 617 p2.To.Type = obj.TYPE_MEM 618 p2.To.Reg = v.Args[0].Reg() 619 p3 := s.Prog(loong64.ABEQ) 620 p3.From.Type = obj.TYPE_REG 621 p3.From.Reg = loong64.REGTMP 622 p3.To.Type = obj.TYPE_BRANCH 623 p3.To.SetTarget(p) 624 s.Prog(loong64.ADBAR) 625 p4 := s.Prog(loong64.AADDVU) 626 p4.From.Type = obj.TYPE_CONST 627 p4.From.Offset = v.AuxInt 628 p4.Reg = v.Reg0() 629 p4.To.Type = obj.TYPE_REG 630 p4.To.Reg = v.Reg0() 631 case ssa.OpLOONG64LoweredAtomicCas32, ssa.OpLOONG64LoweredAtomicCas64: 632 // MOVV $0, Rout 633 // DBAR 634 // LL (Rarg0), Rtmp 635 // BNE Rtmp, Rarg1, 4(PC) 636 // MOVV Rarg2, Rout 637 // SC Rout, (Rarg0) 638 // BEQ Rout, -4(PC) 639 // DBAR 640 ll := loong64.ALLV 641 sc := loong64.ASCV 642 if v.Op == ssa.OpLOONG64LoweredAtomicCas32 { 643 ll = loong64.ALL 644 sc = loong64.ASC 645 } 646 p := s.Prog(loong64.AMOVV) 647 p.From.Type = obj.TYPE_REG 648 p.From.Reg = loong64.REGZERO 649 p.To.Type = obj.TYPE_REG 650 p.To.Reg = v.Reg0() 651 s.Prog(loong64.ADBAR) 652 p1 := s.Prog(ll) 653 p1.From.Type = obj.TYPE_MEM 654 p1.From.Reg = v.Args[0].Reg() 655 p1.To.Type = obj.TYPE_REG 656 p1.To.Reg = loong64.REGTMP 657 p2 := s.Prog(loong64.ABNE) 658 p2.From.Type = obj.TYPE_REG 659 p2.From.Reg = v.Args[1].Reg() 660 p2.Reg = loong64.REGTMP 661 p2.To.Type = obj.TYPE_BRANCH 662 p3 := s.Prog(loong64.AMOVV) 663 p3.From.Type = obj.TYPE_REG 664 p3.From.Reg = v.Args[2].Reg() 665 p3.To.Type = obj.TYPE_REG 666 p3.To.Reg = v.Reg0() 667 p4 := s.Prog(sc) 668 p4.From.Type = obj.TYPE_REG 669 p4.From.Reg = v.Reg0() 670 p4.To.Type = obj.TYPE_MEM 671 p4.To.Reg = v.Args[0].Reg() 672 p5 := s.Prog(loong64.ABEQ) 673 p5.From.Type = obj.TYPE_REG 674 p5.From.Reg = v.Reg0() 675 p5.To.Type = obj.TYPE_BRANCH 676 p5.To.SetTarget(p1) 677 p6 := s.Prog(loong64.ADBAR) 678 p2.To.SetTarget(p6) 679 case ssa.OpLOONG64LoweredNilCheck: 680 // Issue a load which will fault if arg is nil. 681 p := s.Prog(loong64.AMOVB) 682 p.From.Type = obj.TYPE_MEM 683 p.From.Reg = v.Args[0].Reg() 684 ssagen.AddAux(&p.From, v) 685 p.To.Type = obj.TYPE_REG 686 p.To.Reg = loong64.REGTMP 687 if logopt.Enabled() { 688 logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name) 689 } 690 if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers 691 base.WarnfAt(v.Pos, "generated nil check") 692 } 693 case ssa.OpLOONG64FPFlagTrue, 694 ssa.OpLOONG64FPFlagFalse: 695 // MOVV $0, r 696 // BFPF 2(PC) 697 // MOVV $1, r 698 branch := loong64.ABFPF 699 if v.Op == ssa.OpLOONG64FPFlagFalse { 700 branch = loong64.ABFPT 701 } 702 p := s.Prog(loong64.AMOVV) 703 p.From.Type = obj.TYPE_REG 704 p.From.Reg = loong64.REGZERO 705 p.To.Type = obj.TYPE_REG 706 p.To.Reg = v.Reg() 707 p2 := s.Prog(branch) 708 p2.To.Type = obj.TYPE_BRANCH 709 p3 := s.Prog(loong64.AMOVV) 710 p3.From.Type = obj.TYPE_CONST 711 p3.From.Offset = 1 712 p3.To.Type = obj.TYPE_REG 713 p3.To.Reg = v.Reg() 714 p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land 715 p2.To.SetTarget(p4) 716 case ssa.OpLOONG64LoweredGetClosurePtr: 717 // Closure pointer is R22 (loong64.REGCTXT). 718 ssagen.CheckLoweredGetClosurePtr(v) 719 case ssa.OpLOONG64LoweredGetCallerSP: 720 // caller's SP is FixedFrameSize below the address of the first arg 721 p := s.Prog(loong64.AMOVV) 722 p.From.Type = obj.TYPE_ADDR 723 p.From.Offset = -base.Ctxt.Arch.FixedFrameSize 724 p.From.Name = obj.NAME_PARAM 725 p.To.Type = obj.TYPE_REG 726 p.To.Reg = v.Reg() 727 case ssa.OpLOONG64LoweredGetCallerPC: 728 p := s.Prog(obj.AGETCALLERPC) 729 p.To.Type = obj.TYPE_REG 730 p.To.Reg = v.Reg() 731 case ssa.OpLOONG64MASKEQZ, ssa.OpLOONG64MASKNEZ: 732 p := s.Prog(v.Op.Asm()) 733 p.From.Type = obj.TYPE_REG 734 p.From.Reg = v.Args[1].Reg() 735 p.Reg = v.Args[0].Reg() 736 p.To.Type = obj.TYPE_REG 737 p.To.Reg = v.Reg() 738 case ssa.OpClobber, ssa.OpClobberReg: 739 // TODO: implement for clobberdead experiment. Nop is ok for now. 740 default: 741 v.Fatalf("genValue not implemented: %s", v.LongString()) 742 } 743 } 744 745 var blockJump = map[ssa.BlockKind]struct { 746 asm, invasm obj.As 747 }{ 748 ssa.BlockLOONG64EQ: {loong64.ABEQ, loong64.ABNE}, 749 ssa.BlockLOONG64NE: {loong64.ABNE, loong64.ABEQ}, 750 ssa.BlockLOONG64LTZ: {loong64.ABLTZ, loong64.ABGEZ}, 751 ssa.BlockLOONG64GEZ: {loong64.ABGEZ, loong64.ABLTZ}, 752 ssa.BlockLOONG64LEZ: {loong64.ABLEZ, loong64.ABGTZ}, 753 ssa.BlockLOONG64GTZ: {loong64.ABGTZ, loong64.ABLEZ}, 754 ssa.BlockLOONG64FPT: {loong64.ABFPT, loong64.ABFPF}, 755 ssa.BlockLOONG64FPF: {loong64.ABFPF, loong64.ABFPT}, 756 } 757 758 func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { 759 switch b.Kind { 760 case ssa.BlockPlain: 761 if b.Succs[0].Block() != next { 762 p := s.Prog(obj.AJMP) 763 p.To.Type = obj.TYPE_BRANCH 764 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 765 } 766 case ssa.BlockDefer: 767 // defer returns in R19: 768 // 0 if we should continue executing 769 // 1 if we should jump to deferreturn call 770 p := s.Prog(loong64.ABNE) 771 p.From.Type = obj.TYPE_REG 772 p.From.Reg = loong64.REGZERO 773 p.Reg = loong64.REG_R19 774 p.To.Type = obj.TYPE_BRANCH 775 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()}) 776 if b.Succs[0].Block() != next { 777 p := s.Prog(obj.AJMP) 778 p.To.Type = obj.TYPE_BRANCH 779 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 780 } 781 case ssa.BlockExit, ssa.BlockRetJmp: 782 case ssa.BlockRet: 783 s.Prog(obj.ARET) 784 case ssa.BlockLOONG64EQ, ssa.BlockLOONG64NE, 785 ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ, 786 ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ, 787 ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF: 788 jmp := blockJump[b.Kind] 789 var p *obj.Prog 790 switch next { 791 case b.Succs[0].Block(): 792 p = s.Br(jmp.invasm, b.Succs[1].Block()) 793 case b.Succs[1].Block(): 794 p = s.Br(jmp.asm, b.Succs[0].Block()) 795 default: 796 if b.Likely != ssa.BranchUnlikely { 797 p = s.Br(jmp.asm, b.Succs[0].Block()) 798 s.Br(obj.AJMP, b.Succs[1].Block()) 799 } else { 800 p = s.Br(jmp.invasm, b.Succs[1].Block()) 801 s.Br(obj.AJMP, b.Succs[0].Block()) 802 } 803 } 804 if !b.Controls[0].Type.IsFlags() { 805 p.From.Type = obj.TYPE_REG 806 p.From.Reg = b.Controls[0].Reg() 807 } 808 default: 809 b.Fatalf("branch not implemented: %s", b.LongString()) 810 } 811 } 812 813 func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { 814 p := s.Prog(loadByType(t, reg)) 815 p.From.Type = obj.TYPE_MEM 816 p.From.Name = obj.NAME_AUTO 817 p.From.Sym = n.Linksym() 818 p.From.Offset = n.FrameOffset() + off 819 p.To.Type = obj.TYPE_REG 820 p.To.Reg = reg 821 return p 822 } 823 824 func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { 825 p = pp.Append(p, storeByType(t, reg), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) 826 p.To.Name = obj.NAME_PARAM 827 p.To.Sym = n.Linksym() 828 p.Pos = p.Pos.WithNotStmt() 829 return p 830 }