Add ld/st (attribute variant) instruction decoding, initial implementation of expression propagation, changes to the IR

This commit is contained in:
gdkchan 2018-03-23 19:12:28 -03:00
parent 1220d24a3f
commit 06a19248cd
18 changed files with 472 additions and 128 deletions

View file

@ -1,5 +1,7 @@
using System;
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
@ -74,29 +76,28 @@ namespace Ryujinx.Graphics.Gal.Shader
bool Ab = ((OpCode >> 49) & 1) != 0;
bool Ad = ((OpCode >> 50) & 1) != 0;
EmitAluOperANode(Block, OpCode);
ShaderIrOper OperA = GetAluOperANode_R(OpCode);
ShaderIrOper OperB;
if (Inst == ShaderIrInst.Fadd)
{
EmitAluAbsNeg(Block, Aa, Na);
OperA = GetAluAbsNeg(OperA, Aa, Na);
}
switch (Oper)
{
case ShaderOper.RR: EmitAluOperBNode_RR (Block, OpCode); break;
case ShaderOper.CR: EmitAluOperBCNode_C (Block, OpCode); break;
case ShaderOper.Imm: EmitAluOperBNode_Imm(Block, OpCode); break;
case ShaderOper.RR: OperB = GetAluOperBNode_RR (OpCode); break;
case ShaderOper.CR: OperB = GetAluOperBCNode_C (OpCode); break;
case ShaderOper.Imm: OperB = GetAluOperBNode_Imm(OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
EmitAluAbsNeg(Block, Ab, Nb);
OperB = GetAluAbsNeg(OperB, Ab, Nb);
Block.AddNode(new ShaderIrNode(Inst));
ShaderIrOper Op = GetAluAbs(new ShaderIrOperOp(Inst, OperA, OperB), Ad);
EmitAluAbs(Block, Ad);
EmitAluStrResult(Block, OpCode);
Block.AddNode(new ShaderIrNode(GetAluOperDNode(OpCode), Op));
}
private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@ -104,88 +105,49 @@ namespace Ryujinx.Graphics.Gal.Shader
bool Nb = ((OpCode >> 48) & 1) != 0;
bool Nc = ((OpCode >> 49) & 1) != 0;
EmitAluOperANode(Block, OpCode);
ShaderIrOper OperA = GetAluOperANode_R(OpCode);
ShaderIrOper OperB;
ShaderIrOper OperC;
switch (Oper)
{
case ShaderOper.RR: EmitAluOperBNode_RR (Block, OpCode); break;
case ShaderOper.CR: EmitAluOperBCNode_C (Block, OpCode); break;
case ShaderOper.RC: EmitAluOperBCNode_R (Block, OpCode); break;
case ShaderOper.Imm: EmitAluOperBNode_Imm(Block, OpCode); break;
case ShaderOper.RR: OperB = GetAluOperBNode_RR (OpCode); break;
case ShaderOper.CR: OperB = GetAluOperBCNode_C (OpCode); break;
case ShaderOper.RC: OperB = GetAluOperBCNode_R (OpCode); break;
case ShaderOper.Imm: OperB = GetAluOperBNode_Imm(OpCode); break;
default: throw new ArgumentException(nameof(Oper));
}
EmitAluNeg(Block, Nb);
Block.AddNode(new ShaderIrNode(ShaderIrInst.Fmul));
OperB = GetAluNeg(OperB, Nb);
if (Oper == ShaderOper.RC)
{
EmitAluOperBCNode_C(Block, OpCode);
OperC = GetAluNeg(GetAluOperBCNode_C(OpCode), Nc);
}
else
{
EmitAluOperBCNode_R(Block, OpCode);
OperC = GetAluNeg(GetAluOperBCNode_R(OpCode), Nc);
}
EmitAluNeg(Block, Nc);
ShaderIrOper Op = new ShaderIrOperOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
Block.AddNode(new ShaderIrNode(ShaderIrInst.Fadd));
EmitAluStrResult(Block, OpCode);
Block.AddNode(new ShaderIrNode(GetAluOperDNode(OpCode), Op));
}
private static void EmitAluAbsNeg(ShaderIrBlock Block, bool Abs, bool Neg)
private static ShaderIrOper GetAluAbsNeg(ShaderIrOper Node, bool Abs, bool Neg)
{
EmitAluAbs(Block, Abs);
EmitAluNeg(Block, Neg);
return GetAluNeg(GetAluAbs(Node, Abs), Neg);
}
private static void EmitAluAbs(ShaderIrBlock Block, bool Abs)
private static ShaderIrOper GetAluAbs(ShaderIrOper Node, bool Abs)
{
if (Abs)
{
Block.AddNode(new ShaderIrNode(ShaderIrInst.Fabs));
}
return Abs ? new ShaderIrOperOp(ShaderIrInst.Fabs, Node) : Node;
}
private static void EmitAluNeg(ShaderIrBlock Block, bool Neg)
private static ShaderIrOper GetAluNeg(ShaderIrOper Node, bool Neg)
{
if (Neg)
{
Block.AddNode(new ShaderIrNode(ShaderIrInst.Fneg));
}
}
private static void EmitAluOperANode(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(new ShaderIrNodeLdr((int)(OpCode >> 8) & 0xff));
}
private static void EmitAluOperBNode_RR(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(new ShaderIrNodeLdr((int)(OpCode >> 20) & 0xff));
}
private static void EmitAluOperBCNode_R(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(new ShaderIrNodeLdr((int)(OpCode >> 39) & 0xff));
}
private static void EmitAluOperBCNode_C(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(new ShaderIrNodeLdb(
(int)(OpCode >> 34) & 0x1f,
(int)(OpCode >> 20) & 0x3fff));
}
private static void EmitAluOperBNode_Imm(ShaderIrBlock Block, long OpCode)
{
//TODO
}
private static void EmitAluStrResult(ShaderIrBlock Block, long OpCode)
{
Block.AddNode(new ShaderIrNodeStr((int)(OpCode >> 0) & 0xff));
return Neg ? new ShaderIrOperOp(ShaderIrInst.Fneg, Node) : Node;
}
}
}

