5.3.13Advanced Microarchitecture

VLIW architectures

3,318 words15 min readdifficulty · medium

What Is VLIW?

Why VLIW? The Design Philosophy

Traditional superscalar processors use complex hardware logic:

  • Out-of-order execution engines to find parallelism at runtime
  • Register renaming logic to eliminate false dependencies
  • Reservation stations and reorder buffers to track in-flight instructions
  • Wakeup/select logic that consumes significant power and area

VLIW says: "What if we move all this complexity to the compiler?"

WHY this trade-off makes sense:

  1. Compile-time analysis has unlimited time and global code visibility
  2. Hardware complexity grows super-linearly with issue width (O(n²) for many structures)
  3. Power consumption of dynamic scheduling hardware is substantial
  4. Simpler hardware → lower power, higher clock speeds, easier verification

VLIW Instruction Format

WHY fixed slots? Hardware can decode and route operations to functional units with simple combinational logic—no complex dependency matrices or dynamic dispatch logic needed.

Deriving Code Density Impact

If we have a VLIW machine with nn operation slots per instruction:

Best case (full parallelism): nn operations per instruction word Worst case (no parallelism): 1 real operation + (n1)(n-1) NOPs per instruction word

Code density efficiency compared to sequential RISC: Efficiency=Operations executedInstruction bits transmitted=kn×bRISCbVLIW\text{Efficiency} = \frac{\text{Operations executed}}{\text{Instruction bits transmitted}} = \frac{k}{n} \times \frac{b_{\text{RISC}}}{b_{\text{VLIW}}}

Where:

  • kk = average operations per VLIW instruction (1 ≤ k ≤ n)
  • bRISCb_{\text{RISC}} = bits per RISC instruction (~32 bits)
  • bVLIWb_{\text{VLIW}} = bits per VLIW instruction (n×bopn \times b_{\text{op}}, typically 128-512 bits)

WHY this matters: If parallelism is low, you pay the code size cost of NOPs while getting sequential performance. VLIW lives or dies by the compiler's ability to find parallelism.

The Compiler's Role: Static Scheduling

The VLIW compiler must perform sophisticated analysis:

1. Dependency Analysis

Data dependencies:

  • RAW (Read After Write): True dependency, must preserve order
  • WAR (Write After Read): Anti-dependency, can eliminate with register renaming
  • WAW (Write After Write): Output dependency, can eliminate with register renaming

WHY the compiler handles this: In superscalar processors, hardware register renaming eliminates WAR/WAW. In VLIW, the compiler must rename registers itself during scheduling.

2. Software Pipelining (Modulo Scheduling)

The most powerful technique for VLIW loop performance:

Traditional loop execution (sequential):

Iteration 1: [Load A] → [Compute] → [Store]
Iteration 2:                          [Load A] → [Compute] → [Store]  
Iteration 3:                                [Load A] → ...

Software pipelined (overlapped):

Prologue:    [Load A for iter 1]
Steady:      [Load A for iter 2] [Compute iter 1] [Store iter 0]
             [Load A for iter 3] [Compute iter 2] [Store iter 1]
             [Load A for iter 4] [Compute iter 3] [Store iter 2]
Epilogue:                [Compute iter n] [Store iter n-1]
                                [Store iter n]

WHY this works: Different iterations occupy different pipeline stages within the same instruction cycle, maximizing functional unit utilization.

3. Predication

VLIW architectures heavily use predication (conditional execution) to eliminate branches:

Instead of:

if (x > 0)
    a = b + c;
else
    a = b - c;

VLIW with predication:

cmp.gt p1,p2 = x,0           // p1 = (x>0), p2 = !(x>0)
(p1) add a = b, c            // execute if p1 is true
(p2) sub a = b, c            // execute if p2 is true

