From 781a8203b4c44018cb1ffa8dd8bc4a049038f316 Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:09:23 +0100 Subject: [PATCH 1/6] Further cases for renaming in order to avoid collisions --- .../cfg_block_actions/inline_function.py | 4 +- src/cfg_methods/function_inlining.py | 56 +++++++++++-------- src/cfg_methods/variable_renaming.py | 22 ++++---- src/liveness/layout_generation.py | 1 - src/parser/utils_parser.py | 1 - 5 files changed, 48 insertions(+), 36 deletions(-) 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] From b36648accedf86558c2728f379c9aa1cb0328c4f Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:38:38 +0100 Subject: [PATCH 2/6] Function renaming using just the dictionary --- .../cfg_block_actions/inline_function.py | 6 +-- src/cfg_methods/function_inlining.py | 4 +- src/cfg_methods/variable_renaming.py | 47 +++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/cfg_methods/cfg_block_actions/inline_function.py b/src/cfg_methods/cfg_block_actions/inline_function.py index 44c37440..4d184c0d 100644 --- a/src/cfg_methods/cfg_block_actions/inline_function.py +++ b/src/cfg_methods/cfg_block_actions/inline_function.py @@ -9,7 +9,7 @@ from parser.cfg_function import CFGFunction from parser.cfg_object import CFGObject from cfg_methods.cfg_block_actions.utils import modify_comes_from, modify_successors -from cfg_methods.variable_renaming import rename_variables_block_list, rename_cfg_function +from cfg_methods.variable_renaming import rename_block_list, rename_function class InlineFunction(BlockAction): @@ -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, False) + rename_function(self._cfg_function, relabel_dict) 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, False) + rename_block_list(self._cfg_blocklist, relabel_dict) 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 c01b3a16..7c72290c 100644 --- a/src/cfg_methods/function_inlining.py +++ b/src/cfg_methods/function_inlining.py @@ -15,7 +15,7 @@ from parser.cfg_object import CFGObject from parser.cfg import CFG from cfg_methods.cfg_block_actions.inline_function import InlineFunction -from cfg_methods.variable_renaming import rename_cfg_function +from cfg_methods.variable_renaming import rename_function from cfg_methods.cost_computation import function2costs_T, compute_gas_bytes # For each time a function is invoked, we store the position of the instruction (int) in the @@ -206,7 +206,7 @@ def _generate_function_to_inline(original_function: CFGFunction, func_idx: int, 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) + rename_function(copied_function, renaming_vars) nx.nx_agraph.write_dot(copied_function.blocks.to_graph_info(), "aft.dot") diff --git a/src/cfg_methods/variable_renaming.py b/src/cfg_methods/variable_renaming.py index 407f2e1b..05ecedc4 100644 --- a/src/cfg_methods/variable_renaming.py +++ b/src/cfg_methods/variable_renaming.py @@ -92,3 +92,50 @@ def modified_var_list(var_list: List[var_id_T], assigned_global: Set[var_id_T], updated_var_list.append(variable) return updated_var_list, free_index + + +# Methods for renaming just using a dict + +def rename_function(cfg_function: CFGFunction, renaming_dict: Dict[var_id_T, var_id_T]) -> None: + cfg_function.arguments = rename_var_list(cfg_function.arguments, renaming_dict) + rename_block_list(cfg_function.blocks, renaming_dict) + + +def rename_block_list(block_list: CFGBlockList, renaming_dict: Dict[var_id_T, var_id_T]) -> None: + # 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(): + rename_vars_in_instr(instruction, renaming_dict) + + if block.get_condition() is not None: + new_cond_list = rename_var_list([block.get_condition()], renaming_dict) + block.set_condition(new_cond_list[0]) + block.final_stack_elements = rename_var_list(block.final_stack_elements, renaming_dict) + + +def rename_vars_in_instr(instruction: CFGInstruction, renaming_dict: Dict[var_id_T, var_id_T]) -> None: + instruction.out_args = rename_var_list(instruction.out_args, renaming_dict) + instruction.in_args = rename_var_list(instruction.in_args, renaming_dict) + + +def rename_var_list(var_list: List[var_id_T], renaming_dict: Dict[var_id_T, var_id_T]) -> List[var_id_T]: + """ + Only renames the variables that appear in the renaming dict + """ + updated_var_list = [] + for variable in var_list: + new_variable_name = renaming_dict.get(variable, None) + + # We ignore constants in the renaming and the already assigned ones are added as is + if variable.startswith("0x"): + updated_var_list.append(variable) + + # If it is not None, it means we have already assigned a new name to this variable + elif new_variable_name is not None: + updated_var_list.append(new_variable_name) + + else: + updated_var_list.append(variable) + + return updated_var_list From 53c94517a6217eeac93220b43d5aef2a427948a5 Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Wed, 13 Nov 2024 04:16:59 +0100 Subject: [PATCH 3/6] Fix variable renamign --- .../cfg_block_actions/inline_function.py | 4 +- src/cfg_methods/function_inlining.py | 14 +-- src/cfg_methods/variable_renaming.py | 85 +++++-------------- 3 files changed, 29 insertions(+), 74 deletions(-) diff --git a/src/cfg_methods/cfg_block_actions/inline_function.py b/src/cfg_methods/cfg_block_actions/inline_function.py index 4d184c0d..52fab358 100644 --- a/src/cfg_methods/cfg_block_actions/inline_function.py +++ b/src/cfg_methods/cfg_block_actions/inline_function.py @@ -125,7 +125,9 @@ def perform_action(self): for exit_id in function_exists_ids: exit_block = self._cfg_blocklist.get_block(exit_id) # Finally, we remove the function return from the exit id - exit_block.remove_instruction(-1) + exit_function_return = exit_block.remove_instruction(-1) + assert exit_function_return.get_op_name() == "functionReturn", f"Last instruction of exit block " \ + f"{exit_id} must be a functionReturn" # is_correct, reason = validate_block_list_comes_from(self._cfg_blocklist) diff --git a/src/cfg_methods/function_inlining.py b/src/cfg_methods/function_inlining.py index 7c72290c..e42868ac 100644 --- a/src/cfg_methods/function_inlining.py +++ b/src/cfg_methods/function_inlining.py @@ -107,8 +107,7 @@ 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 = _generate_function_to_inline(cfg_function, func_idx, call_idx, - len(call_info)) + 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") @@ -201,17 +200,12 @@ def _generate_function_to_inline(original_function: CFGFunction, func_idx: int, block_list.rename_blocks(renaming_dict) 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") + renaming_vars = {var_: f"{var_.split('_')[0]}_f{func_idx}_{current_call_idx}" for var_ in var_ids} + renaming_vars.update((var_, f"{var_.split('_')[0]}_f{func_idx}_{current_call_idx}") for var_ in copied_function.arguments) + n_renaming_vars = len(renaming_vars) rename_function(copied_function, renaming_vars) - 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" diff --git a/src/cfg_methods/variable_renaming.py b/src/cfg_methods/variable_renaming.py index 05ecedc4..0e9e044a 100644 --- a/src/cfg_methods/variable_renaming.py +++ b/src/cfg_methods/variable_renaming.py @@ -13,14 +13,14 @@ def rename_variables_cfg(cfg: CFG) -> None: # Store which variable names have been already assigned for object_id, cfg_object in cfg.objectCFG.items(): - already_assigned = set() free_index = 0 - free_index = rename_variables_block_list(cfg_object.blocks, already_assigned, dict(), free_index) + renaming_dict, free_index = renaming_dict_from_instrs(cfg_object.blocks, [], free_index) + rename_block_list(cfg_object.blocks, renaming_dict) # We also consider the information per function for cfg_function in cfg_object.functions.values(): - free_index = rename_cfg_function(cfg_function, already_assigned, dict(), free_index) + free_index = new_variables_function(cfg_function, free_index) sub_object = cfg.get_subobject() @@ -28,73 +28,32 @@ def rename_variables_cfg(cfg: CFG) -> None: rename_variables_cfg(sub_object) -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, 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, generate_next) +def new_variables_function(cfg_function: CFGFunction, free_index: int) -> int: + renaming_dict, free_index = renaming_dict_from_instrs(cfg_function.blocks, cfg_function.arguments, free_index) + cfg_function.arguments = rename_var_list(cfg_function.arguments, renaming_dict) + rename_block_list(cfg_function.blocks, renaming_dict) 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, 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(): +def renaming_dict_from_instrs(block_list: CFGBlockList, input_args: List[var_id_T], + free_index: int) -> Tuple[Dict[var_id_T, var_id_T], int]: + """ + Generates a renaming dict from the output of the instructions, pointing to the next free index + """ + renaming_dict = dict() + # Note that input args from functions must be also assigned a new name + for input_arg in input_args: + renaming_dict[input_arg] = f"v{free_index}" + free_index += 1 + for block in block_list.blocks.values(): for instruction in block.get_instructions(): - 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, 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()) - return free_index - - -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, generate_next: bool = True) -> int: - instruction.out_args, free_index = modified_var_list(instruction.out_args, assigned_global, - renaming_dict, free_index, generate_next) - instruction.in_args, free_index = modified_var_list(instruction.in_args, assigned_global, - 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, 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) - - # We ignore constants in the renaming and the already assigned ones are added as is - if variable.startswith("0x"): - updated_var_list.append(variable) - - # If it is not None, it means we have already assigned a new name to this variable - elif new_variable_name is not None: - updated_var_list.append(new_variable_name) - - # 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) - free_index += 1 - - # Last case: first time we encounter this value in the block list - else: - if generate_next: - free_index = max(free_index, int(variable[1:]) + 1) - renaming_dict[variable] = variable - updated_var_list.append(variable) - - return updated_var_list, free_index + for out_arg in instruction.out_args: + renaming_dict[out_arg] = f"v{free_index}" + free_index += 1 + return renaming_dict, free_index -# Methods for renaming just using a dict def rename_function(cfg_function: CFGFunction, renaming_dict: Dict[var_id_T, var_id_T]) -> None: cfg_function.arguments = rename_var_list(cfg_function.arguments, renaming_dict) From 7bdb65bea3e7ac10f1ae3b8dac9286a4f362abd2 Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Wed, 13 Nov 2024 04:27:04 +0100 Subject: [PATCH 4/6] Pass function calls from each cfg object --- src/cfg_methods/sub_block_generation.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cfg_methods/sub_block_generation.py b/src/cfg_methods/sub_block_generation.py index 774f176d..8a244baf 100644 --- a/src/cfg_methods/sub_block_generation.py +++ b/src/cfg_methods/sub_block_generation.py @@ -18,11 +18,12 @@ def split_blocks_cfg(cfg: CFG) -> None: Splits the blocks in the cfg """ for object_id, cfg_object in cfg.objectCFG.items(): - modify_block_list_split(cfg_object.blocks) + function_names = list(cfg_object.functions.keys()) + modify_block_list_split(cfg_object.blocks, function_names) # We also consider the information per function for function_name, cfg_function in cfg_object.functions.items(): - modify_block_list_split(cfg_function.blocks) + modify_block_list_split(cfg_function.blocks, function_names) sub_object = cfg.get_subobject() @@ -30,7 +31,7 @@ def split_blocks_cfg(cfg: CFG) -> None: split_blocks_cfg(sub_object) -def modify_block_list_split(block_list: CFGBlockList) -> None: +def modify_block_list_split(block_list: CFGBlockList, function_calls: List[function_name_T]) -> None: """ Modifies a CFGBlockList by splitting blocks when function calls and split instructions are found """ @@ -46,7 +47,7 @@ def modify_block_list_split(block_list: CFGBlockList) -> None: instr = current_block.get_instructions()[instr_idx] is_split_instr = instr.get_op_name() in constants.split_block - is_function_call = instr.get_op_name() in cfg_block.function_calls + is_function_call = instr.get_op_name() in function_calls if is_split_instr or is_function_call: # Sub blocks contain a split instruction or a function call as the last instruction From dc302fd3cbebf1d5bcde4fa5314a0fecfa9d1e79 Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Wed, 13 Nov 2024 04:42:30 +0100 Subject: [PATCH 5/6] Long names for renaming --- src/cfg_methods/function_inlining.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cfg_methods/function_inlining.py b/src/cfg_methods/function_inlining.py index e42868ac..67884bec 100644 --- a/src/cfg_methods/function_inlining.py +++ b/src/cfg_methods/function_inlining.py @@ -200,8 +200,8 @@ def _generate_function_to_inline(original_function: CFGFunction, func_idx: int, block_list.rename_blocks(renaming_dict) var_ids = _var_ids_from_list(block_list) - renaming_vars = {var_: f"{var_.split('_')[0]}_f{func_idx}_{current_call_idx}" for var_ in var_ids} - renaming_vars.update((var_, f"{var_.split('_')[0]}_f{func_idx}_{current_call_idx}") for var_ in copied_function.arguments) + 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) n_renaming_vars = len(renaming_vars) rename_function(copied_function, renaming_vars) From dbeaefa5438439a898cbdf7083d8f77f0bc1d667 Mon Sep 17 00:00:00 2001 From: alexcere <48130030+alexcere@users.noreply.github.com> Date: Wed, 13 Nov 2024 05:02:33 +0100 Subject: [PATCH 6/6] Initial stack assigned to the input values from function --- src/liveness/layout_generation.py | 3 +-- src/liveness/liveness_analysis.py | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/liveness/layout_generation.py b/src/liveness/layout_generation.py index b0b3058b..e3feab88 100644 --- a/src/liveness/layout_generation.py +++ b/src/liveness/layout_generation.py @@ -296,8 +296,7 @@ def _construct_code_from_block(self, block: CFGBlock, input_stacks: Dict[str, Li # We introduce the necessary args in the generation of the first output stack layout # The stack elements we have to "force" a certain order correspond to the input parameters of # the function - input_stack = output_stack_layout([], self._function_inputs[self._component_id], - liveness_info.in_state.live_vars, self._variable_order[block_id]) + input_stack = self._function_inputs[self._block_list.name] input_stacks[block.block_id] = input_stack diff --git a/src/liveness/liveness_analysis.py b/src/liveness/liveness_analysis.py index 6b291718..67a8e0e4 100644 --- a/src/liveness/liveness_analysis.py +++ b/src/liveness/liveness_analysis.py @@ -17,6 +17,7 @@ # "terminal_blocks: the list of terminal block ids, in order to start the analysis cfg_info_T = Dict[str, Union[Dict[str, LivenessBlockInfo], List[str]]] +i = 0 class LivenessAnalysisInfo(BlockAnalysisInfo): """ @@ -189,8 +190,14 @@ def dot_from_analysis_cfg(cfg: CFG, final_dir: Path = Path(".")) -> Dict[str, Di renamed_digraph = nx.relabel_nodes(digraph, renaming_dict) short_component_name = shorten_name(component_name) + try: + nx.nx_agraph.write_dot(renamed_digraph, final_dir.joinpath(f"{short_component_name}.dot")) + except: - nx.nx_agraph.write_dot(renamed_digraph, final_dir.joinpath(f"{short_component_name}.dot")) + global i + + nx.nx_agraph.write_dot(renamed_digraph, final_dir.joinpath(f"too_long_name_{i}.dot")) + i += 1 return results