[clang] [CIR] Upstream AtomicFenceOp and signal/thread fence builtins (PR #168346)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 17 03:13:58 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clangir
Author: Hendrik Hübner (HendrikHuebner)
<details>
<summary>Changes</summary>
This PR upstreams the AtomicFence operation from the incubator repo.
---
Full diff: https://github.com/llvm/llvm-project/pull/168346.diff
3 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+35)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+39)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+21)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 2124b1dc62a81..ee63ac39c5ba4 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4845,4 +4845,39 @@ def CIR_AtomicClearOp : CIR_Op<"atomic.clear"> {
}];
}
+def CIR_MemScopeKind : CIR_I32EnumAttr<"MemScopeKind", "memory scope kind", [
+ I32EnumAttrCase<"SingleThread", 0, "single_thread">,
+ I32EnumAttrCase<"System", 1, "system">
+]>;
+
+def CIR_AtomicFence : CIR_Op<"atomic.fence"> {
+ let summary = "Atomic thread fence";
+ let description = [{
+ C/C++ Atomic thread fence synchronization primitive. Implements the builtin
+ `__atomic_thread_fence` which enforces memory ordering constraints across
+ threads within the specified synchronization scope.
+
+ This handles all variations including:
+ - `__atomic_thread_fence`
+ - `__atomic_signal_fence`
+ - `__c11_atomic_thread_fence`
+ - `__c11_atomic_signal_fence`
+
+ Example:
+ ```mlir
+ cir.atomic.fence syncscope(system) seq_cst
+ cir.atomic.fence syncscope(single_thread) seq_cst
+ ```
+ }];
+
+ let arguments = (ins
+ Arg<CIR_MemOrder, "memory order">:$ordering,
+ OptionalAttr<CIR_MemScopeKind>:$syncscope
+ );
+
+ let assemblyFormat = [{
+ (`syncscope` `(` $syncscope^ `)`)? $ordering attr-dict
+ }];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 77f19343653db..1223669a76ab1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -58,6 +58,28 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
return RValue::get(result);
}
+static mlir::Value makeAtomicFenceValue(CIRGenFunction &cgf,
+ const CallExpr *expr,
+ cir::MemScopeKind syncScope) {
+ auto &builder = cgf.getBuilder();
+ mlir::Value orderingVal = cgf.emitScalarExpr(expr->getArg(0));
+
+ auto constOrdering = orderingVal.getDefiningOp<cir::ConstantOp>();
+ if (!constOrdering)
+ llvm_unreachable("NYI: variable ordering not supported");
+
+ if (auto constOrderingAttr = constOrdering.getValueAttr<cir::IntAttr>()) {
+ cir::MemOrder ordering =
+ static_cast<cir::MemOrder>(constOrderingAttr.getUInt());
+
+ cir::AtomicFence::create(
+ builder, cgf.getLoc(expr->getSourceRange()), ordering,
+ cir::MemScopeKindAttr::get(&cgf.getMLIRContext(), syncScope));
+ }
+
+ return {};
+}
+
RValue CIRGenFunction::emitRotate(const CallExpr *e, bool isRotateLeft) {
mlir::Value input = emitScalarExpr(e->getArg(0));
mlir::Value amount = emitScalarExpr(e->getArg(1));
@@ -520,6 +542,23 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
cir::PrefetchOp::create(builder, loc, address, locality, isWrite);
return RValue::get(nullptr);
}
+ case Builtin::BI__c11_atomic_is_lock_free:
+ llvm_unreachable("BI__c11_atomic_is_lock_free NYI");
+ case Builtin::BI__atomic_is_lock_free:
+ llvm_unreachable("BI__atomic_is_lock_free NYI");
+ case Builtin::BI__atomic_test_and_set:
+ llvm_unreachable("BI__atomic_test_and_set NYI");
+ case Builtin::BI__atomic_clear:
+ llvm_unreachable("BI__atomic_clear NYI");
+ case Builtin::BI__atomic_thread_fence:
+ return RValue::get(
+ makeAtomicFenceValue(*this, e, cir::MemScopeKind::System));
+ case Builtin::BI__atomic_signal_fence:
+ return RValue::get(
+ makeAtomicFenceValue(*this, e, cir::MemScopeKind::SingleThread));
+ case Builtin::BI__c11_atomic_thread_fence:
+ case Builtin::BI__c11_atomic_signal_fence:
+ llvm_unreachable("BI__c11_atomic_thread_fence like NYI");
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index d88a4ad76f27b..755a9b572f3ca 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -732,6 +732,14 @@ getLLVMMemOrder(std::optional<cir::MemOrder> memorder) {
llvm_unreachable("unknown memory order");
}
+static std::optional<llvm::StringRef>
+getLLVMSyncScope(std::optional<cir::MemScopeKind> syncScope) {
+ if (syncScope.has_value())
+ return syncScope.value() == cir::MemScopeKind::SingleThread ? "singlethread"
+ : "";
+ return std::nullopt;
+}
+
mlir::LogicalResult CIRToLLVMAtomicCmpXchgOpLowering::matchAndRewrite(
cir::AtomicCmpXchgOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -808,6 +816,19 @@ mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMAtomicFenceLowering::matchAndRewrite(
+ cir::AtomicFence op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(adaptor.getOrdering());
+
+ auto fence = mlir::LLVM::FenceOp::create(rewriter, op.getLoc(), llvmOrder);
+ fence.setSyncscope(getLLVMSyncScope(adaptor.getSyncscope()));
+
+ rewriter.replaceOp(op, fence);
+
+ return mlir::success();
+}
+
static mlir::LLVM::AtomicBinOp
getLLVMAtomicBinOp(cir::AtomicFetchKind k, bool isInt, bool isSignedInt) {
switch (k) {
``````````
</details>
https://github.com/llvm/llvm-project/pull/168346
More information about the cfe-commits
mailing list