lto_phase1.sh (17393B)
1 #!/usr/bin/env bash 2 # Cross-TU LTO Phase 1: all source-building verbs route through the shared 3 # staging engine, semantic frontends can emit into one open KitCg, and opaque 4 # asm remains an ordinary object participant. 5 set -euo pipefail 6 7 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 8 KIT="${KIT:-$ROOT/build/kit}" 9 WORK="$ROOT/build/test/opt/lto_phase1" 10 mkdir -p "$WORK" 11 12 call_mnemonics='\b(bl|blr|callq?|jalr?)\b' 13 14 fail_log() { 15 local label="$1" 16 local log="$2" 17 printf 'lto-phase1 FAILED: %s\n' "$label" >&2 18 if [ -s "$log" ]; then 19 sed 's/^/ | /' "$log" >&2 20 fi 21 exit 1 22 } 23 24 require_no_calls() { 25 local dis="$1" 26 local fn="$2" 27 local label="$3" 28 local body ncalls 29 body="$(sed -n "/<$fn>:/,/^$/p" "$dis")" 30 if [ -z "$body" ]; then 31 fail_log "$label missing <$fn> in disassembly" "$dis" 32 fi 33 ncalls=$(printf '%s\n' "$body" | grep -cE "$call_mnemonics" || true) 34 if [ "$ncalls" -ne 0 ]; then 35 printf 'lto-phase1 FAILED: %s left %s call(s) in <%s>\n' \ 36 "$label" "$ncalls" "$fn" >&2 37 printf '%s\n' "$body" | sed 's/^/ | /' >&2 38 exit 1 39 fi 40 } 41 42 require_has_calls() { 43 local dis="$1" 44 local fn="$2" 45 local label="$3" 46 local body ncalls 47 body="$(sed -n "/<$fn>:/,/^$/p" "$dis")" 48 if [ -z "$body" ]; then 49 fail_log "$label missing <$fn> in disassembly" "$dis" 50 fi 51 ncalls=$(printf '%s\n' "$body" | grep -cE "$call_mnemonics" || true) 52 if [ "$ncalls" -eq 0 ]; then 53 printf 'lto-phase1 FAILED: %s inlined an interposable weak callee\n' \ 54 "$label" >&2 55 printf '%s\n' "$body" | sed 's/^/ | /' >&2 56 exit 1 57 fi 58 } 59 60 require_symbol_bind() { 61 local symtab="$1" 62 local sym="$2" 63 local bind="$3" 64 local label="$4" 65 if ! awk -v sym="$sym" -v bind="$bind" \ 66 '$2 == bind && $NF == sym { found = 1 } END { exit found ? 0 : 1 }' \ 67 "$symtab"; then 68 fail_log "$label expected symbol '$sym' with bind '$bind'" "$symtab" 69 fi 70 } 71 72 cat > "$WORK/callee.c" <<'EOF' 73 int add7(int x) { return x + 7; } 74 EOF 75 cat > "$WORK/caller.c" <<'EOF' 76 int add7(int); 77 int call_add7(int x) { return add7(x) * 2; } 78 EOF 79 cat > "$WORK/entry.c" <<'EOF' 80 int add7(int); 81 int _start(void) { return add7(5); } 82 EOF 83 84 if ! "$KIT" build-obj -target aarch64-linux-gnu -O1 -ffreestanding -flto \ 85 "$WORK/callee.c" "$WORK/caller.c" -o "$WORK/build_obj.o" \ 86 > "$WORK/build_obj.out" 2>&1; then 87 fail_log "build-obj -flto two-TU compile failed" "$WORK/build_obj.out" 88 fi 89 "$KIT" objdump -d "$WORK/build_obj.o" > "$WORK/build_obj.dis" 2>&1 90 require_no_calls "$WORK/build_obj.dis" call_add7 "build-obj -flto" 91 printf 'lto-phase1 build-obj fused cross-TU call\n' 92 93 if ! "$KIT" cc -target aarch64-linux-gnu -O1 -ffreestanding -nostdlib \ 94 -e _start -flto "$WORK/callee.c" "$WORK/entry.c" \ 95 -o "$WORK/cc_lto.elf" > "$WORK/cc_lto.out" 2>&1; then 96 fail_log "cc -flto link failed" "$WORK/cc_lto.out" 97 fi 98 "$KIT" objdump -d "$WORK/cc_lto.elf" > "$WORK/cc_lto.dis" 2>&1 99 require_no_calls "$WORK/cc_lto.dis" _start "cc -flto" 100 printf 'lto-phase1 cc fused cross-TU call\n' 101 102 if ! "$KIT" build-exe -target aarch64-linux-gnu -O1 -ffreestanding \ 103 -nostdlib -e _start -flto "$WORK/callee.c" "$WORK/entry.c" \ 104 -o "$WORK/build_lto.elf" > "$WORK/build_lto.out" 2>&1; then 105 fail_log "build-exe -flto link failed" "$WORK/build_lto.out" 106 fi 107 "$KIT" objdump -d "$WORK/build_lto.elf" > "$WORK/build_lto.dis" 2>&1 108 require_no_calls "$WORK/build_lto.dis" _start "build-exe -flto" 109 printf 'lto-phase1 build-exe fused cross-TU call\n' 110 111 cat > "$WORK/internal_helper.c" <<'EOF' 112 int arch_helper(int x) { return x + 9; } 113 EOF 114 cat > "$WORK/internal_entry.c" <<'EOF' 115 int arch_helper(int); 116 int _start(void) { return arch_helper(2); } 117 EOF 118 for target in aarch64-linux-gnu x86_64-linux-gnu riscv64-linux-gnu; do 119 out="$WORK/internal_$target.elf" 120 if ! "$KIT" cc -target "$target" -O1 -ffreestanding -nostdlib \ 121 -e _start -flto "$WORK/internal_helper.c" "$WORK/internal_entry.c" \ 122 -o "$out" > "$WORK/internal_$target.out" 2>&1; then 123 fail_log "cc -flto internalization failed for $target" \ 124 "$WORK/internal_$target.out" 125 fi 126 "$KIT" objdump -d "$out" > "$WORK/internal_$target.dis" 2>&1 127 "$KIT" objdump -t "$out" > "$WORK/internal_$target.sym" 2>&1 128 require_no_calls "$WORK/internal_$target.dis" _start \ 129 "cc -flto internalized helper for $target" 130 require_symbol_bind "$WORK/internal_$target.sym" arch_helper l \ 131 "cc -flto internal helper for $target" 132 require_symbol_bind "$WORK/internal_$target.sym" _start g \ 133 "cc -flto entry preservation for $target" 134 done 135 printf 'lto-phase1 internalized non-preserved helpers on aa64/x64/rv64\n' 136 137 cat > "$WORK/dead_ref.c" <<'EOF' 138 int missing_external(void); 139 int dead_global(void) { return missing_external(); } 140 int _start(void) { return 0; } 141 EOF 142 if ! "$KIT" cc -target aarch64-linux-gnu -O1 -ffreestanding -nostdlib \ 143 -e _start -flto "$WORK/dead_ref.c" -o "$WORK/dead_ref.elf" \ 144 > "$WORK/dead_ref.out" 2>&1; then 145 fail_log "dead LTO semantic ref leaked into final link" "$WORK/dead_ref.out" 146 fi 147 "$KIT" objdump -t "$WORK/dead_ref.elf" > "$WORK/dead_ref.sym" 2>&1 148 if grep -q "missing_external" "$WORK/dead_ref.sym"; then 149 fail_log "dead LTO semantic ref remained in symbol table" \ 150 "$WORK/dead_ref.sym" 151 fi 152 printf 'lto-phase1 dead semantic refs do not leak after prepass\n' 153 154 if ! "$KIT" build-lib -target aarch64-linux-gnu -O1 -ffreestanding -flto \ 155 "$WORK/callee.c" "$WORK/caller.c" -o "$WORK/liblto.a" \ 156 > "$WORK/build_lib.out" 2>&1; then 157 fail_log "build-lib -flto failed" "$WORK/build_lib.out" 158 fi 159 if ! "$KIT" ar t "$WORK/liblto.a" > "$WORK/ar.out" 2>&1; then 160 fail_log "ar t on LTO archive failed" "$WORK/ar.out" 161 fi 162 members=$(grep -cE '\.o$' "$WORK/ar.out" || true) 163 if [ "$members" -ne 1 ]; then 164 fail_log "build-lib -flto should archive one merged semantic object" \ 165 "$WORK/ar.out" 166 fi 167 printf 'lto-phase1 build-lib archived one merged LTO object\n' 168 169 cat > "$WORK/weak_only.c" <<'EOF' 170 __attribute__((weak)) int weak_add1(int x) { return x + 1; } 171 EOF 172 cat > "$WORK/weak_caller.c" <<'EOF' 173 int weak_add1(int); 174 int weak_call(int x) { return weak_add1(x); } 175 EOF 176 if ! "$KIT" build-obj -target aarch64-linux-gnu -O1 -ffreestanding -flto \ 177 "$WORK/weak_only.c" "$WORK/weak_caller.c" -o "$WORK/weak_lto.o" \ 178 > "$WORK/weak_lto.out" 2>&1; then 179 fail_log "weak LTO compile failed" "$WORK/weak_lto.out" 180 fi 181 "$KIT" objdump -d "$WORK/weak_lto.o" > "$WORK/weak_lto.dis" 2>&1 182 require_has_calls "$WORK/weak_lto.dis" weak_call "weak LTO guard" 183 printf 'lto-phase1 weak callee stayed out-of-line\n' 184 185 cat > "$WORK/weak_entry.c" <<'EOF' 186 int weak_add1(int); 187 int _start(void) { return weak_add1(1); } 188 EOF 189 if ! "$KIT" cc -target aarch64-linux-gnu -O1 -ffreestanding -nostdlib \ 190 -e _start -flto "$WORK/weak_only.c" "$WORK/weak_entry.c" \ 191 -o "$WORK/weak_exe.elf" > "$WORK/weak_exe.out" 2>&1; then 192 fail_log "weak executable LTO link failed" "$WORK/weak_exe.out" 193 fi 194 "$KIT" objdump -d "$WORK/weak_exe.elf" > "$WORK/weak_exe.dis" 2>&1 195 "$KIT" objdump -t "$WORK/weak_exe.elf" > "$WORK/weak_exe.sym" 2>&1 196 require_has_calls "$WORK/weak_exe.dis" _start "weak executable LTO guard" 197 require_symbol_bind "$WORK/weak_exe.sym" weak_add1 w \ 198 "weak executable LTO preservation" 199 printf 'lto-phase1 weak executable callee stayed weak and out-of-line\n' 200 201 cat > "$WORK/weak_impl.c" <<'EOF' 202 __attribute__((weak)) int pick(void) { return 1; } 203 EOF 204 cat > "$WORK/strong_impl.c" <<'EOF' 205 int pick(void) { return 2; } 206 EOF 207 cat > "$WORK/pick_main.c" <<'EOF' 208 int pick(void); 209 int main(void) { return pick() == 2 ? 0 : 1; } 210 EOF 211 if ! "$KIT" cc -O1 -flto "$WORK/weak_impl.c" "$WORK/strong_impl.c" \ 212 "$WORK/pick_main.c" -o "$WORK/weakstrong" \ 213 > "$WORK/weakstrong.out" 2>&1; then 214 fail_log "strong-over-weak function LTO link failed" "$WORK/weakstrong.out" 215 fi 216 if ! "$WORK/weakstrong"; then 217 fail_log "strong-over-weak function LTO executable returned nonzero" \ 218 "$WORK/weakstrong.out" 219 fi 220 printf 'lto-phase1 strong function overrides weak definition\n' 221 222 cat > "$WORK/weak_data.c" <<'EOF' 223 __attribute__((weak)) int lto_data = 1; 224 EOF 225 cat > "$WORK/strong_data.c" <<'EOF' 226 int lto_data = 2; 227 EOF 228 cat > "$WORK/data_main.c" <<'EOF' 229 extern int lto_data; 230 int main(void) { return lto_data == 2 ? 0 : 1; } 231 EOF 232 if ! "$KIT" cc -O1 -flto "$WORK/weak_data.c" "$WORK/strong_data.c" \ 233 "$WORK/data_main.c" -o "$WORK/weakdata" \ 234 > "$WORK/weakdata.out" 2>&1; then 235 fail_log "strong-over-weak data LTO link failed" "$WORK/weakdata.out" 236 fi 237 if ! "$WORK/weakdata"; then 238 fail_log "strong-over-weak data LTO executable returned nonzero" \ 239 "$WORK/weakdata.out" 240 fi 241 printf 'lto-phase1 strong data overrides weak definition\n' 242 243 cat > "$WORK/odr1.c" <<'EOF' 244 int odr_dup(void) { return 1; } 245 EOF 246 cat > "$WORK/odr2.c" <<'EOF' 247 int odr_dup(void) { return 2; } 248 EOF 249 if bash -c '"$@"; rc=$?; exit $rc' _ "$KIT" build-obj -target aarch64-linux-gnu -O1 \ 250 -ffreestanding -flto "$WORK/odr1.c" "$WORK/odr2.c" \ 251 -o "$WORK/odr.o" > "$WORK/odr.out" 2>&1; then 252 fail_log "duplicate strong definitions unexpectedly compiled" "$WORK/odr.out" 253 fi 254 if ! grep -q "duplicate definition of symbol" "$WORK/odr.out"; then 255 fail_log "duplicate strong definitions lacked ODR diagnostic" "$WORK/odr.out" 256 fi 257 printf 'lto-phase1 duplicate strong definitions are rejected\n' 258 259 # Cross-TU tentative definitions. kit is -fno-common: the C frontend lowers a 260 # file-scope `int g;` to a strong .bss definition, so two of them in different 261 # TUs conflict exactly as the non-LTO linker resolves them. These checks pin the 262 # Phase 1 resolution-fidelity invariant — -flto staging merges symbols the same 263 # way the linker does — and guard same-TU tentative coalescing inside a -flto 264 # build (the legal `int g; int g;` case must not be misread as a redefinition). 265 cat > "$WORK/tent_a.c" <<'EOF' 266 int tentative_dup; 267 EOF 268 cat > "$WORK/tent_b.c" <<'EOF' 269 int tentative_dup; 270 EOF 271 cat > "$WORK/tent_entry.c" <<'EOF' 272 extern int tentative_dup; 273 int _start(void) { return tentative_dup; } 274 EOF 275 276 # -flto staging must reject the duplicate tentative defs with the ODR diagnostic. 277 if bash -c '"$@"; rc=$?; exit $rc' _ "$KIT" build-obj -target aarch64-linux-gnu -O1 \ 278 -ffreestanding -flto "$WORK/tent_a.c" "$WORK/tent_b.c" \ 279 -o "$WORK/tent_dup.o" > "$WORK/tent_dup_lto.out" 2>&1; then 280 fail_log "cross-TU duplicate tentative defs compiled under -flto" \ 281 "$WORK/tent_dup_lto.out" 282 fi 283 if ! grep -q "duplicate definition of" "$WORK/tent_dup_lto.out"; then 284 fail_log "cross-TU duplicate tentative defs lacked ODR diagnostic under -flto" \ 285 "$WORK/tent_dup_lto.out" 286 fi 287 288 # The non-LTO link of the same inputs must reject them too: LTO == linker. 289 "$KIT" cc -target aarch64-linux-gnu -O0 -ffreestanding -c "$WORK/tent_a.c" \ 290 -o "$WORK/tent_a.o" > "$WORK/tent_a.out" 2>&1 || 291 fail_log "tentative TU a failed to compile" "$WORK/tent_a.out" 292 "$KIT" cc -target aarch64-linux-gnu -O0 -ffreestanding -c "$WORK/tent_b.c" \ 293 -o "$WORK/tent_b.o" > "$WORK/tent_b.out" 2>&1 || 294 fail_log "tentative TU b failed to compile" "$WORK/tent_b.out" 295 "$KIT" cc -target aarch64-linux-gnu -O0 -ffreestanding -c "$WORK/tent_entry.c" \ 296 -o "$WORK/tent_entry.o" > "$WORK/tent_entry.out" 2>&1 || 297 fail_log "tentative entry TU failed to compile" "$WORK/tent_entry.out" 298 if bash -c '"$@"; rc=$?; exit $rc' _ "$KIT" cc -target aarch64-linux-gnu \ 299 -ffreestanding -nostdlib -e _start "$WORK/tent_a.o" "$WORK/tent_b.o" \ 300 "$WORK/tent_entry.o" -o "$WORK/tent_dup.elf" \ 301 > "$WORK/tent_dup_link.out" 2>&1; then 302 fail_log "cross-TU duplicate tentative defs linked without -flto" \ 303 "$WORK/tent_dup_link.out" 304 fi 305 if ! grep -q "duplicate definition of" "$WORK/tent_dup_link.out"; then 306 fail_log "non-LTO link lacked duplicate-definition diagnostic" \ 307 "$WORK/tent_dup_link.out" 308 fi 309 printf 'lto-phase1 cross-TU duplicate tentative defs rejected by -flto and linker\n' 310 311 # Positive: one definition coalesced from same-TU tentatives, shared across TUs 312 # through extern refs, links and observes shared storage at run time under -flto. 313 cat > "$WORK/tent_def.c" <<'EOF' 314 int shared_tentative; 315 int shared_tentative; /* same-TU tentative coalescing inside an -flto build */ 316 EOF 317 cat > "$WORK/tent_use.c" <<'EOF' 318 extern int shared_tentative; 319 int read_shared(void) { return shared_tentative; } 320 EOF 321 cat > "$WORK/tent_shared_main.c" <<'EOF' 322 extern int shared_tentative; 323 int read_shared(void); 324 int main(void) { shared_tentative = 5; return read_shared() == 5 ? 0 : 1; } 325 EOF 326 if ! "$KIT" cc -O1 -flto "$WORK/tent_def.c" "$WORK/tent_use.c" \ 327 "$WORK/tent_shared_main.c" -o "$WORK/tent_shared" \ 328 > "$WORK/tent_shared.out" 2>&1; then 329 fail_log "single tentative def shared across TUs failed under -flto" \ 330 "$WORK/tent_shared.out" 331 fi 332 if ! "$WORK/tent_shared"; then 333 fail_log "cross-TU tentative shared storage incorrect under -flto" \ 334 "$WORK/tent_shared.out" 335 fi 336 printf 'lto-phase1 single tentative def shared across TUs under -flto\n' 337 338 cat > "$WORK/c_frontend.c" <<'EOF' 339 int c_frontend_value(void) { return 5; } 340 EOF 341 cat > "$WORK/toy_frontend.toy" <<'EOF' 342 fn toy_frontend_value(): i64 { 343 return 3; 344 } 345 EOF 346 cat > "$WORK/wasm_frontend.wat" <<'EOF' 347 (module 348 (func (export "wasm_frontend_value") (result i32) 349 i32.const 4)) 350 EOF 351 if ! "$KIT" build-obj -O1 -flto "$WORK/c_frontend.c" \ 352 "$WORK/toy_frontend.toy" "$WORK/wasm_frontend.wat" \ 353 -o "$WORK/semantic_frontends.o" \ 354 > "$WORK/semantic_frontends.out" 2>&1; then 355 fail_log "C/Toy/Wasm semantic LTO staging failed" \ 356 "$WORK/semantic_frontends.out" 357 fi 358 printf 'lto-phase1 C/Toy/Wasm semantic frontends staged together\n' 359 360 if ! "$KIT" build-obj -O1 "$WORK/c_frontend.c" -o "$WORK/c_onetu.o" \ 361 > "$WORK/c_onetu.out" 2>&1; then 362 fail_log "C one-TU compile_cg wrapper failed" "$WORK/c_onetu.out" 363 fi 364 if ! "$KIT" build-obj -O1 "$WORK/toy_frontend.toy" -o "$WORK/toy_onetu.o" \ 365 > "$WORK/toy_onetu.out" 2>&1; then 366 fail_log "Toy one-TU compile_cg wrapper failed" "$WORK/toy_onetu.out" 367 fi 368 if ! "$KIT" build-obj -O1 "$WORK/wasm_frontend.wat" -o "$WORK/wasm_onetu.o" \ 369 > "$WORK/wasm_onetu.out" 2>&1; then 370 fail_log "Wasm one-TU compile_cg wrapper failed" "$WORK/wasm_onetu.out" 371 fi 372 printf 'lto-phase1 C/Toy/Wasm one-TU builds use compile_cg wrapper\n' 373 374 cat > "$WORK/use_asm.c" <<'EOF' 375 int asm_add1(int); 376 int call_asm(int x) { return asm_add1(x); } 377 EOF 378 cat > "$WORK/asm_add1.s" <<'EOF' 379 .text 380 .globl asm_add1 381 asm_add1: 382 add x0, x0, #1 383 ret 384 EOF 385 if ! "$KIT" build-obj -target aarch64-linux-gnu -O1 -ffreestanding -flto \ 386 "$WORK/use_asm.c" "$WORK/asm_add1.s" -o "$WORK/opaque_asm.o" \ 387 > "$WORK/opaque_asm.out" 2>&1; then 388 fail_log "opaque asm participation under -flto failed" "$WORK/opaque_asm.out" 389 fi 390 "$KIT" objdump -t "$WORK/opaque_asm.o" > "$WORK/opaque_asm.sym" 2>&1 391 if ! grep -q "asm_add1" "$WORK/opaque_asm.sym"; then 392 fail_log "opaque asm symbol missing from relocatable output" \ 393 "$WORK/opaque_asm.sym" 394 fi 395 printf 'lto-phase1 asm participated as opaque object\n' 396 397 cat > "$WORK/opaque_keep.c" <<'EOF' 398 int keep_me(void) { return 17; } 399 int _start(void) { return 0; } 400 EOF 401 cat > "$WORK/opaque_ref.s" <<'EOF' 402 .text 403 .globl opaque_ref 404 opaque_ref: 405 bl keep_me 406 ret 407 EOF 408 if ! "$KIT" cc -target aarch64-linux-gnu -O1 -ffreestanding -nostdlib \ 409 -e _start -flto "$WORK/opaque_keep.c" "$WORK/opaque_ref.s" \ 410 -o "$WORK/opaque_ref.elf" > "$WORK/opaque_ref.out" 2>&1; then 411 fail_log "opaque object reference did not preserve LTO symbol" \ 412 "$WORK/opaque_ref.out" 413 fi 414 "$KIT" objdump -t "$WORK/opaque_ref.elf" > "$WORK/opaque_ref.sym" 2>&1 415 require_symbol_bind "$WORK/opaque_ref.sym" keep_me g \ 416 "opaque object reference preservation" 417 printf 'lto-phase1 opaque object reference preserved LTO definition\n' 418 419 cat > "$WORK/archive_lto.c" <<'EOF' 420 int archive_func(void); 421 int lto_target(void) { return 41; } 422 int _start(void) { return archive_func(); } 423 EOF 424 cat > "$WORK/archive_member.c" <<'EOF' 425 int lto_target(void); 426 int archive_func(void) { return lto_target() + 1; } 427 EOF 428 if ! "$KIT" cc -target aarch64-linux-gnu -O0 -ffreestanding -c \ 429 "$WORK/archive_member.c" -o "$WORK/archive_member.o" \ 430 > "$WORK/archive_member.out" 2>&1; then 431 fail_log "archive member compile failed" "$WORK/archive_member.out" 432 fi 433 if ! "$KIT" ar rcs "$WORK/libsemantic.a" "$WORK/archive_member.o" \ 434 > "$WORK/archive_ar.out" 2>&1; then 435 fail_log "archive creation failed" "$WORK/archive_ar.out" 436 fi 437 if ! "$KIT" cc -target aarch64-linux-gnu -O1 -ffreestanding -nostdlib \ 438 -e _start -flto "$WORK/archive_lto.c" "$WORK/libsemantic.a" \ 439 -o "$WORK/archive_lto.elf" > "$WORK/archive_lto.out" 2>&1; then 440 fail_log "archive selected by semantic LTO ref failed to link back" \ 441 "$WORK/archive_lto.out" 442 fi 443 "$KIT" objdump -t "$WORK/archive_lto.elf" > "$WORK/archive_lto.sym" 2>&1 444 require_symbol_bind "$WORK/archive_lto.sym" lto_target g \ 445 "archive semantic-ref preservation" 446 require_symbol_bind "$WORK/archive_lto.sym" archive_func g \ 447 "archive semantic-ref selection" 448 printf 'lto-phase1 archive semantic ref preserved callback target\n' 449 450 if "$KIT" cc -shared -flto -nostdlib "$WORK/callee.c" \ 451 -o "$WORK/libbad.so" > "$WORK/shared_lto.out" 2>&1; then 452 fail_log "cc -shared -flto unexpectedly succeeded" "$WORK/shared_lto.out" 453 fi 454 if ! grep -q "shared-library LTO output is not exercised" \ 455 "$WORK/shared_lto.out"; then 456 fail_log "cc -shared -flto rejection missing shared-LTO diagnostic" \ 457 "$WORK/shared_lto.out" 458 fi 459 printf 'lto-phase1 shared-library LTO remains disabled\n' 460 461 printf 'lto-phase1: ok\n'