Skip to content

Ci26 device fixes - #15

Merged
matgla merged 7 commits into
mobfrom
ci26-device-fixes
Oct 14, 2026
Merged

matgla merged 7 commits into
mobfrom
ci26-device-fixes

Conversation

@matgla

@matgla matgla commented Oct 14, 2026

Copy link
Copy Markdown
Owner

No description provided.

matgla and others added 7 commits October 14, 2026 22:35
…fall-through

try_kill_rotated_loop dropped the back-edge of a pure counting loop once
nothing the body computed was read outside, but a body exit to another
label (`if (a[i] == x) goto found;`) fires on whichever trip its condition
first holds, so the rewritten loop only ever tested element 0.  Loop
detection pulls an exit within 50 instructions into the loop's range, so
only a far exit label reached the rewrite.

This miscompiled tcc_ir_opt_dse's chain-op scan in the self-hosted
native tcc: a SET_CHAIN/INIT_CHAIN_SLOT past the first instruction was
never seen, so the parent frame's stores to variables captured by nested
functions were deleted (device-only failures of the bug_nested_* and
bug_r5_nested_inline_combined tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ly exit

try_kill_rotated_loop checked the loop's entries but not its exits.  A pure
search loop whose body leaves early (an inlined `return 0`) was treated as a
dead counting loop and lost its back-edge, so it ran one iteration.  The
loop_max_idx check only caught exits within detect_loops' 50-instruction
padding; a far exit target slipped through.  loop_relayout exposes the shape
by placing the early-exit block inside the loop's index range.

Self-host impact: the cross collapsed the consecutive-offset check in
ssa_opt_load_combine, so the native tcc combined non-consecutive byte loads
(870_readint_unroll_combine -O2 on the device).

The rotated arm now requires the latch fall-through to be the only exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he latch's

try_kill_rotated_loop deleted the back-edge of a pure rotated loop as long
as no value it computed was read afterwards, but a pure body still decides
where control goes: a dedup search `for (i < s->count) if (hit) return;`
leaves through its early return, so killing the back-edge compared only
element 0.  The early return is a jump to the epilogue (target == the
instruction count), which has no CFG edge, so the successor checks never
saw it.  Once ssa:licm_field_ptr hoisted the s->count bound out of the
loop, the bound was invariant and the kill fired; the self-hosted tcc's
sccp_add_mem_dep then appended duplicate deps until "memory full"
(device ir_tests/nested_struct_return.c and gcc nestfunc-7 at -O2).

The rotated arm now requires every jump and CFG edge leaving the body to
land on the latch's fall-through block (no return / computed jump), and
dead_loop_body_hi treats a jump to the epilogue as an exit as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s another exit

The rotated arm (try_kill_rotated_loop) dropped the latch JUMPIF of a pure
counting loop once nothing varying escaped, but a search loop that leaves
early (`found = 1; break`, `return`, a jump to the epilogue) escapes a
per-edge constant: killing the back-edge ran the body once and looked at
items[0] only.  licm_field_ptr exposed it by hoisting the field bound out of
the loop, which made the bound invariant and the loop eligible.

Self-hosted, this miscompiled tcc_ir_tu_analyze_dead_statics, so the native
tcc marked statics read by reachable functions as having no readers
(bug_guard_head_partial_inline -O2 on the device).  The arm now requires the
latch's fall-through to be the only way out of the loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… follow every plain copy to a fixpoint

tcc_ir_opt_dead_addrvar_elim tracked a local's address only through
LEA -> STORE V=T -> ASSIGN T=V, in one linear scan.  A VAR-to-VAR copy
(V4 <-- T9; V6 <-- V4; T12 <-- V6; *T12 = x, from inlining sub/look into
main in gcc 990128-1) hid the store through T12, so the LEA of the dead
&pp was deleted while the store stayed and wrote through whatever register
held T9 (argc on the device: HardFault MMFAR=0x1).

The pass now computes, to a fixpoint over plain copies of TEMPs and VARs
in any order, which local each value may point to.  A value that may also
hold anything else (another def, a second local, an address-taken VAR) is
untracked and its locals escape, as does any use other than a tracked copy
or a STORE/STORE_INDEXED base.  When the local is dead the LEA, the stores
through it and every copy of the address go together, so no copy is left
reading the deleted TEMP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e destination registers

tcc_gen_machine_assign_mop_ex reads a 64-bit lvalue as [base] / [base+4].
The address scratch was allocated with no exclusions, so under pressure
(-fpic reserves r9, a nested function r10) at -O0 the picker handed out the
low half's own register and saved it around the use:

  push {r0}; ldr r0,<&GA[1]>; ldr r0,[r0]; ldr.w ip,[r0,#4]; pop {r0}

The high word was read through the loaded low word (HardFault MMFAR=0x6 on
YasOS in bug_nested_static_chain_scratch_reg -O0) and the low word was then
popped away.  The scratch now excludes the destination registers, and an
address that already sits in the low half's register is read for the high
word first.

Regression: bug_pic_ll_global_load_base_alias.c (runtime, all levels) and
test_pic_ll_load_codegen.py (the -fpic shape; red at -O0 before the fix).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…offset by the push

th_offset_to_reg_ex materializes an out-of-range frame offset in a scratch
register, and with every register busy it PUSHes one first.  The caller had
already computed the SP-relative offset, so the access landed 4 bytes below
the slot.  gcc pr28982b (256 KB frame, 60 live values) read a neighbouring
spill slot at -O1/-O2 under the YasOS ABI, where R9 is reserved.

The helper now takes the base register and adds 4 when it pushed and the
base is SP.  Regression: bug_far_spill_scratch_push_sp_bias.c (8 KB frame,
22 live pointer/float pairs), red at -O1/-O2/-Os before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@matgla
matgla merged commit 209e212 into mob Oct 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant