Skip to content

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

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 11, 2024

Conversation

dsandersllvm
Copy link
Collaborator

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.

@llvmbot
Copy link
Member

llvmbot commented Oct 28, 2024

@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-mc

Author: Daniel Sanders (dsandersllvm)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/113971.diff

11 Files Affected:

  • (modified) bolt/lib/Core/BinaryFunction.cpp (+3)
  • (modified) llvm/include/llvm/MC/MCDwarf.h (+11-2)
  • (modified) llvm/include/llvm/MC/MCStreamer.h (+2)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp (+3)
  • (modified) llvm/lib/CodeGen/CFIInstrInserter.cpp (+1)
  • (modified) llvm/lib/MC/MCAsmStreamer.cpp (+10)
  • (modified) llvm/lib/MC/MCDwarf.cpp (+19)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+19)
  • (modified) llvm/lib/MC/MCParser/MasmParser.cpp (+1)
  • (modified) llvm/lib/MC/MCStreamer.cpp (+10)
  • (added) llvm/test/MC/AArch64/cfi_val_offset.s (+34)
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index ef3fba37817daa..4a35e6cc755487 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -2579,6 +2579,7 @@ struct CFISnapshot {
     case MCCFIInstruction::OpNegateRAState:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       break;
     case MCCFIInstruction::OpRememberState:
@@ -2717,6 +2718,7 @@ struct CFISnapshotDiff : public CFISnapshot {
     case MCCFIInstruction::OpNegateRAState:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       return false;
     case MCCFIInstruction::OpRememberState:
@@ -2866,6 +2868,7 @@ BinaryFunction::unwindCFIState(int32_t FromState, int32_t ToState,
     case MCCFIInstruction::OpNegateRAState:
     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 a376ba810ba515..3fc1e92083d31e 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 daad82d26da652..c9e67caac8710e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -261,6 +261,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 b9ad0b4eac9c7b..ce9a5ff3ca39fb 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 4774e5112af535..b9ccc4e238469e 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 d88fd09a1aa07c..46fb5e1e457f8c 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

@llvmbot
Copy link
Member

llvmbot commented Oct 28, 2024

@llvm/pr-subscribers-backend-aarch64

Author: Daniel Sanders (dsandersllvm)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/113971.diff

11 Files Affected:

  • (modified) bolt/lib/Core/BinaryFunction.cpp (+3)
  • (modified) llvm/include/llvm/MC/MCDwarf.h (+11-2)
  • (modified) llvm/include/llvm/MC/MCStreamer.h (+2)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp (+3)
  • (modified) llvm/lib/CodeGen/CFIInstrInserter.cpp (+1)
  • (modified) llvm/lib/MC/MCAsmStreamer.cpp (+10)
  • (modified) llvm/lib/MC/MCDwarf.cpp (+19)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+19)
  • (modified) llvm/lib/MC/MCParser/MasmParser.cpp (+1)
  • (modified) llvm/lib/MC/MCStreamer.cpp (+10)
  • (added) llvm/test/MC/AArch64/cfi_val_offset.s (+34)
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index ef3fba37817daa..4a35e6cc755487 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -2579,6 +2579,7 @@ struct CFISnapshot {
     case MCCFIInstruction::OpNegateRAState:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       break;
     case MCCFIInstruction::OpRememberState:
@@ -2717,6 +2718,7 @@ struct CFISnapshotDiff : public CFISnapshot {
     case MCCFIInstruction::OpNegateRAState:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       return false;
     case MCCFIInstruction::OpRememberState:
@@ -2866,6 +2868,7 @@ BinaryFunction::unwindCFIState(int32_t FromState, int32_t ToState,
     case MCCFIInstruction::OpNegateRAState:
     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 a376ba810ba515..3fc1e92083d31e 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 daad82d26da652..c9e67caac8710e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -261,6 +261,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 b9ad0b4eac9c7b..ce9a5ff3ca39fb 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 4774e5112af535..b9ccc4e238469e 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 d88fd09a1aa07c..46fb5e1e457f8c 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

@llvmbot
Copy link
Member

llvmbot commented Oct 28, 2024

@llvm/pr-subscribers-bolt

Author: Daniel Sanders (dsandersllvm)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/113971.diff

11 Files Affected:

  • (modified) bolt/lib/Core/BinaryFunction.cpp (+3)
  • (modified) llvm/include/llvm/MC/MCDwarf.h (+11-2)
  • (modified) llvm/include/llvm/MC/MCStreamer.h (+2)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp (+3)
  • (modified) llvm/lib/CodeGen/CFIInstrInserter.cpp (+1)
  • (modified) llvm/lib/MC/MCAsmStreamer.cpp (+10)
  • (modified) llvm/lib/MC/MCDwarf.cpp (+19)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+19)
  • (modified) llvm/lib/MC/MCParser/MasmParser.cpp (+1)
  • (modified) llvm/lib/MC/MCStreamer.cpp (+10)
  • (added) llvm/test/MC/AArch64/cfi_val_offset.s (+34)
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index ef3fba37817daa..4a35e6cc755487 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -2579,6 +2579,7 @@ struct CFISnapshot {
     case MCCFIInstruction::OpNegateRAState:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       break;
     case MCCFIInstruction::OpRememberState:
@@ -2717,6 +2718,7 @@ struct CFISnapshotDiff : public CFISnapshot {
     case MCCFIInstruction::OpNegateRAState:
     case MCCFIInstruction::OpLLVMDefAspaceCfa:
     case MCCFIInstruction::OpLabel:
+    case MCCFIInstruction::OpValOffset:
       llvm_unreachable("unsupported CFI opcode");
       return false;
     case MCCFIInstruction::OpRememberState:
@@ -2866,6 +2868,7 @@ BinaryFunction::unwindCFIState(int32_t FromState, int32_t ToState,
     case MCCFIInstruction::OpNegateRAState:
     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 a376ba810ba515..3fc1e92083d31e 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 daad82d26da652..c9e67caac8710e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
@@ -261,6 +261,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 b9ad0b4eac9c7b..ce9a5ff3ca39fb 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 4774e5112af535..b9ccc4e238469e 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 d88fd09a1aa07c..46fb5e1e457f8c 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

Copy link
Collaborator

@adrian-prantl adrian-prantl left a comment

Choose a reason for hiding this comment

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

Mechanically this seems fine. Maybe wait a bit in case someone else has concerns.

@dsandersllvm
Copy link
Collaborator Author

Thanks. I plan to merge this tomorrow if nobody else has comments before then

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.
@dsandersllvm dsandersllvm merged commit 74003f1 into llvm:main Nov 11, 2024
4 of 5 checks passed
@dsandersllvm dsandersllvm deleted the cfi-valoffset branch November 11, 2024 19:38
Groverkss pushed a commit to iree-org/llvm-project that referenced this pull request Nov 15, 2024
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants