"was working fairly well until we put optimization on in the
code"
This is almost always a sign that the keyword 'volatile' is missing from someplace that it is required.
The optimizer removes code that it thinks has no side effects, for example reading a register without using the read value.
To the compiler that is wasted time. It does not know that the reading that register actually changes a bit in the register.
'volatile' forces the read or write to happen even if the compiler does not think it is required.
There can also be the obscure case of Code Motion where memory barrier instructions are required, and missing, to prevent the optimizer from reordering code. For example moving a disable interrupt instruction to someplace it will have no effect.
A lot of inline assembly instructions I see will have this problem, they also need to be marked volatile.