Skip to content

Conversation

@slinder1
Copy link
Contributor

While these can be represented with .cfi_escape, using these pseudo-cfi
instructions makes .s/.mir files more readable, and it is necessary to
support updating registers in CFI instructions (something that the
AMDGPU backend requires).

Copy link
Contributor Author

slinder1 commented Oct 22, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@llvmbot
Copy link
Member

llvmbot commented Oct 22, 2025

@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-backend-amdgpu

Author: Scott Linder (slinder1)

Changes

While these can be represented with .cfi_escape, using these pseudo-cfi
instructions makes .s/.mir files more readable, and it is necessary to
support updating registers in CFI instructions (something that the
AMDGPU backend requires).


Patch is 49.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/164721.diff

18 Files Affected:

  • (modified) llvm/include/llvm/MC/MCDwarf.h (+91-1)
  • (modified) llvm/include/llvm/MC/MCStreamer.h (+18)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp (+33)
  • (modified) llvm/lib/CodeGen/CFIInstrInserter.cpp (+4)
  • (modified) llvm/lib/CodeGen/MIRParser/MILexer.cpp (+5)
  • (modified) llvm/lib/CodeGen/MIRParser/MILexer.h (+4)
  • (modified) llvm/lib/CodeGen/MIRParser/MIParser.cpp (+75)
  • (modified) llvm/lib/CodeGen/MachineOperand.cpp (+58)
  • (modified) llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp (+10)
  • (modified) llvm/lib/MC/MCAsmStreamer.cpp (+76)
  • (modified) llvm/lib/MC/MCDwarf.cpp (+186)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+106)
  • (modified) llvm/lib/MC/MCParser/MasmParser.cpp (+4)
  • (modified) llvm/lib/MC/MCStreamer.cpp (+54)
  • (added) llvm/test/DebugInfo/AMDGPU/heterogeneous-dwarf-cfi-directives.s (+57)
  • (added) llvm/test/MC/ELF/cfi-register-pair.s (+56)
  • (added) llvm/test/MC/ELF/cfi-vector-offset.s (+56)
  • (added) llvm/test/MC/ELF/cfi-vector-registers.s (+56)
diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h
index 640d2eeb68d61..e602f03de5ebf 100644
--- a/llvm/include/llvm/MC/MCDwarf.h
+++ b/llvm/include/llvm/MC/MCDwarf.h
@@ -530,6 +530,10 @@ class MCCFIInstruction {
     OpGnuArgsSize,
     OpLabel,
     OpValOffset,
+    OpLLVMRegisterPair,
+    OpLLVMVectorRegisters,
+    OpLLVMVectorOffset,
+    OpLLVMVectorRegisterMask,
   };
 
   // Held in ExtraFields for most common OpTypes, exceptions follow.
