diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8e24087b3dcdb..9a07ad42b8f6f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -440,6 +440,13 @@ Non-comprehensive list of changes in this release pointers, enabling more powerful alias analysis when accessing pointer types. The new behavior can be enabled using ``-fpointer-tbaa``. +- The ``__atomic_always_lock_free`` and ``__atomic_is_lock_free`` + builtins may now return true if the pointer argument is a + compile-time constant (e.g. ``(void*)4``), and constant pointer is + sufficiently-aligned for the access requested. Previously, only the + type of the pointer was taken into account. This improves + compatibility with GCC's libstdc++. + New Compiler Flags ------------------ - ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 5af712dd7257b..fcb382474ea62 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -12949,19 +12949,35 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, Info.Ctx.getTargetInfo().getMaxAtomicInlineWidth(); if (Size <= Info.Ctx.toCharUnitsFromBits(InlineWidthBits)) { if (BuiltinOp == Builtin::BI__c11_atomic_is_lock_free || - Size == CharUnits::One() || - E->getArg(1)->isNullPointerConstant(Info.Ctx, - Expr::NPC_NeverValueDependent)) - // OK, we will inline appropriately-aligned operations of this size, - // and _Atomic(T) is appropriately-aligned. + Size == CharUnits::One()) return Success(1, E); - QualType PointeeType = E->getArg(1)->IgnoreImpCasts()->getType()-> - castAs()->getPointeeType(); - if (!PointeeType->isIncompleteType() && - Info.Ctx.getTypeAlignInChars(PointeeType) >= Size) { - // OK, we will inline operations on this object. + // If the pointer argument can be evaluated to a compile-time constant + // integer (or nullptr), check if that value is appropriately aligned. + const Expr *PtrArg = E->getArg(1); + Expr::EvalResult ExprResult; + APSInt IntResult; + if (PtrArg->EvaluateAsRValue(ExprResult, Info.Ctx) && + ExprResult.Val.toIntegralConstant(IntResult, PtrArg->getType(), + Info.Ctx) && + IntResult.isAligned(Size.getAsAlign())) return Success(1, E); + + // Otherwise, check if the type's alignment against Size. + if (auto *ICE = dyn_cast(PtrArg)) { + // Drop the potential implicit-cast to 'const volatile void*', getting + // the underlying type. + if (ICE->getCastKind() == CK_BitCast) + PtrArg = ICE->getSubExpr(); + } + + if (auto PtrTy = PtrArg->getType()->getAs()) { + QualType PointeeType = PtrTy->getPointeeType(); + if (!PointeeType->isIncompleteType() && + Info.Ctx.getTypeAlignInChars(PointeeType) >= Size) { + // OK, we will inline operations on this object. + return Success(1, E); + } } } } diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c index 9b82d82ff8269..2405f804d0da5 100644 --- a/clang/test/Sema/atomic-ops.c +++ b/clang/test/Sema/atomic-ops.c @@ -124,6 +124,31 @@ _Static_assert(__atomic_always_lock_free(4, &i64), ""); _Static_assert(!__atomic_always_lock_free(8, &i32), ""); _Static_assert(__atomic_always_lock_free(8, &i64), ""); +// Validate use with fake pointers constants. This mechanism is used to allow +// validating atomicity of a given size and alignment. +_Static_assert(__atomic_is_lock_free(1, (void*)1), ""); +_Static_assert(__atomic_is_lock_free(1, (void*)-1), ""); +_Static_assert(__atomic_is_lock_free(4, (void*)2), ""); // expected-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(4, (void*)-2), ""); // expected-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(4, (void*)4), ""); +_Static_assert(__atomic_is_lock_free(4, (void*)-4), ""); + +_Static_assert(__atomic_always_lock_free(1, (void*)1), ""); +_Static_assert(__atomic_always_lock_free(1, (void*)-1), ""); +_Static_assert(!__atomic_always_lock_free(4, (void*)2), ""); +_Static_assert(!__atomic_always_lock_free(4, (void*)-2), ""); +_Static_assert(__atomic_always_lock_free(4, (void*)4), ""); +_Static_assert(__atomic_always_lock_free(4, (void*)-4), ""); + +// Ensure that "weird" constants don't cause trouble. +_Static_assert(__atomic_always_lock_free(1, "string"), ""); +_Static_assert(!__atomic_always_lock_free(2, "string"), ""); +_Static_assert(__atomic_always_lock_free(2, (int[2]){}), ""); +void dummyfn(); +_Static_assert(__atomic_always_lock_free(2, dummyfn) || 1, ""); + + + #define _AS1 __attribute__((address_space(1))) #define _AS2 __attribute__((address_space(2)))