shader: support returning 64-bit LDS max and compare-exchange
This commit is contained in:
parent
e998f21e71
commit
9e41d8320a
|
|
@ -322,6 +322,8 @@ inline constexpr auto EmitSharedAtomicDec32 = EmitSharedIncDec;
|
||||||
inline constexpr auto EmitSharedAtomicSMin32 = EmitAtomic32;
|
inline constexpr auto EmitSharedAtomicSMin32 = EmitAtomic32;
|
||||||
inline constexpr auto EmitSharedAtomicUMin32 = EmitAtomic32;
|
inline constexpr auto EmitSharedAtomicUMin32 = EmitAtomic32;
|
||||||
inline constexpr auto EmitSharedAtomicSMax32 = EmitAtomic32;
|
inline constexpr auto EmitSharedAtomicSMax32 = EmitAtomic32;
|
||||||
|
inline constexpr auto EmitSharedAtomicUMax64 = EmitSharedAtomic64;
|
||||||
|
inline constexpr auto EmitSharedAtomicCmpSwap64 = EmitSharedAtomic64;
|
||||||
inline constexpr auto EmitSharedAtomicUMax32 = EmitAtomic32;
|
inline constexpr auto EmitSharedAtomicUMax32 = EmitAtomic32;
|
||||||
inline constexpr auto EmitSharedAtomicAnd32 = EmitAtomic32;
|
inline constexpr auto EmitSharedAtomicAnd32 = EmitAtomic32;
|
||||||
inline constexpr auto EmitSharedAtomicOr32 = EmitAtomic32;
|
inline constexpr auto EmitSharedAtomicOr32 = EmitAtomic32;
|
||||||
|
|
|
||||||
|
|
@ -1240,32 +1240,48 @@ void EmitSharedAtomic64(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||||
auto& state = ctx.state;
|
auto& state = ctx.state;
|
||||||
const auto& mem = ctx.Memory(inst);
|
const auto& mem = ctx.Memory(inst);
|
||||||
EnsureLdsStorage(state);
|
EnsureLdsStorage(state);
|
||||||
EmitIfCondition(state, ctx.Arg(inst, 2), [&]() {
|
const auto address = Binary(state, spv::OpBitwiseAnd, TypeU32(state),
|
||||||
const auto address = Binary(state, spv::OpBitwiseAnd, TypeU32(state),
|
ByteAddress(ctx, inst, mem), ConstantU32(state, 0xfff8u));
|
||||||
ByteAddress(ctx, inst, mem), ConstantU32(state, 0xfff8u));
|
const auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), address,
|
||||||
const auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), address,
|
ConstantU32(state, 3u));
|
||||||
ConstantU32(state, 3u));
|
const auto in_bounds = Binary(state, spv::OpULessThan, TypeBool(state), index,
|
||||||
const auto in_bounds = Binary(state, spv::OpULessThan, TypeBool(state), index,
|
ConstantU32(state, LdsDwordCount(state) / 2u));
|
||||||
ConstantU32(state, LdsDwordCount(state) / 2u));
|
const auto active = EmitLogicalAndBool(state, ctx.Arg(inst, inst.NumArgs() - 1), in_bounds);
|
||||||
EmitIfCondition(state, in_bounds, [&]() {
|
const auto emit = [&]() {
|
||||||
auto native_index = index;
|
auto native_index = index;
|
||||||
auto semantics = spv::MemorySemanticsWorkgroupMemoryMask;
|
auto semantics = spv::MemorySemanticsWorkgroupMemoryMask;
|
||||||
if (state.lds_storage_class == spv::StorageClassStorageBuffer) {
|
if (state.lds_storage_class == spv::StorageClassStorageBuffer) {
|
||||||
native_index = EmitAddU32(state, index, EmitBinaryU32(
|
native_index = EmitAddU32(state, index, EmitBinaryU32(
|
||||||
state, spv::OpShiftRightLogical, state.lds_base_dwords, ConstantU32(state, 1)));
|
state, spv::OpShiftRightLogical, state.lds_base_dwords, ConstantU32(state, 1)));
|
||||||
semantics = spv::MemorySemanticsUniformMemoryMask;
|
semantics = spv::MemorySemanticsUniformMemoryMask;
|
||||||
}
|
}
|
||||||
const auto pointer = state.builder.AllocateId();
|
const auto pointer = state.builder.AllocateId();
|
||||||
|
state.builder.AddFunction(
|
||||||
|
spv::OpAccessChain, TypePointer(state, state.lds_storage_class, TypeU64(state)),
|
||||||
|
pointer, state.lds_u64_variable, ConstantU32(state, 0), native_index);
|
||||||
|
const auto old = state.builder.AllocateId();
|
||||||
|
const auto opcode = SpirvAtomicOpcode(inst.GetOpcode());
|
||||||
|
const auto scope = ConstantU32(state, spv::ScopeWorkgroup);
|
||||||
|
const auto acquire_release =
|
||||||
|
ConstantU32(state, spv::MemorySemanticsAcquireReleaseMask | semantics);
|
||||||
|
if (opcode == spv::OpAtomicCompareExchange) {
|
||||||
|
// RDNA2 DS compares DATA and stores DATA2, opposite to BUFFER atomics.
|
||||||
state.builder.AddFunction(
|
state.builder.AddFunction(
|
||||||
spv::OpAccessChain, TypePointer(state, state.lds_storage_class, TypeU64(state)),
|
opcode, TypeU64(state), old, pointer, scope, acquire_release,
|
||||||
pointer, state.lds_u64_variable, ConstantU32(state, 0), native_index);
|
ConstantU32(state, spv::MemorySemanticsAcquireMask | semantics),
|
||||||
const auto value = ctx.Arg(inst, 1);
|
ctx.Arg(inst, 2), ctx.Arg(inst, 1));
|
||||||
state.builder.AddFunction(
|
} else {
|
||||||
SpirvAtomicOpcode(inst.GetOpcode()), TypeU64(state), state.builder.AllocateId(),
|
state.builder.AddFunction(opcode, TypeU64(state), old, pointer, scope,
|
||||||
pointer, ConstantU32(state, spv::ScopeWorkgroup),
|
acquire_release, ctx.Arg(inst, 1));
|
||||||
ConstantU32(state, spv::MemorySemanticsAcquireReleaseMask | semantics), value);
|
}
|
||||||
});
|
return old;
|
||||||
});
|
};
|
||||||
|
if (inst.HasUses()) {
|
||||||
|
ctx.Define(inst, EmitValueOrDefaultIfCondition(
|
||||||
|
state, active, TypeU64(state), ConstantU64(state, 0), emit));
|
||||||
|
} else {
|
||||||
|
EmitIfCondition(state, active, emit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EmitBufferFloatAtomic(ValueEmitContext& ctx, const IR::Inst& inst) {
|
uint32_t EmitBufferFloatAtomic(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||||
|
|
|
||||||
|
|
@ -464,6 +464,7 @@ uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::Buffer
|
||||||
|
|
||||||
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
|
case IR::ValueOpcode::SharedAtomicCmpSwap64:
|
||||||
case IR::ValueOpcode::ImageAtomicCompareSwap32:
|
case IR::ValueOpcode::ImageAtomicCompareSwap32:
|
||||||
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
|
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
|
||||||
case IR::ValueOpcode::ImageAtomicSwap32:
|
case IR::ValueOpcode::ImageAtomicSwap32:
|
||||||
|
|
@ -491,7 +492,8 @@ spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
||||||
case IR::ValueOpcode::ImageAtomicUMax32:
|
case IR::ValueOpcode::ImageAtomicUMax32:
|
||||||
case IR::ValueOpcode::ImageAtomicUMax64:
|
case IR::ValueOpcode::ImageAtomicUMax64:
|
||||||
case IR::ValueOpcode::BufferAtomicUMax32:
|
case IR::ValueOpcode::BufferAtomicUMax32:
|
||||||
case IR::ValueOpcode::SharedAtomicUMax32: return spv::OpAtomicUMax;
|
case IR::ValueOpcode::SharedAtomicUMax32:
|
||||||
|
case IR::ValueOpcode::SharedAtomicUMax64: return spv::OpAtomicUMax;
|
||||||
case IR::ValueOpcode::ImageAtomicAnd32:
|
case IR::ValueOpcode::ImageAtomicAnd32:
|
||||||
case IR::ValueOpcode::ImageAtomicAnd64:
|
case IR::ValueOpcode::ImageAtomicAnd64:
|
||||||
case IR::ValueOpcode::BufferAtomicAnd32:
|
case IR::ValueOpcode::BufferAtomicAnd32:
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
||||||
{0x3fu, Opcode::DS_ORDERED_COUNT, 1, 32},
|
{0x3fu, Opcode::DS_ORDERED_COUNT, 1, 32},
|
||||||
{0x40u, Opcode::DS_ADD_U64, 2, 32}, {0x4au, Opcode::DS_OR_B64, 2, 32},
|
{0x40u, Opcode::DS_ADD_U64, 2, 32}, {0x4au, Opcode::DS_OR_B64, 2, 32},
|
||||||
{0x4du, Opcode::DS_WRITE_B64, 2, 32}, {0x4eu, Opcode::DS_WRITE2_B64, 4, 32},
|
{0x4du, Opcode::DS_WRITE_B64, 2, 32}, {0x4eu, Opcode::DS_WRITE2_B64, 4, 32},
|
||||||
{0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32},
|
{0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x68u, Opcode::DS_MAX_RTN_U64, 2, 32},
|
||||||
|
{0x70u, Opcode::DS_CMPST_RTN_B64, 2, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32},
|
||||||
{0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32},
|
{0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32},
|
||||||
{0xa0u, Opcode::DS_WRITE_B8_D16_HI, 1, 8},
|
{0xa0u, Opcode::DS_WRITE_B8_D16_HI, 1, 8},
|
||||||
{0xa1u, Opcode::DS_WRITE_B16_D16_HI, 1, 16},
|
{0xa1u, Opcode::DS_WRITE_B16_D16_HI, 1, 16},
|
||||||
|
|
@ -192,6 +193,8 @@ bool IsDsAtomicOpcode(Opcode opcode) {
|
||||||
case Opcode::DS_MIN_RTN_U32:
|
case Opcode::DS_MIN_RTN_U32:
|
||||||
case Opcode::DS_MAX_U32:
|
case Opcode::DS_MAX_U32:
|
||||||
case Opcode::DS_MAX_RTN_U32:
|
case Opcode::DS_MAX_RTN_U32:
|
||||||
|
case Opcode::DS_MAX_RTN_U64:
|
||||||
|
case Opcode::DS_CMPST_RTN_B64:
|
||||||
case Opcode::DS_AND_B32:
|
case Opcode::DS_AND_B32:
|
||||||
case Opcode::DS_AND_RTN_B32:
|
case Opcode::DS_AND_RTN_B32:
|
||||||
case Opcode::DS_OR_B32:
|
case Opcode::DS_OR_B32:
|
||||||
|
|
@ -211,6 +214,7 @@ uint32_t DsSourceCount(Opcode opcode) {
|
||||||
case Opcode::DS_WRITE2ST64_B32:
|
case Opcode::DS_WRITE2ST64_B32:
|
||||||
case Opcode::DS_WRITE2_B64:
|
case Opcode::DS_WRITE2_B64:
|
||||||
case Opcode::DS_WRITE2ST64_B64:
|
case Opcode::DS_WRITE2ST64_B64:
|
||||||
|
case Opcode::DS_CMPST_RTN_B64:
|
||||||
case Opcode::DS_MSKOR_B32: return 3u;
|
case Opcode::DS_MSKOR_B32: return 3u;
|
||||||
case Opcode::DS_MIN_F32:
|
case Opcode::DS_MIN_F32:
|
||||||
case Opcode::DS_MAX_F32:
|
case Opcode::DS_MAX_F32:
|
||||||
|
|
|
||||||
|
|
@ -708,6 +708,8 @@ std::string InstructionToString(const Instruction& inst) {
|
||||||
case Opcode::DS_MIN_RTN_U32:
|
case Opcode::DS_MIN_RTN_U32:
|
||||||
case Opcode::DS_MAX_U32:
|
case Opcode::DS_MAX_U32:
|
||||||
case Opcode::DS_MAX_RTN_U32:
|
case Opcode::DS_MAX_RTN_U32:
|
||||||
|
case Opcode::DS_MAX_RTN_U64:
|
||||||
|
case Opcode::DS_CMPST_RTN_B64:
|
||||||
case Opcode::DS_AND_B32:
|
case Opcode::DS_AND_B32:
|
||||||
case Opcode::DS_AND_RTN_B32:
|
case Opcode::DS_AND_RTN_B32:
|
||||||
case Opcode::DS_OR_B32:
|
case Opcode::DS_OR_B32:
|
||||||
|
|
|
||||||
|
|
@ -624,6 +624,8 @@ enum class Opcode {
|
||||||
DS_MIN_RTN_U32,
|
DS_MIN_RTN_U32,
|
||||||
DS_MAX_U32,
|
DS_MAX_U32,
|
||||||
DS_MAX_RTN_U32,
|
DS_MAX_RTN_U32,
|
||||||
|
DS_MAX_RTN_U64,
|
||||||
|
DS_CMPST_RTN_B64,
|
||||||
DS_AND_B32,
|
DS_AND_B32,
|
||||||
DS_AND_RTN_B32,
|
DS_AND_RTN_B32,
|
||||||
DS_OR_B32,
|
DS_OR_B32,
|
||||||
|
|
|
||||||
|
|
@ -578,11 +578,13 @@ void Translator::DS_ATOMIC(const Decoder::Instruction& inst, IR::ValueOpcode opc
|
||||||
bool returns_value) {
|
bool returns_value) {
|
||||||
const auto memory = MemoryInfoFromDecoded(inst);
|
const auto memory = MemoryInfoFromDecoded(inst);
|
||||||
const auto address = ReadU32(inst.src0);
|
const auto address = ReadU32(inst.src0);
|
||||||
const IR::Value value =
|
const auto read = [&](const Decoder::Operand& operand) -> IR::Value {
|
||||||
memory.data_dwords == 2u ? IR::Value(ReadU64(inst.src1)) : IR::Value(ReadU32(inst.src1));
|
return memory.data_dwords == 2u ? IR::Value(ReadU64(operand)) : IR::Value(ReadU32(operand));
|
||||||
|
};
|
||||||
|
const auto value = read(inst.src1);
|
||||||
const auto flags = AddMemoryInfo(memory, inst.pc);
|
const auto flags = AddMemoryInfo(memory, inst.pc);
|
||||||
const auto result = inst.src_count == 3u
|
const auto result = inst.src_count == 3u
|
||||||
? ir.Emit(opcode, {address, value, ReadU32(inst.src2), ir.GetExec()}, flags)
|
? ir.Emit(opcode, {address, value, read(inst.src2), ir.GetExec()}, flags)
|
||||||
: ir.Emit(opcode, {address, value, ir.GetExec()}, flags);
|
: ir.Emit(opcode, {address, value, ir.GetExec()}, flags);
|
||||||
if (returns_value) {
|
if (returns_value) {
|
||||||
WriteOperand(inst.dst, result);
|
WriteOperand(inst.dst, result);
|
||||||
|
|
@ -1073,6 +1075,10 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, false);
|
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, false);
|
||||||
case Decoder::Opcode::DS_MAX_RTN_U32:
|
case Decoder::Opcode::DS_MAX_RTN_U32:
|
||||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, true);
|
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, true);
|
||||||
|
case Decoder::Opcode::DS_MAX_RTN_U64:
|
||||||
|
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax64, true);
|
||||||
|
case Decoder::Opcode::DS_CMPST_RTN_B64:
|
||||||
|
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicCmpSwap64, true);
|
||||||
case Decoder::Opcode::DS_AND_B32:
|
case Decoder::Opcode::DS_AND_B32:
|
||||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicAnd32, false);
|
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicAnd32, false);
|
||||||
case Decoder::Opcode::DS_AND_RTN_B32:
|
case Decoder::Opcode::DS_AND_RTN_B32:
|
||||||
|
|
|
||||||
|
|
@ -948,9 +948,14 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
|
||||||
switch (inst.opcode) {
|
switch (inst.opcode) {
|
||||||
case Decoder::Opcode::DS_ADD_U64:
|
case Decoder::Opcode::DS_ADD_U64:
|
||||||
case Decoder::Opcode::DS_OR_B64:
|
case Decoder::Opcode::DS_OR_B64:
|
||||||
|
case Decoder::Opcode::DS_MAX_RTN_U64:
|
||||||
case Decoder::Opcode::DS_WRITE_B64:
|
case Decoder::Opcode::DS_WRITE_B64:
|
||||||
case Decoder::Opcode::DS_WRITE_B96:
|
case Decoder::Opcode::DS_WRITE_B96:
|
||||||
case Decoder::Opcode::DS_WRITE_B128: include_vector(inst.src1, inst.data_dwords); break;
|
case Decoder::Opcode::DS_WRITE_B128: include_vector(inst.src1, inst.data_dwords); break;
|
||||||
|
case Decoder::Opcode::DS_CMPST_RTN_B64:
|
||||||
|
include_vector(inst.src1, 2u);
|
||||||
|
include_vector(inst.src2, 2u);
|
||||||
|
break;
|
||||||
case Decoder::Opcode::DS_WRITE2_B32:
|
case Decoder::Opcode::DS_WRITE2_B32:
|
||||||
case Decoder::Opcode::DS_WRITE2ST64_B32:
|
case Decoder::Opcode::DS_WRITE2ST64_B32:
|
||||||
case Decoder::Opcode::DS_WRITE2_B64:
|
case Decoder::Opcode::DS_WRITE2_B64:
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,8 @@ SharedAccess SharedAccessOf(ValueOpcode opcode) {
|
||||||
case ValueOpcode::SharedAtomicUMin32:
|
case ValueOpcode::SharedAtomicUMin32:
|
||||||
case ValueOpcode::SharedAtomicSMax32:
|
case ValueOpcode::SharedAtomicSMax32:
|
||||||
case ValueOpcode::SharedAtomicUMax32:
|
case ValueOpcode::SharedAtomicUMax32:
|
||||||
|
case ValueOpcode::SharedAtomicUMax64:
|
||||||
|
case ValueOpcode::SharedAtomicCmpSwap64:
|
||||||
case ValueOpcode::SharedAtomicAnd32:
|
case ValueOpcode::SharedAtomicAnd32:
|
||||||
case ValueOpcode::SharedAtomicOr32:
|
case ValueOpcode::SharedAtomicOr32:
|
||||||
case ValueOpcode::SharedAtomicOr64:
|
case ValueOpcode::SharedAtomicOr64:
|
||||||
|
|
@ -190,6 +192,8 @@ uint32_t SharedComponentCount(ValueOpcode opcode) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case ValueOpcode::SharedAtomicIAdd64:
|
case ValueOpcode::SharedAtomicIAdd64:
|
||||||
case ValueOpcode::SharedAtomicOr64:
|
case ValueOpcode::SharedAtomicOr64:
|
||||||
|
case ValueOpcode::SharedAtomicUMax64:
|
||||||
|
case ValueOpcode::SharedAtomicCmpSwap64:
|
||||||
case ValueOpcode::LoadSharedU32x2:
|
case ValueOpcode::LoadSharedU32x2:
|
||||||
case ValueOpcode::WriteSharedU32x2: return 2u;
|
case ValueOpcode::WriteSharedU32x2: return 2u;
|
||||||
case ValueOpcode::LoadSharedU32x3:
|
case ValueOpcode::LoadSharedU32x3:
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,8 @@ VALUE_OPCODE(SharedAtomicSMin32, U32, U32, U32, U1)
|
||||||
VALUE_OPCODE(SharedAtomicUMin32, U32, U32, U32, U1)
|
VALUE_OPCODE(SharedAtomicUMin32, U32, U32, U32, U1)
|
||||||
VALUE_OPCODE(SharedAtomicSMax32, U32, U32, U32, U1)
|
VALUE_OPCODE(SharedAtomicSMax32, U32, U32, U32, U1)
|
||||||
VALUE_OPCODE(SharedAtomicUMax32, U32, U32, U32, U1)
|
VALUE_OPCODE(SharedAtomicUMax32, U32, U32, U32, U1)
|
||||||
|
VALUE_OPCODE(SharedAtomicUMax64, U64, U32, U64, U1)
|
||||||
|
VALUE_OPCODE(SharedAtomicCmpSwap64, U64, U32, U64, U64, U1)
|
||||||
VALUE_OPCODE(SharedAtomicAnd32, U32, U32, U32, U1)
|
VALUE_OPCODE(SharedAtomicAnd32, U32, U32, U32, U1)
|
||||||
VALUE_OPCODE(SharedAtomicOr32, U32, U32, U32, U1)
|
VALUE_OPCODE(SharedAtomicOr32, U32, U32, U32, U1)
|
||||||
VALUE_OPCODE(SharedAtomicOr64, Void, U32, U64, U1)
|
VALUE_OPCODE(SharedAtomicOr64, Void, U32, U64, U1)
|
||||||
|
|
|
||||||
|
|
@ -20100,6 +20100,8 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||||
case Opcode::FLAT_STORE_DWORDX4:
|
case Opcode::FLAT_STORE_DWORDX4:
|
||||||
case Opcode::DS_ADD_U32:
|
case Opcode::DS_ADD_U32:
|
||||||
case Opcode::DS_ADD_U64:
|
case Opcode::DS_ADD_U64:
|
||||||
|
case Opcode::DS_MAX_RTN_U64:
|
||||||
|
case Opcode::DS_CMPST_RTN_B64:
|
||||||
case Opcode::DS_ADD_RTN_U32:
|
case Opcode::DS_ADD_RTN_U32:
|
||||||
case Opcode::DS_SUB_U32:
|
case Opcode::DS_SUB_U32:
|
||||||
case Opcode::DS_SUB_RTN_U32:
|
case Opcode::DS_SUB_RTN_U32:
|
||||||
|
|
@ -33184,6 +33186,90 @@ TestCase DsAtomic64Bounds(bool add) {
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase DsAtomicReturn64() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
TestCase test;
|
||||||
|
test.name = "DsAtomicReturn64";
|
||||||
|
auto &code = test.code;
|
||||||
|
const auto pair = [&](u32 reg, uint64_t value) {
|
||||||
|
AppendVMovLiteral(&code, reg, static_cast<u32>(value));
|
||||||
|
AppendVMovLiteral(&code, reg + 1u, static_cast<u32>(value >> 32u));
|
||||||
|
};
|
||||||
|
const auto result = [&](u32 reg, uint64_t value) {
|
||||||
|
AppendStoreVgpr(&code, reg, static_cast<u32>(test.expected.size()));
|
||||||
|
test.expected.push_back(static_cast<u32>(value));
|
||||||
|
AppendStoreVgpr(&code, reg + 1u, static_cast<u32>(test.expected.size()));
|
||||||
|
test.expected.push_back(static_cast<u32>(value >> 32u));
|
||||||
|
};
|
||||||
|
AppendVMovU32(&code, 7, 0);
|
||||||
|
AppendVMovLiteral(&code, 6, 0x13579bdfu);
|
||||||
|
code.push_back(EncodeDs0(0x0d, 24));
|
||||||
|
code.push_back(EncodeDs1(0, 6, 7));
|
||||||
|
struct Case { uint64_t before, value; u32 address, offset; };
|
||||||
|
constexpr std::array cases{
|
||||||
|
Case{0x7fffffffffffffffull, 0x8000000000000000ull, 0, 8},
|
||||||
|
Case{0x8000000000000000ull, 0x7fffffffffffffffull, 0, 8},
|
||||||
|
Case{0x8000000000000001ull, 0x8000000000000002ull, 0x10007, 2},
|
||||||
|
Case{0xffffffffffffffffull, 0, 0, 8}};
|
||||||
|
for (const auto &item : cases) {
|
||||||
|
pair(2, item.before);
|
||||||
|
code.push_back(EncodeDs0(0x4d, 8));
|
||||||
|
code.push_back(EncodeDs1(0, 2, 7));
|
||||||
|
pair(4, item.value);
|
||||||
|
AppendVMovLiteral(&code, 0, item.address);
|
||||||
|
// CS23e96bf5029f62fd pc 0xec8: d9a00008 00000400. VDst aliases ADDR.
|
||||||
|
code.push_back(EncodeDs0(0x68, item.offset));
|
||||||
|
code.push_back(0x00000400u);
|
||||||
|
result(0, item.before);
|
||||||
|
code.push_back(EncodeDs0(0x76, 8));
|
||||||
|
code.push_back(EncodeDs1(8, 0, 7));
|
||||||
|
result(8, std::max(item.before, item.value));
|
||||||
|
}
|
||||||
|
struct CompareCase { uint64_t before, compare, replacement; };
|
||||||
|
constexpr std::array compare_cases{
|
||||||
|
CompareCase{0xfedcba9876543210ull, 0xfedcba9876543210ull, 0x0123456789abcdefull},
|
||||||
|
CompareCase{0xfedcba9876543210ull, 0x0edcba9876543210ull, UINT64_MAX},
|
||||||
|
CompareCase{0xfedcba9876543210ull, 0xfedcba9876543211ull, UINT64_MAX},
|
||||||
|
CompareCase{UINT64_MAX, UINT64_MAX, 0}};
|
||||||
|
AppendVMovU32(&code, 63, 0);
|
||||||
|
for (const auto &item : compare_cases) {
|
||||||
|
pair(4, item.before);
|
||||||
|
code.push_back(EncodeDs0(0x4d, 16));
|
||||||
|
code.push_back(EncodeDs1(0, 4, 7));
|
||||||
|
pair(0, item.compare);
|
||||||
|
pair(2, item.replacement);
|
||||||
|
// pc 0x1048: DATA compares, DATA2 replaces, VDst aliases DATA2.
|
||||||
|
code.insert(code.end(), {0xd9c00010u, 0x0202003fu});
|
||||||
|
result(2, item.before);
|
||||||
|
code.push_back(EncodeDs0(0x76, 16));
|
||||||
|
code.push_back(EncodeDs1(8, 0, 7));
|
||||||
|
result(8, item.before == item.compare ? item.replacement : item.before);
|
||||||
|
}
|
||||||
|
pair(4, UINT64_MAX);
|
||||||
|
AppendVMovU32(&code, 0, 24); // One DWORD remains: ignore the entire atomic.
|
||||||
|
code.push_back(EncodeDs0(0x68));
|
||||||
|
code.push_back(EncodeDs1(2, 4, 0));
|
||||||
|
result(2, 0);
|
||||||
|
pair(2, 0xabcdef0112345678ull);
|
||||||
|
AppendVMovU32(&code, 0, 0);
|
||||||
|
code.push_back(EncodeSMovB32(126, InlineU32(0)));
|
||||||
|
code.insert(code.end(), {0xd9a00008u, 0x02000400u});
|
||||||
|
code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||||
|
result(2, 0xabcdef0112345678ull); // Inactive lanes retain both destination DWORDs.
|
||||||
|
code.push_back(EncodeDs0(0x36, 24));
|
||||||
|
code.push_back(EncodeDs1(6, 0, 7));
|
||||||
|
AppendStoreVgpr(&code, 6, static_cast<u32>(test.expected.size()));
|
||||||
|
test.expected.push_back(0x13579bdfu);
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.compute_info.lds_size_dwords = 7;
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::DS_WRITE_B32, O::DS_WRITE_B64,
|
||||||
|
O::DS_MAX_RTN_U64, O::DS_CMPST_RTN_B64, O::DS_READ_B32, O::DS_READ_B64,
|
||||||
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.required_spirv = {"OpAtomicUMax %ulong", "OpAtomicCompareExchange %ulong", "WorkgroupMemoryExplicitLayoutKHR", "Aliased"};
|
||||||
|
test.forbidden_spirv = {"OpAtomicLoad", "OpLoopMerge"};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase DsAtomic64Contention(bool add, u32 wave_size) {
|
TestCase DsAtomic64Contention(bool add, u32 wave_size) {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
TestCase test;
|
TestCase test;
|
||||||
|
|
@ -39204,6 +39290,7 @@ std::vector<TestCase> MakeCases() {
|
||||||
cases.push_back(DsMaskedOrContention(wave_size));
|
cases.push_back(DsMaskedOrContention(wave_size));
|
||||||
}
|
}
|
||||||
AddCase(DsAtomicReturnVariants);
|
AddCase(DsAtomicReturnVariants);
|
||||||
|
AddCase(DsAtomicReturn64);
|
||||||
for (bool add : {false, true}) {
|
for (bool add : {false, true}) {
|
||||||
cases.push_back(DsAtomic64Bounds(add));
|
cases.push_back(DsAtomic64Bounds(add));
|
||||||
for (u32 wave_size : {32, 64}) {
|
for (u32 wave_size : {32, 64}) {
|
||||||
|
|
@ -44656,7 +44743,7 @@ int main(int argc, char **argv) {
|
||||||
RunCase(&vulkan, DsWideLdsPartialBounds());
|
RunCase(&vulkan, DsWideLdsPartialBounds());
|
||||||
RunCase(&vulkan, DsAtomic64Bounds(false));
|
RunCase(&vulkan, DsAtomic64Bounds(false));
|
||||||
RunCase(&vulkan, DsAtomic64Bounds(true));
|
RunCase(&vulkan, DsAtomic64Bounds(true));
|
||||||
for (auto test : {DsWideLdsPartialBounds(), DsAtomic64Bounds(false), DsAtomic64Bounds(true)}) {
|
for (auto test : {DsWideLdsPartialBounds(), DsAtomic64Bounds(false), DsAtomic64Bounds(true), DsAtomicReturn64()}) {
|
||||||
test.compute_info.lds_storage = true;
|
test.compute_info.lds_storage = true;
|
||||||
std::erase(test.required_spirv, "WorkgroupMemoryExplicitLayoutKHR");
|
std::erase(test.required_spirv, "WorkgroupMemoryExplicitLayoutKHR");
|
||||||
test.required_spirv.push_back("StorageBuffer");
|
test.required_spirv.push_back("StorageBuffer");
|
||||||
|
|
@ -44700,6 +44787,7 @@ int main(int argc, char **argv) {
|
||||||
RunCase(&vulkan, DsAtomic64Contention(add, wave_size));
|
RunCase(&vulkan, DsAtomic64Contention(add, wave_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RunCase(&vulkan, DsAtomicReturn64());
|
||||||
RunCase(&vulkan, BufferAtomicAndX2GlcAndExec());
|
RunCase(&vulkan, BufferAtomicAndX2GlcAndExec());
|
||||||
RunCase(&vulkan, ImageAtomicSignedMinMax<false>());
|
RunCase(&vulkan, ImageAtomicSignedMinMax<false>());
|
||||||
RunCase(&vulkan, ImageAtomicSignedMinMax<true>());
|
RunCase(&vulkan, ImageAtomicSignedMinMax<true>());
|
||||||
|
|
|
||||||
|
|
@ -7913,6 +7913,29 @@ void TestNewShaderRecompilerDsAddtidTranslation() {
|
||||||
CheckSpirvBinaryValidates(result.spirv);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestDsReturningAtomic64Decoder() {
|
||||||
|
using namespace ShaderRecompiler::Decoder;
|
||||||
|
// CS23e96bf5029f62fd: native DATA is the comparison and DATA2 the replacement.
|
||||||
|
const uint32_t max_words[] = {0xd9a00008u, 0x00000400u};
|
||||||
|
const uint32_t cmp_words[] = {0xd9c00010u, 0x0202003fu};
|
||||||
|
Instruction decoded;
|
||||||
|
DecodeInstruction(max_words, 0u, decoded);
|
||||||
|
Check(decoded.opcode == Opcode::DS_MAX_RTN_U64 &&
|
||||||
|
decoded.word_count == 2u && decoded.data_dwords == 2u &&
|
||||||
|
decoded.data_bits == 32u && decoded.src_count == 2u &&
|
||||||
|
decoded.src0.reg == 0u && decoded.src1.reg == 4u &&
|
||||||
|
decoded.dst.reg == 0u && decoded.offset == 8u && !decoded.gds,
|
||||||
|
"captured DS_MAX_RTN_U64 lost its QWORD width, byte offset, or aliases");
|
||||||
|
DecodeInstruction(cmp_words, 0u, decoded);
|
||||||
|
Check(decoded.opcode == Opcode::DS_CMPST_RTN_B64 &&
|
||||||
|
decoded.word_count == 2u && decoded.data_dwords == 2u &&
|
||||||
|
decoded.data_bits == 32u && decoded.src_count == 3u &&
|
||||||
|
decoded.src0.reg == 63u && decoded.src1.reg == 0u &&
|
||||||
|
decoded.src2.reg == 2u && decoded.dst.reg == 2u &&
|
||||||
|
decoded.offset == 16u && !decoded.gds,
|
||||||
|
"captured DS_CMPST_RTN_B64 lost its comparison/replacement pairs or aliases");
|
||||||
|
}
|
||||||
|
|
||||||
void TestDsFloatMinMaxDecoder() {
|
void TestDsFloatMinMaxDecoder() {
|
||||||
using namespace ShaderRecompiler::Decoder;
|
using namespace ShaderRecompiler::Decoder;
|
||||||
for (bool max_value : {false, true}) {
|
for (bool max_value : {false, true}) {
|
||||||
|
|
@ -15716,6 +15739,7 @@ int main() {
|
||||||
// here.
|
// here.
|
||||||
TestScalarAshrI64Decoder();
|
TestScalarAshrI64Decoder();
|
||||||
TestImageAtomicWidthDecoder();
|
TestImageAtomicWidthDecoder();
|
||||||
|
TestDsReturningAtomic64Decoder();
|
||||||
TestDsFloatMinMaxDecoder();
|
TestDsFloatMinMaxDecoder();
|
||||||
TestNewShaderDecoderArchitecture();
|
TestNewShaderDecoderArchitecture();
|
||||||
TestImageAddressOperands();
|
TestImageAddressOperands();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue