The interrupts should not be removed.
But they need to be treated in a special way, e.g. in the linker file:
KEEP(*(.isr_vector))
I did had some wrong removal with -LTO especially in the FreeRTOS port (I have modified FreeRTOS so it links fine with -LTO. Other than that LTO has helped me to find some issues in code too: https://mcuoneclipse.com/2018/05/31/gnu-link-time-optimization-finds-non-matching-declarations/
My experience with aggressive optimizations in gcc is that they are working well: whenever I have found issues they were not because of the optimization but because the optimized code uncovered a bug (e.g. some stuff not properly initialized): without optimization things were just running fine by pure luck, and the optimization exposed the problem.
BTW: volatile does not help for any race condition or reentrancy problems: it only helps and is appropriate if a hardware register changes its value after a write to it, or if a read of the register has a side effect on that register. Nothing else. If volatile is used for anything else than hardware, this is usually sign of a programmers problem (there are a few exceptions, of course).
Erich