[3/3] libgcc: Specialize execute_cfa_program in DWARF unwinder for alignments

Message ID e5de1b7feecc0ce5bec77e6a21032ab1f6c0a315.1667930077.git.fweimer@redhat.com
State Committed
Commit cb775ecd6e437de8fdba9a3f173f3787e90e98f2
Headers
Series Further libgcc unwinder improvements |

Commit Message

Florian Weimer Nov. 8, 2022, 6:05 p.m. UTC
  The parameters fs->data_align and fs->code_align always have fixed
values for a particular target in GCC-generated code.  Specialize
execute_cfa_program for these values, to avoid multiplications.

gcc/

	* c-family/c-cppbuiltin.c (c_cpp_builtins): Define
	__LIBGCC_DWARF_CIE_DATA_ALIGNMENT__.

libgcc/

	* unwind-dw2-execute_cfa.h: New file.  Extracted from
	the execute_cfa_program function in unwind-dw2.c.
	* unwind-dw2.c (execute_cfa_program_generic): New function.
	(execute_cfa_program_specialized): Likewise.
	(execute_cfa_program): Call execute_cfa_program_specialized
	or execute_cfa_program_generic, as appropriate.
---
 gcc/c-family/c-cppbuiltin.cc    |   2 +
 libgcc/unwind-dw2-execute_cfa.h | 322 ++++++++++++++++++++++++++++++++
 libgcc/unwind-dw2.c             | 319 +++----------------------------
 3 files changed, 354 insertions(+), 289 deletions(-)
 create mode 100644 libgcc/unwind-dw2-execute_cfa.h
  

Patch

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index ab98bf3b059..c8c327b3b2e 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1521,6 +1521,8 @@  c_cpp_builtins (cpp_reader *pfile)
 	  builtin_define_with_int_value ("__LIBGCC_DWARF_REG_SIZES_CONSTANT__",
 					 value);
       }
+      builtin_define_with_int_value ("__LIBGCC_DWARF_CIE_DATA_ALIGNMENT__",
+				     DWARF_CIE_DATA_ALIGNMENT);
 #ifdef EH_RETURN_STACKADJ_RTX
       cpp_define (pfile, "__LIBGCC_EH_RETURN_STACKADJ_RTX__");
 #endif
