diff mbox series

Streamline debug info for up-level references

Message ID 1700099.CsN7JsXqnV@polaris
State New
Headers show
Series Streamline debug info for up-level references | expand

Commit Message

Eric Botcazou June 15, 2018, 10:49 a.m. UTC
Hi,

the motivating example is the following program in GNU C:

void foo (int len)
{
  int a = 1;
  int b[len];

  void bar (void)
  {
    b[0] = a;
    a++;
  }

  bar ();
}

int main (void)
{
  foo (4);
}

If you look at the debug info at -O0 -g, you'll see that that the compiler 
generates 2 DIEs for variable "a" and 4 DIEs for variable "b" because:
 1. It duplicates DIEs for each nested function containing up-level references
 2. It generates 2 DIEs for each VLA subject to up-level references.

That's sort of OK for languages where nested functions and VLAs are somewhat 
pathological devices but clearly doesn't scale for languages where they are 
both first-class citizens like Ada.  That's why AdaCore has been using a 
totally different scheme in its toolchain, first using a dedicated encoding 
with STABS then using standard DWARF-2 only.

This scheme is as follows:
 1. The compiler generates only 1 DIE per variable as in the regular case,
 2. For each child function, the static link is passed and the compiler 
generates a DW_AT_static_link attribute pointing to it for the child function,
 3. The debugger reconstructs the static nesting tree at run time by walking 
up the static chain and thus can resolve up-level references on its own.

It turns out that 2. and 3. were contributed a few releases ago, mainly by 
Pierre-Marie, both for GCC and GDB, so the last remaining step is 1. and it's 
the object of this contribution.  Note that it also subsumes:
  https://gcc.gnu.org/ml/gcc-patches/2018-05/msg00873.html
since contributing it piecewise turned out more problematic than helpful.

So the patch entirely removes the support of artificial variables for debug 
info of up-level reference purposes present both in the Fortran FE and the 
gimplifier (the Ada FE has never had such a thing) but only disconnects it in 
the nested function lowering pass because it uses for another purpose by GOMP.

Bootstrapped/regtested (GCC and GDB) on x86-64/Linux, OK for mainline?


2018-06-15  Eric Botcazou  <ebotcazou@adacore.com>

	* gimplify.c (nonlocal_vlas): Delete.
	(nonlocal_vla_vars): Likewise.
	(gimplify_var_or_parm_decl): Do not add debug VAR_DECLs for non-local
	referenced VLAs.
	(gimplify_body): Do not create and destroy nonlocal_vlas.
	* tree-nested.c: Include diagnostic.h.
	(use_pointer_in_frame): Tweak.
	(lookup_field_for_decl): Add assertion and declare the transformation.
	(convert_nonlocal_reference_op) <PARM_DECL>: Rework and issue an
	internal error when the reference is in a wrong context.  Do not
	create a debug decl by default.
	(note_nonlocal_block_vlas): Delete.
	(convert_nonlocal_reference_stmt) <GIMPLE_BIND>: Do not call it.
	(convert_local_reference_op) <PARM_DECL>: Skip the frame decl.  Do not
	create a debug decl by default.
	(convert_gimple_call) <GIMPLE_CALL>: Issue an internal error when the
	call is in a wrong context.
	(fixup_vla_decls): New function.
	(finalize_nesting_tree_1): Adjust comment.  Call fixup_vla_decls if no
	debug variables were created.
	* tree.c (decl_value_expr_lookup): Add checking assertion.
	(decl_value_expr_insert): Likewise.


2018-06-15  Eric Botcazou  <ebotcazou@adacore.com>

	* fortran/trans-decl.c (nonlocal_dummy_decl_pset): Delete.
	(nonlocal_dummy_decls): Likewise.
	(gfc_nonlocal_dummy_array_decl): Likewise.
	(gfc_get_symbol_decl): Do not call gfc_nonlocal_dummy_array_decl.
	(gfc_get_fake_result_decl): Do not generate a new DECL if simply
	reusing the result of a recursive call.
	(gfc_generate_function_code): Do not create, insert and destroy
	nonlocal_dummy_decls.


2018-06-15  Eric Botcazou  <ebotcazou@adacore.com>

	* gcc.dg/debug/dwarf2/pr37726.c: Delete.
	* gnat.dg/stack_usage5.adb: New test.

Comments

Richard Biener June 15, 2018, 11:06 a.m. UTC | #1
On Fri, Jun 15, 2018 at 12:50 PM Eric Botcazou <ebotcazou@adacore.com> wrote:
>
> Hi,
>
> the motivating example is the following program in GNU C:
>
> void foo (int len)
> {
>   int a = 1;
>   int b[len];
>
>   void bar (void)
>   {
>     b[0] = a;
>     a++;
>   }
>
>   bar ();
> }
>
> int main (void)
> {
>   foo (4);
> }
>
> If you look at the debug info at -O0 -g, you'll see that that the compiler
> generates 2 DIEs for variable "a" and 4 DIEs for variable "b" because:
>  1. It duplicates DIEs for each nested function containing up-level references
>  2. It generates 2 DIEs for each VLA subject to up-level references.
>
> That's sort of OK for languages where nested functions and VLAs are somewhat
> pathological devices but clearly doesn't scale for languages where they are
> both first-class citizens like Ada.  That's why AdaCore has been using a
> totally different scheme in its toolchain, first using a dedicated encoding
> with STABS then using standard DWARF-2 only.
>
> This scheme is as follows:
>  1. The compiler generates only 1 DIE per variable as in the regular case,
>  2. For each child function, the static link is passed and the compiler
> generates a DW_AT_static_link attribute pointing to it for the child function,
>  3. The debugger reconstructs the static nesting tree at run time by walking
> up the static chain and thus can resolve up-level references on its own.
>
> It turns out that 2. and 3. were contributed a few releases ago, mainly by
> Pierre-Marie, both for GCC and GDB, so the last remaining step is 1. and it's
> the object of this contribution.  Note that it also subsumes:
>   https://gcc.gnu.org/ml/gcc-patches/2018-05/msg00873.html
> since contributing it piecewise turned out more problematic than helpful.
>
> So the patch entirely removes the support of artificial variables for debug
> info of up-level reference purposes present both in the Fortran FE and the
> gimplifier (the Ada FE has never had such a thing) but only disconnects it in
> the nested function lowering pass because it uses for another purpose by GOMP.
>
> Bootstrapped/regtested (GCC and GDB) on x86-64/Linux, OK for mainline?

Looks good to me.  Rather than removing dwarf2/pr37726.c can you try
turning that into a guality test that verifies the debug experience is the
same (or better) than before?  I realize guality stuff is fragile but you can
restrict it to -O0 if you like (not sure if dg-skip-if supports that).

Thanks,
Richard.

>
> 2018-06-15  Eric Botcazou  <ebotcazou@adacore.com>
>
>         * gimplify.c (nonlocal_vlas): Delete.
>         (nonlocal_vla_vars): Likewise.
>         (gimplify_var_or_parm_decl): Do not add debug VAR_DECLs for non-local
>         referenced VLAs.
>         (gimplify_body): Do not create and destroy nonlocal_vlas.
>         * tree-nested.c: Include diagnostic.h.
>         (use_pointer_in_frame): Tweak.
>         (lookup_field_for_decl): Add assertion and declare the transformation.
>         (convert_nonlocal_reference_op) <PARM_DECL>: Rework and issue an
>         internal error when the reference is in a wrong context.  Do not
>         create a debug decl by default.
>         (note_nonlocal_block_vlas): Delete.
>         (convert_nonlocal_reference_stmt) <GIMPLE_BIND>: Do not call it.
>         (convert_local_reference_op) <PARM_DECL>: Skip the frame decl.  Do not
>         create a debug decl by default.
>         (convert_gimple_call) <GIMPLE_CALL>: Issue an internal error when the
>         call is in a wrong context.
>         (fixup_vla_decls): New function.
>         (finalize_nesting_tree_1): Adjust comment.  Call fixup_vla_decls if no
>         debug variables were created.
>         * tree.c (decl_value_expr_lookup): Add checking assertion.
>         (decl_value_expr_insert): Likewise.
>
>
> 2018-06-15  Eric Botcazou  <ebotcazou@adacore.com>
>
>         * fortran/trans-decl.c (nonlocal_dummy_decl_pset): Delete.
>         (nonlocal_dummy_decls): Likewise.
>         (gfc_nonlocal_dummy_array_decl): Likewise.
>         (gfc_get_symbol_decl): Do not call gfc_nonlocal_dummy_array_decl.
>         (gfc_get_fake_result_decl): Do not generate a new DECL if simply
>         reusing the result of a recursive call.
>         (gfc_generate_function_code): Do not create, insert and destroy
>         nonlocal_dummy_decls.
>
>
> 2018-06-15  Eric Botcazou  <ebotcazou@adacore.com>
>
>         * gcc.dg/debug/dwarf2/pr37726.c: Delete.
>         * gnat.dg/stack_usage5.adb: New test.
>
> --
> Eric Botcazou
Eric Botcazou June 17, 2018, 11:35 a.m. UTC | #2
> Looks good to me.  Rather than removing dwarf2/pr37726.c can you try
> turning that into a guality test that verifies the debug experience is the
> same (or better) than before?  I realize guality stuff is fragile but you
> can restrict it to -O0 if you like (not sure if dg-skip-if supports that).

Yes, it does, I have attached the final testcase.  It passes with e.g. the 
system debugger on OpenSuSE Leap 42.3.


	* gcc.dg/debug/dwarf2/pr37726.c: Move to...
	* gcc.dg/guality/pr37726.c: ...here and turn into GDB test.
Eric Botcazou June 18, 2018, 7:50 a.m. UTC | #3
> Looks good to me.  Rather than removing dwarf2/pr37726.c can you try
> turning that into a guality test that verifies the debug experience is the
> same (or better) than before?  I realize guality stuff is fragile but you
> can restrict it to -O0 if you like (not sure if dg-skip-if supports that).

Gah.  I got caught by "make -k check-fortran" not running the Fortran part of 
libgomp and missed the handful of ICEs in the Fortran part of libgomp...

It turns out that omp-low.c uses lots of and chains of DECL_VALUE_EXPRs (w or 
w/o my change) and trips over the assertions, so I have applied the attached 
patch to revert the problematic changes for now.  I'll further think about it.


	* tree.c (decl_value_expr_lookup): Revert latest change.
	(decl_value_expr_insert): Likewise.
fortran/
	* trans-decl.c (gfc_get_fake_result_decl): Revert latest change.
diff mbox series

Patch

Index: fortran/trans-decl.c
===================================================================
--- fortran/trans-decl.c	(revision 261473)
+++ fortran/trans-decl.c	(working copy)
@@ -61,9 +61,6 @@  static GTY(()) tree parent_fake_result_d
 static GTY(()) tree saved_function_decls;
 static GTY(()) tree saved_parent_function_decls;
 
-static hash_set<tree> *nonlocal_dummy_decl_pset;
-static GTY(()) tree nonlocal_dummy_decls;
-
 /* Holds the variable DECLs that are locals.  */
 
 static GTY(()) tree saved_local_decls;
@@ -1284,39 +1281,6 @@  gfc_build_dummy_array_decl (gfc_symbol *
   return decl;
 }
 
-/* For symbol SYM with GFC_DECL_SAVED_DESCRIPTOR used in contained
-   function add a VAR_DECL to the current function with DECL_VALUE_EXPR
-   pointing to the artificial variable for debug info purposes.  */
-
-static void
-gfc_nonlocal_dummy_array_decl (gfc_symbol *sym)
-{
-  tree decl, dummy;
-
-  if (! nonlocal_dummy_decl_pset)
-    nonlocal_dummy_decl_pset = new hash_set<tree>;
-
-  if (nonlocal_dummy_decl_pset->add (sym->backend_decl))
-    return;
-
-  dummy = GFC_DECL_SAVED_DESCRIPTOR (sym->backend_decl);
-  decl = build_decl (input_location, VAR_DECL, DECL_NAME (dummy),
-		     TREE_TYPE (sym->backend_decl));
-  DECL_ARTIFICIAL (decl) = 0;
-  TREE_USED (decl) = 1;
-  TREE_PUBLIC (decl) = 0;
-  TREE_STATIC (decl) = 0;
-  DECL_EXTERNAL (decl) = 0;
-  if (DECL_BY_REFERENCE (dummy))
-    DECL_BY_REFERENCE (decl) = 1;
-  DECL_LANG_SPECIFIC (decl) = DECL_LANG_SPECIFIC (sym->backend_decl);
-  SET_DECL_VALUE_EXPR (decl, sym->backend_decl);
-  DECL_HAS_VALUE_EXPR_P (decl) = 1;
-  DECL_CONTEXT (decl) = DECL_CONTEXT (sym->backend_decl);
-  DECL_CHAIN (decl) = nonlocal_dummy_decls;
-  nonlocal_dummy_decls = decl;
-}
-
 /* Return a constant or a variable to use as a string length.  Does not
    add the decl to the current scope.  */
 
@@ -1643,12 +1607,6 @@  gfc_get_symbol_decl (gfc_symbol * sym)
 	  gfc_add_assign_aux_vars (sym);
 	}
 
-      if ((sym->attr.dimension || IS_CLASS_ARRAY (sym))
-	  && DECL_LANG_SPECIFIC (sym->backend_decl)
-	  && GFC_DECL_SAVED_DESCRIPTOR (sym->backend_decl)
-	  && DECL_CONTEXT (sym->backend_decl) != current_function_decl)
-	gfc_nonlocal_dummy_array_decl (sym);
-
       if (sym->ts.type == BT_CLASS && sym->backend_decl)
 	GFC_DECL_CLASS(sym->backend_decl) = 1;
 
@@ -2950,13 +2908,14 @@  gfc_get_fake_result_decl (gfc_symbol * s
       && sym->ns->proc_name->attr.entry_master
       && sym != sym->ns->proc_name)
     {
-      tree t = NULL, var;
+      tree t = NULL, var, field;
       if (this_fake_result_decl != NULL)
 	for (t = TREE_CHAIN (this_fake_result_decl); t; t = TREE_CHAIN (t))
 	  if (strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t)), sym->name) == 0)
 	    break;
       if (t)
 	return TREE_VALUE (t);
+
       decl = gfc_get_fake_result_decl (sym->ns->proc_name, parent_flag);
 
       if (parent_flag)
@@ -2964,20 +2923,17 @@  gfc_get_fake_result_decl (gfc_symbol * s
       else
 	this_fake_result_decl = current_fake_result_decl;
 
-      if (decl && sym->ns->proc_name->attr.mixed_entry_master)
-	{
-	  tree field;
-
-	  for (field = TYPE_FIELDS (TREE_TYPE (decl));
-	       field; field = DECL_CHAIN (field))
-	    if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)),
-		sym->name) == 0)
-	      break;
+      if (!sym->ns->proc_name->attr.mixed_entry_master)
+	return decl;
 
-	  gcc_assert (field != NULL_TREE);
-	  decl = fold_build3_loc (input_location, COMPONENT_REF,
-				  TREE_TYPE (field), decl, field, NULL_TREE);
-	}
+      for (field = TYPE_FIELDS (TREE_TYPE (decl));
+	   field; field = DECL_CHAIN (field))
+	if (strcmp (IDENTIFIER_POINTER (DECL_NAME (field)), sym->name) == 0)
+	  break;
+
+      gcc_assert (field != NULL_TREE);
+      decl = fold_build3_loc (input_location, COMPONENT_REF,
+			      TREE_TYPE (field), decl, field, NULL_TREE);
 
       var = create_tmp_var_raw (TREE_TYPE (decl), sym->name);
       if (parent_flag)
@@ -6442,9 +6398,6 @@  gfc_generate_function_code (gfc_namespac
 
   gfc_generate_contained_functions (ns);
 
-  nonlocal_dummy_decls = NULL;
-  nonlocal_dummy_decl_pset = NULL;
-
   has_coarray_vars = false;
   generate_local_vars (ns);
 
@@ -6644,15 +6597,6 @@  gfc_generate_function_code (gfc_namespac
     = build3_v (BIND_EXPR, decl, DECL_SAVED_TREE (fndecl),
 		DECL_INITIAL (fndecl));
 
-  if (nonlocal_dummy_decls)
-    {
-      BLOCK_VARS (DECL_INITIAL (fndecl))
-	= chainon (BLOCK_VARS (DECL_INITIAL (fndecl)), nonlocal_dummy_decls);
-      delete nonlocal_dummy_decl_pset;
-      nonlocal_dummy_decls = NULL;
-      nonlocal_dummy_decl_pset = NULL;
-    }
-
   /* Output the GENERIC tree.  */
   dump_function (TDI_original, fndecl);
 
Index: gimplify.c
===================================================================
--- gimplify.c	(revision 261473)
+++ gimplify.c	(working copy)
@@ -2674,12 +2674,6 @@  gimplify_conversion (tree *expr_p)
   return GS_OK;
 }
 
-/* Nonlocal VLAs seen in the current function.  */
-static hash_set<tree> *nonlocal_vlas;
-
-/* The VAR_DECLs created for nonlocal VLAs for debug info purposes.  */
-static tree nonlocal_vla_vars;
-
 /* Gimplify a VAR_DECL or PARM_DECL.  Return GS_OK if we expanded a
    DECL_VALUE_EXPR, and it's worth re-examining things.  */
 
@@ -2710,38 +2704,7 @@  gimplify_var_or_parm_decl (tree *expr_p)
   /* If the decl is an alias for another expression, substitute it now.  */
   if (DECL_HAS_VALUE_EXPR_P (decl))
     {
-      tree value_expr = DECL_VALUE_EXPR (decl);
-
-      /* For referenced nonlocal VLAs add a decl for debugging purposes
-	 to the current function.  */
-      if (VAR_P (decl)
-	  && TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
-	  && nonlocal_vlas != NULL
-	  && TREE_CODE (value_expr) == INDIRECT_REF
-	  && TREE_CODE (TREE_OPERAND (value_expr, 0)) == VAR_DECL
-	  && decl_function_context (decl) != current_function_decl)
-	{
-	  struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp;
-	  while (ctx
-		 && (ctx->region_type == ORT_WORKSHARE
-		     || ctx->region_type == ORT_SIMD
-		     || ctx->region_type == ORT_ACC))
-	    ctx = ctx->outer_context;
-	  if (!ctx && !nonlocal_vlas->add (decl))
-	    {
-	      tree copy = copy_node (decl);
-
-	      lang_hooks.dup_lang_specific_decl (copy);
-	      SET_DECL_RTL (copy, 0);
-	      TREE_USED (copy) = 1;
-	      DECL_CHAIN (copy) = nonlocal_vla_vars;
-	      nonlocal_vla_vars = copy;
-	      SET_DECL_VALUE_EXPR (copy, unshare_expr (value_expr));
-	      DECL_HAS_VALUE_EXPR_P (copy) = 1;
-	    }
-	}
-
-      *expr_p = unshare_expr (value_expr);
+      *expr_p = unshare_expr (DECL_VALUE_EXPR (decl));
       return GS_OK;
     }
 
@@ -12591,7 +12554,6 @@  gimplify_body (tree fndecl, bool do_parm
   gimple_seq parm_stmts, parm_cleanup = NULL, seq;
   gimple *outer_stmt;
   gbind *outer_bind;
-  struct cgraph_node *cgn;
 
   timevar_push (TV_TREE_GIMPLIFY);
 
@@ -12618,10 +12580,6 @@  gimplify_body (tree fndecl, bool do_parm
   unshare_body (fndecl);
   unvisit_body (fndecl);
 
-  cgn = cgraph_node::get (fndecl);
-  if (cgn && cgn->origin)
-    nonlocal_vlas = new hash_set<tree>;
-
   /* Make sure input_location isn't set to something weird.  */
   input_location = DECL_SOURCE_LOCATION (fndecl);
 
@@ -12674,27 +12632,6 @@  gimplify_body (tree fndecl, bool do_parm
 	  }
     }
 
-  if (nonlocal_vlas)
-    {
-      if (nonlocal_vla_vars)
-	{
-	  /* tree-nested.c may later on call declare_vars (..., true);
-	     which relies on BLOCK_VARS chain to be the tail of the
-	     gimple_bind_vars chain.  Ensure we don't violate that
-	     assumption.  */
-	  if (gimple_bind_block (outer_bind)
-	      == DECL_INITIAL (current_function_decl))
-	    declare_vars (nonlocal_vla_vars, outer_bind, true);
-	  else
-	    BLOCK_VARS (DECL_INITIAL (current_function_decl))
-	      = chainon (BLOCK_VARS (DECL_INITIAL (current_function_decl)),
-			 nonlocal_vla_vars);
-	  nonlocal_vla_vars = NULL_TREE;
-	}
-      delete nonlocal_vlas;
-      nonlocal_vlas = NULL;
-    }
-
   if ((flag_openacc || flag_openmp || flag_openmp_simd)
       && gimplify_omp_ctxp)
     {
Index: testsuite/gcc.dg/debug/dwarf2/pr37726.c
===================================================================
--- testsuite/gcc.dg/debug/dwarf2/pr37726.c	(revision 261473)
+++ testsuite/gcc.dg/debug/dwarf2/pr37726.c	(nonexistent)
@@ -1,25 +0,0 @@ 
-/* PR debug/37726 */
-/* { dg-do compile } */
-/* { dg-options "-gdwarf -O0 -dA -fno-merge-debug-strings" } */
-
-int foo (int parm)
-{
-  int var = 0;
-  int bar (void)
-  {
-    return parm + var;
-  }
-  parm++;
-  var++;
-  return bar ();
-}
-
-int
-main (void)
-{
-  return foo (4) - 6;
-}
-
-/* Both parm and var variables should be in debug info for both foo and bar.  */
-/* { dg-final { scan-assembler-times "\"parm\[^\n\]*\"\[^\n\]*DW_AT_name" 2 } } */
-/* { dg-final { scan-assembler-times "\"var\[^\n\]*\"\[^\n\]*DW_AT_name" 2 } } */
Index: testsuite/gnat.dg/stack_usage5.adb
===================================================================
--- testsuite/gnat.dg/stack_usage5.adb	(nonexistent)
+++ testsuite/gnat.dg/stack_usage5.adb	(working copy)
@@ -0,0 +1,15 @@ 
+-- { dg-do compile }
+-- { dg-options "-Wstack-usage=512" }
+
+procedure Stack_Usage5 (C : Character) is
+
+  S : String (1 .. 300);
+
+  procedure Set is
+  begin
+    S (1) := C;
+  end;
+
+begin
+  Set;
+end;
Index: tree-nested.c
===================================================================
--- tree-nested.c	(revision 261473)
+++ tree-nested.c	(working copy)
@@ -41,6 +41,7 @@ 
 #include "langhooks.h"
 #include "gimple-low.h"
 #include "gomp-constants.h"
+#include "diagnostic.h"
 
 
 /* The object of this pass is to lower the representation of a set of nested
@@ -236,23 +237,25 @@  get_frame_type (struct nesting_info *inf
   return type;
 }
 
-/* Return true if DECL should be referenced by pointer in the non-local
-   frame structure.  */
+/* Return true if DECL should be referenced by pointer in the non-local frame
+   structure.  */
 
 static bool
 use_pointer_in_frame (tree decl)
 {
   if (TREE_CODE (decl) == PARM_DECL)
     {
-      /* It's illegal to copy TREE_ADDRESSABLE, impossible to copy variable
-         sized decls, and inefficient to copy large aggregates.  Don't bother
-         moving anything but scalar variables.  */
+      /* It's illegal to copy TREE_ADDRESSABLE, impossible to copy variable-
+	 sized DECLs, and inefficient to copy large aggregates.  Don't bother
+	 moving anything but scalar parameters.  */
       return AGGREGATE_TYPE_P (TREE_TYPE (decl));
     }
   else
     {
-      /* Variable sized types make things "interesting" in the frame.  */
-      return DECL_SIZE (decl) == NULL || !TREE_CONSTANT (DECL_SIZE (decl));
+      /* Variable-sized DECLs can only come from OMP clauses at this point
+	 since the gimplifier has already turned the regular variables into
+	 pointers.  Do the same as the gimplifier.  */
+      return !DECL_SIZE (decl) || TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST;
     }
 }
 
@@ -263,6 +266,8 @@  static tree
 lookup_field_for_decl (struct nesting_info *info, tree decl,
 		       enum insert_option insert)
 {
+  gcc_checking_assert (decl_function_context (decl) == info->context);
+
   if (insert == NO_INSERT)
     {
       tree *slot = info->field_map->get (decl);
@@ -272,6 +277,7 @@  lookup_field_for_decl (struct nesting_in
   tree *slot = &info->field_map->get_or_insert (decl);
   if (!*slot)
     {
+      tree type = get_frame_type (info);
       tree field = make_node (FIELD_DECL);
       DECL_NAME (field) = DECL_NAME (decl);
 
@@ -290,9 +296,35 @@  lookup_field_for_decl (struct nesting_in
           TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (decl);
           DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (decl);
           TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (decl);
+
+	  /* Declare the transformation and adjust the original DECL.  For a
+	     variable or for a parameter when not optimizing, we make it point
+	     to the field in the frame directly.  For a parameter, we don't do
+	     it when optimizing because the variable tracking pass will already
+	     do the job,  */
+	  if (VAR_P (decl) || !optimize)
+	    {
+	      tree x
+		= build3 (COMPONENT_REF, TREE_TYPE (field), info->frame_decl,
+			  field, NULL_TREE);
+
+	      /* If the next declaration is a PARM_DECL pointing to the DECL,
+		 we need to adjust its VALUE_EXPR directly, since chains of
+		 VALUE_EXPRs run afoul of garbage collection.  This occurs
+		 in Ada for Out parameters that aren't copied in.  */
+	      tree next = DECL_CHAIN (decl);
+	      if (next
+		  && TREE_CODE (next) == PARM_DECL
+		  && DECL_HAS_VALUE_EXPR_P (next)
+		  && DECL_VALUE_EXPR (next) == decl)
+		SET_DECL_VALUE_EXPR (next, x);
+
+	      SET_DECL_VALUE_EXPR (decl, x);
+	      DECL_HAS_VALUE_EXPR_P (decl) = 1;
+	    }
 	}
 
-      insert_field_into_struct (get_frame_type (info), field);
+      insert_field_into_struct (type, field);
       *slot = field;
 
       if (TREE_CODE (decl) == PARM_DECL)
@@ -990,37 +1022,48 @@  convert_nonlocal_reference_op (tree *tp,
       /* FALLTHRU */
 
     case PARM_DECL:
-      if (decl_function_context (t) != info->context)
-	{
-	  tree x;
-	  wi->changed = true;
+      {
+	tree x, target_context = decl_function_context (t);
 
-	  x = get_nonlocal_debug_decl (info, t);
-	  if (!bitmap_bit_p (info->suppress_expansion, DECL_UID (t)))
-	    {
-	      tree target_context = decl_function_context (t);
-	      struct nesting_info *i;
-	      for (i = info->outer; i->context != target_context; i = i->outer)
-		continue;
-	      x = lookup_field_for_decl (i, t, INSERT);
-	      x = get_frame_field (info, target_context, x, &wi->gsi);
-	      if (use_pointer_in_frame (t))
-		{
-		  x = init_tmp_var (info, x, &wi->gsi);
-		  x = build_simple_mem_ref (x);
-		}
-	    }
+	if (info->context == target_context)
+	  break;
 
-	  if (wi->val_only)
-	    {
-	      if (wi->is_lhs)
-		x = save_tmp_var (info, x, &wi->gsi);
-	      else
+	wi->changed = true;
+
+	if (bitmap_bit_p (info->suppress_expansion, DECL_UID (t)))
+	  x = get_nonlocal_debug_decl (info, t);
+	else
+	  {
+	    struct nesting_info *i = info;
+	    while (i && i->context != target_context)
+	      i = i->outer;
+	    /* If none of the outer contexts is the target context, this means
+	       that the VAR or PARM_DECL is referenced in a wrong context.  */
+	    if (!i)
+	      internal_error ("%s from %s referenced in %s",
+			      IDENTIFIER_POINTER (DECL_NAME (t)),
+			      IDENTIFIER_POINTER (DECL_NAME (target_context)),
+			      IDENTIFIER_POINTER (DECL_NAME (info->context)));
+
+	    x = lookup_field_for_decl (i, t, INSERT);
+	    x = get_frame_field (info, target_context, x, &wi->gsi);
+	    if (use_pointer_in_frame (t))
+	      {
 		x = init_tmp_var (info, x, &wi->gsi);
-	    }
+		x = build_simple_mem_ref (x);
+	      }
+	  }
 
-	  *tp = x;
-	}
+	if (wi->val_only)
+	  {
+	    if (wi->is_lhs)
+	      x = save_tmp_var (info, x, &wi->gsi);
+	    else
+	      x = init_tmp_var (info, x, &wi->gsi);
+	  }
+
+	*tp = x;
+      }
       break;
 
     case LABEL_DECL:
@@ -1406,22 +1449,6 @@  note_nonlocal_vla_type (struct nesting_i
     }
 }
 
-/* Create nonlocal debug decls for nonlocal VLA array bounds for VLAs
-   in BLOCK.  */
-
-static void
-note_nonlocal_block_vlas (struct nesting_info *info, tree block)
-{
-  tree var;
-
-  for (var = BLOCK_VARS (block); var; var = DECL_CHAIN (var))
-    if (VAR_P (var)
-	&& variably_modified_type_p (TREE_TYPE (var), NULL)
-	&& DECL_HAS_VALUE_EXPR_P (var)
-	&& decl_function_context (var) != info->context)
-      note_nonlocal_vla_type (info, TREE_TYPE (var));
-}
-
 /* Callback for walk_gimple_stmt.  Rewrite all references to VAR and
    PARM_DECLs that belong to outer functions.  This handles statements
    that are not handled via the standard recursion done in
@@ -1566,8 +1593,6 @@  convert_nonlocal_reference_stmt (gimple_
     case GIMPLE_BIND:
       {
       gbind *bind_stmt = as_a <gbind *> (stmt);
-      if (!optimize && gimple_bind_block (bind_stmt))
-	note_nonlocal_block_vlas (info, gimple_bind_block (bind_stmt));
 
       for (tree var = gimple_bind_vars (bind_stmt); var; var = DECL_CHAIN (var))
 	if (TREE_CODE (var) == NAMELIST_DECL)
@@ -1683,7 +1708,7 @@  convert_local_reference_op (tree *tp, in
       /* FALLTHRU */
 
     case PARM_DECL:
-      if (decl_function_context (t) == info->context)
+      if (t != info->frame_decl && decl_function_context (t) == info->context)
 	{
 	  /* If we copied a pointer to the frame, then the original decl
 	     is used unchanged in the parent function.  */
@@ -1697,8 +1722,9 @@  convert_local_reference_op (tree *tp, in
 	    break;
 	  wi->changed = true;
 
-	  x = get_local_debug_decl (info, t, field);
-	  if (!bitmap_bit_p (info->suppress_expansion, DECL_UID (t)))
+	  if (bitmap_bit_p (info->suppress_expansion, DECL_UID (t)))
+	    x = get_local_debug_decl (info, t, field);
+	  else
 	    x = get_frame_field (info, info->context, field, &wi->gsi);
 
 	  if (wi->val_only)
@@ -2620,6 +2646,17 @@  convert_gimple_call (gimple_stmt_iterato
       target_context = decl_function_context (decl);
       if (target_context && DECL_STATIC_CHAIN (decl))
 	{
+	  struct nesting_info *i = info;
+	  while (i && i->context != target_context)
+	    i = i->outer;
+	  /* If none of the outer contexts is the target context, this means
+	     that the function is called in a wrong context.  */
+	  if (!i)
+	    internal_error ("%s from %s called in %s",
+			    IDENTIFIER_POINTER (DECL_NAME (decl)),
+			    IDENTIFIER_POINTER (DECL_NAME (target_context)),
+			    IDENTIFIER_POINTER (DECL_NAME (info->context)));
+
 	  gimple_call_set_chain (as_a <gcall *> (stmt),
 				 get_static_chain (info, target_context,
 						   &wi->gsi));
@@ -2941,6 +2978,33 @@  remap_vla_decls (tree block, struct nest
   delete id.cb.decl_map;
 }
 
+/* Fixup VLA decls in BLOCK and subblocks if remapped variables are
+   involved.  */
+
+static void
+fixup_vla_decls (tree block)
+{
+  for (tree var = BLOCK_VARS (block); var; var = DECL_CHAIN (var))
+    if (VAR_P (var) && DECL_HAS_VALUE_EXPR_P (var))
+      {
+	tree val = DECL_VALUE_EXPR (var);
+
+	if (!(TREE_CODE (val) == INDIRECT_REF
+	      && VAR_P (TREE_OPERAND (val, 0))
+	      && DECL_HAS_VALUE_EXPR_P (TREE_OPERAND (val, 0))))
+	  continue;
+
+	/* Fully expand value expressions.  This avoids having debug variables
+	   only referenced from them and that can be swept during GC.  */
+	val = build1 (INDIRECT_REF, TREE_TYPE (val),
+		      DECL_VALUE_EXPR (TREE_OPERAND (val, 0)));
+	SET_DECL_VALUE_EXPR (var, val);
+      }
+
+  for (tree sub = BLOCK_SUBBLOCKS (block); sub; sub = BLOCK_CHAIN (sub))
+    fixup_vla_decls (sub);
+}
+
 /* Fold the MEM_REF *E.  */
 bool
 fold_mem_refs (tree *const &e, void *data ATTRIBUTE_UNUSED)
@@ -3065,9 +3129,8 @@  finalize_nesting_tree_1 (struct nesting_
 		    gimple_seq_first_stmt (gimple_body (context)), true);
     }
 
-  /* If any parameters were referenced non-locally, then we need to
-     insert a copy.  Likewise, if any variables were referenced by
-     pointer, we need to initialize the address.  */
+  /* If any parameters were referenced non-locally, then we need to insert
+     a copy or a pointer.  */
   if (root->any_parm_remapped)
     {
       tree p;
@@ -3243,6 +3306,8 @@  finalize_nesting_tree_1 (struct nesting_
 	  = chainon (BLOCK_VARS (DECL_INITIAL (root->context)),
 		     root->debug_var_chain);
     }
+  else
+    fixup_vla_decls (DECL_INITIAL (root->context));
 
   /* Fold the rewritten MEM_REF trees.  */
   root->mem_refs->traverse<void *, fold_mem_refs> (NULL);
Index: tree.c
===================================================================
--- tree.c	(revision 261473)
+++ tree.c	(working copy)
@@ -6337,7 +6337,15 @@  decl_value_expr_lookup (tree from)
 
   h = value_expr_for_decl->find_with_hash (&in, DECL_UID (from));
   if (h)
-    return h->to;
+    {
+      /* Chains of value expressions may run afoul of garbage collection.  */
+      gcc_checking_assert (!(h->to
+			     && (TREE_CODE (h->to) == PARM_DECL
+				 || TREE_CODE (h->to) == VAR_DECL)
+			     && DECL_HAS_VALUE_EXPR_P (h->to)));
+      return h->to;
+    }
+
   return NULL_TREE;
 }
 
@@ -6348,6 +6356,12 @@  decl_value_expr_insert (tree from, tree
 {
   struct tree_decl_map *h;
 
+  /* Chains of value expressions may run afoul of garbage collection.  */
+  gcc_checking_assert (!(to
+			 && (TREE_CODE (to) == PARM_DECL
+			     || TREE_CODE (to) == VAR_DECL)
+			 && DECL_HAS_VALUE_EXPR_P (to)));
+
   h = ggc_alloc<tree_decl_map> ();
   h->base.from = from;
   h->to = to;