diff --git a/src/cfg_methods/cfg_block_actions/inline_function.py b/src/cfg_methods/cfg_block_actions/inline_function.py index a61960b3..44c37440 100644 --- a/src/cfg_methods/cfg_block_actions/inline_function.py +++ b/src/cfg_methods/cfg_block_actions/inline_function.py @@ -159,7 +159,7 @@ def _rename_input_args(self, call_instruction: CFGInstruction) -> None: """ relabel_dict = self._function_input2call_input(call_instruction) n_modified_vars = len(relabel_dict) - rename_cfg_function(self._cfg_function, set(), relabel_dict, 0) + rename_cfg_function(self._cfg_function, set(), relabel_dict, 0, False) assert sum(1 for old_name, new_name in relabel_dict.items() if old_name != new_name) == n_modified_vars, \ f"Inlining {self._function_name} should not assign new variables" @@ -171,7 +171,7 @@ def _rename_output_args(self, call_instruction: CFGInstruction) -> None: """ relabel_dict = self._call_output2function_output(call_instruction) n_modified_vars = len(relabel_dict) - rename_variables_block_list(self._cfg_blocklist, set(), relabel_dict, 0) + rename_variables_block_list(self._cfg_blocklist, set(), relabel_dict, 0, False) assert sum(1 for old_name, new_name in relabel_dict.items() if old_name != new_name) == n_modified_vars, \ f"Inlining {self._function_name} should not assign new variables" diff --git a/src/cfg_methods/function_inlining.py b/src/cfg_methods/function_inlining.py index dd7f56ca..c01b3a16 100644 --- a/src/cfg_methods/function_inlining.py +++ b/src/cfg_methods/function_inlining.py @@ -2,7 +2,7 @@ Module to perform function inlining. """ import json -from copy import deepcopy +from copy import deepcopy, copy from typing import Set, Dict, Tuple, List from collections import defaultdict @@ -82,12 +82,10 @@ def inline_functions_cfg_object(cfg_object: CFGObject, function_call_info: funct function2costs: function2costs_T): # Dict that maps each initial block name in the CFG to the set of blocks in which it can be split block2current: Dict[block_id_T, List[block_id_T]] = dict() - free_index = _free_index_from_object(cfg_object) - print("FREE", free_index) function_call_info, topological_sort = prune_cycles_topological_sort(function_call_info) - for function_name in topological_sort: + for func_idx, function_name in enumerate(topological_sort): call_info = function_call_info[function_name] cfg_function = cfg_object.functions[function_name] @@ -109,8 +107,8 @@ def inline_functions_cfg_object(cfg_object: CFGObject, function_call_info: funct split_block_index = 0 position_index = instr_pos + _adjust_phi_function_idx_misalignment(cfg_block_list.blocks[split_blocks[split_block_index]]) - function_to_inline, renaming_dict, free_index = _generate_function_to_inline(cfg_function, call_idx, - len(call_info), free_index) + function_to_inline, renaming_dict = _generate_function_to_inline(cfg_function, func_idx, call_idx, + len(call_info)) # nx.nx_agraph.write_dot(cfg_block_list.to_graph_info(), f"antes.dot") @@ -182,15 +180,15 @@ def _must_be_inlined(function_name: function_name_T, call_info_list: List[call_i return (inlining_extra_size - no_inlining_extra_size) <= 20 * no_inlining_extra_gas -def _generate_function_to_inline(original_function: CFGFunction, current_call_idx: int, - n_calls: int, free_index: int) -> Tuple[CFGFunction, Dict[block_id_T, block_id_T], int]: +def _generate_function_to_inline(original_function: CFGFunction, func_idx: int, current_call_idx: int, + n_calls: int) -> Tuple[CFGFunction, Dict[block_id_T, block_id_T]]: """ We must rename the blocks when inlining to avoid conflicts, as the function can be inlined multiple times in the same function (and hence, the same blocks would appear multiple times). We also return the renaming dict """ # If there is just one call, we avoid renaming the blocks if n_calls == 1: - return original_function, dict(), 0 + return original_function, dict() # If we are making multiple copies, we copy it call_idx - 1 times, as the last one should remove it elif current_call_idx == n_calls - 1: copied_function = original_function @@ -202,11 +200,24 @@ def _generate_function_to_inline(original_function: CFGFunction, current_call_id renaming_dict = {block_name: f"{block_name}_copy_{current_call_idx}" for block_name in block_list.blocks} block_list.rename_blocks(renaming_dict) - new_free_index = rename_cfg_function(copied_function, set(f"v{idx}"for idx in range(free_index)), dict(), free_index) + var_ids = _var_ids_from_list(block_list) + renaming_vars = {var_: f"{var_}_f{func_idx}_{current_call_idx}" for var_ in var_ids} + renaming_vars.update((var_, f"{var_}_f{func_idx}_{current_call_idx}") for var_ in copied_function.arguments) + bef = copy(renaming_vars) + nx.nx_agraph.write_dot(copied_function.blocks.to_graph_info(), "bef.dot") + n_renaming_vars = len(renaming_vars) + rename_cfg_function(copied_function, set(), renaming_vars, 0, False) + + nx.nx_agraph.write_dot(copied_function.blocks.to_graph_info(), "aft.dot") + + print(renaming_vars, bef) + print(block_list.name, set(renaming_vars.keys()).difference(bef.keys())) + assert n_renaming_vars == len(renaming_vars), \ + "Variable renaming in function duplication should not assign new variables" copied_function.exits = [renaming_dict.get(exit_id, exit_id) for exit_id in copied_function.exits] copied_function.name = f"{copied_function.name}_copy_{current_call_idx}" - return copied_function, renaming_dict, new_free_index + return copied_function, renaming_dict # Methods to find a cycle in the call functions, remove them and generate the topological sort @@ -245,22 +256,23 @@ def _prune_cycling_function_calls(function2call_info: function2call_info_T, return function2call_info -def _free_index_from_object(cfg_object: CFGObject) -> int: - free_index = max(0, _free_index_from_list(cfg_object.blocks)) +def _var_ids_from_object(cfg_object: CFGObject) -> Set[var_id_T]: + var_ids = _var_ids_from_list(cfg_object.blocks) for function in cfg_object.functions.values(): - free_index = max(free_index, _free_index_from_list(function.blocks)) - return free_index + var_ids.update(_var_ids_from_list(function.blocks)) + return var_ids -def _free_index_from_list(block_list: CFGBlockList) -> int: - max_idx = 0 +def _var_ids_from_list(block_list: CFGBlockList) -> Set[var_id_T]: + var_ids = set() for block in block_list.blocks.values(): for instr in block.get_instructions(): for arg in [*instr.get_in_args(), *instr.get_out_args()]: if not arg.startswith("0x"): - max_idx = max(max_idx, _idx_from_var(arg)) - return max_idx - + var_ids.add(arg) + cond = block.get_condition() + if cond is not None: + if not cond.startswith("0x"): + var_ids.add(cond) + return var_ids -def _idx_from_var(var_: var_id_T) -> int: - return int(var_[1:]) diff --git a/src/cfg_methods/variable_renaming.py b/src/cfg_methods/variable_renaming.py index 8e1dc8a3..407f2e1b 100644 --- a/src/cfg_methods/variable_renaming.py +++ b/src/cfg_methods/variable_renaming.py @@ -29,24 +29,24 @@ def rename_variables_cfg(cfg: CFG) -> None: def rename_cfg_function(cfg_function: CFGFunction, assigned_global: Set[var_id_T], - renaming_dict: Dict[var_id_T, var_id_T], free_index: int) -> int: + renaming_dict: Dict[var_id_T, var_id_T], free_index: int, generate_next: bool = True) -> int: cfg_function.arguments, free_index = modified_var_list(cfg_function.arguments, assigned_global, renaming_dict, free_index) - free_index = rename_variables_block_list(cfg_function.blocks, assigned_global, renaming_dict, free_index) + free_index = rename_variables_block_list(cfg_function.blocks, assigned_global, renaming_dict, free_index, generate_next) return free_index def rename_variables_block_list(block_list: CFGBlockList, variables_assigned: Set[var_id_T], - renaming_dict: Dict[var_id_T, var_id_T], free_index: int) -> int: + renaming_dict: Dict[var_id_T, var_id_T], free_index: int, generate_next: bool = True) -> int: # The renaming dict keeps track of the changes in this block to maintain the coherence for block_name, block in block_list.blocks.items(): for instruction in block.get_instructions(): - free_index = modify_vars_in_instr(instruction, variables_assigned, renaming_dict, free_index) + free_index = modify_vars_in_instr(instruction, variables_assigned, renaming_dict, free_index, generate_next) if block.get_condition() is not None: new_cond_list, free_index = modified_var_list([block.get_condition()], variables_assigned, - renaming_dict, free_index) + renaming_dict, free_index, generate_next) block.set_condition(new_cond_list[0]) # We have to update the names with the ones that have already been assigned variables_assigned.update(renaming_dict.values()) @@ -54,16 +54,16 @@ def rename_variables_block_list(block_list: CFGBlockList, variables_assigned: Se def modify_vars_in_instr(instruction: CFGInstruction, assigned_global: Set[var_id_T], - renaming_dict: Dict[var_id_T, var_id_T], free_index: int) -> int: + renaming_dict: Dict[var_id_T, var_id_T], free_index: int, generate_next: bool = True) -> int: instruction.out_args, free_index = modified_var_list(instruction.out_args, assigned_global, - renaming_dict, free_index) + renaming_dict, free_index, generate_next) instruction.in_args, free_index = modified_var_list(instruction.in_args, assigned_global, - renaming_dict, free_index) + renaming_dict, free_index, generate_next) return free_index def modified_var_list(var_list: List[var_id_T], assigned_global: Set[var_id_T], - renaming_dict: Dict[var_id_T, var_id_T], free_index: int) -> Tuple[List[var_id_T], int]: + renaming_dict: Dict[var_id_T, var_id_T], free_index: int, generate_next: bool = True) -> Tuple[List[var_id_T], int]: updated_var_list = [] for variable in var_list: new_variable_name = renaming_dict.get(variable, None) @@ -78,6 +78,7 @@ def modified_var_list(var_list: List[var_id_T], assigned_global: Set[var_id_T], # If it has already been assigned elif variable in assigned_global: + assert generate_next, "The renaming shouldn't introduce new elements" new_variable_name = f"v{free_index}" renaming_dict[variable] = new_variable_name updated_var_list.append(new_variable_name) @@ -85,7 +86,8 @@ def modified_var_list(var_list: List[var_id_T], assigned_global: Set[var_id_T], # Last case: first time we encounter this value in the block list else: - free_index = max(free_index, int(variable[1:]) + 1) + if generate_next: + free_index = max(free_index, int(variable[1:]) + 1) renaming_dict[variable] = variable updated_var_list.append(variable) diff --git a/src/liveness/layout_generation.py b/src/liveness/layout_generation.py index a3140c80..ba43cc47 100644 --- a/src/liveness/layout_generation.py +++ b/src/liveness/layout_generation.py @@ -388,7 +388,6 @@ def build_layout(self): Builds the layout of the blocks from the given representation """ json_info = self._construct_code_from_block_list() - print(json_info.keys()) renamed_graph = information_on_graph(self._cfg_graph, {block_name: print_stacks(block_name, json_info[block_name]) diff --git a/src/parser/utils_parser.py b/src/parser/utils_parser.py index 9d986451..cbf7945b 100644 --- a/src/parser/utils_parser.py +++ b/src/parser/utils_parser.py @@ -167,7 +167,6 @@ def get_expression(var: var_id_T, instructions) -> expression_T: return var assert all(candidates[i] == candidates[i+1] for i in range(len(candidates) - 1)), \ "[ERROR]: A variable cannot be generated by more than one instruction" - # Case: build expression from subexpressions new_instruction = candidates[0]