diff --git a/libgcc/unwind-dw2-execute_cfa.h b/libgcc/unwind-dw2-execute_cfa.h
new file mode 100644
index 00000000000..dd97b786668
--- /dev/null
+++ b/libgcc/unwind-dw2-execute_cfa.h
@@ -0,0 +1,322 @@ 
+/* DWARF2 exception handling CFA execution engine.
+   Copyright (C) 1997-2022 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC 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.
+
+   GCC 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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This file is included from unwind-dw2.c to specialize the code for certain
+   values of DATA_ALIGN and CODE_ALIGN.  These macros must be defined prior to
+   including this file.  */
+
+{
+  struct frame_state_reg_info *unused_rs = NULL;
+
+  /* Don't allow remember/restore between CIE and FDE programs.  */
+  fs->regs.prev = NULL;
+
+  /* The comparison with the return address uses < rather than <= because
+     we are only interested in the effects of code before the call; for a
+     noreturn function, the return address may point to unrelated code with
+     a different stack configuration that we are not interested in.  We
+     assume that the call itself is unwind info-neutral; if not, or if
+     there are delay instructions that adjust the stack, these must be
+     reflected at the point immediately before the call insn.
+     In signal frames, return address is after last completed instruction,
+     so we add 1 to return address to make the comparison <=.  */
+  while (insn_ptr < insn_end
+	 && fs->pc < context->ra + _Unwind_IsSignalFrame (context))
+    {
+      unsigned char insn = *insn_ptr++;
+      _uleb128_t reg, utmp;
+      _sleb128_t offset, stmp;
+
+      if ((insn & 0xc0) == DW_CFA_advance_loc)
+	fs->pc += (insn & 0x3f) * CODE_ALIGN;
+      else if ((insn & 0xc0) == DW_CFA_offset)
+	{
+	  reg = insn & 0x3f;
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  offset = (_Unwind_Sword) utmp * DATA_ALIGN;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_OFFSET;
+	      fs->regs.reg[reg].loc.offset = offset;
+	    }
+	}
+      else if ((insn & 0xc0) == DW_CFA_restore)
+	{
+	  reg = insn & 0x3f;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    fs->regs.how[reg] = REG_UNSAVED;
+	}
+      else switch (insn)
+	{
+	case DW_CFA_set_loc:
+	  {
+	    _Unwind_Ptr pc;
+
+	    insn_ptr = read_encoded_value (context, fs->fde_encoding,
+					   insn_ptr, &pc);
+	    fs->pc = (void *) pc;
+	  }
+	  break;
+
+	case DW_CFA_advance_loc1:
+	  fs->pc += read_1u (insn_ptr) * CODE_ALIGN;
+	  insn_ptr += 1;
+	  break;
+	case DW_CFA_advance_loc2:
+	  fs->pc += read_2u (insn_ptr) * CODE_ALIGN;
+	  insn_ptr += 2;
+	  break;
+	case DW_CFA_advance_loc4:
+	  fs->pc += read_4u (insn_ptr) * CODE_ALIGN;
+	  insn_ptr += 4;
+	  break;
+
+	case DW_CFA_offset_extended:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  offset = (_Unwind_Sword) utmp * DATA_ALIGN;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_OFFSET;
+	      fs->regs.reg[reg].loc.offset = offset;
+	    }
+	  break;
+
+	case DW_CFA_restore_extended:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  /* FIXME, this is wrong; the CIE might have said that the
+	     register was saved somewhere.  */
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    fs->regs.how[reg] = REG_UNSAVED;
+	  break;
+
+	case DW_CFA_same_value:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    fs->regs.how[reg] = REG_UNSAVED;
+	  break;
+
+	case DW_CFA_undefined:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    fs->regs.how[reg] = REG_UNDEFINED;
+	  break;
+
+	case DW_CFA_nop:
+	  break;
+
+	case DW_CFA_register:
+	  {
+	    _uleb128_t reg2;
+	    insn_ptr = read_uleb128 (insn_ptr, &reg);
+	    insn_ptr = read_uleb128 (insn_ptr, &reg2);
+	    reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	    if (UNWIND_COLUMN_IN_RANGE (reg))
+	      {
+		fs->regs.how[reg] = REG_SAVED_REG;
+	        fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
+	      }
+	  }
+	  break;
+
+	case DW_CFA_remember_state:
+	  {
+	    struct frame_state_reg_info *new_rs;
+	    if (unused_rs)
+	      {
+		new_rs = unused_rs;
+		unused_rs = unused_rs->prev;
+	      }
+	    else
+	      new_rs = alloca (sizeof (struct frame_state_reg_info));
+
+	    *new_rs = fs->regs;
+	    fs->regs.prev = new_rs;
+	  }
+	  break;
+
+	case DW_CFA_restore_state:
+	  {
+	    struct frame_state_reg_info *old_rs = fs->regs.prev;
+	    fs->regs = *old_rs;
+	    old_rs->prev = unused_rs;
+	    unused_rs = old_rs;
+	  }
+	  break;
+
+	case DW_CFA_def_cfa:
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  fs->regs.cfa_offset = (_Unwind_Word)utmp;
+	  fs->regs.cfa_how = CFA_REG_OFFSET;
+	  break;
+
+	case DW_CFA_def_cfa_register:
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
+	  fs->regs.cfa_how = CFA_REG_OFFSET;
+	  break;
+
+	case DW_CFA_def_cfa_offset:
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  fs->regs.cfa_offset = utmp;
+	  /* cfa_how deliberately not set.  */
+	  break;
+
+	case DW_CFA_def_cfa_expression:
+	  fs->regs.cfa_exp = insn_ptr;
+	  fs->regs.cfa_how = CFA_EXP;
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  insn_ptr += utmp;
+	  break;
+
+	case DW_CFA_expression:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_EXP;
+	      fs->regs.reg[reg].loc.exp = insn_ptr;
+	    }
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  insn_ptr += utmp;
+	  break;
+
+	  /* Dwarf3.  */
+	case DW_CFA_offset_extended_sf:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
+	  offset = stmp * DATA_ALIGN;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_OFFSET;
+	      fs->regs.reg[reg].loc.offset = offset;
+	    }
+	  break;
+
+	case DW_CFA_def_cfa_sf:
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
+	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
+	  fs->regs.cfa_offset = (_Unwind_Sword)stmp;
+	  fs->regs.cfa_how = CFA_REG_OFFSET;
+	  fs->regs.cfa_offset *= DATA_ALIGN;
+	  break;
+
+	case DW_CFA_def_cfa_offset_sf:
+	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
+	  fs->regs.cfa_offset = (_Unwind_Sword)stmp;
+	  fs->regs.cfa_offset *= DATA_ALIGN;
+	  /* cfa_how deliberately not set.  */
+	  break;
+
+	case DW_CFA_val_offset:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  offset = (_Unwind_Sword) utmp * DATA_ALIGN;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
+	      fs->regs.reg[reg].loc.offset = offset;
+	    }
+	  break;
+
+	case DW_CFA_val_offset_sf:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
+	  offset = stmp * DATA_ALIGN;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
+	      fs->regs.reg[reg].loc.offset = offset;
+	    }
+	  break;
+
+	case DW_CFA_val_expression:
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_VAL_EXP;
+	      fs->regs.reg[reg].loc.exp = insn_ptr;
+	    }
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  insn_ptr += utmp;
+	  break;
+
+	case DW_CFA_GNU_window_save:
+#if defined (__aarch64__) && !defined (__ILP32__)
+	  /* This CFA is multiplexed with Sparc.  On AArch64 it's used to toggle
+	     return address signing status.  */
+	  reg = DWARF_REGNUM_AARCH64_RA_STATE;
+	  gcc_assert (fs->regs.how[reg] == REG_UNSAVED);
+	  fs->regs.reg[reg].loc.offset ^= 1;
+#else
+	  /* ??? Hardcoded for SPARC register window configuration.  */
+	  if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
+	    for (reg = 16; reg < 32; ++reg)
+	      {
+		fs->regs.how[reg] = REG_SAVED_OFFSET;
+		fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
+	      }
+#endif
+	  break;
+
+	case DW_CFA_GNU_args_size:
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  context->args_size = (_Unwind_Word)utmp;
+	  break;
+
+	case DW_CFA_GNU_negative_offset_extended:
+	  /* Obsoleted by DW_CFA_offset_extended_sf, but used by
+	     older PowerPC code.  */
+	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
+	  offset = (_Unwind_Word) utmp * DATA_ALIGN;
+	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
+	  if (UNWIND_COLUMN_IN_RANGE (reg))
+	    {
+	      fs->regs.how[reg] = REG_SAVED_OFFSET;
+	      fs->regs.reg[reg].loc.offset = -offset;
+	    }
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+}
+
+#undef DATA_ALIGN
+#undef CODE_ALIGN
diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
index c370121bb29..792338c1e38 100644
--- a/libgcc/unwind-dw2.c
+++ b/libgcc/unwind-dw2.c
@@ -960,302 +960,43 @@  execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end,
    instruction sequence to decode, current register information and
    CIE info, and the PC range to evaluate.  */
 
