Hacker News new | ask | show | jobs
by ithkuil 587 days ago
For the uninitiated, branch prediction roughly works like this:

The CPU fetches instructions from memory well ahead of actually decoding what the instructions are. In case of variable length instruction sets such as x86 that also means the cpu has no ability to "peek ahead" in the instruction stream to find out if there is a branch.

But don't despair, there is a trick:

Each instruction (obviously) has an address. So if you had an associative memory (think of it as a hash map) that stored a pair of (address of a branch; target address) then you can consult this memory while you're fetching instructions to feed to the decoder stage of the pipeline.

When the address of the next instruction you're about to fetch is found in that associative memory you get the address of where the rest of the instruction stream lives. I.e. instead of fetching the next word sequentially you continue fetching from whatever address was found by the lookup.

Now, when you actually end up executing the instructions it may turn out that the target address suggested by that lookup memory was wrong. In that case you just flush the pipeline and start fetching again from the actual target (and you update the branch predictor associative memory).

This basic model works for conditional branches, unconditional and indirect branches too but in practice there are more tricks. Some indirect jumps like returns exploit the natural call/ret pairing as described elsewhere in this thread. Conditional branch entries may contain an extra bit taken/not-taken etc.

But the main idea is more or less this.

To "mispredict" an unconditional jump for example all it takes is to modify the code so that the instruction points to a different target.

If the branch predictor target address still points to the old target, that address will be prefetchec and a stall will be caused. No big deal in practice.

1 comments

Your explanation is amazing. Thanks

How would this happen in practice?

> To "mispredict" an unconditional jump for example all it takes is to modify the code so that the instruction points to a different target.

Perhaps a jump to a pointer that changed value? Or maybe JIT code that was optimized during runtime?

Jumping to a destination via pointer that changed value is a misprediction of an indirect jump and that's common.

More uncommon but technically possible is to mispredict a unconditional direct jump.

For that to happen the code itself has to change.

Indeed JIT is a common cause of mutable code at runtime.

But also unmapping a library and remapping another library in the same memory range can also effectively cause the same address to contain a different instruction that the one predicted but the branch prediction logic (likely not even a branch instruction)