What a Data Hazard Is
In a pipelined processor, several instructions are in progress simultaneously. A Data Hazard occurs when an instruction needs to use a value that a preceding instruction, still moving through the pipeline, has not yet finished computing and written back to the register file.
Consider this sequence of instructions:
add a, b, c
sub d, a, eThe second instruction needs the value of a, but the first instruction has not yet reached its write-back stage by the time the second instruction reaches decode. Without any correction, the second instruction would read a stale, outdated value of a from the register file.
The First Solution: Forwarding
Forwarding (also called Bypassing) solves this without losing any clock cycles, by adding extra wiring that routes a result directly from where it is computed to where it is needed, skipping the normal path through the register file entirely.
Without forwarding:
add computes result → written to register file (cycle 5)
sub needs result → read from register file (cycle 3) — too early, wrong value
With forwarding:
add computes result in EX stage (cycle 3)
result is forwarded directly into sub's EX stage (cycle 4)Because the result the second instruction needs is available right after the first instruction's execute stage, extra hardware paths can feed that value forward in time to reach the second instruction exactly when it needs it, avoiding any wasted cycles.
When Forwarding Alone Is Not Enough
Forwarding solves most data hazards, but not all of them. A Load-Use Hazard occurs specifically when an instruction immediately following a load needs the value that load is retrieving:
ld a, 0(b)
add d, a, eThe loaded value is not available until the end of the memory access stage, which happens later than the point where the following instruction's execute stage needs it, even with forwarding wiring in place. In this specific case, forwarding cannot deliver the value in time.
The Second Solution: Stalling
When forwarding cannot resolve a hazard in time, the pipeline must insert a Stall (also called a Bubble): the dependent instruction, and everything behind it, is held in place for one clock cycle while the needed value becomes available, at the cost of one cycle of lost throughput.
ld a, 0(b) : IF ID EX MEM WB
[bubble inserted]
add d, a, e : IF ID -- EX MEM WBThis deliberately wastes one cycle rather than producing an incorrect result, which is always the correct tradeoff, since accuracy cannot be sacrificed for speed.
Why This Distinction Matters for Compiler Design
Because load-use stalls specifically cost a cycle, compilers that understand this hazard can sometimes reorder independent instructions to be placed immediately after a load, filling what would otherwise be a wasted stall cycle with useful work instead — a technique called Instruction Scheduling, which relies directly on understanding this hazard's exact cause.