[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:44 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);
----------------
AaronBallman wrote:
Please be sure there's a test that overflow gives the expected results. Also, please be sure to test weirder atomic integer types like `__int128`, `_BitInt`, and if we support them, vector types.
(I've not gotten to the tests yet, so if this is already handled, please ignore.)
https://github.com/llvm/llvm-project/pull/98756
More information about the cfe-commits
mailing list