Skip to content

Commit a35599b

Browse files
committed
[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.
1 parent 58aa995 commit a35599b

File tree

3 files changed

+124
-3
lines changed

3 files changed

+124
-3
lines changed

clang/lib/AST/Interp/Context.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
160160
if (T->isReferenceType() || T->isPointerType())
161161
return PT_Ptr;
162162

163-
if (const auto *AT = dyn_cast<AtomicType>(T))
163+
if (const auto *AT = T->getAs<AtomicType>())
164164
return classify(AT->getValueType());
165165

166166
if (const auto *DT = dyn_cast<DecltypeType>(T))

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,73 @@ static bool interp__builtin_bswap(InterpState &S, CodePtr OpPC,
887887
return true;
888888
}
889889

890+
/// bool __atomic_always_lock_free(size_t, void const volatile*)
891+
/// bool __atomic_is_lock_free(size_t, void const volatile*)
892+
/// bool __c11_atomic_is_lock_free(size_t)
893+
static bool interp__builtin_atomic_lock_free(InterpState &S, CodePtr OpPC,
894+
const InterpFrame *Frame,
895+
const Function *Func,
896+
const CallExpr *Call) {
897+
unsigned BuiltinOp = Func->getBuiltinID();
898+
899+
PrimType ValT = *S.getContext().classify(Call->getArg(0));
900+
unsigned SizeValOffset = 0;
901+
if (BuiltinOp != Builtin::BI__c11_atomic_is_lock_free)
902+
SizeValOffset = align(primSize(ValT)) + align(primSize(PT_Ptr));
903+
const APSInt &SizeVal = peekToAPSInt(S.Stk, ValT, SizeValOffset);
904+
905+
auto returnBool = [&S](bool Value) -> bool {
906+
S.Stk.push<Boolean>(Value);
907+
return true;
908+
};
909+
910+
// For __atomic_is_lock_free(sizeof(_Atomic(T))), if the size is a power
911+
// of two less than or equal to the maximum inline atomic width, we know it
912+
// is lock-free. If the size isn't a power of two, or greater than the
913+
// maximum alignment where we promote atomics, we know it is not lock-free
914+
// (at least not in the sense of atomic_is_lock_free). Otherwise,
915+
// the answer can only be determined at runtime; for example, 16-byte
916+
// atomics have lock-free implementations on some, but not all,
917+
// x86-64 processors.
918+
919+
// Check power-of-two.
920+
CharUnits Size = CharUnits::fromQuantity(SizeVal.getZExtValue());
921+
if (Size.isPowerOfTwo()) {
922+
// Check against inlining width.
923+
unsigned InlineWidthBits =
924+
S.getCtx().getTargetInfo().getMaxAtomicInlineWidth();
925+
if (Size <= S.getCtx().toCharUnitsFromBits(InlineWidthBits)) {
926+
927+
// OK, we will inline appropriately-aligned operations of this size,
928+
// and _Atomic(T) is appropriately-aligned.
929+
if (BuiltinOp == Builtin::BI__c11_atomic_is_lock_free ||
930+
Size == CharUnits::One())
931+
return returnBool(true);
932+
933+
// Same for null pointers.
934+
assert(BuiltinOp != Builtin::BI__c11_atomic_is_lock_free);
935+
const Pointer &Ptr = S.Stk.peek<Pointer>();
936+
if (Ptr.isZero())
937+
return returnBool(true);
938+
939+
QualType PointeeType = Call->getArg(1)
940+
->IgnoreImpCasts()
941+
->getType()
942+
->castAs<PointerType>()
943+
->getPointeeType();
944+
// OK, we will inline operations on this object.
945+
if (!PointeeType->isIncompleteType() &&
946+
S.getCtx().getTypeAlignInChars(PointeeType) >= Size)
947+
return returnBool(true);
948+
}
949+
}
950+
951+
if (BuiltinOp == Builtin::BI__atomic_always_lock_free)
952+
return returnBool(false);
953+
954+
return false;
955+
}
956+
890957
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
891958
const CallExpr *Call) {
892959
InterpFrame *Frame = S.Current;
@@ -1186,6 +1253,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
11861253
return false;
11871254
break;
11881255

1256+
case Builtin::BI__atomic_always_lock_free:
1257+
case Builtin::BI__atomic_is_lock_free:
1258+
case Builtin::BI__c11_atomic_is_lock_free:
1259+
if (!interp__builtin_atomic_lock_free(S, OpPC, Frame, F, Call))
1260+
return false;
1261+
break;
1262+
11891263
default:
11901264
return false;
11911265
}

clang/test/AST/Interp/atomic.c

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected %s
2-
// RUN: %clang_cc1 -verify=both,ref %s
1+
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -fexperimental-new-constant-interpreter -verify=both,expected %s
2+
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -verify=both,ref %s
33

44
/// FIXME: Copied from test/Sema/atomic-expr.c.
55
/// this expression seems to be rejected for weird reasons,
@@ -11,3 +11,50 @@ _Atomic int ai = 0;
1111
// The warning is correct but the error is not.
1212
_Atomic(int *) aip3 = &ai; // both-warning {{incompatible pointer types initializing '_Atomic(int *)' with an expression of type '_Atomic(int) *'}} \
1313
// both-error {{initializer element is not a compile-time constant}}
14+
15+
#include <stdatomic.h>
16+
17+
18+
19+
_Static_assert(__GCC_ATOMIC_BOOL_LOCK_FREE == 2, "");
20+
_Static_assert(__GCC_ATOMIC_BOOL_LOCK_FREE == __CLANG_ATOMIC_BOOL_LOCK_FREE, "");
21+
_Static_assert(__GCC_ATOMIC_CHAR_LOCK_FREE == 2, "");
22+
_Static_assert(__GCC_ATOMIC_CHAR_LOCK_FREE == __CLANG_ATOMIC_CHAR_LOCK_FREE, "");
23+
_Static_assert(__GCC_ATOMIC_CHAR16_T_LOCK_FREE == 2, "");
24+
_Static_assert(__GCC_ATOMIC_CHAR16_T_LOCK_FREE == __CLANG_ATOMIC_CHAR16_T_LOCK_FREE, "");
25+
_Static_assert(__GCC_ATOMIC_CHAR32_T_LOCK_FREE == 2, "");
26+
_Static_assert(__GCC_ATOMIC_CHAR32_T_LOCK_FREE == __CLANG_ATOMIC_CHAR32_T_LOCK_FREE, "");
27+
_Static_assert(__GCC_ATOMIC_WCHAR_T_LOCK_FREE == 2, "");
28+
_Static_assert(__GCC_ATOMIC_WCHAR_T_LOCK_FREE == __CLANG_ATOMIC_WCHAR_T_LOCK_FREE, "");
29+
_Static_assert(__GCC_ATOMIC_SHORT_LOCK_FREE == 2, "");
30+
_Static_assert(__GCC_ATOMIC_SHORT_LOCK_FREE == __CLANG_ATOMIC_SHORT_LOCK_FREE, "");
31+
_Static_assert(__GCC_ATOMIC_INT_LOCK_FREE == 2, "");
32+
_Static_assert(__GCC_ATOMIC_INT_LOCK_FREE == __CLANG_ATOMIC_INT_LOCK_FREE, "");
33+
_Static_assert(__GCC_ATOMIC_LONG_LOCK_FREE == 2, "");
34+
_Static_assert(__GCC_ATOMIC_LONG_LOCK_FREE == __CLANG_ATOMIC_LONG_LOCK_FREE, "");
35+
_Static_assert(__GCC_ATOMIC_LLONG_LOCK_FREE == 2, "");
36+
_Static_assert(__GCC_ATOMIC_LLONG_LOCK_FREE == __CLANG_ATOMIC_LLONG_LOCK_FREE, "");
37+
_Static_assert(__GCC_ATOMIC_POINTER_LOCK_FREE == 2, "");
38+
_Static_assert(__GCC_ATOMIC_POINTER_LOCK_FREE == __CLANG_ATOMIC_POINTER_LOCK_FREE, "");
39+
40+
_Static_assert(__c11_atomic_is_lock_free(1), "");
41+
_Static_assert(__c11_atomic_is_lock_free(2), "");
42+
_Static_assert(__c11_atomic_is_lock_free(3), ""); // both-error {{not an integral constant expression}}
43+
_Static_assert(__c11_atomic_is_lock_free(4), "");
44+
_Static_assert(__c11_atomic_is_lock_free(8), "");
45+
_Static_assert(__c11_atomic_is_lock_free(16), ""); // both-error {{not an integral constant expression}}
46+
_Static_assert(__c11_atomic_is_lock_free(17), ""); // both-error {{not an integral constant expression}}
47+
48+
_Static_assert(__atomic_is_lock_free(1, 0), "");
49+
_Static_assert(__atomic_is_lock_free(2, 0), "");
50+
_Static_assert(__atomic_is_lock_free(3, 0), ""); // both-error {{not an integral constant expression}}
51+
_Static_assert(__atomic_is_lock_free(4, 0), "");
52+
_Static_assert(__atomic_is_lock_free(8, 0), "");
53+
_Static_assert(__atomic_is_lock_free(16, 0), ""); // both-error {{not an integral constant expression}}
54+
_Static_assert(__atomic_is_lock_free(17, 0), ""); // both-error {{not an integral constant expression}}
55+
56+
_Static_assert(atomic_is_lock_free((atomic_char*)0), "");
57+
_Static_assert(atomic_is_lock_free((atomic_short*)0), "");
58+
_Static_assert(atomic_is_lock_free((atomic_int*)0), "");
59+
_Static_assert(atomic_is_lock_free((atomic_long*)0), "");
60+
_Static_assert(atomic_is_lock_free(0 + (atomic_char*)0), "");

0 commit comments

Comments
 (0)