C23 adds a memalignment function to determine the alignment of a pointer.
Given how simple it is (convert pointer to an integer then return (p & -p)),
GCC should support expanding it inline.
This patch implements __builtin_memalignment which expands to p & -p
gcc/ChangeLog:
PR middle-end/122117
* builtin-types.def (BT_FN_SIZE_CONST_PTR): New function type.
* builtins.def (BUILT_IN_MEMALIGNMENT): New C23 builtin.
* builtins.cc (fold_builtin_memalignment): New function to fold
__builtin_memalignment to (size_t)ptr & -(size_t)ptr.
(fold_builtin_1): Handle BUILT_IN_MEMALIGNMENT.
(is_inexpensive_builtin): Add BUILT_IN_MEMALIGNMENT.
gcc/testsuite/ChangeLog:
* gcc.dg/builtin-memalignment.c: New test.
* gcc.dg/builtin-memalignment-1.c: New test.
Signed-off-by: Peter Damianov <[email protected]>
---
v3: Fix bug with side effect arguments happening twice.
Add testcase covering it.
gcc/builtin-types.def | 1 +
gcc/builtins.cc | 32 +++++
gcc/builtins.def | 1 +
gcc/testsuite/gcc.dg/builtin-memalignment-1.c | 30 ++++
gcc/testsuite/gcc.dg/builtin-memalignment.c | 134 ++++++++++++++++++
5 files changed, 198 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/builtin-memalignment-1.c
create mode 100644 gcc/testsuite/gcc.dg/builtin-memalignment.c
diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 9583d30dfc0..eecb30fd12d 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -360,6 +360,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_LONGLONG_FLOAT32X, BT_LONGLONG,
BT_FLOAT32X)
DEF_FUNCTION_TYPE_1 (BT_FN_LONGLONG_FLOAT64X, BT_LONGLONG, BT_FLOAT64X)
DEF_FUNCTION_TYPE_1 (BT_FN_LONGLONG_FLOAT128X, BT_LONGLONG, BT_FLOAT128X)
DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTR, BT_VOID, BT_PTR)
+DEF_FUNCTION_TYPE_1 (BT_FN_SIZE_CONST_PTR, BT_SIZE, BT_CONST_PTR)
DEF_FUNCTION_TYPE_1 (BT_FN_SIZE_CONST_STRING, BT_SIZE, BT_CONST_STRING)
DEF_FUNCTION_TYPE_1 (BT_FN_INT_CONST_STRING, BT_INT, BT_CONST_STRING)
DEF_FUNCTION_TYPE_1 (BT_FN_PTR_PTR, BT_PTR, BT_PTR)
diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index fb294ce58cd..f0b1665b1fe 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -172,6 +172,7 @@ static tree fold_builtin_abs (location_t, tree, tree);
static tree fold_builtin_unordered_cmp (location_t, tree, tree, tree, enum
tree_code,
enum tree_code);
static tree fold_builtin_iseqsig (location_t, tree, tree);
+static tree fold_builtin_memalignment (location_t, tree);
static tree fold_builtin_varargs (location_t, tree, tree*, int);
static tree fold_builtin_strpbrk (location_t, tree, tree, tree, tree);
@@ -10115,6 +10116,33 @@ fold_builtin_iseqsig (location_t loc, tree arg0, tree
arg1)
return fold_build2_loc (loc, TRUTH_AND_EXPR, integer_type_node, cmp1, cmp2);
}
+/* Fold a call to __builtin_memalignment(). ARG is the pointer argument.
+ The code is folded to: (size_t)ARG & -(size_t)ARG */
+
+static tree
+fold_builtin_memalignment (location_t loc, tree arg)
+{
+ tree ptr_as_size, negated, result;
+
+ if (!validate_arg (arg, POINTER_TYPE))
+ return NULL_TREE;
+
+ /* Save ARG to ensure side effects occur only once. */
+ arg = builtin_save_expr (arg);
+
+ /* Convert pointer to size_t. */
+ ptr_as_size = fold_convert_loc (loc, size_type_node, arg);
+
+ /* Negate: -(size_t)ARG */
+ negated = fold_build1_loc (loc, NEGATE_EXPR, size_type_node, ptr_as_size);
+
+ /* Compute: (size_t)ARG & -(size_t)ARG */
+ result = fold_build2_loc (loc, BIT_AND_EXPR, size_type_node,
+ ptr_as_size, negated);
+
+ return result;
+}
+
/* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
arithmetics if it can never overflow, or into internal functions that
return both result of arithmetics and overflowed boolean flag in
@@ -10784,6 +10812,9 @@ fold_builtin_1 (location_t loc, tree expr, tree fndecl,
tree arg0)
case BUILT_IN_POPCOUNTG:
return fold_builtin_bit_query (loc, fcode, arg0, NULL_TREE);
+ case BUILT_IN_MEMALIGNMENT:
+ return fold_builtin_memalignment (loc, arg0);
+
default:
break;
}
@@ -12395,6 +12426,7 @@ is_inexpensive_builtin (tree decl)
case BUILT_IN_PARITYLL:
case BUILT_IN_PARITYIMAX:
case BUILT_IN_PARITY:
+ case BUILT_IN_MEMALIGNMENT:
case BUILT_IN_LABS:
case BUILT_IN_LLABS:
case BUILT_IN_PREFETCH:
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 00cfb6a2057..036c79cbd8d 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -1023,6 +1023,7 @@ DEF_GCC_BUILTIN (BUILT_IN_BSWAP16, "bswap16",
BT_FN_UINT16_UINT16, ATTR_C
DEF_GCC_BUILTIN (BUILT_IN_BSWAP32, "bswap32", BT_FN_UINT32_UINT32,
ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_BSWAP64, "bswap64", BT_FN_UINT64_UINT64,
ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_BSWAP128, "bswap128", BT_FN_UINT128_UINT128,
ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_C23_BUILTIN (BUILT_IN_MEMALIGNMENT, "memalignment",
BT_FN_SIZE_CONST_PTR, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_CLEAR_CACHE, "__clear_cache",
BT_FN_VOID_PTR_PTR, ATTR_NOTHROW_LEAF_LIST)
/* [trans-mem]: Adjust BUILT_IN_TM_CALLOC if BUILT_IN_CALLOC is changed. */
diff --git a/gcc/testsuite/gcc.dg/builtin-memalignment-1.c
b/gcc/testsuite/gcc.dg/builtin-memalignment-1.c
new file mode 100644
index 00000000000..09d81f6ee3a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-memalignment-1.c
@@ -0,0 +1,30 @@
+/* Compile-time constant folding test for __builtin_memalignment */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test that constant expressions fold to constants */
+__SIZE_TYPE__ test_const_16 (void)
+{
+ return __builtin_memalignment ((void *)16);
+}
+
+__SIZE_TYPE__ test_const_24 (void)
+{
+ return __builtin_memalignment ((void *)24);
+}
+
+__SIZE_TYPE__ test_const_0 (void)
+{
+ return __builtin_memalignment ((void *)0);
+}
+
+__SIZE_TYPE__ test_const_48 (void)
+{
+ return __builtin_memalignment ((void *)48);
+}
+
+/* Verify that all calls are folded to constants and the builtin is eliminated
*/
+/* { dg-final { scan-tree-dump-not "__builtin_memalignment" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "return 16;" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump "return 8;" "optimized" } } */
+/* { dg-final { scan-tree-dump "return 0;" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-memalignment.c
b/gcc/testsuite/gcc.dg/builtin-memalignment.c
new file mode 100644
index 00000000000..5557abdbfb4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-memalignment.c
@@ -0,0 +1,134 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+extern void abort (void);
+
+static int call_count = 0;
+static int side_effect_var;
+
+static int *get_ptr_with_side_effect (void)
+{
+ call_count++;
+ return &side_effect_var;
+}
+
+void test_alignment (void *ptr, __SIZE_TYPE__ expected)
+{
+ __SIZE_TYPE__ result = __builtin_memalignment (ptr);
+ if (result != expected)
+ abort ();
+}
+
+/* Verify that constant folding happens */
+void test_constant_p (void)
+{
+ /* These should be compile-time constants */
+ if (!__builtin_constant_p (__builtin_memalignment ((void *)16)))
+ abort ();
+ if (!__builtin_constant_p (__builtin_memalignment ((void *)24)))
+ abort ();
+ if (!__builtin_constant_p (__builtin_memalignment ((void *)0)))
+ abort ();
+}
+
+int main (void)
+{
+ /* Test with null pointer - alignment should be 0 */
+ test_alignment ((void *)0, 0);
+
+ /* Test powers of 2 alignments */
+ test_alignment ((void *)1, 1);
+ test_alignment ((void *)2, 2);
+ test_alignment ((void *)4, 4);
+ test_alignment ((void *)8, 8);
+ test_alignment ((void *)16, 16);
+ test_alignment ((void *)32, 32);
+ test_alignment ((void *)64, 64);
+ test_alignment ((void *)128, 128);
+ test_alignment ((void *)256, 256);
+
+ /* Test non-power-of-2 addresses */
+ test_alignment ((void *)3, 1); /* 0b11 -> alignment 1 */
+ test_alignment ((void *)5, 1); /* 0b101 -> alignment 1 */
+ test_alignment ((void *)6, 2); /* 0b110 -> alignment 2 */
+ test_alignment ((void *)12, 4); /* 0b1100 -> alignment 4 */
+ test_alignment ((void *)24, 8); /* 0b11000 -> alignment 8 */
+ test_alignment ((void *)48, 16); /* 0b110000 -> alignment 16 */
+
+ /* Verify constant folding happens */
+ test_constant_p ();
+
+ /* Test with variables having different alignments */
+ char c __attribute__((aligned(1)));
+ short s __attribute__((aligned(2)));
+ int i __attribute__((aligned(4)));
+ long long ll __attribute__((aligned(8)));
+ char a16 __attribute__((aligned(16)));
+ char a32 __attribute__((aligned(32)));
+ char a64 __attribute__((aligned(64)));
+
+ __SIZE_TYPE__ align_c = __builtin_memalignment(&c);
+ __SIZE_TYPE__ align_s = __builtin_memalignment(&s);
+ __SIZE_TYPE__ align_i = __builtin_memalignment(&i);
+ __SIZE_TYPE__ align_ll = __builtin_memalignment(&ll);
+ __SIZE_TYPE__ align_a16 = __builtin_memalignment(&a16);
+ __SIZE_TYPE__ align_a32 = __builtin_memalignment(&a32);
+ __SIZE_TYPE__ align_a64 = __builtin_memalignment(&a64);
+
+ /* The alignment should be at least what we requested */
+ if (align_c < 1)
+ abort();
+ if (align_s < 2)
+ abort();
+ if (align_i < 4)
+ abort();
+ if (align_ll < 8)
+ abort();
+ if (align_a16 < 16)
+ abort();
+ if (align_a32 < 32)
+ abort();
+ if (align_a64 < 64)
+ abort();
+
+ /* Test array elements */
+ int array[16] __attribute__((aligned(64)));
+
+ /* First element should have the array's alignment */
+ if (__builtin_memalignment(&array[0]) < 64)
+ abort();
+
+ /* Other elements depend on their offset */
+ /* array[1] is at offset 4, so alignment is 4 */
+ if (__builtin_memalignment(&array[1]) != 4)
+ abort();
+
+ /* array[2] is at offset 8, so alignment is 8 */
+ if (__builtin_memalignment(&array[2]) != 8)
+ abort();
+
+ /* array[4] is at offset 16, so alignment is 16 */
+ if (__builtin_memalignment(&array[4]) != 16)
+ abort();
+
+ /* Test that side effects occur exactly once */
+ call_count = 0;
+ __SIZE_TYPE__ align = __builtin_memalignment(get_ptr_with_side_effect());
+ if (call_count != 1)
+ abort(); /* Side effect should happen exactly once */
+ if (align < 4)
+ abort();
+
+ /* Test with post-increment */
+ int *p = &array[0];
+ align = __builtin_memalignment(p++);
+ if (p != &array[1])
+ abort(); /* Post-increment should happen */
+ if (align < 64)
+ abort();
+
+ return 0;
+}
+
+/* Verify that the builtin is eliminated and replaced with the inline
computation */
+/* { dg-final { scan-tree-dump-not "__builtin_memalignment" "optimized" } } */
--
2.47.3