@@ -548,10 +552,45 @@ class MCCFIInstruction {
   struct LabelFields {
     MCSymbol *CfiLabel = nullptr;
   };
+  /// Held in ExtraFields when OpLLVMRegisterPair.
+  struct RegisterPairFields {
+    unsigned Register;
+    unsigned Reg1, Reg2;
+    unsigned Reg1SizeInBits, Reg2SizeInBits;
+  };
+  struct VectorRegisterWithLane {
+    unsigned Register;
+    unsigned Lane;
+    unsigned SizeInBits;
+  };
+  /// Held in ExtraFields when OpLLVMVectorRegisters.
+  struct VectorRegistersFields {
+    unsigned Register;
+    std::vector<VectorRegisterWithLane> VectorRegisters;
+  };
+  /// Held in ExtraFields when OpLLVMVectorOffset.
+  struct VectorOffsetFields {
+    unsigned Register;
+    unsigned RegisterSizeInBits;
+    int64_t Offset;
+    unsigned MaskRegister;
+    unsigned MaskRegisterSizeInBits;
+  };
+  /// Held in ExtraFields when OpLLVMVectorRegisterMask.
+  struct VectorRegisterMaskFields {
+    unsigned Register;
+    unsigned SpillRegister;
+    unsigned SpillRegisterLaneSizeInBits;
+    unsigned MaskRegister;
+    unsigned MaskRegisterSizeInBits;
+  };
 
 private:
   MCSymbol *Label;
-  std::variant<CommonFields, EscapeFields, LabelFields> ExtraFields;
+  std::variant<CommonFields, EscapeFields, LabelFields, RegisterPairFields,
+               VectorRegistersFields, VectorOffsetFields,
+               VectorRegisterMaskFields>
+      ExtraFields;
   OpType Operation;
   SMLoc Loc;
 
@@ -694,6 +733,57 @@ class MCCFIInstruction {
     return {OpLabel, L, LabelFields{CfiLabel}, Loc};
   }
 
+  /// .cfi_llvm_register_pair Previous value of Register is saved in R1:R2.
+  static MCCFIInstruction
+  createLLVMRegisterPair(MCSymbol *L, unsigned Register, unsigned R1,
+                         unsigned R1SizeInBits, unsigned R2,
+                         unsigned R2SizeInBits, SMLoc Loc = {}) {
+    RegisterPairFields Extra{Register, R1, R2, R1SizeInBits, R2SizeInBits};
+    return {OpLLVMRegisterPair, L, Extra, Loc};
+  }
+
+  /// .cfi_llvm_vector_registers Previous value of Register is saved in lanes of
+  /// vector registers.
+  static MCCFIInstruction
+  createLLVMVectorRegisters(MCSymbol *L, unsigned Register,
+                            std::vector<VectorRegisterWithLane> VectorRegisters,
+                            SMLoc Loc = {}) {
+    VectorRegistersFields Extra{Register, std::move(VectorRegisters)};
+    return {OpLLVMVectorRegisters, L, std::move(Extra), Loc};
+  }
+
+  /// .cfi_llvm_vector_offset Previous value of Register is saved at Offset from
+  /// CFA. MaskRegister specifies the active lanes of register.
+  static MCCFIInstruction
+  createLLVMVectorOffset(MCSymbol *L, unsigned Register,
+                         unsigned RegisterSizeInBits, unsigned MaskRegister,
+                         unsigned MaskRegisterSizeInBits, int64_t Offset,
+                         SMLoc Loc = {}) {
+    VectorOffsetFields Extra{Register, RegisterSizeInBits, Offset, MaskRegister,
+                             MaskRegisterSizeInBits};
+    return MCCFIInstruction(OpLLVMVectorOffset, L, Extra, Loc);
+  }
+
+  /// .cfi_llvm_vector_register_mask Previous value of Register is saved in
+  /// SpillRegister, predicated on the value of MaskRegister.
+  static MCCFIInstruction createLLVMVectorRegisterMask(
+      MCSymbol *L, unsigned Register, unsigned SpillRegister,
+      unsigned SpillRegisterLaneSizeInBits, unsigned MaskRegister,
+      unsigned MaskRegisterSizeInBits, SMLoc Loc = {}) {
+    VectorRegisterMaskFields Extra{
+        Register,     SpillRegister,          SpillRegisterLaneSizeInBits,
+        MaskRegister, MaskRegisterSizeInBits,
+    };
+    return MCCFIInstruction(OpLLVMVectorRegisterMask, L, Extra, Loc);
+  }
+
+  template <class ExtraFieldsTy> ExtraFieldsTy &getExtraFields() {
+    return std::get<ExtraFieldsTy>(ExtraFields);
+  }
+
+  template <class ExtraFieldsTy> const ExtraFieldsTy &getExtraFields() const {
+    return std::get<ExtraFieldsTy>(ExtraFields);
+  }
   /// .cfi_val_offset Previous value of Register is offset Offset from the
   /// current CFA register.
   static MCCFIInstruction createValOffset(MCSymbol *L, unsigned Register,
diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index 79c715e3820a6..4e76aa323eb30 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -1012,6 +1012,24 @@ class LLVM_ABI MCStreamer {
                                SMLoc Loc = {});
   virtual void emitCFIWindowSave(SMLoc Loc = {});
   virtual void emitCFINegateRAState(SMLoc Loc = {});
+  virtual void emitCFILLVMRegisterPair(int64_t Register, int64_t R1,
+                                       int64_t R1SizeInBits, int64_t R2,
+                                       int64_t R2SizeInBits, SMLoc Loc = {});
+  virtual void emitCFILLVMVectorRegisters(
+      int64_t Register,
+      std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs,
+      SMLoc Loc = {});
+  virtual void emitCFILLVMVectorOffset(int64_t Register,
+                                       int64_t RegisterSizeInBits,
+                                       int64_t MaskRegister,
+                                       int64_t MaskRegisterSizeInBits,
+                                       int64_t Offset, SMLoc Loc = {});
+  virtual void
+  emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister,
+                                int64_t SpillRegisterLaneSizeInBits,
+                                int64_t MaskRegister,
+                                int64_t MaskRegisterSizeInBits, SMLoc Loc = {});
+
   virtual void emitCFINegateRAStateWithPC(SMLoc Loc = {});
   virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name);
   virtual void emitCFIValOffset(int64_t Register, int64_t Offset,
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
index 2a146eb15f709..895c18abc56f9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -260,6 +260,39 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const {
   case MCCFIInstruction::OpRestoreState:
     OutStreamer->emitCFIRestoreState(Loc);
     break;
+  case MCCFIInstruction::OpLLVMRegisterPair: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::RegisterPairFields>();
+    OutStreamer->emitCFILLVMRegisterPair(Fields.Register, Fields.Reg1,
+                                         Fields.Reg1SizeInBits, Fields.Reg2,
+                                         Fields.Reg2SizeInBits, Loc);
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisters: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::VectorRegistersFields>();
+    OutStreamer->emitCFILLVMVectorRegisters(Fields.Register,
+                                            Fields.VectorRegisters, Loc);
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorOffset: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::VectorOffsetFields>();
+    OutStreamer->emitCFILLVMVectorOffset(
+        Fields.Register, Fields.RegisterSizeInBits, Fields.MaskRegister,
+        Fields.MaskRegisterSizeInBits, Fields.Offset, Loc);
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisterMask: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>();
+    OutStreamer->emitCFILLVMVectorRegisterMask(
+        Fields.Register, Fields.SpillRegister,
+        Fields.SpillRegisterLaneSizeInBits, Fields.MaskRegister,
+        Fields.MaskRegisterSizeInBits);
+    break;
+  }
+
   case MCCFIInstruction::OpValOffset:
     OutStreamer->emitCFIValOffset(Inst.getRegister(), Inst.getOffset(), Loc);
     break;
diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp
index 14098bc821617..0d60d17da0cf7 100644
--- a/llvm/lib/CodeGen/CFIInstrInserter.cpp
+++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp
@@ -262,6 +262,10 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
       case MCCFIInstruction::OpNegateRAState:
       case MCCFIInstruction::OpNegateRAStateWithPC:
       case MCCFIInstruction::OpGnuArgsSize:
+      case MCCFIInstruction::OpLLVMRegisterPair:
+      case MCCFIInstruction::OpLLVMVectorRegisters:
+      case MCCFIInstruction::OpLLVMVectorOffset:
+      case MCCFIInstruction::OpLLVMVectorRegisterMask:
       case MCCFIInstruction::OpLabel:
       case MCCFIInstruction::OpValOffset:
         break;
diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
index 8b72c295416a2..8ed590669a3b0 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
@@ -240,6 +240,11 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
       .Case("window_save", MIToken::kw_cfi_window_save)
       .Case("negate_ra_sign_state",
             MIToken::kw_cfi_aarch64_negate_ra_sign_state)
+      .Case("llvm_register_pair", MIToken::kw_cfi_llvm_register_pair)
+      .Case("llvm_vector_registers", MIToken::kw_cfi_llvm_vector_registers)
+      .Case("llvm_vector_offset", MIToken::kw_cfi_llvm_vector_offset)
+      .Case("llvm_vector_register_mask",
+            MIToken::kw_cfi_llvm_vector_register_mask)
       .Case("negate_ra_sign_state_with_pc",
             MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc)
       .Case("blockaddress", MIToken::kw_blockaddress)
diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h
index 0627f176b9e00..abac1880f94e0 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.h
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.h
@@ -98,6 +98,10 @@ struct MIToken {
     kw_cfi_undefined,
     kw_cfi_window_save,
     kw_cfi_aarch64_negate_ra_sign_state,
+    kw_cfi_llvm_register_pair,
+    kw_cfi_llvm_vector_registers,
+    kw_cfi_llvm_vector_offset,
+    kw_cfi_llvm_vector_register_mask,
     kw_cfi_aarch64_negate_ra_sign_state_with_pc,
     kw_blockaddress,
     kw_intrinsic,
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 6a464d9dd6886..3618022d89bed 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -484,6 +484,7 @@ class MIParser {
   bool parseDILocation(MDNode *&Expr);
   bool parseMetadataOperand(MachineOperand &Dest);
   bool parseCFIOffset(int &Offset);
+  bool parseCFIUnsigned(unsigned &Value);
   bool parseCFIRegister(unsigned &Reg);
   bool parseCFIAddressSpace(unsigned &AddressSpace);
   bool parseCFIEscapeValues(std::string& Values);
@@ -2475,6 +2476,13 @@ bool MIParser::parseCFIOffset(int &Offset) {
   return false;
 }
 
+bool MIParser::parseCFIUnsigned(unsigned &Value) {
+  if (getUnsigned(Value))
+    return true;
+  lex();
+  return false;
+}
+
 bool MIParser::parseCFIRegister(unsigned &Reg) {
   if (Token.isNot(MIToken::NamedRegister))
     return error("expected a cfi register");
@@ -2608,6 +2616,69 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) {
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state:
     CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
     break;
+  case MIToken::kw_cfi_llvm_register_pair: {
+    unsigned Reg, R1, R2;
+    unsigned R1Size, R2Size;
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(R1) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(R1Size) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(R2) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(R2Size))
+      return true;
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMRegisterPair(
+        nullptr, Reg, R1, R1Size, R2, R2Size));
+    break;
+  }
+  case MIToken::kw_cfi_llvm_vector_registers: {
+    std::vector<MCCFIInstruction::VectorRegisterWithLane> VectorRegisters;
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma))
+      return true;
+    do {
+      unsigned VR;
+      unsigned Lane, Size;
+      if (parseCFIRegister(VR) || expectAndConsume(MIToken::comma) ||
+          parseCFIUnsigned(Lane) || expectAndConsume(MIToken::comma) ||
+          parseCFIUnsigned(Size))
+        return true;
+      VectorRegisters.push_back({VR, Lane, Size});
+    } while (consumeIfPresent(MIToken::comma));
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisters(
+        nullptr, Reg, std::move(VectorRegisters)));
+    break;
+  }
+  case MIToken::kw_cfi_llvm_vector_offset: {
+    unsigned Reg, MaskReg;
+    unsigned RegSize, MaskRegSize;
+    int Offset = 0;
+
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(RegSize) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(MaskReg) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(MaskRegSize) || expectAndConsume(MIToken::comma) ||
+        parseCFIOffset(Offset))
+      return true;
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorOffset(
+        nullptr, Reg, RegSize, MaskReg, MaskRegSize, Offset));
+    break;
+  }
+  case MIToken::kw_cfi_llvm_vector_register_mask: {
+    unsigned Reg, SpillReg, MaskReg;
+    unsigned SpillRegLaneSize, MaskRegSize;
+
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(SpillReg) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(SpillRegLaneSize) ||
+        expectAndConsume(MIToken::comma) || parseCFIRegister(MaskReg) ||
+        expectAndConsume(MIToken::comma) || parseCFIUnsigned(MaskRegSize))
+      return true;
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisterMask(
+        nullptr, Reg, SpillReg, SpillRegLaneSize, MaskReg, MaskRegSize));
+    break;
+  }
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc:
     CFIIndex =
         MF.addFrameInst(MCCFIInstruction::createNegateRAStateWithPC(nullptr));
