Index: clang/include/clang/Basic/Builtins.def =================================================================== --- clang/include/clang/Basic/Builtins.def +++ clang/include/clang/Basic/Builtins.def @@ -1513,6 +1513,9 @@ BUILTIN(__builtin_coro_suspend, "cIb", "n") BUILTIN(__builtin_coro_param, "bv*v*", "n") +// Builtin to expose llvm fence instruction +BUILTIN(__builtin_memory_fence, "vUiUi", "t") + // OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions. // We need the generic prototype, since the packet type could be anything. LANGBUILTIN(read_pipe, "i.", "tn", OCLC20_LANG) Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6225,6 +6225,8 @@ "cannot %select{decrement|increment}1 value of type %0">; def err_typecheck_expect_int : Error< "used type %0 where integer is required">; +def err_typecheck_expect_uint : Error< + "used type %0 where unsigned is required">; def err_typecheck_arithmetic_incomplete_type : Error< "arithmetic on a pointer to an incomplete type %0">; def err_typecheck_pointer_arith_function_type : Error< @@ -7855,6 +7857,10 @@ InGroup>; def err_atomic_op_has_invalid_synch_scope : Error< "synchronization scope argument to atomic operation is invalid">; +def err_memory_fence_has_invalid_memory_order : Error< + "memory order argument to fence operation is invalid">; +def err_memory_fence_has_invalid_synch_scope : Error< + "synchronization scope argument to fence operation is invalid">; def warn_atomic_implicit_seq_cst : Warning< "implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary">, InGroup>, DefaultIgnore; Index: clang/lib/CodeGen/CGBuiltin.cpp =================================================================== --- clang/lib/CodeGen/CGBuiltin.cpp +++ clang/lib/CodeGen/CGBuiltin.cpp @@ -3698,6 +3698,42 @@ case Builtin::BI__builtin_coro_param: return EmitCoroutineIntrinsic(E, Intrinsic::coro_param); + // Clang builtins to expose llvm fence instruction + case Builtin::BI__builtin_memory_fence: { + llvm::AtomicOrdering AO; + llvm::SyncScope::ID SSID; + Value *Order = EmitScalarExpr(E->getArg(0)); + Value *Scope = EmitScalarExpr(E->getArg(1)); + auto ScopeModel = AtomicScopeModel::create(AtomicScopeModelKind::OpenCL); + + if ( isa(Order) && isa(Scope) ) { + int scp = cast(Scope)->getZExtValue(); + int ord = cast(Order)->getZExtValue(); + switch (ord) { + case 0: // memory_order_relaxed + default: // invalid order + break; + case 1: // memory_order_consume + case 2: // memory_order_acquire + AO = llvm::AtomicOrdering::Acquire; + break; + case 3: // memory_order_release + AO = llvm::AtomicOrdering::Release; + break; + case 4: // memory_order_acq_rel + AO = llvm::AtomicOrdering::AcquireRelease; + break; + case 5: // memory_order_seq_cst + AO = llvm::AtomicOrdering::SequentiallyConsistent; + break; + } + SSID = getTargetHooks().getLLVMSyncScopeID(getLangOpts(), ScopeModel->map(scp), AO, getLLVMContext()); + Builder.CreateFence(AO, SSID); + return RValue::get(nullptr); + } + LLVM_FALLTHROUGH; + } + // OpenCL v2.0 s6.13.16.2, Built-in pipe read and write functions case Builtin::BIread_pipe: case Builtin::BIwrite_pipe: { Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -1865,6 +1865,60 @@ : "__builtin_frame_address") << TheCall->getSourceRange(); break; + + // Clang builtins to expose llvm fence instruction + case Builtin::BI__builtin_memory_fence: { + if (checkArgCount(*this, TheCall, 2)) + return true; + + // Order should be the first argument + ExprResult Arg = TheCall->getArg(0); + auto ArgExpr = Arg.get(); + auto Ty = ArgExpr->getType(); + + // Check if Order is an unsigned + if (!Ty->isIntegerType()) { + Diag(ArgExpr->getExprLoc(), diag::err_typecheck_expect_uint) << Ty; + return ExprError(); + } + + Expr::EvalResult ArgResult; + ArgExpr->EvaluateAsInt(ArgResult, Context); + int ord = ArgResult.Val.getInt().getZExtValue(); + + // Check if Order is one of the valid types + if (!llvm::isValidAtomicOrderingCABI(ord)) { + Diag(ArgExpr->getBeginLoc(), + diag::err_memory_fence_has_invalid_memory_order) + << ArgExpr->getSourceRange(); + return ExprError(); + } + + // Sync scope should be the second argument + Arg = TheCall->getArg(1); + ArgExpr = Arg.get(); + Ty = ArgExpr->getType(); + + // Check if SyncScope is an unsgined + if (!Ty->isIntegerType()) { + Diag(ArgExpr->getExprLoc(), diag::err_typecheck_expect_uint) << Ty; + return ExprError(); + } + + ArgExpr->EvaluateAsInt(ArgResult, Context); + int scp = ArgResult.Val.getInt().getZExtValue(); + auto SM = AtomicScopeModel::create(AtomicScopeModelKind::OpenCL); + AtomicScopeModel *ScopeModel = SM.get(); + + // Check if Scope is one of the valid types + if (!ScopeModel->isValid(scp)) { + Diag(ArgExpr->getBeginLoc(), + diag::err_memory_fence_has_invalid_synch_scope) + << ArgExpr->getSourceRange(); + return ExprError(); + } + break; + } } // Since the target specific builtins for each arch overlap, only check those Index: clang/test/CodeGenOpenCL/atomic-ops.cl =================================================================== --- clang/test/CodeGenOpenCL/atomic-ops.cl +++ clang/test/CodeGenOpenCL/atomic-ops.cl @@ -288,4 +288,44 @@ return __opencl_atomic_load(i, memory_order_seq_cst, memory_scope_work_group); } +void test_memory_fence() { + // CHECK-LABEL: @test_memory_fence + + // CHECK: fence syncscope("workgroup-one-as") acquire + __builtin_memory_fence(memory_order_acquire, memory_scope_work_group); + + // CHECK: fence syncscope("agent-one-as") acquire + __builtin_memory_fence(memory_order_acquire, memory_scope_device); + + // CHECK: fence syncscope("one-as") acquire + __builtin_memory_fence(memory_order_acquire, memory_scope_all_svm_devices); + + // CHECK: fence syncscope("wavefront-one-as") acquire + __builtin_memory_fence(memory_order_acquire, memory_scope_sub_group); + + // CHECK: fence syncscope("workgroup-one-as") release + __builtin_memory_fence(memory_order_release, memory_scope_work_group); + + // CHECK: fence syncscope("agent-one-as") release + __builtin_memory_fence(memory_order_release, memory_scope_device); + + // CHECK: fence syncscope("one-as") release + __builtin_memory_fence(memory_order_release, memory_scope_all_svm_devices); + + // CHECK: fence syncscope("wavefront-one-as") release + __builtin_memory_fence(memory_order_release, memory_scope_sub_group); + + // CHECK: fence syncscope("workgroup") seq_cst + __builtin_memory_fence(memory_order_seq_cst, memory_scope_work_group); + + // CHECK: fence syncscope("agent") seq_cst + __builtin_memory_fence(memory_order_seq_cst, memory_scope_device); + + // CHECK: fence seq_cst + __builtin_memory_fence(memory_order_seq_cst, memory_scope_all_svm_devices); + + // CHECK: fence syncscope("wavefront") seq_cst + __builtin_memory_fence(memory_order_seq_cst, memory_scope_sub_group); +} + #endif Index: clang/test/SemaOpenCL/atomic-ops.cl =================================================================== --- clang/test/SemaOpenCL/atomic-ops.cl +++ clang/test/SemaOpenCL/atomic-ops.cl @@ -194,3 +194,21 @@ // The 'expected' pointer shouldn't be NULL. (void)__opencl_atomic_compare_exchange_strong(Ap, (void *)0, val, memory_order_relaxed, memory_order_relaxed, memory_scope_work_group); // expected-warning {{null passed to a callee that requires a non-null argument}} } + +void memory_fence_errors() { + __builtin_memory_fence(memory_order_seq_cst + 1, memory_scope_work_group); // expected-error {{memory order argument to fence operation is invalid}} + + __builtin_memory_fence(memory_order_seq_cst, memory_scope_sub_group + 1); // expected-error {{synchronization scope argument to fence operation is invalid}} + + __builtin_memory_fence(memory_scope_work_group); // expected-error {{too few arguments to function call, expected 2}} + + __builtin_memory_fence(2, 2, 2); // expected-error {{too many arguments to function call, expected 2}} + + __builtin_memory_fence("string", memory_scope_work_group); // expected-error {{used type '__constant char [7]' where unsigned is required}} + + __builtin_memory_fence(3.14, memory_scope_work_group); // expected-error {{used type 'double' where unsigned is required}} + + __builtin_memory_fence(memory_order_seq_cst, "string"); // expected-error {{used type '__constant char [7]' where unsigned is required}} + + __builtin_memory_fence(memory_order_seq_cst, 3.14); // expected-error {{used type 'double' where unsigned is required}} +}