Skip to content

Commit 8c0951a

Browse files
authored
[CIR] Add support for __atomic_test_and_set and __atomic_clear (#1940)
This patch adds support for the builtin functions `__atomic_test_and_set` and `__atomic_clear`.
1 parent a725efb commit 8c0951a

File tree

5 files changed

+172
-2
lines changed

5 files changed

+172
-2
lines changed

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6063,6 +6063,68 @@ def CIR_AtomicCmpXchg : CIR_Op<"atomic.cmp_xchg", [
60636063
let hasVerifier = 1;
60646064
}
60656065

6066+
def CIR_AtomicTestAndSetOp : CIR_Op<"atomic.test_and_set"> {
6067+
let summary = "Atomic test and set";
6068+
let description = [{
6069+
C/C++ atomic test and set operation. Implements the builtin function
6070+
`__atomic_test_and_set`.
6071+
6072+
The operation takes as its only operand a pointer to an 8-bit signed
6073+
integer. The operation atomically set the integer to an implementation-
6074+
defined non-zero "set" value. The result of the operation is a boolean value
6075+
indicating whether the previous value of the integer was the "set" value.
6076+
6077+
Example:
6078+
```mlir
6079+
%res = cir.atomic.test_and_set seq_cst %ptr : !cir.ptr<!s8i> -> !cir.bool
6080+
```
6081+
}];
6082+
6083+
let arguments = (ins
6084+
Arg<CIR_PtrToType<CIR_SInt8>, "", [MemRead, MemWrite]>:$ptr,
6085+
Arg<CIR_MemOrder, "memory order">:$mem_order,
6086+
OptionalAttr<CIR_MemScopeKind>:$syncscope,
6087+
OptionalAttr<I64Attr>:$alignment,
6088+
UnitAttr:$is_volatile);
6089+
6090+
let results = (outs CIR_BoolType:$result);
6091+
6092+
let assemblyFormat = [{
6093+
$mem_order $ptr
6094+
(`volatile` $is_volatile^)?
6095+
`:` qualified(type($ptr)) `->` qualified(type($result)) attr-dict
6096+
}];
6097+
}
6098+
6099+
def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
6100+
let summary = "Atomic clear";
6101+
let description = [{
6102+
C/C++ atomic clear operation. Implements the builtin function
6103+
`__atomic_clear`.
6104+
6105+
The operation takes as its only operand a pointer to an 8-bit signed
6106+
integer. The operation atomically sets the integer to zero.
6107+
6108+
Example:
6109+
```mlir
6110+
cir.atomic.clear seq_cst %ptr : !cir.ptr<!s8i>
6111+
```
6112+
}];
6113+
6114+
let arguments = (ins
6115+
Arg<CIR_PtrToType<CIR_SInt8>, "", [MemRead, MemWrite]>:$ptr,
6116+
Arg<CIR_MemOrder, "memory order">:$mem_order,
6117+
OptionalAttr<CIR_MemScopeKind>:$syncscope,
6118+
OptionalAttr<I64Attr>:$alignment,
6119+
UnitAttr:$is_volatile);
6120+
6121+
let assemblyFormat = [{
6122+
$mem_order $ptr
6123+
(`volatile` $is_volatile^)?
6124+
`:` qualified(type($ptr)) attr-dict
6125+
}];
6126+
}
6127+
60666128
def CIR_AtomicFence : CIR_Op<"atomic.fence"> {
60676129
let summary = "Atomic thread fence";
60686130
let description = [{

clang/lib/CIR/CodeGen/CIRGenAtomic.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,11 +746,22 @@ static void emitAtomicOp(CIRGenFunction &CGF, AtomicExpr *E, Address Dest,
746746
cir::AtomicFetchKind::Nand);
747747
break;
748748
case AtomicExpr::AO__atomic_test_and_set: {
749-
llvm_unreachable("NYI");
749+
auto op = cir::AtomicTestAndSetOp::create(
750+
builder, loc, Ptr.getPointer(), Order,
751+
cir::MemScopeKindAttr::get(&CGF.getMLIRContext(), Scope),
752+
builder.getI64IntegerAttr(Ptr.getAlignment().getQuantity()),
753+
E->isVolatile());
754+
builder.createStore(loc, op, Dest);
755+
return;
750756
}
751757

752758
case AtomicExpr::AO__atomic_clear: {
753-
llvm_unreachable("NYI");
759+
cir::AtomicClearOp::create(
760+
builder, loc, Ptr.getPointer(), Order,
761+
cir::MemScopeKindAttr::get(&CGF.getMLIRContext(), Scope),
762+
builder.getI64IntegerAttr(Ptr.getAlignment().getQuantity()),
763+
E->isVolatile());
764+
return;
754765
}
755766
}
756767

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3647,6 +3647,41 @@ mlir::LogicalResult CIRToLLVMAtomicFetchLowering::matchAndRewrite(
36473647
return mlir::success();
36483648
}
36493649

3650+
mlir::LogicalResult CIRToLLVMAtomicTestAndSetOpLowering::matchAndRewrite(
3651+
cir::AtomicTestAndSetOp op, OpAdaptor adaptor,
3652+
mlir::ConversionPatternRewriter &rewriter) const {
3653+
mlir::LLVM::AtomicOrdering llvmOrder = getLLVMAtomicOrder(op.getMemOrder());
3654+
llvm::StringRef llvmSyncScope =
3655+
getLLVMSyncScope(adaptor.getSyncscope()).value_or(StringRef());
3656+
3657+
auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
3658+
rewriter.getI8Type(), 1);
3659+
auto rmw = mlir::LLVM::AtomicRMWOp::create(
3660+
rewriter, op.getLoc(), mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(),
3661+
one, llvmOrder, llvmSyncScope, adaptor.getAlignment().value_or(0),
3662+
op.getIsVolatile());
3663+
auto cmp = mlir::LLVM::ICmpOp::create(
3664+
rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, one, rmw);
3665+
3666+
rewriter.replaceOp(op, cmp);
3667+
return mlir::success();
3668+
}
3669+
3670+
mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
3671+
cir::AtomicClearOp op, OpAdaptor adaptor,
3672+
mlir::ConversionPatternRewriter &rewriter) const {
3673+
// FIXME: add syncscope.
3674+
mlir::LLVM::AtomicOrdering llvmOrder = getLLVMAtomicOrder(op.getMemOrder());
3675+
auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
3676+
rewriter.getI8Type(), 0);
3677+
auto store = mlir::LLVM::StoreOp::create(
3678+
rewriter, op.getLoc(), zero, adaptor.getPtr(),
3679+
adaptor.getAlignment().value_or(0), op.getIsVolatile(),
3680+
/*isNonTemporal=*/false, /*isInvariantGroup=*/false, llvmOrder);
3681+
rewriter.replaceOp(op, store);
3682+
return mlir::success();
3683+
}
3684+
36503685
mlir::LogicalResult CIRToLLVMAtomicFenceLowering::matchAndRewrite(
36513686
cir::AtomicFence op, OpAdaptor adaptor,
36523687
mlir::ConversionPatternRewriter &rewriter) const {
@@ -4603,8 +4638,10 @@ void populateCIRToLLVMConversionPatterns(
46034638
CIRToLLVMAssumeAlignedOpLowering,
46044639
CIRToLLVMAssumeOpLowering,
46054640
CIRToLLVMAssumeSepStorageOpLowering,
4641+
CIRToLLVMAtomicClearOpLowering,
46064642
CIRToLLVMAtomicCmpXchgLowering,
46074643
CIRToLLVMAtomicFetchLowering,
4644+
CIRToLLVMAtomicTestAndSetOpLowering,
46084645
CIRToLLVMAtomicXchgLowering,
46094646
CIRToLLVMAtomicFenceLowering,
46104647
CIRToLLVMBaseClassAddrOpLowering,

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,26 @@ class CIRToLLVMAtomicFetchLowering
936936
mlir::ConversionPatternRewriter &) const override;
937937
};
938938