@@ -2962,6 +3033,10 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx,
   case MIToken::kw_cfi_undefined:
   case MIToken::kw_cfi_window_save:
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state:
+  case MIToken::kw_cfi_llvm_register_pair:
+  case MIToken::kw_cfi_llvm_vector_registers:
+  case MIToken::kw_cfi_llvm_vector_offset:
+  case MIToken::kw_cfi_llvm_vector_register_mask:
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc:
     return parseCFIOperand(Dest);
   case MIToken::kw_blockaddress:
diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp
index bb9c76ff0c729..db802cc6e9024 100644
--- a/llvm/lib/CodeGen/MachineOperand.cpp
+++ b/llvm/lib/CodeGen/MachineOperand.cpp
@@ -778,6 +778,64 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI,
     if (MCSymbol *Label = CFI.getLabel())
       MachineOperand::printSymbol(OS, *Label);
     break;
+  case MCCFIInstruction::OpLLVMRegisterPair: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::RegisterPairFields>();
+
+    OS << "llvm_register_pair ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    OS << ", ";
+    printCFIRegister(Fields.Reg1, OS, TRI);
+    OS << ", " << Fields.Reg1SizeInBits << ", ";
+    printCFIRegister(Fields.Reg2, OS, TRI);
+    OS << ", " << Fields.Reg2SizeInBits;
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisters: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::VectorRegistersFields>();
+
+    OS << "llvm_vector_registers ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    for (auto [Reg, Lane, Size] : Fields.VectorRegisters) {
+      OS << ", ";
+      printCFIRegister(Reg, OS, TRI);
+      OS << ", " << Lane << ", " << Size;
+    }
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorOffset: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::VectorOffsetFields>();
+
+    OS << "llvm_vector_offset ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    OS << ", " << Fields.RegisterSizeInBits << ", ";
+    printCFIRegister(Fields.MaskRegister, OS, TRI);
+    OS << ", " << Fields.MaskRegisterSizeInBits << ", " << Fields.Offset;
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisterMask: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>();
+
+    OS << "llvm_vector_register_mask ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    OS << ", ";
+    printCFIRegister(Fields.SpillRegister, OS, TRI);
+    OS << ", " << Fields.SpillRegisterLaneSizeInBits << ", ";
+    printCFIRegister(Fields.MaskRegister, OS, TRI);
+    OS << ", " << Fields.MaskRegisterSizeInBits;
+    break;
+  }
   case MCCFIInstruction::OpNegateRAStateWithPC:
     OS << "negate_ra_sign_state_with_pc ";
     if (MCSymbol *Label = CFI.getLabel())
diff --git a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp
index bca820fa807c8..4d2d2da8a4445 100644
--- a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp
+++ b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp
@@ -161,6 +161,16 @@ dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) {
     CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(),
                         Directive.getOffset());
     break;
+  case MCCFIInstruction::OpLLVMRegisterPair:
+  case MCCFIInstruction::OpLLVMVectorRegisters:
+  case MCCFIInstruction::OpLLVMVectorOffset:
+  case MCCFIInstruction::OpLLVMVectorRegisterMask:
+    // TODO: These should be pretty straightforward to support, but is low
+    // priority. Similarly the implementation of OpLLVMDefAspaceCfa above
+    // seem incomplete and should be fixed.
+    Context->reportWarning(Directive.getLoc(),
+                           "this directive is not supported, ignoring it");
+    break;
   }
 
   return CFIP;
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index be8c022f39ad1..6c54a9efbf2c6 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -369,6 +369,21 @@ class MCAsmStreamer final : public MCStreamer {
   void emitCFINegateRAState(SMLoc Loc) override;
   void emitCFINegateRAStateWithPC(SMLoc Loc) override;
   void emitCFIReturnColumn(int64_t Register) override;
+  void emitCFILLVMRegisterPair(int64_t Register, int64_t R1, int64_t R1Size,
+                               int64_t R2, int64_t R2Size, SMLoc Loc) override;
+  void emitCFILLVMVectorRegisters(
+      int64_t Register,
+      std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs,
+      SMLoc Loc) override;
+  void emitCFILLVMVectorOffset(int64_t Register, int64_t RegisterSize,
+                               int64_t MaskRegister, int64_t MaskRegisterSize,
+                               int64_t Offset, SMLoc Loc) override;
+  void emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister,
+                                     int64_t SpillRegisterLaneSizeInBits,
+                                     int64_t MaskRegister,
+                                     int64_t MaskRegisterSizeInBits,
+                                     SMLoc Loc) override;
+
   void emitCFILabelDirective(SMLoc Loc, StringRef Name) override;
   void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override;
 
@@ -2101,6 +2116,67 @@ void MCAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2,
   EmitEOL();
 }
 
+void MCAsmStreamer::emitCFILLVMRegisterPair(int64_t Register, int64_t R1,
+                                            int64_t R1Size, int64_t R2,
+                                            int64_...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Oct 22, 2025

@llvm/pr-subscribers-llvm-mc

Author: Scott Linder (slinder1)

Changes

While these can be represented with .cfi_escape, using these pseudo-cfi
instructions makes .s/.mir files more readable, and it is necessary to
support updating registers in CFI instructions (something that the
AMDGPU backend requires).


Patch is 49.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/164721.diff

18 Files Affected:

  • (modified) llvm/include/llvm/MC/MCDwarf.h (+91-1)
  • (modified) llvm/include/llvm/MC/MCStreamer.h (+18)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp (+33)
  • (modified) llvm/lib/CodeGen/CFIInstrInserter.cpp (+4)
  • (modified) llvm/lib/CodeGen/MIRParser/MILexer.cpp (+5)
  • (modified) llvm/lib/CodeGen/MIRParser/MILexer.h (+4)
  • (modified) llvm/lib/CodeGen/MIRParser/MIParser.cpp (+75)
  • (modified) llvm/lib/CodeGen/MachineOperand.cpp (+58)
  • (modified) llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp (+10)
  • (modified) llvm/lib/MC/MCAsmStreamer.cpp (+76)
  • (modified) llvm/lib/MC/MCDwarf.cpp (+186)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+106)
  • (modified) llvm/lib/MC/MCParser/MasmParser.cpp (+4)
  • (modified) llvm/lib/MC/MCStreamer.cpp (+54)
  • (added) llvm/test/DebugInfo/AMDGPU/heterogeneous-dwarf-cfi-directives.s (+57)
  • (added) llvm/test/MC/ELF/cfi-register-pair.s (+56)
  • (added) llvm/test/MC/ELF/cfi-vector-offset.s (+56)
  • (added) llvm/test/MC/ELF/cfi-vector-registers.s (+56)
diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h
index 640d2eeb68d61..e602f03de5ebf 100644
--- a/llvm/include/llvm/MC/MCDwarf.h
+++ b/llvm/include/llvm/MC/MCDwarf.h
@@ -530,6 +530,10 @@ class MCCFIInstruction {
     OpGnuArgsSize,
     OpLabel,
     OpValOffset,
+    OpLLVMRegisterPair,
+    OpLLVMVectorRegisters,
+    OpLLVMVectorOffset,
+    OpLLVMVectorRegisterMask,
   };
 
   // Held in ExtraFields for most common OpTypes, exceptions follow.
