Skip to content

Commit 95d1b4f

Browse files
authored
[CIR] Add support for atomic test-and-set and atomic clear (#164162)
This patch adds support for the following atomic builtin functions: - `__atomic_test_and_set` - `__atomic_clear`
1 parent c8c5a38 commit 95d1b4f

File tree

4 files changed

+194
-4
lines changed

4 files changed

+194
-4
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4557,4 +4557,66 @@ def CIR_AtomicCmpXchgOp : CIR_Op<"atomic.cmpxchg", [
45574557
}];
45584558
}
45594559

4560+
def CIR_AtomicTestAndSetOp : CIR_Op<"atomic.test_and_set"> {
4561+
let summary = "Atomic test and set";
4562+
let description = [{
4563+
C/C++ atomic test and set operation. Implements the builtin function
4564+
`__atomic_test_and_set`.
4565+
4566+
The operation takes as its only operand a pointer to an 8-bit signed
4567+
integer. The operation atomically set the integer to an implementation-
4568+
defined non-zero "set" value. The result of the operation is a boolean value
4569+
indicating whether the previous value of the integer was the "set" value.
4570+
4571+
Example:
4572+
```mlir
4573+
%res = cir.atomic.test_and_set seq_cst %ptr : !cir.ptr<!s8i> -> !cir.bool
4574+
```
4575+
}];
4576+
4577+
let arguments = (ins
4578+
Arg<CIR_PtrToType<CIR_SInt8>, "", [MemRead, MemWrite]>:$ptr,
4579+
Arg<CIR_MemOrder, "memory order">:$mem_order,
4580+
OptionalAttr<I64Attr>:$alignment,
4581+
UnitAttr:$is_volatile
4582+
);
4583+
4584+
let results = (outs CIR_BoolType:$result);
4585+
4586+
let assemblyFormat = [{
4587+
$mem_order $ptr
4588+
(`volatile` $is_volatile^)?
4589+
`:` qualified(type($ptr)) `->` qualified(type($result)) attr-dict
4590+
}];
4591+
}
4592+
4593+
def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
4594+
let summary = "Atomic clear";
4595+
let description = [{
4596+
C/C++ atomic clear operation. Implements the builtin function
4597+
`__atomic_clear`.
4598+
4599+
The operation takes as its only operand a pointer to an 8-bit signed
4600+
integer. The operation atomically sets the integer to zero.
4601+
4602+
Example:
4603+
```mlir
4604+
cir.atomic.clear seq_cst %ptr : !cir.ptr<!s8i>
4605+
```
4606+
}];
4607+
4608+
let arguments = (ins
4609+
Arg<CIR_PtrToType<CIR_SInt8>, "", [MemRead, MemWrite]>:$ptr,
4610+
Arg<CIR_MemOrder, "memory order">:$mem_order,
4611+
OptionalAttr<I64Attr>:$alignment,
4612+
UnitAttr:$is_volatile
4613+
);
4614+
4615+
let assemblyFormat = [{
4616+
$mem_order $ptr
4617+
(`volatile` $is_volatile^)?
4618+
`:` qualified(type($ptr)) attr-dict
4619+
}];
4620+
}
4621+
45604622
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

clang/lib/CIR/CodeGen/CIRGenAtomic.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,23 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest,
407407
opName = cir::AtomicXchgOp::getOperationName();
408408
break;
409409

410+
case AtomicExpr::AO__atomic_test_and_set: {
411+
auto op = cir::AtomicTestAndSetOp::create(
412+
builder, loc, ptr.getPointer(), order,
413+
builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
414+
expr->isVolatile());
415+
builder.createStore(loc, op, dest);
416+
return;
417+
}
418+
419+
case AtomicExpr::AO__atomic_clear: {
420+
cir::AtomicClearOp::create(
421+
builder, loc, ptr.getPointer(), order,
422+
builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
423+
expr->isVolatile());
424+
return;
425+
}
426+
410427
case AtomicExpr::AO__opencl_atomic_init:
411428

412429
case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
@@ -502,10 +519,6 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest,
502519
case AtomicExpr::AO__c11_atomic_fetch_nand:
503520
case AtomicExpr::AO__atomic_fetch_nand:
504521
case AtomicExpr::AO__scoped_atomic_fetch_nand:
505-
506-
case AtomicExpr::AO__atomic_test_and_set:
507-
508-
case AtomicExpr::AO__atomic_clear:
509522
cgf.cgm.errorNYI(expr->getSourceRange(), "emitAtomicOp: expr op NYI");
510523
return;
511524
}
@@ -581,6 +594,8 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
581594

582595
case AtomicExpr::AO__atomic_load_n:
583596
case AtomicExpr::AO__c11_atomic_load:
597+
case AtomicExpr::AO__atomic_test_and_set:
598+
case AtomicExpr::AO__atomic_clear:
584599
break;
585600

586601
case AtomicExpr::AO__atomic_load:
@@ -640,6 +655,9 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
640655
dest = atomics.castToAtomicIntPointer(dest);
641656
} else if (e->isCmpXChg()) {
642657
dest = createMemTemp(resultTy, getLoc(e->getSourceRange()), "cmpxchg.bool");
658+
} else if (e->getOp() == AtomicExpr::AO__atomic_test_and_set) {
659+
dest = createMemTemp(resultTy, getLoc(e->getSourceRange()),
660+
"test_and_set.bool");
643661
} else if (!resultTy->isVoidType()) {
644662
dest = atomics.createTempAlloca();
645663
if (shouldCastToIntPtrTy)

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,46 @@ mlir::LogicalResult CIRToLLVMAtomicXchgOpLowering::matchAndRewrite(
730730
return mlir::success();
731731
}
732732

733+
mlir::LogicalResult CIRToLLVMAtomicTestAndSetOpLowering::matchAndRewrite(
734+
cir::AtomicTestAndSetOp op, OpAdaptor adaptor,
735+
mlir::ConversionPatternRewriter &rewriter) const {
736+
assert(!cir::MissingFeatures::atomicSyncScopeID());
737+
738+
mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
739+
740+
auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
741+
rewriter.getI8Type(), 1);
742+
auto rmw = mlir::LLVM::AtomicRMWOp::create(
743+
rewriter, op.getLoc(), mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(),
744+
one, llvmOrder, /*syncscope=*/llvm::StringRef(),
745+
adaptor.getAlignment().value_or(0), op.getIsVolatile());
746+
747+
auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
748+
rewriter.getI8Type(), 0);
749+
auto cmp = mlir::LLVM::ICmpOp::create(
750+
rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, rmw, zero);
751+
752+
rewriter.replaceOp(op, cmp);
753+
return mlir::success();
754+
}
755+
756+
mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
757+
cir::AtomicClearOp op, OpAdaptor adaptor,
758+
mlir::ConversionPatternRewriter &rewriter) const {
759+
assert(!cir::MissingFeatures::atomicSyncScopeID());
760+
761+
mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
762+
auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
763+
rewriter.getI8Type(), 0);
764+
auto store = mlir::LLVM::StoreOp::create(
765+
rewriter, op.getLoc(), zero, adaptor.getPtr(),
766+
adaptor.getAlignment().value_or(0), op.getIsVolatile(),
767+
/*isNonTemporal=*/false, /*isInvariantGroup=*/false, llvmOrder);
768+
769+
rewriter.replaceOp(op, store);
770+
return mlir::success();
771+
}
772+
733773
mlir::LogicalResult CIRToLLVMBitClrsbOpLowering::matchAndRewrite(
734774
cir::BitClrsbOp op, OpAdaptor adaptor,
735775
mlir::ConversionPatternRewriter &rewriter) const {

clang/test/CIR/CodeGen/atomic.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,3 +514,73 @@ void atomic_exchange_n(int *ptr, int value) {
514514
// OGCG: %{{.+}} = atomicrmw xchg ptr %{{.+}}, i32 %{{.+}} acq_rel, align 4
515515
// OGCG: %{{.+}} = atomicrmw xchg ptr %{{.+}}, i32 %{{.+}} seq_cst, align 4
516516
}
517+
518+
void test_and_set(void *p) {
519+
// CIR-LABEL: @test_and_set
520+
// LLVM-LABEL: @test_and_set
521+
// OGCG-LABEL: @test_and_set
522+
523+
__atomic_test_and_set(p, __ATOMIC_SEQ_CST);
524+
// CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
525+
// CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
526+
// CIR-NEXT: %[[RES:.+]] = cir.atomic.test_and_set seq_cst %[[PTR]] : !cir.ptr<!s8i> -> !cir.bool
527+
// CIR-NEXT: cir.store align(1) %[[RES]], %{{.+}} : !cir.bool, !cir.ptr<!cir.bool>
528+
529+
// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
530+
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i8 1 seq_cst, align 1
531+
// LLVM-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
532+
533+
// OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
534+
// OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i8 1 seq_cst, align 1
535+
// OGCG-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
536+
}
537+
538+
void test_and_set_volatile(volatile void *p) {
539+
// CIR-LABEL: @test_and_set_volatile
540+
// LLVM-LABEL: @test_and_set_volatile
541+
// OGCG-LABEL: @test_and_set_volatile
542+
543+
__atomic_test_and_set(p, __ATOMIC_SEQ_CST);
544+
// CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
545+
// CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
546+
// CIR-NEXT: %[[RES:.+]] = cir.atomic.test_and_set seq_cst %[[PTR]] volatile : !cir.ptr<!s8i> -> !cir.bool
547+
// CIR-NEXT: cir.store align(1) %[[RES]], %{{.+}} : !cir.bool, !cir.ptr<!cir.bool>
548+
549+
// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
550+
// LLVM-NEXT: %[[RES:.+]] = atomicrmw volatile xchg ptr %[[PTR]], i8 1 seq_cst, align 1
551+
// LLVM-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
552+
553+
// OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
554+
// OGCG-NEXT: %[[RES:.+]] = atomicrmw volatile xchg ptr %[[PTR]], i8 1 seq_cst, align 1
555+
// OGCG-NEXT: %{{.+}} = icmp ne i8 %[[RES]], 0
556+
}
557+
558+
void clear(void *p) {
559+
// CIR-LABEL: @clear
560+
// LLVM-LABEL: @clear
561+
// OGCG-LABEL: @clear
562+
563+
__atomic_clear(p, __ATOMIC_SEQ_CST);
564+
// CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
565+
// CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
566+
// CIR: cir.atomic.clear seq_cst %[[PTR]] : !cir.ptr<!s8i>
567+
568+
// LLVM: store atomic i8 0, ptr %{{.+}} seq_cst, align 1
569+
570+
// OGCG: store atomic i8 0, ptr %{{.+}} seq_cst, align 1
571+
}
572+
573+
void clear_volatile(volatile void *p) {
574+
// CIR-LABEL: @clear_volatile
575+
// LLVM-LABEL: @clear_volatile
576+
// OGCG-LABEL: @clear_volatile
577+
578+
__atomic_clear(p, __ATOMIC_SEQ_CST);
579+
// CIR: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
580+
// CIR-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
581+
// CIR: cir.atomic.clear seq_cst %[[PTR]] volatile : !cir.ptr<!s8i>
582+
583+
// LLVM: store atomic volatile i8 0, ptr %{{.+}} seq_cst, align 1
584+
585+
// OGCG: store atomic volatile i8 0, ptr %{{.+}} seq_cst, align 1
586+
}

0 commit comments

Comments
 (0)