arichardson updated this revision to Diff 235764. arichardson marked an inline comment as done. arichardson added a comment.
- Address remaining comments Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D71499/new/ https://reviews.llvm.org/D71499 Files: clang/docs/LanguageExtensions.rst clang/include/clang/Basic/Builtins.def clang/include/clang/Basic/DiagnosticASTKinds.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/AST/ExprConstant.cpp clang/lib/CodeGen/CGBuiltin.cpp clang/lib/CodeGen/CodeGenFunction.h clang/lib/Sema/SemaChecking.cpp clang/test/CodeGen/builtin-align-array.c clang/test/CodeGen/builtin-align.c clang/test/Sema/builtin-align.c clang/test/SemaCXX/builtin-align-cxx.cpp
Index: clang/test/SemaCXX/builtin-align-cxx.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/builtin-align-cxx.cpp @@ -0,0 +1,236 @@ +// C++-specific checks for the alignment builtins +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -std=c++11 -o - %s -fsyntax-only -verify + +// Check that we don't crash when using dependent types in __builtin_align: +template <typename a, a b> +void *c(void *d) { // expected-note{{candidate template ignored}} + return __builtin_align_down(d, b); +} + +struct x {}; +x foo; +void test(void *value) { + c<int, 16>(value); + c<struct x, foo>(value); // expected-error{{no matching function for call to 'c'}} +} + +template <typename T, long Alignment, long ArraySize = 16> +void test_templated_arguments() { + T array[ArraySize]; // expected-error{{variable has incomplete type 'fwddecl'}} + static_assert(__is_same(decltype(__builtin_align_up(array, Alignment)), T *), // expected-error{{requested alignment is not a power of 2}} + "return type should be the decayed array type"); + static_assert(__is_same(decltype(__builtin_align_down(array, Alignment)), T *), + "return type should be the decayed array type"); + static_assert(__is_same(decltype(__builtin_is_aligned(array, Alignment)), bool), + "return type should be bool"); + T *x1 = __builtin_align_up(array, Alignment); + T *x2 = __builtin_align_down(array, Alignment); + bool x3 = __builtin_align_up(array, Alignment); +} + +void test() { + test_templated_arguments<int, 32>(); // fine + test_templated_arguments<struct fwddecl, 16>(); + // expected-note@-1{{in instantiation of function template specialization 'test_templated_arguments<fwddecl, 16, 16>'}} + // expected-note@-2{{forward declaration of 'fwddecl'}} + test_templated_arguments<int, 7>(); // invalid alignment value + // expected-note@-1{{in instantiation of function template specialization 'test_templated_arguments<int, 7, 16>'}} +} + +template <typename T> +void test_incorrect_alignment_without_instatiation(T value) { + int array[32]; + static_assert(__is_same(decltype(__builtin_align_up(array, 31)), int *), // expected-error{{requested alignment is not a power of 2}} + "return type should be the decayed array type"); + static_assert(__is_same(decltype(__builtin_align_down(array, 7)), int *), // expected-error{{requested alignment is not a power of 2}} + "return type should be the decayed array type"); + static_assert(__is_same(decltype(__builtin_is_aligned(array, -1)), bool), // expected-error{{requested alignment must be 1 or greater}} + "return type should be bool"); + __builtin_align_up(array); // expected-error{{too few arguments to function call, expected 2, have 1}} + __builtin_align_up(array, 31); // expected-error{{requested alignment is not a power of 2}} + __builtin_align_down(array, 31); // expected-error{{requested alignment is not a power of 2}} + __builtin_align_up(array, 31); // expected-error{{requested alignment is not a power of 2}} + __builtin_align_up(value, 31); // This shouldn't want since the type is dependent + __builtin_align_up(value); // Same here +} + +// The original fix for the issue above broke some legitimate code. +// Here is a regression test: +typedef __SIZE_TYPE__ size_t; +void *allocate_impl(size_t size); +template <typename T> +T *allocate() { + constexpr size_t allocation_size = + __builtin_align_up(sizeof(T), sizeof(void *)); + return static_cast<T *>( + __builtin_assume_aligned(allocate_impl(allocation_size), sizeof(void *))); +} +struct Foo { + int value; +}; +void *test2() { + return allocate<struct Foo>(); +} + +// Check that pointers-to-members cannot be used: +class MemPtr { +public: + int data; + void func(); + virtual void vfunc(); +}; +void test_member_ptr() { + __builtin_align_up(&MemPtr::data, 64); // expected-error{{operand of type 'int MemPtr::*' where arithmetic or pointer type is required}} + __builtin_align_down(&MemPtr::func, 64); // expected-error{{operand of type 'void (MemPtr::*)()' where arithmetic or pointer type is required}} + __builtin_is_aligned(&MemPtr::vfunc, 64); // expected-error{{operand of type 'void (MemPtr::*)()' where arithmetic or pointer type is required}} +} + +void test_references(Foo &i) { + // Check that the builtins look at the referenced type rather than the reference itself. + (void)__builtin_align_up(i, 64); // expected-error{{operand of type 'Foo' where arithmetic or pointer type is required}} + (void)__builtin_align_up(static_cast<Foo &>(i), 64); // expected-error{{operand of type 'Foo' where arithmetic or pointer type is required}} + (void)__builtin_align_up(static_cast<const Foo &>(i), 64); // expected-error{{operand of type 'const Foo' where arithmetic or pointer type is required}} + (void)__builtin_align_up(static_cast<Foo &&>(i), 64); // expected-error{{operand of type 'Foo' where arithmetic or pointer type is required}} + (void)__builtin_align_up(static_cast<const Foo &&>(i), 64); // expected-error{{operand of type 'const Foo' where arithmetic or pointer type is required}} + (void)__builtin_align_up(&i, 64); +} + +// Check that constexpr wrapper functions can be constant-evaluated. +template <typename T> +constexpr bool wrap_is_aligned(T ptr, long align) { + return __builtin_is_aligned(ptr, align); + // expected-note@-1{{requested alignment -3 is not a positive power of two}} + // expected-note@-2{{requested alignment 19 is not a positive power of two}} + // expected-note@-3{{requested alignment must be 128 or less for type 'char'; 4194304 is invalid}} +} +template <typename T> +constexpr T wrap_align_up(T ptr, long align) { + return __builtin_align_up(ptr, align); + // expected-note@-1{{requested alignment -2 is not a positive power of two}} + // expected-note@-2{{requested alignment 18 is not a positive power of two}} + // expected-note@-3{{requested alignment must be 2147483648 or less for type 'int'; 8589934592 is invalid}} + // expected-error@-4{{operand of type 'bool' where arithmetic or pointer type is required}} +} + +template <typename T> +constexpr T wrap_align_down(T ptr, long align) { + return __builtin_align_down(ptr, align); + // expected-note@-1{{requested alignment -1 is not a positive power of two}} + // expected-note@-2{{requested alignment 17 is not a positive power of two}} + // expected-note@-3{{requested alignment must be 32768 or less for type 'short'; 1048576 is invalid}} +} + +constexpr int a1 = wrap_align_up(22, 32); +static_assert(a1 == 32, ""); +constexpr int a2 = wrap_align_down(22, 16); +static_assert(a2 == 16, ""); +constexpr bool a3 = wrap_is_aligned(22, 32); +static_assert(!a3, ""); +static_assert(wrap_align_down(wrap_align_up(22, 16), 32) == 32, ""); +static_assert(wrap_is_aligned(wrap_align_down(wrap_align_up(22, 16), 32), 32), ""); +static_assert(!wrap_is_aligned(wrap_align_down(wrap_align_up(22, 16), 32), 64), ""); + +constexpr long const_value(long l) { return l; } +// Check some invalid values during constant-evaluation +static_assert(wrap_align_down(1, const_value(-1)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_align_down(1, -1)'}} +static_assert(wrap_align_up(1, const_value(-2)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_align_up(1, -2)'}} +static_assert(wrap_is_aligned(1, const_value(-3)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_is_aligned(1, -3)'}} +static_assert(wrap_align_down(1, const_value(17)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_align_down(1, 17)'}} +static_assert(wrap_align_up(1, const_value(18)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_align_up(1, 18)'}} +static_assert(wrap_is_aligned(1, const_value(19)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_is_aligned(1, 19)'}} + +// Check invalid values for smaller types: +static_assert(wrap_align_down(static_cast<short>(1), const_value(1 << 20)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_align_down(1, 1048576)'}} +// Check invalid boolean type +static_assert(wrap_align_up(static_cast<int>(1), const_value(1ull << 33)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_align_up(1, 8589934592)'}} +static_assert(wrap_is_aligned(static_cast<char>(1), const_value(1 << 22)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in call to 'wrap_is_aligned(1, 4194304)'}} + +// Check invalid boolean type +static_assert(wrap_align_up(static_cast<bool>(1), const_value(1 << 21)), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{in instantiation of function template specialization 'wrap_align_up<bool>' requested here}} + +// Check constant evaluation for pointers: +_Alignas(32) char align32array[128]; +static_assert(&align32array[0] == &align32array[0], ""); +// __builtin_align_up/down can be constant evaluated as a no-op for values +// that are known to have greater alignment: +static_assert(__builtin_align_up(&align32array[0], 32) == &align32array[0], ""); +static_assert(__builtin_align_up(&align32array[0], 4) == &align32array[0], ""); +static_assert(__builtin_align_down(&align32array[0], 4) == __builtin_align_up(&align32array[0], 8), ""); +// But it can not be evaluated if the alignment is greater than the minimum +// known alignment, since in that case the value might be the same if it happens +// to actually be aligned to 64 bytes at run time. +static_assert(&align32array[0] == __builtin_align_up(&align32array[0], 64), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} +static_assert(__builtin_align_up(&align32array[0], 64) == __builtin_align_up(&align32array[0], 64), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} + +// However, we can compute in case the requested alignment is less than the +// base alignment: +static_assert(__builtin_align_up(&align32array[0], 4) == &align32array[0], ""); +static_assert(__builtin_align_up(&align32array[1], 4) == &align32array[4], ""); +static_assert(__builtin_align_up(&align32array[2], 4) == &align32array[4], ""); +static_assert(__builtin_align_up(&align32array[3], 4) == &align32array[4], ""); +static_assert(__builtin_align_up(&align32array[4], 4) == &align32array[4], ""); +static_assert(__builtin_align_up(&align32array[5], 4) == &align32array[8], ""); +static_assert(__builtin_align_up(&align32array[6], 4) == &align32array[8], ""); +static_assert(__builtin_align_up(&align32array[7], 4) == &align32array[8], ""); +static_assert(__builtin_align_up(&align32array[8], 4) == &align32array[8], ""); + +static_assert(__builtin_align_down(&align32array[0], 4) == &align32array[0], ""); +static_assert(__builtin_align_down(&align32array[1], 4) == &align32array[0], ""); +static_assert(__builtin_align_down(&align32array[2], 4) == &align32array[0], ""); +static_assert(__builtin_align_down(&align32array[3], 4) == &align32array[0], ""); +static_assert(__builtin_align_down(&align32array[4], 4) == &align32array[4], ""); +static_assert(__builtin_align_down(&align32array[5], 4) == &align32array[4], ""); +static_assert(__builtin_align_down(&align32array[6], 4) == &align32array[4], ""); +static_assert(__builtin_align_down(&align32array[7], 4) == &align32array[4], ""); +static_assert(__builtin_align_down(&align32array[8], 4) == &align32array[8], ""); + +// Achiving the same thing using casts to uintptr_t is not allowed: +static_assert((char *)((__UINTPTR_TYPE__)&align32array[7] & ~3) == &align32array[4], ""); // expected-error{{not an integral constant expression}} + +static_assert(__builtin_align_down(&align32array[1], 4) == &align32array[0], ""); +static_assert(__builtin_align_down(&align32array[1], 64) == &align32array[0], ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} + +// Add some checks for __builtin_is_aligned: +static_assert(__builtin_is_aligned(&align32array[0], 32), ""); +static_assert(__builtin_is_aligned(&align32array[4], 4), ""); +// We cannot constant evaluate whether the array is aligned to > 32 since this +// may well be true at run time. +static_assert(!__builtin_is_aligned(&align32array[0], 64), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{cannot constant evaluate whether run-time alignment is at least 64}} + +// However, if the alignment being checked is less than the minimum alignment of +// the base object we can check the low bits of the alignment: +static_assert(__builtin_is_aligned(&align32array[0], 4), ""); +static_assert(!__builtin_is_aligned(&align32array[1], 4), ""); +static_assert(!__builtin_is_aligned(&align32array[2], 4), ""); +static_assert(!__builtin_is_aligned(&align32array[3], 4), ""); +static_assert(__builtin_is_aligned(&align32array[4], 4), ""); + +// TODO: this should evaluate to true even though we can't evaluate the result +// of __builtin_align_up() to a concrete value +static_assert(__builtin_is_aligned(__builtin_align_up(&align32array[0], 64), 64), ""); // expected-error{{not an integral constant expression}} +// expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} + +// Check different source and alignment type widths are handled correctly. +static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<signed short>(4)), ""); +static_assert(!__builtin_is_aligned(static_cast<signed short>(7), static_cast<signed long>(4)), ""); +// Also check signed -- unsigned mismatch. +static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<signed long>(4)), ""); +static_assert(!__builtin_is_aligned(static_cast<unsigned long>(7), static_cast<unsigned long>(4)), ""); +static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<unsigned long>(4)), ""); +static_assert(!__builtin_is_aligned(static_cast<unsigned long>(7), static_cast<signed long>(4)), ""); +static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<unsigned short>(4)), ""); +static_assert(!__builtin_is_aligned(static_cast<unsigned short>(7), static_cast<signed long>(4)), ""); Index: clang/test/Sema/builtin-align.c =================================================================== --- /dev/null +++ clang/test/Sema/builtin-align.c @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -DALIGN_BUILTIN=__builtin_align_down -DRETURNS_BOOL=0 %s -fsyntax-only -verify -Wpedantic +// RUN: %clang_cc1 -triple x86_64-linux-gnu -DALIGN_BUILTIN=__builtin_align_up -DRETURNS_BOOL=0 %s -fsyntax-only -verify -Wpedantic +// RUN: %clang_cc1 -triple x86_64-linux-gnu -DALIGN_BUILTIN=__builtin_is_aligned -DRETURNS_BOOL=1 %s -fsyntax-only -verify -Wpedantic + +struct Aggregate { + int i; + int j; +}; +enum Enum { EnumValue1, + EnumValue2 }; +typedef __SIZE_TYPE__ size_t; + +void test_parameter_types(char *ptr, size_t size) { + struct Aggregate agg; + enum Enum e = EnumValue2; + _Bool b = 0; + + // The first parameter can be any pointer or integer type: + (void)ALIGN_BUILTIN(ptr, 4); + (void)ALIGN_BUILTIN(size, 2); + (void)ALIGN_BUILTIN(12345, 2); + (void)ALIGN_BUILTIN(agg, 2); // expected-error {{operand of type 'struct Aggregate' where arithmetic or pointer type is required}} + (void)ALIGN_BUILTIN(e, 2); // expected-error {{operand of type 'enum Enum' where arithmetic or pointer type is required}} + (void)ALIGN_BUILTIN(b, 2); // expected-error {{operand of type '_Bool' where arithmetic or pointer type is required}} + (void)ALIGN_BUILTIN((int)e, 2); // but with a cast it is fine + (void)ALIGN_BUILTIN((int)b, 2); // but with a cast it is fine + + // The second parameter must be an integer type (but not enum or _Bool): + (void)ALIGN_BUILTIN(ptr, size); + (void)ALIGN_BUILTIN(ptr, ptr); // expected-error {{used type 'char *' where integer is required}} + (void)ALIGN_BUILTIN(ptr, agg); // expected-error {{used type 'struct Aggregate' where integer is required}} + (void)ALIGN_BUILTIN(ptr, b); // expected-error {{used type '_Bool' where integer is required}} + (void)ALIGN_BUILTIN(ptr, e); // expected-error {{used type 'enum Enum' where integer is required}} + (void)ALIGN_BUILTIN(ptr, (int)e); // but with a cast enums are fine + (void)ALIGN_BUILTIN(ptr, (int)b); // but with a cast booleans are fine + + (void)ALIGN_BUILTIN(ptr, size); + (void)ALIGN_BUILTIN(size, size); +} + +void test_result_unused(int i, int align) { + // -Wunused-result does not trigger for macros so we can't use ALIGN_BUILTIN() + // but need to explicitly call each function. + __builtin_align_up(i, align); // expected-warning{{ignoring return value of function declared with const attribute}} + __builtin_align_down(i, align); // expected-warning{{ignoring return value of function declared with const attribute}} + __builtin_is_aligned(i, align); // expected-warning{{ignoring return value of function declared with const attribute}} + ALIGN_BUILTIN(i, align); // no warning here +} + +#define check_same_type(type1, type2) __builtin_types_compatible_p(type1, type2) && __builtin_types_compatible_p(type1 *, type2 *) + +void test_return_type(void *ptr, int i, long l) { + char array[32]; + __extension__ typedef typeof(ALIGN_BUILTIN(ptr, 4)) result_type_ptr; + __extension__ typedef typeof(ALIGN_BUILTIN(i, 4)) result_type_int; + __extension__ typedef typeof(ALIGN_BUILTIN(l, 4)) result_type_long; + __extension__ typedef typeof(ALIGN_BUILTIN(array, 4)) result_type_char_array; +#if RETURNS_BOOL + _Static_assert(check_same_type(_Bool, result_type_ptr), "Should return bool"); + _Static_assert(check_same_type(_Bool, result_type_int), "Should return bool"); + _Static_assert(check_same_type(_Bool, result_type_long), "Should return bool"); + _Static_assert(check_same_type(_Bool, result_type_char_array), "Should return bool"); +#else + _Static_assert(check_same_type(void *, result_type_ptr), "Should return void*"); + _Static_assert(check_same_type(int, result_type_int), "Should return int"); + _Static_assert(check_same_type(long, result_type_long), "Should return long"); + // Check that we can use the alignment builtins on on array types (result should decay) + _Static_assert(check_same_type(char *, result_type_char_array), + "Using the builtins on an array should yield the decayed type"); +#endif +} + +void test_invalid_alignment_values(char *ptr, long *longptr, size_t align) { + int x = 1; + (void)ALIGN_BUILTIN(ptr, 2); + (void)ALIGN_BUILTIN(longptr, 1024); + (void)ALIGN_BUILTIN(x, 32); + + (void)ALIGN_BUILTIN(ptr, 0); // expected-error {{requested alignment must be 1 or greater}} + (void)ALIGN_BUILTIN(ptr, 1); +#if RETURNS_BOOL + // expected-warning@-2 {{checking whether a value is aligned to 1 byte is always true}} +#else + // expected-warning@-4 {{aligning a value to 1 byte is a no-op}} +#endif + (void)ALIGN_BUILTIN(ptr, 3); // expected-error {{requested alignment is not a power of 2}} + (void)ALIGN_BUILTIN(x, 7); // expected-error {{requested alignment is not a power of 2}} + + // check the maximum range for smaller types: + __UINT8_TYPE__ c = ' '; + + (void)ALIGN_BUILTIN(c, 128); // this is fine + (void)ALIGN_BUILTIN(c, 256); // expected-error {{requested alignment must be 128 or smaller}} + (void)ALIGN_BUILTIN(x, 1ULL << 31); // this is also fine + (void)ALIGN_BUILTIN(x, 1LL << 31); // this is also fine + __INT32_TYPE__ i32 = 3; + __UINT32_TYPE__ u32 = 3; + // Maximum is the same for int32 and uint32 + (void)ALIGN_BUILTIN(i32, 1ULL << 32); // expected-error {{requested alignment must be 2147483648 or smaller}} + (void)ALIGN_BUILTIN(u32, 1ULL << 32); // expected-error {{requested alignment must be 2147483648 or smaller}} + (void)ALIGN_BUILTIN(ptr, ((__int128)1) << 65); // expected-error {{requested alignment must be 9223372036854775808 or smaller}} + (void)ALIGN_BUILTIN(longptr, ((__int128)1) << 65); // expected-error {{requested alignment must be 9223372036854775808 or smaller}} + + const int bad_align = 8 + 1; + (void)ALIGN_BUILTIN(ptr, bad_align); // expected-error {{requested alignment is not a power of 2}} +} + +// Check that it can be used in constant expressions: +void constant_expression(int x) { + _Static_assert(__builtin_is_aligned(1024, 512), ""); + _Static_assert(!__builtin_is_aligned(256, 512ULL), ""); + _Static_assert(__builtin_align_up(33, 32) == 64, ""); + _Static_assert(__builtin_align_down(33, 32) == 32, ""); + + // But not if one of the arguments isn't constant: + _Static_assert(ALIGN_BUILTIN(33, x) != 100, ""); // expected-error {{static_assert expression is not an integral constant expression}} + _Static_assert(ALIGN_BUILTIN(x, 4) != 100, ""); // expected-error {{static_assert expression is not an integral constant expression}} +} + +// Check that it is a constant expression that can be assigned to globals: +int global1 = __builtin_align_down(33, 8); +int global2 = __builtin_align_up(33, 8); +_Bool global3 = __builtin_is_aligned(33, 8); + +extern void test_ptr(char *c); +char *test_array_and_fnptr(void) { + char buf[1024]; + // The builtins should also work on arrays (decaying the return type) + (void)(ALIGN_BUILTIN(buf, 16)); + // But not on functions and function pointers: + (void)(ALIGN_BUILTIN(test_array_and_fnptr, 16)); // expected-error{{operand of type 'char *(void)' where arithmetic or pointer type is required}} + (void)(ALIGN_BUILTIN(&test_array_and_fnptr, 16)); // expected-error{{operand of type 'char *(*)(void)' where arithmetic or pointer type is required}} +} Index: clang/test/CodeGen/builtin-align.c =================================================================== --- /dev/null +++ clang/test/CodeGen/builtin-align.c @@ -0,0 +1,112 @@ +/// Check the code generation for the alignment builtins +/// To make the test case easier to read, run SROA after generating IR to remove the alloca instructions. +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -DTEST_VOID_PTR \ +// RUN: -o - -emit-llvm %s -disable-O0-optnone | opt -S -sroa | \ +// RUN: FileCheck %s -check-prefixes CHECK,POINTER,ALIGNMENT_EXT \ +// RUN: -enable-var-scope '-D$PTRTYPE=i8' +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -DTEST_FLOAT_PTR \ +// RUN: -o - -emit-llvm %s -disable-O0-optnone | opt -S -sroa | \ +// RUN: FileCheck %s -check-prefixes CHECK,POINTER,NON_I8_POINTER,ALIGNMENT_EXT \ +// RUN: -enable-var-scope '-D$PTRTYPE=f32' +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -DTEST_LONG \ +// RUN: -o - -emit-llvm %s -disable-O0-optnone | opt -S -sroa | \ +// RUN: FileCheck %s -check-prefixes CHECK,INTEGER,ALIGNMENT_EXT -enable-var-scope +/// Check that we can handle the case where the alignment parameter is wider +/// than the source type (generate a trunc on alignment instead of zext) +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -DTEST_USHORT \ +// RUN: -o - -emit-llvm %s -disable-O0-optnone | opt -S -sroa | \ +// RUN: FileCheck %s -check-prefixes CHECK,INTEGER,ALIGNMENT_TRUNC -enable-var-scope + +#ifdef TEST_VOID_PTR +#define TYPE void * +#elif defined(TEST_FLOAT_PTR) +#define TYPE float * +#elif defined(TEST_LONG) +#define TYPE long +#elif defined(TEST_CAP) +#define TYPE void *__capability +#elif defined(TEST_USHORT) +#define TYPE unsigned short +#else +#error MISSING TYPE +#endif + +/// Check that constant initializers work and are correct +_Bool aligned_true = __builtin_is_aligned(1024, 512); +// CHECK: @aligned_true = global i8 1, align 1 +_Bool aligned_false = __builtin_is_aligned(123, 512); +// CHECK: @aligned_false = global i8 0, align 1 + +int down_1 = __builtin_align_down(1023, 32); +// CHECK: @down_1 = global i32 992, align 4 +int down_2 = __builtin_align_down(256, 32); +// CHECK: @down_2 = global i32 256, align 4 + +int up_1 = __builtin_align_up(1023, 32); +// CHECK: @up_1 = global i32 1024, align 4 +int up_2 = __builtin_align_up(256, 32); +// CHECK: @up_2 = global i32 256, align 4 + +/// Capture the IR type here to use in the remaining FileCheck captures: +// CHECK: define {{[^@]+}}@get_type() #0 +// CHECK-NEXT: entry: +// POINTER-NEXT: ret [[$TYPE:.+]] null +// INTEGER-NEXT: ret [[$TYPE:.+]] 0 +// +TYPE get_type(void) { + return (TYPE)0; +} + +// CHECK-LABEL: define {{[^@]+}}@is_aligned +// CHECK-SAME: ([[$TYPE]] {{[^%]*}}[[PTR:%.*]], i32 [[ALIGN:%.*]]) #0 +// CHECK-NEXT: entry: +// ALIGNMENT_EXT-NEXT: [[ALIGNMENT:%.*]] = zext i32 [[ALIGN]] to [[ALIGN_TYPE:i64]] +// ALIGNMENT_TRUNC-NEXT: [[ALIGNMENT:%.*]] = trunc i32 [[ALIGN]] to [[ALIGN_TYPE:i16]] +// CHECK-NEXT: [[MASK:%.*]] = sub [[ALIGN_TYPE]] [[ALIGNMENT]], 1 +// POINTER-NEXT: [[PTR:%.*]] = ptrtoint [[$TYPE]] %ptr to i64 +// CHECK-NEXT: [[SET_BITS:%.*]] = and [[ALIGN_TYPE]] [[PTR]], [[MASK]] +// CHECK-NEXT: [[IS_ALIGNED:%.*]] = icmp eq [[ALIGN_TYPE]] [[SET_BITS]], 0 +// CHECK-NEXT: ret i1 [[IS_ALIGNED]] +// +_Bool is_aligned(TYPE ptr, unsigned align) { + return __builtin_is_aligned(ptr, align); +} + +// CHECK-LABEL: define {{[^@]+}}@align_up +// CHECK-SAME: ([[$TYPE]] {{[^%]*}}[[PTR:%.*]], i32 [[ALIGN:%.*]]) #0 +// CHECK-NEXT: entry: +// ALIGNMENT_EXT-NEXT: [[ALIGNMENT:%.*]] = zext i32 [[ALIGN]] to [[ALIGN_TYPE:i64]] +// ALIGNMENT_TRUNC-NEXT: [[ALIGNMENT:%.*]] = trunc i32 [[ALIGN]] to [[ALIGN_TYPE:i16]] +// CHECK-NEXT: [[MASK:%.*]] = sub [[ALIGN_TYPE]] [[ALIGNMENT]], 1 +// NON_I8_POINTER-NEXT: [[PTR:%.*]] = bitcast [[$TYPE]] %ptr to i8* +// POINTER-NEXT: [[OVER_BOUNDARY:%.*]] = getelementptr i8, i8* [[PTR]], i64 [[MASK]] +// INTEGER-NEXT: [[OVER_BOUNDARY:%.*]] = add [[$TYPE]] [[PTR]], [[MASK]] +// CHECK-NEXT: [[NEGATED_MASK:%.*]] = xor [[ALIGN_TYPE]] [[MASK]], -1 +// NOTYET-POINTER-NEXT: [[ALIGNED_RESULT:%.*]] = call [[$TYPE]] @llvm.ptrmask.p0[[$PTRTYPE]].p0i8.i64(i8* [[OVER_BOUNDARY]], [[ALIGN_TYPE]] [[NEGATED_MASK]]) +// POINTER-NEXT: [[INTPTR:%.*]] = ptrtoint i8* [[OVER_BOUNDARY]] to [[ALIGN_TYPE]] +// POINTER-NEXT: [[ALIGNED_INTPTR:%.*]] = and [[ALIGN_TYPE]] [[INTPTR]], [[NEGATED_MASK]] +// POINTER-NEXT: [[ALIGNED_RESULT:%.*]] = inttoptr [[ALIGN_TYPE]] [[ALIGNED_INTPTR]] to [[$TYPE]] +// INTEGER-NEXT: [[ALIGNED_RESULT:%.*]] = and [[$TYPE]] [[OVER_BOUNDARY]], [[NEGATED_MASK]] +// CHECK-NEXT: ret [[$TYPE]] [[ALIGNED_RESULT]] +// +TYPE align_up(TYPE ptr, unsigned align) { + return __builtin_align_up(ptr, align); +} + +// CHECK-LABEL: define {{[^@]+}}@align_down +// CHECK-SAME: ([[$TYPE]] {{[^%]*}}[[PTR:%.*]], i32 [[ALIGN:%.*]]) #0 +// CHECK-NEXT: entry: +// ALIGNMENT_EXT-NEXT: [[ALIGNMENT:%.*]] = zext i32 [[ALIGN]] to [[ALIGN_TYPE:i64]] +// ALIGNMENT_TRUNC-NEXT: [[ALIGNMENT:%.*]] = trunc i32 [[ALIGN]] to [[ALIGN_TYPE:i16]] +// CHECK-NEXT: [[MASK:%.*]] = sub [[ALIGN_TYPE]] [[ALIGNMENT]], 1 +// CHECK-NEXT: [[NEGATED_MASK:%.*]] = xor [[ALIGN_TYPE]] [[MASK]], -1 +// NOTYET-POINTER-NEXT: [[ALIGNED_RESULT:%.*]] = call [[$TYPE]] @llvm.ptrmask.p0[[$PTRTYPE]].p0[[$PTRTYPE]].i64([[$TYPE]] [[PTR]], [[ALIGN_TYPE]] [[NEGATED_MASK]]) +// POINTER-NEXT: [[INTPTR:%.*]] = ptrtoint [[$TYPE]] [[PTR]] to [[ALIGN_TYPE]] +// POINTER-NEXT: [[ALIGNED_INTPTR:%.*]] = and [[ALIGN_TYPE]] [[INTPTR]], [[NEGATED_MASK]] +// POINTER-NEXT: [[ALIGNED_RESULT:%.*]] = inttoptr [[ALIGN_TYPE]] [[ALIGNED_INTPTR]] to [[$TYPE]] +// INTEGER-NEXT: [[ALIGNED_RESULT:%.*]] = and [[$TYPE]] [[PTR]], [[NEGATED_MASK]] +// CHECK-NEXT: ret [[$TYPE]] [[ALIGNED_RESULT]] +// +TYPE align_down(TYPE ptr, unsigned align) { + return __builtin_align_down(ptr, align); +} Index: clang/test/CodeGen/builtin-align-array.c =================================================================== --- /dev/null +++ clang/test/CodeGen/builtin-align-array.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +/// Check that the alignment builtins handle array-to-pointer decay +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -o - -emit-llvm %s | FileCheck %s + +extern int func(char *c); + +// CHECK-LABEL: define {{[^@]+}}@test_array() #0 +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca [1024 x i8], align 16 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [1024 x i8], [1024 x i8]* [[BUF]], i64 0, i64 44 +// CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint i8* [[ARRAYIDX]] to i64 +// CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[INTPTR]], -16 +// CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = inttoptr i64 [[ALIGNED_INTPTR]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call i32 @func(i8* [[ALIGNED_RESULT]]) +// CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [1024 x i8], [1024 x i8]* [[BUF]], i64 0, i64 22 +// CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = getelementptr i8, i8* [[ARRAYIDX1]], i64 31 +// CHECK-NEXT: [[INTPTR2:%.*]] = ptrtoint i8* [[OVER_BOUNDARY]] to i64 +// CHECK-NEXT: [[ALIGNED_INTPTR3:%.*]] = and i64 [[INTPTR2]], -32 +// CHECK-NEXT: [[ALIGNED_RESULT4:%.*]] = inttoptr i64 [[ALIGNED_INTPTR3]] to i8* +// CHECK-NEXT: [[CALL5:%.*]] = call i32 @func(i8* [[ALIGNED_RESULT4]]) +// CHECK-NEXT: [[ARRAYIDX6:%.*]] = getelementptr inbounds [1024 x i8], [1024 x i8]* [[BUF]], i64 0, i64 16 +// CHECK-NEXT: [[SRC_ADDR:%.*]] = ptrtoint i8* [[ARRAYIDX6]] to i64 +// CHECK-NEXT: [[SET_BITS:%.*]] = and i64 [[SRC_ADDR]], 63 +// CHECK-NEXT: [[IS_ALIGNED:%.*]] = icmp eq i64 [[SET_BITS]], 0 +// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[IS_ALIGNED]] to i32 +// CHECK-NEXT: ret i32 [[CONV]] +// +int test_array(void) { + char buf[1024]; + func(__builtin_align_down(&buf[44], 16)); + func(__builtin_align_up(&buf[22], 32)); + return __builtin_is_aligned(&buf[16], 64); +} + +// CHECK-LABEL: define {{[^@]+}}@test_array_should_not_mask() #0 +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca [1024 x i8], align 32 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [1024 x i8], [1024 x i8]* [[BUF]], i64 0, i64 64 +// CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint i8* [[ARRAYIDX]] to i64 +// CHECK-NEXT: [[ALIGNED_INTPTR:%.*]] = and i64 [[INTPTR]], -16 +// CHECK-NEXT: [[ALIGNED_RESULT:%.*]] = inttoptr i64 [[ALIGNED_INTPTR]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call i32 @func(i8* [[ALIGNED_RESULT]]) +// CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [1024 x i8], [1024 x i8]* [[BUF]], i64 0, i64 32 +// CHECK-NEXT: [[OVER_BOUNDARY:%.*]] = getelementptr i8, i8* [[ARRAYIDX1]], i64 31 +// CHECK-NEXT: [[INTPTR2:%.*]] = ptrtoint i8* [[OVER_BOUNDARY]] to i64 +// CHECK-NEXT: [[ALIGNED_INTPTR3:%.*]] = and i64 [[INTPTR2]], -32 +// CHECK-NEXT: [[ALIGNED_RESULT4:%.*]] = inttoptr i64 [[ALIGNED_INTPTR3]] to i8* +// CHECK-NEXT: [[CALL5:%.*]] = call i32 @func(i8* [[ALIGNED_RESULT4]]) +// CHECK-NEXT: ret i32 1 +// +int test_array_should_not_mask(void) { + _Alignas(32) char buf[1024]; + // TODO: The align_up and align_down calls should be folded to no-ops + func(__builtin_align_down(&buf[64], 16)); + func(__builtin_align_up(&buf[32], 32)); + // This expression can be constant-evaluated: + return __builtin_is_aligned(&buf[64], 32); +} Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -201,6 +201,87 @@ return false; } +/// Check that the value argument for __builtin_is_aligned(value, alignment) and +/// __builtin_aligned_{up,down}(value, alignment) is an integer or a pointer +/// type (but not a function pointer) and that the alignment is a power-of-two. +static bool SemaBuiltinAlignment(Sema &S, CallExpr *TheCall, unsigned ID) { + if (checkArgCount(S, TheCall, 2)) + return true; + + clang::Expr *Source = TheCall->getArg(0); + bool IsBooleanAlignBuiltin = ID == Builtin::BI__builtin_is_aligned; + + auto IsValidIntegerType = [](QualType Ty) { + return Ty->isIntegerType() && !Ty->isEnumeralType() && !Ty->isBooleanType(); + }; + QualType SrcTy = Source->getType(); + // We should also be able to use it with arrays (but not functions!). + if (SrcTy->canDecayToPointerType() && SrcTy->isArrayType()) { + SrcTy = S.Context.getDecayedType(SrcTy); + } + if ((!SrcTy->isPointerType() && !IsValidIntegerType(SrcTy)) || + SrcTy->isFunctionPointerType()) { + // FIXME: this is not quite the right error message since we don't allow + // floating point types, or member pointers. + S.Diag(Source->getExprLoc(), diag::err_typecheck_expect_scalar_operand) + << SrcTy; + return true; + } + + clang::Expr *AlignOp = TheCall->getArg(1); + if (!IsValidIntegerType(AlignOp->getType())) { + S.Diag(AlignOp->getExprLoc(), diag::err_typecheck_expect_int) + << AlignOp->getType(); + return true; + } + Expr::EvalResult AlignResult; + unsigned MaxAlignmentBits = S.Context.getIntWidth(SrcTy) - 1; + // We can't check validity of alignment if it is type dependent. + if (!AlignOp->isInstantiationDependent() && + AlignOp->EvaluateAsInt(AlignResult, S.Context, + Expr::SE_AllowSideEffects)) { + llvm::APSInt AlignValue = AlignResult.Val.getInt(); + llvm::APSInt MaxValue( + llvm::APInt::getOneBitSet(MaxAlignmentBits + 1, MaxAlignmentBits)); + if (AlignValue < 1) { + S.Diag(AlignOp->getExprLoc(), diag::err_alignment_too_small) << 1; + return true; + } + if (llvm::APSInt::compareValues(AlignValue, MaxValue) > 0) { + S.Diag(AlignOp->getExprLoc(), diag::err_alignment_too_big) + << MaxValue.toString(10); + return true; + } + if (!AlignValue.isPowerOf2()) { + S.Diag(AlignOp->getExprLoc(), diag::err_alignment_not_power_of_two); + return true; + } + if (AlignValue == 1) { + S.Diag(AlignOp->getExprLoc(), diag::warn_alignment_builtin_useless) + << IsBooleanAlignBuiltin; + } + } + + ExprResult SrcArg = S.PerformCopyInitialization( + InitializedEntity::InitializeParameter(S.Context, SrcTy, false), + SourceLocation(), Source); + if (SrcArg.isInvalid()) + return true; + TheCall->setArg(0, SrcArg.get()); + ExprResult AlignArg = + S.PerformCopyInitialization(InitializedEntity::InitializeParameter( + S.Context, AlignOp->getType(), false), + SourceLocation(), AlignOp); + if (AlignArg.isInvalid()) + return true; + TheCall->setArg(1, AlignArg.get()); + // For align_up/align_down, the return type is the same as the (potentially + // decayed) argument type including qualifiers. For is_aligned(), the result + // is always bool. + TheCall->setType(IsBooleanAlignBuiltin ? S.Context.BoolTy : SrcTy); + return false; +} + static bool SemaBuiltinOverflow(Sema &S, CallExpr *TheCall) { if (checkArgCount(S, TheCall, 3)) return true; @@ -1354,6 +1435,12 @@ if (SemaBuiltinAddressof(*this, TheCall)) return ExprError(); break; + case Builtin::BI__builtin_is_aligned: + case Builtin::BI__builtin_align_up: + case Builtin::BI__builtin_align_down: + if (SemaBuiltinAlignment(*this, TheCall, BuiltinID)) + return ExprError(); + break; case Builtin::BI__builtin_add_overflow: case Builtin::BI__builtin_sub_overflow: case Builtin::BI__builtin_mul_overflow: Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -3731,6 +3731,11 @@ /// Emit IR for __builtin_os_log_format. RValue emitBuiltinOSLogFormat(const CallExpr &E); + /// Emit IR for __builtin_is_aligned. + RValue EmitBuiltinIsAligned(const CallExpr *E); + /// Emit IR for __builtin_align_up/__builtin_align_down. + RValue EmitBuiltinAlignTo(const CallExpr *E, bool AlignUp); + llvm::Function *generateBuiltinOSLogHelperFunction( const analyze_os_log::OSLogBufferLayout &Layout, CharUnits BufferAlignment); Index: clang/lib/CodeGen/CGBuiltin.cpp =================================================================== --- clang/lib/CodeGen/CGBuiltin.cpp +++ clang/lib/CodeGen/CGBuiltin.cpp @@ -3501,6 +3501,13 @@ return EmitBuiltinNewDeleteCall( E->getCallee()->getType()->castAs<FunctionProtoType>(), E, true); + case Builtin::BI__builtin_is_aligned: + return EmitBuiltinIsAligned(E); + case Builtin::BI__builtin_align_up: + return EmitBuiltinAlignTo(E, true); + case Builtin::BI__builtin_align_down: + return EmitBuiltinAlignTo(E, false); + case Builtin::BI__noop: // __noop always evaluates to an integer literal zero. return RValue::get(ConstantInt::get(IntTy, 0)); @@ -14248,6 +14255,84 @@ } } +struct BuiltinAlignArgs { + llvm::Value *Src = nullptr; + llvm::Type *SrcType = nullptr; + llvm::Value *Alignment = nullptr; + llvm::Value *Mask = nullptr; + llvm::IntegerType *IntType = nullptr; + + BuiltinAlignArgs(const CallExpr *E, CodeGenFunction &CGF) { + QualType AstType = E->getArg(0)->getType(); + if (AstType->isArrayType()) + Src = CGF.EmitArrayToPointerDecay(E->getArg(0)).getPointer(); + else + Src = CGF.EmitScalarExpr(E->getArg(0)); + SrcType = Src->getType(); + if (SrcType->isPointerTy()) { + IntType = IntegerType::get( + CGF.getLLVMContext(), + CGF.CGM.getDataLayout().getIndexTypeSizeInBits(SrcType)); + } else { + assert(SrcType->isIntegerTy()); + IntType = cast<llvm::IntegerType>(SrcType); + } + Alignment = CGF.EmitScalarExpr(E->getArg(1)); + Alignment = CGF.Builder.CreateZExtOrTrunc(Alignment, IntType, "alignment"); + auto *One = llvm::ConstantInt::get(IntType, 1); + Mask = CGF.Builder.CreateSub(Alignment, One, "mask"); + } +}; + +/// Generate (x & (y-1)) == 0. +RValue CodeGenFunction::EmitBuiltinIsAligned(const CallExpr *E) { + BuiltinAlignArgs Args(E, *this); + llvm::Value *SrcAddress = Args.Src; + if (Args.SrcType->isPointerTy()) + SrcAddress = + Builder.CreateBitOrPointerCast(Args.Src, Args.IntType, "src_addr"); + return RValue::get(Builder.CreateICmpEQ( + Builder.CreateAnd(SrcAddress, Args.Mask, "set_bits"), + llvm::Constant::getNullValue(Args.IntType), "is_aligned")); +} + +/// Generate (x & ~(y-1)) to align down or ((x+(y-1)) & ~(y-1)) to align up. +/// Note: For pointer types we can avoid ptrtoint/inttoptr pairs by using the +/// llvm.ptrmask instrinsic (with a GEP before in the align_up case). +/// TODO: actually use ptrmask once most optimization passes know about it. +RValue CodeGenFunction::EmitBuiltinAlignTo(const CallExpr *E, bool AlignUp) { + BuiltinAlignArgs Args(E, *this); + llvm::Value *SrcForMask = Args.Src; + if (AlignUp) { + // When aligning up we have to first add the mask to ensure we go over the + // next alignment value and then align down to the next valid multiple. + // By adding the mask, we ensure that align_up on an already aligned + // value will not change the value. + if (SrcForMask->getType()->isPointerTy()) { + SrcForMask = EmitCastToVoidPtr(SrcForMask); + SrcForMask = Builder.CreateGEP(SrcForMask, Args.Mask, "over_boundary"); + } else { + SrcForMask = Builder.CreateAdd(SrcForMask, Args.Mask, "over_boundary"); + } + } + // Negate the mask to only clear the lower bits. + llvm::Value *NegatedMask = Builder.CreateNot(Args.Mask, "negated_mask"); + llvm::Value *Result; + if (SrcForMask->getType()->isPointerTy()) { + /// TODO: Use ptrmask instead of ptrtoint/inttoptr + // Result = Builder.CreateIntrinsic( + // Intrinsic::ptrmask, {Args.SrcType, SrcForMask->getType(), Args.IntType}, + // {SrcForMask, NegatedMask}, nullptr, "aligned_result"); + Result = Builder.CreatePtrToInt(SrcForMask, Args.IntType, "intptr"); + Result = Builder.CreateAnd(Result, NegatedMask, "aligned_intptr"); + Result = Builder.CreateIntToPtr(Result, Args.SrcType, "aligned_result"); + } else { + Result = Builder.CreateAnd(SrcForMask, NegatedMask, "aligned_result"); + } + assert(Result->getType() == Args.SrcType); + return RValue::get(Result); +} + Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { switch (BuiltinID) { Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -8150,6 +8150,42 @@ return GetAlignOfType(Info, E->getType(), ExprKind); } +static CharUnits getBaseAlignment(EvalInfo &Info, const LValue &Value) { + if (const auto *VD = Value.Base.dyn_cast<const ValueDecl *>()) + return Info.Ctx.getDeclAlign(VD); + if (const auto *E = Value.Base.dyn_cast<const Expr *>()) + return GetAlignOfExpr(Info, E, UETT_AlignOf); + return GetAlignOfType(Info, Value.Base.getTypeInfoType(), UETT_AlignOf); +} + +/// Evaluate the value of the alignment argument to __builtin_align_{up,down}, +/// __builtin_is_aligned and __builtin_assume_aligned. +static bool getAlignmentArgument(const Expr *E, QualType ForType, + EvalInfo &Info, APSInt &Alignment) { + if (!EvaluateInteger(E, Alignment, Info)) + return false; + if (Alignment < 0 || !Alignment.isPowerOf2()) { + Info.FFDiag(E, diag::note_constexpr_invalid_alignment) << Alignment; + return false; + } + unsigned SrcWidth = Info.Ctx.getIntWidth(ForType); + APSInt MaxValue(APInt::getOneBitSet(SrcWidth, SrcWidth - 1)); + if (APSInt::compareValues(Alignment, MaxValue) > 0) { + Info.FFDiag(E, diag::note_constexpr_alignment_too_big) + << MaxValue << ForType << Alignment; + return false; + } + // Ensure both alignment and source value have the same bit width so that we + // don't assert when computing the resulting value. + APSInt ExtAlignment = + APSInt(Alignment.zextOrTrunc(SrcWidth), /*isUnsigned=*/true); + assert(APSInt::compareValues(Alignment, ExtAlignment) == 0 && + "Alignment should not be changed by ext/trunc"); + Alignment = ExtAlignment; + assert(Alignment.getBitWidth() == SrcWidth); + return true; +} + // To be clear: this happily visits unsupported builtins. Better name welcomed. bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) { if (ExprEvaluatorBaseTy::VisitCallExpr(E)) @@ -8188,7 +8224,8 @@ LValue OffsetResult(Result); APSInt Alignment; - if (!EvaluateInteger(E->getArg(1), Alignment, Info)) + if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info, + Alignment)) return false; CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue()); @@ -8203,16 +8240,7 @@ // If there is a base object, then it must have the correct alignment. if (OffsetResult.Base) { - CharUnits BaseAlignment; - if (const ValueDecl *VD = - OffsetResult.Base.dyn_cast<const ValueDecl*>()) { - BaseAlignment = Info.Ctx.getDeclAlign(VD); - } else if (const Expr *E = OffsetResult.Base.dyn_cast<const Expr *>()) { - BaseAlignment = GetAlignOfExpr(Info, E, UETT_AlignOf); - } else { - BaseAlignment = GetAlignOfType( - Info, OffsetResult.Base.getTypeInfoType(), UETT_AlignOf); - } + CharUnits BaseAlignment = getBaseAlignment(Info, OffsetResult); if (BaseAlignment < Align) { Result.Designator.setInvalid(); @@ -8241,6 +8269,43 @@ return true; } + case Builtin::BI__builtin_align_up: + case Builtin::BI__builtin_align_down: { + if (!evaluatePointer(E->getArg(0), Result)) + return false; + APSInt Alignment; + if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info, + Alignment)) + return false; + CharUnits BaseAlignment = getBaseAlignment(Info, Result); + CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Result.Offset); + // For align_up/align_down, we can return the same value if the alignment + // is known to be greater or equal to the requested value. + if (PtrAlign.getQuantity() >= Alignment) + return true; + + // The alignment could be greater than the minimum at run-time, so we cannot + // infer much about the resulting pointer value. One case is possible: + // For `_Alignas(32) char buf[N]; __builtin_align_down(&buf[idx], 32)` we + // can infer the correct index if the requested alignment is smaller than + // the base alignment so we can perform the computation on the offset. + if (BaseAlignment.getQuantity() >= Alignment) { + assert(Alignment.getBitWidth() <= 64 && + "Cannot handle > 64-bit address-space"); + uint64_t Alignment64 = Alignment.getZExtValue(); + CharUnits NewOffset = CharUnits::fromQuantity( + BuiltinOp == Builtin::BI__builtin_align_down + ? llvm::alignDown(Result.Offset.getQuantity(), Alignment64) + : llvm::alignTo(Result.Offset.getQuantity(), Alignment64)); + Result.adjustOffset(NewOffset - Result.Offset); + // TODO: diagnose out-of-bounds values/only allow for arrays? + return true; + } + // Otherwise, we cannot constant-evaluate the result. + Info.FFDiag(E->getArg(0), diag::note_constexpr_alignment_adjust) + << Alignment; + return false; + } case Builtin::BI__builtin_operator_new: return HandleOperatorNewCall(Info, E, Result); case Builtin::BI__builtin_launder: @@ -10539,6 +10604,33 @@ return ExprEvaluatorBaseTy::VisitCallExpr(E); } +static bool getBuiltinAlignArguments(const CallExpr *E, EvalInfo &Info, + APValue &Val, APSInt &Alignment) { + QualType SrcTy = E->getArg(0)->getType(); + if (!getAlignmentArgument(E->getArg(1), SrcTy, Info, Alignment)) + return false; + // Even though we are evaluating integer expressions we could get a pointer + // argument for the __builtin_is_aligned() case. + if (SrcTy->isPointerType()) { + LValue Ptr; + if (!EvaluatePointer(E->getArg(0), Ptr, Info)) + return false; + Ptr.moveInto(Val); + } else if (!SrcTy->isIntegralOrEnumerationType()) { + Info.FFDiag(E->getArg(0)); + return false; + } else { + APSInt SrcInt; + if (!EvaluateInteger(E->getArg(0), SrcInt, Info)) + return false; + assert(SrcInt.getBitWidth() >= Alignment.getBitWidth() && + "Bit widths must be the same"); + Val = APValue(SrcInt); + } + assert(Val.hasValue()); + return true; +} + bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { switch (unsigned BuiltinOp = E->getBuiltinCallee()) { @@ -10581,6 +10673,66 @@ return Success(Layout.size().getQuantity(), E); } + case Builtin::BI__builtin_is_aligned: { + APValue Src; + APSInt Alignment; + if (!getBuiltinAlignArguments(E, Info, Src, Alignment)) + return false; + if (Src.isLValue()) { + // If we evaluated a pointer, check the minimum known alignment. + LValue Ptr; + Ptr.setFrom(Info.Ctx, Src); + CharUnits BaseAlignment = getBaseAlignment(Info, Ptr); + CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset); + // We can return true if the known alignment at the computed offset is + // greater than the requested alignment. + assert(PtrAlign.isPowerOfTwo()); + assert(Alignment.isPowerOf2()); + if (PtrAlign.getQuantity() >= Alignment) + return Success(1, E); + // If the alignment is not known to be sufficient, some cases could still + // be aligned at run time. However, if the requested alignment is less or + // equal to the base alignment and the offset is not aligned, we know that + // the run-time value can never be aligned. + if (BaseAlignment.getQuantity() >= Alignment && + PtrAlign.getQuantity() < Alignment) + return Success(0, E); + // Otherwise we can't infer whether the value is sufficiently aligned. + // TODO: __builtin_is_aligned(__builtin_align_{down,up{(expr, N), N) + // in cases where we can't fully evaluate the pointer. + Info.FFDiag(E->getArg(0), diag::note_constexpr_alignment_compute) + << Alignment; + return false; + } + assert(Src.isInt()); + return Success((Src.getInt() & (Alignment - 1)) == 0 ? 1 : 0, E); + } + case Builtin::BI__builtin_align_up: { + APValue Src; + APSInt Alignment; + if (!getBuiltinAlignArguments(E, Info, Src, Alignment)) + return false; + if (!Src.isInt()) + return Error(E); + APSInt AlignedVal = + APSInt((Src.getInt() + (Alignment - 1)) & ~(Alignment - 1), + Src.getInt().isUnsigned()); + assert(AlignedVal.getBitWidth() == Src.getInt().getBitWidth()); + return Success(AlignedVal, E); + } + case Builtin::BI__builtin_align_down: { + APValue Src; + APSInt Alignment; + if (!getBuiltinAlignArguments(E, Info, Src, Alignment)) + return false; + if (!Src.isInt()) + return Error(E); + APSInt AlignedVal = + APSInt(Src.getInt() & ~(Alignment - 1), Src.getInt().isUnsigned()); + assert(AlignedVal.getBitWidth() == Src.getInt().getBitWidth()); + return Success(AlignedVal, E); + } + case Builtin::BI__builtin_bswap16: case Builtin::BI__builtin_bswap32: case Builtin::BI__builtin_bswap64: { Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2909,6 +2909,9 @@ def err_alignment_dependent_typedef_name : Error< "requested alignment is dependent but declaration is not dependent">; +def warn_alignment_builtin_useless : Warning< + "%select{aligning a value|the result of checking whether a value is aligned}0" + " to 1 byte is %select{a no-op|always true}0">, InGroup<TautologicalCompare>; def err_attribute_aligned_too_great : Error< "requested alignment must be %0 bytes or smaller">; def warn_assume_aligned_too_great Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -216,6 +216,14 @@ def note_constexpr_baa_value_insufficient_alignment : Note< "value of the aligned pointer (%0) is not a multiple of the asserted %1 " "%plural{1:byte|:bytes}1">; +def note_constexpr_invalid_alignment : Note< + "requested alignment %0 is not a positive power of two">; +def note_constexpr_alignment_too_big : Note< + "requested alignment must be %0 or less for type %1; %2 is invalid">; +def note_constexpr_alignment_compute : Note< + "cannot constant evaluate whether run-time alignment is at least %0">; +def note_constexpr_alignment_adjust : Note< + "cannot constant evaluate the result of adjusting alignment to %0">; def note_constexpr_destroy_out_of_lifetime : Note< "destroying object '%0' whose lifetime has already ended">; def note_constexpr_unsupported_destruction : Note< Index: clang/include/clang/Basic/Builtins.def =================================================================== --- clang/include/clang/Basic/Builtins.def +++ clang/include/clang/Basic/Builtins.def @@ -1475,6 +1475,11 @@ BUILTIN(__builtin_dump_struct, "ivC*v*", "tn") BUILTIN(__builtin_preserve_access_index, "v.", "t") +// Alignment builtins (uses custom parsing to support pointers and integers) +BUILTIN(__builtin_is_aligned, "bvC*z", "nct") +BUILTIN(__builtin_align_up, "v*vC*z", "nct") +BUILTIN(__builtin_align_down, "v*vC*z", "nct") + // Safestack builtins BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn") BUILTIN(__builtin___get_unsafe_stack_bottom, "v*", "Fn") Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -2788,6 +2788,73 @@ When the invocation point of ``__builtin_FUNCTION`` is not a function scope the empty string is returned. +Alignment builtins +------------------ +Clang provides builtins to support checking and adjusting alignment of +pointers and integers. +These builtins can be used to avoid relying on implementation-defined behavior +of arithmetic on integers derived from pointers. +Additionally, these builtins retain type information and, unlike bitwise +arithmentic, they can perform semantic checking on the alignment value. + +**Syntax**: + +.. code-block:: c + + Type __builtin_align_up(Type value, size_t alignment); + Type __builtin_align_down(Type value, size_t alignment); + bool __builtin_is_aligned(Type value, size_t alignment); + + +**Example of use**: + +.. code-block:: c++ + + char* global_alloc_buffer; + void* my_aligned_allocator(size_t alloc_size, size_t alignment) { + char* result = __builtin_align_up(global_alloc_buffer, alignment); + // result now contains the value of global_alloc_buffer rounded up to the + // next multiple of alignment. + global_alloc_buffer = result + alloc_size; + return result; + } + + void* get_start_of_page(void* ptr) { + return __builtin_align_down(ptr, PAGE_SIZE); + } + + void example(char* buffer) { + if (__builtin_is_aligned(buffer, 64)) { + do_fast_aligned_copy(buffer); + } else { + do_unaligned_copy(buffer); + } + } + + // In addition to pointers, the builtins can also be used on integer types + // and are evaluatable inside constant expressions. + static_assert(__builtin_align_up(123, 64) == 128, ""); + static_assert(__builtin_align_down(123u, 64) == 64u, ""); + static_assert(!__builtin_is_aligned(123, 64), ""); + + +**Description**: + +The builtins ``__builtin_align_up``, ``__builtin_align_down``, return their +first argument aligned up/down to the next multiple of the second argument. +The builtin ``__builtin_is_aligned`` returns whether the first argument is +aligned to a multiple of the second argument. +All of these builtins expect the alignment to be expressed as a number of bytes. + +These builtins can be used for all integer types as well as (non-function) +pointer types. For pointer types, these builtins operate in terms of the integer +address of the pointer and return a new pointer of the same type (including +qualifiers such as ``const``) with an adjusted address. + +If Clang can determine that the alignment is not a power of two at compile time, +it will result in a compilation failure. If the alignment argument is not a +power of two at run time, the behavior of these builtins is undefined. + Non-standard C++11 Attributes =============================
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits