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.