-
Notifications
You must be signed in to change notification settings - Fork 6
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
[WIP] Added region aware testing #50
Changes from 9 commits
63e1194
1d9689a
dae773c
0ea0e6c
4213e58
1f5d955
ac2802e
7a3b4c2
e1097ad
fcdc9b9
8cd3009
462c716
e4dd79c
42434fd
851c03a
d0a0fa6
7b71b2c
cadd031
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
import dis | ||
import yaml | ||
from textwrap import dedent | ||
from typing import Set, Tuple, Dict, List, Iterator | ||
from dataclasses import dataclass, field | ||
from collections import deque | ||
|
@@ -15,9 +14,22 @@ | |
SyntheticTail, | ||
SyntheticReturn, | ||
SyntheticFill, | ||
PythonBytecodeBlock, | ||
RegionBlock, | ||
block_type_names, | ||
) | ||
from numba_rvsdg.core.datastructures.block_names import ( | ||
block_types, | ||
PYTHON_BYTECODE, | ||
SYNTH_HEAD, | ||
SYNTH_BRANCH, | ||
SYNTH_TAIL, | ||
SYNTH_EXIT, | ||
SYNTH_ASSIGN, | ||
SYNTH_RETURN, | ||
SYNTH_EXIT_LATCH, | ||
SYNTH_EXIT_BRANCH, | ||
) | ||
from numba_rvsdg.core.datastructures import block_names | ||
|
||
|
||
@dataclass(frozen=True) | ||
|
@@ -604,9 +616,7 @@ def insert_block_and_control_blocks( | |
# predecessors to a successor and insert it between the predecessor | ||
# and the newly created block | ||
for s in set(jt).intersection(successors): | ||
synth_assign = self.name_gen.new_block_name( | ||
block_names.SYNTH_ASSIGN | ||
) | ||
synth_assign = self.name_gen.new_block_name(SYNTH_ASSIGN) | ||
variable_assignment = {} | ||
variable_assignment[branch_variable] = branch_variable_value | ||
synth_assign_block = SyntheticAssignment( | ||
|
@@ -652,9 +662,7 @@ def join_returns(self): | |
] | ||
# close if more than one is found | ||
if len(return_nodes) > 1: | ||
return_solo_name = self.name_gen.new_block_name( | ||
block_names.SYNTH_RETURN | ||
) | ||
return_solo_name = self.name_gen.new_block_name(SYNTH_RETURN) | ||
self.insert_SyntheticReturn( | ||
return_solo_name, return_nodes, tuple() | ||
) | ||
|
@@ -685,29 +693,21 @@ def join_tails_and_exits(self, tails: Set[str], exits: Set[str]): | |
if len(tails) == 1 and len(exits) == 2: | ||
# join only exits | ||
solo_tail_name = next(iter(tails)) | ||
solo_exit_name = self.name_gen.new_block_name( | ||
block_names.SYNTH_EXIT | ||
) | ||
solo_exit_name = self.name_gen.new_block_name(SYNTH_EXIT) | ||
self.insert_SyntheticExit(solo_exit_name, tails, exits) | ||
return solo_tail_name, solo_exit_name | ||
|
||
if len(tails) >= 2 and len(exits) == 1: | ||
# join only tails | ||
solo_tail_name = self.name_gen.new_block_name( | ||
block_names.SYNTH_TAIL | ||
) | ||
solo_tail_name = self.name_gen.new_block_name(SYNTH_TAIL) | ||
solo_exit_name = next(iter(exits)) | ||
self.insert_SyntheticTail(solo_tail_name, tails, exits) | ||
return solo_tail_name, solo_exit_name | ||
|
||
if len(tails) >= 2 and len(exits) >= 2: | ||
# join both tails and exits | ||
solo_tail_name = self.name_gen.new_block_name( | ||
block_names.SYNTH_TAIL | ||
) | ||
solo_exit_name = self.name_gen.new_block_name( | ||
block_names.SYNTH_EXIT | ||
) | ||
solo_tail_name = self.name_gen.new_block_name(SYNTH_TAIL) | ||
solo_exit_name = self.name_gen.new_block_name(SYNTH_EXIT) | ||
self.insert_SyntheticTail(solo_tail_name, tails, exits) | ||
self.insert_SyntheticExit(solo_exit_name, {solo_tail_name}, exits) | ||
return solo_tail_name, solo_exit_name | ||
|
@@ -731,7 +731,7 @@ def bcmap_from_bytecode(bc: dis.Bytecode): | |
return {inst.offset: inst for inst in bc} | ||
|
||
@staticmethod | ||
def from_yaml(yaml_string): | ||
def from_yaml(yaml_string: str): | ||
"""Static method that creates an SCFG object from a YAML | ||
representation. | ||
|
||
|
@@ -781,23 +781,110 @@ def from_dict(graph_dict: dict): | |
Dictionary of block names in YAML string corresponding to their | ||
representation/unique name IDs in the SCFG. | ||
""" | ||
scfg_graph = {} | ||
name_gen = NameGenerator() | ||
block_dict = {} | ||
for index in graph_dict.keys(): | ||
block_dict[index] = name_gen.new_block_name(block_names.BASIC) | ||
for index, attributes in graph_dict.items(): | ||
jump_targets = attributes["jt"] | ||
backedges = attributes.get("be", ()) | ||
name = block_dict[index] | ||
block = BasicBlock( | ||
name=name, | ||
backedges=tuple(block_dict[idx] for idx in backedges), | ||
_jump_targets=tuple(block_dict[idx] for idx in jump_targets), | ||
) | ||
scfg_graph[name] = block | ||
scfg = SCFG(scfg_graph, name_gen=name_gen) | ||
return scfg, block_dict | ||
block_ref_dict = {} | ||
|
||
blocks = graph_dict["blocks"] | ||
edges = graph_dict["edges"] | ||
backedges = graph_dict["backedges"] | ||
if backedges is None: | ||
backedges = {} | ||
|
||
for key, block in blocks.items(): | ||
assert block["type"] in block_types | ||
block_ref_dict[key] = key | ||
|
||
# Find head of the graph, i.e. node which isn't in anyones contains | ||
# and no edges point towards it (backedges are allowed) | ||
heads = set(blocks.keys()) | ||
for block_name, block_data in blocks.items(): | ||
if block_data.get("contains"): | ||
heads.difference_update(block_data["contains"]) | ||
jump_targets = set(edges[block_name]) | ||
if backedges.get(block_name): | ||
jump_targets.difference_update(set(backedges[block_name])) | ||
heads.difference_update(jump_targets) | ||
assert len(heads) > 0 | ||
|
||
seen = set() | ||
|
||
def make_scfg(curr_heads: set, exiting: str = None): | ||
scfg_graph = {} | ||
queue = curr_heads | ||
while queue: | ||
current_name = queue.pop() | ||
if current_name in seen: | ||
continue | ||
seen.add(current_name) | ||
|
||
block_info = blocks[current_name] | ||
block_edges = tuple( | ||
block_ref_dict[idx] for idx in edges[current_name] | ||
) | ||
if backedges and backedges.get(current_name): | ||
block_backedges = tuple( | ||
block_ref_dict[idx] for idx in backedges[current_name] | ||
) | ||
else: | ||
block_backedges = () | ||
block_type = block_info.get("type") | ||
block_class = block_type_names[block_type] | ||
if block_type == "region": | ||
scfg = make_scfg( | ||
{block_info["header"]}, block_info["exiting"] | ||
) | ||
block = RegionBlock( | ||
name=current_name, | ||
_jump_targets=block_edges, | ||
backedges=block_backedges, | ||
kind=block_info["kind"], | ||
header=block_info["header"], | ||
exiting=block_info["exiting"], | ||
subregion=scfg, | ||
) | ||
elif block_type in [ | ||
SYNTH_BRANCH, | ||
SYNTH_HEAD, | ||
SYNTH_EXIT_LATCH, | ||
SYNTH_EXIT_BRANCH, | ||
]: | ||
block = block_class( | ||
name=current_name, | ||
backedges=block_backedges, | ||
_jump_targets=block_edges, | ||
branch_value_table=block_info["branch_value_table"], | ||
variable=block_info["variable"], | ||
) | ||
elif block_type in [SYNTH_ASSIGN]: | ||
block = SyntheticAssignment( | ||
name=current_name, | ||
_jump_targets=block_edges, | ||
backedges=block_backedges, | ||
variable_assignment=block_info["variable_assignment"], | ||
) | ||
elif block_type in [PYTHON_BYTECODE]: | ||
block = PythonBytecodeBlock( | ||
name=current_name, | ||
_jump_targets=block_edges, | ||
backedges=block_backedges, | ||
begin=block_info["begin"], | ||
end=block_info["end"], | ||
) | ||
else: | ||
block = block_class( | ||
name=current_name, | ||
backedges=block_backedges, | ||
_jump_targets=block_edges, | ||
) | ||
scfg_graph[current_name] = block | ||
if current_name != exiting: | ||
queue.update(edges[current_name]) | ||
|
||
scfg = SCFG(scfg_graph, name_gen=name_gen) | ||
return scfg | ||
|
||
scfg = make_scfg(heads) | ||
return scfg, block_ref_dict | ||
|
||
def to_yaml(self): | ||
"""Converts the SCFG object to a YAML string representation. | ||
|
@@ -813,23 +900,33 @@ def to_yaml(self): | |
A YAML string representing the SCFG. | ||
""" | ||
# Convert to yaml | ||
scfg_graph = self.graph | ||
yaml_string = """""" | ||
|
||
for key, value in scfg_graph.items(): | ||
jump_targets = [i for i in value._jump_targets] | ||
jump_targets = str(jump_targets).replace("'", '"') | ||
back_edges = [i for i in value.backedges] | ||
jump_target_str = f""" | ||
"{key}": | ||
jt: {jump_targets}""" | ||
|
||
if back_edges: | ||
back_edges = str(back_edges).replace("'", '"') | ||
jump_target_str += f""" | ||
be: {back_edges}""" | ||
yaml_string += dedent(jump_target_str) | ||
|
||
graph_dict = self.to_dict() | ||
|
||
blocks = graph_dict["blocks"] | ||
edges = graph_dict["edges"] | ||
backedges = graph_dict["backedges"] | ||
|
||
yaml_string += "\nblocks:" | ||
for _block in sorted(blocks.keys()): | ||
yaml_string += f""" | ||
'{_block}':""" | ||
block_dict = blocks[_block] | ||
for key, value in block_dict.items(): | ||
yaml_string += f""" | ||
{key}: {value}""" | ||
|
||
yaml_string += "\nedges:" | ||
for _block in sorted(blocks.keys()): | ||
yaml_string += f""" | ||
'{_block}': {edges[_block]}""" | ||
|
||
yaml_string += "\nbackedges:" | ||
for _block in sorted(blocks.keys()): | ||
if backedges[_block]: | ||
yaml_string += f""" | ||
'{_block}': {backedges[_block]}""" | ||
return yaml_string | ||
|
||
def to_dict(self): | ||
|
@@ -845,14 +942,46 @@ def to_dict(self): | |
graph_dict: Dict[Dict[...]] | ||
A dictionary representing the SCFG. | ||
""" | ||
scfg_graph = self.graph | ||
graph_dict = {} | ||
for key, value in scfg_graph.items(): | ||
curr_dict = {} | ||
curr_dict["jt"] = [i for i in value._jump_targets] | ||
if value.backedges: | ||
curr_dict["be"] = [i for i in value.backedges] | ||
graph_dict[key] = curr_dict | ||
blocks = {} | ||
edges = {} | ||
backedges = {} | ||
kc611 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def reverse_lookup(value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this needs a type signature There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the correct signature for this would be |
||
for k, v in block_type_names.items(): | ||
if v == value: | ||
return k | ||
else: | ||
raise TypeError("Block type not found.") | ||
|
||
for key, value in self: | ||
kc611 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
block_type = reverse_lookup(type(value)) | ||
blocks[key] = {"type": block_type} | ||
if block_type == "region": | ||
blocks[key]["kind"] = value.kind | ||
blocks[key]["contains"] = sorted( | ||
[idx.name for idx in value.subregion.graph.values()] | ||
) | ||
blocks[key]["header"] = value.header | ||
blocks[key]["exiting"] = value.exiting | ||
blocks[key]["parent_region"] = value.parent_region.name | ||
elif block_type in [ | ||
SYNTH_BRANCH, | ||
SYNTH_HEAD, | ||
SYNTH_EXIT_LATCH, | ||
SYNTH_EXIT_BRANCH, | ||
]: | ||
blocks[key]["branch_value_table"] = value.branch_value_table | ||
blocks[key]["variable"] = value.variable | ||
elif block_type in [SYNTH_ASSIGN]: | ||
blocks[key]["variable_assignment"] = value.variable_assignment | ||
elif block_type in [PYTHON_BYTECODE]: | ||
blocks[key]["begin"] = value.begin | ||
blocks[key]["end"] = value.end | ||
edges[key] = sorted([i for i in value._jump_targets]) | ||
backedges[key] = sorted([i for i in value.backedges]) | ||
|
||
graph_dict = {"blocks": blocks, "edges": edges, "backedges": backedges} | ||
|
||
return graph_dict | ||
|
||
def view(self, name: str = None): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should not be a closure, unless it really needs to be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I managed to write this such that it isn't a closure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I investigated this a bit more and I think this function will need some significant changes so as to make it more readable and that it becomes clearer what goes on. I think the
make_scfg
shouldn't be a closure and I also think that some stuff can be decomposed into smaller functions too.