early-access version 1866
This commit is contained in:
58
src/shader_recompiler/frontend/ir/abstract_syntax_list.h
Executable file
58
src/shader_recompiler/frontend/ir/abstract_syntax_list.h
Executable file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block;
|
||||
|
||||
struct AbstractSyntaxNode {
|
||||
enum class Type {
|
||||
Block,
|
||||
If,
|
||||
EndIf,
|
||||
Loop,
|
||||
Repeat,
|
||||
Break,
|
||||
Return,
|
||||
Unreachable,
|
||||
};
|
||||
union Data {
|
||||
Block* block;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* body;
|
||||
Block* merge;
|
||||
} if_node;
|
||||
struct {
|
||||
Block* merge;
|
||||
} end_if;
|
||||
struct {
|
||||
Block* body;
|
||||
Block* continue_block;
|
||||
Block* merge;
|
||||
} loop;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* loop_header;
|
||||
Block* merge;
|
||||
} repeat;
|
||||
struct {
|
||||
U1 cond;
|
||||
Block* merge;
|
||||
Block* skip;
|
||||
} break_node;
|
||||
};
|
||||
|
||||
Data data{};
|
||||
Type type{};
|
||||
};
|
||||
using AbstractSyntaxList = std::vector<AbstractSyntaxNode>;
|
||||
|
||||
} // namespace Shader::IR
|
454
src/shader_recompiler/frontend/ir/attribute.cpp
Executable file
454
src/shader_recompiler/frontend/ir/attribute.cpp
Executable file
@@ -0,0 +1,454 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
bool IsGeneric(Attribute attribute) noexcept {
|
||||
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
|
||||
}
|
||||
|
||||
u32 GenericAttributeIndex(Attribute attribute) {
|
||||
if (!IsGeneric(attribute)) {
|
||||
throw InvalidArgument("Attribute is not generic {}", attribute);
|
||||
}
|
||||
return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u;
|
||||
}
|
||||
|
||||
u32 GenericAttributeElement(Attribute attribute) {
|
||||
if (!IsGeneric(attribute)) {
|
||||
throw InvalidArgument("Attribute is not generic {}", attribute);
|
||||
}
|
||||
return static_cast<u32>(attribute) % 4;
|
||||
}
|
||||
|
||||
std::string NameOf(Attribute attribute) {
|
||||
switch (attribute) {
|
||||
case Attribute::PrimitiveId:
|
||||
return "PrimitiveId";
|
||||
case Attribute::Layer:
|
||||
return "Layer";
|
||||
case Attribute::ViewportIndex:
|
||||
return "ViewportIndex";
|
||||
case Attribute::PointSize:
|
||||
return "PointSize";
|
||||
case Attribute::PositionX:
|
||||
return "Position.X";
|
||||
case Attribute::PositionY:
|
||||
return "Position.Y";
|
||||
case Attribute::PositionZ:
|
||||
return "Position.Z";
|
||||
case Attribute::PositionW:
|
||||
return "Position.W";
|
||||
case Attribute::Generic0X:
|
||||
return "Generic[0].X";
|
||||
case Attribute::Generic0Y:
|
||||
return "Generic[0].Y";
|
||||
case Attribute::Generic0Z:
|
||||
return "Generic[0].Z";
|
||||
case Attribute::Generic0W:
|
||||
return "Generic[0].W";
|
||||
case Attribute::Generic1X:
|
||||
return "Generic[1].X";
|
||||
case Attribute::Generic1Y:
|
||||
return "Generic[1].Y";
|
||||
case Attribute::Generic1Z:
|
||||
return "Generic[1].Z";
|
||||
case Attribute::Generic1W:
|
||||
return "Generic[1].W";
|
||||
case Attribute::Generic2X:
|
||||
return "Generic[2].X";
|
||||
case Attribute::Generic2Y:
|
||||
return "Generic[2].Y";
|
||||
case Attribute::Generic2Z:
|
||||
return "Generic[2].Z";
|
||||
case Attribute::Generic2W:
|
||||
return "Generic[2].W";
|
||||
case Attribute::Generic3X:
|
||||
return "Generic[3].X";
|
||||
case Attribute::Generic3Y:
|
||||
return "Generic[3].Y";
|
||||
case Attribute::Generic3Z:
|
||||
return "Generic[3].Z";
|
||||
case Attribute::Generic3W:
|
||||
return "Generic[3].W";
|
||||
case Attribute::Generic4X:
|
||||
return "Generic[4].X";
|
||||
case Attribute::Generic4Y:
|
||||
return "Generic[4].Y";
|
||||
case Attribute::Generic4Z:
|
||||
return "Generic[4].Z";
|
||||
case Attribute::Generic4W:
|
||||
return "Generic[4].W";
|
||||
case Attribute::Generic5X:
|
||||
return "Generic[5].X";
|
||||
case Attribute::Generic5Y:
|
||||
return "Generic[5].Y";
|
||||
case Attribute::Generic5Z:
|
||||
return "Generic[5].Z";
|
||||
case Attribute::Generic5W:
|
||||
return "Generic[5].W";
|
||||
case Attribute::Generic6X:
|
||||
return "Generic[6].X";
|
||||
case Attribute::Generic6Y:
|
||||
return "Generic[6].Y";
|
||||
case Attribute::Generic6Z:
|
||||
return "Generic[6].Z";
|
||||
case Attribute::Generic6W:
|
||||
return "Generic[6].W";
|
||||
case Attribute::Generic7X:
|
||||
return "Generic[7].X";
|
||||
case Attribute::Generic7Y:
|
||||
return "Generic[7].Y";
|
||||
case Attribute::Generic7Z:
|
||||
return "Generic[7].Z";
|
||||
case Attribute::Generic7W:
|
||||
return "Generic[7].W";
|
||||
case Attribute::Generic8X:
|
||||
return "Generic[8].X";
|
||||
case Attribute::Generic8Y:
|
||||
return "Generic[8].Y";
|
||||
case Attribute::Generic8Z:
|
||||
return "Generic[8].Z";
|
||||
case Attribute::Generic8W:
|
||||
return "Generic[8].W";
|
||||
case Attribute::Generic9X:
|
||||
return "Generic[9].X";
|
||||
case Attribute::Generic9Y:
|
||||
return "Generic[9].Y";
|
||||
case Attribute::Generic9Z:
|
||||
return "Generic[9].Z";
|
||||
case Attribute::Generic9W:
|
||||
return "Generic[9].W";
|
||||
case Attribute::Generic10X:
|
||||
return "Generic[10].X";
|
||||
case Attribute::Generic10Y:
|
||||
return "Generic[10].Y";
|
||||
case Attribute::Generic10Z:
|
||||
return "Generic[10].Z";
|
||||
case Attribute::Generic10W:
|
||||
return "Generic[10].W";
|
||||
case Attribute::Generic11X:
|
||||
return "Generic[11].X";
|
||||
case Attribute::Generic11Y:
|
||||
return "Generic[11].Y";
|
||||
case Attribute::Generic11Z:
|
||||
return "Generic[11].Z";
|
||||
case Attribute::Generic11W:
|
||||
return "Generic[11].W";
|
||||
case Attribute::Generic12X:
|
||||
return "Generic[12].X";
|
||||
case Attribute::Generic12Y:
|
||||
return "Generic[12].Y";
|
||||
case Attribute::Generic12Z:
|
||||
return "Generic[12].Z";
|
||||
case Attribute::Generic12W:
|
||||
return "Generic[12].W";
|
||||
case Attribute::Generic13X:
|
||||
return "Generic[13].X";
|
||||
case Attribute::Generic13Y:
|
||||
return "Generic[13].Y";
|
||||
case Attribute::Generic13Z:
|
||||
return "Generic[13].Z";
|
||||
case Attribute::Generic13W:
|
||||
return "Generic[13].W";
|
||||
case Attribute::Generic14X:
|
||||
return "Generic[14].X";
|
||||
case Attribute::Generic14Y:
|
||||
return "Generic[14].Y";
|
||||
case Attribute::Generic14Z:
|
||||
return "Generic[14].Z";
|
||||
case Attribute::Generic14W:
|
||||
return "Generic[14].W";
|
||||
case Attribute::Generic15X:
|
||||
return "Generic[15].X";
|
||||
case Attribute::Generic15Y:
|
||||
return "Generic[15].Y";
|
||||
case Attribute::Generic15Z:
|
||||
return "Generic[15].Z";
|
||||
case Attribute::Generic15W:
|
||||
return "Generic[15].W";
|
||||
case Attribute::Generic16X:
|
||||
return "Generic[16].X";
|
||||
case Attribute::Generic16Y:
|
||||
return "Generic[16].Y";
|
||||
case Attribute::Generic16Z:
|
||||
return "Generic[16].Z";
|
||||
case Attribute::Generic16W:
|
||||
return "Generic[16].W";
|
||||
case Attribute::Generic17X:
|
||||
return "Generic[17].X";
|
||||
case Attribute::Generic17Y:
|
||||
return "Generic[17].Y";
|
||||
case Attribute::Generic17Z:
|
||||
return "Generic[17].Z";
|
||||
case Attribute::Generic17W:
|
||||
return "Generic[17].W";
|
||||
case Attribute::Generic18X:
|
||||
return "Generic[18].X";
|
||||
case Attribute::Generic18Y:
|
||||
return "Generic[18].Y";
|
||||
case Attribute::Generic18Z:
|
||||
return "Generic[18].Z";
|
||||
case Attribute::Generic18W:
|
||||
return "Generic[18].W";
|
||||
case Attribute::Generic19X:
|
||||
return "Generic[19].X";
|
||||
case Attribute::Generic19Y:
|
||||
return "Generic[19].Y";
|
||||
case Attribute::Generic19Z:
|
||||
return "Generic[19].Z";
|
||||
case Attribute::Generic19W:
|
||||
return "Generic[19].W";
|
||||
case Attribute::Generic20X:
|
||||
return "Generic[20].X";
|
||||
case Attribute::Generic20Y:
|
||||
return "Generic[20].Y";
|
||||
case Attribute::Generic20Z:
|
||||
return "Generic[20].Z";
|
||||
case Attribute::Generic20W:
|
||||
return "Generic[20].W";
|
||||
case Attribute::Generic21X:
|
||||
return "Generic[21].X";
|
||||
case Attribute::Generic21Y:
|
||||
return "Generic[21].Y";
|
||||
case Attribute::Generic21Z:
|
||||
return "Generic[21].Z";
|
||||
case Attribute::Generic21W:
|
||||
return "Generic[21].W";
|
||||
case Attribute::Generic22X:
|
||||
return "Generic[22].X";
|
||||
case Attribute::Generic22Y:
|
||||
return "Generic[22].Y";
|
||||
case Attribute::Generic22Z:
|
||||
return "Generic[22].Z";
|
||||
case Attribute::Generic22W:
|
||||
return "Generic[22].W";
|
||||
case Attribute::Generic23X:
|
||||
return "Generic[23].X";
|
||||
case Attribute::Generic23Y:
|
||||
return "Generic[23].Y";
|
||||
case Attribute::Generic23Z:
|
||||
return "Generic[23].Z";
|
||||
case Attribute::Generic23W:
|
||||
return "Generic[23].W";
|
||||
case Attribute::Generic24X:
|
||||
return "Generic[24].X";
|
||||
case Attribute::Generic24Y:
|
||||
return "Generic[24].Y";
|
||||
case Attribute::Generic24Z:
|
||||
return "Generic[24].Z";
|
||||
case Attribute::Generic24W:
|
||||
return "Generic[24].W";
|
||||
case Attribute::Generic25X:
|
||||
return "Generic[25].X";
|
||||
case Attribute::Generic25Y:
|
||||
return "Generic[25].Y";
|
||||
case Attribute::Generic25Z:
|
||||
return "Generic[25].Z";
|
||||
case Attribute::Generic25W:
|
||||
return "Generic[25].W";
|
||||
case Attribute::Generic26X:
|
||||
return "Generic[26].X";
|
||||
case Attribute::Generic26Y:
|
||||
return "Generic[26].Y";
|
||||
case Attribute::Generic26Z:
|
||||
return "Generic[26].Z";
|
||||
case Attribute::Generic26W:
|
||||
return "Generic[26].W";
|
||||
case Attribute::Generic27X:
|
||||
return "Generic[27].X";
|
||||
case Attribute::Generic27Y:
|
||||
return "Generic[27].Y";
|
||||
case Attribute::Generic27Z:
|
||||
return "Generic[27].Z";
|
||||
case Attribute::Generic27W:
|
||||
return "Generic[27].W";
|
||||
case Attribute::Generic28X:
|
||||
return "Generic[28].X";
|
||||
case Attribute::Generic28Y:
|
||||
return "Generic[28].Y";
|
||||
case Attribute::Generic28Z:
|
||||
return "Generic[28].Z";
|
||||
case Attribute::Generic28W:
|
||||
return "Generic[28].W";
|
||||
case Attribute::Generic29X:
|
||||
return "Generic[29].X";
|
||||
case Attribute::Generic29Y:
|
||||
return "Generic[29].Y";
|
||||
case Attribute::Generic29Z:
|
||||
return "Generic[29].Z";
|
||||
case Attribute::Generic29W:
|
||||
return "Generic[29].W";
|
||||
case Attribute::Generic30X:
|
||||
return "Generic[30].X";
|
||||
case Attribute::Generic30Y:
|
||||
return "Generic[30].Y";
|
||||
case Attribute::Generic30Z:
|
||||
return "Generic[30].Z";
|
||||
case Attribute::Generic30W:
|
||||
return "Generic[30].W";
|
||||
case Attribute::Generic31X:
|
||||
return "Generic[31].X";
|
||||
case Attribute::Generic31Y:
|
||||
return "Generic[31].Y";
|
||||
case Attribute::Generic31Z:
|
||||
return "Generic[31].Z";
|
||||
case Attribute::Generic31W:
|
||||
return "Generic[31].W";
|
||||
case Attribute::ColorFrontDiffuseR:
|
||||
return "ColorFrontDiffuse.R";
|
||||
case Attribute::ColorFrontDiffuseG:
|
||||
return "ColorFrontDiffuse.G";
|
||||
case Attribute::ColorFrontDiffuseB:
|
||||
return "ColorFrontDiffuse.B";
|
||||
case Attribute::ColorFrontDiffuseA:
|
||||
return "ColorFrontDiffuse.A";
|
||||
case Attribute::ColorFrontSpecularR:
|
||||
return "ColorFrontSpecular.R";
|
||||
case Attribute::ColorFrontSpecularG:
|
||||
return "ColorFrontSpecular.G";
|
||||
case Attribute::ColorFrontSpecularB:
|
||||
return "ColorFrontSpecular.B";
|
||||
case Attribute::ColorFrontSpecularA:
|
||||
return "ColorFrontSpecular.A";
|
||||
case Attribute::ColorBackDiffuseR:
|
||||
return "ColorBackDiffuse.R";
|
||||
case Attribute::ColorBackDiffuseG:
|
||||
return "ColorBackDiffuse.G";
|
||||
case Attribute::ColorBackDiffuseB:
|
||||
return "ColorBackDiffuse.B";
|
||||
case Attribute::ColorBackDiffuseA:
|
||||
return "ColorBackDiffuse.A";
|
||||
case Attribute::ColorBackSpecularR:
|
||||
return "ColorBackSpecular.R";
|
||||
case Attribute::ColorBackSpecularG:
|
||||
return "ColorBackSpecular.G";
|
||||
case Attribute::ColorBackSpecularB:
|
||||
return "ColorBackSpecular.B";
|
||||
case Attribute::ColorBackSpecularA:
|
||||
return "ColorBackSpecular.A";
|
||||
case Attribute::ClipDistance0:
|
||||
return "ClipDistance[0]";
|
||||
case Attribute::ClipDistance1:
|
||||
return "ClipDistance[1]";
|
||||
case Attribute::ClipDistance2:
|
||||
return "ClipDistance[2]";
|
||||
case Attribute::ClipDistance3:
|
||||
return "ClipDistance[3]";
|
||||
case Attribute::ClipDistance4:
|
||||
return "ClipDistance[4]";
|
||||
case Attribute::ClipDistance5:
|
||||
return "ClipDistance[5]";
|
||||
case Attribute::ClipDistance6:
|
||||
return "ClipDistance[6]";
|
||||
case Attribute::ClipDistance7:
|
||||
return "ClipDistance[7]";
|
||||
case Attribute::PointSpriteS:
|
||||
return "PointSprite.S";
|
||||
case Attribute::PointSpriteT:
|
||||
return "PointSprite.T";
|
||||
case Attribute::FogCoordinate:
|
||||
return "FogCoordinate";
|
||||
case Attribute::TessellationEvaluationPointU:
|
||||
return "TessellationEvaluationPoint.U";
|
||||
case Attribute::TessellationEvaluationPointV:
|
||||
return "TessellationEvaluationPoint.V";
|
||||
case Attribute::InstanceId:
|
||||
return "InstanceId";
|
||||
case Attribute::VertexId:
|
||||
return "VertexId";
|
||||
case Attribute::FixedFncTexture0S:
|
||||
return "FixedFncTexture[0].S";
|
||||
case Attribute::FixedFncTexture0T:
|
||||
return "FixedFncTexture[0].T";
|
||||
case Attribute::FixedFncTexture0R:
|
||||
return "FixedFncTexture[0].R";
|
||||
case Attribute::FixedFncTexture0Q:
|
||||
return "FixedFncTexture[0].Q";
|
||||
case Attribute::FixedFncTexture1S:
|
||||
return "FixedFncTexture[1].S";
|
||||
case Attribute::FixedFncTexture1T:
|
||||
return "FixedFncTexture[1].T";
|
||||
case Attribute::FixedFncTexture1R:
|
||||
return "FixedFncTexture[1].R";
|
||||
case Attribute::FixedFncTexture1Q:
|
||||
return "FixedFncTexture[1].Q";
|
||||
case Attribute::FixedFncTexture2S:
|
||||
return "FixedFncTexture[2].S";
|
||||
case Attribute::FixedFncTexture2T:
|
||||
return "FixedFncTexture[2].T";
|
||||
case Attribute::FixedFncTexture2R:
|
||||
return "FixedFncTexture[2].R";
|
||||
case Attribute::FixedFncTexture2Q:
|
||||
return "FixedFncTexture[2].Q";
|
||||
case Attribute::FixedFncTexture3S:
|
||||
return "FixedFncTexture[3].S";
|
||||
case Attribute::FixedFncTexture3T:
|
||||
return "FixedFncTexture[3].T";
|
||||
case Attribute::FixedFncTexture3R:
|
||||
return "FixedFncTexture[3].R";
|
||||
case Attribute::FixedFncTexture3Q:
|
||||
return "FixedFncTexture[3].Q";
|
||||
case Attribute::FixedFncTexture4S:
|
||||
return "FixedFncTexture[4].S";
|
||||
case Attribute::FixedFncTexture4T:
|
||||
return "FixedFncTexture[4].T";
|
||||
case Attribute::FixedFncTexture4R:
|
||||
return "FixedFncTexture[4].R";
|
||||
case Attribute::FixedFncTexture4Q:
|
||||
return "FixedFncTexture[4].Q";
|
||||
case Attribute::FixedFncTexture5S:
|
||||
return "FixedFncTexture[5].S";
|
||||
case Attribute::FixedFncTexture5T:
|
||||
return "FixedFncTexture[5].T";
|
||||
case Attribute::FixedFncTexture5R:
|
||||
return "FixedFncTexture[5].R";
|
||||
case Attribute::FixedFncTexture5Q:
|
||||
return "FixedFncTexture[5].Q";
|
||||
case Attribute::FixedFncTexture6S:
|
||||
return "FixedFncTexture[6].S";
|
||||
case Attribute::FixedFncTexture6T:
|
||||
return "FixedFncTexture[6].T";
|
||||
case Attribute::FixedFncTexture6R:
|
||||
return "FixedFncTexture[6].R";
|
||||
case Attribute::FixedFncTexture6Q:
|
||||
return "FixedFncTexture[6].Q";
|
||||
case Attribute::FixedFncTexture7S:
|
||||
return "FixedFncTexture[7].S";
|
||||
case Attribute::FixedFncTexture7T:
|
||||
return "FixedFncTexture[7].T";
|
||||
case Attribute::FixedFncTexture7R:
|
||||
return "FixedFncTexture[7].R";
|
||||
case Attribute::FixedFncTexture7Q:
|
||||
return "FixedFncTexture[7].Q";
|
||||
case Attribute::FixedFncTexture8S:
|
||||
return "FixedFncTexture[8].S";
|
||||
case Attribute::FixedFncTexture8T:
|
||||
return "FixedFncTexture[8].T";
|
||||
case Attribute::FixedFncTexture8R:
|
||||
return "FixedFncTexture[8].R";
|
||||
case Attribute::FixedFncTexture8Q:
|
||||
return "FixedFncTexture[8].Q";
|
||||
case Attribute::FixedFncTexture9S:
|
||||
return "FixedFncTexture[9].S";
|
||||
case Attribute::FixedFncTexture9T:
|
||||
return "FixedFncTexture[9].T";
|
||||
case Attribute::FixedFncTexture9R:
|
||||
return "FixedFncTexture[9].R";
|
||||
case Attribute::FixedFncTexture9Q:
|
||||
return "FixedFncTexture[9].Q";
|
||||
case Attribute::ViewportMask:
|
||||
return "ViewportMask";
|
||||
case Attribute::FrontFace:
|
||||
return "FrontFace";
|
||||
}
|
||||
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
250
src/shader_recompiler/frontend/ir/attribute.h
Executable file
250
src/shader_recompiler/frontend/ir/attribute.h
Executable file
@@ -0,0 +1,250 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Attribute : u64 {
|
||||
PrimitiveId = 24,
|
||||
Layer = 25,
|
||||
ViewportIndex = 26,
|
||||
PointSize = 27,
|
||||
PositionX = 28,
|
||||
PositionY = 29,
|
||||
PositionZ = 30,
|
||||
PositionW = 31,
|
||||
Generic0X = 32,
|
||||
Generic0Y = 33,
|
||||
Generic0Z = 34,
|
||||
Generic0W = 35,
|
||||
Generic1X = 36,
|
||||
Generic1Y = 37,
|
||||
Generic1Z = 38,
|
||||
Generic1W = 39,
|
||||
Generic2X = 40,
|
||||
Generic2Y = 41,
|
||||
Generic2Z = 42,
|
||||
Generic2W = 43,
|
||||
Generic3X = 44,
|
||||
Generic3Y = 45,
|
||||
Generic3Z = 46,
|
||||
Generic3W = 47,
|
||||
Generic4X = 48,
|
||||
Generic4Y = 49,
|
||||
Generic4Z = 50,
|
||||
Generic4W = 51,
|
||||
Generic5X = 52,
|
||||
Generic5Y = 53,
|
||||
Generic5Z = 54,
|
||||
Generic5W = 55,
|
||||
Generic6X = 56,
|
||||
Generic6Y = 57,
|
||||
Generic6Z = 58,
|
||||
Generic6W = 59,
|
||||
Generic7X = 60,
|
||||
Generic7Y = 61,
|
||||
Generic7Z = 62,
|
||||
Generic7W = 63,
|
||||
Generic8X = 64,
|
||||
Generic8Y = 65,
|
||||
Generic8Z = 66,
|
||||
Generic8W = 67,
|
||||
Generic9X = 68,
|
||||
Generic9Y = 69,
|
||||
Generic9Z = 70,
|
||||
Generic9W = 71,
|
||||
Generic10X = 72,
|
||||
Generic10Y = 73,
|
||||
Generic10Z = 74,
|
||||
Generic10W = 75,
|
||||
Generic11X = 76,
|
||||
Generic11Y = 77,
|
||||
Generic11Z = 78,
|
||||
Generic11W = 79,
|
||||
Generic12X = 80,
|
||||
Generic12Y = 81,
|
||||
Generic12Z = 82,
|
||||
Generic12W = 83,
|
||||
Generic13X = 84,
|
||||
Generic13Y = 85,
|
||||
Generic13Z = 86,
|
||||
Generic13W = 87,
|
||||
Generic14X = 88,
|
||||
Generic14Y = 89,
|
||||
Generic14Z = 90,
|
||||
Generic14W = 91,
|
||||
Generic15X = 92,
|
||||
Generic15Y = 93,
|
||||
Generic15Z = 94,
|
||||
Generic15W = 95,
|
||||
Generic16X = 96,
|
||||
Generic16Y = 97,
|
||||
Generic16Z = 98,
|
||||
Generic16W = 99,
|
||||
Generic17X = 100,
|
||||
Generic17Y = 101,
|
||||
Generic17Z = 102,
|
||||
Generic17W = 103,
|
||||
Generic18X = 104,
|
||||
Generic18Y = 105,
|
||||
Generic18Z = 106,
|
||||
Generic18W = 107,
|
||||
Generic19X = 108,
|
||||
Generic19Y = 109,
|
||||
Generic19Z = 110,
|
||||
Generic19W = 111,
|
||||
Generic20X = 112,
|
||||
Generic20Y = 113,
|
||||
Generic20Z = 114,
|
||||
Generic20W = 115,
|
||||
Generic21X = 116,
|
||||
Generic21Y = 117,
|
||||
Generic21Z = 118,
|
||||
Generic21W = 119,
|
||||
Generic22X = 120,
|
||||
Generic22Y = 121,
|
||||
Generic22Z = 122,
|
||||
Generic22W = 123,
|
||||
Generic23X = 124,
|
||||
Generic23Y = 125,
|
||||
Generic23Z = 126,
|
||||
Generic23W = 127,
|
||||
Generic24X = 128,
|
||||
Generic24Y = 129,
|
||||
Generic24Z = 130,
|
||||
Generic24W = 131,
|
||||
Generic25X = 132,
|
||||
Generic25Y = 133,
|
||||
Generic25Z = 134,
|
||||
Generic25W = 135,
|
||||
Generic26X = 136,
|
||||
Generic26Y = 137,
|
||||
Generic26Z = 138,
|
||||
Generic26W = 139,
|
||||
Generic27X = 140,
|
||||
Generic27Y = 141,
|
||||
Generic27Z = 142,
|
||||
Generic27W = 143,
|
||||
Generic28X = 144,
|
||||
Generic28Y = 145,
|
||||
Generic28Z = 146,
|
||||
Generic28W = 147,
|
||||
Generic29X = 148,
|
||||
Generic29Y = 149,
|
||||
Generic29Z = 150,
|
||||
Generic29W = 151,
|
||||
Generic30X = 152,
|
||||
Generic30Y = 153,
|
||||
Generic30Z = 154,
|
||||
Generic30W = 155,
|
||||
Generic31X = 156,
|
||||
Generic31Y = 157,
|
||||
Generic31Z = 158,
|
||||
Generic31W = 159,
|
||||
ColorFrontDiffuseR = 160,
|
||||
ColorFrontDiffuseG = 161,
|
||||
ColorFrontDiffuseB = 162,
|
||||
ColorFrontDiffuseA = 163,
|
||||
ColorFrontSpecularR = 164,
|
||||
ColorFrontSpecularG = 165,
|
||||
ColorFrontSpecularB = 166,
|
||||
ColorFrontSpecularA = 167,
|
||||
ColorBackDiffuseR = 168,
|
||||
ColorBackDiffuseG = 169,
|
||||
ColorBackDiffuseB = 170,
|
||||
ColorBackDiffuseA = 171,
|
||||
ColorBackSpecularR = 172,
|
||||
ColorBackSpecularG = 173,
|
||||
ColorBackSpecularB = 174,
|
||||
ColorBackSpecularA = 175,
|
||||
ClipDistance0 = 176,
|
||||
ClipDistance1 = 177,
|
||||
ClipDistance2 = 178,
|
||||
ClipDistance3 = 179,
|
||||
ClipDistance4 = 180,
|
||||
ClipDistance5 = 181,
|
||||
ClipDistance6 = 182,
|
||||
ClipDistance7 = 183,
|
||||
PointSpriteS = 184,
|
||||
PointSpriteT = 185,
|
||||
FogCoordinate = 186,
|
||||
TessellationEvaluationPointU = 188,
|
||||
TessellationEvaluationPointV = 189,
|
||||
InstanceId = 190,
|
||||
VertexId = 191,
|
||||
FixedFncTexture0S = 192,
|
||||
FixedFncTexture0T = 193,
|
||||
FixedFncTexture0R = 194,
|
||||
FixedFncTexture0Q = 195,
|
||||
FixedFncTexture1S = 196,
|
||||
FixedFncTexture1T = 197,
|
||||
FixedFncTexture1R = 198,
|
||||
FixedFncTexture1Q = 199,
|
||||
FixedFncTexture2S = 200,
|
||||
FixedFncTexture2T = 201,
|
||||
FixedFncTexture2R = 202,
|
||||
FixedFncTexture2Q = 203,
|
||||
FixedFncTexture3S = 204,
|
||||
FixedFncTexture3T = 205,
|
||||
FixedFncTexture3R = 206,
|
||||
FixedFncTexture3Q = 207,
|
||||
FixedFncTexture4S = 208,
|
||||
FixedFncTexture4T = 209,
|
||||
FixedFncTexture4R = 210,
|
||||
FixedFncTexture4Q = 211,
|
||||
FixedFncTexture5S = 212,
|
||||
FixedFncTexture5T = 213,
|
||||
FixedFncTexture5R = 214,
|
||||
FixedFncTexture5Q = 215,
|
||||
FixedFncTexture6S = 216,
|
||||
FixedFncTexture6T = 217,
|
||||
FixedFncTexture6R = 218,
|
||||
FixedFncTexture6Q = 219,
|
||||
FixedFncTexture7S = 220,
|
||||
FixedFncTexture7T = 221,
|
||||
FixedFncTexture7R = 222,
|
||||
FixedFncTexture7Q = 223,
|
||||
FixedFncTexture8S = 224,
|
||||
FixedFncTexture8T = 225,
|
||||
FixedFncTexture8R = 226,
|
||||
FixedFncTexture8Q = 227,
|
||||
FixedFncTexture9S = 228,
|
||||
FixedFncTexture9T = 229,
|
||||
FixedFncTexture9R = 230,
|
||||
FixedFncTexture9Q = 231,
|
||||
ViewportMask = 232,
|
||||
FrontFace = 255,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeElement(Attribute attribute);
|
||||
|
||||
[[nodiscard]] std::string NameOf(Attribute attribute);
|
||||
|
||||
[[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept {
|
||||
return static_cast<IR::Attribute>(static_cast<size_t>(attribute) + value);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Attribute> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute));
|
||||
}
|
||||
};
|
149
src/shader_recompiler/frontend/ir/basic_block.cpp
Executable file
149
src/shader_recompiler/frontend/ir/basic_block.cpp
Executable file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||
|
||||
Block::~Block() = default;
|
||||
|
||||
void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
|
||||
PrependNewInst(end(), op, args);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args, u32 flags) {
|
||||
Inst* const inst{inst_pool->Create(op, flags)};
|
||||
const auto result_it{instructions.insert(insertion_point, *inst)};
|
||||
|
||||
if (inst->NumArgs() != args.size()) {
|
||||
throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op);
|
||||
}
|
||||
std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable {
|
||||
inst->SetArg(index, arg);
|
||||
++index;
|
||||
});
|
||||
return result_it;
|
||||
}
|
||||
|
||||
void Block::AddBranch(Block* block) {
|
||||
if (std::ranges::find(imm_successors, block) != imm_successors.end()) {
|
||||
throw LogicError("Successor already inserted");
|
||||
}
|
||||
if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) {
|
||||
throw LogicError("Predecessor already inserted");
|
||||
}
|
||||
imm_successors.push_back(block);
|
||||
block->imm_predecessors.push_back(this);
|
||||
}
|
||||
|
||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
||||
Block* block) {
|
||||
if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
|
||||
return fmt::format("{{Block ${}}}", it->second);
|
||||
}
|
||||
return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
|
||||
}
|
||||
|
||||
static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
const Inst* inst) {
|
||||
const auto [it, is_inserted]{inst_to_index.emplace(inst, inst_index + 1)};
|
||||
if (is_inserted) {
|
||||
++inst_index;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||
const Value& arg) {
|
||||
if (arg.IsEmpty()) {
|
||||
return "<null>";
|
||||
}
|
||||
if (!arg.IsImmediate() || arg.IsIdentity()) {
|
||||
return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst()));
|
||||
}
|
||||
switch (arg.Type()) {
|
||||
case Type::U1:
|
||||
return fmt::format("#{}", arg.U1() ? "true" : "false");
|
||||
case Type::U8:
|
||||
return fmt::format("#{}", arg.U8());
|
||||
case Type::U16:
|
||||
return fmt::format("#{}", arg.U16());
|
||||
case Type::U32:
|
||||
return fmt::format("#{}", arg.U32());
|
||||
case Type::U64:
|
||||
return fmt::format("#{}", arg.U64());
|
||||
case Type::F32:
|
||||
return fmt::format("#{}", arg.F32());
|
||||
case Type::Reg:
|
||||
return fmt::format("{}", arg.Reg());
|
||||
case Type::Pred:
|
||||
return fmt::format("{}", arg.Pred());
|
||||
case Type::Attribute:
|
||||
return fmt::format("{}", arg.Attribute());
|
||||
default:
|
||||
return "<unknown immediate type>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string DumpBlock(const Block& block) {
|
||||
size_t inst_index{0};
|
||||
std::map<const Inst*, size_t> inst_to_index;
|
||||
return DumpBlock(block, {}, inst_to_index, inst_index);
|
||||
}
|
||||
|
||||
std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index,
|
||||
std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) {
|
||||
std::string ret{"Block"};
|
||||
if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
|
||||
ret += fmt::format(" ${}", it->second);
|
||||
}
|
||||
ret += '\n';
|
||||
for (const Inst& inst : block) {
|
||||
const Opcode op{inst.GetOpcode()};
|
||||
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
|
||||
if (TypeOf(op) != Type::Void) {
|
||||
ret += fmt::format("%{:<5} = {}", InstIndex(inst_to_index, inst_index, &inst), op);
|
||||
} else {
|
||||
ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
|
||||
}
|
||||
const size_t arg_count{inst.NumArgs()};
|
||||
for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
|
||||
const Value arg{inst.Arg(arg_index)};
|
||||
const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)};
|
||||
ret += arg_index != 0 ? ", " : " ";
|
||||
if (op == Opcode::Phi) {
|
||||
ret += fmt::format("[ {}, {} ]", arg_str,
|
||||
BlockToIndex(block_to_index, inst.PhiBlock(arg_index)));
|
||||
} else {
|
||||
ret += arg_str;
|
||||
}
|
||||
if (op != Opcode::Phi) {
|
||||
const Type actual_type{arg.Type()};
|
||||
const Type expected_type{ArgTypeOf(op, arg_index)};
|
||||
if (!AreTypesCompatible(actual_type, expected_type)) {
|
||||
ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TypeOf(op) != Type::Void) {
|
||||
ret += fmt::format(" (uses: {})\n", inst.UseCount());
|
||||
} else {
|
||||
ret += '\n';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
185
src/shader_recompiler/frontend/ir/basic_block.h
Executable file
185
src/shader_recompiler/frontend/ir/basic_block.h
Executable file
@@ -0,0 +1,185 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block {
|
||||
public:
|
||||
using InstructionList = boost::intrusive::list<Inst>;
|
||||
using size_type = InstructionList::size_type;
|
||||
using iterator = InstructionList::iterator;
|
||||
using const_iterator = InstructionList::const_iterator;
|
||||
using reverse_iterator = InstructionList::reverse_iterator;
|
||||
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
||||
|
||||
explicit Block(ObjectPool<Inst>& inst_pool_);
|
||||
~Block();
|
||||
|
||||
Block(const Block&) = delete;
|
||||
Block& operator=(const Block&) = delete;
|
||||
|
||||
Block(Block&&) = default;
|
||||
Block& operator=(Block&&) = default;
|
||||
|
||||
/// Appends a new instruction to the end of this basic block.
|
||||
void AppendNewInst(Opcode op, std::initializer_list<Value> args);
|
||||
|
||||
/// Prepends a new instruction to this basic block before the insertion point.
|
||||
iterator PrependNewInst(iterator insertion_point, Opcode op,
|
||||
std::initializer_list<Value> args = {}, u32 flags = 0);
|
||||
|
||||
/// Adds a new branch to this basic block.
|
||||
void AddBranch(Block* block);
|
||||
|
||||
/// Gets a mutable reference to the instruction list for this basic block.
|
||||
[[nodiscard]] InstructionList& Instructions() noexcept {
|
||||
return instructions;
|
||||
}
|
||||
/// Gets an immutable reference to the instruction list for this basic block.
|
||||
[[nodiscard]] const InstructionList& Instructions() const noexcept {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
/// Gets an immutable span to the immediate predecessors.
|
||||
[[nodiscard]] std::span<Block* const> ImmPredecessors() const noexcept {
|
||||
return imm_predecessors;
|
||||
}
|
||||
/// Gets an immutable span to the immediate successors.
|
||||
[[nodiscard]] std::span<Block* const> ImmSuccessors() const noexcept {
|
||||
return imm_successors;
|
||||
}
|
||||
|
||||
/// Intrusively store the host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
void SetDefinition(DefinitionType def) {
|
||||
definition = Common::BitCast<u32>(def);
|
||||
}
|
||||
|
||||
/// Return the intrusively stored host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
[[nodiscard]] DefinitionType Definition() const noexcept {
|
||||
return Common::BitCast<DefinitionType>(definition);
|
||||
}
|
||||
|
||||
void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept {
|
||||
ssa_reg_values[RegIndex(reg)] = value;
|
||||
}
|
||||
const Value& SsaRegValue(IR::Reg reg) const noexcept {
|
||||
return ssa_reg_values[RegIndex(reg)];
|
||||
}
|
||||
|
||||
void SsaSeal() noexcept {
|
||||
is_ssa_sealed = true;
|
||||
}
|
||||
[[nodiscard]] bool IsSsaSealed() const noexcept {
|
||||
return is_ssa_sealed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return instructions.empty();
|
||||
}
|
||||
[[nodiscard]] size_type size() const {
|
||||
return instructions.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] Inst& front() {
|
||||
return instructions.front();
|
||||
}
|
||||
[[nodiscard]] const Inst& front() const {
|
||||
return instructions.front();
|
||||
}
|
||||
|
||||
[[nodiscard]] Inst& back() {
|
||||
return instructions.back();
|
||||
}
|
||||
[[nodiscard]] const Inst& back() const {
|
||||
return instructions.back();
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator begin() {
|
||||
return instructions.begin();
|
||||
}
|
||||
[[nodiscard]] const_iterator begin() const {
|
||||
return instructions.begin();
|
||||
}
|
||||
[[nodiscard]] iterator end() {
|
||||
return instructions.end();
|
||||
}
|
||||
[[nodiscard]] const_iterator end() const {
|
||||
return instructions.end();
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rbegin() {
|
||||
return instructions.rbegin();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const {
|
||||
return instructions.rbegin();
|
||||
}
|
||||
[[nodiscard]] reverse_iterator rend() {
|
||||
return instructions.rend();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator rend() const {
|
||||
return instructions.rend();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator cbegin() const {
|
||||
return instructions.cbegin();
|
||||
}
|
||||
[[nodiscard]] const_iterator cend() const {
|
||||
return instructions.cend();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const {
|
||||
return instructions.crbegin();
|
||||
}
|
||||
[[nodiscard]] const_reverse_iterator crend() const {
|
||||
return instructions.crend();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Memory pool for instruction list
|
||||
ObjectPool<Inst>* inst_pool;
|
||||
|
||||
/// List of instructions in this block
|
||||
InstructionList instructions;
|
||||
|
||||
/// Block immediate predecessors
|
||||
std::vector<Block*> imm_predecessors;
|
||||
/// Block immediate successors
|
||||
std::vector<Block*> imm_successors;
|
||||
|
||||
/// Intrusively store the value of a register in the block.
|
||||
std::array<Value, NUM_REGS> ssa_reg_values;
|
||||
/// Intrusively store if the block is sealed in the SSA pass.
|
||||
bool is_ssa_sealed{false};
|
||||
|
||||
/// Intrusively stored host definition of this block.
|
||||
u32 definition{};
|
||||
};
|
||||
|
||||
using BlockList = std::vector<Block*>;
|
||||
|
||||
[[nodiscard]] std::string DumpBlock(const Block& block);
|
||||
|
||||
[[nodiscard]] std::string DumpBlock(const Block& block,
|
||||
const std::map<const Block*, size_t>& block_to_index,
|
||||
std::map<const Inst*, size_t>& inst_to_index,
|
||||
size_t& inst_index);
|
||||
|
||||
} // namespace Shader::IR
|
56
src/shader_recompiler/frontend/ir/breadth_first_search.h
Executable file
56
src/shader_recompiler/frontend/ir/breadth_first_search.h
Executable file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
template <typename Pred>
|
||||
auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
-> std::invoke_result_t<Pred, const Inst*> {
|
||||
if (value.IsImmediate()) {
|
||||
// Nothing to do with immediates
|
||||
return std::nullopt;
|
||||
}
|
||||
// Breadth-first search visiting the right most arguments first
|
||||
// Small vector has been determined from shaders in Super Smash Bros. Ultimate
|
||||
boost::container::small_vector<const Inst*, 2> visited;
|
||||
std::queue<const Inst*> queue;
|
||||
queue.push(value.InstRecursive());
|
||||
|
||||
while (!queue.empty()) {
|
||||
// Pop one instruction from the queue
|
||||
const Inst* const inst{queue.front()};
|
||||
queue.pop();
|
||||
if (const std::optional result = pred(inst)) {
|
||||
// This is the instruction we were looking for
|
||||
return result;
|
||||
}
|
||||
// Visit the right most arguments first
|
||||
for (size_t arg = inst->NumArgs(); arg--;) {
|
||||
const Value arg_value{inst->Arg(arg)};
|
||||
if (arg_value.IsImmediate()) {
|
||||
continue;
|
||||
}
|
||||
// Queue instruction if it hasn't been visited
|
||||
const Inst* const arg_inst{arg_value.InstRecursive()};
|
||||
if (std::ranges::find(visited, arg_inst) == visited.end()) {
|
||||
visited.push_back(arg_inst);
|
||||
queue.push(arg_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
// SSA tree has been traversed and the result hasn't been found
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
29
src/shader_recompiler/frontend/ir/condition.cpp
Executable file
29
src/shader_recompiler/frontend/ir/condition.cpp
Executable file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/condition.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(Condition condition) {
|
||||
std::string ret;
|
||||
if (condition.GetFlowTest() != FlowTest::T) {
|
||||
ret = fmt::to_string(condition.GetFlowTest());
|
||||
}
|
||||
const auto [pred, negated]{condition.GetPred()};
|
||||
if (!ret.empty()) {
|
||||
ret += '&';
|
||||
}
|
||||
if (negated) {
|
||||
ret += '!';
|
||||
}
|
||||
ret += fmt::to_string(pred);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
60
src/shader_recompiler/frontend/ir/condition.h
Executable file
60
src/shader_recompiler/frontend/ir/condition.h
Executable file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <compare>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/frontend/ir/flow_test.h"
|
||||
#include "shader_recompiler/frontend/ir/pred.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Condition {
|
||||
public:
|
||||
Condition() noexcept = default;
|
||||
|
||||
explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept
|
||||
: flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)},
|
||||
pred_negated{pred_negated_ ? u8{1} : u8{0}} {}
|
||||
|
||||
explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept
|
||||
: Condition(FlowTest::T, pred_, pred_negated_) {}
|
||||
|
||||
explicit Condition(bool value) : Condition(Pred::PT, !value) {}
|
||||
|
||||
auto operator<=>(const Condition&) const noexcept = default;
|
||||
|
||||
[[nodiscard]] IR::FlowTest GetFlowTest() const noexcept {
|
||||
return static_cast<IR::FlowTest>(flow_test);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<IR::Pred, bool> GetPred() const noexcept {
|
||||
return {static_cast<IR::Pred>(pred), pred_negated != 0};
|
||||
}
|
||||
|
||||
private:
|
||||
u16 flow_test;
|
||||
u8 pred;
|
||||
u8 pred_negated;
|
||||
};
|
||||
|
||||
std::string NameOf(Condition condition);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Condition> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Condition& cond, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond));
|
||||
}
|
||||
};
|
83
src/shader_recompiler/frontend/ir/flow_test.cpp
Executable file
83
src/shader_recompiler/frontend/ir/flow_test.cpp
Executable file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/flow_test.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(FlowTest flow_test) {
|
||||
switch (flow_test) {
|
||||
case FlowTest::F:
|
||||
return "F";
|
||||
case FlowTest::LT:
|
||||
return "LT";
|
||||
case FlowTest::EQ:
|
||||
return "EQ";
|
||||
case FlowTest::LE:
|
||||
return "LE";
|
||||
case FlowTest::GT:
|
||||
return "GT";
|
||||
case FlowTest::NE:
|
||||
return "NE";
|
||||
case FlowTest::GE:
|
||||
return "GE";
|
||||
case FlowTest::NUM:
|
||||
return "NUM";
|
||||
case FlowTest::NaN:
|
||||
return "NAN";
|
||||
case FlowTest::LTU:
|
||||
return "LTU";
|
||||
case FlowTest::EQU:
|
||||
return "EQU";
|
||||
case FlowTest::LEU:
|
||||
return "LEU";
|
||||
case FlowTest::GTU:
|
||||
return "GTU";
|
||||
case FlowTest::NEU:
|
||||
return "NEU";
|
||||
case FlowTest::GEU:
|
||||
return "GEU";
|
||||
case FlowTest::T:
|
||||
return "T";
|
||||
case FlowTest::OFF:
|
||||
return "OFF";
|
||||
case FlowTest::LO:
|
||||
return "LO";
|
||||
case FlowTest::SFF:
|
||||
return "SFF";
|
||||
case FlowTest::LS:
|
||||
return "LS";
|
||||
case FlowTest::HI:
|
||||
return "HI";
|
||||
case FlowTest::SFT:
|
||||
return "SFT";
|
||||
case FlowTest::HS:
|
||||
return "HS";
|
||||
case FlowTest::OFT:
|
||||
return "OFT";
|
||||
case FlowTest::CSM_TA:
|
||||
return "CSM_TA";
|
||||
case FlowTest::CSM_TR:
|
||||
return "CSM_TR";
|
||||
case FlowTest::CSM_MX:
|
||||
return "CSM_MX";
|
||||
case FlowTest::FCSM_TA:
|
||||
return "FCSM_TA";
|
||||
case FlowTest::FCSM_TR:
|
||||
return "FCSM_TR";
|
||||
case FlowTest::FCSM_MX:
|
||||
return "FCSM_MX";
|
||||
case FlowTest::RLE:
|
||||
return "RLE";
|
||||
case FlowTest::RGT:
|
||||
return "RGT";
|
||||
}
|
||||
return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
62
src/shader_recompiler/frontend/ir/flow_test.h
Executable file
62
src/shader_recompiler/frontend/ir/flow_test.h
Executable file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class FlowTest : u64 {
|
||||
F,
|
||||
LT,
|
||||
EQ,
|
||||
LE,
|
||||
GT,
|
||||
NE,
|
||||
GE,
|
||||
NUM,
|
||||
NaN,
|
||||
LTU,
|
||||
EQU,
|
||||
LEU,
|
||||
GTU,
|
||||
NEU,
|
||||
GEU,
|
||||
T,
|
||||
OFF,
|
||||
LO,
|
||||
SFF,
|
||||
LS,
|
||||
HI,
|
||||
SFT,
|
||||
HS,
|
||||
OFT,
|
||||
CSM_TA,
|
||||
CSM_TR,
|
||||
CSM_MX,
|
||||
FCSM_TA,
|
||||
FCSM_TR,
|
||||
FCSM_MX,
|
||||
RLE,
|
||||
RGT,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string NameOf(FlowTest flow_test);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::FlowTest> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test));
|
||||
}
|
||||
};
|
2017
src/shader_recompiler/frontend/ir/ir_emitter.cpp
Executable file
2017
src/shader_recompiler/frontend/ir/ir_emitter.cpp
Executable file
File diff suppressed because it is too large
Load Diff
413
src/shader_recompiler/frontend/ir/ir_emitter.h
Executable file
413
src/shader_recompiler/frontend/ir/ir_emitter.h
Executable file
@@ -0,0 +1,413 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class IREmitter {
|
||||
public:
|
||||
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
|
||||
explicit IREmitter(Block& block_, Block::iterator insertion_point_)
|
||||
: block{&block_}, insertion_point{insertion_point_} {}
|
||||
|
||||
Block* block;
|
||||
|
||||
[[nodiscard]] U1 Imm1(bool value) const;
|
||||
[[nodiscard]] U8 Imm8(u8 value) const;
|
||||
[[nodiscard]] U16 Imm16(u16 value) const;
|
||||
[[nodiscard]] U32 Imm32(u32 value) const;
|
||||
[[nodiscard]] U32 Imm32(s32 value) const;
|
||||
[[nodiscard]] F32 Imm32(f32 value) const;
|
||||
[[nodiscard]] U64 Imm64(u64 value) const;
|
||||
[[nodiscard]] U64 Imm64(s64 value) const;
|
||||
[[nodiscard]] F64 Imm64(f64 value) const;
|
||||
|
||||
U1 ConditionRef(const U1& value);
|
||||
void Reference(const Value& value);
|
||||
|
||||
void PhiMove(IR::Inst& phi, const Value& value);
|
||||
|
||||
void Prologue();
|
||||
void Epilogue();
|
||||
void DemoteToHelperInvocation();
|
||||
void EmitVertex(const U32& stream);
|
||||
void EndPrimitive(const U32& stream);
|
||||
|
||||
[[nodiscard]] U32 GetReg(IR::Reg reg);
|
||||
void SetReg(IR::Reg reg, const U32& value);
|
||||
|
||||
[[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false);
|
||||
void SetPred(IR::Pred pred, const U1& value);
|
||||
|
||||
[[nodiscard]] U1 GetGotoVariable(u32 id);
|
||||
void SetGotoVariable(u32 id, const U1& value);
|
||||
|
||||
[[nodiscard]] U32 GetIndirectBranchVariable();
|
||||
void SetIndirectBranchVariable(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset);
|
||||
[[nodiscard]] Value GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize,
|
||||
bool is_signed);
|
||||
[[nodiscard]] F32 GetFloatCbuf(const U32& binding, const U32& byte_offset);
|
||||
|
||||
[[nodiscard]] U1 GetZFlag();
|
||||
[[nodiscard]] U1 GetSFlag();
|
||||
[[nodiscard]] U1 GetCFlag();
|
||||
[[nodiscard]] U1 GetOFlag();
|
||||
|
||||
void SetZFlag(const U1& value);
|
||||
void SetSFlag(const U1& value);
|
||||
void SetCFlag(const U1& value);
|
||||
void SetOFlag(const U1& value);
|
||||
|
||||
[[nodiscard]] U1 Condition(IR::Condition cond);
|
||||
[[nodiscard]] U1 GetFlowTestResult(FlowTest test);
|
||||
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute);
|
||||
[[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex);
|
||||
void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address);
|
||||
[[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex);
|
||||
void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex);
|
||||
|
||||
[[nodiscard]] F32 GetPatch(Patch patch);
|
||||
void SetPatch(Patch patch, const F32& value);
|
||||
|
||||
void SetFragColor(u32 index, u32 component, const F32& value);
|
||||
void SetSampleMask(const U32& value);
|
||||
void SetFragDepth(const F32& value);
|
||||
|
||||
[[nodiscard]] U32 WorkgroupIdX();
|
||||
[[nodiscard]] U32 WorkgroupIdY();
|
||||
[[nodiscard]] U32 WorkgroupIdZ();
|
||||
|
||||
[[nodiscard]] Value LocalInvocationId();
|
||||
[[nodiscard]] U32 LocalInvocationIdX();
|
||||
[[nodiscard]] U32 LocalInvocationIdY();
|
||||
[[nodiscard]] U32 LocalInvocationIdZ();
|
||||
|
||||
[[nodiscard]] U32 InvocationId();
|
||||
[[nodiscard]] U32 SampleId();
|
||||
[[nodiscard]] U1 IsHelperInvocation();
|
||||
[[nodiscard]] F32 YDirection();
|
||||
|
||||
[[nodiscard]] U32 LaneId();
|
||||
|
||||
[[nodiscard]] U32 LoadGlobalU8(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalS8(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalU16(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobalS16(const U64& address);
|
||||
[[nodiscard]] U32 LoadGlobal32(const U64& address);
|
||||
[[nodiscard]] Value LoadGlobal64(const U64& address);
|
||||
[[nodiscard]] Value LoadGlobal128(const U64& address);
|
||||
|
||||
void WriteGlobalU8(const U64& address, const U32& value);
|
||||
void WriteGlobalS8(const U64& address, const U32& value);
|
||||
void WriteGlobalU16(const U64& address, const U32& value);
|
||||
void WriteGlobalS16(const U64& address, const U32& value);
|
||||
void WriteGlobal32(const U64& address, const U32& value);
|
||||
void WriteGlobal64(const U64& address, const IR::Value& vector);
|
||||
void WriteGlobal128(const U64& address, const IR::Value& vector);
|
||||
|
||||
[[nodiscard]] U32 LoadLocal(const U32& word_offset);
|
||||
void WriteLocal(const U32& word_offset, const U32& value);
|
||||
|
||||
[[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset);
|
||||
void WriteShared(int bit_size, const U32& offset, const Value& value);
|
||||
|
||||
[[nodiscard]] U1 GetZeroFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetSignFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetCarryFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetOverflowFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetSparseFromOp(const Value& op);
|
||||
[[nodiscard]] U1 GetInBoundsFromOp(const Value& op);
|
||||
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2);
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3);
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3,
|
||||
const Value& e4);
|
||||
[[nodiscard]] Value CompositeExtract(const Value& vector, size_t element);
|
||||
[[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element);
|
||||
|
||||
[[nodiscard]] Value Select(const U1& condition, const Value& true_value,
|
||||
const Value& false_value);
|
||||
|
||||
void Barrier();
|
||||
void WorkgroupMemoryBarrier();
|
||||
void DeviceMemoryBarrier();
|
||||
|
||||
template <typename Dest, typename Source>
|
||||
[[nodiscard]] Dest BitCast(const Source& value);
|
||||
|
||||
[[nodiscard]] U64 PackUint2x32(const Value& vector);
|
||||
[[nodiscard]] Value UnpackUint2x32(const U64& value);
|
||||
|
||||
[[nodiscard]] U32 PackFloat2x16(const Value& vector);
|
||||
[[nodiscard]] Value UnpackFloat2x16(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 PackHalf2x16(const Value& vector);
|
||||
[[nodiscard]] Value UnpackHalf2x16(const U32& value);
|
||||
|
||||
[[nodiscard]] F64 PackDouble2x32(const Value& vector);
|
||||
[[nodiscard]] Value UnpackDouble2x32(const F64& value);
|
||||
|
||||
[[nodiscard]] F16F32F64 FPAdd(const F16F32F64& a, const F16F32F64& b, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPMul(const F16F32F64& a, const F16F32F64& b, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPFma(const F16F32F64& a, const F16F32F64& b, const F16F32F64& c,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] F16F32F64 FPAbs(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPNeg(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPAbsNeg(const F16F32F64& value, bool abs, bool neg);
|
||||
|
||||
[[nodiscard]] F32 FPCos(const F32& value);
|
||||
[[nodiscard]] F32 FPSin(const F32& value);
|
||||
[[nodiscard]] F32 FPExp2(const F32& value);
|
||||
[[nodiscard]] F32 FPLog2(const F32& value);
|
||||
[[nodiscard]] F32F64 FPRecip(const F32F64& value);
|
||||
[[nodiscard]] F32F64 FPRecipSqrt(const F32F64& value);
|
||||
[[nodiscard]] F32 FPSqrt(const F32& value);
|
||||
[[nodiscard]] F16F32F64 FPSaturate(const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 FPClamp(const F16F32F64& value, const F16F32F64& min_value,
|
||||
const F16F32F64& max_value);
|
||||
[[nodiscard]] F16F32F64 FPRoundEven(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPFloor(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPCeil(const F16F32F64& value, FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 FPTrunc(const F16F32F64& value, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U1 FPEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPNotEqual(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPLessThan(const F16F32F64& lhs, const F16F32F64& rhs, FpControl control = {},
|
||||
bool ordered = true);
|
||||
[[nodiscard]] U1 FPGreaterThan(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPLessThanEqual(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPGreaterThanEqual(const F16F32F64& lhs, const F16F32F64& rhs,
|
||||
FpControl control = {}, bool ordered = true);
|
||||
[[nodiscard]] U1 FPIsNan(const F16F32F64& value);
|
||||
[[nodiscard]] U1 FPOrdered(const F16F32F64& lhs, const F16F32F64& rhs);
|
||||
[[nodiscard]] U1 FPUnordered(const F16F32F64& lhs, const F16F32F64& rhs);
|
||||
[[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, FpControl control = {});
|
||||
[[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32 IMul(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32U64 INeg(const U32U64& value);
|
||||
[[nodiscard]] U32 IAbs(const U32& value);
|
||||
[[nodiscard]] U32U64 ShiftLeftLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32 BitwiseAnd(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseOr(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseXor(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitFieldInsert(const U32& base, const U32& insert, const U32& offset,
|
||||
const U32& count);
|
||||
[[nodiscard]] U32 BitFieldExtract(const U32& base, const U32& offset, const U32& count,
|
||||
bool is_signed = false);
|
||||
[[nodiscard]] U32 BitReverse(const U32& value);
|
||||
[[nodiscard]] U32 BitCount(const U32& value);
|
||||
[[nodiscard]] U32 BitwiseNot(const U32& value);
|
||||
|
||||
[[nodiscard]] U32 FindSMsb(const U32& value);
|
||||
[[nodiscard]] U32 FindUMsb(const U32& value);
|
||||
[[nodiscard]] U32 SMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMax(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed);
|
||||
[[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max);
|
||||
[[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max);
|
||||
|
||||
[[nodiscard]] U1 ILessThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs);
|
||||
[[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 INotEqual(const U32& lhs, const U32& rhs);
|
||||
[[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
|
||||
[[nodiscard]] U32 SharedAtomicIAdd(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicSMin(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicUMin(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicIMin(const U32& pointer_offset, const U32& value, bool is_signed);
|
||||
[[nodiscard]] U32 SharedAtomicSMax(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicUMax(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicIMax(const U32& pointer_offset, const U32& value, bool is_signed);
|
||||
[[nodiscard]] U32 SharedAtomicInc(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicDec(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicAnd(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicOr(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 SharedAtomicXor(const U32& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32U64 SharedAtomicExchange(const U32& pointer_offset, const U32U64& value);
|
||||
|
||||
[[nodiscard]] U32U64 GlobalAtomicIAdd(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicSMin(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicUMin(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicIMin(const U64& pointer_offset, const U32U64& value,
|
||||
bool is_signed);
|
||||
[[nodiscard]] U32U64 GlobalAtomicSMax(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicUMax(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicIMax(const U64& pointer_offset, const U32U64& value,
|
||||
bool is_signed);
|
||||
[[nodiscard]] U32 GlobalAtomicInc(const U64& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32 GlobalAtomicDec(const U64& pointer_offset, const U32& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicAnd(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicOr(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicXor(const U64& pointer_offset, const U32U64& value);
|
||||
[[nodiscard]] U32U64 GlobalAtomicExchange(const U64& pointer_offset, const U32U64& value);
|
||||
|
||||
[[nodiscard]] F32 GlobalAtomicF32Add(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Add(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Min(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
[[nodiscard]] Value GlobalAtomicF16x2Max(const U64& pointer_offset, const Value& value,
|
||||
const FpControl control = {});
|
||||
|
||||
[[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalXor(const U1& a, const U1& b);
|
||||
[[nodiscard]] U1 LogicalNot(const U1& value);
|
||||
|
||||
[[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const F16F32F64& value);
|
||||
[[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const F16F32F64& value);
|
||||
[[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const F16F32F64& value);
|
||||
[[nodiscard]] F16F32F64 ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Value& value,
|
||||
FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Value& value,
|
||||
FpControl control = {});
|
||||
[[nodiscard]] F16F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed,
|
||||
const Value& value, FpControl control = {});
|
||||
|
||||
[[nodiscard]] U32U64 UConvert(size_t result_bitsize, const U32U64& value);
|
||||
[[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& bias, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& lod, const Value& offset,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] F32 ImageSampleDrefImplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& dref, const F32& bias,
|
||||
const Value& offset, const F32& lod_clamp,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
|
||||
const F32& dref, const F32& lod,
|
||||
const Value& offset, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
|
||||
|
||||
[[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset,
|
||||
const Value& offset2, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords,
|
||||
const Value& offset, const Value& offset2, const F32& dref,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
|
||||
const U32& lod, const U32& multisampling, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
|
||||
const Value& derivates, const Value& offset,
|
||||
const F32& lod_clamp, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
|
||||
[[nodiscard]] void ImageWrite(const Value& handle, const Value& coords, const Value& color,
|
||||
TextureInstInfo info);
|
||||
|
||||
[[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicSMin(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicUMin(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicIMin(const Value& handle, const Value& coords,
|
||||
const Value& value, bool is_signed, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicSMax(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords,
|
||||
const Value& value, bool is_signed, TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicDec(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicAnd(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicOr(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicXor(const Value& handle, const Value& coords, const Value& value,
|
||||
TextureInstInfo info);
|
||||
[[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords,
|
||||
const Value& value, TextureInstInfo info);
|
||||
[[nodiscard]] U1 VoteAll(const U1& value);
|
||||
[[nodiscard]] U1 VoteAny(const U1& value);
|
||||
[[nodiscard]] U1 VoteEqual(const U1& value);
|
||||
[[nodiscard]] U32 SubgroupBallot(const U1& value);
|
||||
[[nodiscard]] U32 SubgroupEqMask();
|
||||
[[nodiscard]] U32 SubgroupLtMask();
|
||||
[[nodiscard]] U32 SubgroupLeMask();
|
||||
[[nodiscard]] U32 SubgroupGtMask();
|
||||
[[nodiscard]] U32 SubgroupGeMask();
|
||||
[[nodiscard]] U32 ShuffleIndex(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleUp(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleDown(const IR::U32& value, const IR::U32& index, const IR::U32& clamp,
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
|
||||
const IR::U32& clamp, const IR::U32& seg_mask);
|
||||
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
|
||||
FpControl control = {});
|
||||
|
||||
[[nodiscard]] F32 DPdxFine(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdyFine(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdxCoarse(const F32& a);
|
||||
|
||||
[[nodiscard]] F32 DPdyCoarse(const F32& a);
|
||||
|
||||
private:
|
||||
IR::Block::iterator insertion_point;
|
||||
|
||||
template <typename T = Value, typename... Args>
|
||||
T Inst(Opcode op, Args... args) {
|
||||
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...})};
|
||||
return T{Value{&*it}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags {
|
||||
Flags() = default;
|
||||
Flags(T proxy_) : proxy{proxy_} {}
|
||||
|
||||
T proxy;
|
||||
};
|
||||
|
||||
template <typename T = Value, typename FlagType, typename... Args>
|
||||
T Inst(Opcode op, Flags<FlagType> flags, Args... args) {
|
||||
u32 raw_flags{};
|
||||
std::memcpy(&raw_flags, &flags.proxy, sizeof(flags.proxy));
|
||||
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...}, raw_flags)};
|
||||
return T{Value{&*it}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader::IR
|
411
src/shader_recompiler/frontend/ir/microinstruction.cpp
Executable file
411
src/shader_recompiler/frontend/ir/microinstruction.cpp
Executable file
@@ -0,0 +1,411 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
namespace {
|
||||
void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) {
|
||||
if (inst && inst->GetOpcode() != opcode) {
|
||||
throw LogicError("Invalid pseudo-instruction");
|
||||
}
|
||||
}
|
||||
|
||||
void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) {
|
||||
if (dest_inst) {
|
||||
throw LogicError("Only one of each type of pseudo-op allowed");
|
||||
}
|
||||
dest_inst = pseudo_inst;
|
||||
}
|
||||
|
||||
void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) {
|
||||
if (inst->GetOpcode() != expected_opcode) {
|
||||
throw LogicError("Undoing use of invalid pseudo-op");
|
||||
}
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
void AllocAssociatedInsts(std::unique_ptr<AssociatedInsts>& associated_insts) {
|
||||
if (!associated_insts) {
|
||||
associated_insts = std::make_unique<AssociatedInsts>();
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Inst::Inst(IR::Opcode op_, u32 flags_) noexcept : op{op_}, flags{flags_} {
|
||||
if (op == Opcode::Phi) {
|
||||
std::construct_at(&phi_args);
|
||||
} else {
|
||||
std::construct_at(&args);
|
||||
}
|
||||
}
|
||||
|
||||
Inst::~Inst() {
|
||||
if (op == Opcode::Phi) {
|
||||
std::destroy_at(&phi_args);
|
||||
} else {
|
||||
std::destroy_at(&args);
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::MayHaveSideEffects() const noexcept {
|
||||
switch (op) {
|
||||
case Opcode::ConditionRef:
|
||||
case Opcode::Reference:
|
||||
case Opcode::PhiMove:
|
||||
case Opcode::Prologue:
|
||||
case Opcode::Epilogue:
|
||||
case Opcode::Join:
|
||||
case Opcode::DemoteToHelperInvocation:
|
||||
case Opcode::Barrier:
|
||||
case Opcode::WorkgroupMemoryBarrier:
|
||||
case Opcode::DeviceMemoryBarrier:
|
||||
case Opcode::EmitVertex:
|
||||
case Opcode::EndPrimitive:
|
||||
case Opcode::SetAttribute:
|
||||
case Opcode::SetAttributeIndexed:
|
||||
case Opcode::SetPatch:
|
||||
case Opcode::SetFragColor:
|
||||
case Opcode::SetSampleMask:
|
||||
case Opcode::SetFragDepth:
|
||||
case Opcode::WriteGlobalU8:
|
||||
case Opcode::WriteGlobalS8:
|
||||
case Opcode::WriteGlobalU16:
|
||||
case Opcode::WriteGlobalS16:
|
||||
case Opcode::WriteGlobal32:
|
||||
case Opcode::WriteGlobal64:
|
||||
case Opcode::WriteGlobal128:
|
||||
case Opcode::WriteStorageU8:
|
||||
case Opcode::WriteStorageS8:
|
||||
case Opcode::WriteStorageU16:
|
||||
case Opcode::WriteStorageS16:
|
||||
case Opcode::WriteStorage32:
|
||||
case Opcode::WriteStorage64:
|
||||
case Opcode::WriteStorage128:
|
||||
case Opcode::WriteLocal:
|
||||
case Opcode::WriteSharedU8:
|
||||
case Opcode::WriteSharedU16:
|
||||
case Opcode::WriteSharedU32:
|
||||
case Opcode::WriteSharedU64:
|
||||
case Opcode::WriteSharedU128:
|
||||
case Opcode::SharedAtomicIAdd32:
|
||||
case Opcode::SharedAtomicSMin32:
|
||||
case Opcode::SharedAtomicUMin32:
|
||||
case Opcode::SharedAtomicSMax32:
|
||||
case Opcode::SharedAtomicUMax32:
|
||||
case Opcode::SharedAtomicInc32:
|
||||
case Opcode::SharedAtomicDec32:
|
||||
case Opcode::SharedAtomicAnd32:
|
||||
case Opcode::SharedAtomicOr32:
|
||||
case Opcode::SharedAtomicXor32:
|
||||
case Opcode::SharedAtomicExchange32:
|
||||
case Opcode::SharedAtomicExchange64:
|
||||
case Opcode::GlobalAtomicIAdd32:
|
||||
case Opcode::GlobalAtomicSMin32:
|
||||
case Opcode::GlobalAtomicUMin32:
|
||||
case Opcode::GlobalAtomicSMax32:
|
||||
case Opcode::GlobalAtomicUMax32:
|
||||
case Opcode::GlobalAtomicInc32:
|
||||
case Opcode::GlobalAtomicDec32:
|
||||
case Opcode::GlobalAtomicAnd32:
|
||||
case Opcode::GlobalAtomicOr32:
|
||||
case Opcode::GlobalAtomicXor32:
|
||||
case Opcode::GlobalAtomicExchange32:
|
||||
case Opcode::GlobalAtomicIAdd64:
|
||||
case Opcode::GlobalAtomicSMin64:
|
||||
case Opcode::GlobalAtomicUMin64:
|
||||
case Opcode::GlobalAtomicSMax64:
|
||||
case Opcode::GlobalAtomicUMax64:
|
||||
case Opcode::GlobalAtomicAnd64:
|
||||
case Opcode::GlobalAtomicOr64:
|
||||
case Opcode::GlobalAtomicXor64:
|
||||
case Opcode::GlobalAtomicExchange64:
|
||||
case Opcode::GlobalAtomicAddF32:
|
||||
case Opcode::GlobalAtomicAddF16x2:
|
||||
case Opcode::GlobalAtomicAddF32x2:
|
||||
case Opcode::GlobalAtomicMinF16x2:
|
||||
case Opcode::GlobalAtomicMinF32x2:
|
||||
case Opcode::GlobalAtomicMaxF16x2:
|
||||
case Opcode::GlobalAtomicMaxF32x2:
|
||||
case Opcode::StorageAtomicIAdd32:
|
||||
case Opcode::StorageAtomicSMin32:
|
||||
case Opcode::StorageAtomicUMin32:
|
||||
case Opcode::StorageAtomicSMax32:
|
||||
case Opcode::StorageAtomicUMax32:
|
||||
case Opcode::StorageAtomicInc32:
|
||||
case Opcode::StorageAtomicDec32:
|
||||
case Opcode::StorageAtomicAnd32:
|
||||
case Opcode::StorageAtomicOr32:
|
||||
case Opcode::StorageAtomicXor32:
|
||||
case Opcode::StorageAtomicExchange32:
|
||||
case Opcode::StorageAtomicIAdd64:
|
||||
case Opcode::StorageAtomicSMin64:
|
||||
case Opcode::StorageAtomicUMin64:
|
||||
case Opcode::StorageAtomicSMax64:
|
||||
case Opcode::StorageAtomicUMax64:
|
||||
case Opcode::StorageAtomicAnd64:
|
||||
case Opcode::StorageAtomicOr64:
|
||||
case Opcode::StorageAtomicXor64:
|
||||
case Opcode::StorageAtomicExchange64:
|
||||
case Opcode::StorageAtomicAddF32:
|
||||
case Opcode::StorageAtomicAddF16x2:
|
||||
case Opcode::StorageAtomicAddF32x2:
|
||||
case Opcode::StorageAtomicMinF16x2:
|
||||
case Opcode::StorageAtomicMinF32x2:
|
||||
case Opcode::StorageAtomicMaxF16x2:
|
||||
case Opcode::StorageAtomicMaxF32x2:
|
||||
case Opcode::BindlessImageWrite:
|
||||
case Opcode::BoundImageWrite:
|
||||
case Opcode::ImageWrite:
|
||||
case IR::Opcode::BindlessImageAtomicIAdd32:
|
||||
case IR::Opcode::BindlessImageAtomicSMin32:
|
||||
case IR::Opcode::BindlessImageAtomicUMin32:
|
||||
case IR::Opcode::BindlessImageAtomicSMax32:
|
||||
case IR::Opcode::BindlessImageAtomicUMax32:
|
||||
case IR::Opcode::BindlessImageAtomicInc32:
|
||||
case IR::Opcode::BindlessImageAtomicDec32:
|
||||
case IR::Opcode::BindlessImageAtomicAnd32:
|
||||
case IR::Opcode::BindlessImageAtomicOr32:
|
||||
case IR::Opcode::BindlessImageAtomicXor32:
|
||||
case IR::Opcode::BindlessImageAtomicExchange32:
|
||||
case IR::Opcode::BoundImageAtomicIAdd32:
|
||||
case IR::Opcode::BoundImageAtomicSMin32:
|
||||
case IR::Opcode::BoundImageAtomicUMin32:
|
||||
case IR::Opcode::BoundImageAtomicSMax32:
|
||||
case IR::Opcode::BoundImageAtomicUMax32:
|
||||
case IR::Opcode::BoundImageAtomicInc32:
|
||||
case IR::Opcode::BoundImageAtomicDec32:
|
||||
case IR::Opcode::BoundImageAtomicAnd32:
|
||||
case IR::Opcode::BoundImageAtomicOr32:
|
||||
case IR::Opcode::BoundImageAtomicXor32:
|
||||
case IR::Opcode::BoundImageAtomicExchange32:
|
||||
case IR::Opcode::ImageAtomicIAdd32:
|
||||
case IR::Opcode::ImageAtomicSMin32:
|
||||
case IR::Opcode::ImageAtomicUMin32:
|
||||
case IR::Opcode::ImageAtomicSMax32:
|
||||
case IR::Opcode::ImageAtomicUMax32:
|
||||
case IR::Opcode::ImageAtomicInc32:
|
||||
case IR::Opcode::ImageAtomicDec32:
|
||||
case IR::Opcode::ImageAtomicAnd32:
|
||||
case IR::Opcode::ImageAtomicOr32:
|
||||
case IR::Opcode::ImageAtomicXor32:
|
||||
case IR::Opcode::ImageAtomicExchange32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::IsPseudoInstruction() const noexcept {
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
case Opcode::GetSignFromOp:
|
||||
case Opcode::GetCarryFromOp:
|
||||
case Opcode::GetOverflowFromOp:
|
||||
case Opcode::GetSparseFromOp:
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Inst::AreAllArgsImmediates() const {
|
||||
if (op == Opcode::Phi) {
|
||||
throw LogicError("Testing for all arguments are immediates on phi instruction");
|
||||
}
|
||||
return std::all_of(args.begin(), args.begin() + NumArgs(),
|
||||
[](const IR::Value& value) { return value.IsImmediate(); });
|
||||
}
|
||||
|
||||
Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
|
||||
if (!associated_insts) {
|
||||
return nullptr;
|
||||
}
|
||||
switch (opcode) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
CheckPseudoInstruction(associated_insts->zero_inst, Opcode::GetZeroFromOp);
|
||||
return associated_insts->zero_inst;
|
||||
case Opcode::GetSignFromOp:
|
||||
CheckPseudoInstruction(associated_insts->sign_inst, Opcode::GetSignFromOp);
|
||||
return associated_insts->sign_inst;
|
||||
case Opcode::GetCarryFromOp:
|
||||
CheckPseudoInstruction(associated_insts->carry_inst, Opcode::GetCarryFromOp);
|
||||
return associated_insts->carry_inst;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
CheckPseudoInstruction(associated_insts->overflow_inst, Opcode::GetOverflowFromOp);
|
||||
return associated_insts->overflow_inst;
|
||||
case Opcode::GetSparseFromOp:
|
||||
CheckPseudoInstruction(associated_insts->sparse_inst, Opcode::GetSparseFromOp);
|
||||
return associated_insts->sparse_inst;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
CheckPseudoInstruction(associated_insts->in_bounds_inst, Opcode::GetInBoundsFromOp);
|
||||
return associated_insts->in_bounds_inst;
|
||||
default:
|
||||
throw InvalidArgument("{} is not a pseudo-instruction", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Type Inst::Type() const {
|
||||
return TypeOf(op);
|
||||
}
|
||||
|
||||
void Inst::SetArg(size_t index, Value value) {
|
||||
if (index >= NumArgs()) {
|
||||
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
|
||||
}
|
||||
const IR::Value arg{Arg(index)};
|
||||
if (!arg.IsImmediate()) {
|
||||
UndoUse(arg);
|
||||
}
|
||||
if (!value.IsImmediate()) {
|
||||
Use(value);
|
||||
}
|
||||
if (op == Opcode::Phi) {
|
||||
phi_args[index].second = value;
|
||||
} else {
|
||||
args[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Block* Inst::PhiBlock(size_t index) const {
|
||||
if (op != Opcode::Phi) {
|
||||
throw LogicError("{} is not a Phi instruction", op);
|
||||
}
|
||||
if (index >= phi_args.size()) {
|
||||
throw InvalidArgument("Out of bounds argument index {} in phi instruction");
|
||||
}
|
||||
return phi_args[index].first;
|
||||
}
|
||||
|
||||
void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
|
||||
if (!value.IsImmediate()) {
|
||||
Use(value);
|
||||
}
|
||||
phi_args.emplace_back(predecessor, value);
|
||||
}
|
||||
|
||||
void Inst::Invalidate() {
|
||||
ClearArgs();
|
||||
ReplaceOpcode(Opcode::Void);
|
||||
}
|
||||
|
||||
void Inst::ClearArgs() {
|
||||
if (op == Opcode::Phi) {
|
||||
for (auto& pair : phi_args) {
|
||||
IR::Value& value{pair.second};
|
||||
if (!value.IsImmediate()) {
|
||||
UndoUse(value);
|
||||
}
|
||||
}
|
||||
phi_args.clear();
|
||||
} else {
|
||||
for (auto& value : args) {
|
||||
if (!value.IsImmediate()) {
|
||||
UndoUse(value);
|
||||
}
|
||||
}
|
||||
// Reset arguments to null
|
||||
// std::memset was measured to be faster on MSVC than std::ranges:fill
|
||||
std::memset(reinterpret_cast<char*>(&args), 0, sizeof(args));
|
||||
}
|
||||
}
|
||||
|
||||
void Inst::ReplaceUsesWith(Value replacement) {
|
||||
Invalidate();
|
||||
ReplaceOpcode(Opcode::Identity);
|
||||
if (!replacement.IsImmediate()) {
|
||||
Use(replacement);
|
||||
}
|
||||
args[0] = replacement;
|
||||
}
|
||||
|
||||
void Inst::ReplaceOpcode(IR::Opcode opcode) {
|
||||
if (opcode == IR::Opcode::Phi) {
|
||||
throw LogicError("Cannot transition into Phi");
|
||||
}
|
||||
if (op == Opcode::Phi) {
|
||||
// Transition out of phi arguments into non-phi
|
||||
std::destroy_at(&phi_args);
|
||||
std::construct_at(&args);
|
||||
}
|
||||
op = opcode;
|
||||
}
|
||||
|
||||
void Inst::Use(const Value& value) {
|
||||
Inst* const inst{value.Inst()};
|
||||
++inst->use_count;
|
||||
|
||||
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->zero_inst, this);
|
||||
break;
|
||||
case Opcode::GetSignFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->sign_inst, this);
|
||||
break;
|
||||
case Opcode::GetCarryFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->carry_inst, this);
|
||||
break;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->overflow_inst, this);
|
||||
break;
|
||||
case Opcode::GetSparseFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->sparse_inst, this);
|
||||
break;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
SetPseudoInstruction(assoc_inst->in_bounds_inst, this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Inst::UndoUse(const Value& value) {
|
||||
Inst* const inst{value.Inst()};
|
||||
--inst->use_count;
|
||||
|
||||
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
|
||||
switch (op) {
|
||||
case Opcode::GetZeroFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->zero_inst, Opcode::GetZeroFromOp);
|
||||
break;
|
||||
case Opcode::GetSignFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->sign_inst, Opcode::GetSignFromOp);
|
||||
break;
|
||||
case Opcode::GetCarryFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->carry_inst, Opcode::GetCarryFromOp);
|
||||
break;
|
||||
case Opcode::GetOverflowFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->overflow_inst, Opcode::GetOverflowFromOp);
|
||||
break;
|
||||
case Opcode::GetSparseFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->sparse_inst, Opcode::GetSparseFromOp);
|
||||
break;
|
||||
case Opcode::GetInBoundsFromOp:
|
||||
AllocAssociatedInsts(assoc_inst);
|
||||
RemovePseudoInstruction(assoc_inst->in_bounds_inst, Opcode::GetInBoundsFromOp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
49
src/shader_recompiler/frontend/ir/modifiers.h
Executable file
49
src/shader_recompiler/frontend/ir/modifiers.h
Executable file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class FmzMode : u8 {
|
||||
DontCare, // Not specified for this instruction
|
||||
FTZ, // Flush denorms to zero, NAN is propagated (D3D11, NVN, GL, VK)
|
||||
FMZ, // Flush denorms to zero, x * 0 == 0 (D3D9)
|
||||
None, // Denorms are not flushed, NAN is propagated (nouveau)
|
||||
};
|
||||
|
||||
enum class FpRounding : u8 {
|
||||
DontCare, // Not specified for this instruction
|
||||
RN, // Round to nearest even,
|
||||
RM, // Round towards negative infinity
|
||||
RP, // Round towards positive infinity
|
||||
RZ, // Round towards zero
|
||||
};
|
||||
|
||||
struct FpControl {
|
||||
bool no_contraction{false};
|
||||
FpRounding rounding{FpRounding::DontCare};
|
||||
FmzMode fmz_mode{FmzMode::DontCare};
|
||||
};
|
||||
static_assert(sizeof(FpControl) <= sizeof(u32));
|
||||
|
||||
union TextureInstInfo {
|
||||
u32 raw;
|
||||
BitField<0, 16, u32> descriptor_index;
|
||||
BitField<16, 3, TextureType> type;
|
||||
BitField<19, 1, u32> is_depth;
|
||||
BitField<20, 1, u32> has_bias;
|
||||
BitField<21, 1, u32> has_lod_clamp;
|
||||
BitField<22, 1, u32> relaxed_precision;
|
||||
BitField<23, 2, u32> gather_component;
|
||||
BitField<25, 2, u32> num_derivates;
|
||||
BitField<27, 3, ImageFormat> image_format;
|
||||
};
|
||||
static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
|
||||
|
||||
} // namespace Shader::IR
|
15
src/shader_recompiler/frontend/ir/opcodes.cpp
Executable file
15
src/shader_recompiler/frontend/ir/opcodes.cpp
Executable file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/opcodes.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string_view NameOf(Opcode op) {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].name;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
109
src/shader_recompiler/frontend/ir/opcodes.h
Executable file
109
src/shader_recompiler/frontend/ir/opcodes.h
Executable file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Opcode {
|
||||
#define OPCODE(name, ...) name,
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
|
||||
namespace Detail {
|
||||
struct OpcodeMeta {
|
||||
std::string_view name;
|
||||
Type type;
|
||||
std::array<Type, 5> arg_types;
|
||||
};
|
||||
|
||||
// using enum Type;
|
||||
constexpr Type Void{Type::Void};
|
||||
constexpr Type Opaque{Type::Opaque};
|
||||
constexpr Type Reg{Type::Reg};
|
||||
constexpr Type Pred{Type::Pred};
|
||||
constexpr Type Attribute{Type::Attribute};
|
||||
constexpr Type Patch{Type::Patch};
|
||||
constexpr Type U1{Type::U1};
|
||||
constexpr Type U8{Type::U8};
|
||||
constexpr Type U16{Type::U16};
|
||||
constexpr Type U32{Type::U32};
|
||||
constexpr Type U64{Type::U64};
|
||||
constexpr Type F16{Type::F16};
|
||||
constexpr Type F32{Type::F32};
|
||||
constexpr Type F64{Type::F64};
|
||||
constexpr Type U32x2{Type::U32x2};
|
||||
constexpr Type U32x3{Type::U32x3};
|
||||
constexpr Type U32x4{Type::U32x4};
|
||||
constexpr Type F16x2{Type::F16x2};
|
||||
constexpr Type F16x3{Type::F16x3};
|
||||
constexpr Type F16x4{Type::F16x4};
|
||||
constexpr Type F32x2{Type::F32x2};
|
||||
constexpr Type F32x3{Type::F32x3};
|
||||
constexpr Type F32x4{Type::F32x4};
|
||||
constexpr Type F64x2{Type::F64x2};
|
||||
constexpr Type F64x3{Type::F64x3};
|
||||
constexpr Type F64x4{Type::F64x4};
|
||||
|
||||
constexpr OpcodeMeta META_TABLE[]{
|
||||
#define OPCODE(name_token, type_token, ...) \
|
||||
{ \
|
||||
.name{#name_token}, \
|
||||
.type = type_token, \
|
||||
.arg_types{__VA_ARGS__}, \
|
||||
},
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
constexpr size_t CalculateNumArgsOf(Opcode op) {
|
||||
const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
|
||||
return std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void));
|
||||
}
|
||||
|
||||
constexpr u8 NUM_ARGS[]{
|
||||
#define OPCODE(name_token, type_token, ...) static_cast<u8>(CalculateNumArgsOf(Opcode::name_token)),
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
/// Get return type of an opcode
|
||||
[[nodiscard]] inline Type TypeOf(Opcode op) noexcept {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].type;
|
||||
}
|
||||
|
||||
/// Get the number of arguments an opcode accepts
|
||||
[[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept {
|
||||
return static_cast<size_t>(Detail::NUM_ARGS[static_cast<size_t>(op)]);
|
||||
}
|
||||
|
||||
/// Get the required type of an argument of an opcode
|
||||
[[nodiscard]] inline Type ArgTypeOf(Opcode op, size_t arg_index) noexcept {
|
||||
return Detail::META_TABLE[static_cast<size_t>(op)].arg_types[arg_index];
|
||||
}
|
||||
|
||||
/// Get the name of an opcode
|
||||
[[nodiscard]] std::string_view NameOf(Opcode op);
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Opcode> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
|
||||
}
|
||||
};
|
550
src/shader_recompiler/frontend/ir/opcodes.inc
Executable file
550
src/shader_recompiler/frontend/ir/opcodes.inc
Executable file
@@ -0,0 +1,550 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, arg4 type, ...
|
||||
OPCODE(Phi, Opaque, )
|
||||
OPCODE(Identity, Opaque, Opaque, )
|
||||
OPCODE(Void, Void, )
|
||||
OPCODE(ConditionRef, U1, U1, )
|
||||
OPCODE(Reference, Void, Opaque, )
|
||||
OPCODE(PhiMove, Void, Opaque, Opaque, )
|
||||
|
||||
// Special operations
|
||||
OPCODE(Prologue, Void, )
|
||||
OPCODE(Epilogue, Void, )
|
||||
OPCODE(Join, Void, )
|
||||
OPCODE(DemoteToHelperInvocation, Void, )
|
||||
OPCODE(EmitVertex, Void, U32, )
|
||||
OPCODE(EndPrimitive, Void, U32, )
|
||||
|
||||
// Barriers
|
||||
OPCODE(Barrier, Void, )
|
||||
OPCODE(WorkgroupMemoryBarrier, Void, )
|
||||
OPCODE(DeviceMemoryBarrier, Void, )
|
||||
|
||||
// Context getters/setters
|
||||
OPCODE(GetRegister, U32, Reg, )
|
||||
OPCODE(SetRegister, Void, Reg, U32, )
|
||||
OPCODE(GetPred, U1, Pred, )
|
||||
OPCODE(SetPred, Void, Pred, U1, )
|
||||
OPCODE(GetGotoVariable, U1, U32, )
|
||||
OPCODE(SetGotoVariable, Void, U32, U1, )
|
||||
OPCODE(GetIndirectBranchVariable, U32, )
|
||||
OPCODE(SetIndirectBranchVariable, Void, U32, )
|
||||
OPCODE(GetCbufU8, U32, U32, U32, )
|
||||
OPCODE(GetCbufS8, U32, U32, U32, )
|
||||
OPCODE(GetCbufU16, U32, U32, U32, )
|
||||
OPCODE(GetCbufS16, U32, U32, U32, )
|
||||
OPCODE(GetCbufU32, U32, U32, U32, )
|
||||
OPCODE(GetCbufF32, F32, U32, U32, )
|
||||
OPCODE(GetCbufU32x2, U32x2, U32, U32, )
|
||||
OPCODE(GetAttribute, F32, Attribute, U32, )
|
||||
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
|
||||
OPCODE(GetAttributeIndexed, F32, U32, U32, )
|
||||
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
|
||||
OPCODE(GetPatch, F32, Patch, )
|
||||
OPCODE(SetPatch, Void, Patch, F32, )
|
||||
OPCODE(SetFragColor, Void, U32, U32, F32, )
|
||||
OPCODE(SetSampleMask, Void, U32, )
|
||||
OPCODE(SetFragDepth, Void, F32, )
|
||||
OPCODE(GetZFlag, U1, Void, )
|
||||
OPCODE(GetSFlag, U1, Void, )
|
||||
OPCODE(GetCFlag, U1, Void, )
|
||||
OPCODE(GetOFlag, U1, Void, )
|
||||
OPCODE(SetZFlag, Void, U1, )
|
||||
OPCODE(SetSFlag, Void, U1, )
|
||||
OPCODE(SetCFlag, Void, U1, )
|
||||
OPCODE(SetOFlag, Void, U1, )
|
||||
OPCODE(WorkgroupId, U32x3, )
|
||||
OPCODE(LocalInvocationId, U32x3, )
|
||||
OPCODE(InvocationId, U32, )
|
||||
OPCODE(SampleId, U32, )
|
||||
OPCODE(IsHelperInvocation, U1, )
|
||||
OPCODE(YDirection, F32, )
|
||||
|
||||
// Undefined
|
||||
OPCODE(UndefU1, U1, )
|
||||
OPCODE(UndefU8, U8, )
|
||||
OPCODE(UndefU16, U16, )
|
||||
OPCODE(UndefU32, U32, )
|
||||
OPCODE(UndefU64, U64, )
|
||||
|
||||
// Memory operations
|
||||
OPCODE(LoadGlobalU8, U32, Opaque, )
|
||||
OPCODE(LoadGlobalS8, U32, Opaque, )
|
||||
OPCODE(LoadGlobalU16, U32, Opaque, )
|
||||
OPCODE(LoadGlobalS16, U32, Opaque, )
|
||||
OPCODE(LoadGlobal32, U32, Opaque, )
|
||||
OPCODE(LoadGlobal64, U32x2, Opaque, )
|
||||
OPCODE(LoadGlobal128, U32x4, Opaque, )
|
||||
OPCODE(WriteGlobalU8, Void, Opaque, U32, )
|
||||
OPCODE(WriteGlobalS8, Void, Opaque, U32, )
|
||||
OPCODE(WriteGlobalU16, Void, Opaque, U32, )
|
||||
OPCODE(WriteGlobalS16, Void, Opaque, U32, )
|
||||
OPCODE(WriteGlobal32, Void, Opaque, U32, )
|
||||
OPCODE(WriteGlobal64, Void, Opaque, U32x2, )
|
||||
OPCODE(WriteGlobal128, Void, Opaque, U32x4, )
|
||||
|
||||
// Storage buffer operations
|
||||
OPCODE(LoadStorageU8, U32, U32, U32, )
|
||||
OPCODE(LoadStorageS8, U32, U32, U32, )
|
||||
OPCODE(LoadStorageU16, U32, U32, U32, )
|
||||
OPCODE(LoadStorageS16, U32, U32, U32, )
|
||||
OPCODE(LoadStorage32, U32, U32, U32, )
|
||||
OPCODE(LoadStorage64, U32x2, U32, U32, )
|
||||
OPCODE(LoadStorage128, U32x4, U32, U32, )
|
||||
OPCODE(WriteStorageU8, Void, U32, U32, U32, )
|
||||
OPCODE(WriteStorageS8, Void, U32, U32, U32, )
|
||||
OPCODE(WriteStorageU16, Void, U32, U32, U32, )
|
||||
OPCODE(WriteStorageS16, Void, U32, U32, U32, )
|
||||
OPCODE(WriteStorage32, Void, U32, U32, U32, )
|
||||
OPCODE(WriteStorage64, Void, U32, U32, U32x2, )
|
||||
OPCODE(WriteStorage128, Void, U32, U32, U32x4, )
|
||||
|
||||
// Local memory operations
|
||||
OPCODE(LoadLocal, U32, U32, )
|
||||
OPCODE(WriteLocal, Void, U32, U32, )
|
||||
|
||||
// Shared memory operations
|
||||
OPCODE(LoadSharedU8, U32, U32, )
|
||||
OPCODE(LoadSharedS8, U32, U32, )
|
||||
OPCODE(LoadSharedU16, U32, U32, )
|
||||
OPCODE(LoadSharedS16, U32, U32, )
|
||||
OPCODE(LoadSharedU32, U32, U32, )
|
||||
OPCODE(LoadSharedU64, U32x2, U32, )
|
||||
OPCODE(LoadSharedU128, U32x4, U32, )
|
||||
OPCODE(WriteSharedU8, Void, U32, U32, )
|
||||
OPCODE(WriteSharedU16, Void, U32, U32, )
|
||||
OPCODE(WriteSharedU32, Void, U32, U32, )
|
||||
OPCODE(WriteSharedU64, Void, U32, U32x2, )
|
||||
OPCODE(WriteSharedU128, Void, U32, U32x4, )
|
||||
|
||||
// Vector utility
|
||||
OPCODE(CompositeConstructU32x2, U32x2, U32, U32, )
|
||||
OPCODE(CompositeConstructU32x3, U32x3, U32, U32, U32, )
|
||||
OPCODE(CompositeConstructU32x4, U32x4, U32, U32, U32, U32, )
|
||||
OPCODE(CompositeExtractU32x2, U32, U32x2, U32, )
|
||||
OPCODE(CompositeExtractU32x3, U32, U32x3, U32, )
|
||||
OPCODE(CompositeExtractU32x4, U32, U32x4, U32, )
|
||||
OPCODE(CompositeInsertU32x2, U32x2, U32x2, U32, U32, )
|
||||
OPCODE(CompositeInsertU32x3, U32x3, U32x3, U32, U32, )
|
||||
OPCODE(CompositeInsertU32x4, U32x4, U32x4, U32, U32, )
|
||||
OPCODE(CompositeConstructF16x2, F16x2, F16, F16, )
|
||||
OPCODE(CompositeConstructF16x3, F16x3, F16, F16, F16, )
|
||||
OPCODE(CompositeConstructF16x4, F16x4, F16, F16, F16, F16, )
|
||||
OPCODE(CompositeExtractF16x2, F16, F16x2, U32, )
|
||||
OPCODE(CompositeExtractF16x3, F16, F16x3, U32, )
|
||||
OPCODE(CompositeExtractF16x4, F16, F16x4, U32, )
|
||||
OPCODE(CompositeInsertF16x2, F16x2, F16x2, F16, U32, )
|
||||
OPCODE(CompositeInsertF16x3, F16x3, F16x3, F16, U32, )
|
||||
OPCODE(CompositeInsertF16x4, F16x4, F16x4, F16, U32, )
|
||||
OPCODE(CompositeConstructF32x2, F32x2, F32, F32, )
|
||||
OPCODE(CompositeConstructF32x3, F32x3, F32, F32, F32, )
|
||||
OPCODE(CompositeConstructF32x4, F32x4, F32, F32, F32, F32, )
|
||||
OPCODE(CompositeExtractF32x2, F32, F32x2, U32, )
|
||||
OPCODE(CompositeExtractF32x3, F32, F32x3, U32, )
|
||||
OPCODE(CompositeExtractF32x4, F32, F32x4, U32, )
|
||||
OPCODE(CompositeInsertF32x2, F32x2, F32x2, F32, U32, )
|
||||
OPCODE(CompositeInsertF32x3, F32x3, F32x3, F32, U32, )
|
||||
OPCODE(CompositeInsertF32x4, F32x4, F32x4, F32, U32, )
|
||||
OPCODE(CompositeConstructF64x2, F64x2, F64, F64, )
|
||||
OPCODE(CompositeConstructF64x3, F64x3, F64, F64, F64, )
|
||||
OPCODE(CompositeConstructF64x4, F64x4, F64, F64, F64, F64, )
|
||||
OPCODE(CompositeExtractF64x2, F64, F64x2, U32, )
|
||||
OPCODE(CompositeExtractF64x3, F64, F64x3, U32, )
|
||||
OPCODE(CompositeExtractF64x4, F64, F64x4, U32, )
|
||||
OPCODE(CompositeInsertF64x2, F64x2, F64x2, F64, U32, )
|
||||
OPCODE(CompositeInsertF64x3, F64x3, F64x3, F64, U32, )
|
||||
OPCODE(CompositeInsertF64x4, F64x4, F64x4, F64, U32, )
|
||||
|
||||
// Select operations
|
||||
OPCODE(SelectU1, U1, U1, U1, U1, )
|
||||
OPCODE(SelectU8, U8, U1, U8, U8, )
|
||||
OPCODE(SelectU16, U16, U1, U16, U16, )
|
||||
OPCODE(SelectU32, U32, U1, U32, U32, )
|
||||
OPCODE(SelectU64, U64, U1, U64, U64, )
|
||||
OPCODE(SelectF16, F16, U1, F16, F16, )
|
||||
OPCODE(SelectF32, F32, U1, F32, F32, )
|
||||
OPCODE(SelectF64, F64, U1, F64, F64, )
|
||||
|
||||
// Bitwise conversions
|
||||
OPCODE(BitCastU16F16, U16, F16, )
|
||||
OPCODE(BitCastU32F32, U32, F32, )
|
||||
OPCODE(BitCastU64F64, U64, F64, )
|
||||
OPCODE(BitCastF16U16, F16, U16, )
|
||||
OPCODE(BitCastF32U32, F32, U32, )
|
||||
OPCODE(BitCastF64U64, F64, U64, )
|
||||
OPCODE(PackUint2x32, U64, U32x2, )
|
||||
OPCODE(UnpackUint2x32, U32x2, U64, )
|
||||
OPCODE(PackFloat2x16, U32, F16x2, )
|
||||
OPCODE(UnpackFloat2x16, F16x2, U32, )
|
||||
OPCODE(PackHalf2x16, U32, F32x2, )
|
||||
OPCODE(UnpackHalf2x16, F32x2, U32, )
|
||||
OPCODE(PackDouble2x32, F64, U32x2, )
|
||||
OPCODE(UnpackDouble2x32, U32x2, F64, )
|
||||
|
||||
// Pseudo-operation, handled specially at final emit
|
||||
OPCODE(GetZeroFromOp, U1, Opaque, )
|
||||
OPCODE(GetSignFromOp, U1, Opaque, )
|
||||
OPCODE(GetCarryFromOp, U1, Opaque, )
|
||||
OPCODE(GetOverflowFromOp, U1, Opaque, )
|
||||
OPCODE(GetSparseFromOp, U1, Opaque, )
|
||||
OPCODE(GetInBoundsFromOp, U1, Opaque, )
|
||||
|
||||
// Floating-point operations
|
||||
OPCODE(FPAbs16, F16, F16, )
|
||||
OPCODE(FPAbs32, F32, F32, )
|
||||
OPCODE(FPAbs64, F64, F64, )
|
||||
OPCODE(FPAdd16, F16, F16, F16, )
|
||||
OPCODE(FPAdd32, F32, F32, F32, )
|
||||
OPCODE(FPAdd64, F64, F64, F64, )
|
||||
OPCODE(FPFma16, F16, F16, F16, F16, )
|
||||
OPCODE(FPFma32, F32, F32, F32, F32, )
|
||||
OPCODE(FPFma64, F64, F64, F64, F64, )
|
||||
OPCODE(FPMax32, F32, F32, F32, )
|
||||
OPCODE(FPMax64, F64, F64, F64, )
|
||||
OPCODE(FPMin32, F32, F32, F32, )
|
||||
OPCODE(FPMin64, F64, F64, F64, )
|
||||
OPCODE(FPMul16, F16, F16, F16, )
|
||||
OPCODE(FPMul32, F32, F32, F32, )
|
||||
OPCODE(FPMul64, F64, F64, F64, )
|
||||
OPCODE(FPNeg16, F16, F16, )
|
||||
OPCODE(FPNeg32, F32, F32, )
|
||||
OPCODE(FPNeg64, F64, F64, )
|
||||
OPCODE(FPRecip32, F32, F32, )
|
||||
OPCODE(FPRecip64, F64, F64, )
|
||||
OPCODE(FPRecipSqrt32, F32, F32, )
|
||||
OPCODE(FPRecipSqrt64, F64, F64, )
|
||||
OPCODE(FPSqrt, F32, F32, )
|
||||
OPCODE(FPSin, F32, F32, )
|
||||
OPCODE(FPExp2, F32, F32, )
|
||||
OPCODE(FPCos, F32, F32, )
|
||||
OPCODE(FPLog2, F32, F32, )
|
||||
OPCODE(FPSaturate16, F16, F16, )
|
||||
OPCODE(FPSaturate32, F32, F32, )
|
||||
OPCODE(FPSaturate64, F64, F64, )
|
||||
OPCODE(FPClamp16, F16, F16, F16, F16, )
|
||||
OPCODE(FPClamp32, F32, F32, F32, F32, )
|
||||
OPCODE(FPClamp64, F64, F64, F64, F64, )
|
||||
OPCODE(FPRoundEven16, F16, F16, )
|
||||
OPCODE(FPRoundEven32, F32, F32, )
|
||||
OPCODE(FPRoundEven64, F64, F64, )
|
||||
OPCODE(FPFloor16, F16, F16, )
|
||||
OPCODE(FPFloor32, F32, F32, )
|
||||
OPCODE(FPFloor64, F64, F64, )
|
||||
OPCODE(FPCeil16, F16, F16, )
|
||||
OPCODE(FPCeil32, F32, F32, )
|
||||
OPCODE(FPCeil64, F64, F64, )
|
||||
OPCODE(FPTrunc16, F16, F16, )
|
||||
OPCODE(FPTrunc32, F32, F32, )
|
||||
OPCODE(FPTrunc64, F64, F64, )
|
||||
|
||||
OPCODE(FPOrdEqual16, U1, F16, F16, )
|
||||
OPCODE(FPOrdEqual32, U1, F32, F32, )
|
||||
OPCODE(FPOrdEqual64, U1, F64, F64, )
|
||||
OPCODE(FPUnordEqual16, U1, F16, F16, )
|
||||
OPCODE(FPUnordEqual32, U1, F32, F32, )
|
||||
OPCODE(FPUnordEqual64, U1, F64, F64, )
|
||||
OPCODE(FPOrdNotEqual16, U1, F16, F16, )
|
||||
OPCODE(FPOrdNotEqual32, U1, F32, F32, )
|
||||
OPCODE(FPOrdNotEqual64, U1, F64, F64, )
|
||||
OPCODE(FPUnordNotEqual16, U1, F16, F16, )
|
||||
OPCODE(FPUnordNotEqual32, U1, F32, F32, )
|
||||
OPCODE(FPUnordNotEqual64, U1, F64, F64, )
|
||||
OPCODE(FPOrdLessThan16, U1, F16, F16, )
|
||||
OPCODE(FPOrdLessThan32, U1, F32, F32, )
|
||||
OPCODE(FPOrdLessThan64, U1, F64, F64, )
|
||||
OPCODE(FPUnordLessThan16, U1, F16, F16, )
|
||||
OPCODE(FPUnordLessThan32, U1, F32, F32, )
|
||||
OPCODE(FPUnordLessThan64, U1, F64, F64, )
|
||||
OPCODE(FPOrdGreaterThan16, U1, F16, F16, )
|
||||
OPCODE(FPOrdGreaterThan32, U1, F32, F32, )
|
||||
OPCODE(FPOrdGreaterThan64, U1, F64, F64, )
|
||||
OPCODE(FPUnordGreaterThan16, U1, F16, F16, )
|
||||
OPCODE(FPUnordGreaterThan32, U1, F32, F32, )
|
||||
OPCODE(FPUnordGreaterThan64, U1, F64, F64, )
|
||||
OPCODE(FPOrdLessThanEqual16, U1, F16, F16, )
|
||||
OPCODE(FPOrdLessThanEqual32, U1, F32, F32, )
|
||||
OPCODE(FPOrdLessThanEqual64, U1, F64, F64, )
|
||||
OPCODE(FPUnordLessThanEqual16, U1, F16, F16, )
|
||||
OPCODE(FPUnordLessThanEqual32, U1, F32, F32, )
|
||||
OPCODE(FPUnordLessThanEqual64, U1, F64, F64, )
|
||||
OPCODE(FPOrdGreaterThanEqual16, U1, F16, F16, )
|
||||
OPCODE(FPOrdGreaterThanEqual32, U1, F32, F32, )
|
||||
OPCODE(FPOrdGreaterThanEqual64, U1, F64, F64, )
|
||||
OPCODE(FPUnordGreaterThanEqual16, U1, F16, F16, )
|
||||
OPCODE(FPUnordGreaterThanEqual32, U1, F32, F32, )
|
||||
OPCODE(FPUnordGreaterThanEqual64, U1, F64, F64, )
|
||||
OPCODE(FPIsNan16, U1, F16, )
|
||||
OPCODE(FPIsNan32, U1, F32, )
|
||||
OPCODE(FPIsNan64, U1, F64, )
|
||||
|
||||
// Integer operations
|
||||
OPCODE(IAdd32, U32, U32, U32, )
|
||||
OPCODE(IAdd64, U64, U64, U64, )
|
||||
OPCODE(ISub32, U32, U32, U32, )
|
||||
OPCODE(ISub64, U64, U64, U64, )
|
||||
OPCODE(IMul32, U32, U32, U32, )
|
||||
OPCODE(INeg32, U32, U32, )
|
||||
OPCODE(INeg64, U64, U64, )
|
||||
OPCODE(IAbs32, U32, U32, )
|
||||
OPCODE(ShiftLeftLogical32, U32, U32, U32, )
|
||||
OPCODE(ShiftLeftLogical64, U64, U64, U32, )
|
||||
OPCODE(ShiftRightLogical32, U32, U32, U32, )
|
||||
OPCODE(ShiftRightLogical64, U64, U64, U32, )
|
||||
OPCODE(ShiftRightArithmetic32, U32, U32, U32, )
|
||||
OPCODE(ShiftRightArithmetic64, U64, U64, U32, )
|
||||
OPCODE(BitwiseAnd32, U32, U32, U32, )
|
||||
OPCODE(BitwiseOr32, U32, U32, U32, )
|
||||
OPCODE(BitwiseXor32, U32, U32, U32, )
|
||||
OPCODE(BitFieldInsert, U32, U32, U32, U32, U32, )
|
||||
OPCODE(BitFieldSExtract, U32, U32, U32, U32, )
|
||||
OPCODE(BitFieldUExtract, U32, U32, U32, U32, )
|
||||
OPCODE(BitReverse32, U32, U32, )
|
||||
OPCODE(BitCount32, U32, U32, )
|
||||
OPCODE(BitwiseNot32, U32, U32, )
|
||||
|
||||
OPCODE(FindSMsb32, U32, U32, )
|
||||
OPCODE(FindUMsb32, U32, U32, )
|
||||
OPCODE(SMin32, U32, U32, U32, )
|
||||
OPCODE(UMin32, U32, U32, U32, )
|
||||
OPCODE(SMax32, U32, U32, U32, )
|
||||
OPCODE(UMax32, U32, U32, U32, )
|
||||
OPCODE(SClamp32, U32, U32, U32, U32, )
|
||||
OPCODE(UClamp32, U32, U32, U32, U32, )
|
||||
OPCODE(SLessThan, U1, U32, U32, )
|
||||
OPCODE(ULessThan, U1, U32, U32, )
|
||||
OPCODE(IEqual, U1, U32, U32, )
|
||||
OPCODE(SLessThanEqual, U1, U32, U32, )
|
||||
OPCODE(ULessThanEqual, U1, U32, U32, )
|
||||
OPCODE(SGreaterThan, U1, U32, U32, )
|
||||
OPCODE(UGreaterThan, U1, U32, U32, )
|
||||
OPCODE(INotEqual, U1, U32, U32, )
|
||||
OPCODE(SGreaterThanEqual, U1, U32, U32, )
|
||||
OPCODE(UGreaterThanEqual, U1, U32, U32, )
|
||||
|
||||
// Atomic operations
|
||||
OPCODE(SharedAtomicIAdd32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicSMin32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicUMin32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicSMax32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicUMax32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicInc32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicDec32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicAnd32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicOr32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicXor32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicExchange32, U32, U32, U32, )
|
||||
OPCODE(SharedAtomicExchange64, U64, U32, U64, )
|
||||
|
||||
OPCODE(GlobalAtomicIAdd32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicSMin32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicUMin32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicSMax32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicUMax32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicInc32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicDec32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicAnd32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicOr32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicXor32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicExchange32, U32, U64, U32, )
|
||||
OPCODE(GlobalAtomicIAdd64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicSMin64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicUMin64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicSMax64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicUMax64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicAnd64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicOr64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicXor64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicExchange64, U64, U64, U64, )
|
||||
OPCODE(GlobalAtomicAddF32, F32, U64, F32, )
|
||||
OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, )
|
||||
OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, )
|
||||
OPCODE(GlobalAtomicMinF16x2, U32, U64, F16x2, )
|
||||
OPCODE(GlobalAtomicMinF32x2, U32, U64, F32x2, )
|
||||
OPCODE(GlobalAtomicMaxF16x2, U32, U64, F16x2, )
|
||||
OPCODE(GlobalAtomicMaxF32x2, U32, U64, F32x2, )
|
||||
|
||||
OPCODE(StorageAtomicIAdd32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicSMin32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicUMin32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicSMax32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicUMax32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicInc32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicDec32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicAnd32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicOr32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicXor32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicExchange32, U32, U32, U32, U32, )
|
||||
OPCODE(StorageAtomicIAdd64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicSMin64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicUMin64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicSMax64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicUMax64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicAnd64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicOr64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicXor64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, )
|
||||
OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, )
|
||||
OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, )
|
||||
OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, )
|
||||
OPCODE(StorageAtomicMinF16x2, U32, U32, U32, F16x2, )
|
||||
OPCODE(StorageAtomicMinF32x2, U32, U32, U32, F32x2, )
|
||||
OPCODE(StorageAtomicMaxF16x2, U32, U32, U32, F16x2, )
|
||||
OPCODE(StorageAtomicMaxF32x2, U32, U32, U32, F32x2, )
|
||||
|
||||
// Logical operations
|
||||
OPCODE(LogicalOr, U1, U1, U1, )
|
||||
OPCODE(LogicalAnd, U1, U1, U1, )
|
||||
OPCODE(LogicalXor, U1, U1, U1, )
|
||||
OPCODE(LogicalNot, U1, U1, )
|
||||
|
||||
// Conversion operations
|
||||
OPCODE(ConvertS16F16, U32, F16, )
|
||||
OPCODE(ConvertS16F32, U32, F32, )
|
||||
OPCODE(ConvertS16F64, U32, F64, )
|
||||
OPCODE(ConvertS32F16, U32, F16, )
|
||||
OPCODE(ConvertS32F32, U32, F32, )
|
||||
OPCODE(ConvertS32F64, U32, F64, )
|
||||
OPCODE(ConvertS64F16, U64, F16, )
|
||||
OPCODE(ConvertS64F32, U64, F32, )
|
||||
OPCODE(ConvertS64F64, U64, F64, )
|
||||
OPCODE(ConvertU16F16, U32, F16, )
|
||||
OPCODE(ConvertU16F32, U32, F32, )
|
||||
OPCODE(ConvertU16F64, U32, F64, )
|
||||
OPCODE(ConvertU32F16, U32, F16, )
|
||||
OPCODE(ConvertU32F32, U32, F32, )
|
||||
OPCODE(ConvertU32F64, U32, F64, )
|
||||
OPCODE(ConvertU64F16, U64, F16, )
|
||||
OPCODE(ConvertU64F32, U64, F32, )
|
||||
OPCODE(ConvertU64F64, U64, F64, )
|
||||
OPCODE(ConvertU64U32, U64, U32, )
|
||||
OPCODE(ConvertU32U64, U32, U64, )
|
||||
OPCODE(ConvertF16F32, F16, F32, )
|
||||
OPCODE(ConvertF32F16, F32, F16, )
|
||||
OPCODE(ConvertF32F64, F32, F64, )
|
||||
OPCODE(ConvertF64F32, F64, F32, )
|
||||
OPCODE(ConvertF16S8, F16, U32, )
|
||||
OPCODE(ConvertF16S16, F16, U32, )
|
||||
OPCODE(ConvertF16S32, F16, U32, )
|
||||
OPCODE(ConvertF16S64, F16, U64, )
|
||||
OPCODE(ConvertF16U8, F16, U32, )
|
||||
OPCODE(ConvertF16U16, F16, U32, )
|
||||
OPCODE(ConvertF16U32, F16, U32, )
|
||||
OPCODE(ConvertF16U64, F16, U64, )
|
||||
OPCODE(ConvertF32S8, F32, U32, )
|
||||
OPCODE(ConvertF32S16, F32, U32, )
|
||||
OPCODE(ConvertF32S32, F32, U32, )
|
||||
OPCODE(ConvertF32S64, F32, U64, )
|
||||
OPCODE(ConvertF32U8, F32, U32, )
|
||||
OPCODE(ConvertF32U16, F32, U32, )
|
||||
OPCODE(ConvertF32U32, F32, U32, )
|
||||
OPCODE(ConvertF32U64, F32, U64, )
|
||||
OPCODE(ConvertF64S8, F64, U32, )
|
||||
OPCODE(ConvertF64S16, F64, U32, )
|
||||
OPCODE(ConvertF64S32, F64, U32, )
|
||||
OPCODE(ConvertF64S64, F64, U64, )
|
||||
OPCODE(ConvertF64U8, F64, U32, )
|
||||
OPCODE(ConvertF64U16, F64, U32, )
|
||||
OPCODE(ConvertF64U32, F64, U32, )
|
||||
OPCODE(ConvertF64U64, F64, U64, )
|
||||
|
||||
// Image operations
|
||||
OPCODE(BindlessImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BindlessImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BindlessImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, )
|
||||
OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, )
|
||||
OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
|
||||
OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
|
||||
OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, )
|
||||
OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, )
|
||||
OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BindlessImageRead, U32x4, U32, Opaque, )
|
||||
OPCODE(BindlessImageWrite, Void, U32, Opaque, U32x4, )
|
||||
|
||||
OPCODE(BoundImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BoundImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BoundImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, )
|
||||
OPCODE(BoundImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, )
|
||||
OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
|
||||
OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
|
||||
OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, )
|
||||
OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, )
|
||||
OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
|
||||
OPCODE(BoundImageRead, U32x4, U32, Opaque, )
|
||||
OPCODE(BoundImageWrite, Void, U32, Opaque, U32x4, )
|
||||
|
||||
OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
|
||||
OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
|
||||
OPCODE(ImageSampleDrefImplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, )
|
||||
OPCODE(ImageSampleDrefExplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, )
|
||||
OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
|
||||
OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
|
||||
OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
|
||||
OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, )
|
||||
OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
|
||||
OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
|
||||
OPCODE(ImageRead, U32x4, Opaque, Opaque, )
|
||||
OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, )
|
||||
|
||||
// Atomic Image operations
|
||||
|
||||
OPCODE(BindlessImageAtomicIAdd32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicSMin32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicUMin32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicSMax32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicUMax32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicInc32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicDec32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicAnd32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicOr32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicXor32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BindlessImageAtomicExchange32, U32, U32, Opaque, U32, )
|
||||
|
||||
OPCODE(BoundImageAtomicIAdd32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicSMin32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicUMin32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicSMax32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicUMax32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicInc32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicDec32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicAnd32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicOr32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicXor32, U32, U32, Opaque, U32, )
|
||||
OPCODE(BoundImageAtomicExchange32, U32, U32, Opaque, U32, )
|
||||
|
||||
OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicSMin32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, )
|
||||
|
||||
// Warp operations
|
||||
OPCODE(LaneId, U32, )
|
||||
OPCODE(VoteAll, U1, U1, )
|
||||
OPCODE(VoteAny, U1, U1, )
|
||||
OPCODE(VoteEqual, U1, U1, )
|
||||
OPCODE(SubgroupBallot, U32, U1, )
|
||||
OPCODE(SubgroupEqMask, U32, )
|
||||
OPCODE(SubgroupLtMask, U32, )
|
||||
OPCODE(SubgroupLeMask, U32, )
|
||||
OPCODE(SubgroupGtMask, U32, )
|
||||
OPCODE(SubgroupGeMask, U32, )
|
||||
OPCODE(ShuffleIndex, U32, U32, U32, U32, U32, )
|
||||
OPCODE(ShuffleUp, U32, U32, U32, U32, U32, )
|
||||
OPCODE(ShuffleDown, U32, U32, U32, U32, U32, )
|
||||
OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, )
|
||||
OPCODE(FSwizzleAdd, F32, F32, F32, U32, )
|
||||
OPCODE(DPdxFine, F32, F32, )
|
||||
OPCODE(DPdyFine, F32, F32, )
|
||||
OPCODE(DPdxCoarse, F32, F32, )
|
||||
OPCODE(DPdyCoarse, F32, F32, )
|
28
src/shader_recompiler/frontend/ir/patch.cpp
Executable file
28
src/shader_recompiler/frontend/ir/patch.cpp
Executable file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/patch.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
bool IsGeneric(Patch patch) noexcept {
|
||||
return patch >= Patch::Component0 && patch <= Patch::Component119;
|
||||
}
|
||||
|
||||
u32 GenericPatchIndex(Patch patch) {
|
||||
if (!IsGeneric(patch)) {
|
||||
throw InvalidArgument("Patch {} is not generic", patch);
|
||||
}
|
||||
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) / 4;
|
||||
}
|
||||
|
||||
u32 GenericPatchElement(Patch patch) {
|
||||
if (!IsGeneric(patch)) {
|
||||
throw InvalidArgument("Patch {} is not generic", patch);
|
||||
}
|
||||
return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) % 4;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
149
src/shader_recompiler/frontend/ir/patch.h
Executable file
149
src/shader_recompiler/frontend/ir/patch.h
Executable file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Patch : u64 {
|
||||
TessellationLodLeft,
|
||||
TessellationLodTop,
|
||||
TessellationLodRight,
|
||||
TessellationLodBottom,
|
||||
TessellationLodInteriorU,
|
||||
TessellationLodInteriorV,
|
||||
ComponentPadding0,
|
||||
ComponentPadding1,
|
||||
Component0,
|
||||
Component1,
|
||||
Component2,
|
||||
Component3,
|
||||
Component4,
|
||||
Component5,
|
||||
Component6,
|
||||
Component7,
|
||||
Component8,
|
||||
Component9,
|
||||
Component10,
|
||||
Component11,
|
||||
Component12,
|
||||
Component13,
|
||||
Component14,
|
||||
Component15,
|
||||
Component16,
|
||||
Component17,
|
||||
Component18,
|
||||
Component19,
|
||||
Component20,
|
||||
Component21,
|
||||
Component22,
|
||||
Component23,
|
||||
Component24,
|
||||
Component25,
|
||||
Component26,
|
||||
Component27,
|
||||
Component28,
|
||||
Component29,
|
||||
Component30,
|
||||
Component31,
|
||||
Component32,
|
||||
Component33,
|
||||
Component34,
|
||||
Component35,
|
||||
Component36,
|
||||
Component37,
|
||||
Component38,
|
||||
Component39,
|
||||
Component40,
|
||||
Component41,
|
||||
Component42,
|
||||
Component43,
|
||||
Component44,
|
||||
Component45,
|
||||
Component46,
|
||||
Component47,
|
||||
Component48,
|
||||
Component49,
|
||||
Component50,
|
||||
Component51,
|
||||
Component52,
|
||||
Component53,
|
||||
Component54,
|
||||
Component55,
|
||||
Component56,
|
||||
Component57,
|
||||
Component58,
|
||||
Component59,
|
||||
Component60,
|
||||
Component61,
|
||||
Component62,
|
||||
Component63,
|
||||
Component64,
|
||||
Component65,
|
||||
Component66,
|
||||
Component67,
|
||||
Component68,
|
||||
Component69,
|
||||
Component70,
|
||||
Component71,
|
||||
Component72,
|
||||
Component73,
|
||||
Component74,
|
||||
Component75,
|
||||
Component76,
|
||||
Component77,
|
||||
Component78,
|
||||
Component79,
|
||||
Component80,
|
||||
Component81,
|
||||
Component82,
|
||||
Component83,
|
||||
Component84,
|
||||
Component85,
|
||||
Component86,
|
||||
Component87,
|
||||
Component88,
|
||||
Component89,
|
||||
Component90,
|
||||
Component91,
|
||||
Component92,
|
||||
Component93,
|
||||
Component94,
|
||||
Component95,
|
||||
Component96,
|
||||
Component97,
|
||||
Component98,
|
||||
Component99,
|
||||
Component100,
|
||||
Component101,
|
||||
Component102,
|
||||
Component103,
|
||||
Component104,
|
||||
Component105,
|
||||
Component106,
|
||||
Component107,
|
||||
Component108,
|
||||
Component109,
|
||||
Component110,
|
||||
Component111,
|
||||
Component112,
|
||||
Component113,
|
||||
Component114,
|
||||
Component115,
|
||||
Component116,
|
||||
Component117,
|
||||
Component118,
|
||||
Component119,
|
||||
};
|
||||
static_assert(static_cast<u64>(Patch::Component119) == 127);
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericPatchIndex(Patch patch);
|
||||
|
||||
[[nodiscard]] u32 GenericPatchElement(Patch patch);
|
||||
|
||||
} // namespace Shader::IR
|
46
src/shader_recompiler/frontend/ir/post_order.cpp
Executable file
46
src/shader_recompiler/frontend/ir/post_order.cpp
Executable file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/post_order.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
BlockList PostOrder(const AbstractSyntaxNode& root) {
|
||||
boost::container::small_vector<Block*, 16> block_stack;
|
||||
boost::container::flat_set<Block*> visited;
|
||||
BlockList post_order_blocks;
|
||||
|
||||
if (root.type != AbstractSyntaxNode::Type::Block) {
|
||||
throw LogicError("First node in abstract syntax list root is not a block");
|
||||
}
|
||||
Block* const first_block{root.data.block};
|
||||
visited.insert(first_block);
|
||||
block_stack.push_back(first_block);
|
||||
|
||||
while (!block_stack.empty()) {
|
||||
Block* const block{block_stack.back()};
|
||||
const auto visit{[&](Block* branch) {
|
||||
if (!visited.insert(branch).second) {
|
||||
return false;
|
||||
}
|
||||
// Calling push_back twice is faster than insert on MSVC
|
||||
block_stack.push_back(block);
|
||||
block_stack.push_back(branch);
|
||||
return true;
|
||||
}};
|
||||
block_stack.pop_back();
|
||||
if (std::ranges::none_of(block->ImmSuccessors(), visit)) {
|
||||
post_order_blocks.push_back(block);
|
||||
}
|
||||
}
|
||||
return post_order_blocks;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
14
src/shader_recompiler/frontend/ir/post_order.h
Executable file
14
src/shader_recompiler/frontend/ir/post_order.h
Executable file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
BlockList PostOrder(const AbstractSyntaxNode& root);
|
||||
|
||||
} // namespace Shader::IR
|
44
src/shader_recompiler/frontend/ir/pred.h
Executable file
44
src/shader_recompiler/frontend/ir/pred.h
Executable file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Pred : u64 {
|
||||
P0,
|
||||
P1,
|
||||
P2,
|
||||
P3,
|
||||
P4,
|
||||
P5,
|
||||
P6,
|
||||
PT,
|
||||
};
|
||||
|
||||
constexpr size_t NUM_USER_PREDS = 7;
|
||||
constexpr size_t NUM_PREDS = 8;
|
||||
|
||||
[[nodiscard]] constexpr size_t PredIndex(Pred pred) noexcept {
|
||||
return static_cast<size_t>(pred);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Pred> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Pred& pred, FormatContext& ctx) {
|
||||
if (pred == Shader::IR::Pred::PT) {
|
||||
return fmt::format_to(ctx.out(), "PT");
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred));
|
||||
}
|
||||
}
|
||||
};
|
32
src/shader_recompiler/frontend/ir/program.cpp
Executable file
32
src/shader_recompiler/frontend/ir/program.cpp
Executable file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string DumpProgram(const Program& program) {
|
||||
size_t index{0};
|
||||
std::map<const IR::Inst*, size_t> inst_to_index;
|
||||
std::map<const IR::Block*, size_t> block_to_index;
|
||||
|
||||
for (const IR::Block* const block : program.blocks) {
|
||||
block_to_index.emplace(block, index);
|
||||
++index;
|
||||
}
|
||||
std::string ret;
|
||||
for (const auto& block : program.blocks) {
|
||||
ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
35
src/shader_recompiler/frontend/ir/program.h
Executable file
35
src/shader_recompiler/frontend/ir/program.h
Executable file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/program_header.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
struct Program {
|
||||
AbstractSyntaxList syntax_list;
|
||||
BlockList blocks;
|
||||
BlockList post_order_blocks;
|
||||
Info info;
|
||||
Stage stage{};
|
||||
std::array<u32, 3> workgroup_size{};
|
||||
OutputTopology output_topology{};
|
||||
u32 output_vertices{};
|
||||
u32 invocations{};
|
||||
u32 local_memory_size{};
|
||||
u32 shared_memory_size{};
|
||||
bool is_geometry_passthrough{};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string DumpProgram(const Program& program);
|
||||
|
||||
} // namespace Shader::IR
|
332
src/shader_recompiler/frontend/ir/reg.h
Executable file
332
src/shader_recompiler/frontend/ir/reg.h
Executable file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Reg : u64 {
|
||||
R0,
|
||||
R1,
|
||||
R2,
|
||||
R3,
|
||||
R4,
|
||||
R5,
|
||||
R6,
|
||||
R7,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
R16,
|
||||
R17,
|
||||
R18,
|
||||
R19,
|
||||
R20,
|
||||
R21,
|
||||
R22,
|
||||
R23,
|
||||
R24,
|
||||
R25,
|
||||
R26,
|
||||
R27,
|
||||
R28,
|
||||
R29,
|
||||
R30,
|
||||
R31,
|
||||
R32,
|
||||
R33,
|
||||
R34,
|
||||
R35,
|
||||
R36,
|
||||
R37,
|
||||
R38,
|
||||
R39,
|
||||
R40,
|
||||
R41,
|
||||
R42,
|
||||
R43,
|
||||
R44,
|
||||
R45,
|
||||
R46,
|
||||
R47,
|
||||
R48,
|
||||
R49,
|
||||
R50,
|
||||
R51,
|
||||
R52,
|
||||
R53,
|
||||
R54,
|
||||
R55,
|
||||
R56,
|
||||
R57,
|
||||
R58,
|
||||
R59,
|
||||
R60,
|
||||
R61,
|
||||
R62,
|
||||
R63,
|
||||
R64,
|
||||
R65,
|
||||
R66,
|
||||
R67,
|
||||
R68,
|
||||
R69,
|
||||
R70,
|
||||
R71,
|
||||
R72,
|
||||
R73,
|
||||
R74,
|
||||
R75,
|
||||
R76,
|
||||
R77,
|
||||
R78,
|
||||
R79,
|
||||
R80,
|
||||
R81,
|
||||
R82,
|
||||
R83,
|
||||
R84,
|
||||
R85,
|
||||
R86,
|
||||
R87,
|
||||
R88,
|
||||
R89,
|
||||
R90,
|
||||
R91,
|
||||
R92,
|
||||
R93,
|
||||
R94,
|
||||
R95,
|
||||
R96,
|
||||
R97,
|
||||
R98,
|
||||
R99,
|
||||
R100,
|
||||
R101,
|
||||
R102,
|
||||
R103,
|
||||
R104,
|
||||
R105,
|
||||
R106,
|
||||
R107,
|
||||
R108,
|
||||
R109,
|
||||
R110,
|
||||
R111,
|
||||
R112,
|
||||
R113,
|
||||
R114,
|
||||
R115,
|
||||
R116,
|
||||
R117,
|
||||
R118,
|
||||
R119,
|
||||
R120,
|
||||
R121,
|
||||
R122,
|
||||
R123,
|
||||
R124,
|
||||
R125,
|
||||
R126,
|
||||
R127,
|
||||
R128,
|
||||
R129,
|
||||
R130,
|
||||
R131,
|
||||
R132,
|
||||
R133,
|
||||
R134,
|
||||
R135,
|
||||
R136,
|
||||
R137,
|
||||
R138,
|
||||
R139,
|
||||
R140,
|
||||
R141,
|
||||
R142,
|
||||
R143,
|
||||
R144,
|
||||
R145,
|
||||
R146,
|
||||
R147,
|
||||
R148,
|
||||
R149,
|
||||
R150,
|
||||
R151,
|
||||
R152,
|
||||
R153,
|
||||
R154,
|
||||
R155,
|
||||
R156,
|
||||
R157,
|
||||
R158,
|
||||
R159,
|
||||
R160,
|
||||
R161,
|
||||
R162,
|
||||
R163,
|
||||
R164,
|
||||
R165,
|
||||
R166,
|
||||
R167,
|
||||
R168,
|
||||
R169,
|
||||
R170,
|
||||
R171,
|
||||
R172,
|
||||
R173,
|
||||
R174,
|
||||
R175,
|
||||
R176,
|
||||
R177,
|
||||
R178,
|
||||
R179,
|
||||
R180,
|
||||
R181,
|
||||
R182,
|
||||
R183,
|
||||
R184,
|
||||
R185,
|
||||
R186,
|
||||
R187,
|
||||
R188,
|
||||
R189,
|
||||
R190,
|
||||
R191,
|
||||
R192,
|
||||
R193,
|
||||
R194,
|
||||
R195,
|
||||
R196,
|
||||
R197,
|
||||
R198,
|
||||
R199,
|
||||
R200,
|
||||
R201,
|
||||
R202,
|
||||
R203,
|
||||
R204,
|
||||
R205,
|
||||
R206,
|
||||
R207,
|
||||
R208,
|
||||
R209,
|
||||
R210,
|
||||
R211,
|
||||
R212,
|
||||
R213,
|
||||
R214,
|
||||
R215,
|
||||
R216,
|
||||
R217,
|
||||
R218,
|
||||
R219,
|
||||
R220,
|
||||
R221,
|
||||
R222,
|
||||
R223,
|
||||
R224,
|
||||
R225,
|
||||
R226,
|
||||
R227,
|
||||
R228,
|
||||
R229,
|
||||
R230,
|
||||
R231,
|
||||
R232,
|
||||
R233,
|
||||
R234,
|
||||
R235,
|
||||
R236,
|
||||
R237,
|
||||
R238,
|
||||
R239,
|
||||
R240,
|
||||
R241,
|
||||
R242,
|
||||
R243,
|
||||
R244,
|
||||
R245,
|
||||
R246,
|
||||
R247,
|
||||
R248,
|
||||
R249,
|
||||
R250,
|
||||
R251,
|
||||
R252,
|
||||
R253,
|
||||
R254,
|
||||
RZ,
|
||||
};
|
||||
static_assert(static_cast<int>(Reg::RZ) == 255);
|
||||
|
||||
constexpr size_t NUM_USER_REGS = 255;
|
||||
constexpr size_t NUM_REGS = 256;
|
||||
|
||||
[[nodiscard]] constexpr Reg operator+(Reg reg, int num) {
|
||||
if (reg == Reg::RZ) {
|
||||
// Adding or subtracting registers from RZ yields RZ
|
||||
return Reg::RZ;
|
||||
}
|
||||
const int result{static_cast<int>(reg) + num};
|
||||
if (result >= static_cast<int>(Reg::RZ)) {
|
||||
throw LogicError("Overflow on register arithmetic");
|
||||
}
|
||||
if (result < 0) {
|
||||
throw LogicError("Underflow on register arithmetic");
|
||||
}
|
||||
return static_cast<Reg>(result);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Reg operator-(Reg reg, int num) {
|
||||
return reg + (-num);
|
||||
}
|
||||
|
||||
constexpr Reg operator++(Reg& reg) {
|
||||
reg = reg + 1;
|
||||
return reg;
|
||||
}
|
||||
|
||||
constexpr Reg operator++(Reg& reg, int) {
|
||||
const Reg copy{reg};
|
||||
reg = reg + 1;
|
||||
return copy;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr size_t RegIndex(Reg reg) noexcept {
|
||||
return static_cast<size_t>(reg);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) {
|
||||
return RegIndex(reg) % align == 0 || reg == Reg::RZ;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Reg> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Reg& reg, FormatContext& ctx) {
|
||||
if (reg == Shader::IR::Reg::RZ) {
|
||||
return fmt::format_to(ctx.out(), "RZ");
|
||||
} else if (static_cast<int>(reg) >= 0 && static_cast<int>(reg) < 255) {
|
||||
return fmt::format_to(ctx.out(), "R{}", static_cast<int>(reg));
|
||||
} else {
|
||||
throw Shader::LogicError("Invalid register with raw value {}", static_cast<int>(reg));
|
||||
}
|
||||
}
|
||||
};
|
38
src/shader_recompiler/frontend/ir/type.cpp
Executable file
38
src/shader_recompiler/frontend/ir/type.cpp
Executable file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
std::string NameOf(Type type) {
|
||||
static constexpr std::array names{
|
||||
"Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32",
|
||||
"U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3",
|
||||
"F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4",
|
||||
};
|
||||
const size_t bits{static_cast<size_t>(type)};
|
||||
if (bits == 0) {
|
||||
return "Void";
|
||||
}
|
||||
std::string result;
|
||||
for (size_t i = 0; i < names.size(); i++) {
|
||||
if ((bits & (size_t{1} << i)) != 0) {
|
||||
if (!result.empty()) {
|
||||
result += '|';
|
||||
}
|
||||
result += names[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AreTypesCompatible(Type lhs, Type rhs) noexcept {
|
||||
return lhs == rhs || lhs == Type::Opaque || rhs == Type::Opaque;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
61
src/shader_recompiler/frontend/ir/type.h
Executable file
61
src/shader_recompiler/frontend/ir/type.h
Executable file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
enum class Type {
|
||||
Void = 0,
|
||||
Opaque = 1 << 0,
|
||||
Reg = 1 << 1,
|
||||
Pred = 1 << 2,
|
||||
Attribute = 1 << 3,
|
||||
Patch = 1 << 4,
|
||||
U1 = 1 << 5,
|
||||
U8 = 1 << 6,
|
||||
U16 = 1 << 7,
|
||||
U32 = 1 << 8,
|
||||
U64 = 1 << 9,
|
||||
F16 = 1 << 10,
|
||||
F32 = 1 << 11,
|
||||
F64 = 1 << 12,
|
||||
U32x2 = 1 << 13,
|
||||
U32x3 = 1 << 14,
|
||||
U32x4 = 1 << 15,
|
||||
F16x2 = 1 << 16,
|
||||
F16x3 = 1 << 17,
|
||||
F16x4 = 1 << 18,
|
||||
F32x2 = 1 << 19,
|
||||
F32x3 = 1 << 20,
|
||||
F32x4 = 1 << 21,
|
||||
F64x2 = 1 << 22,
|
||||
F64x3 = 1 << 23,
|
||||
F64x4 = 1 << 24,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(Type)
|
||||
|
||||
[[nodiscard]] std::string NameOf(Type type);
|
||||
|
||||
[[nodiscard]] bool AreTypesCompatible(Type lhs, Type rhs) noexcept;
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::IR::Type> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Type& type, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "{}", NameOf(type));
|
||||
}
|
||||
};
|
99
src/shader_recompiler/frontend/ir/value.cpp
Executable file
99
src/shader_recompiler/frontend/ir/value.cpp
Executable file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/frontend/ir/opcodes.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {}
|
||||
|
||||
Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {}
|
||||
|
||||
Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
|
||||
|
||||
Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {}
|
||||
|
||||
Value::Value(IR::Patch value) noexcept : type{Type::Patch}, patch{value} {}
|
||||
|
||||
Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {}
|
||||
|
||||
Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {}
|
||||
|
||||
Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
|
||||
|
||||
Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
|
||||
|
||||
Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
|
||||
|
||||
Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
|
||||
|
||||
Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {}
|
||||
|
||||
IR::Type Value::Type() const noexcept {
|
||||
if (IsPhi()) {
|
||||
// The type of a phi node is stored in its flags
|
||||
return inst->Flags<IR::Type>();
|
||||
}
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).Type();
|
||||
}
|
||||
if (type == Type::Opaque) {
|
||||
return inst->Type();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
bool Value::operator==(const Value& other) const {
|
||||
if (type != other.type) {
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
case Type::Void:
|
||||
return true;
|
||||
case Type::Opaque:
|
||||
return inst == other.inst;
|
||||
case Type::Reg:
|
||||
return reg == other.reg;
|
||||
case Type::Pred:
|
||||
return pred == other.pred;
|
||||
case Type::Attribute:
|
||||
return attribute == other.attribute;
|
||||
case Type::Patch:
|
||||
return patch == other.patch;
|
||||
case Type::U1:
|
||||
return imm_u1 == other.imm_u1;
|
||||
case Type::U8:
|
||||
return imm_u8 == other.imm_u8;
|
||||
case Type::U16:
|
||||
case Type::F16:
|
||||
return imm_u16 == other.imm_u16;
|
||||
case Type::U32:
|
||||
case Type::F32:
|
||||
return imm_u32 == other.imm_u32;
|
||||
case Type::U64:
|
||||
case Type::F64:
|
||||
return imm_u64 == other.imm_u64;
|
||||
case Type::U32x2:
|
||||
case Type::U32x3:
|
||||
case Type::U32x4:
|
||||
case Type::F16x2:
|
||||
case Type::F16x3:
|
||||
case Type::F16x4:
|
||||
case Type::F32x2:
|
||||
case Type::F32x3:
|
||||
case Type::F32x4:
|
||||
case Type::F64x2:
|
||||
case Type::F64x3:
|
||||
case Type::F64x4:
|
||||
break;
|
||||
}
|
||||
throw LogicError("Invalid type {}", type);
|
||||
}
|
||||
|
||||
bool Value::operator!=(const Value& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
398
src/shader_recompiler/frontend/ir/value.h
Executable file
398
src/shader_recompiler/frontend/ir/value.h
Executable file
@@ -0,0 +1,398 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/attribute.h"
|
||||
#include "shader_recompiler/frontend/ir/opcodes.h"
|
||||
#include "shader_recompiler/frontend/ir/patch.h"
|
||||
#include "shader_recompiler/frontend/ir/pred.h"
|
||||
#include "shader_recompiler/frontend/ir/reg.h"
|
||||
#include "shader_recompiler/frontend/ir/type.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
class Block;
|
||||
class Inst;
|
||||
|
||||
struct AssociatedInsts;
|
||||
|
||||
class Value {
|
||||
public:
|
||||
Value() noexcept = default;
|
||||
explicit Value(IR::Inst* value) noexcept;
|
||||
explicit Value(IR::Reg value) noexcept;
|
||||
explicit Value(IR::Pred value) noexcept;
|
||||
explicit Value(IR::Attribute value) noexcept;
|
||||
explicit Value(IR::Patch value) noexcept;
|
||||
explicit Value(bool value) noexcept;
|
||||
explicit Value(u8 value) noexcept;
|
||||
explicit Value(u16 value) noexcept;
|
||||
explicit Value(u32 value) noexcept;
|
||||
explicit Value(f32 value) noexcept;
|
||||
explicit Value(u64 value) noexcept;
|
||||
explicit Value(f64 value) noexcept;
|
||||
|
||||
[[nodiscard]] bool IsIdentity() const noexcept;
|
||||
[[nodiscard]] bool IsPhi() const noexcept;
|
||||
[[nodiscard]] bool IsEmpty() const noexcept;
|
||||
[[nodiscard]] bool IsImmediate() const noexcept;
|
||||
[[nodiscard]] IR::Type Type() const noexcept;
|
||||
|
||||
[[nodiscard]] IR::Inst* Inst() const;
|
||||
[[nodiscard]] IR::Inst* InstRecursive() const;
|
||||
[[nodiscard]] IR::Value Resolve() const;
|
||||
[[nodiscard]] IR::Reg Reg() const;
|
||||
[[nodiscard]] IR::Pred Pred() const;
|
||||
[[nodiscard]] IR::Attribute Attribute() const;
|
||||
[[nodiscard]] IR::Patch Patch() const;
|
||||
[[nodiscard]] bool U1() const;
|
||||
[[nodiscard]] u8 U8() const;
|
||||
[[nodiscard]] u16 U16() const;
|
||||
[[nodiscard]] u32 U32() const;
|
||||
[[nodiscard]] f32 F32() const;
|
||||
[[nodiscard]] u64 U64() const;
|
||||
[[nodiscard]] f64 F64() const;
|
||||
|
||||
[[nodiscard]] bool operator==(const Value& other) const;
|
||||
[[nodiscard]] bool operator!=(const Value& other) const;
|
||||
|
||||
private:
|
||||
IR::Type type{};
|
||||
union {
|
||||
IR::Inst* inst{};
|
||||
IR::Reg reg;
|
||||
IR::Pred pred;
|
||||
IR::Attribute attribute;
|
||||
IR::Patch patch;
|
||||
bool imm_u1;
|
||||
u8 imm_u8;
|
||||
u16 imm_u16;
|
||||
u32 imm_u32;
|
||||
f32 imm_f32;
|
||||
u64 imm_u64;
|
||||
f64 imm_f64;
|
||||
};
|
||||
};
|
||||
static_assert(static_cast<u32>(IR::Type::Void) == 0, "memset relies on IR::Type being zero");
|
||||
static_assert(std::is_trivially_copyable_v<Value>);
|
||||
|
||||
template <IR::Type type_>
|
||||
class TypedValue : public Value {
|
||||
public:
|
||||
TypedValue() = default;
|
||||
|
||||
template <IR::Type other_type>
|
||||
requires((other_type & type_) != IR::Type::Void) explicit(false)
|
||||
TypedValue(const TypedValue<other_type>& value)
|
||||
: Value(value) {}
|
||||
|
||||
explicit TypedValue(const Value& value) : Value(value) {
|
||||
if ((value.Type() & type_) == IR::Type::Void) {
|
||||
throw InvalidArgument("Incompatible types {} and {}", type_, value.Type());
|
||||
}
|
||||
}
|
||||
|
||||
explicit TypedValue(IR::Inst* inst_) : TypedValue(Value(inst_)) {}
|
||||
};
|
||||
|
||||
class Inst : public boost::intrusive::list_base_hook<> {
|
||||
public:
|
||||
explicit Inst(IR::Opcode op_, u32 flags_) noexcept;
|
||||
~Inst();
|
||||
|
||||
Inst& operator=(const Inst&) = delete;
|
||||
Inst(const Inst&) = delete;
|
||||
|
||||
Inst& operator=(Inst&&) = delete;
|
||||
Inst(Inst&&) = delete;
|
||||
|
||||
/// Get the number of uses this instruction has.
|
||||
[[nodiscard]] int UseCount() const noexcept {
|
||||
return use_count;
|
||||
}
|
||||
|
||||
/// Determines whether this instruction has uses or not.
|
||||
[[nodiscard]] bool HasUses() const noexcept {
|
||||
return use_count > 0;
|
||||
}
|
||||
|
||||
/// Get the opcode this microinstruction represents.
|
||||
[[nodiscard]] IR::Opcode GetOpcode() const noexcept {
|
||||
return op;
|
||||
}
|
||||
|
||||
/// Determines if there is a pseudo-operation associated with this instruction.
|
||||
[[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept {
|
||||
return associated_insts != nullptr;
|
||||
}
|
||||
|
||||
/// Determines whether or not this instruction may have side effects.
|
||||
[[nodiscard]] bool MayHaveSideEffects() const noexcept;
|
||||
|
||||
/// Determines whether or not this instruction is a pseudo-instruction.
|
||||
/// Pseudo-instructions depend on their parent instructions for their semantics.
|
||||
[[nodiscard]] bool IsPseudoInstruction() const noexcept;
|
||||
|
||||
/// Determines if all arguments of this instruction are immediates.
|
||||
[[nodiscard]] bool AreAllArgsImmediates() const;
|
||||
|
||||
/// Gets a pseudo-operation associated with this instruction
|
||||
[[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode);
|
||||
|
||||
/// Get the type this instruction returns.
|
||||
[[nodiscard]] IR::Type Type() const;
|
||||
|
||||
/// Get the number of arguments this instruction has.
|
||||
[[nodiscard]] size_t NumArgs() const {
|
||||
return op == IR::Opcode::Phi ? phi_args.size() : NumArgsOf(op);
|
||||
}
|
||||
|
||||
/// Get the value of a given argument index.
|
||||
[[nodiscard]] Value Arg(size_t index) const noexcept {
|
||||
if (op == IR::Opcode::Phi) {
|
||||
return phi_args[index].second;
|
||||
} else {
|
||||
return args[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the value of a given argument index.
|
||||
void SetArg(size_t index, Value value);
|
||||
|
||||
/// Get a pointer to the block of a phi argument.
|
||||
[[nodiscard]] Block* PhiBlock(size_t index) const;
|
||||
/// Add phi operand to a phi instruction.
|
||||
void AddPhiOperand(Block* predecessor, const Value& value);
|
||||
|
||||
void Invalidate();
|
||||
void ClearArgs();
|
||||
|
||||
void ReplaceUsesWith(Value replacement);
|
||||
|
||||
void ReplaceOpcode(IR::Opcode opcode);
|
||||
|
||||
template <typename FlagsType>
|
||||
requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
|
||||
[[nodiscard]] FlagsType Flags() const noexcept {
|
||||
FlagsType ret;
|
||||
std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename FlagsType>
|
||||
requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
|
||||
[[nodiscard]] void SetFlags(FlagsType value) noexcept {
|
||||
std::memcpy(&flags, &value, sizeof(value));
|
||||
}
|
||||
|
||||
/// Intrusively store the host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
void SetDefinition(DefinitionType def) {
|
||||
definition = Common::BitCast<u32>(def);
|
||||
}
|
||||
|
||||
/// Return the intrusively stored host definition of this instruction.
|
||||
template <typename DefinitionType>
|
||||
[[nodiscard]] DefinitionType Definition() const noexcept {
|
||||
return Common::BitCast<DefinitionType>(definition);
|
||||
}
|
||||
|
||||
/// Destructively remove one reference count from the instruction
|
||||
/// Useful for register allocation
|
||||
void DestructiveRemoveUsage() {
|
||||
--use_count;
|
||||
}
|
||||
|
||||
/// Destructively add usages to the instruction
|
||||
/// Useful for register allocation
|
||||
void DestructiveAddUsage(int count) {
|
||||
use_count += count;
|
||||
}
|
||||
|
||||
private:
|
||||
struct NonTriviallyDummy {
|
||||
NonTriviallyDummy() noexcept {}
|
||||
};
|
||||
|
||||
void Use(const Value& value);
|
||||
void UndoUse(const Value& value);
|
||||
|
||||
IR::Opcode op{};
|
||||
int use_count{};
|
||||
u32 flags{};
|
||||
u32 definition{};
|
||||
union {
|
||||
NonTriviallyDummy dummy{};
|
||||
boost::container::small_vector<std::pair<Block*, Value>, 2> phi_args;
|
||||
std::array<Value, 5> args;
|
||||
};
|
||||
std::unique_ptr<AssociatedInsts> associated_insts;
|
||||
};
|
||||
static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased");
|
||||
|
||||
struct AssociatedInsts {
|
||||
union {
|
||||
Inst* in_bounds_inst;
|
||||
Inst* sparse_inst;
|
||||
Inst* zero_inst{};
|
||||
};
|
||||
Inst* sign_inst{};
|
||||
Inst* carry_inst{};
|
||||
Inst* overflow_inst{};
|
||||
};
|
||||
|
||||
using U1 = TypedValue<Type::U1>;
|
||||
using U8 = TypedValue<Type::U8>;
|
||||
using U16 = TypedValue<Type::U16>;
|
||||
using U32 = TypedValue<Type::U32>;
|
||||
using U64 = TypedValue<Type::U64>;
|
||||
using F16 = TypedValue<Type::F16>;
|
||||
using F32 = TypedValue<Type::F32>;
|
||||
using F64 = TypedValue<Type::F64>;
|
||||
using U32U64 = TypedValue<Type::U32 | Type::U64>;
|
||||
using F32F64 = TypedValue<Type::F32 | Type::F64>;
|
||||
using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>;
|
||||
using F16F32F64 = TypedValue<Type::F16 | Type::F32 | Type::F64>;
|
||||
using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>;
|
||||
|
||||
inline bool Value::IsIdentity() const noexcept {
|
||||
return type == Type::Opaque && inst->GetOpcode() == Opcode::Identity;
|
||||
}
|
||||
|
||||
inline bool Value::IsPhi() const noexcept {
|
||||
return type == Type::Opaque && inst->GetOpcode() == Opcode::Phi;
|
||||
}
|
||||
|
||||
inline bool Value::IsEmpty() const noexcept {
|
||||
return type == Type::Void;
|
||||
}
|
||||
|
||||
inline bool Value::IsImmediate() const noexcept {
|
||||
IR::Type current_type{type};
|
||||
const IR::Inst* current_inst{inst};
|
||||
while (current_type == Type::Opaque && current_inst->GetOpcode() == Opcode::Identity) {
|
||||
const Value& arg{current_inst->Arg(0)};
|
||||
current_type = arg.type;
|
||||
current_inst = arg.inst;
|
||||
}
|
||||
return current_type != Type::Opaque;
|
||||
}
|
||||
|
||||
inline IR::Inst* Value::Inst() const {
|
||||
DEBUG_ASSERT(type == Type::Opaque);
|
||||
return inst;
|
||||
}
|
||||
|
||||
inline IR::Inst* Value::InstRecursive() const {
|
||||
DEBUG_ASSERT(type == Type::Opaque);
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).InstRecursive();
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
inline IR::Value Value::Resolve() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).Resolve();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IR::Reg Value::Reg() const {
|
||||
DEBUG_ASSERT(type == Type::Reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
inline IR::Pred Value::Pred() const {
|
||||
DEBUG_ASSERT(type == Type::Pred);
|
||||
return pred;
|
||||
}
|
||||
|
||||
inline IR::Attribute Value::Attribute() const {
|
||||
DEBUG_ASSERT(type == Type::Attribute);
|
||||
return attribute;
|
||||
}
|
||||
|
||||
inline IR::Patch Value::Patch() const {
|
||||
DEBUG_ASSERT(type == Type::Patch);
|
||||
return patch;
|
||||
}
|
||||
|
||||
inline bool Value::U1() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).U1();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::U1);
|
||||
return imm_u1;
|
||||
}
|
||||
|
||||
inline u8 Value::U8() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).U8();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::U8);
|
||||
return imm_u8;
|
||||
}
|
||||
|
||||
inline u16 Value::U16() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).U16();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::U16);
|
||||
return imm_u16;
|
||||
}
|
||||
|
||||
inline u32 Value::U32() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).U32();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::U32);
|
||||
return imm_u32;
|
||||
}
|
||||
|
||||
inline f32 Value::F32() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).F32();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::F32);
|
||||
return imm_f32;
|
||||
}
|
||||
|
||||
inline u64 Value::U64() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).U64();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::U64);
|
||||
return imm_u64;
|
||||
}
|
||||
|
||||
inline f64 Value::F64() const {
|
||||
if (IsIdentity()) {
|
||||
return inst->Arg(0).F64();
|
||||
}
|
||||
DEBUG_ASSERT(type == Type::F64);
|
||||
return imm_f64;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool IsPhi(const Inst& inst) {
|
||||
return inst.GetOpcode() == Opcode::Phi;
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
Reference in New Issue
Block a user