@@ -548,10 +552,45 @@ class MCCFIInstruction {
   struct LabelFields {
     MCSymbol *CfiLabel = nullptr;
   };
+  /// Held in ExtraFields when OpLLVMRegisterPair.
+  struct RegisterPairFields {
+    unsigned Register;
+    unsigned Reg1, Reg2;
+    unsigned Reg1SizeInBits, Reg2SizeInBits;
+  };
+  struct VectorRegisterWithLane {
+    unsigned Register;
+    unsigned Lane;
+    unsigned SizeInBits;
+  };
+  /// Held in ExtraFields when OpLLVMVectorRegisters.
+  struct VectorRegistersFields {
+    unsigned Register;
+    std::vector<VectorRegisterWithLane> VectorRegisters;
+  };
+  /// Held in ExtraFields when OpLLVMVectorOffset.
+  struct VectorOffsetFields {
+    unsigned Register;
+    unsigned RegisterSizeInBits;
+    int64_t Offset;
+    unsigned MaskRegister;
+    unsigned MaskRegisterSizeInBits;
+  };
+  /// Held in ExtraFields when OpLLVMVectorRegisterMask.
+  struct VectorRegisterMaskFields {
+    unsigned Register;
+    unsigned SpillRegister;
+    unsigned SpillRegisterLaneSizeInBits;
+    unsigned MaskRegister;
+    unsigned MaskRegisterSizeInBits;
+  };
 
 private:
   MCSymbol *Label;
-  std::variant<CommonFields, EscapeFields, LabelFields> ExtraFields;
+  std::variant<CommonFields, EscapeFields, LabelFields, RegisterPairFields,
+               VectorRegistersFields, VectorOffsetFields,
+               VectorRegisterMaskFields>
+      ExtraFields;
   OpType Operation;
   SMLoc Loc;
 
@@ -694,6 +733,57 @@ class MCCFIInstruction {
     return {OpLabel, L, LabelFields{CfiLabel}, Loc};
   }
 
+  /// .cfi_llvm_register_pair Previous value of Register is saved in R1:R2.
+  static MCCFIInstruction
+  createLLVMRegisterPair(MCSymbol *L, unsigned Register, unsigned R1,
+                         unsigned R1SizeInBits, unsigned R2,
+                         unsigned R2SizeInBits, SMLoc Loc = {}) {
+    RegisterPairFields Extra{Register, R1, R2, R1SizeInBits, R2SizeInBits};
+    return {OpLLVMRegisterPair, L, Extra, Loc};
+  }
+
+  /// .cfi_llvm_vector_registers Previous value of Register is saved in lanes of
+  /// vector registers.
+  static MCCFIInstruction
+  createLLVMVectorRegisters(MCSymbol *L, unsigned Register,
+                            std::vector<VectorRegisterWithLane> VectorRegisters,
+                            SMLoc Loc = {}) {
+    VectorRegistersFields Extra{Register, std::move(VectorRegisters)};
+    return {OpLLVMVectorRegisters, L, std::move(Extra), Loc};
+  }
+
+  /// .cfi_llvm_vector_offset Previous value of Register is saved at Offset from
+  /// CFA. MaskRegister specifies the active lanes of register.
+  static MCCFIInstruction
+  createLLVMVectorOffset(MCSymbol *L, unsigned Register,
+                         unsigned RegisterSizeInBits, unsigned MaskRegister,
+                         unsigned MaskRegisterSizeInBits, int64_t Offset,
+                         SMLoc Loc = {}) {
+    VectorOffsetFields Extra{Register, RegisterSizeInBits, Offset, MaskRegister,
+                             MaskRegisterSizeInBits};
+    return MCCFIInstruction(OpLLVMVectorOffset, L, Extra, Loc);
+  }
+
+  /// .cfi_llvm_vector_register_mask Previous value of Register is saved in
+  /// SpillRegister, predicated on the value of MaskRegister.
+  static MCCFIInstruction createLLVMVectorRegisterMask(
+      MCSymbol *L, unsigned Register, unsigned SpillRegister,
+      unsigned SpillRegisterLaneSizeInBits, unsigned MaskRegister,
+      unsigned MaskRegisterSizeInBits, SMLoc Loc = {}) {
+    VectorRegisterMaskFields Extra{
+        Register,     SpillRegister,          SpillRegisterLaneSizeInBits,
+        MaskRegister, MaskRegisterSizeInBits,
+    };
+    return MCCFIInstruction(OpLLVMVectorRegisterMask, L, Extra, Loc);
+  }
+
+  template <class ExtraFieldsTy> ExtraFieldsTy &getExtraFields() {
+    return std::get<ExtraFieldsTy>(ExtraFields);
+  }
+
+  template <class ExtraFieldsTy> const ExtraFieldsTy &getExtraFields() const {
+    return std::get<ExtraFieldsTy>(ExtraFields);
+  }
   /// .cfi_val_offset Previous value of Register is offset Offset from the
   /// current CFA register.
   static MCCFIInstruction createValOffset(MCSymbol *L, unsigned Register,
diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index 79c715e3820a6..4e76aa323eb30 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -1012,6 +1012,24 @@ class LLVM_ABI MCStreamer {
                                SMLoc Loc = {});
   virtual void emitCFIWindowSave(SMLoc Loc = {});
   virtual void emitCFINegateRAState(SMLoc Loc = {});
+  virtual void emitCFILLVMRegisterPair(int64_t Register, int64_t R1,
+                                       int64_t R1SizeInBits, int64_t R2,
+                                       int64_t R2SizeInBits, SMLoc Loc = {});
+  virtual void emitCFILLVMVectorRegisters(
+      int64_t Register,
+      std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs,
+      SMLoc Loc = {});
+  virtual void emitCFILLVMVectorOffset(int64_t Register,
+                                       int64_t RegisterSizeInBits,
+                                       int64_t MaskRegister,
+                                       int64_t MaskRegisterSizeInBits,
+                                       int64_t Offset, SMLoc Loc = {});
+  virtual void
+  emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister,
+                                int64_t SpillRegisterLaneSizeInBits,
+                                int64_t MaskRegister,
+                                int64_t MaskRegisterSizeInBits, SMLoc Loc = {});
+
   virtual void emitCFINegateRAStateWithPC(SMLoc Loc = {});
   virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name);
   virtual void emitCFIValOffset(int64_t Register, int64_t Offset,
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
index 2a146eb15f709..895c18abc56f9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -260,6 +260,39 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const {
   case MCCFIInstruction::OpRestoreState:
     OutStreamer->emitCFIRestoreState(Loc);
     break;
+  case MCCFIInstruction::OpLLVMRegisterPair: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::RegisterPairFields>();
+    OutStreamer->emitCFILLVMRegisterPair(Fields.Register, Fields.Reg1,
+                                         Fields.Reg1SizeInBits, Fields.Reg2,
+                                         Fields.Reg2SizeInBits, Loc);
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisters: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::VectorRegistersFields>();
+    OutStreamer->emitCFILLVMVectorRegisters(Fields.Register,
+                                            Fields.VectorRegisters, Loc);
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorOffset: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::VectorOffsetFields>();
+    OutStreamer->emitCFILLVMVectorOffset(
+        Fields.Register, Fields.RegisterSizeInBits, Fields.MaskRegister,
+        Fields.MaskRegisterSizeInBits, Fields.Offset, Loc);
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisterMask: {
+    const auto &Fields =
+        Inst.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>();
+    OutStreamer->emitCFILLVMVectorRegisterMask(
+        Fields.Register, Fields.SpillRegister,
+        Fields.SpillRegisterLaneSizeInBits, Fields.MaskRegister,
+        Fields.MaskRegisterSizeInBits);
+    break;
+  }
+
   case MCCFIInstruction::OpValOffset:
     OutStreamer->emitCFIValOffset(Inst.getRegister(), Inst.getOffset(), Loc);
     break;
diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp
index 14098bc821617..0d60d17da0cf7 100644
--- a/llvm/lib/CodeGen/CFIInstrInserter.cpp
+++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp
@@ -262,6 +262,10 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
       case MCCFIInstruction::OpNegateRAState:
       case MCCFIInstruction::OpNegateRAStateWithPC:
       case MCCFIInstruction::OpGnuArgsSize:
+      case MCCFIInstruction::OpLLVMRegisterPair:
+      case MCCFIInstruction::OpLLVMVectorRegisters:
+      case MCCFIInstruction::OpLLVMVectorOffset:
+      case MCCFIInstruction::OpLLVMVectorRegisterMask:
       case MCCFIInstruction::OpLabel:
       case MCCFIInstruction::OpValOffset:
         break;
diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
index 8b72c295416a2..8ed590669a3b0 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
@@ -240,6 +240,11 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
       .Case("window_save", MIToken::kw_cfi_window_save)
       .Case("negate_ra_sign_state",
             MIToken::kw_cfi_aarch64_negate_ra_sign_state)
+      .Case("llvm_register_pair", MIToken::kw_cfi_llvm_register_pair)
+      .Case("llvm_vector_registers", MIToken::kw_cfi_llvm_vector_registers)
+      .Case("llvm_vector_offset", MIToken::kw_cfi_llvm_vector_offset)
+      .Case("llvm_vector_register_mask",
+            MIToken::kw_cfi_llvm_vector_register_mask)
       .Case("negate_ra_sign_state_with_pc",
             MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc)
       .Case("blockaddress", MIToken::kw_blockaddress)
diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h
index 0627f176b9e00..abac1880f94e0 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.h
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.h
@@ -98,6 +98,10 @@ struct MIToken {
     kw_cfi_undefined,
     kw_cfi_window_save,
     kw_cfi_aarch64_negate_ra_sign_state,
+    kw_cfi_llvm_register_pair,
+    kw_cfi_llvm_vector_registers,
+    kw_cfi_llvm_vector_offset,
+    kw_cfi_llvm_vector_register_mask,
     kw_cfi_aarch64_negate_ra_sign_state_with_pc,
     kw_blockaddress,
     kw_intrinsic,
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 6a464d9dd6886..3618022d89bed 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -484,6 +484,7 @@ class MIParser {
   bool parseDILocation(MDNode *&Expr);
   bool parseMetadataOperand(MachineOperand &Dest);
   bool parseCFIOffset(int &Offset);
+  bool parseCFIUnsigned(unsigned &Value);
   bool parseCFIRegister(unsigned &Reg);
   bool parseCFIAddressSpace(unsigned &AddressSpace);
   bool parseCFIEscapeValues(std::string& Values);
@@ -2475,6 +2476,13 @@ bool MIParser::parseCFIOffset(int &Offset) {
   return false;
 }
 
+bool MIParser::parseCFIUnsigned(unsigned &Value) {
+  if (getUnsigned(Value))
+    return true;
+  lex();
+  return false;
+}
+
 bool MIParser::parseCFIRegister(unsigned &Reg) {
   if (Token.isNot(MIToken::NamedRegister))
     return error("expected a cfi register");
@@ -2608,6 +2616,69 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) {
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state:
     CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
     break;
+  case MIToken::kw_cfi_llvm_register_pair: {
+    unsigned Reg, R1, R2;
+    unsigned R1Size, R2Size;
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(R1) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(R1Size) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(R2) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(R2Size))
+      return true;
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMRegisterPair(
+        nullptr, Reg, R1, R1Size, R2, R2Size));
+    break;
+  }
+  case MIToken::kw_cfi_llvm_vector_registers: {
+    std::vector<MCCFIInstruction::VectorRegisterWithLane> VectorRegisters;
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma))
+      return true;
+    do {
+      unsigned VR;
+      unsigned Lane, Size;
+      if (parseCFIRegister(VR) || expectAndConsume(MIToken::comma) ||
+          parseCFIUnsigned(Lane) || expectAndConsume(MIToken::comma) ||
+          parseCFIUnsigned(Size))
+        return true;
+      VectorRegisters.push_back({VR, Lane, Size});
+    } while (consumeIfPresent(MIToken::comma));
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisters(
+        nullptr, Reg, std::move(VectorRegisters)));
+    break;
+  }
+  case MIToken::kw_cfi_llvm_vector_offset: {
+    unsigned Reg, MaskReg;
+    unsigned RegSize, MaskRegSize;
+    int Offset = 0;
+
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(RegSize) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(MaskReg) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(MaskRegSize) || expectAndConsume(MIToken::comma) ||
+        parseCFIOffset(Offset))
+      return true;
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorOffset(
+        nullptr, Reg, RegSize, MaskReg, MaskRegSize, Offset));
+    break;
+  }
+  case MIToken::kw_cfi_llvm_vector_register_mask: {
+    unsigned Reg, SpillReg, MaskReg;
+    unsigned SpillRegLaneSize, MaskRegSize;
+
+    if (parseCFIRegister(Reg) || expectAndConsume(MIToken::comma) ||
+        parseCFIRegister(SpillReg) || expectAndConsume(MIToken::comma) ||
+        parseCFIUnsigned(SpillRegLaneSize) ||
+        expectAndConsume(MIToken::comma) || parseCFIRegister(MaskReg) ||
+        expectAndConsume(MIToken::comma) || parseCFIUnsigned(MaskRegSize))
+      return true;
+
+    CFIIndex = MF.addFrameInst(MCCFIInstruction::createLLVMVectorRegisterMask(
+        nullptr, Reg, SpillReg, SpillRegLaneSize, MaskReg, MaskRegSize));
+    break;
+  }
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc:
     CFIIndex =
         MF.addFrameInst(MCCFIInstruction::createNegateRAStateWithPC(nullptr));
@@ -2962,6 +3033,10 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx,
   case MIToken::kw_cfi_undefined:
   case MIToken::kw_cfi_window_save:
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state:
+  case MIToken::kw_cfi_llvm_register_pair:
+  case MIToken::kw_cfi_llvm_vector_registers:
+  case MIToken::kw_cfi_llvm_vector_offset:
+  case MIToken::kw_cfi_llvm_vector_register_mask:
   case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc:
     return parseCFIOperand(Dest);
   case MIToken::kw_blockaddress:
diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp
index bb9c76ff0c729..db802cc6e9024 100644
--- a/llvm/lib/CodeGen/MachineOperand.cpp
+++ b/llvm/lib/CodeGen/MachineOperand.cpp
@@ -778,6 +778,64 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI,
     if (MCSymbol *Label = CFI.getLabel())
       MachineOperand::printSymbol(OS, *Label);
     break;
+  case MCCFIInstruction::OpLLVMRegisterPair: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::RegisterPairFields>();
+
+    OS << "llvm_register_pair ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    OS << ", ";
+    printCFIRegister(Fields.Reg1, OS, TRI);
+    OS << ", " << Fields.Reg1SizeInBits << ", ";
+    printCFIRegister(Fields.Reg2, OS, TRI);
+    OS << ", " << Fields.Reg2SizeInBits;
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisters: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::VectorRegistersFields>();
+
+    OS << "llvm_vector_registers ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    for (auto [Reg, Lane, Size] : Fields.VectorRegisters) {
+      OS << ", ";
+      printCFIRegister(Reg, OS, TRI);
+      OS << ", " << Lane << ", " << Size;
+    }
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorOffset: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::VectorOffsetFields>();
+
+    OS << "llvm_vector_offset ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    OS << ", " << Fields.RegisterSizeInBits << ", ";
+    printCFIRegister(Fields.MaskRegister, OS, TRI);
+    OS << ", " << Fields.MaskRegisterSizeInBits << ", " << Fields.Offset;
+    break;
+  }
+  case MCCFIInstruction::OpLLVMVectorRegisterMask: {
+    const auto &Fields =
+        CFI.getExtraFields<MCCFIInstruction::VectorRegisterMaskFields>();
+
+    OS << "llvm_vector_register_mask ";
+    if (MCSymbol *Label = CFI.getLabel())
+      MachineOperand::printSymbol(OS, *Label);
+    printCFIRegister(Fields.Register, OS, TRI);
+    OS << ", ";
+    printCFIRegister(Fields.SpillRegister, OS, TRI);
+    OS << ", " << Fields.SpillRegisterLaneSizeInBits << ", ";
+    printCFIRegister(Fields.MaskRegister, OS, TRI);
+    OS << ", " << Fields.MaskRegisterSizeInBits;
+    break;
+  }
   case MCCFIInstruction::OpNegateRAStateWithPC:
     OS << "negate_ra_sign_state_with_pc ";
     if (MCSymbol *Label = CFI.getLabel())
diff --git a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp
index bca820fa807c8..4d2d2da8a4445 100644
--- a/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp
+++ b/llvm/lib/DWARFCFIChecker/DWARFCFIState.cpp
@@ -161,6 +161,16 @@ dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) {
     CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(),
                         Directive.getOffset());
     break;
+  case MCCFIInstruction::OpLLVMRegisterPair:
+  case MCCFIInstruction::OpLLVMVectorRegisters:
+  case MCCFIInstruction::OpLLVMVectorOffset:
+  case MCCFIInstruction::OpLLVMVectorRegisterMask:
+    // TODO: These should be pretty straightforward to support, but is low
+    // priority. Similarly the implementation of OpLLVMDefAspaceCfa above
+    // seem incomplete and should be fixed.
+    Context->reportWarning(Directive.getLoc(),
+                           "this directive is not supported, ignoring it");
+    break;
   }
 
   return CFIP;
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index be8c022f39ad1..6c54a9efbf2c6 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -369,6 +369,21 @@ class MCAsmStreamer final : public MCStreamer {
   void emitCFINegateRAState(SMLoc Loc) override;
   void emitCFINegateRAStateWithPC(SMLoc Loc) override;
   void emitCFIReturnColumn(int64_t Register) override;
+  void emitCFILLVMRegisterPair(int64_t Register, int64_t R1, int64_t R1Size,
+                               int64_t R2, int64_t R2Size, SMLoc Loc) override;
+  void emitCFILLVMVectorRegisters(
+      int64_t Register,
+      std::vector<MCCFIInstruction::VectorRegisterWithLane> VRs,
+      SMLoc Loc) override;
+  void emitCFILLVMVectorOffset(int64_t Register, int64_t RegisterSize,
+                               int64_t MaskRegister, int64_t MaskRegisterSize,
+                               int64_t Offset, SMLoc Loc) override;
+  void emitCFILLVMVectorRegisterMask(int64_t Register, int64_t SpillRegister,
+                                     int64_t SpillRegisterLaneSizeInBits,
+                                     int64_t MaskRegister,
+                                     int64_t MaskRegisterSizeInBits,
+                                     SMLoc Loc) override;
+
   void emitCFILabelDirective(SMLoc Loc, StringRef Name) override;
   void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override;
 
@@ -2101,6 +2116,67 @@ void MCAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2,
   EmitEOL();
 }
 
+void MCAsmStreamer::emitCFILLVMRegisterPair(int64_t Register, int64_t R1,
+                                            int64_t R1Size, int64_t R2,
+                                            int64_...
[truncated]

return false;
}

bool MIParser::parseCFIUnsigned(unsigned &Value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing mir print/parse tests

epilk and others added 2 commits November 4, 2025 23:05
While these can be represented with .cfi_escape, using these pseudo-cfi
instructions makes .s/.mir files more readable, and it is necessary to
support updating registers in CFI instructions (something that the
AMDGPU backend requires).
# RUN: llc -mtriple=amdgcn -mcpu=gfx908 -x=mir -run-pass none -o - | \
# RUN: FileCheck %s

# Verify we can parse and emit these CFI pseudos.
Copy link
Contributor

Choose a reason for hiding this comment

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

This belongs in test/CodeGen/MIR/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants