Skip to content

Commit

Permalink
Further cases for renaming in order to avoid collisions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcere committed Nov 11, 2024
1 parent 9921ea9 commit 781a820
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 36 deletions.
4 changes: 2 additions & 2 deletions src/cfg_methods/cfg_block_actions/inline_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
56 changes: 34 additions & 22 deletions src/cfg_methods/function_inlining.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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]

Expand All @@ -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")

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:])
22 changes: 12 additions & 10 deletions src/cfg_methods/variable_renaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,41 @@ 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())
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) -> 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)
Expand All @@ -78,14 +78,16 @@ 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)
free_index += 1

# 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)

Expand Down
1 change: 0 additions & 1 deletion src/liveness/layout_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
1 change: 0 additions & 1 deletion src/parser/utils_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down

0 comments on commit 781a820

Please sign in to comment.