When a processor executes a single instruction, this can be assumed to be an atomic operation. (Like a database committing a transaction that updates the CPU state, not necessarily in terms of effects on memory visible from other cores.)
So an interrupt or exception happens before or after an instruction, not in the middle.
But how does that work when the processor uses pipelining? The instruction is executed in a number of steps, in parallel with many other instructions, all at different steps. But what if one of those other instructions interferes with ours? How can the processor "roll back" the effects of the instruction, or avoid interference altogether?
There are many strategies employed by various processors, I am sure. I once had a project where I added pipelining to a simulated processor. The techniques I employed were
Bubbling. For certain operations that have a chance of interfering with later instructions, I know how far back the interference might occur. For example, if a conditional jump evaluation is not complete until the following instruction has already passed through one stage of the pipeline, I might place what is effectively a NOP into the pipeline directly behind the conditional jump.
Forwarding. Under a couple conditions I was able to see that I needed a value from the instruction that was a stage or two ahead of the current one, but that had not yet been copied into the register/gate that it normally accesses it from. In this case, it accesses it directly from the later stage.
Branch Prediction and correction. The prediction part isn't so much about how to avoid collisions, but it is important to note. In the case of conditional jumps, you want to make a prediction about what will occur and load the next instruction into the pipeline as early as possible. I always assumed that the condition would evaluate such that a jump is NOT taken, because then I can immediately load the next instruction into the pipeline without first evaluating the jump address, thereby avoiding the need for a bubble.
When the prediction comes true, yay, I am happy. If the prediction does not come true, then I need to negate the effect of the next instruction that we optimistically started up early. I did this by switching a signal to the nand gates within the previous couple pipeline stages to effectively NOP out the instruction that was currently executing there.
This is what I remember from my only personal experience. I took a look at the wikipedia page for Instruction Pipeline and see some of those same ideas present, with far better explanation, I'm sure :) http://en.wikipedia.org/wiki/Instruction_pipeline