[llvm] [SFrames] Implement .cfi_restore, remember_state, and restore_state (PR #159832)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 19 11:45:55 PDT 2025


https://github.com/Sterling-Augustine created https://github.com/llvm/llvm-project/pull/159832

As in the description. Very straightforward.



>From 2b24cc6a3337490f12aac32582b218097d37da42 Mon Sep 17 00:00:00 2001
From: Sterling Augustine <saugustine at google.com>
Date: Fri, 19 Sep 2025 11:44:05 -0700
Subject: [PATCH] [SFrames] Implement .cfi_restore, .cfi_remember_state, and
 .cfi_restore_state

As in the description. Very straightforward.
---
 llvm/lib/MC/MCSFrame.cpp                |  30 +++++--
 llvm/test/MC/ELF/cfi-sframe-fre-cases.s | 110 +++++++++++++++++++-----
 2 files changed, 115 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp
index 066d1a34e1548..ef6700fb1dccf 100644
--- a/llvm/lib/MC/MCSFrame.cpp
+++ b/llvm/lib/MC/MCSFrame.cpp
@@ -111,6 +111,8 @@ struct SFrameFDE {
   MCFragment *Frag;
   // Unwinding fres
   SmallVector<SFrameFRE> FREs;
+  // .cfi_remember_state stack
+  SmallVector<SFrameFRE> SaveState;
 
   SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
       : DFrame(DF), FREStart(FRES), Frag(nullptr) {}
@@ -237,13 +239,31 @@ class SFrameEmitterImpl {
     case MCCFIInstruction::OpAdjustCfaOffset:
       return setCFAOffset(FRE, CFI.getLoc(), FRE.CFAOffset + CFI.getOffset());
     case MCCFIInstruction::OpRememberState:
-      // TODO: Implement. Will use FDE.
+      if (FDE.FREs.size() == 1) {
+        // Error for gas compatibility: If the initial FRE isn't complete,
+        // then any state is incomplete.  FIXME: Dwarf doesn't error here.
+        // Why should sframe?
+        Streamer.getContext().reportWarning(
+            CFI.getLoc(), "skipping SFrame FDE; .cfi_remember_state without "
+                          "prior SFrame FRE state");
+        return false;
+      }
+      FDE.SaveState.push_back(FRE);
       return true;
     case MCCFIInstruction::OpRestore:
-      // TODO: Implement. Will use FDE.
+      // The first FRE generated has the original state.
+      if (CFI.getRegister() == FPReg)
+        FRE.FPOffset = FDE.FREs.front().FPOffset;
+      else if (CFI.getRegister() == RAReg)
+        FRE.RAOffset = FDE.FREs.front().RAOffset;
       return true;
     case MCCFIInstruction::OpRestoreState:
-      // TODO: Implement. Will use FDE.
+      // The cfi parser will have caught unbalanced directives earlier, so a
+      // mismatch here is an implementation error.
+      assert(!FDE.SaveState.empty() &&
+             "cfi_restore_state without cfi_save_state");
+      FRE = FDE.SaveState.back();
+      FDE.SaveState.pop_back();
       return true;
     case MCCFIInstruction::OpEscape:
       // TODO: Implement. Will use FDE.
@@ -394,8 +414,8 @@ class SFrameEmitterImpl {
     // shf_fdeoff. With no sfh_auxhdr, these immediately follow this header.
     Streamer.emitInt32(0);
     // shf_freoff
-    Streamer.emitAbsoluteSymbolDiff(FRESubSectionStart, FDESubSectionStart,
-                                    sizeof(uint32_t));
+    Streamer.emitInt32(FDEs.size() *
+                       sizeof(sframe::FuncDescEntry<endianness::native>));
   }
 
   void emitFDEs() {
diff --git a/llvm/test/MC/ELF/cfi-sframe-fre-cases.s b/llvm/test/MC/ELF/cfi-sframe-fre-cases.s
index 6d9e8c1b6480f..eeaa4021ceefd 100644
--- a/llvm/test/MC/ELF/cfi-sframe-fre-cases.s
+++ b/llvm/test/MC/ELF/cfi-sframe-fre-cases.s
@@ -17,7 +17,7 @@ fde4_fre_offset_sizes:
 # CHECK:        FuncDescEntry [0] {
 # CHECK:          Start FRE Offset: 0
 # CHECK:            FRE Type: Addr1 (0x0)
-	.cfi_startproc
+        .cfi_startproc
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x0
 # CHECK-NEXT:          Return Address Signed: No
@@ -27,9 +27,9 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          RA Offset: -8
         .long 0
 # Uninteresting register no new fre, no effect on cfa
-	.cfi_offset 0, 8
+        .cfi_offset 0, 8
         .long 0
-	.cfi_def_cfa_offset 0x78
+        .cfi_def_cfa_offset 0x78
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x8
 # CHECK-NEXT:          Return Address Signed: No
@@ -37,11 +37,11 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: SP (0x1)
 # CHECK-NEXT:          CFA Offset: 120
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
+        .long 0
 # Uninteresting register no new fre, no effect on cfa
         .cfi_rel_offset 1, 8
         .long 0 
-	.cfi_def_cfa_offset 0x80 
+        .cfi_def_cfa_offset 0x80 
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x10
 # CHECK-NEXT:          Return Address Signed: No
@@ -49,11 +49,11 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: SP (0x1)
 # CHECK-NEXT:          CFA Offset: 128
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
+        .long 0
 # Uninteresting register no new fre, no effect on cfa
         .cfi_val_offset 1, 8
         .long 0
-	.cfi_def_cfa_offset 0x7FFF
+        .cfi_def_cfa_offset 0x7FFF
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x18
 # CHECK-NEXT:          Return Address Signed: No
@@ -61,8 +61,8 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: SP (0x1)
 # CHECK-NEXT:          CFA Offset: 32767
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
-	.cfi_def_cfa_offset 0x8000
+        .long 0
+        .cfi_def_cfa_offset 0x8000
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x1C
 # CHECK-NEXT:          Return Address Signed: No
@@ -70,8 +70,8 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: SP (0x1)
 # CHECK-NEXT:          CFA Offset: 32768
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
-	.cfi_def_cfa_offset 0x8
+        .long 0
+        .cfi_def_cfa_offset 0x8
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x20
 # CHECK-NEXT:          Return Address Signed: No
@@ -79,8 +79,8 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: SP (0x1)
 # CHECK-NEXT:          CFA Offset: 8
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
-	.cfi_adjust_cfa_offset 0x8
+        .long 0
+        .cfi_adjust_cfa_offset 0x8
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x24
 # CHECK-NEXT:          Return Address Signed: No
@@ -88,8 +88,8 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: SP (0x1)
 # CHECK-NEXT:          CFA Offset: 16
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
-	.cfi_def_cfa_register  6  # switch to fp
+        .long 0
+        .cfi_def_cfa_register  6  # switch to fp
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x28
 # CHECK-NEXT:          Return Address Signed: No
@@ -97,10 +97,10 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          Base Register: FP (0x0)
 # CHECK-NEXT:          CFA Offset: 16
 # CHECK-NEXT:          RA Offset: -8
-	.long 0
-	.cfi_offset 7, 32
-	# sp not the cfa but with large offset still changes encoding.
-	.cfi_offset 6, 0x7FF8
+        .long 0
+        .cfi_offset 7, 32
+        # sp not the cfa but with large offset still changes encoding.
+        .cfi_offset 6, 0x7FF8
 # CHECK:        Frame Row Entry {
 # CHECK-NEXT:          Start Address: 0x2C
 # CHECK-NEXT:          Return Address Signed: No
@@ -109,5 +109,75 @@ fde4_fre_offset_sizes:
 # CHECK-NEXT:          CFA Offset: 16
 # CHECK-NEXT:          RA Offset: -8
 # CHECK-NEXT:          FP Offset: 32760
-	.long 0
+        .long 0
+        .cfi_endproc
+
+        .align 1024
+restore_reg:
+# CHECK:        FuncDescEntry [1] {
+# CHECK:          Start FRE Offset: 0x23
+# CHECK-NEXT:         Num FREs: 3
+        .cfi_startproc
+# CHECK:        Frame Row Entry {
+# CHECK-NEXT:        Start Address: 0x400
+# CHECK-NOT        FP Offset{{.*}}
+# CHECK:        }
+        .long 0
+        .cfi_offset 6, 32
+# CHECK      Frame Row Entry {
+# CHECK-NEXT     Start Address: 0x404
+# CHECK:          FP Offset: 32
+          .long 0
+        .cfi_restore 6
+# CHECK:        Frame Row Entry {
+# CHECK-NEXT:        Start Address: 0x408
+# CHECK-NOT        FP Offset{{.*}}
+# CHECK:         }
+        .long 0
+        .cfi_endproc
+
+        .align 1024
+remember_restore_state:
+# CHECK:        FuncDescEntry [2] {
+# CHECK:          Start FRE Offset: 0x2D
+# CHECK-NEXT:         Num FREs: 4
+        .cfi_startproc
+# CHECK:        Frame Row Entry {
+# CHECK-NEXT:        Start Address: 0x800
+# CHECK-NOT        FP Offset{{.*}}
+# CHECK:        }
+        .long 0
+        .cfi_offset 6, 8
+        .cfi_offset 7, 16
+        .cfi_offset 8, 24
+# CHECK:        Frame Row Entry {
+# CHECK-NEXT:      Start Address: 0x804
+# CHECK:          Base Register: SP (0x1)
+# CHECK-NEXT:          CFA Offset: 8
+# CHECK-NEXT:          RA Offset: -8
+# CHECK-NEXT:          FP Offset: 8
+# CHECK-NEXT:        }
+        .long 0
+        .cfi_remember_state
+# CHECK:        Frame Row Entry {
+# CHECK-NEXT:          Start Address: 0x808
+# CHECK:          Base Register: SP (0x1)
+# CHECK-NEXT:          CFA Offset: 8
+# CHECK-NEXT:          RA Offset: -8
+# CHECK-NEXT:          FP Offset: 32
+# CHECK-NEXT:        }
+        .cfi_offset 6, 32
+        .cfi_offset 7, 40
+        .cfi_offset 8, 48
+        .long 0
+# CHECK:        Frame Row Entry {
+# CHECK-NEXT:      Start Address: 0x80C
+# CHECK:          Base Register: SP (0x1)
+# CHECK-NEXT:          CFA Offset: 8
+# CHECK-NEXT:          RA Offset: -8
+# CHECK-NEXT:          FP Offset: 8
+# CHECK-NEXT:        }
+        .cfi_restore_state
+        .long 0
+
         .cfi_endproc



More information about the llvm-commits mailing list