[clang] [llvm] [HLSL][DXIL] InterlockedOr and InterlockedOr64 builtins (PR #180804)
Alexander Johnston via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 9 09:51:46 PDT 2026
================
@@ -301,6 +300,98 @@ static Value *handleElementwiseF32ToF16(CodeGenFunction &CGF,
llvm_unreachable("Intrinsic F32ToF16 not supported by target architecture");
}
+static Value *handleInterlockedOr(CodeGenFunction &CGF, const CallExpr *E,
+ const bool HasReturn) {
+ const bool Is32Bit = CGF.getContext().getTypeSize(
+ E->getArg(E->getNumArgs() - 1)->getType()) == 32;
+ Value *HandleOp = CGF.EmitScalarExpr(E->getArg(0));
+ Value *IndexOp = CGF.EmitScalarExpr(E->getArg(1));
+ Value *StructuredBufIndexOp;
+ Value *NewValueOp;
+ Value *OldValueOp;
+ unsigned OldValueArgIdx;
+ if (E->getNumArgs() == 3) {
+ // (handle, index, newValue)
+ NewValueOp = CGF.EmitScalarExpr(E->getArg(2));
+ } else if (E->getNumArgs() == 4) {
+ if (HasReturn) {
+ // (handle, index, newValue, oldValue)
+ NewValueOp = CGF.EmitScalarExpr(E->getArg(2));
+ OldValueArgIdx = 3;
+ } else {
+ // (handle, index, index, newValue)
+ StructuredBufIndexOp = CGF.EmitScalarExpr(E->getArg(2));
+ NewValueOp = CGF.EmitScalarExpr(E->getArg(3));
+ }
+ } else {
+ // (handle, index, index, newValue, oldValue)
+ StructuredBufIndexOp = CGF.EmitScalarExpr(E->getArg(2));
+ NewValueOp = CGF.EmitScalarExpr(E->getArg(3));
+ OldValueArgIdx = 4;
+ }
+
+ switch (CGF.CGM.getTarget().getTriple().getArch()) {
+ case llvm::Triple::dxil: {
+ QualType HandleTy = E->getArg(0)->getType();
+ const HLSLAttributedResourceType *ResourceTy =
+ HandleTy->getAs<HLSLAttributedResourceType>();
+
+ // AtomicBinOp has 3 coordinate params which must be handled differently
+ // depending on the resource type being accessed.
+ // Initially poison all the coordinates then fill as required
+ Value *Poison = PoisonValue::get(CGF.Int32Ty);
+ Value *C0 = Poison;
+ Value *C1 = Poison;
+ Value *C2 = Poison;
+ if (!ResourceTy->getAttrs().RawBuffer) {
+ assert(
+ (ResourceTy->getContainedType() == CGF.getContext().IntTy ||
+ ResourceTy->getContainedType() == CGF.getContext().UnsignedIntTy ||
+ ResourceTy->getContainedType() == CGF.getContext().LongTy ||
+ ResourceTy->getContainedType() == CGF.getContext().UnsignedLongTy) &&
+ "AtomicBinOp RWBuffer must contain 32 or 64bit (unsigned) int type");
+ // RWBuffer: c0
+ C0 = IndexOp;
+
+ // RWByteAddressBuffers are output as char8_t, but as that isn't
+ // recognised by HLSL we can't use it as an attribute to define them in
+ // tests, so must also check for char ([[hlsl::contained_type(char)]])
+ } else if (ResourceTy->getContainedType() == CGF.getContext().Char8Ty ||
+ ResourceTy->getContainedType() == CGF.getContext().CharTy) {
+ // RWByteAddressBuffer: c0
+ C0 = IndexOp;
+ } else {
+ // RWStructuredBuffer: c0 and c1
+ C0 = IndexOp;
+ C1 = StructuredBufIndexOp;
+ }
+ assert(C0 != Poison && "Failed to identify coordinates for Interlocked");
+ // TODO: Add coordinate logic for texture and groupshared (#186154)
+
+ // atomicBinOp
+ // opcode, handle, binary operation code, coordinates c0, c1, c2, new val
+ llvm::Type *ReturnType = Is32Bit ? CGF.Int32Ty : CGF.Int64Ty;
+ OldValueOp = CGF.Builder.CreateIntrinsic(
+ ReturnType, Intrinsic::dx_interlocked_or,
+ ArrayRef<Value *>{HandleOp, C0, C1, C2, NewValueOp}, nullptr,
+ "hlsl.interlocked.or");
+ break;
+ }
+ default:
+ llvm_unreachable(
+ "Interlocked intrinsic not supported by target architecture");
+ }
+
+ // Destination may or may not be provided
+ // If it is provided create a store to it
+ if (HasReturn) {
----------------
Alexander-Johnston wrote:
I wanted to do something akin to this, but I found it creates an ambiguity
There's 4 input possibilities
```
1. interlocked(buffer, index, newVal) non-structured, no out
2. interlocked(buffer, index, index, newVal) structured, no out
3. interlocked(buffer, index, newVal, oldVal) non-structured, out
4. interlocked(buffer, index, index, newVal, oldVal) structured, out
```
If we only have one builtin it's ambiguous at the callsite between the second and third cases. The cleanest solution I saw was to remove any risk of ambiguity with the ret vs no return versions.
Happy to move the naming from `return` to `hasout`/`out` though
https://github.com/llvm/llvm-project/pull/180804
More information about the cfe-commits
mailing list