aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Shader/Decoders/Decoder.cs')
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Decoder.cs230
1 files changed, 124 insertions, 106 deletions
diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
index 3f08bdd9..ca45aab5 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
@@ -9,149 +9,172 @@ namespace Ryujinx.Graphics.Shader.Decoders
{
static class Decoder
{
- public static Block[] Decode(IGpuAccessor gpuAccessor, ulong startAddress)
+ public static Block[][] Decode(IGpuAccessor gpuAccessor, ulong startAddress)
{
- List<Block> blocks = new List<Block>();
+ List<Block[]> funcs = new List<Block[]>();
- Queue<Block> workQueue = new Queue<Block>();
+ Queue<ulong> funcQueue = new Queue<ulong>();
+ HashSet<ulong> funcVisited = new HashSet<ulong>();
- Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
-
- Block GetBlock(ulong blkAddress)
+ void EnqueueFunction(ulong funcAddress)
{
- if (!visited.TryGetValue(blkAddress, out Block block))
+ if (funcVisited.Add(funcAddress))
{
- block = new Block(blkAddress);
-
- workQueue.Enqueue(block);
-
- visited.Add(blkAddress, block);
+ funcQueue.Enqueue(funcAddress);
}
-
- return block;
}
- GetBlock(0);
+ funcQueue.Enqueue(0);
- while (workQueue.TryDequeue(out Block currBlock))
+ while (funcQueue.TryDequeue(out ulong funcAddress))
{
- // Check if the current block is inside another block.
- if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
- {
- Block nBlock = blocks[nBlkIndex];
+ List<Block> blocks = new List<Block>();
+ Queue<Block> workQueue = new Queue<Block>();
+ Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
- if (nBlock.Address == currBlock.Address)
+ Block GetBlock(ulong blkAddress)
+ {
+ if (!visited.TryGetValue(blkAddress, out Block block))
{
- throw new InvalidOperationException("Found duplicate block address on the list.");
- }
-
- nBlock.Split(currBlock);
+ block = new Block(blkAddress);
- blocks.Insert(nBlkIndex + 1, currBlock);
+ workQueue.Enqueue(block);
+ visited.Add(blkAddress, block);
+ }
- continue;
+ return block;
}
- // If we have a block after the current one, set the limit address.
- ulong limitAddress = ulong.MaxValue;
+ GetBlock(funcAddress);
- if (nBlkIndex != blocks.Count)
+ while (workQueue.TryDequeue(out Block currBlock))
{
- Block nBlock = blocks[nBlkIndex];
+ // Check if the current block is inside another block.
+ if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
+ {
+ Block nBlock = blocks[nBlkIndex];
- int nextIndex = nBlkIndex + 1;
+ if (nBlock.Address == currBlock.Address)
+ {
+ throw new InvalidOperationException("Found duplicate block address on the list.");
+ }
- if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
- {
- limitAddress = blocks[nextIndex].Address;
+ nBlock.Split(currBlock);
+ blocks.Insert(nBlkIndex + 1, currBlock);
+
+ continue;
}
- else if (nBlock.Address > currBlock.Address)
+
+ // If we have a block after the current one, set the limit address.
+ ulong limitAddress = ulong.MaxValue;
+
+ if (nBlkIndex != blocks.Count)
{
- limitAddress = blocks[nBlkIndex].Address;
+ Block nBlock = blocks[nBlkIndex];
+
+ int nextIndex = nBlkIndex + 1;
+
+ if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
+ {
+ limitAddress = blocks[nextIndex].Address;
+ }
+ else if (nBlock.Address > currBlock.Address)
+ {
+ limitAddress = blocks[nBlkIndex].Address;
+ }
}
- }
- FillBlock(gpuAccessor, currBlock, limitAddress, startAddress);
+ FillBlock(gpuAccessor, currBlock, limitAddress, startAddress);
- if (currBlock.OpCodes.Count != 0)
- {
- // We should have blocks for all possible branch targets,
- // including those from SSY/PBK instructions.
- foreach (OpCodePush pushOp in currBlock.PushOpCodes)
+ if (currBlock.OpCodes.Count != 0)
{
- GetBlock(pushOp.GetAbsoluteAddress());
+ // We should have blocks for all possible branch targets,
+ // including those from SSY/PBK instructions.
+ foreach (OpCodePush pushOp in currBlock.PushOpCodes)
+ {
+ GetBlock(pushOp.GetAbsoluteAddress());
+ }
+
+ // Set child blocks. "Branch" is the block the branch instruction
+ // points to (when taken), "Next" is the block at the next address,
+ // executed when the branch is not taken. For Unconditional Branches
+ // or end of program, Next is null.
+ OpCode lastOp = currBlock.GetLastOp();
+
+ if (lastOp is OpCodeBranch opBr)
+ {
+ if (lastOp.Emitter == InstEmit.Cal)
+ {
+ EnqueueFunction(opBr.GetAbsoluteAddress());
+ }
+ else
+ {
+ currBlock.Branch = GetBlock(opBr.GetAbsoluteAddress());
+ }
+ }
+ else if (lastOp is OpCodeBranchIndir opBrIndir)
+ {
+ // An indirect branch could go anywhere, we don't know the target.
+ // Those instructions are usually used on a switch to jump table
+ // compiler optimization, and in those cases the possible targets
+ // seems to be always right after the BRX itself. We can assume
+ // that the possible targets are all the blocks in-between the
+ // instruction right after the BRX, and the common target that
+ // all the "cases" should eventually jump to, acting as the
+ // switch break.
+ Block firstTarget = GetBlock(currBlock.EndAddress);
+
+ firstTarget.BrIndir = opBrIndir;
+
+ opBrIndir.PossibleTargets.Add(firstTarget);
+ }
+
+ if (!IsUnconditionalBranch(lastOp))
+ {
+ currBlock.Next = GetBlock(currBlock.EndAddress);
+ }
}
- // Set child blocks. "Branch" is the block the branch instruction
- // points to (when taken), "Next" is the block at the next address,
- // executed when the branch is not taken. For Unconditional Branches
- // or end of program, Next is null.
- OpCode lastOp = currBlock.GetLastOp();
-
- if (lastOp is OpCodeBranch opBr)
+ // Insert the new block on the list (sorted by address).
+ if (blocks.Count != 0)
{
- currBlock.Branch = GetBlock(opBr.GetAbsoluteAddress());
+ Block nBlock = blocks[nBlkIndex];
+
+ blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
}
- else if (lastOp is OpCodeBranchIndir opBrIndir)
+ else
{
- // An indirect branch could go anywhere, we don't know the target.
- // Those instructions are usually used on a switch to jump table
- // compiler optimization, and in those cases the possible targets
- // seems to be always right after the BRX itself. We can assume
- // that the possible targets are all the blocks in-between the
- // instruction right after the BRX, and the common target that
- // all the "cases" should eventually jump to, acting as the
- // switch break.
- Block firstTarget = GetBlock(currBlock.EndAddress);
-
- firstTarget.BrIndir = opBrIndir;
-
- opBrIndir.PossibleTargets.Add(firstTarget);
+ blocks.Add(currBlock);
}
- if (!IsUnconditionalBranch(lastOp))
+ // Do we have a block after the current one?
+ if (currBlock.BrIndir != null && HasBlockAfter(gpuAccessor, currBlock, startAddress))
{
- currBlock.Next = GetBlock(currBlock.EndAddress);
- }
- }
+ bool targetVisited = visited.ContainsKey(currBlock.EndAddress);
- // Insert the new block on the list (sorted by address).
- if (blocks.Count != 0)
- {
- Block nBlock = blocks[nBlkIndex];
+ Block possibleTarget = GetBlock(currBlock.EndAddress);
- blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
- }
- else
- {
- blocks.Add(currBlock);
+ currBlock.BrIndir.PossibleTargets.Add(possibleTarget);
+
+ if (!targetVisited)
+ {
+ possibleTarget.BrIndir = currBlock.BrIndir;
+ }
+ }
}
- // Do we have a block after the current one?
- if (currBlock.BrIndir != null && HasBlockAfter(gpuAccessor, currBlock, startAddress))
+ foreach (Block block in blocks.Where(x => x.PushOpCodes.Count != 0))
{
- bool targetVisited = visited.ContainsKey(currBlock.EndAddress);
-
- Block possibleTarget = GetBlock(currBlock.EndAddress);
-
- currBlock.BrIndir.PossibleTargets.Add(possibleTarget);
-
- if (!targetVisited)
+ for (int pushOpIndex = 0; pushOpIndex < block.PushOpCodes.Count; pushOpIndex++)
{
- possibleTarget.BrIndir = currBlock.BrIndir;
+ PropagatePushOp(visited, block, pushOpIndex);
}
}
- }
- foreach (Block block in blocks.Where(x => x.PushOpCodes.Count != 0))
- {
- for (int pushOpIndex = 0; pushOpIndex < block.PushOpCodes.Count; pushOpIndex++)
- {
- PropagatePushOp(visited, block, pushOpIndex);
- }
+ funcs.Add(blocks.ToArray());
}
- return blocks.ToArray();
+ return funcs.ToArray();
}
private static bool HasBlockAfter(IGpuAccessor gpuAccessor, Block currBlock, ulong startAdddress)
@@ -251,7 +274,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
block.OpCodes.Add(op);
}
- while (!IsBranch(block.GetLastOp()));
+ while (!IsControlFlowChange(block.GetLastOp()));
block.EndAddress = address;
@@ -260,7 +283,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
private static bool IsUnconditionalBranch(OpCode opCode)
{
- return IsUnconditional(opCode) && IsBranch(opCode);
+ return IsUnconditional(opCode) && IsControlFlowChange(opCode);
}
private static bool IsUnconditional(OpCode opCode)
@@ -273,7 +296,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
return opCode.Predicate.Index == RegisterConsts.PredicateTrueIndex && !opCode.InvertPredicate;
}
- private static bool IsBranch(OpCode opCode)
+ private static bool IsControlFlowChange(OpCode opCode)
{
return (opCode is OpCodeBranch opBranch && !opBranch.PushTarget) ||
opCode is OpCodeBranchIndir ||
@@ -281,11 +304,6 @@ namespace Ryujinx.Graphics.Shader.Decoders
opCode is OpCodeExit;
}
- private static bool IsExit(OpCode opCode)
- {
- return opCode is OpCodeExit;
- }
-
private struct PathBlockState
{
public Block Block { get; }