-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CIR] Add support for atomic test-and-set and atomic clear #164162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
+194
−4
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This patch adds support for the following atomic builtin functions: - `__atomic_test_and_set` - `__atomic_clear`
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Sirui Mu (Lancern) ChangesThis patch adds support for the following atomic builtin functions:
Full diff: https://github.com/llvm/llvm-project/pull/164162.diff 4 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e0163a4fecd5f..5a3dfec6e015c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4554,4 +4554,66 @@ def CIR_AtomicCmpXchgOp : CIR_Op<"atomic.cmpxchg", [
}];
}
+def CIR_AtomicTestAndSetOp : CIR_Op<"atomic.test_and_set"> {
+ let summary = "Atomic test and set";
+ let description = [{
+ C/C++ atomic test and set operation. Implements the builtin function
+ `__atomic_test_and_set`.
+
+ The operation takes as its only operand a pointer to an 8-bit signed
+ integer. The operation atomically set the integer to an implementation-
+ defined non-zero "set" value. The result of the operation is a boolean value
+ indicating whether the previous value of the integer was the "set" value.
+
+ Example:
+ ```mlir
+ %res = cir.atomic.test_and_set seq_cst %ptr : !cir.ptr<!s8i> -> !cir.bool
+ ```
+ }];
+
+ let arguments = (ins
+ Arg<CIR_PtrToType<CIR_SInt8>, "", [MemRead, MemWrite]>:$ptr,
+ Arg<CIR_MemOrder, "memory order">:$mem_order,
+ OptionalAttr<I64Attr>:$alignment,
+ UnitAttr:$is_volatile
+ );
+
+ let results = (outs CIR_BoolType:$result);
+
+ let assemblyFormat = [{
+ $mem_order $ptr
+ (`volatile` $is_volatile^)?
+ `:` qualified(type($ptr)) `->` qualified(type($result)) attr-dict
+ }];
+}
+
+def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
+ let summary = "Atomic clear";
+ let description = [{
+ C/C++ atomic clear operation. Implements the builtin function
+ `__atomic_clear`.
+
+ The operation takes as its only operand a pointer to an 8-bit signed
+ integer. The operation atomically sets the integer to zero.
+
+ Example:
+ ```mlir
+ cir.atomic.clear seq_cst %ptr : !cir.ptr<!s8i>
+ ```
+ }];
+
+ let arguments = (ins
+ Arg<CIR_PtrToType<CIR_SInt8>, "", [MemRead, MemWrite]>:$ptr,
+ Arg<CIR_MemOrder, "memory order">:$mem_order,
+ OptionalAttr<I64Attr>:$alignment,
+ UnitAttr:$is_volatile
+ );
+
+ let assemblyFormat = [{
+ $mem_order $ptr
+ (`volatile` $is_volatile^)?
+ `:` qualified(type($ptr)) attr-dict
+ }];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
index a9983f882e28c..67ca60c971e04 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
@@ -407,6 +407,23 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest,
opName = cir::AtomicXchgOp::getOperationName();
break;
+ case AtomicExpr::AO__atomic_test_and_set: {
+ auto op = cir::AtomicTestAndSetOp::create(
+ builder, loc, ptr.getPointer(), order,
+ builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
+ expr->isVolatile());
+ builder.createStore(loc, op, dest);
+ return;
+ }
+
+ case AtomicExpr::AO__atomic_clear: {
+ cir::AtomicClearOp::create(
+ builder, loc, ptr.getPointer(), order,
+ builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
+ expr->isVolatile());
+ return;
+ }
+
case AtomicExpr::AO__opencl_atomic_init:
case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
@@ -502,10 +519,6 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest,
case AtomicExpr::AO__c11_atomic_fetch_nand:
case AtomicExpr::AO__atomic_fetch_nand:
case AtomicExpr::AO__scoped_atomic_fetch_nand:
-
- case AtomicExpr::AO__atomic_test_and_set:
-
- case AtomicExpr::AO__atomic_clear:
cgf.cgm.errorNYI(expr->getSourceRange(), "emitAtomicOp: expr op NYI");
return;
}
@@ -581,6 +594,8 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
case AtomicExpr::AO__atomic_load_n:
case AtomicExpr::AO__c11_atomic_load:
+ case AtomicExpr::AO__atomic_test_and_set:
+ case AtomicExpr::AO__atomic_clear:
break;
case AtomicExpr::AO__atomic_load:
@@ -640,6 +655,9 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
dest = atomics.castToAtomicIntPointer(dest);
} else if (e->isCmpXChg()) {
dest = createMemTemp(resultTy, getLoc(e->getSourceRange()), "cmpxchg.bool");
+ } else if (e->getOp() == AtomicExpr::AO__atomic_test_and_set) {
+ dest = createMemTemp(resultTy, getLoc(e->getSourceRange()),
+ "test_and_set.bool");
} else if (!resultTy->isVoidType()) {
dest = atomics.createTempAlloca();
if (shouldCastToIntPtrTy)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 0243bf120f396..b688184c70af0 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -730,6 +730,46 @@ mlir::LogicalResult CIRToLLVMAtomicXchgOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMAtomicTestAndSetOpLowering::matchAndRewrite(
+ cir::AtomicTestAndSetOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ assert(!cir::MissingFeatures::atomicSyncScopeID());
+
+ mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
+
+ auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
+ rewriter.getI8Type(), 1);
+ auto rmw = mlir::LLVM::AtomicRMWOp::create(
+ rewriter, op.getLoc(), mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(),
+ one, llvmOrder, /*syncscope=*/llvm::StringRef(),
+ adaptor.getAlignment().value_or(0), op.getIsVolatile());
+
+ auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
+ rewriter.getI8Type(), 0);
+ auto cmp = mlir::LLVM::ICmpOp::create(
+ rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, rmw, zero);
+
+ rewriter.replaceOp(op, cmp);
+ return mlir::success();
+}
+
+mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
+ cir::AtomicClearOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ assert(!cir::MissingFeatures::atomicSyncScopeID());
+
+ mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
+ auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
+ rewriter.getI8Type(), 0);
+ auto store = mlir::LLVM::StoreOp::create(
+ rewriter, op.getLoc(), zero, adaptor.getPtr(),
+ adaptor.getAlignment().value_or(0), op.getIsVolatile(),
+ /*isNonTemporal=*/false, /*isInvariantGroup=*/false, llvmOrder);
+
+ rewriter.replaceOp(op, store);
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMBitClrsbOpLowering::matchAndRewrite(
cir::BitClrsbOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c
index 440010a0b6938..cf202266f5716 100644
--- a/clang/test/CIR/CodeGen/atomic.c
+++ b/clang/test/CIR/CodeGen/atomic.c
@@ -514,3 +514,73 @@ void atomic_exchange_n(int *ptr, int value) {
// OGCG: %{{.+}} = atomicrmw xchg ptr %{{.+}}, i32 %{{.+}} acq_rel, align 4
// OGCG: %{{.+}} = atomicrmw xchg ptr %{{.+}}, i32 %{{.+}} seq_cst, align 4
}
+
+void test_and_set(void *p) {
+ // CIR-LABEL: @test_and_set
+ // LLVM-LABEL: @test_and_set
+ // OGCG-LABEL: @test_and_set
+
+ __atomic_test_and_set(p, __ATOMIC_SEQ_CST);
+ // CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
+ // CIR-NEXT: %[[RES:.+]] = cir.atomic.test_and_set seq_cst %[[PTR]] : !cir.ptr<!s8i> -> !cir.bool
+ // CIR-NEXT: cir.store align(1) %[[RES]], %{{.+}} : !cir.bool, !cir.ptr<!cir.bool>
+
+ // LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
+ // LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i8 1 seq_cst, align 1
+ // LLVM-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
+
+ // OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
+ // OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i8 1 seq_cst, align 1
+ // OGCG-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
+}
+
+void test_and_set_volatile(volatile void *p) {
+ // CIR-LABEL: @test_and_set_volatile
+ // LLVM-LABEL: @test_and_set_volatile
+ // OGCG-LABEL: @test_and_set_volatile
+
+ __atomic_test_and_set(p, __ATOMIC_SEQ_CST);
+ // CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
+ // CIR-NEXT: %[[RES:.+]] = cir.atomic.test_and_set seq_cst %[[PTR]] volatile : !cir.ptr<!s8i> -> !cir.bool
+ // CIR-NEXT: cir.store align(1) %[[RES]], %{{.+}} : !cir.bool, !cir.ptr<!cir.bool>
+
+ // LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
+ // LLVM-NEXT: %[[RES:.+]] = atomicrmw volatile xchg ptr %[[PTR]], i8 1 seq_cst, align 1
+ // LLVM-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
+
+ // OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
+ // OGCG-NEXT: %[[RES:.+]] = atomicrmw volatile xchg ptr %[[PTR]], i8 1 seq_cst, align 1
+ // OGCG-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
+}
+
+void clear(void *p) {
+ // CIR-LABEL: @clear
+ // LLVM-LABEL: @clear
+ // OGCG-LABEL: @clear
+
+ __atomic_clear(p, __ATOMIC_SEQ_CST);
+ // CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
+ // CIR: cir.atomic.clear seq_cst %[[PTR]] : !cir.ptr<!s8i>
+
+ // LLVM: store atomic i8 0, ptr %{{.+}} seq_cst, align 1
+
+ // OGCG: store atomic i8 0, ptr %{{.+}} seq_cst, align 1
+}
+
+void clear_volatile(volatile void *p) {
+ // CIR-LABEL: @clear_volatile
+ // LLVM-LABEL: @clear_volatile
+ // OGCG-LABEL: @clear_volatile
+
+ __atomic_clear(p, __ATOMIC_SEQ_CST);
+ // CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+ // CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
+ // CIR: cir.atomic.clear seq_cst %[[PTR]] volatile : !cir.ptr<!s8i>
+
+ // LLVM: store atomic volatile i8 0, ptr %{{.+}} seq_cst, align 1
+
+ // OGCG: store atomic volatile i8 0, ptr %{{.+}} seq_cst, align 1
+}
|
andykaylor
approved these changes
Oct 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This patch adds support for the following atomic builtin functions:
__atomic_test_and_set__atomic_clear