[libcxx-commits] [clang] [libcxx] [clang] [libc++] P3309 constexpr atomic and atomic ref [WIP] (PR #98738)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 13 04:04:10 PDT 2024


Hana =?utf-8?q?Dusíková?= <hanicka at hanicka.net>,
Hana =?utf-8?q?Dusíková?= <hanicka at hanicka.net>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/98738 at github.com>


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hana Dusíková (hanickadot)

<details>
<summary>Changes</summary>

This implements 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. 

Implementation in library is then simple, just marking everything constexpr, in `atomic_ref` is used `if consteval` code forking to avoid `reinterpret_cast` which is there to avoid missing capability of GCC's atomic builtins which doesn't support operations on floats.

---

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


7 Files Affected:

- (modified) clang/include/clang/Basic/Builtins.td (+42-42) 
- (modified) clang/lib/AST/ExprConstant.cpp (+412) 
- (modified) libcxx/include/__atomic/atomic.h (+49-40) 
- (modified) libcxx/include/__atomic/atomic_base.h (+46-30) 
- (modified) libcxx/include/__atomic/atomic_flag.h (+35-21) 
- (modified) libcxx/include/__atomic/atomic_ref.h (+260-116) 
- (modified) libcxx/include/__atomic/cxx_atomic_impl.h (+43-36) 


``````````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..2cb7de0ed747a 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1900,6 +1900,9 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
 // Misc utilities
 //===----------------------------------------------------------------------===//
 
+static bool isOnePastTheEndOfCompleteObject(const ASTContext &Ctx,
+                                            const LValue &LV);
+
 /// 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) {
@@ -7884,6 +7887,404 @@ class ExprEvaluatorBase
     return StmtVisitorTy::Visit(Source);
   }
 
