-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:costa-group/grey
- Loading branch information
Showing
6 changed files
with
257 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
from typing import Optional | ||
from parser.cfg_block_actions.actions_interface import BlockAction | ||
from parser.cfg_block_actions.split_block import SplitBlock | ||
from parser.cfg_block_list import CFGBlockList | ||
from parser.cfg_block import CFGBlock | ||
from parser.cfg_function import CFGFunction | ||
from parser.cfg_object import CFGObject | ||
from parser.cfg_block_actions.utils import modify_comes_from, modify_successors | ||
|
||
|
||
def new_node_name(current_node: str) -> str: | ||
""" | ||
Given a node, generates a new name for the resulting split | ||
""" | ||
split_name = current_node.split("_") | ||
if len(split_name) > 1: | ||
split_name[1] = str(int(split_name[1]) + 1) | ||
return '_'.join(split_name) | ||
else: | ||
return current_node + "_1" | ||
|
||
|
||
class InlineFunction(BlockAction): | ||
""" | ||
Action for performing inlining of a different block list into an existing one. | ||
""" | ||
|
||
def __init__(self, instr_position: int, cfg_block: CFGBlock, cfg_blocklist: CFGBlockList, | ||
function_name: str, cfg_object: CFGObject): | ||
""" | ||
It receives the position in which we want to split, the corresponding block in which we are appending | ||
the corresponding block list, its block list, the function name and the block list | ||
associated to this function name | ||
""" | ||
self._instr_position: int = instr_position | ||
self._cfg_block: CFGBlock = cfg_block | ||
self._cfg_blocklist: CFGBlockList = cfg_blocklist | ||
self._function_name: str = function_name | ||
self._cfg_function: CFGFunction = cfg_object.functions[function_name] | ||
self._function_blocklist: CFGBlockList = self._cfg_function.blocks | ||
self._cfg_object: CFGObject = cfg_object | ||
self._first_sub_block: Optional[CFGObject] = None | ||
self._second_sub_block: Optional[CFGObject] = None | ||
|
||
def perform_action(self): | ||
# First we need to split the block in the function call, which is given by the instr position. | ||
# As a final check, we ensure the instruction in that position corresponds to the function name passed as | ||
# an argument | ||
assert self._cfg_block.get_instructions()[self._instr_position].get_op_name() == self._function_name, \ | ||
f"Expected function call {self._function_name} in position {self._instr_position}" | ||
|
||
# Include the blocks from the function into the CFG block list | ||
for block in self._function_blocklist.blocks.values(): | ||
self._cfg_blocklist.add_block(block) | ||
|
||
function_start_id = self._cfg_function.entry | ||
function_exists_ids = self._cfg_function.exits | ||
|
||
# Even after splitting the blocks, we have to remove the first instruction | ||
# from the first block (the function call) | ||
split_action = SplitBlock(self._instr_position, self._cfg_block, self._cfg_blocklist) | ||
split_action.perform_action() | ||
|
||
first_sub_block = split_action.first_half | ||
# We have to remove the function call | ||
first_sub_block.remove_instruction(-1) | ||
second_sub_block = split_action.second_half | ||
|
||
self._first_sub_block = first_sub_block | ||
self._second_sub_block = second_sub_block | ||
|
||
# For now, we only connect the blocks without considering if some of them could be empty | ||
# We need to modify both the comes from and the falls to/jumps to fields of the start and exits field and the | ||
# halves | ||
modify_successors(first_sub_block.block_id, second_sub_block.block_id, function_start_id, self._cfg_blocklist) | ||
modify_comes_from(function_start_id, None, first_sub_block.block_id, self._cfg_blocklist) | ||
|
||
# We set the "comes_from" from the second block to empty. This way, we can ensure only the terminal blocks | ||
# appear in this list | ||
self._cfg_blocklist.blocks[second_sub_block.block_id].set_comes_from([]) | ||
for exit_id in function_exists_ids: | ||
# Now the exit id must jump to the second sub_block | ||
modify_successors(exit_id, None, second_sub_block.block_id, self._cfg_blocklist) | ||
self._cfg_blocklist.blocks[second_sub_block.block_id].add_comes_from(exit_id) | ||
|
||
# Last step consists of removing the blocklist, the function and remove the function | ||
# from the corresponding object | ||
self._function_blocklist.blocks.clear() | ||
del self._function_blocklist | ||
del self._cfg_function | ||
self._cfg_object.functions.pop(self._function_name) | ||
|
||
@property | ||
def first_sub_block(self) -> Optional[CFGBlock]: | ||
""" | ||
First sub block after splitting the function call. Only contains a None value if the | ||
action has not been performed yet | ||
""" | ||
return self._first_sub_block | ||
|
||
@property | ||
def second_sub_block(self) -> Optional[CFGBlock]: | ||
""" | ||
Second sub block after splitting the function call. Only contains a None value if the | ||
actions has not been performed yet | ||
""" | ||
return self._second_sub_block | ||
|
||
def __str__(self): | ||
return f"Inlining function {self._function_name}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
from hypothesis import given, strategies as st | ||
from parser.cfg_instruction import CFGInstruction | ||
from parser.cfg_block import CFGBlock | ||
from parser.cfg_block_list import CFGBlockList | ||
from parser.cfg_function import CFGFunction | ||
from parser.cfg_object import CFGObject | ||
from parser.cfg_block_actions.inline_function import InlineFunction | ||
from utils import cfg_instruction_list | ||
|
||
|
||
class TestInlineFunction: | ||
|
||
@given(cfg_instruction_list(5, 36)) | ||
def test_inline_function_one_function(self, instructions): | ||
split_list_index = len(instructions) // 12 | ||
cfg_block_0 = CFGBlock("block_0", instructions[:split_list_index], "unconditional", dict()) | ||
cfg_block_1 = CFGBlock("block_1", instructions[split_list_index:2*split_list_index], "unconditional", dict()) | ||
cfg_block_2 = CFGBlock("block_2", instructions[2*split_list_index:3*split_list_index], "unconditional", dict()) | ||
cfg_block_3 = CFGBlock("block_3", instructions[3*split_list_index:4*split_list_index], "conditional", dict()) | ||
cfg_block_4 = CFGBlock("block_4", instructions[4*split_list_index:5*split_list_index], "terminal", dict()) | ||
cfg_block_5 = CFGBlock("block_5", instructions[5*split_list_index:6*split_list_index], "terminal", dict()) | ||
cfg_block_6 = CFGBlock("block_6", instructions[6*split_list_index:7*split_list_index], "conditional", dict()) | ||
|
||
function_cfg_block_list = CFGBlockList() | ||
function_cfg_block_list.add_block(cfg_block_0) | ||
function_cfg_block_list.add_block(cfg_block_1) | ||
function_cfg_block_list.add_block(cfg_block_2) | ||
function_cfg_block_list.add_block(cfg_block_3) | ||
function_cfg_block_list.add_block(cfg_block_4) | ||
function_cfg_block_list.add_block(cfg_block_5) | ||
function_cfg_block_list.add_block(cfg_block_6) | ||
|
||
cfg_function = CFGFunction("function", [], [], "block_0", function_cfg_block_list) | ||
cfg_function.add_exit_point(cfg_block_4.block_id) | ||
cfg_function.add_exit_point(cfg_block_5.block_id) | ||
|
||
# Function CFG structure: | ||
# 6 | ||
# 0 1 | ||
# 2 | ||
# 3 | ||
# 4 5 | ||
edges = [(cfg_block_6.block_id, cfg_block_0.block_id, "falls_to"), | ||
(cfg_block_6.block_id, cfg_block_1.block_id, "jumps_to"), | ||
(cfg_block_0.block_id, cfg_block_2.block_id, "falls_to"), | ||
(cfg_block_1.block_id, cfg_block_2.block_id, "jumps_to"), | ||
(cfg_block_2.block_id, cfg_block_3.block_id, "jumps_to"), | ||
(cfg_block_3.block_id, cfg_block_4.block_id, "falls_to"), | ||
(cfg_block_3.block_id, cfg_block_5.block_id, "jumps_to")] | ||
|
||
for u, v, jump_type in edges: | ||
function_cfg_block_list.blocks[v].add_comes_from(u) | ||
if jump_type == "jumps_to": | ||
function_cfg_block_list.blocks[u].set_jump_to(v) | ||
else: | ||
function_cfg_block_list.blocks[u].set_falls_to(v) | ||
|
||
|
||
# Creation of the main block list | ||
instruction_call = CFGInstruction("function", [], []) | ||
cfg_block_7 = CFGBlock("block_7", instructions[7*split_list_index:8*split_list_index], "conditional", dict()) | ||
cfg_block_8 = CFGBlock("block_8", instructions[8*split_list_index:9*split_list_index], "conditional", dict()) | ||
cfg_block_9 = CFGBlock("block_9", [instruction_call] + instructions[9*split_list_index:10*split_list_index], "conditional", dict()) | ||
cfg_block_10 = CFGBlock("block_10", instructions[10*split_list_index:11*split_list_index], "terminal", dict()) | ||
cfg_block_11 = CFGBlock("block_11", instructions[11*split_list_index:], "terminal", dict()) | ||
|
||
# We add a call to function in cfg_block_9 | ||
|
||
cfg_object_block_list = CFGBlockList() | ||
cfg_object_block_list.add_block(cfg_block_7) | ||
cfg_object_block_list.add_block(cfg_block_8) | ||
cfg_object_block_list.add_block(cfg_block_9) | ||
cfg_object_block_list.add_block(cfg_block_10) | ||
cfg_object_block_list.add_block(cfg_block_11) | ||
|
||
# Main CFG structure: | ||
# 7 8 | ||
# 9 | ||
# 10 11 | ||
|
||
edges = [(cfg_block_7.block_id, cfg_block_9.block_id, "jumps_to"), | ||
(cfg_block_8.block_id, cfg_block_9.block_id, "jumps_to"), | ||
(cfg_block_9.block_id, cfg_block_10.block_id, "falls_to"), | ||
(cfg_block_9.block_id, cfg_block_11.block_id, "jumps_to")] | ||
|
||
for u, v, jump_type in edges: | ||
cfg_object_block_list.blocks[v].add_comes_from(u) | ||
if jump_type == "jumps_to": | ||
cfg_object_block_list.blocks[u].set_jump_to(v) | ||
else: | ||
cfg_object_block_list.blocks[u].set_falls_to(v) | ||
|
||
# Creation of the CFG object | ||
cfg_object = CFGObject("object", cfg_object_block_list) | ||
cfg_object.add_function(cfg_function) | ||
|
||
inline_object = InlineFunction(0, cfg_block_9, cfg_object_block_list, "function", cfg_object) | ||
inline_object.perform_action() | ||
assert len(function_cfg_block_list.blocks) == 0, "There must be no sub block in the function" | ||
|
||
# 13 blocks: initial 12 blocks + block 9 split | ||
assert len(cfg_object_block_list.blocks) == 13, "There must be 13 sub blocks in the function" | ||
|
||
|
||
first_split_sub_block = inline_object.first_sub_block | ||
second_split_sub_block = inline_object.second_sub_block | ||
# joined_block_id = merged_block_id("block_2", "block_3") | ||
# assert joined_block_id in function_cfg_block_list.blocks, f"Block {joined_block_id} must appear in the block list" | ||
# assert joined_block_id == cfg_block_0.get_falls_to(), "Joined block must be the falls to from block 0" | ||
# assert joined_block_id == cfg_block_1.get_jump_to(), "Joined block must be the jumps to from block 1" | ||
# assert joined_block_id in cfg_block_4.get_comes_from(), "Joined block must appear in the comes from block 4" | ||
# assert joined_block_id in cfg_block_5.get_comes_from(), "Joined block must appear in the comes from block 5" |