Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] dialects (arm): add cf instructions #3744

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions tests/filecheck/dialects/arm_cf/test_ops.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: XDSL_ROUNDTRIP
// RUN: XDSL_GENERIC_ROUNDTRIP
// RUN: xdsl-opt -t arm-asm %s | filecheck %s --check-prefix=CHECK-ASM

module {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It surprises me that this verifies, as ModuleOp is tagged as being single_block

// CHECK: %x1 = arm.get_register : !arm.reg<x1>
%x1 = arm.get_register : !arm.reg<x1>

// CHECK: %x2 = arm.get_register : !arm.reg<x2>
%x2 = arm.get_register : !arm.reg<x2>

// CHECK: arm_func.beq %x1, %x2, ^loop {"comment" = "branch if equal"}
// CHECK-ASM: beq %x1, %x2, ^loop # branch if equal

arm_cf.beq %x1, %x2, ^loop {"comment" = "branch if equal"} : (!arm.reg<x1>, !arm.reg<x2>) -> ()

// Define the loop block
^loop:
arm.label "loop"

// CHECK-GENERIC: "arm_cf.beq"(%x1, %x2, ^loop) {"comment" = "branch if equal"} : (!arm.register, !arm.register) -> ()
}
6 changes: 6 additions & 0 deletions xdsl/dialects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ def get_arm():

return ARM

def get_arm_cf():
from xdsl.dialects.arm_cf import ARM_CF

return ARM_CF

def get_arm_func():
from xdsl.dialects.arm_func import ARM_FUNC

Expand Down Expand Up @@ -334,6 +339,7 @@ def get_transform():
"air": get_air,
"arith": get_arith,
"arm": get_arm,
"arm_cf": get_arm_cf,
"arm_func": get_arm_func,
"bufferization": get_bufferization,
"builtin": get_builtin,
Expand Down
3 changes: 2 additions & 1 deletion xdsl/dialects/arm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from xdsl.dialects.builtin import ModuleOp
from xdsl.ir import Dialect

from .ops import ARMOperation, DSMovOp, DSSMulOp, GetRegisterOp
from .ops import ARMOperation, DSMovOp, DSSMulOp, GetRegisterOp, LabelOp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this could be its own PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is actually its own PR now (#3749), and I think all of your other comments are resolved in that one. apologies for the confusion, I will clearly mark WIP PRs from now on

from .register import IntRegisterType


Expand All @@ -26,6 +26,7 @@ def print_assembly(module: ModuleOp, output: IO[str]) -> None:
GetRegisterOp,
DSMovOp,
DSSMulOp,
LabelOp,
],
[
IntRegisterType,
Expand Down
39 changes: 32 additions & 7 deletions xdsl/dialects/arm/assembly.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
from typing import TypeAlias

from xdsl.dialects.arm.register import ARMRegisterType
from xdsl.dialects.builtin import StringAttr
from xdsl.ir import SSAValue
from xdsl.dialects.builtin import (
AnyIntegerAttr,
StringAttr,
)
from xdsl.ir import (
Data,
SSAValue,
)
from xdsl.irdl import irdl_attr_definition
from xdsl.parser import AttrParser
from xdsl.printer import Printer

AssemblyInstructionArg: TypeAlias = SSAValue

@irdl_attr_definition
class LabelAttr(Data[str]):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use a StringAttr here?

name = "arm.label"

@classmethod
def parse_parameter(cls, parser: AttrParser) -> str:
with parser.in_angle_brackets():
return parser.parse_str_literal()

def print_parameter(self, printer: Printer) -> None:
with printer.in_angle_brackets():
printer.print_string_literal(self.data)


AssemblyInstructionArg: TypeAlias = (
AnyIntegerAttr | LabelAttr | SSAValue | ARMRegisterType | str | int
)


def append_comment(line: str, comment: StringAttr | None) -> str:
Expand All @@ -17,11 +43,10 @@ def append_comment(line: str, comment: StringAttr | None) -> str:


def assembly_arg_str(arg: AssemblyInstructionArg) -> str:
if isinstance(arg.type, ARMRegisterType):
reg = arg.type.register_name
return reg
if isinstance(arg, ARMRegisterType):
return arg.name
else:
raise ValueError(f"Unexpected register type {arg.type}")
raise ValueError(f"Unexpected register type {type(arg)}")


def assembly_line(
Expand Down
43 changes: 42 additions & 1 deletion xdsl/dialects/arm/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
from xdsl.ir import Operation, SSAValue
from xdsl.irdl import (
IRDLOperation,
attr_def,
irdl_op_definition,
operand_def,
opt_attr_def,
result_def,
)

from .assembly import AssemblyInstructionArg, assembly_arg_str, assembly_line
from .assembly import AssemblyInstructionArg, LabelAttr, assembly_arg_str, assembly_line
from .register import IntRegisterType


Expand Down Expand Up @@ -154,3 +155,43 @@ def __init__(

def assembly_line_args(self):
return (self.d, self.s1, self.s2)


@irdl_op_definition
class LabelOp(ARMOperation):
"""
The label operation is used to emit text labels (e.g. loop:) that are used
as branch, unconditional jump targets and symbol offsets.

https://developer.arm.com/documentation/dui0801/l/Symbols--Literals--Expressions--and-Operators/Labels
"""

name = "arm.label"
label = attr_def(LabelAttr)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason this isn't a property?

comment = opt_attr_def(StringAttr)

assembly_format = "attr-dict"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not have label in it somewhere?


def __init__(
self,
label: str | LabelAttr,
*,
comment: str | StringAttr | None = None,
):
if isinstance(label, str):
label = LabelAttr(label)
if isinstance(comment, str):
comment = StringAttr(comment)

super().__init__(
attributes={
"label": label,
"comment": comment,
},
)

def assembly_line_args(self):
return ()

def assembly_instruction_name(self) -> str:
return self.label.data
66 changes: 66 additions & 0 deletions xdsl/dialects/arm_cf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from xdsl.dialects import arm
from xdsl.dialects.arm.assembly import AssemblyInstructionArg
from xdsl.dialects.arm.ops import LabelOp
from xdsl.dialects.arm.register import IntRegisterType
from xdsl.dialects.builtin import StringAttr
from xdsl.ir import Dialect, Operation, SSAValue
from xdsl.irdl import (
Successor,
irdl_op_definition,
operand_def,
successor_def,
traits_def,
)
from xdsl.traits import IsTerminator


@irdl_op_definition
class BEqOp(arm.ops.ARMInstruction):
"""
Branch if equal.
"""

name = "arm_cf.beq"

s1 = operand_def(IntRegisterType)
s2 = operand_def(IntRegisterType)

then_block = successor_def()

assembly_format = (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm aware that this is not correct, but I'm not sure how exactly to fix it. In arm, beq must follow a cmp in the line beforehand. (Related question: Shall I still treat is as one instruction and just include the cmp in the printing, or implement cmp separately?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd add the cmp separately and have a verification check here in beq the enforces this constraint (i.e., the cmp must be the instruction prior to this), similarly to some of the verify_ methods in the RISC-V dialect.
We don't have many there and the constraints are a bit more involved, but you should get an idea.

Let me know if you have more questions on this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure, if the constraint is so hard-coded, then maybe we could get away with a single op. Probably worth a discussion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it depends on the CPSR AFAIU, and I'm not sure how to model that fully, e.g., when these are cleared, as the specification phrases this as "set by the last flag-setting instruction... etc.". For example, the cmp instruction sets the Z flag.

"$s1 `,` $s2 `,` $then_block attr-dict `:` `(` type($s1) `,` type($s2) `)`"
)

traits = traits_def(IsTerminator())

def __init__(
self,
s1: Operation | SSAValue,
s2: Operation | SSAValue,
then_block: Successor,
*,
comment: str | StringAttr | None = None,
):
if isinstance(comment, str):
comment = StringAttr(comment)

super().__init__(
operands=[s1, s2],
attributes={
"comment": comment,
},
successors=(then_block,),
)

def assembly_line_args(self) -> tuple[AssemblyInstructionArg, ...]:
then_label = self.then_block.first_op
assert isinstance(then_label, LabelOp)
return self.s1, self.s2, then_label.label


ARM_CF = Dialect(
"arm_cf",
[
BEqOp,
],
)
Loading