I've seen code generated along the lines of:
ld (hl), a
ld a, (hl)
The second load is obviously redundant, and a peephole optimisation rule could eliminate it in this simple case. This is safe as long as there is no label inbetween the to two, else we could jump here from somewhere else, and would need to reload anyway.
This could be generalised and broadened (although I'm not sure how wide the scope of the peephole optimiser is). I have also seen code such as:
'a' remains unmodified, but is reloaded with the same value on line 3.
In theory, this could be expressed more generally along the lines of:
if R1 is loaded from R2, then any number of instructions can follow, then if R2 is reloaded from R1, the second load can be eliminated if R2 is not modified in between the two, nor are there any labels.
In the last example, R1 = b and R2 = a. R2(a) remains unmodified and there are no labels, thus we could eliminate "ld R2, R1", i.e. the last line.
I'm using SDCC : z80 3.0.6 #6988 (Oct 22 2011) (Solaris i386)
Example code is attached for inspection. I accept that the way this came about could be classed as a struct/union handling issue, but if the more general form above can be expressed as a peephole rule, it could have benefits elsewhere as well as here.