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

            Bug ID: 127434
           Summary: 13/14/15/16/17] IPA-SRA introduces unconditional load
                    from conditionally accessed indexing header at -O2
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: clopez at igalia dot com
  Target Milestone: ---

Created attachment 65608
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65608&action=edit
simplified single-file code reproducer.

A standalone reduction of a JavaScriptCore crash starts failing with GCC 13.1.

See related bug report in WebKit bugzilla:
https://bugs.webkit.org/show_bug.cgi?id=324343

Compiler Explorer testing via https://godbolt.org/ shows GCC versions before 13
pass, while tested versions from 13.1 through current trunk fail.

The original crash occurred in a 32-bit ARM WebKit build compiled with -O2
-flto=auto.
The code has been reduced on single .cpp file that reproduces the failure.

Thanks to this reproducer I was able to check that the issue has nothing to do
with -flto or with ARM-32.

The reproducer triggers on a native x86-64 Linux build environment with GCC >=
13

The failure depends on IPA-SRA and early-inlining decisions.

Reproduction

Compile the attached standalone C++ file and run it without arguments:

g++ -O2 GCC-guarded-load.cpp -o reproducer && ./reproducer


Expected output:

indexing header present: no
result: 0
PASS: absent indexing header was not read

Actual output:

indexing header present: no
Segmentation fault

Source pattern

IndexingHeader::indexingPayloadSizeInBytes() switches on a type stored in a
separate Structure object. It reads m_vectorLength only in the indexed cases;
the default case returns zero without accessing header fields.

The caller invokes this helper when the indexing type is zero. The test places
the header address in a PROT_NONE page to make an unexpected load observable.

With the failing compilation, a load from the header occurs before the switch
checks whether that field is needed. On x86-64, the resulting growLikeJSC()
contains an unconditional:

movl -4(%rdx), %esi

Here %rdx holds the butterfly pointer, and offset -4 corresponds to the
header’s m_vectorLength field.

Optimization isolation

Results with native GCC 14.2.0:

Compiler options                                   Result
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━
-O0                                                Pass
─────────────────────────────────────────────────  ─────────
-O1                                                Pass
─────────────────────────────────────────────────  ─────────
-O1 -fipa-sra                                      SIGSEGV
─────────────────────────────────────────────────  ─────────
-O2                                                SIGSEGV
─────────────────────────────────────────────────  ─────────
-O2 -fno-ipa-sra                                   Pass
─────────────────────────────────────────────────  ─────────
-O2 -flto=auto                                     SIGSEGV
─────────────────────────────────────────────────  ─────────
-O2 -flto=auto -fno-ipa-sra                        Pass
─────────────────────────────────────────────────  ─────────
-O3                                                Pass
─────────────────────────────────────────────────  ─────────
-O3 --param=early-inlining-insns=6                 SIGSEGV
─────────────────────────────────────────────────  ─────────
-O3 --param=early-inlining-insns=6 -fno-ipa-sra    Pass
─────────────────────────────────────────────────  ─────────
-O2 --param=early-inlining-insns=13                Pass

The -O3 result is explained by early inlining. On this compiler,
early-inlining-insns defaults to 6 at -O2 and 14 at -O3. GCC estimates inline
growth of 13 for indexingPayloadSizeInBytes():

will not early inline:
growLikeJSC -> IndexingHeader::indexingPayloadSizeInBytes,
growth 13 exceeds --param early-inlining-insns

Limits through 12 reproduce the failure; 13 and 14 pass. When early-inlined,
the final optimized representation retains the header loads inside the indexed
branches.

-O0 -fipa-sra also passes, but -fdump-passes reports ipa-sra: OFF at that
optimization level.

Source workaround

The attachment supports:

g++ -O2 -DWORKAROUND=1 GCC-guarded-load.cpp -o reproducer && ./reproducer

Enabling -DWORKAROUND is equivalent to the patch suggested on the bug
description at the related bug report in the WebKit bugzilla:
https://bugs.webkit.org/show_bug.cgi?id=324343

This checks hasIndexingHeader() in the caller and invokes the header helpers
only when it is true. Otherwise, their results remain zero.

Reply via email to