Skip to content

Comments

feat[venom]: add direct venom pipeline#4811

Draft
charles-cooper wants to merge 148 commits intovyperlang:masterfrom
charles-cooper:feat/venom-ir-rewrite
Draft

feat[venom]: add direct venom pipeline#4811
charles-cooper wants to merge 148 commits intovyperlang:masterfrom
charles-cooper:feat/venom-ir-rewrite

Conversation

@charles-cooper
Copy link
Member

Summary

Add experimental codegen pathway (--experimental-codegen) that compiles Vyper AST directly to Venom IR, bypassing the legacy s-expression IR intermediate representation.

What's New

New module: vyper/codegen_venom/ (~10,500 LOC)

Core lowering:

  • expr.py - Expression lowering (literals, operators, subscripts, attributes, calls)
  • stmt.py - Statement lowering (assignments, control flow, loops)
  • context.py - Codegen context with memory/storage/transient abstractions
  • module.py - Module compilation with external function dispatch

Abstractions:

  • value.py - VyperValue: explicit tagged union for stack values vs located pointers
  • buffer.py - Buffer/Ptr: memory allocation and pointer arithmetic

Builtins (builtins/, ~3,400 LOC):

  • Separated into dedicated modules (vs inline in legacy)
  • simple.py, math.py, hashing.py, bytes.py, strings.py
  • convert.py, create.py, system.py, misc.py, abi.py

ABI (abi/):

  • abi_encoder.py - ABI encoding for calls/returns
  • abi_decoder.py - ABI decoding with bounds checking

Also: vyper/venom/builder.py (~580 LOC) - Type-safe API for IR construction

Architecture

  • VyperValue: Tracks values with location (memory/storage/transient/code) to prevent location confusion bugs
  • Buffer/Ptr: Clean abstractions replacing ad-hoc pointer arithmetic
  • Two-phase compilation: Separate deploy and runtime contexts
  • Jumptable dispatch: Sparse and dense dispatch for efficient selector lookup

Why ~38% larger than legacy codegen?

The new codegen is more explicit:

  • Builtins separated into dedicated modules (clearer, more maintainable)
  • Explicit location tracking (VyperValue) vs implicit in IRnode
  • Symmetric ABI encoder/decoder (vs scattered decode logic)
  • Direct Venom IR emission (no IRnode intermediary)

Changes to Existing Code

  • vyper/compiler/phases.py - Hook to use new codegen when flag enabled
  • vyper/venom/builder.py - New VenomBuilder class (~580 LOC)
  • Deleted vyper/venom/ir_node_to_venom.py (~860 LOC) - legacy IRnode-to-Venom translation no longer needed
  • cfg/cfg_runtime output formats now require --experimental-codegen
  • No changes to legacy codegen or default behavior

Test Status

  • Full test suite passes with --experimental-codegen
  • Gated behind flag - default behavior unchanged

charles-cooper and others added 30 commits December 21, 2025 14:53
Add vyper/venom/builder.py with VenomBuilder - a clean API wrapping
IRBasicBlock.append_instruction() for explicit, type-safe IR emission.

Includes block management, arithmetic/bitwise/comparison ops, memory/storage,
control flow, internal/external calls, crypto, environment, and source tracking.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create new vyper/codegen_venom/ directory for direct AST-to-Venom
code generation, bypassing legacy IRnode intermediate representation.

- Add generate_venom_for_module() entry point returning (deploy_ctx, runtime_ctx)
- Add VenomCodegenContext for tracking variables, scopes, and codegen state
- Hook into CompilerData via _venom_direct property
- Route venom_runtime/venom_deploytime through new path when experimental_codegen=True

Currently emits minimal valid IR (just stop instruction). Foundation for
subsequent tasks that will implement expression/statement lowering.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement codegen_venom/expr.py with literal expression lowering:
- Int: direct integer value
- Decimal: scaled by DECIMAL_DIVISOR (10^10)
- Hex: addresses + bytesN (left-padded)
- NameConstant: True/False to 1/0
- Bytes/HexBytes/Str: memory allocation with length + data chunks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add arithmetic and bitwise operation lowering to Expr class:
- Bitwise: and, or, xor (no overflow checks)
- Shifts: shl, shr, sar (256-bit types only)
- Arithmetic: safe_add/sub/mul/div/floordiv/mod/pow with overflow checks
- Unary: not, invert, usub

Overflow checks follow patterns from vyper/codegen/arithmetic.py:
- 256-bit add/sub: check result vs operand
- int256 mul/div: special case for MIN_INT * -1
- Decimal mul divides by divisor, div multiplies numerator
- Power requires compile-time literal for bounds computation

