@@ -2137,6 +2137,34 @@ if test "$Py_BOLT" = 'true' ; then
21372137 AC_MSG_ERROR ( [ llvm-bolt is required for a --enable-bolt build but could not be found.] )
21382138 fi
21392139
2140+ # Check for packed relocations (-Wl,-z,pack-relative-relocs) which use DT_RELR format.
2141+ # BOLT has issues analyzing these compact relocations.
2142+ AC_MSG_CHECKING ( [ for packed relocations in LDFLAGS] )
2143+ has_packed_relocs="no"
2144+ case "$LDFLAGS $LDFLAGS_NODIST" in
2145+ *-Wl,-z,pack-relative-relocs*)
2146+ has_packed_relocs="yes"
2147+ ;;
2148+ esac
2149+ AC_MSG_RESULT ( [ $has_packed_relocs] )
2150+
2151+ # Check BOLT version to determine if we need to skip functions with computed gotos.
2152+ AC_MSG_CHECKING ( [ llvm-bolt version] )
2153+ llvm_bolt_version=$("${LLVM_BOLT}" --version 2>/dev/null | grep -oE '[ [ 0-9] ] +\.[ [ 0-9] ] +\.[ [ 0-9] ] +' | head -1)
2154+ AC_MSG_RESULT ( [ ${llvm_bolt_version}] )
2155+
2156+ # Parse version
2157+ llvm_bolt_major=$(echo "${llvm_bolt_version}" | cut -d. -f1)
2158+ llvm_bolt_minor=$(echo "${llvm_bolt_version}" | cut -d. -f2)
2159+
2160+ bolt_need_skip_computed_goto="yes"
2161+ if test -n "${llvm_bolt_major}" && test "${llvm_bolt_major}" -ge 21; then
2162+ if test "${llvm_bolt_major}" -gt 21 || test "${llvm_bolt_minor}" -ge 1; then
2163+ bolt_need_skip_computed_goto="no"
2164+ AC_MSG_RESULT ( [ LLVM ${llvm_bolt_version} supports computed gotos] )
2165+ fi
2166+ fi
2167+
21402168 AC_SUBST ( [ MERGE_FDATA] )
21412169 AC_PATH_TOOL ( [ MERGE_FDATA] , [ merge-fdata] , [ ''] , [ ${llvm_path}] )
21422170 if test -n "${MERGE_FDATA}" -a -x "${MERGE_FDATA}"
@@ -2166,15 +2194,80 @@ then
21662194 [ BOLT_COMMON_FLAGS] ,
21672195 [ m4_normalize ( "
21682196 [ -update-debug-sections]
2169-
2170- dnl At least LLVM 19.x doesn't support computed gotos in PIC compiled code.
2171- dnl Exclude functions containing computed gotos.
2172- dnl TODO this may be fixed in LLVM 20.x via https://github.com/llvm/llvm-project/pull/120267.
2173- dnl GCC's LTO creates .lto_priv.0 clones of these functions.
2174- [ -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1,sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1]
21752197 " ) ]
21762198 )
2199+
2200+ dnl BOLT versions before LLVM 21.1.0 don't support computed gotos in PIC compiled code.
2201+ dnl Exclude functions containing computed gotos for older versions.
2202+ dnl Fixed in LLVM 21.1.0+ via https://github.com/llvm/llvm-project/pull/120267
2203+ if test "${bolt_need_skip_computed_goto}" = "yes"; then
2204+ dnl Skip base computed goto functions
2205+ BOLT_COMMON_FLAGS="${BOLT_COMMON_FLAGS} -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1"
2206+ dnl GCC's LTO creates .lto_priv.0 clones that also need to be skipped
2207+ dnl Skip if packed relocs not present
2208+ if test "${has_packed_relocs}" != "yes" && test "${ac_cv_cc_name}" = "gcc" && test "${with_lto}" != "no" && test -n "${with_lto}"; then
2209+ BOLT_COMMON_FLAGS="${BOLT_COMMON_FLAGS},sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1"
2210+ fi
2211+ fi
2212+
2213+ dnl When packed relocations are used, BOLT cannot properly
2214+ dnl analyze the DT_RELR format. Build skip list for functions that fail with packed relocs.
2215+ if test "${has_packed_relocs}" = "yes"; then
2216+ bolt_base="_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1"
2217+ bolt_sre_lto="sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1"
2218+ bolt_all_lto="_PyEval_EvalFrameDefault.lto_priv.0/1,${bolt_sre_lto}"
2219+
2220+ case "${ac_cv_cc_name}" in
2221+ gcc)
2222+ dnl GCC's LTO creates .lto_priv.0 variants; PGO creates .localalias variants (in shared builds)
2223+ bolt_skip_list=""
2224+
2225+ dnl Determine what to include based on build configuration
2226+ bolt_need_base=$( (test "${bolt_need_skip_computed_goto}" != "yes" && test "${enable_shared}" = "yes") && echo "yes" || echo "no")
2227+ bolt_need_lto=$( (test "${with_lto}" != "no" && test -n "${with_lto}") && echo "yes" || echo "no")
2228+ bolt_need_pgo=$( (test "${enable_optimizations}" = "yes" && test "${enable_shared}" = "yes") && echo "yes" || echo "no")
2229+
2230+ dnl Build the skip list
2231+ test "${bolt_need_base}" = "yes" && bolt_skip_list="${bolt_base}"
2232+ if test "${bolt_need_lto}" = "yes"; then
2233+ if test "${enable_shared}" = "yes"; then
2234+ test -n "${bolt_skip_list}" && bolt_skip_list="${bolt_skip_list},"
2235+ bolt_skip_list="${bolt_skip_list}${bolt_all_lto}"
2236+ elif test "${bolt_need_skip_computed_goto}" != "yes"; then
2237+ bolt_skip_list="${bolt_all_lto}"
2238+ else
2239+ dnl BOLT < 21.1.0 + static: need to add LTO variants
2240+ test -n "${bolt_skip_list}" && bolt_skip_list="${bolt_skip_list},"
2241+ bolt_skip_list="${bolt_skip_list}${bolt_all_lto}"
2242+ fi
2243+ fi
2244+ if test "${bolt_need_pgo}" = "yes"; then
2245+ test -n "${bolt_skip_list}" && bolt_skip_list="${bolt_skip_list},"
2246+ if test "${bolt_need_lto}" = "yes"; then
2247+ bolt_skip_list="${bolt_skip_list}_PyEval_EvalFrameDefault.localalias.lto_priv.0/1"
2248+ else
2249+ bolt_skip_list="${bolt_skip_list}_PyEval_EvalFrameDefault.localalias/1"
2250+ fi
2251+ fi
2252+
2253+ dnl Apply the skip list
2254+ if test -n "${bolt_skip_list}"; then
2255+ if test "${bolt_need_skip_computed_goto}" = "yes"; then
2256+ BOLT_COMMON_FLAGS="${BOLT_COMMON_FLAGS},${bolt_skip_list}"
2257+ else
2258+ BOLT_COMMON_FLAGS="${BOLT_COMMON_FLAGS} -skip-funcs=${bolt_skip_list}"
2259+ fi
2260+ fi
2261+ ;;
2262+ clang*)
2263+ if test "${bolt_need_skip_computed_goto}" != "yes"; then
2264+ BOLT_COMMON_FLAGS="${BOLT_COMMON_FLAGS} -skip-funcs=${bolt_base}"
2265+ fi
2266+ ;;
2267+ esac
2268+ fi
21772269fi
2270+ AC_MSG_RESULT ( [ $BOLT_COMMON_FLAGS] )
21782271
21792272AC_ARG_VAR (
21802273 [ BOLT_INSTRUMENT_FLAGS] ,
0 commit comments