diff --git a/tests/filecheck/dialects/arm/test_ops.mlir b/tests/filecheck/dialects/arm/test_ops.mlir index 6c9f27d945..4ce5afbf2b 100644 --- a/tests/filecheck/dialects/arm/test_ops.mlir +++ b/tests/filecheck/dialects/arm/test_ops.mlir @@ -1,12 +1,14 @@ // RUN: XDSL_ROUNDTRIP // RUN: XDSL_GENERIC_ROUNDTRIP +// RUN: xdsl-opt -t arm-asm %s | filecheck %s --check-prefix=CHECK-ASM // CHECK: %x1 = arm.get_register : !arm.reg %x1 = arm.get_register : !arm.reg -// CHECK: %ds_mov = arm.ds.mov %x1 : (!arm.reg) -> !arm.reg -%ds_mov = arm.ds.mov %x1 : (!arm.reg) -> !arm.reg +// CHECK: %ds_mov = arm.ds.mov %x1 {"comment" = "move contents of s to d"} : (!arm.reg) -> !arm.reg +// CHECK-ASM: mov x2, x1 # move contents of s to d +%ds_mov = arm.ds.mov %x1 {"comment" = "move contents of s to d"} : (!arm.reg) -> !arm.reg // CHECK-GENERIC: %x1 = "arm.get_register"() : () -> !arm.reg -// CHECK-GENERIC: %ds_mov = "arm.ds.mov"(%x1) : (!arm.reg) -> !arm.reg +// CHECK-GENERIC: %ds_mov = "arm.ds.mov"(%x1) {"comment" = "move contents of s to d"} : (!arm.reg) -> !arm.reg diff --git a/xdsl/dialects/arm/__init__.py b/xdsl/dialects/arm/__init__.py index 3934c4510a..0c6bfa6cba 100644 --- a/xdsl/dialects/arm/__init__.py +++ b/xdsl/dialects/arm/__init__.py @@ -3,11 +3,23 @@ https://developer.arm.com/documentation/102374/0101/Overview """ +from typing import IO + +from xdsl.dialects.builtin import ModuleOp from xdsl.ir import Dialect -from .ops import DSMovOp, GetRegisterOp +from .ops import ARMOperation, DSMovOp, GetRegisterOp from .register import IntRegisterType + +def print_assembly(module: ModuleOp, output: IO[str]) -> None: + for op in module.body.walk(): + assert isinstance(op, ARMOperation), f"{op}" + asm = op.assembly_line() + if asm is not None: + print(asm, file=output) + + ARM = Dialect( "arm", [ diff --git a/xdsl/dialects/arm/assembly.py b/xdsl/dialects/arm/assembly.py new file mode 100644 index 0000000000..f38c901089 --- /dev/null +++ b/xdsl/dialects/arm/assembly.py @@ -0,0 +1,38 @@ +from typing import TypeAlias + +from xdsl.dialects.arm.register import ARMRegisterType +from xdsl.dialects.builtin import StringAttr +from xdsl.ir import SSAValue + +AssemblyInstructionArg: TypeAlias = SSAValue + + +def append_comment(line: str, comment: StringAttr | None) -> str: + if comment is None: + return line + + padding = " " * max(0, 48 - len(line)) + + return f"{line}{padding} # {comment.data}" + + +def assembly_arg_str(arg: AssemblyInstructionArg) -> str: + if isinstance(arg.type, ARMRegisterType): + reg = arg.type.register_name + return reg + else: + raise ValueError(f"Unexpected register type {arg.type}") + + +def assembly_line( + name: str, + arg_str: str, + comment: StringAttr | None = None, + is_indented: bool = True, +) -> str: + code = " " if is_indented else "" + code += name + if arg_str: + code += f" {arg_str}" + code = append_comment(code, comment) + return code diff --git a/xdsl/dialects/arm/ops.py b/xdsl/dialects/arm/ops.py index 3a17ec7dd5..97ea772c1f 100644 --- a/xdsl/dialects/arm/ops.py +++ b/xdsl/dialects/arm/ops.py @@ -1,4 +1,4 @@ -from abc import ABC +from abc import ABC, abstractmethod from xdsl.dialects.builtin import StringAttr from xdsl.ir import Operation, SSAValue @@ -10,6 +10,7 @@ result_def, ) +from .assembly import AssemblyInstructionArg, assembly_arg_str, assembly_line from .register import IntRegisterType @@ -18,14 +19,51 @@ class ARMOperation(IRDLOperation, ABC): Base class for operations that can be a part of ARM assembly printing. """ + @abstractmethod + def assembly_line(self) -> str | None: + raise NotImplementedError() + + +class ARMInstruction(ARMOperation, ABC): + """ + Base class for operations that can be a part of x86 assembly printing. Must + represent an instruction in the x86 instruction set. + The name of the operation will be used as the x86 assembly instruction name. + """ + comment = opt_attr_def(StringAttr) """ An optional comment that will be printed along with the instruction. """ + @abstractmethod + def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]: + """ + The arguments to the instruction, in the order they should be printed in the + assembly. + """ + raise NotImplementedError() + + def assembly_instruction_name(self) -> str: + """ + By default, the name of the instruction is the same as the name of the operation. + """ + + return self.name.split(".")[-1] + + def assembly_line(self) -> str | None: + # default assembly code generator + instruction_name = self.assembly_instruction_name() + arg_str = ", ".join( + assembly_arg_str(arg) + for arg in self.assembly_line_args() + if arg is not None + ) + return assembly_line(instruction_name, arg_str, self.comment) + @irdl_op_definition -class DSMovOp(ARMOperation): +class DSMovOp(ARMInstruction): """ Copies the value of s into d. @@ -56,6 +94,9 @@ def __init__( result_types=(d,), ) + def assembly_line_args(self): + return (self.d, self.s) + @irdl_op_definition class GetRegisterOp(ARMOperation): @@ -70,3 +111,6 @@ class GetRegisterOp(ARMOperation): def __init__(self, register_type: IntRegisterType): super().__init__(result_types=[register_type]) + + def assembly_line(self): + return None diff --git a/xdsl/xdsl_opt_main.py b/xdsl/xdsl_opt_main.py index 0270d705cc..879a2ac33c 100644 --- a/xdsl/xdsl_opt_main.py +++ b/xdsl/xdsl_opt_main.py @@ -192,6 +192,11 @@ def register_all_targets(self): Add other/additional targets by overloading this function. """ + def _output_arm_asm(prog: ModuleOp, output: IO[str]): + from xdsl.dialects.arm import print_assembly + + print_assembly(prog, output) + def _output_mlir(prog: ModuleOp, output: IO[str]): printer = Printer( stream=output, @@ -241,6 +246,7 @@ def _print_to_csl(prog: ModuleOp, output: IO[str]): print_to_csl(prog, output) + self.available_targets["arm-asm"] = _output_arm_asm self.available_targets["mlir"] = _output_mlir self.available_targets["riscv-asm"] = _output_riscv_asm self.available_targets["x86-asm"] = _output_x86_asm