Skip to content

Commit

Permalink
[CIR][CIRGen] Support for builtin __atomic_thread_fence
Browse files Browse the repository at this point in the history
Resolves #1274

Implements atomic thread fence synchronization primitive
corresponding to `atomic.thread_fence` CIR.
  • Loading branch information
Rajveer100 committed Feb 4, 2025
1 parent d329c96 commit 6425312
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 1 deletion.
41 changes: 41 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -5409,6 +5409,47 @@ def AtomicCmpXchg : CIR_Op<"atomic.cmp_xchg",
let hasVerifier = 0;
}

def MemScope_SingleThread : I32EnumAttrCase<"MemScope_SingleThread",
0, "single_thread">;
def MemScope_System : I32EnumAttrCase<"MemScope_System",
1, "system">;

def MemScopeKind : I32EnumAttr<
"MemScopeKind",
"Memory Scope Enumeration",
[MemScope_SingleThread, MemScope_System]> {
let cppNamespace = "::cir";
}

def 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:


}];
let results = (outs);
let arguments = (ins Arg<MemScopeKind, "sync scope">:$sync_scope,
Arg<MemOrder, "memory order">:$ordering);

let assemblyFormat = [{
`(` `sync_scope` `=` $sync_scope `,`
`ordering` `=` $ordering `)` attr-dict
}];

let hasVerifier = 0;
}

def SignBitOp : CIR_Op<"signbit", [Pure]> {
let summary = "Checks the sign of a floating-point number";
let description = [{
Expand Down
34 changes: 33 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
#include "CIRGenCstEmitter.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#include "CIRGenValue.h"
#include "TargetInfo.h"
#include "clang/AST/Expr.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/MissingFeatures.h"

// TODO(cir): we shouldn't need this but we currently reuse intrinsic IDs for
Expand All @@ -30,6 +33,7 @@
#include "clang/Frontend/FrontendDiagnostic.h"

#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Value.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -330,6 +334,30 @@ static mlir::Value MakeAtomicCmpXchgValue(CIRGenFunction &cgf,
return returnBool ? op.getResult(1) : op.getResult(0);
}

static mlir::Value MakeAtomicFenceValue(CIRGenFunction &cgf,
const CallExpr *expr,
cir::MemScopeKind syncScope) {
QualType typ = expr->getType();
auto &builder = cgf.getBuilder();

auto intType =
expr->getArg(0)->getType()->getPointeeType()->isUnsignedIntegerType()
? builder.getUIntNTy(cgf.getContext().getTypeSize(typ))
: builder.getSIntNTy(cgf.getContext().getTypeSize(typ));

auto orderingVal =
emitToInt(cgf, cgf.emitScalarExpr(expr->getArg(0)), typ, intType);
auto orderingAttr =
orderingVal.getDefiningOp()->getAttrOfType<mlir::IntegerAttr>("value");

cir::MemOrder ordering = static_cast<cir::MemOrder>(orderingAttr.getInt());

builder.create<cir::AtomicFence>(cgf.getLoc(expr->getSourceRange()),
syncScope, ordering);

return mlir::Value();
}

static bool
typeRequiresBuiltinLaunderImp(const ASTContext &astContext, QualType ty,
llvm::SmallPtrSetImpl<const Decl *> &seen) {
Expand Down Expand Up @@ -1840,10 +1868,14 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
llvm_unreachable("BI__atomic_clear NYI");

case Builtin::BI__atomic_thread_fence:
return RValue::get(
MakeAtomicFenceValue(*this, E, cir::MemScopeKind::MemScope_System));
case Builtin::BI__atomic_signal_fence:
return RValue::get(MakeAtomicFenceValue(
*this, E, cir::MemScopeKind::MemScope_SingleThread));
case Builtin::BI__c11_atomic_thread_fence:
case Builtin::BI__c11_atomic_signal_fence:
llvm_unreachable("BI__atomic_thread_fence like NYI");
llvm_unreachable("BI__c11_atomic_thread_fence like NYI");

case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
#include "mlir/Dialect/DLTI/DLTI.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
#include "mlir/Dialect/LLVMIR/Transforms/Passes.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
Expand All @@ -41,6 +42,8 @@
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Export.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Dialect/Passes.h"
#include "clang/CIR/LoweringHelpers.h"
#include "clang/CIR/MissingFeatures.h"
Expand Down Expand Up @@ -3399,6 +3402,12 @@ mlir::LogicalResult CIRToLLVMAtomicFetchLowering::matchAndRewrite(
return mlir::success();
}

// mlir::LogicalResult CIRToLLVMAtomicFenceLowering::matchAndRewrite(
// cir::AtomicFence op, OpAdaptor adaptor,
// mlir::ConversionPatternRewriter &rewriter) const {
// return mlir::success();
// }

mlir::LogicalResult CIRToLLVMByteswapOpLowering::matchAndRewrite(
cir::ByteswapOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "mlir/IR/MLIRContext.h"
#include "mlir/Interfaces/DataLayoutInterfaces.h"
#include "mlir/Transforms/DialectConversion.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"

namespace cir {
namespace direct {
Expand Down Expand Up @@ -820,6 +821,16 @@ class CIRToLLVMAtomicFetchLowering
mlir::ConversionPatternRewriter &) const override;
};

// class CIRToLLVMAtomicFenceLowering
// : public mlir::OpConversionPattern<cir::AtomicFence> {
// public:
// using mlir::OpConversionPattern<cir::AtomicFence>::OpConversionPattern;
//
// mlir::LogicalResult
// matchAndRewrite(cir::AtomicFence op, OpAdaptor,
// mlir::ConversionPatternRewriter &) const override;
// };

class CIRToLLVMByteswapOpLowering
: public mlir::OpConversionPattern<cir::ByteswapOp> {
public:
Expand Down
40 changes: 40 additions & 0 deletions clang/test/CIR/CodeGen/atomic-thread-fence.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s
// UN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// UN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s


struct Data {
int value;
void *ptr;
};

typedef struct Data *DataPtr;

void applyThreadFence() {
__atomic_thread_fence(5);
}

void applySignalFence() {
__atomic_signal_fence(5);
}

void modifyWithThreadFence(DataPtr d) {
__atomic_thread_fence(5);
d->value = 42;
}

void modifyWithSignalFence(DataPtr d) {
__atomic_signal_fence(5);
d->value = 24;
}

void loadWithThreadFence(DataPtr d) {
__atomic_thread_fence(5);
__atomic_load_n(&d->ptr, 5);
}

void loadWithSignalFence(DataPtr d) {
__atomic_signal_fence(5);
__atomic_load_n(&d->ptr, 5);
}

0 comments on commit 6425312

Please sign in to comment.