View file

@ -0,0 +1,54 @@
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderDecodeHelper
{
public static ShaderIrOper[] GetAluOperANode_A(long OpCode)
{
int Abuf = (int)(OpCode >> 20) & 0x3ff;
int Reg = (int)(OpCode >> 39) & 0xff;
int Size = (int)(OpCode >> 47) & 3;
ShaderIrOper[] Opers = new ShaderIrOper[Size + 1];
for (int Index = 0; Index <= Size; Index++)
{
Opers[Index] = new ShaderIrOperAbuf(Abuf, (Reg + Index) & 0xff);
}
return Opers;
}
public static ShaderIrOper GetAluOperANode_R(long OpCode)
{
return new ShaderIrOperReg((int)(OpCode >> 8) & 0xff);
}
public static ShaderIrOper GetAluOperBNode_RR(long OpCode)
{
return new ShaderIrOperReg((int)(OpCode >> 20) & 0xff);
}
public static ShaderIrOper GetAluOperBCNode_R(long OpCode)
{
return new ShaderIrOperReg((int)(OpCode >> 39) & 0xff);
}
public static ShaderIrOper GetAluOperBCNode_C(long OpCode)
{
return new ShaderIrOperCbuf(
(int)(OpCode >> 34) & 0x1f,
(int)(OpCode >> 20) & 0x3fff);
}
public static ShaderIrOper GetAluOperDNode(long OpCode)
{
return new ShaderIrOperReg((int)(OpCode >> 0) & 0xff);
}
public static ShaderIrOper GetAluOperBNode_Imm(long OpCode)
{
//TODO
return new ShaderIrOper();
}
}
}

View file

@ -0,0 +1,29 @@
using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Ld_A(ShaderIrBlock Block, long OpCode)
{
ShaderIrOper[] Opers = GetAluOperANode_A(OpCode);
ShaderIrOper OperD = GetAluOperDNode(OpCode);
foreach (ShaderIrOper OperA in Opers)
{
Block.AddNode(new ShaderIrNode(OperD, OperA));
}
}
public static void St_A(ShaderIrBlock Block, long OpCode)
{
ShaderIrOper[] Opers = GetAluOperANode_A(OpCode);
ShaderIrOper OperD = GetAluOperDNode(OpCode);
foreach (ShaderIrOper OperA in Opers)
{
Block.AddNode(new ShaderIrNode(OperA, OperD));
}
}
}
}

View file

@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Decode(Block, OpCode);
}
Block.RunOptimizationPasses();
return Block;
}
}

View file

@ -16,6 +16,11 @@ namespace Ryujinx.Graphics.Gal.Shader
Nodes.Add(Node);
}
public void RunOptimizationPasses()
{
ShaderOptExprProp.Optimize(Nodes);
}
public ShaderIrNode[] GetNodes()
{
return Nodes.ToArray();

View file

@ -4,9 +4,8 @@ namespace Ryujinx.Graphics.Gal.Shader
{
Fabs,
Fadd,
Ffma,
Fmul,
Fneg,
Ld,
St
}
}

View file

@ -2,11 +2,13 @@ namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrNode
{
public ShaderIrInst Inst;
public ShaderIrOper Dst { get; set; }
public ShaderIrOper Src { get; set; }
public ShaderIrNode(ShaderIrInst Inst)
public ShaderIrNode(ShaderIrOper Dst, ShaderIrOper Src)
{
this.Inst = Inst;
this.Dst = Dst;
this.Src = Src;
}
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrNodeLdb : ShaderIrNode
{
public int Cbuf { get; private set; }
public int Offs { get; private set; }
public ShaderIrNodeLdb(int Cbuf, int Offs) : base(ShaderIrInst.Ld)
{
this.Cbuf = Cbuf;
this.Offs = Offs;
}
}
}

View file

@ -1,12 +0,0 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrNodeLdr : ShaderIrNode
{
public int GprIndex { get; private set; }
public ShaderIrNodeLdr(int GprIndex) : base(ShaderIrInst.Ld)
{
this.GprIndex = GprIndex;
}
}
}

View file

@ -1,12 +0,0 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrNodeStr : ShaderIrNode
{
public int GprIndex { get; private set; }
public ShaderIrNodeStr(int GprIndex) : base(ShaderIrInst.St)
{
this.GprIndex = GprIndex;
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperAbuf : ShaderIrOper
{
public int Offs { get; private set; }
public int GprIndex { get; private set; }
public ShaderIrOperAbuf(int Offs, int GprIndex)
{
this.Offs = Offs;
this.GprIndex = GprIndex;
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperCbuf : ShaderIrOper
{
public int Index { get; private set; }
public int Offs { get; private set; }
public ShaderIrOperCbuf(int Index, int Offs)
{
this.Index = Index;
this.Offs = Offs;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperImm : ShaderIrOper
{
public int Imm { get; private set; }
public ShaderIrOperImm(int Imm)
{
this.Imm = Imm;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperImmf : ShaderIrOper
{
public float Imm { get; private set; }
public ShaderIrOperImmf(float Imm)
{
this.Imm = Imm;
}
}
}

View file

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrOperOp : ShaderIrOper
{
public ShaderIrInst Inst { get; private set; }
public ShaderIrOper OperandA { get; set; }
public ShaderIrOper OperandB { get; set; }
public ShaderIrOper OperandC { get; set; }
public ShaderIrOperOp(
ShaderIrInst Inst,
ShaderIrOper OperandA = null,
ShaderIrOper OperandB = null,
ShaderIrOper OperandC = null)
{
this.Inst = Inst;
this.OperandA = OperandA;
this.OperandB = OperandB;
this.OperandC = OperandC;
}
}
}

View file

@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("0101110001101x", ShaderDecode.Fmul_R);
Set("0100110001101x", ShaderDecode.Fmul_C);
Set("0011100x01101x", ShaderDecode.Fmul_Imm);
Set("1110111111011x", ShaderDecode.Ld_A);
Set("1110111111110x", ShaderDecode.St_A);
#endregion
}

View file

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderOptExprProp
{
private struct UseSite
{
public object Parent;
public int OperIndex;
public UseSite(object Parent, int OperIndex)
{
this.Parent = Parent;
this.OperIndex = OperIndex;
}
}
private class RegUse
{
public ShaderIrNode Node { get; private set; }
private List<UseSite> Sites;
public RegUse()
{
Sites = new List<UseSite>();
}
public void AddUseSite(UseSite Site)
{
Sites.Add(Site);
}
public bool TryPropagate()
{
//If the use count of the register is more than 1,
//then propagating the expression is not worth it,
//because the code will be larger, harder to read,
//and less efficient due to the common sub-expression being
//propagated.
if (Sites.Count == 1 || !(Node.Src is ShaderIrOperOp))
{
foreach (UseSite Site in Sites)
{
if (Site.Parent is ShaderIrOperOp Op)
{
switch (Site.OperIndex)
{
case 0: Op.OperandA = Node.Src; break;
case 1: Op.OperandB = Node.Src; break;
case 2: Op.OperandC = Node.Src; break;
default: throw new InvalidOperationException();
}
}
else if (Site.Parent is ShaderIrNode SiteNode)
{
SiteNode.Src = Node.Src;
}
else
{
throw new InvalidOperationException();
}
}
return true;
}
return Sites.Count == 0;
}
public void SetNewAsg(ShaderIrNode Node)
{
this.Node = Node;
Sites.Clear();
}
}
public static void Optimize(List<ShaderIrNode> Nodes)
{
Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
RegUse GetRegUse(int GprIndex)
{
RegUse Use;
if (!Uses.TryGetValue(GprIndex, out Use))
{
Use = new RegUse();
Uses.Add(GprIndex, Use);
}
return Use;
}
void TryAddRegUse(object Parent, ShaderIrOper Oper, int OperIndex = 0)
{
if (Oper is ShaderIrOperOp Op)
{
TryAddRegUse(Op, Op.OperandA, 0);
TryAddRegUse(Op, Op.OperandB, 1);
TryAddRegUse(Op, Op.OperandC, 2);
}
else if (Oper is ShaderIrOperReg Reg && Reg.GprIndex != 0xff)
{
GetRegUse(Reg.GprIndex).AddUseSite(new UseSite(Parent, OperIndex));
}
}
for (int Index = 0; Index < Nodes.Count; Index++)
{
ShaderIrNode Node = Nodes[Index];
if (Node.Src is ShaderIrOperOp Op)
{
TryAddRegUse(Node, Op);
}
else if (Node.Src is ShaderIrOperReg)
{
TryAddRegUse(Node, Node.Src);
}
if (Node.Dst is ShaderIrOperReg Reg && Reg.GprIndex != 0xff)
{
RegUse Use = GetRegUse(Reg.GprIndex);
if (Use.Node != null && Use.TryPropagate())
{
Nodes.Remove(Use.Node);
Index--;
}
Use.SetNewAsg(Node);
}
}
foreach (RegUse Use in Uses.Values)
{
if (Use.TryPropagate())
{
Nodes.Remove(Use.Node);
}
}
}
}
}

View file

@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
public static class ShaderTest
@ -26,25 +29,123 @@ namespace Ryujinx.Graphics.Gal.Shader
foreach (ShaderIrNode Node in Nodes)
{
System.Console.Write(Node.Inst);
if (Node is ShaderIrNodeLdr LdrNode)
{
System.Console.Write($" r{LdrNode.GprIndex}");
}
else if (Node is ShaderIrNodeStr StrNode)
{
System.Console.Write($" r{StrNode.GprIndex}");
}
else if (Node is ShaderIrNodeLdb LdbNode)
{
System.Console.Write($" c{LdbNode.Cbuf}[0x{LdbNode.Offs.ToString("x")}]");
}
System.Console.WriteLine(string.Empty);
System.Console.WriteLine($"{GetOutOperName(Node.Dst)} = {GetInOperName(Node.Src, true)}");
}
System.Console.WriteLine("Test code finished!");
}
private static string GetOutOperName(ShaderIrOper Oper)
{
switch (Oper)
{
case ShaderIrOperAbuf Abuf: return GetOAbufName(Abuf);
case ShaderIrOperReg Reg: return GetRegName(Reg);
default: throw new ArgumentException(nameof(Oper));
}
}
private static string GetInOperName(ShaderIrOper Oper, bool Entry = false)
{
switch (Oper)
{
case ShaderIrOperAbuf Abuf: return GetIAbufName(Abuf);
case ShaderIrOperCbuf Cbuf: return GetCbufName(Cbuf);
case ShaderIrOperReg Reg: return GetRegName(Reg);
case ShaderIrOperOp Op:
string Expr;
if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr))
{
Expr = GetExpr(Op);
}
else
{
throw new NotImplementedException(Op.Inst.ToString());
}
if (!(Entry || IsUnary(Op.Inst)))
{
Expr = $"({Expr})";
}
return Expr;
default: throw new ArgumentException(nameof(Oper));
}
}
private static bool IsUnary(ShaderIrInst Inst)
{
return Inst == ShaderIrInst.Fabs ||
Inst == ShaderIrInst.Fneg;
}
private static string GetOAbufName(ShaderIrOperAbuf Abuf)
{
return $"a_out[0x{Abuf.Offs:x} + {GetRegName(Abuf.GprIndex)}]";
}
private static string GetIAbufName(ShaderIrOperAbuf Abuf)
{
return $"a_in[0x{Abuf.Offs:x} + {GetRegName(Abuf.GprIndex)}]";
}
private static string GetCbufName(ShaderIrOperCbuf Cbuf)
{
return $"c{Cbuf.Index}[{Cbuf.Offs}]";
}
private static string GetRegName(ShaderIrOperReg Reg)
{
return GetRegName(Reg.GprIndex);
}
private static string GetRegName(int GprIndex)
{
return GprIndex == 0xff ? "0" : $"r{GprIndex}";
}
private delegate string GetInstExpr(ShaderIrOperOp Op);
private static Dictionary<ShaderIrInst, GetInstExpr> InstsExpr = new
Dictionary<ShaderIrInst, GetInstExpr>()
{
{ ShaderIrInst.Fabs, GetFabsExpr },
{ ShaderIrInst.Fadd, GetFaddExpr },
{ ShaderIrInst.Ffma, GetFfmaExpr },
{ ShaderIrInst.Fmul, GetFmulExpr },
{ ShaderIrInst.Fneg, GetFnegExpr },
};
private static string GetFabsExpr(ShaderIrOperOp Op)
{
return $"abs({GetInOperName(Op.OperandA)})";
}
private static string GetFaddExpr(ShaderIrOperOp Op)
{
return $"{GetInOperName(Op.OperandA)} + " +
$"{GetInOperName(Op.OperandB)}";
}
private static string GetFfmaExpr(ShaderIrOperOp Op)
{
return $"{GetInOperName(Op.OperandA)} * " +
$"{GetInOperName(Op.OperandB)} + " +
$"{GetInOperName(Op.OperandC)}";
}
private static string GetFmulExpr(ShaderIrOperOp Op)
{
return $"{GetInOperName(Op.OperandA)} * " +
$"{GetInOperName(Op.OperandB)}";
}
private static string GetFnegExpr(ShaderIrOperOp Op)
{
return $"-{GetInOperName(Op.OperandA)}";
}
}
}