InstEmitFloatArithmetic.cs
1 using Ryujinx.Graphics.Shader.Decoders; 2 using Ryujinx.Graphics.Shader.IntermediateRepresentation; 3 using Ryujinx.Graphics.Shader.Translation; 4 using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; 5 using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; 6 using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; 7 8 namespace Ryujinx.Graphics.Shader.Instructions 9 { 10 static partial class InstEmit 11 { 12 public static void DaddR(EmitterContext context) 13 { 14 InstDaddR op = context.GetOp<InstDaddR>(); 15 16 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 17 var srcB = GetSrcReg(context, op.SrcB, isFP64: true); 18 19 EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC); 20 } 21 22 public static void DaddI(EmitterContext context) 23 { 24 InstDaddI op = context.GetOp<InstDaddI>(); 25 26 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 27 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true); 28 29 EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC); 30 } 31 32 public static void DaddC(EmitterContext context) 33 { 34 InstDaddC op = context.GetOp<InstDaddC>(); 35 36 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 37 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true); 38 39 EmitFadd(context, Instruction.FP64, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC); 40 } 41 42 public static void DfmaR(EmitterContext context) 43 { 44 InstDfmaR op = context.GetOp<InstDfmaR>(); 45 46 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 47 var srcB = GetSrcReg(context, op.SrcB, isFP64: true); 48 var srcC = GetSrcReg(context, op.SrcC, isFP64: true); 49 50 EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC); 51 } 52 53 public static void DfmaI(EmitterContext context) 54 { 55 InstDfmaI op = context.GetOp<InstDfmaI>(); 56 57 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 58 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true); 59 var srcC = GetSrcReg(context, op.SrcC, isFP64: true); 60 61 EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC); 62 } 63 64 public static void DfmaC(EmitterContext context) 65 { 66 InstDfmaC op = context.GetOp<InstDfmaC>(); 67 68 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 69 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true); 70 var srcC = GetSrcReg(context, op.SrcC, isFP64: true); 71 72 EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC); 73 } 74 75 public static void DfmaRc(EmitterContext context) 76 { 77 InstDfmaRc op = context.GetOp<InstDfmaRc>(); 78 79 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 80 var srcB = GetSrcReg(context, op.SrcC, isFP64: true); 81 var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true); 82 83 EmitFfma(context, Instruction.FP64, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, false, op.WriteCC); 84 } 85 86 public static void DmulR(EmitterContext context) 87 { 88 InstDmulR op = context.GetOp<InstDmulR>(); 89 90 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 91 var srcB = GetSrcReg(context, op.SrcB, isFP64: true); 92 93 EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC); 94 } 95 96 public static void DmulI(EmitterContext context) 97 { 98 InstDmulI op = context.GetOp<InstDmulI>(); 99 100 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 101 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20), isFP64: true); 102 103 EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC); 104 } 105 106 public static void DmulC(EmitterContext context) 107 { 108 InstDmulC op = context.GetOp<InstDmulC>(); 109 110 var srcA = GetSrcReg(context, op.SrcA, isFP64: true); 111 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset, isFP64: true); 112 113 EmitFmul(context, Instruction.FP64, MultiplyScale.NoScale, srcA, srcB, op.Dest, op.NegA, false, op.WriteCC); 114 } 115 116 public static void FaddR(EmitterContext context) 117 { 118 InstFaddR op = context.GetOp<InstFaddR>(); 119 120 var srcA = GetSrcReg(context, op.SrcA); 121 var srcB = GetSrcReg(context, op.SrcB); 122 123 EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC); 124 } 125 126 public static void FaddI(EmitterContext context) 127 { 128 InstFaddI op = context.GetOp<InstFaddI>(); 129 130 var srcA = GetSrcReg(context, op.SrcA); 131 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20)); 132 133 EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC); 134 } 135 136 public static void FaddC(EmitterContext context) 137 { 138 InstFaddC op = context.GetOp<InstFaddC>(); 139 140 var srcA = GetSrcReg(context, op.SrcA); 141 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 142 143 EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, op.Sat, op.WriteCC); 144 } 145 146 public static void Fadd32i(EmitterContext context) 147 { 148 InstFadd32i op = context.GetOp<InstFadd32i>(); 149 150 var srcA = GetSrcReg(context, op.SrcA); 151 var srcB = GetSrcImm(context, op.Imm32); 152 153 EmitFadd(context, Instruction.FP32, srcA, srcB, op.Dest, op.NegA, op.NegB, op.AbsA, op.AbsB, false, op.WriteCC); 154 } 155 156 public static void FfmaR(EmitterContext context) 157 { 158 InstFfmaR op = context.GetOp<InstFfmaR>(); 159 160 var srcA = GetSrcReg(context, op.SrcA); 161 var srcB = GetSrcReg(context, op.SrcB); 162 var srcC = GetSrcReg(context, op.SrcC); 163 164 EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC); 165 } 166 167 public static void FfmaI(EmitterContext context) 168 { 169 InstFfmaI op = context.GetOp<InstFfmaI>(); 170 171 var srcA = GetSrcReg(context, op.SrcA); 172 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20)); 173 var srcC = GetSrcReg(context, op.SrcC); 174 175 EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC); 176 } 177 178 public static void FfmaC(EmitterContext context) 179 { 180 InstFfmaC op = context.GetOp<InstFfmaC>(); 181 182 var srcA = GetSrcReg(context, op.SrcA); 183 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 184 var srcC = GetSrcReg(context, op.SrcC); 185 186 EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC); 187 } 188 189 public static void FfmaRc(EmitterContext context) 190 { 191 InstFfmaRc op = context.GetOp<InstFfmaRc>(); 192 193 var srcA = GetSrcReg(context, op.SrcA); 194 var srcB = GetSrcReg(context, op.SrcC); 195 var srcC = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 196 197 EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC); 198 } 199 200 public static void Ffma32i(EmitterContext context) 201 { 202 InstFfma32i op = context.GetOp<InstFfma32i>(); 203 204 var srcA = GetSrcReg(context, op.SrcA); 205 var srcB = GetSrcImm(context, op.Imm32); 206 var srcC = GetSrcReg(context, op.Dest); 207 208 EmitFfma(context, Instruction.FP32, srcA, srcB, srcC, op.Dest, op.NegA, op.NegC, op.Sat, op.WriteCC); 209 } 210 211 public static void FmulR(EmitterContext context) 212 { 213 InstFmulR op = context.GetOp<InstFmulR>(); 214 215 var srcA = GetSrcReg(context, op.SrcA); 216 var srcB = GetSrcReg(context, op.SrcB); 217 218 EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC); 219 } 220 221 public static void FmulI(EmitterContext context) 222 { 223 InstFmulI op = context.GetOp<InstFmulI>(); 224 225 var srcA = GetSrcReg(context, op.SrcA); 226 var srcB = GetSrcImm(context, Imm20ToFloat(op.Imm20)); 227 228 EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC); 229 } 230 231 public static void FmulC(EmitterContext context) 232 { 233 InstFmulC op = context.GetOp<InstFmulC>(); 234 235 var srcA = GetSrcReg(context, op.SrcA); 236 var srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset); 237 238 EmitFmul(context, Instruction.FP32, op.Scale, srcA, srcB, op.Dest, op.NegA, op.Sat, op.WriteCC); 239 } 240 241 public static void Fmul32i(EmitterContext context) 242 { 243 InstFmul32i op = context.GetOp<InstFmul32i>(); 244 245 var srcA = GetSrcReg(context, op.SrcA); 246 var srcB = GetSrcImm(context, op.Imm32); 247 248 EmitFmul(context, Instruction.FP32, MultiplyScale.NoScale, srcA, srcB, op.Dest, false, op.Sat, op.WriteCC); 249 } 250 251 public static void Hadd2R(EmitterContext context) 252 { 253 InstHadd2R op = context.GetOp<InstHadd2R>(); 254 255 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 256 var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegB, op.AbsB); 257 258 EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat); 259 } 260 261 public static void Hadd2I(EmitterContext context) 262 { 263 InstHadd2I op = context.GetOp<InstHadd2I>(); 264 265 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 266 var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1); 267 268 EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat); 269 } 270 271 public static void Hadd2C(EmitterContext context) 272 { 273 InstHadd2C op = context.GetOp<InstHadd2C>(); 274 275 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 276 var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegB, op.AbsB); 277 278 EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: true, op.Dest, op.Sat); 279 } 280 281 public static void Hadd232i(EmitterContext context) 282 { 283 InstHadd232i op = context.GetOp<InstHadd232i>(); 284 285 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, false); 286 var srcB = GetHalfSrc(context, op.Imm); 287 288 EmitHadd2Hmul2(context, OFmt.F16, srcA, srcB, isAdd: true, op.Dest, op.Sat); 289 } 290 291 public static void Hfma2R(EmitterContext context) 292 { 293 InstHfma2R op = context.GetOp<InstHfma2R>(); 294 295 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false); 296 var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegA, false); 297 var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false); 298 299 EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat); 300 } 301 302 public static void Hfma2I(EmitterContext context) 303 { 304 InstHfma2I op = context.GetOp<InstHfma2I>(); 305 306 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false); 307 var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1); 308 var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false); 309 310 EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat); 311 } 312 313 public static void Hfma2C(EmitterContext context) 314 { 315 InstHfma2C op = context.GetOp<InstHfma2C>(); 316 317 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false); 318 var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegA, false); 319 var srcC = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegC, false); 320 321 EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat); 322 } 323 324 public static void Hfma2Rc(EmitterContext context) 325 { 326 InstHfma2Rc op = context.GetOp<InstHfma2Rc>(); 327 328 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false); 329 var srcB = GetHalfSrc(context, op.CSwizzle, op.SrcC, op.NegA, false); 330 var srcC = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegC, false); 331 332 EmitHfma2(context, op.OFmt, srcA, srcB, srcC, op.Dest, op.Sat); 333 } 334 335 public static void Hfma232i(EmitterContext context) 336 { 337 InstHfma232i op = context.GetOp<InstHfma232i>(); 338 339 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false); 340 var srcB = GetHalfSrc(context, op.Imm); 341 var srcC = GetHalfSrc(context, HalfSwizzle.F16, op.Dest, op.NegC, false); 342 343 EmitHfma2(context, OFmt.F16, srcA, srcB, srcC, op.Dest, saturate: false); 344 } 345 346 public static void Hmul2R(EmitterContext context) 347 { 348 InstHmul2R op = context.GetOp<InstHmul2R>(); 349 350 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, op.AbsA); 351 var srcB = GetHalfSrc(context, op.BSwizzle, op.SrcB, op.NegA, op.AbsB); 352 353 EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat); 354 } 355 356 public static void Hmul2I(EmitterContext context) 357 { 358 InstHmul2I op = context.GetOp<InstHmul2I>(); 359 360 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, op.NegA, op.AbsA); 361 var srcB = GetHalfSrc(context, op.BimmH0, op.BimmH1); 362 363 EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat); 364 } 365 366 public static void Hmul2C(EmitterContext context) 367 { 368 InstHmul2C op = context.GetOp<InstHmul2C>(); 369 370 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, op.AbsA); 371 var srcB = GetHalfSrc(context, HalfSwizzle.F32, op.CbufSlot, op.CbufOffset, op.NegA, op.AbsB); 372 373 EmitHadd2Hmul2(context, op.OFmt, srcA, srcB, isAdd: false, op.Dest, op.Sat); 374 } 375 376 public static void Hmul232i(EmitterContext context) 377 { 378 InstHmul232i op = context.GetOp<InstHmul232i>(); 379 380 var srcA = GetHalfSrc(context, op.ASwizzle, op.SrcA, false, false); 381 var srcB = GetHalfSrc(context, op.Imm32); 382 383 EmitHadd2Hmul2(context, OFmt.F16, srcA, srcB, isAdd: false, op.Dest, op.Sat); 384 } 385 386 private static void EmitFadd( 387 EmitterContext context, 388 Instruction fpType, 389 Operand srcA, 390 Operand srcB, 391 int rd, 392 bool negateA, 393 bool negateB, 394 bool absoluteA, 395 bool absoluteB, 396 bool saturate, 397 bool writeCC) 398 { 399 bool isFP64 = fpType == Instruction.FP64; 400 401 srcA = context.FPAbsNeg(srcA, absoluteA, negateA, fpType); 402 srcB = context.FPAbsNeg(srcB, absoluteB, negateB, fpType); 403 404 Operand res = context.FPSaturate(context.FPAdd(srcA, srcB, fpType), saturate, fpType); 405 406 SetDest(context, res, rd, isFP64); 407 408 SetFPZnFlags(context, res, writeCC, fpType); 409 } 410 411 private static void EmitFfma( 412 EmitterContext context, 413 Instruction fpType, 414 Operand srcA, 415 Operand srcB, 416 Operand srcC, 417 int rd, 418 bool negateB, 419 bool negateC, 420 bool saturate, 421 bool writeCC) 422 { 423 bool isFP64 = fpType == Instruction.FP64; 424 425 srcB = context.FPNegate(srcB, negateB, fpType); 426 srcC = context.FPNegate(srcC, negateC, fpType); 427 428 Operand res = context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC, fpType), saturate, fpType); 429 430 SetDest(context, res, rd, isFP64); 431 432 SetFPZnFlags(context, res, writeCC, fpType); 433 } 434 435 private static void EmitFmul( 436 EmitterContext context, 437 Instruction fpType, 438 MultiplyScale scale, 439 Operand srcA, 440 Operand srcB, 441 int rd, 442 bool negateB, 443 bool saturate, 444 bool writeCC) 445 { 446 bool isFP64 = fpType == Instruction.FP64; 447 448 srcB = context.FPNegate(srcB, negateB, fpType); 449 450 if (scale != MultiplyScale.NoScale) 451 { 452 Operand scaleConst = scale switch 453 { 454 MultiplyScale.D2 => ConstF(0.5f), 455 MultiplyScale.D4 => ConstF(0.25f), 456 MultiplyScale.D8 => ConstF(0.125f), 457 MultiplyScale.M2 => ConstF(2f), 458 MultiplyScale.M4 => ConstF(4f), 459 MultiplyScale.M8 => ConstF(8f), 460 _ => ConstF(1f), // Invalid, behave as if it had no scale. 461 }; 462 463 if (scaleConst.AsFloat() == 1f) 464 { 465 context.TranslatorContext.GpuAccessor.Log($"Invalid FP multiply scale \"{scale}\"."); 466 } 467 468 if (isFP64) 469 { 470 scaleConst = context.FP32ConvertToFP64(scaleConst); 471 } 472 473 srcA = context.FPMultiply(srcA, scaleConst, fpType); 474 } 475 476 Operand res = context.FPSaturate(context.FPMultiply(srcA, srcB, fpType), saturate, fpType); 477 478 SetDest(context, res, rd, isFP64); 479 480 SetFPZnFlags(context, res, writeCC, fpType); 481 } 482 483 private static void EmitHadd2Hmul2( 484 EmitterContext context, 485 OFmt swizzle, 486 Operand[] srcA, 487 Operand[] srcB, 488 bool isAdd, 489 int rd, 490 bool saturate) 491 { 492 Operand[] res = new Operand[2]; 493 494 for (int index = 0; index < res.Length; index++) 495 { 496 if (isAdd) 497 { 498 res[index] = context.FPAdd(srcA[index], srcB[index]); 499 } 500 else 501 { 502 res[index] = context.FPMultiply(srcA[index], srcB[index]); 503 } 504 505 res[index] = context.FPSaturate(res[index], saturate); 506 } 507 508 context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd)); 509 } 510 511 public static void EmitHfma2( 512 EmitterContext context, 513 OFmt swizzle, 514 Operand[] srcA, 515 Operand[] srcB, 516 Operand[] srcC, 517 int rd, 518 bool saturate) 519 { 520 Operand[] res = new Operand[2]; 521 522 for (int index = 0; index < res.Length; index++) 523 { 524 res[index] = context.FPFusedMultiplyAdd(srcA[index], srcB[index], srcC[index]); 525 res[index] = context.FPSaturate(res[index], saturate); 526 } 527 528 context.Copy(GetDest(rd), GetHalfPacked(context, swizzle, res, rd)); 529 } 530 } 531 }