Complete List of Opcodes Supported by Monty's Bytecode VM
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 (lines 23–88), with execution logic implemented in 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) 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.
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 PythonNone.LoadTrue– Pushes booleanTrue.LoadFalse– Pushes booleanFalse.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 forx % k == 0checks.
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 asliceobject.BuildFString– Assemble f-string components.
F-string formatting:
FormatValue– Format a value according to conversion/format-spec flags.
Subscript and Attribute Access
BinarySubscr– Loadobj[idx].StoreSubscr– Store toobj[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/**kwargsunpacking.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 anexceptclause.
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 deferredImportError.
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 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:
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:
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:
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:
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 incrates/monty/src/bytecode/op.rs, enabling efficient table-lookup dispatch viastrum::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_effectmethod (lines 99–112), which calculates the net stack height change for each instruction. - The execution loop in
crates/monty/src/run.rsmatches on these opcodes to drive the interpreter, while the compiler incrates/monty/src/compiler.rsmaps 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, 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. 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →