Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 22 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
40 changes: 40 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
70 changes: 70 additions & 0 deletions clang/test/CIR/CodeGen/atomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}