diff options
Diffstat (limited to 'Ryujinx.Graphics.Shader/Decoders/Decoder.cs')
| -rw-r--r-- | Ryujinx.Graphics.Shader/Decoders/Decoder.cs | 230 |
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; } |
