[clang] [clang] constexpr atomic builtins (__c11_atomic_OP and __atomic_OP) (PR #98756)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jul 13 11:12:59 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Hana Dusíková (hanickadot)

<details>
<summary>Changes</summary>

This implements clang support for P3309 constexpr std::atomic & std::atomic_ref (currently in LWG) by allowing constant evaluation of clang's __c11_atomic_OP and GCC's __atomic_OP builtins.

---

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


4 Files Affected:

- (modified) clang/include/clang/Basic/Builtins.td (+42-42) 
- (modified) clang/lib/AST/ExprConstant.cpp (+547-6) 
- (added) clang/test/SemaCXX/atomic-constexpr-c11-builtins.cpp (+288) 
- (added) clang/test/SemaCXX/atomic-constexpr-gcc-builtins.cpp (+494) 


``````````diff
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index f5b15cf90d1f8..0716cf02f5110 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1682,97 +1682,97 @@ def SyncSwapN : Builtin, SyncBuiltinsTemplate {
 // C11 _Atomic operations for <stdatomic.h>.
 def C11AtomicInit : AtomicBuiltin {
   let Spellings = ["__c11_atomic_init"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicLoad : AtomicBuiltin {
   let Spellings = ["__c11_atomic_load"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicStore : AtomicBuiltin {
   let Spellings = ["__c11_atomic_store"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicExchange : AtomicBuiltin {
   let Spellings = ["__c11_atomic_exchange"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicCompareExchangeStrong : AtomicBuiltin {
   let Spellings = ["__c11_atomic_compare_exchange_strong"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicCompareExchangeWeak : AtomicBuiltin {
   let Spellings = ["__c11_atomic_compare_exchange_weak"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchAdd : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_add"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchSub : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_sub"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchAnd : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_and"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchOr : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_or"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchXor : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_xor"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchNand : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_nand"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchMax : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_max"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicFetchMin : AtomicBuiltin {
   let Spellings = ["__c11_atomic_fetch_min"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def C11AtomicThreadFence : Builtin {
   let Spellings = ["__c11_atomic_thread_fence"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   let Prototype = "void(int)";
 }
 
 def C11AtomicSignalFence : Builtin {
   let Spellings = ["__c11_atomic_signal_fence"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   let Prototype = "void(int)";
 }
 
@@ -1785,157 +1785,157 @@ def C11AtomicIsLockFree : Builtin {
 // GNU atomic builtins.
 def AtomicLoad : AtomicBuiltin {
   let Spellings = ["__atomic_load"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicLoadN : AtomicBuiltin {
   let Spellings = ["__atomic_load_n"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicStore : AtomicBuiltin {
   let Spellings = ["__atomic_store"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicStoreN : AtomicBuiltin {
   let Spellings = ["__atomic_store_n"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicExchange : AtomicBuiltin {
   let Spellings = ["__atomic_exchange"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicExchangeN : AtomicBuiltin {
   let Spellings = ["__atomic_exchange_n"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicCompareExchange : AtomicBuiltin {
   let Spellings = ["__atomic_compare_exchange"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicCompareExchangeN : AtomicBuiltin {
   let Spellings = ["__atomic_compare_exchange_n"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicFetchAdd : AtomicBuiltin {
   let Spellings = ["__atomic_fetch_add"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicFetchSub : AtomicBuiltin {
   let Spellings = ["__atomic_fetch_sub"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicFetchAnd : AtomicBuiltin {
   let Spellings = ["__atomic_fetch_and"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicFetchOr : AtomicBuiltin {
   let Spellings = ["__atomic_fetch_or"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicFetchXor : AtomicBuiltin {
   let Spellings = ["__atomic_fetch_xor"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicFetchNand : AtomicBuiltin {
   let Spellings = ["__atomic_fetch_nand"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicAddFetch : AtomicBuiltin {
   let Spellings = ["__atomic_add_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicSubFetch : AtomicBuiltin {
   let Spellings = ["__atomic_sub_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicAndFetch : AtomicBuiltin {
   let Spellings = ["__atomic_and_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicOrFetch : AtomicBuiltin {
   let Spellings = ["__atomic_or_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicXorFetch : AtomicBuiltin {
   let Spellings = ["__atomic_xor_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicMaxFetch : AtomicBuiltin {
   let Spellings = ["__atomic_max_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicMinFetch : AtomicBuiltin {
   let Spellings = ["__atomic_min_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicNandFetch : AtomicBuiltin {
   let Spellings = ["__atomic_nand_fetch"];
-  let Attributes = [CustomTypeChecking];
+  let Attributes = [CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def AtomicTestAndSet : Builtin {
   let Spellings = ["__atomic_test_and_set"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   let Prototype = "bool(void volatile*, int)";
 }
 
 def AtomicClear : Builtin {
   let Spellings = ["__atomic_clear"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   let Prototype = "void(void volatile*, int)";
 }
 
 def AtomicThreadFence : Builtin {
   let Spellings = ["__atomic_thread_fence"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   let Prototype = "void(int)";
 }
 
 def AtomicSignalFence : Builtin {
   let Spellings = ["__atomic_signal_fence"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   let Prototype = "void(int)";
 }
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 0aeac9d03eed3..c472b4b998aa3 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1900,6 +1900,17 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
 // Misc utilities
 //===----------------------------------------------------------------------===//
 
+static bool isOnePastTheEndOfCompleteObject(const ASTContext &Ctx,
+                                            const LValue &LV);
+
+enum class SizeOfType {
+  SizeOf,
+  DataSizeOf,
+};
+
+static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, QualType Type,
+                         CharUnits &Size, SizeOfType SOT = SizeOfType::SizeOf);
+
 /// Negate an APSInt in place, converting it to a signed form if necessary, and
 /// preserving its value (by extending by up to one bit as needed).
 static void negateAsSigned(APSInt &Int) {
@@ -3222,14 +3233,9 @@ static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E,
   return true;
 }
 
-enum class SizeOfType {
-  SizeOf,
-  DataSizeOf,
-};
-
 /// Get the size of the given type in char units.
 static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, QualType Type,
-                         CharUnits &Size, SizeOfType SOT = SizeOfType::SizeOf) {
+                         CharUnits &Size, SizeOfType SOT) {
   // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc
   // extension.
   if (Type->isVoidType() || Type->isFunctionType()) {
@@ -7884,6 +7890,522 @@ class ExprEvaluatorBase
     return StmtVisitorTy::Visit(Source);
   }
 
+  static bool EvaluateAtomicOrderToIgnore(const AtomicExpr *E, EvalInfo &Info) {
+    // we ignore results, but we need to evaluate them
+    [[maybe_unused]] APSInt OrderIgnoredResult;
+
+    const Expr *OrderSuccess = E->getOrder();
+    if (!EvaluateInteger(OrderSuccess, OrderIgnoredResult, Info))
+      return false;
+
+    if (E->isCmpXChg()) {
+      const Expr *OrderFail = E->getOrderFail();
+      if (!EvaluateInteger(OrderFail, OrderIgnoredResult, Info))
+        return false;
+    }
+
+    return true;
+  }
+
+  static bool EvaluateAtomicWeakToIgnore(const AtomicExpr *E, EvalInfo &Info) {
+    // we ignore results, but we need to evaluate them
+    [[maybe_unused]] APSInt WeakIgnoredResult;
+
+    if (E->getOp() == AtomicExpr::AO__atomic_compare_exchange_n ||
+        E->getOp() == AtomicExpr::AO__atomic_compare_exchange) {
+      const Expr *Weak = E->getWeak();
+      if (!EvaluateInteger(Weak, WeakIgnoredResult, Info))
+        return false;
+    }
+
+    return true;
+  }
+
+  static bool LoadAtomicValue(const AtomicExpr *E, APValue &Result,
+                              EvalInfo &Info) {
+    LValue AtomicStorageLV;
+
+    if (!EvaluatePointer(E->getPtr(), AtomicStorageLV, Info))
+      return false;
+
+    return handleLValueToRValueConversion(Info, E->getPtr(), E->getValueType(),
+                                          AtomicStorageLV, Result);
+  }
+
+  static bool StoreValueIntoResultPointer(Expr *ResultPtr,
+                                          APValue &ValueToStore,
+                                          EvalInfo &Info) {
+    // TODO check it must be a pointer
+    assert(ResultPtr->getType()->isPointerType());
+    QualType PointeeTy = ResultPtr->getType()->getPointeeType();
+    LValue PointeeLV;
+
+    if (!EvaluatePointer(ResultPtr, PointeeLV, Info))
+      return false;
+
+    return handleAssignment(Info, ResultPtr, PointeeLV, PointeeTy,
+                            ValueToStore);
+  }
+
+  static bool LoadAtomicValueInto(const AtomicExpr *E, EvalInfo &Info) {
+    APValue LocalResult;
+
+    if (!LoadAtomicValue(E, LocalResult, Info))
+      return false;
+
+    if (!StoreValueIntoResultPointer(E->getVal1(), LocalResult, Info))
+      return false;
+
+    return true;
+  }
+
+  static bool StoreAtomicValue(const AtomicExpr *E, EvalInfo &Info) {
+    LValue AtomicStorageLV;
+
+    if (!EvaluatePointer(E->getPtr(), AtomicStorageLV, Info))
+      return false;
+
+    APValue ProvidedValue;
+
+    // GCC's atomic_store takes pointer to value, not value itself
+    if (E->getOp() == AtomicExpr::AO__atomic_store) {
+      LValue ProvidedLV;
+      if (!EvaluatePointer(E->getVal1(), ProvidedLV, Info))
+        return false;
+
+      if (!handleLValueToRValueConversion(Info, E->getVal1(),
+                                          E->getVal1()->getType(), ProvidedLV,
+                                          ProvidedValue))
+        return false;
+
+    } else {
+      if (!Evaluate(ProvidedValue, Info, E->getVal1()))
+        return false;
+    }
+    if (!handleAssignment(Info, E, AtomicStorageLV, E->getValueType(),
+                          ProvidedValue))
+      return false;
+
+    return true;
+  }
+
+  static bool ExchangeAtomicValueInto(const AtomicExpr *E, EvalInfo &Info) {
+    assert(E->getOp() == AtomicExpr::AO__atomic_exchange);
+    // implementation of GCC's exchange (non _n version)
+    LValue AtomicStorageLV;
+    if (!EvaluatePointer(E->getPtr(), AtomicStorageLV, Info))
+      return false;
+
+    // read previous value
+    APValue PreviousValue;
+    if (!handleLValueToRValueConversion(Info, E->getPtr(), E->getValueType(),
+                                        AtomicStorageLV, PreviousValue))
+      return false;
+
+    // get provided value from argument (pointer)
+    LValue ProvidedLV;
+    if (!EvaluatePointer(E->getVal1(), ProvidedLV, Info))
+      return false;
+
+    APValue ProvidedValue;
+    if (!handleLValueToRValueConversion(Info, E->getVal1(),
+                                        E->getVal1()->getType(), ProvidedLV,
+                                        ProvidedValue))
+      return false;
+
+    // store provided value to atomic value
+    if (!handleAssignment(Info, E, AtomicStorageLV, E->getValueType(),
+                          ProvidedValue))
+      return false;
+
+    // store previous value in output pointer
+    if (!StoreValueIntoResultPointer(E->getVal2(), PreviousValue, Info))
+      return false;
+
+    return true;
+  }
+
+  static bool FetchAtomicOp(const AtomicExpr *E, APValue &Result,
+                            EvalInfo &Info, bool StoreToResultAfter) {
+    // read atomic
+    LValue AtomicStorageLV;
+    QualType AtomicValueTy = E->getValueType();
+    if (!EvaluatePointer(E->getPtr(), AtomicStorageLV, Info))
+      return false;
+
+    APValue CurrentValue;
+    if (!handleLValueToRValueConversion(Info, E->getPtr(), E->getType(),
+                                        AtomicStorageLV, CurrentValue))
+      return false;
+
+    // store current value for fetch-OP operations
+    if (!StoreToResultAfter)
+      Result = CurrentValue;
+
+    // read argument for fetch OP
+    APValue ArgumentVal;
+    if (!Evaluate(ArgumentVal, Info, E->getVal1()))
+      return false;
+
+    // calculate new value
+    APValue Replacement;
+    if (AtomicValueTy->isIntegralOrEnumerationType()) {
+      // both arguments are integers
+      const APSInt AtomicInt = CurrentValue.getInt();
+      const APSInt ArgumentInt = ArgumentVal.getInt();
+
+      switch (E->getOp()) {
+      case AtomicExpr::AO__c11_atomic_fetch_add:
+      case AtomicExpr::AO__atomic_fetch_add:
+      case AtomicExpr::AO__atomic_add_fetch:
+        Replacement = APValue(AtomicInt + ArgumentInt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_sub:
+      case AtomicExpr::AO__atomic_fetch_sub:
+      case AtomicExpr::AO__atomic_sub_fetch:
+        Replacement = APValue(AtomicInt - ArgumentInt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_and:
+      case AtomicExpr::AO__atomic_fetch_and:
+      case AtomicExpr::AO__atomic_and_fetch:
+        Replacement = APValue(AtomicInt & ArgumentInt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_or:
+      case AtomicExpr::AO__atomic_fetch_or:
+      case AtomicExpr::AO__atomic_or_fetch:
+        Replacement = APValue(AtomicInt | ArgumentInt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_xor:
+      case AtomicExpr::AO__atomic_fetch_xor:
+      case AtomicExpr::AO__atomic_xor_fetch:
+        Replacement = APValue(AtomicInt ^ ArgumentInt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_nand:
+      case AtomicExpr::AO__atomic_fetch_nand:
+      case AtomicExpr::AO__atomic_nand_fetch:
+        Replacement = APValue(~(AtomicInt & ArgumentInt));
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_max:
+      case AtomicExpr::AO__atomic_fetch_max:
+      case AtomicExpr::AO__atomic_max_fetch:
+        Replacement =
+            APValue((AtomicInt > ArgumentInt) ? AtomicInt : ArgumentInt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_min:
+      case AtomicExpr::AO__atomic_fetch_min:
+      case AtomicExpr::AO__atomic_min_fetch:
+        Replacement =
+            APValue((AtomicInt < ArgumentInt) ? AtomicInt : ArgumentInt);
+        break;
+      default:
+        return false;
+      }
+    } else if (AtomicValueTy->isRealFloatingType()) {
+      // both arguments are float operations
+      const llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
+      APFloat AtomicFlt = CurrentValue.getFloat();
+      const APFloat ArgumentFlt = ArgumentVal.getFloat();
+      APFloat::opStatus St;
+
+      switch (E->getOp()) {
+      case AtomicExpr::AO__c11_atomic_fetch_add: // GCC atomics doesn't support
+                                                 // floats
+        St = AtomicFlt.add(ArgumentFlt, RM);
+        Replacement = APValue(AtomicFlt);
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_sub:
+        St = AtomicFlt.subtract(ArgumentFlt, RM);
+        Replacement = APValue(AtomicFlt);
+        break;
+      default:
+        return false;
+      }
+
+      if (!checkFloatingPointResult(Info, E, St))
+        return false;
+
+    } else if (AtomicValueTy->isPointerType()) {
+      // pointer + int arguments
+      LValue AtomicPtr;
+      AtomicPtr.setFrom(Info.Ctx, CurrentValue);
+
+      APSInt ArgumentInt = ArgumentVal.getInt();
+
+      // calculate size of pointee object
+      CharUnits SizeOfPointee;
+      if (!HandleSizeof(Info, E->getExprLoc(), AtomicValueTy->getPointeeType(),
+                        SizeOfPointee))
+        return false;
+
+      // GCC's atomic_fetch add/sub compute new pointer by bytes and not
+      // sizeof(T)
+      switch (E->getOp()) {
+      case AtomicExpr::AO__atomic_fetch_add:
+      case AtomicExpr::AO__atomic_add_fetch:
+      case AtomicExpr::AO__atomic_fetch_sub:
+      case AtomicExpr::AO__atomic_sub_fetch: {
+        const auto sizeOfOneItem =
+            APSInt(APInt(ArgumentInt.getBitWidth(), SizeOfPointee.getQuantity(),
+                         false),
+                   false);
+        // incrementing pointer by size which is not dividable by pointee size
+        // is UB and therefore disallowed
+        if ((ArgumentInt % sizeOfOneItem) != 0)
+          return false;
+
+        ArgumentInt /= sizeOfOneItem;
+      } break;
+  ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/98756


More information about the cfe-commits mailing list