Tests are xfail pending lower_Name implementation (Task 06).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add expression lowering for:
- lower_Compare: <, <=, >, >=, ==, != comparisons with signed/unsigned
  dispatch, plus flag membership (in/not in)
- lower_BoolOp: short-circuit and/or via control flow blocks
- lower_Name: local variables, self keyword, constants, immutables
- lower_Attribute: environment vars (msg/block/tx/chain), address
  properties (.balance, .codesize, etc.), state variables (self.x)

Fix Compare node access to use node.right (not node.comparators[0]).
Fix test_rshift_signed to use unsigned shift amount.
Add test helpers that register function parameters in VenomCodegenContext.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements statement lowering in codegen_venom:
- lower_AnnAssign: variable declaration with initialization
- lower_Assign: regular assignment (primitive types)
- lower_AugAssign: augmented assignment with all operators

Includes safe arithmetic operations with overflow checking for AugAssign.
State variable assignment via sload/sstore is supported.

Complex types (structs/arrays) and tuple unpacking are deferred to
later tasks as planned.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add lower_If for if/elif/else statements and lower_IfExp for ternary expressions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add For loop support to direct AST-to-Venom codegen:

- lower_For: dispatches to range or iter loop
- _lower_range_loop: handles range(n), range(start, end), and bound kwarg
- _lower_iter_loop: handles static and dynamic array iteration
- lower_Break/lower_Continue: jump to exit/incr blocks
- lower_Pass: no-op statement
- forvars dict in context for loop variable tracking
- _lower_array_membership: x in array using loop with early break

5-block CFG structure: entry, cond, body, incr, exit
Uses loop_scope context manager for nested break/continue targets.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…cess

Add support for subscript and attribute lowering in the Venom IR codegen:

- lower_Subscript() for array[index], mapping[key] access
- Extend lower_Attribute() for struct field access (point.x)
- Handle subscript/attribute assignment in Stmt class
- Bounds checking for array access (signed/unsigned indices)
- sha3_64 for mapping slot computation
- Proper word scale handling (storage=1, memory=32)
- 28 new tests covering storage arrays, mappings, structs, and nested access

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add helper methods for memory operations in direct Venom codegen:
- load_memory: load from memory ptr (primitive vs complex type handling)
- store_memory: store to memory ptr with automatic copy for complex types
- copy_memory: mcopy for Cancun+, word-by-word fallback for earlier EVMs
- load_calldata: calldataload for primitives, calldatacopy for complex
- allocate_buffer: allocate temporary scratch buffers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add storage operation methods to support sload/sstore, tload/tstore,
and iload/istore with proper handling of word-addressed storage vs
byte-addressed memory.

- load_storage / store_storage for primitive and multi-word types
- load_transient / store_transient for EIP-1153 (Cancun+)
- load_immutable / store_immutable for bytecode-stored values
- get/set_storage_dyn_array_length helpers
- Multi-word copy helpers with proper word_scale handling

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for internal function definitions and calls:
- is_word_type(): check if type fits in 32-byte stack slot
- pass_via_stack(): determine stack vs memory arg passing
- returns_stack_count(): count return values via stack (0, 1, or 2)
- emit_nonreentrant_lock/unlock(): reentrancy guard emission

Expr changes:
- lower_Call(): dispatch for internal/external/builtin calls
- _lower_internal_call(): full invoke generation with arg staging

Stmt changes:
- lower_Return(): handle stack/memory return value placement

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add module-level code generation for external function dispatch:

- VenomModuleCompiler: compiles Vyper module to Venom IR
  - Linear O(n) selector dispatch (simple and correct first)
  - External function entry point generation with kwargs support
  - Fallback/default function handling
  - Payable and calldatasize checks
  - Nonreentrant lock integration

- Stmt: add external function return with ABI encoding
  - _lower_external_return for encoding return values
  - lower_Expr for expression statements (side-effect calls)

- Expr: fix internal call to get func_t from correct metadata

- Tests: 13 new tests for external function dispatch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement the module assembly layer for direct-to-Venom codegen:

- Restructure to two-phase compilation: generate_runtime_venom() produces
  runtime IR which is compiled to bytecode, then generate_deploy_venom()
  produces deploy IR with the runtime bytecode embedded as a data section

- Add deploy epilogue (_emit_deploy_epilogue) that copies runtime bytecode
  from the data section to memory and returns it, with proper handling for
  immutables (Cancun mcopy vs pre-Cancun identity precompile)

