Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update parser with new Yul JSON format #4

Merged
merged 6 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions scripts/expressions_from_blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Script that computes all the expressions in a Yul JSON
"""
import sys
from typing import List, Dict
from parser.parser import parse_CFG
from parser.cfg_instruction import CFGInstruction


def create_expression(instruction: CFGInstruction, term_map: Dict[str, str]) -> str:
term_args = []
for in_arg in instruction.in_args:
term_args.append(term_map.get(in_arg, in_arg))
if len(term_args) > 0:
term_args_str = '(' + ', '.join(term_args) + ')'
else:
term_args_str = ""
full_term_repr = instruction.get_op_name() + term_args_str

# Only store the term repr if there is more than one element
if len(instruction.out_args) == 1:
term_map[instruction.out_args[0]] = full_term_repr

# We need to store a distinct term for each value produced by the opcode
elif len(instruction.out_args) > 1:
for i, out_arg in enumerate(instruction.out_args):
term_map[out_arg] = f"{full_term_repr}_{i}"

return full_term_repr


def compute_expressions(instructions: List[CFGInstruction]) -> List[str]:
"""
Returns the list of all expressions generated by the instructions in the list
"""
term_map = dict()
return [create_expression(instruction, term_map) for instruction in instructions if instruction.op != "PhiFunction"]


if __name__ == "__main__":
input_file = sys.argv[1]
cfg = parse_CFG(input_file)

for block_list_name, block_list in cfg.block_list.items():
print("Blocks in...", block_list_name)
for block_name, cfg_block in block_list.blocks.items():
print(block_name, compute_expressions(cfg_block.get_instructions()))
12 changes: 7 additions & 5 deletions src/execution/main_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from timeit import default_timer as dtimer
import pandas as pd

from parser.optimizable_block_list import compute_sub_block_cfg
from parser.parser import parse_CFG
from parser.cfg import store_sfs_json
from greedy.greedy import greedy_standalone
Expand Down Expand Up @@ -43,13 +44,13 @@ def main():
dot_file_dir = final_dir.joinpath("liveness")
dot_file_dir.mkdir(exist_ok=True, parents=True)

sub_block_cfg = compute_sub_block_cfg(cfg)

if args.visualize:
liveness_info = dot_from_analysis(cfg, dot_file_dir)
else:
liveness_info = perform_liveness_analysis(cfg)
liveness_info = dot_from_analysis(sub_block_cfg, dot_file_dir)

x = dtimer()
jsons = layout_generation(cfg, dot_file_dir)
jsons = layout_generation(sub_block_cfg, dot_file_dir)
sfs_final_dir = final_dir.joinpath("sfs")
sfs_final_dir.mkdir(exist_ok=True, parents=True)
y = dtimer()
Expand All @@ -59,10 +60,11 @@ def main():
if args.greedy:
csv_rows = []
for block_name, sfs in jsons.items():
store_sfs_json(sfs, sfs_final_dir)
store_sfs_json(block_name, sfs, sfs_final_dir)

outcome, time, solution_found = greedy_standalone(sfs)
csv_row = generate_statistics_info(block_name, solution_found, outcome, time, sfs)
csv_rows.append(csv_row)

df = pd.DataFrame(csv_rows)
df.to_csv("outcome.csv")
22 changes: 9 additions & 13 deletions src/liveness/layout_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""
import heapq
import itertools
from typing import Dict, List, Type, Any, Set, Tuple
from typing import Dict, List, Type, Any, Set, Tuple, Optional
import networkx as nx
from pathlib import Path

Expand Down Expand Up @@ -179,15 +179,15 @@ def var_order_repr(block_name: str, var_info: Dict[str, int]):
return '\n'.join(text_format)


def print_stacks(block_name: str, block: CFGBlock):
text_format = [f"{block_name}:", f"Src: {block.input_stack}", f"Tgt: {block.output_stack}"]
def print_stacks(block_name: str, json_dict: Dict[str, Any]):
text_format = [f"{block_name}:", f"Src: {json_dict[block_name]['src_ws']}", f"Tgt: {json_dict[block_name]['tgt_ws']}"]
return '\n'.join(text_format)


class LayoutGeneration:

def __init__(self, object_id, block_list: CFGBlockList, liveness_info: Dict[str, LivenessAnalysisInfo], name: Path,
cfg_graph: nx.Graph = None):
cfg_graph: Optional[nx.Graph] = None):
self._id = object_id
self._block_list = block_list
self._liveness_info = liveness_info
Expand Down Expand Up @@ -309,10 +309,6 @@ def _construct_code_from_block(self, block: CFGBlock, input_stacks: Dict[str, Li
block_json[subblock_name]["src_ws"] = input_stack
block_json[subblock_name]["tgt_ws"] = output_stack

# TODO: temporal hack to output the input and output stacks
block.input_stack = input_stack
block.output_stack = output_stack

return block_json

def _construct_code_from_block_list(self):
Expand Down Expand Up @@ -341,8 +337,8 @@ def _construct_code_from_block_list(self):
# Retrieve the block
current_block = self._block_list.get_block(block_name)

block_specification = self._construct_code_from_block(current_block, input_stacks, output_stacks, combined_stacks)

block_specification = self._construct_code_from_block(current_block, input_stacks,
output_stacks, combined_stacks)
json_info.update(block_specification)

successors = [possible_successor for possible_successor in
Expand All @@ -359,9 +355,9 @@ def build_layout(self):
"""
json_info = self._construct_code_from_block_list()

