diff options
Diffstat (limited to 'src/shader_recompiler/frontend/ir')
29 files changed, 6227 insertions, 0 deletions
diff --git a/src/shader_recompiler/frontend/ir/abstract_syntax_list.h b/src/shader_recompiler/frontend/ir/abstract_syntax_list.h new file mode 100644 index 000000000..b61773487 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/abstract_syntax_list.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | class Block; | ||
| 14 | |||
| 15 | struct AbstractSyntaxNode { | ||
| 16 | enum class Type { | ||
| 17 | Block, | ||
| 18 | If, | ||
| 19 | EndIf, | ||
| 20 | Loop, | ||
| 21 | Repeat, | ||
| 22 | Break, | ||
| 23 | Return, | ||
| 24 | Unreachable, | ||
| 25 | }; | ||
| 26 | union Data { | ||
| 27 | Block* block; | ||
| 28 | struct { | ||
| 29 | U1 cond; | ||
| 30 | Block* body; | ||
| 31 | Block* merge; | ||
| 32 | } if_node; | ||
| 33 | struct { | ||
| 34 | Block* merge; | ||
| 35 | } end_if; | ||
| 36 | struct { | ||
| 37 | Block* body; | ||
| 38 | Block* continue_block; | ||
| 39 | Block* merge; | ||
| 40 | } loop; | ||
| 41 | struct { | ||
| 42 | U1 cond; | ||
| 43 | Block* loop_header; | ||
| 44 | Block* merge; | ||
| 45 | } repeat; | ||
| 46 | struct { | ||
| 47 | U1 cond; | ||
| 48 | Block* merge; | ||
| 49 | Block* skip; | ||
| 50 | } break_node; | ||
| 51 | }; | ||
| 52 | |||
| 53 | Data data{}; | ||
| 54 | Type type{}; | ||
| 55 | }; | ||
| 56 | using AbstractSyntaxList = std::vector<AbstractSyntaxNode>; | ||
| 57 | |||
| 58 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/attribute.cpp b/src/shader_recompiler/frontend/ir/attribute.cpp new file mode 100644 index 000000000..4d0b8b8e5 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/attribute.cpp | |||
| @@ -0,0 +1,454 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <fmt/format.h> | ||
| 6 | |||
| 7 | #include "shader_recompiler/exception.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/attribute.h" | ||
| 9 | |||
| 10 | namespace Shader::IR { | ||
| 11 | |||
| 12 | bool IsGeneric(Attribute attribute) noexcept { | ||
| 13 | return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X; | ||
| 14 | } | ||
| 15 | |||
| 16 | u32 GenericAttributeIndex(Attribute attribute) { | ||
| 17 | if (!IsGeneric(attribute)) { | ||
| 18 | throw InvalidArgument("Attribute is not generic {}", attribute); | ||
| 19 | } | ||
| 20 | return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u; | ||
| 21 | } | ||
| 22 | |||
| 23 | u32 GenericAttributeElement(Attribute attribute) { | ||
| 24 | if (!IsGeneric(attribute)) { | ||
| 25 | throw InvalidArgument("Attribute is not generic {}", attribute); | ||
| 26 | } | ||
| 27 | return static_cast<u32>(attribute) % 4; | ||
| 28 | } | ||
| 29 | |||
| 30 | std::string NameOf(Attribute attribute) { | ||
| 31 | switch (attribute) { | ||
| 32 | case Attribute::PrimitiveId: | ||
| 33 | return "PrimitiveId"; | ||
| 34 | case Attribute::Layer: | ||
| 35 | return "Layer"; | ||
| 36 | case Attribute::ViewportIndex: | ||
| 37 | return "ViewportIndex"; | ||
| 38 | case Attribute::PointSize: | ||
| 39 | return "PointSize"; | ||
| 40 | case Attribute::PositionX: | ||
| 41 | return "Position.X"; | ||
| 42 | case Attribute::PositionY: | ||
| 43 | return "Position.Y"; | ||
| 44 | case Attribute::PositionZ: | ||
| 45 | return "Position.Z"; | ||
| 46 | case Attribute::PositionW: | ||
| 47 | return "Position.W"; | ||
| 48 | case Attribute::Generic0X: | ||
| 49 | return "Generic[0].X"; | ||
| 50 | case Attribute::Generic0Y: | ||
| 51 | return "Generic[0].Y"; | ||
| 52 | case Attribute::Generic0Z: | ||
| 53 | return "Generic[0].Z"; | ||
| 54 | case Attribute::Generic0W: | ||
| 55 | return "Generic[0].W"; | ||
| 56 | case Attribute::Generic1X: | ||
| 57 | return "Generic[1].X"; | ||
| 58 | case Attribute::Generic1Y: | ||
| 59 | return "Generic[1].Y"; | ||
| 60 | case Attribute::Generic1Z: | ||
| 61 | return "Generic[1].Z"; | ||
| 62 | case Attribute::Generic1W: | ||
| 63 | return "Generic[1].W"; | ||
| 64 | case Attribute::Generic2X: | ||
| 65 | return "Generic[2].X"; | ||
| 66 | case Attribute::Generic2Y: | ||
| 67 | return "Generic[2].Y"; | ||
| 68 | case Attribute::Generic2Z: | ||
| 69 | return "Generic[2].Z"; | ||
| 70 | case Attribute::Generic2W: | ||
| 71 | return "Generic[2].W"; | ||
| 72 | case Attribute::Generic3X: | ||
| 73 | return "Generic[3].X"; | ||
| 74 | case Attribute::Generic3Y: | ||
| 75 | return "Generic[3].Y"; | ||
| 76 | case Attribute::Generic3Z: | ||
| 77 | return "Generic[3].Z"; | ||
| 78 | case Attribute::Generic3W: | ||
| 79 | return "Generic[3].W"; | ||
| 80 | case Attribute::Generic4X: | ||
| 81 | return "Generic[4].X"; | ||
| 82 | case Attribute::Generic4Y: | ||
| 83 | return "Generic[4].Y"; | ||
| 84 | case Attribute::Generic4Z: | ||
| 85 | return "Generic[4].Z"; | ||
| 86 | case Attribute::Generic4W: | ||
| 87 | return "Generic[4].W"; | ||
| 88 | case Attribute::Generic5X: | ||
| 89 | return "Generic[5].X"; | ||
| 90 | case Attribute::Generic5Y: | ||
| 91 | return "Generic[5].Y"; | ||
| 92 | case Attribute::Generic5Z: | ||
| 93 | return "Generic[5].Z"; | ||
| 94 | case Attribute::Generic5W: | ||
| 95 | return "Generic[5].W"; | ||
| 96 | case Attribute::Generic6X: | ||
| 97 | return "Generic[6].X"; | ||
| 98 | case Attribute::Generic6Y: | ||
| 99 | return "Generic[6].Y"; | ||
| 100 | case Attribute::Generic6Z: | ||
| 101 | return "Generic[6].Z"; | ||
| 102 | case Attribute::Generic6W: | ||
| 103 | return "Generic[6].W"; | ||
| 104 | case Attribute::Generic7X: | ||
| 105 | return "Generic[7].X"; | ||
| 106 | case Attribute::Generic7Y: | ||
| 107 | return "Generic[7].Y"; | ||
| 108 | case Attribute::Generic7Z: | ||
| 109 | return "Generic[7].Z"; | ||
| 110 | case Attribute::Generic7W: | ||
| 111 | return "Generic[7].W"; | ||
| 112 | case Attribute::Generic8X: | ||
| 113 | return "Generic[8].X"; | ||
| 114 | case Attribute::Generic8Y: | ||
| 115 | return "Generic[8].Y"; | ||
| 116 | case Attribute::Generic8Z: | ||
| 117 | return "Generic[8].Z"; | ||
| 118 | case Attribute::Generic8W: | ||
| 119 | return "Generic[8].W"; | ||
| 120 | case Attribute::Generic9X: | ||
| 121 | return "Generic[9].X"; | ||
| 122 | case Attribute::Generic9Y: | ||
| 123 | return "Generic[9].Y"; | ||
| 124 | case Attribute::Generic9Z: | ||
| 125 | return "Generic[9].Z"; | ||
| 126 | case Attribute::Generic9W: | ||
| 127 | return "Generic[9].W"; | ||
| 128 | case Attribute::Generic10X: | ||
| 129 | return "Generic[10].X"; | ||
| 130 | case Attribute::Generic10Y: | ||
| 131 | return "Generic[10].Y"; | ||
| 132 | case Attribute::Generic10Z: | ||
| 133 | return "Generic[10].Z"; | ||
| 134 | case Attribute::Generic10W: | ||
| 135 | return "Generic[10].W"; | ||
| 136 | case Attribute::Generic11X: | ||
| 137 | return "Generic[11].X"; | ||
| 138 | case Attribute::Generic11Y: | ||
| 139 | return "Generic[11].Y"; | ||
| 140 | case Attribute::Generic11Z: | ||
| 141 | return "Generic[11].Z"; | ||
| 142 | case Attribute::Generic11W: | ||
| 143 | return "Generic[11].W"; | ||
| 144 | case Attribute::Generic12X: | ||
| 145 | return "Generic[12].X"; | ||
| 146 | case Attribute::Generic12Y: | ||
| 147 | return "Generic[12].Y"; | ||
| 148 | case Attribute::Generic12Z: | ||
| 149 | return "Generic[12].Z"; | ||
| 150 | case Attribute::Generic12W: | ||
| 151 | return "Generic[12].W"; | ||
| 152 | case Attribute::Generic13X: | ||
| 153 | return "Generic[13].X"; | ||
| 154 | case Attribute::Generic13Y: | ||
| 155 | return "Generic[13].Y"; | ||
| 156 | case Attribute::Generic13Z: | ||
| 157 | return "Generic[13].Z"; | ||
| 158 | case Attribute::Generic13W: | ||
| 159 | return "Generic[13].W"; | ||
| 160 | case Attribute::Generic14X: | ||
| 161 | return "Generic[14].X"; | ||
| 162 | case Attribute::Generic14Y: | ||
| 163 | return "Generic[14].Y"; | ||
| 164 | case Attribute::Generic14Z: | ||
| 165 | return "Generic[14].Z"; | ||
| 166 | case Attribute::Generic14W: | ||
| 167 | return "Generic[14].W"; | ||
| 168 | case Attribute::Generic15X: | ||
| 169 | return "Generic[15].X"; | ||
| 170 | case Attribute::Generic15Y: | ||
| 171 | return "Generic[15].Y"; | ||
| 172 | case Attribute::Generic15Z: | ||
| 173 | return "Generic[15].Z"; | ||
| 174 | case Attribute::Generic15W: | ||
| 175 | return "Generic[15].W"; | ||
| 176 | case Attribute::Generic16X: | ||
| 177 | return "Generic[16].X"; | ||
| 178 | case Attribute::Generic16Y: | ||
| 179 | return "Generic[16].Y"; | ||
| 180 | case Attribute::Generic16Z: | ||
| 181 | return "Generic[16].Z"; | ||
| 182 | case Attribute::Generic16W: | ||
| 183 | return "Generic[16].W"; | ||
| 184 | case Attribute::Generic17X: | ||
| 185 | return "Generic[17].X"; | ||
| 186 | case Attribute::Generic17Y: | ||
| 187 | return "Generic[17].Y"; | ||
| 188 | case Attribute::Generic17Z: | ||
| 189 | return "Generic[17].Z"; | ||
| 190 | case Attribute::Generic17W: | ||
| 191 | return "Generic[17].W"; | ||
| 192 | case Attribute::Generic18X: | ||
| 193 | return "Generic[18].X"; | ||
| 194 | case Attribute::Generic18Y: | ||
| 195 | return "Generic[18].Y"; | ||
| 196 | case Attribute::Generic18Z: | ||
| 197 | return "Generic[18].Z"; | ||
| 198 | case Attribute::Generic18W: | ||
| 199 | return "Generic[18].W"; | ||
| 200 | case Attribute::Generic19X: | ||
| 201 | return "Generic[19].X"; | ||
| 202 | case Attribute::Generic19Y: | ||
| 203 | return "Generic[19].Y"; | ||
| 204 | case Attribute::Generic19Z: | ||
| 205 | return "Generic[19].Z"; | ||
| 206 | case Attribute::Generic19W: | ||
| 207 | return "Generic[19].W"; | ||
| 208 | case Attribute::Generic20X: | ||
| 209 | return "Generic[20].X"; | ||
| 210 | case Attribute::Generic20Y: | ||
| 211 | return "Generic[20].Y"; | ||
| 212 | case Attribute::Generic20Z: | ||
| 213 | return "Generic[20].Z"; | ||
| 214 | case Attribute::Generic20W: | ||
| 215 | return "Generic[20].W"; | ||
| 216 | case Attribute::Generic21X: | ||
| 217 | return "Generic[21].X"; | ||
| 218 | case Attribute::Generic21Y: | ||
| 219 | return "Generic[21].Y"; | ||
| 220 | case Attribute::Generic21Z: | ||
| 221 | return "Generic[21].Z"; | ||
| 222 | case Attribute::Generic21W: | ||
| 223 | return "Generic[21].W"; | ||
| 224 | case Attribute::Generic22X: | ||
| 225 | return "Generic[22].X"; | ||
| 226 | case Attribute::Generic22Y: | ||
| 227 | return "Generic[22].Y"; | ||
| 228 | case Attribute::Generic22Z: | ||
| 229 | return "Generic[22].Z"; | ||
| 230 | case Attribute::Generic22W: | ||
| 231 | return "Generic[22].W"; | ||
| 232 | case Attribute::Generic23X: | ||
| 233 | return "Generic[23].X"; | ||
| 234 | case Attribute::Generic23Y: | ||
| 235 | return "Generic[23].Y"; | ||
| 236 | case Attribute::Generic23Z: | ||
| 237 | return "Generic[23].Z"; | ||
| 238 | case Attribute::Generic23W: | ||
| 239 | return "Generic[23].W"; | ||
| 240 | case Attribute::Generic24X: | ||
| 241 | return "Generic[24].X"; | ||
| 242 | case Attribute::Generic24Y: | ||
| 243 | return "Generic[24].Y"; | ||
| 244 | case Attribute::Generic24Z: | ||
| 245 | return "Generic[24].Z"; | ||
| 246 | case Attribute::Generic24W: | ||
| 247 | return "Generic[24].W"; | ||
| 248 | case Attribute::Generic25X: | ||
| 249 | return "Generic[25].X"; | ||
| 250 | case Attribute::Generic25Y: | ||
| 251 | return "Generic[25].Y"; | ||
| 252 | case Attribute::Generic25Z: | ||
| 253 | return "Generic[25].Z"; | ||
| 254 | case Attribute::Generic25W: | ||
| 255 | return "Generic[25].W"; | ||
| 256 | case Attribute::Generic26X: | ||
| 257 | return "Generic[26].X"; | ||
| 258 | case Attribute::Generic26Y: | ||
| 259 | return "Generic[26].Y"; | ||
| 260 | case Attribute::Generic26Z: | ||
| 261 | return "Generic[26].Z"; | ||
| 262 | case Attribute::Generic26W: | ||
| 263 | return "Generic[26].W"; | ||
| 264 | case Attribute::Generic27X: | ||
| 265 | return "Generic[27].X"; | ||
| 266 | case Attribute::Generic27Y: | ||
| 267 | return "Generic[27].Y"; | ||
| 268 | case Attribute::Generic27Z: | ||
| 269 | return "Generic[27].Z"; | ||
| 270 | case Attribute::Generic27W: | ||
| 271 | return "Generic[27].W"; | ||
| 272 | case Attribute::Generic28X: | ||
| 273 | return "Generic[28].X"; | ||
| 274 | case Attribute::Generic28Y: | ||
| 275 | return "Generic[28].Y"; | ||
| 276 | case Attribute::Generic28Z: | ||
| 277 | return "Generic[28].Z"; | ||
| 278 | case Attribute::Generic28W: | ||
| 279 | return "Generic[28].W"; | ||
| 280 | case Attribute::Generic29X: | ||
| 281 | return "Generic[29].X"; | ||
| 282 | case Attribute::Generic29Y: | ||
| 283 | return "Generic[29].Y"; | ||
| 284 | case Attribute::Generic29Z: | ||
| 285 | return "Generic[29].Z"; | ||
| 286 | case Attribute::Generic29W: | ||
| 287 | return "Generic[29].W"; | ||
| 288 | case Attribute::Generic30X: | ||
| 289 | return "Generic[30].X"; | ||
| 290 | case Attribute::Generic30Y: | ||
| 291 | return "Generic[30].Y"; | ||
| 292 | case Attribute::Generic30Z: | ||
| 293 | return "Generic[30].Z"; | ||
| 294 | case Attribute::Generic30W: | ||
| 295 | return "Generic[30].W"; | ||
| 296 | case Attribute::Generic31X: | ||
| 297 | return "Generic[31].X"; | ||
| 298 | case Attribute::Generic31Y: | ||
| 299 | return "Generic[31].Y"; | ||
| 300 | case Attribute::Generic31Z: | ||
| 301 | return "Generic[31].Z"; | ||
| 302 | case Attribute::Generic31W: | ||
| 303 | return "Generic[31].W"; | ||
| 304 | case Attribute::ColorFrontDiffuseR: | ||
| 305 | return "ColorFrontDiffuse.R"; | ||
| 306 | case Attribute::ColorFrontDiffuseG: | ||
| 307 | return "ColorFrontDiffuse.G"; | ||
| 308 | case Attribute::ColorFrontDiffuseB: | ||
| 309 | return "ColorFrontDiffuse.B"; | ||
| 310 | case Attribute::ColorFrontDiffuseA: | ||
| 311 | return "ColorFrontDiffuse.A"; | ||
| 312 | case Attribute::ColorFrontSpecularR: | ||
| 313 | return "ColorFrontSpecular.R"; | ||
| 314 | case Attribute::ColorFrontSpecularG: | ||
| 315 | return "ColorFrontSpecular.G"; | ||
| 316 | case Attribute::ColorFrontSpecularB: | ||
| 317 | return "ColorFrontSpecular.B"; | ||
| 318 | case Attribute::ColorFrontSpecularA: | ||
| 319 | return "ColorFrontSpecular.A"; | ||
| 320 | case Attribute::ColorBackDiffuseR: | ||
| 321 | return "ColorBackDiffuse.R"; | ||
| 322 | case Attribute::ColorBackDiffuseG: | ||
| 323 | return "ColorBackDiffuse.G"; | ||
| 324 | case Attribute::ColorBackDiffuseB: | ||
| 325 | return "ColorBackDiffuse.B"; | ||
| 326 | case Attribute::ColorBackDiffuseA: | ||
| 327 | return "ColorBackDiffuse.A"; | ||
| 328 | case Attribute::ColorBackSpecularR: | ||
| 329 | return "ColorBackSpecular.R"; | ||
| 330 | case Attribute::ColorBackSpecularG: | ||
| 331 | return "ColorBackSpecular.G"; | ||
| 332 | case Attribute::ColorBackSpecularB: | ||
| 333 | return "ColorBackSpecular.B"; | ||
| 334 | case Attribute::ColorBackSpecularA: | ||
| 335 | return "ColorBackSpecular.A"; | ||
| 336 | case Attribute::ClipDistance0: | ||
| 337 | return "ClipDistance[0]"; | ||
| 338 | case Attribute::ClipDistance1: | ||
| 339 | return "ClipDistance[1]"; | ||
| 340 | case Attribute::ClipDistance2: | ||
| 341 | return "ClipDistance[2]"; | ||
| 342 | case Attribute::ClipDistance3: | ||
| 343 | return "ClipDistance[3]"; | ||
| 344 | case Attribute::ClipDistance4: | ||
| 345 | return "ClipDistance[4]"; | ||
| 346 | case Attribute::ClipDistance5: | ||
| 347 | return "ClipDistance[5]"; | ||
| 348 | case Attribute::ClipDistance6: | ||
| 349 | return "ClipDistance[6]"; | ||
| 350 | case Attribute::ClipDistance7: | ||
| 351 | return "ClipDistance[7]"; | ||
| 352 | case Attribute::PointSpriteS: | ||
| 353 | return "PointSprite.S"; | ||
| 354 | case Attribute::PointSpriteT: | ||
| 355 | return "PointSprite.T"; | ||
| 356 | case Attribute::FogCoordinate: | ||
| 357 | return "FogCoordinate"; | ||
| 358 | case Attribute::TessellationEvaluationPointU: | ||
| 359 | return "TessellationEvaluationPoint.U"; | ||
| 360 | case Attribute::TessellationEvaluationPointV: | ||
| 361 | return "TessellationEvaluationPoint.V"; | ||
| 362 | case Attribute::InstanceId: | ||
| 363 | return "InstanceId"; | ||
| 364 | case Attribute::VertexId: | ||
| 365 | return "VertexId"; | ||
| 366 | case Attribute::FixedFncTexture0S: | ||
| 367 | return "FixedFncTexture[0].S"; | ||
| 368 | case Attribute::FixedFncTexture0T: | ||
| 369 | return "FixedFncTexture[0].T"; | ||
| 370 | case Attribute::FixedFncTexture0R: | ||
| 371 | return "FixedFncTexture[0].R"; | ||
| 372 | case Attribute::FixedFncTexture0Q: | ||
| 373 | return "FixedFncTexture[0].Q"; | ||
| 374 | case Attribute::FixedFncTexture1S: | ||
| 375 | return "FixedFncTexture[1].S"; | ||
| 376 | case Attribute::FixedFncTexture1T: | ||
| 377 | return "FixedFncTexture[1].T"; | ||
| 378 | case Attribute::FixedFncTexture1R: | ||
| 379 | return "FixedFncTexture[1].R"; | ||
| 380 | case Attribute::FixedFncTexture1Q: | ||
| 381 | return "FixedFncTexture[1].Q"; | ||
| 382 | case Attribute::FixedFncTexture2S: | ||
| 383 | return "FixedFncTexture[2].S"; | ||
| 384 | case Attribute::FixedFncTexture2T: | ||
| 385 | return "FixedFncTexture[2].T"; | ||
| 386 | case Attribute::FixedFncTexture2R: | ||
| 387 | return "FixedFncTexture[2].R"; | ||
| 388 | case Attribute::FixedFncTexture2Q: | ||
| 389 | return "FixedFncTexture[2].Q"; | ||
| 390 | case Attribute::FixedFncTexture3S: | ||
| 391 | return "FixedFncTexture[3].S"; | ||
| 392 | case Attribute::FixedFncTexture3T: | ||
| 393 | return "FixedFncTexture[3].T"; | ||
| 394 | case Attribute::FixedFncTexture3R: | ||
| 395 | return "FixedFncTexture[3].R"; | ||
| 396 | case Attribute::FixedFncTexture3Q: | ||
| 397 | return "FixedFncTexture[3].Q"; | ||
| 398 | case Attribute::FixedFncTexture4S: | ||
| 399 | return "FixedFncTexture[4].S"; | ||
| 400 | case Attribute::FixedFncTexture4T: | ||
| 401 | return "FixedFncTexture[4].T"; | ||
| 402 | case Attribute::FixedFncTexture4R: | ||
| 403 | return "FixedFncTexture[4].R"; | ||
| 404 | case Attribute::FixedFncTexture4Q: | ||
| 405 | return "FixedFncTexture[4].Q"; | ||
| 406 | case Attribute::FixedFncTexture5S: | ||
| 407 | return "FixedFncTexture[5].S"; | ||
| 408 | case Attribute::FixedFncTexture5T: | ||
| 409 | return "FixedFncTexture[5].T"; | ||
| 410 | case Attribute::FixedFncTexture5R: | ||
| 411 | return "FixedFncTexture[5].R"; | ||
| 412 | case Attribute::FixedFncTexture5Q: | ||
| 413 | return "FixedFncTexture[5].Q"; | ||
| 414 | case Attribute::FixedFncTexture6S: | ||
| 415 | return "FixedFncTexture[6].S"; | ||
| 416 | case Attribute::FixedFncTexture6T: | ||
| 417 | return "FixedFncTexture[6].T"; | ||
| 418 | case Attribute::FixedFncTexture6R: | ||
| 419 | return "FixedFncTexture[6].R"; | ||
| 420 | case Attribute::FixedFncTexture6Q: | ||
| 421 | return "FixedFncTexture[6].Q"; | ||
| 422 | case Attribute::FixedFncTexture7S: | ||
| 423 | return "FixedFncTexture[7].S"; | ||
| 424 | case Attribute::FixedFncTexture7T: | ||
| 425 | return "FixedFncTexture[7].T"; | ||
| 426 | case Attribute::FixedFncTexture7R: | ||
| 427 | return "FixedFncTexture[7].R"; | ||
| 428 | case Attribute::FixedFncTexture7Q: | ||
| 429 | return "FixedFncTexture[7].Q"; | ||
| 430 | case Attribute::FixedFncTexture8S: | ||
| 431 | return "FixedFncTexture[8].S"; | ||
| 432 | case Attribute::FixedFncTexture8T: | ||
| 433 | return "FixedFncTexture[8].T"; | ||
| 434 | case Attribute::FixedFncTexture8R: | ||
| 435 | return "FixedFncTexture[8].R"; | ||
| 436 | case Attribute::FixedFncTexture8Q: | ||
| 437 | return "FixedFncTexture[8].Q"; | ||
| 438 | case Attribute::FixedFncTexture9S: | ||
| 439 | return "FixedFncTexture[9].S"; | ||
| 440 | case Attribute::FixedFncTexture9T: | ||
| 441 | return "FixedFncTexture[9].T"; | ||
| 442 | case Attribute::FixedFncTexture9R: | ||
| 443 | return "FixedFncTexture[9].R"; | ||
| 444 | case Attribute::FixedFncTexture9Q: | ||
| 445 | return "FixedFncTexture[9].Q"; | ||
| 446 | case Attribute::ViewportMask: | ||
| 447 | return "ViewportMask"; | ||
| 448 | case Attribute::FrontFace: | ||
| 449 | return "FrontFace"; | ||
| 450 | } | ||
| 451 | return fmt::format("<reserved attribute {}>", static_cast<int>(attribute)); | ||
| 452 | } | ||
| 453 | |||
| 454 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h new file mode 100644 index 000000000..ca1199494 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/attribute.h | |||
| @@ -0,0 +1,250 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | enum class Attribute : u64 { | ||
| 14 | PrimitiveId = 24, | ||
| 15 | Layer = 25, | ||
| 16 | ViewportIndex = 26, | ||
| 17 | PointSize = 27, | ||
| 18 | PositionX = 28, | ||
| 19 | PositionY = 29, | ||
| 20 | PositionZ = 30, | ||
| 21 | PositionW = 31, | ||
| 22 | Generic0X = 32, | ||
| 23 | Generic0Y = 33, | ||
| 24 | Generic0Z = 34, | ||
| 25 | Generic0W = 35, | ||
| 26 | Generic1X = 36, | ||
| 27 | Generic1Y = 37, | ||
| 28 | Generic1Z = 38, | ||
| 29 | Generic1W = 39, | ||
| 30 | Generic2X = 40, | ||
| 31 | Generic2Y = 41, | ||
| 32 | Generic2Z = 42, | ||
| 33 | Generic2W = 43, | ||
| 34 | Generic3X = 44, | ||
| 35 | Generic3Y = 45, | ||
| 36 | Generic3Z = 46, | ||
| 37 | Generic3W = 47, | ||
| 38 | Generic4X = 48, | ||
| 39 | Generic4Y = 49, | ||
| 40 | Generic4Z = 50, | ||
| 41 | Generic4W = 51, | ||
| 42 | Generic5X = 52, | ||
| 43 | Generic5Y = 53, | ||
| 44 | Generic5Z = 54, | ||
| 45 | Generic5W = 55, | ||
| 46 | Generic6X = 56, | ||
| 47 | Generic6Y = 57, | ||
| 48 | Generic6Z = 58, | ||
| 49 | Generic6W = 59, | ||
| 50 | Generic7X = 60, | ||
| 51 | Generic7Y = 61, | ||
| 52 | Generic7Z = 62, | ||
| 53 | Generic7W = 63, | ||
| 54 | Generic8X = 64, | ||
| 55 | Generic8Y = 65, | ||
| 56 | Generic8Z = 66, | ||
| 57 | Generic8W = 67, | ||
| 58 | Generic9X = 68, | ||
| 59 | Generic9Y = 69, | ||
| 60 | Generic9Z = 70, | ||
| 61 | Generic9W = 71, | ||
| 62 | Generic10X = 72, | ||
| 63 | Generic10Y = 73, | ||
| 64 | Generic10Z = 74, | ||
| 65 | Generic10W = 75, | ||
| 66 | Generic11X = 76, | ||
| 67 | Generic11Y = 77, | ||
| 68 | Generic11Z = 78, | ||
| 69 | Generic11W = 79, | ||
| 70 | Generic12X = 80, | ||
| 71 | Generic12Y = 81, | ||
| 72 | Generic12Z = 82, | ||
| 73 | Generic12W = 83, | ||
| 74 | Generic13X = 84, | ||
| 75 | Generic13Y = 85, | ||
| 76 | Generic13Z = 86, | ||
| 77 | Generic13W = 87, | ||
| 78 | Generic14X = 88, | ||
| 79 | Generic14Y = 89, | ||
| 80 | Generic14Z = 90, | ||
| 81 | Generic14W = 91, | ||
| 82 | Generic15X = 92, | ||
| 83 | Generic15Y = 93, | ||
| 84 | Generic15Z = 94, | ||
| 85 | Generic15W = 95, | ||
| 86 | Generic16X = 96, | ||
| 87 | Generic16Y = 97, | ||
| 88 | Generic16Z = 98, | ||
| 89 | Generic16W = 99, | ||
| 90 | Generic17X = 100, | ||
| 91 | Generic17Y = 101, | ||
| 92 | Generic17Z = 102, | ||
| 93 | Generic17W = 103, | ||
| 94 | Generic18X = 104, | ||
| 95 | Generic18Y = 105, | ||
| 96 | Generic18Z = 106, | ||
| 97 | Generic18W = 107, | ||
| 98 | Generic19X = 108, | ||
| 99 | Generic19Y = 109, | ||
| 100 | Generic19Z = 110, | ||
| 101 | Generic19W = 111, | ||
| 102 | Generic20X = 112, | ||
| 103 | Generic20Y = 113, | ||
| 104 | Generic20Z = 114, | ||
| 105 | Generic20W = 115, | ||
| 106 | Generic21X = 116, | ||
| 107 | Generic21Y = 117, | ||
| 108 | Generic21Z = 118, | ||
| 109 | Generic21W = 119, | ||
| 110 | Generic22X = 120, | ||
| 111 | Generic22Y = 121, | ||
| 112 | Generic22Z = 122, | ||
| 113 | Generic22W = 123, | ||
| 114 | Generic23X = 124, | ||
| 115 | Generic23Y = 125, | ||
| 116 | Generic23Z = 126, | ||
| 117 | Generic23W = 127, | ||
| 118 | Generic24X = 128, | ||
| 119 | Generic24Y = 129, | ||
| 120 | Generic24Z = 130, | ||
| 121 | Generic24W = 131, | ||
| 122 | Generic25X = 132, | ||
| 123 | Generic25Y = 133, | ||
| 124 | Generic25Z = 134, | ||
| 125 | Generic25W = 135, | ||
| 126 | Generic26X = 136, | ||
| 127 | Generic26Y = 137, | ||
| 128 | Generic26Z = 138, | ||
| 129 | Generic26W = 139, | ||
| 130 | Generic27X = 140, | ||
| 131 | Generic27Y = 141, | ||
| 132 | Generic27Z = 142, | ||
| 133 | Generic27W = 143, | ||
| 134 | Generic28X = 144, | ||
| 135 | Generic28Y = 145, | ||
| 136 | Generic28Z = 146, | ||
| 137 | Generic28W = 147, | ||
| 138 | Generic29X = 148, | ||
| 139 | Generic29Y = 149, | ||
| 140 | Generic29Z = 150, | ||
| 141 | Generic29W = 151, | ||
| 142 | Generic30X = 152, | ||
| 143 | Generic30Y = 153, | ||
| 144 | Generic30Z = 154, | ||
| 145 | Generic30W = 155, | ||
| 146 | Generic31X = 156, | ||
| 147 | Generic31Y = 157, | ||
| 148 | Generic31Z = 158, | ||
| 149 | Generic31W = 159, | ||
| 150 | ColorFrontDiffuseR = 160, | ||
| 151 | ColorFrontDiffuseG = 161, | ||
| 152 | ColorFrontDiffuseB = 162, | ||
| 153 | ColorFrontDiffuseA = 163, | ||
| 154 | ColorFrontSpecularR = 164, | ||
| 155 | ColorFrontSpecularG = 165, | ||
| 156 | ColorFrontSpecularB = 166, | ||
| 157 | ColorFrontSpecularA = 167, | ||
| 158 | ColorBackDiffuseR = 168, | ||
| 159 | ColorBackDiffuseG = 169, | ||
| 160 | ColorBackDiffuseB = 170, | ||
| 161 | ColorBackDiffuseA = 171, | ||
| 162 | ColorBackSpecularR = 172, | ||
| 163 | ColorBackSpecularG = 173, | ||
| 164 | ColorBackSpecularB = 174, | ||
| 165 | ColorBackSpecularA = 175, | ||
| 166 | ClipDistance0 = 176, | ||
| 167 | ClipDistance1 = 177, | ||
| 168 | ClipDistance2 = 178, | ||
| 169 | ClipDistance3 = 179, | ||
| 170 | ClipDistance4 = 180, | ||
| 171 | ClipDistance5 = 181, | ||
| 172 | ClipDistance6 = 182, | ||
| 173 | ClipDistance7 = 183, | ||
| 174 | PointSpriteS = 184, | ||
| 175 | PointSpriteT = 185, | ||
| 176 | FogCoordinate = 186, | ||
| 177 | TessellationEvaluationPointU = 188, | ||
| 178 | TessellationEvaluationPointV = 189, | ||
| 179 | InstanceId = 190, | ||
| 180 | VertexId = 191, | ||
| 181 | FixedFncTexture0S = 192, | ||
| 182 | FixedFncTexture0T = 193, | ||
| 183 | FixedFncTexture0R = 194, | ||
| 184 | FixedFncTexture0Q = 195, | ||
| 185 | FixedFncTexture1S = 196, | ||
| 186 | FixedFncTexture1T = 197, | ||
| 187 | FixedFncTexture1R = 198, | ||
| 188 | FixedFncTexture1Q = 199, | ||
| 189 | FixedFncTexture2S = 200, | ||
| 190 | FixedFncTexture2T = 201, | ||
| 191 | FixedFncTexture2R = 202, | ||
| 192 | FixedFncTexture2Q = 203, | ||
| 193 | FixedFncTexture3S = 204, | ||
| 194 | FixedFncTexture3T = 205, | ||
| 195 | FixedFncTexture3R = 206, | ||
| 196 | FixedFncTexture3Q = 207, | ||
| 197 | FixedFncTexture4S = 208, | ||
| 198 | FixedFncTexture4T = 209, | ||
| 199 | FixedFncTexture4R = 210, | ||
| 200 | FixedFncTexture4Q = 211, | ||
| 201 | FixedFncTexture5S = 212, | ||
| 202 | FixedFncTexture5T = 213, | ||
| 203 | FixedFncTexture5R = 214, | ||
| 204 | FixedFncTexture5Q = 215, | ||
| 205 | FixedFncTexture6S = 216, | ||
| 206 | FixedFncTexture6T = 217, | ||
| 207 | FixedFncTexture6R = 218, | ||
| 208 | FixedFncTexture6Q = 219, | ||
| 209 | FixedFncTexture7S = 220, | ||
| 210 | FixedFncTexture7T = 221, | ||
| 211 | FixedFncTexture7R = 222, | ||
| 212 | FixedFncTexture7Q = 223, | ||
| 213 | FixedFncTexture8S = 224, | ||
| 214 | FixedFncTexture8T = 225, | ||
| 215 | FixedFncTexture8R = 226, | ||
| 216 | FixedFncTexture8Q = 227, | ||
| 217 | FixedFncTexture9S = 228, | ||
| 218 | FixedFncTexture9T = 229, | ||
| 219 | FixedFncTexture9R = 230, | ||
| 220 | FixedFncTexture9Q = 231, | ||
| 221 | ViewportMask = 232, | ||
| 222 | FrontFace = 255, | ||
| 223 | }; | ||
| 224 | |||
| 225 | constexpr size_t NUM_GENERICS = 32; | ||
| 226 | |||
| 227 | [[nodiscard]] bool IsGeneric(Attribute attribute) noexcept; | ||
| 228 | |||
| 229 | [[nodiscard]] u32 GenericAttributeIndex(Attribute attribute); | ||
| 230 | |||
| 231 | [[nodiscard]] u32 GenericAttributeElement(Attribute attribute); | ||
| 232 | |||
| 233 | [[nodiscard]] std::string NameOf(Attribute attribute); | ||
| 234 | |||
| 235 | [[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept { | ||
| 236 | return static_cast<IR::Attribute>(static_cast<size_t>(attribute) + value); | ||
| 237 | } | ||
| 238 | |||
| 239 | } // namespace Shader::IR | ||
| 240 | |||
| 241 | template <> | ||
| 242 | struct fmt::formatter<Shader::IR::Attribute> { | ||
| 243 | constexpr auto parse(format_parse_context& ctx) { | ||
| 244 | return ctx.begin(); | ||
| 245 | } | ||
| 246 | template <typename FormatContext> | ||
| 247 | auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) { | ||
| 248 | return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute)); | ||
| 249 | } | ||
| 250 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp new file mode 100644 index 000000000..7c08b25ce --- /dev/null +++ b/src/shader_recompiler/frontend/ir/basic_block.cpp | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <initializer_list> | ||
| 7 | #include <map> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/bit_cast.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 13 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 14 | |||
| 15 | namespace Shader::IR { | ||
| 16 | |||
| 17 | Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {} | ||
| 18 | |||
| 19 | Block::~Block() = default; | ||
| 20 | |||
| 21 | void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) { | ||
| 22 | PrependNewInst(end(), op, args); | ||
| 23 | } | ||
| 24 | |||
| 25 | Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, | ||
| 26 | std::initializer_list<Value> args, u32 flags) { | ||
| 27 | Inst* const inst{inst_pool->Create(op, flags)}; | ||
| 28 | const auto result_it{instructions.insert(insertion_point, *inst)}; | ||
| 29 | |||
| 30 | if (inst->NumArgs() != args.size()) { | ||
| 31 | throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op); | ||
| 32 | } | ||
| 33 | std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable { | ||
| 34 | inst->SetArg(index, arg); | ||
| 35 | ++index; | ||
| 36 | }); | ||
| 37 | return result_it; | ||
| 38 | } | ||
| 39 | |||
| 40 | void Block::AddBranch(Block* block) { | ||
| 41 | if (std::ranges::find(imm_successors, block) != imm_successors.end()) { | ||
| 42 | throw LogicError("Successor already inserted"); | ||
| 43 | } | ||
| 44 | if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) { | ||
| 45 | throw LogicError("Predecessor already inserted"); | ||
| 46 | } | ||
| 47 | imm_successors.push_back(block); | ||
| 48 | block->imm_predecessors.push_back(this); | ||
| 49 | } | ||
| 50 | |||
| 51 | static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index, | ||
| 52 | Block* block) { | ||
| 53 | if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) { | ||
| 54 | return fmt::format("{{Block ${}}}", it->second); | ||
| 55 | } | ||
| 56 | return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block)); | ||
| 57 | } | ||
| 58 | |||
| 59 | static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index, | ||
| 60 | const Inst* inst) { | ||
| 61 | const auto [it, is_inserted]{inst_to_index.emplace(inst, inst_index + 1)}; | ||
| 62 | if (is_inserted) { | ||
| 63 | ++inst_index; | ||
| 64 | } | ||
| 65 | return it->second; | ||
| 66 | } | ||
| 67 | |||
| 68 | static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index, | ||
| 69 | const Value& arg) { | ||
| 70 | if (arg.IsEmpty()) { | ||
| 71 | return "<null>"; | ||
| 72 | } | ||
| 73 | if (!arg.IsImmediate() || arg.IsIdentity()) { | ||
| 74 | return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst())); | ||
| 75 | } | ||
| 76 | switch (arg.Type()) { | ||
| 77 | case Type::U1: | ||
| 78 | return fmt::format("#{}", arg.U1() ? "true" : "false"); | ||
| 79 | case Type::U8: | ||
| 80 | return fmt::format("#{}", arg.U8()); | ||
| 81 | case Type::U16: | ||
| 82 | return fmt::format("#{}", arg.U16()); | ||
| 83 | case Type::U32: | ||
| 84 | return fmt::format("#{}", arg.U32()); | ||
| 85 | case Type::U64: | ||
| 86 | return fmt::format("#{}", arg.U64()); | ||
| 87 | case Type::F32: | ||
| 88 | return fmt::format("#{}", arg.F32()); | ||
| 89 | case Type::Reg: | ||
| 90 | return fmt::format("{}", arg.Reg()); | ||
| 91 | case Type::Pred: | ||
| 92 | return fmt::format("{}", arg.Pred()); | ||
| 93 | case Type::Attribute: | ||
| 94 | return fmt::format("{}", arg.Attribute()); | ||
| 95 | default: | ||
| 96 | return "<unknown immediate type>"; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string DumpBlock(const Block& block) { | ||
| 101 | size_t inst_index{0}; | ||
| 102 | std::map<const Inst*, size_t> inst_to_index; | ||
| 103 | return DumpBlock(block, {}, inst_to_index, inst_index); | ||
| 104 | } | ||
| 105 | |||
| 106 | std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index, | ||
| 107 | std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) { | ||
| 108 | std::string ret{"Block"}; | ||
| 109 | if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) { | ||
| 110 | ret += fmt::format(" ${}", it->second); | ||
| 111 | } | ||
| 112 | ret += '\n'; | ||
| 113 | for (const Inst& inst : block) { | ||
| 114 | const Opcode op{inst.GetOpcode()}; | ||
| 115 | ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst)); | ||
| 116 | if (TypeOf(op) != Type::Void) { | ||
| 117 | ret += fmt::format("%{:<5} = {}", InstIndex(inst_to_index, inst_index, &inst), op); | ||
| 118 | } else { | ||
| 119 | ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces | ||
| 120 | } | ||
| 121 | const size_t arg_count{inst.NumArgs()}; | ||
| 122 | for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { | ||
| 123 | const Value arg{inst.Arg(arg_index)}; | ||
| 124 | const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)}; | ||
| 125 | ret += arg_index != 0 ? ", " : " "; | ||
| 126 | if (op == Opcode::Phi) { | ||
| 127 | ret += fmt::format("[ {}, {} ]", arg_str, | ||
| 128 | BlockToIndex(block_to_index, inst.PhiBlock(arg_index))); | ||
| 129 | } else { | ||
| 130 | ret += arg_str; | ||
| 131 | } | ||
| 132 | if (op != Opcode::Phi) { | ||
| 133 | const Type actual_type{arg.Type()}; | ||
| 134 | const Type expected_type{ArgTypeOf(op, arg_index)}; | ||
| 135 | if (!AreTypesCompatible(actual_type, expected_type)) { | ||
| 136 | ret += fmt::format("<type error: {} != {}>", actual_type, expected_type); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | if (TypeOf(op) != Type::Void) { | ||
| 141 | ret += fmt::format(" (uses: {})\n", inst.UseCount()); | ||
| 142 | } else { | ||
| 143 | ret += '\n'; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | return ret; | ||
| 147 | } | ||
| 148 | |||
| 149 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/basic_block.h b/src/shader_recompiler/frontend/ir/basic_block.h new file mode 100644 index 000000000..7e134b4c7 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/basic_block.h | |||
| @@ -0,0 +1,185 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <initializer_list> | ||
| 8 | #include <map> | ||
| 9 | #include <span> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | #include <boost/intrusive/list.hpp> | ||
| 13 | |||
| 14 | #include "common/bit_cast.h" | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "shader_recompiler/frontend/ir/condition.h" | ||
| 17 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 18 | #include "shader_recompiler/object_pool.h" | ||
| 19 | |||
| 20 | namespace Shader::IR { | ||
| 21 | |||
| 22 | class Block { | ||
| 23 | public: | ||
| 24 | using InstructionList = boost::intrusive::list<Inst>; | ||
| 25 | using size_type = InstructionList::size_type; | ||
| 26 | using iterator = InstructionList::iterator; | ||
| 27 | using const_iterator = InstructionList::const_iterator; | ||
| 28 | using reverse_iterator = InstructionList::reverse_iterator; | ||
| 29 | using const_reverse_iterator = InstructionList::const_reverse_iterator; | ||
| 30 | |||
| 31 | explicit Block(ObjectPool<Inst>& inst_pool_); | ||
| 32 | ~Block(); | ||
| 33 | |||
| 34 | Block(const Block&) = delete; | ||
| 35 | Block& operator=(const Block&) = delete; | ||
| 36 | |||
| 37 | Block(Block&&) = default; | ||
| 38 | Block& operator=(Block&&) = default; | ||
| 39 | |||
| 40 | /// Appends a new instruction to the end of this basic block. | ||
| 41 | void AppendNewInst(Opcode op, std::initializer_list<Value> args); | ||
| 42 | |||
| 43 | /// Prepends a new instruction to this basic block before the insertion point. | ||
| 44 | iterator PrependNewInst(iterator insertion_point, Opcode op, | ||
| 45 | std::initializer_list<Value> args = {}, u32 flags = 0); | ||
| 46 | |||
| 47 | /// Adds a new branch to this basic block. | ||
| 48 | void AddBranch(Block* block); | ||
| 49 | |||
| 50 | /// Gets a mutable reference to the instruction list for this basic block. | ||
| 51 | [[nodiscard]] InstructionList& Instructions() noexcept { | ||
| 52 | return instructions; | ||
| 53 | } | ||
| 54 | /// Gets an immutable reference to the instruction list for this basic block. | ||
| 55 | [[nodiscard]] const InstructionList& Instructions() const noexcept { | ||
| 56 | return instructions; | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Gets an immutable span to the immediate predecessors. | ||
| 60 | [[nodiscard]] std::span<Block* const> ImmPredecessors() const noexcept { | ||
| 61 | return imm_predecessors; | ||
| 62 | } | ||
| 63 | /// Gets an immutable span to the immediate successors. | ||
| 64 | [[nodiscard]] std::span<Block* const> ImmSuccessors() const noexcept { | ||
| 65 | return imm_successors; | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Intrusively store the host definition of this instruction. | ||
| 69 | template <typename DefinitionType> | ||
| 70 | void SetDefinition(DefinitionType def) { | ||
| 71 | definition = Common::BitCast<u32>(def); | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Return the intrusively stored host definition of this instruction. | ||
| 75 | template <typename DefinitionType> | ||
| 76 | [[nodiscard]] DefinitionType Definition() const noexcept { | ||
| 77 | return Common::BitCast<DefinitionType>(definition); | ||
| 78 | } | ||
| 79 | |||
| 80 | void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept { | ||
| 81 | ssa_reg_values[RegIndex(reg)] = value; | ||
| 82 | } | ||
| 83 | const Value& SsaRegValue(IR::Reg reg) const noexcept { | ||
| 84 | return ssa_reg_values[RegIndex(reg)]; | ||
| 85 | } | ||
| 86 | |||
| 87 | void SsaSeal() noexcept { | ||
| 88 | is_ssa_sealed = true; | ||
| 89 | } | ||
| 90 | [[nodiscard]] bool IsSsaSealed() const noexcept { | ||
| 91 | return is_ssa_sealed; | ||
| 92 | } | ||
| 93 | |||
| 94 | [[nodiscard]] bool empty() const { | ||
| 95 | return instructions.empty(); | ||
| 96 | } | ||
| 97 | [[nodiscard]] size_type size() const { | ||
| 98 | return instructions.size(); | ||
| 99 | } | ||
| 100 | |||
| 101 | [[nodiscard]] Inst& front() { | ||
| 102 | return instructions.front(); | ||
| 103 | } | ||
| 104 | [[nodiscard]] const Inst& front() const { | ||
| 105 | return instructions.front(); | ||
| 106 | } | ||
| 107 | |||
| 108 | [[nodiscard]] Inst& back() { | ||
| 109 | return instructions.back(); | ||
| 110 | } | ||
| 111 | [[nodiscard]] const Inst& back() const { | ||
| 112 | return instructions.back(); | ||
| 113 | } | ||
| 114 | |||
| 115 | [[nodiscard]] iterator begin() { | ||
| 116 | return instructions.begin(); | ||
| 117 | } | ||
| 118 | [[nodiscard]] const_iterator begin() const { | ||
| 119 | return instructions.begin(); | ||
| 120 | } | ||
| 121 | [[nodiscard]] iterator end() { | ||
| 122 | return instructions.end(); | ||
| 123 | } | ||
| 124 | [[nodiscard]] const_iterator end() const { | ||
| 125 | return instructions.end(); | ||
| 126 | } | ||
| 127 | |||
| 128 | [[nodiscard]] reverse_iterator rbegin() { | ||
| 129 | return instructions.rbegin(); | ||
| 130 | } | ||
| 131 | [[nodiscard]] const_reverse_iterator rbegin() const { | ||
| 132 | return instructions.rbegin(); | ||
| 133 | } | ||
| 134 | [[nodiscard]] reverse_iterator rend() { | ||
| 135 | return instructions.rend(); | ||
| 136 | } | ||
| 137 | [[nodiscard]] const_reverse_iterator rend() const { | ||
| 138 | return instructions.rend(); | ||
| 139 | } | ||
| 140 | |||
| 141 | [[nodiscard]] const_iterator cbegin() const { | ||
| 142 | return instructions.cbegin(); | ||
| 143 | } | ||
| 144 | [[nodiscard]] const_iterator cend() const { | ||
| 145 | return instructions.cend(); | ||
| 146 | } | ||
| 147 | |||
| 148 | [[nodiscard]] const_reverse_iterator crbegin() const { | ||
| 149 | return instructions.crbegin(); | ||
| 150 | } | ||
| 151 | [[nodiscard]] const_reverse_iterator crend() const { | ||
| 152 | return instructions.crend(); | ||
| 153 | } | ||
| 154 | |||
| 155 | private: | ||
| 156 | /// Memory pool for instruction list | ||
| 157 | ObjectPool<Inst>* inst_pool; | ||
| 158 | |||
| 159 | /// List of instructions in this block | ||
| 160 | InstructionList instructions; | ||
| 161 | |||
| 162 | /// Block immediate predecessors | ||
| 163 | std::vector<Block*> imm_predecessors; | ||
| 164 | /// Block immediate successors | ||
| 165 | std::vector<Block*> imm_successors; | ||
| 166 | |||
| 167 | /// Intrusively store the value of a register in the block. | ||
| 168 | std::array<Value, NUM_REGS> ssa_reg_values; | ||
| 169 | /// Intrusively store if the block is sealed in the SSA pass. | ||
| 170 | bool is_ssa_sealed{false}; | ||
| 171 | |||
| 172 | /// Intrusively stored host definition of this block. | ||
| 173 | u32 definition{}; | ||
| 174 | }; | ||
| 175 | |||
| 176 | using BlockList = std::vector<Block*>; | ||
| 177 | |||
| 178 | [[nodiscard]] std::string DumpBlock(const Block& block); | ||
| 179 | |||
| 180 | [[nodiscard]] std::string DumpBlock(const Block& block, | ||
| 181 | const std::map<const Block*, size_t>& block_to_index, | ||
| 182 | std::map<const Inst*, size_t>& inst_to_index, | ||
| 183 | size_t& inst_index); | ||
| 184 | |||
| 185 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/breadth_first_search.h b/src/shader_recompiler/frontend/ir/breadth_first_search.h new file mode 100644 index 000000000..a52ccbd58 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/breadth_first_search.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | #include <type_traits> | ||
| 9 | #include <queue> | ||
| 10 | |||
| 11 | #include <boost/container/small_vector.hpp> | ||
| 12 | |||
| 13 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 14 | |||
| 15 | namespace Shader::IR { | ||
| 16 | |||
| 17 | template <typename Pred> | ||
| 18 | auto BreadthFirstSearch(const Value& value, Pred&& pred) | ||
| 19 | -> std::invoke_result_t<Pred, const Inst*> { | ||
| 20 | if (value.IsImmediate()) { | ||
| 21 | // Nothing to do with immediates | ||
| 22 | return std::nullopt; | ||
| 23 | } | ||
| 24 | // Breadth-first search visiting the right most arguments first | ||
| 25 | // Small vector has been determined from shaders in Super Smash Bros. Ultimate | ||
| 26 | boost::container::small_vector<const Inst*, 2> visited; | ||
| 27 | std::queue<const Inst*> queue; | ||
| 28 | queue.push(value.InstRecursive()); | ||
| 29 | |||
| 30 | while (!queue.empty()) { | ||
| 31 | // Pop one instruction from the queue | ||
| 32 | const Inst* const inst{queue.front()}; | ||
| 33 | queue.pop(); | ||
| 34 | if (const std::optional result = pred(inst)) { | ||
| 35 | // This is the instruction we were looking for | ||
| 36 | return result; | ||
| 37 | } | ||
| 38 | // Visit the right most arguments first | ||
| 39 | for (size_t arg = inst->NumArgs(); arg--;) { | ||
| 40 | const Value arg_value{inst->Arg(arg)}; | ||
| 41 | if (arg_value.IsImmediate()) { | ||
| 42 | continue; | ||
| 43 | } | ||
| 44 | // Queue instruction if it hasn't been visited | ||
| 45 | const Inst* const arg_inst{arg_value.InstRecursive()}; | ||
| 46 | if (std::ranges::find(visited, arg_inst) == visited.end()) { | ||
| 47 | visited.push_back(arg_inst); | ||
| 48 | queue.push(arg_inst); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | // SSA tree has been traversed and the result hasn't been found | ||
| 53 | return std::nullopt; | ||
| 54 | } | ||
| 55 | |||
| 56 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/condition.cpp b/src/shader_recompiler/frontend/ir/condition.cpp new file mode 100644 index 000000000..fc18ea2a2 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/condition.cpp | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "shader_recompiler/frontend/ir/condition.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | std::string NameOf(Condition condition) { | ||
| 14 | std::string ret; | ||
| 15 | if (condition.GetFlowTest() != FlowTest::T) { | ||
| 16 | ret = fmt::to_string(condition.GetFlowTest()); | ||
| 17 | } | ||
| 18 | const auto [pred, negated]{condition.GetPred()}; | ||
| 19 | if (!ret.empty()) { | ||
| 20 | ret += '&'; | ||
| 21 | } | ||
| 22 | if (negated) { | ||
| 23 | ret += '!'; | ||
| 24 | } | ||
| 25 | ret += fmt::to_string(pred); | ||
| 26 | return ret; | ||
| 27 | } | ||
| 28 | |||
| 29 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/condition.h b/src/shader_recompiler/frontend/ir/condition.h new file mode 100644 index 000000000..aa8597c60 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/condition.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <compare> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | #include <fmt/format.h> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "shader_recompiler/frontend/ir/flow_test.h" | ||
| 14 | #include "shader_recompiler/frontend/ir/pred.h" | ||
| 15 | |||
| 16 | namespace Shader::IR { | ||
| 17 | |||
| 18 | class Condition { | ||
| 19 | public: | ||
| 20 | Condition() noexcept = default; | ||
| 21 | |||
| 22 | explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept | ||
| 23 | : flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)}, | ||
| 24 | pred_negated{pred_negated_ ? u8{1} : u8{0}} {} | ||
| 25 | |||
| 26 | explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept | ||
| 27 | : Condition(FlowTest::T, pred_, pred_negated_) {} | ||
| 28 | |||
| 29 | explicit Condition(bool value) : Condition(Pred::PT, !value) {} | ||
| 30 | |||
| 31 | auto operator<=>(const Condition&) const noexcept = default; | ||
| 32 | |||
| 33 | [[nodiscard]] IR::FlowTest GetFlowTest() const noexcept { | ||
| 34 | return static_cast<IR::FlowTest>(flow_test); | ||
| 35 | } | ||
| 36 | |||
| 37 | [[nodiscard]] std::pair<IR::Pred, bool> GetPred() const noexcept { | ||
| 38 | return {static_cast<IR::Pred>(pred), pred_negated != 0}; | ||
| 39 | } | ||
| 40 | |||
| 41 | private: | ||
| 42 | u16 flow_test; | ||
| 43 | u8 pred; | ||
| 44 | u8 pred_negated; | ||
| 45 | }; | ||
| 46 | |||
| 47 | std::string NameOf(Condition condition); | ||
| 48 | |||
| 49 | } // namespace Shader::IR | ||
| 50 | |||
| 51 | template <> | ||
| 52 | struct fmt::formatter<Shader::IR::Condition> { | ||
| 53 | constexpr auto parse(format_parse_context& ctx) { | ||
| 54 | return ctx.begin(); | ||
| 55 | } | ||
| 56 | template <typename FormatContext> | ||
| 57 | auto format(const Shader::IR::Condition& cond, FormatContext& ctx) { | ||
| 58 | return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond)); | ||
| 59 | } | ||
| 60 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/flow_test.cpp b/src/shader_recompiler/frontend/ir/flow_test.cpp new file mode 100644 index 000000000..6ebb4ad89 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/flow_test.cpp | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "shader_recompiler/frontend/ir/flow_test.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | std::string NameOf(FlowTest flow_test) { | ||
| 14 | switch (flow_test) { | ||
| 15 | case FlowTest::F: | ||
| 16 | return "F"; | ||
| 17 | case FlowTest::LT: | ||
| 18 | return "LT"; | ||
| 19 | case FlowTest::EQ: | ||
| 20 | return "EQ"; | ||
| 21 | case FlowTest::LE: | ||
| 22 | return "LE"; | ||
| 23 | case FlowTest::GT: | ||
| 24 | return "GT"; | ||
| 25 | case FlowTest::NE: | ||
| 26 | return "NE"; | ||
| 27 | case FlowTest::GE: | ||
| 28 | return "GE"; | ||
| 29 | case FlowTest::NUM: | ||
| 30 | return "NUM"; | ||
| 31 | case FlowTest::NaN: | ||
| 32 | return "NAN"; | ||
| 33 | case FlowTest::LTU: | ||
| 34 | return "LTU"; | ||
| 35 | case FlowTest::EQU: | ||
| 36 | return "EQU"; | ||
| 37 | case FlowTest::LEU: | ||
| 38 | return "LEU"; | ||
| 39 | case FlowTest::GTU: | ||
| 40 | return "GTU"; | ||
| 41 | case FlowTest::NEU: | ||
| 42 | return "NEU"; | ||
| 43 | case FlowTest::GEU: | ||
| 44 | return "GEU"; | ||
| 45 | case FlowTest::T: | ||
| 46 | return "T"; | ||
| 47 | case FlowTest::OFF: | ||
| 48 | return "OFF"; | ||
| 49 | case FlowTest::LO: | ||
| 50 | return "LO"; | ||
| 51 | case FlowTest::SFF: | ||
| 52 | return "SFF"; | ||
| 53 | case FlowTest::LS: | ||
| 54 | return "LS"; | ||
| 55 | case FlowTest::HI: | ||
| 56 | return "HI"; | ||
| 57 | case FlowTest::SFT: | ||
| 58 | return "SFT"; | ||
| 59 | case FlowTest::HS: | ||
| 60 | return "HS"; | ||
| 61 | case FlowTest::OFT: | ||
| 62 | return "OFT"; | ||
| 63 | case FlowTest::CSM_TA: | ||
| 64 | return "CSM_TA"; | ||
| 65 | case FlowTest::CSM_TR: | ||
| 66 | return "CSM_TR"; | ||
| 67 | case FlowTest::CSM_MX: | ||
| 68 | return "CSM_MX"; | ||
| 69 | case FlowTest::FCSM_TA: | ||
| 70 | return "FCSM_TA"; | ||
| 71 | case FlowTest::FCSM_TR: | ||
| 72 | return "FCSM_TR"; | ||
| 73 | case FlowTest::FCSM_MX: | ||
| 74 | return "FCSM_MX"; | ||
| 75 | case FlowTest::RLE: | ||
| 76 | return "RLE"; | ||
| 77 | case FlowTest::RGT: | ||
| 78 | return "RGT"; | ||
| 79 | } | ||
| 80 | return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test)); | ||
| 81 | } | ||
| 82 | |||
| 83 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/flow_test.h b/src/shader_recompiler/frontend/ir/flow_test.h new file mode 100644 index 000000000..09e113773 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/flow_test.h | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include <fmt/format.h> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Shader::IR { | ||
| 13 | |||
| 14 | enum class FlowTest : u64 { | ||
| 15 | F, | ||
| 16 | LT, | ||
| 17 | EQ, | ||
| 18 | LE, | ||
| 19 | GT, | ||
| 20 | NE, | ||
| 21 | GE, | ||
| 22 | NUM, | ||
| 23 | NaN, | ||
| 24 | LTU, | ||
| 25 | EQU, | ||
| 26 | LEU, | ||
| 27 | GTU, | ||
| 28 | NEU, | ||
| 29 | GEU, | ||
| 30 | T, | ||
| 31 | OFF, | ||
| 32 | LO, | ||
| 33 | SFF, | ||
| 34 | LS, | ||
| 35 | HI, | ||
| 36 | SFT, | ||
| 37 | HS, | ||
| 38 | OFT, | ||
| 39 | CSM_TA, | ||
| 40 | CSM_TR, | ||
| 41 | CSM_MX, | ||
| 42 | FCSM_TA, | ||
| 43 | FCSM_TR, | ||
| 44 | FCSM_MX, | ||
| 45 | RLE, | ||
| 46 | RGT, | ||
| 47 | }; | ||
| 48 | |||
| 49 | [[nodiscard]] std::string NameOf(FlowTest flow_test); | ||
| 50 | |||
| 51 | } // namespace Shader::IR | ||
| 52 | |||
| 53 | template <> | ||
| 54 | struct fmt::formatter<Shader::IR::FlowTest> { | ||
| 55 | constexpr auto parse(format_parse_context& ctx) { | ||
| 56 | return ctx.begin(); | ||
| 57 | } | ||
| 58 | template <typename FormatContext> | ||
| 59 | auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) { | ||
| 60 | return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test)); | ||
| 61 | } | ||
| 62 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp new file mode 100644 index 000000000..13159a68d --- /dev/null +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -0,0 +1,2017 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/bit_cast.h" | ||
| 6 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 8 | |||
| 9 | namespace Shader::IR { | ||
| 10 | namespace { | ||
| 11 | [[noreturn]] void ThrowInvalidType(Type type) { | ||
| 12 | throw InvalidArgument("Invalid type {}", type); | ||
| 13 | } | ||
| 14 | |||
| 15 | Value MakeLodClampPair(IREmitter& ir, const F32& bias_lod, const F32& lod_clamp) { | ||
| 16 | if (!bias_lod.IsEmpty() && !lod_clamp.IsEmpty()) { | ||
| 17 | return ir.CompositeConstruct(bias_lod, lod_clamp); | ||
| 18 | } else if (!bias_lod.IsEmpty()) { | ||
| 19 | return bias_lod; | ||
| 20 | } else if (!lod_clamp.IsEmpty()) { | ||
| 21 | return lod_clamp; | ||
| 22 | } else { | ||
| 23 | return Value{}; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } // Anonymous namespace | ||
| 27 | |||
| 28 | U1 IREmitter::Imm1(bool value) const { | ||
| 29 | return U1{Value{value}}; | ||
| 30 | } | ||
| 31 | |||
| 32 | U8 IREmitter::Imm8(u8 value) const { | ||
| 33 | return U8{Value{value}}; | ||
| 34 | } | ||
| 35 | |||
| 36 | U16 IREmitter::Imm16(u16 value) const { | ||
| 37 | return U16{Value{value}}; | ||
| 38 | } | ||
| 39 | |||
| 40 | U32 IREmitter::Imm32(u32 value) const { | ||
| 41 | return U32{Value{value}}; | ||
| 42 | } | ||
| 43 | |||
| 44 | U32 IREmitter::Imm32(s32 value) const { | ||
| 45 | return U32{Value{static_cast<u32>(value)}}; | ||
| 46 | } | ||
| 47 | |||
| 48 | F32 IREmitter::Imm32(f32 value) const { | ||
| 49 | return F32{Value{value}}; | ||
| 50 | } | ||
| 51 | |||
| 52 | U64 IREmitter::Imm64(u64 value) const { | ||
| 53 | return U64{Value{value}}; | ||
| 54 | } | ||
| 55 | |||
| 56 | U64 IREmitter::Imm64(s64 value) const { | ||
| 57 | return U64{Value{static_cast<u64>(value)}}; | ||
| 58 | } | ||
| 59 | |||
| 60 | F64 IREmitter::Imm64(f64 value) const { | ||
| 61 | return F64{Value{value}}; | ||
| 62 | } | ||
| 63 | |||
| 64 | U1 IREmitter::ConditionRef(const U1& value) { | ||
| 65 | return Inst<U1>(Opcode::ConditionRef, value); | ||
| 66 | } | ||
| 67 | |||
| 68 | void IREmitter::Reference(const Value& value) { | ||
| 69 | Inst(Opcode::Reference, value); | ||
| 70 | } | ||
| 71 | |||
| 72 | void IREmitter::PhiMove(IR::Inst& phi, const Value& value) { | ||
| 73 | Inst(Opcode::PhiMove, Value{&phi}, value); | ||
| 74 | } | ||
| 75 | |||
| 76 | void IREmitter::Prologue() { | ||
| 77 | Inst(Opcode::Prologue); | ||
| 78 | } | ||
| 79 | |||
| 80 | void IREmitter::Epilogue() { | ||
| 81 | Inst(Opcode::Epilogue); | ||
| 82 | } | ||
| 83 | |||
| 84 | void IREmitter::DemoteToHelperInvocation() { | ||
| 85 | Inst(Opcode::DemoteToHelperInvocation); | ||
| 86 | } | ||
| 87 | |||
| 88 | void IREmitter::EmitVertex(const U32& stream) { | ||
| 89 | Inst(Opcode::EmitVertex, stream); | ||
| 90 | } | ||
| 91 | |||
| 92 | void IREmitter::EndPrimitive(const U32& stream) { | ||
| 93 | Inst(Opcode::EndPrimitive, stream); | ||
| 94 | } | ||
| 95 | |||
| 96 | void IREmitter::Barrier() { | ||
| 97 | Inst(Opcode::Barrier); | ||
| 98 | } | ||
| 99 | |||
| 100 | void IREmitter::WorkgroupMemoryBarrier() { | ||
| 101 | Inst(Opcode::WorkgroupMemoryBarrier); | ||
| 102 | } | ||
| 103 | |||
| 104 | void IREmitter::DeviceMemoryBarrier() { | ||
| 105 | Inst(Opcode::DeviceMemoryBarrier); | ||
| 106 | } | ||
| 107 | |||
| 108 | U32 IREmitter::GetReg(IR::Reg reg) { | ||
| 109 | return Inst<U32>(Opcode::GetRegister, reg); | ||
| 110 | } | ||
| 111 | |||
| 112 | void IREmitter::SetReg(IR::Reg reg, const U32& value) { | ||
| 113 | Inst(Opcode::SetRegister, reg, value); | ||
| 114 | } | ||
| 115 | |||
| 116 | U1 IREmitter::GetPred(IR::Pred pred, bool is_negated) { | ||
| 117 | if (pred == Pred::PT) { | ||
| 118 | return Imm1(!is_negated); | ||
| 119 | } | ||
| 120 | const U1 value{Inst<U1>(Opcode::GetPred, pred)}; | ||
| 121 | if (is_negated) { | ||
| 122 | return Inst<U1>(Opcode::LogicalNot, value); | ||
| 123 | } else { | ||
| 124 | return value; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | void IREmitter::SetPred(IR::Pred pred, const U1& value) { | ||
| 129 | if (pred != IR::Pred::PT) { | ||
| 130 | Inst(Opcode::SetPred, pred, value); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | U1 IREmitter::GetGotoVariable(u32 id) { | ||
| 135 | return Inst<U1>(Opcode::GetGotoVariable, id); | ||
| 136 | } | ||
| 137 | |||
| 138 | void IREmitter::SetGotoVariable(u32 id, const U1& value) { | ||
| 139 | Inst(Opcode::SetGotoVariable, id, value); | ||
| 140 | } | ||
| 141 | |||
| 142 | U32 IREmitter::GetIndirectBranchVariable() { | ||
| 143 | return Inst<U32>(Opcode::GetIndirectBranchVariable); | ||
| 144 | } | ||
| 145 | |||
| 146 | void IREmitter::SetIndirectBranchVariable(const U32& value) { | ||
| 147 | Inst(Opcode::SetIndirectBranchVariable, value); | ||
| 148 | } | ||
| 149 | |||
| 150 | U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) { | ||
| 151 | return Inst<U32>(Opcode::GetCbufU32, binding, byte_offset); | ||
| 152 | } | ||
| 153 | |||
| 154 | Value IREmitter::GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, | ||
| 155 | bool is_signed) { | ||
| 156 | switch (bitsize) { | ||
| 157 | case 8: | ||
| 158 | return Inst<U32>(is_signed ? Opcode::GetCbufS8 : Opcode::GetCbufU8, binding, byte_offset); | ||
| 159 | case 16: | ||
| 160 | return Inst<U32>(is_signed ? Opcode::GetCbufS16 : Opcode::GetCbufU16, binding, byte_offset); | ||
| 161 | case 32: | ||
| 162 | return Inst<U32>(Opcode::GetCbufU32, binding, byte_offset); | ||
| 163 | case 64: | ||
| 164 | return Inst(Opcode::GetCbufU32x2, binding, byte_offset); | ||
| 165 | default: | ||
| 166 | throw InvalidArgument("Invalid bit size {}", bitsize); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | F32 IREmitter::GetFloatCbuf(const U32& binding, const U32& byte_offset) { | ||
| 171 | return Inst<F32>(Opcode::GetCbufF32, binding, byte_offset); | ||
| 172 | } | ||
| 173 | |||
| 174 | U1 IREmitter::GetZFlag() { | ||
| 175 | return Inst<U1>(Opcode::GetZFlag); | ||
| 176 | } | ||
| 177 | |||
| 178 | U1 IREmitter::GetSFlag() { | ||
| 179 | return Inst<U1>(Opcode::GetSFlag); | ||
| 180 | } | ||
| 181 | |||
| 182 | U1 IREmitter::GetCFlag() { | ||
| 183 | return Inst<U1>(Opcode::GetCFlag); | ||
| 184 | } | ||
| 185 | |||
| 186 | U1 IREmitter::GetOFlag() { | ||
| 187 | return Inst<U1>(Opcode::GetOFlag); | ||
| 188 | } | ||
| 189 | |||
| 190 | void IREmitter::SetZFlag(const U1& value) { | ||
| 191 | Inst(Opcode::SetZFlag, value); | ||
| 192 | } | ||
| 193 | |||
| 194 | void IREmitter::SetSFlag(const U1& value) { | ||
| 195 | Inst(Opcode::SetSFlag, value); | ||
| 196 | } | ||
| 197 | |||
| 198 | void IREmitter::SetCFlag(const U1& value) { | ||
| 199 | Inst(Opcode::SetCFlag, value); | ||
| 200 | } | ||
| 201 | |||
| 202 | void IREmitter::SetOFlag(const U1& value) { | ||
| 203 | Inst(Opcode::SetOFlag, value); | ||
| 204 | } | ||
| 205 | |||
| 206 | static U1 GetFlowTest(IREmitter& ir, FlowTest flow_test) { | ||
| 207 | switch (flow_test) { | ||
| 208 | case FlowTest::F: | ||
| 209 | return ir.Imm1(false); | ||
| 210 | case FlowTest::LT: | ||
| 211 | return ir.LogicalXor(ir.LogicalAnd(ir.GetSFlag(), ir.LogicalNot(ir.GetZFlag())), | ||
| 212 | ir.GetOFlag()); | ||
| 213 | case FlowTest::EQ: | ||
| 214 | return ir.LogicalAnd(ir.LogicalNot(ir.GetSFlag()), ir.GetZFlag()); | ||
| 215 | case FlowTest::LE: | ||
| 216 | return ir.LogicalXor(ir.GetSFlag(), ir.LogicalOr(ir.GetZFlag(), ir.GetOFlag())); | ||
| 217 | case FlowTest::GT: | ||
| 218 | return ir.LogicalAnd(ir.LogicalXor(ir.LogicalNot(ir.GetSFlag()), ir.GetOFlag()), | ||
| 219 | ir.LogicalNot(ir.GetZFlag())); | ||
| 220 | case FlowTest::NE: | ||
| 221 | return ir.LogicalNot(ir.GetZFlag()); | ||
| 222 | case FlowTest::GE: | ||
| 223 | return ir.LogicalNot(ir.LogicalXor(ir.GetSFlag(), ir.GetOFlag())); | ||
| 224 | case FlowTest::NUM: | ||
| 225 | return ir.LogicalOr(ir.LogicalNot(ir.GetSFlag()), ir.LogicalNot(ir.GetZFlag())); | ||
| 226 | case FlowTest::NaN: | ||
| 227 | return ir.LogicalAnd(ir.GetSFlag(), ir.GetZFlag()); | ||
| 228 | case FlowTest::LTU: | ||
| 229 | return ir.LogicalXor(ir.GetSFlag(), ir.GetOFlag()); | ||
| 230 | case FlowTest::EQU: | ||
| 231 | return ir.GetZFlag(); | ||
| 232 | case FlowTest::LEU: | ||
| 233 | return ir.LogicalOr(ir.LogicalXor(ir.GetSFlag(), ir.GetOFlag()), ir.GetZFlag()); | ||
| 234 | case FlowTest::GTU: | ||
| 235 | return ir.LogicalXor(ir.LogicalNot(ir.GetSFlag()), | ||
| 236 | ir.LogicalOr(ir.GetZFlag(), ir.GetOFlag())); | ||
| 237 | case FlowTest::NEU: | ||
| 238 | return ir.LogicalOr(ir.GetSFlag(), ir.LogicalNot(ir.GetZFlag())); | ||
| 239 | case FlowTest::GEU: | ||
| 240 | return ir.LogicalXor(ir.LogicalOr(ir.LogicalNot(ir.GetSFlag()), ir.GetZFlag()), | ||
| 241 | ir.GetOFlag()); | ||
| 242 | case FlowTest::T: | ||
| 243 | return ir.Imm1(true); | ||
| 244 | case FlowTest::OFF: | ||
| 245 | return ir.LogicalNot(ir.GetOFlag()); | ||
| 246 | case FlowTest::LO: | ||
| 247 | return ir.LogicalNot(ir.GetCFlag()); | ||
| 248 | case FlowTest::SFF: | ||
| 249 | return ir.LogicalNot(ir.GetSFlag()); | ||
| 250 | case FlowTest::LS: | ||
| 251 | return ir.LogicalOr(ir.GetZFlag(), ir.LogicalNot(ir.GetCFlag())); | ||
| 252 | case FlowTest::HI: | ||
| 253 | return ir.LogicalAnd(ir.GetCFlag(), ir.LogicalNot(ir.GetZFlag())); | ||
| 254 | case FlowTest::SFT: | ||
| 255 | return ir.GetSFlag(); | ||
| 256 | case FlowTest::HS: | ||
| 257 | return ir.GetCFlag(); | ||
| 258 | case FlowTest::OFT: | ||
| 259 | return ir.GetOFlag(); | ||
| 260 | case FlowTest::RLE: | ||
| 261 | return ir.LogicalOr(ir.GetSFlag(), ir.GetZFlag()); | ||
| 262 | case FlowTest::RGT: | ||
| 263 | return ir.LogicalAnd(ir.LogicalNot(ir.GetSFlag()), ir.LogicalNot(ir.GetZFlag())); | ||
| 264 | case FlowTest::FCSM_TR: | ||
| 265 | LOG_WARNING(Shader, "(STUBBED) FCSM_TR"); | ||
| 266 | return ir.Imm1(false); | ||
| 267 | case FlowTest::CSM_TA: | ||
| 268 | case FlowTest::CSM_TR: | ||
| 269 | case FlowTest::CSM_MX: | ||
| 270 | case FlowTest::FCSM_TA: | ||
| 271 | case FlowTest::FCSM_MX: | ||
| 272 | default: | ||
| 273 | throw NotImplementedException("Flow test {}", flow_test); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | U1 IREmitter::Condition(IR::Condition cond) { | ||
| 278 | const FlowTest flow_test{cond.GetFlowTest()}; | ||
| 279 | const auto [pred, is_negated]{cond.GetPred()}; | ||
| 280 | if (flow_test == FlowTest::T) { | ||
| 281 | return GetPred(pred, is_negated); | ||
| 282 | } | ||
| 283 | return LogicalAnd(GetPred(pred, is_negated), GetFlowTest(*this, flow_test)); | ||
| 284 | } | ||
| 285 | |||
| 286 | U1 IREmitter::GetFlowTestResult(FlowTest test) { | ||
| 287 | return GetFlowTest(*this, test); | ||
| 288 | } | ||
| 289 | |||
| 290 | F32 IREmitter::GetAttribute(IR::Attribute attribute) { | ||
| 291 | return GetAttribute(attribute, Imm32(0)); | ||
| 292 | } | ||
| 293 | |||
| 294 | F32 IREmitter::GetAttribute(IR::Attribute attribute, const U32& vertex) { | ||
| 295 | return Inst<F32>(Opcode::GetAttribute, attribute, vertex); | ||
| 296 | } | ||
| 297 | |||
| 298 | void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) { | ||
| 299 | Inst(Opcode::SetAttribute, attribute, value, vertex); | ||
| 300 | } | ||
| 301 | |||
| 302 | F32 IREmitter::GetAttributeIndexed(const U32& phys_address) { | ||
| 303 | return GetAttributeIndexed(phys_address, Imm32(0)); | ||
| 304 | } | ||
| 305 | |||
| 306 | F32 IREmitter::GetAttributeIndexed(const U32& phys_address, const U32& vertex) { | ||
| 307 | return Inst<F32>(Opcode::GetAttributeIndexed, phys_address, vertex); | ||
| 308 | } | ||
| 309 | |||
| 310 | void IREmitter::SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex) { | ||
| 311 | Inst(Opcode::SetAttributeIndexed, phys_address, value, vertex); | ||
| 312 | } | ||
| 313 | |||
| 314 | F32 IREmitter::GetPatch(Patch patch) { | ||
| 315 | return Inst<F32>(Opcode::GetPatch, patch); | ||
| 316 | } | ||
| 317 | |||
| 318 | void IREmitter::SetPatch(Patch patch, const F32& value) { | ||
| 319 | Inst(Opcode::SetPatch, patch, value); | ||
| 320 | } | ||
| 321 | |||
| 322 | void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) { | ||
| 323 | Inst(Opcode::SetFragColor, Imm32(index), Imm32(component), value); | ||
| 324 | } | ||
| 325 | |||
| 326 | void IREmitter::SetSampleMask(const U32& value) { | ||
| 327 | Inst(Opcode::SetSampleMask, value); | ||
| 328 | } | ||
| 329 | |||
| 330 | void IREmitter::SetFragDepth(const F32& value) { | ||
| 331 | Inst(Opcode::SetFragDepth, value); | ||
| 332 | } | ||
| 333 | |||
| 334 | U32 IREmitter::WorkgroupIdX() { | ||
| 335 | return U32{CompositeExtract(Inst(Opcode::WorkgroupId), 0)}; | ||
| 336 | } | ||
| 337 | |||
| 338 | U32 IREmitter::WorkgroupIdY() { | ||
| 339 | return U32{CompositeExtract(Inst(Opcode::WorkgroupId), 1)}; | ||
| 340 | } | ||
| 341 | |||
| 342 | U32 IREmitter::WorkgroupIdZ() { | ||
| 343 | return U32{CompositeExtract(Inst(Opcode::WorkgroupId), 2)}; | ||
| 344 | } | ||
| 345 | |||
| 346 | Value IREmitter::LocalInvocationId() { | ||
| 347 | return Inst(Opcode::LocalInvocationId); | ||
| 348 | } | ||
| 349 | |||
| 350 | U32 IREmitter::LocalInvocationIdX() { | ||
| 351 | return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 0)}; | ||
| 352 | } | ||
| 353 | |||
| 354 | U32 IREmitter::LocalInvocationIdY() { | ||
| 355 | return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 1)}; | ||
| 356 | } | ||
| 357 | |||
| 358 | U32 IREmitter::LocalInvocationIdZ() { | ||
| 359 | return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 2)}; | ||
| 360 | } | ||
| 361 | |||
| 362 | U32 IREmitter::InvocationId() { | ||
| 363 | return Inst<U32>(Opcode::InvocationId); | ||
| 364 | } | ||
| 365 | |||
| 366 | U32 IREmitter::SampleId() { | ||
| 367 | return Inst<U32>(Opcode::SampleId); | ||
| 368 | } | ||
| 369 | |||
| 370 | U1 IREmitter::IsHelperInvocation() { | ||
| 371 | return Inst<U1>(Opcode::IsHelperInvocation); | ||
| 372 | } | ||
| 373 | |||
| 374 | F32 IREmitter::YDirection() { | ||
| 375 | return Inst<F32>(Opcode::YDirection); | ||
| 376 | } | ||
| 377 | |||
| 378 | U32 IREmitter::LaneId() { | ||
| 379 | return Inst<U32>(Opcode::LaneId); | ||
| 380 | } | ||
| 381 | |||
| 382 | U32 IREmitter::LoadGlobalU8(const U64& address) { | ||
| 383 | return Inst<U32>(Opcode::LoadGlobalU8, address); | ||
| 384 | } | ||
| 385 | |||
| 386 | U32 IREmitter::LoadGlobalS8(const U64& address) { | ||
| 387 | return Inst<U32>(Opcode::LoadGlobalS8, address); | ||
| 388 | } | ||
| 389 | |||
| 390 | U32 IREmitter::LoadGlobalU16(const U64& address) { | ||
| 391 | return Inst<U32>(Opcode::LoadGlobalU16, address); | ||
| 392 | } | ||
| 393 | |||
| 394 | U32 IREmitter::LoadGlobalS16(const U64& address) { | ||
| 395 | return Inst<U32>(Opcode::LoadGlobalS16, address); | ||
| 396 | } | ||
| 397 | |||
| 398 | U32 IREmitter::LoadGlobal32(const U64& address) { | ||
| 399 | return Inst<U32>(Opcode::LoadGlobal32, address); | ||
| 400 | } | ||
| 401 | |||
| 402 | Value IREmitter::LoadGlobal64(const U64& address) { | ||
| 403 | return Inst<Value>(Opcode::LoadGlobal64, address); | ||
| 404 | } | ||
| 405 | |||
| 406 | Value IREmitter::LoadGlobal128(const U64& address) { | ||
| 407 | return Inst<Value>(Opcode::LoadGlobal128, address); | ||
| 408 | } | ||
| 409 | |||
| 410 | void IREmitter::WriteGlobalU8(const U64& address, const U32& value) { | ||
| 411 | Inst(Opcode::WriteGlobalU8, address, value); | ||
| 412 | } | ||
| 413 | |||
| 414 | void IREmitter::WriteGlobalS8(const U64& address, const U32& value) { | ||
| 415 | Inst(Opcode::WriteGlobalS8, address, value); | ||
| 416 | } | ||
| 417 | |||
| 418 | void IREmitter::WriteGlobalU16(const U64& address, const U32& value) { | ||
| 419 | Inst(Opcode::WriteGlobalU16, address, value); | ||
| 420 | } | ||
| 421 | |||
| 422 | void IREmitter::WriteGlobalS16(const U64& address, const U32& value) { | ||
| 423 | Inst(Opcode::WriteGlobalS16, address, value); | ||
| 424 | } | ||
| 425 | |||
| 426 | void IREmitter::WriteGlobal32(const U64& address, const U32& value) { | ||
| 427 | Inst(Opcode::WriteGlobal32, address, value); | ||
| 428 | } | ||
| 429 | |||
| 430 | void IREmitter::WriteGlobal64(const U64& address, const IR::Value& vector) { | ||
| 431 | Inst(Opcode::WriteGlobal64, address, vector); | ||
| 432 | } | ||
| 433 | |||
| 434 | void IREmitter::WriteGlobal128(const U64& address, const IR::Value& vector) { | ||
| 435 | Inst(Opcode::WriteGlobal128, address, vector); | ||
| 436 | } | ||
| 437 | |||
| 438 | U32 IREmitter::LoadLocal(const IR::U32& word_offset) { | ||
| 439 | return Inst<U32>(Opcode::LoadLocal, word_offset); | ||
| 440 | } | ||
| 441 | |||
| 442 | void IREmitter::WriteLocal(const IR::U32& word_offset, const IR::U32& value) { | ||
| 443 | Inst(Opcode::WriteLocal, word_offset, value); | ||
| 444 | } | ||
| 445 | |||
| 446 | Value IREmitter::LoadShared(int bit_size, bool is_signed, const IR::U32& offset) { | ||
| 447 | switch (bit_size) { | ||
| 448 | case 8: | ||
| 449 | return Inst(is_signed ? Opcode::LoadSharedS8 : Opcode::LoadSharedU8, offset); | ||
| 450 | case 16: | ||
| 451 | return Inst(is_signed ? Opcode::LoadSharedS16 : Opcode::LoadSharedU16, offset); | ||
| 452 | case 32: | ||
| 453 | return Inst(Opcode::LoadSharedU32, offset); | ||
| 454 | case 64: | ||
| 455 | return Inst(Opcode::LoadSharedU64, offset); | ||
| 456 | case 128: | ||
| 457 | return Inst(Opcode::LoadSharedU128, offset); | ||
| 458 | } | ||
| 459 | throw InvalidArgument("Invalid bit size {}", bit_size); | ||
| 460 | } | ||
| 461 | |||
| 462 | void IREmitter::WriteShared(int bit_size, const IR::U32& offset, const IR::Value& value) { | ||
| 463 | switch (bit_size) { | ||
| 464 | case 8: | ||
| 465 | Inst(Opcode::WriteSharedU8, offset, value); | ||
| 466 | break; | ||
| 467 | case 16: | ||
| 468 | Inst(Opcode::WriteSharedU16, offset, value); | ||
| 469 | break; | ||
| 470 | case 32: | ||
| 471 | Inst(Opcode::WriteSharedU32, offset, value); | ||
| 472 | break; | ||
| 473 | case 64: | ||
| 474 | Inst(Opcode::WriteSharedU64, offset, value); | ||
| 475 | break; | ||
| 476 | case 128: | ||
| 477 | Inst(Opcode::WriteSharedU128, offset, value); | ||
| 478 | break; | ||
| 479 | default: | ||
| 480 | throw InvalidArgument("Invalid bit size {}", bit_size); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | U1 IREmitter::GetZeroFromOp(const Value& op) { | ||
| 485 | return Inst<U1>(Opcode::GetZeroFromOp, op); | ||
| 486 | } | ||
| 487 | |||
| 488 | U1 IREmitter::GetSignFromOp(const Value& op) { | ||
| 489 | return Inst<U1>(Opcode::GetSignFromOp, op); | ||
| 490 | } | ||
| 491 | |||
| 492 | U1 IREmitter::GetCarryFromOp(const Value& op) { | ||
| 493 | return Inst<U1>(Opcode::GetCarryFromOp, op); | ||
| 494 | } | ||
| 495 | |||
| 496 | U1 IREmitter::GetOverflowFromOp(const Value& op) { | ||
| 497 | return Inst<U1>(Opcode::GetOverflowFromOp, op); | ||
| 498 | } | ||
| 499 | |||
| 500 | U1 IREmitter::GetSparseFromOp(const Value& op) { | ||
| 501 | return Inst<U1>(Opcode::GetSparseFromOp, op); | ||
| 502 | } | ||
| 503 | |||
| 504 | U1 IREmitter::GetInBoundsFromOp(const Value& op) { | ||
| 505 | return Inst<U1>(Opcode::GetInBoundsFromOp, op); | ||
| 506 | } | ||
| 507 | |||
| 508 | F16F32F64 IREmitter::FPAdd(const F16F32F64& a, const F16F32F64& b, FpControl control) { | ||
| 509 | if (a.Type() != b.Type()) { | ||
| 510 | throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type()); | ||
| 511 | } | ||
| 512 | switch (a.Type()) { | ||
| 513 | case Type::F16: | ||
| 514 | return Inst<F16>(Opcode::FPAdd16, Flags{control}, a, b); | ||
| 515 | case Type::F32: | ||
| 516 | return Inst<F32>(Opcode::FPAdd32, Flags{control}, a, b); | ||
| 517 | case Type::F64: | ||
| 518 | return Inst<F64>(Opcode::FPAdd64, Flags{control}, a, b); | ||
| 519 | default: | ||
| 520 | ThrowInvalidType(a.Type()); | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | Value IREmitter::CompositeConstruct(const Value& e1, const Value& e2) { | ||
| 525 | if (e1.Type() != e2.Type()) { | ||
| 526 | throw InvalidArgument("Mismatching types {} and {}", e1.Type(), e2.Type()); | ||
| 527 | } | ||
| 528 | switch (e1.Type()) { | ||
| 529 | case Type::U32: | ||
| 530 | return Inst(Opcode::CompositeConstructU32x2, e1, e2); | ||
| 531 | case Type::F16: | ||
| 532 | return Inst(Opcode::CompositeConstructF16x2, e1, e2); | ||
| 533 | case Type::F32: | ||
| 534 | return Inst(Opcode::CompositeConstructF32x2, e1, e2); | ||
| 535 | case Type::F64: | ||
| 536 | return Inst(Opcode::CompositeConstructF64x2, e1, e2); | ||
| 537 | default: | ||
| 538 | ThrowInvalidType(e1.Type()); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | Value IREmitter::CompositeConstruct(const Value& e1, const Value& e2, const Value& e3) { | ||
| 543 | if (e1.Type() != e2.Type() || e1.Type() != e3.Type()) { | ||
| 544 | throw InvalidArgument("Mismatching types {}, {}, and {}", e1.Type(), e2.Type(), e3.Type()); | ||
| 545 | } | ||
| 546 | switch (e1.Type()) { | ||
| 547 | case Type::U32: | ||
| 548 | return Inst(Opcode::CompositeConstructU32x3, e1, e2, e3); | ||
| 549 | case Type::F16: | ||
| 550 | return Inst(Opcode::CompositeConstructF16x3, e1, e2, e3); | ||
| 551 | case Type::F32: | ||
| 552 | return Inst(Opcode::CompositeConstructF32x3, e1, e2, e3); | ||
| 553 | case Type::F64: | ||
| 554 | return Inst(Opcode::CompositeConstructF64x3, e1, e2, e3); | ||
| 555 | default: | ||
| 556 | ThrowInvalidType(e1.Type()); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | Value IREmitter::CompositeConstruct(const Value& e1, const Value& e2, const Value& e3, | ||
| 561 | const Value& e4) { | ||
| 562 | if (e1.Type() != e2.Type() || e1.Type() != e3.Type() || e1.Type() != e4.Type()) { | ||
| 563 | throw InvalidArgument("Mismatching types {}, {}, {}, and {}", e1.Type(), e2.Type(), | ||
| 564 | e3.Type(), e4.Type()); | ||
| 565 | } | ||
| 566 | switch (e1.Type()) { | ||
| 567 | case Type::U32: | ||
| 568 | return Inst(Opcode::CompositeConstructU32x4, e1, e2, e3, e4); | ||
| 569 | case Type::F16: | ||
| 570 | return Inst(Opcode::CompositeConstructF16x4, e1, e2, e3, e4); | ||
| 571 | case Type::F32: | ||
| 572 | return Inst(Opcode::CompositeConstructF32x4, e1, e2, e3, e4); | ||
| 573 | case Type::F64: | ||
| 574 | return Inst(Opcode::CompositeConstructF64x4, e1, e2, e3, e4); | ||
| 575 | default: | ||
| 576 | ThrowInvalidType(e1.Type()); | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | Value IREmitter::CompositeExtract(const Value& vector, size_t element) { | ||
| 581 | const auto read{[&](Opcode opcode, size_t limit) -> Value { | ||
| 582 | if (element >= limit) { | ||
| 583 | throw InvalidArgument("Out of bounds element {}", element); | ||
| 584 | } | ||
| 585 | return Inst(opcode, vector, Value{static_cast<u32>(element)}); | ||
| 586 | }}; | ||
| 587 | switch (vector.Type()) { | ||
| 588 | case Type::U32x2: | ||
| 589 | return read(Opcode::CompositeExtractU32x2, 2); | ||
| 590 | case Type::U32x3: | ||
| 591 | return read(Opcode::CompositeExtractU32x3, 3); | ||
| 592 | case Type::U32x4: | ||
| 593 | return read(Opcode::CompositeExtractU32x4, 4); | ||
| 594 | case Type::F16x2: | ||
| 595 | return read(Opcode::CompositeExtractF16x2, 2); | ||
| 596 | case Type::F16x3: | ||
| 597 | return read(Opcode::CompositeExtractF16x3, 3); | ||
| 598 | case Type::F16x4: | ||
| 599 | return read(Opcode::CompositeExtractF16x4, 4); | ||
| 600 | case Type::F32x2: | ||
| 601 | return read(Opcode::CompositeExtractF32x2, 2); | ||
| 602 | case Type::F32x3: | ||
| 603 | return read(Opcode::CompositeExtractF32x3, 3); | ||
| 604 | case Type::F32x4: | ||
| 605 | return read(Opcode::CompositeExtractF32x4, 4); | ||
| 606 | case Type::F64x2: | ||
| 607 | return read(Opcode::CompositeExtractF64x2, 2); | ||
| 608 | case Type::F64x3: | ||
| 609 | return read(Opcode::CompositeExtractF64x3, 3); | ||
| 610 | case Type::F64x4: | ||
| 611 | return read(Opcode::CompositeExtractF64x4, 4); | ||
| 612 | default: | ||
| 613 | ThrowInvalidType(vector.Type()); | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | Value IREmitter::CompositeInsert(const Value& vector, const Value& object, size_t element) { | ||
| 618 | const auto insert{[&](Opcode opcode, size_t limit) { | ||
| 619 | if (element >= limit) { | ||
| 620 | throw InvalidArgument("Out of bounds element {}", element); | ||
| 621 | } | ||
| 622 | return Inst(opcode, vector, object, Value{static_cast<u32>(element)}); | ||
| 623 | }}; | ||
| 624 | switch (vector.Type()) { | ||
| 625 | case Type::U32x2: | ||
| 626 | return insert(Opcode::CompositeInsertU32x2, 2); | ||
| 627 | case Type::U32x3: | ||
| 628 | return insert(Opcode::CompositeInsertU32x3, 3); | ||
| 629 | case Type::U32x4: | ||
| 630 | return insert(Opcode::CompositeInsertU32x4, 4); | ||
| 631 | case Type::F16x2: | ||
| 632 | return insert(Opcode::CompositeInsertF16x2, 2); | ||
| 633 | case Type::F16x3: | ||
| 634 | return insert(Opcode::CompositeInsertF16x3, 3); | ||
| 635 | case Type::F16x4: | ||
| 636 | return insert(Opcode::CompositeInsertF16x4, 4); | ||
| 637 | case Type::F32x2: | ||
| 638 | return insert(Opcode::CompositeInsertF32x2, 2); | ||
| 639 | case Type::F32x3: | ||
| 640 | return insert(Opcode::CompositeInsertF32x3, 3); | ||
| 641 | case Type::F32x4: | ||
| 642 | return insert(Opcode::CompositeInsertF32x4, 4); | ||
| 643 | case Type::F64x2: | ||
| 644 | return insert(Opcode::CompositeInsertF64x2, 2); | ||
| 645 | case Type::F64x3: | ||
| 646 | return insert(Opcode::CompositeInsertF64x3, 3); | ||
| 647 | case Type::F64x4: | ||
| 648 | return insert(Opcode::CompositeInsertF64x4, 4); | ||
| 649 | default: | ||
| 650 | ThrowInvalidType(vector.Type()); | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | Value IREmitter::Select(const U1& condition, const Value& true_value, const Value& false_value) { | ||
| 655 | if (true_value.Type() != false_value.Type()) { | ||
| 656 | throw InvalidArgument("Mismatching types {} and {}", true_value.Type(), false_value.Type()); | ||
| 657 | } | ||
| 658 | switch (true_value.Type()) { | ||
| 659 | case Type::U1: | ||
| 660 | return Inst(Opcode::SelectU1, condition, true_value, false_value); | ||
| 661 | case Type::U8: | ||
| 662 | return Inst(Opcode::SelectU8, condition, true_value, false_value); | ||
| 663 | case Type::U16: | ||
| 664 | return Inst(Opcode::SelectU16, condition, true_value, false_value); | ||
| 665 | case Type::U32: | ||
| 666 | return Inst(Opcode::SelectU32, condition, true_value, false_value); | ||
| 667 | case Type::U64: | ||
| 668 | return Inst(Opcode::SelectU64, condition, true_value, false_value); | ||
| 669 | case Type::F32: | ||
| 670 | return Inst(Opcode::SelectF32, condition, true_value, false_value); | ||
| 671 | case Type::F64: | ||
| 672 | return Inst(Opcode::SelectF64, condition, true_value, false_value); | ||
| 673 | default: | ||
| 674 | throw InvalidArgument("Invalid type {}", true_value.Type()); | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | template <> | ||
| 679 | IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) { | ||
| 680 | return Inst<IR::U32>(Opcode::BitCastU32F32, value); | ||
| 681 | } | ||
| 682 | |||
| 683 | template <> | ||
| 684 | IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { | ||
| 685 | return Inst<IR::F32>(Opcode::BitCastF32U32, value); | ||
| 686 | } | ||
| 687 | |||
| 688 | template <> | ||
| 689 | IR::U16 IREmitter::BitCast<IR::U16, IR::F16>(const IR::F16& value) { | ||
| 690 | return Inst<IR::U16>(Opcode::BitCastU16F16, value); | ||
| 691 | } | ||
| 692 | |||
| 693 | template <> | ||
| 694 | IR::F16 IREmitter::BitCast<IR::F16, IR::U16>(const IR::U16& value) { | ||
| 695 | return Inst<IR::F16>(Opcode::BitCastF16U16, value); | ||
| 696 | } | ||
| 697 | |||
| 698 | template <> | ||
| 699 | IR::U64 IREmitter::BitCast<IR::U64, IR::F64>(const IR::F64& value) { | ||
| 700 | return Inst<IR::U64>(Opcode::BitCastU64F64, value); | ||
| 701 | } | ||
| 702 | |||
| 703 | template <> | ||
| 704 | IR::F64 IREmitter::BitCast<IR::F64, IR::U64>(const IR::U64& value) { | ||
| 705 | return Inst<IR::F64>(Opcode::BitCastF64U64, value); | ||
| 706 | } | ||
| 707 | |||
| 708 | U64 IREmitter::PackUint2x32(const Value& vector) { | ||
| 709 | return Inst<U64>(Opcode::PackUint2x32, vector); | ||
| 710 | } | ||
| 711 | |||
| 712 | Value IREmitter::UnpackUint2x32(const U64& value) { | ||
| 713 | return Inst<Value>(Opcode::UnpackUint2x32, value); | ||
| 714 | } | ||
| 715 | |||
| 716 | U32 IREmitter::PackFloat2x16(const Value& vector) { | ||
| 717 | return Inst<U32>(Opcode::PackFloat2x16, vector); | ||
| 718 | } | ||
| 719 | |||
| 720 | Value IREmitter::UnpackFloat2x16(const U32& value) { | ||
| 721 | return Inst(Opcode::UnpackFloat2x16, value); | ||
| 722 | } | ||
| 723 | |||
| 724 | U32 IREmitter::PackHalf2x16(const Value& vector) { | ||
| 725 | return Inst<U32>(Opcode::PackHalf2x16, vector); | ||
| 726 | } | ||
| 727 | |||
| 728 | Value IREmitter::UnpackHalf2x16(const U32& value) { | ||
| 729 | return Inst(Opcode::UnpackHalf2x16, value); | ||
| 730 | } | ||
| 731 | |||
| 732 | F64 IREmitter::PackDouble2x32(const Value& vector) { | ||
| 733 | return Inst<F64>(Opcode::PackDouble2x32, vector); | ||
| 734 | } | ||
| 735 | |||
| 736 | Value IREmitter::UnpackDouble2x32(const F64& value) { | ||
| 737 | return Inst<Value>(Opcode::UnpackDouble2x32, value); | ||
| 738 | } | ||
| 739 | |||
| 740 | F16F32F64 IREmitter::FPMul(const F16F32F64& a, const F16F32F64& b, FpControl control) { | ||
| 741 | if (a.Type() != b.Type()) { | ||
| 742 | throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type()); | ||
| 743 | } | ||
| 744 | switch (a.Type()) { | ||
| 745 | case Type::F16: | ||
| 746 | return Inst<F16>(Opcode::FPMul16, Flags{control}, a, b); | ||
| 747 | case Type::F32: | ||
| 748 | return Inst<F32>(Opcode::FPMul32, Flags{control}, a, b); | ||
| 749 | case Type::F64: | ||
| 750 | return Inst<F64>(Opcode::FPMul64, Flags{control}, a, b); | ||
| 751 | default: | ||
| 752 | ThrowInvalidType(a.Type()); | ||
| 753 | } | ||
| 754 | } | ||
| 755 | |||
| 756 | F16F32F64 IREmitter::FPFma(const F16F32F64& a, const F16F32F64& b, const F16F32F64& c, | ||
| 757 | FpControl control) { | ||
| 758 | if (a.Type() != b.Type() || a.Type() != c.Type()) { | ||
| 759 | throw InvalidArgument("Mismatching types {}, {}, and {}", a.Type(), b.Type(), c.Type()); | ||
| 760 | } | ||
| 761 | switch (a.Type()) { | ||
| 762 | case Type::F16: | ||
| 763 | return Inst<F16>(Opcode::FPFma16, Flags{control}, a, b, c); | ||
| 764 | case Type::F32: | ||
| 765 | return Inst<F32>(Opcode::FPFma32, Flags{control}, a, b, c); | ||
| 766 | case Type::F64: | ||
| 767 | return Inst<F64>(Opcode::FPFma64, Flags{control}, a, b, c); | ||
| 768 | default: | ||
| 769 | ThrowInvalidType(a.Type()); | ||
| 770 | } | ||
| 771 | } | ||
| 772 | |||
| 773 | F16F32F64 IREmitter::FPAbs(const F16F32F64& value) { | ||
| 774 | switch (value.Type()) { | ||
| 775 | case Type::F16: | ||
| 776 | return Inst<F16>(Opcode::FPAbs16, value); | ||
| 777 | case Type::F32: | ||
| 778 | return Inst<F32>(Opcode::FPAbs32, value); | ||
| 779 | case Type::F64: | ||
| 780 | return Inst<F64>(Opcode::FPAbs64, value); | ||
| 781 | default: | ||
| 782 | ThrowInvalidType(value.Type()); | ||
| 783 | } | ||
| 784 | } | ||
| 785 | |||
| 786 | F16F32F64 IREmitter::FPNeg(const F16F32F64& value) { | ||
| 787 | switch (value.Type()) { | ||
| 788 | case Type::F16: | ||
| 789 | return Inst<F16>(Opcode::FPNeg16, value); | ||
| 790 | case Type::F32: | ||
| 791 | return Inst<F32>(Opcode::FPNeg32, value); | ||
| 792 | case Type::F64: | ||
| 793 | return Inst<F64>(Opcode::FPNeg64, value); | ||
| 794 | default: | ||
| 795 | ThrowInvalidType(value.Type()); | ||
| 796 | } | ||
| 797 | } | ||
| 798 | |||
| 799 | F16F32F64 IREmitter::FPAbsNeg(const F16F32F64& value, bool abs, bool neg) { | ||
| 800 | F16F32F64 result{value}; | ||
| 801 | if (abs) { | ||
| 802 | result = FPAbs(result); | ||
| 803 | } | ||
| 804 | if (neg) { | ||
| 805 | result = FPNeg(result); | ||
| 806 | } | ||
| 807 | return result; | ||
| 808 | } | ||
| 809 | |||
| 810 | F32 IREmitter::FPCos(const F32& value) { | ||
| 811 | return Inst<F32>(Opcode::FPCos, value); | ||
| 812 | } | ||
| 813 | |||
| 814 | F32 IREmitter::FPSin(const F32& value) { | ||
| 815 | return Inst<F32>(Opcode::FPSin, value); | ||
| 816 | } | ||
| 817 | |||
| 818 | F32 IREmitter::FPExp2(const F32& value) { | ||
| 819 | return Inst<F32>(Opcode::FPExp2, value); | ||
| 820 | } | ||
| 821 | |||
| 822 | F32 IREmitter::FPLog2(const F32& value) { | ||
| 823 | return Inst<F32>(Opcode::FPLog2, value); | ||
| 824 | } | ||
| 825 | |||
| 826 | F32F64 IREmitter::FPRecip(const F32F64& value) { | ||
| 827 | switch (value.Type()) { | ||
| 828 | case Type::F32: | ||
| 829 | return Inst<F32>(Opcode::FPRecip32, value); | ||
| 830 | case Type::F64: | ||
| 831 | return Inst<F64>(Opcode::FPRecip64, value); | ||
| 832 | default: | ||
| 833 | ThrowInvalidType(value.Type()); | ||
| 834 | } | ||
| 835 | } | ||
| 836 | |||
| 837 | F32F64 IREmitter::FPRecipSqrt(const F32F64& value) { | ||
| 838 | switch (value.Type()) { | ||
| 839 | case Type::F32: | ||
| 840 | return Inst<F32>(Opcode::FPRecipSqrt32, value); | ||
| 841 | case Type::F64: | ||
| 842 | return Inst<F64>(Opcode::FPRecipSqrt64, value); | ||
| 843 | default: | ||
| 844 | ThrowInvalidType(value.Type()); | ||
| 845 | } | ||
| 846 | } | ||
| 847 | |||
| 848 | F32 IREmitter::FPSqrt(const F32& value) { | ||
| 849 | return Inst<F32>(Opcode::FPSqrt, value); | ||
| 850 | } | ||
| 851 | |||
| 852 | F16F32F64 IREmitter::FPSaturate(const F16F32F64& value) { | ||
| 853 | switch (value.Type()) { | ||
| 854 | case Type::F16: | ||
| 855 | return Inst<F16>(Opcode::FPSaturate16, value); | ||
| 856 | case Type::F32: | ||
| 857 | return Inst<F32>(Opcode::FPSaturate32, value); | ||
| 858 | case Type::F64: | ||
| 859 | return Inst<F64>(Opcode::FPSaturate64, value); | ||
| 860 | default: | ||
| 861 | ThrowInvalidType(value.Type()); | ||
| 862 | } | ||
| 863 | } | ||
| 864 | |||
| 865 | F16F32F64 IREmitter::FPClamp(const F16F32F64& value, const F16F32F64& min_value, | ||
| 866 | const F16F32F64& max_value) { | ||
| 867 | if (value.Type() != min_value.Type() || value.Type() != max_value.Type()) { | ||
| 868 | throw InvalidArgument("Mismatching types {}, {}, and {}", value.Type(), min_value.Type(), | ||
| 869 | max_value.Type()); | ||
| 870 | } | ||
| 871 | switch (value.Type()) { | ||
| 872 | case Type::F16: | ||
| 873 | return Inst<F16>(Opcode::FPClamp16, value, min_value, max_value); | ||
| 874 | case Type::F32: | ||
| 875 | return Inst<F32>(Opcode::FPClamp32, value, min_value, max_value); | ||
| 876 | case Type::F64: | ||
| 877 | return Inst<F64>(Opcode::FPClamp64, value, min_value, max_value); | ||
| 878 | default: | ||
| 879 | ThrowInvalidType(value.Type()); | ||
| 880 | } | ||
| 881 | } | ||
| 882 | |||
| 883 | F16F32F64 IREmitter::FPRoundEven(const F16F32F64& value, FpControl control) { | ||
| 884 | switch (value.Type()) { | ||
| 885 | case Type::F16: | ||
| 886 | return Inst<F16>(Opcode::FPRoundEven16, Flags{control}, value); | ||
| 887 | case Type::F32: | ||
| 888 | return Inst<F32>(Opcode::FPRoundEven32, Flags{control}, value); | ||
| 889 | case Type::F64: | ||
| 890 | return Inst<F64>(Opcode::FPRoundEven64, Flags{control}, value); | ||
| 891 | default: | ||
| 892 | ThrowInvalidType(value.Type()); | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | F16F32F64 IREmitter::FPFloor(const F16F32F64& value, FpControl control) { | ||
| 897 | switch (value.Type()) { | ||
| 898 | case Type::F16: | ||
| 899 | return Inst<F16>(Opcode::FPFloor16, Flags{control}, value); | ||
| 900 | case Type::F32: | ||
| 901 | return Inst<F32>(Opcode::FPFloor32, Flags{control}, value); | ||
| 902 | case Type::F64: | ||
| 903 | return Inst<F64>(Opcode::FPFloor64, Flags{control}, value); | ||
| 904 | default: | ||
| 905 | ThrowInvalidType(value.Type()); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | |||
| 909 | F16F32F64 IREmitter::FPCeil(const F16F32F64& value, FpControl control) { | ||
| 910 | switch (value.Type()) { | ||
| 911 | case Type::F16: | ||
| 912 | return Inst<F16>(Opcode::FPCeil16, Flags{control}, value); | ||
| 913 | case Type::F32: | ||
| 914 | return Inst<F32>(Opcode::FPCeil32, Flags{control}, value); | ||
| 915 | case Type::F64: | ||
| 916 | return Inst<F64>(Opcode::FPCeil64, Flags{control}, value); | ||
| 917 | default: | ||
| 918 | ThrowInvalidType(value.Type()); | ||
| 919 | } | ||
| 920 | } | ||
| 921 | |||
| 922 | F16F32F64 IREmitter::FPTrunc(const F16F32F64& value, FpControl control) { | ||
| 923 | switch (value.Type()) { | ||
| 924 | case Type::F16: | ||
| 925 | return Inst<F16>(Opcode::FPTrunc16, Flags{control}, value); | ||
| 926 | case Type::F32: | ||
| 927 | return Inst<F32>(Opcode::FPTrunc32, Flags{control}, value); | ||
| 928 | case Type::F64: | ||
| 929 | return Inst<F64>(Opcode::FPTrunc64, Flags{control}, value); | ||
| 930 | default: | ||
| 931 | ThrowInvalidType(value.Type()); | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | U1 IREmitter::FPEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control, bool ordered) { | ||
| 936 | if (lhs.Type() != rhs.Type()) { | ||
| 937 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 938 | } | ||
| 939 | switch (lhs.Type()) { | ||
| 940 | case Type::F16: | ||
| 941 | return Inst<U1>(ordered ? Opcode::FPOrdEqual16 : Opcode::FPUnordEqual16, Flags{control}, | ||
| 942 | lhs, rhs); | ||
| 943 | case Type::F32: | ||
| 944 | return Inst<U1>(ordered ? Opcode::FPOrdEqual32 : Opcode::FPUnordEqual32, Flags{control}, | ||
| 945 | lhs, rhs); | ||
| 946 | case Type::F64: | ||
| 947 | return Inst<U1>(ordered ? Opcode::FPOrdEqual64 : Opcode::FPUnordEqual64, Flags{control}, | ||
| 948 | lhs, rhs); | ||
| 949 | default: | ||
| 950 | ThrowInvalidType(lhs.Type()); | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | U1 IREmitter::FPNotEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control, | ||
| 955 | bool ordered) { | ||
| 956 | if (lhs.Type() != rhs.Type()) { | ||
| 957 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 958 | } | ||
| 959 | switch (lhs.Type()) { | ||
| 960 | case Type::F16: | ||
| 961 | return Inst<U1>(ordered ? Opcode::FPOrdNotEqual16 : Opcode::FPUnordNotEqual16, | ||
| 962 | Flags{control}, lhs, rhs); | ||
| 963 | case Type::F32: | ||
| 964 | return Inst<U1>(ordered ? Opcode::FPOrdNotEqual32 : Opcode::FPUnordNotEqual32, | ||
| 965 | Flags{control}, lhs, rhs); | ||
| 966 | case Type::F64: | ||
| 967 | return Inst<U1>(ordered ? Opcode::FPOrdNotEqual64 : Opcode::FPUnordNotEqual64, | ||
| 968 | Flags{control}, lhs, rhs); | ||
| 969 | default: | ||
| 970 | ThrowInvalidType(lhs.Type()); | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | U1 IREmitter::FPLessThan(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control, | ||
| 975 | bool ordered) { | ||
| 976 | if (lhs.Type() != rhs.Type()) { | ||
| 977 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 978 | } | ||
| 979 | switch (lhs.Type()) { | ||
| 980 | case Type::F16: | ||
| 981 | return Inst<U1>(ordered ? Opcode::FPOrdLessThan16 : Opcode::FPUnordLessThan16, | ||
| 982 | Flags{control}, lhs, rhs); | ||
| 983 | case Type::F32: | ||
| 984 | return Inst<U1>(ordered ? Opcode::FPOrdLessThan32 : Opcode::FPUnordLessThan32, | ||
| 985 | Flags{control}, lhs, rhs); | ||
| 986 | case Type::F64: | ||
| 987 | return Inst<U1>(ordered ? Opcode::FPOrdLessThan64 : Opcode::FPUnordLessThan64, | ||
| 988 | Flags{control}, lhs, rhs); | ||
| 989 | default: | ||
| 990 | ThrowInvalidType(lhs.Type()); | ||
| 991 | } | ||
| 992 | } | ||
| 993 | |||
| 994 | U1 IREmitter::FPGreaterThan(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control, | ||
| 995 | bool ordered) { | ||
| 996 | if (lhs.Type() != rhs.Type()) { | ||
| 997 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 998 | } | ||
| 999 | switch (lhs.Type()) { | ||
| 1000 | case Type::F16: | ||
| 1001 | return Inst<U1>(ordered ? Opcode::FPOrdGreaterThan16 : Opcode::FPUnordGreaterThan16, | ||
| 1002 | Flags{control}, lhs, rhs); | ||
| 1003 | case Type::F32: | ||
| 1004 | return Inst<U1>(ordered ? Opcode::FPOrdGreaterThan32 : Opcode::FPUnordGreaterThan32, | ||
| 1005 | Flags{control}, lhs, rhs); | ||
| 1006 | case Type::F64: | ||
| 1007 | return Inst<U1>(ordered ? Opcode::FPOrdGreaterThan64 : Opcode::FPUnordGreaterThan64, | ||
| 1008 | Flags{control}, lhs, rhs); | ||
| 1009 | default: | ||
| 1010 | ThrowInvalidType(lhs.Type()); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | U1 IREmitter::FPLessThanEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control, | ||
| 1015 | bool ordered) { | ||
| 1016 | if (lhs.Type() != rhs.Type()) { | ||
| 1017 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1018 | } | ||
| 1019 | switch (lhs.Type()) { | ||
| 1020 | case Type::F16: | ||
| 1021 | return Inst<U1>(ordered ? Opcode::FPOrdLessThanEqual16 : Opcode::FPUnordLessThanEqual16, | ||
| 1022 | Flags{control}, lhs, rhs); | ||
| 1023 | case Type::F32: | ||
| 1024 | return Inst<U1>(ordered ? Opcode::FPOrdLessThanEqual32 : Opcode::FPUnordLessThanEqual32, | ||
| 1025 | Flags{control}, lhs, rhs); | ||
| 1026 | case Type::F64: | ||
| 1027 | return Inst<U1>(ordered ? Opcode::FPOrdLessThanEqual64 : Opcode::FPUnordLessThanEqual64, | ||
| 1028 | Flags{control}, lhs, rhs); | ||
| 1029 | default: | ||
| 1030 | ThrowInvalidType(lhs.Type()); | ||
| 1031 | } | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | U1 IREmitter::FPGreaterThanEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control, | ||
| 1035 | bool ordered) { | ||
| 1036 | if (lhs.Type() != rhs.Type()) { | ||
| 1037 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1038 | } | ||
| 1039 | switch (lhs.Type()) { | ||
| 1040 | case Type::F16: | ||
| 1041 | return Inst<U1>(ordered ? Opcode::FPOrdGreaterThanEqual16 | ||
| 1042 | : Opcode::FPUnordGreaterThanEqual16, | ||
| 1043 | Flags{control}, lhs, rhs); | ||
| 1044 | case Type::F32: | ||
| 1045 | return Inst<U1>(ordered ? Opcode::FPOrdGreaterThanEqual32 | ||
| 1046 | : Opcode::FPUnordGreaterThanEqual32, | ||
| 1047 | Flags{control}, lhs, rhs); | ||
| 1048 | case Type::F64: | ||
| 1049 | return Inst<U1>(ordered ? Opcode::FPOrdGreaterThanEqual64 | ||
| 1050 | : Opcode::FPUnordGreaterThanEqual64, | ||
| 1051 | Flags{control}, lhs, rhs); | ||
| 1052 | default: | ||
| 1053 | ThrowInvalidType(lhs.Type()); | ||
| 1054 | } | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | U1 IREmitter::FPIsNan(const F16F32F64& value) { | ||
| 1058 | switch (value.Type()) { | ||
| 1059 | case Type::F16: | ||
| 1060 | return Inst<U1>(Opcode::FPIsNan16, value); | ||
| 1061 | case Type::F32: | ||
| 1062 | return Inst<U1>(Opcode::FPIsNan32, value); | ||
| 1063 | case Type::F64: | ||
| 1064 | return Inst<U1>(Opcode::FPIsNan64, value); | ||
| 1065 | default: | ||
| 1066 | ThrowInvalidType(value.Type()); | ||
| 1067 | } | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | U1 IREmitter::FPOrdered(const F16F32F64& lhs, const F16F32F64& rhs) { | ||
| 1071 | if (lhs.Type() != rhs.Type()) { | ||
| 1072 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1073 | } | ||
| 1074 | return LogicalAnd(LogicalNot(FPIsNan(lhs)), LogicalNot(FPIsNan(rhs))); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | U1 IREmitter::FPUnordered(const F16F32F64& lhs, const F16F32F64& rhs) { | ||
| 1078 | if (lhs.Type() != rhs.Type()) { | ||
| 1079 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1080 | } | ||
| 1081 | return LogicalOr(FPIsNan(lhs), FPIsNan(rhs)); | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | F32F64 IREmitter::FPMax(const F32F64& lhs, const F32F64& rhs, FpControl control) { | ||
| 1085 | if (lhs.Type() != rhs.Type()) { | ||
| 1086 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1087 | } | ||
| 1088 | switch (lhs.Type()) { | ||
| 1089 | case Type::F32: | ||
| 1090 | return Inst<F32>(Opcode::FPMax32, Flags{control}, lhs, rhs); | ||
| 1091 | case Type::F64: | ||
| 1092 | return Inst<F64>(Opcode::FPMax64, Flags{control}, lhs, rhs); | ||
| 1093 | default: | ||
| 1094 | ThrowInvalidType(lhs.Type()); | ||
| 1095 | } | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | F32F64 IREmitter::FPMin(const F32F64& lhs, const F32F64& rhs, FpControl control) { | ||
| 1099 | if (lhs.Type() != rhs.Type()) { | ||
| 1100 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1101 | } | ||
| 1102 | switch (lhs.Type()) { | ||
| 1103 | case Type::F32: | ||
| 1104 | return Inst<F32>(Opcode::FPMin32, Flags{control}, lhs, rhs); | ||
| 1105 | case Type::F64: | ||
| 1106 | return Inst<F64>(Opcode::FPMin64, Flags{control}, lhs, rhs); | ||
| 1107 | default: | ||
| 1108 | ThrowInvalidType(lhs.Type()); | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | U32U64 IREmitter::IAdd(const U32U64& a, const U32U64& b) { | ||
| 1113 | if (a.Type() != b.Type()) { | ||
| 1114 | throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type()); | ||
| 1115 | } | ||
| 1116 | switch (a.Type()) { | ||
| 1117 | case Type::U32: | ||
| 1118 | return Inst<U32>(Opcode::IAdd32, a, b); | ||
| 1119 | case Type::U64: | ||
| 1120 | return Inst<U64>(Opcode::IAdd64, a, b); | ||
| 1121 | default: | ||
| 1122 | ThrowInvalidType(a.Type()); | ||
| 1123 | } | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | U32U64 IREmitter::ISub(const U32U64& a, const U32U64& b) { | ||
| 1127 | if (a.Type() != b.Type()) { | ||
| 1128 | throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type()); | ||
| 1129 | } | ||
| 1130 | switch (a.Type()) { | ||
| 1131 | case Type::U32: | ||
| 1132 | return Inst<U32>(Opcode::ISub32, a, b); | ||
| 1133 | case Type::U64: | ||
| 1134 | return Inst<U64>(Opcode::ISub64, a, b); | ||
| 1135 | default: | ||
| 1136 | ThrowInvalidType(a.Type()); | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | U32 IREmitter::IMul(const U32& a, const U32& b) { | ||
| 1141 | return Inst<U32>(Opcode::IMul32, a, b); | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | U32U64 IREmitter::INeg(const U32U64& value) { | ||
| 1145 | switch (value.Type()) { | ||
| 1146 | case Type::U32: | ||
| 1147 | return Inst<U32>(Opcode::INeg32, value); | ||
| 1148 | case Type::U64: | ||
| 1149 | return Inst<U64>(Opcode::INeg64, value); | ||
| 1150 | default: | ||
| 1151 | ThrowInvalidType(value.Type()); | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | U32 IREmitter::IAbs(const U32& value) { | ||
| 1156 | return Inst<U32>(Opcode::IAbs32, value); | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | U32U64 IREmitter::ShiftLeftLogical(const U32U64& base, const U32& shift) { | ||
| 1160 | switch (base.Type()) { | ||
| 1161 | case Type::U32: | ||
| 1162 | return Inst<U32>(Opcode::ShiftLeftLogical32, base, shift); | ||
| 1163 | case Type::U64: | ||
| 1164 | return Inst<U64>(Opcode::ShiftLeftLogical64, base, shift); | ||
| 1165 | default: | ||
| 1166 | ThrowInvalidType(base.Type()); | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | U32U64 IREmitter::ShiftRightLogical(const U32U64& base, const U32& shift) { | ||
| 1171 | switch (base.Type()) { | ||
| 1172 | case Type::U32: | ||
| 1173 | return Inst<U32>(Opcode::ShiftRightLogical32, base, shift); | ||
| 1174 | case Type::U64: | ||
| 1175 | return Inst<U64>(Opcode::ShiftRightLogical64, base, shift); | ||
| 1176 | default: | ||
| 1177 | ThrowInvalidType(base.Type()); | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | U32U64 IREmitter::ShiftRightArithmetic(const U32U64& base, const U32& shift) { | ||
| 1182 | switch (base.Type()) { | ||
| 1183 | case Type::U32: | ||
| 1184 | return Inst<U32>(Opcode::ShiftRightArithmetic32, base, shift); | ||
| 1185 | case Type::U64: | ||
| 1186 | return Inst<U64>(Opcode::ShiftRightArithmetic64, base, shift); | ||
| 1187 | default: | ||
| 1188 | ThrowInvalidType(base.Type()); | ||
| 1189 | } | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | U32 IREmitter::BitwiseAnd(const U32& a, const U32& b) { | ||
| 1193 | return Inst<U32>(Opcode::BitwiseAnd32, a, b); | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | U32 IREmitter::BitwiseOr(const U32& a, const U32& b) { | ||
| 1197 | return Inst<U32>(Opcode::BitwiseOr32, a, b); | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | U32 IREmitter::BitwiseXor(const U32& a, const U32& b) { | ||
| 1201 | return Inst<U32>(Opcode::BitwiseXor32, a, b); | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | U32 IREmitter::BitFieldInsert(const U32& base, const U32& insert, const U32& offset, | ||
| 1205 | const U32& count) { | ||
| 1206 | return Inst<U32>(Opcode::BitFieldInsert, base, insert, offset, count); | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | U32 IREmitter::BitFieldExtract(const U32& base, const U32& offset, const U32& count, | ||
| 1210 | bool is_signed) { | ||
| 1211 | return Inst<U32>(is_signed ? Opcode::BitFieldSExtract : Opcode::BitFieldUExtract, base, offset, | ||
| 1212 | count); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | U32 IREmitter::BitReverse(const U32& value) { | ||
| 1216 | return Inst<U32>(Opcode::BitReverse32, value); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | U32 IREmitter::BitCount(const U32& value) { | ||
| 1220 | return Inst<U32>(Opcode::BitCount32, value); | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | U32 IREmitter::BitwiseNot(const U32& value) { | ||
| 1224 | return Inst<U32>(Opcode::BitwiseNot32, value); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | U32 IREmitter::FindSMsb(const U32& value) { | ||
| 1228 | return Inst<U32>(Opcode::FindSMsb32, value); | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | U32 IREmitter::FindUMsb(const U32& value) { | ||
| 1232 | return Inst<U32>(Opcode::FindUMsb32, value); | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | U32 IREmitter::SMin(const U32& a, const U32& b) { | ||
| 1236 | return Inst<U32>(Opcode::SMin32, a, b); | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | U32 IREmitter::UMin(const U32& a, const U32& b) { | ||
| 1240 | return Inst<U32>(Opcode::UMin32, a, b); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | U32 IREmitter::IMin(const U32& a, const U32& b, bool is_signed) { | ||
| 1244 | return is_signed ? SMin(a, b) : UMin(a, b); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | U32 IREmitter::SMax(const U32& a, const U32& b) { | ||
| 1248 | return Inst<U32>(Opcode::SMax32, a, b); | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | U32 IREmitter::UMax(const U32& a, const U32& b) { | ||
| 1252 | return Inst<U32>(Opcode::UMax32, a, b); | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | U32 IREmitter::IMax(const U32& a, const U32& b, bool is_signed) { | ||
| 1256 | return is_signed ? SMax(a, b) : UMax(a, b); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | U32 IREmitter::SClamp(const U32& value, const U32& min, const U32& max) { | ||
| 1260 | return Inst<U32>(Opcode::SClamp32, value, min, max); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | U32 IREmitter::UClamp(const U32& value, const U32& min, const U32& max) { | ||
| 1264 | return Inst<U32>(Opcode::UClamp32, value, min, max); | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | U1 IREmitter::ILessThan(const U32& lhs, const U32& rhs, bool is_signed) { | ||
| 1268 | return Inst<U1>(is_signed ? Opcode::SLessThan : Opcode::ULessThan, lhs, rhs); | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | U1 IREmitter::IEqual(const U32U64& lhs, const U32U64& rhs) { | ||
| 1272 | if (lhs.Type() != rhs.Type()) { | ||
| 1273 | throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); | ||
| 1274 | } | ||
| 1275 | switch (lhs.Type()) { | ||
| 1276 | case Type::U32: | ||
| 1277 | return Inst<U1>(Opcode::IEqual, lhs, rhs); | ||
| 1278 | case Type::U64: { | ||
| 1279 | // Manually compare the unpacked values | ||
| 1280 | const Value lhs_vector{UnpackUint2x32(lhs)}; | ||
| 1281 | const Value rhs_vector{UnpackUint2x32(rhs)}; | ||
| 1282 | return LogicalAnd(IEqual(IR::U32{CompositeExtract(lhs_vector, 0)}, | ||
| 1283 | IR::U32{CompositeExtract(rhs_vector, 0)}), | ||
| 1284 | IEqual(IR::U32{CompositeExtract(lhs_vector, 1)}, | ||
| 1285 | IR::U32{CompositeExtract(rhs_vector, 1)})); | ||
| 1286 | } | ||
| 1287 | default: | ||
| 1288 | ThrowInvalidType(lhs.Type()); | ||
| 1289 | } | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | U1 IREmitter::ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed) { | ||
| 1293 | return Inst<U1>(is_signed ? Opcode::SLessThanEqual : Opcode::ULessThanEqual, lhs, rhs); | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | U1 IREmitter::IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed) { | ||
| 1297 | return Inst<U1>(is_signed ? Opcode::SGreaterThan : Opcode::UGreaterThan, lhs, rhs); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | U1 IREmitter::INotEqual(const U32& lhs, const U32& rhs) { | ||
| 1301 | return Inst<U1>(Opcode::INotEqual, lhs, rhs); | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | U1 IREmitter::IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed) { | ||
| 1305 | return Inst<U1>(is_signed ? Opcode::SGreaterThanEqual : Opcode::UGreaterThanEqual, lhs, rhs); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | U32 IREmitter::SharedAtomicIAdd(const U32& pointer_offset, const U32& value) { | ||
| 1309 | return Inst<U32>(Opcode::SharedAtomicIAdd32, pointer_offset, value); | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | U32 IREmitter::SharedAtomicSMin(const U32& pointer_offset, const U32& value) { | ||
| 1313 | return Inst<U32>(Opcode::SharedAtomicSMin32, pointer_offset, value); | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | U32 IREmitter::SharedAtomicUMin(const U32& pointer_offset, const U32& value) { | ||
| 1317 | return Inst<U32>(Opcode::SharedAtomicUMin32, pointer_offset, value); | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | U32 IREmitter::SharedAtomicIMin(const U32& pointer_offset, const U32& value, bool is_signed) { | ||
| 1321 | return is_signed ? SharedAtomicSMin(pointer_offset, value) | ||
| 1322 | : SharedAtomicUMin(pointer_offset, value); | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | U32 IREmitter::SharedAtomicSMax(const U32& pointer_offset, const U32& value) { | ||
| 1326 | return Inst<U32>(Opcode::SharedAtomicSMax32, pointer_offset, value); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | U32 IREmitter::SharedAtomicUMax(const U32& pointer_offset, const U32& value) { | ||
| 1330 | return Inst<U32>(Opcode::SharedAtomicUMax32, pointer_offset, value); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | U32 IREmitter::SharedAtomicIMax(const U32& pointer_offset, const U32& value, bool is_signed) { | ||
| 1334 | return is_signed ? SharedAtomicSMax(pointer_offset, value) | ||
| 1335 | : SharedAtomicUMax(pointer_offset, value); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | U32 IREmitter::SharedAtomicInc(const U32& pointer_offset, const U32& value) { | ||
| 1339 | return Inst<U32>(Opcode::SharedAtomicInc32, pointer_offset, value); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | U32 IREmitter::SharedAtomicDec(const U32& pointer_offset, const U32& value) { | ||
| 1343 | return Inst<U32>(Opcode::SharedAtomicDec32, pointer_offset, value); | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | U32 IREmitter::SharedAtomicAnd(const U32& pointer_offset, const U32& value) { | ||
| 1347 | return Inst<U32>(Opcode::SharedAtomicAnd32, pointer_offset, value); | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | U32 IREmitter::SharedAtomicOr(const U32& pointer_offset, const U32& value) { | ||
| 1351 | return Inst<U32>(Opcode::SharedAtomicOr32, pointer_offset, value); | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | U32 IREmitter::SharedAtomicXor(const U32& pointer_offset, const U32& value) { | ||
| 1355 | return Inst<U32>(Opcode::SharedAtomicXor32, pointer_offset, value); | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | U32U64 IREmitter::SharedAtomicExchange(const U32& pointer_offset, const U32U64& value) { | ||
| 1359 | switch (value.Type()) { | ||
| 1360 | case Type::U32: | ||
| 1361 | return Inst<U32>(Opcode::SharedAtomicExchange32, pointer_offset, value); | ||
| 1362 | case Type::U64: | ||
| 1363 | return Inst<U64>(Opcode::SharedAtomicExchange64, pointer_offset, value); | ||
| 1364 | default: | ||
| 1365 | ThrowInvalidType(pointer_offset.Type()); | ||
| 1366 | } | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | U32U64 IREmitter::GlobalAtomicIAdd(const U64& pointer_offset, const U32U64& value) { | ||
| 1370 | switch (value.Type()) { | ||
| 1371 | case Type::U32: | ||
| 1372 | return Inst<U32>(Opcode::GlobalAtomicIAdd32, pointer_offset, value); | ||
| 1373 | case Type::U64: | ||
| 1374 | return Inst<U64>(Opcode::GlobalAtomicIAdd64, pointer_offset, value); | ||
| 1375 | default: | ||
| 1376 | ThrowInvalidType(value.Type()); | ||
| 1377 | } | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | U32U64 IREmitter::GlobalAtomicSMin(const U64& pointer_offset, const U32U64& value) { | ||
| 1381 | switch (value.Type()) { | ||
| 1382 | case Type::U32: | ||
| 1383 | return Inst<U32>(Opcode::GlobalAtomicSMin32, pointer_offset, value); | ||
| 1384 | case Type::U64: | ||
| 1385 | return Inst<U64>(Opcode::GlobalAtomicSMin64, pointer_offset, value); | ||
| 1386 | default: | ||
| 1387 | ThrowInvalidType(value.Type()); | ||
| 1388 | } | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | U32U64 IREmitter::GlobalAtomicUMin(const U64& pointer_offset, const U32U64& value) { | ||
| 1392 | switch (value.Type()) { | ||
| 1393 | case Type::U32: | ||
| 1394 | return Inst<U32>(Opcode::GlobalAtomicUMin32, pointer_offset, value); | ||
| 1395 | case Type::U64: | ||
| 1396 | return Inst<U64>(Opcode::GlobalAtomicUMin64, pointer_offset, value); | ||
| 1397 | default: | ||
| 1398 | ThrowInvalidType(value.Type()); | ||
| 1399 | } | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | U32U64 IREmitter::GlobalAtomicIMin(const U64& pointer_offset, const U32U64& value, bool is_signed) { | ||
| 1403 | return is_signed ? GlobalAtomicSMin(pointer_offset, value) | ||
| 1404 | : GlobalAtomicUMin(pointer_offset, value); | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | U32U64 IREmitter::GlobalAtomicSMax(const U64& pointer_offset, const U32U64& value) { | ||
| 1408 | switch (value.Type()) { | ||
| 1409 | case Type::U32: | ||
| 1410 | return Inst<U32>(Opcode::GlobalAtomicSMax32, pointer_offset, value); | ||
| 1411 | case Type::U64: | ||
| 1412 | return Inst<U64>(Opcode::GlobalAtomicSMax64, pointer_offset, value); | ||
| 1413 | default: | ||
| 1414 | ThrowInvalidType(value.Type()); | ||
| 1415 | } | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | U32U64 IREmitter::GlobalAtomicUMax(const U64& pointer_offset, const U32U64& value) { | ||
| 1419 | switch (value.Type()) { | ||
| 1420 | case Type::U32: | ||
| 1421 | return Inst<U32>(Opcode::GlobalAtomicUMax32, pointer_offset, value); | ||
| 1422 | case Type::U64: | ||
| 1423 | return Inst<U64>(Opcode::GlobalAtomicUMax64, pointer_offset, value); | ||
| 1424 | default: | ||
| 1425 | ThrowInvalidType(value.Type()); | ||
| 1426 | } | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | U32U64 IREmitter::GlobalAtomicIMax(const U64& pointer_offset, const U32U64& value, bool is_signed) { | ||
| 1430 | return is_signed ? GlobalAtomicSMax(pointer_offset, value) | ||
| 1431 | : GlobalAtomicUMax(pointer_offset, value); | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | U32 IREmitter::GlobalAtomicInc(const U64& pointer_offset, const U32& value) { | ||
| 1435 | return Inst<U32>(Opcode::GlobalAtomicInc32, pointer_offset, value); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | U32 IREmitter::GlobalAtomicDec(const U64& pointer_offset, const U32& value) { | ||
| 1439 | return Inst<U32>(Opcode::GlobalAtomicDec32, pointer_offset, value); | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | U32U64 IREmitter::GlobalAtomicAnd(const U64& pointer_offset, const U32U64& value) { | ||
| 1443 | switch (value.Type()) { | ||
| 1444 | case Type::U32: | ||
| 1445 | return Inst<U32>(Opcode::GlobalAtomicAnd32, pointer_offset, value); | ||
| 1446 | case Type::U64: | ||
| 1447 | return Inst<U64>(Opcode::GlobalAtomicAnd64, pointer_offset, value); | ||
| 1448 | default: | ||
| 1449 | ThrowInvalidType(value.Type()); | ||
| 1450 | } | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | U32U64 IREmitter::GlobalAtomicOr(const U64& pointer_offset, const U32U64& value) { | ||
| 1454 | switch (value.Type()) { | ||
| 1455 | case Type::U32: | ||
| 1456 | return Inst<U32>(Opcode::GlobalAtomicOr32, pointer_offset, value); | ||
| 1457 | case Type::U64: | ||
| 1458 | return Inst<U64>(Opcode::GlobalAtomicOr64, pointer_offset, value); | ||
| 1459 | default: | ||
| 1460 | ThrowInvalidType(value.Type()); | ||
| 1461 | } | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | U32U64 IREmitter::GlobalAtomicXor(const U64& pointer_offset, const U32U64& value) { | ||
| 1465 | switch (value.Type()) { | ||
| 1466 | case Type::U32: | ||
| 1467 | return Inst<U32>(Opcode::GlobalAtomicXor32, pointer_offset, value); | ||
| 1468 | case Type::U64: | ||
| 1469 | return Inst<U64>(Opcode::GlobalAtomicXor64, pointer_offset, value); | ||
| 1470 | default: | ||
| 1471 | ThrowInvalidType(value.Type()); | ||
| 1472 | } | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | U32U64 IREmitter::GlobalAtomicExchange(const U64& pointer_offset, const U32U64& value) { | ||
| 1476 | switch (value.Type()) { | ||
| 1477 | case Type::U32: | ||
| 1478 | return Inst<U32>(Opcode::GlobalAtomicExchange32, pointer_offset, value); | ||
| 1479 | case Type::U64: | ||
| 1480 | return Inst<U64>(Opcode::GlobalAtomicExchange64, pointer_offset, value); | ||
| 1481 | default: | ||
| 1482 | ThrowInvalidType(pointer_offset.Type()); | ||
| 1483 | } | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | F32 IREmitter::GlobalAtomicF32Add(const U64& pointer_offset, const Value& value, | ||
| 1487 | const FpControl control) { | ||
| 1488 | return Inst<F32>(Opcode::GlobalAtomicAddF32, Flags{control}, pointer_offset, value); | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | Value IREmitter::GlobalAtomicF16x2Add(const U64& pointer_offset, const Value& value, | ||
| 1492 | const FpControl control) { | ||
| 1493 | return Inst(Opcode::GlobalAtomicAddF16x2, Flags{control}, pointer_offset, value); | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | Value IREmitter::GlobalAtomicF16x2Min(const U64& pointer_offset, const Value& value, | ||
| 1497 | const FpControl control) { | ||
| 1498 | return Inst(Opcode::GlobalAtomicMinF16x2, Flags{control}, pointer_offset, value); | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | Value IREmitter::GlobalAtomicF16x2Max(const U64& pointer_offset, const Value& value, | ||
| 1502 | const FpControl control) { | ||
| 1503 | return Inst(Opcode::GlobalAtomicMaxF16x2, Flags{control}, pointer_offset, value); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | U1 IREmitter::LogicalOr(const U1& a, const U1& b) { | ||
| 1507 | return Inst<U1>(Opcode::LogicalOr, a, b); | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | U1 IREmitter::LogicalAnd(const U1& a, const U1& b) { | ||
| 1511 | return Inst<U1>(Opcode::LogicalAnd, a, b); | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | U1 IREmitter::LogicalXor(const U1& a, const U1& b) { | ||
| 1515 | return Inst<U1>(Opcode::LogicalXor, a, b); | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | U1 IREmitter::LogicalNot(const U1& value) { | ||
| 1519 | return Inst<U1>(Opcode::LogicalNot, value); | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | U32U64 IREmitter::ConvertFToS(size_t bitsize, const F16F32F64& value) { | ||
| 1523 | switch (bitsize) { | ||
| 1524 | case 16: | ||
| 1525 | switch (value.Type()) { | ||
| 1526 | case Type::F16: | ||
| 1527 | return Inst<U32>(Opcode::ConvertS16F16, value); | ||
| 1528 | case Type::F32: | ||
| 1529 | return Inst<U32>(Opcode::ConvertS16F32, value); | ||
| 1530 | case Type::F64: | ||
| 1531 | return Inst<U32>(Opcode::ConvertS16F64, value); | ||
| 1532 | default: | ||
| 1533 | ThrowInvalidType(value.Type()); | ||
| 1534 | } | ||
| 1535 | case 32: | ||
| 1536 | switch (value.Type()) { | ||
| 1537 | case Type::F16: | ||
| 1538 | return Inst<U32>(Opcode::ConvertS32F16, value); | ||
| 1539 | case Type::F32: | ||
| 1540 | return Inst<U32>(Opcode::ConvertS32F32, value); | ||
| 1541 | case Type::F64: | ||
| 1542 | return Inst<U32>(Opcode::ConvertS32F64, value); | ||
| 1543 | default: | ||
| 1544 | ThrowInvalidType(value.Type()); | ||
| 1545 | } | ||
| 1546 | case 64: | ||
| 1547 | switch (value.Type()) { | ||
| 1548 | case Type::F16: | ||
| 1549 | return Inst<U64>(Opcode::ConvertS64F16, value); | ||
| 1550 | case Type::F32: | ||
| 1551 | return Inst<U64>(Opcode::ConvertS64F32, value); | ||
| 1552 | case Type::F64: | ||
| 1553 | return Inst<U64>(Opcode::ConvertS64F64, value); | ||
| 1554 | default: | ||
| 1555 | ThrowInvalidType(value.Type()); | ||
| 1556 | } | ||
| 1557 | default: | ||
| 1558 | throw InvalidArgument("Invalid destination bitsize {}", bitsize); | ||
| 1559 | } | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | U32U64 IREmitter::ConvertFToU(size_t bitsize, const F16F32F64& value) { | ||
| 1563 | switch (bitsize) { | ||
| 1564 | case 16: | ||
| 1565 | switch (value.Type()) { | ||
| 1566 | case Type::F16: | ||
| 1567 | return Inst<U32>(Opcode::ConvertU16F16, value); | ||
| 1568 | case Type::F32: | ||
| 1569 | return Inst<U32>(Opcode::ConvertU16F32, value); | ||
| 1570 | case Type::F64: | ||
| 1571 | return Inst<U32>(Opcode::ConvertU16F64, value); | ||
| 1572 | default: | ||
| 1573 | ThrowInvalidType(value.Type()); | ||
| 1574 | } | ||
| 1575 | case 32: | ||
| 1576 | switch (value.Type()) { | ||
| 1577 | case Type::F16: | ||
| 1578 | return Inst<U32>(Opcode::ConvertU32F16, value); | ||
| 1579 | case Type::F32: | ||
| 1580 | return Inst<U32>(Opcode::ConvertU32F32, value); | ||
| 1581 | case Type::F64: | ||
| 1582 | return Inst<U32>(Opcode::ConvertU32F64, value); | ||
| 1583 | default: | ||
| 1584 | ThrowInvalidType(value.Type()); | ||
| 1585 | } | ||
| 1586 | case 64: | ||
| 1587 | switch (value.Type()) { | ||
| 1588 | case Type::F16: | ||
| 1589 | return Inst<U64>(Opcode::ConvertU64F16, value); | ||
| 1590 | case Type::F32: | ||
| 1591 | return Inst<U64>(Opcode::ConvertU64F32, value); | ||
| 1592 | case Type::F64: | ||
| 1593 | return Inst<U64>(Opcode::ConvertU64F64, value); | ||
| 1594 | default: | ||
| 1595 | ThrowInvalidType(value.Type()); | ||
| 1596 | } | ||
| 1597 | default: | ||
| 1598 | throw InvalidArgument("Invalid destination bitsize {}", bitsize); | ||
| 1599 | } | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | U32U64 IREmitter::ConvertFToI(size_t bitsize, bool is_signed, const F16F32F64& value) { | ||
| 1603 | return is_signed ? ConvertFToS(bitsize, value) : ConvertFToU(bitsize, value); | ||
| 1604 | } | ||
| 1605 | |||
| 1606 | F16F32F64 IREmitter::ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Value& value, | ||
| 1607 | FpControl control) { | ||
| 1608 | switch (dest_bitsize) { | ||
| 1609 | case 16: | ||
| 1610 | switch (src_bitsize) { | ||
| 1611 | case 8: | ||
| 1612 | return Inst<F16>(Opcode::ConvertF16S8, Flags{control}, value); | ||
| 1613 | case 16: | ||
| 1614 | return Inst<F16>(Opcode::ConvertF16S16, Flags{control}, value); | ||
| 1615 | case 32: | ||
| 1616 | return Inst<F16>(Opcode::ConvertF16S32, Flags{control}, value); | ||
| 1617 | case 64: | ||
| 1618 | return Inst<F16>(Opcode::ConvertF16S64, Flags{control}, value); | ||
| 1619 | } | ||
| 1620 | break; | ||
| 1621 | case 32: | ||
| 1622 | switch (src_bitsize) { | ||
| 1623 | case 8: | ||
| 1624 | return Inst<F32>(Opcode::ConvertF32S8, Flags{control}, value); | ||
| 1625 | case 16: | ||
| 1626 | return Inst<F32>(Opcode::ConvertF32S16, Flags{control}, value); | ||
| 1627 | case 32: | ||
| 1628 | return Inst<F32>(Opcode::ConvertF32S32, Flags{control}, value); | ||
| 1629 | case 64: | ||
| 1630 | return Inst<F32>(Opcode::ConvertF32S64, Flags{control}, value); | ||
| 1631 | } | ||
| 1632 | break; | ||
| 1633 | case 64: | ||
| 1634 | switch (src_bitsize) { | ||
| 1635 | case 8: | ||
| 1636 | return Inst<F64>(Opcode::ConvertF64S8, Flags{control}, value); | ||
| 1637 | case 16: | ||
| 1638 | return Inst<F64>(Opcode::ConvertF64S16, Flags{control}, value); | ||
| 1639 | case 32: | ||
| 1640 | return Inst<F64>(Opcode::ConvertF64S32, Flags{control}, value); | ||
| 1641 | case 64: | ||
| 1642 | return Inst<F64>(Opcode::ConvertF64S64, Flags{control}, value); | ||
| 1643 | } | ||
| 1644 | break; | ||
| 1645 | } | ||
| 1646 | throw InvalidArgument("Invalid bit size combination dst={} src={}", dest_bitsize, src_bitsize); | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | F16F32F64 IREmitter::ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Value& value, | ||
| 1650 | FpControl control) { | ||
| 1651 | switch (dest_bitsize) { | ||
| 1652 | case 16: | ||
| 1653 | switch (src_bitsize) { | ||
| 1654 | case 8: | ||
| 1655 | return Inst<F16>(Opcode::ConvertF16U8, Flags{control}, value); | ||
| 1656 | case 16: | ||
| 1657 | return Inst<F16>(Opcode::ConvertF16U16, Flags{control}, value); | ||
| 1658 | case 32: | ||
| 1659 | return Inst<F16>(Opcode::ConvertF16U32, Flags{control}, value); | ||
| 1660 | case 64: | ||
| 1661 | return Inst<F16>(Opcode::ConvertF16U64, Flags{control}, value); | ||
| 1662 | } | ||
| 1663 | break; | ||
| 1664 | case 32: | ||
| 1665 | switch (src_bitsize) { | ||
| 1666 | case 8: | ||
| 1667 | return Inst<F32>(Opcode::ConvertF32U8, Flags{control}, value); | ||
| 1668 | case 16: | ||
| 1669 | return Inst<F32>(Opcode::ConvertF32U16, Flags{control}, value); | ||
| 1670 | case 32: | ||
| 1671 | return Inst<F32>(Opcode::ConvertF32U32, Flags{control}, value); | ||
| 1672 | case 64: | ||
| 1673 | return Inst<F32>(Opcode::ConvertF32U64, Flags{control}, value); | ||
| 1674 | } | ||
| 1675 | break; | ||
| 1676 | case 64: | ||
| 1677 | switch (src_bitsize) { | ||
| 1678 | case 8: | ||
| 1679 | return Inst<F64>(Opcode::ConvertF64U8, Flags{control}, value); | ||
| 1680 | case 16: | ||
| 1681 | return Inst<F64>(Opcode::ConvertF64U16, Flags{control}, value); | ||
| 1682 | case 32: | ||
| 1683 | return Inst<F64>(Opcode::ConvertF64U32, Flags{control}, value); | ||
| 1684 | case 64: | ||
| 1685 | return Inst<F64>(Opcode::ConvertF64U64, Flags{control}, value); | ||
| 1686 | } | ||
| 1687 | break; | ||
| 1688 | } | ||
| 1689 | throw InvalidArgument("Invalid bit size combination dst={} src={}", dest_bitsize, src_bitsize); | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | F16F32F64 IREmitter::ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed, | ||
| 1693 | const Value& value, FpControl control) { | ||
| 1694 | return is_signed ? ConvertSToF(dest_bitsize, src_bitsize, value, control) | ||
| 1695 | : ConvertUToF(dest_bitsize, src_bitsize, value, control); | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | U32U64 IREmitter::UConvert(size_t result_bitsize, const U32U64& value) { | ||
| 1699 | switch (result_bitsize) { | ||
| 1700 | case 32: | ||
| 1701 | switch (value.Type()) { | ||
| 1702 | case Type::U32: | ||
| 1703 | // Nothing to do | ||
| 1704 | return value; | ||
| 1705 | case Type::U64: | ||
| 1706 | return Inst<U32>(Opcode::ConvertU32U64, value); | ||
| 1707 | default: | ||
| 1708 | break; | ||
| 1709 | } | ||
| 1710 | break; | ||
| 1711 | case 64: | ||
| 1712 | switch (value.Type()) { | ||
| 1713 | case Type::U32: | ||
| 1714 | return Inst<U64>(Opcode::ConvertU64U32, value); | ||
| 1715 | case Type::U64: | ||
| 1716 | // Nothing to do | ||
| 1717 | return value; | ||
| 1718 | default: | ||
| 1719 | break; | ||
| 1720 | } | ||
| 1721 | } | ||
| 1722 | throw NotImplementedException("Conversion from {} to {} bits", value.Type(), result_bitsize); | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | F16F32F64 IREmitter::FPConvert(size_t result_bitsize, const F16F32F64& value, FpControl control) { | ||
| 1726 | switch (result_bitsize) { | ||
| 1727 | case 16: | ||
| 1728 | switch (value.Type()) { | ||
| 1729 | case Type::F16: | ||
| 1730 | // Nothing to do | ||
| 1731 | return value; | ||
| 1732 | case Type::F32: | ||
| 1733 | return Inst<F16>(Opcode::ConvertF16F32, Flags{control}, value); | ||
| 1734 | case Type::F64: | ||
| 1735 | throw LogicError("Illegal conversion from F64 to F16"); | ||
| 1736 | default: | ||
| 1737 | break; | ||
| 1738 | } | ||
| 1739 | break; | ||
| 1740 | case 32: | ||
| 1741 | switch (value.Type()) { | ||
| 1742 | case Type::F16: | ||
| 1743 | return Inst<F32>(Opcode::ConvertF32F16, Flags{control}, value); | ||
| 1744 | case Type::F32: | ||
| 1745 | // Nothing to do | ||
| 1746 | return value; | ||
| 1747 | case Type::F64: | ||
| 1748 | return Inst<F32>(Opcode::ConvertF32F64, Flags{control}, value); | ||
| 1749 | default: | ||
| 1750 | break; | ||
| 1751 | } | ||
| 1752 | break; | ||
| 1753 | case 64: | ||
| 1754 | switch (value.Type()) { | ||
| 1755 | case Type::F16: | ||
| 1756 | throw LogicError("Illegal conversion from F16 to F64"); | ||
| 1757 | case Type::F32: | ||
| 1758 | return Inst<F64>(Opcode::ConvertF64F32, Flags{control}, value); | ||
| 1759 | case Type::F64: | ||
| 1760 | // Nothing to do | ||
| 1761 | return value; | ||
| 1762 | default: | ||
| 1763 | break; | ||
| 1764 | } | ||
| 1765 | break; | ||
| 1766 | } | ||
| 1767 | throw NotImplementedException("Conversion from {} to {} bits", value.Type(), result_bitsize); | ||
| 1768 | } | ||
| 1769 | |||
| 1770 | Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& coords, const F32& bias, | ||
| 1771 | const Value& offset, const F32& lod_clamp, | ||
| 1772 | TextureInstInfo info) { | ||
| 1773 | const Value bias_lc{MakeLodClampPair(*this, bias, lod_clamp)}; | ||
| 1774 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageSampleImplicitLod | ||
| 1775 | : Opcode::BindlessImageSampleImplicitLod}; | ||
| 1776 | return Inst(op, Flags{info}, handle, coords, bias_lc, offset); | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | Value IREmitter::ImageSampleExplicitLod(const Value& handle, const Value& coords, const F32& lod, | ||
| 1780 | const Value& offset, TextureInstInfo info) { | ||
| 1781 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageSampleExplicitLod | ||
| 1782 | : Opcode::BindlessImageSampleExplicitLod}; | ||
| 1783 | return Inst(op, Flags{info}, handle, coords, lod, offset); | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | F32 IREmitter::ImageSampleDrefImplicitLod(const Value& handle, const Value& coords, const F32& dref, | ||
| 1787 | const F32& bias, const Value& offset, | ||
| 1788 | const F32& lod_clamp, TextureInstInfo info) { | ||
| 1789 | const Value bias_lc{MakeLodClampPair(*this, bias, lod_clamp)}; | ||
| 1790 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageSampleDrefImplicitLod | ||
| 1791 | : Opcode::BindlessImageSampleDrefImplicitLod}; | ||
| 1792 | return Inst<F32>(op, Flags{info}, handle, coords, dref, bias_lc, offset); | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | F32 IREmitter::ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, const F32& dref, | ||
| 1796 | const F32& lod, const Value& offset, | ||
| 1797 | TextureInstInfo info) { | ||
| 1798 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageSampleDrefExplicitLod | ||
| 1799 | : Opcode::BindlessImageSampleDrefExplicitLod}; | ||
| 1800 | return Inst<F32>(op, Flags{info}, handle, coords, dref, lod, offset); | ||
| 1801 | } | ||
| 1802 | |||
| 1803 | Value IREmitter::ImageGather(const Value& handle, const Value& coords, const Value& offset, | ||
| 1804 | const Value& offset2, TextureInstInfo info) { | ||
| 1805 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGather : Opcode::BindlessImageGather}; | ||
| 1806 | return Inst(op, Flags{info}, handle, coords, offset, offset2); | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const Value& offset, | ||
| 1810 | const Value& offset2, const F32& dref, TextureInstInfo info) { | ||
| 1811 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGatherDref | ||
| 1812 | : Opcode::BindlessImageGatherDref}; | ||
| 1813 | return Inst(op, Flags{info}, handle, coords, offset, offset2, dref); | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Value& offset, | ||
| 1817 | const U32& lod, const U32& multisampling, TextureInstInfo info) { | ||
| 1818 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageFetch : Opcode::BindlessImageFetch}; | ||
| 1819 | return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { | ||
| 1823 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions | ||
| 1824 | : Opcode::BindlessImageQueryDimensions}; | ||
| 1825 | return Inst(op, handle, lod); | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { | ||
| 1829 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryLod | ||
| 1830 | : Opcode::BindlessImageQueryLod}; | ||
| 1831 | return Inst(op, Flags{info}, handle, coords); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivates, | ||
| 1835 | const Value& offset, const F32& lod_clamp, TextureInstInfo info) { | ||
| 1836 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient | ||
| 1837 | : Opcode::BindlessImageGradient}; | ||
| 1838 | return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { | ||
| 1842 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageRead : Opcode::BindlessImageRead}; | ||
| 1843 | return Inst(op, Flags{info}, handle, coords); | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value& color, | ||
| 1847 | TextureInstInfo info) { | ||
| 1848 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageWrite : Opcode::BindlessImageWrite}; | ||
| 1849 | Inst(op, Flags{info}, handle, coords, color); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | Value IREmitter::ImageAtomicIAdd(const Value& handle, const Value& coords, const Value& value, | ||
| 1853 | TextureInstInfo info) { | ||
| 1854 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicIAdd32 | ||
| 1855 | : Opcode::BindlessImageAtomicIAdd32}; | ||
| 1856 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | Value IREmitter::ImageAtomicSMin(const Value& handle, const Value& coords, const Value& value, | ||
| 1860 | TextureInstInfo info) { | ||
| 1861 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicSMin32 | ||
| 1862 | : Opcode::BindlessImageAtomicSMin32}; | ||
| 1863 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1864 | } | ||
| 1865 | |||
| 1866 | Value IREmitter::ImageAtomicUMin(const Value& handle, const Value& coords, const Value& value, | ||
| 1867 | TextureInstInfo info) { | ||
| 1868 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicUMin32 | ||
| 1869 | : Opcode::BindlessImageAtomicUMin32}; | ||
| 1870 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | Value IREmitter::ImageAtomicIMin(const Value& handle, const Value& coords, const Value& value, | ||
| 1874 | bool is_signed, TextureInstInfo info) { | ||
| 1875 | return is_signed ? ImageAtomicSMin(handle, coords, value, info) | ||
| 1876 | : ImageAtomicUMin(handle, coords, value, info); | ||
| 1877 | } | ||
| 1878 | |||
| 1879 | Value IREmitter::ImageAtomicSMax(const Value& handle, const Value& coords, const Value& value, | ||
| 1880 | TextureInstInfo info) { | ||
| 1881 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicSMax32 | ||
| 1882 | : Opcode::BindlessImageAtomicSMax32}; | ||
| 1883 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | Value IREmitter::ImageAtomicUMax(const Value& handle, const Value& coords, const Value& value, | ||
| 1887 | TextureInstInfo info) { | ||
| 1888 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicUMax32 | ||
| 1889 | : Opcode::BindlessImageAtomicUMax32}; | ||
| 1890 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | Value IREmitter::ImageAtomicIMax(const Value& handle, const Value& coords, const Value& value, | ||
| 1894 | bool is_signed, TextureInstInfo info) { | ||
| 1895 | return is_signed ? ImageAtomicSMax(handle, coords, value, info) | ||
| 1896 | : ImageAtomicUMax(handle, coords, value, info); | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | Value IREmitter::ImageAtomicInc(const Value& handle, const Value& coords, const Value& value, | ||
| 1900 | TextureInstInfo info) { | ||
| 1901 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicInc32 | ||
| 1902 | : Opcode::BindlessImageAtomicInc32}; | ||
| 1903 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | Value IREmitter::ImageAtomicDec(const Value& handle, const Value& coords, const Value& value, | ||
| 1907 | TextureInstInfo info) { | ||
| 1908 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicDec32 | ||
| 1909 | : Opcode::BindlessImageAtomicDec32}; | ||
| 1910 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | Value IREmitter::ImageAtomicAnd(const Value& handle, const Value& coords, const Value& value, | ||
| 1914 | TextureInstInfo info) { | ||
| 1915 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicAnd32 | ||
| 1916 | : Opcode::BindlessImageAtomicAnd32}; | ||
| 1917 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1918 | } | ||
| 1919 | |||
| 1920 | Value IREmitter::ImageAtomicOr(const Value& handle, const Value& coords, const Value& value, | ||
| 1921 | TextureInstInfo info) { | ||
| 1922 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicOr32 | ||
| 1923 | : Opcode::BindlessImageAtomicOr32}; | ||
| 1924 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | Value IREmitter::ImageAtomicXor(const Value& handle, const Value& coords, const Value& value, | ||
| 1928 | TextureInstInfo info) { | ||
| 1929 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicXor32 | ||
| 1930 | : Opcode::BindlessImageAtomicXor32}; | ||
| 1931 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1932 | } | ||
| 1933 | |||
| 1934 | Value IREmitter::ImageAtomicExchange(const Value& handle, const Value& coords, const Value& value, | ||
| 1935 | TextureInstInfo info) { | ||
| 1936 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageAtomicExchange32 | ||
| 1937 | : Opcode::BindlessImageAtomicExchange32}; | ||
| 1938 | return Inst(op, Flags{info}, handle, coords, value); | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | U1 IREmitter::VoteAll(const U1& value) { | ||
| 1942 | return Inst<U1>(Opcode::VoteAll, value); | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | U1 IREmitter::VoteAny(const U1& value) { | ||
| 1946 | return Inst<U1>(Opcode::VoteAny, value); | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | U1 IREmitter::VoteEqual(const U1& value) { | ||
| 1950 | return Inst<U1>(Opcode::VoteEqual, value); | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | U32 IREmitter::SubgroupBallot(const U1& value) { | ||
| 1954 | return Inst<U32>(Opcode::SubgroupBallot, value); | ||
| 1955 | } | ||
| 1956 | |||
| 1957 | U32 IREmitter::SubgroupEqMask() { | ||
| 1958 | return Inst<U32>(Opcode::SubgroupEqMask); | ||
| 1959 | } | ||
| 1960 | |||
| 1961 | U32 IREmitter::SubgroupLtMask() { | ||
| 1962 | return Inst<U32>(Opcode::SubgroupLtMask); | ||
| 1963 | } | ||
| 1964 | |||
| 1965 | U32 IREmitter::SubgroupLeMask() { | ||
| 1966 | return Inst<U32>(Opcode::SubgroupLeMask); | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | U32 IREmitter::SubgroupGtMask() { | ||
| 1970 | return Inst<U32>(Opcode::SubgroupGtMask); | ||
| 1971 | } | ||
| 1972 | |||
| 1973 | U32 IREmitter::SubgroupGeMask() { | ||
| 1974 | return Inst<U32>(Opcode::SubgroupGeMask); | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | U32 IREmitter::ShuffleIndex(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 1978 | const IR::U32& seg_mask) { | ||
| 1979 | return Inst<U32>(Opcode::ShuffleIndex, value, index, clamp, seg_mask); | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | U32 IREmitter::ShuffleUp(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 1983 | const IR::U32& seg_mask) { | ||
| 1984 | return Inst<U32>(Opcode::ShuffleUp, value, index, clamp, seg_mask); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | U32 IREmitter::ShuffleDown(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 1988 | const IR::U32& seg_mask) { | ||
| 1989 | return Inst<U32>(Opcode::ShuffleDown, value, index, clamp, seg_mask); | ||
| 1990 | } | ||
| 1991 | |||
| 1992 | U32 IREmitter::ShuffleButterfly(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 1993 | const IR::U32& seg_mask) { | ||
| 1994 | return Inst<U32>(Opcode::ShuffleButterfly, value, index, clamp, seg_mask); | ||
| 1995 | } | ||
| 1996 | |||
| 1997 | F32 IREmitter::FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, FpControl control) { | ||
| 1998 | return Inst<F32>(Opcode::FSwizzleAdd, Flags{control}, a, b, swizzle); | ||
| 1999 | } | ||
| 2000 | |||
| 2001 | F32 IREmitter::DPdxFine(const F32& a) { | ||
| 2002 | return Inst<F32>(Opcode::DPdxFine, a); | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | F32 IREmitter::DPdyFine(const F32& a) { | ||
| 2006 | return Inst<F32>(Opcode::DPdyFine, a); | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | F32 IREmitter::DPdxCoarse(const F32& a) { | ||
| 2010 | return Inst<F32>(Opcode::DPdxCoarse, a); | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | F32 IREmitter::DPdyCoarse(const F32& a) { | ||
| 2014 | return Inst<F32>(Opcode::DPdyCoarse, a); | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h new file mode 100644 index 000000000..53f7b3b06 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -0,0 +1,413 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstring> | ||
| 8 | #include <type_traits> | ||
| 9 | |||
| 10 | #include "shader_recompiler/frontend/ir/attribute.h" | ||
| 11 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/modifiers.h" | ||
| 13 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 14 | |||
| 15 | namespace Shader::IR { | ||
| 16 | |||
| 17 | class IREmitter { | ||
| 18 | public: | ||
| 19 | explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {} | ||
| 20 | explicit IREmitter(Block& block_, Block::iterator insertion_point_) | ||
| 21 | : block{&block_}, insertion_point{insertion_point_} {} | ||
| 22 | |||
| 23 | Block* block; | ||
| 24 | |||
| 25 | [[nodiscard]] U1 Imm1(bool value) const; | ||
| 26 | [[nodiscard]] U8 Imm8(u8 value) const; | ||
| 27 | [[nodiscard]] U16 Imm16(u16 value) const; | ||
| 28 | [[nodiscard]] U32 Imm32(u32 value) const; | ||
| 29 | [[nodiscard]] U32 Imm32(s32 value) const; | ||
| 30 | [[nodiscard]] F32 Imm32(f32 value) const; | ||
| 31 | [[nodiscard]] U64 Imm64(u64 value) const; | ||
| 32 | [[nodiscard]] U64 Imm64(s64 value) const; | ||
| 33 | [[nodiscard]] F64 Imm64(f64 value) const; | ||
| 34 | |||
| 35 | U1 ConditionRef(const U1& value); | ||
| 36 | void Reference(const Value& value); | ||
| 37 | |||
| 38 | void PhiMove(IR::Inst& phi, const Value& value); | ||
| 39 | |||
| 40 | void Prologue(); | ||
| 41 | void Epilogue(); | ||
| 42 | void DemoteToHelperInvocation(); | ||
| 43 | void EmitVertex(const U32& stream); | ||
| 44 | void EndPrimitive(const U32& stream); | ||
| 45 | |||
| 46 | [[nodiscard]] U32 GetReg(IR::Reg reg); | ||
| 47 | void SetReg(IR::Reg reg, const U32& value); | ||
| 48 | |||
| 49 | [[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false); | ||
| 50 | void SetPred(IR::Pred pred, const U1& value); | ||
| 51 | |||
| 52 | [[nodiscard]] U1 GetGotoVariable(u32 id); | ||
| 53 | void SetGotoVariable(u32 id, const U1& value); | ||
| 54 | |||
| 55 | [[nodiscard]] U32 GetIndirectBranchVariable(); | ||
| 56 | void SetIndirectBranchVariable(const U32& value); | ||
| 57 | |||
| 58 | [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); | ||
| 59 | [[nodiscard]] Value GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, | ||
| 60 | bool is_signed); | ||
| 61 | [[nodiscard]] F32 GetFloatCbuf(const U32& binding, const U32& byte_offset); | ||
| 62 | |||
| 63 | [[nodiscard]] U1 GetZFlag(); | ||
| 64 | [[nodiscard]] U1 GetSFlag(); | ||
| 65 | [[nodiscard]] U1 GetCFlag(); | ||
| 66 | [[nodiscard]] U1 GetOFlag(); | ||
| 67 | |||
| 68 | void SetZFlag(const U1& value); | ||
| 69 | void SetSFlag(const U1& value); | ||
| 70 | void SetCFlag(const U1& value); | ||
| 71 | void SetOFlag(const U1& value); | ||
| 72 | |||
| 73 | [[nodiscard]] U1 Condition(IR::Condition cond); | ||
| 74 | [[nodiscard]] U1 GetFlowTestResult(FlowTest test); | ||
| 75 | |||
| 76 | [[nodiscard]] F32 GetAttribute(IR::Attribute attribute); | ||
| 77 | [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex); | ||
| 78 | void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex); | ||
| 79 | |||
| 80 | [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address); | ||
| 81 | [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex); | ||
| 82 | void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex); | ||
| 83 | |||
| 84 | [[nodiscard]] F32 GetPatch(Patch patch); | ||
| 85 | void SetPatch(Patch patch, const F32& value); | ||
| 86 | |||
| 87 | void SetFragColor(u32 index, u32 component, const F32& value); | ||
| 88 | void SetSampleMask(const U32& value); | ||
| 89 | void SetFragDepth(const F32& value); | ||
| 90 | |||
| 91 | [[nodiscard]] U32 WorkgroupIdX(); | ||
| 92 | [[nodiscard]] U32 WorkgroupIdY(); | ||
| 93 | [[nodiscard]] U32 WorkgroupIdZ(); | ||
| 94 | |||
| 95 | [[nodiscard]] Value LocalInvocationId(); | ||
| 96 | [[nodiscard]] U32 LocalInvocationIdX(); | ||
| 97 | [[nodiscard]] U32 LocalInvocationIdY(); | ||
| 98 | [[nodiscard]] U32 LocalInvocationIdZ(); | ||
| 99 | |||
| 100 | [[nodiscard]] U32 InvocationId(); | ||
| 101 | [[nodiscard]] U32 SampleId(); | ||
| 102 | [[nodiscard]] U1 IsHelperInvocation(); | ||
| 103 | [[nodiscard]] F32 YDirection(); | ||
| 104 | |||
| 105 | [[nodiscard]] U32 LaneId(); | ||
| 106 | |||
| 107 | [[nodiscard]] U32 LoadGlobalU8(const U64& address); | ||
| 108 | [[nodiscard]] U32 LoadGlobalS8(const U64& address); | ||
| 109 | [[nodiscard]] U32 LoadGlobalU16(const U64& address); | ||
| 110 | [[nodiscard]] U32 LoadGlobalS16(const U64& address); | ||
| 111 | [[nodiscard]] U32 LoadGlobal32(const U64& address); | ||
| 112 | [[nodiscard]] Value LoadGlobal64(const U64& address); | ||
| 113 | [[nodiscard]] Value LoadGlobal128(const U64& address); | ||
| 114 | |||
| 115 | void WriteGlobalU8(const U64& address, const U32& value); | ||
| 116 | void WriteGlobalS8(const U64& address, const U32& value); | ||
| 117 | void WriteGlobalU16(const U64& address, const U32& value); | ||
| 118 | void WriteGlobalS16(const U64& address, const U32& value); | ||
| 119 | void WriteGlobal32(const U64& address, const U32& value); | ||
| 120 | void WriteGlobal64(const U64& address, const IR::Value& vector); | ||
| 121 | void WriteGlobal128(const U64& address, const IR::Value& vector); | ||
| 122 | |||
| 123 | [[nodiscard]] U32 LoadLocal(const U32& word_offset); | ||
| 124 | void WriteLocal(const U32& word_offset, const U32& value); | ||
| 125 | |||
| 126 | [[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset); | ||
| 127 | void WriteShared(int bit_size, const U32& offset, const Value& value); | ||
| 128 | |||
| 129 | [[nodiscard]] U1 GetZeroFromOp(const Value& op); | ||
| 130 | [[nodiscard]] U1 GetSignFromOp(const Value& op); | ||
| 131 | [[nodiscard]] U1 GetCarryFromOp(const Value& op); | ||
| 132 | [[nodiscard]] U1 GetOverflowFromOp(const Value& op); | ||
| 133 | [[nodiscard]] U1 GetSparseFromOp(const Value& op); | ||
| 134 | [[nodiscard]] U1 GetInBoundsFromOp(const Value& op); | ||
| 135 | |||
| 136 | [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2); | ||
| 137 | [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3); | ||
| 138 | [[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3, | ||
| 139 | const Value& e4); | ||
| 140 | [[nodiscard]] Value CompositeExtract(const Value& vector, size_t element); | ||
| 141 | [[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element); | ||
| 142 | |||
| 143 | [[nodiscard]] Value Select(const U1& condition, const Value& true_value, | ||
| 144 | const Value& false_value); | ||
| 145 | |||
| 146 | void Barrier(); | ||
| 147 | void WorkgroupMemoryBarrier(); | ||
| 148 | void DeviceMemoryBarrier(); | ||
| 149 | |||
| 150 | template <typename Dest, typename Source> | ||
| 151 | [[nodiscard]] Dest BitCast(const Source& value); | ||
| 152 | |||
| 153 | [[nodiscard]] U64 PackUint2x32(const Value& vector); | ||
| 154 | [[nodiscard]] Value UnpackUint2x32(const U64& value); | ||
| 155 | |||
| 156 | [[nodiscard]] U32 PackFloat2x16(const Value& vector); | ||
| 157 | [[nodiscard]] Value UnpackFloat2x16(const U32& value); | ||
| 158 | |||
| 159 | [[nodiscard]] U32 PackHalf2x16(const Value& vector); | ||
| 160 | [[nodiscard]] Value UnpackHalf2x16(const U32& value); | ||
| 161 | |||
| 162 | [[nodiscard]] F64 PackDouble2x32(const Value& vector); | ||
| 163 | [[nodiscard]] Value UnpackDouble2x32(const F64& value); | ||
| 164 | |||
| 165 | [[nodiscard]] F16F32F64 FPAdd(const F16F32F64& a, const F16F32F64& b, FpControl control = {}); | ||
| 166 | [[nodiscard]] F16F32F64 FPMul(const F16F32F64& a, const F16F32F64& b, FpControl control = {}); | ||
| 167 | [[nodiscard]] F16F32F64 FPFma(const F16F32F64& a, const F16F32F64& b, const F16F32F64& c, | ||
| 168 | FpControl control = {}); | ||
| 169 | |||
| 170 | [[nodiscard]] F16F32F64 FPAbs(const F16F32F64& value); | ||
| 171 | [[nodiscard]] F16F32F64 FPNeg(const F16F32F64& value); | ||
| 172 | [[nodiscard]] F16F32F64 FPAbsNeg(const F16F32F64& value, bool abs, bool neg); | ||
| 173 | |||
| 174 | [[nodiscard]] F32 FPCos(const F32& value); | ||
| 175 | [[nodiscard]] F32 FPSin(const F32& value); | ||
| 176 | [[nodiscard]] F32 FPExp2(const F32& value); | ||
| 177 | [[nodiscard]] F32 FPLog2(const F32& value); | ||
| 178 | [[nodiscard]] F32F64 FPRecip(const F32F64& value); | ||
| 179 | [[nodiscard]] F32F64 FPRecipSqrt(const F32F64& value); | ||
| 180 | [[nodiscard]] F32 FPSqrt(const F32& value); | ||
| 181 | [[nodiscard]] F16F32F64 FPSaturate(const F16F32F64& value); | ||
| 182 | [[nodiscard]] F16F32F64 FPClamp(const F16F32F64& value, const F16F32F64& min_value, | ||
| 183 | const F16F32F64& max_value); | ||
| 184 | [[nodiscard]] F16F32F64 FPRoundEven(const F16F32F64& value, FpControl control = {}); | ||
| 185 | [[nodiscard]] F16F32F64 FPFloor(const F16F32F64& value, FpControl control = {}); | ||
| 186 | [[nodiscard]] F16F32F64 FPCeil(const F16F32F64& value, FpControl control = {}); | ||
| 187 | [[nodiscard]] F16F32F64 FPTrunc(const F16F32F64& value, FpControl control = {}); | ||
| 188 | |||
| 189 | [[nodiscard]] U1 FPEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {}, | ||
| 190 | bool ordered = true); | ||
| 191 | [[nodiscard]] U1 FPNotEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {}, | ||
| 192 | bool ordered = true); | ||
| 193 | [[nodiscard]] U1 FPLessThan(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {}, | ||
| 194 | bool ordered = true); | ||
| 195 | [[nodiscard]] U1 FPGreaterThan(const F16F32F64& lhs, const F16F32F64& rhs, | ||
| 196 | FpControl control = {}, bool ordered = true); | ||
| 197 | [[nodiscard]] U1 FPLessThanEqual(const F16F32F64& lhs, const F16F32F64& rhs, | ||
| 198 | FpControl control = {}, bool ordered = true); | ||
| 199 | [[nodiscard]] U1 FPGreaterThanEqual(const F16F32F64& lhs, const F16F32F64& rhs, | ||
| 200 | FpControl control = {}, bool ordered = true); | ||
| 201 | [[nodiscard]] U1 FPIsNan(const F16F32F64& value); | ||
| 202 | [[nodiscard]] U1 FPOrdered(const F16F32F64& lhs, const F16F32F64& rhs); | ||
| 203 | [[nodiscard]] U1 FPUnordered(const F16F32F64& lhs, const F16F32F64& rhs); | ||
| 204 | [[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, FpControl control = {}); | ||
| 205 | [[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs, FpControl control = {}); | ||
| 206 | |||
| 207 | [[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b); | ||
| 208 | [[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b); | ||
| 209 | [[nodiscard]] U32 IMul(const U32& a, const U32& b); | ||
| 210 | [[nodiscard]] U32U64 INeg(const U32U64& value); | ||
| 211 | [[nodiscard]] U32 IAbs(const U32& value); | ||
| 212 | [[nodiscard]] U32U64 ShiftLeftLogical(const U32U64& base, const U32& shift); | ||
| 213 | [[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift); | ||
| 214 | [[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift); | ||
| 215 | [[nodiscard]] U32 BitwiseAnd(const U32& a, const U32& b); | ||
| 216 | [[nodiscard]] U32 BitwiseOr(const U32& a, const U32& b); | ||
| 217 | [[nodiscard]] U32 BitwiseXor(const U32& a, const U32& b); | ||
| 218 | [[nodiscard]] U32 BitFieldInsert(const U32& base, const U32& insert, const U32& offset, | ||
| 219 | const U32& count); | ||
| 220 | [[nodiscard]] U32 BitFieldExtract(const U32& base, const U32& offset, const U32& count, | ||
| 221 | bool is_signed = false); | ||
| 222 | [[nodiscard]] U32 BitReverse(const U32& value); | ||
| 223 | [[nodiscard]] U32 BitCount(const U32& value); | ||
| 224 | [[nodiscard]] U32 BitwiseNot(const U32& value); | ||
| 225 | |||
| 226 | [[nodiscard]] U32 FindSMsb(const U32& value); | ||
| 227 | [[nodiscard]] U32 FindUMsb(const U32& value); | ||
| 228 | [[nodiscard]] U32 SMin(const U32& a, const U32& b); | ||
| 229 | [[nodiscard]] U32 UMin(const U32& a, const U32& b); | ||
| 230 | [[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed); | ||
| 231 | [[nodiscard]] U32 SMax(const U32& a, const U32& b); | ||
| 232 | [[nodiscard]] U32 UMax(const U32& a, const U32& b); | ||
| 233 | [[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed); | ||
| 234 | [[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max); | ||
| 235 | [[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max); | ||
| 236 | |||
| 237 | [[nodiscard]] U1 ILessThan(const U32& lhs, const U32& rhs, bool is_signed); | ||
| 238 | [[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs); | ||
| 239 | [[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed); | ||
| 240 | [[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed); | ||
| 241 | [[nodiscard]] U1 INotEqual(const U32& lhs, const U32& rhs); | ||
| 242 | [[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed); | ||
| 243 | |||
| 244 | [[nodiscard]] U32 SharedAtomicIAdd(const U32& pointer_offset, const U32& value); | ||
| 245 | [[nodiscard]] U32 SharedAtomicSMin(const U32& pointer_offset, const U32& value); | ||
| 246 | [[nodiscard]] U32 SharedAtomicUMin(const U32& pointer_offset, const U32& value); | ||
| 247 | [[nodiscard]] U32 SharedAtomicIMin(const U32& pointer_offset, const U32& value, bool is_signed); | ||
| 248 | [[nodiscard]] U32 SharedAtomicSMax(const U32& pointer_offset, const U32& value); | ||
| 249 | [[nodiscard]] U32 SharedAtomicUMax(const U32& pointer_offset, const U32& value); | ||
| 250 | [[nodiscard]] U32 SharedAtomicIMax(const U32& pointer_offset, const U32& value, bool is_signed); | ||
| 251 | [[nodiscard]] U32 SharedAtomicInc(const U32& pointer_offset, const U32& value); | ||
| 252 | [[nodiscard]] U32 SharedAtomicDec(const U32& pointer_offset, const U32& value); | ||
| 253 | [[nodiscard]] U32 SharedAtomicAnd(const U32& pointer_offset, const U32& value); | ||
| 254 | [[nodiscard]] U32 SharedAtomicOr(const U32& pointer_offset, const U32& value); | ||
| 255 | [[nodiscard]] U32 SharedAtomicXor(const U32& pointer_offset, const U32& value); | ||
| 256 | [[nodiscard]] U32U64 SharedAtomicExchange(const U32& pointer_offset, const U32U64& value); | ||
| 257 | |||
| 258 | [[nodiscard]] U32U64 GlobalAtomicIAdd(const U64& pointer_offset, const U32U64& value); | ||
| 259 | [[nodiscard]] U32U64 GlobalAtomicSMin(const U64& pointer_offset, const U32U64& value); | ||
| 260 | [[nodiscard]] U32U64 GlobalAtomicUMin(const U64& pointer_offset, const U32U64& value); | ||
| 261 | [[nodiscard]] U32U64 GlobalAtomicIMin(const U64& pointer_offset, const U32U64& value, | ||
| 262 | bool is_signed); | ||
| 263 | [[nodiscard]] U32U64 GlobalAtomicSMax(const U64& pointer_offset, const U32U64& value); | ||
| 264 | [[nodiscard]] U32U64 GlobalAtomicUMax(const U64& pointer_offset, const U32U64& value); | ||
| 265 | [[nodiscard]] U32U64 GlobalAtomicIMax(const U64& pointer_offset, const U32U64& value, | ||
| 266 | bool is_signed); | ||
| 267 | [[nodiscard]] U32 GlobalAtomicInc(const U64& pointer_offset, const U32& value); | ||
| 268 | [[nodiscard]] U32 GlobalAtomicDec(const U64& pointer_offset, const U32& value); | ||
| 269 | [[nodiscard]] U32U64 GlobalAtomicAnd(const U64& pointer_offset, const U32U64& value); | ||
| 270 | [[nodiscard]] U32U64 GlobalAtomicOr(const U64& pointer_offset, const U32U64& value); | ||
| 271 | [[nodiscard]] U32U64 GlobalAtomicXor(const U64& pointer_offset, const U32U64& value); | ||
| 272 | [[nodiscard]] U32U64 GlobalAtomicExchange(const U64& pointer_offset, const U32U64& value); | ||
| 273 | |||
| 274 | [[nodiscard]] F32 GlobalAtomicF32Add(const U64& pointer_offset, const Value& value, | ||
| 275 | const FpControl control = {}); | ||
| 276 | [[nodiscard]] Value GlobalAtomicF16x2Add(const U64& pointer_offset, const Value& value, | ||
| 277 | const FpControl control = {}); | ||
| 278 | [[nodiscard]] Value GlobalAtomicF16x2Min(const U64& pointer_offset, const Value& value, | ||
| 279 | const FpControl control = {}); | ||
| 280 | [[nodiscard]] Value GlobalAtomicF16x2Max(const U64& pointer_offset, const Value& value, | ||
| 281 | const FpControl control = {}); | ||
| 282 | |||
| 283 | [[nodiscard]] U1 LogicalOr(const U1& a, const U1& b); | ||
| 284 | [[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b); | ||
| 285 | [[nodiscard]] U1 LogicalXor(const U1& a, const U1& b); | ||
| 286 | [[nodiscard]] U1 LogicalNot(const U1& value); | ||
| 287 | |||
| 288 | [[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const F16F32F64& value); | ||
| 289 | [[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const F16F32F64& value); | ||
| 290 | [[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const F16F32F64& value); | ||
| 291 | [[nodiscard]] F16F32F64 ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Value& value, | ||
| 292 | FpControl control = {}); | ||
| 293 | [[nodiscard]] F16F32F64 ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Value& value, | ||
| 294 | FpControl control = {}); | ||
| 295 | [[nodiscard]] F16F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed, | ||
| 296 | const Value& value, FpControl control = {}); | ||
| 297 | |||
| 298 | [[nodiscard]] U32U64 UConvert(size_t result_bitsize, const U32U64& value); | ||
| 299 | [[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value, | ||
| 300 | FpControl control = {}); | ||
| 301 | |||
| 302 | [[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& coords, | ||
| 303 | const F32& bias, const Value& offset, | ||
| 304 | const F32& lod_clamp, TextureInstInfo info); | ||
| 305 | [[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& coords, | ||
| 306 | const F32& lod, const Value& offset, | ||
| 307 | TextureInstInfo info); | ||
| 308 | [[nodiscard]] F32 ImageSampleDrefImplicitLod(const Value& handle, const Value& coords, | ||
| 309 | const F32& dref, const F32& bias, | ||
| 310 | const Value& offset, const F32& lod_clamp, | ||
| 311 | TextureInstInfo info); | ||
| 312 | [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, | ||
| 313 | const F32& dref, const F32& lod, | ||
| 314 | const Value& offset, TextureInstInfo info); | ||
| 315 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod); | ||
| 316 | |||
| 317 | [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, | ||
| 318 | TextureInstInfo info); | ||
| 319 | [[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset, | ||
| 320 | const Value& offset2, TextureInstInfo info); | ||
| 321 | [[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords, | ||
| 322 | const Value& offset, const Value& offset2, const F32& dref, | ||
| 323 | TextureInstInfo info); | ||
| 324 | [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, | ||
| 325 | const U32& lod, const U32& multisampling, TextureInstInfo info); | ||
| 326 | [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, | ||
| 327 | const Value& derivates, const Value& offset, | ||
| 328 | const F32& lod_clamp, TextureInstInfo info); | ||
| 329 | [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); | ||
| 330 | [[nodiscard]] void ImageWrite(const Value& handle, const Value& coords, const Value& color, | ||
| 331 | TextureInstInfo info); | ||
| 332 | |||
| 333 | [[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords, | ||
| 334 | const Value& value, TextureInstInfo info); | ||
| 335 | [[nodiscard]] Value ImageAtomicSMin(const Value& handle, const Value& coords, | ||
| 336 | const Value& value, TextureInstInfo info); | ||
| 337 | [[nodiscard]] Value ImageAtomicUMin(const Value& handle, const Value& coords, | ||
| 338 | const Value& value, TextureInstInfo info); | ||
| 339 | [[nodiscard]] Value ImageAtomicIMin(const Value& handle, const Value& coords, | ||
| 340 | const Value& value, bool is_signed, TextureInstInfo info); | ||
| 341 | [[nodiscard]] Value ImageAtomicSMax(const Value& handle, const Value& coords, | ||
| 342 | const Value& value, TextureInstInfo info); | ||
| 343 | [[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords, | ||
| 344 | const Value& value, TextureInstInfo info); | ||
| 345 | [[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords, | ||
| 346 | const Value& value, bool is_signed, TextureInstInfo info); | ||
| 347 | [[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value, | ||
| 348 | TextureInstInfo info); | ||
| 349 | [[nodiscard]] Value ImageAtomicDec(const Value& handle, const Value& coords, const Value& value, | ||
| 350 | TextureInstInfo info); | ||
| 351 | [[nodiscard]] Value ImageAtomicAnd(const Value& handle, const Value& coords, const Value& value, | ||
| 352 | TextureInstInfo info); | ||
| 353 | [[nodiscard]] Value ImageAtomicOr(const Value& handle, const Value& coords, const Value& value, | ||
| 354 | TextureInstInfo info); | ||
| 355 | [[nodiscard]] Value ImageAtomicXor(const Value& handle, const Value& coords, const Value& value, | ||
| 356 | TextureInstInfo info); | ||
| 357 | [[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords, | ||
| 358 | const Value& value, TextureInstInfo info); | ||
| 359 | [[nodiscard]] U1 VoteAll(const U1& value); | ||
| 360 | [[nodiscard]] U1 VoteAny(const U1& value); | ||
| 361 | [[nodiscard]] U1 VoteEqual(const U1& value); | ||
| 362 | [[nodiscard]] U32 SubgroupBallot(const U1& value); | ||
| 363 | [[nodiscard]] U32 SubgroupEqMask(); | ||
| 364 | [[nodiscard]] U32 SubgroupLtMask(); | ||
| 365 | [[nodiscard]] U32 SubgroupLeMask(); | ||
| 366 | [[nodiscard]] U32 SubgroupGtMask(); | ||
| 367 | [[nodiscard]] U32 SubgroupGeMask(); | ||
| 368 | [[nodiscard]] U32 ShuffleIndex(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 369 | const IR::U32& seg_mask); | ||
| 370 | [[nodiscard]] U32 ShuffleUp(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 371 | const IR::U32& seg_mask); | ||
| 372 | [[nodiscard]] U32 ShuffleDown(const IR::U32& value, const IR::U32& index, const IR::U32& clamp, | ||
| 373 | const IR::U32& seg_mask); | ||
| 374 | [[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index, | ||
| 375 | const IR::U32& clamp, const IR::U32& seg_mask); | ||
| 376 | [[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, | ||
| 377 | FpControl control = {}); | ||
| 378 | |||
| 379 | [[nodiscard]] F32 DPdxFine(const F32& a); | ||
| 380 | |||
| 381 | [[nodiscard]] F32 DPdyFine(const F32& a); | ||
| 382 | |||
| 383 | [[nodiscard]] F32 DPdxCoarse(const F32& a); | ||
| 384 | |||
| 385 | [[nodiscard]] F32 DPdyCoarse(const F32& a); | ||
| 386 | |||
| 387 | private: | ||
| 388 | IR::Block::iterator insertion_point; | ||
| 389 | |||
| 390 | template <typename T = Value, typename... Args> | ||
| 391 | T Inst(Opcode op, Args... args) { | ||
| 392 | auto it{block->PrependNewInst(insertion_point, op, {Value{args}...})}; | ||
| 393 | return T{Value{&*it}}; | ||
| 394 | } | ||
| 395 | |||
| 396 | template <typename T> | ||
| 397 | requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags { | ||
| 398 | Flags() = default; | ||
| 399 | Flags(T proxy_) : proxy{proxy_} {} | ||
| 400 | |||
| 401 | T proxy; | ||
| 402 | }; | ||
| 403 | |||
| 404 | template <typename T = Value, typename FlagType, typename... Args> | ||
| 405 | T Inst(Opcode op, Flags<FlagType> flags, Args... args) { | ||
| 406 | u32 raw_flags{}; | ||
| 407 | std::memcpy(&raw_flags, &flags.proxy, sizeof(flags.proxy)); | ||
| 408 | auto it{block->PrependNewInst(insertion_point, op, {Value{args}...}, raw_flags)}; | ||
| 409 | return T{Value{&*it}}; | ||
| 410 | } | ||
| 411 | }; | ||
| 412 | |||
| 413 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp new file mode 100644 index 000000000..3dfa5a880 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -0,0 +1,411 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "shader_recompiler/exception.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/type.h" | ||
| 10 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 11 | |||
| 12 | namespace Shader::IR { | ||
| 13 | namespace { | ||
| 14 | void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) { | ||
| 15 | if (inst && inst->GetOpcode() != opcode) { | ||
| 16 | throw LogicError("Invalid pseudo-instruction"); | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) { | ||
| 21 | if (dest_inst) { | ||
| 22 | throw LogicError("Only one of each type of pseudo-op allowed"); | ||
| 23 | } | ||
| 24 | dest_inst = pseudo_inst; | ||
| 25 | } | ||
| 26 | |||
| 27 | void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) { | ||
| 28 | if (inst->GetOpcode() != expected_opcode) { | ||
| 29 | throw LogicError("Undoing use of invalid pseudo-op"); | ||
| 30 | } | ||
| 31 | inst = nullptr; | ||
| 32 | } | ||
| 33 | |||
| 34 | void AllocAssociatedInsts(std::unique_ptr<AssociatedInsts>& associated_insts) { | ||
| 35 | if (!associated_insts) { | ||
| 36 | associated_insts = std::make_unique<AssociatedInsts>(); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } // Anonymous namespace | ||
| 40 | |||
| 41 | Inst::Inst(IR::Opcode op_, u32 flags_) noexcept : op{op_}, flags{flags_} { | ||
| 42 | if (op == Opcode::Phi) { | ||
| 43 | std::construct_at(&phi_args); | ||
| 44 | } else { | ||
| 45 | std::construct_at(&args); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Inst::~Inst() { | ||
| 50 | if (op == Opcode::Phi) { | ||
| 51 | std::destroy_at(&phi_args); | ||
| 52 | } else { | ||
| 53 | std::destroy_at(&args); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | bool Inst::MayHaveSideEffects() const noexcept { | ||
| 58 | switch (op) { | ||
| 59 | case Opcode::ConditionRef: | ||
| 60 | case Opcode::Reference: | ||
| 61 | case Opcode::PhiMove: | ||
| 62 | case Opcode::Prologue: | ||
| 63 | case Opcode::Epilogue: | ||
| 64 | case Opcode::Join: | ||
| 65 | case Opcode::DemoteToHelperInvocation: | ||
| 66 | case Opcode::Barrier: | ||
| 67 | case Opcode::WorkgroupMemoryBarrier: | ||
| 68 | case Opcode::DeviceMemoryBarrier: | ||
| 69 | case Opcode::EmitVertex: | ||
| 70 | case Opcode::EndPrimitive: | ||
| 71 | case Opcode::SetAttribute: | ||
| 72 | case Opcode::SetAttributeIndexed: | ||
| 73 | case Opcode::SetPatch: | ||
| 74 | case Opcode::SetFragColor: | ||
| 75 | case Opcode::SetSampleMask: | ||
| 76 | case Opcode::SetFragDepth: | ||
| 77 | case Opcode::WriteGlobalU8: | ||
| 78 | case Opcode::WriteGlobalS8: | ||
| 79 | case Opcode::WriteGlobalU16: | ||
| 80 | case Opcode::WriteGlobalS16: | ||
| 81 | case Opcode::WriteGlobal32: | ||
| 82 | case Opcode::WriteGlobal64: | ||
| 83 | case Opcode::WriteGlobal128: | ||
| 84 | case Opcode::WriteStorageU8: | ||
| 85 | case Opcode::WriteStorageS8: | ||
| 86 | case Opcode::WriteStorageU16: | ||
| 87 | case Opcode::WriteStorageS16: | ||
| 88 | case Opcode::WriteStorage32: | ||
| 89 | case Opcode::WriteStorage64: | ||
| 90 | case Opcode::WriteStorage128: | ||
| 91 | case Opcode::WriteLocal: | ||
| 92 | case Opcode::WriteSharedU8: | ||
| 93 | case Opcode::WriteSharedU16: | ||
| 94 | case Opcode::WriteSharedU32: | ||
| 95 | case Opcode::WriteSharedU64: | ||
| 96 | case Opcode::WriteSharedU128: | ||
| 97 | case Opcode::SharedAtomicIAdd32: | ||
| 98 | case Opcode::SharedAtomicSMin32: | ||
| 99 | case Opcode::SharedAtomicUMin32: | ||
| 100 | case Opcode::SharedAtomicSMax32: | ||
| 101 | case Opcode::SharedAtomicUMax32: | ||
| 102 | case Opcode::SharedAtomicInc32: | ||
| 103 | case Opcode::SharedAtomicDec32: | ||
| 104 | case Opcode::SharedAtomicAnd32: | ||
| 105 | case Opcode::SharedAtomicOr32: | ||
| 106 | case Opcode::SharedAtomicXor32: | ||
| 107 | case Opcode::SharedAtomicExchange32: | ||
| 108 | case Opcode::SharedAtomicExchange64: | ||
| 109 | case Opcode::GlobalAtomicIAdd32: | ||
| 110 | case Opcode::GlobalAtomicSMin32: | ||
| 111 | case Opcode::GlobalAtomicUMin32: | ||
| 112 | case Opcode::GlobalAtomicSMax32: | ||
| 113 | case Opcode::GlobalAtomicUMax32: | ||
| 114 | case Opcode::GlobalAtomicInc32: | ||
| 115 | case Opcode::GlobalAtomicDec32: | ||
| 116 | case Opcode::GlobalAtomicAnd32: | ||
| 117 | case Opcode::GlobalAtomicOr32: | ||
| 118 | case Opcode::GlobalAtomicXor32: | ||
| 119 | case Opcode::GlobalAtomicExchange32: | ||
| 120 | case Opcode::GlobalAtomicIAdd64: | ||
| 121 | case Opcode::GlobalAtomicSMin64: | ||
| 122 | case Opcode::GlobalAtomicUMin64: | ||
| 123 | case Opcode::GlobalAtomicSMax64: | ||
| 124 | case Opcode::GlobalAtomicUMax64: | ||
| 125 | case Opcode::GlobalAtomicAnd64: | ||
| 126 | case Opcode::GlobalAtomicOr64: | ||
| 127 | case Opcode::GlobalAtomicXor64: | ||
| 128 | case Opcode::GlobalAtomicExchange64: | ||
| 129 | case Opcode::GlobalAtomicAddF32: | ||
| 130 | case Opcode::GlobalAtomicAddF16x2: | ||
| 131 | case Opcode::GlobalAtomicAddF32x2: | ||
| 132 | case Opcode::GlobalAtomicMinF16x2: | ||
| 133 | case Opcode::GlobalAtomicMinF32x2: | ||
| 134 | case Opcode::GlobalAtomicMaxF16x2: | ||
| 135 | case Opcode::GlobalAtomicMaxF32x2: | ||
| 136 | case Opcode::StorageAtomicIAdd32: | ||
| 137 | case Opcode::StorageAtomicSMin32: | ||
| 138 | case Opcode::StorageAtomicUMin32: | ||
| 139 | case Opcode::StorageAtomicSMax32: | ||
| 140 | case Opcode::StorageAtomicUMax32: | ||
| 141 | case Opcode::StorageAtomicInc32: | ||
| 142 | case Opcode::StorageAtomicDec32: | ||
| 143 | case Opcode::StorageAtomicAnd32: | ||
| 144 | case Opcode::StorageAtomicOr32: | ||
| 145 | case Opcode::StorageAtomicXor32: | ||
| 146 | case Opcode::StorageAtomicExchange32: | ||
| 147 | case Opcode::StorageAtomicIAdd64: | ||
| 148 | case Opcode::StorageAtomicSMin64: | ||
| 149 | case Opcode::StorageAtomicUMin64: | ||
| 150 | case Opcode::StorageAtomicSMax64: | ||
| 151 | case Opcode::StorageAtomicUMax64: | ||
| 152 | case Opcode::StorageAtomicAnd64: | ||
| 153 | case Opcode::StorageAtomicOr64: | ||
| 154 | case Opcode::StorageAtomicXor64: | ||
| 155 | case Opcode::StorageAtomicExchange64: | ||
| 156 | case Opcode::StorageAtomicAddF32: | ||
| 157 | case Opcode::StorageAtomicAddF16x2: | ||
| 158 | case Opcode::StorageAtomicAddF32x2: | ||
| 159 | case Opcode::StorageAtomicMinF16x2: | ||
| 160 | case Opcode::StorageAtomicMinF32x2: | ||
| 161 | case Opcode::StorageAtomicMaxF16x2: | ||
| 162 | case Opcode::StorageAtomicMaxF32x2: | ||
| 163 | case Opcode::BindlessImageWrite: | ||
| 164 | case Opcode::BoundImageWrite: | ||
| 165 | case Opcode::ImageWrite: | ||
| 166 | case IR::Opcode::BindlessImageAtomicIAdd32: | ||
| 167 | case IR::Opcode::BindlessImageAtomicSMin32: | ||
| 168 | case IR::Opcode::BindlessImageAtomicUMin32: | ||
| 169 | case IR::Opcode::BindlessImageAtomicSMax32: | ||
| 170 | case IR::Opcode::BindlessImageAtomicUMax32: | ||
| 171 | case IR::Opcode::BindlessImageAtomicInc32: | ||
| 172 | case IR::Opcode::BindlessImageAtomicDec32: | ||
| 173 | case IR::Opcode::BindlessImageAtomicAnd32: | ||
| 174 | case IR::Opcode::BindlessImageAtomicOr32: | ||
| 175 | case IR::Opcode::BindlessImageAtomicXor32: | ||
| 176 | case IR::Opcode::BindlessImageAtomicExchange32: | ||
| 177 | case IR::Opcode::BoundImageAtomicIAdd32: | ||
| 178 | case IR::Opcode::BoundImageAtomicSMin32: | ||
| 179 | case IR::Opcode::BoundImageAtomicUMin32: | ||
| 180 | case IR::Opcode::BoundImageAtomicSMax32: | ||
| 181 | case IR::Opcode::BoundImageAtomicUMax32: | ||
| 182 | case IR::Opcode::BoundImageAtomicInc32: | ||
| 183 | case IR::Opcode::BoundImageAtomicDec32: | ||
| 184 | case IR::Opcode::BoundImageAtomicAnd32: | ||
| 185 | case IR::Opcode::BoundImageAtomicOr32: | ||
| 186 | case IR::Opcode::BoundImageAtomicXor32: | ||
| 187 | case IR::Opcode::BoundImageAtomicExchange32: | ||
| 188 | case IR::Opcode::ImageAtomicIAdd32: | ||
| 189 | case IR::Opcode::ImageAtomicSMin32: | ||
| 190 | case IR::Opcode::ImageAtomicUMin32: | ||
| 191 | case IR::Opcode::ImageAtomicSMax32: | ||
| 192 | case IR::Opcode::ImageAtomicUMax32: | ||
| 193 | case IR::Opcode::ImageAtomicInc32: | ||
| 194 | case IR::Opcode::ImageAtomicDec32: | ||
| 195 | case IR::Opcode::ImageAtomicAnd32: | ||
| 196 | case IR::Opcode::ImageAtomicOr32: | ||
| 197 | case IR::Opcode::ImageAtomicXor32: | ||
| 198 | case IR::Opcode::ImageAtomicExchange32: | ||
| 199 | return true; | ||
| 200 | default: | ||
| 201 | return false; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | bool Inst::IsPseudoInstruction() const noexcept { | ||
| 206 | switch (op) { | ||
| 207 | case Opcode::GetZeroFromOp: | ||
| 208 | case Opcode::GetSignFromOp: | ||
| 209 | case Opcode::GetCarryFromOp: | ||
| 210 | case Opcode::GetOverflowFromOp: | ||
| 211 | case Opcode::GetSparseFromOp: | ||
| 212 | case Opcode::GetInBoundsFromOp: | ||
| 213 | return true; | ||
| 214 | default: | ||
| 215 | return false; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | bool Inst::AreAllArgsImmediates() const { | ||
| 220 | if (op == Opcode::Phi) { | ||
| 221 | throw LogicError("Testing for all arguments are immediates on phi instruction"); | ||
| 222 | } | ||
| 223 | return std::all_of(args.begin(), args.begin() + NumArgs(), | ||
| 224 | [](const IR::Value& value) { return value.IsImmediate(); }); | ||
| 225 | } | ||
| 226 | |||
| 227 | Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) { | ||
| 228 | if (!associated_insts) { | ||
| 229 | return nullptr; | ||
| 230 | } | ||
| 231 | switch (opcode) { | ||
| 232 | case Opcode::GetZeroFromOp: | ||
| 233 | CheckPseudoInstruction(associated_insts->zero_inst, Opcode::GetZeroFromOp); | ||
| 234 | return associated_insts->zero_inst; | ||
| 235 | case Opcode::GetSignFromOp: | ||
| 236 | CheckPseudoInstruction(associated_insts->sign_inst, Opcode::GetSignFromOp); | ||
| 237 | return associated_insts->sign_inst; | ||
| 238 | case Opcode::GetCarryFromOp: | ||
| 239 | CheckPseudoInstruction(associated_insts->carry_inst, Opcode::GetCarryFromOp); | ||
| 240 | return associated_insts->carry_inst; | ||
| 241 | case Opcode::GetOverflowFromOp: | ||
| 242 | CheckPseudoInstruction(associated_insts->overflow_inst, Opcode::GetOverflowFromOp); | ||
| 243 | return associated_insts->overflow_inst; | ||
| 244 | case Opcode::GetSparseFromOp: | ||
| 245 | CheckPseudoInstruction(associated_insts->sparse_inst, Opcode::GetSparseFromOp); | ||
| 246 | return associated_insts->sparse_inst; | ||
| 247 | case Opcode::GetInBoundsFromOp: | ||
| 248 | CheckPseudoInstruction(associated_insts->in_bounds_inst, Opcode::GetInBoundsFromOp); | ||
| 249 | return associated_insts->in_bounds_inst; | ||
| 250 | default: | ||
| 251 | throw InvalidArgument("{} is not a pseudo-instruction", opcode); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | IR::Type Inst::Type() const { | ||
| 256 | return TypeOf(op); | ||
| 257 | } | ||
| 258 | |||
| 259 | void Inst::SetArg(size_t index, Value value) { | ||
| 260 | if (index >= NumArgs()) { | ||
| 261 | throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); | ||
| 262 | } | ||
| 263 | const IR::Value arg{Arg(index)}; | ||
| 264 | if (!arg.IsImmediate()) { | ||
| 265 | UndoUse(arg); | ||
| 266 | } | ||
| 267 | if (!value.IsImmediate()) { | ||
| 268 | Use(value); | ||
| 269 | } | ||
| 270 | if (op == Opcode::Phi) { | ||
| 271 | phi_args[index].second = value; | ||
| 272 | } else { | ||
| 273 | args[index] = value; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | Block* Inst::PhiBlock(size_t index) const { | ||
| 278 | if (op != Opcode::Phi) { | ||
| 279 | throw LogicError("{} is not a Phi instruction", op); | ||
| 280 | } | ||
| 281 | if (index >= phi_args.size()) { | ||
| 282 | throw InvalidArgument("Out of bounds argument index {} in phi instruction"); | ||
| 283 | } | ||
| 284 | return phi_args[index].first; | ||
| 285 | } | ||
| 286 | |||
| 287 | void Inst::AddPhiOperand(Block* predecessor, const Value& value) { | ||
| 288 | if (!value.IsImmediate()) { | ||
| 289 | Use(value); | ||
| 290 | } | ||
| 291 | phi_args.emplace_back(predecessor, value); | ||
| 292 | } | ||
| 293 | |||
| 294 | void Inst::Invalidate() { | ||
| 295 | ClearArgs(); | ||
| 296 | ReplaceOpcode(Opcode::Void); | ||
| 297 | } | ||
| 298 | |||
| 299 | void Inst::ClearArgs() { | ||
| 300 | if (op == Opcode::Phi) { | ||
| 301 | for (auto& pair : phi_args) { | ||
| 302 | IR::Value& value{pair.second}; | ||
| 303 | if (!value.IsImmediate()) { | ||
| 304 | UndoUse(value); | ||
| 305 | } | ||
| 306 | } | ||
| 307 | phi_args.clear(); | ||
| 308 | } else { | ||
| 309 | for (auto& value : args) { | ||
| 310 | if (!value.IsImmediate()) { | ||
| 311 | UndoUse(value); | ||
| 312 | } | ||
| 313 | } | ||
| 314 | // Reset arguments to null | ||
| 315 | // std::memset was measured to be faster on MSVC than std::ranges:fill | ||
| 316 | std::memset(reinterpret_cast<char*>(&args), 0, sizeof(args)); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | void Inst::ReplaceUsesWith(Value replacement) { | ||
| 321 | Invalidate(); | ||
| 322 | ReplaceOpcode(Opcode::Identity); | ||
| 323 | if (!replacement.IsImmediate()) { | ||
| 324 | Use(replacement); | ||
| 325 | } | ||
| 326 | args[0] = replacement; | ||
| 327 | } | ||
| 328 | |||
| 329 | void Inst::ReplaceOpcode(IR::Opcode opcode) { | ||
| 330 | if (opcode == IR::Opcode::Phi) { | ||
| 331 | throw LogicError("Cannot transition into Phi"); | ||
| 332 | } | ||
| 333 | if (op == Opcode::Phi) { | ||
| 334 | // Transition out of phi arguments into non-phi | ||
| 335 | std::destroy_at(&phi_args); | ||
| 336 | std::construct_at(&args); | ||
| 337 | } | ||
| 338 | op = opcode; | ||
| 339 | } | ||
| 340 | |||
| 341 | void Inst::Use(const Value& value) { | ||
| 342 | Inst* const inst{value.Inst()}; | ||
| 343 | ++inst->use_count; | ||
| 344 | |||
| 345 | std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts}; | ||
| 346 | switch (op) { | ||
| 347 | case Opcode::GetZeroFromOp: | ||
| 348 | AllocAssociatedInsts(assoc_inst); | ||
| 349 | SetPseudoInstruction(assoc_inst->zero_inst, this); | ||
| 350 | break; | ||
| 351 | case Opcode::GetSignFromOp: | ||
| 352 | AllocAssociatedInsts(assoc_inst); | ||
| 353 | SetPseudoInstruction(assoc_inst->sign_inst, this); | ||
| 354 | break; | ||
| 355 | case Opcode::GetCarryFromOp: | ||
| 356 | AllocAssociatedInsts(assoc_inst); | ||
| 357 | SetPseudoInstruction(assoc_inst->carry_inst, this); | ||
| 358 | break; | ||
| 359 | case Opcode::GetOverflowFromOp: | ||
| 360 | AllocAssociatedInsts(assoc_inst); | ||
| 361 | SetPseudoInstruction(assoc_inst->overflow_inst, this); | ||
| 362 | break; | ||
| 363 | case Opcode::GetSparseFromOp: | ||
| 364 | AllocAssociatedInsts(assoc_inst); | ||
| 365 | SetPseudoInstruction(assoc_inst->sparse_inst, this); | ||
| 366 | break; | ||
| 367 | case Opcode::GetInBoundsFromOp: | ||
| 368 | AllocAssociatedInsts(assoc_inst); | ||
| 369 | SetPseudoInstruction(assoc_inst->in_bounds_inst, this); | ||
| 370 | break; | ||
| 371 | default: | ||
| 372 | break; | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | void Inst::UndoUse(const Value& value) { | ||
| 377 | Inst* const inst{value.Inst()}; | ||
| 378 | --inst->use_count; | ||
| 379 | |||
| 380 | std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts}; | ||
| 381 | switch (op) { | ||
| 382 | case Opcode::GetZeroFromOp: | ||
| 383 | AllocAssociatedInsts(assoc_inst); | ||
| 384 | RemovePseudoInstruction(assoc_inst->zero_inst, Opcode::GetZeroFromOp); | ||
| 385 | break; | ||
| 386 | case Opcode::GetSignFromOp: | ||
| 387 | AllocAssociatedInsts(assoc_inst); | ||
| 388 | RemovePseudoInstruction(assoc_inst->sign_inst, Opcode::GetSignFromOp); | ||
| 389 | break; | ||
| 390 | case Opcode::GetCarryFromOp: | ||
| 391 | AllocAssociatedInsts(assoc_inst); | ||
| 392 | RemovePseudoInstruction(assoc_inst->carry_inst, Opcode::GetCarryFromOp); | ||
| 393 | break; | ||
| 394 | case Opcode::GetOverflowFromOp: | ||
| 395 | AllocAssociatedInsts(assoc_inst); | ||
| 396 | RemovePseudoInstruction(assoc_inst->overflow_inst, Opcode::GetOverflowFromOp); | ||
| 397 | break; | ||
| 398 | case Opcode::GetSparseFromOp: | ||
| 399 | AllocAssociatedInsts(assoc_inst); | ||
| 400 | RemovePseudoInstruction(assoc_inst->sparse_inst, Opcode::GetSparseFromOp); | ||
| 401 | break; | ||
| 402 | case Opcode::GetInBoundsFromOp: | ||
| 403 | AllocAssociatedInsts(assoc_inst); | ||
| 404 | RemovePseudoInstruction(assoc_inst->in_bounds_inst, Opcode::GetInBoundsFromOp); | ||
| 405 | break; | ||
| 406 | default: | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h new file mode 100644 index 000000000..77cda1f8a --- /dev/null +++ b/src/shader_recompiler/frontend/ir/modifiers.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "shader_recompiler/shader_info.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | enum class FmzMode : u8 { | ||
| 14 | DontCare, // Not specified for this instruction | ||
| 15 | FTZ, // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK) | ||
| 16 | FMZ, // Flush denorms to zero, x * 0 == 0 (D3D9) | ||
| 17 | None, // Denorms are not flushed, NAN is propagated (nouveau) | ||
| 18 | }; | ||
| 19 | |||
| 20 | enum class FpRounding : u8 { | ||
| 21 | DontCare, // Not specified for this instruction | ||
| 22 | RN, // Round to nearest even, | ||
| 23 | RM, // Round towards negative infinity | ||
| 24 | RP, // Round towards positive infinity | ||
| 25 | RZ, // Round towards zero | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct FpControl { | ||
| 29 | bool no_contraction{false}; | ||
| 30 | FpRounding rounding{FpRounding::DontCare}; | ||
| 31 | FmzMode fmz_mode{FmzMode::DontCare}; | ||
| 32 | }; | ||
| 33 | static_assert(sizeof(FpControl) <= sizeof(u32)); | ||
| 34 | |||
| 35 | union TextureInstInfo { | ||
| 36 | u32 raw; | ||
| 37 | BitField<0, 16, u32> descriptor_index; | ||
| 38 | BitField<16, 3, TextureType> type; | ||
| 39 | BitField<19, 1, u32> is_depth; | ||
| 40 | BitField<20, 1, u32> has_bias; | ||
| 41 | BitField<21, 1, u32> has_lod_clamp; | ||
| 42 | BitField<22, 1, u32> relaxed_precision; | ||
| 43 | BitField<23, 2, u32> gather_component; | ||
| 44 | BitField<25, 2, u32> num_derivates; | ||
| 45 | BitField<27, 3, ImageFormat> image_format; | ||
| 46 | }; | ||
| 47 | static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); | ||
| 48 | |||
| 49 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/opcodes.cpp b/src/shader_recompiler/frontend/ir/opcodes.cpp new file mode 100644 index 000000000..24d024ad7 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/opcodes.cpp | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string_view> | ||
| 6 | |||
| 7 | #include "shader_recompiler/frontend/ir/opcodes.h" | ||
| 8 | |||
| 9 | namespace Shader::IR { | ||
| 10 | |||
| 11 | std::string_view NameOf(Opcode op) { | ||
| 12 | return Detail::META_TABLE[static_cast<size_t>(op)].name; | ||
| 13 | } | ||
| 14 | |||
| 15 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h new file mode 100644 index 000000000..9ab108292 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/opcodes.h | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <string_view> | ||
| 10 | |||
| 11 | #include <fmt/format.h> | ||
| 12 | |||
| 13 | #include "shader_recompiler/frontend/ir/type.h" | ||
| 14 | |||
| 15 | namespace Shader::IR { | ||
| 16 | |||
| 17 | enum class Opcode { | ||
| 18 | #define OPCODE(name, ...) name, | ||
| 19 | #include "opcodes.inc" | ||
| 20 | #undef OPCODE | ||
| 21 | }; | ||
| 22 | |||
| 23 | namespace Detail { | ||
| 24 | struct OpcodeMeta { | ||
| 25 | std::string_view name; | ||
| 26 | Type type; | ||
| 27 | std::array<Type, 5> arg_types; | ||
| 28 | }; | ||
| 29 | |||
| 30 | // using enum Type; | ||
| 31 | constexpr Type Void{Type::Void}; | ||
| 32 | constexpr Type Opaque{Type::Opaque}; | ||
| 33 | constexpr Type Reg{Type::Reg}; | ||
| 34 | constexpr Type Pred{Type::Pred}; | ||
| 35 | constexpr Type Attribute{Type::Attribute}; | ||
| 36 | constexpr Type Patch{Type::Patch}; | ||
| 37 | constexpr Type U1{Type::U1}; | ||
| 38 | constexpr Type U8{Type::U8}; | ||
| 39 | constexpr Type U16{Type::U16}; | ||
| 40 | constexpr Type U32{Type::U32}; | ||
| 41 | constexpr Type U64{Type::U64}; | ||
| 42 | constexpr Type F16{Type::F16}; | ||
| 43 | constexpr Type F32{Type::F32}; | ||
| 44 | constexpr Type F64{Type::F64}; | ||
| 45 | constexpr Type U32x2{Type::U32x2}; | ||
| 46 | constexpr Type U32x3{Type::U32x3}; | ||
| 47 | constexpr Type U32x4{Type::U32x4}; | ||
| 48 | constexpr Type F16x2{Type::F16x2}; | ||
| 49 | constexpr Type F16x3{Type::F16x3}; | ||
| 50 | constexpr Type F16x4{Type::F16x4}; | ||
| 51 | constexpr Type F32x2{Type::F32x2}; | ||
| 52 | constexpr Type F32x3{Type::F32x3}; | ||
| 53 | constexpr Type F32x4{Type::F32x4}; | ||
| 54 | constexpr Type F64x2{Type::F64x2}; | ||
| 55 | constexpr Type F64x3{Type::F64x3}; | ||
| 56 | constexpr Type F64x4{Type::F64x4}; | ||
| 57 | |||
| 58 | constexpr OpcodeMeta META_TABLE[]{ | ||
| 59 | #define OPCODE(name_token, type_token, ...) \ | ||
| 60 | { \ | ||
| 61 | .name{#name_token}, \ | ||
| 62 | .type = type_token, \ | ||
| 63 | .arg_types{__VA_ARGS__}, \ | ||
| 64 | }, | ||
| 65 | #include "opcodes.inc" | ||
| 66 | #undef OPCODE | ||
| 67 | }; | ||
| 68 | constexpr size_t CalculateNumArgsOf(Opcode op) { | ||
| 69 | const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types}; | ||
| 70 | return static_cast<size_t>( | ||
| 71 | std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void))); | ||
| 72 | } | ||
| 73 | |||
| 74 | constexpr u8 NUM_ARGS[]{ | ||
| 75 | #define OPCODE(name_token, type_token, ...) static_cast<u8>(CalculateNumArgsOf(Opcode::name_token)), | ||
| 76 | #include "opcodes.inc" | ||
| 77 | #undef OPCODE | ||
| 78 | }; | ||
| 79 | } // namespace Detail | ||
| 80 | |||
| 81 | /// Get return type of an opcode | ||
| 82 | [[nodiscard]] inline Type TypeOf(Opcode op) noexcept { | ||
| 83 | return Detail::META_TABLE[static_cast<size_t>(op)].type; | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Get the number of arguments an opcode accepts | ||
| 87 | [[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept { | ||
| 88 | return static_cast<size_t>(Detail::NUM_ARGS[static_cast<size_t>(op)]); | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Get the required type of an argument of an opcode | ||
| 92 | [[nodiscard]] inline Type ArgTypeOf(Opcode op, size_t arg_index) noexcept { | ||
| 93 | return Detail::META_TABLE[static_cast<size_t>(op)].arg_types[arg_index]; | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Get the name of an opcode | ||
| 97 | [[nodiscard]] std::string_view NameOf(Opcode op); | ||
| 98 | |||
| 99 | } // namespace Shader::IR | ||
| 100 | |||
| 101 | template <> | ||
| 102 | struct fmt::formatter<Shader::IR::Opcode> { | ||
| 103 | constexpr auto parse(format_parse_context& ctx) { | ||
| 104 | return ctx.begin(); | ||
| 105 | } | ||
| 106 | template <typename FormatContext> | ||
| 107 | auto format(const Shader::IR::Opcode& op, FormatContext& ctx) { | ||
| 108 | return format_to(ctx.out(), "{}", Shader::IR::NameOf(op)); | ||
| 109 | } | ||
| 110 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc new file mode 100644 index 000000000..d91098c80 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -0,0 +1,550 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | // opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, arg4 type, ... | ||
| 6 | OPCODE(Phi, Opaque, ) | ||
| 7 | OPCODE(Identity, Opaque, Opaque, ) | ||
| 8 | OPCODE(Void, Void, ) | ||
| 9 | OPCODE(ConditionRef, U1, U1, ) | ||
| 10 | OPCODE(Reference, Void, Opaque, ) | ||
| 11 | OPCODE(PhiMove, Void, Opaque, Opaque, ) | ||
| 12 | |||
| 13 | // Special operations | ||
| 14 | OPCODE(Prologue, Void, ) | ||
| 15 | OPCODE(Epilogue, Void, ) | ||
| 16 | OPCODE(Join, Void, ) | ||
| 17 | OPCODE(DemoteToHelperInvocation, Void, ) | ||
| 18 | OPCODE(EmitVertex, Void, U32, ) | ||
| 19 | OPCODE(EndPrimitive, Void, U32, ) | ||
| 20 | |||
| 21 | // Barriers | ||
| 22 | OPCODE(Barrier, Void, ) | ||
| 23 | OPCODE(WorkgroupMemoryBarrier, Void, ) | ||
| 24 | OPCODE(DeviceMemoryBarrier, Void, ) | ||
| 25 | |||
| 26 | // Context getters/setters | ||
| 27 | OPCODE(GetRegister, U32, Reg, ) | ||
| 28 | OPCODE(SetRegister, Void, Reg, U32, ) | ||
| 29 | OPCODE(GetPred, U1, Pred, ) | ||
| 30 | OPCODE(SetPred, Void, Pred, U1, ) | ||
| 31 | OPCODE(GetGotoVariable, U1, U32, ) | ||
| 32 | OPCODE(SetGotoVariable, Void, U32, U1, ) | ||
| 33 | OPCODE(GetIndirectBranchVariable, U32, ) | ||
| 34 | OPCODE(SetIndirectBranchVariable, Void, U32, ) | ||
| 35 | OPCODE(GetCbufU8, U32, U32, U32, ) | ||
| 36 | OPCODE(GetCbufS8, U32, U32, U32, ) | ||
| 37 | OPCODE(GetCbufU16, U32, U32, U32, ) | ||
| 38 | OPCODE(GetCbufS16, U32, U32, U32, ) | ||
| 39 | OPCODE(GetCbufU32, U32, U32, U32, ) | ||
| 40 | OPCODE(GetCbufF32, F32, U32, U32, ) | ||
| 41 | OPCODE(GetCbufU32x2, U32x2, U32, U32, ) | ||
| 42 | OPCODE(GetAttribute, F32, Attribute, U32, ) | ||
| 43 | OPCODE(SetAttribute, Void, Attribute, F32, U32, ) | ||
| 44 | OPCODE(GetAttributeIndexed, F32, U32, U32, ) | ||
| 45 | OPCODE(SetAttributeIndexed, Void, U32, F32, U32, ) | ||
| 46 | OPCODE(GetPatch, F32, Patch, ) | ||
| 47 | OPCODE(SetPatch, Void, Patch, F32, ) | ||
| 48 | OPCODE(SetFragColor, Void, U32, U32, F32, ) | ||
| 49 | OPCODE(SetSampleMask, Void, U32, ) | ||
| 50 | OPCODE(SetFragDepth, Void, F32, ) | ||
| 51 | OPCODE(GetZFlag, U1, Void, ) | ||
| 52 | OPCODE(GetSFlag, U1, Void, ) | ||
| 53 | OPCODE(GetCFlag, U1, Void, ) | ||
| 54 | OPCODE(GetOFlag, U1, Void, ) | ||
| 55 | OPCODE(SetZFlag, Void, U1, ) | ||
| 56 | OPCODE(SetSFlag, Void, U1, ) | ||
| 57 | OPCODE(SetCFlag, Void, U1, ) | ||
| 58 | OPCODE(SetOFlag, Void, U1, ) | ||
| 59 | OPCODE(WorkgroupId, U32x3, ) | ||
| 60 | OPCODE(LocalInvocationId, U32x3, ) | ||
| 61 | OPCODE(InvocationId, U32, ) | ||
| 62 | OPCODE(SampleId, U32, ) | ||
| 63 | OPCODE(IsHelperInvocation, U1, ) | ||
| 64 | OPCODE(YDirection, F32, ) | ||
| 65 | |||
| 66 | // Undefined | ||
| 67 | OPCODE(UndefU1, U1, ) | ||
| 68 | OPCODE(UndefU8, U8, ) | ||
| 69 | OPCODE(UndefU16, U16, ) | ||
| 70 | OPCODE(UndefU32, U32, ) | ||
| 71 | OPCODE(UndefU64, U64, ) | ||
| 72 | |||
| 73 | // Memory operations | ||
| 74 | OPCODE(LoadGlobalU8, U32, Opaque, ) | ||
| 75 | OPCODE(LoadGlobalS8, U32, Opaque, ) | ||
| 76 | OPCODE(LoadGlobalU16, U32, Opaque, ) | ||
| 77 | OPCODE(LoadGlobalS16, U32, Opaque, ) | ||
| 78 | OPCODE(LoadGlobal32, U32, Opaque, ) | ||
| 79 | OPCODE(LoadGlobal64, U32x2, Opaque, ) | ||
| 80 | OPCODE(LoadGlobal128, U32x4, Opaque, ) | ||
| 81 | OPCODE(WriteGlobalU8, Void, Opaque, U32, ) | ||
| 82 | OPCODE(WriteGlobalS8, Void, Opaque, U32, ) | ||
| 83 | OPCODE(WriteGlobalU16, Void, Opaque, U32, ) | ||
| 84 | OPCODE(WriteGlobalS16, Void, Opaque, U32, ) | ||
| 85 | OPCODE(WriteGlobal32, Void, Opaque, U32, ) | ||
| 86 | OPCODE(WriteGlobal64, Void, Opaque, U32x2, ) | ||
| 87 | OPCODE(WriteGlobal128, Void, Opaque, U32x4, ) | ||
| 88 | |||
| 89 | // Storage buffer operations | ||
| 90 | OPCODE(LoadStorageU8, U32, U32, U32, ) | ||
| 91 | OPCODE(LoadStorageS8, U32, U32, U32, ) | ||
| 92 | OPCODE(LoadStorageU16, U32, U32, U32, ) | ||
| 93 | OPCODE(LoadStorageS16, U32, U32, U32, ) | ||
| 94 | OPCODE(LoadStorage32, U32, U32, U32, ) | ||
| 95 | OPCODE(LoadStorage64, U32x2, U32, U32, ) | ||
| 96 | OPCODE(LoadStorage128, U32x4, U32, U32, ) | ||
| 97 | OPCODE(WriteStorageU8, Void, U32, U32, U32, ) | ||
| 98 | OPCODE(WriteStorageS8, Void, U32, U32, U32, ) | ||
| 99 | OPCODE(WriteStorageU16, Void, U32, U32, U32, ) | ||
| 100 | OPCODE(WriteStorageS16, Void, U32, U32, U32, ) | ||
| 101 | OPCODE(WriteStorage32, Void, U32, U32, U32, ) | ||
| 102 | OPCODE(WriteStorage64, Void, U32, U32, U32x2, ) | ||
| 103 | OPCODE(WriteStorage128, Void, U32, U32, U32x4, ) | ||
| 104 | |||
| 105 | // Local memory operations | ||
| 106 | OPCODE(LoadLocal, U32, U32, ) | ||
| 107 | OPCODE(WriteLocal, Void, U32, U32, ) | ||
| 108 | |||
| 109 | // Shared memory operations | ||
| 110 | OPCODE(LoadSharedU8, U32, U32, ) | ||
| 111 | OPCODE(LoadSharedS8, U32, U32, ) | ||
| 112 | OPCODE(LoadSharedU16, U32, U32, ) | ||
| 113 | OPCODE(LoadSharedS16, U32, U32, ) | ||
| 114 | OPCODE(LoadSharedU32, U32, U32, ) | ||
| 115 | OPCODE(LoadSharedU64, U32x2, U32, ) | ||
| 116 | OPCODE(LoadSharedU128, U32x4, U32, ) | ||
| 117 | OPCODE(WriteSharedU8, Void, U32, U32, ) | ||
| 118 | OPCODE(WriteSharedU16, Void, U32, U32, ) | ||
| 119 | OPCODE(WriteSharedU32, Void, U32, U32, ) | ||
| 120 | OPCODE(WriteSharedU64, Void, U32, U32x2, ) | ||
| 121 | OPCODE(WriteSharedU128, Void, U32, U32x4, ) | ||
| 122 | |||
| 123 | // Vector utility | ||
| 124 | OPCODE(CompositeConstructU32x2, U32x2, U32, U32, ) | ||
| 125 | OPCODE(CompositeConstructU32x3, U32x3, U32, U32, U32, ) | ||
| 126 | OPCODE(CompositeConstructU32x4, U32x4, U32, U32, U32, U32, ) | ||
| 127 | OPCODE(CompositeExtractU32x2, U32, U32x2, U32, ) | ||
| 128 | OPCODE(CompositeExtractU32x3, U32, U32x3, U32, ) | ||
| 129 | OPCODE(CompositeExtractU32x4, U32, U32x4, U32, ) | ||
| 130 | OPCODE(CompositeInsertU32x2, U32x2, U32x2, U32, U32, ) | ||
| 131 | OPCODE(CompositeInsertU32x3, U32x3, U32x3, U32, U32, ) | ||
| 132 | OPCODE(CompositeInsertU32x4, U32x4, U32x4, U32, U32, ) | ||
| 133 | OPCODE(CompositeConstructF16x2, F16x2, F16, F16, ) | ||
| 134 | OPCODE(CompositeConstructF16x3, F16x3, F16, F16, F16, ) | ||
| 135 | OPCODE(CompositeConstructF16x4, F16x4, F16, F16, F16, F16, ) | ||
| 136 | OPCODE(CompositeExtractF16x2, F16, F16x2, U32, ) | ||
| 137 | OPCODE(CompositeExtractF16x3, F16, F16x3, U32, ) | ||
| 138 | OPCODE(CompositeExtractF16x4, F16, F16x4, U32, ) | ||
| 139 | OPCODE(CompositeInsertF16x2, F16x2, F16x2, F16, U32, ) | ||
| 140 | OPCODE(CompositeInsertF16x3, F16x3, F16x3, F16, U32, ) | ||
| 141 | OPCODE(CompositeInsertF16x4, F16x4, F16x4, F16, U32, ) | ||
| 142 | OPCODE(CompositeConstructF32x2, F32x2, F32, F32, ) | ||
| 143 | OPCODE(CompositeConstructF32x3, F32x3, F32, F32, F32, ) | ||
| 144 | OPCODE(CompositeConstructF32x4, F32x4, F32, F32, F32, F32, ) | ||
| 145 | OPCODE(CompositeExtractF32x2, F32, F32x2, U32, ) | ||
| 146 | OPCODE(CompositeExtractF32x3, F32, F32x3, U32, ) | ||
| 147 | OPCODE(CompositeExtractF32x4, F32, F32x4, U32, ) | ||
| 148 | OPCODE(CompositeInsertF32x2, F32x2, F32x2, F32, U32, ) | ||
| 149 | OPCODE(CompositeInsertF32x3, F32x3, F32x3, F32, U32, ) | ||
| 150 | OPCODE(CompositeInsertF32x4, F32x4, F32x4, F32, U32, ) | ||
| 151 | OPCODE(CompositeConstructF64x2, F64x2, F64, F64, ) | ||
| 152 | OPCODE(CompositeConstructF64x3, F64x3, F64, F64, F64, ) | ||
| 153 | OPCODE(CompositeConstructF64x4, F64x4, F64, F64, F64, F64, ) | ||
| 154 | OPCODE(CompositeExtractF64x2, F64, F64x2, U32, ) | ||
| 155 | OPCODE(CompositeExtractF64x3, F64, F64x3, U32, ) | ||
| 156 | OPCODE(CompositeExtractF64x4, F64, F64x4, U32, ) | ||
| 157 | OPCODE(CompositeInsertF64x2, F64x2, F64x2, F64, U32, ) | ||
| 158 | OPCODE(CompositeInsertF64x3, F64x3, F64x3, F64, U32, ) | ||
| 159 | OPCODE(CompositeInsertF64x4, F64x4, F64x4, F64, U32, ) | ||
| 160 | |||
| 161 | // Select operations | ||
| 162 | OPCODE(SelectU1, U1, U1, U1, U1, ) | ||
| 163 | OPCODE(SelectU8, U8, U1, U8, U8, ) | ||
| 164 | OPCODE(SelectU16, U16, U1, U16, U16, ) | ||
| 165 | OPCODE(SelectU32, U32, U1, U32, U32, ) | ||
| 166 | OPCODE(SelectU64, U64, U1, U64, U64, ) | ||
| 167 | OPCODE(SelectF16, F16, U1, F16, F16, ) | ||
| 168 | OPCODE(SelectF32, F32, U1, F32, F32, ) | ||
| 169 | OPCODE(SelectF64, F64, U1, F64, F64, ) | ||
| 170 | |||
| 171 | // Bitwise conversions | ||
| 172 | OPCODE(BitCastU16F16, U16, F16, ) | ||
| 173 | OPCODE(BitCastU32F32, U32, F32, ) | ||
| 174 | OPCODE(BitCastU64F64, U64, F64, ) | ||
| 175 | OPCODE(BitCastF16U16, F16, U16, ) | ||
| 176 | OPCODE(BitCastF32U32, F32, U32, ) | ||
| 177 | OPCODE(BitCastF64U64, F64, U64, ) | ||
| 178 | OPCODE(PackUint2x32, U64, U32x2, ) | ||
| 179 | OPCODE(UnpackUint2x32, U32x2, U64, ) | ||
| 180 | OPCODE(PackFloat2x16, U32, F16x2, ) | ||
| 181 | OPCODE(UnpackFloat2x16, F16x2, U32, ) | ||
| 182 | OPCODE(PackHalf2x16, U32, F32x2, ) | ||
| 183 | OPCODE(UnpackHalf2x16, F32x2, U32, ) | ||
| 184 | OPCODE(PackDouble2x32, F64, U32x2, ) | ||
| 185 | OPCODE(UnpackDouble2x32, U32x2, F64, ) | ||
| 186 | |||
| 187 | // Pseudo-operation, handled specially at final emit | ||
| 188 | OPCODE(GetZeroFromOp, U1, Opaque, ) | ||
| 189 | OPCODE(GetSignFromOp, U1, Opaque, ) | ||
| 190 | OPCODE(GetCarryFromOp, U1, Opaque, ) | ||
| 191 | OPCODE(GetOverflowFromOp, U1, Opaque, ) | ||
| 192 | OPCODE(GetSparseFromOp, U1, Opaque, ) | ||
| 193 | OPCODE(GetInBoundsFromOp, U1, Opaque, ) | ||
| 194 | |||
| 195 | // Floating-point operations | ||
| 196 | OPCODE(FPAbs16, F16, F16, ) | ||
| 197 | OPCODE(FPAbs32, F32, F32, ) | ||
| 198 | OPCODE(FPAbs64, F64, F64, ) | ||
| 199 | OPCODE(FPAdd16, F16, F16, F16, ) | ||
| 200 | OPCODE(FPAdd32, F32, F32, F32, ) | ||
| 201 | OPCODE(FPAdd64, F64, F64, F64, ) | ||
| 202 | OPCODE(FPFma16, F16, F16, F16, F16, ) | ||
| 203 | OPCODE(FPFma32, F32, F32, F32, F32, ) | ||
| 204 | OPCODE(FPFma64, F64, F64, F64, F64, ) | ||
| 205 | OPCODE(FPMax32, F32, F32, F32, ) | ||
| 206 | OPCODE(FPMax64, F64, F64, F64, ) | ||
| 207 | OPCODE(FPMin32, F32, F32, F32, ) | ||
| 208 | OPCODE(FPMin64, F64, F64, F64, ) | ||
| 209 | OPCODE(FPMul16, F16, F16, F16, ) | ||
| 210 | OPCODE(FPMul32, F32, F32, F32, ) | ||
| 211 | OPCODE(FPMul64, F64, F64, F64, ) | ||
| 212 | OPCODE(FPNeg16, F16, F16, ) | ||
| 213 | OPCODE(FPNeg32, F32, F32, ) | ||
| 214 | OPCODE(FPNeg64, F64, F64, ) | ||
| 215 | OPCODE(FPRecip32, F32, F32, ) | ||
| 216 | OPCODE(FPRecip64, F64, F64, ) | ||
| 217 | OPCODE(FPRecipSqrt32, F32, F32, ) | ||
| 218 | OPCODE(FPRecipSqrt64, F64, F64, ) | ||
| 219 | OPCODE(FPSqrt, F32, F32, ) | ||
| 220 | OPCODE(FPSin, F32, F32, ) | ||
| 221 | OPCODE(FPExp2, F32, F32, ) | ||
| 222 | OPCODE(FPCos, F32, F32, ) | ||
| 223 | OPCODE(FPLog2, F32, F32, ) | ||
| 224 | OPCODE(FPSaturate16, F16, F16, ) | ||
| 225 | OPCODE(FPSaturate32, F32, F32, ) | ||
| 226 | OPCODE(FPSaturate64, F64, F64, ) | ||
| 227 | OPCODE(FPClamp16, F16, F16, F16, F16, ) | ||
| 228 | OPCODE(FPClamp32, F32, F32, F32, F32, ) | ||
| 229 | OPCODE(FPClamp64, F64, F64, F64, F64, ) | ||
| 230 | OPCODE(FPRoundEven16, F16, F16, ) | ||
| 231 | OPCODE(FPRoundEven32, F32, F32, ) | ||
| 232 | OPCODE(FPRoundEven64, F64, F64, ) | ||
| 233 | OPCODE(FPFloor16, F16, F16, ) | ||
| 234 | OPCODE(FPFloor32, F32, F32, ) | ||
| 235 | OPCODE(FPFloor64, F64, F64, ) | ||
| 236 | OPCODE(FPCeil16, F16, F16, ) | ||
| 237 | OPCODE(FPCeil32, F32, F32, ) | ||
| 238 | OPCODE(FPCeil64, F64, F64, ) | ||
| 239 | OPCODE(FPTrunc16, F16, F16, ) | ||
| 240 | OPCODE(FPTrunc32, F32, F32, ) | ||
| 241 | OPCODE(FPTrunc64, F64, F64, ) | ||
| 242 | |||
| 243 | OPCODE(FPOrdEqual16, U1, F16, F16, ) | ||
| 244 | OPCODE(FPOrdEqual32, U1, F32, F32, ) | ||
| 245 | OPCODE(FPOrdEqual64, U1, F64, F64, ) | ||
| 246 | OPCODE(FPUnordEqual16, U1, F16, F16, ) | ||
| 247 | OPCODE(FPUnordEqual32, U1, F32, F32, ) | ||
| 248 | OPCODE(FPUnordEqual64, U1, F64, F64, ) | ||
| 249 | OPCODE(FPOrdNotEqual16, U1, F16, F16, ) | ||
| 250 | OPCODE(FPOrdNotEqual32, U1, F32, F32, ) | ||
| 251 | OPCODE(FPOrdNotEqual64, U1, F64, F64, ) | ||
| 252 | OPCODE(FPUnordNotEqual16, U1, F16, F16, ) | ||
| 253 | OPCODE(FPUnordNotEqual32, U1, F32, F32, ) | ||
| 254 | OPCODE(FPUnordNotEqual64, U1, F64, F64, ) | ||
| 255 | OPCODE(FPOrdLessThan16, U1, F16, F16, ) | ||
| 256 | OPCODE(FPOrdLessThan32, U1, F32, F32, ) | ||
| 257 | OPCODE(FPOrdLessThan64, U1, F64, F64, ) | ||
| 258 | OPCODE(FPUnordLessThan16, U1, F16, F16, ) | ||
| 259 | OPCODE(FPUnordLessThan32, U1, F32, F32, ) | ||
| 260 | OPCODE(FPUnordLessThan64, U1, F64, F64, ) | ||
| 261 | OPCODE(FPOrdGreaterThan16, U1, F16, F16, ) | ||
| 262 | OPCODE(FPOrdGreaterThan32, U1, F32, F32, ) | ||
| 263 | OPCODE(FPOrdGreaterThan64, U1, F64, F64, ) | ||
| 264 | OPCODE(FPUnordGreaterThan16, U1, F16, F16, ) | ||
| 265 | OPCODE(FPUnordGreaterThan32, U1, F32, F32, ) | ||
| 266 | OPCODE(FPUnordGreaterThan64, U1, F64, F64, ) | ||
| 267 | OPCODE(FPOrdLessThanEqual16, U1, F16, F16, ) | ||
| 268 | OPCODE(FPOrdLessThanEqual32, U1, F32, F32, ) | ||
| 269 | OPCODE(FPOrdLessThanEqual64, U1, F64, F64, ) | ||
| 270 | OPCODE(FPUnordLessThanEqual16, U1, F16, F16, ) | ||
| 271 | OPCODE(FPUnordLessThanEqual32, U1, F32, F32, ) | ||
| 272 | OPCODE(FPUnordLessThanEqual64, U1, F64, F64, ) | ||
| 273 | OPCODE(FPOrdGreaterThanEqual16, U1, F16, F16, ) | ||
| 274 | OPCODE(FPOrdGreaterThanEqual32, U1, F32, F32, ) | ||
| 275 | OPCODE(FPOrdGreaterThanEqual64, U1, F64, F64, ) | ||
| 276 | OPCODE(FPUnordGreaterThanEqual16, U1, F16, F16, ) | ||
| 277 | OPCODE(FPUnordGreaterThanEqual32, U1, F32, F32, ) | ||
| 278 | OPCODE(FPUnordGreaterThanEqual64, U1, F64, F64, ) | ||
| 279 | OPCODE(FPIsNan16, U1, F16, ) | ||
| 280 | OPCODE(FPIsNan32, U1, F32, ) | ||
| 281 | OPCODE(FPIsNan64, U1, F64, ) | ||
| 282 | |||
| 283 | // Integer operations | ||
| 284 | OPCODE(IAdd32, U32, U32, U32, ) | ||
| 285 | OPCODE(IAdd64, U64, U64, U64, ) | ||
| 286 | OPCODE(ISub32, U32, U32, U32, ) | ||
| 287 | OPCODE(ISub64, U64, U64, U64, ) | ||
| 288 | OPCODE(IMul32, U32, U32, U32, ) | ||
| 289 | OPCODE(INeg32, U32, U32, ) | ||
| 290 | OPCODE(INeg64, U64, U64, ) | ||
| 291 | OPCODE(IAbs32, U32, U32, ) | ||
| 292 | OPCODE(ShiftLeftLogical32, U32, U32, U32, ) | ||
| 293 | OPCODE(ShiftLeftLogical64, U64, U64, U32, ) | ||
| 294 | OPCODE(ShiftRightLogical32, U32, U32, U32, ) | ||
| 295 | OPCODE(ShiftRightLogical64, U64, U64, U32, ) | ||
| 296 | OPCODE(ShiftRightArithmetic32, U32, U32, U32, ) | ||
| 297 | OPCODE(ShiftRightArithmetic64, U64, U64, U32, ) | ||
| 298 | OPCODE(BitwiseAnd32, U32, U32, U32, ) | ||
| 299 | OPCODE(BitwiseOr32, U32, U32, U32, ) | ||
| 300 | OPCODE(BitwiseXor32, U32, U32, U32, ) | ||
| 301 | OPCODE(BitFieldInsert, U32, U32, U32, U32, U32, ) | ||
| 302 | OPCODE(BitFieldSExtract, U32, U32, U32, U32, ) | ||
| 303 | OPCODE(BitFieldUExtract, U32, U32, U32, U32, ) | ||
| 304 | OPCODE(BitReverse32, U32, U32, ) | ||
| 305 | OPCODE(BitCount32, U32, U32, ) | ||
| 306 | OPCODE(BitwiseNot32, U32, U32, ) | ||
| 307 | |||
| 308 | OPCODE(FindSMsb32, U32, U32, ) | ||
| 309 | OPCODE(FindUMsb32, U32, U32, ) | ||
| 310 | OPCODE(SMin32, U32, U32, U32, ) | ||
| 311 | OPCODE(UMin32, U32, U32, U32, ) | ||
| 312 | OPCODE(SMax32, U32, U32, U32, ) | ||
| 313 | OPCODE(UMax32, U32, U32, U32, ) | ||
| 314 | OPCODE(SClamp32, U32, U32, U32, U32, ) | ||
| 315 | OPCODE(UClamp32, U32, U32, U32, U32, ) | ||
| 316 | OPCODE(SLessThan, U1, U32, U32, ) | ||
| 317 | OPCODE(ULessThan, U1, U32, U32, ) | ||
| 318 | OPCODE(IEqual, U1, U32, U32, ) | ||
| 319 | OPCODE(SLessThanEqual, U1, U32, U32, ) | ||
| 320 | OPCODE(ULessThanEqual, U1, U32, U32, ) | ||
| 321 | OPCODE(SGreaterThan, U1, U32, U32, ) | ||
| 322 | OPCODE(UGreaterThan, U1, U32, U32, ) | ||
| 323 | OPCODE(INotEqual, U1, U32, U32, ) | ||
| 324 | OPCODE(SGreaterThanEqual, U1, U32, U32, ) | ||
| 325 | OPCODE(UGreaterThanEqual, U1, U32, U32, ) | ||
| 326 | |||
| 327 | // Atomic operations | ||
| 328 | OPCODE(SharedAtomicIAdd32, U32, U32, U32, ) | ||
| 329 | OPCODE(SharedAtomicSMin32, U32, U32, U32, ) | ||
| 330 | OPCODE(SharedAtomicUMin32, U32, U32, U32, ) | ||
| 331 | OPCODE(SharedAtomicSMax32, U32, U32, U32, ) | ||
| 332 | OPCODE(SharedAtomicUMax32, U32, U32, U32, ) | ||
| 333 | OPCODE(SharedAtomicInc32, U32, U32, U32, ) | ||
| 334 | OPCODE(SharedAtomicDec32, U32, U32, U32, ) | ||
| 335 | OPCODE(SharedAtomicAnd32, U32, U32, U32, ) | ||
| 336 | OPCODE(SharedAtomicOr32, U32, U32, U32, ) | ||
| 337 | OPCODE(SharedAtomicXor32, U32, U32, U32, ) | ||
| 338 | OPCODE(SharedAtomicExchange32, U32, U32, U32, ) | ||
| 339 | OPCODE(SharedAtomicExchange64, U64, U32, U64, ) | ||
| 340 | |||
| 341 | OPCODE(GlobalAtomicIAdd32, U32, U64, U32, ) | ||
| 342 | OPCODE(GlobalAtomicSMin32, U32, U64, U32, ) | ||
| 343 | OPCODE(GlobalAtomicUMin32, U32, U64, U32, ) | ||
| 344 | OPCODE(GlobalAtomicSMax32, U32, U64, U32, ) | ||
| 345 | OPCODE(GlobalAtomicUMax32, U32, U64, U32, ) | ||
| 346 | OPCODE(GlobalAtomicInc32, U32, U64, U32, ) | ||
| 347 | OPCODE(GlobalAtomicDec32, U32, U64, U32, ) | ||
| 348 | OPCODE(GlobalAtomicAnd32, U32, U64, U32, ) | ||
| 349 | OPCODE(GlobalAtomicOr32, U32, U64, U32, ) | ||
| 350 | OPCODE(GlobalAtomicXor32, U32, U64, U32, ) | ||
| 351 | OPCODE(GlobalAtomicExchange32, U32, U64, U32, ) | ||
| 352 | OPCODE(GlobalAtomicIAdd64, U64, U64, U64, ) | ||
| 353 | OPCODE(GlobalAtomicSMin64, U64, U64, U64, ) | ||
| 354 | OPCODE(GlobalAtomicUMin64, U64, U64, U64, ) | ||
| 355 | OPCODE(GlobalAtomicSMax64, U64, U64, U64, ) | ||
| 356 | OPCODE(GlobalAtomicUMax64, U64, U64, U64, ) | ||
| 357 | OPCODE(GlobalAtomicAnd64, U64, U64, U64, ) | ||
| 358 | OPCODE(GlobalAtomicOr64, U64, U64, U64, ) | ||
| 359 | OPCODE(GlobalAtomicXor64, U64, U64, U64, ) | ||
| 360 | OPCODE(GlobalAtomicExchange64, U64, U64, U64, ) | ||
| 361 | OPCODE(GlobalAtomicAddF32, F32, U64, F32, ) | ||
| 362 | OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, ) | ||
| 363 | OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, ) | ||
| 364 | OPCODE(GlobalAtomicMinF16x2, U32, U64, F16x2, ) | ||
| 365 | OPCODE(GlobalAtomicMinF32x2, U32, U64, F32x2, ) | ||
| 366 | OPCODE(GlobalAtomicMaxF16x2, U32, U64, F16x2, ) | ||
| 367 | OPCODE(GlobalAtomicMaxF32x2, U32, U64, F32x2, ) | ||
| 368 | |||
| 369 | OPCODE(StorageAtomicIAdd32, U32, U32, U32, U32, ) | ||
| 370 | OPCODE(StorageAtomicSMin32, U32, U32, U32, U32, ) | ||
| 371 | OPCODE(StorageAtomicUMin32, U32, U32, U32, U32, ) | ||
| 372 | OPCODE(StorageAtomicSMax32, U32, U32, U32, U32, ) | ||
| 373 | OPCODE(StorageAtomicUMax32, U32, U32, U32, U32, ) | ||
| 374 | OPCODE(StorageAtomicInc32, U32, U32, U32, U32, ) | ||
| 375 | OPCODE(StorageAtomicDec32, U32, U32, U32, U32, ) | ||
| 376 | OPCODE(StorageAtomicAnd32, U32, U32, U32, U32, ) | ||
| 377 | OPCODE(StorageAtomicOr32, U32, U32, U32, U32, ) | ||
| 378 | OPCODE(StorageAtomicXor32, U32, U32, U32, U32, ) | ||
| 379 | OPCODE(StorageAtomicExchange32, U32, U32, U32, U32, ) | ||
| 380 | OPCODE(StorageAtomicIAdd64, U64, U32, U32, U64, ) | ||
| 381 | OPCODE(StorageAtomicSMin64, U64, U32, U32, U64, ) | ||
| 382 | OPCODE(StorageAtomicUMin64, U64, U32, U32, U64, ) | ||
| 383 | OPCODE(StorageAtomicSMax64, U64, U32, U32, U64, ) | ||
| 384 | OPCODE(StorageAtomicUMax64, U64, U32, U32, U64, ) | ||
| 385 | OPCODE(StorageAtomicAnd64, U64, U32, U32, U64, ) | ||
| 386 | OPCODE(StorageAtomicOr64, U64, U32, U32, U64, ) | ||
| 387 | OPCODE(StorageAtomicXor64, U64, U32, U32, U64, ) | ||
| 388 | OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, ) | ||
| 389 | OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, ) | ||
| 390 | OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, ) | ||
| 391 | OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, ) | ||
| 392 | OPCODE(StorageAtomicMinF16x2, U32, U32, U32, F16x2, ) | ||
| 393 | OPCODE(StorageAtomicMinF32x2, U32, U32, U32, F32x2, ) | ||
| 394 | OPCODE(StorageAtomicMaxF16x2, U32, U32, U32, F16x2, ) | ||
| 395 | OPCODE(StorageAtomicMaxF32x2, U32, U32, U32, F32x2, ) | ||
| 396 | |||
| 397 | // Logical operations | ||
| 398 | OPCODE(LogicalOr, U1, U1, U1, ) | ||
| 399 | OPCODE(LogicalAnd, U1, U1, U1, ) | ||
| 400 | OPCODE(LogicalXor, U1, U1, U1, ) | ||
| 401 | OPCODE(LogicalNot, U1, U1, ) | ||
| 402 | |||
| 403 | // Conversion operations | ||
| 404 | OPCODE(ConvertS16F16, U32, F16, ) | ||
| 405 | OPCODE(ConvertS16F32, U32, F32, ) | ||
| 406 | OPCODE(ConvertS16F64, U32, F64, ) | ||
| 407 | OPCODE(ConvertS32F16, U32, F16, ) | ||
| 408 | OPCODE(ConvertS32F32, U32, F32, ) | ||
| 409 | OPCODE(ConvertS32F64, U32, F64, ) | ||
| 410 | OPCODE(ConvertS64F16, U64, F16, ) | ||
| 411 | OPCODE(ConvertS64F32, U64, F32, ) | ||
| 412 | OPCODE(ConvertS64F64, U64, F64, ) | ||
| 413 | OPCODE(ConvertU16F16, U32, F16, ) | ||
| 414 | OPCODE(ConvertU16F32, U32, F32, ) | ||
| 415 | OPCODE(ConvertU16F64, U32, F64, ) | ||
| 416 | OPCODE(ConvertU32F16, U32, F16, ) | ||
| 417 | OPCODE(ConvertU32F32, U32, F32, ) | ||
| 418 | OPCODE(ConvertU32F64, U32, F64, ) | ||
| 419 | OPCODE(ConvertU64F16, U64, F16, ) | ||
| 420 | OPCODE(ConvertU64F32, U64, F32, ) | ||
| 421 | OPCODE(ConvertU64F64, U64, F64, ) | ||
| 422 | OPCODE(ConvertU64U32, U64, U32, ) | ||
| 423 | OPCODE(ConvertU32U64, U32, U64, ) | ||
| 424 | OPCODE(ConvertF16F32, F16, F32, ) | ||
| 425 | OPCODE(ConvertF32F16, F32, F16, ) | ||
| 426 | OPCODE(ConvertF32F64, F32, F64, ) | ||
| 427 | OPCODE(ConvertF64F32, F64, F32, ) | ||
| 428 | OPCODE(ConvertF16S8, F16, U32, ) | ||
| 429 | OPCODE(ConvertF16S16, F16, U32, ) | ||
| 430 | OPCODE(ConvertF16S32, F16, U32, ) | ||
| 431 | OPCODE(ConvertF16S64, F16, U64, ) | ||
| 432 | OPCODE(ConvertF16U8, F16, U32, ) | ||
| 433 | OPCODE(ConvertF16U16, F16, U32, ) | ||
| 434 | OPCODE(ConvertF16U32, F16, U32, ) | ||
| 435 | OPCODE(ConvertF16U64, F16, U64, ) | ||
| 436 | OPCODE(ConvertF32S8, F32, U32, ) | ||
| 437 | OPCODE(ConvertF32S16, F32, U32, ) | ||
| 438 | OPCODE(ConvertF32S32, F32, U32, ) | ||
| 439 | OPCODE(ConvertF32S64, F32, U64, ) | ||
| 440 | OPCODE(ConvertF32U8, F32, U32, ) | ||
| 441 | OPCODE(ConvertF32U16, F32, U32, ) | ||
| 442 | OPCODE(ConvertF32U32, F32, U32, ) | ||
| 443 | OPCODE(ConvertF32U64, F32, U64, ) | ||
| 444 | OPCODE(ConvertF64S8, F64, U32, ) | ||
| 445 | OPCODE(ConvertF64S16, F64, U32, ) | ||
| 446 | OPCODE(ConvertF64S32, F64, U32, ) | ||
| 447 | OPCODE(ConvertF64S64, F64, U64, ) | ||
| 448 | OPCODE(ConvertF64U8, F64, U32, ) | ||
| 449 | OPCODE(ConvertF64U16, F64, U32, ) | ||
| 450 | OPCODE(ConvertF64U32, F64, U32, ) | ||
| 451 | OPCODE(ConvertF64U64, F64, U64, ) | ||
| 452 | |||
| 453 | // Image operations | ||
| 454 | OPCODE(BindlessImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) | ||
| 455 | OPCODE(BindlessImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) | ||
| 456 | OPCODE(BindlessImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) | ||
| 457 | OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) | ||
| 458 | OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) | ||
| 459 | OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) | ||
| 460 | OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) | ||
| 461 | OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, ) | ||
| 462 | OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) | ||
| 463 | OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) | ||
| 464 | OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) | ||
| 465 | OPCODE(BindlessImageWrite, Void, U32, Opaque, U32x4, ) | ||
| 466 | |||
| 467 | OPCODE(BoundImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) | ||
| 468 | OPCODE(BoundImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) | ||
| 469 | OPCODE(BoundImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) | ||
| 470 | OPCODE(BoundImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) | ||
| 471 | OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) | ||
| 472 | OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) | ||
| 473 | OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) | ||
| 474 | OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, ) | ||
| 475 | OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) | ||
| 476 | OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) | ||
| 477 | OPCODE(BoundImageRead, U32x4, U32, Opaque, ) | ||
| 478 | OPCODE(BoundImageWrite, Void, U32, Opaque, U32x4, ) | ||
| 479 | |||
| 480 | OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, ) | ||
| 481 | OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, ) | ||
| 482 | OPCODE(ImageSampleDrefImplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, ) | ||
| 483 | OPCODE(ImageSampleDrefExplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, ) | ||
| 484 | OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) | ||
| 485 | OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) | ||
| 486 | OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) | ||
| 487 | OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, ) | ||
| 488 | OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) | ||
| 489 | OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) | ||
| 490 | OPCODE(ImageRead, U32x4, Opaque, Opaque, ) | ||
| 491 | OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, ) | ||
| 492 | |||
| 493 | // Atomic Image operations | ||
| 494 | |||
| 495 | OPCODE(BindlessImageAtomicIAdd32, U32, U32, Opaque, U32, ) | ||
| 496 | OPCODE(BindlessImageAtomicSMin32, U32, U32, Opaque, U32, ) | ||
| 497 | OPCODE(BindlessImageAtomicUMin32, U32, U32, Opaque, U32, ) | ||
| 498 | OPCODE(BindlessImageAtomicSMax32, U32, U32, Opaque, U32, ) | ||
| 499 | OPCODE(BindlessImageAtomicUMax32, U32, U32, Opaque, U32, ) | ||
| 500 | OPCODE(BindlessImageAtomicInc32, U32, U32, Opaque, U32, ) | ||
| 501 | OPCODE(BindlessImageAtomicDec32, U32, U32, Opaque, U32, ) | ||
| 502 | OPCODE(BindlessImageAtomicAnd32, U32, U32, Opaque, U32, ) | ||
| 503 | OPCODE(BindlessImageAtomicOr32, U32, U32, Opaque, U32, ) | ||
| 504 | OPCODE(BindlessImageAtomicXor32, U32, U32, Opaque, U32, ) | ||
| 505 | OPCODE(BindlessImageAtomicExchange32, U32, U32, Opaque, U32, ) | ||
| 506 | |||
| 507 | OPCODE(BoundImageAtomicIAdd32, U32, U32, Opaque, U32, ) | ||
| 508 | OPCODE(BoundImageAtomicSMin32, U32, U32, Opaque, U32, ) | ||
| 509 | OPCODE(BoundImageAtomicUMin32, U32, U32, Opaque, U32, ) | ||
| 510 | OPCODE(BoundImageAtomicSMax32, U32, U32, Opaque, U32, ) | ||
| 511 | OPCODE(BoundImageAtomicUMax32, U32, U32, Opaque, U32, ) | ||
| 512 | OPCODE(BoundImageAtomicInc32, U32, U32, Opaque, U32, ) | ||
| 513 | OPCODE(BoundImageAtomicDec32, U32, U32, Opaque, U32, ) | ||
| 514 | OPCODE(BoundImageAtomicAnd32, U32, U32, Opaque, U32, ) | ||
| 515 | OPCODE(BoundImageAtomicOr32, U32, U32, Opaque, U32, ) | ||
| 516 | OPCODE(BoundImageAtomicXor32, U32, U32, Opaque, U32, ) | ||
| 517 | OPCODE(BoundImageAtomicExchange32, U32, U32, Opaque, U32, ) | ||
| 518 | |||
| 519 | OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, ) | ||
| 520 | OPCODE(ImageAtomicSMin32, U32, Opaque, Opaque, U32, ) | ||
| 521 | OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, ) | ||
| 522 | OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, ) | ||
| 523 | OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, ) | ||
| 524 | OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, ) | ||
| 525 | OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, ) | ||
| 526 | OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, ) | ||
| 527 | OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, ) | ||
| 528 | OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, ) | ||
| 529 | OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, ) | ||
| 530 | |||
| 531 | // Warp operations | ||
| 532 | OPCODE(LaneId, U32, ) | ||
| 533 | OPCODE(VoteAll, U1, U1, ) | ||
| 534 | OPCODE(VoteAny, U1, U1, ) | ||
| 535 | OPCODE(VoteEqual, U1, U1, ) | ||
| 536 | OPCODE(SubgroupBallot, U32, U1, ) | ||
| 537 | OPCODE(SubgroupEqMask, U32, ) | ||
| 538 | OPCODE(SubgroupLtMask, U32, ) | ||
| 539 | OPCODE(SubgroupLeMask, U32, ) | ||
| 540 | OPCODE(SubgroupGtMask, U32, ) | ||
| 541 | OPCODE(SubgroupGeMask, U32, ) | ||
| 542 | OPCODE(ShuffleIndex, U32, U32, U32, U32, U32, ) | ||
| 543 | OPCODE(ShuffleUp, U32, U32, U32, U32, U32, ) | ||
| 544 | OPCODE(ShuffleDown, U32, U32, U32, U32, U32, ) | ||
| 545 | OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, ) | ||
| 546 | OPCODE(FSwizzleAdd, F32, F32, F32, U32, ) | ||
| 547 | OPCODE(DPdxFine, F32, F32, ) | ||
| 548 | OPCODE(DPdyFine, F32, F32, ) | ||
| 549 | OPCODE(DPdxCoarse, F32, F32, ) | ||
| 550 | OPCODE(DPdyCoarse, F32, F32, ) | ||
diff --git a/src/shader_recompiler/frontend/ir/patch.cpp b/src/shader_recompiler/frontend/ir/patch.cpp new file mode 100644 index 000000000..4c956a970 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/patch.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/exception.h" | ||
| 6 | #include "shader_recompiler/frontend/ir/patch.h" | ||
| 7 | |||
| 8 | namespace Shader::IR { | ||
| 9 | |||
| 10 | bool IsGeneric(Patch patch) noexcept { | ||
| 11 | return patch >= Patch::Component0 && patch <= Patch::Component119; | ||
| 12 | } | ||
| 13 | |||
| 14 | u32 GenericPatchIndex(Patch patch) { | ||
| 15 | if (!IsGeneric(patch)) { | ||
| 16 | throw InvalidArgument("Patch {} is not generic", patch); | ||
| 17 | } | ||
| 18 | return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) / 4; | ||
| 19 | } | ||
| 20 | |||
| 21 | u32 GenericPatchElement(Patch patch) { | ||
| 22 | if (!IsGeneric(patch)) { | ||
| 23 | throw InvalidArgument("Patch {} is not generic", patch); | ||
| 24 | } | ||
| 25 | return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) % 4; | ||
| 26 | } | ||
| 27 | |||
| 28 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/patch.h b/src/shader_recompiler/frontend/ir/patch.h new file mode 100644 index 000000000..6d66ff0d6 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/patch.h | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Shader::IR { | ||
| 10 | |||
| 11 | enum class Patch : u64 { | ||
| 12 | TessellationLodLeft, | ||
| 13 | TessellationLodTop, | ||
| 14 | TessellationLodRight, | ||
| 15 | TessellationLodBottom, | ||
| 16 | TessellationLodInteriorU, | ||
| 17 | TessellationLodInteriorV, | ||
| 18 | ComponentPadding0, | ||
| 19 | ComponentPadding1, | ||
| 20 | Component0, | ||
| 21 | Component1, | ||
| 22 | Component2, | ||
| 23 | Component3, | ||
| 24 | Component4, | ||
| 25 | Component5, | ||
| 26 | Component6, | ||
| 27 | Component7, | ||
| 28 | Component8, | ||
| 29 | Component9, | ||
| 30 | Component10, | ||
| 31 | Component11, | ||
| 32 | Component12, | ||
| 33 | Component13, | ||
| 34 | Component14, | ||
| 35 | Component15, | ||
| 36 | Component16, | ||
| 37 | Component17, | ||
| 38 | Component18, | ||
| 39 | Component19, | ||
| 40 | Component20, | ||
| 41 | Component21, | ||
| 42 | Component22, | ||
| 43 | Component23, | ||
| 44 | Component24, | ||
| 45 | Component25, | ||
| 46 | Component26, | ||
| 47 | Component27, | ||
| 48 | Component28, | ||
| 49 | Component29, | ||
| 50 | Component30, | ||
| 51 | Component31, | ||
| 52 | Component32, | ||
| 53 | Component33, | ||
| 54 | Component34, | ||
| 55 | Component35, | ||
| 56 | Component36, | ||
| 57 | Component37, | ||
| 58 | Component38, | ||
| 59 | Component39, | ||
| 60 | Component40, | ||
| 61 | Component41, | ||
| 62 | Component42, | ||
| 63 | Component43, | ||
| 64 | Component44, | ||
| 65 | Component45, | ||
| 66 | Component46, | ||
| 67 | Component47, | ||
| 68 | Component48, | ||
| 69 | Component49, | ||
| 70 | Component50, | ||
| 71 | Component51, | ||
| 72 | Component52, | ||
| 73 | Component53, | ||
| 74 | Component54, | ||
| 75 | Component55, | ||
| 76 | Component56, | ||
| 77 | Component57, | ||
| 78 | Component58, | ||
| 79 | Component59, | ||
| 80 | Component60, | ||
| 81 | Component61, | ||
| 82 | Component62, | ||
| 83 | Component63, | ||
| 84 | Component64, | ||
| 85 | Component65, | ||
| 86 | Component66, | ||
| 87 | Component67, | ||
| 88 | Component68, | ||
| 89 | Component69, | ||
| 90 | Component70, | ||
| 91 | Component71, | ||
| 92 | Component72, | ||
| 93 | Component73, | ||
| 94 | Component74, | ||
| 95 | Component75, | ||
| 96 | Component76, | ||
| 97 | Component77, | ||
| 98 | Component78, | ||
| 99 | Component79, | ||
| 100 | Component80, | ||
| 101 | Component81, | ||
| 102 | Component82, | ||
| 103 | Component83, | ||
| 104 | Component84, | ||
| 105 | Component85, | ||
| 106 | Component86, | ||
| 107 | Component87, | ||
| 108 | Component88, | ||
| 109 | Component89, | ||
| 110 | Component90, | ||
| 111 | Component91, | ||
| 112 | Component92, | ||
| 113 | Component93, | ||
| 114 | Component94, | ||
| 115 | Component95, | ||
| 116 | Component96, | ||
| 117 | Component97, | ||
| 118 | Component98, | ||
| 119 | Component99, | ||
| 120 | Component100, | ||
| 121 | Component101, | ||
| 122 | Component102, | ||
| 123 | Component103, | ||
| 124 | Component104, | ||
| 125 | Component105, | ||
| 126 | Component106, | ||
| 127 | Component107, | ||
| 128 | Component108, | ||
| 129 | Component109, | ||
| 130 | Component110, | ||
| 131 | Component111, | ||
| 132 | Component112, | ||
| 133 | Component113, | ||
| 134 | Component114, | ||
| 135 | Component115, | ||
| 136 | Component116, | ||
| 137 | Component117, | ||
| 138 | Component118, | ||
| 139 | Component119, | ||
| 140 | }; | ||
| 141 | static_assert(static_cast<u64>(Patch::Component119) == 127); | ||
| 142 | |||
| 143 | [[nodiscard]] bool IsGeneric(Patch patch) noexcept; | ||
| 144 | |||
| 145 | [[nodiscard]] u32 GenericPatchIndex(Patch patch); | ||
| 146 | |||
| 147 | [[nodiscard]] u32 GenericPatchElement(Patch patch); | ||
| 148 | |||
| 149 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/post_order.cpp b/src/shader_recompiler/frontend/ir/post_order.cpp new file mode 100644 index 000000000..16bc44101 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/post_order.cpp | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include <boost/container/flat_set.hpp> | ||
| 8 | #include <boost/container/small_vector.hpp> | ||
| 9 | |||
| 10 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 11 | #include "shader_recompiler/frontend/ir/post_order.h" | ||
| 12 | |||
| 13 | namespace Shader::IR { | ||
| 14 | |||
| 15 | BlockList PostOrder(const AbstractSyntaxNode& root) { | ||
| 16 | boost::container::small_vector<Block*, 16> block_stack; | ||
| 17 | boost::container::flat_set<Block*> visited; | ||
| 18 | BlockList post_order_blocks; | ||
| 19 | |||
| 20 | if (root.type != AbstractSyntaxNode::Type::Block) { | ||
| 21 | throw LogicError("First node in abstract syntax list root is not a block"); | ||
| 22 | } | ||
| 23 | Block* const first_block{root.data.block}; | ||
| 24 | visited.insert(first_block); | ||
| 25 | block_stack.push_back(first_block); | ||
| 26 | |||
| 27 | while (!block_stack.empty()) { | ||
| 28 | Block* const block{block_stack.back()}; | ||
| 29 | const auto visit{[&](Block* branch) { | ||
| 30 | if (!visited.insert(branch).second) { | ||
| 31 | return false; | ||
| 32 | } | ||
| 33 | // Calling push_back twice is faster than insert on MSVC | ||
| 34 | block_stack.push_back(block); | ||
| 35 | block_stack.push_back(branch); | ||
| 36 | return true; | ||
| 37 | }}; | ||
| 38 | block_stack.pop_back(); | ||
| 39 | if (std::ranges::none_of(block->ImmSuccessors(), visit)) { | ||
| 40 | post_order_blocks.push_back(block); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | return post_order_blocks; | ||
| 44 | } | ||
| 45 | |||
| 46 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/post_order.h b/src/shader_recompiler/frontend/ir/post_order.h new file mode 100644 index 000000000..07bfbadc3 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/post_order.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "shader_recompiler/frontend/ir/abstract_syntax_list.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 9 | |||
| 10 | namespace Shader::IR { | ||
| 11 | |||
| 12 | BlockList PostOrder(const AbstractSyntaxNode& root); | ||
| 13 | |||
| 14 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/pred.h b/src/shader_recompiler/frontend/ir/pred.h new file mode 100644 index 000000000..4e7f32423 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/pred.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | namespace Shader::IR { | ||
| 10 | |||
| 11 | enum class Pred : u64 { | ||
| 12 | P0, | ||
| 13 | P1, | ||
| 14 | P2, | ||
| 15 | P3, | ||
| 16 | P4, | ||
| 17 | P5, | ||
| 18 | P6, | ||
| 19 | PT, | ||
| 20 | }; | ||
| 21 | |||
| 22 | constexpr size_t NUM_USER_PREDS = 7; | ||
| 23 | constexpr size_t NUM_PREDS = 8; | ||
| 24 | |||
| 25 | [[nodiscard]] constexpr size_t PredIndex(Pred pred) noexcept { | ||
| 26 | return static_cast<size_t>(pred); | ||
| 27 | } | ||
| 28 | |||
| 29 | } // namespace Shader::IR | ||
| 30 | |||
| 31 | template <> | ||
| 32 | struct fmt::formatter<Shader::IR::Pred> { | ||
| 33 | constexpr auto parse(format_parse_context& ctx) { | ||
| 34 | return ctx.begin(); | ||
| 35 | } | ||
| 36 | template <typename FormatContext> | ||
| 37 | auto format(const Shader::IR::Pred& pred, FormatContext& ctx) { | ||
| 38 | if (pred == Shader::IR::Pred::PT) { | ||
| 39 | return fmt::format_to(ctx.out(), "PT"); | ||
| 40 | } else { | ||
| 41 | return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred)); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/program.cpp b/src/shader_recompiler/frontend/ir/program.cpp new file mode 100644 index 000000000..3fc06f855 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/program.cpp | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <map> | ||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include <fmt/format.h> | ||
| 9 | |||
| 10 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 11 | #include "shader_recompiler/frontend/ir/program.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 13 | |||
| 14 | namespace Shader::IR { | ||
| 15 | |||
| 16 | std::string DumpProgram(const Program& program) { | ||
| 17 | size_t index{0}; | ||
| 18 | std::map<const IR::Inst*, size_t> inst_to_index; | ||
| 19 | std::map<const IR::Block*, size_t> block_to_index; | ||
| 20 | |||
| 21 | for (const IR::Block* const block : program.blocks) { | ||
| 22 | block_to_index.emplace(block, index); | ||
| 23 | ++index; | ||
| 24 | } | ||
| 25 | std::string ret; | ||
| 26 | for (const auto& block : program.blocks) { | ||
| 27 | ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; | ||
| 28 | } | ||
| 29 | return ret; | ||
| 30 | } | ||
| 31 | |||
| 32 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/program.h b/src/shader_recompiler/frontend/ir/program.h new file mode 100644 index 000000000..ebcaa8bc2 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/program.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | #include "shader_recompiler/frontend/ir/abstract_syntax_list.h" | ||
| 11 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 12 | #include "shader_recompiler/program_header.h" | ||
| 13 | #include "shader_recompiler/shader_info.h" | ||
| 14 | #include "shader_recompiler/stage.h" | ||
| 15 | |||
| 16 | namespace Shader::IR { | ||
| 17 | |||
| 18 | struct Program { | ||
| 19 | AbstractSyntaxList syntax_list; | ||
| 20 | BlockList blocks; | ||
| 21 | BlockList post_order_blocks; | ||
| 22 | Info info; | ||
| 23 | Stage stage{}; | ||
| 24 | std::array<u32, 3> workgroup_size{}; | ||
| 25 | OutputTopology output_topology{}; | ||
| 26 | u32 output_vertices{}; | ||
| 27 | u32 invocations{}; | ||
| 28 | u32 local_memory_size{}; | ||
| 29 | u32 shared_memory_size{}; | ||
| 30 | bool is_geometry_passthrough{}; | ||
| 31 | }; | ||
| 32 | |||
| 33 | [[nodiscard]] std::string DumpProgram(const Program& program); | ||
| 34 | |||
| 35 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/reg.h b/src/shader_recompiler/frontend/ir/reg.h new file mode 100644 index 000000000..a4b635792 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/reg.h | |||
| @@ -0,0 +1,332 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <fmt/format.h> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "shader_recompiler/exception.h" | ||
| 11 | |||
| 12 | namespace Shader::IR { | ||
| 13 | |||
| 14 | enum class Reg : u64 { | ||
| 15 | R0, | ||
| 16 | R1, | ||
| 17 | R2, | ||
| 18 | R3, | ||
| 19 | R4, | ||
| 20 | R5, | ||
| 21 | R6, | ||
| 22 | R7, | ||
| 23 | R8, | ||
| 24 | R9, | ||
| 25 | R10, | ||
| 26 | R11, | ||
| 27 | R12, | ||
| 28 | R13, | ||
| 29 | R14, | ||
| 30 | R15, | ||
| 31 | R16, | ||
| 32 | R17, | ||
| 33 | R18, | ||
| 34 | R19, | ||
| 35 | R20, | ||
| 36 | R21, | ||
| 37 | R22, | ||
| 38 | R23, | ||
| 39 | R24, | ||
| 40 | R25, | ||
| 41 | R26, | ||
| 42 | R27, | ||
| 43 | R28, | ||
| 44 | R29, | ||
| 45 | R30, | ||
| 46 | R31, | ||
| 47 | R32, | ||
| 48 | R33, | ||
| 49 | R34, | ||
| 50 | R35, | ||
| 51 | R36, | ||
| 52 | R37, | ||
| 53 | R38, | ||
| 54 | R39, | ||
| 55 | R40, | ||
| 56 | R41, | ||
| 57 | R42, | ||
| 58 | R43, | ||
| 59 | R44, | ||
| 60 | R45, | ||
| 61 | R46, | ||
| 62 | R47, | ||
| 63 | R48, | ||
| 64 | R49, | ||
| 65 | R50, | ||
| 66 | R51, | ||
| 67 | R52, | ||
| 68 | R53, | ||
| 69 | R54, | ||
| 70 | R55, | ||
| 71 | R56, | ||
| 72 | R57, | ||
| 73 | R58, | ||
| 74 | R59, | ||
| 75 | R60, | ||
| 76 | R61, | ||
| 77 | R62, | ||
| 78 | R63, | ||
| 79 | R64, | ||
| 80 | R65, | ||
| 81 | R66, | ||
| 82 | R67, | ||
| 83 | R68, | ||
| 84 | R69, | ||
| 85 | R70, | ||
| 86 | R71, | ||
| 87 | R72, | ||
| 88 | R73, | ||
| 89 | R74, | ||
| 90 | R75, | ||
| 91 | R76, | ||
| 92 | R77, | ||
| 93 | R78, | ||
| 94 | R79, | ||
| 95 | R80, | ||
| 96 | R81, | ||
| 97 | R82, | ||
| 98 | R83, | ||
| 99 | R84, | ||
| 100 | R85, | ||
| 101 | R86, | ||
| 102 | R87, | ||
| 103 | R88, | ||
| 104 | R89, | ||
| 105 | R90, | ||
| 106 | R91, | ||
| 107 | R92, | ||
| 108 | R93, | ||
| 109 | R94, | ||
| 110 | R95, | ||
| 111 | R96, | ||
| 112 | R97, | ||
| 113 | R98, | ||
| 114 | R99, | ||
| 115 | R100, | ||
| 116 | R101, | ||
| 117 | R102, | ||
| 118 | R103, | ||
| 119 | R104, | ||
| 120 | R105, | ||
| 121 | R106, | ||
| 122 | R107, | ||
| 123 | R108, | ||
| 124 | R109, | ||
| 125 | R110, | ||
| 126 | R111, | ||
| 127 | R112, | ||
| 128 | R113, | ||
| 129 | R114, | ||
| 130 | R115, | ||
| 131 | R116, | ||
| 132 | R117, | ||
| 133 | R118, | ||
| 134 | R119, | ||
| 135 | R120, | ||
| 136 | R121, | ||
| 137 | R122, | ||
| 138 | R123, | ||
| 139 | R124, | ||
| 140 | R125, | ||
| 141 | R126, | ||
| 142 | R127, | ||
| 143 | R128, | ||
| 144 | R129, | ||
| 145 | R130, | ||
| 146 | R131, | ||
| 147 | R132, | ||
| 148 | R133, | ||
| 149 | R134, | ||
| 150 | R135, | ||
| 151 | R136, | ||
| 152 | R137, | ||
| 153 | R138, | ||
| 154 | R139, | ||
| 155 | R140, | ||
| 156 | R141, | ||
| 157 | R142, | ||
| 158 | R143, | ||
| 159 | R144, | ||
| 160 | R145, | ||
| 161 | R146, | ||
| 162 | R147, | ||
| 163 | R148, | ||
| 164 | R149, | ||
| 165 | R150, | ||
| 166 | R151, | ||
| 167 | R152, | ||
| 168 | R153, | ||
| 169 | R154, | ||
| 170 | R155, | ||
| 171 | R156, | ||
| 172 | R157, | ||
| 173 | R158, | ||
| 174 | R159, | ||
| 175 | R160, | ||
| 176 | R161, | ||
| 177 | R162, | ||
| 178 | R163, | ||
| 179 | R164, | ||
| 180 | R165, | ||
| 181 | R166, | ||
| 182 | R167, | ||
| 183 | R168, | ||
| 184 | R169, | ||
| 185 | R170, | ||
| 186 | R171, | ||
| 187 | R172, | ||
| 188 | R173, | ||
| 189 | R174, | ||
| 190 | R175, | ||
| 191 | R176, | ||
| 192 | R177, | ||
| 193 | R178, | ||
| 194 | R179, | ||
| 195 | R180, | ||
| 196 | R181, | ||
| 197 | R182, | ||
| 198 | R183, | ||
| 199 | R184, | ||
| 200 | R185, | ||
| 201 | R186, | ||
| 202 | R187, | ||
| 203 | R188, | ||
| 204 | R189, | ||
| 205 | R190, | ||
| 206 | R191, | ||
| 207 | R192, | ||
| 208 | R193, | ||
| 209 | R194, | ||
| 210 | R195, | ||
| 211 | R196, | ||
| 212 | R197, | ||
| 213 | R198, | ||
| 214 | R199, | ||
| 215 | R200, | ||
| 216 | R201, | ||
| 217 | R202, | ||
| 218 | R203, | ||
| 219 | R204, | ||
| 220 | R205, | ||
| 221 | R206, | ||
| 222 | R207, | ||
| 223 | R208, | ||
| 224 | R209, | ||
| 225 | R210, | ||
| 226 | R211, | ||
| 227 | R212, | ||
| 228 | R213, | ||
| 229 | R214, | ||
| 230 | R215, | ||
| 231 | R216, | ||
| 232 | R217, | ||
| 233 | R218, | ||
| 234 | R219, | ||
| 235 | R220, | ||
| 236 | R221, | ||
| 237 | R222, | ||
| 238 | R223, | ||
| 239 | R224, | ||
| 240 | R225, | ||
| 241 | R226, | ||
| 242 | R227, | ||
| 243 | R228, | ||
| 244 | R229, | ||
| 245 | R230, | ||
| 246 | R231, | ||
| 247 | R232, | ||
| 248 | R233, | ||
| 249 | R234, | ||
| 250 | R235, | ||
| 251 | R236, | ||
| 252 | R237, | ||
| 253 | R238, | ||
| 254 | R239, | ||
| 255 | R240, | ||
| 256 | R241, | ||
| 257 | R242, | ||
| 258 | R243, | ||
| 259 | R244, | ||
| 260 | R245, | ||
| 261 | R246, | ||
| 262 | R247, | ||
| 263 | R248, | ||
| 264 | R249, | ||
| 265 | R250, | ||
| 266 | R251, | ||
| 267 | R252, | ||
| 268 | R253, | ||
| 269 | R254, | ||
| 270 | RZ, | ||
| 271 | }; | ||
| 272 | static_assert(static_cast<int>(Reg::RZ) == 255); | ||
| 273 | |||
| 274 | constexpr size_t NUM_USER_REGS = 255; | ||
| 275 | constexpr size_t NUM_REGS = 256; | ||
| 276 | |||
| 277 | [[nodiscard]] constexpr Reg operator+(Reg reg, int num) { | ||
| 278 | if (reg == Reg::RZ) { | ||
| 279 | // Adding or subtracting registers from RZ yields RZ | ||
| 280 | return Reg::RZ; | ||
| 281 | } | ||
| 282 | const int result{static_cast<int>(reg) + num}; | ||
| 283 | if (result >= static_cast<int>(Reg::RZ)) { | ||
| 284 | throw LogicError("Overflow on register arithmetic"); | ||
| 285 | } | ||
| 286 | if (result < 0) { | ||
| 287 | throw LogicError("Underflow on register arithmetic"); | ||
| 288 | } | ||
| 289 | return static_cast<Reg>(result); | ||
| 290 | } | ||
| 291 | |||
| 292 | [[nodiscard]] constexpr Reg operator-(Reg reg, int num) { | ||
| 293 | return reg + (-num); | ||
| 294 | } | ||
| 295 | |||
| 296 | constexpr Reg operator++(Reg& reg) { | ||
| 297 | reg = reg + 1; | ||
| 298 | return reg; | ||
| 299 | } | ||
| 300 | |||
| 301 | constexpr Reg operator++(Reg& reg, int) { | ||
| 302 | const Reg copy{reg}; | ||
| 303 | reg = reg + 1; | ||
| 304 | return copy; | ||
| 305 | } | ||
| 306 | |||
| 307 | [[nodiscard]] constexpr size_t RegIndex(Reg reg) noexcept { | ||
| 308 | return static_cast<size_t>(reg); | ||
| 309 | } | ||
| 310 | |||
| 311 | [[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) { | ||
| 312 | return RegIndex(reg) % align == 0 || reg == Reg::RZ; | ||
| 313 | } | ||
| 314 | |||
| 315 | } // namespace Shader::IR | ||
| 316 | |||
| 317 | template <> | ||
| 318 | struct fmt::formatter<Shader::IR::Reg> { | ||
| 319 | constexpr auto parse(format_parse_context& ctx) { | ||
| 320 | return ctx.begin(); | ||
| 321 | } | ||
| 322 | template <typename FormatContext> | ||
| 323 | auto format(const Shader::IR::Reg& reg, FormatContext& ctx) { | ||
| 324 | if (reg == Shader::IR::Reg::RZ) { | ||
| 325 | return fmt::format_to(ctx.out(), "RZ"); | ||
| 326 | } else if (static_cast<int>(reg) >= 0 && static_cast<int>(reg) < 255) { | ||
| 327 | return fmt::format_to(ctx.out(), "R{}", static_cast<int>(reg)); | ||
| 328 | } else { | ||
| 329 | throw Shader::LogicError("Invalid register with raw value {}", static_cast<int>(reg)); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/type.cpp b/src/shader_recompiler/frontend/ir/type.cpp new file mode 100644 index 000000000..f28341bfe --- /dev/null +++ b/src/shader_recompiler/frontend/ir/type.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include "shader_recompiler/frontend/ir/type.h" | ||
| 9 | |||
| 10 | namespace Shader::IR { | ||
| 11 | |||
| 12 | std::string NameOf(Type type) { | ||
| 13 | static constexpr std::array names{ | ||
| 14 | "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", | ||
| 15 | "U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3", | ||
| 16 | "F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", | ||
| 17 | }; | ||
| 18 | const size_t bits{static_cast<size_t>(type)}; | ||
| 19 | if (bits == 0) { | ||
| 20 | return "Void"; | ||
| 21 | } | ||
| 22 | std::string result; | ||
| 23 | for (size_t i = 0; i < names.size(); i++) { | ||
| 24 | if ((bits & (size_t{1} << i)) != 0) { | ||
| 25 | if (!result.empty()) { | ||
| 26 | result += '|'; | ||
| 27 | } | ||
| 28 | result += names[i]; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | return result; | ||
| 32 | } | ||
| 33 | |||
| 34 | bool AreTypesCompatible(Type lhs, Type rhs) noexcept { | ||
| 35 | return lhs == rhs || lhs == Type::Opaque || rhs == Type::Opaque; | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h new file mode 100644 index 000000000..294b230c4 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/type.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/common_funcs.h" | ||
| 12 | #include "shader_recompiler/exception.h" | ||
| 13 | |||
| 14 | namespace Shader::IR { | ||
| 15 | |||
| 16 | enum class Type { | ||
| 17 | Void = 0, | ||
| 18 | Opaque = 1 << 0, | ||
| 19 | Reg = 1 << 1, | ||
| 20 | Pred = 1 << 2, | ||
| 21 | Attribute = 1 << 3, | ||
| 22 | Patch = 1 << 4, | ||
| 23 | U1 = 1 << 5, | ||
| 24 | U8 = 1 << 6, | ||
| 25 | U16 = 1 << 7, | ||
| 26 | U32 = 1 << 8, | ||
| 27 | U64 = 1 << 9, | ||
| 28 | F16 = 1 << 10, | ||
| 29 | F32 = 1 << 11, | ||
| 30 | F64 = 1 << 12, | ||
| 31 | U32x2 = 1 << 13, | ||
| 32 | U32x3 = 1 << 14, | ||
| 33 | U32x4 = 1 << 15, | ||
| 34 | F16x2 = 1 << 16, | ||
| 35 | F16x3 = 1 << 17, | ||
| 36 | F16x4 = 1 << 18, | ||
| 37 | F32x2 = 1 << 19, | ||
| 38 | F32x3 = 1 << 20, | ||
| 39 | F32x4 = 1 << 21, | ||
| 40 | F64x2 = 1 << 22, | ||
| 41 | F64x3 = 1 << 23, | ||
| 42 | F64x4 = 1 << 24, | ||
| 43 | }; | ||
| 44 | DECLARE_ENUM_FLAG_OPERATORS(Type) | ||
| 45 | |||
| 46 | [[nodiscard]] std::string NameOf(Type type); | ||
| 47 | |||
| 48 | [[nodiscard]] bool AreTypesCompatible(Type lhs, Type rhs) noexcept; | ||
| 49 | |||
| 50 | } // namespace Shader::IR | ||
| 51 | |||
| 52 | template <> | ||
| 53 | struct fmt::formatter<Shader::IR::Type> { | ||
| 54 | constexpr auto parse(format_parse_context& ctx) { | ||
| 55 | return ctx.begin(); | ||
| 56 | } | ||
| 57 | template <typename FormatContext> | ||
| 58 | auto format(const Shader::IR::Type& type, FormatContext& ctx) { | ||
| 59 | return fmt::format_to(ctx.out(), "{}", NameOf(type)); | ||
| 60 | } | ||
| 61 | }; | ||
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp new file mode 100644 index 000000000..d365ea1bc --- /dev/null +++ b/src/shader_recompiler/frontend/ir/value.cpp | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/frontend/ir/opcodes.h" | ||
| 6 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 7 | |||
| 8 | namespace Shader::IR { | ||
| 9 | |||
| 10 | Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {} | ||
| 11 | |||
| 12 | Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {} | ||
| 13 | |||
| 14 | Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {} | ||
| 15 | |||
| 16 | Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {} | ||
| 17 | |||
| 18 | Value::Value(IR::Patch value) noexcept : type{Type::Patch}, patch{value} {} | ||
| 19 | |||
| 20 | Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {} | ||
| 21 | |||
| 22 | Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {} | ||
| 23 | |||
| 24 | Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {} | ||
| 25 | |||
| 26 | Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} | ||
| 27 | |||
| 28 | Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} | ||
| 29 | |||
| 30 | Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} | ||
| 31 | |||
| 32 | Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {} | ||
| 33 | |||
| 34 | IR::Type Value::Type() const noexcept { | ||
| 35 | if (IsPhi()) { | ||
| 36 | // The type of a phi node is stored in its flags | ||
| 37 | return inst->Flags<IR::Type>(); | ||
| 38 | } | ||
| 39 | if (IsIdentity()) { | ||
| 40 | return inst->Arg(0).Type(); | ||
| 41 | } | ||
| 42 | if (type == Type::Opaque) { | ||
| 43 | return inst->Type(); | ||
| 44 | } | ||
| 45 | return type; | ||
| 46 | } | ||
| 47 | |||
| 48 | bool Value::operator==(const Value& other) const { | ||
| 49 | if (type != other.type) { | ||
| 50 | return false; | ||
| 51 | } | ||
| 52 | switch (type) { | ||
| 53 | case Type::Void: | ||
| 54 | return true; | ||
| 55 | case Type::Opaque: | ||
| 56 | return inst == other.inst; | ||
| 57 | case Type::Reg: | ||
| 58 | return reg == other.reg; | ||
| 59 | case Type::Pred: | ||
| 60 | return pred == other.pred; | ||
| 61 | case Type::Attribute: | ||
| 62 | return attribute == other.attribute; | ||
| 63 | case Type::Patch: | ||
| 64 | return patch == other.patch; | ||
| 65 | case Type::U1: | ||
| 66 | return imm_u1 == other.imm_u1; | ||
| 67 | case Type::U8: | ||
| 68 | return imm_u8 == other.imm_u8; | ||
| 69 | case Type::U16: | ||
| 70 | case Type::F16: | ||
| 71 | return imm_u16 == other.imm_u16; | ||
| 72 | case Type::U32: | ||
| 73 | case Type::F32: | ||
| 74 | return imm_u32 == other.imm_u32; | ||
| 75 | case Type::U64: | ||
| 76 | case Type::F64: | ||
| 77 | return imm_u64 == other.imm_u64; | ||
| 78 | case Type::U32x2: | ||
| 79 | case Type::U32x3: | ||
| 80 | case Type::U32x4: | ||
| 81 | case Type::F16x2: | ||
| 82 | case Type::F16x3: | ||
| 83 | case Type::F16x4: | ||
| 84 | case Type::F32x2: | ||
| 85 | case Type::F32x3: | ||
| 86 | case Type::F32x4: | ||
| 87 | case Type::F64x2: | ||
| 88 | case Type::F64x3: | ||
| 89 | case Type::F64x4: | ||
| 90 | break; | ||
| 91 | } | ||
| 92 | throw LogicError("Invalid type {}", type); | ||
| 93 | } | ||
| 94 | |||
| 95 | bool Value::operator!=(const Value& other) const { | ||
| 96 | return !operator==(other); | ||
| 97 | } | ||
| 98 | |||
| 99 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h new file mode 100644 index 000000000..0c6bf684d --- /dev/null +++ b/src/shader_recompiler/frontend/ir/value.h | |||
| @@ -0,0 +1,398 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <cstring> | ||
| 9 | #include <memory> | ||
| 10 | #include <type_traits> | ||
| 11 | #include <utility> | ||
| 12 | #include <vector> | ||
| 13 | |||
| 14 | #include <boost/container/small_vector.hpp> | ||
| 15 | #include <boost/intrusive/list.hpp> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "common/bit_cast.h" | ||
| 19 | #include "common/common_types.h" | ||
| 20 | #include "shader_recompiler/exception.h" | ||
| 21 | #include "shader_recompiler/frontend/ir/attribute.h" | ||
| 22 | #include "shader_recompiler/frontend/ir/opcodes.h" | ||
| 23 | #include "shader_recompiler/frontend/ir/patch.h" | ||
| 24 | #include "shader_recompiler/frontend/ir/pred.h" | ||
| 25 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 26 | #include "shader_recompiler/frontend/ir/type.h" | ||
| 27 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 28 | |||
| 29 | namespace Shader::IR { | ||
| 30 | |||
| 31 | class Block; | ||
| 32 | class Inst; | ||
| 33 | |||
| 34 | struct AssociatedInsts; | ||
| 35 | |||
| 36 | class Value { | ||
| 37 | public: | ||
| 38 | Value() noexcept = default; | ||
| 39 | explicit Value(IR::Inst* value) noexcept; | ||
| 40 | explicit Value(IR::Reg value) noexcept; | ||
| 41 | explicit Value(IR::Pred value) noexcept; | ||
| 42 | explicit Value(IR::Attribute value) noexcept; | ||
| 43 | explicit Value(IR::Patch value) noexcept; | ||
| 44 | explicit Value(bool value) noexcept; | ||
| 45 | explicit Value(u8 value) noexcept; | ||
| 46 | explicit Value(u16 value) noexcept; | ||
| 47 | explicit Value(u32 value) noexcept; | ||
| 48 | explicit Value(f32 value) noexcept; | ||
| 49 | explicit Value(u64 value) noexcept; | ||
| 50 | explicit Value(f64 value) noexcept; | ||
| 51 | |||
| 52 | [[nodiscard]] bool IsIdentity() const noexcept; | ||
| 53 | [[nodiscard]] bool IsPhi() const noexcept; | ||
| 54 | [[nodiscard]] bool IsEmpty() const noexcept; | ||
| 55 | [[nodiscard]] bool IsImmediate() const noexcept; | ||
| 56 | [[nodiscard]] IR::Type Type() const noexcept; | ||
| 57 | |||
| 58 | [[nodiscard]] IR::Inst* Inst() const; | ||
| 59 | [[nodiscard]] IR::Inst* InstRecursive() const; | ||
| 60 | [[nodiscard]] IR::Value Resolve() const; | ||
| 61 | [[nodiscard]] IR::Reg Reg() const; | ||
| 62 | [[nodiscard]] IR::Pred Pred() const; | ||
| 63 | [[nodiscard]] IR::Attribute Attribute() const; | ||
| 64 | [[nodiscard]] IR::Patch Patch() const; | ||
| 65 | [[nodiscard]] bool U1() const; | ||
| 66 | [[nodiscard]] u8 U8() const; | ||
| 67 | [[nodiscard]] u16 U16() const; | ||
| 68 | [[nodiscard]] u32 U32() const; | ||
| 69 | [[nodiscard]] f32 F32() const; | ||
| 70 | [[nodiscard]] u64 U64() const; | ||
| 71 | [[nodiscard]] f64 F64() const; | ||
| 72 | |||
| 73 | [[nodiscard]] bool operator==(const Value& other) const; | ||
| 74 | [[nodiscard]] bool operator!=(const Value& other) const; | ||
| 75 | |||
| 76 | private: | ||
| 77 | IR::Type type{}; | ||
| 78 | union { | ||
| 79 | IR::Inst* inst{}; | ||
| 80 | IR::Reg reg; | ||
| 81 | IR::Pred pred; | ||
| 82 | IR::Attribute attribute; | ||
| 83 | IR::Patch patch; | ||
| 84 | bool imm_u1; | ||
| 85 | u8 imm_u8; | ||
| 86 | u16 imm_u16; | ||
| 87 | u32 imm_u32; | ||
| 88 | f32 imm_f32; | ||
| 89 | u64 imm_u64; | ||
| 90 | f64 imm_f64; | ||
| 91 | }; | ||
| 92 | }; | ||
| 93 | static_assert(static_cast<u32>(IR::Type::Void) == 0, "memset relies on IR::Type being zero"); | ||
| 94 | static_assert(std::is_trivially_copyable_v<Value>); | ||
| 95 | |||
| 96 | template <IR::Type type_> | ||
| 97 | class TypedValue : public Value { | ||
| 98 | public: | ||
| 99 | TypedValue() = default; | ||
| 100 | |||
| 101 | template <IR::Type other_type> | ||
| 102 | requires((other_type & type_) != IR::Type::Void) explicit(false) | ||
| 103 | TypedValue(const TypedValue<other_type>& value) | ||
| 104 | : Value(value) {} | ||
| 105 | |||
| 106 | explicit TypedValue(const Value& value) : Value(value) { | ||
| 107 | if ((value.Type() & type_) == IR::Type::Void) { | ||
| 108 | throw InvalidArgument("Incompatible types {} and {}", type_, value.Type()); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | explicit TypedValue(IR::Inst* inst_) : TypedValue(Value(inst_)) {} | ||
| 113 | }; | ||
| 114 | |||
| 115 | class Inst : public boost::intrusive::list_base_hook<> { | ||
| 116 | public: | ||
| 117 | explicit Inst(IR::Opcode op_, u32 flags_) noexcept; | ||
| 118 | ~Inst(); | ||
| 119 | |||
| 120 | Inst& operator=(const Inst&) = delete; | ||
| 121 | Inst(const Inst&) = delete; | ||
| 122 | |||
| 123 | Inst& operator=(Inst&&) = delete; | ||
| 124 | Inst(Inst&&) = delete; | ||
| 125 | |||
| 126 | /// Get the number of uses this instruction has. | ||
| 127 | [[nodiscard]] int UseCount() const noexcept { | ||
| 128 | return use_count; | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Determines whether this instruction has uses or not. | ||
| 132 | [[nodiscard]] bool HasUses() const noexcept { | ||
| 133 | return use_count > 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | /// Get the opcode this microinstruction represents. | ||
| 137 | [[nodiscard]] IR::Opcode GetOpcode() const noexcept { | ||
| 138 | return op; | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Determines if there is a pseudo-operation associated with this instruction. | ||
| 142 | [[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept { | ||
| 143 | return associated_insts != nullptr; | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Determines whether or not this instruction may have side effects. | ||
| 147 | [[nodiscard]] bool MayHaveSideEffects() const noexcept; | ||
| 148 | |||
| 149 | /// Determines whether or not this instruction is a pseudo-instruction. | ||
| 150 | /// Pseudo-instructions depend on their parent instructions for their semantics. | ||
| 151 | [[nodiscard]] bool IsPseudoInstruction() const noexcept; | ||
| 152 | |||
| 153 | /// Determines if all arguments of this instruction are immediates. | ||
| 154 | [[nodiscard]] bool AreAllArgsImmediates() const; | ||
| 155 | |||
| 156 | /// Gets a pseudo-operation associated with this instruction | ||
| 157 | [[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode); | ||
| 158 | |||
| 159 | /// Get the type this instruction returns. | ||
| 160 | [[nodiscard]] IR::Type Type() const; | ||
| 161 | |||
| 162 | /// Get the number of arguments this instruction has. | ||
| 163 | [[nodiscard]] size_t NumArgs() const { | ||
| 164 | return op == IR::Opcode::Phi ? phi_args.size() : NumArgsOf(op); | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Get the value of a given argument index. | ||
| 168 | [[nodiscard]] Value Arg(size_t index) const noexcept { | ||
| 169 | if (op == IR::Opcode::Phi) { | ||
| 170 | return phi_args[index].second; | ||
| 171 | } else { | ||
| 172 | return args[index]; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Set the value of a given argument index. | ||
| 177 | void SetArg(size_t index, Value value); | ||
| 178 | |||
| 179 | /// Get a pointer to the block of a phi argument. | ||
| 180 | [[nodiscard]] Block* PhiBlock(size_t index) const; | ||
| 181 | /// Add phi operand to a phi instruction. | ||
| 182 | void AddPhiOperand(Block* predecessor, const Value& value); | ||
| 183 | |||
| 184 | void Invalidate(); | ||
| 185 | void ClearArgs(); | ||
| 186 | |||
| 187 | void ReplaceUsesWith(Value replacement); | ||
| 188 | |||
| 189 | void ReplaceOpcode(IR::Opcode opcode); | ||
| 190 | |||
| 191 | template <typename FlagsType> | ||
| 192 | requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>) | ||
| 193 | [[nodiscard]] FlagsType Flags() const noexcept { | ||
| 194 | FlagsType ret; | ||
| 195 | std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret)); | ||
| 196 | return ret; | ||
| 197 | } | ||
| 198 | |||
| 199 | template <typename FlagsType> | ||
| 200 | requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>) | ||
| 201 | [[nodiscard]] void SetFlags(FlagsType value) noexcept { | ||
| 202 | std::memcpy(&flags, &value, sizeof(value)); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Intrusively store the host definition of this instruction. | ||
| 206 | template <typename DefinitionType> | ||
| 207 | void SetDefinition(DefinitionType def) { | ||
| 208 | definition = Common::BitCast<u32>(def); | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Return the intrusively stored host definition of this instruction. | ||
| 212 | template <typename DefinitionType> | ||
| 213 | [[nodiscard]] DefinitionType Definition() const noexcept { | ||
| 214 | return Common::BitCast<DefinitionType>(definition); | ||
| 215 | } | ||
| 216 | |||
| 217 | /// Destructively remove one reference count from the instruction | ||
| 218 | /// Useful for register allocation | ||
| 219 | void DestructiveRemoveUsage() { | ||
| 220 | --use_count; | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Destructively add usages to the instruction | ||
| 224 | /// Useful for register allocation | ||
| 225 | void DestructiveAddUsage(int count) { | ||
| 226 | use_count += count; | ||
| 227 | } | ||
| 228 | |||
| 229 | private: | ||
| 230 | struct NonTriviallyDummy { | ||
| 231 | NonTriviallyDummy() noexcept {} | ||
| 232 | }; | ||
| 233 | |||
| 234 | void Use(const Value& value); | ||
| 235 | void UndoUse(const Value& value); | ||
| 236 | |||
| 237 | IR::Opcode op{}; | ||
| 238 | int use_count{}; | ||
| 239 | u32 flags{}; | ||
| 240 | u32 definition{}; | ||
| 241 | union { | ||
| 242 | NonTriviallyDummy dummy{}; | ||
| 243 | boost::container::small_vector<std::pair<Block*, Value>, 2> phi_args; | ||
| 244 | std::array<Value, 5> args; | ||
| 245 | }; | ||
| 246 | std::unique_ptr<AssociatedInsts> associated_insts; | ||
| 247 | }; | ||
| 248 | static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased"); | ||
| 249 | |||
| 250 | struct AssociatedInsts { | ||
| 251 | union { | ||
| 252 | Inst* in_bounds_inst; | ||
| 253 | Inst* sparse_inst; | ||
| 254 | Inst* zero_inst{}; | ||
| 255 | }; | ||
| 256 | Inst* sign_inst{}; | ||
| 257 | Inst* carry_inst{}; | ||
| 258 | Inst* overflow_inst{}; | ||
| 259 | }; | ||
| 260 | |||
| 261 | using U1 = TypedValue<Type::U1>; | ||
| 262 | using U8 = TypedValue<Type::U8>; | ||
| 263 | using U16 = TypedValue<Type::U16>; | ||
| 264 | using U32 = TypedValue<Type::U32>; | ||
| 265 | using U64 = TypedValue<Type::U64>; | ||
| 266 | using F16 = TypedValue<Type::F16>; | ||
| 267 | using F32 = TypedValue<Type::F32>; | ||
| 268 | using F64 = TypedValue<Type::F64>; | ||
| 269 | using U32U64 = TypedValue<Type::U32 | Type::U64>; | ||
| 270 | using F32F64 = TypedValue<Type::F32 | Type::F64>; | ||
| 271 | using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>; | ||
| 272 | using F16F32F64 = TypedValue<Type::F16 | Type::F32 | Type::F64>; | ||
| 273 | using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>; | ||
| 274 | |||
| 275 | inline bool Value::IsIdentity() const noexcept { | ||
| 276 | return type == Type::Opaque && inst->GetOpcode() == Opcode::Identity; | ||
| 277 | } | ||
| 278 | |||
| 279 | inline bool Value::IsPhi() const noexcept { | ||
| 280 | return type == Type::Opaque && inst->GetOpcode() == Opcode::Phi; | ||
| 281 | } | ||
| 282 | |||
| 283 | inline bool Value::IsEmpty() const noexcept { | ||
| 284 | return type == Type::Void; | ||
| 285 | } | ||
| 286 | |||
| 287 | inline bool Value::IsImmediate() const noexcept { | ||
| 288 | IR::Type current_type{type}; | ||
| 289 | const IR::Inst* current_inst{inst}; | ||
| 290 | while (current_type == Type::Opaque && current_inst->GetOpcode() == Opcode::Identity) { | ||
| 291 | const Value& arg{current_inst->Arg(0)}; | ||
| 292 | current_type = arg.type; | ||
| 293 | current_inst = arg.inst; | ||
| 294 | } | ||
| 295 | return current_type != Type::Opaque; | ||
| 296 | } | ||
| 297 | |||
| 298 | inline IR::Inst* Value::Inst() const { | ||
| 299 | DEBUG_ASSERT(type == Type::Opaque); | ||
| 300 | return inst; | ||
| 301 | } | ||
| 302 | |||
| 303 | inline IR::Inst* Value::InstRecursive() const { | ||
| 304 | DEBUG_ASSERT(type == Type::Opaque); | ||
| 305 | if (IsIdentity()) { | ||
| 306 | return inst->Arg(0).InstRecursive(); | ||
| 307 | } | ||
| 308 | return inst; | ||
| 309 | } | ||
| 310 | |||
| 311 | inline IR::Value Value::Resolve() const { | ||
| 312 | if (IsIdentity()) { | ||
| 313 | return inst->Arg(0).Resolve(); | ||
| 314 | } | ||
| 315 | return *this; | ||
| 316 | } | ||
| 317 | |||
| 318 | inline IR::Reg Value::Reg() const { | ||
| 319 | DEBUG_ASSERT(type == Type::Reg); | ||
| 320 | return reg; | ||
| 321 | } | ||
| 322 | |||
| 323 | inline IR::Pred Value::Pred() const { | ||
| 324 | DEBUG_ASSERT(type == Type::Pred); | ||
| 325 | return pred; | ||
| 326 | } | ||
| 327 | |||
| 328 | inline IR::Attribute Value::Attribute() const { | ||
| 329 | DEBUG_ASSERT(type == Type::Attribute); | ||
| 330 | return attribute; | ||
| 331 | } | ||
| 332 | |||
| 333 | inline IR::Patch Value::Patch() const { | ||
| 334 | DEBUG_ASSERT(type == Type::Patch); | ||
| 335 | return patch; | ||
| 336 | } | ||
| 337 | |||
| 338 | inline bool Value::U1() const { | ||
| 339 | if (IsIdentity()) { | ||
| 340 | return inst->Arg(0).U1(); | ||
| 341 | } | ||
| 342 | DEBUG_ASSERT(type == Type::U1); | ||
| 343 | return imm_u1; | ||
| 344 | } | ||
| 345 | |||
| 346 | inline u8 Value::U8() const { | ||
| 347 | if (IsIdentity()) { | ||
| 348 | return inst->Arg(0).U8(); | ||
| 349 | } | ||
| 350 | DEBUG_ASSERT(type == Type::U8); | ||
| 351 | return imm_u8; | ||
| 352 | } | ||
| 353 | |||
| 354 | inline u16 Value::U16() const { | ||
| 355 | if (IsIdentity()) { | ||
| 356 | return inst->Arg(0).U16(); | ||
| 357 | } | ||
| 358 | DEBUG_ASSERT(type == Type::U16); | ||
| 359 | return imm_u16; | ||
| 360 | } | ||
| 361 | |||
| 362 | inline u32 Value::U32() const { | ||
| 363 | if (IsIdentity()) { | ||
| 364 | return inst->Arg(0).U32(); | ||
| 365 | } | ||
| 366 | DEBUG_ASSERT(type == Type::U32); | ||
| 367 | return imm_u32; | ||
| 368 | } | ||
| 369 | |||
| 370 | inline f32 Value::F32() const { | ||
| 371 | if (IsIdentity()) { | ||
| 372 | return inst->Arg(0).F32(); | ||
| 373 | } | ||
| 374 | DEBUG_ASSERT(type == Type::F32); | ||
| 375 | return imm_f32; | ||
| 376 | } | ||
| 377 | |||
| 378 | inline u64 Value::U64() const { | ||
| 379 | if (IsIdentity()) { | ||
| 380 | return inst->Arg(0).U64(); | ||
| 381 | } | ||
| 382 | DEBUG_ASSERT(type == Type::U64); | ||
| 383 | return imm_u64; | ||
| 384 | } | ||
| 385 | |||
| 386 | inline f64 Value::F64() const { | ||
| 387 | if (IsIdentity()) { | ||
| 388 | return inst->Arg(0).F64(); | ||
| 389 | } | ||
| 390 | DEBUG_ASSERT(type == Type::F64); | ||
| 391 | return imm_f64; | ||
| 392 | } | ||
| 393 | |||
| 394 | [[nodiscard]] inline bool IsPhi(const Inst& inst) { | ||
| 395 | return inst.GetOpcode() == Opcode::Phi; | ||
| 396 | } | ||
| 397 | |||
| 398 | } // namespace Shader::IR | ||