Skip to content

Decode

BytecodeDecoder -- syntactic Program -> stack_move ir.Method.

BytecodeDecoder dataclass

BytecodeDecoder(
    frame: StackMachineFrame = StackMachineFrame(),
)

Turn a bytecode Program into a stack_move ir.Method.

Maintains a virtual stack of SSA values during decoding: each bytecode push emits a stack_move statement whose result is pushed onto the virtual stack, and each pop consumes the top SSA reference. Stack ops (Pop/Dup/Swap) emit corresponding stack_move statements (linear-IR style -- see the design doc).

All stack and IR manipulation is delegated to self.frame; the handlers below are thin translators from bytecode opcodes to stack_move statements.

DecodingError dataclass

DecodingError(
    instruction_index: int,
    opcode_name: str,
    stack_snapshot: tuple[SSAValue, ...],
    reason: str,
)

Bases: Exception


              flowchart TD
              bloqade.lanes.bytecode.decode.DecodingError[DecodingError]

              

              click bloqade.lanes.bytecode.decode.DecodingError href "" "bloqade.lanes.bytecode.decode.DecodingError"
            

Raised when the decoder fails.

Carries the offending instruction's index, opcode, and a snapshot of the virtual stack of SSA values at the point of failure.

StackMachineFrame dataclass

StackMachineFrame(stack: list[SSAValue] = list())

Tracks IR construction state during bytecode decoding.

Mirrors kirin.lowering.Frame's shape for the bytecode case: a single basic block wrapped in a Region, plus a virtual stack of SSA values.

peek_value

peek_value() -> ir.SSAValue

Return the top-of-stack SSA value without popping.

Raises:

Type Description
StackUnderflowError

when the stack is empty.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
113
114
115
116
117
118
119
120
121
def peek_value(self) -> ir.SSAValue:
    """Return the top-of-stack SSA value without popping.

    Raises:
        StackUnderflowError: when the stack is empty.
    """
    if not self.stack:
        raise StackUnderflowError(snapshot=self.snapshot(), required=1)
    return self.stack[-1]

pop_n

pop_n(n: int) -> list[ir.SSAValue]

Pop n values from the top of the stack.

Returns them in bottom-to-top order so the caller can pass them as a tuple matching the 'top-of-stack = last argument' convention.

Raises:

Type Description
StackUnderflowError

when the stack has fewer than n values.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def pop_n(self, n: int) -> list[ir.SSAValue]:
    """Pop ``n`` values from the top of the stack.

    Returns them in bottom-to-top order so the caller can pass them
    as a tuple matching the 'top-of-stack = last argument' convention.

    Raises:
        StackUnderflowError: when the stack has fewer than ``n`` values.
    """
    if len(self.stack) < n:
        raise StackUnderflowError(snapshot=self.snapshot(), required=n)
    popped = [self.stack.pop() for _ in range(n)]
    popped.reverse()
    return popped

pop_value

pop_value() -> ir.SSAValue

Pop the top of the virtual stack.

Raises:

Type Description
StackUnderflowError

when the stack is empty.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
88
89
90
91
92
93
94
95
96
def pop_value(self) -> ir.SSAValue:
    """Pop the top of the virtual stack.

    Raises:
        StackUnderflowError: when the stack is empty.
    """
    if not self.stack:
        raise StackUnderflowError(snapshot=self.snapshot(), required=1)
    return self.stack.pop()

push

push(stmt: T) -> T

Append a statement to the current block and push its results onto the virtual stack.

Results are pushed in reverse declaration order — the first-declared result (highest on the stack per the dialect convention) ends up on top after pushing. For Swap this means out_top (declared first) sits above out_bot (declared second). For single-result statements there is only one result, so the reversed iteration is a no-op.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def push(self, stmt: T) -> T:
    """Append a statement to the current block and push its results onto
    the virtual stack.

    Results are pushed in reverse declaration order — the first-declared
    result (highest on the stack per the dialect convention) ends up on
    top after pushing. For Swap this means out_top (declared first) sits
    above out_bot (declared second). For single-result statements there
    is only one result, so the reversed iteration is a no-op.
    """
    self.current_block.stmts.append(stmt)
    for result in reversed(list(stmt.results)):
        self.stack.append(result)
    return stmt

push_value

push_value(value: SSAValue) -> None

Push an SSA value onto the virtual stack.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
84
85
86
def push_value(self, value: ir.SSAValue) -> None:
    """Push an SSA value onto the virtual stack."""
    self.stack.append(value)

snapshot

snapshot() -> tuple[ir.SSAValue, ...]

Return a tuple snapshot of the stack -- used for error reporting.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
133
134
135
def snapshot(self) -> tuple[ir.SSAValue, ...]:
    """Return a tuple snapshot of the stack -- used for error reporting."""
    return tuple(self.stack)

swap_values

swap_values() -> None

Swap the top two values on the virtual stack in place.

Raises:

Type Description
StackUnderflowError

when the stack has fewer than 2 values.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
123
124
125
126
127
128
129
130
131
def swap_values(self) -> None:
    """Swap the top two values on the virtual stack in place.

    Raises:
        StackUnderflowError: when the stack has fewer than 2 values.
    """
    if len(self.stack) < 2:
        raise StackUnderflowError(snapshot=self.snapshot(), required=2)
    self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]

StackUnderflowError

StackUnderflowError(
    snapshot: tuple[SSAValue, ...], required: int
)

Bases: Exception


              flowchart TD
              bloqade.lanes.bytecode.decode.StackUnderflowError[StackUnderflowError]

              

              click bloqade.lanes.bytecode.decode.StackUnderflowError href "" "bloqade.lanes.bytecode.decode.StackUnderflowError"
            

Raised by StackMachineFrame when a pop operation would fail.

Carries a snapshot of the stack at the point of failure and how many values the operation required. The decoder catches this at the _visit dispatcher and rewraps into DecodingError with instruction context (index + opcode name).

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
42
43
44
45
def __init__(self, snapshot: tuple[ir.SSAValue, ...], required: int) -> None:
    self.snapshot = snapshot
    self.required = required
    super().__init__(f"stack underflow: need {required}, have {len(snapshot)}")

load_program

load_program(
    program: "Program", kernel_name: str = "main"
) -> ir.Method

Decode a bytecode Program into a stack_move ir.Method and run Kirin's type-inference pass on the result.

The returned method carries inferred types on every SSA value, including the method's overall return type -- useful for downstream passes (analysis, rewrites) that rely on type information.

Source code in .venv/lib/python3.12/site-packages/bloqade/lanes/bytecode/decode.py
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def load_program(program: "Program", kernel_name: str = "main") -> ir.Method:
    """Decode a bytecode Program into a stack_move ir.Method and run
    Kirin's type-inference pass on the result.

    The returned method carries inferred types on every SSA value,
    including the method's overall return type -- useful for downstream
    passes (analysis, rewrites) that rely on type information.
    """
    from kirin.passes.typeinfer import TypeInfer

    decoder = BytecodeDecoder()
    method = decoder.decode(program, kernel_name)
    TypeInfer(method.dialects).unsafe_run(method)
    return method