diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index 578dc44fe4f8d..b09019f3e65b7 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -160,7 +160,7 @@ std::optional Context::classify(QualType T) const { if (T->isReferenceType() || T->isPointerType()) return PT_Ptr; - if (const auto *AT = dyn_cast(T)) + if (const auto *AT = T->getAs()) return classify(AT->getValueType()); if (const auto *DT = dyn_cast(T)) diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index 401af580e1aaf..2fb076d7793a8 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -887,6 +887,73 @@ static bool interp__builtin_bswap(InterpState &S, CodePtr OpPC, return true; } +/// bool __atomic_always_lock_free(size_t, void const volatile*) +/// bool __atomic_is_lock_free(size_t, void const volatile*) +/// bool __c11_atomic_is_lock_free(size_t) +static bool interp__builtin_atomic_lock_free(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, + const CallExpr *Call) { + unsigned BuiltinOp = Func->getBuiltinID(); + + PrimType ValT = *S.getContext().classify(Call->getArg(0)); + unsigned SizeValOffset = 0; + if (BuiltinOp != Builtin::BI__c11_atomic_is_lock_free) + SizeValOffset = align(primSize(ValT)) + align(primSize(PT_Ptr)); + const APSInt &SizeVal = peekToAPSInt(S.Stk, ValT, SizeValOffset); + + auto returnBool = [&S](bool Value) -> bool { + S.Stk.push(Value); + return true; + }; + + // For __atomic_is_lock_free(sizeof(_Atomic(T))), if the size is a power + // of two less than or equal to the maximum inline atomic width, we know it + // is lock-free. If the size isn't a power of two, or greater than the + // maximum alignment where we promote atomics, we know it is not lock-free + // (at least not in the sense of atomic_is_lock_free). Otherwise, + // the answer can only be determined at runtime; for example, 16-byte + // atomics have lock-free implementations on some, but not all, + // x86-64 processors. + + // Check power-of-two. + CharUnits Size = CharUnits::fromQuantity(SizeVal.getZExtValue()); + if (Size.isPowerOfTwo()) { + // Check against inlining width. + unsigned InlineWidthBits = + S.getCtx().getTargetInfo().getMaxAtomicInlineWidth(); + if (Size <= S.getCtx().toCharUnitsFromBits(InlineWidthBits)) { + + // OK, we will inline appropriately-aligned operations of this size, + // and _Atomic(T) is appropriately-aligned. + if (BuiltinOp == Builtin::BI__c11_atomic_is_lock_free || + Size == CharUnits::One()) + return returnBool(true); + + // Same for null pointers. + assert(BuiltinOp != Builtin::BI__c11_atomic_is_lock_free); + const Pointer &Ptr = S.Stk.peek(); + if (Ptr.isZero()) + return returnBool(true); + + QualType PointeeType = Call->getArg(1) + ->IgnoreImpCasts() + ->getType() + ->castAs() + ->getPointeeType(); + // OK, we will inline operations on this object. + if (!PointeeType->isIncompleteType() && + S.getCtx().getTypeAlignInChars(PointeeType) >= Size) + return returnBool(true); + } + } + + if (BuiltinOp == Builtin::BI__atomic_always_lock_free) + return returnBool(false); + + return false; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call) { InterpFrame *Frame = S.Current; @@ -1186,6 +1253,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case Builtin::BI__atomic_always_lock_free: + case Builtin::BI__atomic_is_lock_free: + case Builtin::BI__c11_atomic_is_lock_free: + if (!interp__builtin_atomic_lock_free(S, OpPC, Frame, F, Call)) + return false; + break; + default: return false; } diff --git a/clang/test/AST/Interp/atomic.c b/clang/test/AST/Interp/atomic.c index 8d93b57c1945b..316e8d5bf3516 100644 --- a/clang/test/AST/Interp/atomic.c +++ b/clang/test/AST/Interp/atomic.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected %s -// RUN: %clang_cc1 -verify=both,ref %s +// RUN: %clang_cc1 -fgnuc-version=4.2.1 -fexperimental-new-constant-interpreter -verify=both,expected %s +// RUN: %clang_cc1 -fgnuc-version=4.2.1 -verify=both,ref %s /// FIXME: Copied from test/Sema/atomic-expr.c. /// this expression seems to be rejected for weird reasons, @@ -11,3 +11,50 @@ _Atomic int ai = 0; // The warning is correct but the error is not. _Atomic(int *) aip3 = &ai; // both-warning {{incompatible pointer types initializing '_Atomic(int *)' with an expression of type '_Atomic(int) *'}} \ // both-error {{initializer element is not a compile-time constant}} + +#include + + + +_Static_assert(__GCC_ATOMIC_BOOL_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_BOOL_LOCK_FREE == __CLANG_ATOMIC_BOOL_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_CHAR_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_CHAR_LOCK_FREE == __CLANG_ATOMIC_CHAR_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_CHAR16_T_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_CHAR16_T_LOCK_FREE == __CLANG_ATOMIC_CHAR16_T_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_CHAR32_T_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_CHAR32_T_LOCK_FREE == __CLANG_ATOMIC_CHAR32_T_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_WCHAR_T_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_WCHAR_T_LOCK_FREE == __CLANG_ATOMIC_WCHAR_T_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_SHORT_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_SHORT_LOCK_FREE == __CLANG_ATOMIC_SHORT_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_INT_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_INT_LOCK_FREE == __CLANG_ATOMIC_INT_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_LONG_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_LONG_LOCK_FREE == __CLANG_ATOMIC_LONG_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_LLONG_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_LLONG_LOCK_FREE == __CLANG_ATOMIC_LLONG_LOCK_FREE, ""); +_Static_assert(__GCC_ATOMIC_POINTER_LOCK_FREE == 2, ""); +_Static_assert(__GCC_ATOMIC_POINTER_LOCK_FREE == __CLANG_ATOMIC_POINTER_LOCK_FREE, ""); + +_Static_assert(__c11_atomic_is_lock_free(1), ""); +_Static_assert(__c11_atomic_is_lock_free(2), ""); +_Static_assert(__c11_atomic_is_lock_free(3), ""); // both-error {{not an integral constant expression}} +_Static_assert(__c11_atomic_is_lock_free(4), ""); +_Static_assert(__c11_atomic_is_lock_free(8), ""); +_Static_assert(__c11_atomic_is_lock_free(16), ""); // both-error {{not an integral constant expression}} +_Static_assert(__c11_atomic_is_lock_free(17), ""); // both-error {{not an integral constant expression}} + +_Static_assert(__atomic_is_lock_free(1, 0), ""); +_Static_assert(__atomic_is_lock_free(2, 0), ""); +_Static_assert(__atomic_is_lock_free(3, 0), ""); // both-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(4, 0), ""); +_Static_assert(__atomic_is_lock_free(8, 0), ""); +_Static_assert(__atomic_is_lock_free(16, 0), ""); // both-error {{not an integral constant expression}} +_Static_assert(__atomic_is_lock_free(17, 0), ""); // both-error {{not an integral constant expression}} + +_Static_assert(atomic_is_lock_free((atomic_char*)0), ""); +_Static_assert(atomic_is_lock_free((atomic_short*)0), ""); +_Static_assert(atomic_is_lock_free((atomic_int*)0), ""); +_Static_assert(atomic_is_lock_free((atomic_long*)0), ""); +_Static_assert(atomic_is_lock_free(0 + (atomic_char*)0), "");