diff --git a/gas/Makefile.am b/gas/Makefile.am
index 5f0f24abf8d..f92bfd0a0c8 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -70,6 +70,8 @@ GAS_CFILES = \
 	atof-generic.c \
 	compress-debug.c \
 	cond.c \
+	sframe-opt.c \
+	gen-sframe.c \
 	depend.c \
 	dwarf2dbg.c \
 	dw2gencfi.c \
@@ -105,6 +107,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	compress-debug.h \
+	gen-sframe.h \
 	dwarf2dbg.h \
 	dw2gencfi.h \
 	ecoff.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index 5a4dd702252..f62d2af2afe 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -162,14 +162,15 @@ CONFIG_CLEAN_FILES = gdb.ini .gdbinit po/Makefile.in
 CONFIG_CLEAN_VPATH_FILES =
 PROGRAMS = $(noinst_PROGRAMS)
 am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
-	compress-debug.$(OBJEXT) cond.$(OBJEXT) depend.$(OBJEXT) \
-	dwarf2dbg.$(OBJEXT) dw2gencfi.$(OBJEXT) ecoff.$(OBJEXT) \
-	ehopt.$(OBJEXT) expr.$(OBJEXT) flonum-copy.$(OBJEXT) \
-	flonum-konst.$(OBJEXT) flonum-mult.$(OBJEXT) frags.$(OBJEXT) \
-	hash.$(OBJEXT) input-file.$(OBJEXT) input-scrub.$(OBJEXT) \
-	listing.$(OBJEXT) literal.$(OBJEXT) macro.$(OBJEXT) \
-	messages.$(OBJEXT) output-file.$(OBJEXT) read.$(OBJEXT) \
-	remap.$(OBJEXT) sb.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
+	compress-debug.$(OBJEXT) cond.$(OBJEXT) sframe-opt.$(OBJEXT) \
+	gen-sframe.$(OBJEXT) depend.$(OBJEXT) dwarf2dbg.$(OBJEXT) \
+	dw2gencfi.$(OBJEXT) ecoff.$(OBJEXT) ehopt.$(OBJEXT) \
+	expr.$(OBJEXT) flonum-copy.$(OBJEXT) flonum-konst.$(OBJEXT) \
+	flonum-mult.$(OBJEXT) frags.$(OBJEXT) hash.$(OBJEXT) \
+	input-file.$(OBJEXT) input-scrub.$(OBJEXT) listing.$(OBJEXT) \
+	literal.$(OBJEXT) macro.$(OBJEXT) messages.$(OBJEXT) \
+	output-file.$(OBJEXT) read.$(OBJEXT) remap.$(OBJEXT) \
+	sb.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
 	symbols.$(OBJEXT) write.$(OBJEXT)
 am_as_new_OBJECTS = $(am__objects_1)
 am__dirstamp = $(am__leading_dot)dirstamp
@@ -556,6 +557,8 @@ GAS_CFILES = \
 	atof-generic.c \
 	compress-debug.c \
 	cond.c \
+	sframe-opt.c \
+	gen-sframe.c \
 	depend.c \
 	dwarf2dbg.c \
 	dw2gencfi.c \
@@ -590,6 +593,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	compress-debug.h \
+	gen-sframe.h \
 	dwarf2dbg.h \
 	dw2gencfi.h \
 	ecoff.h \
@@ -1302,6 +1306,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flonum-konst.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flonum-mult.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frags.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen-sframe.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-file.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-scrub.Po@am__quote@
@@ -1316,6 +1321,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sframe-opt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stabs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subsegs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbols.Po@am__quote@
diff --git a/gas/as.h b/gas/as.h
index 1b924071938..23542e452b6 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -261,7 +261,10 @@ enum _relax_state
   rs_cfa,
 
   /* Cross-fragment dwarf2 line number optimization.  */
-  rs_dwarf2dbg
+  rs_dwarf2dbg,
+
+  /* SFrame FRE type selection optimization.  */
+  rs_sframe
 };
 
 typedef enum _relax_state relax_stateT;
@@ -528,6 +531,11 @@ int eh_frame_relax_frag (fragS *);
 void eh_frame_convert_frag (fragS *);
 int generic_force_reloc (struct fix *);
 
+/* SFrame FRE optimization.  */
+int sframe_estimate_size_before_relax (fragS *);
+int sframe_relax_frag (fragS *);
+void sframe_convert_frag (fragS *);
+
 #include "expr.h"		/* Before targ-*.h */
 
 /* This one starts the chain of target dependent headers.  */
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index 98a7ca5878f..db1d38e2e55 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -31,6 +31,7 @@
 #ifdef OBJ_ELF
 #include "elf/aarch64.h"
 #include "dw2gencfi.h"
+#include "gen-sframe.h"
 #endif
 
 #include "dwarf2dbg.h"
@@ -70,6 +71,11 @@ enum aarch64_abi_type
   AARCH64_ABI_ILP32 = 2
 };
 
+unsigned int aarch64_sframe_cfa_sp_reg;
+/* The other CFA base register for SFrame unwind info.  */
+unsigned int aarch64_sframe_cfa_fp_reg;
+unsigned int aarch64_sframe_cfa_ra_reg;
+
 #ifndef DEFAULT_ARCH
 #define DEFAULT_ARCH "aarch64"
 #endif
@@ -8402,6 +8408,35 @@ tc_aarch64_frame_initial_instructions (void)
 {
   cfi_add_CFA_def_cfa (REG_SP, 0);
 }
+
+bool
+aarch64_support_sframe_p (void)
+{
+  /* At this time, SFrame is supported for aarch64 only.  */
+  return (aarch64_abi == AARCH64_ABI_LP64);
+}
+
+bool
+aarch64_sframe_ra_tracking_p (void)
+{
+  return true;
+}
+
+offsetT
+aarch64_sframe_cfa_ra_offset (void)
+{
+  return (offsetT)0;
+}
+
+unsigned char
+aarch64_sframe_get_abi_arch (void)
+{
+  if (aarch64_support_sframe_p ())
+    return sframe_get_abi_arch_callback ("aarch64", target_big_endian);
+  else
+    return 0;
+}
+
 #endif /* OBJ_ELF */
 
 /* Convert REGNAME to a DWARF-2 register number.  */
@@ -9665,6 +9700,12 @@ md_begin (void)
   mach = ilp32_p ? bfd_mach_aarch64_ilp32 : bfd_mach_aarch64;
 
   bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+#ifdef OBJ_ELF
+  /* FIXME - is there a better way to do it ?  */
+  aarch64_sframe_cfa_sp_reg = 31;
+  aarch64_sframe_cfa_fp_reg = 29; /* x29.  */
+  aarch64_sframe_cfa_ra_reg = 30;
+#endif
 }
 
 /* Command line processing.  */
diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
index f5c17523796..da038f5d06a 100644
--- a/gas/config/tc-aarch64.h
+++ b/gas/config/tc-aarch64.h
@@ -235,6 +235,35 @@ struct aarch64_segment_info_type
 /* We want .cfi_* pseudo-ops for generating unwind info.  */
 #define TARGET_USE_CFIPOP              1
 
+/* Whether SFrame unwind info is supported.  */
+extern bool aarch64_support_sframe_p (void);
+#define support_sframe_p aarch64_support_sframe_p
+
+/* The stack-pointer register number for SFrame unwind info.  */
+extern unsigned int aarch64_sframe_cfa_sp_reg;
+#define SFRAME_CFA_SP_REG aarch64_sframe_cfa_sp_reg
+
+/* The base-pointer register number for CFA unwind info.  */
+extern unsigned int aarch64_sframe_cfa_fp_reg;
+#define SFRAME_CFA_FP_REG aarch64_sframe_cfa_fp_reg
+
+/* The return address register number for CFA unwind info.  */
+extern unsigned int aarch64_sframe_cfa_ra_reg;
+#define SFRAME_CFA_RA_REG aarch64_sframe_cfa_ra_reg
+
+/* Specify if RA tracking is needed.  */
+extern bool aarch64_sframe_ra_tracking_p (void);
+#define sframe_ra_tracking_p aarch64_sframe_ra_tracking_p
+
+/* Specify the fixed offset to recover RA from CFA.
+   (useful only when RA tracking is not needed).  */
+extern offsetT aarch64_sframe_cfa_ra_offset (void);
+#define sframe_cfa_ra_offset aarch64_sframe_cfa_ra_offset
+
+/* The abi/arch indentifier for SFrame.  */
+unsigned char aarch64_sframe_get_abi_arch (void);
+#define sframe_get_abi_arch aarch64_sframe_get_abi_arch
+
 /* CFI hooks.  */
 #define tc_regname_to_dw2regnum            tc_aarch64_regname_to_dw2regnum
 #define tc_cfi_frame_initial_instructions  tc_aarch64_frame_initial_instructions
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 01f84cb9a36..502c7cd6f79 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -30,6 +30,7 @@
 #include "subsegs.h"
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
+#include "gen-sframe.h"
 #include "elf/x86-64.h"
 #include "opcodes/i386-init.h"
 #include <limits.h>
@@ -587,6 +588,12 @@ static int use_big_obj = 0;
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 /* 1 if generating code for a shared library.  */
 static int shared = 0;
+
+unsigned int x86_sframe_cfa_sp_reg;
+/* The other CFA base register for SFrame unwind info.  */
+unsigned int x86_sframe_cfa_fp_reg;
+unsigned int x86_sframe_cfa_ra_reg;
+
 #endif
 
 /* 1 for intel syntax,
@@ -3072,6 +3079,10 @@ md_begin (void)
       x86_dwarf2_return_column = 16;
 #endif
       x86_cie_data_alignment = -8;
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+      x86_sframe_cfa_sp_reg = 7;
+      x86_sframe_cfa_fp_reg = 6;
+#endif
     }
   else
     {
@@ -9099,6 +9110,39 @@ x86_cleanup (void)
   if (seg && subseg)
     subseg_set (seg, subseg);
 }
+
+bool
+x86_support_sframe_p (void)
+{
+  /* At this time, SFrame unwind is supported for AMD64 ABI only.  */
+  return (x86_elf_abi == X86_64_ABI);
+}
+
+bool
+x86_sframe_ra_tracking_p (void)
+{
+  /* In AMD64, return address is always stored on the stack at a fixed offset
+     from the CFA (provided via x86_sframe_cfa_ra_offset ()).
+     Do not track explicitly via an SFrame Frame Row Entry.  */
+  return false;
+}
+
+offsetT
+x86_sframe_cfa_ra_offset (void)
+{
+  gas_assert (x86_elf_abi == X86_64_ABI);
+  return (offsetT)-8;
+}
+
+unsigned char
+x86_sframe_get_abi_arch (void)
+{
+  if (x86_support_sframe_p ())
+    return sframe_get_abi_arch_callback ("x86-64", target_big_endian);
+  else
+    return 0;
+}
+
 #endif
 
 static unsigned int
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index a6e096ee110..90037269eea 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -362,6 +362,32 @@ extern bfd_vma x86_64_section_letter (int, const char **);
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 extern void x86_cleanup (void);
 #define md_cleanup() x86_cleanup ()
+
+/* Whether SFrame unwind info is supported.  */
+extern bool x86_support_sframe_p (void);
+#define support_sframe_p x86_support_sframe_p
+
+/* The stack-pointer register number for SFrame unwind info.  */
+extern unsigned int x86_sframe_cfa_sp_reg;
+#define SFRAME_CFA_SP_REG x86_sframe_cfa_sp_reg
+
+/* The frame-pointer register number for CFA unwind info.  */
+extern unsigned int x86_sframe_cfa_fp_reg;
+#define SFRAME_CFA_FP_REG x86_sframe_cfa_fp_reg
+
+/* Specify if RA tracking is needed.  */
+extern bool x86_sframe_ra_tracking_p (void);
+#define sframe_ra_tracking_p x86_sframe_ra_tracking_p
+
+/* Specify the fixed offset to recover RA from CFA.
+   (useful only when RA tracking is not needed).  */
+extern offsetT x86_sframe_cfa_ra_offset (void);
+#define sframe_cfa_ra_offset x86_sframe_cfa_ra_offset
+
+/* The abi/arch indentifier for SFrame.  */
+extern unsigned char x86_sframe_get_abi_arch (void);
+#define sframe_get_abi_arch x86_sframe_get_abi_arch
+
 #endif
 
 #ifdef TE_PE
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index b7403ac45d4..f7a28d32d0c 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -8616,6 +8616,7 @@ unrelaxed_frag_max_size (fragS *fragP)
     case rs_leb128:
     case rs_cfa:
     case rs_dwarf2dbg:
+    case rs_sframe:
       /* No further adjustments needed.  */
       break;
     case rs_machine_dependent:
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 83f49c518d7..32300d73dcb 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -4883,11 +4883,15 @@ Each expression is assembled into the next byte.
 @subsection @code{.cfi_sections @var{section_list}}
 @cindex @code{cfi_sections} directive
 @code{.cfi_sections} may be used to specify whether CFI directives
-should emit @code{.eh_frame} section and/or @code{.debug_frame} section.
-If @var{section_list} is @code{.eh_frame}, @code{.eh_frame} is emitted,
-if @var{section_list} is @code{.debug_frame}, @code{.debug_frame} is emitted.
-To emit both use @code{.eh_frame, .debug_frame}.  The default if this
-directive is not used is @code{.cfi_sections .eh_frame}.
+should emit @code{.eh_frame} section, @code{.debug_frame} section and/or
+@code{.sframe} section.  If @var{section_list} contains @code{.eh_frame},
+@code{.eh_frame} is emitted, if @var{section_list} contains
+@code{.debug_frame}, @code{.debug_frame} is emitted, and finally, if
+@var{section_list} contains @code{.sframe}, @code{.sframe} is emitted.
+To emit multiple sections, specify them together in a list.  For example, to
+emit both @code{.eh_frame} and @code{.debug_frame}, use
+@code{.eh_frame, .debug_frame}.  The default if this directive is not used
+is @code{.cfi_sections .eh_frame}.
 
 On targets that support compact unwinding tables these can be generated
 by specifying @code{.eh_frame_entry} instead of @code{.eh_frame}.
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 6be8cb50495..ffa542153a5 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -23,6 +23,7 @@
 #include "dw2gencfi.h"
 #include "subsegs.h"
 #include "dwarf2dbg.h"
+#include "gen-sframe.h"
 
 #ifdef TARGET_USE_CFIPOP
 
@@ -1231,6 +1232,8 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 	else if (strcmp (name, tc_cfi_section_name) == 0)
 	  sections |= CFI_EMIT_target;
 #endif
+	else if (startswith (name, ".sframe"))
+	    sections |= CFI_EMIT_sframe;
 	else
 	  {
 	    *input_line_pointer = c;
@@ -2471,6 +2474,33 @@ cfi_finish (void)
       flag_traditional_format = save_flag_traditional_format;
     }
 
+  cfi_sections_set = true;
+  // FIXME - remove this commented line once the compiler can specify
+  // .sframe for .cfi_sections directive
+  // if ((all_cfi_sections & CFI_EMIT_sframe) != 0)
+  if (flag_gen_sframe || (all_cfi_sections & CFI_EMIT_sframe) != 0)
+    {
+#ifdef support_sframe_p
+      if (support_sframe_p ())
+	{
+	  segT sframe_seg;
+	  int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
+
+	  if (!SUPPORT_FRAME_LINKONCE)
+	    sframe_seg = get_cfi_seg (NULL, ".sframe",
+					 (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					  | DWARF2_EH_FRAME_READ_ONLY),
+					 alignment);
+	  output_sframe (sframe_seg);
+	}
+      else
+	as_bad (_(".sframe not supported for target"));
+
+#else
+	as_bad (_(".sframe not supported for target"));
+#endif
+    }
+
   cfi_sections_set = true;
   if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
     {
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index d570cdb8db3..3863ec37a53 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -200,5 +200,6 @@ extern struct fde_entry *all_fde_data;
 #define CFI_EMIT_debug_frame            (1 << 1)
 #define CFI_EMIT_target                 (1 << 2)
 #define CFI_EMIT_eh_frame_compact       (1 << 3)
+#define CFI_EMIT_sframe              (1 << 4)
 
 #endif /* DW2GENCFI_H */
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
new file mode 100644
index 00000000000..35b6218290b
--- /dev/null
+++ b/gas/gen-sframe.c
@@ -0,0 +1,1306 @@
+/* gen-sframe.c - Support for generating SFrame section.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "subsegs.h"
+#include "sframe.h"
+#include "gen-sframe.h"
+#include "dw2gencfi.h"
+
+#ifdef support_sframe_p
+
+/* By default, use 32-bit relocations from .sframe into .text.  */
+#ifndef SFRAME_RELOC_SIZE
+# define SFRAME_RELOC_SIZE 4
+#endif
+
+/* Whether frame row entries track RA.
+
+   A target may not need return address tracking for stack unwinding.  If it
+   does need the same, SFRAME_CFA_RA_REG must be defined with the return
+   address register number.  */
+
+#if defined (sframe_ra_tracking_p) && defined (SFRAME_CFA_RA_REG)
+# ifndef SFRAME_FRE_RA_TRACKING
+# define SFRAME_FRE_RA_TRACKING 1
+# endif
+#endif
+
+/* SFrame FRE type selection optimization is an optimization for size.
+
+   There are three flavors of SFrame FRE representation in the binary format:
+     - sframe_frame_row_entry_addr1 where the FRE start address is 1 byte.
+     - sframe_frame_row_entry_addr2 where the FRE start address is 2 bytes.
+     - sframe_frame_row_entry_addr4 where the FRE start address is 4 bytes.
+
+   Note that in the SFrame format, all SFrame FREs of a function use one
+   single representation.  The SFrame FRE type itself is identified via the
+   information in the SFrame FDE function info.
+
+   Now, to select the minimum required one from the list above, one needs to
+   make a decision based on the size (in bytes) of the function.
+
+   As a result, for this optimization, some fragments (generated with a new
+   type rs_sframe) for the SFrame section are fixed up later.
+
+   This optimization (for size) is enabled by default.  */
+
+#ifndef SFRAME_FRE_TYPE_SELECTION_OPT
+# define SFRAME_FRE_TYPE_SELECTION_OPT 1
+#endif
+
+/* Emit a single byte into the current segment.  */
+
+static inline void
+out_one (int byte)
+{
+  FRAG_APPEND_1_CHAR (byte);
+}
+
+/* Emit a two-byte word into the current segment.  */
+
+static inline void
+out_two (int data)
+{
+  md_number_to_chars (frag_more (2), data, 2);
+}
+
+/* Emit a four byte word into the current segment.  */
+
+static inline void
+out_four (int data)
+{
+  md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Get the start address symbol from the DWARF FDE.  */
+
+static symbolS*
+get_dw_fde_start_addrS (const struct fde_entry *dw_fde)
+{
+  return dw_fde->start_address;
+}
+
+/* Get the start address symbol from the DWARF FDE.  */
+
+static symbolS*
+get_dw_fde_end_addrS (const struct fde_entry *dw_fde)
+{
+  return dw_fde->end_address;
+}
+
+/* Callback to create the abi/arch identifier for SFrame section.  */
+
+unsigned char
+sframe_get_abi_arch_callback (const char *target_arch,
+			      int big_endian_p)
+{
+  unsigned char sframe_abi_arch = 0;
+
+  if (strcmp (target_arch, "aarch64") == 0)
+    {
+      sframe_abi_arch = big_endian_p
+	? SFRAME_ABI_AARCH64_ENDIAN_BIG
+	: SFRAME_ABI_AARCH64_ENDIAN_LITTLE;
+    }
+  else if (strcmp (target_arch, "x86-64") == 0)
+    {
+      gas_assert (!big_endian_p);
+      sframe_abi_arch = SFRAME_ABI_AMD64_ENDIAN_LITTLE;
+    }
+  else
+    {
+      /* Other abi/arch are not supported.  Should be unreachable.  */
+      printf (_("SFrame Unsupported abi or arch\n"));
+      abort ();
+    }
+
+  return sframe_abi_arch;
+}
+
+/* SFrame Frame Row Entry (FRE) related functions.  */
+
+static void
+sframe_fre_set_begin_addr (struct sframe_row_entry *fre, symbolS *beginS)
+{
+  fre->pc_begin = beginS;
+}
+
+static void
+sframe_fre_set_end_addr (struct sframe_row_entry *fre, symbolS *endS)
+{
+  fre->pc_end = endS;
+}
+
+static void
+sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre,
+			     unsigned int cfa_base_reg)
+{
+  fre->cfa_base_reg = cfa_base_reg;
+  fre->merge_candidate = false;
+}
+
+static void
+sframe_fre_set_cfa_offset (struct sframe_row_entry *fre,
+			   offsetT cfa_offset)
+{
+  fre->cfa_offset = cfa_offset;
+  fre->merge_candidate = false;
+}
+
+#ifdef SFRAME_FRE_RA_TRACKING
+static void
+sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset)
+{
+  fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
+  fre->ra_offset = ra_offset;
+  fre->merge_candidate = false;
+}
+#endif
+
+static void
+sframe_fre_set_bp_track (struct sframe_row_entry *fre, offsetT bp_offset)
+{
+  fre->bp_loc = SFRAME_FRE_ELEM_LOC_STACK;
+  fre->bp_offset = bp_offset;
+  fre->merge_candidate = false;
+}
+
+/* All stack offset values within an FRE are uniformly encoded in the same
+   number of bytes.  The size of the stack offset values will, however, vary
+   across FREs.  */
+
+#define VALUE_8BIT  0x7f
+#define VALUE_16BIT 0x7fff
+#define VALUE_32BIT 0x7fffffff
+#define VALUE_64BIT 0x7fffffffffffffff
+
+/* Given a signed offset, return the size in bytes needed to represent it.  */
+
+static unsigned int
+get_offset_size_in_bytes (offsetT value)
+{
+  unsigned int size = 0;
+
+  if (value <= VALUE_8BIT && value >= (offsetT)-VALUE_8BIT)
+    size = 1;
+  else if (value <= VALUE_16BIT && value >= (offsetT)-VALUE_16BIT)
+    size = 2;
+  else if (value <= VALUE_32BIT && value >= (offsetT)-VALUE_32BIT)
+    size = 4;
+  else if ((sizeof (offsetT) > 4)
+	   && (value <= (offsetT)VALUE_64BIT && value >= (offsetT)-VALUE_64BIT))
+    size = 8;
+
+  return size;
+}
+
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B  0 /* SFRAME_FRE_OFFSET_1B.  */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B  1 /* SFRAME_FRE_OFFSET_2B.  */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B  2 /* SFRAME_FRE_OFFSET_4B.  */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B  3 /* Not supported in SFrame.  */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B
+
+/* Helper struct for mapping offset size to output functions.  */
+
+struct sframe_fre_offset_func_map
+{
+  unsigned int offset_size;
+  void (*out_func)(int);
+};
+
+/* Given an OFFSET_SIZE, return the size in bytes needed to represent it.  */
+
+static unsigned int
+sframe_fre_offset_func_map_index (unsigned int offset_size)
+{
+  unsigned int index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX;
+
+  switch (offset_size)
+    {
+      case SFRAME_FRE_OFFSET_1B:
+	index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B;
+	break;
+      case SFRAME_FRE_OFFSET_2B:
+	index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B;
+	break;
+      case SFRAME_FRE_OFFSET_4B:
+	index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B;
+	break;
+      default:
+	/* Not supported in SFrame.  */
+	break;
+    }
+
+  return index;
+}
+
+/* Mapping from offset size to the output function to emit the value.  */
+
+static const
+struct sframe_fre_offset_func_map
+fre_offset_func_map[SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX+1] =
+{
+  { SFRAME_FRE_OFFSET_1B, out_one },
+  { SFRAME_FRE_OFFSET_2B, out_two },
+  { SFRAME_FRE_OFFSET_4B, out_four },
+  { -1, NULL } /* Not Supported in SFrame.  */
+};
+
+/* SFrame version specific operations access.  */
+
+static struct sframe_version_ops sframe_ver_ops;
+
+/* SFrame (SFRAME_VERSION_1) set FRE info.  */
+
+static unsigned char
+sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+			unsigned int offset_size)
+{
+  unsigned char fre_info;
+  fre_info = SFRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size);
+  return fre_info;
+}
+
+/* SFrame (SFRAME_VERSION_1) set function info.  */
+static unsigned char
+sframe_v1_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+  unsigned char func_info;
+  func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
+  return func_info;
+}
+
+/* SFrame version specific operations setup.  */
+
+static void
+sframe_set_version (uint32_t sframe_version __attribute__((unused)))
+{
+  sframe_ver_ops.format_version = SFRAME_VERSION_1;
+
+  sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info;
+
+  sframe_ver_ops.set_func_info = sframe_v1_set_func_info;
+}
+
+/* SFrame set FRE info.  */
+
+static unsigned char
+sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+		     unsigned int offset_size)
+{
+  return sframe_ver_ops.set_fre_info (base_reg, num_offsets,
+					 offset_size);
+}
+
+/* SFrame set func info. */
+
+ATTRIBUTE_UNUSED static unsigned char
+sframe_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+  return sframe_ver_ops.set_func_info (fde_type, fre_type);
+}
+
+/* Get the number of SFrame FDEs for the current file.  */
+
+static unsigned int
+get_num_sframe_fdes (void);
+
+/* Get the number of SFrame frame row entries for the current file.  */
+
+static unsigned int
+get_num_sframe_fres (void);
+
+/* Get CFA base register ID as represented in SFrame Frame Row Entry.  */
+
+static unsigned int
+get_fre_base_reg_id (struct sframe_row_entry *sframe_fre)
+{
+  unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg;
+  unsigned fre_base_reg = SFRAME_BASE_REG_SP;
+
+  if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG)
+    fre_base_reg = SFRAME_BASE_REG_FP;
+
+  /* Only one bit is reserved in SFRAME_VERSION_1.  */
+  gas_assert (fre_base_reg == SFRAME_BASE_REG_SP
+	      || fre_base_reg == SFRAME_BASE_REG_FP);
+
+  return fre_base_reg;
+}
+
+/* Get number of offsets necessary for the SFrame Frame Row Entry.  */
+
+static unsigned int
+get_fre_num_offsets (struct sframe_row_entry *sframe_fre)
+{
+  /* Atleast 1 must always be present (to recover CFA).  */
+  unsigned int fre_num_offsets = 1;
+
+  if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+    fre_num_offsets++;
+#ifdef SFRAME_FRE_RA_TRACKING
+  if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+    fre_num_offsets++;
+#endif
+  return fre_num_offsets;
+}
+
+/* Get the minimum necessary offset size (in bytes) for this
+   SFrame frame row entry.  */
+
+static unsigned int
+sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre)
+{
+  unsigned int max_offset_size = 0;
+  unsigned int cfa_offset_size = 0;
+  unsigned int bp_offset_size = 0;
+  unsigned int ra_offset_size = 0;
+
+  unsigned int fre_offset_size = 0;
+
+  /* What size of offsets appear in this frame row entry.  */
+  cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset);
+  if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+    bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset);
+#ifdef SFRAME_FRE_RA_TRACKING
+  if (sframe_ra_tracking_p ()
+      && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+    ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset);
+#endif
+
+  /* Get the maximum size needed to represent the offsets.  */
+  max_offset_size = cfa_offset_size;
+  if (bp_offset_size > max_offset_size)
+    max_offset_size = bp_offset_size;
+  if (ra_offset_size > max_offset_size)
+    max_offset_size = ra_offset_size;
+
+  gas_assert (max_offset_size);
+
+  switch (max_offset_size)
+    {
+    case 1:
+      fre_offset_size = SFRAME_FRE_OFFSET_1B;
+      break;
+    case 2:
+      fre_offset_size = SFRAME_FRE_OFFSET_2B;
+      break;
+    case 4:
+      fre_offset_size = SFRAME_FRE_OFFSET_4B;
+      break;
+    default:
+      /* Offset of size 8 bytes is not supported in SFrame format
+	 version 1.  */
+      printf (_("SFrame unsupported offset value\n"));
+      abort ();
+      break;
+    }
+
+  return fre_offset_size;
+}
+
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+
+/* Create a composite exression CEXP (for SFrame FRE start address) such that:
+
+      exp = <val> OP_absent <width>, where,
+
+    - <val> and <width> are themselves expressionS.
+    - <val> stores the expression which when evaluated gives the value of the
+      start address offset of the FRE.
+    - <width> stores the expression when when evaluated gives the number of
+      bytes needed to encode the start address offset of the FRE.
+
+   The use of OP_absent as the X_op_symbol helps identify this expression
+   later when fragments are fixed up.  */
+
+static void
+create_fre_start_addr_exp (expressionS *cexp, symbolS *fre_pc_begin,
+			   symbolS *fde_start_address,
+			   symbolS *fde_end_address)
+{
+  expressionS val;
+  expressionS width;
+
+  /* val expression stores the FDE start address offset from the start PC
+     of function.  */
+  val.X_op = O_subtract;
+  val.X_add_symbol = fre_pc_begin;
+  val.X_op_symbol = fde_start_address;
+  val.X_add_number = 0;
+
+  /* width expressions stores the size of the function.  This is used later
+     to determine the number of bytes to be used to encode the FRE start
+     address of each FRE of the function.  */
+  width.X_op = O_subtract;
+  width.X_add_symbol = fde_end_address;
+  width.X_op_symbol = fde_start_address;
+  width.X_add_number = 0;
+
+  cexp->X_op = O_absent;
+  cexp->X_add_symbol = make_expr_symbol (&val);
+  cexp->X_op_symbol = make_expr_symbol (&width);
+  cexp->X_add_number = 0;
+}
+
+#endif
+
+static void
+output_sframe_row_entry (symbolS *fde_start_addr,
+			 symbolS *fde_end_addr,
+			 struct sframe_row_entry *sframe_fre)
+{
+  unsigned char fre_info;
+  unsigned int fre_num_offsets;
+  unsigned int fre_offset_size;
+  unsigned int fre_base_reg;
+  expressionS exp;
+  unsigned int fre_addr_size;
+
+  unsigned int index = 0;
+  unsigned int fre_write_offsets = 0;
+
+  fre_addr_size = 4; /* 4 bytes by default.   FIXME tie it to fre_type? */
+
+  /* SFrame FRE Start Address.  */
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+  create_fre_start_addr_exp (&exp, sframe_fre->pc_begin, fde_start_addr,
+			     fde_end_addr);
+  frag_grow (fre_addr_size);
+  frag_var (rs_sframe, fre_addr_size, 0, (relax_substateT) 0,
+	    make_expr_symbol (&exp), 0, (char *) frag_now);
+#else
+  gas_assert (fde_end_addr);
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = sframe_fre->pc_begin; /* to.  */
+  exp.X_op_symbol = fde_start_addr; /* from.  */
+  exp.X_add_number = 0;
+  emit_expr (&exp, fre_addr_size);
+#endif
+
+  /* Create the fre_info using the CFA base register, number of offsets and max
+     size of offset in this frame row entry.  */
+  fre_base_reg = get_fre_base_reg_id (sframe_fre);
+  fre_num_offsets = get_fre_num_offsets (sframe_fre);
+  fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
+  fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
+				     fre_offset_size);
+  out_one (fre_info);
+
+  index = sframe_fre_offset_func_map_index (fre_offset_size);
+  gas_assert (index < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
+
+  /* Write out the offsets in order - cfa, bp, ra.  */
+  fre_offset_func_map[index].out_func (sframe_fre->cfa_offset);
+  fre_write_offsets++;
+
+  if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+    {
+      fre_offset_func_map[index].out_func (sframe_fre->bp_offset);
+      fre_write_offsets++;
+    }
+
+#ifdef SFRAME_FRE_RA_TRACKING
+  if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+    {
+      fre_offset_func_map[index].out_func (sframe_fre->ra_offset);
+      fre_write_offsets++;
+    }
+#endif
+  /* Check if the expected number offsets have been written out
+     in this FRE.  */
+  gas_assert (fre_write_offsets == fre_num_offsets);
+}
+
+static void
+output_sframe_funcdesc (symbolS *start_of_fre_section,
+			symbolS *fre_symbol,
+			struct sframe_func_entry *sframe_fde)
+{
+  expressionS exp;
+  unsigned int addr_size;
+  symbolS *dw_fde_start_addrS, *dw_fde_end_addrS;
+
+  addr_size = SFRAME_RELOC_SIZE;
+  dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde);
+  dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde);
+
+  /* Start address of the function.  */
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = dw_fde_start_addrS; /* to location.  */
+  exp.X_op_symbol = symbol_temp_new_now (); /* from location.  */
+  exp.X_add_number = 0;
+  emit_expr (&exp, addr_size);
+
+  /* Size of the function in bytes.  */
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = dw_fde_end_addrS;
+  exp.X_op_symbol = dw_fde_start_addrS;
+  exp.X_add_number = 0;
+  emit_expr (&exp, addr_size);
+
+  /* Offset to the first frame row entry.  */
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = fre_symbol; /* Minuend.  */
+  exp.X_op_symbol = start_of_fre_section; /* Subtrahend.  */
+  exp.X_add_number = 0;
+  emit_expr (&exp, addr_size);
+
+  /* Number of FREs.  */
+  out_four (sframe_fde->num_fres);
+
+  /* SFrame FDE function info.  */
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+  expressionS width;
+  width.X_op = O_subtract;
+  width.X_add_symbol = dw_fde_end_addrS;
+  width.X_op_symbol = dw_fde_start_addrS;
+  width.X_add_number = 0;
+  frag_grow (1); /* Size of func info is unsigned char.  */
+  frag_var (rs_sframe, 1, 0, (relax_substateT) 0,
+	    make_expr_symbol (&width), 0, (char *) frag_now);
+#else
+  unsigned char func_info;
+  func_info = sframe_set_func_info (SFRAME_FDE_TYPE_PCINC,
+				    SFRAME_FRE_TYPE_ADDR4);
+  out_one (func_info);
+#endif
+}
+
+static void
+output_sframe_internal (void)
+{
+  expressionS exp;
+  unsigned int i = 0;
+
+  symbolS *end_of_frame_hdr;
+  symbolS *end_of_frame_section;
+  symbolS *start_of_func_desc_section;
+  symbolS *start_of_fre_section;
+  struct sframe_func_entry *sframe_fde;
+  struct sframe_row_entry *sframe_fre;
+  unsigned char abi_arch = 0;
+  int fixed_bp_offset = SFRAME_CFA_FIXED_FP_INVALID;
+  int fixed_ra_offset = SFRAME_CFA_FIXED_RA_INVALID;
+  unsigned int addr_size;
+
+  addr_size = SFRAME_RELOC_SIZE;
+
+  /* The function desciptor entries as dumped by the assembler are not
+     sorted on PCs.  */
+  unsigned char sframe_flags = 0;
+  sframe_flags |= !SFRAME_F_FDE_SORTED;
+
+  unsigned int num_fdes = get_num_sframe_fdes ();
+  unsigned int num_fres = get_num_sframe_fres ();
+  symbolS **fre_symbols = XNEWVEC (symbolS *, num_fres);
+  for (i = 0; i < num_fres; i++)
+    fre_symbols[i] = symbol_temp_make ();
+
+  end_of_frame_hdr = symbol_temp_make ();
+  start_of_fre_section = symbol_temp_make ();
+  start_of_func_desc_section = symbol_temp_make ();
+  end_of_frame_section = symbol_temp_make ();
+
+  /* Output the preamble of SFrame section.  */
+  out_two (SFRAME_MAGIC);
+  out_one (SFRAME_VERSION);
+  out_one (sframe_flags);
+  /* abi/arch.  */
+#ifdef sframe_get_abi_arch
+  abi_arch = sframe_get_abi_arch ();
+#endif
+  gas_assert (abi_arch);
+  out_one (abi_arch);
+
+  /* Offset for the BP register from CFA.  Neither of the AMD64 or AAPCS64
+     ABIs have a fixed offset for the BP register from the CFA.  This may be
+     useful in future (but not without additional support in the toolchain)
+     for specialized handling/encoding for cases where, for example,
+     -fno-omit-frame-pointer is used.  */
+  out_one (fixed_bp_offset);
+
+  /* Offset for the return address from CFA is fixed for some ABIs
+     (e.g., AMD64), output a zero otherwise.  */
+#ifdef SFRAME_FRE_RA_TRACKING
+  if (!sframe_ra_tracking_p ())
+    fixed_ra_offset = sframe_cfa_ra_offset ();
+#endif
+  out_one (fixed_ra_offset);
+
+  /* None of the AMD64, or AARCH64 ABIs need the auxilliary header.
+     When the need does arise to use this field, the appropriate backend
+     must provide this information.  */
+  out_one (0); /* Auxilliary SFrame header length.  */
+
+  out_four (num_fdes); /* Number of FDEs.  */
+  out_four (num_fres); /* Number of FREs.  */
+
+  /* FRE sub-section len.  */
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = end_of_frame_section;
+  exp.X_op_symbol = start_of_fre_section;
+  exp.X_add_number = 0;
+  emit_expr (&exp, addr_size);
+
+  /* Offset of Function Index sub-section.  */
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = end_of_frame_hdr;
+  exp.X_op_symbol = start_of_func_desc_section;
+  exp.X_add_number = 0;
+  emit_expr (&exp, addr_size);
+
+  /* Offset of FRE sub-section.  */
+  exp.X_op = O_subtract;
+  exp.X_add_symbol = start_of_fre_section;
+  exp.X_op_symbol = end_of_frame_hdr;
+  exp.X_add_number = 0;
+  emit_expr (&exp, addr_size);
+
+  symbol_set_value_now (end_of_frame_hdr);
+  symbol_set_value_now (start_of_func_desc_section);
+
+  /* Output the SFrame function descriptor entries.  */
+  i = 0;
+  for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next)
+    {
+      output_sframe_funcdesc (start_of_fre_section,
+			      fre_symbols[i], sframe_fde);
+      i += sframe_fde->num_fres;
+    }
+
+  symbol_set_value_now (start_of_fre_section);
+
+  /* Output the SFrame FREs.  */
+  i = 0;
+  sframe_fde = all_sframe_fdes;
+
+  for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next)
+    {
+      for (sframe_fre = sframe_fde->sframe_fres;
+	   sframe_fre;
+	   sframe_fre = sframe_fre->next)
+	{
+	  symbol_set_value_now (fre_symbols[i]);
+	  output_sframe_row_entry (get_dw_fde_start_addrS (sframe_fde->dw_fde),
+				   get_dw_fde_end_addrS (sframe_fde->dw_fde),
+				   sframe_fre);
+	  i++;
+	}
+    }
+
+  symbol_set_value_now (end_of_frame_section);
+
+  gas_assert (i == num_fres);
+
+  free (fre_symbols);
+  fre_symbols = NULL;
+}
+
+/* List of SFrame FDE entries.  */
+
+struct sframe_func_entry *all_sframe_fdes;
+
+/* Tail of the list to add to.  */
+
+static struct sframe_func_entry **last_sframe_fde = &all_sframe_fdes;
+
+static unsigned int
+get_num_sframe_fdes (void)
+{
+  struct sframe_func_entry *sframe_fde;
+  unsigned int total_fdes = 0;
+
+  for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next)
+    total_fdes++;
+
+  return total_fdes;
+}
+
+/* Get the total number of SFrame row entries across the FDEs.  */
+
+static unsigned int
+get_num_sframe_fres (void)
+{
+  struct sframe_func_entry *sframe_fde;
+  unsigned int total_fres = 0;
+
+  for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next)
+    total_fres += sframe_fde->num_fres;
+
+  return total_fres;
+}
+
+/* Allocate an SFrame FDE.  */
+
+static struct sframe_func_entry*
+sframe_fde_alloc (void)
+{
+  struct sframe_func_entry *sframe_fde = XCNEW (struct sframe_func_entry);
+  return sframe_fde;
+}
+
+/* Link the SFrame FDE in.  */
+
+static int
+sframe_fde_link (struct sframe_func_entry *sframe_fde)
+{
+  *last_sframe_fde = sframe_fde;
+  last_sframe_fde = &sframe_fde->next;
+
+  return 0;
+}
+
+/* Free up the SFrame FDE.  */
+
+static void
+sframe_fde_free (struct sframe_func_entry *sframe_fde)
+{
+  XDELETE (sframe_fde);
+  sframe_fde = NULL;
+}
+
+/* SFrame translation context functions.  */
+
+/* Allocate a new SFrame translation context.  */
+
+static struct sframe_xlate_ctx*
+sframe_xlate_ctx_alloc (void)
+{
+  struct sframe_xlate_ctx* xlate_ctx = XCNEW (struct sframe_xlate_ctx);
+  return xlate_ctx;
+}
+
+/* Initialize the given SFrame translation context.  */
+
+static void
+sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx)
+{
+  xlate_ctx->dw_fde = NULL;
+  xlate_ctx->first_fre = NULL;
+  xlate_ctx->last_fre = NULL;
+  xlate_ctx->cur_fre = NULL;
+  xlate_ctx->remember_fre = NULL;
+  xlate_ctx->num_xlate_fres = 0;
+}
+
+/* Cleanup the given SFrame translation context.  */
+
+static void
+sframe_xlate_ctx_cleanup (struct sframe_xlate_ctx *xlate_ctx)
+{
+  struct sframe_row_entry *fre, *fre_next;
+
+  if (xlate_ctx->num_xlate_fres)
+  {
+    fre = xlate_ctx->first_fre;
+    while (fre)
+    {
+      fre_next = fre->next;
+      XDELETE (fre);
+      fre = fre_next;
+    }
+  }
+
+  sframe_xlate_ctx_init (xlate_ctx);
+}
+
+/* Transfer the state from the SFrame translation context to the SFrame FDE.  */
+
+static void
+sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx,
+			   struct sframe_func_entry *sframe_fde)
+{
+  sframe_fde->dw_fde = xlate_ctx->dw_fde;
+  sframe_fde->sframe_fres = xlate_ctx->first_fre;
+  sframe_fde->num_fres = xlate_ctx->num_xlate_fres;
+}
+
+static struct sframe_row_entry*
+sframe_row_entry_new (void)
+{
+  struct sframe_row_entry *fre = XCNEW (struct sframe_row_entry);
+  /* Reset cfa_base_reg to -1.  A value of 0 will imply some valid register
+     for the supported arches.  */
+  fre->cfa_base_reg = -1;
+  fre->merge_candidate = true;
+
+  return fre;
+}
+
+/* Add the given FRE in the list of frame row entries in the given FDE
+   translation context.  */
+
+static void
+sframe_xlate_ctx_add_fre (struct sframe_xlate_ctx *xlate_ctx,
+			 struct sframe_row_entry *fre)
+{
+  gas_assert (xlate_ctx && fre);
+
+  /* Add the frame row entry.  */
+  if (!xlate_ctx->first_fre)
+    xlate_ctx->first_fre = fre;
+  else if (xlate_ctx->last_fre)
+    xlate_ctx->last_fre->next = fre;
+
+  xlate_ctx->last_fre = fre;
+
+  /* Keep track of the total number of SFrame frame row entries.  */
+  xlate_ctx->num_xlate_fres++;
+}
+
+/* A SFrame Frame Row Entry is self-sufficient in terms of unwind information
+   for a given PC.  It contains information assimilated from multiple CFI
+   instructions, and hence, a new SFrame FRE is initialized with the data from
+   the previous known FRE, if any.
+
+   Understandably, not all information (especially the instruction begin
+   and end boundaries) needs to be relayed.  Hence, the caller of this API
+   must set the pc_begin and pc_end as applicable.  */
+
+static void
+sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
+			     struct sframe_row_entry *prev_fre)
+{
+  gas_assert (prev_fre);
+  cur_fre->cfa_base_reg = prev_fre->cfa_base_reg;
+  cur_fre->cfa_offset = prev_fre->cfa_offset;
+  cur_fre->bp_loc = prev_fre->bp_loc;
+  cur_fre->bp_offset = prev_fre->bp_offset;
+  cur_fre->ra_loc = prev_fre->ra_loc;
+  cur_fre->ra_offset = prev_fre->ra_offset;
+}
+
+static int
+sframe_xlate_do_advance_loc (struct sframe_xlate_ctx *xlate_ctx,
+			     struct cfi_insn_data *cfi_insn)
+{
+  struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+  /* Get the scratchpad FRE currently being updated as the cfi_insn's
+     get interpreted.  This FRE eventually gets linked in into the
+     list of FREs for the specific function.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  if (cur_fre)
+    {
+      if (!cur_fre->merge_candidate)
+	{
+	  sframe_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+	  sframe_xlate_ctx_add_fre (xlate_ctx, cur_fre);
+	  last_fre = xlate_ctx->last_fre;
+
+	  xlate_ctx->cur_fre = sframe_row_entry_new ();
+	  cur_fre = xlate_ctx->cur_fre;
+
+	  if (last_fre)
+	    sframe_row_entry_initialize (cur_fre, last_fre);
+	}
+      else
+	{
+	  sframe_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2);
+	  gas_assert (last_fre->merge_candidate == false);
+	}
+    }
+  else
+    {
+      xlate_ctx->cur_fre = sframe_row_entry_new ();
+      cur_fre = xlate_ctx->cur_fre;
+    }
+
+  gas_assert (cur_fre);
+  sframe_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+  return 0;
+}
+
+static int
+sframe_xlate_do_def_cfa (struct sframe_xlate_ctx *xlate_ctx,
+			 struct cfi_insn_data *cfi_insn)
+
+{
+  /* Get the scratchpad FRE.  This FRE will eventually get linked in.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+  if (!cur_fre)
+  {
+    xlate_ctx->cur_fre = sframe_row_entry_new ();
+    cur_fre = xlate_ctx->cur_fre;
+    sframe_fre_set_begin_addr (cur_fre,
+			       get_dw_fde_start_addrS (xlate_ctx->dw_fde));
+  }
+  /* Define the current CFA rule to use the provided register and
+     offset.  */
+  sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+  sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset);
+  cur_fre->merge_candidate = false;
+
+  return 0;
+}
+
+static int
+sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx,
+				  struct cfi_insn_data *cfi_insn)
+{
+  struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+  /* Get the scratchpad FRE.  This FRE will eventually get linked in.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+  gas_assert (cur_fre);
+  /* Define the current CFA rule to use the provided register (but to
+     keep the old offset).  */
+  sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+  sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset);
+  cur_fre->merge_candidate = false;
+
+  return 0;
+}
+
+static int
+sframe_xlate_do_def_cfa_offset (struct sframe_xlate_ctx *xlate_ctx,
+				struct cfi_insn_data *cfi_insn)
+{
+  /* The scratchpad FRE currently being updated with each cfi_insn
+     being interpreted.  This FRE eventually gets linked in into the
+     list of FREs for the specific function.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  gas_assert (cur_fre);
+  /*  Define the current CFA rule to use the provided offset (but to keep
+      the old register).  However, if the old register is not FP/SP,
+      skip creating SFrame unwind info for the function. */
+  if ((cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG)
+      || (cur_fre->cfa_base_reg == SFRAME_CFA_SP_REG))
+    {
+      sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.i);
+      cur_fre->merge_candidate = false;
+    }
+  else
+    return -1;
+
+  return 0;
+}
+
+static int
+sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx,
+			struct cfi_insn_data *cfi_insn)
+{
+  /* The scratchpad FRE currently being updated with each cfi_insn
+     being interpreted.  This FRE eventually gets linked in into the
+     list of FREs for the specific function.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  gas_assert (cur_fre);
+  /* Change the rule for the register indicated by the register number to
+     be the specified offset.  */
+  if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+    {
+      gas_assert (!cur_fre->base_reg);
+      sframe_fre_set_bp_track (cur_fre, cfi_insn->u.ri.offset);
+      cur_fre->merge_candidate = false;
+    }
+#ifdef SFRAME_FRE_RA_TRACKING
+  else if (sframe_ra_tracking_p ()
+	   && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+    {
+      sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
+      cur_fre->merge_candidate = false;
+    }
+#endif
+  /* This is used to track changes to non-rsp registers, skip all others
+     except FP / RA for now.  */
+  return 0;
+}
+
+static int
+sframe_xlate_do_val_offset (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
+			    struct cfi_insn_data *cfi_insn)
+{
+  /* Previous value of register is CFA + offset.  However, if the specified
+     register is not interesting (FP or RA reg), the current DW_CFA_val_offset
+     instruction can be safely skipped without sacrificing the asynchonicity of
+     unwind information.  */
+  if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+    return 1; /* Not represented.  */
+#ifdef SFRAME_FRE_RA_TRACKING
+  else if (sframe_ra_tracking_p ()
+	   && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+    return 1; /* Not represented.  */
+#endif
+
+  /* Safe to skip.  */
+  return 0;
+}
+
+
+static int
+sframe_xlate_do_remember_state (struct sframe_xlate_ctx *xlate_ctx)
+{
+  struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+
+  /* If there is no FRE state to remember, nothing to do here.  Return
+     early with non-zero error code, this will cause no SFrame unwind info
+     for the function involved.  */
+  if (!last_fre)
+    return -1;
+
+  if (!xlate_ctx->remember_fre)
+    xlate_ctx->remember_fre = sframe_row_entry_new ();
+  sframe_row_entry_initialize (xlate_ctx->remember_fre, last_fre);
+
+  return 0;
+}
+
+static int
+sframe_xlate_do_restore_state (struct sframe_xlate_ctx *xlate_ctx)
+{
+  /* The scratchpad FRE currently being updated with each cfi_insn
+     being interpreted.  This FRE eventually gets linked in into the
+     list of FREs for the specific function.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  gas_assert (xlate_ctx->remember_fre);
+  gas_assert (cur_fre && cur_fre->merge_candidate);
+
+  /* Get the CFA state from the DW_CFA_remember_state insn.  */
+  sframe_row_entry_initialize (cur_fre, xlate_ctx->remember_fre);
+  /* The PC boundaries of the current SFrame FRE are updated
+     via other machinery.  */
+  cur_fre->merge_candidate = false;
+  return 0;
+}
+
+static int
+sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx,
+			 struct cfi_insn_data *cfi_insn)
+{
+  struct sframe_row_entry *cie_fre = xlate_ctx->first_fre;
+  /* The scratchpad FRE currently being updated with each cfi_insn
+     being interpreted.  This FRE eventually gets linked in into the
+     list of FREs for the specific function.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  /* Change the rule for the indicated register to the rule assigned to
+     it by the initial_instructions in the CIE.  */
+  gas_assert (cie_fre);
+  /* SFrame FREs track only CFA and FP / RA for backtracing purposes;
+     skip the other .cfi_restore directives.  */
+  if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+    {
+      gas_assert (cur_fre);
+      cur_fre->bp_loc = cie_fre->bp_loc;
+      cur_fre->bp_offset = cie_fre->bp_offset;
+      cur_fre->merge_candidate = false;
+    }
+#ifdef SFRAME_FRE_RA_TRACKING
+  else if (sframe_ra_tracking_p ()
+	   && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+    {
+      gas_assert (cur_fre);
+      cur_fre->ra_loc = cie_fre->ra_loc;
+      cur_fre->ra_offset = cie_fre->ra_offset;
+      cur_fre->merge_candidate = false;
+    }
+#endif
+  return 0;
+}
+
+/* Process CFI_INSN and update the translation context with the FRE
+   information.
+
+   Returns an error code if CFI_INSN is not successfully processed.  */
+
+static int
+sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
+		    struct cfi_insn_data *cfi_insn)
+{
+  int err = 0;
+
+  /* Atleast one cfi_insn per FDE is expected.  */
+  gas_assert (cfi_insn);
+  int op = cfi_insn->insn;
+
+  switch (op)
+    {
+    case DW_CFA_advance_loc:
+      err = sframe_xlate_do_advance_loc (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_def_cfa:
+      err = sframe_xlate_do_def_cfa (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_def_cfa_register:
+      err = sframe_xlate_do_def_cfa_register (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_def_cfa_offset:
+      err = sframe_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_offset:
+      err = sframe_xlate_do_offset (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_val_offset:
+      err = sframe_xlate_do_val_offset (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_remember_state:
+      err = sframe_xlate_do_remember_state (xlate_ctx);
+      break;
+    case DW_CFA_restore_state:
+      err = sframe_xlate_do_restore_state (xlate_ctx);
+      break;
+    case DW_CFA_restore:
+      err = sframe_xlate_do_restore (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_undefined:
+    case DW_CFA_same_value:
+      break;
+    default:
+      {
+	/* Other CFI opcodes are not processed at this time.
+	   These do not impact the coverage of the basic stack unwinding
+	   information as conveyed in the SFrame format.
+	    - DW_CFA_register,
+	    - ...
+
+	   Following skipped operations do, however, impact the asynchronicity:
+	     - CFI_escape,
+	     - DW_CFA_GNU_window_save,
+	     - DW_CFA_AARCH64_negate_ra_state (multiplexed with
+	       DW_CFA_GNU_window_save)  */
+
+	err = 1;
+	// printf (_("SFrame Unsupported or unknown Dwarf CFI number: %#x\n"), op);
+      }
+    }
+
+  return err;
+}
+
+static int sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
+			  const struct fde_entry *dw_fde)
+{
+  struct cfi_insn_data *cfi_insn;
+
+  xlate_ctx->dw_fde = dw_fde;
+
+  /* If the return column is not RIP, SFrame format cannot represent it.  */
+  if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN)
+    return 1;
+
+  /* Iterate over the CFIs and create SFrame FREs.  */
+  for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next)
+    {
+      /* Translate each CFI, and buffer the state in translation context.  */
+      if (sframe_do_cfi_insn (xlate_ctx, cfi_insn))
+	{
+	  /* Skip generating SFrame unwind info for the function if any
+	     offending CFI is encountered by sframe_do_cfi_insn ().  */
+	  /* FIXME - get a detailed look at the individual cases.  */
+	  // printf ("WATCH THIS ONE! sframe_do_cfi_insn skipped. \n");
+	  return 1;  /* Error.  */
+	}
+    }
+
+  /* No errors encountered.  */
+
+  /* Link in the scratchpad FRE that the last few CFI insns helped create.  */
+  if (xlate_ctx->cur_fre)
+    {
+      sframe_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre);
+      xlate_ctx->cur_fre = NULL;
+    }
+  /* Designate the end of the last SFrame FRE.  */
+  if (xlate_ctx->last_fre)
+    {
+      xlate_ctx->last_fre->pc_end
+	= get_dw_fde_end_addrS (xlate_ctx->dw_fde);
+    }
+
+  return 0;
+}
+
+/* Create SFrame unwind info for all functions.
+
+   This function consumes the already generated FDEs (by dw2gencfi) and
+   generates unwind data in SFrame format.  */
+
+static void create_sframe_all (void)
+{
+  struct fde_entry *dw_fde = NULL;
+  struct sframe_func_entry *sframe_fde = NULL;
+
+  struct sframe_xlate_ctx *xlate_ctx = sframe_xlate_ctx_alloc ();
+
+  for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next)
+    {
+      /* FIXME - enable this assert once the compiler is generating
+	 .cfi_sections with .sframe enabled.  */
+      // gas_assert ((fde->sections & CFI_EMIT_sframe));
+      sframe_fde = sframe_fde_alloc ();
+      /* Initialize the translation context with information anew.  */
+      sframe_xlate_ctx_init (xlate_ctx);
+
+      /* Process and link SFrame FDEs if no error.  Also skip adding an SFrame
+	 FDE if it does not contain any SFrame FREs.  There is little use of an
+	 SFrame FDE if there is no unwind information about the function.  */
+      int err = sframe_do_fde (xlate_ctx, dw_fde);
+      if (err || xlate_ctx->num_xlate_fres == 0)
+	{
+	  sframe_xlate_ctx_cleanup (xlate_ctx);
+	  sframe_fde_free (sframe_fde);
+	}
+      else
+	{
+	  /* All done.  Transfer the state from the SFrame translation
+	     context to the SFrame FDE.  */
+	  sframe_xlate_ctx_finalize (xlate_ctx, sframe_fde);
+	  sframe_fde_link (sframe_fde);
+	}
+    }
+}
+
+void output_sframe (segT sframe_seg)
+{
+  (void) sframe_seg;
+
+  /* Setup the version specific access functions.  */
+  sframe_set_version (SFRAME_VERSION_1);
+
+  /* Process all fdes and create SFrame unwind information.  */
+  create_sframe_all ();
+
+  output_sframe_internal ();
+}
+
+#else  /*  support_sframe_p  */
+
+/* Callback to create the abi/arch identifier for SFrame section.  */
+
+unsigned char
+sframe_get_abi_arch_callback (const char *tarch __attribute__((unused)),
+			      int big_endian_p __attribute__((unused)))
+{
+  unsigned char sframe_abi_arch = 0;
+
+  return sframe_abi_arch;
+}
+
+void output_sframe (segT sframe_seg __attribute__((unused)))
+{
+}
+
+#endif /*  support_sframe_p  */
diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
new file mode 100644
index 00000000000..3cd1fa2a643
--- /dev/null
+++ b/gas/gen-sframe.h
@@ -0,0 +1,141 @@
+/* gen-sframe.h - Support for generating SFrame.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifndef GENSFRAME_H
+#define GENSFRAME_H
+
+#define SFRAME_FRE_ELEM_LOC_REG		0
+#define SFRAME_FRE_ELEM_LOC_STACK	1
+
+/* SFrame Frame Row Entry (FRE).
+
+   A frame row entry is a slice of the frame and can be valid for a set of
+   program instructions.  It keeps all information needed to retrieve the CFA
+   and the Return Address (RA) if tracked.
+
+   A frame row entry effectively stores accumulated information gathered by
+   interpreting multiple CFI instructions.  More precisely, it is a
+   self-sufficient record in its own right.  Only the subset of information
+   necessary for unwinding is stored: Given a PC, how to retrieve the CFA and
+   the RA.
+*/
+
+struct sframe_row_entry
+{
+  /* A linked list.  */
+  struct sframe_row_entry *next;
+
+  /* Start and end of the frame row entry.  */
+  symbolS *pc_begin;
+  symbolS *pc_end;
+
+  /* A frame row entry is a merge candidate if new information can be updated
+     on it.  */
+  bool merge_candidate;
+
+  /* Track CFA base (architectural) register ID.  */
+  unsigned int cfa_base_reg;
+  /* Offset from the CFA base register for recovering CFA.  */
+  offsetT cfa_offset;
+
+  /* Track the other register used as base register for CFA.  Specify whether
+     it is in register or memory.  */
+  unsigned int base_reg;
+  unsigned int bp_loc;
+  /* If the other register is stashed on stack, note the offset.  */
+  offsetT bp_offset;
+
+  /* Track RA location.  Specify whether it is in register or memory.  */
+  unsigned int ra_loc;
+  /* If RA is stashed on stack, note the offset.  */
+  offsetT ra_offset;
+};
+
+/* SFrame Function Description Entry.  */
+
+struct sframe_func_entry
+{
+  /* A linked list.  */
+  struct sframe_func_entry *next;
+
+  /* Reference to the FDE created from CFI in dw2gencfi.  Some information
+     like the start_address and the segment is made available via this
+     member.  */
+  const struct fde_entry *dw_fde;
+
+  /* Reference to the first FRE for this function.  */
+  struct sframe_row_entry *sframe_fres;
+
+  unsigned int num_fres;
+};
+
+/* SFrame Function Description Entry Translation Context.  */
+
+struct sframe_xlate_ctx
+{
+  /* Reference to the FDE created from CFI in dw2gencfi.  Information
+     like the FDE start_address, end_address and the cfi insns are
+     made available via this member.  */
+  const struct fde_entry *dw_fde;
+
+  /* List of FREs in the current FDE translation context, bounded by first_fre
+     and last_fre.  */
+
+  /* Keep track of the first FRE for the purpose of restoring state if
+     necessary (for DW_CFA_restore).  */
+  struct sframe_row_entry *first_fre;
+  /* The last FRE in the list.  */
+  struct sframe_row_entry *last_fre;
+
+  /* The current FRE under construction.  */
+  struct sframe_row_entry *cur_fre;
+  /* Remember FRE for an eventual restore.  */
+  struct sframe_row_entry *remember_fre;
+
+  unsigned num_xlate_fres;
+};
+
+/* Callback to create the abi/arch identifier for SFrame section.  */
+
+unsigned char
+sframe_get_abi_arch_callback (const char *target_arch,
+			      int big_endian_p);
+
+/* The list of all FDEs with data in SFrame internal representation.  */
+
+extern struct sframe_func_entry *all_sframe_fdes;
+
+/* SFrame version specific operations structure.  */
+
+struct sframe_version_ops
+{
+  unsigned char format_version;    /* SFrame format version.  */
+  /* set SFrame FRE info.  */
+  unsigned char (*set_fre_info) (unsigned int, unsigned int, unsigned int);
+  /* set SFrame Func info.  */
+  unsigned char (*set_func_info) (unsigned int, unsigned int);
+};
+
+/* Generate SFrame unwind info and prepare contents for the output.
+   outout_sframe ()  is called at the end of file.  */
+
+extern void output_sframe (segT sframe_seg);
+
+#endif /* GENSFRAME_H */
diff --git a/gas/sframe-opt.c b/gas/sframe-opt.c
new file mode 100644
index 00000000000..c17fd6b8332
--- /dev/null
+++ b/gas/sframe-opt.c
@@ -0,0 +1,158 @@
+/* sframe-opt.c - optimize FRE and FDE information in SFrame.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "sframe.h"
+
+/* The function estimates the size of a rs_sframe variant frag based on
+   the current values of the symbols.  It is called before the
+   relaxation loop.  We set fr_subtype{0:2} to the expected length.  */
+
+int
+sframe_estimate_size_before_relax (fragS *frag)
+{
+  offsetT width;
+  expressionS *exp;
+  symbolS *widthS;
+  int ret;
+
+  /* We are dealing with two different kind of fragments here which need
+     to be fixed up:
+       - first, FRE start address in each FRE, and
+       - second, Function info in each FDE (function info stores the FRE type)
+     The two kind of fragments can be differentiated based on the opcode
+     of the symbol.  */
+  exp = symbol_get_value_expression (frag->fr_symbol);
+  gas_assert ((exp->X_op == O_subtract) || (exp->X_op == O_absent));
+  /* Fragment for function info in an SFrame FDE will always write
+     only one byte.  */
+  if (exp->X_op == O_subtract)
+    ret = 1;
+  /* Fragment for the start address in an SFrame FRE may write out
+     1/2/4 bytes depending on the value of the diff.  */
+  else
+    {
+      /* Get the width expression from the symbol.  */
+      widthS = exp->X_op_symbol;
+      width = resolve_symbol_value (widthS);
+
+      if (width < 0x100)
+	ret = 1;
+      else if (width < 0x10000)
+	ret = 2;
+      else
+	ret = 4;
+    }
+
+  frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7);
+
+  return ret;
+}
+
+/* This function relaxes a rs_sframe variant frag based on the current
+   values of the symbols.  fr_subtype{0:2} is the current length of
+   the frag.  This returns the change in frag length.  */
+
+int
+sframe_relax_frag (fragS *frag)
+{
+  int oldsize, newsize;
+
+  oldsize = frag->fr_subtype & 7;
+  if (oldsize == 7)
+    oldsize = -1;
+  newsize = sframe_estimate_size_before_relax (frag);
+  return newsize - oldsize;
+}
+
+/* This function converts a rs_sframe variant frag into a normal fill
+   frag.  This is called after all relaxation has been done.
+   fr_subtype{0:2} will be the desired length of the frag.  */
+
+void
+sframe_convert_frag (fragS *frag)
+{
+  offsetT fsize;
+  offsetT diff;
+  offsetT value;
+  unsigned char func_info = SFRAME_FRE_TYPE_ADDR4;
+  expressionS *exp;
+  symbolS *fsizeS, *diffS;
+
+  /* We are dealing with two different kind of fragments here which need
+     to be fixed up:
+       - first, FRE start address in each FRE, and
+       - second, Function info in each FDE (function info stores the FRE type)
+     The two kind of fragments can be differentiated based on the opcode
+     of the symbol.  */
+  exp = symbol_get_value_expression (frag->fr_symbol);
+  gas_assert ((exp->X_op == O_subtract) || (exp->X_op == O_absent));
+  /* Fragment for function info in an SFrame FDE.  */
+  if (exp->X_op == O_subtract)
+    {
+      fsizeS = frag->fr_symbol;
+      fsize = resolve_symbol_value (fsizeS);
+      if (fsize < 0x100)
+	func_info = SFRAME_FRE_TYPE_ADDR1;
+      else if (fsize < 0x10000)
+	func_info = SFRAME_FRE_TYPE_ADDR2;
+      else
+	func_info = SFRAME_FRE_TYPE_ADDR4;
+      value = func_info;
+
+      frag->fr_literal[frag->fr_fix] = value;
+    }
+  /* Fragment for the start address in an SFrame FRE.  */
+  else
+    {
+      /* Get the fsize expression from the symbol.  */
+      fsizeS = exp->X_op_symbol;
+      fsize = resolve_symbol_value (fsizeS);
+      /* Get the diff expression from the symbol.  */
+      diffS= exp->X_add_symbol;
+      diff = resolve_symbol_value (diffS);
+      value = diff;
+
+      switch (frag->fr_subtype & 7)
+	{
+	case 1:
+	  gas_assert (fsize < 0x100);
+	  frag->fr_literal[frag->fr_fix] = diff;
+	  break;
+	case 2:
+	  gas_assert (fsize < 0x10000);
+	  md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
+	  break;
+	case 4:
+	  md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
+	  break;
+	default:
+	  abort ();
+	}
+    }
+
+  frag->fr_fix += frag->fr_subtype & 7;
+  frag->fr_type = rs_fill;
+  frag->fr_subtype = 0;
+  frag->fr_offset = 0;
+  /* FIXME do this now because we have evaluated and fixed up the fragments
+     manually ?  */
+  frag->fr_symbol = 0;
+}
diff --git a/gas/write.c b/gas/write.c
index 1c1b8104222..4f9b600cd37 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -487,6 +487,10 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
       dwarf2dbg_convert_frag (fragP);
       break;
 
+    case rs_sframe:
+      sframe_convert_frag (fragP);
+      break;
+
     case rs_machine_dependent:
       md_convert_frag (stdoutput, sec, fragP);
 
@@ -2788,6 +2792,11 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  address += dwarf2dbg_estimate_size_before_relax (fragP);
 	  break;
 
+	case rs_sframe:
+	  /* Initial estimate can be set to atleast 1 byte.  */
+	  address += sframe_estimate_size_before_relax (fragP);
+	  break;
+
 	default:
 	  BAD_CASE (fragP->fr_type);
 	  break;
@@ -3131,6 +3140,10 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		growth = dwarf2dbg_relax_frag (fragP);
 		break;
 
+	      case rs_sframe:
+		growth = sframe_relax_frag (fragP);
+		break;
+
 	      default:
 		BAD_CASE (fragP->fr_type);
 		break;
