Problem: function copy don't work#4940
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Venom IR function cloning to make IRFunction.copy() produce a more usable duplicate of a function’s basic blocks, addressing the reported issue that “function copy don’t work”.
Changes:
- Clear the auto-created entry block when cloning, then rebuild the function from copied basic blocks.
- Preserve
last_variableacross copies to keep variable numbering consistent. - Fix basic-block parent linkage (
new_bb.parent = new) in the cloned function.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def copy(self): | ||
| new = IRFunction(self.name) | ||
| new.clear_basic_blocks() | ||
| new.last_variable = self.last_variable |
There was a problem hiding this comment.
IRFunction.copy() currently copies last_variable and basic blocks, but it doesn't copy other function-level metadata like _invoke_param_count / _has_memory_return_buffer_param. These fields influence invoke-related optimizations/analyses; dropping them on copy can change behavior compared to the original function. If the intent is a faithful clone, copy these fields as well (or document/reset them explicitly if not).
| new.last_variable = self.last_variable | |
| new.last_variable = self.last_variable | |
| new._invoke_param_count = self._invoke_param_count | |
| new._has_memory_return_buffer_param = self._has_memory_return_buffer_param |
| new = IRFunction(self.name) | ||
| new.clear_basic_blocks() | ||
| new.last_variable = self.last_variable |
There was a problem hiding this comment.
Creating an IRFunction and immediately calling clear_basic_blocks() works, but it relies on the constructor always auto-inserting an entry block (which then becomes an unused orphan). Consider a dedicated constructor arg / classmethod for creating an empty IRFunction (used here and in FunctionInlinerPass._clone_function) to make cloning less error-prone and avoid creating throwaway blocks.
| new.last_variable = self.last_variable | ||
| for bb in self.get_basic_blocks(): | ||
| new_bb = bb.copy() | ||
| new_bb.parent = new | ||
| new.append_basic_block(new_bb) | ||
|
|
There was a problem hiding this comment.
This change is fixing core cloning behavior (variable counter + basic-block/function parent linkage). There are many Venom unit tests, but none appear to cover IRFunction.copy() specifically; adding a regression test that exercises copy() (and then runs at least one analysis/pass on the copy, or appends an instruction and checks variable naming) would help prevent this from breaking again.
| new.last_variable = self.last_variable | |
| for bb in self.get_basic_blocks(): | |
| new_bb = bb.copy() | |
| new_bb.parent = new | |
| new.append_basic_block(new_bb) | |
| new.last_variable = self.last_variable | |
| bb_map = {} | |
| copied_bbs = [] | |
| for bb in self.get_basic_blocks(): | |
| new_bb = bb.copy() | |
| new_bb.parent = new | |
| new.append_basic_block(new_bb) | |
| bb_map[bb] = new_bb | |
| copied_bbs.append((bb, new_bb)) | |
| for old_bb, new_bb in copied_bbs: | |
| if hasattr(new_bb, "out_bbs") and hasattr(old_bb, "out_bbs"): | |
| new_bb.out_bbs = type(new_bb.out_bbs)( | |
| bb_map.get(out_bb, out_bb) for out_bb in old_bb.out_bbs | |
| ) | |
| if hasattr(new_bb, "in_bbs") and hasattr(old_bb, "in_bbs"): | |
| new_bb.in_bbs = type(new_bb.in_bbs)( | |
| bb_map.get(in_bb, in_bb) for in_bb in old_bb.in_bbs | |
| ) | |
| new_bb.parent = new |
| @@ -128,8 +128,11 @@ def error_msg(self) -> Optional[str]: | |||
|
|
|||
| def copy(self): | |||
| new = IRFunction(self.name) | |||
There was a problem hiding this comment.
IRFunction.copy() creates new = IRFunction(self.name) with no ctx, so new.ctx is None. Many analyses/passes assume fn.ctx exists (e.g. IRAnalysesCache._ensure_global_analyses_cache() dereferences self.function.ctx), so a copied function will crash if analyzed unless the caller manually reattaches a context. Consider constructing the copy with ctx=self.ctx (or explicitly setting new.ctx = self.ctx) so the copy is usable with the existing analysis/pass infrastructure.
| new = IRFunction(self.name) | |
| new = IRFunction(self.name, ctx=self.ctx) |
harkal
left a comment
There was a problem hiding this comment.
This method is essentially dead code. We should probably remove it all-together.
we are experimenting with the API to build EVM bytecodes programmatically, we are not sure if it's the right approach yet, so no strong opinions on this. |
What I did
Fix
IRFunctioncopy method to preserve necessary fields.How I did it
How to verify it
Commit message
Commit message for the final, squashed PR. (Optional, but reviewers will appreciate it! Please see our commit message style guide for what we would ideally like to see in a commit message.)
Description for the changelog
Cute Animal Picture