With 4.5.0 and trunk r17011, with or without the patch from #4153, -mmcs51 can store through a pointer whose high byte register has been given to another value. The attached test fails on info_while[2] == 2 in mcs51-small, mcs51-large and mcs51-small-stack-auto.
void rcv(uint8_t port) __reentrant {
if (info_while[port] &&
cmp((__xdata uint8_t *)((__xdata struct pkt *)buf)->bridge.mac,
&dbridge[port].mac, 1) ||
dpid[port])
return;
rxage[port] = rxfwd[port] = clamp(
((__xdata struct pkt *)&buf[rxbody])->fwd_delay, 4, 30, 15);
info_while[port] = port;
}
With sdcc -mmcs51 -S on the test (plus -I for testfwk.h) the end of rcv() is:
lcall _clamp
mov r4, dpl
...
mov dpl,r3
mov dph,r4
movx @dptr,a
The address of info_while[port] is block-spilt around the cmp() call and gets r3:r4 back at the IPOP. A later stack spill makes spillLRWithPtrReg() call spillThis() on temps whose live ranges have ended, and spillThis() frees every register left in their regs[], r4 included.
The patch frees them only while the symbol is still in _G.regAssigned. It does not depend on [#4153]. ds390 has the same code.
With it the new test no longer fails in those three models or on host, and the mcs51-small, mcs51-small-stack-auto and mcs51-large regression runs show no new failures.
The patch against r17011 is attached. It has the three-line change in
src/mcs51/ralloc.cand the test assupport/regression/tests/bug-4154.c. Without the change the test fails oninfo_while[2]in mcs51-small, mcs51-large and mcs51-small-stack-auto. It applies on its own and together with the patch from #4153.A short follow-up in case it makes this easier to look at. Below is a standalone version of the test, without the regression framework. It prints PASS or FAIL through the ucsim simif interface:
With plain 4.5.0 it prints FAIL in the small, large and stack-auto models, and with the patch from this ticket it prints PASS. I also tried shrinking it with cvise, but the function under test barely got smaller, so it seems to need all of those statements to run out of registers.
If another form of report or patch would be easier for you to review, just let me know and I'll redo it. Thanks for taking a look.
I can reproduce the issue using SDCC built from current trunk on my Debian GNU/Linux testing system.
Clearly an issue in register allocation, as we can see in the asm (compiled with --no-peep --i-code-in-asm), since two variables (iTemp3 and iTemp30) with overlapping live-ranges both are (partially) allocated to r4.
Related
Commit: [r4]
Commit: [r7]
The r4 overlap comes from spillThis() freeing registers that the symbol no longer owns.
Traced on r17021, where the call result is iTemp29 instead of iTemp30: at ic 40 spilSomething() puts iTemp0 on the stack. As the first stack spill it calls spillLRWithPtrReg(iTemp0), which spills every range using r0/r1 that does not end before iTemp0 starts (3). That includes iTemp13 [r0 r4], range 13:16, which had already ended and given its registers back; r4 then went to iTemp3 [r3 r4], range 6:43. spillThis(iTemp13) frees r0 and r4 again, so r4 counts as free while iTemp3 still holds it, and the call result gets r4.
The patch frees registers in spillThis() only while the symbol is still in _G.regAssigned; it still clears regs[] and spills the symbol as before. If you'd rather filter ended ranges in spillLRWithPtrReg() instead, just say so and I'll rework it.
Diff:
Related
Bugs:
#4153Patch is applied in [r17039], thanks..
Related
Commit: [r17039]