[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