On 5/7/26 5:00 AM, Jakub Jelinek wrote:
Hi!

The following patch tries to support constexpr folding of
__builtin_object_size and __builtin_dynamic_object_size.
There is already folding of this builtin in builtins.cc, but it doesn't look
through pointer conversions in the argument, only handles the case where
the argument is ADDR_EXPR (plus of course when inside of the objsz passes it
handles more through SSA_NAME tracking).

I've tried to handle this in builtins.cc first, see
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124347#c1 but unfortunately
that resulted in some serious security regressions where some of __bos
or __bdos builtins were folded prematurely, in particular e.g. for cases
where we have __builtin_object_size (something, 1) and the whole object
size is unknown but object size of the element is known.  With the patch
it would just return the remaining subobject size, when it actually should
return MIN_EXPR <object_size, remaining subobject size>.  For mode 1
it is conservatively correct, but larger than necessary with security
implications of not caching out of bounds accesses.

So, this patch instead handles this in the FE, only for manifestly constant
evaluation, by stripping the pointer casts on the first argument in that
case.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

2026-05-07  Jakub Jelinek  <[email protected]>

        PR c++/124347
        * constexpr.cc (cxx_eval_builtin_function_call): For
        mce_true strip nops in first argument of BUILT_IN*_OBJECT_SIZE
        if the inner expression is ADDR_EXPR.

        * g++.dg/ext/builtin-object-size4.C: New test.
        * g++.dg/ext/builtin-object-size5.C: New test.
        * g++.dg/ext/builtin-object-size6.C: New test.

--- gcc/cp/constexpr.cc.jj      2026-05-07 08:29:24.904858087 +0200
+++ gcc/cp/constexpr.cc 2026-05-07 10:17:26.469434446 +0200
@@ -2535,6 +2535,7 @@ cxx_eval_builtin_function_call (const co
int strops = 0;
    int strret = 0;
+  bool bos = false;
    if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
      switch (DECL_FUNCTION_CODE (fun))
        {
@@ -2576,6 +2577,10 @@ cxx_eval_builtin_function_call (const co
          }
        *non_constant_p = true;
        return t;
+      case BUILT_IN_OBJECT_SIZE:
+      case BUILT_IN_DYNAMIC_OBJECT_SIZE:
+       bos = ctx->manifestly_const_eval == mce_true;
+       break;
        default:
        break;
        }
@@ -2634,6 +2639,13 @@ cxx_eval_builtin_function_call (const co
args[i] = arg;
      }
+  if (bos)
+    {
+      tree arg = args[0];
+      STRIP_NOPS (arg);
+      if (TREE_CODE (arg) == ADDR_EXPR)
+       args[0] = arg;
+    }
bool save_ffbcp = force_folding_builtin_constant_p;
    force_folding_builtin_constant_p |= ctx->manifestly_const_eval == mce_true;
--- gcc/testsuite/g++.dg/ext/builtin-object-size4.C.jj  2026-05-07 
10:17:26.503736686 +0200
+++ gcc/testsuite/g++.dg/ext/builtin-object-size4.C     2026-05-07 
10:29:28.256603065 +0200
@@ -0,0 +1,62 @@
+// PR c++/124347
+// { dg-do compile { target c++11 } }
+
+using size_t = decltype (sizeof 0);
+
+constexpr size_t
+foo (void *p)
+{
+  return __builtin_object_size (p, 0);
+}
+
+constexpr size_t
+bar (void *p)
+{
+  return __builtin_object_size (p, 1);
+}
+
+constexpr size_t
+baz (void *p)
+{
+  return __builtin_object_size (p, 2);
+}
+
+constexpr size_t
+qux (void *p)
+{
+  return __builtin_object_size (p, 3);
+}
+
+constexpr size_t
+fred (void *p)
+{
+  return __builtin_dynamic_object_size (p, 0);
+}
+
+constexpr size_t
+corge (void *p)
+{
+  return __builtin_dynamic_object_size (p, 1);
+}
+
+constexpr size_t
+garply (void *p)
+{
+  return __builtin_dynamic_object_size (p, 2);
+}
+
+constexpr size_t
+xyzzy (void *p)
+{
+  return __builtin_dynamic_object_size (p, 3);
+}
+
+struct S { char a; int b; char c[32]; } a[10];
+static_assert (foo (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), "");
+static_assert (bar (&a[2].c[5]) == 32 - 5, "");
+static_assert (baz (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), "");
+static_assert (qux (&a[2].c[5]) == 32 - 5, "");
+static_assert (fred (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), "");
+static_assert (corge (&a[2].c[5]) == 32 - 5, "");
+static_assert (garply (&a[2].c[5]) == &a[9].a - &a[2].c[5] + sizeof (S), "");
+static_assert (xyzzy (&a[2].c[5]) == 32 - 5, "");
--- gcc/testsuite/g++.dg/ext/builtin-object-size5.C.jj  2026-05-07 
10:22:36.781350456 +0200
+++ gcc/testsuite/g++.dg/ext/builtin-object-size5.C     2026-05-07 
10:30:59.710101728 +0200
@@ -0,0 +1,83 @@
+// PR c++/124347
+// { dg-do compile { target c++20 } }
+
+using size_t = decltype (sizeof 0);
+
+constexpr size_t
+foo (void *p)
+{
+  return __builtin_object_size (p, 0);
+}
+
+constexpr size_t
+bar (void *p)
+{
+  return __builtin_object_size (p, 1);
+}
+
+constexpr size_t
+baz (void *p)
+{
+  return __builtin_object_size (p, 2);
+}
+
+constexpr size_t
+qux (void *p)
+{
+  return __builtin_object_size (p, 3);
+}
+
+constexpr size_t
+fred (void *p)
+{
+  return __builtin_dynamic_object_size (p, 0);
+}
+
+constexpr size_t
+corge (void *p)
+{
+  return __builtin_dynamic_object_size (p, 1);
+}
+
+constexpr size_t
+garply (void *p)
+{
+  return __builtin_dynamic_object_size (p, 2);
+}
+
+constexpr size_t
+xyzzy (void *p)
+{
+  return __builtin_dynamic_object_size (p, 3);
+}
+
+struct S { char a; int b; char c[32]; char d; };
+
+constexpr bool
+plugh ()
+{
+  S *a = new S {};
+  if (foo (a) != sizeof (S)
+      || bar (a) != sizeof (S)
+      || baz (a) != sizeof (S)
+      || qux (a) != sizeof (S)
+      || fred (a) != sizeof (S)
+      || corge (a) != sizeof (S)
+      || garply (a) != sizeof (S)
+      || xyzzy (a) != sizeof (S))
+    return false;
+  char *b = &a->c[2];
+  if (foo (b) != sizeof (S) - (&a->c[2]- &a->a)
+      || bar (b) != 32 - 2
+      || baz (b) != sizeof (S) - (&a->c[2]- &a->a)
+      || qux (b) != 32 - 2
+      || fred (b) != sizeof (S) - (&a->c[2]- &a->a)
+      || corge (b) != 32 - 2
+      || garply (b) != sizeof (S) - (&a->c[2]- &a->a)
+      || xyzzy (b) != 32 - 2)
+    return false;
+  delete a;
+  return true;
+}
+
+static_assert (plugh ());
--- gcc/testsuite/g++.dg/ext/builtin-object-size6.C.jj  2026-05-07 
10:41:36.068655061 +0200
+++ gcc/testsuite/g++.dg/ext/builtin-object-size6.C     2026-05-07 
10:42:00.936246823 +0200
@@ -0,0 +1,40 @@
+// PR c++/124347
+// { dg-do compile { target c++20 } }
+
+constexpr int
+foo (const char *p)
+{
+  return __builtin_dynamic_object_size (p, 0);
+}
+
+constexpr int
+bar (int x)
+{
+  char *a = new char[x];
+  int b = foo (a);
+  delete [] a;
+  return b;
+}
+
+constexpr int
+baz (const char *p)
+{
+  return __builtin_object_size (p, 1);
+}
+
+constexpr int
+qux (int x)
+{
+  char *a = new char[x];
+  int b = baz (a);
+  delete [] a;
+  return b;
+}
+
+static_assert (bar (42) == 42);
+static_assert (qux (142) == 142);
+constexpr char a[] = "foobar";
+static_assert (foo (a) == 7);
+static_assert (baz (a) == 7);
+static_assert (foo ("foo") == 4);
+static_assert (baz ("baz") == 4);

        Jakub


Reply via email to