renamed_graph = information_on_graph(self._cfg_graph, {block_name: print_stacks(block_name, block)
for block_name, block in
self._block_list.blocks.items()})
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, Path(self._dir.parent).joinpath(self._dir.stem + "_stacks.dot"))
return json_info
Expand Down
Binary file added src/parser/__pycache__/__init__.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/cfg.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/cfg.cpython-38.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/cfg_block.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/cfg_block.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added src/parser/__pycache__/cfg_object.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/cfg_object.cpython-38.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/constants.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/constants.cpython-38.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/opcodes.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/opcodes.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file added src/parser/__pycache__/parser.cpython-311.pyc
Binary file not shown.
Binary file added src/parser/__pycache__/parser.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
48 changes: 42 additions & 6 deletions src/parser/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
from pathlib import Path
from parser.cfg_object import CFGObject
from parser.cfg_block_list import CFGBlockList
from typing import Dict, List, Optional
from typing import Dict, List, Optional, Callable


def store_sfs_json(blocks: Dict[str, Dict], final_path: Path) -> None:
def store_sfs_json(block_name: str, block: Dict[str, Dict], final_path: Path) -> None:
"""
Stores all SFS from the list of blocks in the corresponding folder
"""
for block_name, block in blocks.items():
file_to_store = final_path.joinpath(block_name + ".json")
with open(file_to_store, 'w') as f:
json.dump(block, f, indent=4)
file_to_store = final_path.joinpath(block_name + ".json")
with open(file_to_store, 'w') as f:
json.dump(block, f, indent=4)


class CFG:
Expand Down Expand Up @@ -79,5 +78,42 @@ def get_as_json(self):

return json_cfg

def cfg_block_list(self) -> List[CFGBlockList]:
"""
Returns the list of all blocks inside the same object, function and subobjects
"""
block_list = []
for object_id, cfg_object in self.objectCFG.items():
block_list.append(cfg_object.blocks)

# We also consider the information per function
for function_name, cfg_function in cfg_object.functions.items():
block_list.append(cfg_function.blocks)

subobject = self.get_subobject()

if subobject is not None:
block_list.extend(subobject.cfg_block_list())

return block_list

def modify_cfg_block_list(self, f: Callable[[CFGBlockList], CFGBlockList]) -> None:
"""
Applies a given function to all CFG lists inside the object
"""
for object_id, cfg_object in self.objectCFG.items():
cfg_object.blocks = f(cfg_object.blocks)
self.block_list[object_id] = cfg_object.blocks

# We also consider the information per function
for function_name, cfg_function in cfg_object.functions.items():
cfg_function.blocks = f(cfg_function.blocks)
self.block_list[function_name] = cfg_function.blocks

subobject = self.get_subobject()

if subobject is not None:
self.modify_cfg_block_list(f)

def __repr__(self):
return str(self.get_as_json())
27 changes: 17 additions & 10 deletions src/parser/cfg_block.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
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, is_assigment_var_used, get_empty_spec, get_expression, are_dependent_accesses, replace_pos_instrsid, generate_dep
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
import parser.constants as constants
import json
import networkx as nx

from typing import List, Dict, Tuple
from typing import List, Dict, Tuple, Any

global tag_idx
tag_idx = 0
Expand All @@ -31,9 +32,9 @@ def __init__(self, identifier : str, instructions: List[CFGInstruction], type_bl
self.function_calls = set()
self.sto_dep = []
self.mem_dep = []
self._cond_value = None
self.output_var_idx = 0


def get_block_id(self) -> str:
return self.block_id

Expand Down Expand Up @@ -73,14 +74,17 @@ def add_instruction(self, new_instr: CFGInstruction) -> None:
# TODO
#self.source_stack = utils.compute_stack_size(map(lambda x: x.disasm, self.instructions_to_optimize_bytecode()))

def add_comes_from(self, block_id: str):
def add_comes_from(self, block_id: str) -> None:
self._comes_from.append(block_id)

def get_comes_from(self) -> List[str]:
return self._comes_from

def set_comes_from(self, new_comes_from: List[str]) -> None:
self._comes_from = new_comes_from

def set_jump_type(self, t : str) -> None:
if t not in ["conditional","unconditional","terminal", "falls_to"]:
if t not in ["conditional","unconditional","terminal", "falls_to", "sub_block"]:
raise Exception("Wrong jump type")
else:
self._jump_type = t
Expand All @@ -94,14 +98,17 @@ def set_falls_to(self, blockId :str) -> None:
def set_length(self) -> int:
return len(self._instructions)

def set_jump_info(self, type_block: str, exit_info: List[str]) -> None:
def set_jump_info(self, exit_info: Dict[str, Any]) -> None:
type_block = exit_info["type"]
targets = exit_info["targets"]
if type_block in ["ConditionalJump"]:
self._jump_type = "conditional"
self._falls_to = exit_info[0]
self._jump_to = exit_info[1]
self._cond_value = exit_info["cond"]
self._falls_to = targets[0]
self._jump_to = targets[1]
elif type_block in ["Jump"]:
self._jump_type = "unconditional"
self._jump_to = exit_info[0]
self._jump_to = targets[0]
elif type_block in ["Terminated"]:
#We do not store the direction as itgenerates a loop
self._jump_type = "terminal"
Expand Down Expand Up @@ -181,7 +188,7 @@ def _compute_memory_dependences(self, instructions):
elif ins.get_op_name() in mem_instrs_offset:
values = ins.get_in_args()

interval_args = utils.get_interval(ins.get_op_name(),values)
interval_args = get_interval(ins.get_op_name(),values)

if ins.get_op_name() not in ["call","callcode","delegatecall","staticcall"]:
input_vals = list(map(lambda x: get_expression(x, instructions[:i]), interval_args))
Expand Down
Loading
Loading