mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-31 23:19:44 +08:00
[shader] add scalar control flow graph
This commit is contained in:
@@ -0,0 +1,161 @@
|
|||||||
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace SharpEmu.ShaderCompiler.Ir;
|
||||||
|
|
||||||
|
public readonly record struct IrBlockRange(uint StartPc, uint EndPc);
|
||||||
|
|
||||||
|
public sealed class IrControlFlowGraph
|
||||||
|
{
|
||||||
|
private IrControlFlowGraph(
|
||||||
|
IReadOnlyList<IrBlockRange> blocks,
|
||||||
|
IReadOnlyDictionary<uint, int> blockByStartPc,
|
||||||
|
IReadOnlyList<IReadOnlyList<int>> successors,
|
||||||
|
IReadOnlyList<IReadOnlyList<int>> predecessors,
|
||||||
|
IReadOnlySet<int> loopHeaders)
|
||||||
|
{
|
||||||
|
Blocks = blocks;
|
||||||
|
BlockByStartPc = blockByStartPc;
|
||||||
|
Successors = successors;
|
||||||
|
Predecessors = predecessors;
|
||||||
|
LoopHeaders = loopHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<IrBlockRange> Blocks { get; }
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<uint, int> BlockByStartPc { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<IReadOnlyList<int>> Successors { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<IReadOnlyList<int>> Predecessors { get; }
|
||||||
|
|
||||||
|
public IReadOnlySet<int> LoopHeaders { get; }
|
||||||
|
|
||||||
|
public bool HasControlFlow => Blocks.Count > 1;
|
||||||
|
|
||||||
|
public static IrControlFlowGraph Build(
|
||||||
|
IReadOnlyList<Gen5ShaderInstruction> instructions,
|
||||||
|
IIrBranchResolver resolver)
|
||||||
|
{
|
||||||
|
var leaders = new SortedSet<uint>();
|
||||||
|
if (instructions.Count > 0)
|
||||||
|
{
|
||||||
|
leaders.Add(instructions[0].Pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index = 0; index < instructions.Count; index++)
|
||||||
|
{
|
||||||
|
var instruction = instructions[index];
|
||||||
|
if (!resolver.IsBranch(instruction))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolver.TryGetBranchTarget(instruction, out var target))
|
||||||
|
{
|
||||||
|
leaders.Add(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index + 1 < instructions.Count)
|
||||||
|
{
|
||||||
|
leaders.Add(instructions[index + 1].Pc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ordered = leaders.ToList();
|
||||||
|
var ranges = new List<IrBlockRange>(ordered.Count);
|
||||||
|
var byStart = new Dictionary<uint, int>();
|
||||||
|
for (var index = 0; index < ordered.Count; index++)
|
||||||
|
{
|
||||||
|
var start = ordered[index];
|
||||||
|
var end = index + 1 < ordered.Count
|
||||||
|
? ordered[index + 1]
|
||||||
|
: instructions.Count > 0 ? instructions[^1].Pc + 1 : start;
|
||||||
|
byStart[start] = ranges.Count;
|
||||||
|
ranges.Add(new IrBlockRange(start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
var successors = new List<List<int>>(ranges.Count);
|
||||||
|
var predecessors = new List<List<int>>(ranges.Count);
|
||||||
|
for (var index = 0; index < ranges.Count; index++)
|
||||||
|
{
|
||||||
|
successors.Add([]);
|
||||||
|
predecessors.Add([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var blockIndex = 0; blockIndex < ranges.Count; blockIndex++)
|
||||||
|
{
|
||||||
|
var range = ranges[blockIndex];
|
||||||
|
var last = instructions
|
||||||
|
.Where(candidate => candidate.Pc >= range.StartPc && candidate.Pc < range.EndPc)
|
||||||
|
.LastOrDefault();
|
||||||
|
if (last is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isBranch = resolver.IsBranch(last);
|
||||||
|
var hasTarget = isBranch && resolver.TryGetBranchTarget(last, out var target) &&
|
||||||
|
byStart.TryGetValue(target, out var targetIndex);
|
||||||
|
if (hasTarget)
|
||||||
|
{
|
||||||
|
_ = resolver.TryGetBranchTarget(last, out var resolved);
|
||||||
|
Link(successors, predecessors, blockIndex, byStart[resolved]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fallsThrough = !isBranch || resolver.IsConditional(last);
|
||||||
|
if (fallsThrough && blockIndex + 1 < ranges.Count)
|
||||||
|
{
|
||||||
|
Link(successors, predecessors, blockIndex, blockIndex + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var headers = new HashSet<int>();
|
||||||
|
for (var blockIndex = 0; blockIndex < ranges.Count; blockIndex++)
|
||||||
|
{
|
||||||
|
foreach (var successor in successors[blockIndex])
|
||||||
|
{
|
||||||
|
if (successor <= blockIndex)
|
||||||
|
{
|
||||||
|
headers.Add(successor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IrControlFlowGraph(
|
||||||
|
ranges,
|
||||||
|
byStart,
|
||||||
|
successors.Select(list => (IReadOnlyList<int>)list).ToList(),
|
||||||
|
predecessors.Select(list => (IReadOnlyList<int>)list).ToList(),
|
||||||
|
headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Link(
|
||||||
|
List<List<int>> successors,
|
||||||
|
List<List<int>> predecessors,
|
||||||
|
int from,
|
||||||
|
int to)
|
||||||
|
{
|
||||||
|
if (!successors[from].Contains(to))
|
||||||
|
{
|
||||||
|
successors[from].Add(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!predecessors[to].Contains(from))
|
||||||
|
{
|
||||||
|
predecessors[to].Add(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IIrBranchResolver
|
||||||
|
{
|
||||||
|
bool IsBranch(Gen5ShaderInstruction instruction);
|
||||||
|
|
||||||
|
bool IsConditional(Gen5ShaderInstruction instruction);
|
||||||
|
|
||||||
|
bool TryGetBranchTarget(Gen5ShaderInstruction instruction, out uint targetPc);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user