939+
class CIRToLLVMAtomicTestAndSetOpLowering
940+
: public mlir::OpConversionPattern<cir::AtomicTestAndSetOp> {
941+
public:
942+
using mlir::OpConversionPattern<cir::AtomicTestAndSetOp>::OpConversionPattern;
943+
944+
mlir::LogicalResult
945+
matchAndRewrite(cir::AtomicTestAndSetOp op, OpAdaptor,
946+
mlir::ConversionPatternRewriter &) const override;
947+
};
948+
949+
class CIRToLLVMAtomicClearOpLowering
950+
: public mlir::OpConversionPattern<cir::AtomicClearOp> {
951+
public:
952+
using mlir::OpConversionPattern<cir::AtomicClearOp>::OpConversionPattern;
953+
954+
mlir::LogicalResult
955+
matchAndRewrite(cir::AtomicClearOp op, OpAdaptor,
956+
mlir::ConversionPatternRewriter &) const override;
957+
};
958+
939959
class CIRToLLVMAtomicFenceLowering
940960
: public mlir::OpConversionPattern<cir::AtomicFence> {
941961
public:

clang/test/CIR/CodeGen/atomic.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,3 +1156,43 @@ extern "C" void test_op_and_fetch(void)
11561156
// LLVM: store i64 [[RET7]], ptr @ull, align 8
11571157
ull = __sync_nand_and_fetch(&ull, uc);
11581158
}
1159+
1160+
// CHECK-LABEL: @_Z12test_and_setPvPVv
1161+
// LLVM-LABEL: @_Z12test_and_setPvPVv
1162+
void test_and_set(void *p, volatile void *vp) {
1163+
bool x = __atomic_test_and_set(p, __ATOMIC_SEQ_CST);
1164+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1165+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1166+
// CHECK: %{{.+}} = cir.atomic.test_and_set seq_cst %[[PTR]] : !cir.ptr<!s8i> -> !cir.bool
1167+
1168+
// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
1169+
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i8 1 seq_cst, align 1
1170+
// LLVM-NEXT: %{{.+}} = icmp ne i8 1, %[[RES]]
1171+
1172+
bool y = __atomic_test_and_set(vp, __ATOMIC_SEQ_CST);
1173+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1174+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1175+
// CHECK: %{{.+}} = cir.atomic.test_and_set seq_cst %[[PTR]] volatile : !cir.ptr<!s8i> -> !cir.bool
1176+
1177+
// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
1178+
// LLVM-NEXT: %[[RES:.+]] = atomicrmw volatile xchg ptr %[[PTR]], i8 1 seq_cst, align 1
1179+
// LLVM-NEXT: %{{.+}} = icmp ne i8 1, %[[RES]]
1180+
}
1181+
1182+
// CHECK-LABEL: @_Z5clearPvPVv
1183+
// LLVM-LABEL: @_Z5clearPvPVv
1184+
void clear(void *p, volatile void *vp) {
1185+
__atomic_clear(p, __ATOMIC_SEQ_CST);
1186+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1187+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1188+
// CHECK: cir.atomic.clear seq_cst %[[PTR]] : !cir.ptr<!s8i>
1189+
1190+
// LLVM: store atomic i8 0, ptr %{{.+}} seq_cst, align 1
1191+
1192+
__atomic_clear(vp, __ATOMIC_SEQ_CST);
1193+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1194+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1195+
// CHECK: cir.atomic.clear seq_cst %[[PTR]] volatile : !cir.ptr<!s8i>
1196+
1197+
// LLVM: store atomic volatile i8 0, ptr %{{.+}} seq_cst, align 1
1198+
}

0 commit comments

Comments
 (0)