Skip to content

Build: BOLT -icf=1 breaks type-slot function-pointer identity, causes SIGSEGV #148832

@ashm-dev

Description

@ashm-dev

Bug description:

CPython's --enable-bolt build sets -icf=1 in BOLT_APPLY_FLAGS (configure.ac:2258), enabling BOLT's aggressive Identical Code Folding. ICF merges functions with identical machine code into a single address, including address-taken functions — concrete type-slot implementations like _PyTuple_Concat, _PyList_Concat, and slot_nb_* wrappers.

This violates two CPython invariants:

  1. Type slots require a function pointer matching the type's layout. When _PyList_Concat and _PyTuple_Concat are folded, calls via sq_concat access the object through the wrong layout → SIGSEGV.
  2. binary_op1 in Objects/abstract.c compares slot pointers (slotw == slotv) to drive reflected-operator dispatch. ICF-induced false equality skips __radd__.

test_no_comdat_folding in Lib/test/test_list.py / Lib/test/test_tuple.py exists exactly to detect this class of bug (originally for MSVC COMDAT folding, gh-8847).

Reproduction

./configure --enable-bolt CC=clang LDFLAGS=-fuse-ld=lld
make -j4
./python -m test -j4

Observed failures (main @ d206d42834b, BOLT build)

== Python build: release BOLT

[268/502] test_list worker non-zero exit code (Exit code -11 (SIGSEGV))
  _PyList_Concat+0x88
  PyNumber_Add+0x11
  File "Lib/test/test_list.py", line 227 in test_no_comdat_folding

[435/502] test_tuple worker non-zero exit code (Exit code -11 (SIGSEGV))
  _PyTuple_Concat+0xbb
  PyNumber_Add+0x11
  File "Lib/test/test_tuple.py", line 427 in test_no_comdat_folding

test test_descr failed -- test_gh146587
  self.assertIsNone(B() + A())
TypeError: can only concatenate tuple (not "A") to tuple

test test_datetime failed -- test_computations
  self.assertRaises(TypeError, lambda: a+i)
AssertionError: TypeError not raised by <lambda>

test test_perf_profiler failed
  ValueError: Perf failed with return code -11

5 tests failed: test_datetime test_descr test_list test_perf_profiler test_tuple

Fix

Switch -icf=1-icf=0 in configure.ac. Disabling ICF preserves the C guarantee (C17 §6.5.9/6) that CPython's type-slot dispatch depends on — every address-taken function keeps a unique identity. Other BOLT passes (-reorder-blocks, -reorder-functions, -split-functions, -inline-all, …) are unaffected; BOLT's ICF in CPython gives <1% .text size and sub-percent throughput, so the cost is negligible.

(-icf=safe would be the theoretically nicer choice — fold non-address-taken only — but it is not recognized by BOLT shipped with LLVM ≤19 which CI uses, so -icf=0 is the portable fix.)

After the change, 4 of 5 failures disappear on ./python -m test -j4. (test_perf_profiler is a separate, pre-existing -Xperf_jit + BOLT issue, not caused by ICF.)

PR: gh-148833.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    buildThe build process and cross-buildinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions