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

            Bug ID: 128127
           Summary: ICE: verify_cgraph_node failed with invalid
                    calls_comdat_local flag at -O3
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: ice-checking, ice-on-valid-code
          Severity: normal
          Priority: P3
         Component: ipa
          Assignee: unassigned at gcc dot gnu.org
          Reporter: yqtian668 at gmail dot com
  Target Milestone: ---
              Host: x86_64-pc-linux-gnu
            Target: x86_64-pc-linux-gnu
             Build: x86_64-pc-linux-gnu

Created attachment 65948
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65948&action=edit
Preprocessed C++ testcase

A checking-enabled GCC trunk build aborts during the IPA inline pass while
compiling this valid C++ program at -O3. The verifier reports a stale
calls_comdat_local flag on the deleting destructor C3::~C3().

Reproducer:

static volatile unsigned in[] = {2435370849u, 1946221516u, 3880391396u,
398565419u, 2592673847u, 3612190063u, 2025886146u, 563125766u};
static unsigned checksum = 0;
static void mix(unsigned v) { checksum = checksum * 31u + v; }
namespace { struct C0; }
unsigned via_C0_f0(C0& r, unsigned a);
__attribute__((noinline)) unsigned nvia_C0_f0(C0& r, unsigned a);
__attribute__((noinline)) unsigned nvia_C0_f1(C0& r);
namespace {
struct C0 {
  unsigned x0 = 2289764861u;
  virtual ~C0() noexcept(false) {
    mix(mc_C0_0());
    mix(nvia_C0_f1(static_cast<C0&>(*this)));
  }
  __attribute__((noinline)) unsigned mc_C0_0() {
    return nvia_C0_f0(static_cast<C0&>(*this), in[1]);
  }
  virtual unsigned f0(unsigned a) { return a * 1u + 53u; }
  virtual unsigned f1() { return x0 * 747u + 362u; }
};
}
struct C1 : virtual C0 {};
struct C3 final : virtual C1, virtual C0 {
  ~C3() noexcept(false) try {
    mix(via_C0_f0(static_cast<C0&>(*this), 7u));
  } catch (...) { throw; }
  unsigned f0(unsigned a) override { return a * 1u + 53u; }
};
unsigned via_C0_f0(C0& r, unsigned a) { return r.f0(a); }
__attribute__((noinline)) unsigned nvia_C0_f0(C0& r, unsigned a) {
  return r.f0(a);
}
__attribute__((noinline)) unsigned nvia_C0_f1(C0& r) { return r.f1(); }
struct Hold_o0 { unsigned pad = 0u; C0 m; };
struct Hold_o2 { unsigned pad = 3u; C3 m; };
int main() {
  {
    Hold_o0 o0;
    Hold_o2 o2;
    C1 o3;
  }
}

Command:

g++ -std=c++17 -O3 -c testcase.cpp

Actual behavior:

G++ reports 'error: invalid calls_comdat_local flag' for C3::~C3() and then
terminates with 'internal compiler error: verify_cgraph_node failed' during the
IPA inline pass. The first useful frame is optimize_inline_calls at
tree-inline.cc:5679.

Complete compiler output:

testcase.cpp: In destructor 'virtual C3::~C3()':
testcase.cpp:25:3: error: invalid calls_comdat_local flag
   25 |   ~C3() noexcept(false) try {
      |   ^
_ZN2C3D0Ev/22 (virtual C3::~C3())
  Type: function definition analyzed
  Visibility: externally_visible semantic_interposition public weak comdat
comdat_group:_ZN2C3D0Ev one_only section:.text._ZN2C3D0Ev (implicit_section)
virtual
  Same comdat group as: _ZTv0_n24_N2C3D0Ev/28
  previous sharing asm name: 115
  Address is taken.
  References:
  Referring: _ZTV2C3/59 (addr)
  Availability: available
  Function flags: count:1073741824 (estimated locally) body calls_comdat_local
  Called by:
  Calls: _Z10nvia_C0_f1RN12_GLOBAL__N_12C0E.isra.0/102 (1073741824 (estimated
locally),1.00 per call) _ZN12_GLOBAL__N_12C07mc_C0_0Ev.constprop.1/97
(1073741824 (estimated locally),1.00 per call) _ZdlPvm/72 (0 (precise),0.00 per
call) _ZdlPvm/72 (1073741824 (estimated locally),1.00 per call)
during IPA pass: inline
testcase.cpp:25:3: internal compiler error: verify_cgraph_node failed
0x2b9120b internal_error(char const*, ...)
        /src/gcc/diagnostic-global-context.cc:787
0xfe4280 cgraph_node::verify_node()
        /src/gcc/cgraph.cc:4505
0xfd1444 symtab_node::verify()
        /src/gcc/symtab.cc:1377
0x162d18a optimize_inline_calls(tree_node*)
        /src/gcc/tree-inline.cc:5679
0x12b6b8b inline_transform(cgraph_node*)
        /src/gcc/ipa-inline-transform.cc:870
0x1452d7e execute_one_ipa_transform_pass
        /src/gcc/passes.cc:2327
0x1452d7e execute_all_ipa_transforms(bool)
        /src/gcc/passes.cc:2390
0xfe9a70 cgraph_node::expand()
        /src/gcc/cgraphunit.cc:1866
0xfe9a70 cgraph_node::expand()
        /src/gcc/cgraphunit.cc:1826
0xfeb04a expand_all_functions
        /src/gcc/cgraphunit.cc:2056
0xfeb04a symbol_table::compile()
        /src/gcc/cgraphunit.cc:2434
0xfedc16 symbol_table::compile()
        /src/gcc/cgraphunit.cc:2344
0xfedc16 symbol_table::finalize_compilation_unit()
        /src/gcc/cgraphunit.cc:2625
/opt/gcc/libexec/gcc/x86_64-pc-linux-gnu/17.0.0/cc1plus -quiet -imultiarch
x86_64-linux-gnu -D_GNU_SOURCE testcase.cpp -quiet -dumpbase testcase.cpp
-dumpbase-ext .cpp -mtune=generic -march=x86-64 -O3 -std=c++17 -o
/tmp/cc7p2g43.s
Please submit a full bug report, with preprocessed source (by using
-freport-bug).
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.

Expected behavior:

G++ should compile the valid C++ translation unit without an internal compiler
error.

The same compiler succeeds with -O2. It also succeeds at -O3 -fchecking=0, and
Clang trunk compiles the testcase at -O3. Adding -Wall -Wextra
-D_GLIBCXX_ASSERTIONS preserves the same ICE without producing a source-level
warning.

Compiler version:

g++ (GCC) 17.0.0 20261021 (experimental)
GCC revision 8cc6e7178c2e3b6156d7992bd18285be0da3c92c
Target: x86_64-pc-linux-gnu
Configured with: /src/configure --prefix=/opt/gcc --enable-languages=c,c++
--disable-multilib --disable-bootstrap --enable-checking=yes

Suspected cause:

calls_comdat_local is a cached callgraph property. gimple_expand_calls_inline
materializes the planned inline tree and removes the last comdat-local call
path from the outer deleting destructor, but the cached flag remains true.
optimize_inline_calls calls id.dst_node->verify() immediately afterward. The
recomputation added for PR94582 occurs later, so the early verifier sees the
stale value and aborts before that repair runs.

PR112601 has the opposite mismatch direction: its stored flag is false and
recomputation returns true. In this testcase, the node dump shows that the
stored flag is true, while verifier failure means recomputation returned false.

Reply via email to