diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 21a7e5d30a968a10912b1a87c3c06d03f9f6f968..ca612fb0fe624e91f3d1f5677981f5d8d142f9dc 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,21 @@
+2005-11-17  Richard Henderson  <rth@redhat.com>
+
+	* dwarf2out.c (dw_cfi_oprnd_struct): Reduce dw_cfi_reg_num to int.
+	(lookup_cfa_1): Apply data alignment to DW_CFA_def_cfa_offset_sf
+	and DW_CFA_def_cfa_sf.
+	(def_cfa_1): Use DW_CFA_def_cfa_offset_sf with negative values.
+	(dbx_reg_number): Don't assert particular registers here.
+	(based_loc_descr): ... do it here instead.  Fold in ...
+	(eliminate_reg_to_offset): ... this function.
+	(compute_frame_pointer_to_cfa_displacement): Fold in the effects
+	of eliminate_reg_to_offset; use FRAME_POINTER_CFA_OFFSET.
+	* unwind-dw2.c (execute_cfa_program): Apply data align factor
+	to DW_CFA_def_cfa_offset_sf and DW_CFA_def_cfa_sf.
+	* function.c (instantiate_new_reg): Use FRAME_POINTER_CFA_OFFSET.
+	(instantiate_virtual_regs): Likewise.
+	* var-tracking.c (adjust_stack_reference): Likewise.
+	* doc/tm.texi (FRAME_POINTER_CFA_OFFSET): New.
+
 2005-11-17  Bernd Schmidt  <bernd.schmidt@analog.com>
 
 	* config/bfin/elf.h (STARTFILE_SPEC): Add "crtlibid%O%s"
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index b339c4ccdc0554eee591015489e9148621f42d09..b229ee823ddc4f84afc146e4f9a0290aa59defa0 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -2926,6 +2926,21 @@ want to support call frame debugging information like that provided by
 DWARF 2.
 @end defmac
 
+@defmac FRAME_POINTER_CFA_OFFSET (@var{fundecl})
+If defined, a C expression whose value is an integer giving the offset
+in bytes from the frame pointer to the canonical frame address (cfa).
+The final value should conincide with that calculated by
+@code{INCOMING_FRAME_SP_OFFSET}.
+
+Normally the CFA is calculated as an offset from the argument pointer,
+via @code{ARG_POINTER_CFA_OFFSET}, but if the argument pointer is 
+variable due to the ABI, this may not be possible.  If this macro is
+defined, it imples that the virtual register instantiation should be
+based on the frame pointer instead of the argument pointer.  Only one
+of @code{FRAME_POINTER_CFA_OFFSET} and @code{ARG_POINTER_CFA_OFFSET}
+should be defined.
+@end defmac
+
 @node Exception Handling
 @subsection Exception Handling Support
 @cindex exception handling
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index f7a006e8980d41d6668f3cc3741aaa77bd5dc2e8..60a7d558084fb3f75cb73ee7cdb61b2581012bc4 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -214,7 +214,7 @@ enum dw_cfi_oprnd_type {
 
 typedef union dw_cfi_oprnd_struct GTY(())
 {
-  unsigned long GTY ((tag ("dw_cfi_oprnd_reg_num"))) dw_cfi_reg_num;
+  unsigned int GTY ((tag ("dw_cfi_oprnd_reg_num"))) dw_cfi_reg_num;
   HOST_WIDE_INT GTY ((tag ("dw_cfi_oprnd_offset"))) dw_cfi_offset;
   const char * GTY ((tag ("dw_cfi_oprnd_addr"))) dw_cfi_addr;
   struct dw_loc_descr_struct * GTY ((tag ("dw_cfi_oprnd_loc"))) dw_cfi_loc;
@@ -671,6 +671,10 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc)
     case DW_CFA_def_cfa_offset:
       loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
       break;
+    case DW_CFA_def_cfa_offset_sf:
+      loc->offset
+	= cfi->dw_cfi_oprnd1.dw_cfi_offset * DWARF_CIE_DATA_ALIGNMENT;
+      break;
     case DW_CFA_def_cfa_register:
       loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
       break;
@@ -678,6 +682,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc)
       loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
       loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
       break;
+    case DW_CFA_def_cfa_sf:
+      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->offset
+	= cfi->dw_cfi_oprnd2.dw_cfi_offset * DWARF_CIE_DATA_ALIGNMENT;
+      break;
     case DW_CFA_def_cfa_expression:
       get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc);
       break;
@@ -775,11 +784,21 @@ def_cfa_1 (const char *label, dw_cfa_location *loc_p)
 
   if (loc.reg == old_cfa.reg && !loc.indirect)
     {
-      /* Construct a "DW_CFA_def_cfa_offset <offset>" instruction,
-	 indicating the CFA register did not change but the offset
-	 did.  */
-      cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
-      cfi->dw_cfi_oprnd1.dw_cfi_offset = loc.offset;
+      /* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, indicating
+	 the CFA register did not change but the offset did.  */
+      if (loc.offset < 0)
+	{
+	  HOST_WIDE_INT f_offset = loc.offset / DWARF_CIE_DATA_ALIGNMENT;
+	  gcc_assert (f_offset * DWARF_CIE_DATA_ALIGNMENT == loc.offset);
+
+	  cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf;
+	  cfi->dw_cfi_oprnd1.dw_cfi_offset = f_offset;
+	}
+      else
+	{
+	  cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
+	  cfi->dw_cfi_oprnd1.dw_cfi_offset = loc.offset;
+	}
     }
 
 #ifndef MIPS_DEBUGGING_INFO  /* SGI dbx thinks this means no offset.  */
@@ -800,9 +819,21 @@ def_cfa_1 (const char *label, dw_cfa_location *loc_p)
       /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
 	 indicating the CFA register has changed to <register> with
 	 the specified offset.  */
-      cfi->dw_cfi_opc = DW_CFA_def_cfa;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
-      cfi->dw_cfi_oprnd2.dw_cfi_offset = loc.offset;
+      if (loc.offset < 0)
+	{
+	  HOST_WIDE_INT f_offset = loc.offset / DWARF_CIE_DATA_ALIGNMENT;
+	  gcc_assert (f_offset * DWARF_CIE_DATA_ALIGNMENT == loc.offset);
+
+	  cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
+	  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
+	  cfi->dw_cfi_oprnd2.dw_cfi_offset = f_offset;
+	}
+      else
+	{
+	  cfi->dw_cfi_opc = DW_CFA_def_cfa;
+	  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
+	  cfi->dw_cfi_oprnd2.dw_cfi_offset = loc.offset;
+	}
     }
   else
     {
@@ -8460,12 +8491,6 @@ dbx_reg_number (rtx rtl)
 {
   unsigned regno = REGNO (rtl);
 
-  /* We do not want to see registers that should have been eliminated.  */
-  gcc_assert (HARD_FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM
-	      || rtl != arg_pointer_rtx);
-  gcc_assert (HARD_FRAME_POINTER_REGNUM == FRAME_POINTER_REGNUM
-	      || rtl != frame_pointer_rtx);
-
   gcc_assert (regno < FIRST_PSEUDO_REGISTER);
 
 #ifdef LEAF_REG_REMAP
@@ -8619,32 +8644,12 @@ int_loc_descriptor (HOST_WIDE_INT i)
   return new_loc_descr (op, i, 0);
 }
 
-/* Return an offset from an eliminable register to the post-prologue
-   frame pointer.  */
-
-static HOST_WIDE_INT
-eliminate_reg_to_offset (rtx reg)
-{
-  HOST_WIDE_INT offset = 0;
-
-  reg = eliminate_regs (reg, VOIDmode, NULL_RTX);
-  if (GET_CODE (reg) == PLUS)
-    {
-      offset = INTVAL (XEXP (reg, 1));
-      reg = XEXP (reg, 0);
-    }
-  gcc_assert (reg == (frame_pointer_needed ? hard_frame_pointer_rtx
-		      : stack_pointer_rtx));
-
-  return offset;
-}
-
 /* Return a location descriptor that designates a base+offset location.  */
 
 static dw_loc_descr_ref
 based_loc_descr (rtx reg, HOST_WIDE_INT offset)
 {
-  dw_loc_descr_ref loc_result;
+  unsigned int regno;
 
   /* We only use "frame base" when we're sure we're talking about the
      post-prologue local stack frame.  We do this by *not* running
@@ -8652,22 +8657,28 @@ based_loc_descr (rtx reg, HOST_WIDE_INT offset)
      argument pointer and soft frame pointer rtx's.  */
   if (reg == arg_pointer_rtx || reg == frame_pointer_rtx)
     {
-      offset += eliminate_reg_to_offset (reg);
-      offset += frame_pointer_cfa_offset;
+      rtx elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
 
-      loc_result = new_loc_descr (DW_OP_fbreg, offset, 0);
-    }
-  else
-    {
-      unsigned int regno = dbx_reg_number (reg);
+      if (elim != reg)
+	{
+	  if (GET_CODE (elim) == PLUS)
+	    {
+	      offset += INTVAL (XEXP (elim, 1));
+	      elim = XEXP (elim, 0);
+	    }
+	  gcc_assert (elim == (frame_pointer_needed ? hard_frame_pointer_rtx
+		      : stack_pointer_rtx));
+          offset += frame_pointer_cfa_offset;
 
-      if (regno <= 31)
-	loc_result = new_loc_descr (DW_OP_breg0 + regno, offset, 0);
-      else
-	loc_result = new_loc_descr (DW_OP_bregx, regno, offset);
+          return new_loc_descr (DW_OP_fbreg, offset, 0);
+	}
     }
 
-  return loc_result;
+  regno = dbx_reg_number (reg);
+  if (regno <= 31)
+    return new_loc_descr (DW_OP_breg0 + regno, offset, 0);
+  else
+    return new_loc_descr (DW_OP_bregx, regno, offset);
 }
 
 /* Return true if this RTL expression describes a base+offset calculation.  */
@@ -10406,9 +10417,24 @@ static void
 compute_frame_pointer_to_cfa_displacement (void)
 {
   HOST_WIDE_INT offset;
+  rtx reg, elim;
+
+#ifdef FRAME_POINTER_CFA_OFFSET
+  reg = frame_pointer_rtx;
+  offset = FRAME_POINTER_CFA_OFFSET (current_function_decl);
+#else
+  reg = arg_pointer_rtx;
+  offset = ARG_POINTER_CFA_OFFSET (current_function_decl);
+#endif
 
-  offset = eliminate_reg_to_offset (arg_pointer_rtx);
-  offset += ARG_POINTER_CFA_OFFSET (current_function_decl);
+  elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
+  if (GET_CODE (elim) == PLUS)
+    {
+      offset += INTVAL (XEXP (elim, 1));
+      elim = XEXP (elim, 0);
+    }
+  gcc_assert (elim == (frame_pointer_needed ? hard_frame_pointer_rtx
+		       : stack_pointer_rtx));
 
   frame_pointer_cfa_offset = -offset;
 }
diff --git a/gcc/function.c b/gcc/function.c
index dbf9df74ade0400397dec99cdf87b571b2428b85..c0339b8950e54b84fa2ea37feb10e9a014522de5 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -1246,7 +1246,14 @@ instantiate_new_reg (rtx x, HOST_WIDE_INT *poffset)
   else if (x == virtual_outgoing_args_rtx)
     new = stack_pointer_rtx, offset = out_arg_offset;
   else if (x == virtual_cfa_rtx)
-    new = arg_pointer_rtx, offset = cfa_offset;
+    {
+#ifdef FRAME_POINTER_CFA_OFFSET
+      new = frame_pointer_rtx;
+#else
+      new = arg_pointer_rtx;
+#endif
+      offset = cfa_offset;
+    }
   else
     return NULL_RTX;
 
@@ -1632,7 +1639,11 @@ instantiate_virtual_regs (void)
   var_offset = STARTING_FRAME_OFFSET;
   dynamic_offset = STACK_DYNAMIC_OFFSET (current_function_decl);
   out_arg_offset = STACK_POINTER_OFFSET;
+#ifdef FRAME_POINTER_CFA_OFFSET
+  cfa_offset = FRAME_POINTER_CFA_OFFSET (current_function_decl);
+#else
   cfa_offset = ARG_POINTER_CFA_OFFSET (current_function_decl);
+#endif
 
   /* Initialize recognition, indicating that volatile is OK.  */
   init_recog ();
diff --git a/gcc/unwind-dw2.c b/gcc/unwind-dw2.c
index 4626ec6d84cbe364441dcf54041ab6918110541a..930f02f37992003479123d4a757b504c744f30ab 100644
--- a/gcc/unwind-dw2.c
+++ b/gcc/unwind-dw2.c
@@ -902,7 +902,7 @@ execute_cfa_program (const unsigned char *insn_ptr,
 	  insn_ptr += utmp;
 	  break;
 
-	  /* From the 2.1 draft.  */
+	  /* From the dwarf3 draft.  */
 	case DW_CFA_offset_extended_sf:
 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
 	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
@@ -916,10 +916,12 @@ execute_cfa_program (const unsigned char *insn_ptr,
 	  insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg);
 	  insn_ptr = read_sleb128 (insn_ptr, &fs->cfa_offset);
 	  fs->cfa_how = CFA_REG_OFFSET;
+	  fs->cfa_offset *= fs->data_align;
 	  break;
 
 	case DW_CFA_def_cfa_offset_sf:
 	  insn_ptr = read_sleb128 (insn_ptr, &fs->cfa_offset);
+	  fs->cfa_offset *= fs->data_align;
 	  /* cfa_how deliberately not set.  */
 	  break;
 
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index d69ca139c42457ef77520a2c9fc4bc757e21a0b1..fb4a1813218fe7d1e3bed25c16e220eb905de9c4 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -557,8 +557,13 @@ adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
 {
   rtx addr, cfa, tmp;
 
+#ifdef FRAME_POINTER_CFA_OFFSET
+  adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
+  cfa = plus_constant (frame_pointer_rtx, adjustment);
+#else
   adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
   cfa = plus_constant (arg_pointer_rtx, adjustment);
+#endif
 
   addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
   tmp = simplify_rtx (addr);