+static void  __attribute__ ((__noinline__))
+execute_cfa_program_generic (const unsigned char *insn_ptr,
+			     const unsigned char *insn_end,
+			     struct _Unwind_Context *context,
+			     _Unwind_FrameState *fs)
+{
+#define DATA_ALIGN fs->data_align
+#define CODE_ALIGN fs->code_align
+#include "unwind-dw2-execute_cfa.h"
+}
+
+static inline void
+execute_cfa_program_specialized (const unsigned char *insn_ptr,
+				 const unsigned char *insn_end,
+				 struct _Unwind_Context *context,
+				 _Unwind_FrameState *fs)
+{
+#define DATA_ALIGN __LIBGCC_DWARF_CIE_DATA_ALIGNMENT__
+  /* GCC always uses 1 even on architectures with a fixed instruction
+     width.  */
+#define CODE_ALIGN 1
+#include "unwind-dw2-execute_cfa.h"
+}
+
 static void
 execute_cfa_program (const unsigned char *insn_ptr,
 		     const unsigned char *insn_end,
 		     struct _Unwind_Context *context,
 		     _Unwind_FrameState *fs)
 {
-  struct frame_state_reg_info *unused_rs = NULL;
-
-  /* Don't allow remember/restore between CIE and FDE programs.  */
-  fs->regs.prev = NULL;
-
-  /* The comparison with the return address uses < rather than <= because
-     we are only interested in the effects of code before the call; for a
-     noreturn function, the return address may point to unrelated code with
-     a different stack configuration that we are not interested in.  We
-     assume that the call itself is unwind info-neutral; if not, or if
-     there are delay instructions that adjust the stack, these must be
-     reflected at the point immediately before the call insn.
-     In signal frames, return address is after last completed instruction,
-     so we add 1 to return address to make the comparison <=.  */
-  while (insn_ptr < insn_end
-	 && fs->pc < context->ra + _Unwind_IsSignalFrame (context))
-    {
-      unsigned char insn = *insn_ptr++;
-      _uleb128_t reg, utmp;
-      _sleb128_t offset, stmp;
-
-      if ((insn & 0xc0) == DW_CFA_advance_loc)
-	fs->pc += (insn & 0x3f) * fs->code_align;
-      else if ((insn & 0xc0) == DW_CFA_offset)
-	{
-	  reg = insn & 0x3f;
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  offset = (_Unwind_Sword) utmp * fs->data_align;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_OFFSET;
-	      fs->regs.reg[reg].loc.offset = offset;
-	    }
-	}
-      else if ((insn & 0xc0) == DW_CFA_restore)
-	{
-	  reg = insn & 0x3f;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    fs->regs.how[reg] = REG_UNSAVED;
-	}
-      else switch (insn)
-	{
-	case DW_CFA_set_loc:
-	  {
-	    _Unwind_Ptr pc;
-
-	    insn_ptr = read_encoded_value (context, fs->fde_encoding,
-					   insn_ptr, &pc);
-	    fs->pc = (void *) pc;
-	  }
-	  break;
-
-	case DW_CFA_advance_loc1:
-	  fs->pc += read_1u (insn_ptr) * fs->code_align;
-	  insn_ptr += 1;
-	  break;
-	case DW_CFA_advance_loc2:
-	  fs->pc += read_2u (insn_ptr) * fs->code_align;
-	  insn_ptr += 2;
-	  break;
-	case DW_CFA_advance_loc4:
-	  fs->pc += read_4u (insn_ptr) * fs->code_align;
-	  insn_ptr += 4;
-	  break;
-
-	case DW_CFA_offset_extended:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  offset = (_Unwind_Sword) utmp * fs->data_align;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_OFFSET;
-	      fs->regs.reg[reg].loc.offset = offset;
-	    }
-	  break;
-
-	case DW_CFA_restore_extended:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  /* FIXME, this is wrong; the CIE might have said that the
-	     register was saved somewhere.  */
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    fs->regs.how[reg] = REG_UNSAVED;
-	  break;
-
-	case DW_CFA_same_value:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    fs->regs.how[reg] = REG_UNSAVED;
-	  break;
-
-	case DW_CFA_undefined:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    fs->regs.how[reg] = REG_UNDEFINED;
-	  break;
-
-	case DW_CFA_nop:
-	  break;
-
-	case DW_CFA_register:
-	  {
-	    _uleb128_t reg2;
-	    insn_ptr = read_uleb128 (insn_ptr, &reg);
-	    insn_ptr = read_uleb128 (insn_ptr, &reg2);
-	    reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	    if (UNWIND_COLUMN_IN_RANGE (reg))
-	      {
-		fs->regs.how[reg] = REG_SAVED_REG;
-	        fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
-	      }
-	  }
-	  break;
-
-	case DW_CFA_remember_state:
-	  {
-	    struct frame_state_reg_info *new_rs;
-	    if (unused_rs)
-	      {
-		new_rs = unused_rs;
-		unused_rs = unused_rs->prev;
-	      }
-	    else
-	      new_rs = alloca (sizeof (struct frame_state_reg_info));
-
-	    *new_rs = fs->regs;
-	    fs->regs.prev = new_rs;
-	  }
-	  break;
-
-	case DW_CFA_restore_state:
-	  {
-	    struct frame_state_reg_info *old_rs = fs->regs.prev;
-	    fs->regs = *old_rs;
-	    old_rs->prev = unused_rs;
-	    unused_rs = old_rs;
-	  }
-	  break;
-
-	case DW_CFA_def_cfa:
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  fs->regs.cfa_offset = (_Unwind_Word)utmp;
-	  fs->regs.cfa_how = CFA_REG_OFFSET;
-	  break;
-
-	case DW_CFA_def_cfa_register:
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
-	  fs->regs.cfa_how = CFA_REG_OFFSET;
-	  break;
-
-	case DW_CFA_def_cfa_offset:
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  fs->regs.cfa_offset = utmp;
-	  /* cfa_how deliberately not set.  */
-	  break;
-
-	case DW_CFA_def_cfa_expression:
-	  fs->regs.cfa_exp = insn_ptr;
-	  fs->regs.cfa_how = CFA_EXP;
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  insn_ptr += utmp;
-	  break;
-
-	case DW_CFA_expression:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_EXP;
-	      fs->regs.reg[reg].loc.exp = insn_ptr;
-	    }
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  insn_ptr += utmp;
-	  break;
-
-	  /* Dwarf3.  */
-	case DW_CFA_offset_extended_sf:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
-	  offset = stmp * fs->data_align;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_OFFSET;
-	      fs->regs.reg[reg].loc.offset = offset;
-	    }
-	  break;
-
-	case DW_CFA_def_cfa_sf:
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
-	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
-	  fs->regs.cfa_offset = (_Unwind_Sword)stmp;
-	  fs->regs.cfa_how = CFA_REG_OFFSET;
-	  fs->regs.cfa_offset *= fs->data_align;
-	  break;
-
-	case DW_CFA_def_cfa_offset_sf:
-	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
-	  fs->regs.cfa_offset = (_Unwind_Sword)stmp;
-	  fs->regs.cfa_offset *= fs->data_align;
-	  /* cfa_how deliberately not set.  */
-	  break;
-
-	case DW_CFA_val_offset:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  offset = (_Unwind_Sword) utmp * fs->data_align;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
-	      fs->regs.reg[reg].loc.offset = offset;
-	    }
-	  break;
-
-	case DW_CFA_val_offset_sf:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
-	  offset = stmp * fs->data_align;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
-	      fs->regs.reg[reg].loc.offset = offset;
-	    }
-	  break;
-
-	case DW_CFA_val_expression:
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_VAL_EXP;
-	      fs->regs.reg[reg].loc.exp = insn_ptr;
-	    }
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  insn_ptr += utmp;
-	  break;
-
-	case DW_CFA_GNU_window_save:
-#if defined (__aarch64__) && !defined (__ILP32__)
-	  /* This CFA is multiplexed with Sparc.  On AArch64 it's used to toggle
-	     return address signing status.  */
-	  reg = DWARF_REGNUM_AARCH64_RA_STATE;
-	  gcc_assert (fs->regs.how[reg] == REG_UNSAVED);
-	  fs->regs.reg[reg].loc.offset ^= 1;
-#else
-	  /* ??? Hardcoded for SPARC register window configuration.  */
-	  if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
-	    for (reg = 16; reg < 32; ++reg)
-	      {
-		fs->regs.how[reg] = REG_SAVED_OFFSET;
-		fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
-	      }
-#endif
-	  break;
-
-	case DW_CFA_GNU_args_size:
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  context->args_size = (_Unwind_Word)utmp;
-	  break;
-
-	case DW_CFA_GNU_negative_offset_extended:
-	  /* Obsoleted by DW_CFA_offset_extended_sf, but used by
-	     older PowerPC code.  */
-	  insn_ptr = read_uleb128 (insn_ptr, &reg);
-	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
-	  offset = (_Unwind_Word) utmp * fs->data_align;
-	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
-	  if (UNWIND_COLUMN_IN_RANGE (reg))
-	    {
-	      fs->regs.how[reg] = REG_SAVED_OFFSET;
-	      fs->regs.reg[reg].loc.offset = -offset;
-	    }
-	  break;
-
-	default:
-	  gcc_unreachable ();
-	}
-    }
+  if (fs->data_align == __LIBGCC_DWARF_CIE_DATA_ALIGNMENT__
+      && fs->code_align == 1)
+    execute_cfa_program_specialized (insn_ptr, insn_end, context, fs);
+  else
+    execute_cfa_program_generic (insn_ptr, insn_end, context, fs);
 }
+
 
 /* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for
    its caller and decode it into FS.  This function also sets the