llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Akash Manna (akash-manna-sky)

<details>
<summary>Changes</summary>

[Clang] Accept vector types in the atomic builtins
 

Fixes #<!-- -->213237

`atomicrmw` operates elementwise on vector operands, but Sema rejected every
vector value type, so none of the atomic builtins could be used with one:

```c
typedef _Float16 half2 __attribute__((ext_vector_type(2)));

half2 f(half2 *p, half2 v) {
  return __scoped_atomic_fetch_add(p, v, __ATOMIC_RELAXED,
                                   __MEMORY_SCOPE_DEVICE);
  // error: address argument to atomic operation must be a pointer to
  //        integer, pointer or supported floating point type
}
```

The same failure occurred for the `__atomic`, `__c11_atomic`, `__hip_atomic`
and `__opencl_atomic` spellings.

## Sema

`BuildAtomicExpr` tested `isIntegerType()`, `isPointerType()` and
`isFloatingType()` directly on the value type, and all three are false for a
`VectorType`. This checks the element type instead, which matches how the LLVM
verifier states the rule for `atomicrmw` (`isFPOrFPVectorTy()` /
`isIntOrIntVectorTy()`). Vectors of `_Bool` remain rejected, since their
elements are not individually addressable.

The arithmetic operations are the one family with no libcall fallback --
`EmitAtomicExpr()` reaches an `llvm_unreachable` on the libcall path -- so Sema
also rejects a vector that cannot be emitted inline, rather than crashing on
`float3` (not a power-of-two size) or `float8` (larger than the 16 bytes
CodeGen emits without a libcall). This is reported by a new diagnostic,
`err_atomic_op_needs_supported_vector`.

## CodeGen

Two matching assumptions had to move with it:

* the `atomicrmw` opcode was selected from 
`getValueType()-&gt;isFloatingType()`,
  which would have picked the integer `Add` for a vector of floats;
* `shouldCastToInt()` bitcast any non-scalar operand to `iN`. That is required
  for `cmpxchg`, which has no vector form, and harmless for load and store, but
  wrong for arithmetic: an `atomicrmw add` on an `i32` propagates carries across
  the lanes of a `&lt;2 x i16&gt;`. A vector whose size is not a power of two 
is still
  accessed as an integer, as it is not a valid atomic operand, so existing
  codegen for e.g. `_Atomic(float3)` is unchanged.

The same `shouldCastToInt()` rule is mirrored in `CIRGenAtomic.cpp`, so
`-fclangir` reports its existing "unsupported type" error instead of silently
emitting an integer operation.

## Tests

* `clang/test/Sema/atomic-ops-vector.c` (new) -- accepted and rejected vector
  types across the GNU, C11 and scoped forms, on `x86_64` and `amdgcn`.
* `clang/test/CodeGen/atomic-ops-vector.c` (new) -- verifies the emitted
  `atomicrmw` / `load atomic` / `store atomic` uses the vector type directly,
  including the non-power-of-two integer fallback and the AMDGPU `syncscope`.

Fixes #<!-- -->213237


---

Patch is 21.73 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/214653.diff


7 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+6) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4) 
- (modified) clang/lib/CIR/CodeGen/CIRGenAtomic.cpp (+5) 
- (modified) clang/lib/CodeGen/CGAtomic.cpp (+43-29) 
- (modified) clang/lib/Sema/SemaChecking.cpp (+33-1) 
- (added) clang/test/CodeGen/atomic-ops-vector.c (+95) 
- (added) clang/test/Sema/atomic-ops-vector.c (+65) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7976b82b63f6e..7764ec68e8e91 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -169,6 +169,12 @@ features cannot lower the translation-unit ABI level;
 
 ### Non-comprehensive list of changes in this release
 
+- The `__atomic`, `__c11_atomic`, `__hip_atomic`, `__opencl_atomic` and
+  `__scoped_atomic` builtins now accept vector types. The operation is 
performed
+  elementwise by a single atomic instruction. The arithmetic operations require
+  the vector to have a power-of-two size of at most 16 bytes, as they cannot be
+  lowered to a libcall.
+
 ### New Compiler Flags
 
 - New option `-fdefined-pointer-subtraction` added to preserve stable semantics
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b57ff6c197d1d..4da92e5170d45 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9704,6 +9704,10 @@ def err_atomic_op_needs_atomic_int : Error<
 def err_atomic_op_needs_atomic_fp
     : Error<"address argument to atomic operation must be a pointer to "
             "%select{|atomic }0floating point type (%1 invalid)">;
