Merge #930 (pr/wave-lane-state)

This commit is contained in:
kuba 2026-09-30 00:12:17 +02:00
commit d04084b6d5
2 changed files with 41 additions and 16 deletions

View File

@ -1309,7 +1309,7 @@ public static partial class Gen5SpirvTranslator
StoreS(register.Value, value);
}
private enum SharedMemoryPhase { None, Read, Write }
private enum SharedMemoryPhase { None, Read, Write, Unknown }
private bool TryEmitBlock(
IReadOnlyList<ShaderBlock> blocks,
@ -1318,10 +1318,13 @@ public static partial class Gen5SpirvTranslator
{
error = string.Empty;
var block = blocks[blockIndex];
// One guest wave can span two host subgroups. Keep its shared-memory phases ordered.
// Restrict added barriers to a single block, where all invocations follow the same path.
var synchronizeSharedMemory = _emulateWave64 && blocks.Count == 1;
var sharedMemoryPhase = SharedMemoryPhase.None;
// One guest wave can span two host subgroups; the guest orders its lanes' LDS accesses
// by executing in lockstep, so a change between reading and writing needs a barrier
// between the halves. Every guest branch condition is wave-uniform (EXEC and VCC tests
// go through the wave mask, SCC is scalar), so every block is entered by the whole wave
// and may hold a barrier. Another block may have left either phase behind.
var synchronizeSharedMemory = _emulateWave64;
var sharedMemoryPhase = blocks.Count == 1 ? SharedMemoryPhase.None : SharedMemoryPhase.Unknown;
for (var index = block.StartIndex; index < block.EndIndex; index++)
{
var instruction = _request.Program.Instructions[index];
@ -6917,14 +6920,35 @@ public static partial class Gen5SpirvTranslator
Store(ScalarPointer(register), value);
if (register is 106 or 107)
{
Store(_vcc, IsWaveMaskActive(LoadS64(106)));
if (_waveLaneCount != 32 || register == 106)
{
Store(_vcc, IsLaneSetInMaskRegisters(106));
}
}
else if (register is 126 or 127)
{
Store(_exec, IsWaveMaskActive(LoadS64(126)));
if (_waveLaneCount != 32 || register == 126)
{
Store(_exec, IsLaneSetInMaskRegisters(126));
}
}
}
// In wave32 a lane mask is its low register alone (VCC_HI and EXEC_HI are ordinary SGPRs),
// so the lane's bit is tested on one dword instead of a composed 64-bit mask.
private uint IsLaneSetInMaskRegisters(uint lowRegister)
{
if (_waveLaneCount != 32)
{
return IsWaveMaskActive(LoadS64(lowRegister));
}
var laneBit = _subgroupInvocationIdInput == 0
? UInt(1)
: ShiftLeftLogical(UInt(1), GuestWaveLane());
return IsNotZero(BitwiseAnd(LoadS(lowRegister), laneBit));
}
private void StoreV(uint register, uint value, bool guardWithExec = true)
{
if (guardWithExec)

View File

@ -22,7 +22,7 @@ public sealed class Gen5WaveMaskSpirvTests
// the per-lane _vcc predicate from the wave mask via IsWaveMaskActive.
var spirv = Compile([0x7C04_0300u]);
// The lane's bit in single-lane emulation is the 64-bit constant 1, so the
// The lane's bit in single-lane emulation is the constant 1, so the
// predicate is `(mask & 1) != 0`. The whole-word bug emitted `mask != 0`
// with no such mask. Require the lane-bit AND to be present.
Assert.True(
@ -47,26 +47,27 @@ public sealed class Gen5WaveMaskSpirvTests
Assert.NotEmpty(spirv);
}
// True when the module contains an OpBitwiseAnd whose operand is a 64-bit
// constant of value 1 — the current-lane bit that IsCurrentLaneSet masks the
// wave mask with before the non-zero test.
// True when the module contains an OpBitwiseAnd whose operand is an integer
// constant of value 1 — the current-lane bit that the lane test masks the
// wave mask with before the non-zero test. A wave64 mask is a 64-bit value
// and a wave32 mask is a single dword, so either width counts.
private static bool ContainsLaneBitMaskedWaveTest(byte[] spirv)
{
var laneBitConstIds = new HashSet<uint>();
// Pass 1: collect 64-bit OpConstant result-ids whose value is 1.
// Pass 1: collect OpConstant result-ids whose integer value is 1.
foreach (var (op, wordCount, offset) in EnumerateInstructions(spirv))
{
// OpConstant = 43; a 64-bit constant occupies 5 words
// (opcode, resultType, resultId, valueLow, valueHigh).
if (op != 43 || wordCount != 5)
// OpConstant = 43; a 32-bit constant occupies 4 words and a 64-bit one
// 5 (opcode, resultType, resultId, valueLow[, valueHigh]).
if (op != 43 || wordCount is not (4 or 5))
{
continue;
}
var resultId = ReadWord(spirv, offset + 8);
var low = ReadWord(spirv, offset + 12);
var high = ReadWord(spirv, offset + 16);
var high = wordCount == 5 ? ReadWord(spirv, offset + 16) : 0u;
if (low == 1 && high == 0)
{
laneBitConstIds.Add(resultId);