[llvm] [mc] Add CFI directive to emit val_offset() rules (PR #113971)

Daniel Sanders via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 11 09:28:06 PST 2024


https://github.com/dsandersllvm updated https://github.com/llvm/llvm-project/pull/113971

>From 647234b6a96db5cbb6fd0dba413ea50bce074f22 Mon Sep 17 00:00:00 2001
From: Daniel Sanders <daniel_l_sanders at apple.com>
Date: Wed, 27 Mar 2024 15:06:43 -0700
Subject: [PATCH] [mc] Add CFI directive to emit val_offset() rules

These specify that the value of the given register in the previous frame is
the CFA plus some offset. This isn't very common but can be necessary if the
original value is normally reconstructed from the stack/frame pointer
instead of being saved on the stack and reloaded from there.
---
 bolt/lib/Core/BinaryFunction.cpp              |  3 ++
 llvm/include/llvm/MC/MCDwarf.h                | 13 +++++--
 llvm/include/llvm/MC/MCStreamer.h             |  2 ++
 .../CodeGen/AsmPrinter/AsmPrinterDwarf.cpp    |  3 ++
 llvm/lib/CodeGen/CFIInstrInserter.cpp         |  1 +
 llvm/lib/MC/MCAsmStreamer.cpp                 | 10 ++++++
 llvm/lib/MC/MCDwarf.cpp                       | 19 +++++++++++
 llvm/lib/MC/MCParser/AsmParser.cpp            | 19 +++++++++++
 llvm/lib/MC/MCParser/MasmParser.cpp           |  1 +
 llvm/lib/MC/MCStreamer.cpp                    | 10 ++++++
 llvm/test/MC/AArch64/cfi_val_offset.s         | 34 +++++++++++++++++++
 11 files changed, 113 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/MC/AArch64/cfi_val_offset.s

diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index c12217d549479b..5da777411ba7a1 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -2580,6 +2580,7 @@ struct CFISnapshot {
     case MCCFIInstruction::OpNegateRAStateWithPC:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       break;
     case MCCFIInstruction::OpRememberState:
@@ -2719,6 +2720,7 @@ struct CFISnapshotDiff : public CFISnapshot {
     case MCCFIInstruction::OpNegateRAStateWithPC:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       return false;
     case MCCFIInstruction::OpRememberState:
@@ -2869,6 +2871,7 @@ BinaryFunction::unwindCFIState(int32_t FromState, int32_t ToState,
     case MCCFIInstruction::OpNegateRAStateWithPC:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       break;
     case MCCFIInstruction::OpGnuArgsSize:
diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h
index 1392336968e74a..2fa7d73e1fa25a 100644
--- a/llvm/include/llvm/MC/MCDwarf.h
+++ b/llvm/include/llvm/MC/MCDwarf.h
@@ -518,6 +518,7 @@ class MCCFIInstruction {
     OpNegateRAStateWithPC,
     OpGnuArgsSize,
     OpLabel,
+    OpValOffset,
   };
 
 private:
@@ -699,6 +700,13 @@ class MCCFIInstruction {
     return MCCFIInstruction(OpLabel, L, CfiLabel, Loc);
   }
 
+  /// .cfi_val_offset Previous value of Register is offset Offset from the
+  /// current CFA register.
+  static MCCFIInstruction createValOffset(MCSymbol *L, unsigned Register,
+                                          int64_t Offset, SMLoc Loc = {}) {
+    return MCCFIInstruction(OpValOffset, L, Register, Offset, Loc);
+  }
+
   OpType getOperation() const { return Operation; }
   MCSymbol *getLabel() const { return Label; }
 
@@ -710,7 +718,7 @@ class MCCFIInstruction {
     assert(Operation == OpDefCfa || Operation == OpOffset ||
            Operation == OpRestore || Operation == OpUndefined ||
            Operation == OpSameValue || Operation == OpDefCfaRegister ||
-           Operation == OpRelOffset);
+           Operation == OpRelOffset || Operation == OpValOffset);
     return U.RI.Register;
   }
 
@@ -729,7 +737,8 @@ class MCCFIInstruction {
       return U.RIA.Offset;
     assert(Operation == OpDefCfa || Operation == OpOffset ||
            Operation == OpRelOffset || Operation == OpDefCfaOffset ||
-           Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize);
+           Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize ||
+           Operation == OpValOffset);
     return U.RI.Offset;
   }
 
diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index df2eb9cf136bc9..d12ab4257613a3 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -1024,6 +1024,8 @@ class MCStreamer {
   virtual void emitCFINegateRAState(SMLoc Loc = {});
   virtual void emitCFINegateRAStateWithPC(SMLoc Loc = {});
   virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name);
+  virtual void emitCFIValOffset(int64_t Register, int64_t Offset,
+                                SMLoc Loc = {});
 
   virtual void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc = SMLoc());
   virtual void emitWinCFIEndProc(SMLoc Loc = SMLoc());
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
index 4ca2cf02a26016..2a146eb15f709d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -260,6 +260,9 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const {
   case MCCFIInstruction::OpRestoreState:
     OutStreamer->emitCFIRestoreState(Loc);
     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 4217ec6a1cca8a..be8393cd386746 100644
--- a/llvm/lib/CodeGen/CFIInstrInserter.cpp
+++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp
@@ -263,6 +263,7 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
       case MCCFIInstruction::OpNegateRAStateWithPC:
       case MCCFIInstruction::OpGnuArgsSize:
       case MCCFIInstruction::OpLabel:
+      case MCCFIInstruction::OpValOffset:
         break;
       }
       if (CSRReg || CSROffset) {
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 112a4b3f1e9cd4..d48b384f21cbce 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -376,6 +376,7 @@ class MCAsmStreamer final : public MCStreamer {
   void emitCFINegateRAStateWithPC(SMLoc Loc) override;
   void emitCFIReturnColumn(int64_t Register) override;
   void emitCFILabelDirective(SMLoc Loc, StringRef Name) override;
+  void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override;
 
   void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override;
   void emitWinCFIEndProc(SMLoc Loc) override;
@@ -2177,6 +2178,15 @@ void MCAsmStreamer::emitCFIMTETaggedFrame() {
   EmitEOL();
 }
 
+void MCAsmStreamer::emitCFIValOffset(int64_t Register, int64_t Offset,
+                                     SMLoc Loc) {
+  MCStreamer::emitCFIValOffset(Register, Offset, Loc);
+  OS << "\t.cfi_val_offset ";
+  EmitRegisterName(Register);
+  OS << ", " << Offset;
+  EmitEOL();
+}
+
 void MCAsmStreamer::emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) {
   MCStreamer::emitWinCFIStartProc(Symbol, Loc);
 
diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp
index e058358fb8ad4b..552822e1b1838b 100644
--- a/llvm/lib/MC/MCDwarf.cpp
+++ b/llvm/lib/MC/MCDwarf.cpp
@@ -1503,6 +1503,25 @@ void FrameEmitterImpl::emitCFIInstruction(const MCCFIInstruction &Instr) {
   case MCCFIInstruction::OpLabel:
     Streamer.emitLabel(Instr.getCfiLabel(), Instr.getLoc());
     return;
+  case MCCFIInstruction::OpValOffset: {
+    unsigned Reg = Instr.getRegister();
+    if (!IsEH)
+      Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);
+
+    int Offset = Instr.getOffset();
+    Offset = Offset / dataAlignmentFactor;
+
+    if (Offset < 0) {
+      Streamer.emitInt8(dwarf::DW_CFA_val_offset_sf);
+      Streamer.emitULEB128IntValue(Reg);
+      Streamer.emitSLEB128IntValue(Offset);
+    } else {
+      Streamer.emitInt8(dwarf::DW_CFA_val_offset);
+      Streamer.emitULEB128IntValue(Reg);
+      Streamer.emitULEB128IntValue(Offset);
+    }
+    return;
+  }
   }
   llvm_unreachable("Unhandled case in switch");
 }
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index ecccb228c8c387..505b905617837d 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -523,6 +523,7 @@ class AsmParser : public MCAsmParser {
     DK_CFI_WINDOW_SAVE,
     DK_CFI_LABEL,
     DK_CFI_B_KEY_FRAME,
+    DK_CFI_VAL_OFFSET,
     DK_MACROS_ON,
     DK_MACROS_OFF,
     DK_ALTMACRO,
@@ -626,6 +627,7 @@ class AsmParser : public MCAsmParser {
   bool parseDirectiveCFISignalFrame(SMLoc DirectiveLoc);
   bool parseDirectiveCFIUndefined(SMLoc DirectiveLoc);
   bool parseDirectiveCFILabel(SMLoc DirectiveLoc);
+  bool parseDirectiveCFIValOffset(SMLoc DirectiveLoc);
 
   // macro directives
   bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
@@ -2232,6 +2234,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info,
       return parseDirectiveCFIWindowSave(IDLoc);
     case DK_CFI_LABEL:
       return parseDirectiveCFILabel(IDLoc);
+    case DK_CFI_VAL_OFFSET:
+      return parseDirectiveCFIValOffset(IDLoc);
     case DK_MACROS_ON:
     case DK_MACROS_OFF:
       return parseDirectiveMacrosOnOff(IDVal);
@@ -4531,6 +4535,20 @@ bool AsmParser::parseDirectiveCFILabel(SMLoc Loc) {
   return false;
 }
 
+/// parseDirectiveCFIValOffset
+/// ::= .cfi_val_offset register, offset
+bool AsmParser::parseDirectiveCFIValOffset(SMLoc DirectiveLoc) {
+  int64_t Register = 0;
+  int64_t Offset = 0;
+
+  if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
+      parseAbsoluteExpression(Offset) || parseEOL())
+    return true;
+
+  getStreamer().emitCFIValOffset(Register, Offset, DirectiveLoc);
+  return false;
+}
+
 /// parseDirectiveAltmacro
 /// ::= .altmacro
 /// ::= .noaltmacro
@@ -5603,6 +5621,7 @@ void AsmParser::initializeDirectiveKindMap() {
   DirectiveKindMap[".cfi_label"] = DK_CFI_LABEL;
   DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
   DirectiveKindMap[".cfi_mte_tagged_frame"] = DK_CFI_MTE_TAGGED_FRAME;
+  DirectiveKindMap[".cfi_val_offset"] = DK_CFI_VAL_OFFSET;
   DirectiveKindMap[".macros_on"] = DK_MACROS_ON;
   DirectiveKindMap[".macros_off"] = DK_MACROS_OFF;
   DirectiveKindMap[".macro"] = DK_MACRO;
diff --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp
index a7f37d81f64092..9e947bab2a0157 100644
--- a/llvm/lib/MC/MCParser/MasmParser.cpp
+++ b/llvm/lib/MC/MCParser/MasmParser.cpp
@@ -6750,6 +6750,7 @@ void MasmParser::initializeDirectiveKindMap() {
   // DirectiveKindMap[".cfi_register"] = DK_CFI_REGISTER;
   // DirectiveKindMap[".cfi_window_save"] = DK_CFI_WINDOW_SAVE;
   // DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
+  // DirectiveKindMap[".cfi_val_offset"] = DK_CFI_VAL_OFFSET;
   DirectiveKindMap["macro"] = DK_MACRO;
   DirectiveKindMap["exitm"] = DK_EXITM;
   DirectiveKindMap["endm"] = DK_ENDM;
diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp
index 5474db1315f141..cfef318f502abc 100644
--- a/llvm/lib/MC/MCStreamer.cpp
+++ b/llvm/lib/MC/MCStreamer.cpp
@@ -712,6 +712,16 @@ void MCStreamer::emitCFILabelDirective(SMLoc Loc, StringRef Name) {
     F->Instructions.push_back(MCCFIInstruction::createLabel(Label, Sym, Loc));
 }
 
+void MCStreamer::emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) {
+  MCSymbol *Label = emitCFILabel();
+  MCCFIInstruction Instruction =
+      MCCFIInstruction::createValOffset(Label, Register, Offset, Loc);
+  MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo();
+  if (!CurFrame)
+    return;
+  CurFrame->Instructions.push_back(Instruction);
+}
+
 WinEH::FrameInfo *MCStreamer::EnsureValidWinFrameInfo(SMLoc Loc) {
   const MCAsmInfo *MAI = Context.getAsmInfo();
   if (!MAI->usesWindowsCFI()) {
diff --git a/llvm/test/MC/AArch64/cfi_val_offset.s b/llvm/test/MC/AArch64/cfi_val_offset.s
new file mode 100644
index 00000000000000..e345030c661d6f
--- /dev/null
+++ b/llvm/test/MC/AArch64/cfi_val_offset.s
@@ -0,0 +1,34 @@
+// RUN: llvm-mc -triple aarch64-- -o - %s | FileCheck %s
+// RUN: llvm-mc -triple aarch64-- -filetype=obj -o - %s | llvm-dwarfdump --debug-frame - | FileCheck --check-prefix=DWARF %s
+
+// This test just confirms the .cfi_val_offset directive emits a val_offset()
+// rule. It's not testing anything AArch64 specific, it just needs a targets
+// registers to be able to use the directive.
+example:
+// CHECK:      .cfi_startproc
+  .cfi_startproc
+  add wsp, wsp, 16
+  .cfi_def_cfa wsp, -16
+// CHECK: .cfi_def_cfa wsp, -16
+// DWARF: DW_CFA_advance_loc: 4 to 0x4
+// DWARF: DW_CFA_def_cfa: WSP -16
+  .cfi_val_offset wsp, 0
+// CHECK: .cfi_val_offset wsp, 0
+// DWARF: DW_CFA_val_offset: WSP 0
+  nop
+  sub wsp, wsp, 16
+  .cfi_def_cfa wsp, 0
+// CHECK: .cfi_def_cfa wsp, 0
+// DWARF: DW_CFA_advance_loc: 8 to 0xc
+// DWARF: DW_CFA_def_cfa: WSP +0
+  .cfi_register wsp, wsp
+// CHECK: .cfi_register wsp, wsp
+// DWARF: DW_CFA_register: WSP WSP
+  ret
+  .cfi_endproc
+// CHECK: .cfi_endproc
+
+
+// DWARF: 0x0: CFA=WSP
+// DWARF: 0x4: CFA=WSP-16: WSP=CFA
+// DWARF: 0xc: CFA=WSP: WSP=WSP



More information about the llvm-commits mailing list