+def err_atomic_op_needs_supported_vector
+    : Error<"address argument to atomic operation must be a pointer to a "
+            "vector with a power-of-two size of at most %0 bytes "
+            "(%1 invalid)">;
 def warn_atomic_op_has_invalid_memory_order : Warning<
   "%select{|success |failure }0memory order argument to atomic operation is 
invalid">,
   InGroup<DiagGroup<"atomic-memory-ordering">>;
diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp 
b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
index 4a028e0be34f7..21b145a9cbee4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
@@ -326,6 +326,11 @@ bool AtomicInfo::emitMemSetZeroIfNecessary() const {
 /// floating point operands.  TODO: Allow compare-and-exchange and FP - see
 /// comment in CIRGenAtomicExpandPass.cpp.
 static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg) {
+  // The atomic operations act on a vector directly, which is required for the
+  // elementwise arithmetic operations. cmpxchg has no vector form, so it is
+  // still handled as an integer of the same size.
+  if (isa<cir::VectorType>(valueTy))
+    return cmpxchg;
   if (cir::isAnyFloatingPointType(valueTy))
     return isa<cir::FP80Type>(valueTy) || cmpxchg;
   return !isa<cir::IntType>(valueTy) && !isa<cir::PointerType>(valueTy);
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index cd7b278eab15d..2760cd9b03eb6 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/Support/MathExtras.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -529,7 +530,7 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy 
&Builder,
                                          bool IsSigned,
                                          llvm::Value *OldVal,
                                          llvm::Value *RHS) {
-  const bool IsFP = OldVal->getType()->isFloatingPointTy();
+  const bool IsFP = OldVal->getType()->isFPOrFPVectorTy();
 
   if (IsFP) {
     llvm::Intrinsic::ID IID = (Op == AtomicExpr::AO__atomic_max_fetch ||
@@ -567,6 +568,13 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr 
*E, Address Dest,
   bool PostOpMinMax = false;
   unsigned PostOp = 0;
 
+  // Atomic operations on a vector are performed elementwise, so it is the
+  // element type which selects between the integer and the floating point
+  // form of an operation.
+  QualType ElemTy = E->getValueType();
+  if (const auto *VecTy = ElemTy->getAs<VectorType>())
+    ElemTy = VecTy->getElementType();
+
   switch (E->getOp()) {
   case AtomicExpr::AO__c11_atomic_init:
   case AtomicExpr::AO__opencl_atomic_init:
@@ -666,30 +674,30 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr 
*E, Address Dest,
 
   case AtomicExpr::AO__atomic_add_fetch:
   case AtomicExpr::AO__scoped_atomic_add_fetch:
-    PostOp = E->getValueType()->isFloatingType() ? llvm::Instruction::FAdd
-                                                 : llvm::Instruction::Add;
+    PostOp = ElemTy->isFloatingType() ? llvm::Instruction::FAdd
+                                      : llvm::Instruction::Add;
     [[fallthrough]];
   case AtomicExpr::AO__c11_atomic_fetch_add:
   case AtomicExpr::AO__hip_atomic_fetch_add:
   case AtomicExpr::AO__opencl_atomic_fetch_add:
   case AtomicExpr::AO__atomic_fetch_add:
   case AtomicExpr::AO__scoped_atomic_fetch_add:
-    Op = E->getValueType()->isFloatingType() ? llvm::AtomicRMWInst::FAdd
-                                             : llvm::AtomicRMWInst::Add;
+    Op = ElemTy->isFloatingType() ? llvm::AtomicRMWInst::FAdd
+                                  : llvm::AtomicRMWInst::Add;
     break;
 
   case AtomicExpr::AO__atomic_sub_fetch:
   case AtomicExpr::AO__scoped_atomic_sub_fetch:
-    PostOp = E->getValueType()->isFloatingType() ? llvm::Instruction::FSub
-                                                 : llvm::Instruction::Sub;
+    PostOp = ElemTy->isFloatingType() ? llvm::Instruction::FSub
+                                      : llvm::Instruction::Sub;
     [[fallthrough]];
   case AtomicExpr::AO__c11_atomic_fetch_sub:
   case AtomicExpr::AO__hip_atomic_fetch_sub:
   case AtomicExpr::AO__opencl_atomic_fetch_sub:
   case AtomicExpr::AO__atomic_fetch_sub:
   case AtomicExpr::AO__scoped_atomic_fetch_sub:
-    Op = E->getValueType()->isFloatingType() ? llvm::AtomicRMWInst::FSub
-                                             : llvm::AtomicRMWInst::Sub;
+    Op = ElemTy->isFloatingType() ? llvm::AtomicRMWInst::FSub
+                                  : llvm::AtomicRMWInst::Sub;
     break;
 
   case AtomicExpr::AO__atomic_min_fetch:
@@ -701,23 +709,22 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr 
*E, Address Dest,
   case AtomicExpr::AO__opencl_atomic_fetch_min:
   case AtomicExpr::AO__atomic_fetch_min:
   case AtomicExpr::AO__scoped_atomic_fetch_min:
-    Op = E->getValueType()->isFloatingType()
-             ? llvm::AtomicRMWInst::FMin
-             : (E->getValueType()->isSignedIntegerType()
-                    ? llvm::AtomicRMWInst::Min
-                    : llvm::AtomicRMWInst::UMin);
+    Op = ElemTy->isFloatingType() ? llvm::AtomicRMWInst::FMin
+                                  : (ElemTy->isSignedIntegerType()
+                                         ? llvm::AtomicRMWInst::Min
+                                         : llvm::AtomicRMWInst::UMin);
     break;
 
   case AtomicExpr::AO__atomic_fetch_fminimum:
   case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
-    assert(E->getValueType()->isFloatingType() &&
+    assert(ElemTy->isFloatingType() &&
            "fminimum operations only support floating-point types");
     Op = llvm::AtomicRMWInst::FMinimum;
     break;
 
   case AtomicExpr::AO__atomic_fetch_fminimum_num:
   case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
-    assert(E->getValueType()->isFloatingType() &&
+    assert(ElemTy->isFloatingType() &&
            "fminimum_num operations only support floating-point types");
     Op = llvm::AtomicRMWInst::FMinimumNum;
     break;
@@ -731,23 +738,22 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr 
*E, Address Dest,
   case AtomicExpr::AO__opencl_atomic_fetch_max:
   case AtomicExpr::AO__atomic_fetch_max:
   case AtomicExpr::AO__scoped_atomic_fetch_max:
-    Op = E->getValueType()->isFloatingType()
-             ? llvm::AtomicRMWInst::FMax
-             : (E->getValueType()->isSignedIntegerType()
-                    ? llvm::AtomicRMWInst::Max
-                    : llvm::AtomicRMWInst::UMax);
+    Op = ElemTy->isFloatingType() ? llvm::AtomicRMWInst::FMax
+                                  : (ElemTy->isSignedIntegerType()
+                                         ? llvm::AtomicRMWInst::Max
+                                         : llvm::AtomicRMWInst::UMax);
     break;
 
   case AtomicExpr::AO__atomic_fetch_fmaximum:
   case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
-    assert(E->getValueType()->isFloatingType() &&
+    assert(ElemTy->isFloatingType() &&
            "fmaximum operations only support floating-point types");
     Op = llvm::AtomicRMWInst::FMaximum;
     break;
 
   case AtomicExpr::AO__atomic_fetch_fmaximum_num:
   case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
-    assert(E->getValueType()->isFloatingType() &&
+    assert(ElemTy->isFloatingType() &&
            "fmaximum_num operations only support floating-point types");
     Op = llvm::AtomicRMWInst::FMaximumNum;
     break;
@@ -840,8 +846,8 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr 
*E, Address Dest,
   llvm::Value *Result = RMWI;
   if (PostOpMinMax)
     Result = EmitPostAtomicMinMax(CGF.Builder, E->getOp(),
-                                  E->getValueType()->isSignedIntegerType(),
-                                  RMWI, LoadVal1);
+                                  ElemTy->isSignedIntegerType(), RMWI,
+                                  LoadVal1);
   else if (PostOp)
     Result = CGF.Builder.CreateBinOp((llvm::Instruction::BinaryOps)PostOp, 
RMWI,
                                      LoadVal1);
@@ -868,6 +874,13 @@ EmitValToTemp(CodeGenFunction &CGF, Expr *E) {
 /// floating point operands.  TODO: Allow compare-and-exchange and FP - see
 /// comment in AtomicExpandPass.cpp.
 static bool shouldCastToInt(llvm::Type *ValTy, bool CmpXchg) {
+  // The atomic instructions operate on a vector directly, which is required 
for
+  // the elementwise arithmetic operations. cmpxchg has no vector form, and a
+  // vector whose size is not a power of two is not a valid atomic operand, so
+  // both are still handled as an integer of the enclosing atomic type's size.
+  if (auto *VecTy = dyn_cast<llvm::FixedVectorType>(ValTy))
+    return CmpXchg ||
+           
!llvm::isPowerOf2_64(VecTy->getPrimitiveSizeInBits().getFixedValue());
   if (ValTy->isFloatingPointTy())
     return ValTy->isX86_FP80Ty() || CmpXchg;
   return !ValTy->isIntegerTy() && !ValTy->isPointerTy();
@@ -1571,10 +1584,11 @@ RValue AtomicInfo::ConvertToValueOrAtomic(llvm::Value 
*Val,
                                           SourceLocation Loc, bool AsValue,
                                           bool CmpXchg) const {
   // Try not to in some easy cases.
-  assert((Val->getType()->isIntegerTy() || Val->getType()->isPointerTy() ||
-          Val->getType()->isIEEELikeFPTy()) &&
-         "Expected integer, pointer or floating point value when converting "
-         "result.");
+  llvm::Type *ValScalarTy = Val->getType()->getScalarType();
+  assert((ValScalarTy->isIntegerTy() || ValScalarTy->isPointerTy() ||
+          ValScalarTy->isIEEELikeFPTy()) &&
+         "Expected integer, pointer or floating point value, or a vector "
+         "thereof, when converting result.");
   if (getEvaluationKind() == TEK_Scalar &&
       (((!LVal.isBitField() ||
          LVal.getBitFieldInfo().Size == ValueSizeInBits) &&
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0db040ed90e3f..bd9252f409f3d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5335,6 +5335,15 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, 
SourceRange ExprRange,
     // trivial type errors.
     auto IsAllowedValueType = [&](QualType ValType,
                                   unsigned AllowedType) -> bool {
+      // Atomic operations on a vector are performed elementwise, so it is the
+      // element type which decides whether the operation is well-formed. A
+      // vector of _Bool is rejected outright, as its elements are not
+      // individually addressable.
+      if (const auto *VecTy = ValType->getAs<VectorType>()) {
+        if (VecTy->getElementType()->isBooleanType())
+          return false;
+        ValType = VecTy->getElementType();
+      }
       bool IsX87LongDouble =
           ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
           &Context.getTargetInfo().getLongDoubleFormat() ==
@@ -5367,6 +5376,26 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, 
SourceRange ExprRange,
           << IsC11 << Ptr->getType() << Ptr->getSourceRange();
       return ExprError();
     }
+    // An arithmetic operation on a vector is always emitted as a single
+    // atomicrmw instruction; unlike the other forms there is no libcall to 
fall
+    // back on for a size which cannot be handled inline. The size of the 
vector
+    // itself is checked here, as the type holding it is padded out when the
+    // element count is not a power of two.
+    if (Form == Arithmetic) {
+      if (const auto *VecTy = ValType->getAs<VectorType>()) {
+        // The largest size EmitAtomicExpr() emits without a libcall.
+        constexpr unsigned MaxVectorSizeInBytes = 16;
+        uint64_t VecSizeInBits = Context.getTypeSize(VecTy->getElementType()) *
+                                 VecTy->getNumElements();
+        if (!llvm::isPowerOf2_64(VecSizeInBits) ||
+            VecSizeInBits > MaxVectorSizeInBytes * Context.getCharWidth()) {
+          Diag(ExprRange.getBegin(), 
diag::err_atomic_op_needs_supported_vector)
+              << MaxVectorSizeInBytes << Ptr->getType()
+              << Ptr->getSourceRange();
+          return ExprError();
+        }
+      }
+    }
     if (IsC11 && ValType->isPointerType() &&
         RequireCompleteType(Ptr->getBeginLoc(), ValType->getPointeeType(),
                             diag::err_incomplete_type)) {
@@ -5635,7 +5664,10 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, 
SourceRange ExprRange,
                 ? 0
                 : 1);
 
-  if (ValType->isBitIntType()) {
+  QualType BitIntCandidateTy = ValType;
+  if (const auto *VecTy = ValType->getAs<VectorType>())
+    BitIntCandidateTy = VecTy->getElementType();
+  if (BitIntCandidateTy->isBitIntType()) {
     Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit);
     return ExprError();
   }
diff --git a/clang/test/CodeGen/atomic-ops-vector.c 
b/clang/test/CodeGen/atomic-ops-vector.c
new file mode 100644
index 0000000000000..a6bb8d13fef39
--- /dev/null
+++ b/clang/test/CodeGen/atomic-ops-vector.c
@@ -0,0 +1,95 @@
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-unknown-linux-gnu \
+// RUN:   -target-feature +cx16 -Wno-atomic-alignment \
+// RUN:   | FileCheck --check-prefixes=CHECK,X64 %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=amdgcn-amd-amdhsa \
+// RUN:   -Wno-atomic-alignment | FileCheck --check-prefixes=CHECK,AMDGCN %s
+
+// Atomic operations on a vector are performed elementwise by a single atomic
+// instruction, rather than being bitcast to an integer of the same size.
+
+typedef _Float16 half2 __attribute__((ext_vector_type(2)));
+typedef __bf16 bfloat2 __attribute__((ext_vector_type(2)));
+typedef float float2 __attribute__((ext_vector_type(2)));
+typedef float float3 __attribute__((ext_vector_type(3)));
+typedef int int2 __attribute__((ext_vector_type(2)));
+
+// CHECK-LABEL: @test_load(
+half2 test_load(half2 *p) {
+  // CHECK: load atomic <2 x half>, ptr {{.*}} monotonic
+  return __atomic_load_n(p, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_store(
+void test_store(half2 *p, half2 v) {
+  // CHECK: store atomic <2 x half> {{.*}}, ptr {{.*}} monotonic
+  __atomic_store_n(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_exchange(
+half2 test_exchange(half2 *p, half2 v) {
+  // CHECK: atomicrmw xchg ptr {{.*}}, <2 x half> {{.*}} monotonic
+  return __atomic_exchange_n(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_fetch_add_half2(
+half2 test_fetch_add_half2(half2 *p, half2 v) {
+  // CHECK: atomicrmw fadd ptr {{.*}}, <2 x half> {{.*}} monotonic
+  return __atomic_fetch_add(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_fetch_add_bfloat2(
+bfloat2 test_fetch_add_bfloat2(bfloat2 *p, bfloat2 v) {
+  // CHECK: atomicrmw fadd ptr {{.*}}, <2 x bfloat> {{.*}} monotonic
+  return __atomic_fetch_add(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_add_fetch_float2(
+float2 test_add_fetch_float2(float2 *p, float2 v) {
+  // CHECK: atomicrmw fadd ptr {{.*}}, <2 x float> {{.*}} monotonic
+  // CHECK: fadd <2 x float>
+  return __atomic_add_fetch(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_fetch_fmaximum_float2(
+float2 test_fetch_fmaximum_float2(float2 *p, float2 v) {
+  // CHECK: atomicrmw fmaximum ptr {{.*}}, <2 x float> {{.*}} monotonic
+  return __atomic_fetch_fmaximum(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_max_fetch_int2(
+int2 test_max_fetch_int2(int2 *p, int2 v) {
+  // CHECK: atomicrmw max ptr {{.*}}, <2 x i32> {{.*}} monotonic
+  // CHECK: icmp sgt <2 x i32>
+  // CHECK: select <2 x i1>
+  return __atomic_max_fetch(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_nand_fetch_int2(
+int2 test_nand_fetch_int2(int2 *p, int2 v) {
+  // CHECK: atomicrmw nand ptr {{.*}}, <2 x i32> {{.*}} monotonic
+  // CHECK: and <2 x i32>
+  // CHECK: xor <2 x i32>
+  return __atomic_nand_fetch(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_c11_fetch_add(
+half2 test_c11_fetch_add(_Atomic(half2) *p, half2 v) {
+  // CHECK: atomicrmw fadd ptr {{.*}}, <2 x half> {{.*}} monotonic
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_RELAXED);
+}
+
+// CHECK-LABEL: @test_scoped_fetch_add(
+half2 test_scoped_fetch_add(half2 *p, half2 v) {
+  // X64: atomicrmw fadd ptr {{.*}}, <2 x half> {{.*}} monotonic
+  // AMDGCN: atomicrmw fadd ptr {{.*}}, <2 x half> {{.*}} syncscope("agent") 
monotonic
+  return __scoped_atomic_fetch_add(p, v, __ATOMIC_RELAXED,
+                                   __MEMORY_SCOPE_DEVICE);
+}
+
+// A vector whose size is not a power of two is not a valid atomic operand, so
+// it is still accessed as an integer of the size of the type holding it.
+// CHECK-LABEL: @test_load_float3(
+float3 test_load_float3(float3 *p) {
+  // CHECK: load atomic i128, ptr {{.*}} monotonic
+  return __atomic_load_n(p, __ATOMIC_RELAXED);
+}
diff --git a/clang/test/Sema/atomic-ops-vector.c 
b/clang/test/Sema/atomic-ops-vector.c
new file mode 100644
index 0000000000000..752bb4a5bb933
--- /dev/null
+++ b/clang/test/Sema/atomic-ops-vector.c
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only -triple=x86_64-unknown-linux-gnu 
-std=c11
+// RUN: %clang_cc1 %s -verify -fsyntax-only -triple=amdgcn-amd-amdhsa -std=c11
+
+// Atomic builtins accept vector types; the operation is performed elementwise
+// by a single atomicrmw instruction.
+
+typedef _Float16 half2 __attribute__((ext_vector_type(2)));
+typedef __bf16 bfloat2 __attribute__((ext_vector_type(2)));
+typedef float float2 __attribute__((ext_vector_type(2)));
+typedef float float3 __attribute__((ext_vector_type(3)));
+typedef float float8 __attribute__((ext_vector_type(8)));
+typedef int int2 __attribute__((ext_vector_type(2)));
+typedef unsigned int uint4 __attribute__((ext_vector_type(4)));
+typedef _Bool bool8 __attribute__((ext_vector_type(8)));
+typedef _BitInt(16) bitint2 __attribute__((ext_vector_type(2)));
+
+void test_gnu(half2 *h2, bfloat2 *b2, float2 *f2, int2 *i2, uint4 *u4) {
+  (void)__atomic_load_n(h2, __ATOMIC_RELAXED);
+  __atomic_store_n(h2, *h2, __ATOMIC_RELAXED);
+  (void)__atomic_exchange_n(h2, *h2, __ATOMIC_RELAXED);
+
+  (void)__atomic_fetch_add(h2, *h2, __ATOMIC_RELAXED);
+  (void)__atomic_fetch_add(b2, *b2, __ATOMIC_RELAXED);
+  (void)__atomic_fetch_sub(f2, *f2, __ATOMIC_RELAXED);
+  (void)__atomic_add_fetch(f2, *f2, __ATOMIC_RELAXED);
+  (void)__atomic_fetch_min(f2, *f2, __ATOMIC_RELAXED);
+  (void)__atomic_max_fetch(i2, *i2, __ATOMIC_RELAXED);
+  (void)__atomic_fetch_fmaximum(f2, *f2, __ATOMIC_RELAXED);
+  (void)__atomic_fetch_and(i2, *i2, __ATOMIC_RELAXED);
+  (void)__atomic_or_fetch(u4, *u4, __ATOMIC_RELAXED);
+  (void)__atomic_nand_fetch(i2, *i2, __ATOMIC_RELAXED);
+
+  // The integer operations still reject a vector of floating point elements.
+  (void)__atomic_fetch_and(f2, *f2, __ATOMIC_RELAXED); // expected-error 
{{must be a pointer to integer}}
+  // The f-prefixed operations still reject a vector of integer elements.
+  (void)__atomic_fetch_fminimum(i2, *i2, __ATOMIC_RELAXED); // expected-error 
{{must be a pointer to floating point ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/214653
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to