diff --git a/src/parser/cfg_block.py b/src/parser/cfg_block.py index 8892c241..a081f2c4 100644 --- a/src/parser/cfg_block.py +++ b/src/parser/cfg_block.py @@ -30,6 +30,9 @@ def get_block_id(self) -> str: def get_instructions(self) -> List[CFGInstruction]: return self._instructions + def get_instructions_to_compute(self) -> List[CFGInstruction]: + return [instruction for instruction in self._instructions if instruction.must_be_computed()] + def get_source_stack(self) -> int: return self.source_stack diff --git a/src/parser/cfg_instruction.py b/src/parser/cfg_instruction.py index c31fb3d9..9a6b907e 100644 --- a/src/parser/cfg_instruction.py +++ b/src/parser/cfg_instruction.py @@ -97,11 +97,20 @@ def build_custom_function_spec(function_name: str, input_args: List[str], output class CFGInstruction: - def __init__(self, op : str, in_args: List[str], out_args: List[str]): + def __init__(self, op: str, in_args: List[str], out_args: List[str]): self.op = op self.in_args = in_args[::-1] self.out_args = out_args[::-1] self.builtin_args = None + self.assignments = None + + def must_be_computed(self): + """ + Check whether an instruction must be computed (i.e. added as part of the opertions fed into the greedy + algorithm) or not. Instructions that must not be computed include assignments (as they are propagated directly) + and functionReturns + """ + return self.op != "assignments" def set_builtin_args(self, builtin: List[str]) -> None: self.builtin_args = builtin @@ -112,6 +121,9 @@ def get_as_json(self): if self.builtin_args is not None: instruction["builtinArgs"] = self.builtin_args + if self.assignments is not None: + instruction["assignment"] = self.assignments + return instruction def build_spec(self, out_idx, instrs_idx, map_instructions, assignments: Dict[str, str]): diff --git a/src/parser/parser.py b/src/parser/parser.py index f19a4f9f..d94d2081 100644 --- a/src/parser/parser.py +++ b/src/parser/parser.py @@ -7,7 +7,7 @@ from parser.cfg_function import CFGFunction from parser.cfg_block import CFGBlock from parser.cfg_instruction import CFGInstruction -from parser.utils_parser import check_instruction_validity, check_block_validity +from parser.utils_parser import check_instruction_validity, check_block_validity, check_assignment_validity block_id_T = str @@ -28,10 +28,26 @@ def parse_instruction(ins_json: Dict[str,Any]) -> CFGInstruction: return instruction -def parse_assignment(assignment: Dict[str, Any], assignment_dict: Dict[str, str]) -> None: - for in_var, out_var in zip(assignment["in"], assignment["out"]): +def parse_assignment(assignment: Dict[str, Any], assignment_dict: Dict[str, str]) -> CFGInstruction: + """ + Assignments are handled differently than other instructions, as they have no op field and (theoretically) + multiple assignments can be made at the same time. An instruction is generated per assignment + """ + in_args = assignment.get("in", -1) + assignment_args = assignment.get("assignment", -1) + out_args = assignment.get("out", -1) + + check_assignment_validity(in_args, assignment_args, out_args) + + # Assignments are combined into a single instruction, maintaining the original format + instruction = CFGInstruction("assignments", in_args, out_args) + + # Extend the assignment dict with the corresponding information + for in_var, out_var in zip(in_args, out_args): assignment_dict[out_var] = in_var + return instruction + def parse_block(block_json: Dict[str,Any]) -> Tuple[block_id_T, CFGBlock, block_id_T]: block_id = block_json.get("id", -1) @@ -43,11 +59,11 @@ def parse_block(block_json: Dict[str,Any]) -> Tuple[block_id_T, CFGBlock, block_ list_cfg_instructions = [] assignment_dict = dict() - for instructions in block_instructions: - if "assignment" in instructions: - parse_assignment(instructions, assignment_dict) + for instruction in block_instructions: + if "assignment" in instruction: + list_cfg_instructions.append(parse_assignment(instruction, assignment_dict)) else: - cfg_instruction = parse_instruction(instructions) if block_type != "FunctionReturn" else [] + cfg_instruction = parse_instruction(instruction) if block_type != "FunctionReturn" else [] list_cfg_instructions.append(cfg_instruction) block = CFGBlock(block_id, list_cfg_instructions, block_type, assignment_dict) diff --git a/src/parser/utils_parser.py b/src/parser/utils_parser.py index e4161c81..2ca8f5f2 100644 --- a/src/parser/utils_parser.py +++ b/src/parser/utils_parser.py @@ -1,4 +1,4 @@ - +from typing import List def check_block_validity(block_id, block_instructions, block_exit, block_type): if block_id == -1: @@ -25,6 +25,15 @@ def check_instruction_validity(in_args, op, out_args): if out_args == -1: raise Exception("[ERROR]: instruction does not contain out argument") + +def check_assignment_validity(in_args: List[str], assignments: List[str], out_args: List[str]): + """ + Check that there is the same number of elements in fields in_args, assignments and out_args + """ + if len(in_args) != len(assignments) or len(assignments) != len(out_args): + raise Exception("[ERROR]: Assignment must contain the same number of elements for all its fields") + + def process_opcode(result): op_val = hex(int(result))[2:]