mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-24 13:44:16 +00:00
4da44e09cb
* Make all structs readonly when applicable. It should reduce amount of needless defensive copies * Make structs with trivial boilerplate equality code record structs * Remove unnecessary readonly modifiers from TextureCreateInfo * Make BitMap structs readonly too
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 readonly 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 readonly 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");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|