Repository: xdslproject/xdsl
Pull request #4427: dialects: (x86) fix mov operations
Author: login: superlopuh
Labels: name: dialects
description: Changes on the dialects
Created: 2025-05-20T07:59:31Z | Merged: 2025-05-23T14:22:12Z

This PR does three things:

 * fix the mov operation from source to destination register, by adding a new ABC
 * fix the mov operation from immediate value to destination register, by adding a new ABC
 * refactor the existing ABC for inout operations with the new mnemonic and naming convention for registers, as well as adding a new constraint to enforce that the source and destination registers are the same

This temporarily puts the file in an inconsistent state, as some ABCs use R_RR etc naming convention, whereas these classes use D for destiation, S for source, and I for inout. I'll open a PR to migrate the rest of the file once this gets merged.


Diff:
diff --git a/tests/filecheck/backend/x86/convert_func_to_x86.mlir b/tests/filecheck/backend/x86/convert_func_to_x86.mlir
index cb30c5638a..a28e3dcad5 100644
--- a/tests/filecheck/backend/x86/convert_func_to_x86.mlir
+++ b/tests/filecheck/backend/x86/convert_func_to_x86.mlir
@@ -10,8 +10,7 @@ func.func @foo_const() -> i32 {
 // CHECK-NEXT:    x86_func.func @foo_const(%0 : !x86.reg<rsp>) -> !x86.reg<rax> {
 // CHECK-NEXT:      %1 = "test.op"() : () -> i32
 // CHECK-NEXT:      %2 = builtin.unrealized_conversion_cast %1 : i32 to !x86.reg
-// CHECK-NEXT:      %3 = x86.get_register : () -> !x86.reg<rax>
-// CHECK-NEXT:      %4 = x86.rs.mov %2, %3 : (!x86.reg, !x86.reg<rax>) -> !x86.reg<rax>
+// CHECK-NEXT:      %3 = x86.ds.mov %2 : (!x86.reg) -> !x86.reg<rax>
 // CHECK-NEXT:      x86_func.ret
 // CHECK-NEXT:    }
 // CHECK-NEXT:  }
@@ -50,8 +49,7 @@ func.func public @foo_int(%0: i32, %1: i32, %2: i32, %3: i32, %4: i32, %5: i32,
 // CHECK-NEXT:      %f = "test.op"(%e, %14) : (i32, i32) -> i32
 // CHECK-NEXT:      %g = "test.op"(%f, %16) : (i32, i32) -> i32
 // CHECK-NEXT:      %17 = builtin.unrealized_conversion_cast %g : i32 to !x86.reg
-// CHECK-NEXT:      %18 = x86.get_register : () -> !x86.reg<rax>
-// CHECK-NEXT:      %19 = x86.rs.mov %17, %18 : (!x86.reg, !x86.reg<rax>) -> !x86.reg<rax>
+// CHECK-NEXT:      %18 = x86.ds.mov %17 : (!x86.reg) -> !x86.reg<rax>
 // CHECK-NEXT:      x86_func.ret
 // CHECK-NEXT:    }
 // CHECK-NEXT:  }
diff --git a/tests/filecheck/dialects/x86/x86_assembly_emission.mlir b/tests/filecheck/dialects/x86/x86_assembly_emission.mlir
index 03180e8940..1184d5ce4d 100644
--- a/tests/filecheck/dialects/x86/x86_assembly_emission.mlir
+++ b/tests/filecheck/dialects/x86/x86_assembly_emission.mlir
@@ -17,7 +17,7 @@
 // CHECK-NEXT: or rax, rdx
 %rr_xor = x86.rs.xor %0, %1 : (!x86.reg<rax>, !x86.reg<rdx>) -> !x86.reg<rax>
 // CHECK-NEXT: xor rax, rdx
-%rr_mov = x86.rs.mov %0, %1 : (!x86.reg<rax>, !x86.reg<rdx>) -> !x86.reg<rax>
+%ds_mov = x86.ds.mov %1 : (!x86.reg<rdx>) -> !x86.reg<rax>
 // CHECK-NEXT: mov rax, rdx
 %rr_cmp = x86.ss.cmp %0, %1 : (!x86.reg<rax>, !x86.reg<rdx>) -> !x86.rflags<rflags>
 // CHECK-NEXT: cmp rax, rdx
@@ -72,7 +72,7 @@
 // CHECK-NEXT: or rax, 2
 %ri_xor = x86.ri.xor %0, 2 : (!x86.reg<rax>) -> !x86.reg<rax>
 // CHECK-NEXT: xor rax, 2
-%ri_mov = x86.ri.mov %0, 2 : (!x86.reg<rax>) -> !x86.reg<rax>
+%di_mov = x86.di.mov 2 : () -> !x86.reg<rax>
 // CHECK-NEXT: mov rax, 2
 %ri_cmp = x86.si.cmp %0, 2 : (!x86.reg<rax>) -> !x86.rflags<rflags>
 // CHECK-NEXT: cmp rax, 2
diff --git a/tests/filecheck/dialects/x86/x86_ops.mlir b/tests/filecheck/dialects/x86/x86_ops.mlir
index 4e085be4b3..558b32926b 100644
--- a/tests/filecheck/dialects/x86/x86_ops.mlir
+++ b/tests/filecheck/dialects/x86/x86_ops.mlir
@@ -19,8 +19,8 @@
 // CHECK-NEXT: %{{.*}} = x86.rs.or %{{.*}}, %{{.*}} : (!x86.reg, !x86.reg) -> !x86.reg
 %rr_xor = x86.rs.xor %0, %1 : (!x86.reg, !x86.reg) -> !x86.reg
 // CHECK-NEXT: %{{.*}} = x86.rs.xor %{{.*}}, %{{.*}} : (!x86.reg, !x86.reg) -> !x86.reg
-%rr_mov = x86.rs.mov %0, %1 : (!x86.reg, !x86.reg) -> !x86.reg
-// CHECK-NEXT: %{{.*}} = x86.rs.mov %{{.*}}, %{{.*}} : (!x86.reg, !x86.reg) -> !x86.reg
+%ds_mov = x86.ds.mov %0 : (!x86.reg) -> !x86.reg
+// CHECK-NEXT: %ds_mov = x86.ds.mov %{{.*}} : (!x86.reg) -> !x86.reg
 %rr_cmp = x86.ss.cmp %0, %1 : (!x86.reg, !x86.reg) -> !x86.rflags<rflags>
 // CHECK: %{{.*}} = x86.ss.cmp %{{.*}}, %{{.*}} : (!x86.reg, !x86.reg) -> !x86.rflags
 
@@ -73,8 +73,8 @@
 // CHECK-NEXT: %{{.*}} = x86.ri.or %{{.*}}, 2 : (!x86.reg) -> !x86.reg
 %ri_xor = x86.ri.xor %0, 2 : (!x86.reg) -> !x86.reg
 // CHECK-NEXT: %{{.*}} = x86.ri.xor %{{.*}}, 2 : (!x86.reg) -> !x86.reg
-%ri_mov = x86.ri.mov %0, 2 : (!x86.reg) -> !x86.reg
-// CHECK-NEXT: %{{.*}} = x86.ri.mov %{{.*}}, 2 : (!x86.reg) -> !x86.reg
+%di_mov = x86.di.mov 2 : () -> !x86.reg
+// CHECK-NEXT: %di_mov = x86.di.mov 2 : () -> !x86.reg
 %ri_cmp = x86.si.cmp %0, 2 : (!x86.reg) -> !x86.rflags<rflags>
 // CHECK-NEXT: %{{.*}} = x86.si.cmp %{{.*}}, 2 : (!x86.reg) -> !x86.rflags
 
diff --git a/xdsl/backend/x86/lowering/convert_func_to_x86_func.py b/xdsl/backend/x86/lowering/convert_func_to_x86_func.py
index 659433aac9..f2d7de187c 100644
--- a/xdsl/backend/x86/lowering/convert_func_to_x86_func.py
+++ b/xdsl/backend/x86/lowering/convert_func_to_x86_func.py
@@ -145,13 +145,10 @@ def match_and_rewrite(self, op: func.ReturnOp, rewriter: PatternRewriter):
         cast_op = builtin.UnrealizedConversionCastOp.get(
             (return_value,), (x86.register.UNALLOCATED_GENERAL,)
         )
-        get_reg_op = x86.ops.GetRegisterOp(return_passing_register)
-        mov_op = x86.ops.RS_MovOp(
-            cast_op, get_reg_op, register_out=return_passing_register
-        )
+        mov_op = x86.ops.DS_MovOp(cast_op, destination=return_passing_register)
         ret_op = x86_func.RetOp()
 
-        rewriter.replace_matched_op([cast_op, get_reg_op, mov_op, ret_op])
+        rewriter.replace_matched_op([cast_op, mov_op, ret_op])
 
 
 class ConvertFuncToX86FuncPass(ModulePass):
diff --git a/xdsl/dialects/x86/__init__.py b/xdsl/dialects/x86/__init__.py
index 186e7a10bd..e0aa715b03 100644
--- a/xdsl/dialects/x86/__init__.py
+++ b/xdsl/dialects/x86/__init__.py
@@ -34,6 +34,7 @@
     C_JsOp,
     C_JzOp,
     D_PopOp,
+    DI_MovOp,
     DirectiveOp,
     DM_LeaOp,
     DM_MovOp,
@@ -41,6 +42,7 @@
     DM_VbroadcastssOp,
     DM_VmovupsOp,
     DMI_ImulOp,
+    DS_MovOp,
     DSI_ImulOp,
     GetAVXRegisterOp,
     GetRegisterOp,
@@ -75,7 +77,6 @@
     R_NotOp,
     RI_AddOp,
     RI_AndOp,
-    RI_MovOp,
     RI_OrOp,
     RI_SubOp,
     RI_XorOp,
@@ -88,7 +89,6 @@
     RS_AddOp,
     RS_AndOp,
     RS_ImulOp,
-    RS_MovOp,
     RS_OrOp,
     RS_SubOp,
     RS_VmovapdOp,
@@ -145,6 +145,7 @@
         C_JsOp,
         C_JzOp,
         D_PopOp,
+        DI_MovOp,
         DirectiveOp,
         DM_LeaOp,
         DM_MovOp,
@@ -152,6 +153,7 @@
         DM_VbroadcastssOp,
         DM_VmovupsOp,
         DMI_ImulOp,
+        DS_MovOp,
         DSI_ImulOp,
         GetAVXRegisterOp,
         GetRegisterOp,
@@ -186,7 +188,6 @@
         R_NotOp,
         RI_AddOp,
         RI_AndOp,
-        RI_MovOp,
         RI_OrOp,
         RI_SubOp,
         RI_XorOp,
@@ -199,7 +200,6 @@
         RS_AddOp,
         RS_AndOp,
         RS_ImulOp,
-        RS_MovOp,
         RS_OrOp,
         RS_SubOp,
         RS_VmovapdOp,
diff --git a/xdsl/dialects/x86/ops.py b/xdsl/dialects/x86/ops.py
index 0980682ee7..8357d9c640 100644
--- a/xdsl/dialects/x86/ops.py
+++ b/xdsl/dialects/x86/ops.py
@@ -263,6 +263,37 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.register_in, self.source
 
 
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and one source
+    register.
+    """
+
+    destination = result_def(R1InvT)
+    source = operand_def(R1InvT)
+
+    def __init__(
+        self,
+        source: Operation | SSAValue,
+        *,
+        comment: str | StringAttr | None = None,
+        destination: R1InvT,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[source],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[destination],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.destination, self.source)
+
+
 class R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register that is read and written to.
@@ -397,6 +428,54 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"memory_offset"}
 
 
+class DI_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
+    """
+
+    immediate = attr_def(IntegerAttr)
+    destination = result_def(R1InvT)
+
+    def __init__(
+        self,
+        immediate: int | IntegerAttr,
+        *,
+        comment: str | StringAttr | None = None,
+        destination: R1InvT,
+    ):
+        if isinstance(immediate, int):
+            immediate = IntegerAttr(
+                immediate, 32
+            )  # the default immediate size is 32 bits
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            attributes={
+                "immediate": immediate,
+                "comment": comment,
+            },
+            result_types=[destination],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return self.destination, self.immediate
+
+    @classmethod
+    def custom_parse_attributes(cls, parser: Parser) -> dict[str, Attribute]:
+        return {
+            "immediate": parse_immediate_value(
+                parser, IntegerType(32, Signedness.SIGNED)
+            )
+        }
+
+    def custom_print_attributes(self, printer: Printer) -> Set[str]:
+        printer.print_string(" ", indent=0)
+        print_immediate_value(printer, self.immediate)
+        return {"immediate"}
+
+
 class RI_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register that is read and written to
@@ -971,7 +1050,7 @@ class RS_XorOp(RS_Operation[GeneralRegisterType, GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RS_MovOp(RS_Operation[GeneralRegisterType, GeneralRegisterType]):
+class DS_MovOp(DS_Operation[GeneralRegisterType]):
     """
     Copies the value of s into r.
     ```C
@@ -981,7 +1060,7 @@ class RS_MovOp(RS_Operation[GeneralRegisterType, GeneralRegisterType]):
     See external [documentation](https://www.felixcloutier.com/x86/mov).
     """
 
-    name = "x86.rs.mov"
+    name = "x86.ds.mov"
 
 
 @irdl_op_definition
@@ -1387,7 +1466,7 @@ class RI_XorOp(RI_Operation[GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RI_MovOp(RI_Operation[GeneralRegisterType]):
+class DI_MovOp(DI_Operation[GeneralRegisterType]):
     """
     Copies the immediate value into r.
     ```C
@@ -1397,7 +1476,7 @@ class RI_MovOp(RI_Operation[GeneralRegisterType]):
     See external [documentation](https://www.felixcloutier.com/x86/mov).
     """
 
-    name = "x86.ri.mov"
+    name = "x86.di.mov"
 
 
 @irdl_op_definition


Reviews:
user: login: alexarice
body: Not the review you asked for, but the review you got anyway
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T08:47:53Z
user: login: alexarice
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T09:04:09Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T09:12:05Z
user: login: alexarice
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T09:15:46Z
user: login: alexarice
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T09:16:58Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T09:36:17Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T09:37:41Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T09:37:49Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T09:39:53Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T10:02:29Z
user: login: alexarice
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T10:09:03Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T10:18:48Z
user: login: superlopuh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T10:24:18Z
user: login: qaco
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-23T10:41:09Z

Review comments:
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: I feel it would be good for the docstring to explain the naming scheme. I assume this one is "Destination" and "Source". They also appear in the opposite order in both the definition below and init, but I assume not in the assembly?
created_at: 2025-05-20T08:41:00Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: Again would be nice to know what `I` stands for
created_at: 2025-05-20T08:41:36Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: the old names seem more informative, why change them?
created_at: 2025-05-20T08:42:00Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
 
     def __init__(
         self,
-        source: Operation | SSAValue | None = None,
+        s: Operation | SSAValue,
         *,
         comment: str | StringAttr | None = None,
-        destination: R1InvT | None = None,
     ):
         if isinstance(comment, str):
             comment = StringAttr(comment)
+        if isinstance(s, Operation):
+            s = SSAValue.get(s)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: ```suggestion
        s = SSAValue.get(s)
```
I think `SSAValue.get` already does the check
created_at: 2025-05-20T08:42:56Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
 
     def __init__(
         self,
-        source: Operation | SSAValue | None = None,
+        s: Operation | SSAValue,
         *,
         comment: str | StringAttr | None = None,
-        destination: R1InvT | None = None,
     ):
         if isinstance(comment, str):
             comment = StringAttr(comment)
+        if isinstance(s, Operation):
+            s = SSAValue.get(s)
+        assert isinstance(s.type, GeneralRegisterType)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: Side note: maybe we can have an `SSAValue.get(s, type=GeneralRegisterType)` which includes this check and returns an `SSAValue[GeneralRegisterType]`
created_at: 2025-05-20T08:44:20Z
author_association: COLLABORATOR
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: Same again with `D` (destination?) and `I` (immediate? unsure if the above `I` was immediate)
created_at: 2025-05-20T08:45:29Z
author_association: COLLABORATOR
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
+    """
+
+    imm = attr_def(IntegerAttr)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: feels like we should either stick to single letters throughout or use full names throughout to me
created_at: 2025-05-20T08:45:53Z
author_association: COLLABORATOR
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
+    """
+
+    imm = attr_def(IntegerAttr)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: Should this really be an `attr_def`?
created_at: 2025-05-20T08:46:15Z
author_association: COLLABORATOR
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
+    """
+
+    imm = attr_def(IntegerAttr)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        imm: int | IntegerAttr,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT,
+    ):
+        if isinstance(imm, int):
+            imm = IntegerAttr(imm, 32)  # the default immediate size is 32 bits
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            attributes={
+                "imm": imm,
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return self.d, self.imm
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: had no idea you could do this
created_at: 2025-05-20T08:46:29Z
author_association: COLLABORATOR
diff_hunk: @@ -940,7 +1021,7 @@ class RR_XorOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RR_MovOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
+class DS_MovOp(DS_Operation[GeneralRegisterType]):
     """
     Copies the value of r1 into r2.
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: ```suggestion
    Copies the value of s into d.
```
?
created_at: 2025-05-20T08:47:18Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
 
     def __init__(
         self,
-        source: Operation | SSAValue | None = None,
+        s: Operation | SSAValue,
         *,
         comment: str | StringAttr | None = None,
-        destination: R1InvT | None = None,
     ):
         if isinstance(comment, str):
             comment = StringAttr(comment)
+        if isinstance(s, Operation):
+            s = SSAValue.get(s)
+        assert isinstance(s.type, GeneralRegisterType)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: #4428 
created_at: 2025-05-20T09:04:09Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: The idea is to make the whole file consistent, and the plan is to have s1, s2, d1, d2 etc registers, which feels quite verbose when you have a bunch of registers together. I'm glad you commented though, because I'm happy to be convinced otherwise.
created_at: 2025-05-20T09:12:04Z
author_association: MEMBER
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: From the outside perspective, it was not immediately obvious to me what these were, but maybe if you're familiar with these things then it is fine. In any case I think documenting at least what each letter means in the docstring would help
created_at: 2025-05-20T09:15:46Z
author_association: COLLABORATOR
diff_hunk: @@ -232,34 +234,69 @@ def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
         return self.r1, self.r2
 
 
-class R_R_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+class DS_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one source register and one destination
+    register.
+    """
+
+    s = operand_def(R1InvT)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        s: Operation | SSAValue | None = None,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT | None = None,
+    ):
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            operands=[s],
+            attributes={
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return (self.d, self.s)
+
+
+class I_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
     """
     A base class for x86 operations that have one register acting as both source and destination.
     """
 
-    source = operand_def(R1InvT)
-    destination = result_def(R1InvT)
+    INOUT: ClassVar = VarConstraint("INOUT", base(GeneralRegisterType))
+
+    s = operand_def(INOUT)
+    d = result_def(INOUT)
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: possible other option is `src1`, `src2`, `dest1`, `dest2` which in my opinion is more informative than `s1`, `s2` but not too verbose? I don't really have a stake in these dialects so will leave it up to you to do what you think is best.
created_at: 2025-05-20T09:16:58Z
author_association: COLLABORATOR
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: I was inout, I'm really struggling with naming here, `I` for immediate would also make sense, but I don't know what letter to use for inout then.
created_at: 2025-05-20T09:36:17Z
author_association: MEMBER
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
+    """
+
+    imm = attr_def(IntegerAttr)
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: This and the riscv dialect have this super weird setup for custom syntax that we'd need a one time massive change to fix. You're invited to look at the API there, it's not like anything else we have in xDSL. I'd like to move it more towards what we have in the ARM dialect.
created_at: 2025-05-20T09:37:41Z
author_association: MEMBER
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
+    """
+
+    imm = attr_def(IntegerAttr)
+    d = result_def(R1InvT)
+
+    def __init__(
+        self,
+        imm: int | IntegerAttr,
+        *,
+        comment: str | StringAttr | None = None,
+        d: R1InvT,
+    ):
+        if isinstance(imm, int):
+            imm = IntegerAttr(imm, 32)  # the default immediate size is 32 bits
+        if isinstance(comment, str):
+            comment = StringAttr(comment)
+
+        super().__init__(
+            attributes={
+                "imm": imm,
+                "comment": comment,
+            },
+            result_types=[d],
+        )
+
+    def assembly_line_args(self) -> tuple[AssemblyInstructionArg | None, ...]:
+        return self.d, self.imm
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: yep
created_at: 2025-05-20T09:37:48Z
author_association: MEMBER
diff_hunk: @@ -368,6 +405,50 @@ def custom_print_attributes(self, printer: Printer) -> Set[str]:
         return {"offset"}
 
 
+class DImm_Operation(Generic[R1InvT], X86Instruction, X86CustomFormatOperation, ABC):
+    """
+    A base class for x86 operations that have one destination register and an immediate
+    value.
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: Actually maybe the solution here is just to use R, I'll give that a go
created_at: 2025-05-20T09:39:53Z
author_association: MEMBER
diff_hunk: @@ -940,7 +1021,7 @@ class RR_XorOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RR_MovOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
+class DS_MovOp(DS_Operation[GeneralRegisterType]):
     """
     Copies the value of r1 into r2.
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: I've changed this back to use `r1` and `r2` names of regs
created_at: 2025-05-20T10:02:29Z
author_association: MEMBER
diff_hunk: @@ -940,7 +1021,7 @@ class RR_XorOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RR_MovOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
+class DS_MovOp(DS_Operation[GeneralRegisterType]):
     """
     Copies the value of r1 into r2.
path: xdsl/dialects/x86/ops.py
user: login: alexarice
body: I'm not sure I understand why. The class is called `DS_Operation` but there's no mention of `d` or `s`. I'm maybe unsure what the purpose of this PR is or the intended naming scheme here.
created_at: 2025-05-20T10:09:03Z
author_association: COLLABORATOR
diff_hunk: @@ -940,7 +1021,7 @@ class RR_XorOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RR_MovOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
+class DS_MovOp(DS_Operation[GeneralRegisterType]):
     """
     Copies the value of r1 into r2.
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: So this PR is weird because I have two problems: 1. there is a bug in the mov ops in that they take operands they don't need, 2. the naming conventions existing in this file don't let me distinguish between operations that take an input and an output which could be different vs have to be the same. My original plan was to first change the naming convention in this file to be more explicit, and then to fix the `mov` but then I thought that was too weird. It feels like that may have been a mistake, and that I should first open a PR to have the naming conversations and how to document them properly, and then to fix the mov ops?
created_at: 2025-05-20T10:18:48Z
author_association: MEMBER
diff_hunk: @@ -940,7 +1021,7 @@ class RR_XorOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
 
 
 @irdl_op_definition
-class RR_MovOp(R_RR_Operation[GeneralRegisterType, GeneralRegisterType]):
+class DS_MovOp(DS_Operation[GeneralRegisterType]):
     """
     Copies the value of r1 into r2.
path: xdsl/dialects/x86/ops.py
user: login: superlopuh
body: I think I'll move this to draft, and move the naming convention discussion to a new PR. I'll start with moving from `Imm` to `I`: #4429
created_at: 2025-05-20T10:24:17Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-05-20T08:05:46Z
body: ## [Codecov](https://app.codecov.io/gh/xdslproject/xdsl/pull/4427?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject) Report
:x: Patch coverage is `78.78788%` with `7 lines` in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 89.18%. Comparing base ([`2fdd3dd`](https://app.codecov.io/gh/xdslproject/xdsl/commit/2fdd3ddefdd0146dbc4be804c41810892a454fd3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject)) to head ([`18d9fb2`](https://app.codecov.io/gh/xdslproject/xdsl/commit/18d9fb2b63a7cc4f71b8e7e1855a429b03930689?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject)).
:warning: Report is 566 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/xdslproject/xdsl/pull/4427?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject) | Patch % | Lines |
|---|---|---|
| [xdsl/dialects/x86/ops.py](https://app.codecov.io/gh/xdslproject/xdsl/pull/4427?src=pr&el=tree&filepath=xdsl%2Fdialects%2Fx86%2Fops.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject#diff-eGRzbC9kaWFsZWN0cy94ODYvb3BzLnB5) | 77.41% | [6 Missing and 1 partial :warning: ](https://app.codecov.io/gh/xdslproject/xdsl/pull/4427?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject) |

<details><summary>Additional details and impacted files</summary>



```diff
@@            Coverage Diff             @@
##             main    #4427      +/-   ##
==========================================
- Coverage   89.19%   89.18%   -0.01%     
==========================================
  Files         339      339              
  Lines       45909    45935      +26     
  Branches     5625     5628       +3     
==========================================
+ Hits        40947    40966      +19     
- Misses       3564     3570       +6     
- Partials     1398     1399       +1     
```
</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/xdslproject/xdsl/pull/4427?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xdslproject).
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: superlopuh
created_at: 2025-05-20T09:38:11Z
body: OK @alexarice I'll move everything to full names
author_association: MEMBER
user: login: superlopuh
created_at: 2025-05-20T09:52:39Z
body: @alexarice, if you and others are happy to just go with `R` as a mnemonic for inout register, I'm happy to do a separate PR migrating `Imm` to `I` for immediate, WDYT?
author_association: MEMBER
user: login: alexarice
created_at: 2025-05-20T10:20:39Z
body: I feel it would make sense to fix all the naming in one go. Don't mind whether this happens before or after fixing `mov`
author_association: COLLABORATOR
user: login: superlopuh
created_at: 2025-05-20T10:24:42Z
body: OK I'll first fix and document the naming, then fix mov
author_association: MEMBER
user: login: superlopuh
created_at: 2025-05-22T07:56:01Z
body: Naming fixed!
author_association: MEMBER
user: login: manueljacob
created_at: 2025-05-26T20:18:40Z
body: It’s hard to find a good naming scheme, but this seems to work. It would be useful to have some documentation (e.g. before the base classes) that explain the scheme, though.
author_association: CONTRIBUTOR
user: login: superlopuh
created_at: 2025-05-26T20:56:01Z
body: I agree, we added it in the [doc comment at top of file](https://xdsl.readthedocs.io/stable/reference/dialects/x86/ops/)
author_association: MEMBER
user: login: manueljacob
created_at: 2025-05-27T00:22:49Z
body: I missed that, but I agree that it is the correct place to put it.
author_association: CONTRIBUTOR