Compare commits
1 Commits
1016_part3
...
xnetcat/st
| Author | SHA1 | Date |
|---|---|---|
|
|
164a9918a3 |
|
|
@ -314,6 +314,25 @@ public static partial class Gen5SpirvTranslator
|
|||
}
|
||||
EmitInitialState();
|
||||
|
||||
if (StructuredControlFlow && blocks.Count > 1 && TryStructureRange(blocks, 0, blocks.Count, blocks.Count, emit: false, out _))
|
||||
{
|
||||
// As the dispatcher does before its first block: an out-of-bounds invocation runs nothing.
|
||||
var runLabel = _module.AllocateId();
|
||||
var doneLabel = _module.AllocateId();
|
||||
var isActive = Load(_boolType, _programActive);
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, doneLabel, 0);
|
||||
_module.AddStatement(SpirvOp.BranchConditional, isActive, runLabel, doneLabel);
|
||||
_module.AddLabel(runLabel);
|
||||
if (!TryStructureRange(blocks, 0, blocks.Count, blocks.Count, emit: true, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_module.AddStatement(SpirvOp.Branch, doneLabel);
|
||||
_module.AddLabel(doneLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
var loopHeader = _module.AllocateId();
|
||||
var switchHeader = _module.AllocateId();
|
||||
var switchMerge = _module.AllocateId();
|
||||
|
|
@ -388,6 +407,7 @@ public static partial class Gen5SpirvTranslator
|
|||
loopHeader,
|
||||
loopMerge);
|
||||
_module.AddLabel(loopMerge);
|
||||
}
|
||||
if (_stage == Gen5SpirvStage.Pixel &&
|
||||
Environment.GetEnvironmentVariable(
|
||||
"SHARPEMU_TRACE_TITLE_SHADER_STATE") == "1" &&
|
||||
|
|
@ -1295,6 +1315,20 @@ public static partial class Gen5SpirvTranslator
|
|||
IReadOnlyList<ShaderBlock> blocks,
|
||||
int blockIndex,
|
||||
out string error)
|
||||
{
|
||||
if (!TryEmitBlockBody(blocks, blockIndex, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryEmitBlockTerminator(blocks, blockIndex, out error);
|
||||
}
|
||||
|
||||
// Every instruction of a block except its terminating branch or s_endpgm.
|
||||
private bool TryEmitBlockBody(
|
||||
IReadOnlyList<ShaderBlock> blocks,
|
||||
int blockIndex,
|
||||
out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
var block = blocks[blockIndex];
|
||||
|
|
@ -1337,6 +1371,17 @@ public static partial class Gen5SpirvTranslator
|
|||
}
|
||||
|
||||
if (synchronizeSharedMemory && sharedMemoryPhase != SharedMemoryPhase.None) EmitWave64Barrier();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stores the next dispatcher block (or ends the program) for a block's terminator.
|
||||
private bool TryEmitBlockTerminator(
|
||||
IReadOnlyList<ShaderBlock> blocks,
|
||||
int blockIndex,
|
||||
out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
var block = blocks[blockIndex];
|
||||
var terminator = _request.Program.Instructions[block.EndIndex - 1];
|
||||
if (terminator.Opcode == "SEndpgm")
|
||||
{
|
||||
|
|
@ -1414,6 +1459,182 @@ public static partial class Gen5SpirvTranslator
|
|||
return true;
|
||||
}
|
||||
|
||||
// Forward-only, properly nested control flow is emitted as structured selections instead of
|
||||
// the PC-dispatch loop: a loop around a switch keeps every register live across iterations,
|
||||
// blocks cannot be optimized together, and the Metal compiler spills. SHARPEMU_STRUCTURED_CF=0
|
||||
// keeps the dispatcher for every program.
|
||||
private static readonly bool StructuredControlFlow =
|
||||
Environment.GetEnvironmentVariable("SHARPEMU_STRUCTURED_CF") != "0";
|
||||
|
||||
private static bool IsStructuredCondition(string opcode) => opcode is
|
||||
"SCbranchScc0" or "SCbranchScc1" or "SCbranchVccz" or "SCbranchVccnz" or
|
||||
"SCbranchExecz" or "SCbranchExecnz" or
|
||||
"SCbranchCdbgsys" or "SCbranchCdbguser" or "SCbranchCdbgsysOrUser" or "SCbranchCdbgsysAndUser";
|
||||
|
||||
// The block a branch continues at, or blocks.Count for a branch past the last instruction.
|
||||
private bool TryResolveBranchBlock(IReadOnlyList<ShaderBlock> blocks, Gen5ShaderInstruction branch, out int target)
|
||||
{
|
||||
target = -1;
|
||||
if (!TryGetBranchTargetPc(branch, out var targetPc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsExitBranchTarget(_request.Program.Instructions, targetPc))
|
||||
{
|
||||
target = blocks.Count;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryFindBlock(blocks, targetPc, out target);
|
||||
}
|
||||
|
||||
// Emits (or, without emit, only checks) blocks [begin, end). A branch from the range's last
|
||||
// block to leave falls through to the range end.
|
||||
private bool TryStructureRange(
|
||||
IReadOnlyList<ShaderBlock> blocks,
|
||||
int begin,
|
||||
int end,
|
||||
int leave,
|
||||
bool emit,
|
||||
out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
var instructions = _request.Program.Instructions;
|
||||
var index = begin;
|
||||
while (index < end)
|
||||
{
|
||||
if (emit && !TryEmitBlockBody(blocks, index, out error))
|
||||
{
|
||||
error = $"block=0x{blocks[index].StartPc:X}: {error}";
|
||||
return false;
|
||||
}
|
||||
|
||||
var terminator = instructions[blocks[index].EndIndex - 1];
|
||||
var next = index + 1;
|
||||
if (terminator.Opcode == "SEndpgm")
|
||||
{
|
||||
if (next != blocks.Count)
|
||||
{
|
||||
error = "s_endpgm before the last block";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (emit)
|
||||
{
|
||||
Store(_programActive, _module.ConstantBool(false));
|
||||
}
|
||||
|
||||
index = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (terminator.Opcode == "SBranch")
|
||||
{
|
||||
if (!TryResolveBranchBlock(blocks, terminator, out var branchTarget) ||
|
||||
!(branchTarget == next || (next == end && branchTarget == leave)))
|
||||
{
|
||||
error = $"unstructured s_branch at 0x{terminator.Pc:X}";
|
||||
return false;
|
||||
}
|
||||
|
||||
index = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!terminator.Opcode.StartsWith("SCbranch", StringComparison.Ordinal))
|
||||
{
|
||||
index = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsStructuredCondition(terminator.Opcode) ||
|
||||
!TryResolveBranchBlock(blocks, terminator, out var target) ||
|
||||
target <= index || target > end)
|
||||
{
|
||||
error = $"unstructured {terminator.Opcode} at 0x{terminator.Pc:X}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == next)
|
||||
{
|
||||
index = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if/else: the skipped range ends with an s_branch over the taken range.
|
||||
var thenLast = instructions[blocks[target - 1].EndIndex - 1];
|
||||
if (target < blocks.Count && target - 1 > index && thenLast.Opcode == "SBranch" &&
|
||||
TryResolveBranchBlock(blocks, thenLast, out var joinTarget) && joinTarget > target && joinTarget <= end)
|
||||
{
|
||||
if (!emit)
|
||||
{
|
||||
if (!TryStructureRange(blocks, next, target, joinTarget, emit: false, out error) ||
|
||||
!TryStructureRange(blocks, target, joinTarget, joinTarget, emit: false, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TryGetBranchCondition(terminator.Opcode, out var taken);
|
||||
var thenLabel = _module.AllocateId();
|
||||
var elseLabel = _module.AllocateId();
|
||||
var joinLabel = _module.AllocateId();
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, joinLabel, 0);
|
||||
_module.AddStatement(SpirvOp.BranchConditional, taken, elseLabel, thenLabel);
|
||||
_module.AddLabel(thenLabel);
|
||||
if (!TryStructureRange(blocks, next, target, joinTarget, emit: true, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_module.AddStatement(SpirvOp.Branch, joinLabel);
|
||||
_module.AddLabel(elseLabel);
|
||||
if (!TryStructureRange(blocks, target, joinTarget, joinTarget, emit: true, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_module.AddStatement(SpirvOp.Branch, joinLabel);
|
||||
_module.AddLabel(joinLabel);
|
||||
}
|
||||
|
||||
index = joinTarget;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (!taken) { skipped range }
|
||||
if (!emit)
|
||||
{
|
||||
if (!TryStructureRange(blocks, next, target, target, emit: false, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TryGetBranchCondition(terminator.Opcode, out var taken);
|
||||
var bodyLabel = _module.AllocateId();
|
||||
var mergeLabel = _module.AllocateId();
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
|
||||
_module.AddStatement(SpirvOp.BranchConditional, taken, mergeLabel, bodyLabel);
|
||||
_module.AddLabel(bodyLabel);
|
||||
if (!TryStructureRange(blocks, next, target, target, emit: true, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_module.AddStatement(SpirvOp.Branch, mergeLabel);
|
||||
_module.AddLabel(mergeLabel);
|
||||
}
|
||||
|
||||
index = target;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string FormatBlockStarts(IReadOnlyList<ShaderBlock> blocks)
|
||||
{
|
||||
const int maxBlocks = 32;
|
||||
|
|
|
|||
Loading…
Reference in New Issue