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)};
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
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);
}
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) {
const auto& buffer = ctx.buffers[binding];
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 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 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 EmitLaneId(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,
ScalarType::Uint32},
// 16 = IMAGE_ATOMIC_CMPSWAP
{InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined,
ScalarType::Undefined},
{InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32,
ScalarType::Uint32},
// 17 = IMAGE_ATOMIC_ADD
{InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32,
ScalarType::Uint32},

View File

@ -137,6 +137,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
// Image atomic operations
case Opcode::IMAGE_ATOMIC_SWAP:
return IMAGE_ATOMIC(AtomicOp::Swap, inst);
case Opcode::IMAGE_ATOMIC_CMPSWAP:
return IMAGE_ATOMIC(AtomicOp::CmpSwap, inst);
case Opcode::IMAGE_ATOMIC_ADD:
return IMAGE_ATOMIC(AtomicOp::Add, inst);
case Opcode::IMAGE_ATOMIC_SMIN:
@ -520,6 +522,10 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
switch (op) {
case AtomicOp::Swap:
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:
return ir.ImageAtomicIAdd(handle, body, value, info);
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);
}
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,
const Value& address1, const Value& address2, const Value& address3,
const Value& address4, TextureInstInfo info) {

View File

@ -360,6 +360,9 @@ public:
TextureInstInfo info);
[[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords,
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,
const Value& address1, const Value& address2,

View File

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

View File

@ -436,6 +436,7 @@ OPCODE(ImageAtomicAnd32, U32, Opaq
OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicXor32, 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
OPCODE(CubeFaceIndex, F32, F32x3, )

View File

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