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

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 8 06:47:45 PST 2025


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/98756 at github.com>


================
@@ -17893,4 +18005,425 @@ std::optional<bool> EvaluateBuiltinIsWithinLifetime(IntExprEvaluator &IEE,
   IsWithinLifetimeHandler handler{Info};
   return findSubobject(Info, E, CO, Val.getLValueDesignator(), handler);
 }
+
 } // namespace
+
+static bool EvaluateAtomicOrder(const AtomicExpr *E, EvalInfo &Info) {
+  // We need to evaluate Order argument(s), but we ignore it as constant
+  // evaluation is single threaded.
+  APSInt OrderIgnoredResult;
+
+  if (E->getOp() != AtomicExpr::AO__c11_atomic_init) {
+    const Expr *OrderSuccess = E->getOrder();
+    if (OrderSuccess &&
+        !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 EvaluateAtomicLoad(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 EvaluateAtomicLoadInto(const AtomicExpr *E, EvalInfo &Info) {
+  APValue LocalResult;
+
+  if (!EvaluateAtomicLoad(E, LocalResult, Info))
+    return false;
+
+  assert(E->getVal1()->getType()->isPointerType());
+  QualType PointeeTy = E->getVal1()->getType()->getPointeeType();
+  LValue PointeeLV;
+
+  if (!EvaluatePointer(E->getVal1(), PointeeLV, Info))
+    return false;
+
+  if (!handleAssignment(Info, E->getVal1(), PointeeLV, PointeeTy, LocalResult))
+    return false;
+
+  return true;
+}
+
+static bool EvaluateAtomicStore(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 EvaluateAtomicExchange(const AtomicExpr *E, APValue &Result,
+                                   EvalInfo &Info) {
+  assert(E->getOp() == AtomicExpr::AO__c11_atomic_exchange ||
+         E->getOp() == AtomicExpr::AO__atomic_exchange_n);
+
+  if (!EvaluateAtomicLoad(E, Result, Info))
+    return false;
+
+  if (!EvaluateAtomicStore(E, Info))
+    return false;
+
+  return true;
+}
+
+static bool EvaluateAtomicExchangeInto(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 value pointer by argument of the exchange operation.
+  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.
+  assert(E->getVal2()->getType()->isPointerType());
+  QualType PointeeTy = E->getVal2()->getType()->getPointeeType();
+  LValue PointeeLV;
+
+  if (!EvaluatePointer(E->getVal2(), PointeeLV, Info))
+    return false;
+
+  if (!handleAssignment(Info, E->getVal2(), PointeeLV, PointeeTy,
+                        PreviousValue))
+    return false;
+
+  return true;
+}
+
+static bool EvaluateAtomicFetchOp(const AtomicExpr *E, APValue &Result,
+                                  EvalInfo &Info, bool StoreToResultAfter) {
+  // Read the 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()) {
+    assert(CurrentValue.isInt());
+    assert(ArgumentVal.isInt());
+
+    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:
+      // Atomic operations are defined for overflow
+      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(std::max(AtomicInt, ArgumentInt));
+      break;
+    case AtomicExpr::AO__c11_atomic_fetch_min:
+    case AtomicExpr::AO__atomic_fetch_min:
+    case AtomicExpr::AO__atomic_min_fetch:
+      Replacement = APValue(std::min(AtomicInt, ArgumentInt));
+      break;
+    default:
+      return false;
+    }
+  } else if (AtomicValueTy->isRealFloatingType()) {
+    assert(CurrentValue.isFloat());
+    assert(ArgumentVal.isFloat());
+
+    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:
+      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:
+      // GCC's atomic fetch-op doesn't support float operands.
+      return false;
+    }
+
+    if (!checkFloatingPointResult(Info, E, St))
+      return false;
+
+  } else if (AtomicValueTy->isPointerType()) {
+    assert(CurrentValue.isLValue());
+    assert(ArgumentVal.isInt());
+
+    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 operations takes arguments in bytes and
+    // not in multiplies of sizeof(T).
----------------
AaronBallman wrote:

```suggestion
    // not in multiples of sizeof(T).
```

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


More information about the cfe-commits mailing list