refactor(shader): preserve native wide shared operations

This commit is contained in:
nmzik
2026-08-19 09:11:25 +02:00
parent c9e942a972
commit c236ba713d
25 changed files with 1486 additions and 986 deletions
@@ -65,11 +65,6 @@ IR::Value Translator::GetSamplerResource(const IR::MemoryInfo& memory) {
GetResourceDword(memory.sampler, 2), GetResourceDword(memory.sampler, 3)});
}
IR::Value Translator::GetSharedResource(IR::ResourceKind kind) {
return ir.Emit(kind == IR::ResourceKind::Gds ? IR::ValueOpcode::GetGdsResource
: IR::ValueOpcode::GetLdsResource);
}
IR::Value Translator::MakeImageAddress(const IR::Instruction& inst, const IR::Operand& base) {
std::array<IR::Value, 13> components {};
components[0] = ReadRawU32(PlainOperand(base));
@@ -170,22 +165,7 @@ IR::ValueOpcode Translator::BufferAtomicOpcode(IR::Opcode opcode) {
}
}
IR::ValueOpcode Translator::SharedAtomicOpcode(IR::Opcode opcode, bool gds) {
if (gds) {
switch (opcode) {
case IR::Opcode::AtomicSwapU32: return IR::ValueOpcode::GdsAtomicSwap32;
case IR::Opcode::AtomicAddU32: return IR::ValueOpcode::GdsAtomicIAdd32;
case IR::Opcode::AtomicSubU32: return IR::ValueOpcode::GdsAtomicISub32;
case IR::Opcode::AtomicSMinI32: return IR::ValueOpcode::GdsAtomicSMin32;
case IR::Opcode::AtomicUMinU32: return IR::ValueOpcode::GdsAtomicUMin32;
case IR::Opcode::AtomicSMaxI32: return IR::ValueOpcode::GdsAtomicSMax32;
case IR::Opcode::AtomicUMaxU32: return IR::ValueOpcode::GdsAtomicUMax32;
case IR::Opcode::AtomicAndU32: return IR::ValueOpcode::GdsAtomicAnd32;
case IR::Opcode::AtomicOrU32: return IR::ValueOpcode::GdsAtomicOr32;
case IR::Opcode::AtomicXorU32: return IR::ValueOpcode::GdsAtomicXor32;
default: EXIT("invalid GDS atomic opcode");
}
}
IR::ValueOpcode Translator::SharedAtomicOpcode(IR::Opcode opcode) {
switch (opcode) {
case IR::Opcode::AtomicSwapU32: return IR::ValueOpcode::SharedAtomicSwap32;
case IR::Opcode::AtomicAddU32: return IR::ValueOpcode::SharedAtomicIAdd32;
@@ -197,7 +177,7 @@ IR::ValueOpcode Translator::SharedAtomicOpcode(IR::Opcode opcode, bool gds) {
case IR::Opcode::AtomicAndU32: return IR::ValueOpcode::SharedAtomicAnd32;
case IR::Opcode::AtomicOrU32: return IR::ValueOpcode::SharedAtomicOr32;
case IR::Opcode::AtomicXorU32: return IR::ValueOpcode::SharedAtomicXor32;
default: EXIT("invalid LDS atomic opcode");
default: EXIT("invalid shared atomic opcode");
}
}
@@ -363,13 +343,9 @@ bool Translator::TranslateAtomicMemory(const IR::Instruction& inst) {
}
case IR::ResourceKind::Lds:
case IR::ResourceKind::Gds: {
const bool gds = inst.memory.kind == IR::ResourceKind::Gds;
const auto resource = GetSharedResource(inst.memory.kind);
const auto address =
ir.IAdd(ReadU32(inst.src[1]), IR::U32(IR::Value(inst.memory.offset)));
result = ir.Emit(SharedAtomicOpcode(inst.op, gds),
{resource, address, ReadU32(inst.src[0]), ir.GetExec()},
AddMemoryInfo(inst));
const auto address = ReadU32(inst.src[1]);
result = ir.Emit(SharedAtomicOpcode(inst.op),
{address, ReadU32(inst.src[0]), ir.GetExec()}, AddMemoryInfo(inst));
break;
}
default: return false;
@@ -518,10 +494,51 @@ bool Translator::TranslateSharedMemory(const IR::Instruction& inst) {
if (!shared && inst.op != IR::Opcode::DsSwizzleB32) {
return false;
}
const bool gds = inst.memory.kind == IR::ResourceKind::Gds;
const auto resource = shared ? GetSharedResource(inst.memory.kind) : IR::Value {};
const auto shared_address = [&](uint32_t source) {
return ir.IAdd(ReadU32(inst.src[source]), IR::U32(IR::Value(inst.memory.offset)));
const auto shared_address = [&](uint32_t source) { return ReadU32(inst.src[source]); };
const auto load_u32 = [&](uint32_t width, IR::U32 address, const IR::Instruction& source) {
IR::ValueOpcode opcode;
switch (width) {
case 1u: opcode = IR::ValueOpcode::LoadSharedU32; break;
case 2u: opcode = IR::ValueOpcode::LoadSharedU32x2; break;
case 3u: opcode = IR::ValueOpcode::LoadSharedU32x3; break;
case 4u: opcode = IR::ValueOpcode::LoadSharedU32x4; break;
default: EXIT("invalid shared load width");
}
return ir.Emit(opcode, {address, ir.GetExec()}, AddMemoryInfo(source));
};
const auto extract_u32 = [&](IR::Value value, uint32_t width, uint32_t index) {
if (width == 1u) {
return value;
}
const auto opcode = width == 2u ? IR::ValueOpcode::CompositeExtractU32x2
: width == 3u ? IR::ValueOpcode::CompositeExtractU32x3
: IR::ValueOpcode::CompositeExtractU32x4;
return ir.Emit(opcode, {value, IR::Value(index)});
};
const auto write_u32 = [&](uint32_t width, IR::U32 address,
const std::array<IR::Value, 4>& values,
const IR::Instruction& source) {
switch (width) {
case 1u:
ir.Emit(IR::ValueOpcode::WriteSharedU32, {address, values[0], ir.GetExec()},
AddMemoryInfo(source));
break;
case 2u:
ir.Emit(IR::ValueOpcode::WriteSharedU32x2,
{address, values[0], values[1], ir.GetExec()}, AddMemoryInfo(source));
break;
case 3u:
ir.Emit(IR::ValueOpcode::WriteSharedU32x3,
{address, values[0], values[1], values[2], ir.GetExec()},
AddMemoryInfo(source));
break;
case 4u:
ir.Emit(IR::ValueOpcode::WriteSharedU32x4,
{address, values[0], values[1], values[2], values[3], ir.GetExec()},
AddMemoryInfo(source));
break;
default: EXIT("invalid shared store width");
}
};
switch (inst.op) {
case IR::Opcode::DsReadUbyte:
@@ -533,65 +550,109 @@ bool Translator::TranslateSharedMemory(const IR::Instruction& inst) {
uint32_t bits;
bool sign;
if (inst.op == IR::Opcode::DsReadUbyte || inst.op == IR::Opcode::DsReadSbyte) {
opcode = gds ? IR::ValueOpcode::LoadGdsU8 : IR::ValueOpcode::LoadSharedU8;
opcode = IR::ValueOpcode::LoadSharedU8;
bits = 8u;
sign = inst.op == IR::Opcode::DsReadSbyte;
} else if (inst.op == IR::Opcode::DsReadUshort || inst.op == IR::Opcode::DsReadSshort) {
opcode = gds ? IR::ValueOpcode::LoadGdsU16 : IR::ValueOpcode::LoadSharedU16;
opcode = IR::ValueOpcode::LoadSharedU16;
bits = 16u;
sign = inst.op == IR::Opcode::DsReadSshort;
} else {
opcode = gds ? IR::ValueOpcode::LoadGdsU32 : IR::ValueOpcode::LoadSharedU32;
bits = 32u;
sign = false;
const auto width = inst.memory.data_dwords;
const auto loaded = load_u32(width, shared_address(0), inst);
for (uint32_t index = 0; index < width; index++) {
WriteOperand(OffsetOperand(inst.dst, index), extract_u32(loaded, width, index));
}
return true;
}
const auto loaded =
ir.Emit(opcode, {resource, shared_address(0), ir.GetExec()}, AddMemoryInfo(inst));
ir.Emit(opcode, {shared_address(0), ir.GetExec()}, AddMemoryInfo(inst));
WriteOperand(inst.dst, bits == 32u ? loaded : WidenSubdword(loaded, bits, sign));
return true;
}
case IR::Opcode::DsRead2B32: {
const auto width = inst.memory.data_dwords / 2u;
const auto address = shared_address(0);
auto first = inst;
first.memory.data_dwords = width;
first.memory.component_count = width;
const auto first_value = load_u32(width, address, first);
IR::Value second_value = first_value;
if (inst.memory.secondary_offset != inst.memory.offset) {
auto second = first;
second.memory.offset = inst.memory.secondary_offset;
second_value = load_u32(width, address, second);
}
for (uint32_t index = 0; index < width; index++) {
WriteOperand(OffsetOperand(inst.dst, index),
extract_u32(first_value, width, index));
WriteOperand(OffsetOperand(inst.dst, width + index),
extract_u32(second_value, width, index));
}
return true;
}
case IR::Opcode::DsWriteByte:
case IR::Opcode::DsWriteShort:
case IR::Opcode::DsWriteB32: {
const auto data = ReadU32(inst.src[0]);
const auto width = inst.memory.data_dwords;
std::array<IR::Value, 4> values {};
for (uint32_t index = 0; index < width; index++) {
values[index] = ReadU32(OffsetOperand(inst.src[0], index));
}
const auto data = IR::U32(values[0]);
const auto address = shared_address(1);
IR::ValueOpcode opcode;
IR::Value value;
if (inst.op == IR::Opcode::DsWriteByte) {
opcode = gds ? IR::ValueOpcode::WriteGdsU8 : IR::ValueOpcode::WriteSharedU8;
opcode = IR::ValueOpcode::WriteSharedU8;
value = NarrowSubdword(data, 8u);
} else if (inst.op == IR::Opcode::DsWriteShort) {
opcode = gds ? IR::ValueOpcode::WriteGdsU16 : IR::ValueOpcode::WriteSharedU16;
opcode = IR::ValueOpcode::WriteSharedU16;
value = NarrowSubdword(data, 16u);
} else {
opcode = gds ? IR::ValueOpcode::WriteGdsU32 : IR::ValueOpcode::WriteSharedU32;
value = data;
write_u32(width, address, values, inst);
return true;
}
ir.Emit(opcode, {address, value, ir.GetExec()}, AddMemoryInfo(inst));
return true;
}
case IR::Opcode::DsWrite2B32: {
const auto width = inst.memory.data_dwords / 2u;
const auto address = shared_address(1);
std::array<IR::Value, 4> first_values {};
std::array<IR::Value, 4> second_values {};
for (uint32_t index = 0; index < width; index++) {
first_values[index] = ReadU32(OffsetOperand(inst.src[0], index));
second_values[index] = ReadU32(OffsetOperand(inst.src[2], index));
}
auto first = inst;
first.memory.data_dwords = width;
first.memory.component_count = width;
write_u32(width, address, first_values, first);
if (inst.memory.secondary_offset != inst.memory.offset) {
auto second = first;
second.memory.offset = inst.memory.secondary_offset;
write_u32(width, address, second_values, second);
}
ir.Emit(opcode, {resource, address, value, ir.GetExec()}, AddMemoryInfo(inst));
return true;
}
case IR::Opcode::DsMinF32:
case IR::Opcode::DsMaxF32: {
const auto opcode =
gds ? (inst.op == IR::Opcode::DsMinF32 ? IR::ValueOpcode::GdsAtomicFMin32
: IR::ValueOpcode::GdsAtomicFMax32)
: (inst.op == IR::Opcode::DsMinF32 ? IR::ValueOpcode::SharedAtomicFMin32
: IR::ValueOpcode::SharedAtomicFMax32);
const auto opcode = inst.op == IR::Opcode::DsMinF32
? IR::ValueOpcode::SharedAtomicFMin32
: IR::ValueOpcode::SharedAtomicFMax32;
ir.Emit(opcode,
{resource, shared_address(1), ReadU32(inst.src[0]), ReadU32(inst.src[2]),
ir.GetExec()},
{shared_address(1), ReadU32(inst.src[0]), ReadU32(inst.src[2]), ir.GetExec()},
AddMemoryInfo(inst));
return true;
}
case IR::Opcode::DsAppend:
case IR::Opcode::DsConsume: {
const bool append = inst.op == IR::Opcode::DsAppend;
const auto opcode =
gds ? (append ? IR::ValueOpcode::GdsDataAppend : IR::ValueOpcode::GdsDataConsume)
: (append ? IR::ValueOpcode::DataAppend : IR::ValueOpcode::DataConsume);
const auto opcode = append ? IR::ValueOpcode::DataAppend : IR::ValueOpcode::DataConsume;
WriteOperand(inst.dst, ir.Emit(opcode,
{resource, ReadU32(inst.src[0]), ir.GetExec(),
ir.GetExecLo(), ir.GetExecHi()},
{ReadU32(inst.src[0]), ir.GetExec(), ir.GetExecLo(),
ir.GetExecHi()},
AddMemoryInfo(inst)));
return true;
}
@@ -601,16 +662,13 @@ bool Translator::TranslateSharedMemory(const IR::Instruction& inst) {
const auto base =
ir.BitwiseAnd(ReadU32(inst.src[m0_index]), IR::U32(IR::Value(0xffffu)));
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
const auto address = ir.IAdd(ir.IAdd(base, IR::U32(IR::Value(inst.memory.offset))),
ir.ShiftLeftLogical(lane, IR::U32(IR::Value(2u))));
const auto address = ir.IAdd(base, ir.ShiftLeftLogical(lane, IR::U32(IR::Value(2u))));
if (inst.op == IR::Opcode::DsWriteAddtidB32) {
ir.Emit(IR::ValueOpcode::WriteSharedU32,
{resource, address, ReadU32(inst.src[0]), ir.GetExec()},
AddMemoryInfo(inst));
{address, ReadU32(inst.src[0]), ir.GetExec()}, AddMemoryInfo(inst));
} else {
WriteOperand(inst.dst,
ir.Emit(IR::ValueOpcode::LoadSharedU32,
{resource, address, ir.GetExec()}, AddMemoryInfo(inst)));
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::LoadSharedU32,
{address, ir.GetExec()}, AddMemoryInfo(inst)));
}
return true;
}
@@ -676,9 +734,11 @@ bool Translator::TranslateMemoryOperation(const IR::Instruction& inst,
case IR::Opcode::DsReadUshort:
case IR::Opcode::DsReadSshort:
case IR::Opcode::DsReadB32:
case IR::Opcode::DsRead2B32:
case IR::Opcode::DsWriteByte:
case IR::Opcode::DsWriteShort:
case IR::Opcode::DsWriteB32:
case IR::Opcode::DsWrite2B32:
case IR::Opcode::DsMinF32:
case IR::Opcode::DsMaxF32:
case IR::Opcode::DsSwizzleB32:
@@ -768,6 +768,14 @@ bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
include_vector(inst.dst, std::max(inst.memory.data_dwords, 1u));
} else if (inst.op == IR::Opcode::BufferStoreDword) {
include_vector(inst.src[0], std::max(inst.memory.data_dwords, 1u));
} else if (inst.op == IR::Opcode::DsReadB32 || inst.op == IR::Opcode::DsRead2B32) {
include_vector(inst.dst, std::max(inst.memory.data_dwords, 1u));
} else if (inst.op == IR::Opcode::DsWriteB32) {
include_vector(inst.src[0], std::max(inst.memory.data_dwords, 1u));
} else if (inst.op == IR::Opcode::DsWrite2B32) {
const auto width = std::max(inst.memory.data_dwords / 2u, 1u);
include_vector(inst.src[0], width);
include_vector(inst.src[2], width);
} else if (inst.op == IR::Opcode::LoadInputF32) {
include_vector(inst.dst, std::max(inst.input_info.component_count, 1u));
}
@@ -75,7 +75,6 @@ private:
IR::Value GetScalarAddressResource(uint32_t base);
IR::Value GetImageResource(const IR::MemoryInfo& memory);
IR::Value GetSamplerResource(const IR::MemoryInfo& memory);
IR::Value GetSharedResource(IR::ResourceKind kind);
IR::Value MakeImageAddress(const IR::Instruction& inst, const IR::Operand& base);
IR::Value ConstructU32x4(const IR::Operand& base, uint32_t count);
void WriteImageComponents(const IR::Operand& dst, IR::Value value, uint32_t dmask,
@@ -85,7 +84,7 @@ private:
IR::U32 WidenSubdword(IR::Value value, uint32_t bits, bool sign);
IR::Value NarrowSubdword(IR::U32 value, uint32_t bits);
IR::ValueOpcode BufferAtomicOpcode(IR::Opcode opcode);
IR::ValueOpcode SharedAtomicOpcode(IR::Opcode opcode, bool gds);
IR::ValueOpcode SharedAtomicOpcode(IR::Opcode opcode);
bool IsScalarMemoryLoadOperation(IR::Opcode opcode);
ScalarMemorySourceValues ReadScalarMemorySource(const IR::Instruction& inst);
bool TranslateScalarMemory(const IR::Instruction& inst,