From fb57655ab12c87cc79ac769db369e66edfea2c87 Mon Sep 17 00:00:00 2001 From: Dalia Shaaban <144673861+dshaaban01@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:24:13 +0000 Subject: [PATCH] interactive: condense feature added and condense_pass list calculated dynamically! (3/7) (#1793) Condense feature has been added. It is computed based on the current_module (i.e. the output) and is recomputed every time a pass is applied (i.e. the dynamic update we discussed). --- tests/interactive/test_app.py | 72 ++++++++++++++++++++++++++- xdsl/interactive/app.py | 93 ++++++++++++++++++++++++++++------- xdsl/interactive/app.tcss | 2 +- 3 files changed, 148 insertions(+), 19 deletions(-) diff --git a/tests/interactive/test_app.py b/tests/interactive/test_app.py index 57dd77fce2..2bcc9ba009 100644 --- a/tests/interactive/test_app.py +++ b/tests/interactive/test_app.py @@ -2,7 +2,10 @@ import pytest -from xdsl.backend.riscv.lowering import convert_func_to_riscv_func +from xdsl.backend.riscv.lowering import ( + convert_arith_to_riscv, + convert_func_to_riscv_func, +) from xdsl.builder import ImplicitBuilder from xdsl.dialects import arith, func, riscv, riscv_func from xdsl.dialects.builtin import ( @@ -13,6 +16,14 @@ ) from xdsl.interactive.app import InputApp from xdsl.ir import Block, Region +from xdsl.transforms import ( + mlir_opt, + printf_to_llvm, +) +from xdsl.transforms.experimental import ( + hls_convert_stencil_to_ll_mlir, +) +from xdsl.transforms.experimental.dmp import stencil_global_to_local from xdsl.utils.exceptions import ParseError @@ -136,6 +147,47 @@ async def test_buttons(): """ ) + await pilot.pause() + # assert that the Input and Output Text Area's have changed + assert ( + app.input_text_area.text + == """ + func.func @hello(%n : index) -> index { + %two = arith.constant 2 : index + %res = arith.muli %n, %two : index + func.return %res : index + } + """ + ) + assert ( + app.output_text_area.text + == """builtin.module { + func.func @hello(%n : index) -> index { + %two = arith.constant 2 : index + %res = arith.muli %n, %two : index + func.return %res : index + } +} +""" + ) + + # Test clicking the "clear input" button + await pilot.click("#clear_input_button") + + # assert that the input text area has been cleared + await pilot.pause() + assert app.input_text_area.text == "" + + app.input_text_area.insert( + """ + func.func @hello(%n : index) -> index { + %two = arith.constant 2 : index + %res = arith.muli %n, %two : index + func.return %res : index + } + """ + ) + # Select a pass app.pass_pipeline = tuple( (*app.pass_pipeline, convert_func_to_riscv_func.ConvertFuncToRiscvFuncPass) @@ -194,6 +246,24 @@ async def test_buttons(): assert isinstance(app.current_module, ModuleOp) assert app.current_module.is_structurally_equivalent(expected_module) + # press "Condense" button + await pilot.click("#condense_button") + + condensed_list = tuple( + ( + convert_arith_to_riscv.ConvertArithToRiscvPass, + convert_func_to_riscv_func.ConvertFuncToRiscvFuncPass, + stencil_global_to_local.DistributeStencilPass, + hls_convert_stencil_to_ll_mlir.HLSConvertStencilToLLMLIRPass, + mlir_opt.MLIROptPass, + printf_to_llvm.PrintfToLLVM, + ) + ) + + await pilot.pause() + assert app.condense_mode is True + assert app.available_pass_list == condensed_list + @pytest.mark.asyncio() async def test_passes(): diff --git a/xdsl/interactive/app.py b/xdsl/interactive/app.py index 7feec7592f..e30ea308c5 100644 --- a/xdsl/interactive/app.py +++ b/xdsl/interactive/app.py @@ -15,6 +15,7 @@ from textual.reactive import reactive from textual.widgets import Button, Footer, Label, ListItem, ListView, TextArea +from xdsl.dialects import builtin from xdsl.dialects.builtin import ModuleOp from xdsl.ir import MLContext from xdsl.parser import Parser @@ -28,6 +29,30 @@ """Contains the list of xDSL passes.""" +def condensed_pass_list(input: builtin.ModuleOp) -> tuple[type[ModulePass], ...]: + """Returns a tuple of passes (pass name and pass instance) that modify the IR.""" + + ctx = MLContext(True) + + for dialect in get_all_dialects(): + ctx.load_dialect(dialect) + + selections: tuple[type[ModulePass], ...] = () + for _, value in ALL_PASSES: + try: + cloned_module = input.clone() + cloned_ctx = ctx.clone() + value().apply(cloned_ctx, cloned_module) + + if not input.is_structurally_equivalent(cloned_module): + rhs = (*selections, value) + selections = tuple(rhs) + except Exception: + selections = tuple((*selections, value)) + + return selections + + class OutputTextArea(TextArea): """Used to prevent users from being able to alter the Output TextArea.""" @@ -55,6 +80,11 @@ class InputApp(App[None]): pass_pipeline = reactive(tuple[type[ModulePass], ...]) """Reactive variable that saves the list of selected passes.""" + condense_mode = reactive(False, always_update=True) + """Reactive boolean.""" + available_pass_list = reactive(tuple[type[ModulePass], ...]) + """Reactive variable that saves the list of passes that have an effect on current_module.""" + input_text_area: TextArea """Input TextArea.""" output_text_area: OutputTextArea @@ -83,6 +113,7 @@ def compose(self) -> ComposeResult: with Vertical(id="buttons"): yield Button("Copy Query", id="copy_query_button") yield Button("Clear Passes", id="clear_passes_button") + yield Button("Condense", id="condense_button") with ScrollableContainer(id="selected_passes"): yield self.selected_query_label with Horizontal(id="bottom_container"): @@ -94,6 +125,47 @@ def compose(self) -> ComposeResult: yield Button("Copy Output", id="copy_output_button") yield Footer() + def on_mount(self) -> None: + """Configure widgets in this application before it is first shown.""" + + # register's the theme for the Input/Output TextArea's + self.input_text_area.theme = "vscode_dark" + self.output_text_area.theme = "vscode_dark" + + self.query_one("#input_container").border_title = "Input xDSL IR" + self.query_one("#output_container").border_title = "Output xDSL IR" + self.query_one( + "#passes_list_view" + ).border_title = "Choose a pass or multiple passes to be applied." + self.query_one("#selected_passes").border_title = "Selected passes/query" + + for n, _ in ALL_PASSES: + self.passes_list_view.append(ListItem(Label(n), name=n)) + + def compute_available_pass_list(self) -> tuple[type[ModulePass], ...]: + match self.current_module: + case None: + return tuple(p for _, p in ALL_PASSES) + case Exception(): + return () + case ModuleOp(): + if self.condense_mode: + return condensed_pass_list(self.current_module) + else: + return tuple(p for _, p in ALL_PASSES) + + def watch_available_pass_list( + self, + old_pass_list: tuple[type[ModulePass], ...], + new_pass_list: tuple[type[ModulePass], ...], + ) -> None: + if old_pass_list != new_pass_list: + self.passes_list_view.clear() + for value in new_pass_list: + self.passes_list_view.append( + ListItem(Label(value.name), name=value.name) + ) + @on(ListView.Selected) def update_pass_pipeline(self, event: ListView.Selected) -> None: """ @@ -155,23 +227,6 @@ def watch_current_module(self): self.output_text_area.load_text(output_text) - def on_mount(self) -> None: - """Configure widgets in this application before it is first shown.""" - - # register's the theme for the Input/Output TextArea's - self.input_text_area.theme = "vscode_dark" - self.output_text_area.theme = "vscode_dark" - - self.query_one("#input_container").border_title = "Input xDSL IR" - self.query_one("#output_container").border_title = "Output xDSL IR" - self.query_one( - "#passes_list_view" - ).border_title = "Choose a pass or multiple passes to be applied." - self.query_one("#selected_passes").border_title = "Selected passes/query" - - for n, _ in ALL_PASSES: - self.passes_list_view.append(ListItem(Label(n), name=n)) - def action_toggle_dark(self) -> None: """An action to toggle dark mode.""" self.dark = not self.dark @@ -202,6 +257,10 @@ def clear_passes(self, event: Button.Pressed) -> None: """Selected passes cleared when "Clear Passes" button is pressed.""" self.pass_pipeline = () + @on(Button.Pressed, "#condense_button") + def condense(self, event: Button.Pressed) -> None: + self.condense_mode = True + def main(): return InputApp().run() diff --git a/xdsl/interactive/app.tcss b/xdsl/interactive/app.tcss index 6fb3bc74bc..c4a382ec2d 100644 --- a/xdsl/interactive/app.tcss +++ b/xdsl/interactive/app.tcss @@ -7,7 +7,7 @@ height: 100%; } -# Vertical(Button, Button) +# Vertical(Button, Button, Button) #buttons{ layout: vertical; width: auto;