- Update phases.py to use the new two-phase API for experimental_codegen

- Run fix_mem_loc() and run_passes_on() after IR generation to prepare
  for assembly generation (CFG normalization, optimizations)

- Set entry_function on both contexts (required by function inliner pass)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements builtin dispatch infrastructure and initial builtin handlers:
- Task 16a: Call dispatch infrastructure with modular builtins/ directory
- Task 16b: Simple builtins (len, empty, min, max, abs)
- Task 16c: Unsafe math ops (unsafe_add/sub/mul/div, pow_mod256, addmod, mulmod)
- Task 16d: Hashing (keccak256, sha256)

The builtins are organized into separate files by category:
- simple.py: len, empty, min, max, abs
- math.py: unsafe math operations
- hashing.py: keccak256, sha256

Adds VenomBuilder.select() for branchless conditional selection using
xor(b, mul(cond, xor(a, b))) - matches ir_node_to_venom.py implementation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Implement concat() for bytes/string concatenation
- Implement slice() with adhoc support (msg.data, self.code, addr.code)
- Implement extract32() with output type clamping
- Add copy_memory_dynamic() to context for runtime-length copies
- Restructure builtin tests into separate files by category

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement the convert(value, type) builtin function for direct Venom
codegen. Handles all type conversions:

- to_bool: any type -> bool (nonzero check)
- to_int: bytes/decimal/address/flag -> integer with clamping
- to_decimal: integer/bytes -> fixed-point (multiply by 10^10)
- to_bytes_m: integer/bytes -> bytesM (left-align)
- to_address: bytes/integer -> address (160-bit clamp)
- to_bytes/to_string: bytestring pointer casts with length check
- to_flag: integer -> flag type with range check

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ports vyper/codegen/abi_encoder.py logic to Venom IR. Creates
vyper/codegen_venom/abi_encoder.py with abi_encode_to_buf() function
that handles all type cases:

- Fast path: static types with matching Vyper/ABI layout (copy)
- Bytestrings: copy + zero padding
- Complex types (tuples, structs, static arrays): element-by-element
- Dynamic arrays: length word + loop encoding

Updates stmt.py to use the new encoder for external function returns.
Adds unit tests covering all encoding paths.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create codegen_venom/abi/ directory structure
- Move abi_encoder.py to abi/abi_encoder.py
- Add abi_decoder.py with full decode implementation:
  - needs_clamp(), int_clamp(), bytes_clamp() for validation
  - clamp_bytestring(), clamp_dyn_array() with bounds checking
  - _getelemptr_abi() for double dereference pattern
  - abi_decode_to_buf() main entry point
- Add abi/__init__.py with re-exports
- Add 46 unit tests for decoder

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wire up the abi_encode and abi_decode builtins to use the internal
ABI encode/decode routines. Also register deprecated aliases
_abi_encode and _abi_decode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add system.py with low-level operation builtins:
- raw_call: external call with full kwarg support (gas, value, max_outsize,
  is_delegate_call, is_static_call, revert_on_failure)
- send: ether transfer with optional gas stipend
- raw_log: emit LOG0-4 with variable topics
- raw_revert: revert with custom data

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement create operation builtins for Venom IR:
- raw_create: deploy from bytecode with optional ctor args
- create_minimal_proxy_to: EIP-1167 minimal proxy creation
- create_copy_of: copy another contract's bytecode
- create_from_blueprint: deploy from EIP-5202 blueprint

All support optional salt (CREATE2) and revert_on_failure kwargs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement remaining builtin functions for venom codegen:
- EC precompiles: ecrecover, ecadd, ecmul
- Block info: blockhash, blobhash
- Decimal truncation: floor, ceil
- Wei conversion: as_wei_value
- Type constants: min_value, max_value, epsilon
- Numeric: isqrt (Babylonian method)
- Debug: breakpoint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement lower_Log in stmt.py to emit LOG0-LOG4 opcodes for events:
- topic0: event signature hash (keccak256 of signature)
- topic1-3: indexed parameters (up to 3)
- data: non-indexed parameters, ABI-encoded as tuple

Also fix VenomBuilder.log() interface to match EVM order:
log(topic_count, offset, size, topic0, topic1, ...)

The builder now handles internal reordering to match venom IR format.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement Assert and Raise statement lowering for the Venom IR:

- lower_Assert: handles simple assert, UNREACHABLE, and assert with reason string
- lower_Raise: handles bare raise, UNREACHABLE, and raise with reason string
- _assert_with_reason: shared logic for assert with message
- _revert_with_reason: encodes Error(string) for revert with reason

Key implementation details:
- Simple assert: jnz to ok/fail blocks, fail block reverts with 0,0
- UNREACHABLE: fail block uses invalid opcode
- With reason: encodes Error(string) selector (0x08c379a0) + ABI-encoded
  message tuple, reverts from buf+28 with 4 + encoded_len bytes
- Message evaluation happens in constant context to prevent state changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements:
- lower_Tuple() for tuple literal construction (a, b, c)
- _lower_struct_constructor() for struct literals MyStruct(x=1, y=2)
- _lower_interface_constructor() for interface constructors
- _copy_complex_type() for multi-word assignment with temp buffer
- _lower_tuple_unpack() for tuple unpacking (a, b = expr)

Uses conservative temp buffer approach for overlap safety - the
optimizer can eliminate unnecessary copies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement external call lowering for the direct-to-Venom codegen:

- Add `lower_ExtCall()` and `lower_StaticCall()` dispatch methods
- Implement `_lower_external_call()` for full external call handling:
  - Argument ABI encoding with 4-byte method selector
  - CALL/STATICCALL dispatch based on function mutability
  - Return value ABI decoding with bounds checking
  - extcodesize contract existence check
  - Revert propagation on call failure
  - Support for kwargs: value, gas, skip_contract_check, default_return_value

- Add 20 tests covering:
  - Basic staticcall/extcall
  - Return types (uint256, bool, address, bytes32)
  - Kwargs handling (value, gas, skip_contract_check, default_return_value)
  - Multiple calls, conditional calls, nested interface calls
  - Mutability handling (view/pure -> staticcall, nonpayable/payable -> call)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add memory DynArray length helpers to context.py
- Implement _lower_dynarray_append() for storage and memory DynArrays
- Implement _lower_dynarray_pop() for storage and memory DynArrays
- Implement lower_List() for list literal lowering
- Add comprehensive tests for all DynArray operations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix bug in len(msg.data) handling: was checking for vy_ast.Attribute
when msg is actually a vy_ast.Name node.

Add comprehensive test suite for string/bytes operations:
- len() for memory and storage bytes/string/dynarray
- len(msg.data) special case
- concat() for memory and storage sources
- slice() for standard, adhoc, and storage sources
- extract32() with various output types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix Operand type alias in builder.py to use IROperand base class
  (eliminates ~265 mypy arg-type errors)
- Add constants.py for magic number extraction
- Add arithmetic.py skeleton for safe arithmetic extraction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
charles-cooper and others added 10 commits January 11, 2026 04:49
Add defensive CompilerPanic for .code access outside slice() context,
matching the pattern used for msg.data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- raw_call: check if value kwarg is provided (not its value) for delegate/static
- .code: only trigger error for address types, not struct fields named "code"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- bytesm downcast: use bounds check (shl+iszero+assert) instead of mask
- Add regression tests for extract32, create salt, dynarray overlap,
  internal args, and address.code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete 492 unit tests (81%) that only checked isinstance(result, IRVariable)
without verifying correctness. These tests would pass with broken codegen.

Keep 114 tests that verify:
- Handler registration (guards missing implementations)
- Decision logic (needs_clamp, pass_via_stack, is_word_type)
- Numeric invariants (EIP-1167 bytecode lengths, calldatasize)
- Control flow structure (assert/raise/loop/if blocks)
- Literal values (specific value verification)
- Regression tests (bytesM downcast clamp)

Functional tests (11,500+) already cover behavior comprehensively.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete 606 unit tests that were added during venom codegen development.
After systematic audit, all were found redundant with the functional
test suite (11,500+ tests).

The tests fell into these categories:
- 80% only checked isinstance(result, IRVariable) - passes with broken code
- 15% tested helper functions covered by behavioral tests
- 5% tested Python infrastructure or optimizations

Key insight: codegen unit tests are inherently brittle (many valid IR
representations) while functional tests verify actual behavior. The
functional suite already covers all the behaviors these tests claimed
to verify, including:
- Default parameter entry point generation (test_default_param_abi)
- bytesM downcast clamping (test_conversion_failures: 1488 cases)
- ABI encode/decode correctness
- Control flow behavior
- Calling conventions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace hardcoded DST_OFFSET=64 with dynamic alloca-based allocation.
codegen_venom now uses 100% alloca-based memory management.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add `_generate_selector_section_sparse()` for O(1) average-case dispatch
- Uses `jumptable_utils.generate_sparse_jumptable_buckets()` for bucket calculation
- Creates "selector_buckets" data section with 2-byte offsets per bucket
- Uses `djmp` instruction to dynamically jump to bucket label
- Each bucket does linear search (typically 1-3 items)
- Select sparse jumptable when >3 external functions, else linear
- Fix `builder.offset()` signature to match Venom IR spec

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add _generate_selector_section_dense() for O(1) codesize-optimized
selector dispatch using two-level perfect hash.

Selection logic (matches legacy):
- opt_none: linear search
- opt_codesize with >4 functions: dense jumptable
- >3 functions: sparse jumptable
- otherwise: linear search

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…fsets

The previous implementation used fixed literal offsets (256+) as a
"staging area" for kwargs communication between entry points and common
body. This could collide with allocator-assigned memory when positional
args exceeded 256 bytes of allocations.

Replace with shared allocas that are created before entry points and
used by both entry points and common body. This matches how legacy
ir_node_to_venom handles kwargs via _alloca_table, ensuring the
allocator manages all memory without collisions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ntal flag

- Delete vyper/venom/ir_node_to_venom.py (~860 LOC) - no longer needed
  since legacy path goes directly IRnode → assembly via compile_ir
- Gate cfg/cfg_runtime output formats behind --experimental-codegen
- Move _pass_via_stack/_returns_word helpers to codegen_venom/__init__.py
- Clean up unused imports in venom/__init__.py and compiler/phases.py

Co-Authored-By: Charles Cooper <cooper.charles.m@gmail.com>
@charles-cooper charles-cooper marked this pull request as draft January 15, 2026 02:39
Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

charles-cooper and others added 6 commits January 15, 2026 04:35
Pre-Cancun copy_memory now uses identity precompile for copies >= 96
bytes (3 words) instead of unrolling. This produces smaller bytecode
for large struct/array copies on pre-Cancun networks.

Also removes test_convert_basicblock_simple.py which tested the deleted
ir_node_to_venom module.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- abi_encode_to_buf now always returns encoded length (remove returns_len param)
- Replace compile-time pointer arithmetic with runtime ops (optimizer folds)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move inline imports to module level (except circular import prevention)
- Replace `T | None` with `Optional[T]` for type annotations
- Fix mypy errors with proper type assertions and annotations
- Fix flake8 errors (unused imports, line length, unused variables)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The test infrastructure uses parse_vyper_source() which runs the Lark
grammar checker. Unlike Python's parser, Lark doesn't support implicit
string concatenation, so byte strings must remain on single lines.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The test comment said "remove venom related from output formats" but
never actually did. cfg/cfg_runtime require --experimental-codegen.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The ret instruction was only scheduling return_pc for stack reordering,
leaving return values in arbitrary order on the stack. This caused
struct return values to be swapped when functions were not inlined
(--optimize codesize).

Fix: schedule all ret operands (values + return_pc) so the stack
scheduler places them in correct order per IR convention.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Jan 16, 2026

Codecov Report

❌ Patch coverage is 88.39370% with 612 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.76%. Comparing base (9964d42) to head (08651ac).

Files with missing lines Patch % Lines
vyper/codegen_venom/context.py 76.81% 90 Missing and 28 partials ⚠️
vyper/codegen_venom/expr.py 92.61% 41 Missing and 34 partials ⚠️
vyper/codegen_venom/module.py 88.38% 59 Missing and 15 partials ⚠️
vyper/codegen_venom/stmt.py 89.31% 41 Missing and 26 partials ⚠️
vyper/codegen_venom/builtins/convert.py 81.92% 35 Missing and 10 partials ⚠️
vyper/codegen_venom/builtins/misc.py 85.28% 32 Missing and 7 partials ⚠️
vyper/codegen_venom/abi/abi_encoder.py 80.54% 27 Missing and 9 partials ⚠️
vyper/venom/builder.py 93.03% 16 Missing and 4 partials ⚠️
vyper/codegen_venom/abi/abi_decoder.py 90.81% 9 Missing and 9 partials ⚠️
vyper/codegen_venom/builtins/abi.py 87.78% 11 Missing and 5 partials ⚠️
... and 14 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4811      +/-   ##
==========================================
- Coverage   93.29%   91.76%   -1.54%     
==========================================
  Files         148      171      +23     
  Lines       20592    25318    +4726     
  Branches     3577     4285     +708     
==========================================
+ Hits        19211    23232    +4021     
- Misses        924     1443     +519     
- Partials      457      643     +186     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

charles-cooper and others added 2 commits January 16, 2026 04:31
… storage

When assigning empty values ([], b"", empty()) to storage DynArray or
Bytestring, legacy codegen only writes 0 to the length slot. The
experimental codegen was copying the entire buffer including potentially
uninitialized element slots, causing hevm symbolic equivalence tests to
fail due to different storage states.

Fix: detect empty values at assignment time and only sstore/tstore 0
to the length slot, matching legacy behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When copying DynArray to storage, legacy codegen writes only
length + actual elements. Experimental codegen was writing full
capacity (storage_size_in_words), including garbage beyond length.

This caused hevm symbolic equivalence failures because the storage
state after assignment differed between legacy and experimental.

Fix: add _copy_dynarray_to_storage() that loops over actual length
and writes only length + 1 words per element, matching legacy.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@charles-cooper charles-cooper changed the title feat: add direct AST-to-Venom IR codegen (experimental) feat[venom]: add direct venom pipeline Jan 16, 2026
@github-actions
Copy link

github-actions bot commented Feb 6, 2026

📊 Bytecode Size Changes (venom)

Contract legacy-O2 legacy-Os -O2 -O3 -Os
curvefi/amm/stableswap/meta_implementation/meta_implementation_v_700.vy 23610 22805 21289 (🔴+2587) 20866 (🔴+2500) 19875 (🔴+2523)
curvefi/legacy/CurveStableSwapMetaNG.vy 24952 23578 21054 (🔴+1630) 20885 (🔴+1605) 19796 (🔴+2107)
curvefi/amm/stableswap/implementation/implementation_v_700.vy 24962 23769 20735 (🔴+946) 20424 (🔴+846) 19373 (🔴+1380)
curvefi/legacy/CurveStableSwapNG.vy 24473 23298 20174 (🔴+922) 19881 (🔴+855) 18838 (🔴+1365)
curvefi/amm/tricryptoswap/implementation/implementation_v_200.vy 20590 19825 18630 (🔴+2137) 18342 (🔴+2104) 17543 (🔴+2229)
yearnfi/VaultV3.vy 19972 19063 17422 (🔴+760) 16968 (🔴+504) 14939 (🔴+885)
curvefi/amm/twocryptoswap/implementation/implementation_v_210.vy 18090 17350 16832 (🔴+2140) 16465 (🔴+2023) 15701 (🔴+2221)
curvefi/legacy/CurveCryptoSwap2.vy 18947 18382 16186 (🔴+1718) 15782 (🔴+1609) 15260 (🔴+1728)
yearnfi/VaultV2.vy 16676 15763 14510 (🔴+1207) 14486 (🔴+1245) 13248 (🔴+1294)
curvefi/amm/stableswap/factory/factory_v_100.vy 14558 13978 14006 (🔴+1079) 13236 (🔴+866) 12552 (🔴+1125)
curvefi/amm/stableswap/views/views_v_120.vy 12784 12368 10671 (🔴+1075) 10363 (🔴+1035) 10200 (🔴+1450)
curvefi/gauge/child_gauge/implementation/implementation_v_110.vy 12338 11561 10637 (🔴+621) 10479 (🔴+596) 9636 (🔴+651)
curvefi/gauge/child_gauge/implementation/implementation_v_100.vy 12017 11249 10333 (🔴+598) 10176 (🔴+574) 9342 (🔴+628)
curvefi/amm/tricryptoswap/math/math_v_200.vy 11055 10992 9678 (🔴+452) 9124 (🔴+390) 8554 (🔴+590)
curvefi/legacy/CurveCryptoMathOptimized3.vy 11054 10991 9677 (🔴+452) 9123 (🔴+390) 8554 (🔴+590)
curvefi/gauge/child_gauge/implementation/implementation_v_020.vy 10665 9947 9308 (🔴+618) 9117 (🔴+603) 8380 (🔴+667)
curvefi/helpers/router/router_v_110.vy 6717 6717 7341 (🔴+1523) 6883 (🔴+1405) 6915 (🔴+1469)
curvefi/amm/tricryptoswap/views/views_v_200.vy 7821 7776 7246 (🔴+1074) 6942 (🔴+911) 7030 (🔴+1093)
curvefi/registries/metaregistry/metaregistry_v_110.vy 7590 6732 7111 (🔴+782) 6354 (🔴+496) 6154 (🔴+807)
curvefi/helpers/stable_swap_meta_zap/stable_swap_meta_zap_v_100.vy 7302 7067 6903 (🔴+983) 6637 (🔴+861) 6605 (🔴+1151)
curvefi/amm/twocryptoswap/views/views_v_200.vy 6991 6946 6695 (🔴+997) 6408 (🔴+838) 6479 (🔴+1016)
curvefi/registries/metaregistry/registry_handlers/stableswap/handler_v_110.vy 6633 6259 6563 (🔴+902) 5663 (🔴+384) 6203 (🔴+987)
curvefi/amm/twocryptoswap/factory/factory_v_200.vy 5540 5252 6065 (🔴+798) 5366 (🔴+741) 4956 (🔴+735)
curvefi/amm/tricryptoswap/factory/factory_v_200.vy 5246 5021 6049 (🔴+1218) 5509 (🔴+1184) 5153 (🔴+1070)
curvefi/amm/twocryptoswap/math/math_v_210.vy 6666 6666 5598 (🔴+377) 5581 (🔴+360) 5119 (🔴+472)
curvefi/gauge/child_gauge/factory/factory_v_201.vy 4844 4547 4300 (🔴+305) 4295 (🔴+318) 3841 (🔴+301)
curvefi/registries/metaregistry/registry_handlers/tricryptoswap/handler_v_110.vy 4241 3939 4230 (🔴+653) 3877 (🔴+577) 3906 (🔴+703)
curvefi/registries/metaregistry/registry_handlers/twocryptoswap/handler_v_110.vy 4186 3884 4104 (🔴+640) 3743 (🔴+544) 3757 (🔴+692)
yearnfi/VaultFactory.vy 3765 3617 4064 (🔴+147) 3028 (🔴+101) 3043 (🔴+107)
curvefi/gauge/child_gauge/factory/factory_v_100.vy 4183 3914 3677 (🔴+323) 3669 (🔴+333) 3253 (🔴+290)
curvefi/registries/address_provider/address_provider_v_201.vy 2973 2782 2981 (🔴+329) 2971 (🔴+326) 2599 (🔴+234)
curvefi/amm/stableswap/math/math_v_100.vy 3067 3046 2663 (🔴+221) 2660 (🔴+219) 2455 (🔴+157)
curvefi/helpers/rate_provider/rate_provider_v_101.vy 3260 3260 2535 (🔴+66) 2454 (🔴+7) 2356 (🔴+66)
curvefi/helpers/rate_provider/rate_provider_v_100.vy 2847 2841 2223 (🟢-131) 2151 (🟢-195) 2044 (🟢-49)
curvefi/helpers/deposit_and_stake_zap/deposit_and_stake_zap_v_100.vy 2322 2316 2218 (🔴+182) 2165 (🔴+158) 1986 (🔴+187)
curvefi/governance/relayer/taiko/relayer_v_001.vy 2068 2064 1726 (🟢-69) 1701 (🟢-87) 1611 (🟢-58)
curvefi/governance/relayer/polygon_cdk/relayer_v_101.vy 1556 1523 1512 (🔴+52) 1504 (🔴+51) 1388 (🔴+65)
curvefi/governance/relayer/arb_orbit/relayer_v_101.vy 1266 1262 1212 (🔴+40) 1200 (🔴+42) 1144 (🔴+51)
curvefi/governance/relayer/op_stack/relayer_v_101.vy 1186 1182 1145 (🔴+36) 1136 (🔴+38) 1077 (🔴+47)
curvefi/governance/relayer/not_rollup/relayer_v_100.vy 1168 1153 1144 (🔴+41) 1141 (🔴+43) 1066 (🔴+54)
curvefi/governance/vault/vault_v_100.vy 964 941 967 (🔴+74) 972 (🔴+79) 913 (🔴+89)
curvefi/governance/agent/agent_v_100.vy 541 541 496 (🟢-21) 492 (🟢-25) 442 (🟢-21)
curvefi/governance/agent/agent_v_101.vy 541 541 496 (🟢-21) 492 (🟢-25) 442 (🟢-21)
curvefi/governance/relayer/relayer_v_100.vy 496 496 472 (🔴+7) 467 (🔴+7) 472 (🔴+7)

Full bytecode sizes

Contract legacy-O2 legacy-Os -O2 -O3 -Os
curvefi/amm/stableswap/meta_implementation/meta_implementation_v_700.vy 23610 22805 21289 20866 19875
curvefi/legacy/CurveStableSwapMetaNG.vy 24952 23578 21054 20885 19796
curvefi/amm/stableswap/implementation/implementation_v_700.vy 24962 23769 20735 20424 19373
curvefi/legacy/CurveStableSwapNG.vy 24473 23298 20174 19881 18838
curvefi/amm/tricryptoswap/implementation/implementation_v_200.vy 20590 19825 18630 18342 17543
yearnfi/VaultV3.vy 19972 19063 17422 16968 14939
curvefi/amm/twocryptoswap/implementation/implementation_v_210.vy 18090 17350 16832 16465 15701
curvefi/legacy/CurveCryptoSwap2.vy 18947 18382 16186 15782 15260
yearnfi/VaultV2.vy 16676 15763 14510 14486 13248
curvefi/amm/stableswap/factory/factory_v_100.vy 14558 13978 14006 13236 12552
curvefi/amm/stableswap/views/views_v_120.vy 12784 12368 10671 10363 10200
curvefi/gauge/child_gauge/implementation/implementation_v_110.vy 12338 11561 10637 10479 9636
curvefi/gauge/child_gauge/implementation/implementation_v_100.vy 12017 11249 10333 10176 9342
curvefi/amm/tricryptoswap/math/math_v_200.vy 11055 10992 9678 9124 8554
curvefi/legacy/CurveCryptoMathOptimized3.vy 11054 10991 9677 9123 8554
curvefi/gauge/child_gauge/implementation/implementation_v_020.vy 10665 9947 9308 9117 8380
curvefi/helpers/router/router_v_110.vy 6717 6717 7341 6883 6915
curvefi/amm/tricryptoswap/views/views_v_200.vy 7821 7776 7246 6942 7030
curvefi/registries/metaregistry/metaregistry_v_110.vy 7590 6732 7111 6354 6154
curvefi/helpers/stable_swap_meta_zap/stable_swap_meta_zap_v_100.vy 7302 7067 6903 6637 6605
curvefi/amm/twocryptoswap/views/views_v_200.vy 6991 6946 6695 6408 6479
curvefi/registries/metaregistry/registry_handlers/stableswap/handler_v_110.vy 6633 6259 6563 5663 6203
curvefi/amm/twocryptoswap/factory/factory_v_200.vy 5540 5252 6065 5366 4956
curvefi/amm/tricryptoswap/factory/factory_v_200.vy 5246 5021 6049 5509 5153
curvefi/amm/twocryptoswap/math/math_v_210.vy 6666 6666 5598 5581 5119
curvefi/gauge/child_gauge/factory/factory_v_201.vy 4844 4547 4300 4295 3841
curvefi/registries/metaregistry/registry_handlers/tricryptoswap/handler_v_110.vy 4241 3939 4230 3877 3906
curvefi/registries/metaregistry/registry_handlers/twocryptoswap/handler_v_110.vy 4186 3884 4104 3743 3757
yearnfi/VaultFactory.vy 3765 3617 4064 3028 3043
curvefi/gauge/child_gauge/factory/factory_v_100.vy 4183 3914 3677 3669 3253
curvefi/registries/address_provider/address_provider_v_201.vy 2973 2782 2981 2971 2599
curvefi/amm/stableswap/math/math_v_100.vy 3067 3046 2663 2660 2455
curvefi/helpers/rate_provider/rate_provider_v_101.vy 3260 3260 2535 2454 2356
curvefi/helpers/rate_provider/rate_provider_v_100.vy 2847 2841 2223 2151 2044
curvefi/helpers/deposit_and_stake_zap/deposit_and_stake_zap_v_100.vy 2322 2316 2218 2165 1986
curvefi/governance/relayer/taiko/relayer_v_001.vy 2068 2064 1726 1701 1611
curvefi/governance/relayer/polygon_cdk/relayer_v_101.vy 1556 1523 1512 1504 1388
curvefi/governance/relayer/arb_orbit/relayer_v_101.vy 1266 1262 1212 1200 1144
curvefi/governance/relayer/op_stack/relayer_v_101.vy 1186 1182 1145 1136 1077
curvefi/governance/relayer/not_rollup/relayer_v_100.vy 1168 1153 1144 1141 1066
curvefi/governance/vault/vault_v_100.vy 964 941 967 972 913
curvefi/governance/agent/agent_v_100.vy 541 541 496 492 442
curvefi/governance/agent/agent_v_101.vy 541 541 496 492 442
curvefi/governance/relayer/relayer_v_100.vy 496 496 472 467 472

vyperteam-bot added 2 commits February 13, 2026 14:40
# Conflicts:
#	vyper/venom/__init__.py
#	vyper/venom/ir_node_to_venom.py
staticcall/call already writes min(returndatasize, ret_len) bytes to
buf._ptr. The subsequent returndatacopy was overwriting the same data.
payload_bound caps reads at size_bound() (== ret_len), so even when
returndatasize > ret_len, the extra bytes are never accessed.

This matches master's behavior which has zero returndatacopy in the
external call success path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant