[llvm] [CodeGen] Refactor register operand parsing in MI parser (PR #181748)

Rahul Joshi via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 17 15:50:19 PST 2026


https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/181748

>From 4742405ef2f0e4e578707451543c675e86fc72b4 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Mon, 16 Feb 2026 12:56:59 -0800
Subject: [PATCH 1/4] [NFC][CodeGen] Refactor register operand parsing in MI
 parser

Refactor register operand parsing to factor out duplicated type
parsing code into a lambda. Additionally, fix the MI LangRef to
fix the syntax for a register operand to match what the parser
supports.
---
 llvm/docs/MIRLangRef.rst                |  7 ++-
 llvm/lib/CodeGen/MIRParser/MIParser.cpp | 69 ++++++++++---------------
 2 files changed, 32 insertions(+), 44 deletions(-)

diff --git a/llvm/docs/MIRLangRef.rst b/llvm/docs/MIRLangRef.rst
index f7df57d05baa0..a2ee28151b354 100644
--- a/llvm/docs/MIRLangRef.rst
+++ b/llvm/docs/MIRLangRef.rst
@@ -523,7 +523,7 @@ The full syntax of a register operand is shown below:
 
 .. code-block:: text
 
-    [<flags>] <register> [ :<subregister-idx-name> ] [ (tied-def <tied-op>) ]
+    [<flags>] <register> [ .<subregister-idx-name> ] [ :<register-class> ] [ (tied-def <tied-op>) ] [ (<type>) ]
 
 This example shows an instance of the X86 ``XOR32rr`` instruction that has
 5 register operands with different register flags:
@@ -532,6 +532,9 @@ This example shows an instance of the X86 ``XOR32rr`` instruction that has
 
   dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al
 
+Note that subregister-index, register-class and type cannot be specified for
+physical registers. Additionally, tied-def can only be specified for a use.
+
 .. _register-flags:
 
 Register Flags
@@ -602,7 +605,7 @@ lower bits from the 32-bit virtual register 0 to the 8-bit virtual register 1:
 
 .. code-block:: text
 
-    %1 = COPY %0:sub_8bit
+    %1 = COPY %0.sub_8bit
 
 The names of the subregister indices are target specific, and are typically
 defined in the target's ``*RegisterInfo.td`` file.
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 9329adcb995b8..e94dbc9f30583 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -1728,9 +1728,7 @@ bool MIParser::parseRegisterTiedDefIndex(unsigned &TiedDefIdx) {
   if (getUnsigned(TiedDefIdx))
     return true;
   lex();
-  if (expectAndConsume(MIToken::rparen))
-    return true;
-  return false;
+  return expectAndConsume(MIToken::rparen);
 }
 
 bool MIParser::assignRegisterTies(MachineInstr &MI,
@@ -1799,50 +1797,37 @@ bool MIParser::parseRegisterOperand(MachineOperand &Dest,
     if (parseRegisterClassOrBank(*RegInfo))
         return true;
   }
-  MachineRegisterInfo &MRI = MF.getRegInfo();
-  if (!hasRegState(Flags, RegState::Define)) {
-    if (consumeIfPresent(MIToken::lparen)) {
-      unsigned Idx;
-      if (!parseRegisterTiedDefIndex(Idx))
-        TiedDefIdx = Idx;
-      else {
-        // Try a redundant low-level type.
-        LLT Ty;
-        if (parseLowLevelType(Token.location(), Ty))
-          return error("expected tied-def or low-level type after '('");
-
-        if (expectAndConsume(MIToken::rparen))
-          return true;
 
-        if (MRI.getType(Reg).isValid() && MRI.getType(Reg) != Ty)
-          return error("inconsistent type for generic virtual register");
-
-        MRI.setRegClassOrRegBank(Reg, static_cast<RegisterBank *>(nullptr));
-        MRI.setType(Reg, Ty);
-        MRI.noteNewVirtualRegister(Reg);
-      }
-    }
-  } else if (consumeIfPresent(MIToken::lparen)) {
-    // Virtual registers may have a tpe with GlobalISel.
-    if (!Reg.isVirtual())
-      return error("unexpected type on physical register");
+  if (consumeIfPresent(MIToken::lparen)) {
+    // For a def, we only expect a type. For use we expect either a type or a
+    // tied-def.
+    unsigned Idx;
+    if (!IsDef && !parseRegisterTiedDefIndex(Idx)) {
+      TiedDefIdx = Idx;
+    } else {
+      LLT Ty;
+      auto TyLoc = Token.location();
+      // If type parsing fails, forwad the parse error for defs.
+      if (parseLowLevelType(TyLoc, Ty))
+        return IsDef ? true
+                     : error("expected tied-def or low-level type after '('");
 
-    LLT Ty;
-    if (parseLowLevelType(Token.location(), Ty))
-      return true;
+      if (expectAndConsume(MIToken::rparen))
+        return true;
 
-    if (expectAndConsume(MIToken::rparen))
-      return true;
+      if (!Reg.isVirtual())
+        return error(TyLoc, "unexpected type on physical register");
 
-    if (MRI.getType(Reg).isValid() && MRI.getType(Reg) != Ty)
-      return error("inconsistent type for generic virtual register");
+      MachineRegisterInfo &MRI = MF.getRegInfo();
+      if (MRI.getType(Reg).isValid() && MRI.getType(Reg) != Ty)
+        return error("inconsistent type for generic virtual register");
 
-    MRI.setRegClassOrRegBank(Reg, static_cast<RegisterBank *>(nullptr));
-    MRI.setType(Reg, Ty);
-  } else if (Reg.isVirtual()) {
-    // Generic virtual registers must have a type.
-    // If we end up here this means the type hasn't been specified and
-    // this is bad!
+      MRI.setRegClassOrRegBank(Reg, static_cast<RegisterBank *>(nullptr));
+      MRI.setType(Reg, Ty);
+      MRI.noteNewVirtualRegister(Reg);
+    }
+  } else if (IsDef && Reg.isVirtual()) {
+    // Generic virtual registers defs must have a type.
     if (RegInfo->Kind == VRegInfo::GENERIC ||
         RegInfo->Kind == VRegInfo::REGBANK)
       return error("generic virtual registers must have a type");

>From 020c81b4100cbaa17da19cd23829907411e681e5 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Tue, 17 Feb 2026 12:56:15 -0800
Subject: [PATCH 2/4] Review feedback

---
 llvm/lib/CodeGen/MIRParser/MIParser.cpp       | 21 +++++++++++--------
 .../X86/expected-integer-after-tied-def.mir   |  2 +-
 .../X86/expected-tied-def-after-lparen.mir    |  2 +-
 3 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index e94dbc9f30583..98fdbc2a61180 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -1721,8 +1721,8 @@ bool MIParser::parseSubRegisterIndex(unsigned &SubReg) {
 }
 
 bool MIParser::parseRegisterTiedDefIndex(unsigned &TiedDefIdx) {
-  if (!consumeIfPresent(MIToken::kw_tied_def))
-    return true;
+  assert(Token.is(MIToken::kw_tied_def));
+  lex();
   if (Token.isNot(MIToken::IntegerLiteral))
     return error("expected an integer literal after 'tied-def'");
   if (getUnsigned(TiedDefIdx))
@@ -1800,24 +1800,27 @@ bool MIParser::parseRegisterOperand(MachineOperand &Dest,
 
   if (consumeIfPresent(MIToken::lparen)) {
     // For a def, we only expect a type. For use we expect either a type or a
-    // tied-def.
+    // tied-def. Additionally, for physical registers, we don't expect a type.
     unsigned Idx;
-    if (!IsDef && !parseRegisterTiedDefIndex(Idx)) {
+    if (Token.is(MIToken::kw_tied_def)) {
+      if (IsDef)
+        return error("tied-def not supported for defs");
+      if (parseRegisterTiedDefIndex(Idx))
+        return true;
       TiedDefIdx = Idx;
     } else {
+      if (!Reg.isVirtual())
+        return error("unexpected type on physical register");
+
       LLT Ty;
-      auto TyLoc = Token.location();
       // If type parsing fails, forwad the parse error for defs.
-      if (parseLowLevelType(TyLoc, Ty))
+      if (parseLowLevelType(Token.location(), Ty))
         return IsDef ? true
                      : error("expected tied-def or low-level type after '('");
 
       if (expectAndConsume(MIToken::rparen))
         return true;
 
-      if (!Reg.isVirtual())
-        return error(TyLoc, "unexpected type on physical register");
-
       MachineRegisterInfo &MRI = MF.getRegInfo();
       if (MRI.getType(Reg).isValid() && MRI.getType(Reg) != Ty)
         return error("inconsistent type for generic virtual register");
diff --git a/llvm/test/CodeGen/MIR/X86/expected-integer-after-tied-def.mir b/llvm/test/CodeGen/MIR/X86/expected-integer-after-tied-def.mir
index 7160a3e3c3d84..5666c2141c5ee 100644
--- a/llvm/test/CodeGen/MIR/X86/expected-integer-after-tied-def.mir
+++ b/llvm/test/CodeGen/MIR/X86/expected-integer-after-tied-def.mir
@@ -17,7 +17,7 @@ body: |
   bb.0.entry:
     liveins: $rdi
 
-  ; CHECK: [[@LINE+1]]:78: expected tied-def or low-level type after '('
+  ; CHECK: [[@LINE+1]]:78: expected an integer literal after 'tied-def'
     INLINEASM &"$foo", 1, 2818058, def $rdi, 2147483657, killed $rdi(tied-def)
     $rax = COPY killed $rdi
     RET64 killed $rax
diff --git a/llvm/test/CodeGen/MIR/X86/expected-tied-def-after-lparen.mir b/llvm/test/CodeGen/MIR/X86/expected-tied-def-after-lparen.mir
index a2c65dd3f0195..f2d94339e3ae5 100644
--- a/llvm/test/CodeGen/MIR/X86/expected-tied-def-after-lparen.mir
+++ b/llvm/test/CodeGen/MIR/X86/expected-tied-def-after-lparen.mir
@@ -17,7 +17,7 @@ body: |
   bb.0.entry:
     liveins: $rdi
 
-  ; CHECK: [[@LINE+1]]:70: expected tied-def or low-level type after '('
+  ; CHECK: [[@LINE+1]]:70: unexpected type on physical register
     INLINEASM &"$foo", 1, 2818058, def $rdi, 2147483657, killed $rdi(3)
     $rax = COPY killed $rdi
     RET64 killed $rax

>From 05b9f7feca7f1b3cd2b142ce08e71b3b7b26d896 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Tue, 17 Feb 2026 15:12:00 -0800
Subject: [PATCH 3/4] Rename test file

---
 ...ed-tied-def-after-lparen.mir => invalid-type-physical-reg.mir} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename llvm/test/CodeGen/MIR/X86/{expected-tied-def-after-lparen.mir => invalid-type-physical-reg.mir} (100%)

diff --git a/llvm/test/CodeGen/MIR/X86/expected-tied-def-after-lparen.mir b/llvm/test/CodeGen/MIR/X86/invalid-type-physical-reg.mir
similarity index 100%
rename from llvm/test/CodeGen/MIR/X86/expected-tied-def-after-lparen.mir
rename to llvm/test/CodeGen/MIR/X86/invalid-type-physical-reg.mir

>From 382d274d0f239b3cdaf7843a4406d0eb0917ddf9 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Tue, 17 Feb 2026 15:49:49 -0800
Subject: [PATCH 4/4] Add unit test for tied-def on a definition

---
 llvm/lib/CodeGen/MIRParser/MIParser.cpp           |  4 +++-
 .../MIR/X86/invalid-tied-physical-reg-def.mir     | 15 +++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/MIR/X86/invalid-tied-physical-reg-def.mir

diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 98fdbc2a61180..62d1a0ee5cf88 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -1776,6 +1776,8 @@ bool MIParser::parseRegisterOperand(MachineOperand &Dest,
     if (parseRegisterFlag(Flags))
       return true;
   }
+  // Update IsDef as we may have read a def flag.
+  IsDef = hasRegState(Flags, RegState::Define);
   if (!Token.isRegister())
     return error("expected a register after register flags");
   Register Reg;
@@ -1801,10 +1803,10 @@ bool MIParser::parseRegisterOperand(MachineOperand &Dest,
   if (consumeIfPresent(MIToken::lparen)) {
     // For a def, we only expect a type. For use we expect either a type or a
     // tied-def. Additionally, for physical registers, we don't expect a type.
-    unsigned Idx;
     if (Token.is(MIToken::kw_tied_def)) {
       if (IsDef)
         return error("tied-def not supported for defs");
+      unsigned Idx;
       if (parseRegisterTiedDefIndex(Idx))
         return true;
       TiedDefIdx = Idx;
diff --git a/llvm/test/CodeGen/MIR/X86/invalid-tied-physical-reg-def.mir b/llvm/test/CodeGen/MIR/X86/invalid-tied-physical-reg-def.mir
new file mode 100644
index 0000000000000..66c458e79f316
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/X86/invalid-tied-physical-reg-def.mir
@@ -0,0 +1,15 @@
+# RUN: not llc -mtriple=x86_64 -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
+---
+name:            test
+tracksRegLiveness: true
+liveins:
+  - { reg: '$rdi' }
+body: |
+  bb.0.entry:
+    liveins: $rdi
+
+  ; CHECK: [[@LINE+1]]:45: tied-def not supported for defs
+    INLINEASM &"$foo", 1, 2818058, def $rdi(tied-def 5), 2147483657, killed $rdi(tied-def 3)
+    $rax = COPY killed $rdi
+    RET64 killed $rax
+...



More information about the llvm-commits mailing list