https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127685

            Bug ID: 127685
           Summary: [17 Regression] Constant member-function pointer table
                    loads optimized to null
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: alexey.klimkin at intel dot com
  Target Milestone: ---

AI-assisted diagnosis and reduction; compiler output and execution results
were checked against the reproducer and a local fix.

GCC 17 optimizes a populated static table of member-function pointers into
a call through address zero at -O1, -O2, -O3, and -Os. The standalone test
uses no headers and bounds the table index to 0 or 1. It should exit 0.

Target: x86_64-pc-linux-gnu
Compiler: GCC 17.0.0 20261001 (experimental)
Upstream base: 2e70e94cef1d76dbb3202d64184a7a8329c92f4e
Tested local compiler checkout: b9fdf4bc282fee44596de7f2ddfeec71e717c2ec
(the checkout also carries unrelated local fixes).

Regression is associated with commit
bd948cdc8664d9a1cd84ffeaae676a286c98e911, 2026-09-29:
"ranger: do not special case pointers in range_from_readonly_*"
https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=bd948cdc8664d9a1cd84ffeaae676a286c98e911

Reproducer (also attached as testcase.C):

// { dg-do run { target c++11 } }

// Equivalent member-function pointer types can have distinct FIELD_DECLs
// in the constant initializer and the load.  Do not infer a null pointer
// when matching those fields by identity fails.
struct Matcher
{
  bool first () { return true; }
  bool second () { return false; }

  __attribute__((noinline)) bool dispatch (unsigned index)
  {
    using Proc = bool (Matcher::*)();
    static const Proc table[] = { &Matcher::first, &Matcher::second };
    const Proc proc = table[index];
    return (this->*proc)();
  }
};

int
main (int argc, char **)
{
  Matcher matcher;
  unsigned index = static_cast<unsigned>(argc - 1) & 1U;
  if (matcher.dispatch (index) != (index == 0)
      || matcher.dispatch (index ^ 1U) != (index != 0))
    __builtin_abort ();
}

Commands:
  g++ -std=c++11 -O1 testcase.C -o testcase
  ./testcase

Expected: exit 0.
Actual: SIGSEGV from an indirect call to address zero.

The same test fails at -O2, -O3 and -Os, passes at -O0, and passes with:
  g++ -std=c++11 -O1 --param=vrp-cstload-limit=0 testcase.C -o testcase

GCC 14.3.0 passes the test at -O1, -O2 and -O3.
A header-only Boost.Regex search reproducer shows the same failure with
GCC 17 at -O1/-O2/-O3 and passes with GCC 14.3.0 and Clang 24.0.0.

Optimized Matcher::dispatch contains:
  mov $0, %eax
  call *%rax

Diagnosis:
  gcc/gimple-range-fold.cc:range_from_readonly_load handles COMPONENT_REF
  by comparing constructor field nodes with ref_fld using pointer identity.
  The member-function pointer constructor and the load have distinct
  FIELD_DECL nodes for __pfn, belonging to equivalent record types.
  Constructor lookup therefore misses a field that is actually populated.
  The missing-record-field path calls range_from_missing_constructor_part
  and infers zero. Ranger subsequently folds the function pointer to null.

GDB confirmed that the load's __pfn FIELD_DECL differs from the populated
constructor's __pfn FIELD_DECL. Replacing the constant-load range logic in
an isolated cc1plus with the version before bd948cdc8664 removes the failure.
This is an isolation experiment, not a full revision bisection.

Proposed conservative fix:
  When field lookup fails, return unknown if DECL_CONTEXT(ref_fld) differs
  from TREE_TYPE(cst), rather than inferring zero initialization. An attached
  four-line guard preserves the existing behavior for exact record types.

Validation:
  - Reproducer fails before the guard and passes afterward.
  - GCC DejaGNU torture test: 14 expected passes, no unexpected failures,
    including -O0/-O1/-O2/-O3/-Os and both LTO variants, with patched cc1plus
    and lto1. Regression test checks both table entries.
  - Original downstream test suite: 25/25 tests pass at -O3 with the guard,
    including a Valgrind test. Before the guard, 11 tests segfault in
    Boost.Regex search dispatch emitted into consumer code.
  - Full GCC bootstrap/testsuites have not run.

The proposed fix and test were AI-assisted. Patch is offered for review.

Reply via email to