llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Hana Dusíková (hanickadot) <details> <summary>Changes</summary> This implements clang support for 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. --- Patch is 63.00 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/98756.diff 4 Files Affected: - (modified) clang/include/clang/Basic/Builtins.td (+42-42) - (modified) clang/lib/AST/ExprConstant.cpp (+547-6) - (added) clang/test/SemaCXX/atomic-constexpr-c11-builtins.cpp (+288) - (added) clang/test/SemaCXX/atomic-constexpr-gcc-builtins.cpp (+494) ``````````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..c472b4b998aa3 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1900,6 +1900,17 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, // Misc utilities //===----------------------------------------------------------------------===// +static bool isOnePastTheEndOfCompleteObject(const ASTContext &Ctx, + const LValue &LV); + +enum class SizeOfType { + SizeOf, + DataSizeOf, +}; + +static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, QualType Type, + CharUnits &Size, SizeOfType SOT = SizeOfType::SizeOf); + /// 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) { @@ -3222,14 +3233,9 @@ static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E, return true; } -enum class SizeOfType { - SizeOf, - DataSizeOf, -}; - /// Get the size of the given type in char units. static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, QualType Type, - CharUnits &Size, SizeOfType SOT = SizeOfType::SizeOf) { + CharUnits &Size, SizeOfType SOT) { // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc // extension. if (Type->isVoidType() || Type->isFunctionType()) { @@ -7884,6 +7890,522 @@ class ExprEvaluatorBase return StmtVisitorTy::Visit(Source); } + static bool EvaluateAtomicOrderToIgnore(const AtomicExpr *E, EvalInfo &Info) { + // we ignore results, but we need to evaluate them + [[maybe_unused]] APSInt OrderIgnoredResult; + + const Expr *OrderSuccess = E->getOrder(); + if (!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 EvaluateAtomicWeakToIgnore(const AtomicExpr *E, EvalInfo &Info) { + // we ignore results, but we need to evaluate them + [[maybe_unused]] APSInt WeakIgnoredResult; + + if (E->getOp() == AtomicExpr::AO__atomic_compare_exchange_n || + E->getOp() == AtomicExpr::AO__atomic_compare_exchange) { + const Expr *Weak = E->getWeak(); + if (!EvaluateInteger(Weak, WeakIgnoredResult, Info)) + return false; + } + + return true; + } + + static bool LoadAtomicValue(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 StoreValueIntoResultPointer(Expr *ResultPtr, + APValue &ValueToStore, + EvalInfo &Info) { + // TODO check it must be a pointer + assert(ResultPtr->getType()->isPointerType()); + QualType PointeeTy = ResultPtr->getType()->getPointeeType(); + LValue PointeeLV; + + if (!EvaluatePointer(ResultPtr, PointeeLV, Info)) + return false; + + return handleAssignment(Info, ResultPtr, PointeeLV, PointeeTy, + ValueToStore); + } + + static bool LoadAtomicValueInto(const AtomicExpr *E, EvalInfo &Info) { + APValue LocalResult; + + if (!LoadAtomicValue(E, LocalResult, Info)) + return false; + + if (!StoreValueIntoResultPointer(E->getVal1(), LocalResult, Info)) + return false; + + return true; + } + + static bool StoreAtomicValue(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 ExchangeAtomicValueInto(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 provided value from argument (pointer) + 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 + if (!StoreValueIntoResultPointer(E->getVal2(), PreviousValue, Info)) + return false; + + return true; + } + + static bool FetchAtomicOp(const AtomicExpr *E, APValue &Result, + EvalInfo &Info, bool StoreToResultAfter) { + // read 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()) { + // both arguments are integers + 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: + 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 (AtomicValueTy->isRealFloatingType()) { + // both arguments are float operations + 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: // 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 (AtomicValueTy->isPointerType()) { + // pointer + int arguments + 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 compute new pointer by bytes and not + // sizeof(T) + switch (E->getOp()) { + case AtomicExpr::AO__atomic_fetch_add: + case AtomicExpr::AO__atomic_add_fetch: + case AtomicExpr::AO__atomic_fetch_sub: + case AtomicExpr::AO__atomic_sub_fetch: { + const auto sizeOfOneItem = + APSInt(APInt(ArgumentInt.getBitWidth(), SizeOfPointee.getQuantity(), + false), + false); + // incrementing pointer by size which is not dividable by pointee size + // is UB and therefore disallowed + if ((ArgumentInt % sizeOfOneItem) != 0) + return false; + + ArgumentInt /= sizeOfOneItem; + } break; + ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/98756 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits