Skip to content

Conversation

@harkal
Copy link
Collaborator

@harkal harkal commented Dec 4, 2025

What I did

Add flow-sensitive range analysis and assert pruning

How I did it

How to verify it

Commit message

This commit adds `VariableRangeAnalysis` to track per-block/instruction ValueRange environments
with loop widening, evaluates arithmetic/bitwise/comparison ops and phi merges to tighten bounds 
under branch conditions.

As a minimal usage example for the analysis this commit also implements assert dropping when 
proven nonzero via range info. 

Finally it provides cover for the lattice/optimizer with unit tests.

Description for the changelog

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

wip

wip

wip

more

non zero case

more tests

refactor tests

tests

wip

improve precition

rename ValueInfo to VariableRangeAnalysis and update references

lint

rename

worklist refactor

optimization

Revert "optimization"

This reverts commit 0c1cd77f6f3f1008f655926ddbd4c963c133ae07.

refactor ValueRange

refactor

bytes_range

add comment

fancy

more

asserts

split out assert elimination

move test

lint

widening

rename test file

comments

convert `is_` methods to properties

more comments

udpate sigs

dispatch with dict

lint

split out evaluators

refactor

lint

private

cleanup

redundant check

cleanups

post fix

multi-output update

reduntant

back to TOP BOTTOM
@codecov
Copy link

codecov bot commented Dec 4, 2025

Codecov Report

❌ Patch coverage is 72.25275% with 202 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.54%. Comparing base (418dbda) to head (570bed3).

Files with missing lines Patch % Lines
vyper/venom/analysis/variable_range/evaluators.py 69.02% 63 Missing and 42 partials ⚠️
vyper/venom/analysis/variable_range/analysis.py 71.02% 45 Missing and 26 partials ⚠️
vyper/venom/analysis/variable_range/value_range.py 81.30% 17 Missing and 3 partials ⚠️
vyper/venom/passes/assert_elimination.py 80.64% 2 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4790      +/-   ##
==========================================
- Coverage   93.26%   92.54%   -0.72%     
==========================================
  Files         148      153       +5     
  Lines       20531    21259     +728     
  Branches     3566     3755     +189     
==========================================
+ Hits        19148    19674     +526     
- Misses        926     1053     +127     
- Partials      457      532      +75     

☔ 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.

harkal and others added 10 commits December 18, 2025 13:18
Known bugs in variable range analysis:

Miscompiles (assert elimination):
- lt false branch excludes negatives from signed ranges
- gt true branch excludes negatives from signed ranges
- iszero false branch intersects with [1, MAX], excluding negatives
- signextend BOTTOM propagates through phi, losing branch values

Additional bugs:
- lt with negative constant gives wrong result (signed vs unsigned)
- signextend produces BOTTOM for out-of-range input
- and with signed range gives narrow upper bound
- unsigned comparison narrowing doesn't handle signed ranges

Root cause: analysis uses single signed range but doesn't account for
signed/unsigned duality in EVM (negative values are large unsigned).

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@harkal harkal marked this pull request as ready for review January 6, 2026 16:51

# If the range is growing, widen to top
if new_range.lo < old_range.lo or new_range.hi > old_range.hi:
return ValueRange.top()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just putting a note here that there should be a possibility to be more precise in the widening. You would need to have the set of some values that would be considered for possible ends for a range after widening (and for that set to be finite) to be able to get fixed point. But that should be outside of the scope of this PR

if self.is_top:
lo = SIGNED_MIN if lo is None else max(SIGNED_MIN, lo)
hi = UNSIGNED_MAX if hi is None else min(UNSIGNED_MAX, hi)
return ValueRange((lo, hi)) if lo <= hi else ValueRange.empty()
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this ternary necessary shouldn't it be handled by __post_init__

assert self.bounds is not None
new_lo = self.bounds[0] if lo is None else max(self.bounds[0], lo)
new_hi = self.bounds[1] if hi is None else min(self.bounds[1], hi)
return ValueRange((new_lo, new_hi)) if new_lo <= new_hi else ValueRange.empty()
Copy link
Collaborator

Choose a reason for hiding this comment

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

same as above

index = _get_uint_literal(index_op)
if index is not None and index >= 32:
return ValueRange.constant(0)
return ValueRange.bytes_range()
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is kinda imprecise for case like

main:
    %x = calldataload 0
    %cond = lt %x, 10
    jnz %cond, @then, @else
then:
    %b = byte 0, %x ; here the result could be only in range [0, 10] but this would set it to [0, 255]
    sink %b
else:
    sink %x

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.

3 participants