[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