# Complete List of Opcodes Supported by Monty's Bytecode VM

> Explore over 80 opcodes supported by Monty's bytecode VM for stack manipulation, arithmetic, control flow, function calls, async await and exception handling. Get the complete list.

- Repository: [Pydantic/monty](https://github.com/pydantic/monty)
- Tags: api-reference
- Published: 2026-02-16

---

**Monty's bytecode VM supports over 80 distinct opcodes encoded as single bytes, covering stack manipulation, arithmetic, control flow, function calls, async/await, and exception handling.**

Monty is a high-performance Python compiler and runtime developed by Pydantic. At its core, Monty compiles Python source into a compact, **byte-aligned** instruction stream (`Vec<u8>`) executed by a stack-based virtual machine. Understanding the opcodes supported by Monty's bytecode VM is essential for debugging compiled programs and optimizing performance-critical code. The complete opcode enumeration is defined in **[`crates/monty/src/bytecode/op.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/bytecode/op.rs)** (lines 23–88), with execution logic implemented in **[`crates/monty/src/run.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/run.rs)**.

## Opcode Encoding and Architecture

Each instruction in Monty is identified by a one-byte discriminant defined by the `Opcode` enum. The enum is annotated with `#[repr(u8)]`, ensuring every opcode occupies exactly one byte. This design allows the VM to decode instructions using a simple table-lookup via `strum::FromRepr`, minimizing dispatch overhead during execution.

The `stack_effect` method (lines 99–112 in [`op.rs`](https://github.com/pydantic/monty/blob/main/op.rs)) encodes the static stack height change for most opcodes, which the compiler uses when emitting bytecode to verify stack consistency without executing the program.

## Complete Opcode Reference by Category

The opcodes are grouped by functionality. Below is the comprehensive catalogue derived from the source definition in [`crates/monty/src/bytecode/op.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/bytecode/op.rs).

### Stack Manipulation

These opcodes manipulate the operand stack without side effects:

- **`Pop`** – Removes the top item from the stack.
- **`Dup`** – Duplicates the top item.
- **`Rot2`** – Swaps the top two items.
- **`Rot3`** – Rotates the top three items, moving the third item to the top.

### Literals and Constants

These opcodes push constant values onto the stack:

- **`LoadConst`** – Pushes a value from the constant pool by index.
- **`LoadNone`** – Pushes Python `None`.
- **`LoadTrue`** – Pushes boolean `True`.
- **`LoadFalse`** – Pushes boolean `False`.
- **`LoadSmallInt`** – Pushes a small signed integer directly without pool lookup.

### Variable Access

These opcodes handle local, global, and closure cell variables:

- **`LoadLocal0`…`LoadLocal3`** – Fast paths for loading the first four locals.
- **`LoadLocal`** – Load local by 8-bit index.
- **`LoadLocalW`** – Load local by 16-bit index ("wide" variant).
- **`StoreLocal`** – Store to local by 8-bit index.
- **`StoreLocalW`** – Store to local by 16-bit index.
- **`LoadGlobal`** – Load a global variable.
- **`StoreGlobal`** – Store to a global variable.
- **`LoadCell`** – Load a closure cell (free variable).
- **`StoreCell`** – Store to a closure cell.
- **`DeleteLocal`** – Delete a local variable.

### Arithmetic and Bitwise Operations

Binary arithmetic opcodes pop two operands and push the result:

- **`BinaryAdd`**, **`BinarySub`**, **`BinaryMul`**, **`BinaryDiv`**, **`BinaryFloorDiv`**, **`BinaryMod`**, **`BinaryPow`**, **`BinaryMatMul`** – Standard arithmetic (`+`, `-`, `*`, `/`, `//`, `%`, `**`, `@`).

Bitwise operations:

- **`BinaryAnd`**, **`BinaryOr`**, **`BinaryXor`**, **`BinaryLShift`**, **`BinaryRShift`** – `&`, `|`, `^`, `<<`, `>>`.

### Comparisons and Membership Tests

These opcodes handle rich comparisons and special Python operators:

- **`CompareEq`**, **`CompareNe`**, **`CompareLt`**, **`CompareLe`**, **`CompareGt`**, **`CompareGe`** – Equality and ordering.
- **`CompareIs`**, **`CompareIsNot`** – Identity tests (`is`, `is not`).
- **`CompareIn`**, **`CompareNotIn`** – Membership tests (`in`, `not in`).
- **`CompareModEq`** – Special optimization for `x % k == 0` checks.

### Unary and In-Place Operations

Unary operations pop one operand and push the result:

- **`UnaryNot`** – Logical negation.
- **`UnaryNeg`** – Arithmetic negation.
- **`UnaryPos`** – Unary plus (no-op).
- **`UnaryInvert`** – Bitwise inversion (`~`).

In-place operations for compound assignments:

- **`InplaceAdd`**, **`InplaceSub`**, **`InplaceMul`**, **`InplaceDiv`**, **`InplaceFloorDiv`**, **`InplaceMod`**, **`InplacePow`** – `+=`, `-=`, etc.
- **`InplaceAnd`**, **`InplaceOr`**, **`InplaceXor`**, **`InplaceLShift`**, **`InplaceRShift`** – Bitwise compound assignments.

### Collection Builders and F-Strings

These opcodes construct Python containers:

- **`BuildList`**, **`BuildTuple`**, **`BuildDict`**, **`BuildSet`** – Create collections from stack items.
- **`BuildSlice`** – Create a `slice` object.
- **`BuildFString`** – Assemble f-string components.

F-string formatting:

- **`FormatValue`** – Format a value according to conversion/format-spec flags.

### Subscript and Attribute Access

- **`BinarySubscr`** – Load `obj[idx]`.
- **`StoreSubscr`** – Store to `obj[idx]`.
- **`LoadAttr`** – Load an attribute.
- **`LoadAttrImport`** – Load an attribute for import statements.
- **`StoreAttr`** – Store to an attribute.

### Function Calls and Async/Await

Monty supports multiple calling conventions:

- **`CallFunction`** – Call a function with positional arguments.
- **`CallBuiltinFunction`** – Call a built-in function.
- **`CallBuiltinType`** – Call a built-in type constructor.
- **`CallFunctionKw`** – Call with keyword arguments.
- **`CallAttr`** – Call a method (attribute call).
- **`CallAttrKw`** – Call a method with keywords.
- **`CallFunctionExtended`** – Call with `*args`/`**kwargs` unpacking.
- **`CallAttrExtended`** – Method call with extended arguments.

Async operations:

- **`Await`** – Await a coroutine or future.

### Control Flow and Iteration

- **`Jump`** – Unconditional relative jump.
- **`JumpIfTrue`**, **`JumpIfFalse`** – Conditional jumps based on top of stack.
- **`JumpIfTrueOrPop`**, **`JumpIfFalseOrPop`** – Conditional jumps that keep the condition on stack when not jumping.

Iteration:

- **`GetIter`** – Convert a value to an iterator.
- **`ForIter`** – Advance the iterator or exit the loop.

### Exception Handling and Returns

- **`Raise`** – Raise an exception.
- **`Reraise`** – Re-raise the current exception.
- **`ClearException`** – Clear the active exception.
- **`CheckExcMatch`** – Test an exception against an `except` clause.

Function returns:

- **`ReturnValue`** – Return from the current function.

### Unpacking and Special Operations

- **`UnpackSequence`** – Decompose a sequence into a fixed number of targets.
- **`UnpackEx`** – Unpack with "rest" (`*rest`) support.

Special opcodes:

- **`Nop`** – No-operation (used for patching/alignment).
- **`LoadModule`** – Import a builtin module.
- **`RaiseImportError`** – Raise a deferred `ImportError`.

## Stack Effect Analysis

The VM uses static stack analysis to verify bytecode safety before execution. The `stack_effect` method defined at lines 99–112 in [`crates/monty/src/bytecode/op.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/bytecode/op.rs) returns the net change in stack height for each opcode. For example, `BinaryAdd` has a stack effect of `-1` (pops two, pushes one), while `LoadConst` has an effect of `+1`. This metadata allows the compiler to detect stack underflows or overflows without running the code.

## Executing Bytecode: Practical Examples

The following examples demonstrate how high-level Python constructs map to Monty opcodes. These snippets use the public `pydantic_monty` API to compile and execute code.

### Arithmetic and Local Variables

This example triggers binary arithmetic opcodes and local variable access:

```python
from pydantic_monty import Monty

code = "def f(x):\n    return (x + 2) * 3"
m = Monty(code, inputs=['x'])
result = m.run(inputs={'x': 4})  # → 18

```

The VM executes: `LoadLocal` → `LoadConst` → `BinaryAdd` → `LoadConst` → `BinaryMul` → `ReturnValue`.

### List Comprehensions and Iteration

This example exercises collection building and iteration opcodes:

```python
from pydantic_monty import Monty

code = "[i * i for i in range(5)]"
m = Monty(code)
print(m.run())  # → [0, 1, 4, 9, 16]

```

Key opcodes: `BuildList`, `GetIter`, `ForIter`, `BinaryMul`, and list append operations.

### Async/Await Support

Monty supports asynchronous execution through dedicated opcodes:

```python
from pydantic_monty import Monty

code = """
import asyncio
async def hello():
    await asyncio.sleep(0.01)
    return "hi"
"""
m = Monty(code, inputs=[])

```

The `Await` opcode suspends the coroutine and yields control until the future resolves.

### Attribute Access and Method Calls

This example demonstrates object-oriented opcodes:

```python
from pydantic_monty import Monty

code = """
class C:
    def greet(self): return "hey"
c = C()
c.greet()
"""
m = Monty(code)
print(m.run())  # → "hey"

```

The VM uses `LoadAttr` to resolve the method and `CallAttr` to invoke it.

## Summary

- Monty's bytecode VM uses a **single-byte opcode format** (`#[repr(u8)]`) defined in [`crates/monty/src/bytecode/op.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/bytecode/op.rs), enabling efficient table-lookup dispatch via `strum::FromRepr`.
- The instruction set includes **over 80 opcodes** organized into categories: stack manipulation, variable access, arithmetic, comparisons, collection building, control flow, function calls, exception handling, and async/await.
- **Stack safety** is verified statically using the `stack_effect` method (lines 99–112), which calculates the net stack height change for each instruction.
- The execution loop in [`crates/monty/src/run.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/run.rs) matches on these opcodes to drive the interpreter, while the compiler in [`crates/monty/src/compiler.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/compiler.rs) maps Python AST nodes to these bytecodes.

## Frequently Asked Questions

### How many opcodes does Monty's bytecode VM support?

Monty's VM supports over 80 distinct opcodes, each encoded as a single byte. The complete enumeration spans lines 23–88 in [`crates/monty/src/bytecode/op.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/bytecode/op.rs), covering everything from basic stack operations (`Pop`, `Dup`) to complex async/await primitives (`Await`).

### What is the difference between LoadLocal and LoadLocalW?

`LoadLocal` loads a local variable using an 8-bit index, suitable for functions with fewer than 256 local variables. `LoadLocalW` is the "wide" variant that accepts a 16-bit index, allowing access to up to 65,536 local slots. The compiler automatically selects the appropriate variant based on the variable index.

### How does Monty handle async/await at the bytecode level?

Monty implements async/await through the `Await` opcode, defined alongside other control-flow instructions. When the VM encounters `Await`, it suspends the current coroutine's frame, yields control to the event loop, and resumes execution once the awaited future resolves. This matches the behavior of Python's `async`/`await` syntax while operating within Monty's compact bytecode format.

### Where is the opcode execution loop implemented?

The opcode execution loop is implemented in **[`crates/monty/src/run.rs`](https://github.com/pydantic/monty/blob/main/crates/monty/src/run.rs)**. This file contains the interpreter's main dispatch logic, which uses a `match` statement on the `Opcode` enum to execute the corresponding semantics for each instruction. The loop handles operand fetching from the byte stream and manages the evaluation stack and heap objects.