Data Hazards and Forwarding

Imagine a five-stage pipelined processor. It executes three back-to-back instructions that all touch register `t0‘.Theinstructionthatcomputes‘t0`. The instruction that computes `t0` has not finished writing its result back to the register file. This happens by the time the next instruction tries to read it. If the pipeline naively lets that next instruction read `$t0` anyway, it reads a stale value. The program then silently produces the wrong answer. This is a data hazard. It is one of the fundamental correctness problems introduced by The Pipelined Datapath. Data hazards matter for an important reason. Pipelining is the single most important technique for making processors fast without shrinking transistors. Pipelining only works if overlapping instruction execution never changes the answer a sequential machine would give. Every modern CPU must solve this problem. This includes everything from a microcontroller to a superscalar out-of-order server chip. Instructions constantly depend on the results of instructions that immediately precede them. Examples include arithmetic chains, address computations, and loop counters. A processor that stalled the pipeline every time this happened would lose most of the benefit of pipelining in ordinary code. The elegant fix is forwarding, also called bypassing. This means routing the result of an instruction directly from the pipeline stage that produces it. It goes straight to the pipeline stage that needs it. This happens without waiting for that result to be written into the register file. Forwarding turns what looks like an unavoidable stall into a wire and a multiplexer. It is one of the cleanest examples in computer architecture of solving a correctness problem. It does this with extra hardware paths instead of extra time. This article builds the forwarding mechanism from first principles. It defines data hazards precisely (RAW, WAR, WAW). It derives exactly which forwarding paths a classic five-stage pipeline needs. It gives the forwarding-unit control logic as pseudocode. It also proves that the resulting datapath produces the same results as sequential execution. It closes with the one case forwarding cannot fix by itself. That case is the load-use hazard. This is picked up in the article on Stalling and the Load-Use Hazard.

Key takeaways
  • A data hazard occurs when an instruction needs a value that a nearby, not-yet-finished instruction is producing.
  • In a pipeline that reads registers early and writes them late, a naive design reads stale data — this is the RAW hazard.
  • Forwarding (bypassing) copies a result from a later pipeline stage's output latch straight to an earlier stage's input, skipping the register file.
  • A forwarding unit compares source and destination register numbers across pipeline stages and drives ALU-input multiplexers.
  • Forwarding costs only wires, comparators, and muxes — it adds no cycles for most RAW hazards in a simple in-order pipeline.
  • One case — a load immediately followed by a use of its result — cannot be forwarded fast enough and needs a one-cycle stall.
The core insight

Forwarding resolves a data hazard by routing a computed value directly between pipeline stages, so instructions never have to wait for it to reach the register file.

The idea

A data hazard arises when two instructions that are close together in the pipeline access the same register, and the order in which the pipeline would naturally access it differs from program order. The classical taxonomy has three kinds, defined relative to a pair of instructions ii (earlier) and jj (later):

  • RAW (read-after-write): jj reads a register that ii writes. This is a true dependency — jj genuinely needs ii's result. If jj reads before ii writes, it gets the wrong value.
  • WAR (write-after-read): jj writes a register that ii reads. If jj writes before ii reads, ii picks up the wrong value. This is a false (name) dependency — no real data flows between them.
  • WAW (write-after-write): ii and jj both write the same register. If they complete out of order, the register ends up with ii's value instead of jj's.

In the classic in-order, single-issue five-stage pipeline (IF, ID, EX, MEM, WB), only RAW hazards actually occur, because instructions read operands in ID and write results in WB, always in program order and always with each instruction's ID stage happening before later instructions' ID stages, and WB happening in the same relative order. WAR and WAW require instructions to be reordered or to read/write out of program sequence, which does not happen in this simple in-order design; they become real concerns only in more advanced techniques such as out-of-order execution, where Structural Hazards-style resource conflicts and reordering can let a later instruction's write or read race ahead. So the problem to solve here is exactly RAW: an ALU instruction produces a value at the end of its EX stage (or MEM/WB for loads), but that value logically belongs to the destination register the instant the instruction is "conceptually" done, long before it is physically written into the register file in WB. Forwarding (or bypassing) exploits the fact that the value already exists in a pipeline latch several cycles before WB; it just needs a wire and a multiplexer to deliver it early to any later instruction's ALU input, rather than forcing that instruction to fetch a stale copy from the register file.

How it works

Consider the standard five-stage MIPS/RISC-V-style pipeline. Each instruction passes through IF → ID → EX → MEM → WB, one stage per cycle, with the ID stage reading register operands from the register file and the WB stage writing a result back. Register-file reads and writes happen in the same cycle when they alias (write-then-read within a cycle, so a same-cycle WB/ID pair on the same register already works via internal register-file bypassing), but that alone does not fix the tighter cases. Take this instruction sequence: ``` add t0,t0, t1, t2;I1:t2 ; I1: t0 = t1+t1 + t2 sub t3,t3, t0, t4;I2:needst4 ; I2: needs t0, immediately after I1 defines it and t5,t5, t0, t6;I3:alsoneedst6 ; I3: also needs t0 or t7,t7, t0, t8;I4:alsoneedst8 ; I4: also needs t0, but by now WB has happened ``` Lay these out on a pipeline diagram (cycles across the top, stages across the rows): ``` Cycle: 1 2 3 4 5 6 I1 (add): IF ID EX MEM WB I2 (sub): IF ID EX MEM WB I3 (and): IF ID EX MEM WB I4 (or ): IF ID EX MEM WB ``` I1 computes `t0‘initsEXstage,whichfinishesattheendofcycle3,andthatvaluesitsintheEX/MEMpipelineregisteratthestartofcycle4.I2′sEXstageisalsocycle4—I2needs‘t0` in its EX stage, which finishes at the end of cycle 3, and that value sits in the EX/MEM pipeline register at the start of cycle 4. I2's EX stage is also cycle 4 — I2 needs `t0` exactly when I1's fresh result is sitting one stage ahead of it. Without forwarding, I2's ID stage (cycle 3) would have already read the register file and gotten the old, wrong value for `t0‘,andevenifIDhappenedlaterI1′sWBdoesnotoccuruntilcycle5.SothevaluemustberoutedfromtheEX/MEMlatch(producedattheendofcycle3)directlyintotheALUinputforI2′sEXstage(cycle4).Thisis∗∗EX−to−EXforwarding∗∗(alsocalledEX/MEMforwarding).I3′sEXstageiscycle5,whichisexactlywhenI1′sresultissittingintheMEM/WBpipelineregister(I1isinWBthatsamecycle).SoI3needsthevalueforwardedfromMEM/WBintoitsALUinput—∗∗MEM−to−EXforwarding∗∗.I4′sEXstageiscycle6,onecycleafterI1hasalreadywritten‘t0`, and even if ID happened later I1's WB does not occur until cycle 5. So the value must be routed from the EX/MEM latch (produced at the end of cycle 3) directly into the ALU input for I2's EX stage (cycle 4). This is **EX-to-EX forwarding** (also called EX/MEM forwarding). I3's EX stage is cycle 5, which is exactly when I1's result is sitting in the MEM/WB pipeline register (I1 is in WB that same cycle). So I3 needs the value forwarded from MEM/WB into its ALU input — **MEM-to-EX forwarding**. I4's EX stage is cycle 6, one cycle after I1 has already written `t0` back into the register file (WB happens in cycle 5, and by the conventional "write in first half, read in second half of the cycle" register-file discipline, or simply because ID for I4 is cycle 5 and WB for I1 is also cycle 5 with careful ordering, the value is available through the normal register read). I4 needs no forwarding at all — this is the case forwarding does not need to touch, because the pipeline's natural timing already delivers the correct value. This gives exactly two forwarding paths for a classic five-stage integer pipeline: 1. EX/MEM → EX — forward the ALU result computed one instruction ago. 2. MEM/WB → EX — forward the ALU result (or loaded value) computed two instructions ago, taken from the MEM/WB latch (this also covers forwarding a loaded value from a `lw` two instructions back, and it covers the case where EX/MEM forwarding was "skipped" because the intervening instruction did not need it). Two multiplexers are placed on the ALU's two input operands in the EX stage, and a small combinational block called the forwarding unit decides, every cycle, whether each ALU input should come from the register file (no hazard), from EX/MEM (forward from one instruction back), or from MEM/WB (forward from two instructions back).

Forwarding-unit logic

Let the pipeline registers carry, alongside data, the destination register number and a valid/regwrite flag for each in-flight instruction. Denote the instruction currently in EX as the consumer, and the instructions currently in MEM and WB as candidate producers. ``` // Registers available combinationally this cycle: // ID_EX.Rs, ID_EX.Rt -- source register numbers of instruction in EX // EX_MEM.RegWrite, EX_MEM.Rd -- info about instruction now in MEM // MEM_WB.RegWrite, MEM_WB.Rd -- info about instruction now in WB function FORWARDING_UNIT(ID_EX, EX_MEM, MEM_WB): ForwardA = "NONE" // selects source for ALU input A (Rs) ForwardB = "NONE" // selects source for ALU input B (Rt) // --- EX hazard: instruction in MEM (one ahead) defines a source of EX --- if EX_MEM.RegWrite and EX_MEM.Rd != 0 and EX_MEM.Rd == ID_EX.Rs: ForwardA = "FROM_EX_MEM" if EX_MEM.RegWrite and EX_MEM.Rd != 0 and EX_MEM.Rd == ID_EX.Rt: ForwardB = "FROM_EX_MEM" // --- MEM hazard: instruction in WB (two ahead) defines a source of EX, // but only if the EX/MEM case above did NOT already claim it // (EX/MEM hazard is closer in program order, so it takes priority) --- if MEM_WB.RegWrite and MEM_WB.Rd != 0 and MEM_WB.Rd == ID_EX.Rs and not (EX_MEM.RegWrite and EX_MEM.Rd != 0 and EX_MEM.Rd == ID_EX.Rs): ForwardA = "FROM_MEM_WB" if MEM_WB.RegWrite and MEM_WB.Rd != 0 and MEM_WB.Rd == ID_EX.Rt and not (EX_MEM.RegWrite and EX_MEM.Rd != 0 and EX_MEM.Rd == ID_EX.Rt): ForwardB = "FROM_MEM_WB" return ForwardA, ForwardB ``` ``` function ALU_INPUT(ForwardSelect, RegFileValue, EX_MEM_Value, MEM_WB_Value): if ForwardSelect == "FROM_EX_MEM": return EX_MEM_Value if ForwardSelect == "FROM_MEM_WB": return MEM_WB_Value return RegFileValue // "NONE": no hazard, use value read in ID ``` Two subtleties in this pseudocode are load-bearing, not stylistic:

  • Checking `Rd != 0` guards against architectures (like MIPS) where register `$0` is hardwired to zero and instructions that "write" to it (or have no destination) must never trigger a forward — otherwise two unrelated instructions that both nominally target register 0 would be treated as dependent.
  • Priority of EX/MEM over MEM/WB encodes program order: if both the instruction in MEM and the instruction in WB wrote the same register that EX now needs, the one in MEM is more recent (it is closer to EX in program order, having been issued later), so its value is the correct one and must win. Getting this priority backwards is a classic, silent correctness bug: the pipeline would forward a stale value from further up the pipeline even though a fresher one exists.
Figure
EX-stage forwarding paths in a five-stage pipeline
IF ID EX MEM WB MUX ALU input IF/ID ID/EX EX/MEM MEM/WB EX/MEM → EX (forward result from previous instr.) MEM/WB → EX (forward result from two instrs. back) Forwarding Unit compares Rd (EX/MEM, MEM/WB) to Rs, Rt of instr. in EX

Complexity & correctness

Space/hardware cost. Forwarding adds: two 3-to-1 multiplexers on the ALU inputs, a handful of extra wires carrying the EX/MEM and MEM/WB result values back to the EX stage, and a forwarding unit whose logic is a small, constant number of equality comparisons — O(1)O(1) comparators independent of program size, since it only ever compares the (fixed, small) register-number fields of at most three pipeline registers each cycle. This is negligible next to the ALU, register file, and caches. Compare this to the without-forwarding alternative, which requires no extra hardware but forces the compiler or hardware to insert stall cycles (bubbles) whenever a RAW hazard exists — a real time cost on essentially every dependent instruction pair, since dependent arithmetic chains are extremely common in real code (loop-carried accumulations, address arithmetic, pointer chasing).

Time cost. With full EX/MEM and MEM/WB forwarding as described, a RAW hazard between an ALU instruction and a consumer that is one or two instructions later costs zero extra cycles — the pipeline still issues one instruction per cycle. The only place this specific mechanism fails is when the producer is a load instruction and the consumer immediately follows it, because a load's data does not exist until the end of its MEM stage, one cycle later than an ALU instruction's EX stage — at that point the consuming instruction's EX stage has already happened. No forwarding path can send a value backward in time; this residual case needs a single stall cycle and is handled by hazard detection plus a bubble, covered in Stalling and the Load-Use Hazard.

