What is measured
ir_codegen386.inc, esi and edi as scratch with no save:
64: EmitB($89); EmitB($C6); { mov esi, eax (src) }
295: EmitStoreStrLen386(1, 7, ...); { [edi] := ecx }
302: EmitB($46); { inc esi }
303: EmitB($47); { inc edi }
and ebx handled the other way, deliberately:
472: EmitB($53); { push ebx }
486: EmitB($53); { push ebx }
So the backend has a convention and it is internally consistent. It is simply not the i386 System V one, in exactly two registers.
Why it has not shown up
Every consumer so far is pxx-compiled end to end. busybox at the 394-applet
scope links 521 pxx objects with gcc -m32 doing nothing but the link, and
crtl is pxx too — so there is no frame anywhere in that program that expects
esi or edi to survive a pxx call.
The reachable case is a pxx --emit-obj object linked into a program whose
other objects are gcc's, where a gcc caller holds a live value in esi or edi
across a call into pxx code. That is the whole point of separate compilation,
so this is a real defect rather than a theoretical one — it just has no
reproducer in the corpus today.
What a fix costs, and why it is not free
Two options, and the choice is the ticket:
- Save and restore in the prologue for any function whose body touches esi/edi. Correct, and it puts a push/pop pair on a large number of functions — the string paths use edi constantly.
- Stop using them, leaving eax/ecx/edx plus spill slots. Correct, and it costs a register file the backend is already short of on a 32-bit target.
Measure before choosing: count the functions that would gain a pair, and the extra spills the second option would add, on a real program rather than on a test.
The inline-asm pool is downstream of this and is not the bug
CAsmPool386 (cparser.inc) is ecx esi edi eax edx, and CAsmRegIsCalleeSaved
answers ebx/ebp/esp on i386. Both are written to match what this backend
actually preserves, with a comment saying so and pointing here. If this ticket
resolves toward the ABI, those two are part of the same change — and until it
does, making the pool three registers on its own would refuse busybox's
tls_pstm_mul_comba.c while fixing nothing, since the enclosing function would
still clobber esi and edi on its own account.