The compiler takes the wheel: what a VLIW is

After this lesson you will read a VLIW bundle slot by slot, say who handles each hazard — the compiler, or the little hardware that is left — and weigh what static scheduling buys against what it costs.

In 1984 Josh Fisher — the researcher who coined the term instruction-level parallelism — could not find a company willing to build his very-long-instruction-word machine, so he founded one. Multiflow's TRACE 28/200 issued up to 28 operations from a single 1024-bit instruction word.

Twenty-eight operations leave in the same clock cycle, and in a VLIW no circuit compares their registers to see whether one of them needs the result of another. The notes say it plainly: "No need for runtime dependency checking anymore" (p. 267). How can such a machine compute the right answer?

And if it can, why did the idea stay out of general-purpose processors? Multiflow closed in 1990 after about 125 machines; Intel's Itanium, built on the same idea, became the "Itanic" — a nickname coined the very day its name was announced in 1999; and the processor in your laptop still checks every dependence in hardware. Keep the two questions: section 2 answers the first, section 6 the second.

One long word, one slot per unit

Your first architecture course built a pipeline that issues one instruction per cycle. Lesson 8 widened it: a superscalar issues several per cycle, and its hardware decides at run time which ones may go together. A VLIW issues several per cycle too, but the decision was taken earlier, by the compiler, and written into the program.

VLIW (Very Long Instruction Word) — a processor that executes several operations per clock cycle and relies on the compiler, not on dynamic hardware, to detect and schedule the parallelism. Independent operations are grouped into one long instruction word, the bundle, made of fixed slots, each corresponding to a functional unit (p. 266).

The notes sum the idea up in one highlighted line: "VLIW architectures represent a shift of complexity from the hardware to the compiler" (p. 267). A statically scheduled processor trusts "the compiler to 'fill the pipeline' and avoid hazards at compile time" (p. 266).

The operations inside a bundle are also called "syllables" (p. 267). A 4-issue VLIW has four slots per bundle, a 5-issue one five. The 2024 and 2026 papers use a 3-issue machine, June 2025 a 4-issue one with a second memory unit, and the sentence that describes the machine comes back almost word for word in all three.

"a 3-issue VLIW machine with fully pipelined functional units: 1 Memory Units with 3 cycles latency; 1 FP ALUs with 3 cycles latency; 1 Integer ALU with 1 cycle latency to next Int/FP/L/S & 2 cycle latency to next Branch. The branch is completed with 1 cycle delay slot (branch solved in ID stage). No branch prediction. In the Register File, it is possible to read and write at the same address at the same clock cycle."

Read it once slowly: every schedule of this lesson uses it.

Put a loop on that machine: the first loop of the 22 June 2026 paper.

LOOP1: LD    F2, 0(R1)      ; I0
       FADD  F6, F2, F2     ; I1
       SD    F6, 0(R1)      ; I2
       ADDUI R1, R1, 4      ; I3
       BNE   R1, R3, LOOP1  ; I4

The compiler fills a timetable with one row per clock cycle and one column per unit. The LD goes in C1. The FADD needs F2, which the memory unit delivers 3 cycles later: C4. The SD needs F6, ready 3 cycles after the FADD: C7. The ADDUI overwrites R1, which the SD still has to read; since the register file allows a read and a write of the same register in one cycle, the ADDUI may share C7 with the SD. The BNE needs the new R1, and an integer result reaches a branch 2 cycles later: C9. C10 is the delay slot.

Table 1. LOOP1 on the papers' 3-issue VLIW, one row per bundle. Every empty cell is a NOP in the program.
Memory UnitFloating Point UnitInteger Unit
C1LD F2, 0(R1)
C2
C3
C4FADD F6, F2, F2
C5
C6
C7SD F6, 0(R1)ADDUI R1, R1, 4
C8
C9BNE R1, R3, LOOP1
C10(delay slot)

Ten bundles of three slots: 30 slots, 5 operations, 25 empty. In the exam you leave the empty cells blank — the papers say "Please do not write in NOPs" — but in the program each one is a NOP, an operation that does nothing and still occupies its slot of the word (p. 266). The example formula sheet on the course page, prepared by a student, measures the waste in one line:

