From f6bbad9f866174a3d2224bc54e9baac606b76fae Mon Sep 17 00:00:00 2001 From: kc611 Date: Wed, 21 Jun 2023 10:43:19 +0530 Subject: [PATCH 1/8] Added pre-commit configuration --- .pre-commit-config.yaml | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2c7f014 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +exclude: | + (?x)^( + docs/.* + )$ +repos: + # Checks for debug statements and merge conflicts + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: debug-statements + - id: check-merge-conflict + # Pyupgrade: upgrades older python syntax to newer one + - repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade + args: ["--py38-plus"] + # Black + - repo: https://github.com/psf/black + rev: 23.1.0 + hooks: + - id: black + language_version: python3 + # Autoflake + - repo: https://github.com/humitos/mirrors-autoflake.git + rev: v1.1 + hooks: + - id: autoflake + args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variable'] + # Manual Linting: Flake 8 + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + # Static Type checking: MyPy + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.1.1 + hooks: + - id: mypy + additional_dependencies: + - types-filelock + - types-setuptools From 5e6ee37345b39f517aeb5389b1abb040eeaecb98 Mon Sep 17 00:00:00 2001 From: kc611 Date: Wed, 21 Jun 2023 10:44:15 +0530 Subject: [PATCH 2/8] Pre-commit formatting: Pyupgrade changes --- .../core/datastructures/block_names.py | 1 - numba_rvsdg/core/datastructures/flow_info.py | 6 ++-- numba_rvsdg/core/datastructures/scfg.py | 5 ++-- numba_rvsdg/core/transformations.py | 12 ++++---- numba_rvsdg/rendering/rendering.py | 30 +++++++++---------- numba_rvsdg/tests/test_scfg.py | 1 - numba_rvsdg/tests/test_transforms.py | 1 - 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/numba_rvsdg/core/datastructures/block_names.py b/numba_rvsdg/core/datastructures/block_names.py index 9855307..07ac40d 100644 --- a/numba_rvsdg/core/datastructures/block_names.py +++ b/numba_rvsdg/core/datastructures/block_names.py @@ -1,4 +1,3 @@ - BASIC = "basic" PYTHON_BYTECODE = "python_bytecode" diff --git a/numba_rvsdg/core/datastructures/flow_info.py b/numba_rvsdg/core/datastructures/flow_info.py index 8afb9ca..c8fa63c 100644 --- a/numba_rvsdg/core/datastructures/flow_info.py +++ b/numba_rvsdg/core/datastructures/flow_info.py @@ -115,9 +115,9 @@ def build_basicblocks(self: "FlowInfo", end_offset=None) -> "SCFG": scfg = SCFG() offsets = sorted(self.block_offsets) # enumerate names - names = dict( - (offset, scfg.name_gen.new_block_name(block_names.PYTHON_BYTECODE)) for offset in offsets - ) + names = { + offset: scfg.name_gen.new_block_name(block_names.PYTHON_BYTECODE) for offset in offsets + } if end_offset is None: end_offset = _next_inst_offset(self.last_offset) diff --git a/numba_rvsdg/core/datastructures/scfg.py b/numba_rvsdg/core/datastructures/scfg.py index 7965267..ab55a9a 100644 --- a/numba_rvsdg/core/datastructures/scfg.py +++ b/numba_rvsdg/core/datastructures/scfg.py @@ -222,8 +222,7 @@ def __iter__(self): yield (name, block) # if this is a region, recursively yield everything from that region if type(block) == RegionBlock: - for i in block.subregion: - yield i + yield from block.subregion # finally add any jump_targets to the list of names to visit to_visit.extend(block.jump_targets) @@ -661,7 +660,7 @@ def join_tails_and_exits(self, tails: Set[str], exits: Set[str]): 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) self.insert_SyntheticTail(solo_tail_name, tails, exits) - self.insert_SyntheticExit(solo_exit_name, set((solo_tail_name,)), exits) + self.insert_SyntheticExit(solo_exit_name, {solo_tail_name}, exits) return solo_tail_name, solo_exit_name @staticmethod diff --git a/numba_rvsdg/core/transformations.py b/numba_rvsdg/core/transformations.py index cfff6c1..3fc6e37 100644 --- a/numba_rvsdg/core/transformations.py +++ b/numba_rvsdg/core/transformations.py @@ -77,11 +77,11 @@ def loop_restructure_helper(scfg: SCFG, loop: Set[str]): backedge_variable = scfg.name_gen.new_var_name("backedge") # Now we setup the lookup tables for the various control variables, # depending on the state of the CFG and what is needed - exit_value_table = dict(((i, j) for i, j in enumerate(exit_blocks))) + exit_value_table = {i: j for i, j in enumerate(exit_blocks)} if needs_synth_exit: - backedge_value_table = dict((i, j) for i, j in enumerate((loop_head, synth_exit))) + backedge_value_table = {i: j for i, j in enumerate((loop_head, synth_exit))} else: - backedge_value_table = dict((i, j) for i, j in enumerate((loop_head, next(iter(exit_blocks))))) + backedge_value_table = {i: j for i, j in enumerate((loop_head, next(iter(exit_blocks))))} if headers_were_unified: header_value_table = scfg[solo_head_name].branch_value_table else: @@ -277,7 +277,7 @@ def _find_branch_regions(scfg: SCFG, begin: str, end: str) -> Set[str]: def find_tail_blocks( scfg: SCFG, begin: Set[str], head_region_blocks, branch_regions ): - tail_subregion = set((b for b in scfg.graph.keys())) + tail_subregion = {b for b in scfg.graph.keys()} tail_subregion.difference_update(head_region_blocks) for reg in branch_regions: if not reg: @@ -555,7 +555,7 @@ def _find_dominators_internal(entries, nodes, preds_table, succs_table): doms = {} for e in entries: - doms[e] = set([e]) + doms[e] = {e} todo = [] for n in nodes: @@ -567,7 +567,7 @@ def _find_dominators_internal(entries, nodes, preds_table, succs_table): n = todo.pop() if n in entries: continue - new_doms = set([n]) + new_doms = {n} preds = preds_table[n] if preds: new_doms |= functools.reduce(set.intersection, [doms[p] for p in preds]) diff --git a/numba_rvsdg/rendering/rendering.py b/numba_rvsdg/rendering/rendering.py index da71d11..77453e4 100644 --- a/numba_rvsdg/rendering/rendering.py +++ b/numba_rvsdg/rendering/rendering.py @@ -16,7 +16,7 @@ import dis from typing import Dict -class BaseRenderer(object): +class BaseRenderer: """Base Renderer class. This is the base class for all types of graph renderers. It defines two @@ -127,12 +127,12 @@ def render_region_block( def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): if name.startswith('python_bytecode'): instlist = block.get_instructions(self.bcmap) - body = name + "\l" - body += "\l".join( + body = name + r"\l" + body += r"\l".join( [f"{inst.offset:3}: {inst.opname}" for inst in instlist] + [""] ) else: - body = name + "\l" + body = name + r"\l" digraph.node(str(name), shape="rect", label=body) @@ -140,8 +140,8 @@ def render_control_variable_block( self, digraph: "Digraph", name: str, block: BasicBlock ): if isinstance(name, str): - body = name + "\l" - body += "\l".join( + body = name + r"\l" + body += r"\l".join( (f"{k} = {v}" for k, v in block.variable_assignment.items()) ) else: @@ -152,9 +152,9 @@ def render_branching_block( self, digraph: "Digraph", name: str, block: BasicBlock ): if isinstance(name, str): - body = name + "\l" - body += f"variable: {block.variable}\l" - body += "\l".join( + body = name + r"\l" + body += fr"variable: {block.variable}\l" + body += r"\l".join( (f"{k}=>{v}" for k, v in block.branch_value_table.items()) ) else: @@ -217,7 +217,7 @@ def render_region_block( self.render_block(subg, name, block) def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): - body = name + "\l"+ \ + body = name + r"\l"+ \ "\njump targets: " + str(block.jump_targets) + \ "\nback edges: " + str(block.backedges) @@ -227,8 +227,8 @@ def render_control_variable_block( self, digraph: "Digraph", name: str, block: BasicBlock ): if isinstance(name, str): - body = name + "\l" - body += "\l".join( + body = name + r"\l" + body += r"\l".join( (f"{k} = {v}" for k, v in block.variable_assignment.items()) ) body += \ @@ -243,9 +243,9 @@ def render_branching_block( self, digraph: "Digraph", name: str, block: BasicBlock ): if isinstance(name, str): - body = name + "\l" - body += f"variable: {block.variable}\l" - body += "\l".join( + body = name + r"\l" + body += fr"variable: {block.variable}\l" + body += r"\l".join( (f"{k}=>{v}" for k, v in block.branch_value_table.items()) ) body += \ diff --git a/numba_rvsdg/tests/test_scfg.py b/numba_rvsdg/tests/test_scfg.py index 6d920e9..ca1e2e3 100644 --- a/numba_rvsdg/tests/test_scfg.py +++ b/numba_rvsdg/tests/test_scfg.py @@ -1,4 +1,3 @@ - from unittest import main, TestCase from textwrap import dedent from numba_rvsdg.core.datastructures.scfg import SCFG, NameGenerator diff --git a/numba_rvsdg/tests/test_transforms.py b/numba_rvsdg/tests/test_transforms.py index d0fa921..4b713df 100644 --- a/numba_rvsdg/tests/test_transforms.py +++ b/numba_rvsdg/tests/test_transforms.py @@ -1,4 +1,3 @@ - from unittest import main from numba_rvsdg.core.datastructures.scfg import SCFG From e5c26232664de00f8b9497534dd939fc920c2d3c Mon Sep 17 00:00:00 2001 From: kc611 Date: Wed, 21 Jun 2023 10:44:51 +0530 Subject: [PATCH 3/8] Pre-commit formatting changes: Black changes --- .../core/datastructures/basic_block.py | 17 ++- numba_rvsdg/core/datastructures/byte_flow.py | 8 +- numba_rvsdg/core/datastructures/flow_info.py | 3 +- numba_rvsdg/core/datastructures/scfg.py | 129 ++++++++++-------- numba_rvsdg/core/transformations.py | 72 ++++++---- numba_rvsdg/rendering/rendering.py | 61 ++++++--- numba_rvsdg/tests/mock_asm.py | 5 +- numba_rvsdg/tests/simulator.py | 7 +- numba_rvsdg/tests/test_byteflow.py | 7 +- numba_rvsdg/tests/test_fig4.py | 1 + numba_rvsdg/tests/test_mock_asm.py | 13 +- numba_rvsdg/tests/test_scc.py | 3 + numba_rvsdg/tests/test_scfg.py | 114 +++++++--------- numba_rvsdg/tests/test_transforms.py | 90 ++++++++---- numba_rvsdg/tests/test_utils.py | 21 ++- 15 files changed, 335 insertions(+), 216 deletions(-) diff --git a/numba_rvsdg/core/datastructures/basic_block.py b/numba_rvsdg/core/datastructures/basic_block.py index a823277..6c6706b 100644 --- a/numba_rvsdg/core/datastructures/basic_block.py +++ b/numba_rvsdg/core/datastructures/basic_block.py @@ -202,36 +202,46 @@ class SyntheticBlock(BasicBlock): """The SyntheticBlock represents a artificially added block in a structured control flow graph (SCFG). """ + pass + @dataclass(frozen=True) class SyntheticExit(SyntheticBlock): """The SyntheticExit class represents a artificially added exit block in a structured control flow graph (SCFG). """ + pass + @dataclass(frozen=True) class SyntheticReturn(SyntheticBlock): """The SyntheticReturn class represents a artificially added return block in a structured control flow graph (SCFG). """ + pass + @dataclass(frozen=True) class SyntheticTail(SyntheticBlock): """The SyntheticTail class represents a artificially added tail block in a structured control flow graph (SCFG). """ + pass + @dataclass(frozen=True) class SyntheticFill(SyntheticBlock): """The SyntheticFill class represents a artificially added fill block in a structured control flow graph (SCFG). """ + pass + @dataclass(frozen=True) class SyntheticAssignment(SyntheticBlock): """The SyntheticAssignment class represents a artificially added assignment block @@ -247,6 +257,7 @@ class SyntheticAssignment(SyntheticBlock): the variable name to the value that is is assigned when the block is executed. """ + variable_assignment: dict = None @@ -264,6 +275,7 @@ class SyntheticBranch(SyntheticBlock): The value table maps variable values to the repective jump target to be executed on the basis of that value. """ + variable: str = None branch_value_table: dict = None @@ -320,6 +332,7 @@ class SyntheticHead(SyntheticBranch): """The SyntheticHead class represents a artificially added head block in a structured control flow graph (SCFG). """ + pass @@ -328,6 +341,7 @@ class SyntheticExitingLatch(SyntheticBranch): """The SyntheticExitingLatch class represents a artificially added exiting latch block in a structured control flow graph (SCFG). """ + pass @@ -336,6 +350,7 @@ class SyntheticExitBranch(SyntheticBranch): """The SyntheticExitBranch class represents a artificially added exit branch block in a structured control flow graph (SCFG). """ + pass @@ -384,5 +399,5 @@ def replace_exiting(self, new_exiting): ---------- new_exiting: str The new exiting block of the region represented by the RegionBlock. - """ + """ object.__setattr__(self, "exiting", new_exiting) diff --git a/numba_rvsdg/core/datastructures/byte_flow.py b/numba_rvsdg/core/datastructures/byte_flow.py index ecb4555..5030708 100644 --- a/numba_rvsdg/core/datastructures/byte_flow.py +++ b/numba_rvsdg/core/datastructures/byte_flow.py @@ -97,10 +97,10 @@ def _restructure_loop(self): def _restructure_branch(self): """Restructures the branches within the corresponding SCFG. - Creates a deep copy of the SCFG and performs the operation to - restructure branch constructs within the control flow. It applies - the restructuring operation to both the main SCFG and any - subregions within it. It returns a new ByteFlow object with + Creates a deep copy of the SCFG and performs the operation to + restructure branch constructs within the control flow. It applies + the restructuring operation to both the main SCFG and any + subregions within it. It returns a new ByteFlow object with the updated SCFG. Returns diff --git a/numba_rvsdg/core/datastructures/flow_info.py b/numba_rvsdg/core/datastructures/flow_info.py index c8fa63c..562bf86 100644 --- a/numba_rvsdg/core/datastructures/flow_info.py +++ b/numba_rvsdg/core/datastructures/flow_info.py @@ -116,7 +116,8 @@ def build_basicblocks(self: "FlowInfo", end_offset=None) -> "SCFG": offsets = sorted(self.block_offsets) # enumerate names names = { - offset: scfg.name_gen.new_block_name(block_names.PYTHON_BYTECODE) for offset in offsets + offset: scfg.name_gen.new_block_name(block_names.PYTHON_BYTECODE) + for offset in offsets } if end_offset is None: end_offset = _next_inst_offset(self.last_offset) diff --git a/numba_rvsdg/core/datastructures/scfg.py b/numba_rvsdg/core/datastructures/scfg.py index ab55a9a..d7c476e 100644 --- a/numba_rvsdg/core/datastructures/scfg.py +++ b/numba_rvsdg/core/datastructures/scfg.py @@ -19,11 +19,12 @@ ) from numba_rvsdg.core.datastructures import block_names + @dataclass(frozen=True) class NameGenerator: """Unique Name Generator. - The NameGenerator class is responsible for generating unique names + The NameGenerator class is responsible for generating unique names for blocks, regions, and variables within the SCFG. Attributes @@ -35,12 +36,12 @@ class NameGenerator: kinds: dict[str, int] = field(default_factory=dict) def new_block_name(self, kind: str) -> str: - """Generate a new unique name for a block of the specified kind. + """Generate a new unique name for a block of the specified kind. This method checks if the given string 'kind' already exists - in the kinds dictionary attribute. If it exists, the respective - index is incremented, if it doesn't then a new index (starting - from zero) is asigned to the given kind. This ensures that + in the kinds dictionary attribute. If it exists, the respective + index is incremented, if it doesn't then a new index (starting + from zero) is asigned to the given kind. This ensures that the given name is unique by a combination of it's kind and it's index. It returns the generated name. @@ -56,21 +57,21 @@ def new_block_name(self, kind: str) -> str: """ if kind in self.kinds.keys(): idx = self.kinds[kind] - name = str(kind) + '_block_' + str(idx) + name = str(kind) + "_block_" + str(idx) self.kinds[kind] = idx + 1 else: idx = 0 - name = str(kind) + '_block_' + str(idx) + name = str(kind) + "_block_" + str(idx) self.kinds[kind] = idx + 1 return name def new_region_name(self, kind: str) -> str: - """Generate a new unique name for a region of the specified kind. + """Generate a new unique name for a region of the specified kind. This method checks if the given string 'kind' already exists - in the kinds dictionary attribute. If it exists, the respective - index is incremented, if it doesn't then a new index (starting - from zero) is asigned to the given kind. This ensures that + in the kinds dictionary attribute. If it exists, the respective + index is incremented, if it doesn't then a new index (starting + from zero) is asigned to the given kind. This ensures that the given name is unique by a combination of it's kind and it's index. It returns the generated name. @@ -86,21 +87,21 @@ def new_region_name(self, kind: str) -> str: """ if kind in self.kinds.keys(): idx = self.kinds[kind] - name = str(kind) + '_region_' + str(idx) + name = str(kind) + "_region_" + str(idx) self.kinds[kind] = idx + 1 else: idx = 0 - name = str(kind) + '_region_' + str(idx) + name = str(kind) + "_region_" + str(idx) self.kinds[kind] = idx + 1 return name def new_var_name(self, kind: str) -> str: - """Generate a new unique name for a variable of the specified kind. + """Generate a new unique name for a variable of the specified kind. This method checks if the given string 'kind' already exists - in the kinds dictionary attribute. If it exists, the respective - index is incremented, if it doesn't then a new index (starting - from zero) is asigned to the given kind. This ensures that + in the kinds dictionary attribute. If it exists, the respective + index is incremented, if it doesn't then a new index (starting + from zero) is asigned to the given kind. This ensures that the given name is unique by a combination of it's kind and it's index. It returns the generated name. @@ -116,11 +117,11 @@ def new_var_name(self, kind: str) -> str: """ if kind in self.kinds.keys(): idx = self.kinds[kind] - name = str(kind) + '_var_' + str(idx) + name = str(kind) + "_var_" + str(idx) self.kinds[kind] = idx + 1 else: idx = 0 - name = str(kind) + '_var_' + str(idx) + name = str(kind) + "_var_" + str(idx) self.kinds[kind] = idx + 1 return name @@ -142,18 +143,21 @@ class SCFG: graph: Dict[str, BasicBlock] = field(default_factory=dict) - name_gen: NameGenerator = field( - default_factory=NameGenerator, compare=False - ) + name_gen: NameGenerator = field(default_factory=NameGenerator, compare=False) # This is the top-level region that this SCFG represents. region: RegionBlock = field(init=False, compare=False) def __post_init__(self): name = self.name_gen.new_region_name("meta") - new_region = RegionBlock(name=name, kind="meta", header=None, - exiting=None, parent_region=None, - subregion=self) + new_region = RegionBlock( + name=name, + kind="meta", + header=None, + exiting=None, + parent_region=None, + subregion=self, + ) object.__setattr__(self, "region", new_region) def __getitem__(self, index): @@ -191,7 +195,7 @@ def __iter__(self): """Returns an iterator over the blocks in the SCFG. Returns an iterator that yields the names and corresponding blocks - in the SCFG. It follows a breadth-first search + in the SCFG. It follows a breadth-first search traversal starting from the head block. Returns @@ -228,7 +232,7 @@ def __iter__(self): @property def concealed_region_view(self): - """A property that returns a ConcealedRegionView object, representing + """A property that returns a ConcealedRegionView object, representing a concealed view of the control flow graph. Returns @@ -241,8 +245,8 @@ def concealed_region_view(self): def exclude_blocks(self, exclude_blocks: Set[str]) -> Iterator[str]: """Returns an iterator over the blocks in the SCFG with exclusions. - Returns an iterator over all nodes (blocks) in the control flow graph - that are not present in the exclude_blocks set. It filters out the + Returns an iterator over all nodes (blocks) in the control flow graph + that are not present in the exclude_blocks set. It filters out the excluded blocks and yields the remaining blocks. Parameters @@ -309,9 +313,7 @@ def __iter__(self): return list(scc(GraphWrap(self.graph))) - def find_headers_and_entries( - self, subgraph: Set[str] - ) -> Tuple[Set[str], Set[str]]: + def find_headers_and_entries(self, subgraph: Set[str]) -> Tuple[Set[str], Set[str]]: """Finds entries and headers in a given subgraph. Entries are blocks outside the subgraph that have an edge pointing to @@ -336,7 +338,9 @@ def find_headers_and_entries( headers: Set[str] = set() for outside in self.exclude_blocks(subgraph): - nodes_jump_in_loop = subgraph.intersection(self.graph[outside]._jump_targets) + nodes_jump_in_loop = subgraph.intersection( + self.graph[outside]._jump_targets + ) headers.update(nodes_jump_in_loop) if nodes_jump_in_loop: entries.add(outside) @@ -349,12 +353,12 @@ def find_headers_and_entries( # to it's parent region block's graph. if self.region.kind != "meta": parent_region = self.region.parent_region - _, entries = parent_region.subregion.find_headers_and_entries({self.region.name}) + _, entries = parent_region.subregion.find_headers_and_entries( + {self.region.name} + ) return sorted(headers), sorted(entries) - def find_exiting_and_exits( - self, subgraph: Set[str] - ) -> Tuple[Set[str], Set[str]]: + def find_exiting_and_exits(self, subgraph: Set[str]) -> Tuple[Set[str], Set[str]]: """Finds exiting and exit blocks in a given subgraph. Existing blocks are blocks inside the subgraph that have edges to @@ -448,8 +452,11 @@ def remove_blocks(self, names: Set[str]): del self.graph[name] def insert_block( - self, new_name: str, predecessors: Set[str], successors: Set[str], - block_type: SyntheticBlock + self, + new_name: str, + predecessors: Set[str], + successors: Set[str], + block_type: SyntheticBlock, ): """Inserts a new synthetic block into the SCFG between the given successors and predecessors. @@ -477,9 +484,7 @@ def insert_block( """ # TODO: needs a diagram and documentaion # initialize new block - new_block = block_type( - name=new_name, _jump_targets=successors, backedges=set() - ) + new_block = block_type(name=new_name, _jump_targets=successors, backedges=set()) # add block to self self.add_block(new_block) # Replace any arcs from any of predecessors to any of successors with @@ -499,7 +504,10 @@ def insert_block( self.add_block(block.replace_jump_targets(jump_targets=tuple(jt))) def insert_SyntheticExit( - self, new_name: str, predecessors: Set[str], successors: Set[str], + self, + new_name: str, + predecessors: Set[str], + successors: Set[str], ): """Inserts a synthetic exit block into the SCFG. Parameters same as insert_block method. @@ -511,7 +519,10 @@ def insert_SyntheticExit( self.insert_block(new_name, predecessors, successors, SyntheticExit) def insert_SyntheticTail( - self, new_name: str, predecessors: Set[str], successors: Set[str], + self, + new_name: str, + predecessors: Set[str], + successors: Set[str], ): """Inserts a synthetic tail block into the SCFG. Parameters same as insert_block method. @@ -523,7 +534,10 @@ def insert_SyntheticTail( self.insert_block(new_name, predecessors, successors, SyntheticTail) def insert_SyntheticReturn( - self, new_name: str, predecessors: Set[str], successors: Set[str], + self, + new_name: str, + predecessors: Set[str], + successors: Set[str], ): """Inserts a synthetic return block into the SCFG. Parameters same as insert_block method. @@ -535,7 +549,10 @@ def insert_SyntheticReturn( self.insert_block(new_name, predecessors, successors, SyntheticReturn) def insert_SyntheticFill( - self, new_name: str, predecessors: Set[str], successors: Set[str], + self, + new_name: str, + predecessors: Set[str], + successors: Set[str], ): """Inserts a synthetic fill block into the SCFG. Parameters same as insert_block method. @@ -768,14 +785,14 @@ def to_yaml(self): for key, value in scfg_graph.items(): jump_targets = [i for i in value._jump_targets] - jump_targets = str(jump_targets).replace("\'", "\"") + 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("\'", "\"") + back_edges = str(back_edges).replace("'", '"') jump_target_str += f""" be: {back_edges}""" yaml_string += dedent(jump_target_str) @@ -783,11 +800,11 @@ def to_yaml(self): return yaml_string def to_dict(self): - """Converts the SCFG object to a dictionary representation. + """Converts the SCFG object to a dictionary representation. - This method returns a dictionary representing the control flow - graph. It iterates over the graph dictionary and generates a - dictionary entry for each block, including jump targets and + This method returns a dictionary representing the control flow + graph. It iterates over the graph dictionary and generates a + dictionary entry for each block, including jump targets and backedges if present. Returns @@ -805,11 +822,11 @@ def to_dict(self): graph_dict[key] = curr_dict return graph_dict - def view(self, name: str=None): + def view(self, name: str = None): """View the current SCFG as a external PDF file. - This method internally creates a SCFGRenderer corresponding to - the current state of SCFG and calls it's view method to view the + This method internally creates a SCFGRenderer corresponding to + the current state of SCFG and calls it's view method to view the graph as a graphviz generated external PDF file. Parameters @@ -818,8 +835,10 @@ def view(self, name: str=None): Name to be given to the external graphviz generated PDF file. """ from numba_rvsdg.rendering.rendering import SCFGRenderer + SCFGRenderer(self).view(name) + class AbstractGraphView(Mapping): """Abstract Graph View class. diff --git a/numba_rvsdg/core/transformations.py b/numba_rvsdg/core/transformations.py index 3fc6e37..0d6f44e 100644 --- a/numba_rvsdg/core/transformations.py +++ b/numba_rvsdg/core/transformations.py @@ -32,7 +32,7 @@ def loop_restructure_helper(scfg: SCFG, loop: Set[str]): headers, entries = scfg.find_headers_and_entries(loop) exiting_blocks, exit_blocks = scfg.find_exiting_and_exits(loop) - #assert len(entries) == 1 + # assert len(entries) == 1 headers_were_unified = False # If there are multiple headers, insert assignment and control blocks, @@ -51,8 +51,11 @@ def loop_restructure_helper(scfg: SCFG, loop: Set[str]): backedge_blocks = [ block for block in loop if set(headers).intersection(scfg[block].jump_targets) ] - if (len(backedge_blocks) == 1 and len(exiting_blocks) == 1 - and backedge_blocks[0] == next(iter(exiting_blocks))): + if ( + len(backedge_blocks) == 1 + and len(exiting_blocks) == 1 + and backedge_blocks[0] == next(iter(exiting_blocks)) + ): scfg.add_block(scfg.graph.pop(backedge_blocks[0]).declare_backedge(loop_head)) return @@ -81,7 +84,9 @@ def loop_restructure_helper(scfg: SCFG, loop: Set[str]): if needs_synth_exit: backedge_value_table = {i: j for i, j in enumerate((loop_head, synth_exit))} else: - backedge_value_table = {i: j for i, j in enumerate((loop_head, next(iter(exit_blocks))))} + backedge_value_table = { + i: j for i, j in enumerate((loop_head, next(iter(exit_blocks)))) + } if headers_were_unified: header_value_table = scfg[solo_head_name].branch_value_table else: @@ -112,16 +117,22 @@ def reverse_lookup(d, value): # If the target is an exit block if jt in exit_blocks: # Create a new assignment name and record it - synth_assign = scfg.name_gen.new_block_name(block_names.SYNTH_ASSIGN) + synth_assign = scfg.name_gen.new_block_name( + block_names.SYNTH_ASSIGN + ) new_blocks.add(synth_assign) # Setup the table for the variable assignment variable_assignment = {} # Setup the variables in the assignment table to point to # the correct blocks if needs_synth_exit: - variable_assignment[exit_variable] = reverse_lookup(exit_value_table, jt) - variable_assignment[backedge_variable] = reverse_lookup(backedge_value_table, - synth_exit if needs_synth_exit else next(iter(exit_blocks))) + variable_assignment[exit_variable] = reverse_lookup( + exit_value_table, jt + ) + variable_assignment[backedge_variable] = reverse_lookup( + backedge_value_table, + synth_exit if needs_synth_exit else next(iter(exit_blocks)), + ) # Create the actual control variable block synth_assign_block = SyntheticAssignment( name=synth_assign, @@ -138,14 +149,20 @@ def reverse_lookup(d, value): # If the target is the loop_head elif jt in headers and (name not in doms[jt] or name == jt): # Create the assignment and record it - synth_assign = scfg.name_gen.new_block_name(block_names.SYNTH_ASSIGN) + synth_assign = scfg.name_gen.new_block_name( + block_names.SYNTH_ASSIGN + ) new_blocks.add(synth_assign) # Setup the variables in the assignment table to point to # the correct blocks variable_assignment = {} - variable_assignment[backedge_variable] = reverse_lookup(backedge_value_table, loop_head) + variable_assignment[backedge_variable] = reverse_lookup( + backedge_value_table, loop_head + ) if needs_synth_exit: - variable_assignment[exit_variable] = reverse_lookup(header_value_table, jt) + variable_assignment[exit_variable] = reverse_lookup( + header_value_table, jt + ) # Update the backedge block - remove any existing backedges # that point to the headers, no need to add a backedge, # since it will be contained in the SyntheticExitingLatch @@ -178,7 +195,10 @@ def reverse_lookup(d, value): # Insert the exiting latch, add it to the loop and to the graph. synth_exiting_latch_block = SyntheticExitingLatch( name=synth_exiting_latch, - _jump_targets=(synth_exit if needs_synth_exit else next(iter(exit_blocks)), loop_head), + _jump_targets=( + synth_exit if needs_synth_exit else next(iter(exit_blocks)), + loop_head, + ), backedges=(loop_head,), variable=backedge_variable, branch_value_table=backedge_value_table, @@ -274,9 +294,7 @@ def _find_branch_regions(scfg: SCFG, begin: str, end: str) -> Set[str]: return branch_regions -def find_tail_blocks( - scfg: SCFG, begin: Set[str], head_region_blocks, branch_regions -): +def find_tail_blocks(scfg: SCFG, begin: Set[str], head_region_blocks, branch_regions): tail_subregion = {b for b in scfg.graph.keys()} tail_subregion.difference_update(head_region_blocks) for reg in branch_regions: @@ -291,7 +309,9 @@ def find_tail_blocks( return tail_subregion -def update_exiting(region_block: RegionBlock, new_region_header: str, new_region_name: str): +def update_exiting( + region_block: RegionBlock, new_region_header: str, new_region_name: str +): # Recursively updates the exiting blocks of a regionblock region_exiting = region_block.exiting region_exiting_block: BasicBlock = region_block.subregion.graph.pop(region_exiting) @@ -299,16 +319,18 @@ def update_exiting(region_block: RegionBlock, new_region_header: str, new_region for idx, s in enumerate(jt): if s is new_region_header: jt[idx] = new_region_name - region_exiting_block = region_exiting_block.replace_jump_targets(jump_targets=tuple(jt)) + region_exiting_block = region_exiting_block.replace_jump_targets( + jump_targets=tuple(jt) + ) be = list(region_exiting_block.backedges) for idx, s in enumerate(be): if s is new_region_header: be[idx] = new_region_name region_exiting_block = region_exiting_block.replace_backedges(backedges=tuple(be)) if isinstance(region_exiting_block, RegionBlock): - region_exiting_block = update_exiting(region_exiting_block, - new_region_header, - new_region_name) + region_exiting_block = update_exiting( + region_exiting_block, new_region_header, new_region_name + ) region_block.subregion.add_block(region_exiting_block) return region_block @@ -362,7 +384,7 @@ def extract_region(scfg: SCFG, region_blocks, region_kind, parent_region: Region header=region_header, subregion=head_subgraph, exiting=region_exiting, - parent_region=parent_region + parent_region=parent_region, ) scfg.remove_blocks(region_blocks) scfg.graph[region_name] = region @@ -435,8 +457,12 @@ def restructure_branch(parent_region: RegionBlock): else: # Insert SyntheticFill, a placeholder for an empty branch region tail_headers, _ = scfg.find_headers_and_entries(tail_region_blocks) - synthetic_branch_block_name = scfg.name_gen.new_block_name(block_names.SYNTH_FILL) - scfg.insert_SyntheticFill(synthetic_branch_block_name, (begin,), tail_headers) + synthetic_branch_block_name = scfg.name_gen.new_block_name( + block_names.SYNTH_FILL + ) + scfg.insert_SyntheticFill( + synthetic_branch_block_name, (begin,), tail_headers + ) # Recompute regions. head_region_blocks = find_head_blocks(scfg, begin) diff --git a/numba_rvsdg/rendering/rendering.py b/numba_rvsdg/rendering/rendering.py index 77453e4..29cec8b 100644 --- a/numba_rvsdg/rendering/rendering.py +++ b/numba_rvsdg/rendering/rendering.py @@ -16,6 +16,7 @@ import dis from typing import Dict + class BaseRenderer: """Base Renderer class. @@ -63,6 +64,7 @@ def render_edges(self, scfg: SCFG): """ blocks = dict(scfg) + def find_base_header(block: BasicBlock): if isinstance(block, RegionBlock): block = blocks[block.header] @@ -83,7 +85,11 @@ def find_base_header(block: BasicBlock): dst_name = find_base_header(blocks[dst_name]).name if dst_name in blocks.keys(): self.g.edge( - str(src_block.name), str(dst_name), style="dashed", color="grey", constraint="0" + str(src_block.name), + str(dst_name), + style="dashed", + color="grey", + constraint="0", ) else: raise Exception("unreachable " + str(src_block)) @@ -125,7 +131,7 @@ def render_region_block( self.render_block(subg, name, block) def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): - if name.startswith('python_bytecode'): + if name.startswith("python_bytecode"): instlist = block.get_instructions(self.bcmap) body = name + r"\l" body += r"\l".join( @@ -148,12 +154,10 @@ def render_control_variable_block( raise Exception("Unknown name type: " + name) digraph.node(str(name), shape="rect", label=body) - def render_branching_block( - self, digraph: "Digraph", name: str, block: BasicBlock - ): + def render_branching_block(self, digraph: "Digraph", name: str, block: BasicBlock): if isinstance(name, str): body = name + r"\l" - body += fr"variable: {block.variable}\l" + body += rf"variable: {block.variable}\l" body += r"\l".join( (f"{k}=>{v}" for k, v in block.branch_value_table.items()) ) @@ -208,18 +212,27 @@ def render_region_block( color = "purple" if regionblock.kind == "head": color = "red" - label = regionblock.name + \ - "\njump targets: " + str(regionblock.jump_targets) + \ - "\nback edges: " + str(regionblock.backedges) + label = ( + regionblock.name + + "\njump targets: " + + str(regionblock.jump_targets) + + "\nback edges: " + + str(regionblock.backedges) + ) subg.attr(color=color, label=label) for name, block in regionblock.subregion.graph.items(): self.render_block(subg, name, block) def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): - body = name + r"\l"+ \ - "\njump targets: " + str(block.jump_targets) + \ - "\nback edges: " + str(block.backedges) + body = ( + name + + r"\l" + + "\njump targets: " + + str(block.jump_targets) + + "\nback edges: " + + str(block.backedges) + ) digraph.node(str(name), shape="rect", label=body) @@ -231,26 +244,30 @@ def render_control_variable_block( body += r"\l".join( (f"{k} = {v}" for k, v in block.variable_assignment.items()) ) - body += \ - "\njump targets: " + str(block.jump_targets) + \ - "\nback edges: " + str(block.backedges) + body += ( + "\njump targets: " + + str(block.jump_targets) + + "\nback edges: " + + str(block.backedges) + ) else: raise Exception("Unknown name type: " + name) digraph.node(str(name), shape="rect", label=body) - def render_branching_block( - self, digraph: "Digraph", name: str, block: BasicBlock - ): + def render_branching_block(self, digraph: "Digraph", name: str, block: BasicBlock): if isinstance(name, str): body = name + r"\l" - body += fr"variable: {block.variable}\l" + body += rf"variable: {block.variable}\l" body += r"\l".join( (f"{k}=>{v}" for k, v in block.branch_value_table.items()) ) - body += \ - "\njump targets: " + str(block.jump_targets) + \ - "\nback edges: " + str(block.backedges) + body += ( + "\njump targets: " + + str(block.jump_targets) + + "\nback edges: " + + str(block.backedges) + ) else: raise Exception("Unknown name type: " + name) diff --git a/numba_rvsdg/tests/mock_asm.py b/numba_rvsdg/tests/mock_asm.py index 315451f..e6d3135 100644 --- a/numba_rvsdg/tests/mock_asm.py +++ b/numba_rvsdg/tests/mock_asm.py @@ -210,10 +210,7 @@ def generate_program(self, min_base_length=5, max_base_length=30) -> str: # generate BB indent = " " * 4 for i in range(size): - bb: list[str] = [ - f"label BB{i}", - f"{indent}print P{i}" - ] + bb: list[str] = [f"label BB{i}", f"{indent}print P{i}"] [kind] = self.rng.choices(["goto", "brctr", ""], [1, 10, 20]) if kind == "goto": target = self.rng.randrange(size) diff --git a/numba_rvsdg/tests/simulator.py b/numba_rvsdg/tests/simulator.py index 861d7dd..2e2fd61 100644 --- a/numba_rvsdg/tests/simulator.py +++ b/numba_rvsdg/tests/simulator.py @@ -52,7 +52,6 @@ class Simulator: """ def __init__(self, flow: ByteFlow, globals: dict): - self.flow = flow self.scfg = flow.scfg self.globals = ChainMap(globals, builtins.__dict__) @@ -197,11 +196,11 @@ def run_RegionBlock(self, name: str): else: break # break and return action else: - assert False, "unreachable" # in case of coding errors + assert False, "unreachable" # in case of coding errors # Pop the region from the region stack again and return the final # action for this region popped = self.region_stack.pop() - assert (popped == region) + assert popped == region return action def run_PythonBytecodeBlock(self, name: str): @@ -230,7 +229,7 @@ def run_synth_block(self, name: str): print("----", name) print(f"control variable map: {self.ctrl_varmap}") block = self.get_block(name) - handler = getattr(self, 'synth_' + block.__class__.__name__) + handler = getattr(self, "synth_" + block.__class__.__name__) handler(name, block) def run_inst(self, inst: Instruction): diff --git a/numba_rvsdg/tests/test_byteflow.py b/numba_rvsdg/tests/test_byteflow.py index a0341fd..7cf8ca5 100644 --- a/numba_rvsdg/tests/test_byteflow.py +++ b/numba_rvsdg/tests/test_byteflow.py @@ -7,6 +7,7 @@ from numba_rvsdg.core.datastructures.flow_info import FlowInfo from numba_rvsdg.core.datastructures import block_names + def fun(): x = 1 return x @@ -16,6 +17,7 @@ def fun(): # If the function definition line changes, just change the variable below, rest of it will adjust as long as function remains the same func_def_line = 10 + class TestBCMapFromBytecode(unittest.TestCase): def test(self): expected = { @@ -114,7 +116,7 @@ def test_constructor(self): _jump_targets=(), backedges=(), ) - self.assertEqual(block.name, 'python_bytecode_block_0') + self.assertEqual(block.name, "python_bytecode_block_0") self.assertEqual(block.begin, 0) self.assertEqual(block.end, 8) self.assertFalse(block.fallthrough) @@ -131,7 +133,7 @@ def test_is_jump_target(self): _jump_targets=(name_gen.new_block_name(block_names.PYTHON_BYTECODE),), backedges=(), ) - self.assertEqual(block.jump_targets, ('python_bytecode_block_1',)) + self.assertEqual(block.jump_targets, ("python_bytecode_block_1",)) self.assertFalse(block.is_exiting) def test_get_instructions(self): @@ -221,7 +223,6 @@ def test_constructor(self): self.assertEqual(len(flowinfo.jump_insts), 0) def test_from_bytecode(self): - expected = FlowInfo(block_offsets={0}, jump_insts={8: ()}, last_offset=8) received = FlowInfo.from_bytecode(bytecode) diff --git a/numba_rvsdg/tests/test_fig4.py b/numba_rvsdg/tests/test_fig4.py index b54ff7b..7d9a046 100644 --- a/numba_rvsdg/tests/test_fig4.py +++ b/numba_rvsdg/tests/test_fig4.py @@ -31,6 +31,7 @@ def make_flow(): scfg = flow.build_basicblocks() return ByteFlow(bc=bc, scfg=scfg) + def test_fig4(): f = make_flow() f.restructure() diff --git a/numba_rvsdg/tests/test_mock_asm.py b/numba_rvsdg/tests/test_mock_asm.py index b79ee19..ef9eb46 100644 --- a/numba_rvsdg/tests/test_mock_asm.py +++ b/numba_rvsdg/tests/test_mock_asm.py @@ -10,7 +10,8 @@ def test_mock_asm(): - asm = textwrap.dedent(""" + asm = textwrap.dedent( + """ print Start goto A label A @@ -19,7 +20,8 @@ def test_mock_asm(): brctr A B label B print B - """) + """ + ) instlist = parse(asm) assert instlist[0].operands.text == "Start" @@ -39,7 +41,8 @@ def test_mock_asm(): def test_double_exchange_loop(): - asm = textwrap.dedent(""" + asm = textwrap.dedent( + """ print Start label A print A @@ -51,7 +54,8 @@ def test_double_exchange_loop(): brctr A Exit label Exit print Exit - """) + """ + ) instlist = parse(asm) with StringIO() as buf: VM(buf).run(instlist) @@ -70,7 +74,6 @@ def test_program_gen(): print(str(i).center(80, "=")) asm = pg.generate_program() - instlist = parse(asm) with StringIO() as buf: terminated = VM(buf).run(instlist, max_step=1000) diff --git a/numba_rvsdg/tests/test_scc.py b/numba_rvsdg/tests/test_scc.py index 8def674..81a9352 100644 --- a/numba_rvsdg/tests/test_scc.py +++ b/numba_rvsdg/tests/test_scc.py @@ -1,6 +1,7 @@ from numba_rvsdg.core.datastructures.byte_flow import ByteFlow from numba_rvsdg.rendering.rendering import render_flow + def scc(G): preorder = {} lowlink = {} @@ -46,9 +47,11 @@ def scc(G): def make_flow(func): return ByteFlow.from_bytecode(func) + def test_scc(): f = make_flow(scc) f.restructure() + if __name__ == "__main__": render_flow(make_flow(scc)) diff --git a/numba_rvsdg/tests/test_scfg.py b/numba_rvsdg/tests/test_scfg.py index ca1e2e3..8a2d6d8 100644 --- a/numba_rvsdg/tests/test_scfg.py +++ b/numba_rvsdg/tests/test_scfg.py @@ -3,18 +3,20 @@ from numba_rvsdg.core.datastructures.scfg import SCFG, NameGenerator from numba_rvsdg.tests.test_utils import SCFGComparator -from numba_rvsdg.core.datastructures.basic_block import (BasicBlock, - RegionBlock, - PythonBytecodeBlock, - ) +from numba_rvsdg.core.datastructures.basic_block import ( + BasicBlock, + RegionBlock, + PythonBytecodeBlock, +) from numba_rvsdg.core.datastructures.byte_flow import ByteFlow from numba_rvsdg.core.datastructures import block_names -class TestSCFGConversion(SCFGComparator): +class TestSCFGConversion(SCFGComparator): def test_yaml_conversion(self): # Case # 1: Acyclic graph, no back-edges - cases = [""" + cases = [ + """ "0": jt: ["1", "2"] "1": @@ -25,7 +27,7 @@ def test_yaml_conversion(self): jt: ["4"] "4": jt: []""", - # Case # 2: Cyclic graph, no back edges + # Case # 2: Cyclic graph, no back edges """ "0": jt: ["1", "2"] @@ -39,7 +41,7 @@ def test_yaml_conversion(self): jt: [] "5": jt: ["3", "4"]""", - # Case # 3: Graph with backedges + # Case # 3: Graph with backedges """ "0": jt: ["1"] @@ -51,84 +53,71 @@ def test_yaml_conversion(self): jt: [] "4": jt: ["2", "3"] - be: ["2"]"""] + be: ["2"]""", + ] for case in cases: case = dedent(case) scfg, block_dict = SCFG.from_yaml(case) - self.assertYAMLEqual(case, scfg.to_yaml(), {'0': block_dict['0']}) + self.assertYAMLEqual(case, scfg.to_yaml(), {"0": block_dict["0"]}) def test_dict_conversion(self): # Case # 1: Acyclic graph, no back-edges - cases = [{ - "0": - {"jt": ["1", "2"]}, - "1": - {"jt": ["3"]}, - "2": - {"jt": ["4"]}, - "3": - {"jt": ["4"]}, - "4": - {"jt": []}}, - # Case # 2: Cyclic graph, no back edges - { - "0": - {"jt": ["1", "2"]}, - "1": - {"jt": ["5"]}, - "2": - {"jt": ["1", "5"]}, - "3": - {"jt": ["0"]}, - "4": - {"jt": []}, - "5": - {"jt": ["3", "4"]}}, - # Case # 3: Graph with backedges - { - "0": - {"jt": ["1"]}, - "1": - {"jt": ["2", "3"]}, - "2": - {"jt": ["4"]}, - "3": - {"jt": []}, - "4": - {"jt": ["2", "3"], - "be": ["2"]}}] + cases = [ + { + "0": {"jt": ["1", "2"]}, + "1": {"jt": ["3"]}, + "2": {"jt": ["4"]}, + "3": {"jt": ["4"]}, + "4": {"jt": []}, + }, + # Case # 2: Cyclic graph, no back edges + { + "0": {"jt": ["1", "2"]}, + "1": {"jt": ["5"]}, + "2": {"jt": ["1", "5"]}, + "3": {"jt": ["0"]}, + "4": {"jt": []}, + "5": {"jt": ["3", "4"]}, + }, + # Case # 3: Graph with backedges + { + "0": {"jt": ["1"]}, + "1": {"jt": ["2", "3"]}, + "2": {"jt": ["4"]}, + "3": {"jt": []}, + "4": {"jt": ["2", "3"], "be": ["2"]}, + }, + ] for case in cases: scfg, block_dict = SCFG.from_dict(case) - self.assertDictEqual(case, scfg.to_dict(), {'0': block_dict['0']}) + self.assertDictEqual(case, scfg.to_dict(), {"0": block_dict["0"]}) class TestSCFGIterator(SCFGComparator): - def test_scfg_iter(self): name_gen = NameGenerator() block_0 = name_gen.new_block_name(block_names.BASIC) block_1 = name_gen.new_block_name(block_names.BASIC) expected = [ - (block_0, BasicBlock(name=block_0, - _jump_targets=(block_1,))), + (block_0, BasicBlock(name=block_0, _jump_targets=(block_1,))), (block_1, BasicBlock(name=block_1)), ] - scfg, _ = SCFG.from_yaml(""" + scfg, _ = SCFG.from_yaml( + """ "0": jt: ["1"] "1": jt: [] - """) + """ + ) received = list(scfg) self.assertEqual(expected, received) class TestConcealedRegionView(TestCase): - def setUp(self): - def foo(n): c = 0 for i in range(n): @@ -138,13 +127,16 @@ def foo(n): self.foo = foo def test_concealed_region_view_iter(self): - flow = ByteFlow.from_bytecode(self.foo) restructured = flow._restructure_loop() - expected = [('python_bytecode_block_0', PythonBytecodeBlock), - ('loop_region_0', RegionBlock), - ('python_bytecode_block_3', PythonBytecodeBlock)] - received = list(((k, type(v)) for k, v in restructured.scfg.concealed_region_view.items())) + expected = [ + ("python_bytecode_block_0", PythonBytecodeBlock), + ("loop_region_0", RegionBlock), + ("python_bytecode_block_3", PythonBytecodeBlock), + ] + received = list( + ((k, type(v)) for k, v in restructured.scfg.concealed_region_view.items()) + ) self.assertEqual(expected, received) diff --git a/numba_rvsdg/tests/test_transforms.py b/numba_rvsdg/tests/test_transforms.py index 4b713df..84c70a7 100644 --- a/numba_rvsdg/tests/test_transforms.py +++ b/numba_rvsdg/tests/test_transforms.py @@ -6,6 +6,7 @@ from numba_rvsdg.tests.test_utils import SCFGComparator from numba_rvsdg.core.datastructures import block_names + class TestInsertBlock(SCFGComparator): def test_linear(self): original = """ @@ -26,8 +27,7 @@ def test_linear(self): expected_scfg, _ = SCFG.from_yaml(expected) new_name = original_scfg.name_gen.new_block_name(block_names.BASIC) original_scfg.insert_block( - new_name, (block_dict["0"],), (block_dict["1"],), - BasicBlock + new_name, (block_dict["0"],), (block_dict["1"],), BasicBlock ) self.assertSCFGEqual(expected_scfg, original_scfg) @@ -54,11 +54,16 @@ def test_dual_predecessor(self): expected_scfg, expected_block_dict = SCFG.from_yaml(expected) new_name = original_scfg.name_gen.new_block_name(block_names.BASIC) original_scfg.insert_block( - new_name, (block_dict["0"], block_dict["1"]), (block_dict["2"],), - BasicBlock + new_name, (block_dict["0"], block_dict["1"]), (block_dict["2"],), BasicBlock + ) + self.assertSCFGEqual( + expected_scfg, + original_scfg, + { + block_dict["0"]: expected_block_dict["0"], + block_dict["1"]: expected_block_dict["1"], + }, ) - self.assertSCFGEqual(expected_scfg, original_scfg, {block_dict["0"]: expected_block_dict["0"], - block_dict["1"]: expected_block_dict["1"]}) def test_dual_successor(self): original = """ @@ -85,7 +90,7 @@ def test_dual_successor(self): original_scfg.name_gen.new_block_name(block_names.BASIC), (block_dict["0"],), (block_dict["1"], block_dict["2"]), - BasicBlock + BasicBlock, ) self.assertSCFGEqual(expected_scfg, original_scfg) @@ -122,7 +127,7 @@ def test_dual_predecessor_and_dual_successor(self): original_scfg.name_gen.new_block_name(block_names.BASIC), (block_dict["1"], block_dict["2"]), (block_dict["3"], block_dict["4"]), - BasicBlock + BasicBlock, ) self.assertSCFGEqual(expected_scfg, original_scfg) @@ -159,10 +164,16 @@ def test_dual_predecessor_and_dual_successor_with_additional_arcs(self): original_scfg.name_gen.new_block_name(block_names.BASIC), (block_dict["1"], block_dict["2"]), (block_dict["3"], block_dict["4"]), - BasicBlock + BasicBlock, + ) + self.assertSCFGEqual( + expected_scfg, + original_scfg, + { + block_dict["0"]: expected_block_dict["0"], + block_dict["2"]: expected_block_dict["2"], + }, ) - self.assertSCFGEqual(expected_scfg, original_scfg, {block_dict["0"]: expected_block_dict["0"], - block_dict["2"]: expected_block_dict["2"]}) class TestJoinReturns(SCFGComparator): @@ -252,7 +263,10 @@ def test_join_tails_and_exits_case_01(self): self.assertSCFGEqual(expected_scfg, original_scfg) self.assertEqual(block_dict["0"], solo_tail_name) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_EXIT), solo_exit_name) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_EXIT), + solo_exit_name, + ) def test_join_tails_and_exits_case_02_01(self): original = """ @@ -287,7 +301,10 @@ def test_join_tails_and_exits_case_02_01(self): ) self.assertSCFGEqual(expected_scfg, original_scfg) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), solo_tail_name) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), + solo_tail_name, + ) self.assertEqual(block_dict["3"], solo_exit_name) def test_join_tails_and_exits_case_02_02(self): @@ -323,11 +340,13 @@ def test_join_tails_and_exits_case_02_02(self): tails, exits ) self.assertSCFGEqual(expected_scfg, original_scfg) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), solo_tail_name) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), + solo_tail_name, + ) self.assertEqual(block_dict["3"], solo_exit_name) def test_join_tails_and_exits_case_03_01(self): - original = """ "0": jt: ["1", "2"] @@ -369,11 +388,16 @@ def test_join_tails_and_exits_case_03_01(self): tails, exits ) self.assertSCFGEqual(expected_scfg, original_scfg) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), solo_tail_name) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_EXIT), solo_exit_name) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), + solo_tail_name, + ) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_EXIT), + solo_exit_name, + ) def test_join_tails_and_exits_case_03_02(self): - original = """ "0": jt: ["1", "2"] @@ -414,12 +438,17 @@ def test_join_tails_and_exits_case_03_02(self): tails, exits ) self.assertSCFGEqual(expected_scfg, original_scfg) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), solo_tail_name) - self.assertEqual(expected_scfg.name_gen.new_block_name(block_names.SYNTH_EXIT), solo_exit_name) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_TAIL), + solo_tail_name, + ) + self.assertEqual( + expected_scfg.name_gen.new_block_name(block_names.SYNTH_EXIT), + solo_exit_name, + ) class TestLoopRestructure(SCFGComparator): - def test_no_op_mono(self): """Loop consists of a single Block.""" original = """ @@ -585,11 +614,13 @@ def test_double_exit(self): """ original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) - loop_restructure_helper(original_scfg, set({block_dict["1"], block_dict["2"], block_dict["3"]})) + loop_restructure_helper( + original_scfg, set({block_dict["1"], block_dict["2"], block_dict["3"]}) + ) self.assertSCFGEqual(expected_scfg, original_scfg) def test_double_header(self): - """ This is like the example from Bahman2015 fig. 3 -- + """This is like the example from Bahman2015 fig. 3 -- but with one exiting block removed.""" original = """ "0": @@ -636,11 +667,14 @@ def test_double_header(self): """ original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) - loop_restructure_helper(original_scfg, set({block_dict["1"], block_dict["2"], block_dict["3"], block_dict["4"]})) + loop_restructure_helper( + original_scfg, + set({block_dict["1"], block_dict["2"], block_dict["3"], block_dict["4"]}), + ) self.assertSCFGEqual(expected_scfg, original_scfg) def test_double_header_double_exiting(self): - """ This is like the example from Bahman2015 fig. 3. + """This is like the example from Bahman2015 fig. 3. Two headers that need to be multiplexed to, on additional branch that becomes the exiting latch and one branch that becomes the exit. @@ -703,8 +737,12 @@ def test_double_header_double_exiting(self): """ original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) - loop_restructure_helper(original_scfg, set({block_dict["1"], block_dict["2"], block_dict["3"], block_dict["4"]})) + loop_restructure_helper( + original_scfg, + set({block_dict["1"], block_dict["2"], block_dict["3"], block_dict["4"]}), + ) self.assertSCFGEqual(expected_scfg, original_scfg) + if __name__ == "__main__": main() diff --git a/numba_rvsdg/tests/test_utils.py b/numba_rvsdg/tests/test_utils.py index 778753d..937330a 100644 --- a/numba_rvsdg/tests/test_utils.py +++ b/numba_rvsdg/tests/test_utils.py @@ -4,6 +4,7 @@ from numba_rvsdg.core.datastructures.scfg import SCFG from numba_rvsdg.core.datastructures.basic_block import BasicBlock + class SCFGComparator(TestCase): def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): if head_map: @@ -17,7 +18,9 @@ def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): stack = [first_head] # Assert number of blocks are equal in both SCFGs - assert len(first_scfg.graph) == len(second_scfg.graph), "Number of blocks in both graphs are not equal" + assert len(first_scfg.graph) == len( + second_scfg.graph + ), "Number of blocks in both graphs are not equal" seen = set() while stack: @@ -47,13 +50,17 @@ def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): stack.append(be1) def assertYAMLEqual(self, first_yaml: SCFG, second_yaml: SCFG, head_map: dict): - self.assertDictEqual(yaml.safe_load(first_yaml), yaml.safe_load(second_yaml), head_map) + self.assertDictEqual( + yaml.safe_load(first_yaml), yaml.safe_load(second_yaml), head_map + ) def assertDictEqual(self, first_yaml: str, second_yaml: str, head_map: dict): block_mapping = head_map stack = list(block_mapping.keys()) # Assert number of blocks are equal in both SCFGs - assert len(first_yaml) == len(second_yaml), "Number of blocks in both graphs are not equal" + assert len(first_yaml) == len( + second_yaml + ), "Number of blocks in both graphs are not equal" seen = set() while stack: @@ -68,13 +75,13 @@ def assertDictEqual(self, first_yaml: str, second_yaml: str, head_map: dict): second_node_name = block_mapping[node_name] second_node: BasicBlock = second_yaml[second_node_name] # Both nodes should have equal number of jump targets and backedges - assert len(node['jt']) == len(second_node['jt']) - if 'be' in node.keys(): - assert len(node['be']) == len(second_node['be']) + assert len(node["jt"]) == len(second_node["jt"]) + if "be" in node.keys(): + assert len(node["be"]) == len(second_node["be"]) # Add the jump targets as corresponding nodes in block mapping dictionary # Since order must be same we can simply add zip fucntionality as the # correspondence function for nodes - for jt1, jt2 in zip(node['jt'], second_node['jt']): + for jt1, jt2 in zip(node["jt"], second_node["jt"]): block_mapping[jt1] = jt2 stack.append(jt1) From 83ac01cba005713d9c9bccaa52f786fb6f994dd7 Mon Sep 17 00:00:00 2001 From: kc611 Date: Wed, 21 Jun 2023 10:46:04 +0530 Subject: [PATCH 4/8] Pre-commit changes: Autoflake changes --- .../core/datastructures/basic_block.py | 19 +------------------ numba_rvsdg/core/transformations.py | 4 ++-- numba_rvsdg/core/utils.py | 1 - numba_rvsdg/tests/mock_asm.py | 3 --- numba_rvsdg/tests/simulator.py | 2 -- numba_rvsdg/tests/test_mock_asm.py | 4 ---- numba_rvsdg/tests/test_transforms.py | 2 +- 7 files changed, 4 insertions(+), 31 deletions(-) diff --git a/numba_rvsdg/core/datastructures/basic_block.py b/numba_rvsdg/core/datastructures/basic_block.py index 6c6706b..a59b7c5 100644 --- a/numba_rvsdg/core/datastructures/basic_block.py +++ b/numba_rvsdg/core/datastructures/basic_block.py @@ -1,7 +1,6 @@ import dis -from collections import ChainMap from typing import Tuple, Dict, List -from dataclasses import dataclass, field, replace +from dataclasses import dataclass, replace from numba_rvsdg.core.utils import _next_inst_offset @@ -203,8 +202,6 @@ class SyntheticBlock(BasicBlock): structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticExit(SyntheticBlock): @@ -212,8 +209,6 @@ class SyntheticExit(SyntheticBlock): in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticReturn(SyntheticBlock): @@ -221,8 +216,6 @@ class SyntheticReturn(SyntheticBlock): in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticTail(SyntheticBlock): @@ -230,8 +223,6 @@ class SyntheticTail(SyntheticBlock): in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticFill(SyntheticBlock): @@ -239,8 +230,6 @@ class SyntheticFill(SyntheticBlock): in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticAssignment(SyntheticBlock): @@ -333,8 +322,6 @@ class SyntheticHead(SyntheticBranch): in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticExitingLatch(SyntheticBranch): @@ -342,8 +329,6 @@ class SyntheticExitingLatch(SyntheticBranch): block in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class SyntheticExitBranch(SyntheticBranch): @@ -351,8 +336,6 @@ class SyntheticExitBranch(SyntheticBranch): block in a structured control flow graph (SCFG). """ - pass - @dataclass(frozen=True) class RegionBlock(BasicBlock): diff --git a/numba_rvsdg/core/transformations.py b/numba_rvsdg/core/transformations.py index 0d6f44e..d7c1767 100644 --- a/numba_rvsdg/core/transformations.py +++ b/numba_rvsdg/core/transformations.py @@ -264,8 +264,8 @@ def find_branch_regions(scfg: SCFG, begin: str, end: str) -> Set[str]: # identify branch regions doms = _doms(scfg) postdoms = _post_doms(scfg) - postimmdoms = _imm_doms(postdoms) - immdoms = _imm_doms(doms) + _imm_doms(postdoms) + _imm_doms(doms) branch_regions = [] jump_targets = scfg.graph[begin].jump_targets for bra_start in jump_targets: diff --git a/numba_rvsdg/core/utils.py b/numba_rvsdg/core/utils.py index f3123e5..a951233 100644 --- a/numba_rvsdg/core/utils.py +++ b/numba_rvsdg/core/utils.py @@ -1,4 +1,3 @@ -import dis import logging _logger = logging.getLogger(__name__) diff --git a/numba_rvsdg/tests/mock_asm.py b/numba_rvsdg/tests/mock_asm.py index e6d3135..286a25d 100644 --- a/numba_rvsdg/tests/mock_asm.py +++ b/numba_rvsdg/tests/mock_asm.py @@ -6,11 +6,8 @@ from dataclasses import dataclass from enum import IntEnum -from pprint import pprint -from io import StringIO from typing import IO import random -import textwrap class Opcode(IntEnum): diff --git a/numba_rvsdg/tests/simulator.py b/numba_rvsdg/tests/simulator.py index 2e2fd61..1b2d4c7 100644 --- a/numba_rvsdg/tests/simulator.py +++ b/numba_rvsdg/tests/simulator.py @@ -1,7 +1,6 @@ from collections import ChainMap from dis import Instruction from numba_rvsdg.core.datastructures.byte_flow import ByteFlow -from numba_rvsdg.core.datastructures.scfg import SCFG from numba_rvsdg.core.datastructures.basic_block import ( BasicBlock, PythonBytecodeBlock, @@ -299,7 +298,6 @@ def op_LOAD_FAST(self, inst): def op_LOAD_GLOBAL(self, inst): v = self.globals[inst.argval] if inst.argrepr.startswith("NULL"): - append_null = True self.stack.append(v) self.stack.append(None) else: diff --git a/numba_rvsdg/tests/test_mock_asm.py b/numba_rvsdg/tests/test_mock_asm.py index ef9eb46..6773a5f 100644 --- a/numba_rvsdg/tests/test_mock_asm.py +++ b/numba_rvsdg/tests/test_mock_asm.py @@ -1,8 +1,4 @@ -from dataclasses import dataclass -from enum import IntEnum -from pprint import pprint from io import StringIO -from typing import IO import random import textwrap diff --git a/numba_rvsdg/tests/test_transforms.py b/numba_rvsdg/tests/test_transforms.py index 84c70a7..8ba3ed7 100644 --- a/numba_rvsdg/tests/test_transforms.py +++ b/numba_rvsdg/tests/test_transforms.py @@ -1,7 +1,7 @@ from unittest import main from numba_rvsdg.core.datastructures.scfg import SCFG -from numba_rvsdg.core.datastructures.basic_block import BasicBlock, RegionBlock +from numba_rvsdg.core.datastructures.basic_block import BasicBlock from numba_rvsdg.core.transformations import loop_restructure_helper from numba_rvsdg.tests.test_utils import SCFGComparator from numba_rvsdg.core.datastructures import block_names From e4d20df8ecd77116ff61eb046c5a3151503f72f3 Mon Sep 17 00:00:00 2001 From: kc611 Date: Wed, 21 Jun 2023 11:05:11 +0530 Subject: [PATCH 5/8] Pre-commit changes: Flake8 suggested changes --- .pre-commit-config.yaml | 3 +- .../core/datastructures/basic_block.py | 10 +-- numba_rvsdg/core/datastructures/byte_flow.py | 5 +- numba_rvsdg/core/datastructures/scfg.py | 86 +++++++++++++------ numba_rvsdg/core/transformations.py | 65 ++++++++++---- numba_rvsdg/networkx_vendored/scc.py | 6 +- numba_rvsdg/rendering/rendering.py | 63 ++++++++------ numba_rvsdg/tests/mock_asm.py | 7 +- numba_rvsdg/tests/simulator.py | 34 ++++---- numba_rvsdg/tests/test_byteflow.py | 11 ++- numba_rvsdg/tests/test_scc.py | 4 +- numba_rvsdg/tests/test_scfg.py | 5 +- numba_rvsdg/tests/test_transforms.py | 38 ++++++-- numba_rvsdg/tests/test_utils.py | 30 ++++--- 14 files changed, 246 insertions(+), 121 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c7f014..77fd050 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,8 @@ repos: hooks: - id: black language_version: python3 - # Autoflake + args: ["--line-length=79"] + # Autoflake: removes unused imports and variables - repo: https://github.com/humitos/mirrors-autoflake.git rev: v1.1 hooks: diff --git a/numba_rvsdg/core/datastructures/basic_block.py b/numba_rvsdg/core/datastructures/basic_block.py index a59b7c5..34f3ab2 100644 --- a/numba_rvsdg/core/datastructures/basic_block.py +++ b/numba_rvsdg/core/datastructures/basic_block.py @@ -325,15 +325,15 @@ class SyntheticHead(SyntheticBranch): @dataclass(frozen=True) class SyntheticExitingLatch(SyntheticBranch): - """The SyntheticExitingLatch class represents a artificially added exiting latch - block in a structured control flow graph (SCFG). + """The SyntheticExitingLatch class represents a artificially added + exiting latchc block in a structured control flow graph (SCFG). """ @dataclass(frozen=True) class SyntheticExitBranch(SyntheticBranch): - """The SyntheticExitBranch class represents a artificially added exit branch - block in a structured control flow graph (SCFG). + """The SyntheticExitBranch class represents a artificially added + exit branch block in a structured control flow graph (SCFG). """ @@ -362,7 +362,7 @@ class RegionBlock(BasicBlock): kind: str = None parent_region: "RegionBlock" = None header: str = None - subregion: "SCFG" = None + subregion: "SCFG" = None # noqa exiting: str = None def replace_header(self, new_header): diff --git a/numba_rvsdg/core/datastructures/byte_flow.py b/numba_rvsdg/core/datastructures/byte_flow.py index 5030708..9b1ebb0 100644 --- a/numba_rvsdg/core/datastructures/byte_flow.py +++ b/numba_rvsdg/core/datastructures/byte_flow.py @@ -7,7 +7,10 @@ from numba_rvsdg.core.datastructures.flow_info import FlowInfo from numba_rvsdg.core.utils import _logger, _LogWrap -from numba_rvsdg.core.transformations import restructure_loop, restructure_branch +from numba_rvsdg.core.transformations import ( + restructure_loop, + restructure_branch, +) @dataclass(frozen=True) diff --git a/numba_rvsdg/core/datastructures/scfg.py b/numba_rvsdg/core/datastructures/scfg.py index d7c476e..294b314 100644 --- a/numba_rvsdg/core/datastructures/scfg.py +++ b/numba_rvsdg/core/datastructures/scfg.py @@ -30,7 +30,8 @@ class NameGenerator: Attributes ---------- kinds: dict[str, int] - A dictionary that keeps track of the current index for each kind of name. + A dictionary that keeps track of the current index for each kind + of name. """ kinds: dict[str, int] = field(default_factory=dict) @@ -102,8 +103,8 @@ def new_var_name(self, kind: str) -> str: in the kinds dictionary attribute. If it exists, the respective index is incremented, if it doesn't then a new index (starting from zero) is asigned to the given kind. This ensures that - the given name is unique by a combination of it's kind and it's index. - It returns the generated name. + the given name is unique by a combination of it's kind and it's + index. It returns the generated name. Parameters ---------- @@ -130,20 +131,25 @@ def new_var_name(self, kind: str) -> str: class SCFG: """SCFG (Structured Control Flow Graph) class. - The SCFG class represents a map of names to blocks within the control flow graph. + The SCFG class represents a map of names to blocks within the control + flow graph. Attributes ---------- graph: Dict[str, BasicBlock] - A dictionary that maps names to corresponding BasicBlock objects within the control flow graph. + A dictionary that maps names to corresponding BasicBlock objects + within the control flow graph. name_gen: NameGenerator - A NameGenerator object that provides unique names for blocks, regions, and variables. + A NameGenerator object that provides unique names for blocks, + regions, and variables. """ graph: Dict[str, BasicBlock] = field(default_factory=dict) - name_gen: NameGenerator = field(default_factory=NameGenerator, compare=False) + name_gen: NameGenerator = field( + default_factory=NameGenerator, compare=False + ) # This is the top-level region that this SCFG represents. region: RegionBlock = field(init=False, compare=False) @@ -224,7 +230,8 @@ def __iter__(self): continue # yield the name, block combo yield (name, block) - # if this is a region, recursively yield everything from that region + # If this is a region, recursively yield everything from that + # specific region. if type(block) == RegionBlock: yield from block.subregion # finally add any jump_targets to the list of names to visit @@ -284,10 +291,11 @@ def find_head(self) -> str: return next(iter(heads)) def compute_scc(self) -> List[Set[str]]: - """Computes the strongly connected components (SCC) of the current SCFG. + """Computes the strongly connected components (SCC) of the current + SCFG. - This method of SCFG computes the strongly connected components of the - graph using Tarjan's algorithm. The implementation is at the + This method of SCFG computes the stringly connected components of + the graph using Tarjan's algorithm. The implementation is at the scc function from the numba_rvsdg.networkx_vendored.scc module. It returns a list of sets, where each set represents an SCC in the graph. SCCs are useful for detecting loops in the graph. @@ -313,7 +321,9 @@ def __iter__(self): return list(scc(GraphWrap(self.graph))) - def find_headers_and_entries(self, subgraph: Set[str]) -> Tuple[Set[str], Set[str]]: + def find_headers_and_entries( + self, subgraph: Set[str] + ) -> Tuple[Set[str], Set[str]]: """Finds entries and headers in a given subgraph. Entries are blocks outside the subgraph that have an edge pointing to @@ -358,7 +368,9 @@ def find_headers_and_entries(self, subgraph: Set[str]) -> Tuple[Set[str], Set[st ) return sorted(headers), sorted(entries) - def find_exiting_and_exits(self, subgraph: Set[str]) -> Tuple[Set[str], Set[str]]: + def find_exiting_and_exits( + self, subgraph: Set[str] + ) -> Tuple[Set[str], Set[str]]: """Finds exiting and exit blocks in a given subgraph. Existing blocks are blocks inside the subgraph that have edges to @@ -484,7 +496,9 @@ def insert_block( """ # TODO: needs a diagram and documentaion # initialize new block - new_block = block_type(name=new_name, _jump_targets=successors, backedges=set()) + new_block = block_type( + name=new_name, _jump_targets=successors, backedges=set() + ) # add block to self self.add_block(new_block) # Replace any arcs from any of predecessors to any of successors with @@ -590,7 +604,9 @@ 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( + block_names.SYNTH_ASSIGN + ) variable_assignment = {} variable_assignment[branch_variable] = branch_variable_value synth_assign_block = SyntheticAssignment( @@ -609,7 +625,9 @@ def insert_block_and_control_blocks( jt[jt.index(s)] = synth_assign # finally, replace the jump_targets self.add_block( - self.graph.pop(name).replace_jump_targets(jump_targets=tuple(jt)) + self.graph.pop(name).replace_jump_targets( + jump_targets=tuple(jt) + ) ) # initialize new block, which will hold the branching table new_block = SyntheticHead( @@ -629,11 +647,17 @@ def join_returns(self): predescessors and no successors respectively. """ # for all nodes that contain a return - return_nodes = [node for node in self.graph if self.graph[node].is_exiting] + return_nodes = [ + node for node in self.graph if self.graph[node].is_exiting + ] # 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) - self.insert_SyntheticReturn(return_solo_name, return_nodes, tuple()) + return_solo_name = self.name_gen.new_block_name( + block_names.SYNTH_RETURN + ) + self.insert_SyntheticReturn( + return_solo_name, return_nodes, tuple() + ) def join_tails_and_exits(self, tails: Set[str], exits: Set[str]): """Joins the tails and exits of the SCFG. @@ -661,28 +685,37 @@ 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( + block_names.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( + block_names.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( + block_names.SYNTH_TAIL + ) + solo_exit_name = self.name_gen.new_block_name( + block_names.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 @staticmethod def bcmap_from_bytecode(bc: dis.Bytecode): - """Static method that creates a bytecode map from a `dis.Bytecode` object. + """Static method that creates a bytecode map from a `dis.Bytecode` + object. Parameters ---------- @@ -960,7 +993,10 @@ def region_view_iterator(self, head: str = None) -> Iterator[str]: # Initialise housekeeping datastructures: # A set because we only need lookup and have unique items and a deque # because we need a first in, first out (FIFO) structure. - to_visit, seen = deque([head if head else self.scfg.find_head()]), set() + to_visit, seen = ( + deque([head if head else self.scfg.find_head()]), + set(), + ) while to_visit: # get the next name on the list name = to_visit.popleft() diff --git a/numba_rvsdg/core/transformations.py b/numba_rvsdg/core/transformations.py index d7c1767..4826476 100644 --- a/numba_rvsdg/core/transformations.py +++ b/numba_rvsdg/core/transformations.py @@ -49,20 +49,26 @@ def loop_restructure_helper(scfg: SCFG, loop: Set[str]): # backedge to the loop header) we can exit early, since the condition for # SCFG is fullfilled. backedge_blocks = [ - block for block in loop if set(headers).intersection(scfg[block].jump_targets) + block + for block in loop + if set(headers).intersection(scfg[block].jump_targets) ] if ( len(backedge_blocks) == 1 and len(exiting_blocks) == 1 and backedge_blocks[0] == next(iter(exiting_blocks)) ): - scfg.add_block(scfg.graph.pop(backedge_blocks[0]).declare_backedge(loop_head)) + scfg.add_block( + scfg.graph.pop(backedge_blocks[0]).declare_backedge(loop_head) + ) return # The synthetic exiting latch and synthetic exit need to be created # based on the state of the cfg. If there are multiple exits, we need a # SyntheticExit, otherwise we only need a SyntheticExitingLatch - synth_exiting_latch = scfg.name_gen.new_block_name(block_names.SYNTH_EXIT_LATCH) + synth_exiting_latch = scfg.name_gen.new_block_name( + block_names.SYNTH_EXIT_LATCH + ) # Set a flag, this will determine the variable assignment and block # insertion later on needs_synth_exit = len(exit_blocks) > 1 @@ -82,7 +88,9 @@ def loop_restructure_helper(scfg: SCFG, loop: Set[str]): # depending on the state of the CFG and what is needed exit_value_table = {i: j for i, j in enumerate(exit_blocks)} if needs_synth_exit: - backedge_value_table = {i: j for i, j in enumerate((loop_head, synth_exit))} + backedge_value_table = { + i: j for i, j in enumerate((loop_head, synth_exit)) + } else: backedge_value_table = { i: j for i, j in enumerate((loop_head, next(iter(exit_blocks)))) @@ -131,7 +139,9 @@ def reverse_lookup(d, value): ) variable_assignment[backedge_variable] = reverse_lookup( backedge_value_table, - synth_exit if needs_synth_exit else next(iter(exit_blocks)), + synth_exit + if needs_synth_exit + else next(iter(exit_blocks)), ) # Create the actual control variable block synth_assign_block = SyntheticAssignment( @@ -172,7 +182,9 @@ def reverse_lookup(d, value): for h in headers: if h in jts: jts.remove(h) - scfg.add_block(block.replace_jump_targets(jump_targets=tuple(jts))) + scfg.add_block( + block.replace_jump_targets(jump_targets=tuple(jts)) + ) # Setup the assignment block and initialize it with the # correct jump_targets and variable assignment. synth_assign_block = SyntheticAssignment( @@ -185,9 +197,12 @@ def reverse_lookup(d, value): scfg.add_block(synth_assign_block) # Update the jump targets again, order matters new_jt[new_jt.index(jt)] = synth_assign - # finally, replace the jump_targets for this block with the new ones + # finally, replace the jump_targets for this block with the new + # ones scfg.add_block( - scfg.graph.pop(name).replace_jump_targets(jump_targets=tuple(new_jt)) + scfg.graph.pop(name).replace_jump_targets( + jump_targets=tuple(new_jt) + ) ) # Add any new blocks to the loop. loop.update(new_blocks) @@ -231,7 +246,8 @@ def restructure_loop(parent_region: RegionBlock): loops: List[Set[str]] = [ nodes for nodes in scc - if len(nodes) > 1 or next(iter(nodes)) in scfg[next(iter(nodes))].jump_targets + if len(nodes) > 1 + or next(iter(nodes)) in scfg[next(iter(nodes))].jump_targets ] _logger.debug( @@ -294,7 +310,9 @@ def _find_branch_regions(scfg: SCFG, begin: str, end: str) -> Set[str]: return branch_regions -def find_tail_blocks(scfg: SCFG, begin: Set[str], head_region_blocks, branch_regions): +def find_tail_blocks( + scfg: SCFG, begin: Set[str], head_region_blocks, branch_regions +): tail_subregion = {b for b in scfg.graph.keys()} tail_subregion.difference_update(head_region_blocks) for reg in branch_regions: @@ -314,7 +332,9 @@ def update_exiting( ): # Recursively updates the exiting blocks of a regionblock region_exiting = region_block.exiting - region_exiting_block: BasicBlock = region_block.subregion.graph.pop(region_exiting) + region_exiting_block: BasicBlock = region_block.subregion.graph.pop( + region_exiting + ) jt = list(region_exiting_block._jump_targets) for idx, s in enumerate(jt): if s is new_region_header: @@ -326,7 +346,9 @@ def update_exiting( for idx, s in enumerate(be): if s is new_region_header: be[idx] = new_region_name - region_exiting_block = region_exiting_block.replace_backedges(backedges=tuple(be)) + region_exiting_block = region_exiting_block.replace_backedges( + backedges=tuple(be) + ) if isinstance(region_exiting_block, RegionBlock): region_exiting_block = update_exiting( region_exiting_block, new_region_header, new_region_name @@ -335,7 +357,9 @@ def update_exiting( return region_block -def extract_region(scfg: SCFG, region_blocks, region_kind, parent_region: RegionBlock): +def extract_region( + scfg: SCFG, region_blocks, region_kind, parent_region: RegionBlock +): headers, entries = scfg.find_headers_and_entries(region_blocks) exiting_blocks, exit_blocks = scfg.find_exiting_and_exits(region_blocks) assert len(headers) == 1 @@ -346,7 +370,8 @@ def extract_region(scfg: SCFG, region_blocks, region_kind, parent_region: Region # Generate a new region name region_name = scfg.name_gen.new_region_name(region_kind) head_subgraph = SCFG( - {name: scfg.graph[name] for name in region_blocks}, name_gen=scfg.name_gen + {name: scfg.graph[name] for name in region_blocks}, + name_gen=scfg.name_gen, ) # For all entries, replace the header as a jump target @@ -451,7 +476,9 @@ def restructure_branch(parent_region: RegionBlock): if inner_nodes: # Insert SyntheticTail exiting_blocks, _ = scfg.find_exiting_and_exits(inner_nodes) - tail_headers, _ = scfg.find_headers_and_entries(tail_region_blocks) + tail_headers, _ = scfg.find_headers_and_entries( + tail_region_blocks + ) _, _ = scfg.join_tails_and_exits(exiting_blocks, tail_headers) else: @@ -577,7 +604,9 @@ def _find_dominators_internal(entries, nodes, preds_table, succs_table): import functools if not entries: - raise RuntimeError("no entry points: dominator algorithm " "cannot be seeded") + raise RuntimeError( + "no entry points: dominator algorithm " "cannot be seeded" + ) doms = {} for e in entries: @@ -596,7 +625,9 @@ def _find_dominators_internal(entries, nodes, preds_table, succs_table): new_doms = {n} preds = preds_table[n] if preds: - new_doms |= functools.reduce(set.intersection, [doms[p] for p in preds]) + new_doms |= functools.reduce( + set.intersection, [doms[p] for p in preds] + ) if new_doms != doms[n]: assert len(new_doms) < len(doms[n]) doms[n] = new_doms diff --git a/numba_rvsdg/networkx_vendored/scc.py b/numba_rvsdg/networkx_vendored/scc.py index 15e849c..1352e4a 100644 --- a/numba_rvsdg/networkx_vendored/scc.py +++ b/numba_rvsdg/networkx_vendored/scc.py @@ -1,5 +1,5 @@ """ -SCC from https://github.com/networkx/networkx/blob/41a760273eed666e6f966cf14ea524dec56d678b/networkx/algorithms/components/strongly_connected.py#L16 +SCC from https://github.com/networkx/networkx/blob/41a760273eed666e6f966cf14ea524dec56d678b/networkx/algorithms/components/strongly_connected.py#L16 # noqa LICENSE: https://github.com/networkx/networkx/blob/main/LICENSE.txt @@ -37,7 +37,9 @@ def scc(G): queue.pop() if lowlink[v] == preorder[v]: scc = {v} - while scc_queue and preorder[scc_queue[-1]] > preorder[v]: + while ( + scc_queue and preorder[scc_queue[-1]] > preorder[v] + ): k = scc_queue.pop() scc.add(k) scc_found.update(scc) diff --git a/numba_rvsdg/rendering/rendering.py b/numba_rvsdg/rendering/rendering.py index 29cec8b..ca008a2 100644 --- a/numba_rvsdg/rendering/rendering.py +++ b/numba_rvsdg/rendering/rendering.py @@ -4,12 +4,8 @@ RegionBlock, PythonBytecodeBlock, SyntheticAssignment, - SyntheticExitingLatch, - SyntheticExitBranch, SyntheticBranch, SyntheticBlock, - SyntheticHead, - SyntheticExit, ) from numba_rvsdg.core.datastructures.scfg import SCFG from numba_rvsdg.core.datastructures.byte_flow import ByteFlow @@ -21,12 +17,15 @@ class BaseRenderer: """Base Renderer class. This is the base class for all types of graph renderers. It defines two - methods `render_block` and `render_edges` that define how the blocks and edges - of the graph are rendered respectively. + methods `render_block` and `render_edges` that define how the blocks and + edges of the graph are rendered respectively. """ - def render_block(self, digraph: "Digraph", name: str, block: BasicBlock): - """Function that defines how the BasicBlcoks in a graph should be rendered. + def render_block( + self, digraph: "Digraph", name: str, block: BasicBlock # noqa + ): + """Function that defines how the BasicBlcoks in a graph should be + rendered. Parameters ---------- @@ -115,7 +114,7 @@ def __init__(self): self.g = Digraph() def render_region_block( - self, digraph: "Digraph", name: str, regionblock: RegionBlock + self, digraph: "Digraph", name: str, regionblock: RegionBlock # noqa ): # render subgraph with digraph.subgraph(name=f"cluster_{name}") as subg: @@ -130,7 +129,9 @@ def render_region_block( for name, block in regionblock.subregion.graph.items(): self.render_block(subg, name, block) - def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): + def render_basic_block( + self, digraph: "Digraph", name: str, block: BasicBlock # noqa + ): if name.startswith("python_bytecode"): instlist = block.get_instructions(self.bcmap) body = name + r"\l" @@ -143,7 +144,7 @@ def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): digraph.node(str(name), shape="rect", label=body) def render_control_variable_block( - self, digraph: "Digraph", name: str, block: BasicBlock + self, digraph: "Digraph", name: str, block: BasicBlock # noqa ): if isinstance(name, str): body = name + r"\l" @@ -154,7 +155,9 @@ def render_control_variable_block( raise Exception("Unknown name type: " + name) digraph.node(str(name), shape="rect", label=body) - def render_branching_block(self, digraph: "Digraph", name: str, block: BasicBlock): + def render_branching_block( + self, digraph: "Digraph", name: str, block: BasicBlock # noqa + ): if isinstance(name, str): body = name + r"\l" body += rf"variable: {block.variable}\l" @@ -201,7 +204,7 @@ def __init__(self, scfg: SCFG): self.render_edges(scfg) def render_region_block( - self, digraph: "Digraph", name: str, regionblock: RegionBlock + self, digraph: "Digraph", name: str, regionblock: RegionBlock # noqa ): # render subgraph with digraph.subgraph(name=f"cluster_{name}") as subg: @@ -224,7 +227,9 @@ def render_region_block( for name, block in regionblock.subregion.graph.items(): self.render_block(subg, name, block) - def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): + def render_basic_block( + self, digraph: "Digraph", name: str, block: BasicBlock # noqa + ): body = ( name + r"\l" @@ -237,7 +242,7 @@ def render_basic_block(self, digraph: "Digraph", name: str, block: BasicBlock): digraph.node(str(name), shape="rect", label=body) def render_control_variable_block( - self, digraph: "Digraph", name: str, block: BasicBlock + self, digraph: "Digraph", name: str, block: BasicBlock # noqa ): if isinstance(name, str): body = name + r"\l" @@ -255,7 +260,9 @@ def render_control_variable_block( raise Exception("Unknown name type: " + name) digraph.node(str(name), shape="rect", label=body) - def render_branching_block(self, digraph: "Digraph", name: str, block: BasicBlock): + def render_branching_block( + self, digraph: "Digraph", name: str, block: BasicBlock # noqa + ): if isinstance(name, str): body = name + r"\l" body += rf"variable: {block.variable}\l" @@ -274,8 +281,8 @@ def render_branching_block(self, digraph: "Digraph", name: str, block: BasicBloc digraph.node(str(name), shape="rect", label=body) def view(self, name: str): - """Method used to view the current SCFG as an external graphviz generated - PDF file. + """Method used to view the current SCFG as an external graphviz + generated PDF file. Parameters ---------- @@ -302,13 +309,19 @@ def render_func(func): def render_flow(flow): - """Renders multiple ByteFlow representations across various SCFG transformations. - - The `render_flow`` function takes a `flow` parameter as the `ByteFlow` to be transformed and rendered and performs the following operations: - - Renders the pure `ByteFlow` representation of the function using `ByteFlowRenderer` and displays it as a document named "before". - - Joins the return blocks in the `ByteFlow` object graph and renders the graph, displaying it as a document named "closed". - - Restructures the loops recursively in the `ByteFlow` object graph and renders the graph, displaying it as named "loop restructured". - - Restructures the branch recursively in the `ByteFlow` object graph and renders the graph, displaying it as named "branch restructured". + """Renders multiple ByteFlow representations across various SCFG + transformations. + + The `render_flow`` function takes a `flow` parameter as the `ByteFlow` + to be transformed and rendered and performs the following operations: + - Renders the pure `ByteFlow` representation of the function using + `ByteFlowRenderer` and displays it as a document named "before". + - Joins the return blocks in the `ByteFlow` object graph and renders + the graph, displaying it as a document named "closed". + - Restructures the loops recursively in the `ByteFlow` object graph + and renders the graph, displaying it as named "loop restructured". + - Restructures the branch recursively in the `ByteFlow` object graph + and renders the graph, displaying it as named "branch restructured". Parameters ---------- diff --git a/numba_rvsdg/tests/mock_asm.py b/numba_rvsdg/tests/mock_asm.py index 286a25d..76c0c92 100644 --- a/numba_rvsdg/tests/mock_asm.py +++ b/numba_rvsdg/tests/mock_asm.py @@ -128,10 +128,11 @@ class VM: - program counter (PC) - an output buffer for printing; - a LAST_CTR register to store the last accessed counter. - - a table that maps PC location of `ctr` instruction to the counter value + - a table that maps PC location of `ctr` instruction to the counter + value - The VM do not have another other input source beside the instruction stream. - Therefore, a program behavior is known statically. + The VM do not have another other input source beside the instruction + stream. Therefore, a program behavior is known statically. """ diff --git a/numba_rvsdg/tests/simulator.py b/numba_rvsdg/tests/simulator.py index 1b2d4c7..984c3fd 100644 --- a/numba_rvsdg/tests/simulator.py +++ b/numba_rvsdg/tests/simulator.py @@ -2,7 +2,6 @@ from dis import Instruction from numba_rvsdg.core.datastructures.byte_flow import ByteFlow from numba_rvsdg.core.datastructures.basic_block import ( - BasicBlock, PythonBytecodeBlock, RegionBlock, SyntheticBlock, @@ -151,16 +150,17 @@ def run_RegionBlock(self, name: str): """Run region. Execute all BasicBlocks in this region. Stay within the region, only - return the action when we jump out of the region or when we return from - within the region. + return the action when we jump out of the region or when we return + from within the region. Special attention is directed at the use of the `region_stack` here. - Since the blocks for the subregion are stored in the `region.subregion` - graph, we need to use a region aware `get_blocks` in methods such as - `run_BasicBlock` so that we get the correct `BasicBlock`. The net effect - of placing the `region` onto the `region_stack` is that `run_BasicBlock` - will be able to fetch the correct name from the `region.subregion` - graph, and thus be able to run the correct sequence of blocks. + Since the blocks for the subregion are stored in the + `region.subregion` graph, we need to use a region aware + `get_blocks` in methods such as `run_BasicBlock` so that we get + the correct `BasicBlock`. The net effect of placing the `region` + onto the `region_stack` is that `run_BasicBlock` will be able to + fetch the correct name from the `region.subregion` graph, and thus + be able to run the correct sequence of blocks. Parameters ---------- @@ -248,12 +248,14 @@ def run_inst(self, inst: Instruction): print(f"variable map after: {self.varmap}") print(f"stack after: {self.stack}") - ### Synthetic Instructions ### + ### Synthetic Instructions ### # noqa def synth_SyntheticAssignment(self, control_name, block): self.ctrl_varmap.update(block.variable_assignment) def _synth_branch(self, control_name, block): - jump_target = block.branch_value_table[self.ctrl_varmap[block.variable]] + jump_target = block.branch_value_table[ + self.ctrl_varmap[block.variable] + ] self.branch = bool(block._jump_targets.index(jump_target)) def synth_SyntheticExitingLatch(self, control_name, block): @@ -283,7 +285,7 @@ def synth_SyntheticBlock(self, control_name, block): def synth_SyntheticBranch(self, control_name, block): raise NotImplementedError("SyntheticBranch should not be instantiated") - ### Bytecode Instructions ### + ### Bytecode Instructions ### # noqa def op_LOAD_CONST(self, inst): self.stack.append(inst.argval) @@ -374,16 +376,10 @@ def op_RESUME(self, inst): def op_PRECALL(self, inst): pass - def op_CALL_FUNCTION(self, inst): - args = [self.stack.pop() for _ in range(inst.argval)][::-1] - fn = self.stack.pop() - res = fn(*args) - self.stack.append(res) - def op_CALL(self, inst): args = [self.stack.pop() for _ in range(inst.argval)][::-1] first, second = self.stack.pop(), self.stack.pop() - if first == None: + if first is None: func = second else: raise NotImplementedError diff --git a/numba_rvsdg/tests/test_byteflow.py b/numba_rvsdg/tests/test_byteflow.py index 7cf8ca5..e8b046d 100644 --- a/numba_rvsdg/tests/test_byteflow.py +++ b/numba_rvsdg/tests/test_byteflow.py @@ -14,7 +14,8 @@ def fun(): bytecode = Bytecode(fun) -# If the function definition line changes, just change the variable below, rest of it will adjust as long as function remains the same +# If the function definition line changes, just change the variable below, +# rest of it will adjust as long as function remains the same func_def_line = 10 @@ -130,7 +131,9 @@ def test_is_jump_target(self): name=name_gen.new_block_name(block_names.PYTHON_BYTECODE), begin=0, end=8, - _jump_targets=(name_gen.new_block_name(block_names.PYTHON_BYTECODE),), + _jump_targets=( + name_gen.new_block_name(block_names.PYTHON_BYTECODE), + ), backedges=(), ) self.assertEqual(block.jump_targets, ("python_bytecode_block_1",)) @@ -223,7 +226,9 @@ def test_constructor(self): self.assertEqual(len(flowinfo.jump_insts), 0) def test_from_bytecode(self): - expected = FlowInfo(block_offsets={0}, jump_insts={8: ()}, last_offset=8) + expected = FlowInfo( + block_offsets={0}, jump_insts={8: ()}, last_offset=8 + ) received = FlowInfo.from_bytecode(bytecode) self.assertEqual(expected, received) diff --git a/numba_rvsdg/tests/test_scc.py b/numba_rvsdg/tests/test_scc.py index 81a9352..b3ba2d7 100644 --- a/numba_rvsdg/tests/test_scc.py +++ b/numba_rvsdg/tests/test_scc.py @@ -34,7 +34,9 @@ def scc(G): queue.pop() if lowlink[v] == preorder[v]: scc = {v} - while scc_queue and preorder[scc_queue[-1]] > preorder[v]: + while ( + scc_queue and preorder[scc_queue[-1]] > preorder[v] + ): k = scc_queue.pop() scc.add(k) scc_found.update(scc) diff --git a/numba_rvsdg/tests/test_scfg.py b/numba_rvsdg/tests/test_scfg.py index 8a2d6d8..2bb3e3b 100644 --- a/numba_rvsdg/tests/test_scfg.py +++ b/numba_rvsdg/tests/test_scfg.py @@ -135,7 +135,10 @@ def test_concealed_region_view_iter(self): ("python_bytecode_block_3", PythonBytecodeBlock), ] received = list( - ((k, type(v)) for k, v in restructured.scfg.concealed_region_view.items()) + ( + (k, type(v)) + for k, v in restructured.scfg.concealed_region_view.items() + ) ) self.assertEqual(expected, received) diff --git a/numba_rvsdg/tests/test_transforms.py b/numba_rvsdg/tests/test_transforms.py index 8ba3ed7..096b6ca 100644 --- a/numba_rvsdg/tests/test_transforms.py +++ b/numba_rvsdg/tests/test_transforms.py @@ -54,7 +54,10 @@ def test_dual_predecessor(self): expected_scfg, expected_block_dict = SCFG.from_yaml(expected) new_name = original_scfg.name_gen.new_block_name(block_names.BASIC) original_scfg.insert_block( - new_name, (block_dict["0"], block_dict["1"]), (block_dict["2"],), BasicBlock + new_name, + (block_dict["0"], block_dict["1"]), + (block_dict["2"],), + BasicBlock, ) self.assertSCFGEqual( expected_scfg, @@ -498,7 +501,9 @@ def test_no_op(self): """ original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) - loop_restructure_helper(original_scfg, set({block_dict["1"], block_dict["2"]})) + loop_restructure_helper( + original_scfg, set({block_dict["1"], block_dict["2"]}) + ) self.assertSCFGEqual(expected_scfg, original_scfg) def test_backedge_not_exiting(self): @@ -535,7 +540,9 @@ def test_backedge_not_exiting(self): """ original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) - loop_restructure_helper(original_scfg, set({block_dict["1"], block_dict["2"]})) + loop_restructure_helper( + original_scfg, set({block_dict["1"], block_dict["2"]}) + ) self.assertSCFGEqual(expected_scfg, original_scfg) def test_multi_back_edge_with_backedge_from_header(self): @@ -570,7 +577,9 @@ def test_multi_back_edge_with_backedge_from_header(self): """ original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) - loop_restructure_helper(original_scfg, set({block_dict["1"], block_dict["2"]})) + loop_restructure_helper( + original_scfg, set({block_dict["1"], block_dict["2"]}) + ) self.assertSCFGEqual(expected_scfg, original_scfg) def test_double_exit(self): @@ -615,7 +624,8 @@ def test_double_exit(self): original_scfg, block_dict = SCFG.from_yaml(original) expected_scfg, _ = SCFG.from_yaml(expected) loop_restructure_helper( - original_scfg, set({block_dict["1"], block_dict["2"], block_dict["3"]}) + original_scfg, + set({block_dict["1"], block_dict["2"], block_dict["3"]}), ) self.assertSCFGEqual(expected_scfg, original_scfg) @@ -669,7 +679,14 @@ def test_double_header(self): expected_scfg, _ = SCFG.from_yaml(expected) loop_restructure_helper( original_scfg, - set({block_dict["1"], block_dict["2"], block_dict["3"], block_dict["4"]}), + set( + { + block_dict["1"], + block_dict["2"], + block_dict["3"], + block_dict["4"], + } + ), ) self.assertSCFGEqual(expected_scfg, original_scfg) @@ -739,7 +756,14 @@ def test_double_header_double_exiting(self): expected_scfg, _ = SCFG.from_yaml(expected) loop_restructure_helper( original_scfg, - set({block_dict["1"], block_dict["2"], block_dict["3"], block_dict["4"]}), + set( + { + block_dict["1"], + block_dict["2"], + block_dict["3"], + block_dict["4"], + } + ), ) self.assertSCFGEqual(expected_scfg, original_scfg) diff --git a/numba_rvsdg/tests/test_utils.py b/numba_rvsdg/tests/test_utils.py index 937330a..f1a9c4f 100644 --- a/numba_rvsdg/tests/test_utils.py +++ b/numba_rvsdg/tests/test_utils.py @@ -6,7 +6,9 @@ class SCFGComparator(TestCase): - def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): + def assertSCFGEqual( + self, first_scfg: SCFG, second_scfg: SCFG, head_map=None + ): if head_map: # If more than one head the corresponding map needs to be provided block_mapping = head_map @@ -29,7 +31,8 @@ def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): continue seen.add(node_name) node: BasicBlock = first_scfg[node_name] - # Assert that there's a corresponding mapping of current node in second scfg + # Assert that there's a corresponding mapping of current node + # in second scfg assert node_name in block_mapping.keys() # Get the corresponding node in second graph second_node_name = block_mapping[node_name] @@ -38,9 +41,9 @@ def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): assert len(node.jump_targets) == len(second_node.jump_targets) assert len(node.backedges) == len(second_node.backedges) - # Add the jump targets as corresponding nodes in block mapping dictionary - # Since order must be same we can simply add zip fucntionality as the - # correspondence function for nodes + # Add the jump targets as corresponding nodes in block mapping + # dictionary. Since order must be same we can simply add zip + # functionality as the correspondence function for nodes for jt1, jt2 in zip(node.jump_targets, second_node.jump_targets): block_mapping[jt1] = jt2 stack.append(jt1) @@ -49,12 +52,16 @@ def assertSCFGEqual(self, first_scfg: SCFG, second_scfg: SCFG, head_map=None): block_mapping[be1] = be2 stack.append(be1) - def assertYAMLEqual(self, first_yaml: SCFG, second_yaml: SCFG, head_map: dict): + def assertYAMLEqual( + self, first_yaml: SCFG, second_yaml: SCFG, head_map: dict + ): self.assertDictEqual( yaml.safe_load(first_yaml), yaml.safe_load(second_yaml), head_map ) - def assertDictEqual(self, first_yaml: str, second_yaml: str, head_map: dict): + def assertDictEqual( + self, first_yaml: str, second_yaml: str, head_map: dict + ): block_mapping = head_map stack = list(block_mapping.keys()) # Assert number of blocks are equal in both SCFGs @@ -69,7 +76,8 @@ def assertDictEqual(self, first_yaml: str, second_yaml: str, head_map: dict): continue seen.add(node_name) node: BasicBlock = first_yaml[node_name] - # Assert that there's a corresponding mapping of current node in second scfg + # Assert that there's a corresponding mapping of current node + # in second scfg assert node_name in block_mapping.keys() # Get the corresponding node in second graph second_node_name = block_mapping[node_name] @@ -79,9 +87,9 @@ def assertDictEqual(self, first_yaml: str, second_yaml: str, head_map: dict): if "be" in node.keys(): assert len(node["be"]) == len(second_node["be"]) - # Add the jump targets as corresponding nodes in block mapping dictionary - # Since order must be same we can simply add zip fucntionality as the - # correspondence function for nodes + # Add the jump targets as corresponding nodes in block mapping + # dictionary. Since order must be same we can simply add zip + # functionality as the correspondence function for nodes for jt1, jt2 in zip(node["jt"], second_node["jt"]): block_mapping[jt1] = jt2 stack.append(jt1) From aeccffa91e98651021f55902e458f680e1ca3649 Mon Sep 17 00:00:00 2001 From: kc611 Date: Thu, 22 Jun 2023 14:41:24 +0530 Subject: [PATCH 6/8] Fixed test failures --- numba_rvsdg/rendering/rendering.py | 4 ++++ numba_rvsdg/tests/test_byteflow.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/numba_rvsdg/rendering/rendering.py b/numba_rvsdg/rendering/rendering.py index ca008a2..6dafd70 100644 --- a/numba_rvsdg/rendering/rendering.py +++ b/numba_rvsdg/rendering/rendering.py @@ -314,12 +314,16 @@ def render_flow(flow): The `render_flow`` function takes a `flow` parameter as the `ByteFlow` to be transformed and rendered and performs the following operations: + - Renders the pure `ByteFlow` representation of the function using `ByteFlowRenderer` and displays it as a document named "before". + - Joins the return blocks in the `ByteFlow` object graph and renders the graph, displaying it as a document named "closed". + - Restructures the loops recursively in the `ByteFlow` object graph and renders the graph, displaying it as named "loop restructured". + - Restructures the branch recursively in the `ByteFlow` object graph and renders the graph, displaying it as named "branch restructured". diff --git a/numba_rvsdg/tests/test_byteflow.py b/numba_rvsdg/tests/test_byteflow.py index e8b046d..a92db24 100644 --- a/numba_rvsdg/tests/test_byteflow.py +++ b/numba_rvsdg/tests/test_byteflow.py @@ -16,7 +16,7 @@ def fun(): bytecode = Bytecode(fun) # If the function definition line changes, just change the variable below, # rest of it will adjust as long as function remains the same -func_def_line = 10 +func_def_line = 11 class TestBCMapFromBytecode(unittest.TestCase): From 30e3c2be9941f689b09b0decde73d5ad8c8a5c32 Mon Sep 17 00:00:00 2001 From: Kaustubh Date: Thu, 22 Jun 2023 15:39:03 +0530 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: esc --- numba_rvsdg/core/datastructures/basic_block.py | 2 +- numba_rvsdg/core/datastructures/scfg.py | 2 +- numba_rvsdg/rendering/rendering.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/numba_rvsdg/core/datastructures/basic_block.py b/numba_rvsdg/core/datastructures/basic_block.py index 34f3ab2..263e09c 100644 --- a/numba_rvsdg/core/datastructures/basic_block.py +++ b/numba_rvsdg/core/datastructures/basic_block.py @@ -326,7 +326,7 @@ class SyntheticHead(SyntheticBranch): @dataclass(frozen=True) class SyntheticExitingLatch(SyntheticBranch): """The SyntheticExitingLatch class represents a artificially added - exiting latchc block in a structured control flow graph (SCFG). + exiting latch block in a structured control flow graph (SCFG). """ diff --git a/numba_rvsdg/core/datastructures/scfg.py b/numba_rvsdg/core/datastructures/scfg.py index 294b314..16f9607 100644 --- a/numba_rvsdg/core/datastructures/scfg.py +++ b/numba_rvsdg/core/datastructures/scfg.py @@ -294,7 +294,7 @@ def compute_scc(self) -> List[Set[str]]: """Computes the strongly connected components (SCC) of the current SCFG. - This method of SCFG computes the stringly connected components of + This method of SCFG computes the strongly connected components of the graph using Tarjan's algorithm. The implementation is at the scc function from the numba_rvsdg.networkx_vendored.scc module. It returns a list of sets, where each set represents an SCC in diff --git a/numba_rvsdg/rendering/rendering.py b/numba_rvsdg/rendering/rendering.py index 6dafd70..d0671c8 100644 --- a/numba_rvsdg/rendering/rendering.py +++ b/numba_rvsdg/rendering/rendering.py @@ -24,7 +24,7 @@ class BaseRenderer: def render_block( self, digraph: "Digraph", name: str, block: BasicBlock # noqa ): - """Function that defines how the BasicBlcoks in a graph should be + """Function that defines how the BasicBlocks in a graph should be rendered. Parameters From 0da13a08d8434ac00e4f88cab319affae6e85758 Mon Sep 17 00:00:00 2001 From: kc611 Date: Thu, 22 Jun 2023 15:41:35 +0530 Subject: [PATCH 8/8] Removed unused variables --- numba_rvsdg/core/transformations.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/numba_rvsdg/core/transformations.py b/numba_rvsdg/core/transformations.py index 4826476..65b0ffb 100644 --- a/numba_rvsdg/core/transformations.py +++ b/numba_rvsdg/core/transformations.py @@ -279,9 +279,6 @@ def find_head_blocks(scfg: SCFG, begin: str) -> Set[str]: def find_branch_regions(scfg: SCFG, begin: str, end: str) -> Set[str]: # identify branch regions doms = _doms(scfg) - postdoms = _post_doms(scfg) - _imm_doms(postdoms) - _imm_doms(doms) branch_regions = [] jump_targets = scfg.graph[begin].jump_targets for bra_start in jump_targets: