Recompiler: Implement IMAGE_ATOMIC_CMPSWAP (#4109)

* To implement ImageAtomicCmpSwap

...but it doesn't work, so here it shall stay.

* a fix

* Clang

* Add to MayHaveSideEffects

I missed this while digging through IR code.
This commit is contained in:
Stephen Miller 2026-03-09 11:02:22 -05:00 committed by GitHub
parent df6bb8562e
commit 85476e55ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 36 additions and 2 deletions

View File

@ -140,6 +140,15 @@ Id ImageAtomicF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id va
const auto [scope, semantics]{AtomicArgs(ctx)}; const auto [scope, semantics]{AtomicArgs(ctx)};
return (ctx.*atomic_func)(ctx.F32[1], pointer, scope, semantics, value); return (ctx.*atomic_func)(ctx.F32[1], pointer, scope, semantics, value);
} }
Id ImageAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value,
Id cmp_value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) {
const auto& texture = ctx.images[handle & 0xFFFF];
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, texture.id, coords, ctx.ConstU32(0U))};
const auto [scope, semantics]{AtomicArgs(ctx)};
return (ctx.*atomic_func)(ctx.F32[1], pointer, scope, semantics, semantics, value, cmp_value);
}
} // Anonymous namespace } // Anonymous namespace
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) {
@ -420,6 +429,12 @@ Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id co
return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicExchange); return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicExchange);
} }
Id EmitImageAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value,
Id cmp_value) {
return ImageAtomicU32CmpSwap(ctx, inst, handle, coords, value, cmp_value,
&Sirit::Module::OpAtomicCompareExchange);
}
Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) { Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) {
const auto& buffer = ctx.buffers[binding]; const auto& buffer = ctx.buffers[binding];
const auto [id, pointer_type] = buffer.Alias(PointerType::U32); const auto [id, pointer_type] = buffer.Alias(PointerType::U32);

View File

@ -456,6 +456,8 @@ Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value);
Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords); Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords);
Id EmitLaneId(EmitContext& ctx); Id EmitLaneId(EmitContext& ctx);
Id EmitWarpId(EmitContext& ctx); Id EmitWarpId(EmitContext& ctx);

View File

@ -3430,8 +3430,8 @@ constexpr std::array<InstFormat, 112> InstructionFormatMIMG = {{
{InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32,
ScalarType::Uint32}, ScalarType::Uint32},
// 16 = IMAGE_ATOMIC_CMPSWAP // 16 = IMAGE_ATOMIC_CMPSWAP
{InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32,
ScalarType::Undefined}, ScalarType::Uint32},
// 17 = IMAGE_ATOMIC_ADD // 17 = IMAGE_ATOMIC_ADD
{InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32,
ScalarType::Uint32}, ScalarType::Uint32},

View File

@ -137,6 +137,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
// Image atomic operations // Image atomic operations
case Opcode::IMAGE_ATOMIC_SWAP: case Opcode::IMAGE_ATOMIC_SWAP:
return IMAGE_ATOMIC(AtomicOp::Swap, inst); return IMAGE_ATOMIC(AtomicOp::Swap, inst);
case Opcode::IMAGE_ATOMIC_CMPSWAP:
return IMAGE_ATOMIC(AtomicOp::CmpSwap, inst);
case Opcode::IMAGE_ATOMIC_ADD: case Opcode::IMAGE_ATOMIC_ADD:
return IMAGE_ATOMIC(AtomicOp::Add, inst); return IMAGE_ATOMIC(AtomicOp::Add, inst);
case Opcode::IMAGE_ATOMIC_SMIN: case Opcode::IMAGE_ATOMIC_SMIN:
@ -520,6 +522,10 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
switch (op) { switch (op) {
case AtomicOp::Swap: case AtomicOp::Swap:
return ir.ImageAtomicExchange(handle, body, value, {}); return ir.ImageAtomicExchange(handle, body, value, {});
case AtomicOp::CmpSwap: {
const IR::Value cmp_val = ir.GetVectorReg(val_reg + 1);
return ir.ImageAtomicCmpSwap(handle, body, value, cmp_val, info);
}
case AtomicOp::Add: case AtomicOp::Add:
return ir.ImageAtomicIAdd(handle, body, value, info); return ir.ImageAtomicIAdd(handle, body, value, info);
case AtomicOp::Smin: case AtomicOp::Smin:

View File

@ -2055,6 +2055,11 @@ Value IREmitter::ImageAtomicExchange(const Value& handle, const Value& coords, c
return Inst(Opcode::ImageAtomicExchange32, Flags{info}, handle, coords, value); return Inst(Opcode::ImageAtomicExchange32, Flags{info}, handle, coords, value);
} }
Value IREmitter::ImageAtomicCmpSwap(const Value& handle, const Value& coords, const Value& value,
const Value& cmp_value, TextureInstInfo info) {
return Inst(Opcode::ImageAtomicCmpSwap32, Flags{info}, handle, coords, value, cmp_value);
}
Value IREmitter::ImageSampleRaw(const Value& image_handle, const Value& sampler_handle, Value IREmitter::ImageSampleRaw(const Value& image_handle, const Value& sampler_handle,
const Value& address1, const Value& address2, const Value& address3, const Value& address1, const Value& address2, const Value& address3,
const Value& address4, TextureInstInfo info) { const Value& address4, TextureInstInfo info) {

View File

@ -360,6 +360,9 @@ public:
TextureInstInfo info); TextureInstInfo info);
[[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords, [[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info); const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicCmpSwap(const Value& handle, const Value& coords,
const Value& value, const Value& cmp_value,
TextureInstInfo info);
[[nodiscard]] Value ImageSampleRaw(const Value& image_handle, const Value& sampler_handle, [[nodiscard]] Value ImageSampleRaw(const Value& image_handle, const Value& sampler_handle,
const Value& address1, const Value& address2, const Value& address1, const Value& address2,

View File

@ -123,6 +123,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::ImageAtomicOr32: case Opcode::ImageAtomicOr32:
case Opcode::ImageAtomicXor32: case Opcode::ImageAtomicXor32:
case Opcode::ImageAtomicExchange32: case Opcode::ImageAtomicExchange32:
case Opcode::ImageAtomicCmpSwap32:
case Opcode::DebugPrint: case Opcode::DebugPrint:
case Opcode::EmitVertex: case Opcode::EmitVertex:
case Opcode::EmitPrimitive: case Opcode::EmitPrimitive:

View File

@ -436,6 +436,7 @@ OPCODE(ImageAtomicAnd32, U32, Opaq
OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicCmpSwap32, U32, Opaque, Opaque, U32, U32, )
// Cube operations - optional, usable if profile.supports_native_cube_calc // Cube operations - optional, usable if profile.supports_native_cube_calc
OPCODE(CubeFaceIndex, F32, F32x3, ) OPCODE(CubeFaceIndex, F32, F32x3, )

View File

@ -214,6 +214,7 @@ bool IsImageAtomicInstruction(const IR::Inst& inst) {
case IR::Opcode::ImageAtomicOr32: case IR::Opcode::ImageAtomicOr32:
case IR::Opcode::ImageAtomicXor32: case IR::Opcode::ImageAtomicXor32:
case IR::Opcode::ImageAtomicExchange32: case IR::Opcode::ImageAtomicExchange32:
case IR::Opcode::ImageAtomicCmpSwap32:
return true; return true;
default: default:
return false; return false;