Skip to content

Commit f440282

Browse files
Lancernlanza
authored andcommitted
[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 8071b40 commit f440282

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
@@ -6060,6 +6060,68 @@ def CIR_AtomicCmpXchg : CIR_Op<"atomic.cmp_xchg", [
60606060
let hasVerifier = 1;
60616061
}
60626062

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

3815+
mlir::LogicalResult CIRToLLVMAtomicTestAndSetOpLowering::matchAndRewrite(
3816+
cir::AtomicTestAndSetOp op, OpAdaptor adaptor,
3817+
mlir::ConversionPatternRewriter &rewriter) const {
3818+
mlir::LLVM::AtomicOrdering llvmOrder = getLLVMAtomicOrder(op.getMemOrder());
3819+
llvm::StringRef llvmSyncScope =
3820+
getLLVMSyncScope(adaptor.getSyncscope()).value_or(StringRef());
3821+
3822+
auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
3823+
rewriter.getI8Type(), 1);
3824+
auto rmw = mlir::LLVM::AtomicRMWOp::create(
3825+
rewriter, op.getLoc(), mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(),
3826+
one, llvmOrder, llvmSyncScope, adaptor.getAlignment().value_or(0),
3827+
op.getIsVolatile());
3828+
auto cmp = mlir::LLVM::ICmpOp::create(
3829+
rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, one, rmw);
3830+
3831+
rewriter.replaceOp(op, cmp);
3832+
return mlir::success();
3833+
}
3834+
3835+
mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
3836+
cir::AtomicClearOp op, OpAdaptor adaptor,
3837+
mlir::ConversionPatternRewriter &rewriter) const {
3838+
// FIXME: add syncscope.
3839+
mlir::LLVM::AtomicOrdering llvmOrder = getLLVMAtomicOrder(op.getMemOrder());
3840+
auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
3841+
rewriter.getI8Type(), 0);
3842+
auto store = mlir::LLVM::StoreOp::create(
3843+
rewriter, op.getLoc(), zero, adaptor.getPtr(),
3844+
adaptor.getAlignment().value_or(0), op.getIsVolatile(),
3845+
/*isNonTemporal=*/false, /*isInvariantGroup=*/false, llvmOrder);
3846+
rewriter.replaceOp(op, store);
3847+
return mlir::success();
3848+
}
3849+
38153850
mlir::LogicalResult CIRToLLVMAtomicFenceLowering::matchAndRewrite(
38163851
cir::AtomicFence op, OpAdaptor adaptor,
38173852
mlir::ConversionPatternRewriter &rewriter) const {
@@ -4772,8 +4807,10 @@ void populateCIRToLLVMConversionPatterns(
47724807
CIRToLLVMAssumeAlignedOpLowering,
47734808
CIRToLLVMAssumeOpLowering,
47744809
CIRToLLVMAssumeSepStorageOpLowering,
4810+
CIRToLLVMAtomicClearOpLowering,
47754811
CIRToLLVMAtomicCmpXchgLowering,
47764812
CIRToLLVMAtomicFetchLowering,
4813+
CIRToLLVMAtomicTestAndSetOpLowering,
47774814
CIRToLLVMAtomicXchgLowering,
47784815
CIRToLLVMAtomicFenceLowering,
47794816
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
@@ -1155,3 +1155,43 @@ extern "C" void test_op_and_fetch(void)
11551155
// LLVM: store i64 [[RET7]], ptr @ull, align 8
11561156
ull = __sync_nand_and_fetch(&ull, uc);
11571157
}
1158+
1159+
// CHECK-LABEL: @_Z12test_and_setPvPVv
1160+
// LLVM-LABEL: @_Z12test_and_setPvPVv
1161+
void test_and_set(void *p, volatile void *vp) {
1162+
bool x = __atomic_test_and_set(p, __ATOMIC_SEQ_CST);
1163+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1164+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1165+
// CHECK: %{{.+}} = cir.atomic.test_and_set seq_cst %[[PTR]] : !cir.ptr<!s8i> -> !cir.bool
1166+
1167+
// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
1168+
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i8 1 seq_cst, align 1
1169+
// LLVM-NEXT: %{{.+}} = icmp ne i8 1, %[[RES]]
1170+
1171+
bool y = __atomic_test_and_set(vp, __ATOMIC_SEQ_CST);
1172+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1173+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1174+
// CHECK: %{{.+}} = cir.atomic.test_and_set seq_cst %[[PTR]] volatile : !cir.ptr<!s8i> -> !cir.bool
1175+
1176+
// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
1177+
// LLVM-NEXT: %[[RES:.+]] = atomicrmw volatile xchg ptr %[[PTR]], i8 1 seq_cst, align 1
1178+
// LLVM-NEXT: %{{.+}} = icmp ne i8 1, %[[RES]]
1179+
}
1180+
1181+
// CHECK-LABEL: @_Z5clearPvPVv
1182+
// LLVM-LABEL: @_Z5clearPvPVv
1183+
void clear(void *p, volatile void *vp) {
1184+
__atomic_clear(p, __ATOMIC_SEQ_CST);
1185+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1186+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1187+
// CHECK: cir.atomic.clear seq_cst %[[PTR]] : !cir.ptr<!s8i>
1188+
1189+
// LLVM: store atomic i8 0, ptr %{{.+}} seq_cst, align 1
1190+
1191+
__atomic_clear(vp, __ATOMIC_SEQ_CST);
1192+
// CHECK: %[[VOID_PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
1193+
// CHECK-NEXT: %[[PTR:.+]] = cir.cast bitcast %[[VOID_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s8i>
1194+
// CHECK: cir.atomic.clear seq_cst %[[PTR]] volatile : !cir.ptr<!s8i>
1195+
1196+
// LLVM: store atomic volatile i8 0, ptr %{{.+}} seq_cst, align 1
1197+
}

0 commit comments

Comments
 (0)