 99445dd0a6
			
		
	
	
		99445dd0a6
		
			
		
	
	
	
	
		
			
			* 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
		
			
				
	
	
		
			866 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			866 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |