Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat[perf]: optimize optimizer #3782

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions vyper/codegen/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1049,10 +1049,10 @@ def _complex_make_setter(left, right, hi=None):
# sstore(add (dst ofst), (sload (add (src ofst)))) is 16 bytes,
# whereas loop overhead is 16-17 bytes.
base_cost = 3
if left._optimized.is_literal:
if left.optimized.is_literal:
# code size is smaller since add is performed at compile-time
base_cost += 1
if right._optimized.is_literal:
if right.optimized.is_literal:
base_cost += 1
# the formula is a heuristic, but it works.
# (CMC 2023-07-14 could get more detailed for PUSH1 vs
Expand All @@ -1076,10 +1076,10 @@ def _complex_make_setter(left, right, hi=None):
# cost for 0th word - (mstore dst (mload src))
base_unroll_cost = 12
nth_word_cost = base_unroll_cost
if not left._optimized.is_literal:
if not left.optimized.is_literal:
# (mstore (add N dst) (mload src))
nth_word_cost += 6
if not right._optimized.is_literal:
if not right.optimized.is_literal:
# (mstore dst (mload (add N src)))
nth_word_cost += 6

Expand Down
23 changes: 14 additions & 9 deletions vyper/codegen/ir_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
# this creates a magical block which maps to IR `with`
class _WithBuilder:
def __init__(self, ir_node, name, should_inline=False):
if should_inline and ir_node._optimized.is_complex_ir: # pragma: nocover
if should_inline and ir_node.optimized.is_complex_ir: # pragma: nocover
# this can only mean trouble
raise CompilerPanic("trying to inline a complex IR node")

Expand Down Expand Up @@ -175,6 +175,8 @@
self.func_ir = None
self.common_ir = None

self._optimized = None

assert self.value is not None, "None is not allowed as IRnode value"

# Determine this node's valency (1 if it pushes a value on the stack,
Expand Down Expand Up @@ -359,12 +361,12 @@
return ret

# TODO would be nice to rename to `gas_estimate` or `gas_bound`
@property
@cached_property
def gas(self):
return self._gas + self.add_gas_estimate

# the IR should be cached and/or evaluated exactly once
@property
@cached_property
def is_complex_ir(self):
# list of items not to cache. note can add other env variables
# which do not change, e.g. calldatasize, coinbase, etc.
Expand Down Expand Up @@ -424,12 +426,15 @@
# eventually
return self.location is not None

@property # probably could be cached_property but be paranoid
def _optimized(self):
# TODO figure out how to fix this circular import
from vyper.ir.optimizer import optimize
@property
def optimized(self):
if self._optimized is None:
# TODO figure out how to fix this circular import
from vyper.ir.optimizer import optimize

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
vyper.ir.optimizer
begins an import cycle.

self._optimized = optimize(self)

return optimize(self)
return self._optimized

# This function is slightly confusing but abstracts a common pattern:
# when an IR value needs to be computed once and then cached as an
Expand All @@ -451,7 +456,7 @@
# because a non-literal expr could turn into a literal,
# (e.g. `(add 1 2)`)
# TODO this could really be moved into optimizer.py
should_inline = not self._optimized.is_complex_ir
should_inline = not self.optimized.is_complex_ir

return _WithBuilder(self, name, should_inline)

Expand Down
3 changes: 3 additions & 0 deletions vyper/ir/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ def optimize(node: IRnode) -> IRnode:


def _optimize(node: IRnode, parent: Optional[IRnode]) -> Tuple[bool, IRnode]:
if node._optimized is not None:
return node._optimized is not node, node._optimized

starting_symbols = node.unique_symbols

res = [_optimize(arg, node) for arg in node.args]
Expand Down
Loading