diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f465600..b4fddfc 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -8616,8 +8616,9 @@ static void
 ix86_emit_save_reg_using_mov (enum machine_mode mode, unsigned int regno,
 			      HOST_WIDE_INT cfa_offset)
 {
+  struct machine_function *m = cfun->machine;
   rtx reg = gen_rtx_REG (mode, regno);
-  rtx mem, addr, insn;
+  rtx mem, addr, base, insn;
 
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
@@ -8626,20 +8627,64 @@ ix86_emit_save_reg_using_mov (enum machine_mode mode, unsigned int regno,
   set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
 
   insn = emit_move_insn (mem, reg);
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  base = addr;
+  if (GET_CODE (base) == PLUS)
+    base = XEXP (base, 0);
+  gcc_checking_assert (REG_P (base));
+
+  /* When saving registers into a re-aligned local stack frame, avoid
+     any tricky guessing by dwarf2out.  */
+  if (m->fs.realigned)
+    {
+      if (stack_realign_drap && regno == REGNO (crtl->drap_reg))
+	{
+	  /* A bit of a hack.  We force the DRAP register to be saved in
+	     the re-aligned stack frame, which provides us with a copy
+	     of the CFA that will last past the prologue.  Install it.  */
+	  gcc_checking_assert (cfun->machine->fs.fp_valid);
+	  addr = plus_constant (hard_frame_pointer_rtx,
+				cfun->machine->fs.fp_offset - cfa_offset);
+	  mem = gen_rtx_MEM (mode, addr);
+	  add_reg_note (insn, REG_CFA_DEF_CFA, mem);
+	}
+      else if (stack_realign_fp)
+	{
+	  /* The stack pointer may or may not be varying within the
+	     function.  If it is, then we can't use it as a stable
+	     reference to the locations within the frame.  Instead,
+	     simply compute the location of the aligned frame from
+	     the frame pointer.  */
+	  addr = GEN_INT (-crtl->stack_alignment_needed / BITS_PER_UNIT);
+	  addr = gen_rtx_AND (Pmode, hard_frame_pointer_rtx, addr);
+	  addr = plus_constant (addr, -cfa_offset);
+	  mem = gen_rtx_MEM (mode, addr);
+	  add_reg_note (insn, REG_CFA_EXPRESSION,
+			gen_rtx_SET (VOIDmode, mem, reg));
+	}
+      else
+	{
+	  /* The frame pointer is a stable reference within the
+	     aligned frame.  Use it.  */
+	  gcc_checking_assert (cfun->machine->fs.fp_valid);
+	  addr = plus_constant (hard_frame_pointer_rtx,
+				cfun->machine->fs.fp_offset - cfa_offset);
+	  mem = gen_rtx_MEM (mode, addr);
+	  add_reg_note (insn, REG_CFA_EXPRESSION,
+			gen_rtx_SET (VOIDmode, mem, reg));
+	}
+    }
 
   /* The memory may not be relative to the current CFA register,
      which means that we may need to generate a new pattern for
      use by the unwind info.  */
-  if (GET_CODE (addr) == PLUS)
-    addr = XEXP (addr, 0);
-  if (addr != cfun->machine->fs.cfa_reg)
+  else if (base != m->fs.cfa_reg)
     {
-      addr = plus_constant (cfun->machine->fs.cfa_reg,
-			    cfun->machine->fs.cfa_offset - cfa_offset);
+      addr = plus_constant (m->fs.cfa_reg, m->fs.cfa_offset - cfa_offset);
       mem = gen_rtx_MEM (mode, addr);
       add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, mem, reg));
     }
-  RTX_FRAME_RELATED_P (insn) = 1;
 }
 
 /* Emit code to save registers using MOV insns.
@@ -9511,6 +9556,7 @@ ix86_expand_prologue (void)
       /* For the purposes of frame and register save area addressing,
 	 we've started over with a new frame.  */
       m->fs.sp_offset = INCOMING_FRAME_SP_OFFSET;
+      m->fs.realigned = true;
     }
 
   if (frame_pointer_needed && !m->fs.fp_valid)
@@ -9546,8 +9592,9 @@ ix86_expand_prologue (void)
       /* ??? There's no need to place the register save area into the
 	 aligned local stack frame.  We should do this later, after
 	 the register saves.  */
-      m->fs.fp_valid = false;
       m->fs.sp_offset = (m->fs.sp_offset + align_bytes - 1) & -align_bytes;
+      m->fs.fp_valid = false;
+      m->fs.realigned = true;
     }
 
   allocate = frame.to_allocate + frame.nsseregs * 16 + frame.padding0;
@@ -10064,6 +10111,7 @@ ix86_expand_epilogue (int style)
 		 frame.  Thus the FP is suddenly valid and the SP isn't.  */
 	      m->fs.fp_valid = true;
 	      m->fs.sp_valid = false;
+	      m->fs.realigned = false;
 	    }
 
 	  /* Leave results in shorter dependency chains on CPUs that are
@@ -10116,6 +10164,7 @@ ix86_expand_epilogue (int style)
 		 frame.  Thus the FP is suddenly valid and the SP isn't.  */
 	      m->fs.fp_valid = true;
 	      m->fs.sp_valid = false;
