[clang] a35599b - [clang][Interp] Implement a few _is_lock_free builtins
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 26 04:56:55 PST 2024
Author: Timm Bäder
Date: 2024-02-26T13:56:37+01:00
New Revision: a35599b9ae5e7ad924b78c65f6348e0b711bad5d
URL: https://github.com/llvm/llvm-project/commit/a35599b9ae5e7ad924b78c65f6348e0b711bad5d
DIFF: https://github.com/llvm/llvm-project/commit/a35599b9ae5e7ad924b78c65f6348e0b711bad5d.diff
LOG: [clang][Interp] Implement a few _is_lock_free builtins
Implementation looks similar to the one in the current interpreter.
Except for three static assertions, test/Sema/atomic-ops.c works.
Added:
Modified:
clang/lib/AST/Interp/Context.cpp
clang/lib/AST/Interp/InterpBuiltin.cpp
clang/test/AST/Interp/atomic.c
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index 578dc44fe4f8d4..b09019f3e65b79 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -160,7 +160,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isReferenceType() || T->isPointerType())
return PT_Ptr;
- if (const auto *AT = dyn_cast<AtomicType>(T))
+ if (const auto *AT = T->getAs<AtomicType>())
return classify(AT->getValueType());
if (const auto *DT = dyn_cast<DecltypeType>(T))
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 401af580e1aaf6..2fb076d7793a8d 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<Boolean>(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<Pointer>();
+ if (Ptr.isZero())
+ return returnBool(true);
+
+ QualType PointeeType = Call->getArg(1)
+ ->IgnoreImpCasts()
+ ->getType()
+ ->castAs<PointerType>()
+ ->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 8d93b57c1945bc..316e8d5bf35167 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 <stdatomic.h>
+
+
+
+_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), "");
More information about the cfe-commits
mailing list