aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2021-10-28 19:53:12 -0300
committerGitHub <noreply@github.com>2021-10-28 19:53:12 -0300
commit99445dd0a63f4a6fcb53e7818cda689d8299453b (patch)
tree9d979f95e3e4cdf12999bc005655505953a60613 /Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
parenta7a40a77f2c07ea0ea9f6e7bfb57dbe9fce06db7 (diff)
Add support for fragment shader interlock (#2768)
* Support coherent images * Add support for fragment shader interlock * Change to tree based match approach * Refactor + check for branch targets and external registers * Make detection more robust * Use Intel fragment shader ordering if interlock is not available, use nothing if both are not available * Remove unused field
Diffstat (limited to 'Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs')
-rw-r--r--Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs866
1 files changed, 866 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs b/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
new file mode 100644
index 00000000..1c5d8c54
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
@@ -0,0 +1,866 @@
+using Ryujinx.Graphics.Shader.Decoders;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Graphics.Shader.Translation
+{
+ static class FunctionMatch
+ {
+ private static IPatternTreeNode[] _fsiGetAddressTree = PatternTrees.GetFsiGetAddress();
+ private static IPatternTreeNode[] _fsiGetAddressV2Tree = PatternTrees.GetFsiGetAddressV2();
+ private static IPatternTreeNode[] _fsiIsLastWarpThreadPatternTree = PatternTrees.GetFsiIsLastWarpThread();
+ private static IPatternTreeNode[] _fsiBeginPatternTree = PatternTrees.GetFsiBeginPattern();
+ private static IPatternTreeNode[] _fsiEndPatternTree = PatternTrees.GetFsiEndPattern();
+
+ public static void RunPass(DecodedProgram program)
+ {
+ byte[] externalRegs = new byte[4];
+ bool hasGetAddress = false;
+
+ foreach (DecodedFunction function in program)
+ {
+ if (function == program.MainFunction)
+ {
+ continue;
+ }
+
+ int externalReg4 = 0;
+
+ TreeNode[] functionTree = BuildTree(function.Blocks);
+
+ if (Matches(_fsiGetAddressTree, functionTree))
+ {
+ externalRegs[1] = functionTree[0].GetRd();
+ externalRegs[2] = functionTree[2].GetRd();
+ externalRegs[3] = functionTree[1].GetRd();
+ externalReg4 = functionTree[3].GetRd();
+ }
+ else if (Matches(_fsiGetAddressV2Tree, functionTree))
+ {
+ externalRegs[1] = functionTree[2].GetRd();
+ externalRegs[2] = functionTree[1].GetRd();
+ externalRegs[3] = functionTree[0].GetRd();
+ externalReg4 = functionTree[3].GetRd();
+ }
+
+ // Ensure the register allocation is valid.
+ // If so, then we have a match.
+ if (externalRegs[1] != externalRegs[2] &&
+ externalRegs[2] != externalRegs[3] &&
+ externalRegs[1] != externalRegs[3] &&
+ externalRegs[1] + 1 != externalRegs[2] &&
+ externalRegs[1] + 1 != externalRegs[3] &&
+ externalRegs[1] + 1 == externalReg4 &&
+ externalRegs[2] != RegisterConsts.RegisterZeroIndex &&
+ externalRegs[3] != RegisterConsts.RegisterZeroIndex &&
+ externalReg4 != RegisterConsts.RegisterZeroIndex)
+ {
+ hasGetAddress = true;
+ function.Type = FunctionType.Unused;
+ break;
+ }
+ }
+
+ foreach (DecodedFunction function in program)
+ {
+ if (function.IsCompilerGenerated || function == program.MainFunction)
+ {
+ continue;
+ }
+
+ if (hasGetAddress)
+ {
+ TreeNode[] functionTree = BuildTree(function.Blocks);
+
+ if (MatchesFsi(_fsiBeginPatternTree, program, function, functionTree, externalRegs))
+ {
+ function.Type = FunctionType.BuiltInFSIBegin;
+ continue;
+ }
+ else if (MatchesFsi(_fsiEndPatternTree, program, function, functionTree, externalRegs))
+ {
+ function.Type = FunctionType.BuiltInFSIEnd;
+ continue;
+ }
+ }
+ }
+ }
+
+ private struct TreeNodeUse
+ {
+ public TreeNode Node { get; }
+ public int Index { get; }
+ public bool Inverted { get; }
+
+ private TreeNodeUse(int index, bool inverted, TreeNode node)
+ {
+ Index = index;
+ Inverted = inverted;
+ Node = node;
+ }
+
+ public TreeNodeUse(int index, TreeNode node) : this(index, false, node)
+ {
+ }
+
+ public TreeNodeUse Flip()
+ {
+ return new TreeNodeUse(Index, !Inverted, Node);
+ }
+ }
+
+ private enum TreeNodeType : byte
+ {
+ Op,
+ Label
+ }
+
+ private class TreeNode
+ {
+ public readonly InstOp Op;
+ public readonly List<TreeNodeUse> Uses;
+ public TreeNodeType Type { get; }
+ public byte Order { get; }
+
+ public TreeNode(byte order)
+ {
+ Type = TreeNodeType.Label;
+ Order = order;
+ }
+
+ public TreeNode(InstOp op, byte order)
+ {
+ Op = op;
+ Uses = new List<TreeNodeUse>();
+ Type = TreeNodeType.Op;
+ Order = order;
+ }
+
+ public byte GetPd()
+ {
+ return (byte)((Op.RawOpCode >> 3) & 7);
+ }
+
+ public byte GetRd()
+ {
+ return (byte)Op.RawOpCode;
+ }
+ }
+
+ private static TreeNode[] BuildTree(Block[] blocks)
+ {
+ List<TreeNode> nodes = new List<TreeNode>();
+
+ Dictionary<ulong, TreeNode> labels = new Dictionary<ulong, TreeNode>();
+
+ TreeNodeUse[] predDefs = new TreeNodeUse[RegisterConsts.PredsCount];
+ TreeNodeUse[] gprDefs = new TreeNodeUse[RegisterConsts.GprsCount];
+
+ void DefPred(byte predIndex, int index, TreeNode node)
+ {
+ if (predIndex != RegisterConsts.PredicateTrueIndex)
+ {
+ predDefs[predIndex] = new TreeNodeUse(index, node);
+ }
+ }
+
+ void DefGpr(byte regIndex, int index, TreeNode node)
+ {
+ if (regIndex != RegisterConsts.RegisterZeroIndex)
+ {
+ gprDefs[regIndex] = new TreeNodeUse(index, node);
+ }
+ }
+
+ TreeNodeUse UsePred(byte predIndex, bool predInv)
+ {
+ if (predIndex != RegisterConsts.PredicateTrueIndex)
+ {
+ TreeNodeUse use = predDefs[predIndex];
+
+ if (use.Node != null)
+ {
+ nodes.Remove(use.Node);
+ }
+ else
+ {
+ use = new TreeNodeUse(-(predIndex + 2), null);
+ }
+
+ return predInv ? use.Flip() : use;
+ }
+
+ return new TreeNodeUse(-1, null);
+ }
+
+ TreeNodeUse UseGpr(byte regIndex)
+ {
+ if (regIndex != RegisterConsts.RegisterZeroIndex)
+ {
+ TreeNodeUse use = gprDefs[regIndex];
+
+ if (use.Node != null)
+ {
+ nodes.Remove(use.Node);
+ }
+ else
+ {
+ use = new TreeNodeUse(-(regIndex + 2), null);
+ }
+
+ return use;
+ }
+
+ return new TreeNodeUse(-1, null);
+ }
+
+ byte order = 0;
+
+ for (int index = 0; index < blocks.Length; index++)
+ {
+ Block block = blocks[index];
+
+ if (block.Predecessors.Count > 1)
+ {
+ TreeNode label = new TreeNode(order++);
+ nodes.Add(label);
+ labels.Add(block.Address, label);
+ }
+
+ for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++)
+ {
+ InstOp op = block.OpCodes[opIndex];
+
+ TreeNode node = new TreeNode(op, IsOrderDependant(op.Name) ? order : (byte)0);
+
+ // Add uses.
+
+ if (!op.Props.HasFlag(InstProps.NoPred))
+ {
+ byte predIndex = (byte)((op.RawOpCode >> 16) & 7);
+ bool predInv = (op.RawOpCode & 0x80000) != 0;
+ node.Uses.Add(UsePred(predIndex, predInv));
+ }
+
+ if (op.Props.HasFlag(InstProps.Ps))
+ {
+ byte predIndex = (byte)((op.RawOpCode >> 39) & 7);
+ bool predInv = (op.RawOpCode & 0x40000000000) != 0;
+ node.Uses.Add(UsePred(predIndex, predInv));
+ }
+
+ if (op.Props.HasFlag(InstProps.Ra))
+ {
+ byte ra = (byte)(op.RawOpCode >> 8);
+ node.Uses.Add(UseGpr(ra));
+ }
+
+ if ((op.Props & (InstProps.Rb | InstProps.Rb2)) != 0)
+ {
+ byte rb = op.Props.HasFlag(InstProps.Rb2) ? (byte)op.RawOpCode : (byte)(op.RawOpCode >> 20);
+ node.Uses.Add(UseGpr(rb));
+ }
+
+ if (op.Props.HasFlag(InstProps.Rc))
+ {
+ byte rc = (byte)(op.RawOpCode >> 39);
+ node.Uses.Add(UseGpr(rc));
+ }
+
+ if (op.Name == InstName.Bra && labels.TryGetValue(op.GetAbsoluteAddress(), out TreeNode label))
+ {
+ node.Uses.Add(new TreeNodeUse(0, label));
+ }
+
+ // Make definitions.
+
+ int defIndex = 0;
+
+ InstProps pdType = op.Props & InstProps.PdMask;
+
+ if (pdType != 0)
+ {
+ int bit = pdType switch
+ {
+ InstProps.Pd => 3,
+ InstProps.LPd => 48,
+ InstProps.SPd => 30,
+ InstProps.TPd => 51,
+ InstProps.VPd => 45,
+ _ => throw new InvalidOperationException($"Table has unknown predicate destination {pdType}.")
+ };
+
+ byte predIndex = (byte)((op.RawOpCode >> bit) & 7);
+ DefPred(predIndex, defIndex++, node);
+ }
+
+ if (op.Props.HasFlag(InstProps.Rd))
+ {
+ byte rd = (byte)op.RawOpCode;
+ DefGpr(rd, defIndex++, node);
+ }
+
+ nodes.Add(node);
+ }
+ }
+
+ return nodes.ToArray();
+ }
+
+ private static bool IsOrderDependant(InstName name)
+ {
+ switch (name)
+ {
+ case InstName.Atom:
+ case InstName.AtomCas:
+ case InstName.Atoms:
+ case InstName.AtomsCas:
+ case InstName.Ld:
+ case InstName.Ldg:
+ case InstName.Ldl:
+ case InstName.Lds:
+ case InstName.Suatom:
+ case InstName.SuatomB:
+ case InstName.SuatomB2:
+ case InstName.SuatomCas:
+ case InstName.SuatomCasB:
+ case InstName.Suld:
+ case InstName.SuldB:
+ case InstName.SuldD:
+ case InstName.SuldDB:
+ return true;
+ }
+
+ return false;
+ }
+
+ private interface IPatternTreeNode
+ {
+ List<PatternTreeNodeUse> Uses { get; }
+ InstName Name { get; }
+ TreeNodeType Type { get; }
+ byte Order { get; }
+ bool IsImm { get; }
+ bool Matches(in InstOp opInfo);
+ }
+
+ private struct PatternTreeNodeUse
+ {
+ public IPatternTreeNode Node { get; }
+ public int Index { get; }
+ public bool Inverted { get; }
+ public PatternTreeNodeUse Inv => new PatternTreeNodeUse(Index, !Inverted, Node);
+
+ private PatternTreeNodeUse(int index, bool inverted, IPatternTreeNode node)
+ {
+ Index = index;
+ Inverted = inverted;
+ Node = node;
+ }
+
+ public PatternTreeNodeUse(int index, IPatternTreeNode node) : this(index, false, node)
+ {
+ }
+ }
+
+ private class PatternTreeNode<T> : IPatternTreeNode
+ {
+ public List<PatternTreeNodeUse> Uses { get; }
+ private readonly Func<T, bool> _match;
+
+ public InstName Name { get; }
+ public TreeNodeType Type { get; }
+ public byte Order { get; }
+ public bool IsImm { get; }
+ public PatternTreeNodeUse Out => new PatternTreeNodeUse(0, this);
+
+ public PatternTreeNode(InstName name, Func<T, bool> match, TreeNodeType type = TreeNodeType.Op, byte order = 0, bool isImm = false)
+ {
+ Name = name;
+ _match = match;
+ Type = type;
+ Order = order;
+ IsImm = isImm;
+ Uses = new List<PatternTreeNodeUse>();
+ }
+
+ public PatternTreeNode<T> Use(PatternTreeNodeUse use)
+ {
+ Uses.Add(use);
+ return this;
+ }
+
+ public PatternTreeNodeUse OutAt(int index)
+ {
+ return new PatternTreeNodeUse(index, this);
+ }
+
+ public bool Matches(in InstOp opInfo)
+ {
+ if (opInfo.Name != Name)
+ {
+ return false;
+ }
+
+ ulong rawOp = opInfo.RawOpCode;
+ T op = Unsafe.As<ulong, T>(ref rawOp);
+
+ if (!_match(op))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ private static bool MatchesFsi(
+ IPatternTreeNode[] pattern,
+ DecodedProgram program,
+ DecodedFunction function,
+ TreeNode[] functionTree,
+ byte[] externalRegs)
+ {
+ if (function.Blocks.Length == 0)
+ {
+ return false;
+ }
+
+ InstOp callOp = function.Blocks[0].GetLastOp();
+
+ if (callOp.Name != InstName.Cal)
+ {
+ return false;
+ }
+
+ DecodedFunction callTarget = program.GetFunctionByAddress(callOp.GetAbsoluteAddress());
+ TreeNode[] callTargetTree = null;
+
+ if (callTarget == null || !Matches(_fsiIsLastWarpThreadPatternTree, callTargetTree = BuildTree(callTarget.Blocks)))
+ {
+ return false;
+ }
+
+ externalRegs[0] = callTargetTree[0].GetPd();
+
+ if (Matches(pattern, functionTree, externalRegs))
+ {
+ callTarget.RemoveCaller(function);
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool Matches(IPatternTreeNode[] pTree, TreeNode[] cTree, byte[] externalRegs = null)
+ {
+ if (pTree.Length != cTree.Length)
+ {
+ return false;
+ }
+
+ for (int index = 0; index < pTree.Length; index++)
+ {
+ if (!Matches(pTree[index], cTree[index], externalRegs))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool Matches(IPatternTreeNode pTreeNode, TreeNode cTreeNode, byte[] externalRegs)
+ {
+ if (!pTreeNode.Matches(in cTreeNode.Op) ||
+ pTreeNode.Type != cTreeNode.Type ||
+ pTreeNode.Order != cTreeNode.Order ||
+ pTreeNode.IsImm != cTreeNode.Op.Props.HasFlag(InstProps.Ib))
+ {
+ return false;
+ }
+
+ if (pTreeNode.Type == TreeNodeType.Op)
+ {
+ if (pTreeNode.Uses.Count != cTreeNode.Uses.Count)
+ {
+ return false;
+ }
+
+ for (int index = 0; index < pTreeNode.Uses.Count; index++)
+ {
+ var pUse = pTreeNode.Uses[index];
+ var cUse = cTreeNode.Uses[index];
+
+ if (pUse.Index <= -2)
+ {
+ if (externalRegs[-pUse.Index - 2] != (-cUse.Index - 2))
+ {
+ return false;
+ }
+ }
+ else if (pUse.Index != cUse.Index)
+ {
+ return false;
+ }
+
+ if (pUse.Inverted != cUse.Inverted || (pUse.Node == null) != (cUse.Node == null))
+ {
+ return false;
+ }
+
+ if (pUse.Node != null && !Matches(pUse.Node, cUse.Node, externalRegs))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static class PatternTrees
+ {
+ public static IPatternTreeNode[] GetFsiGetAddress()
+ {
+ var affinityValue = S2r(SReg.Affinity).Use(PT).Out;
+ var orderingTicketValue = S2r(SReg.OrderingTicket).Use(PT).Out;
+
+ return new IPatternTreeNode[]
+ {
+ Iscadd(cc: true, 2, 0, 404)
+ .Use(PT)
+ .Use(Iscadd(cc: false, 8)
+ .Use(PT)
+ .Use(Lop32i(LogicOp.And, 0xff)
+ .Use(PT)
+ .Use(affinityValue).Out)
+ .Use(Lop32i(LogicOp.And, 0xff)
+ .Use(PT)
+ .Use(orderingTicketValue).Out).Out),
+ ShrU32W(16)
+ .Use(PT)
+ .Use(orderingTicketValue),
+ Iadd32i(0x200)
+ .Use(PT)
+ .Use(Lop32i(LogicOp.And, 0xfe00)
+ .Use(PT)
+ .Use(orderingTicketValue).Out),
+ Iadd(x: true, 0, 405).Use(PT).Use(RZ),
+ Ret().Use(PT)
+ };
+ }
+
+ public static IPatternTreeNode[] GetFsiGetAddressV2()
+ {
+ var affinityValue = S2r(SReg.Affinity).Use(PT).Out;
+ var orderingTicketValue = S2r(SReg.OrderingTicket).Use(PT).Out;
+
+ return new IPatternTreeNode[]
+ {
+ ShrU32W(16)
+ .Use(PT)
+ .Use(orderingTicketValue),
+ Iadd32i(0x200)
+ .Use(PT)
+ .Use(Lop32i(LogicOp.And, 0xfe00)
+ .Use(PT)
+ .Use(orderingTicketValue).Out),
+ Iscadd(cc: true, 2, 0, 404)
+ .Use(PT)
+ .Use(Bfi(0x808)
+ .Use(PT)
+ .Use(affinityValue)
+ .Use(Lop32i(LogicOp.And, 0xff)
+ .Use(PT)
+ .Use(orderingTicketValue).Out).Out),
+ Iadd(x: true, 0, 405).Use(PT).Use(RZ),
+ Ret().Use(PT)
+ };
+ }
+
+ public static IPatternTreeNode[] GetFsiIsLastWarpThread()
+ {
+ var threadKillValue = S2r(SReg.ThreadKill).Use(PT).Out;
+ var laneIdValue = S2r(SReg.LaneId).Use(PT).Out;
+
+ return new IPatternTreeNode[]
+ {
+ IsetpU32(IComp.Eq)
+ .Use(PT)
+ .Use(PT)
+ .Use(FloU32()
+ .Use(PT)
+ .Use(Vote(VoteMode.Any)
+ .Use(PT)
+ .Use(IsetpU32(IComp.Ne)
+ .Use(PT)
+ .Use(PT)
+ .Use(Lop(negB: true, LogicOp.PassB)
+ .Use(PT)
+ .Use(RZ)
+ .Use(threadKillValue).OutAt(1))
+ .Use(RZ).Out).OutAt(1)).Out)
+ .Use(laneIdValue),
+ Ret().Use(PT)
+ };
+ }
+
+ public static IPatternTreeNode[] GetFsiBeginPattern()
+ {
+ var addressLowValue = CallArg(1);
+
+ static PatternTreeNodeUse HighU16Equals(PatternTreeNodeUse x)
+ {
+ var expectedValue = CallArg(3);
+
+ return IsetpU32(IComp.Eq)
+ .Use(PT)
+ .Use(PT)
+ .Use(ShrU32W(16).Use(PT).Use(x).Out)
+ .Use(expectedValue).Out;
+ }
+
+ PatternTreeNode<byte> label;
+
+ return new IPatternTreeNode[]
+ {
+ Cal(),
+ Ret().Use(CallArg(0).Inv),
+ Ret()
+ .Use(HighU16Equals(LdgE(CacheOpLd.Cg, LsSize.B32)
+ .Use(PT)
+ .Use(addressLowValue).Out)),
+ label = Label(),
+ Bra()
+ .Use(HighU16Equals(LdgE(CacheOpLd.Cg, LsSize.B32, 1)
+ .Use(PT)
+ .Use(addressLowValue).Out).Inv)
+ .Use(label.Out),
+ Ret().Use(PT)
+ };
+ }
+
+ public static IPatternTreeNode[] GetFsiEndPattern()
+ {
+ var voteResult = Vote(VoteMode.All).Use(PT).Use(PT).OutAt(1);
+ var popcResult = Popc().Use(PT).Use(voteResult).Out;
+ var threadKillValue = S2r(SReg.ThreadKill).Use(PT).Out;
+ var laneIdValue = S2r(SReg.LaneId).Use(PT).Out;
+
+ var addressLowValue = CallArg(1);
+ var incrementValue = CallArg(2);
+
+ return new IPatternTreeNode[]
+ {
+ Cal(),
+ Ret().Use(CallArg(0).Inv),
+ Membar(Decoders.Membar.Vc).Use(PT),
+ Ret().Use(IsetpU32(IComp.Ne)
+ .Use(PT)
+ .Use(PT)
+ .Use(threadKillValue)
+ .Use(RZ).Out),
+ RedE(RedOp.Add, AtomSize.U32)
+ .Use(IsetpU32(IComp.Eq)
+ .Use(PT)
+ .Use(PT)
+ .Use(FloU32()
+ .Use(PT)
+ .Use(voteResult).Out)
+ .Use(laneIdValue).Out)
+ .Use(addressLowValue)
+ .Use(Xmad(XmadCop.Cbcc, psl: true, hiloA: true, hiloB: true)
+ .Use(PT)
+ .Use(incrementValue)
+ .Use(Xmad(XmadCop.Cfull, mrg: true, hiloB: true)
+ .Use(PT)
+ .Use(incrementValue)
+ .Use(popcResult)
+ .Use(RZ).Out)
+ .Use(Xmad(XmadCop.Cfull)
+ .Use(PT)
+ .Use(incrementValue)
+ .Use(popcResult)
+ .Use(RZ).Out).Out),
+ Ret().Use(PT)
+ };
+ }
+
+ private static PatternTreeNode<InstBfiI> Bfi(int imm)
+ {
+ return new(InstName.Bfi, (op) => !op.WriteCC && op.Imm20 == imm, isImm: true);
+ }
+
+ private static PatternTreeNode<InstBra> Bra()
+ {
+ return new(InstName.Bra, (op) => op.Ccc == Ccc.T && !op.Ca);
+ }
+
+ private static PatternTreeNode<InstCal> Cal()
+ {
+ return new(InstName.Cal, (op) => !op.Ca && op.Inc);
+ }
+
+ private static PatternTreeNode<InstFloR> FloU32()
+ {
+ return new(InstName.Flo, (op) => !op.Signed && !op.Sh && !op.NegB && !op.WriteCC);
+ }
+
+ private static PatternTreeNode<InstIaddC> Iadd(bool x, int cbufSlot, int cbufOffset)
+ {
+ return new(InstName.Iadd, (op) =>
+ !op.Sat &&
+ !op.WriteCC &&
+ op.X == x &&
+ op.AvgMode == AvgMode.NoNeg &&
+ op.CbufSlot == cbufSlot &&
+ op.CbufOffset == cbufOffset);
+ }
+
+ private static PatternTreeNode<InstIadd32i> Iadd32i(int imm)
+ {
+ return new(InstName.Iadd32i, (op) => !op.Sat && !op.WriteCC && !op.X && op.AvgMode == AvgMode.NoNeg && op.Imm32 == imm);
+ }
+
+ private static PatternTreeNode<InstIscaddR> Iscadd(bool cc, int imm)
+ {
+ return new(InstName.Iscadd, (op) => op.WriteCC == cc && op.AvgMode == AvgMode.NoNeg && op.Imm5 == imm);
+ }
+
+ private static PatternTreeNode<InstIscaddC> Iscadd(bool cc, int imm, int cbufSlot, int cbufOffset)
+ {
+ return new(InstName.Iscadd, (op) =>
+ op.WriteCC == cc &&
+ op.AvgMode == AvgMode.NoNeg &&
+ op.Imm5 == imm &&
+ op.CbufSlot == cbufSlot &&
+ op.CbufOffset == cbufOffset);
+ }
+
+ private static PatternTreeNode<InstIsetpR> IsetpU32(IComp comp)
+ {
+ return new(InstName.Isetp, (op) => !op.Signed && op.IComp == comp && op.Bop == BoolOp.And);
+ }
+
+ private static PatternTreeNode<byte> Label()
+ {
+ return new(InstName.Invalid, (op) => true, type: TreeNodeType.Label);
+ }
+
+ private static PatternTreeNode<InstLopR> Lop(bool negB, LogicOp logicOp)
+ {
+ return new(InstName.Lop, (op) => !op.NegA && op.NegB == negB && !op.WriteCC && !op.X && op.Lop == logicOp && op.PredicateOp == PredicateOp.F);
+ }
+
+ private static PatternTreeNode<InstLop32i> Lop32i(LogicOp logicOp, int imm)
+ {
+ return new(InstName.Lop32i, (op) => !op.NegA && !op.NegB && !op.X && !op.WriteCC && op.LogicOp == logicOp && op.Imm32 == imm);
+ }
+
+ private static PatternTreeNode<InstMembar> Membar(Membar membar)
+ {
+ return new(InstName.Membar, (op) => op.Membar == membar);
+ }
+
+ private static PatternTreeNode<InstPopcR> Popc()
+ {
+ return new(InstName.Popc, (op) => !op.NegB);
+ }
+
+ private static PatternTreeNode<InstRet> Ret()
+ {
+ return new(InstName.Ret, (op) => op.Ccc == Ccc.T);
+ }
+
+ private static PatternTreeNode<InstS2r> S2r(SReg reg)
+ {
+ return new(InstName.S2r, (op) => op.SReg == reg);
+ }
+
+ private static PatternTreeNode<InstShrI> ShrU32W(int imm)
+ {
+ return new(InstName.Shr, (op) => !op.Signed && !op.Brev && op.M && op.XMode == 0 && op.Imm20 == imm, isImm: true);
+ }
+
+ private static PatternTreeNode<InstLdg> LdgE(CacheOpLd cacheOp, LsSize size, byte order = 0)
+ {
+ return new(InstName.Ldg, (op) => op.E && op.CacheOp == cacheOp && op.LsSize == size, order: order);
+ }
+
+ private static PatternTreeNode<InstRed> RedE(RedOp redOp, AtomSize size, byte order = 0)
+ {
+ return new(InstName.Red, (op) => op.E && op.RedOp == redOp && op.RedSize == size, order: order);
+ }
+
+ private static PatternTreeNode<InstVote> Vote(VoteMode mode)
+ {
+ return new(InstName.Vote, (op) => op.VoteMode == mode);
+ }
+
+ private static PatternTreeNode<InstXmadR> Xmad(XmadCop cop, bool psl = false, bool mrg = false, bool hiloA = false, bool hiloB = false)
+ {
+ return new(InstName.Xmad, (op) => op.XmadCop == cop && op.Psl == psl && op.Mrg == mrg && op.HiloA == hiloA && op.HiloB == hiloB);
+ }
+
+ private static PatternTreeNodeUse PT => PTOrRZ();
+ private static PatternTreeNodeUse RZ => PTOrRZ();
+ private static PatternTreeNodeUse Undef => new PatternTreeNodeUse(0, null);
+
+ private static PatternTreeNodeUse CallArg(int index)
+ {
+ return new PatternTreeNodeUse(-(index + 2), null);
+ }
+
+ private static PatternTreeNodeUse PTOrRZ()
+ {
+ return new PatternTreeNodeUse(-1, null);
+ }
+ }
+
+ private static void PrintTreeNode(TreeNode node, string indentation)
+ {
+ Console.WriteLine($" {node.Op.Name}");
+
+ for (int i = 0; i < node.Uses.Count; i++)
+ {
+ TreeNodeUse use = node.Uses[i];
+ bool last = i == node.Uses.Count - 1;
+ char separator = last ? '`' : '|';
+
+ if (use.Node != null)
+ {
+ Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})");
+ PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
+ }
+ else
+ {
+ Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL");
+ }
+ }
+ }
+
+ private static void PrintTreeNode(IPatternTreeNode node, string indentation)
+ {
+ Console.WriteLine($" {node.Name}");
+
+ for (int i = 0; i < node.Uses.Count; i++)
+ {
+ PatternTreeNodeUse use = node.Uses[i];
+ bool last = i == node.Uses.Count - 1;
+ char separator = last ? '`' : '|';
+
+ if (use.Node != null)
+ {
+ Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})");
+ PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
+ }
+ else
+ {
+ Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL");
+ }
+ }
+ }
+ }
+}