Correctness argument. The proof obligation is: the pipelined, forwarding-augmented machine computes the same register-file contents (and takes the same architected steps) as a hypothetical unpipelined machine that executes instructions strictly one at a time, in program order. Sketch of the argument: 1. Invariant: at the moment any instruction's EX stage consumes a source register value, that value is the most recently written value for that register among all instructions preceding it in program order (or the initial register-file value if none precede it and write it). 2. In program order, define for each register rr the sequence of instructions that write rr. For any consumer instruction jj reading rr, let ii be the closest preceding writer of rr (if any). 3. Because instructions enter the pipeline in program order, one per cycle (in this simple in-order issue model), the pipeline-stage distance between ii and jj is exactly j−ij - i instruction slots, which is also exactly the number of cycles separating their EX stages. 4. Case j−i=1j - i = 1: ii's result is in the EX/MEM register exactly when jj is in EX — the forwarding unit's EX/MEM check (`EX_MEM.Rd == ID_EX.Rs/Rt`) fires and selects it. Case j−i=2j - i = 2: ii's result is in the MEM/WB register exactly when jj is in EX — the MEM/WB check fires. Case j−i≥3j - i \geq 3: ii has already completed WB before jj reaches ID, so the ordinary register-file read in jj's ID stage already returns the correct, updated value; no forwarding is needed. 5. The priority rule (EX/MEM over MEM/WB) guarantees that if two preceding instructions both wrote rr (say at distances 1 and 2), the nearer one — which is the true most-recent writer in program order — wins, preserving the invariant even when the naive register-name match alone is ambiguous. 6. The `Rd != 0` guard prevents false hazards on architectures with a hardwired zero register, preserving the invariant that "no write" is never mistaken for "a real write." By induction over instructions in program order, the invariant holds for every instruction, so every read in the pipelined machine sees exactly the value a sequential machine would have produced — establishing that forwarding preserves the sequential-semantics contract that pipelining as a whole must not violate.

Tradeoffs, honestly. Forwarding is not free: it adds combinational-logic depth and mux delay directly into the EX stage's critical path, potentially lengthening the clock period rather than the instruction count — a classic time/complexity-of-hardware tradeoff rather than a time/instruction-count one. Real designs must balance how many forwarding paths to build (from EX/MEM, MEM/WB, and in more aggressive designs even further back, or from store-buffers, floating-point pipelines, etc.) against the wiring cost and the mux delay added to every instruction's ALU input, whether or not it needs forwarding that cycle.

Worked example

Trace this instruction sequence through the pipeline, register file initially `t1=10,t1=10, t2=20, t4=1,t4=1, t6=2`: ``` I1: add t0,t0, t1, t2;t2 ; t0 = 10 + 20 = 30 I2: sub t3,t3, t0, t4;t4 ; t3 = 30 - 1 = 29 (needs I1's t0,oneinstr.back)I3:andt0, one instr. back) I3: and t5, t0,t0, t6 ; t5 = 30 & 2 = 2 (needs I1's t0, two instrs. back) I4: add t7,t7, t3, t5;t5 ; t7 = 29 + 2 = 31 (needs I2's t3,twoback;I3′st3, two back; I3's t5, one back) ```

CycleI1 (add)I2 (sub)I3 (and)I4 (add)Forwarding action this cycle
1IF—
2IDIF—
3EX (computes 30)IDIFI1's ALU result 30 latched into EX/MEM at end of cycle
4MEMEXIDIFForwardA for I2 = FROM_EX_MEM (EX_MEM.Rd=t0==IDEX.Rs=t0 == ID_EX.Rs=t0) → uses 30 instead of stale register-file value; I2 computes 29
5WBMEMEXIDForwardA for I3 = FROM_MEM_WB (MEM_WB.Rd=t0==IDEX.Rs=t0 == ID_EX.Rs=t0, no EX/MEM match since I2 writes $t3) → uses 30; I3 computes 2. Also, I2's result 29 now latched into EX/MEM.
6WBMEMEXForwardA for I4 = FROM_MEM_WB (MEM_WB.Rd=t3fromI2==IDEX.Rs=t3 from I2 == ID_EX.Rs=t3) → uses 29. ForwardB for I4 = FROM_EX_MEM (EX_MEM.Rd=t5fromI3==IDEX.Rt=t5 from I3 == ID_EX.Rt=t5) → uses 2. I4 computes 31.
7WBMEM—
8WB$t7 = 31 written to register file

Every dependent value (`t0‘usedtwice,‘t0` used twice, `t3` and `$t5` both used by I4 in the same cycle from different forwarding sources) reaches its consumer through a forwarding path, and the pipeline never stalls — four instructions complete in 8 cycles (versus 4 in a fully sequential 5-stage-latency machine that never overlaps, which would take 20 cycles), demonstrating forwarding's throughput benefit even under dense dependencies. Note cycle 6 in particular: I4 needs two different registers from two different distances simultaneously, and ForwardA and ForwardB are computed independently — this is exactly why the multiplexers and forwarding logic are duplicated per ALU input rather than shared.

Common pitfalls & traps

PitfallWhy it happensConsequence
Forgetting the `Rd != 0` (or "no destination") guardTreating every instruction as if it always writes a real registerFalse hazards forward garbage between unrelated instructions that both nominally target a hardwired-zero or don't-care destination
Wrong priority between EX/MEM and MEM/WB forwardingNot reasoning about which producer is closer in program orderPipeline forwards a stale value from two instructions back even though a fresher one exists one instruction back
Assuming forwarding fixes load-use hazardsTreating all RAW hazards as identical in timingA load's data isn't ready until MEM completes; an immediately following consumer's EX has already passed — this needs a stall, not just a mux, see Stalling and the Load-Use Hazard
Only forwarding to the EX stageForgetting that stores, branches, or other stages may also consume register values that could be staleBranch-comparison hazards or store-data hazards go unhandled if the design only wires forwarding into the ALU
Comparing register values instead of register numbersConfusing what the hazard-detection hardware actually has available combinationallyThe forwarding unit must compare register indices (which are known early via decode), not compare data values (which is what's actually in question)

Pioneers & history

Forwarding grew directly out of the pipelining work pioneered for early high-performance computers, where designers first confronted the fact that overlapping instruction execution creates genuine, hardware-visible ordering hazards that must be resolved without breaking the illusion of sequential execution.

Origins · 1 minJohn L. Hennessy and how this was discovered
JL
John L. Hennessy
born 1953 · Co-designer of the MIPS architecture and co-author, with David Patterson, of the textbook tradition that formalized pipelining, hazards, and forwarding as core undergraduate curriculum

History
1964
— IBM System/360 Model 91 introduces Tomasulo's algorithm, an early hardware mechanism for resolving data dependencies dynamically.
1975
— CDC and IBM pipeline research formalizes the RAW/WAR/WAW hazard taxonomy used to classify dependency conflicts.
1981
— Stanford MIPS project (Hennessy) and Berkeley RISC project (Patterson) build simple pipelined processors where explicit forwarding paths become a standard, teachable datapath component.
1985
— MIPS R2000 ships as a commercial RISC pipeline relying on forwarding (and compiler-visible delay slots) to hide hazard latency.
1990
s — Superscalar and out-of-order designs (Tomasulo-style reservation stations, register renaming) generalize forwarding into full-blown result buses and bypass networks across many functional units.

Where this matters

Every classroom RISC-V or MIPS pipeline simulator implements exactly the EX/MEM and MEM/WB forwarding paths described here, and every real in-order embedded core — from ARM Cortex-M pipelines to simple RISC-V cores used in microcontrollers — needs equivalent bypass logic to keep single-issue throughput close to one instruction per cycle. In high-performance out-of-order superscalars (modern x86 and ARM server/desktop cores), the same idea scales up dramatically: instead of two forwarding paths, there is a bypass network connecting the outputs of many functional units (integer ALUs, multipliers, load/store units, floating-point units) to the reservation stations or issue queues of many other units, all governed by tag-matching logic that generalizes the simple register-number comparison used here. Compilers and instruction schedulers also reason about forwarding latency directly: they know which operand a just-issued instruction can supply "for free" next cycle versus which result needs extra cycles to become visible, and they reorder independent instructions to hide any residual latency — a compiler-side complement to the hardware technique, and a bridge to the broader story of hazards continued in Control Hazards.

Ask the tutor: How does the forwarding-unit design change once the pipeline supports a load-use stall alongside these two forwarding paths?

Does forwarding eliminate all stalls in a pipelined processor?

No. Forwarding eliminates stalls for RAW hazards between an ALU-type producer and any later consumer, because the producer's result exists in a pipeline latch (EX/MEM or MEM/WB) before the consumer's EX stage needs it. It cannot eliminate the load-use hazard, where the producer is a load instruction: the loaded value isn't ready until the end of the MEM stage, which is too late for an immediately following instruction's EX stage in the very next cycle. That specific case still requires a one-cycle stall (a bubble) inserted by hazard-detection hardware, covered separately under load-use hazards. Control hazards from branches are a different category entirely and need their own resolution techniques such as prediction or delay slots.

Why doesn't forwarding just add a cycle for every dependency, the way stalling does?

Forwarding works because the value a later instruction needs already exists physically in a pipeline register several cycles before it would normally reach the register file via writeback — the pipeline just hadn't wired a path to read it early. Since combinational logic (a multiplexer selecting among a few sources) operates within a single clock cycle, routing that already-computed value directly to where it's needed costs no extra time, only extra wiring and mux logic. Stalling, by contrast, is needed only when the value literally does not exist yet at the moment the consumer's EX stage occurs — no amount of wiring can deliver a number that hasn't been computed.

How does the forwarding unit know which register numbers to compare, given that it must act in the same cycle?

Register source and destination numbers are decoded very early — during the ID stage — and then carried forward as metadata alongside the data in each pipeline register (ID/EX, EX/MEM, MEM/WB). The forwarding unit's comparisons are therefore just equality checks on small integers already sitting in latches at the start of the cycle; they involve no ALU computation and settle well within the EX stage's clock period. This is why forwarding is described as adding "comparators and multiplexers," not additional pipeline stages — the register-number bookkeeping was already free, since decode has to identify source and destination registers regardless.

Can a single instruction need forwarding on both of its operands simultaneously, from different sources?

Yes, and this is common. Consider an instruction whose first operand was defined by the immediately preceding instruction (needs EX/MEM forwarding) and whose second operand was defined two instructions earlier (needs MEM/WB forwarding). The forwarding unit computes ForwardA and ForwardB independently, each with its own priority logic between EX/MEM and MEM/WB, and each drives its own dedicated multiplexer on the corresponding ALU input. There is no conflict because the two operand paths are entirely separate pieces of hardware; the only shared resource is the small comparator logic, which is replicated per operand.

Is forwarding specific to five-stage pipelines, or does it generalize to deeper or wider pipelines?

The core idea — route a result from wherever it is first computed straight to wherever it is next needed, skipping the architectural register file — generalizes completely. Deeper pipelines need forwarding paths from more stages further back; wider, superscalar and out-of-order pipelines need a full bypass network connecting many functional-unit outputs to many reservation stations, with tag-based matching replacing simple register-number comparison. The five-stage case is the cleanest teaching example because it has exactly two nontrivial forwarding distances, but the underlying principle — never wait for writeback if the value already physically exists — is identical in every pipelined machine.

What happens if two forwarding sources both match the same operand at once?

This can only happen if two different in-flight instructions wrote (or claim to write) the same destination register, and both are candidates to forward to the same consumer. Because instructions retire writeback in program order in a simple in-order pipeline, the instruction currently in MEM (heading to EX/MEM) is always more recent in program order than the instruction currently in WB (heading to MEM/WB) when both match the same register. The forwarding unit's priority rule — always prefer EX/MEM over MEM/WB when both match — resolves the conflict by choosing the most recent value, which is exactly the value a sequential machine would have used.

Does forwarding change what the compiler or programmer needs to do?

In a pipeline with full forwarding for ALU-to-ALU dependencies, the compiler can schedule dependent arithmetic instructions back-to-back with no performance penalty and no correctness concern — the hardware guarantees the right value appears, transparently. The programmer or compiler only needs to be aware of the residual hazard forwarding cannot cover, the load-use hazard, where inserting an independent instruction between a load and its first use avoids a stall the hardware would otherwise insert automatically. Beyond that, forwarding is designed to be invisible above the instruction set architecture: it affects timing and internal wiring, never the observable semantics of a program.

Before this

Where this leads