[shader] add scalar control flow graph

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ParantezTech
2026-07-30 05:25:39 +03:00
parent 77f22973cb
commit ae736136d4
@@ -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);
}