Skip to content

Commit

Permalink
Modify greedy and asm generation to store it inside CFG Block
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcere committed Jan 21, 2025
1 parent 7582e15 commit 5adbb0d
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 90 deletions.
47 changes: 8 additions & 39 deletions src/execution/main_execution.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import argparse
import json
from typing import Dict, Any, Optional
from typing import Dict, Optional
from pathlib import Path
from timeit import default_timer as dtimer
import pandas as pd

from parser.utils_parser import split_json
from global_params.types import Yul_CFG_T
from parser.optimizable_block_list import compute_sub_block_cfg
from parser.parser import parse_CFG_from_json_dict
from parser.cfg import store_sfs_json, CFG
from parser.cfg import CFG
from execution.sol_compilation import SolidityCompilation
from greedy.greedy import greedy_standalone
from solution_generation.statistics import generate_statistics_info
from solution_generation.reconstruct_bytecode import asm_from_ids, asm_from_cfg, store_asm_output, store_binary_output
from liveness.liveness_analysis import dot_from_analysis
from solution_generation.reconstruct_bytecode import asm_from_cfg, store_asm_output, store_binary_output
from greedy.ids_from_spec import cfg_spec_ids
from liveness.layout_generation import layout_generation
from cfg_methods.preprocessing_methods import preprocess_cfg

Expand Down Expand Up @@ -76,43 +72,16 @@ def analyze_single_cfg(cfg: CFG, final_dir: Path, args: argparse.Namespace):
tags_dict = preprocess_cfg(cfg, dot_file_dir, args.visualize)

x = dtimer()
jsons_list = layout_generation(cfg, final_dir.joinpath("stack_layouts"))

sfs_final_dir = final_dir.joinpath("sfs")
sfs_final_dir.mkdir(exist_ok=True, parents=True)
layout_generation(cfg, final_dir.joinpath("stack_layouts"))
y = dtimer()

print("Layout generation: " + str(y - x) + "s")

block_name2asm = dict()

json_asm_contract = {}

if args.greedy:
csv_rows = []
for i, jsons in enumerate(jsons_list):
for block_name, sfs in jsons.items():
cfg_sfs_dir = sfs_final_dir.joinpath(str(i))
cfg_sfs_dir.mkdir(exist_ok=True, parents=True)
store_sfs_json(block_name, sfs, cfg_sfs_dir)
try:
_, time, solution_found = greedy_standalone(sfs)
csv_row = generate_statistics_info(block_name, solution_found, time, sfs)
csv_rows.append(csv_row)
solution_asm = asm_from_ids(sfs, solution_found)
block_name2asm[block_name] = solution_asm
except Exception as e:
block_name2asm[block_name] = []
print(f"Error in the greedy algorithm processing {block_name}: {e}")

# Generate complete asm from CFG object + dict

json_asm_contract = asm_from_cfg(cfg, block_name2asm, tags_dict, args.source)
df = pd.DataFrame(csv_rows)
df.to_csv(final_dir.joinpath("statistics.csv"))
cfg_spec_ids(cfg, final_dir.joinpath("statistics.csv"))

json_asm_contract = asm_from_cfg(cfg, tags_dict, args.source)
return json_asm_contract