WHY predication is critical for VLIW:

  • Branches prevent software pipelining (can't overlap iterations across conditional boundaries)
  • Branch mispredictions are expensive (no speculative execution hardware)
  • Predication converts control dependencies into data dependencies, enabling more parallelism

VLIW vs. Superscalar: The Trade-offs

Aspect VLIW Superscalar
Parallelism discovery Compile-time (static) Run-time (dynamic)
Hardware complexity Low (simple dispatch) High (OoO engines)
Power efficiency Better (no speculation) Worse (speculation overhead)
Code portability Poor (ISA tied to # units) Good (HW adapts)
Code size Larger (NOPs when low ILP) Smaller (dense encoding)
Performance on irregular code Poor Better (dynamic adaptation)
Compiler burden Extreme Moderate

WHY VLIW struggled in general-purpose CPUs: The "VLIW dilemma": Adding more functional units for future performance requires changing the ISA (more operation slots), breaking backward compatibility. Intel Itanium tried to solve this with explicit parallelism bits and predication, but compiler technology couldn't consistently find enough ILP in general-purpose code.

WHERE VLIW succeeded:

  • DSPs (Texas Instruments C6x): Signal processing has abundant data parallelism
  • Embedded media processors: Where code is compiled once for a specific processor
  • Some GPU shader cores (historical, e.g. AMD TeraScale VLIW5/VLIW4): These early GPUs did pack independent operations into a VLIW bundle per lane. Note: modern GPUs have largely moved away from VLIW toward scalar SIMT designs (see mistake below).

Memory Disambiguation: The Achilles Heel

Consider:

for (i = 0; i < N; i++) {
    a[i] = b[i] + c[i];
    d[i] = e[i] * f[i];
}

The compiler sees two independent statements and wants to execute them in parallel. But what if a and e overlap in memory? The store to a[i] could affect the load from e[i].

In superscalar: Hardware performs memory disambiguation at runtime, detecting aliasing dynamically.

In VLIW: The compiler must either:

  1. Prove independence through static analysis (often impossible with pointers)
  2. Insert runtime checks (defeating the simplicity advantage)
  3. Assume independence and risk incorrect execution

WHY this limits VLIW: Memory aliasing is undecidable in general. Conservative assumptions kill parallelism; aggressive assumptions risk bugs.

Recall Feynman Explan

Concept Map

motivates shift to

delegates scheduling to

performs

produces

packed into

each targets

padded with

allows

advance via

yields

Superscalar complexity

VLIW architecture

Compiler

Static scheduling

Explicit parallelism

Operation slots per word

Multiple functional units

NOP fills empty slots

No hardware interlocks

Lock-step execution

Simpler HW, lower power, higher clock

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho yaar, VLIW ka core idea bahut simple aur elegant hai. Traditional superscalar processors mein hardware ko real-time mein figure out karna padta hai ki kaunse instructions parallel run ho sakte hain — iske liye bahut complex logic chahiye, jaise out-of-order execution engines, register renaming, reservation stations. Ye sab power aur die area khaata hai. VLIW bolta hai ki "yaar, ye kaam compiler ko de do!" Compiler compile-time pe hi dependencies analyze karke independent operations ko ek single wide instruction mein pack kar deta hai. Uss "meal prep" wale analogy ko yaad rakho — chef (hardware) ko frantically coordinate karne ki zaroorat nahi, kyunki poore meal kits (VLIW instructions) pehle se ready hote hain, sab pre-sorted.

Ab ye kyun matter karta hai? Do main reasons. Pehla, compiler ke paas unlimited time aur poore code ka global view hota hai — jabki hardware ke paas bas milliseconds hote hain aur limited window dikhta hai. Toh compiler zyada smart scheduling kar sakta hai. Dusra, hardware complexity issue-width ke saath super-linearly badhti hai (kai structures ke liye O(n²)), toh jab aap ye complexity hataate ho, tab hardware simple, low-power, high-clock-speed, aur easily verifiable ban jaata hai. Har VLIW instruction word mein fixed operation slots hote hain — jaise humare 4-issue example mein Slot 0 aur 1 integer ALU ke liye, Slot 2 memory ke liye, Slot 3 branch/floating-point ke liye. Ek hi memory unit hone ki wajah se ek instruction mein sirf ek load/store allowed hai — ye constraint aage ke examples mein bahut important hai.

Ek trade-off bhi samajh lo taaki poori picture clear ho. Agar compiler ko parallel work nahi milta, toh usse slots mein NOP (no operation) daalna padta hai — matlab wasted bits. Best case mein saare n slots full hote hain, worst case mein sirf 1 real operation aur baaki NOPs. Isliye code density efficiency important metric hai — kitne actual operations execute ho rahe hain versus kitne instruction bits transmit ho rahe hain. Toh VLIW ka poora game hai: hardware ko simple banao, complexity compiler pe shift karo, lekin dhyaan rakho ki agar compiler achhe se schedule na kar paaye toh NOPs se performance aur code density dono suffer karte hain.

Go deeper — visual, from zero

Test yourself — Advanced Microarchitecture