+	      m->fs.realigned = false;
 	    }
 
 	  /* Leave results in shorter dependency chains on CPUs that are
@@ -10156,6 +10205,7 @@ ix86_expand_epilogue (int style)
       m->fs.cfa_reg = stack_pointer_rtx;
       m->fs.cfa_offset = param_ptr_offset;
       m->fs.sp_offset = param_ptr_offset;
+      m->fs.realigned = false;
 
       add_reg_note (insn, REG_CFA_DEF_CFA,
 		    gen_rtx_PLUS (Pmode, stack_pointer_rtx,
@@ -10190,6 +10240,7 @@ ix86_expand_epilogue (int style)
   gcc_assert (m->fs.sp_offset == UNITS_PER_WORD);
   gcc_assert (m->fs.sp_valid);
   gcc_assert (!m->fs.fp_valid);
+  gcc_assert (!m->fs.realigned);
 
   /* Sibcall epilogues don't want a return instruction.  */
   if (style == 0)
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 300f22e..a2acc71 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2323,6 +2323,11 @@ struct GTY(()) machine_frame_state
   BOOL_BITFIELD sp_valid : 1;
   BOOL_BITFIELD fp_valid : 1;
   BOOL_BITFIELD drap_valid : 1;
+
+  /* Indicate whether the local stack frame has been re-aligned.  When
+     set, the SP/FP offsets above are relative to the aligned frame
+     and not the CFA.  */
+  BOOL_BITFIELD realigned : 1;
 };
 
 struct GTY(()) machine_function {
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index d8d3851..1ac874b 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -479,6 +479,8 @@ static struct dw_loc_descr_struct *build_cfa_loc
 static struct dw_loc_descr_struct *build_cfa_aligned_loc
   (HOST_WIDE_INT, HOST_WIDE_INT);
 static void def_cfa_1 (const char *, dw_cfa_location *);
+static struct dw_loc_descr_struct *mem_loc_descriptor
+  (rtx, enum machine_mode mode, enum var_init_status);
 
 /* How to start an assembler comment.  */
 #ifndef ASM_COMMENT_START
@@ -1833,6 +1835,17 @@ dwarf2out_frame_debug_def_cfa (rtx pat, const char *label)
       cfa.reg = REGNO (pat);
       break;
 
+    case MEM:
+      cfa.indirect = 1;
+      pat = XEXP (pat, 0);
+      if (GET_CODE (pat) == PLUS)
+	{
+	  cfa.base_offset = INTVAL (XEXP (pat, 1));
+	  pat = XEXP (pat, 0);
+	}
+      cfa.reg = REGNO (pat);
+      break;
+
     default:
       /* Recurse and define an expression.  */
       gcc_unreachable ();
@@ -1951,6 +1964,34 @@ dwarf2out_frame_debug_cfa_register (rtx set, const char *label)
   reg_save (label, sregno, dregno, 0);
 }
 
+/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */
+
+static void
+dwarf2out_frame_debug_cfa_expression (rtx set, const char *label)
+{
+  rtx src, dest, span;
+  dw_cfi_ref cfi = new_cfi ();
+
+  dest = SET_DEST (set);
+  src = SET_SRC (set);
+
+  gcc_assert (REG_P (src));
+  gcc_assert (MEM_P (dest));
+
+  span = targetm.dwarf_register_span (src);
+  gcc_assert (!span);
+
+  cfi->dw_cfi_opc = DW_CFA_expression;
+  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = DWARF_FRAME_REGNUM (REGNO (src));
+  cfi->dw_cfi_oprnd2.dw_cfi_loc
+    = mem_loc_descriptor (XEXP (dest, 0), GET_MODE (dest),
+			  VAR_INIT_STATUS_INITIALIZED);
+
+  /* ??? We'd like to use queue_reg_save, were the interface different,
+     and, as above, we could manage flushing for epilogues.  */
+  add_fde_cfi (label, cfi);
+}
+
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note.  */
 
 static void
@@ -2740,6 +2781,14 @@ dwarf2out_frame_debug (rtx insn, bool after_p)
 	handled_one = true;
 	break;
 
+      case REG_CFA_EXPRESSION:
+	n = XEXP (note, 0);
+	if (n == NULL)
+	  n = single_set (insn);
+	dwarf2out_frame_debug_cfa_expression (n, label);
+	handled_one = true;
+	break;
+
       case REG_CFA_RESTORE:
 	n = XEXP (note, 0);
 	if (n == NULL)
@@ -6181,8 +6230,6 @@ static dw_loc_descr_ref based_loc_descr (rtx, HOST_WIDE_INT,
 					 enum var_init_status);
 static int is_based_loc (const_rtx);
 static int resolve_one_addr (rtx *, void *);
-static dw_loc_descr_ref mem_loc_descriptor (rtx, enum machine_mode mode,
-					    enum var_init_status);
 static dw_loc_descr_ref concat_loc_descriptor (rtx, rtx,
 					       enum var_init_status);
 static dw_loc_descr_ref loc_descriptor (rtx, enum machine_mode mode,
diff --git a/gcc/reg-notes.def b/gcc/reg-notes.def
index aa2daea..f82e7b7 100644
--- a/gcc/reg-notes.def
+++ b/gcc/reg-notes.def
@@ -148,6 +148,12 @@ REG_NOTE (CFA_OFFSET)
    or the pattern should be simple reg-reg move.  */
 REG_NOTE (CFA_REGISTER)
 
+/* Attached to insns that are RTX_FRAME_RELATED_P, but are too complex
+   for FRAME_RELATED_EXPR intuition.  This is a save to memory, i.e. will
+   result in a DW_CFA_expression.  The pattern or the insn should be a
+   store of a register to an arbitrary (non-validated) memory address.  */
+REG_NOTE (CFA_EXPRESSION)
+
 /* Attached to insns that are RTX_FRAME_RELATED_P, with the information
    that this is a restore operation, i.e. will result in DW_CFA_restore
    or the like.  Either the attached rtx, or the destination of the insn's
