On Tue, 2 Jun 2026, Richard Biener wrote:

> On Thu, 28 May 2026, Peter Damianov wrote:
> 
> > 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]>
> > ---
> >  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 ab0cbc65cfc..08f1c29785c 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 692e20088c2..c9c19e2f955 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);
> > @@ -10119,6 +10120,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);
> 
> I think using size_t is wrong here, you need to use an integer
> type of the same precision as the pointer argument.
> 
>    tree utype = unsigned_type_for (TREE_TYPE (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);
> 
> And convert the result to size_t if the API requires that.
> 
> > +
> > +  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
> > @@ -10788,6 +10816,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;
> >      }
> > @@ -12399,6 +12430,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 8ab0599b17f..f6bc7082112 100644
> > --- a/gcc/builtins.def
> > +++ b/gcc/builtins.def
> > @@ -1024,6 +1024,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);
> 
> What's the header for 'memalignment'?  Shouldn't we have tests
> covering the non-__builtin use with C23?

Also consider memalignment on a non-standard address-space pointer.

Richard.

> Richard.
> 
> > +}
> > +
> > +__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" } 
> > } */
> > 
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to