Alex Richardson | 8c387cb | 2020-01-09 20:48:06 +0000 | [diff] [blame] | 1 | // C++-specific checks for the alignment builtins |
| 2 | // RUN: %clang_cc1 -triple=x86_64-unknown-unknown -std=c++11 -o - %s -fsyntax-only -verify |
| 3 | |
| 4 | // Check that we don't crash when using dependent types in __builtin_align: |
| 5 | template <typename a, a b> |
| 6 | void *c(void *d) { // expected-note{{candidate template ignored}} |
| 7 | return __builtin_align_down(d, b); |
| 8 | } |
| 9 | |
| 10 | struct x {}; |
| 11 | x foo; |
| 12 | void test(void *value) { |
| 13 | c<int, 16>(value); |
| 14 | c<struct x, foo>(value); // expected-error{{no matching function for call to 'c'}} |
| 15 | } |
| 16 | |
| 17 | template <typename T, long Alignment, long ArraySize = 16> |
| 18 | void test_templated_arguments() { |
| 19 | T array[ArraySize]; // expected-error{{variable has incomplete type 'fwddecl'}} |
| 20 | static_assert(__is_same(decltype(__builtin_align_up(array, Alignment)), T *), // expected-error{{requested alignment is not a power of 2}} |
| 21 | "return type should be the decayed array type"); |
| 22 | static_assert(__is_same(decltype(__builtin_align_down(array, Alignment)), T *), |
| 23 | "return type should be the decayed array type"); |
| 24 | static_assert(__is_same(decltype(__builtin_is_aligned(array, Alignment)), bool), |
| 25 | "return type should be bool"); |
| 26 | T *x1 = __builtin_align_up(array, Alignment); |
| 27 | T *x2 = __builtin_align_down(array, Alignment); |
| 28 | bool x3 = __builtin_align_up(array, Alignment); |
| 29 | } |
| 30 | |
| 31 | void test() { |
| 32 | test_templated_arguments<int, 32>(); // fine |
| 33 | test_templated_arguments<struct fwddecl, 16>(); |
| 34 | // expected-note@-1{{in instantiation of function template specialization 'test_templated_arguments<fwddecl, 16, 16>'}} |
| 35 | // expected-note@-2{{forward declaration of 'fwddecl'}} |
| 36 | test_templated_arguments<int, 7>(); // invalid alignment value |
| 37 | // expected-note@-1{{in instantiation of function template specialization 'test_templated_arguments<int, 7, 16>'}} |
| 38 | } |
| 39 | |
| 40 | template <typename T> |
| 41 | void test_incorrect_alignment_without_instatiation(T value) { |
| 42 | int array[32]; |
| 43 | static_assert(__is_same(decltype(__builtin_align_up(array, 31)), int *), // expected-error{{requested alignment is not a power of 2}} |
| 44 | "return type should be the decayed array type"); |
| 45 | static_assert(__is_same(decltype(__builtin_align_down(array, 7)), int *), // expected-error{{requested alignment is not a power of 2}} |
| 46 | "return type should be the decayed array type"); |
| 47 | static_assert(__is_same(decltype(__builtin_is_aligned(array, -1)), bool), // expected-error{{requested alignment must be 1 or greater}} |
| 48 | "return type should be bool"); |
| 49 | __builtin_align_up(array); // expected-error{{too few arguments to function call, expected 2, have 1}} |
| 50 | __builtin_align_up(array, 31); // expected-error{{requested alignment is not a power of 2}} |
| 51 | __builtin_align_down(array, 31); // expected-error{{requested alignment is not a power of 2}} |
| 52 | __builtin_align_up(array, 31); // expected-error{{requested alignment is not a power of 2}} |
| 53 | __builtin_align_up(value, 31); // This shouldn't want since the type is dependent |
| 54 | __builtin_align_up(value); // Same here |
| 55 | } |
| 56 | |
| 57 | // The original fix for the issue above broke some legitimate code. |
| 58 | // Here is a regression test: |
| 59 | typedef __SIZE_TYPE__ size_t; |
| 60 | void *allocate_impl(size_t size); |
| 61 | template <typename T> |
| 62 | T *allocate() { |
| 63 | constexpr size_t allocation_size = |
| 64 | __builtin_align_up(sizeof(T), sizeof(void *)); |
| 65 | return static_cast<T *>( |
| 66 | __builtin_assume_aligned(allocate_impl(allocation_size), sizeof(void *))); |
| 67 | } |
| 68 | struct Foo { |
| 69 | int value; |
| 70 | }; |
| 71 | void *test2() { |
| 72 | return allocate<struct Foo>(); |
| 73 | } |
| 74 | |
| 75 | // Check that pointers-to-members cannot be used: |
| 76 | class MemPtr { |
| 77 | public: |
| 78 | int data; |
| 79 | void func(); |
| 80 | virtual void vfunc(); |
| 81 | }; |
| 82 | void test_member_ptr() { |
| 83 | __builtin_align_up(&MemPtr::data, 64); // expected-error{{operand of type 'int MemPtr::*' where arithmetic or pointer type is required}} |
| 84 | __builtin_align_down(&MemPtr::func, 64); // expected-error{{operand of type 'void (MemPtr::*)()' where arithmetic or pointer type is required}} |
| 85 | __builtin_is_aligned(&MemPtr::vfunc, 64); // expected-error{{operand of type 'void (MemPtr::*)()' where arithmetic or pointer type is required}} |
| 86 | } |
| 87 | |
| 88 | void test_references(Foo &i) { |
| 89 | // Check that the builtins look at the referenced type rather than the reference itself. |
| 90 | (void)__builtin_align_up(i, 64); // expected-error{{operand of type 'Foo' where arithmetic or pointer type is required}} |
| 91 | (void)__builtin_align_up(static_cast<Foo &>(i), 64); // expected-error{{operand of type 'Foo' where arithmetic or pointer type is required}} |
| 92 | (void)__builtin_align_up(static_cast<const Foo &>(i), 64); // expected-error{{operand of type 'const Foo' where arithmetic or pointer type is required}} |
| 93 | (void)__builtin_align_up(static_cast<Foo &&>(i), 64); // expected-error{{operand of type 'Foo' where arithmetic or pointer type is required}} |
| 94 | (void)__builtin_align_up(static_cast<const Foo &&>(i), 64); // expected-error{{operand of type 'const Foo' where arithmetic or pointer type is required}} |
| 95 | (void)__builtin_align_up(&i, 64); |
| 96 | } |
| 97 | |
| 98 | // Check that constexpr wrapper functions can be constant-evaluated. |
| 99 | template <typename T> |
| 100 | constexpr bool wrap_is_aligned(T ptr, long align) { |
| 101 | return __builtin_is_aligned(ptr, align); |
| 102 | // expected-note@-1{{requested alignment -3 is not a positive power of two}} |
| 103 | // expected-note@-2{{requested alignment 19 is not a positive power of two}} |
| 104 | // expected-note@-3{{requested alignment must be 128 or less for type 'char'; 4194304 is invalid}} |
| 105 | } |
| 106 | template <typename T> |
| 107 | constexpr T wrap_align_up(T ptr, long align) { |
| 108 | return __builtin_align_up(ptr, align); |
| 109 | // expected-note@-1{{requested alignment -2 is not a positive power of two}} |
| 110 | // expected-note@-2{{requested alignment 18 is not a positive power of two}} |
| 111 | // expected-note@-3{{requested alignment must be 2147483648 or less for type 'int'; 8589934592 is invalid}} |
| 112 | // expected-error@-4{{operand of type 'bool' where arithmetic or pointer type is required}} |
| 113 | } |
| 114 | |
| 115 | template <typename T> |
| 116 | constexpr T wrap_align_down(T ptr, long align) { |
| 117 | return __builtin_align_down(ptr, align); |
| 118 | // expected-note@-1{{requested alignment -1 is not a positive power of two}} |
| 119 | // expected-note@-2{{requested alignment 17 is not a positive power of two}} |
| 120 | // expected-note@-3{{requested alignment must be 32768 or less for type 'short'; 1048576 is invalid}} |
| 121 | } |
| 122 | |
| 123 | constexpr int a1 = wrap_align_up(22, 32); |
| 124 | static_assert(a1 == 32, ""); |
| 125 | constexpr int a2 = wrap_align_down(22, 16); |
| 126 | static_assert(a2 == 16, ""); |
| 127 | constexpr bool a3 = wrap_is_aligned(22, 32); |
| 128 | static_assert(!a3, ""); |
| 129 | static_assert(wrap_align_down(wrap_align_up(22, 16), 32) == 32, ""); |
| 130 | static_assert(wrap_is_aligned(wrap_align_down(wrap_align_up(22, 16), 32), 32), ""); |
| 131 | static_assert(!wrap_is_aligned(wrap_align_down(wrap_align_up(22, 16), 32), 64), ""); |
| 132 | |
| 133 | constexpr long const_value(long l) { return l; } |
| 134 | // Check some invalid values during constant-evaluation |
| 135 | static_assert(wrap_align_down(1, const_value(-1)), ""); // expected-error{{not an integral constant expression}} |
| 136 | // expected-note@-1{{in call to 'wrap_align_down(1, -1)'}} |
| 137 | static_assert(wrap_align_up(1, const_value(-2)), ""); // expected-error{{not an integral constant expression}} |
| 138 | // expected-note@-1{{in call to 'wrap_align_up(1, -2)'}} |
| 139 | static_assert(wrap_is_aligned(1, const_value(-3)), ""); // expected-error{{not an integral constant expression}} |
| 140 | // expected-note@-1{{in call to 'wrap_is_aligned(1, -3)'}} |
| 141 | static_assert(wrap_align_down(1, const_value(17)), ""); // expected-error{{not an integral constant expression}} |
| 142 | // expected-note@-1{{in call to 'wrap_align_down(1, 17)'}} |
| 143 | static_assert(wrap_align_up(1, const_value(18)), ""); // expected-error{{not an integral constant expression}} |
| 144 | // expected-note@-1{{in call to 'wrap_align_up(1, 18)'}} |
| 145 | static_assert(wrap_is_aligned(1, const_value(19)), ""); // expected-error{{not an integral constant expression}} |
| 146 | // expected-note@-1{{in call to 'wrap_is_aligned(1, 19)'}} |
| 147 | |
| 148 | // Check invalid values for smaller types: |
| 149 | static_assert(wrap_align_down(static_cast<short>(1), const_value(1 << 20)), ""); // expected-error{{not an integral constant expression}} |
| 150 | // expected-note@-1{{in call to 'wrap_align_down(1, 1048576)'}} |
| 151 | // Check invalid boolean type |
| 152 | static_assert(wrap_align_up(static_cast<int>(1), const_value(1ull << 33)), ""); // expected-error{{not an integral constant expression}} |
| 153 | // expected-note@-1{{in call to 'wrap_align_up(1, 8589934592)'}} |
| 154 | static_assert(wrap_is_aligned(static_cast<char>(1), const_value(1 << 22)), ""); // expected-error{{not an integral constant expression}} |
| 155 | // expected-note@-1{{in call to 'wrap_is_aligned(1, 4194304)'}} |
| 156 | |
| 157 | // Check invalid boolean type |
| 158 | static_assert(wrap_align_up(static_cast<bool>(1), const_value(1 << 21)), ""); // expected-error{{not an integral constant expression}} |
| 159 | // expected-note@-1{{in instantiation of function template specialization 'wrap_align_up<bool>' requested here}} |
| 160 | |
| 161 | // Check constant evaluation for pointers: |
| 162 | _Alignas(32) char align32array[128]; |
| 163 | static_assert(&align32array[0] == &align32array[0], ""); |
| 164 | // __builtin_align_up/down can be constant evaluated as a no-op for values |
| 165 | // that are known to have greater alignment: |
| 166 | static_assert(__builtin_align_up(&align32array[0], 32) == &align32array[0], ""); |
| 167 | static_assert(__builtin_align_up(&align32array[0], 4) == &align32array[0], ""); |
| 168 | static_assert(__builtin_align_down(&align32array[0], 4) == __builtin_align_up(&align32array[0], 8), ""); |
| 169 | // But it can not be evaluated if the alignment is greater than the minimum |
| 170 | // known alignment, since in that case the value might be the same if it happens |
| 171 | // to actually be aligned to 64 bytes at run time. |
| 172 | static_assert(&align32array[0] == __builtin_align_up(&align32array[0], 64), ""); // expected-error{{not an integral constant expression}} |
| 173 | // expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} |
| 174 | static_assert(__builtin_align_up(&align32array[0], 64) == __builtin_align_up(&align32array[0], 64), ""); // expected-error{{not an integral constant expression}} |
| 175 | // expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} |
| 176 | |
| 177 | // However, we can compute in case the requested alignment is less than the |
| 178 | // base alignment: |
| 179 | static_assert(__builtin_align_up(&align32array[0], 4) == &align32array[0], ""); |
| 180 | static_assert(__builtin_align_up(&align32array[1], 4) == &align32array[4], ""); |
| 181 | static_assert(__builtin_align_up(&align32array[2], 4) == &align32array[4], ""); |
| 182 | static_assert(__builtin_align_up(&align32array[3], 4) == &align32array[4], ""); |
| 183 | static_assert(__builtin_align_up(&align32array[4], 4) == &align32array[4], ""); |
| 184 | static_assert(__builtin_align_up(&align32array[5], 4) == &align32array[8], ""); |
| 185 | static_assert(__builtin_align_up(&align32array[6], 4) == &align32array[8], ""); |
| 186 | static_assert(__builtin_align_up(&align32array[7], 4) == &align32array[8], ""); |
| 187 | static_assert(__builtin_align_up(&align32array[8], 4) == &align32array[8], ""); |
| 188 | |
| 189 | static_assert(__builtin_align_down(&align32array[0], 4) == &align32array[0], ""); |
| 190 | static_assert(__builtin_align_down(&align32array[1], 4) == &align32array[0], ""); |
| 191 | static_assert(__builtin_align_down(&align32array[2], 4) == &align32array[0], ""); |
| 192 | static_assert(__builtin_align_down(&align32array[3], 4) == &align32array[0], ""); |
| 193 | static_assert(__builtin_align_down(&align32array[4], 4) == &align32array[4], ""); |
| 194 | static_assert(__builtin_align_down(&align32array[5], 4) == &align32array[4], ""); |
| 195 | static_assert(__builtin_align_down(&align32array[6], 4) == &align32array[4], ""); |
| 196 | static_assert(__builtin_align_down(&align32array[7], 4) == &align32array[4], ""); |
| 197 | static_assert(__builtin_align_down(&align32array[8], 4) == &align32array[8], ""); |
| 198 | |
| 199 | // Achiving the same thing using casts to uintptr_t is not allowed: |
| 200 | static_assert((char *)((__UINTPTR_TYPE__)&align32array[7] & ~3) == &align32array[4], ""); // expected-error{{not an integral constant expression}} |
| 201 | |
| 202 | static_assert(__builtin_align_down(&align32array[1], 4) == &align32array[0], ""); |
| 203 | static_assert(__builtin_align_down(&align32array[1], 64) == &align32array[0], ""); // expected-error{{not an integral constant expression}} |
| 204 | // expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} |
| 205 | |
| 206 | // Add some checks for __builtin_is_aligned: |
| 207 | static_assert(__builtin_is_aligned(&align32array[0], 32), ""); |
| 208 | static_assert(__builtin_is_aligned(&align32array[4], 4), ""); |
| 209 | // We cannot constant evaluate whether the array is aligned to > 32 since this |
| 210 | // may well be true at run time. |
| 211 | static_assert(!__builtin_is_aligned(&align32array[0], 64), ""); // expected-error{{not an integral constant expression}} |
| 212 | // expected-note@-1{{cannot constant evaluate whether run-time alignment is at least 64}} |
| 213 | |
| 214 | // However, if the alignment being checked is less than the minimum alignment of |
| 215 | // the base object we can check the low bits of the alignment: |
| 216 | static_assert(__builtin_is_aligned(&align32array[0], 4), ""); |
| 217 | static_assert(!__builtin_is_aligned(&align32array[1], 4), ""); |
| 218 | static_assert(!__builtin_is_aligned(&align32array[2], 4), ""); |
| 219 | static_assert(!__builtin_is_aligned(&align32array[3], 4), ""); |
| 220 | static_assert(__builtin_is_aligned(&align32array[4], 4), ""); |
| 221 | |
| 222 | // TODO: this should evaluate to true even though we can't evaluate the result |
| 223 | // of __builtin_align_up() to a concrete value |
| 224 | static_assert(__builtin_is_aligned(__builtin_align_up(&align32array[0], 64), 64), ""); // expected-error{{not an integral constant expression}} |
| 225 | // expected-note@-1{{cannot constant evaluate the result of adjusting alignment to 64}} |
| 226 | |
| 227 | // Check different source and alignment type widths are handled correctly. |
| 228 | static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<signed short>(4)), ""); |
| 229 | static_assert(!__builtin_is_aligned(static_cast<signed short>(7), static_cast<signed long>(4)), ""); |
| 230 | // Also check signed -- unsigned mismatch. |
| 231 | static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<signed long>(4)), ""); |
| 232 | static_assert(!__builtin_is_aligned(static_cast<unsigned long>(7), static_cast<unsigned long>(4)), ""); |
| 233 | static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<unsigned long>(4)), ""); |
| 234 | static_assert(!__builtin_is_aligned(static_cast<unsigned long>(7), static_cast<signed long>(4)), ""); |
| 235 | static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<unsigned short>(4)), ""); |
| 236 | static_assert(!__builtin_is_aligned(static_cast<unsigned short>(7), static_cast<signed long>(4)), ""); |