+  static bool EvaluateOrder(const Expr *E, EvalInfo &Info) {
+    // we ignore order
+    [[maybe_unused]] APSInt Order;
+    if (!EvaluateInteger(E, Order, Info)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool ReadAtomicPtr(const AtomicExpr *E, APValue &Result,
+                            EvalInfo &Info) {
+    LValue AtomicLV;
+    if (!EvaluatePointer(E->getPtr(), AtomicLV, Info)) {
+      return false;
+    }
+
+    if (!handleLValueToRValueConversion(Info, E->getPtr(), E->getType(),
+                                        AtomicLV, Result)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool LoadAtomicValue(const AtomicExpr *E, APValue &Result,
+                              EvalInfo &Info) {
+    if (!ReadAtomicPtr(E, Result, Info)) {
+      return false;
+    }
+
+    // we ignore order
+    if (!EvaluateOrder(E->getOrder(), Info)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool FetchAtomicOp(const AtomicExpr *E, APValue &Result,
+                            EvalInfo &Info, bool StoreToResultAfter) {
+    LValue AtomicLV;
+    QualType AtomicTy =
+        E->getPtr()->getType()->getPointeeType().getAtomicUnqualifiedType();
+    if (!EvaluatePointer(E->getPtr(), AtomicLV, Info)) {
+      return false;
+    }
+
+    APValue AtomicVal;
+    if (!handleLValueToRValueConversion(Info, E->getPtr(), E->getType(),
+                                        AtomicLV, AtomicVal)) {
+      return false;
+    }
+    
+    if (!StoreToResultAfter) {
+      Result = AtomicVal;
+    }
+
+    const auto ResultType = E->getType();
+
+    APValue ArgumentVal;
+    if (!Evaluate(ArgumentVal, Info, E->getVal1())) {
+      return false;
+    }
+
+    APValue Replacement;
+    if (ResultType->isIntegralOrEnumerationType()) {
+      const APSInt AtomicInt = AtomicVal.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 (ResultType->isRealFloatingType()) {
+      const llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
+      APFloat AtomicFlt = AtomicVal.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 (ResultType->isPointerType()) {
+      LValue AtomicPtr;
+      AtomicPtr.setFrom(Info.Ctx, AtomicVal);
+
+      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:
+        if (!HandleLValueArrayAdjustment(
+                Info, E, AtomicPtr, AtomicTy->getPointeeType(), ArgumentInt)) {
+          return false;
+        }
+        break;
+      case AtomicExpr::AO__c11_atomic_fetch_sub:
+      case AtomicExpr::AO__atomic_fetch_sub:
+      case AtomicExpr::AO__atomic_sub_fetch:
+        ArgumentInt.negate();
+        if (!HandleLValueArrayAdjustment(
+                Info, E, AtomicPtr, AtomicTy->getPointeeType(), ArgumentInt)) {
+          return false;
+        }
+        break;
+      default:
+        return false;
+      }
+
+      AtomicPtr.moveInto(Replacement);
+    } else {
+      // not float,int,pointer?
+      return false;
+    }
+    
+    if (StoreToResultAfter) {
+      Result = Replacement;
+    }
+
+    if (!handleAssignment(Info, E, AtomicLV, AtomicTy, Replacement)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool StoreAtomicValue(const AtomicExpr *E, EvalInfo &Info) {
+    LValue LV;
+    if (!EvaluatePointer(E->getPtr(), LV, Info)) {
+      return false;
+    }
+
+    APValue NewVal;
+    if (!Evaluate(NewVal, Info, E->getVal1())) {
+      return false;
+    }
+
+    if (!handleAssignment(Info, E, LV, E->getVal1()->getType(), NewVal)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool CompareExchangeAtomicValue(const AtomicExpr *E, APValue &Result,
+                                         EvalInfo &Info) {
+    // dereference _Atomic * (atomic value)
+    LValue AtomicLV;
+    QualType AtomicTy =
+        E->getPtr()->getType()->getPointeeType().getAtomicUnqualifiedType();
+    if (!EvaluatePointer(E->getPtr(), AtomicLV, Info)) {
+      return false;
+    }
+
+    // dereference T * (expected value)
+    LValue ExpectedLV;
+    QualType ExpectedTy = E->getVal1()->getType()->getPointeeType();
+    if (!EvaluatePointer(E->getVal1(), ExpectedLV, Info)) {
+      return false;
+    }
+
+    // get values for atomic and expected
+    APValue AtomicVal;
+    APValue ExpectedVal;
+
+    // convert pointer to value
+    if (!handleLValueToRValueConversion(Info, E->getPtr(), AtomicTy, AtomicLV,
+                                        AtomicVal)) {
+      return false;
+    }
+
+    if (!handleLValueToRValueConversion(Info, E->getVal1(), ExpectedTy,
+                                        ExpectedLV, ExpectedVal)) {
+      return false;
+    }
+
+    bool DoExchange = false;
+
+    // compare atomic<int> and friends
+    if (AtomicTy->isIntegralOrEnumerationType() &&
+        ExpectedTy->isIntegralOrEnumerationType()) {
+      const APSInt AtomicInt = AtomicVal.getInt();
+      const APSInt ExpectedInt = ExpectedVal.getInt();
+      if (AtomicInt == ExpectedInt) {
+        DoExchange = true;
+      }
+    } else if (AtomicTy->isRealFloatingType() &&
+               ExpectedTy->isRealFloatingType()) {
+      const APFloat AtomicFlt = AtomicVal.getFloat();
+      const APFloat ExpectedFlt = ExpectedVal.getFloat();
+      if (AtomicFlt == ExpectedFlt) {
+        DoExchange = true;
+      }
+    } else if (AtomicTy->isPointerType() && ExpectedTy->isPointerType()) {
+      // get LValue of objects pointed to
+      LValue LHS;
+      LHS.setFrom(Info.Ctx, AtomicVal);
+
+      LValue RHS;
+      RHS.setFrom(Info.Ctx, ExpectedVal);
+
+      if (HasSameBase(LHS, RHS)) {
+        const CharUnits &LHSOffset = LHS.getLValueOffset();
+        const CharUnits &RHSOffset = RHS.getLValueOffset();
+
+        const unsigned PtrSize = Info.Ctx.getTypeSize(AtomicTy);
+        assert(PtrSize <= 64 && "Pointer width is larger than expected");
+        const uint64_t Mask = ~0ULL >> (64 - PtrSize);
+
+        const uint64_t CompareLHS = LHSOffset.getQuantity() & Mask;
+        const uint64_t CompareRHS = RHSOffset.getQuantity() & Mask;
+
+        if (CompareLHS == CompareRHS) {
+          DoExchange = true;
+        }
+      } else {
+
+        // it's implementation-defined to compare distinct literals
+        // it's not constant-evaluation
+        if ((IsLiteralLValue(LHS) || IsLiteralLValue(RHS)) && LHS.Base &&
+            RHS.Base) {
+          return false;
+        }
+
+        if (IsWeakLValue(LHS) || IsWeakLValue(RHS)) {
+          return false;
+        }
+
+        if ((!LHS.Base && !LHS.Offset.isZero()) ||
+            (!RHS.Base && !RHS.Offset.isZero())) {
+          return false;
+        }
+
+        if (LHS.Base && LHS.Offset.isZero() &&
+            isOnePastTheEndOfCompleteObject(Info.Ctx, RHS)) {
+          return false;
+        }
+
+        if (RHS.Base && RHS.Offset.isZero() &&
+            isOnePastTheEndOfCompleteObject(Info.Ctx, LHS)) {
+          return false;
+        }
+
+        if ((RHS.Base && isZeroSized(RHS)) || (LHS.Base && isZeroSized(LHS))) {
+          return false;
+        }
+
+        // after all it's a different object
+        DoExchange = false;
+      }
+
+    } else {
+      return false;
+    }
+
+    if (DoExchange) {
+      // if values are same do the exchange with replacement value
+      // but first I must evaluate the replacement value
+      APValue Replacement;
+      if (!Evaluate(Replacement, Info, E->getVal2())) {
+        return false;
+      }
+
+      // and assign it to atomic
+      if (!handleAssignment(Info, E, AtomicLV, AtomicTy, Replacement)) {
+        return false;
+      }
+    }
+
+    // to expected p...
[truncated]

``````````

</details>


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


More information about the libcxx-commits mailing list