Hana =?utf-8?q?Dusíková?= <hani...@hanicka.net>, Hana =?utf-8?q?Dusíková?= <hani...@hanicka.net> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/98...@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits