diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1577,6 +1577,11 @@ BUILTIN(__builtin_ms_va_end, "vc*&", "n") BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n") +// clang builtin to expose llvm fence instruction +// First argument : uint in range [2, 5] i.e. [acquire, seq_cst] +// Second argument : target specific sync scope string +BUILTIN(__builtin_memory_fence, "vUicC*", "n") + #undef BUILTIN #undef LIBBUILTIN #undef LANGBUILTIN diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -28,6 +28,7 @@ #include "clang/CodeGen/CGFunctionInfo.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Intrinsics.h" @@ -13616,6 +13617,43 @@ Function *F = CGM.getIntrinsic(Intrinsic::fshr, Src0->getType()); return Builder.CreateCall(F, { Src0, Src1, Src2 }); } + + case Builtin::BI__builtin_memory_fence: { + llvm::AtomicOrdering AO = llvm::AtomicOrdering::SequentiallyConsistent; + llvm::SyncScope::ID SSID; + Value *Order = EmitScalarExpr(E->getArg(0)); + Value *Scope = EmitScalarExpr(E->getArg(1)); + + if ( isa(Order)) { + int ord = cast(Order)->getZExtValue(); + + // Map C11/C++11 memory ordering to LLVM memory ordering + switch (static_cast(ord)) { + case llvm::AtomicOrderingCABI::acquire: + AO = llvm::AtomicOrdering::Acquire; + break; + case llvm::AtomicOrderingCABI::release: + AO = llvm::AtomicOrdering::Release; + break; + case llvm::AtomicOrderingCABI::acq_rel: + AO = llvm::AtomicOrdering::AcquireRelease; + break; + case llvm::AtomicOrderingCABI::seq_cst: + AO = llvm::AtomicOrdering::SequentiallyConsistent; + break; + case llvm::AtomicOrderingCABI::consume: // not supported by LLVM fence + case llvm::AtomicOrderingCABI::relaxed: // not supported by LLVM fence + break; + } + + StringRef scp; + llvm::getConstantStringInfo(Scope, scp); + SSID = getLLVMContext().getOrInsertSyncScopeID(scp); + + return Builder.CreateFence(AO, SSID); + } + LLVM_FALLTHROUGH; + } default: return nullptr; } diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1855,7 +1855,7 @@ return ExprError(); break; case Builtin::BI__builtin_frame_address: - case Builtin::BI__builtin_return_address: + case Builtin::BI__builtin_return_address: { if (SemaBuiltinConstantArgRange(TheCall, 0, 0, 0xFFFF)) return ExprError(); @@ -1869,6 +1869,30 @@ ? "__builtin_return_address" : "__builtin_frame_address") << TheCall->getSourceRange(); + } + break; + + case Builtin::BI__builtin_memory_fence: { + ExprResult Arg = TheCall->getArg(0); + auto ArgExpr = Arg.get(); + Expr::EvalResult ArgResult; + + if(!ArgExpr->EvaluateAsInt(ArgResult, Context)) { + Diag(ArgExpr->getExprLoc(), diag::err_typecheck_expect_int) << + ArgExpr->getType(); + return ExprError(); + } + int ord = ArgResult.Val.getInt().getZExtValue(); + + // Check valididty of memory ordering as per C11 / C++11's memody model. + if (ord < static_cast(llvm::AtomicOrderingCABI::acquire) || + ord > static_cast(llvm::AtomicOrderingCABI::seq_cst)) { + Diag(ArgExpr->getBeginLoc(), + diag::warn_atomic_op_has_invalid_memory_order) + << ArgExpr->getSourceRange(); + return ExprError(); + } + } break; } diff --git a/clang/test/CodeGenHIP/builtin_memory_fence.cpp b/clang/test/CodeGenHIP/builtin_memory_fence.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenHIP/builtin_memory_fence.cpp @@ -0,0 +1,22 @@ +// REQUIRES: amdgpu-registered-target +// RUN: %clang_cc1 %s -x hip -emit-llvm -O0 -o - \ +// RUN: -triple=amdgcn-amd-amdhsa | opt -instnamer -S | FileCheck %s + +void test_memory_fence_success() { +// CHECK-LABEL: test_memory_fence_success + + // CHECK: fence syncscope("workgroup") seq_cst + __builtin_memory_fence(__ATOMIC_SEQ_CST, "workgroup"); + + // CHECK: fence syncscope("agent") acquire + __builtin_memory_fence(__ATOMIC_ACQUIRE, "agent"); + + // CHECK: fence seq_cst + __builtin_memory_fence(__ATOMIC_SEQ_CST, ""); + + // CHECK: fence syncscope("agent") acq_rel + __builtin_memory_fence(4, "agent"); + + // CHECK: fence syncscope("workgroup") release + __builtin_memory_fence(3, "workgroup"); +} \ No newline at end of file diff --git a/clang/test/Sema/builtins.c b/clang/test/Sema/builtins.c --- a/clang/test/Sema/builtins.c +++ b/clang/test/Sema/builtins.c @@ -320,3 +320,15 @@ // expected-error@+1 {{use of unknown builtin '__builtin_is_constant_evaluated'}} return __builtin_is_constant_evaluated(); } + +void test_memory_fence_errors() { + __builtin_memory_fence(__ATOMIC_SEQ_CST + 1, "workgroup"); // expected-warning {{memory order argument to atomic operation is invalid}} + + __builtin_memory_fence(__ATOMIC_ACQUIRE - 1, "workgroup"); // expected-warning {{memory order argument to atomic operation is invalid}} + + __builtin_memory_fence(4); // expected-error {{too few arguments to function call, expected 2}} + + __builtin_memory_fence(4, 4, 4); // expected-error {{too many arguments to function call, expected 2}} + + __builtin_memory_fence(3.14, ""); // expected-warning {{implicit conversion from 'double' to 'unsigned int' changes value from 3.14 to 3}} +}