Skip to content

Commit 39bfe2b

Browse files
committed
[CIR][CIRGen] Support for builtin __atomic_thread_fence
Resolves #1274 Implements atomic thread fence synchronization primitive corresponding to `atomic.thread_fence` CIR.
1 parent d329c96 commit 39bfe2b

File tree

5 files changed

+199
-1
lines changed

5 files changed

+199
-1
lines changed

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

+41
Original file line numberDiff line numberDiff line change
@@ -5409,6 +5409,47 @@ def AtomicCmpXchg : CIR_Op<"atomic.cmp_xchg",
54095409
let hasVerifier = 0;
54105410
}
54115411

5412+
def MemScope_SingleThread : I32EnumAttrCase<"MemScope_SingleThread",
5413+
0, "single_thread">;
5414+
def MemScope_System : I32EnumAttrCase<"MemScope_System",
5415+
1, "system">;
5416+
5417+
def MemScopeKind : I32EnumAttr<
5418+
"MemScopeKind",
5419+
"Memory Scope Enumeration",
5420+
[MemScope_SingleThread, MemScope_System]> {
5421+
let cppNamespace = "::cir";
5422+
}
5423+
5424+
def AtomicFence : CIR_Op<"atomic.fence"> {
5425+
let summary = "Atomic thread fence";
5426+
let description = [{
5427+
C/C++ Atomic thread fence synchronization primitive. Implements the builtin
5428+
`__atomic_thread_fence` which enforces memory ordering constraints across
5429+
threads within the specified synchronization scope.
5430+
5431+
This handles all variations including:
5432+
- `__atomic_thread_fence`
5433+
- `__atomic_signal_fence`
5434+
- `__c11_atomic_thread_fence`
5435+
- `__c11_atomic_signal_fence`
5436+
5437+
Example:
5438+
5439+
5440+
}];
5441+
let results = (outs);
5442+
let arguments = (ins Arg<MemScopeKind, "sync scope">:$sync_scope,
5443+
Arg<MemOrder, "memory order">:$ordering);
5444+
5445+
let assemblyFormat = [{
5446+
`(` `sync_scope` `=` $sync_scope `,`
5447+
`ordering` `=` $ordering `)` attr-dict
5448+
}];
5449+
5450+
let hasVerifier = 0;
5451+
}
5452+
54125453
def SignBitOp : CIR_Op<"signbit", [Pure]> {
54135454
let summary = "Checks the sign of a floating-point number";
54145455
let description = [{

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

+35-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
#include "CIRGenCstEmitter.h"
1717
#include "CIRGenFunction.h"
1818
#include "CIRGenModule.h"
19+
#include "CIRGenValue.h"
1920
#include "TargetInfo.h"
21+
#include "clang/AST/Expr.h"
22+
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
23+
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
2024
#include "clang/CIR/MissingFeatures.h"
2125

2226
// TODO(cir): we shouldn't need this but we currently reuse intrinsic IDs for
@@ -30,7 +34,9 @@
3034
#include "clang/Frontend/FrontendDiagnostic.h"
3135

3236
#include "mlir/Dialect/Func/IR/FuncOps.h"
37+
#include "mlir/IR/BuiltinAttributes.h"
3338
#include "mlir/IR/Value.h"
39+
#include "mlir/Support/LLVM.h"
3440
#include "clang/CIR/Dialect/IR/CIRDialect.h"
3541
#include "llvm/Support/ErrorHandling.h"
3642

@@ -330,6 +336,30 @@ static mlir::Value MakeAtomicCmpXchgValue(CIRGenFunction &cgf,
330336
return returnBool ? op.getResult(1) : op.getResult(0);
331337
}
332338

339+
static mlir::Value MakeAtomicFenceValue(CIRGenFunction &cgf,
340+
const CallExpr *expr,
341+
cir::MemScopeKind syncScope) {
342+
auto &builder = cgf.getBuilder();
343+
mlir::Value orderingVal = cgf.emitScalarExpr(expr->getArg(0));
344+
345+
auto constOrdering =
346+
mlir::dyn_cast<cir::ConstantOp>(orderingVal.getDefiningOp());
347+
if (!constOrdering)
348+
llvm_unreachable("NYI: variable ordering not supported");
349+
350+
auto constOrderingAttr =
351+
mlir::dyn_cast<cir::IntAttr>(constOrdering.getValue());
352+
if (constOrderingAttr) {
353+
cir::MemOrder ordering =
354+
static_cast<cir::MemOrder>(constOrderingAttr.getUInt());
355+
356+
builder.create<cir::AtomicFence>(cgf.getLoc(expr->getSourceRange()),
357+
syncScope, ordering);
358+
}
359+
360+
return mlir::Value();
361+
}
362+
333363
static bool
334364
typeRequiresBuiltinLaunderImp(const ASTContext &astContext, QualType ty,
335365
llvm::SmallPtrSetImpl<const Decl *> &seen) {
@@ -1840,10 +1870,14 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
18401870
llvm_unreachable("BI__atomic_clear NYI");
18411871

18421872
case Builtin::BI__atomic_thread_fence:
1873+
return RValue::get(
1874+
MakeAtomicFenceValue(*this, E, cir::MemScopeKind::MemScope_System));
18431875
case Builtin::BI__atomic_signal_fence:
1876+
return RValue::get(MakeAtomicFenceValue(
1877+
*this, E, cir::MemScopeKind::MemScope_SingleThread));
18441878
case Builtin::BI__c11_atomic_thread_fence:
18451879
case Builtin::BI__c11_atomic_signal_fence:
1846-
llvm_unreachable("BI__atomic_thread_fence like NYI");
1880+
llvm_unreachable("BI__c11_atomic_thread_fence like NYI");
18471881

18481882
case Builtin::BI__builtin_signbit:
18491883
case Builtin::BI__builtin_signbitf:

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

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
1919
#include "mlir/Dialect/DLTI/DLTI.h"
2020
#include "mlir/Dialect/Func/IR/FuncOps.h"
21+
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
2122
#include "mlir/Dialect/LLVMIR/Transforms/Passes.h"
2223
#include "mlir/IR/Attributes.h"
2324
#include "mlir/IR/Builders.h"
@@ -41,6 +42,8 @@
4142
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
4243
#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
4344
#include "mlir/Target/LLVMIR/Export.h"
45+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
46+
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
4447
#include "clang/CIR/Dialect/Passes.h"
4548
#include "clang/CIR/LoweringHelpers.h"
4649
#include "clang/CIR/MissingFeatures.h"
@@ -3232,6 +3235,10 @@ mlir::LLVM::AtomicOrdering getLLVMAtomicOrder(cir::MemOrder memo) {
32323235
llvm_unreachable("shouldn't get here");
32333236
}
32343237

3238+
// mlir::LLVM::AtomicSyncScope getLLVMSyncScope(cir::MemScopeKind syncScope) {
3239+
//
3240+
// }
3241+
32353242
mlir::LogicalResult CIRToLLVMAtomicCmpXchgLowering::matchAndRewrite(
32363243
cir::AtomicCmpXchg op, OpAdaptor adaptor,
32373244
mlir::ConversionPatternRewriter &rewriter) const {
@@ -3399,6 +3406,12 @@ mlir::LogicalResult CIRToLLVMAtomicFetchLowering::matchAndRewrite(
33993406
return mlir::success();
34003407
}
34013408

3409+
// mlir::LogicalResult CIRToLLVMAtomicFenceLowering::matchAndRewrite(
3410+
// cir::AtomicFence op, OpAdaptor adaptor,
3411+
// mlir::ConversionPatternRewriter &rewriter) const {
3412+
// return mlir::success();
3413+
// }
3414+
34023415
mlir::LogicalResult CIRToLLVMByteswapOpLowering::matchAndRewrite(
34033416
cir::ByteswapOp op, OpAdaptor adaptor,
34043417
mlir::ConversionPatternRewriter &rewriter) const {

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

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "mlir/IR/MLIRContext.h"
1717
#include "mlir/Interfaces/DataLayoutInterfaces.h"
1818
#include "mlir/Transforms/DialectConversion.h"
19+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1920

2021
namespace cir {
2122
namespace direct {
@@ -820,6 +821,16 @@ class CIRToLLVMAtomicFetchLowering
820821
mlir::ConversionPatternRewriter &) const override;
821822
};
822823

824+
// class CIRToLLVMAtomicFenceLowering
825+
// : public mlir::OpConversionPattern<cir::AtomicFence> {
826+
// public:
827+
// using mlir::OpConversionPattern<cir::AtomicFence>::OpConversionPattern;
828+
//
829+
// mlir::LogicalResult
830+
// matchAndRewrite(cir::AtomicFence op, OpAdaptor,
831+
// mlir::ConversionPatternRewriter &) const override;
832+
// };
833+
823834
class CIRToLLVMByteswapOpLowering
824835
: public mlir::OpConversionPattern<cir::ByteswapOp> {
825836
public:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// UN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
4+
// UN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
5+
6+
7+
struct Data {
8+
int value;
9+
void *ptr;
10+
};
11+
12+
typedef struct Data *DataPtr;
13+
14+
void applyThreadFence() {
15+
__atomic_thread_fence(5);
16+
}
17+
18+
// CIR-LABEL: cir.func no_proto @applyThreadFence
19+
// CIR: %0 = cir.const #cir.int<5> : !s32i
20+
// CIR: cir.atomic.fence(sync_scope = system, ordering = seq_cst)
21+
// CIR: cir.return
22+
23+
void applySignalFence() {
24+
__atomic_signal_fence(5);
25+
}
26+
// CIR-LABEL: cir.func no_proto @applySignalFence
27+
// CIR: %0 = cir.const #cir.int<5> : !s32i
28+
// CIR: cir.atomic.fence(sync_scope = single_thread, ordering = seq_cst)
29+
// CIR: cir.return
30+
31+
void modifyWithThreadFence(DataPtr d) {
32+
__atomic_thread_fence(5);
33+
d->value = 42;
34+
}
35+
// CIR-LABEL: cir.func @modifyWithThreadFence
36+
// CIR: %0 = cir.alloca !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>, ["d", init] {alignment = 8 : i64}
37+
// CIR: cir.store %arg0, %0 : !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>
38+
// CIR: %1 = cir.const #cir.int<5> : !s32i
39+
// CIR: cir.atomic.fence(sync_scope = system, ordering = seq_cst)
40+
// CIR: %2 = cir.const #cir.int<42> : !s32i
41+
// CIR: %3 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_Data>>, !cir.ptr<!ty_Data>
42+
// CIR: %4 = cir.get_member %3[0] {name = "value"} : !cir.ptr<!ty_Data> -> !cir.ptr<!s32i>
43+
// CIR: cir.store %2, %4 : !s32i, !cir.ptr<!s32i>
44+
// CIR: cir.return
45+
46+
void modifyWithSignalFence(DataPtr d) {
47+
__atomic_signal_fence(5);
48+
d->value = 24;
49+
}
50+
// CIR-LABEL: cir.func @modifyWithSignalFence
51+
// CIR: %0 = cir.alloca !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>, ["d", init] {alignment = 8 : i64}
52+
// CIR: cir.store %arg0, %0 : !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>
53+
// CIR: %1 = cir.const #cir.int<5> : !s32i
54+
// CIR: cir.atomic.fence(sync_scope = single_thread, ordering = seq_cst)
55+
// CIR: %2 = cir.const #cir.int<24> : !s32i
56+
// CIR: %3 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_Data>>, !cir.ptr<!ty_Data>
57+
// CIR: %4 = cir.get_member %3[0] {name = "value"} : !cir.ptr<!ty_Data> -> !cir.ptr<!s32i>
58+
// CIR: cir.store %2, %4 : !s32i, !cir.ptr<!s32i>
59+
// CIR: cir.return
60+
61+
void loadWithThreadFence(DataPtr d) {
62+
__atomic_thread_fence(5);
63+
__atomic_load_n(&d->ptr, 5);
64+
}
65+
// CIR-LABEL: cir.func @loadWithThreadFence
66+
// CIR: %0 = cir.alloca !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>, ["d", init] {alignment = 8 : i64}
67+
// CIR: %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["atomic-temp"] {alignment = 8 : i64}
68+
// CIR: cir.store %arg0, %0 : !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>
69+
// CIR: %2 = cir.const #cir.int<5> : !s32i
70+
// CIR: cir.atomic.fence(sync_scope = system, ordering = seq_cst)
71+
// CIR: %3 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_Data>>, !cir.ptr<!ty_Data>
72+
// CIR: %4 = cir.get_member %3[1] {name = "ptr"} : !cir.ptr<!ty_Data> -> !cir.ptr<!cir.ptr<!void>>
73+
// CIR: %5 = cir.const #cir.int<5> : !s32i
74+
// CIR: %6 = cir.cast(bitcast, %4 : !cir.ptr<!cir.ptr<!void>>), !cir.ptr<!u64i>
75+
// CIR: %7 = cir.load atomic(seq_cst) %6 : !cir.ptr<!u64i>, !u64i
76+
// CIR: %8 = cir.cast(bitcast, %1 : !cir.ptr<!cir.ptr<!void>>), !cir.ptr<!u64i>
77+
// CIR: cir.store %7, %8 : !u64i, !cir.ptr<!u64i>
78+
// CIR: %9 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
79+
// CIR: cir.return
80+
81+
void loadWithSignalFence(DataPtr d) {
82+
__atomic_signal_fence(5);
83+
__atomic_load_n(&d->ptr, 5);
84+
}
85+
// CIR-LABEL: cir.func @loadWithSignalFence
86+
// CIR: %0 = cir.alloca !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>, ["d", init] {alignment = 8 : i64}
87+
// CIR: %1 = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["atomic-temp"] {alignment = 8 : i64}
88+
// CIR: cir.store %arg0, %0 : !cir.ptr<!ty_Data>, !cir.ptr<!cir.ptr<!ty_Data>>
89+
// CIR: %2 = cir.const #cir.int<5> : !s32i
90+
// CIR: cir.atomic.fence(sync_scope = single_thread, ordering = seq_cst)
91+
// CIR: %3 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_Data>>, !cir.ptr<!ty_Data>
92+
// CIR: %4 = cir.get_member %3[1] {name = "ptr"} : !cir.ptr<!ty_Data> -> !cir.ptr<!cir.ptr<!void>>
93+
// CIR: %5 = cir.const #cir.int<5> : !s32i
94+
// CIR: %6 = cir.cast(bitcast, %4 : !cir.ptr<!cir.ptr<!void>>), !cir.ptr<!u64i>
95+
// CIR: %7 = cir.load atomic(seq_cst) %6 : !cir.ptr<!u64i>, !u64i
96+
// CIR: %8 = cir.cast(bitcast, %1 : !cir.ptr<!cir.ptr<!void>>), !cir.ptr<!u64i>
97+
// CIR: cir.store %7, %8 : !u64i, !cir.ptr<!u64i>
98+
// CIR: %9 = cir.load %1 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
99+
// CIR: cir.return

0 commit comments

Comments
 (0)