\[ \text{code efficiency} = \frac{IC}{\#\text{cycles} \times \#\text{issues}} \] \[ \text{LOOP1:}\quad \frac{5}{10 \times 3} = 0.17 \]
\(IC\)
operations scheduled in one iteration (NOPs not counted)
\(\#\text{cycles}\)
length of the iteration, delay slot included
\(\#\text{issues}\)
slots per bundle, the issue width

The papers ask three more numbers about the same table, all treated in lesson 20: the critical path, by the papers' convention the 10 cycles of the iteration with its delay slot; the CPI, cycles over instructions, 10 / 5 = 2; and the FP operations per cycle, 1 / 10 = 0.1.

Fig. 1 zooms into one row, bundle C7. Three rules keep such a word cheap for the hardware (pp. 267, 269). There is a single program counter, and each fetch brings one whole bundle. A bundle holds at most one branch, so each cycle has one next-PC decision to make. And each slot is wired to its own unit, so the decoder routes nothing.

PC I-cache one bundle per fetch Memory slot FP slot Integer slot SD F6, 0(R1) NOP ADDUI R1, R1, 4 no arbiter Memory FP Integer latency 3 latency 3 1 (2 to a branch)
Fig. 1. Bundle C7 of LOOP1. One program counter fetches one long word per cycle; each slot is wired to its own fully pipelined unit (the three ticks), so no arbiter decides who goes where. The FP slot has no work in this cycle and carries a NOP.

A bundle is one row of the compiler's timetable: one slot per functional unit, an operation or a NOP in each, fetched by a single program counter.

Who handles each hazard

"The key difference compared to traditional superscalar processors lies in who decides what can run in parallel" (p. 266). A superscalar's hardware analyses dependences while the program runs; in a VLIW "this analysis is performed entirely at compile time" (p. 266). The compiler's job list on the same page has four lines: identify independent operations, solve structural hazards, solve data hazards, insert NOPs where nothing fits. Table 2 sets the two answers side by side.

Table 2. Who handles each hazard: the compiler of a VLIW, before the program runs, against the hardware of the dynamically scheduled superscalar of lessons 8–18, while it runs.
HazardVLIW:
the compiler
Superscalar:
the hardware
RAWplaces the consumer at least one latency after the producer; fills the gap with independent operations or NOPsholds the consumer until the operand arrives (scoreboard, reservation stations)
WAR, WAWissues a WAR writer no earlier than the last reader (the same cycle is allowed) and makes a WAW writer's result land after the earlier one — or renames to another registerrenames into reservation-station or ROB tags (the scoreboard of lesson 9 stalls the writer instead)
Structuralone operation per slot: the format itself keeps two operations off one unitthe issue logic checks for a free unit and station
Branchmay give a static hint; the hardware detects a misprediction and flushes (p. 271)predicts dynamically and flushes on a misprediction
Data-cache misscannot be foreseen: the processor stalls (p. 272)independent instructions keep executing around the miss

The RAW row is where the compiler earns its keep. The notes' example: if C = A * B takes two cycles, "the compiler may schedule a NOP after it, before allowing an instruction that depends on C to execute" — or an independent instruction that has to run anyway (p. 270). A pipelined unit does not change the rule. The papers' FP ALU is fully pipelined: it accepts a new FADD every cycle, yet each FADD's result appears 3 cycles after it issues.

"Fully pipelined means latency 1." No. Pipelining fixes the throughput, one new operation per unit per cycle; the latency stays. In LOOP1 an independent FADD could issue in C5, right behind the first one, while the SD that needs F6 cannot go before C7.

Name dependences cost less: a WAR or a WAW shares a register name but carries no value (lesson 7). The compiler resolves them by "appropriately choosing the timing of operations" or by "using register renaming techniques" (p. 270), that is, by writing the result into another register. Structural hazards disappear "through scheduling decisions" (p. 270): with one memory slot, two memory operations simply never share a bundle.

So nothing on the chip checks the schedule. A schedule that ignores a latency does not run slower: it computes a wrong value. That is the meaning of "This static management of hazards is crucial because the hardware in VLIW processors does not dynamically detect or resolve data hazards during execution" (p. 270) — and the answer to the first question of the puzzle: the TRACE did not check its 28 operations because its compiler already had.

Who schedules? The same loop on two machines

Both machines have the papers' three units and latencies and see one iteration at a time (no branch prediction). The superscalar receives the plain code and applies the compiler's rules in hardware, without renaming: every cycle its issue logic re-examines each waiting instruction — operands ready, unit free, no earlier reader or writer of the register it overwrites still pending — and sends the oldest ready one to each unit. The VLIW receives bundles the compiler packed with the same rules, and sends each slot straight to its unit. Step through the cycles; every operation and NOP counts 4 bytes of code.

  1. LOOP1, superscalar. Before stepping: in which cycle will the comparators let the FADD go, and what will they say about it in C2? Then step to C4.
    What you should see

    In C1, C2 and C3 its check reads "F2 ready C4": the load issued in C1 delivers F2 three cycles later. In C4 it issues. The same checks hold the SD until C7 and the BNE until C9 — the hardware discovers, cycle by cycle, the timetable of Table 1.

  2. Switch to VLIW with LOOP1. Predict the code size and the number of NOPs, then read them. Did the code efficiency change?
    What you should see

    30 slots, 5 operations, 25 NOPs: 120 bytes instead of 20, six times the code. The code efficiency is 0.17 in both modes — the slots are just as empty on the superscalar, but there the empty slots are lost at run time and take no room in memory.

  3. Choose the July 2024 loop. Which cycle starts three instructions together, and who found that group in each mode?
    What you should see

    C4: SD F8, FADD F6 and ADDUI R2 — the ADDUI may join because the register file lets it overwrite R2 in the cycle in which SD F8 reads it. On the superscalar the comparators find the group in C4 of every iteration; on the VLIW the compiler found it once and wrote it into bundle C4.

  4. Compare the readout "dependence checks at run time" in the two modes for the July 2024 loop. What does each machine pay that the other does not?
    What you should see

    The superscalar makes 44 instruction checks per iteration and keeps the code at 40 bytes; the VLIW makes none and stores 120 bytes, 80 of them NOPs. Same cycles, same groups, same efficiency: the parallelism is identical; only who finds it, when, and who pays for it change.

In a VLIW the compiler resolves RAW, WAR, WAW and structural hazards with latencies it knows in advance; the hardware only stalls on what nobody can foresee, a cache miss, and flushes a mispredicted branch.

The hardware that stays

Moving the scheduler into the compiler removes the scoreboard, the reservation stations and the dependence checks — "no complex dynamic scheduling (like Tomasulo or Scoreboarding)" (p. 275). It does not remove everything. Fig. 2 redraws the VLIW datapath of the notes (p. 268).

I$ D 4 ops RF 8 reads dispatch network FU1 FU2 … L/S D$ RF 4 writes D$ IF ID RR EX WB
Fig. 2. The VLIW datapath of the notes (p. 268), redrawn for a 4-issue machine. One bundle enters per cycle and the decoder splits it into its operations; the register file serves all of them at once, 8 reads and 4 writes per cycle; the two crossbars form the dispatch network, needed only when the units outnumber the slots.

It is the five-stage pipeline of your first course, widened, with a stage of its own for the register read and the data-cache access inside EX: Instruction Fetch, Instruction Decode, Register Read, Execution and Write Back (p. 268). IF fetches one bundle; ID splits it into its operations; from then on "each operation within a bundle proceeds independently through the functional units connected to the pipeline" (p. 268).

The register file is what the notes call "one of the non-trivial aspects of the hardware design" (p. 268). An operation reads up to two registers and writes one, and all the operations of a bundle do it in the same cycle: a 4-issue VLIW needs a register file able to "read eight source operands and write four destination results every clock cycle" (p. 268). This is the shared, multi-ported register file.

\[ \text{read ports} = 2k \] \[ \text{write ports} = k \]
\(k\)
issue width: operations per bundle, each with up to two sources and one destination (4-issue: 8 + 4; the papers' 3-issue machine: 6 + 3)

As background from the textbooks, not from the notes: every port adds its own access lines to every cell of the register file, so its area and power grow roughly with the square of the number of ports. That is the bill behind the clustered VLIWs of section 4.

Decoding stays trivial when there is one unit per slot: "each operation is directed straight to its corresponding functional unit for execution without the need for complex arbitration" (p. 269). With more units than slots, the machine needs a dispatch network — the crossbars of Fig. 2 — that forwards each operation, with its operands, to a free unit of the right kind (p. 269).

Two run-time events escape the compiler (p. 272). Whether a load misses in the data cache depends on the run: "If a cache miss occurs, the processor experiences a stall, which was not anticipated during static scheduling" — the whole timetable freezes until the data arrives. And a static branch hint can be wrong: the hardware must detect the misprediction and flush the bundles fetched behind it (pp. 271–272). Even a VLIW keeps an interlock for misses and flush logic for branches.

Gone: the hardware that finds parallelism. Kept: the pipeline, a register file with 2k read and k write ports, a dispatch network when units outnumber slots, a stall on a data-cache miss and a flush on a misprediction.

Every result needs a name: register pressure

Take the notes' example (p. 271). A bundle carries two FP operations of five cycles each; their destination registers "remain occupied for the entire duration of those five cycles". A new bundle issues every cycle, so by the time the first two results are written, five bundles have issued: 2 × 5 = 10 registers are reserved for results that do not exist yet. Put such operations in all four slots and you reach the notes' "up to twenty operations competing for register resources".

\[ N_{\text{flight}} = n \times L \]
\(N_{\text{flight}}\)
registers held by results in flight
\(n\)
results started per cycle (destinations per bundle, one bundle per cycle)
\(L\)
latency of those operations; add a term \(n_i L_i\) for each kind of unit

It is the count of people in a queue: arrivals per minute times minutes each one stays. This pressure on the register file is what the notes call register pressure.

Predict: every cycle, a 4-issue bundle starts one load (latency 3), two FP operations (latency 5) and one integer operation (latency 1). How many registers do results in flight hold?

Check yourself

One term per kind of unit: 1 × 3 + 2 × 5 + 1 × 1 = 14. Not 4 × 5 = 20 — that is the notes' "up to twenty", the worst case in which every slot holds a 5-cycle operation.

Renaming makes it worse (p. 271). To remove a WAR or a WAW, the compiler writes the later result into a different register: it "avoids conflicts" but "increases the number of physical register needed". In a VLIW those are the architectural registers, the F0 … F31 the instruction set names; there is no hidden pool behind them. Lesson 15 did exactly this to an unrolled loop: the two registers each copy writes became eight.

Compare Tomasulo. There the code keeps naming F2 and F4, and the hardware renames every result into a reservation-station tag that no program ever sees. The values in flight still need storage — but it is silicon, bought once with the chip, not names taken from the fixed budget of the instruction set.

The register-pressure counter

Twelve bundles issue one per cycle, each with some FP operations. Every bar is one result: the solid part while its operation is in flight (L cycles), the dashed part while the finished value waits to be read. One row is one place where a value lives, and the vertical dashed line counts the bars alive at one cycle once the pipeline has filled. On the VLIW each row is an F register the compiler must name, and the horizontal dashed line is how many it has; on the Tomasulo machine each row is a reservation station.

  1. The sliders start at the notes' example: two FP operations per bundle, latency 5, each result read as soon as it is ready. Predict how many F registers are busy once the first bundles have filled the pipeline, then read the counter.
    What you should see

    10 = 2 × 5. The staircase climbs by two rows per cycle for five cycles, then each new result takes the register a finished one has just freed: five bundles in flight, two results each.

  2. Put four operations in every bundle and find the notes' "up to twenty". The compiler still has 16 F registers. What can it do — and what can it not?
    What you should see

    20 values alive at once. With 16 registers, four results in flight have no register — the rows above the dashed line. Nothing is waiting (the wait is 0), so nothing can be spilled: a result in flight does not exist yet and cannot be stored. The compiler must start fewer operations, at most 16 / 5 = 3.2 per cycle, so three per bundle.

  3. Back to two operations per bundle, latency 5, but now each result waits 2 cycles before it is read, and only 12 registers are free. Predict the overflow first. Would spilling a waiting value to memory fix it?
    What you should see

    14 = 2 × (5 + 2) values alive, two more than the 12 registers. Spilling does not help: on the papers' memory unit the reload must issue 3 cycles before the use and holds its register from then on — longer than the 2-cycle wait it was meant to free — and the store and the reload take two memory slots. The compiler must start fewer operations, at most 12 / 7 ≈ 1.7 per cycle. A spill pays only for a value that waits longer than the reload takes.

  4. Keep the settings of task 2 (four operations, latency 5, 16 registers) and switch to "the hardware (Tomasulo)". Which number collapses, and which one does not move?
    What you should see

    The F registers the code must name drop to four (F2, F4, F6, F8) and nothing overflows, while the twenty values alive do not move: they now sit in reservation stations. Same bars, different owner: static scheduling moves the cost of renaming from hardware tags to architectural registers.

When the names run out, the compiler has the two ways out the widget shows (p. 271): "careful allocation strategies" — fewer operations in flight and a longer schedule — or "spilling values temporarily to memory": a store now and a reload later, both in memory slots, which pays only for a value that waits longer than the reload takes. The open challenges of the notes add a hardware remedy: clustered VLIW designs, several smaller register files, each serving a subset of the units (p. 274).

Static scheduling moves the cost of renaming from hardware tags to architectural registers: every result in flight needs a name the instruction set provides.

As fast as possible, without changing the answer

Code scheduling is "the process of rearranging program instructions to maximize parallelism during execution" (p. 275). Its goal is to reorder the instructions statically so that the execution time is minimised and semantic correctness is preserved: "we want the program to run as fast as possible without changing what it computes" (p. 275). On a VLIW nobody downstream reorders anything. If the compiler schedules badly, "the result is many NOPs and a waste of hardware resources" (p. 275).

Semantic correctness has a precise meaning (p. 275): every RAW is respected — a read comes after the write it depends on — and no WAR or WAW is violated; the scheduler analyses the dependence graph, reorders only when it is safe, and renames to remove false dependences. Try three tempting moves on LOOP1.

Table 3. Three moves on the schedule of Table 1, checked against the rules.
MoveRule at stakeVerdict
FADD F6, F2, F2 up to C2, to fill a slotRAW on F2: the FADD would read F2 before the load has written itwrong value
ADDUI R1, R1, 4 up to C1, "it has nothing to wait for"WAR on R1: the SD in C7 would read the new R1 and store into the next elementwrong address — legal only as SD F6, -4(R1), an index change the papers forbid
ADDUI R1, R1, 4 in C7, beside the SDWAR on R1, kept: the register file reads the old R1 and writes the new one in the same cycleallowed (Table 1)

The second move would even pay: with the ADDUI in C1 and the SD rewritten as SD F6, -4(R1), the BNE could close the iteration in C7 and LOOP1 would take 8 cycles instead of 10. The papers rule this out in so many words — "do NOT introduce any software pipelining, loop unrolling and modifications to loop indexes" — so in the exam you reorder, you do not rewrite.

Such a scheduler works on a basic block, "a straight-line code sequence without branches except at the entry and exit points" (p. 273). Basic blocks are short — "for typical programs, only around 4 to 7 instructions are found between branches" (p. 273) — and the RAW chains inside them limit the parallelism further. LOOP1 is a five-instruction block whose chain LD → FADD → SD leaves 25 of its 30 slots empty. Filling them needs techniques that look beyond one block: unrolling, software pipelining and trace scheduling, the subject of lesson 21.

A correct schedule keeps every RAW and lets no write overtake a read (WAR) or another write (WAW) of the same register; within those rules, the shortest one wins.

What you buy, what you pay — and the answer to the puzzle

The notes list three advantages (p. 273). A VLIW exploits ILP statically, with high performance "even without the complex runtime scheduling hardware used in superscalar designs". The compiler analyses the program "with a much larger instruction window than would be feasible in hardware" — a whole loop, a whole function — especially after unrolling or software pipelining. And less hardware means "smaller die areas", "cheaper production costs and lower power consumption, making VLIWs attractive for embedded applications"; the fixed format simplifies decoding, and adding a unit means adding a slot.

Against them stand four open challenges (pp. 273–274). The machine needs strong compiler technology, inside basic blocks and across them. Code grows, with fixed bundles and NOPs, and compression "add[s] extra decoding complexity". Register management suffers from renaming, which clustered designs only partly fix. And the fourth is "a major limitation", binary incompatibility: "Even small changes in the number of slots, the types of functional units, or their latencies can render code compiled for one VLIW processor incompatible with another, even if they share the same instruction set architecture" (p. 274).

C4 C5 C6 C7 C8 FADD F6 SD F6 F6 SD reads the new F6

Before: compiled for, and run on, an FP latency of 3

C4 C5 C6 C7 C8 FADD F6 SD F6 F6 SD reads the old F6 nothing checks

After: the same binary on an FP latency of 4

Fig. 3. Bundles C4 to C8 of LOOP1 on two implementations of the same instruction set. The binary fixes the SD in C7, three cycles after the FADD. With a 4-cycle FP ALU the value arrives for C8 (dashed cell), but the SD still issues in C7 and stores the F6 of the previous iteration — and no hardware notices. A dynamically scheduled machine would simply wait one more cycle.

A dynamically scheduled processor has no such problem: there "binary compatibility is preserved across generations" (p. 274). A just-in-time compiler could reschedule the binary for each new chip, but such solutions "introduce additional complexity and are rarely adopted in practice"; so VLIW processors live mainly "in embedded systems, where binaries can be compiled specifically for the target hardware" (p. 274).

Intel announced the name Itanium on 4 October 1999, and the Usenet nickname "Itanic" appeared the same day; it stuck as the sales forecasts kept shrinking. The first systems shipped in June 2001, the last shipments ended on 29 July 2021. As textbook background: its EPIC format was meant to soften one form of binary incompatibility, a change in the number of slots — 128-bit bundles carry three 41-bit operations and a 5-bit template whose "stop" bits mark where a group of independent operations ends, so that a wider successor could issue more of them at once from the same binary.

Now the second question. The TRACE's compiler knew every unit and every latency and wrote its answer into the bundles, NOPs included. The price is this section's list: a compiler that must find the parallelism, code that carries its empty slots, register names spent on results in flight, stalls on misses the schedule could not foresee — and above all a binary welded to the latencies of one machine.

A laptop must run yesterday's programs on tomorrow's chip, so it keeps the scheduler in hardware and pays for it in area and power. A signal processor or an embedded controller runs one binary on one chip, so it can let the compiler drive. As textbook background, not in the notes: the Texas Instruments C6000 signal processors are VLIWs, and many GPUs and AI accelerators schedule statically in the same spirit. Multiflow's machines did not survive, but its compiler did: Intel, HP, DEC, Fujitsu and others licensed it.

VLIW trades hardware for compiler work: simpler, cheaper, lower-power chips and a large scheduling window, paid for with code size, register names, stalls on unforeseen misses and binaries tied to one implementation.

Exam-style questions

Explain the main concepts and benefits of each one of the following techniques used by VLIW processors:

  1. Static scheduling by the compiler
  2. Bundle with fixed slots
  3. NOP insertion
  4. Single program counter with at most one branch per bundle
  5. Shared multi-ported register file
  6. Fully pipelined functional units with exposed latencies
  7. Register renaming by the compiler
  8. Hardware support for dynamic events (data-cache misses, branch mispredictions)
Show model answer

1. The compiler analyses data dependences and resource constraints at compile time and decides which operations execute together and in which cycle; the hardware executes the schedule without checking it. Benefit: no dynamic scheduling hardware (no scoreboard, reservation stations or run-time dependence checks), hence less area and power; the compiler sees a much larger instruction window than any hardware. Cost: it must know all latencies and cannot see run-time events.

2. Independent operations are grouped into one very long instruction word; each slot corresponds to one functional unit (e.g. Memory, FP, Integer). Benefit: one fetch delivers k operations, decode is trivial (slot i goes to unit i, no arbitration), and scaling means adding slots.

3. When no independent operation is available for a slot, or a latency must elapse, the compiler fills the slot with a NOP. Benefit: the fixed format and the timing stay explicit, so no stall logic is needed. Cost: wasted slots and larger code; code efficiency = IC / (#cycles × #issues).

4. One PC fetches one bundle per cycle, and a bundle contains at most one branch. Benefit: a single next-PC decision per cycle keeps branch handling simple (in the exam's model the branch is solved in ID with one delay slot).

5. All the operations of a bundle read their operands in the same cycle, and every unit can write a result in every cycle: two read ports and one write port per operation, 8 + 4 for a 4-issue machine. Benefit: every unit gets its operands and writes its result without a bottleneck. Cost: area and power grow quickly with the number of ports, which leads to clustered VLIWs (several smaller register files, each serving a subset of the units).

6. Every unit accepts a new operation each cycle, and its result is available after a fixed latency (e.g. Memory 3, FP 3, Integer 1, 2 to a branch); the compiler places each consumer at least one latency after its producer and fills the gap with independent operations. Benefit: one operation per unit per cycle with no interlocks. Cost: the schedule, hence the binary, depends on the latencies of one implementation.

7. To remove WAR and WAW dependences the compiler writes results into different architectural registers. Benefit: more freedom to reorder and overlap operations, more ILP. Cost: register pressure — every result in flight needs an architectural register; when the register file saturates, values are spilled to memory.

8. The compiler cannot know whether a load will miss in the data cache or whether a static branch hint is wrong. On a miss the processor stalls; on a misprediction the hardware flushes the wrongly fetched bundles. Benefit: correct execution despite unpredictable events, while the regular flow stays free of checks. Cost: a stall freezes the whole schedule, which assumed the hit latency.

The June 2026 paper asks exactly this shape — eight techniques, five lines each, for the Tomasulo-style superscalar; this is its VLIW twin. Give each sub-point its concept in one sentence and its benefit in the next; for 3, 5, 6 and 7 add the cost too, since there the benefit only makes sense against it. Use the precise terms (bundle, slot, NOP, latency, WAR / WAW, renaming, spill), and never let the VLIW hardware "check" or "rename" anything.

Let's consider the following LOOP1 code, where Ri are integer registers and Fi are floating-point registers:

LOOP1: LD    F2, 0(R1)
       FADD  F6, F2, F2
       SD    F6, 0(R1)
       ADDUI R1, R1, 4
       BNE   R1, R3, LOOP1

Given a 3-issue VLIW machine with fully pipelined functional units: 1 Memory Unit with 3 cycles latency; 1 FP ALU with 4 cycles latency; 1 Integer ALU with 1 cycle latency to next Int/FP/L/S & 2 cycle latency to next Branch. The branch is completed with 1 cycle delay slot (branch solved in ID stage). No branch prediction. In the Register File, it is possible to read and write at the same address at the same clock cycle. Considering one iteration of the loop, complete the following table (Memory Unit | Floating Point Unit | Integer Unit, rows C1 … C15) by using the list-based scheduling (do NOT introduce any software pipelining, loop unrolling and modifications to loop indexes) on the 3-issue VLIW machine including the BRANCH DELAY SLOT. Please do not write in NOPs. (C1 is given: LD F2, 0(R1) in the Memory Unit.)

How long is the critical path for a single iteration? What CPI did you achieve? What performance did you achieve in FP ops per cycle? How much is the code efficiency?

Extra question of this lesson, not of the papers: the same loop compiled for a 3-cycle FP ALU (SD in C7) runs unchanged on this machine. Which instruction reads a wrong value?

Show model answer
Memory UnitFloating Point UnitInteger Unit
C1LD F2, 0(R1)
C2
C3
C4FADD F6, F2, F2
C5
C6
C7
C8SD F6, 0(R1)ADDUI R1, R1, 4
C9
C10BNE R1, R3, LOOP1
C11(delay slot)

FADD at C1 + 3 = C4 (F2 from the memory unit); SD at C4 + 4 = C8 (F6 from the FP ALU); ADDUI in C8 with the SD, its last reader of R1 (read and write in the same cycle); BNE at C8 + 2 = C10; delay slot C11.

Critical path = 11 cycles. CPI = 11 / 5 = 2.2. FP ops per cycle = 1 / 11 = 0.09. Code efficiency = 5 / (11 × 3) = 0.15.

Old binary: SD F6, 0(R1) in C7 reads F6 one cycle before the 4-cycle FADD (issued in C4) writes it, so it stores the F6 of the previous iteration. A VLIW has no hardware that detects the RAW hazard.

The table is the June 2026 EX2 (LOOP1) with one number changed, FP latency 4 instead of 3. Four placements decide it: the RAW distances (3 after the load, 4 after the FADD), the ADDUI in the same cycle as the SD, the 2-cycle distance to the branch and the delay slot counted in the 11. The extra question is this lesson's point: on a VLIW a latency change does not slow the old binary down, it makes it wrong.

True or False: "A 4-issue VLIW processor requires 4 Program Counters to load the necessary instructions in the 4 parallel lanes."

  • True The four operations travel together in one bundle, fetched with one address; four PCs would mean four instruction streams, as in multithreading.
  • False A VLIW has a single program counter: each fetch brings one bundle, and a bundle holds at most one branch, so there is one next-PC decision per cycle.
Show model answer

False. One PC fetches one bundle per cycle; the bundle carries up to 4 operations in its 4 slots, one per functional unit, and at most one of them is a branch.

A True/False quiz of the 11 February 2025 paper, as the notes transcribe it (p. 582). The statement mixes up the issue width (operations per bundle) with the number of instruction streams. If a motivation line is asked, name the single PC and the one-branch-per-bundle rule.

True or False: "In VLIW architectures, the compiler can detect parallelism only in basic blocks of the code."

  • True Inside a basic block the dependences are easy to see, but a block holds only about 4 to 7 instructions; a compiler limited to it would leave most VLIW slots empty.
  • False Loop unrolling, software pipelining and trace scheduling find and schedule parallelism across basic blocks and across loop iterations.
Show model answer

False. A basic block (straight-line code with one entry and one exit) holds only about 4 to 7 instructions, and its RAW chains limit the parallelism further. VLIW compilers therefore schedule larger regions: loop unrolling and software pipelining overlap loop iterations, trace scheduling schedules the most likely path through several basic blocks.

A True/False quiz of the 11 February 2025 paper, as the notes transcribe it (p. 581). The notes answer it themselves on p. 273: substantial speedups need "techniques that go beyond individual basic blocks". The three techniques are lesson 21.

In a VLIW processor, which of the following are handled by the compiler at compile time? (MULTIPLE ANSWERS)

  • RAW hazards between operations The compiler places each consumer at least one latency after its producer and fills the gap with independent operations or NOPs.
  • WAR and WAW hazards By the order of the operations (a WAR writer may share the cycle of its reader) or by renaming into other registers.
  • Structural hazards on the functional units One operation per slot, one slot per unit: the bundle format itself prevents two operations on one unit.
  • Data-cache misses Whether an access misses depends on the run; the hardware stalls the processor when it happens.
  • Branch mispredictions The compiler may give a static hint, but detecting a wrong guess and flushing the pipeline is the hardware's job.
Show model answer

Answers 1, 2 and 3. Data hazards (RAW, WAR, WAW) and structural hazards are solved statically with known latencies and resources; cache misses and branch mispredictions are dynamic events handled by the hardware (stall, flush).

Draw the line between compile time and run time exactly: everything known in advance (latencies, units, register names) belongs to the compiler; everything that depends on the data or on the path taken belongs to the hardware.

Check yourself

Why does a VLIW allow at most one branch per bundle?

  • One program counter fetches one bundle per cycle, so there is room for one next-PC decision Right: two branches in one word could ask for two different next addresses in the same cycle.
  • A branch needs all the slots of the bundle No: in LOOP1 the BNE sits in the Integer slot and the other slots could carry other operations.
  • The branch unit is not pipelined Pipelining concerns throughput; the limit is about how many next-PC values one fetch can follow.
  • To keep the delay slot empty The delay slot is the cycle after the branch, a separate matter; one branch per bundle would hold even without it.

On the papers' fully pipelined FP ALU (latency 3), FADD F6 issues in C4. What is the earliest cycle for an independent FADD, and for an SD that stores F6?

  • C5 and C7 Throughput: the pipelined unit takes a new FADD every cycle. Latency: F6 exists only 3 cycles after C4.
  • C7 and C7 That treats the unit as busy for 3 cycles — true for a non-pipelined unit, not for the papers' "fully pipelined" ALU.
  • C5 and C5 The independent FADD may go in C5, but the SD would read F6 two cycles before it is written.
  • C4 and C7 The FP slot of C4 is already taken by the first FADD: one operation per unit per cycle.

A 4-issue superscalar and a 4-issue VLIW have the same functional units. Going from the first to the second, what changes? Select all that apply.

  • The issue logic that checks dependences at run time disappears Yes: the compiler has done the checks; the hardware executes the bundles as they are.
  • Empty slots now take room in the code Yes: on the superscalar an empty slot is just lost at run time; on the VLIW it is a NOP stored in the program.
  • The binary now depends on the units and latencies of this implementation Yes: the schedule is part of the binary, so a chip with other latencies or slots needs a recompiled program.
  • The register file needs fewer ports No: four operations still read up to eight registers and write four per cycle, whoever scheduled them.
  • Data-cache misses no longer stall the processor No: the compiler cannot know which access will miss, so the VLIW stalls on a miss and its whole schedule freezes, while the dynamic superscalar could keep executing independent instructions around it.

The work moves from silicon to the compiler; the register file and the misses stay where they were.

In LOOP1 the compiler wants to put ADDUI R1, R1, 4 in C1, next to the LD, because it has nothing to wait for. What happens?

  • Allowed: the LD in C1 reads the old R1 in the same cycle The LD is fine, but it is not the only reader of R1: the SD in C7 still has to read it.
  • Not allowed as written: the SD in C7 would read the new R1 and store into the next element (a WAR violated) Right. Only an SD offset of −4(R1) would repair it, and the papers forbid modifying the loop index.
  • Not allowed: the ADDUI depends on the LD through a RAW on R1 The LD reads R1 and does not write it: the pair is a WAR, not a RAW.
  • Allowed, and it saves two cycles with no other change It would save two cycles only together with the offset change; without it the program stores to the wrong address.

When does a VLIW need a dispatch network between the decoder and the functional units?

  • When it has more functional units than slots Right (p. 269): the network must route each operation, with its operands, to a free unit of the right kind.
  • Always, because operations must be routed by priority With one unit per slot, each operation goes straight to its unit, with no arbitration at all.
  • When two operations of a bundle depend on each other Dependences decide the cycle, not the route: the compiler never puts a RAW pair in one bundle (a WAR pair may share one, like SD and ADDUI in C7 of LOOP1), and nothing is checked at run time.
  • When the register file has fewer ports than the operations need Ports are a register-file matter: the multi-ported file is built with 2k read and k write ports.

Tomasulo renames registers without using up architectural registers. Why does the same renaming cost architectural registers on a VLIW?

  • A VLIW has no hidden storage: every value in flight must live in a register the compiler names, out of the fixed set of the instruction set Right: Tomasulo hides the values behind tags in its reservation stations; the VLIW has only F0 … F31.
  • The VLIW renames in hardware too, but with fewer tags A VLIW has no renaming hardware at all: the renaming is done by the compiler, in the code.
  • VLIW operations have longer latencies The latencies are the same kind of number on both machines; the difference is where the values in flight are stored.
  • It does not: renaming never increases the number of registers needed The notes say the opposite (p. 271): renaming "increases the number of physical register needed".

In the notes' solution of the May 2025 midterm (p. 511), bundle C4 holds LD F2, 16(R2) (latency 3) and ADDUI R2, R2, 8 (latency 1). From which cycle can an integer operation read the new R2?

  • C4 In C4 the ADDUI only issues; a reader in the same cycle still gets the old R2. That is what lets a WAR writer share its reader's cycle.
  • C5 Right: each operation keeps its own exposed latency, 1 cycle on the integer ALU whatever else sits in its bundle. The notes' table reads R2 in ADD R3, R2, R1 at C5.
  • C6 Two cycles is the distance the papers give only from an integer operation to a branch.
  • C7 That is the lock-step write-back of p. 271, every result of the bundle aligned to the 3-cycle load. It is not the papers' machine — with it, the notes' own 8-cycle table would be wrong.

Per-operation latencies are the model of every VLIW table in the papers: the compiler, not a synchronised write-back, keeps the writes in order.

Key take-aways

What to watch and read