Skip to content

UnrollLoops Pass

Expands ForKind::Unroll loops at compile time by inlining the loop body for each iteration value.

Overview

This pass statically unrolls for loops created with pl.unroll(), replacing them with repeated copies of the loop body where the loop variable is substituted with each iteration's constant value.

When to use: Runs automatically in the default pipeline before ConvertToSSA. Use pl.unroll() when the loop trip count is a compile-time constant and you want the body duplicated for each iteration.

API

C++ Python Level
pass::UnrollLoops() passes.unroll_loops() Function-level

Python usage:

from pypto import passes

# Create and apply the pass
result = passes.unroll_loops()(program)

DSL Syntax

# Basic unroll: body is duplicated 4 times with i = 0, 1, 2, 3
for i in pl.unroll(4):
    x = pl.add(x, i)

# With start/stop/step: body is duplicated for i = 0, 2, 4
for i in pl.unroll(0, 6, 2):
    x = pl.add(x, i)

Constraints

Constraint Reason
start, stop, step must be integer constants Values needed at compile time
step must be non-zero Prevents infinite loops
Input Unroll loops must have no SSA iter_args This pass expands non-SSA bodies; run it before ConvertToSSA
trip count must be ≤ 1024 (kMaxUnrollIterations) Caps compile-time expansion — a larger trip count raises ValueError; restructure the loop or use pl.range()

The IR can nevertheless represent ForKind.Unroll with iter_args, for example when ConvertToSSA is run directly on an accumulator loop. Printer and parser preserve this transitional form using pl.unroll(..., init_values=(...)). Roundtrip verification checks it; it does not certify pass-order legality. UnrollLoops still rejects this SSA input. Executable kernels should use ordinary assignments inside pl.unroll() and the normal pipeline order.

Example

Before (with unroll loop):

@pl.program
class Before:
    @pl.function
    def main(self, x: pl.Tensor[[64], pl.FP32]) -> pl.Tensor[[64], pl.FP32]:
        for i in pl.unroll(3):
            x = pl.add(x, i)
        return x

After (loop expanded):

@pl.program
class After:
    @pl.function
    def main(self, x: pl.Tensor[[64], pl.FP32]) -> pl.Tensor[[64], pl.FP32]:
        x = pl.add(x, 0)
        x = pl.add(x, 1)
        x = pl.add(x, 2)
        return x

Pipeline Position

UnrollLoops runs once in Default, before control flow structuring:

UnrollLoops → CtrlFlowTransform → ConvertToSSA → FlattenCallExpr → OutlineHierarchyScopes → OutlineIncoreScopes → ...

UnrollLoops expands pl.unroll() loops into their fully unrolled, straight-line form.

Pass Properties

Property Value
Required —
Produced UnrollResolved
Invalidated —