def main(args):
print("Grey Main")
Expand Down
64 changes: 64 additions & 0 deletions src/greedy/ids_from_spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Module that handles the generation of the ids from the greedy algorithm.
"""
from pathlib import Path
from typing import Tuple, List, Dict, Optional

import pandas as pd

from global_params.types import instr_id_T
from parser.cfg_block import CFGBlock
from parser.cfg_block_list import CFGBlockList
from parser.cfg_object import CFGObject
from parser.cfg import CFG
from greedy.greedy import greedy_standalone
from solution_generation.statistics import generate_statistics_info


def cfg_block_spec_ids(cfg_block: CFGBlock) -> Tuple[str, float, List[instr_id_T]]:
outcome, time, greedy_ids = greedy_standalone(cfg_block.spec)
cfg_block.greedy_ids = greedy_ids
return outcome, time, greedy_ids


def cfg_block_list_spec_ids(cfg_blocklist: CFGBlockList) -> List[Dict]:
"""
Generates the assembly code of all the blocks in a block list and returns the statistics
"""
csv_dicts = []
for block_name, block in cfg_blocklist.blocks.items():
outcome, time, greedy_ids = cfg_block_spec_ids(block)
csv_dicts.append(generate_statistics_info(block_name, greedy_ids, time, block.spec))
return csv_dicts


def cfg_object_spec_ids(cfg: CFGObject):
"""
Generates the assembly code for a
"""
csv_dicts = cfg_block_list_spec_ids(cfg.blocks)
for cfg_function in cfg.functions.values():
csv_dicts.extend(cfg_block_list_spec_ids(cfg_function.blocks))
return csv_dicts


def recursive_cfg_spec_ids(cfg: CFG):
"""
Generates the assembly for all the blocks inside the CFG, excluding the sub objects.
"""
csv_dicts = []
for cfg_object in cfg.get_objects().values():
csv_dicts.extend(cfg_object_spec_ids(cfg_object))
sub_object = cfg_object.subObject
if sub_object is not None:
csv_dicts.extend(recursive_cfg_spec_ids(sub_object))
return csv_dicts


def cfg_spec_ids(cfg: CFG, csv_file: Optional[Path]) -> None:
"""
Generates the greedy ids from the specification inside the cfg and stores in the field "greedy_ids" inside
each block. Stores the information from the greedy generation in a csv file
"""
csv_dicts = recursive_cfg_spec_ids(cfg)
pd.DataFrame(csv_dicts).to_csv(csv_file)
42 changes: 20 additions & 22 deletions src/liveness/layout_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""
import heapq
import itertools
import json
from typing import Dict, List, Type, Any, Set, Tuple, Optional
import networkx as nx
from pathlib import Path
Expand Down Expand Up @@ -283,6 +284,9 @@ def __init__(self, object_id: str, block_list: CFGBlockList, liveness_info: Dict
self._layout_dir = name.joinpath("layouts")
self._layout_dir.mkdir(exist_ok=True, parents=True)

self._sfs_dir = name.joinpath("sfs")
self._sfs_dir.mkdir(exist_ok=True, parents=True)

# Guess: we need to traverse the code following the dominance tree in topological order
# This is because in the dominance tree together with the SSA, all the nodes

Expand Down Expand Up @@ -357,8 +361,9 @@ def _construct_code_from_block(self, block: CFGBlock, input_stacks: Dict[str, Li
# We store the output stack in the dict, as we have built a new element
output_stacks[block_id] = output_stack

# We build the corresponding specification
# We build the corresponding specification and store it in the block
block_json = block.build_spec(input_stack, output_stack)
block.spec = block_json

return block_json

Expand Down Expand Up @@ -402,49 +407,45 @@ def _construct_code_from_block_list(self):

return json_info

def build_layout(self):
def build_layout(self) -> None:
"""
Builds the layout of the blocks from the given representation
Builds the layout of the blocks from the given representation and stores it inside the CFG
"""
json_info = self._construct_code_from_block_list()

# Here we just store the layouts and the sfs
renamed_graph = information_on_graph(self._cfg_graph,
{block_name: print_stacks(block_name, json_info[block_name])
for block_name in
self._block_list.blocks})

nx.nx_agraph.write_dot(renamed_graph, self._layout_dir.joinpath(f"{self._component_id}.dot"))

return json_info
for block_name, specification in json_info.items():
with open(self._sfs_dir.joinpath(block_name + ".json"), 'w') as f:
json.dump(specification, f)


def layout_generation_cfg(cfg: CFG, final_dir: Path = Path(".")) -> Dict[cfg_object_T, Dict[component_name_T, Dict[block_id_T, SMS_T]]]:
def layout_generation_cfg(cfg: CFG, final_dir: Path = Path(".")) -> None:
"""
Returns the information from the liveness analysis and also stores a dot file for each analyzed structure
in "final_dir". It does not consider sub objects
Generates the layout for all the blocks in the objects inside the CFG level, excluding sub-objects
"""
cfg_info = construct_analysis_info(cfg)
component2inputs = functions_inputs_from_components(cfg)
results = perform_liveness_analysis_from_cfg_info(cfg_info)
component2block_list = cfg.generate_id2block_list()

jsons = defaultdict(lambda: {})

for object_name, object_liveness in results.items():
for component_name, component_liveness in object_liveness.items():
cfg_info_suboject = cfg_info[object_name][component_name]["block_info"]
digraph = digraph_from_block_info(cfg_info_suboject.values())

layout = LayoutGeneration(component_name, component2block_list[object_name][component_name], component_liveness,
component2inputs, final_dir, digraph)

layout_blocks = layout.build_layout()
jsons[object_name][component_name] = layout_blocks
layout = LayoutGeneration(component_name, component2block_list[object_name][component_name],
component_liveness, component2inputs, final_dir, digraph)

return jsons
layout.build_layout()


def layout_generation(cfg: CFG, final_dir: Path = Path("."), positions: List[str] = None) -> Tuple[Dict[str, SMS_T], Dict[str, Any]]:
def layout_generation(cfg: CFG, final_dir: Path = Path("."), positions: List[str] = None) -> None:
"""
Returns the information from the liveness analysis and also stores a dot file for each analyzed structure
in "final_dir"
Expand All @@ -454,12 +455,9 @@ def layout_generation(cfg: CFG, final_dir: Path = Path("."), positions: List[str

layout_dir = final_dir.joinpath('_'.join([str(position) for position in positions]))
layout_dir.mkdir(parents=True, exist_ok=True)
current_layout = layout_generation_cfg(cfg, layout_dir)
object_layouts = dict()
layout_generation_cfg(cfg, layout_dir)
for i, (cfg_name, cfg_object) in enumerate(cfg.get_objects().items()):

sub_object = cfg_object.get_subobject()
if sub_object is not None:
object_layouts[cfg_name] = layout_generation(sub_object, final_dir, positions + [str(i)])

return current_layout, object_layouts
layout_generation(sub_object, final_dir, positions + [str(i)])
42 changes: 33 additions & 9 deletions src/parser/cfg_block.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import itertools
import logging

from global_params.types import instr_id_T, dependencies_T, var_id_T, block_id_T, function_name_T
from global_params.types import instr_id_T, dependencies_T, var_id_T, block_id_T, function_name_T, SMS_T
from parser.cfg_instruction import CFGInstruction, build_push_spec, build_pushtag_spec
from parser.utils_parser import is_in_input_stack, is_in_output_stack, are_dependent_interval, get_empty_spec, \
get_expression, are_dependent_accesses, replace_pos_instrsid, generate_dep, get_interval, replace_aliasing_spec
Expand Down Expand Up @@ -99,6 +99,9 @@ def __init__(self, identifier: block_id_T, instructions: List[CFGInstruction], t
# at position i is generated. Hence, all phi functions must define the values in the same order
self._entries: List[block_id_T] = []

self._spec: SMS_T = None
self._greedy_ids: List[instr_id_T] = None

@property
def final_stack_elements(self) -> List[str]:
"""
Expand Down Expand Up @@ -158,7 +161,7 @@ def remove_instruction(self, instr_idx: int) -> CFGInstruction:
Removes the instruction at position instr_index, updating the last split instruction if it affects
the last instruction
"""

instr_idx = (len(self._instructions) + instr_idx) % len(self._instructions)
if instr_idx >= len(self._instructions):
raise ValueError("Attempting to remove an instruction index out of bounds")
Expand Down Expand Up @@ -223,7 +226,7 @@ def insert_jump_instruction(self, tag_value: str) -> None:
"""
# Add a PUSH tag instruction
self._instructions.append(CFGInstruction("PUSH [tag]", [], [tag_value]))

# Add a JUMP instruction
jump_instr = CFGInstruction("JUMP", [tag_value], [])
self._instructions.append(jump_instr)
Expand Down Expand Up @@ -470,7 +473,7 @@ def _build_spec_for_sequence(self, instructions: List[CFGInstruction], map_instr
Builds the specification for a sequence of instructions. "map_instructions" is passed as an argument
to reuse declarations from other blocks, as we might have split the corresponding basic block
"""

spec = {}

uninter_functions = []
Expand All @@ -481,7 +484,7 @@ def _build_spec_for_sequence(self, instructions: List[CFGInstruction], map_instr

#Key is the original variable and the value is the one that we use
aliasing_dict = {}

unprocessed_instr = None

for i, ins in enumerate(instructions):
Expand Down Expand Up @@ -518,7 +521,7 @@ def _build_spec_for_sequence(self, instructions: List[CFGInstruction], map_instr

out_var = out_var_list[0]
new_out_var = new_out_var_list[0]

candidate_instructions = filter(lambda x: out_var in x["inpt_sk"],uninter_functions)
for uninter in candidate_instructions:
pos = uninter["inpt_sk"].index(out_var)
Expand All @@ -528,7 +531,7 @@ def _build_spec_for_sequence(self, instructions: List[CFGInstruction], map_instr
else:
old_variable = ins.get_out_args()[0]
aliasing_dict[old_variable] = ins_spec["outpt_sk"][0]

# We must remove the final output variable from the unprocessed instruction and
# add the inputs from that instruction
if self.split_instruction is not None:
Expand Down Expand Up @@ -600,13 +603,14 @@ def _build_spec_for_sequence(self, instructions: List[CFGInstruction], map_instr
spec["min_length_bounds"] = 0
spec["min_length"] = 0
spec["rules"] = ""
spec["name"] = self.block_id

vars_list = spec["variables"]
tgt_stack = spec["tgt_ws"]

if aliasing_dict != {}:
replace_aliasing_spec(aliasing_dict, uninter_functions, vars_list, tgt_stack)

return spec, new_out_idx, map_positions_instructions

def _include_jump_tag(self, block_spec: Dict, out_idx: int, block_tags_dict: Dict, block_tag_idx: int) -> \
Expand Down Expand Up @@ -651,6 +655,26 @@ def build_spec(self, initial_stack: List[str], final_stack: List[str]) -> Dict[s

return spec

@property
def spec(self) -> SMS_T:
return self._spec

@spec.setter
def spec(self, spec: SMS_T) -> None:
if self._spec is not None:
raise ValueError("Specification already computed")
self._spec = spec

@property
def greedy_ids(self) -> List[instr_id_T]:
return self._greedy_ids

@greedy_ids.setter
def greedy_ids(self, greedy_ids: List[instr_id_T]) -> None:
if self._greedy_ids is not None:
raise ValueError("Greedy ids already computed")
self._greedy_ids = greedy_ids

def __str__(self):
s = "BlockID: " + self.block_id + "\n"
s += "Type: " + self._jump_type + "\n"
Expand Down
Loading

0 comments on commit 5adbb0d

Please sign in to comment.