shader: preserve full region addresses for LDS and GDS reads and writes

This commit is contained in:
nmzik 2026-10-12 19:31:20 +02:00
parent 460a196fca
commit d692d8ee08
2 changed files with 52 additions and 8 deletions

View File

@ -276,14 +276,8 @@ uint32_t ByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Memo
uint32_t DwordIndex(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
uint32_t prefix = 0) {
auto address = ByteAddress(ctx, inst, mem, prefix);
if (mem.kind == IR::ResourceKind::Lds || mem.kind == IR::ResourceKind::Gds) {
// RDNA2 DS region addresses retain bits [15:2] after adding the byte offset.
address = Binary(ctx.state, spv::OpBitwiseAnd, TypeU32(ctx.state), address,
ConstantU32(ctx.state, 0xffffu));
}
return Binary(ctx.state, spv::OpShiftRightLogical, TypeU32(ctx.state),
address, ConstantU32(ctx.state, 2));
ByteAddress(ctx, inst, mem, prefix), ConstantU32(ctx.state, 2));
}
uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
@ -632,7 +626,12 @@ template <typename Fn>
uint32_t EmitAtomicAccess(ValueEmitContext& ctx, const IR::Inst& inst,
const IR::MemoryInfo& mem, Fn&& operation) {
return EmitValueOrZeroIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
const auto index = DwordIndex(ctx, inst, mem);
auto index = DwordIndex(ctx, inst, mem);
if (mem.kind == IR::ResourceKind::Lds || mem.kind == IR::ResourceKind::Gds) {
// Keep region-address truncation local to DS atomics.
index = Binary(ctx.state, spv::OpBitwiseAnd, TypeU32(ctx.state), index,
ConstantU32(ctx.state, 0x3fffu));
}
const auto resource = PrepareMemoryResourceAccess(ctx.state, mem);
return EmitValueOrZeroIfCondition(
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {

View File

@ -32259,6 +32259,45 @@ TestCase DsReadWriteVariants() {
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase DsReadWriteFullRegionAddress(u32 wave_size, bool gds) {
using O = ShaderOpcode;
TestCase test;
test.name = gds ? (wave_size == 32u ? "DsGdsFullAddress32" : "DsGdsFullAddress64")
: (wave_size == 32u ? "DsLdsFullAddress32" : "DsLdsFullAddress64");
auto &code = test.code;
code.push_back(EncodeVop2(0x1a, 1, InlineU32(2), 0));
code.push_back(EncodeVop2(0x25, 2, InlineU32(1), 0));
code.insert(code.end(), {EncodeDs0(0x0d, 4, gds), EncodeDs1(0, 2, 1)});
AppendVMovLiteral(&code, 3, 0x10000u);
code.push_back(EncodeVop2(0x25, 3, Vgpr(1), 3));
AppendVMovLiteral(&code, 4, 0xdeadbeefu);
// A high region address must not alias the valid word in the same lane.
code.insert(code.end(), {EncodeDs0(0x0d, 4, gds), EncodeDs1(0, 4, 3),
EncodeDs0(0x36, 4, gds), EncodeDs1(5, 0, 1),
EncodeDs0(0x36, 4, gds), EncodeDs1(6, 0, 3)});
AppendStoreVgprAtLaneDwordOffset(&code, 5, 0, 0);
AppendStoreVgprAtLaneDwordOffset(&code, 6, 0, wave_size);
AppendEnd(&code);
test.initial.assign(wave_size * 2u, 0xdeadbeefu);
test.expected.resize(wave_size * 2u);
std::iota(test.expected.begin(), test.expected.begin() + wave_size, 1u);
test.compute_info.threads_num[0] = wave_size;
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1u;
test.compute_info.thread_ids_num = 1;
test.compute_info.wave_size = wave_size;
test.compute_info.lds_size_dwords = wave_size + 1u;
test.has_compute_info = true;
if (gds) {
test.gds_initial.resize(wave_size + 1u);
test.expected_gds.resize(wave_size + 1u);
std::iota(test.expected_gds.begin(), test.expected_gds.end(), 0u);
}
test.opcodes = {O::V_LSHLREV_B32, O::V_ADD_NC_U32, O::V_MOV_B32, O::DS_WRITE_B32,
O::DS_READ_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.forbidden_spirv = {"%uint_65535"};
return test;
}
TestCase DsReadU16D16CapturedPreservesHighHalf() {
using O = ShaderOpcode;
@ -38119,6 +38158,9 @@ std::vector<TestCase> MakeCases() {
}
}
AddCase(DsReadWriteVariants);
for (u32 wave_size : {32u, 64u})
for (bool gds : {false, true})
cases.push_back(DsReadWriteFullRegionAddress(wave_size, gds));
AddCase(DsWriteB16D16HiCapturedUsesHighHalf);
AddCase(DsWriteB8D16HiWritesByteTwo);
AddCase(DsReadU16D16CapturedPreservesHighHalf);
@ -43566,6 +43608,9 @@ int main(int argc, char **argv) {
if (argc == 2 && std::strcmp(argv[1], "--lds-limit-only") == 0) {
VulkanHarness vulkan;
CheckComputeLdsLimit(vulkan);
for (u32 wave_size : {32u, 64u})
for (bool gds : {false, true})
RunCase(&vulkan, DsReadWriteFullRegionAddress(wave_size, gds));
RunCase(&vulkan, DsWideLdsPartialBounds());
RunCase(&vulkan, DsAtomic64Bounds(false));
RunCase(&vulkan, DsAtomic64Bounds(true));