sdcc correctly issues an EI instruction at the end of a __critical block or function (see bug 1160666, fixed in 2006). At the end of a __critical __interrupt function, sdcc issues a RETN to return from a non-maskable interrupt, which restores the state of the z80's interrupt flag IFF1 to whatever it was before the CPU accepted the non-maskable interrupt. At the end of a maskable interrupt, sdcc issues a RETI instruction, but omits the required EI to re-enable interrupts. According to the manual (see http://www.zilog.com/docs/z80/um0080.pdf , page 23): "When the CPU accepts a maskable interrupt, both IFF1 and IFF2 are automatically reset, inhibiting further interrupts until the programmer issues a new El instruction." Presently, a function to handle a maskable interrupt must manually ensure that interrupts are re-enabled by inserting __asm("ei"); or equivalent.
$ sdcc -v
SDCC : mcs51/gbz80/z80/z180/r2k/r3ka/ds390/TININative/ds400/hc08/s08 3.2.1 #8413 (Feb 6 2013) (Linux)
$ sdcc -mz80 interrupt.c