Patchwork turn cfun->local_decls into a VEC

login
register
mail settings
Submitter Nathan Froyd
Date July 1, 2010, 2:09 a.m.
Message ID <20100701020958.GZ22606@codesourcery.com>
Download mbox | patch
Permalink /patch/57461/
State New
Headers show

Comments

Nathan Froyd - July 1, 2010, 2:09 a.m.
Enough with those trivial TREE_LIST removal patches that break
bootstrap.  How about a patch that does some real damage? :)

The patch below turns cfun->local_decls into a VEC.  The order of the
elements in the list doesn't matter, so it's straightforward to retain
the same time bounds for deleting elements in the few places that we do
that.  add_local_decl helps localize the allocation type of the
local_decls VEC (and came in handy when I realized that the vector
couldn't be heap allocated); FOR_EACH_VEC_ELT_REVERSE and
FOR_EACH_LOCAL_DECL help to not make dumb mistakes while converting
everything over.  Both of these additions also make things clearer.

Bootstrapped/tested on x86_64-unknown-linux-gnu, all languages except
for lto (libjava/libstdc++ rebuild and test still ongoing, but expected
to succeed).  I understand that I need to install gold to test that and
will perform that testing prior to installing the patch.  OK to commit?

-Nathan

gcc/
	* vec.h (FOR_EACH_VEC_ELT_REVERSE): New macro.
	* function.h (struct_function): Change type of local_decls field
	to a VEC.
	(add_local_decl): New function.
	(FOR_EACH_LOCAL_DECL): New macro.
	* cfgexpand.c (init_vars_expansion): Adjust for new type of
	cfun->local_decls.
	(estimated_stack_frame_size): Likewise.
	(expand_used_vars): Likewise.
	* cgraphbuild.c (build_cgraph_edges): Likewise.
	* function.c (instantiate_decls_1): Likewise.
	* ipa-struct-reorg.c (build_data_structure): Likewise.
	* ipa-type-escape.c (analyze_function): Likewise.
	* lto-streamer-in.c (input_function): Likewise.
	* lto-streamer-out.c (output_function): Likewise.
	* tree-ssa-live.c (remove_unused_locals): Likewise.
	* tree.c (free_lang_data_in_decl): Likewise.
	(find_decls_types_in_node): Likewise.
	* omp-low.c (remove_exit_barrier): Likewise.
	(expand_omp_taskreg): Likewise.
	(list2chain): Rename to...
	(vec2chain): ...this.  Adjust.
	* cgraphunit.c (assemble_thunk): Call add_local_decl.
	* tree-cfg.c (replace_by_duplicate_decl): Likewise.
	* gimple-low.c (record_vars_into): Likewise.
	* tree-inline.c (remap_decls): Likewise.
	(declare_return_variable): Likewise.
	(declare_inline_vars): Likewise.
	(copy_forbidden): Adjust for new type of cfun->local_decls.
	(add_local_variables): New function.
	(expand_call_inline): Call it.
	(tree_function_versioning): Likewise.

gcc/cp/
	* decl.c (cp_finish_decl): Call add_local_decl.
	* optimize.c (clone_body): Adjust for new type of cfun->local_decls.
Diego Novillo - July 1, 2010, 3:22 a.m.
On Wed, Jun 30, 2010 at 22:09, Nathan Froyd <froydnj@codesourcery.com> wrote:

> gcc/
>        * vec.h (FOR_EACH_VEC_ELT_REVERSE): New macro.
>        * function.h (struct_function): Change type of local_decls field
>        to a VEC.
>        (add_local_decl): New function.
>        (FOR_EACH_LOCAL_DECL): New macro.
>        * cfgexpand.c (init_vars_expansion): Adjust for new type of
>        cfun->local_decls.
>        (estimated_stack_frame_size): Likewise.
>        (expand_used_vars): Likewise.
>        * cgraphbuild.c (build_cgraph_edges): Likewise.
>        * function.c (instantiate_decls_1): Likewise.
>        * ipa-struct-reorg.c (build_data_structure): Likewise.
>        * ipa-type-escape.c (analyze_function): Likewise.
>        * lto-streamer-in.c (input_function): Likewise.
>        * lto-streamer-out.c (output_function): Likewise.
>        * tree-ssa-live.c (remove_unused_locals): Likewise.
>        * tree.c (free_lang_data_in_decl): Likewise.
>        (find_decls_types_in_node): Likewise.
>        * omp-low.c (remove_exit_barrier): Likewise.
>        (expand_omp_taskreg): Likewise.
>        (list2chain): Rename to...
>        (vec2chain): ...this.  Adjust.
>        * cgraphunit.c (assemble_thunk): Call add_local_decl.
>        * tree-cfg.c (replace_by_duplicate_decl): Likewise.
>        * gimple-low.c (record_vars_into): Likewise.
>        * tree-inline.c (remap_decls): Likewise.
>        (declare_return_variable): Likewise.
>        (declare_inline_vars): Likewise.
>        (copy_forbidden): Adjust for new type of cfun->local_decls.
>        (add_local_variables): New function.
>        (expand_call_inline): Call it.
>        (tree_function_versioning): Likewise.
>
> gcc/cp/
>        * decl.c (cp_finish_decl): Call add_local_decl.
>        * optimize.c (clone_body): Adjust for new type of cfun->local_decls.

OK if testing passes for all languages and lto enabled.  Please wait
until after the freeze to commit.


Diego.
H.J. Lu - July 10, 2010, 3:38 p.m.
On Wed, Jun 30, 2010 at 7:09 PM, Nathan Froyd <froydnj@codesourcery.com> wrote:
> Enough with those trivial TREE_LIST removal patches that break
> bootstrap.  How about a patch that does some real damage? :)
>
> The patch below turns cfun->local_decls into a VEC.  The order of the
> elements in the list doesn't matter, so it's straightforward to retain
> the same time bounds for deleting elements in the few places that we do
> that.  add_local_decl helps localize the allocation type of the
> local_decls VEC (and came in handy when I realized that the vector
> couldn't be heap allocated); FOR_EACH_VEC_ELT_REVERSE and
> FOR_EACH_LOCAL_DECL help to not make dumb mistakes while converting
> everything over.  Both of these additions also make things clearer.
>
> Bootstrapped/tested on x86_64-unknown-linux-gnu, all languages except
> for lto (libjava/libstdc++ rebuild and test still ongoing, but expected
> to succeed).  I understand that I need to install gold to test that and
> will perform that testing prior to installing the patch.  OK to commit?
>
> -Nathan
>
> gcc/
>        * vec.h (FOR_EACH_VEC_ELT_REVERSE): New macro.
>        * function.h (struct_function): Change type of local_decls field
>        to a VEC.
>        (add_local_decl): New function.
>        (FOR_EACH_LOCAL_DECL): New macro.
>        * cfgexpand.c (init_vars_expansion): Adjust for new type of
>        cfun->local_decls.
>        (estimated_stack_frame_size): Likewise.
>        (expand_used_vars): Likewise.
>        * cgraphbuild.c (build_cgraph_edges): Likewise.
>        * function.c (instantiate_decls_1): Likewise.
>        * ipa-struct-reorg.c (build_data_structure): Likewise.
>        * ipa-type-escape.c (analyze_function): Likewise.
>        * lto-streamer-in.c (input_function): Likewise.
>        * lto-streamer-out.c (output_function): Likewise.
>        * tree-ssa-live.c (remove_unused_locals): Likewise.
>        * tree.c (free_lang_data_in_decl): Likewise.
>        (find_decls_types_in_node): Likewise.
>        * omp-low.c (remove_exit_barrier): Likewise.
>        (expand_omp_taskreg): Likewise.
>        (list2chain): Rename to...
>        (vec2chain): ...this.  Adjust.
>        * cgraphunit.c (assemble_thunk): Call add_local_decl.
>        * tree-cfg.c (replace_by_duplicate_decl): Likewise.
>        * gimple-low.c (record_vars_into): Likewise.
>        * tree-inline.c (remap_decls): Likewise.
>        (declare_return_variable): Likewise.
>        (declare_inline_vars): Likewise.
>        (copy_forbidden): Adjust for new type of cfun->local_decls.
>        (add_local_variables): New function.
>        (expand_call_inline): Call it.
>        (tree_function_versioning): Likewise.
>
> gcc/cp/
>        * decl.c (cp_finish_decl): Call add_local_decl.
>        * optimize.c (clone_body): Adjust for new type of cfun->local_decls.
>

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44901

Patch

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 0d8026e..966e1d7 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -1226,9 +1226,10 @@  static void
 init_vars_expansion (void)
 {
   tree t;
+  unsigned ix;
   /* Set TREE_USED on all variables in the local_decls.  */
-  for (t = cfun->local_decls; t; t = TREE_CHAIN (t))
-    TREE_USED (TREE_VALUE (t)) = 1;
+  FOR_EACH_LOCAL_DECL (cfun, ix, t)
+    TREE_USED (t) = 1;
 
   /* Clear TREE_USED on all variables associated with a block scope.  */
   clear_tree_used (DECL_INITIAL (current_function_decl));
@@ -1264,14 +1265,13 @@  estimated_stack_frame_size (void)
 {
   HOST_WIDE_INT size = 0;
   size_t i;
-  tree t, outer_block = DECL_INITIAL (current_function_decl);
+  tree var, outer_block = DECL_INITIAL (current_function_decl);
+  unsigned ix;
 
   init_vars_expansion ();
 
-  for (t = cfun->local_decls; t; t = TREE_CHAIN (t))
+  FOR_EACH_LOCAL_DECL (cfun, ix, var)
     {
-      tree var = TREE_VALUE (t);
-
       if (TREE_USED (var))
         size += expand_one_var (var, true, false);
       TREE_USED (var) = 1;
@@ -1296,9 +1296,10 @@  estimated_stack_frame_size (void)
 static void
 expand_used_vars (void)
 {
-  tree t, next, outer_block = DECL_INITIAL (current_function_decl);
-  tree maybe_local_decls = NULL_TREE;
+  tree var, outer_block = DECL_INITIAL (current_function_decl);
+  VEC(tree,heap) *maybe_local_decls = NULL;
   unsigned i;
+  unsigned len;
 
   /* Compute the phase of the stack frame for this function.  */
   {
@@ -1333,15 +1334,12 @@  expand_used_vars (void)
 
   /* At this point all variables on the local_decls with TREE_USED
      set are not associated with any block scope.  Lay them out.  */
-  t = cfun->local_decls;
-  cfun->local_decls = NULL_TREE;
-  for (; t; t = next)
+
+  len = VEC_length (tree, cfun->local_decls);
+  FOR_EACH_LOCAL_DECL (cfun, i, var)
     {
-      tree var = TREE_VALUE (t);
       bool expand_now = false;
 
-      next = TREE_CHAIN (t);
-
       /* Expanded above already.  */
       if (is_gimple_reg (var))
 	{
@@ -1377,25 +1375,35 @@  expand_used_vars (void)
 	  /* Keep artificial non-ignored vars in cfun->local_decls
 	     chain until instantiate_decls.  */
 	  if (rtl && (MEM_P (rtl) || GET_CODE (rtl) == CONCAT))
-	    {
-	      TREE_CHAIN (t) = cfun->local_decls;
-	      cfun->local_decls = t;
-	      continue;
-	    }
+	    add_local_decl (cfun, var);
 	  else if (rtl == NULL_RTX)
-	    {
-	      /* If rtl isn't set yet, which can happen e.g. with
-		 -fstack-protector, retry before returning from this
-		 function.  */
-	      TREE_CHAIN (t) = maybe_local_decls;
-	      maybe_local_decls = t;
-	      continue;
-	    }
+	    /* If rtl isn't set yet, which can happen e.g. with
+	       -fstack-protector, retry before returning from this
+	       function.  */
+	    VEC_safe_push (tree, heap, maybe_local_decls, var);
 	}
-
-      ggc_free (t);
     }
 
+  /* We duplicated some of the decls in CFUN->LOCAL_DECLS.
+
+     +-----------------+-----------------+
+     | ...processed... | ...duplicates...|
+     +-----------------+-----------------+
+                       ^
+		       +-- LEN points here.
+
+     We just want the duplicates, as those are the artificial
+     non-ignored vars that we want to keep until instantiate_decls.
+     Move them down and truncate the array.  */
+  {
+    tree *base = VEC_address (tree, cfun->local_decls);
+    tree *dup_base = base + len;
+    unsigned n = VEC_length (tree, cfun->local_decls) - len;
+
+    memmove (base, dup_base, n*sizeof(*base));
+    VEC_truncate (tree, cfun->local_decls, n);
+  }
+
   /* At this point, all variables within the block tree with TREE_USED
      set are actually used by the optimized function.  Lay them out.  */
   expand_used_vars_for_block (outer_block, true);
@@ -1452,24 +1460,16 @@  expand_used_vars (void)
   /* If there were any artificial non-ignored vars without rtl
      found earlier, see if deferred stack allocation hasn't assigned
      rtl to them.  */
-  for (t = maybe_local_decls; t; t = next)
+  FOR_EACH_VEC_ELT_REVERSE (tree, maybe_local_decls, i, var)
     {
-      tree var = TREE_VALUE (t);
       rtx rtl = DECL_RTL_IF_SET (var);
 
-      next = TREE_CHAIN (t);
-
       /* Keep artificial non-ignored vars in cfun->local_decls
 	 chain until instantiate_decls.  */
       if (rtl && (MEM_P (rtl) || GET_CODE (rtl) == CONCAT))
-	{
-	  TREE_CHAIN (t) = cfun->local_decls;
-	  cfun->local_decls = t;
-	  continue;
-	}
-
-      ggc_free (t);
+	add_local_decl (cfun, var);
     }
+  VEC_free (tree, heap, maybe_local_decls);
 
   /* If the target requires that FRAME_OFFSET be aligned, do it.  */
   if (STACK_ALIGNMENT_NEEDED)
diff --git a/gcc/cgraphbuild.c b/gcc/cgraphbuild.c
index c63b5af..02a496b 100644
--- a/gcc/cgraphbuild.c
+++ b/gcc/cgraphbuild.c
@@ -328,7 +328,8 @@  build_cgraph_edges (void)
   struct cgraph_node *node = cgraph_node (current_function_decl);
   struct pointer_set_t *visited_nodes = pointer_set_create ();
   gimple_stmt_iterator gsi;
-  tree step;
+  tree decl;
+  unsigned ix;
 
   /* Create the callgraph edges and record the nodes referenced by the function.
      body.  */
@@ -378,15 +379,10 @@  build_cgraph_edges (void)
    }
 
   /* Look for initializers of constant variables and private statics.  */
-  for (step = cfun->local_decls;
-       step;
-       step = TREE_CHAIN (step))
-    {
-      tree decl = TREE_VALUE (step);
-      if (TREE_CODE (decl) == VAR_DECL
-	  && (TREE_STATIC (decl) && !DECL_EXTERNAL (decl)))
-	varpool_finalize_decl (decl);
-    }
+  FOR_EACH_LOCAL_DECL (cfun, ix, decl)
+    if (TREE_CODE (decl) == VAR_DECL
+	&& (TREE_STATIC (decl) && !DECL_EXTERNAL (decl)))
+      varpool_finalize_decl (decl);
   record_eh_tables (node, cfun);
 
   pointer_set_destroy (visited_nodes);
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 27faead..1d3006e 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1523,7 +1523,7 @@  assemble_thunk (struct cgraph_node *node)
 	  if (!is_gimple_reg_type (restype))
 	    {
 	      restmp = resdecl;
-	      cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls);
+	      add_local_decl (cfun, restmp);
 	      BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
 	    }
 	  else
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 1d11fb8..541f77e 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5838,8 +5838,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	       but [cd]tors are never actually compiled directly.  We need
 	       to put statics on the list so we can deal with the label
 	       address extension.  */
-	    cfun->local_decls = tree_cons (NULL_TREE, decl,
-					   cfun->local_decls);
+	    add_local_decl (cfun, decl);
 	}
 
       /* Convert the initializer to the type of DECL, if we have not
diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c
index 0196eae..7ec2034 100644
--- a/gcc/cp/optimize.c
+++ b/gcc/cp/optimize.c
@@ -107,12 +107,11 @@  clone_body (tree clone, tree fn, void *arg_map)
   if (DECL_NAME (clone) == base_dtor_identifier
       || DECL_NAME (clone) == base_ctor_identifier)
     {
-      tree decls = DECL_STRUCT_FUNCTION (fn)->local_decls;
-      for (; decls; decls = TREE_CHAIN (decls))
-	{
-	  tree decl = TREE_VALUE (decls);
-	  walk_tree (&DECL_INITIAL (decl), copy_tree_body_r, &id, NULL);
-	}
+      unsigned ix;
+      tree decl;
+
+      FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (fn), ix, decl)
+        walk_tree (&DECL_INITIAL (decl), copy_tree_body_r, &id, NULL);
     }
 
   append_to_statement_list_force (stmts, &DECL_SAVED_TREE (clone));
diff --git a/gcc/function.c b/gcc/function.c
index 3a7bb25..9075e08 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -1815,7 +1815,8 @@  instantiate_decls_1 (tree let)
 static void
 instantiate_decls (tree fndecl)
 {
-  tree decl, t, next;
+  tree decl;
+  unsigned ix;
 
   /* Process all parameters of the function.  */
   for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
@@ -1832,16 +1833,10 @@  instantiate_decls (tree fndecl)
   /* Now process all variables defined in the function or its subblocks.  */
   instantiate_decls_1 (DECL_INITIAL (fndecl));
 
-  t = cfun->local_decls;
-  cfun->local_decls = NULL_TREE;
-  for (; t; t = next)
-    {
-      next = TREE_CHAIN (t);
-      decl = TREE_VALUE (t);
-      if (DECL_RTL_SET_P (decl))
-	instantiate_decl_rtl (DECL_RTL (decl));
-      ggc_free (t);
-    }
+  FOR_EACH_LOCAL_DECL (cfun, ix, decl)
+    if (DECL_RTL_SET_P (decl))
+      instantiate_decl_rtl (DECL_RTL (decl));
+  VEC_free (tree, gc, cfun->local_decls);
 }
 
 /* Pass through the INSNS of function FNDECL and convert virtual register
diff --git a/gcc/function.h b/gcc/function.h
index 6170348..fec0168 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -503,8 +503,8 @@  struct GTY(()) function {
      pointer.  */
   tree nonlocal_goto_save_area;
 
-  /* List of function local variables, functions, types and constants.  */
-  tree local_decls;
+  /* Vector of function local variables, functions, types and constants.  */
+  VEC(tree,gc) *local_decls;
 
   /* For md files.  */
 
@@ -609,6 +609,17 @@  struct GTY(()) function {
   unsigned int is_thunk : 1;
 };
 
+/* Add the decl D to the local_decls list of FUN.  */
+
+static inline void
+add_local_decl (struct function *fun, tree d)
+{
+  VEC_safe_push (tree, gc, fun->local_decls, d);
+}
+
+#define FOR_EACH_LOCAL_DECL(FUN, I, D)		\
+  FOR_EACH_VEC_ELT_REVERSE (tree, (FUN)->local_decls, I, D)
+
 /* If va_list_[gf]pr_size is set to this, it means we don't know how
    many units need to be saved.  */
 #define VA_LIST_MAX_GPR_SIZE	255
diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c
index 0823b89..381068a 100644
--- a/gcc/gimple-low.c
+++ b/gcc/gimple-low.c
@@ -907,8 +907,7 @@  record_vars_into (tree vars, tree fn)
 	continue;
 
       /* Record the variable.  */
-      cfun->local_decls = tree_cons (NULL_TREE, var,
-					     cfun->local_decls);
+      add_local_decl (cfun, var);
     }
 
   if (fn != current_function_decl)
diff --git a/gcc/ipa-struct-reorg.c b/gcc/ipa-struct-reorg.c
index 6ace2fc..3558533 100644
--- a/gcc/ipa-struct-reorg.c
+++ b/gcc/ipa-struct-reorg.c
@@ -3416,7 +3416,6 @@  static void
 build_data_structure (VEC (tree, heap) **unsuitable_types)
 {
   tree var, type;
-  tree var_list;
   struct varpool_node *current_varpool;
   struct cgraph_node *c_node;
 
@@ -3439,6 +3438,7 @@  build_data_structure (VEC (tree, heap) **unsuitable_types)
       if (avail == AVAIL_LOCAL || avail == AVAIL_AVAILABLE)
 	{
 	  struct function *fn = DECL_STRUCT_FUNCTION (c_node->decl);
+	  unsigned ix;
 
 	  for (var = DECL_ARGUMENTS (c_node->decl); var;
 	       var = TREE_CHAIN (var))
@@ -3454,14 +3454,9 @@  build_data_structure (VEC (tree, heap) **unsuitable_types)
 	    }
 
 	  /* Check function local variables.  */
-	  for (var_list = fn->local_decls; var_list;
-	       var_list = TREE_CHAIN (var_list))
-	    {
-	      var = TREE_VALUE (var_list);
-
-	      if (is_candidate (var, &type, unsuitable_types))
-		add_structure (type);
-	    }
+	  FOR_EACH_LOCAL_DECL (fn, ix, var)
+	    if (is_candidate (var, &type, unsuitable_types))
+	      add_structure (type);
 	}
     }
 }
diff --git a/gcc/ipa-type-escape.c b/gcc/ipa-type-escape.c
index b6744a6..3b395f9 100644
--- a/gcc/ipa-type-escape.c
+++ b/gcc/ipa-type-escape.c
@@ -1676,12 +1676,11 @@  analyze_function (struct cgraph_node *fn)
   /* There may be const decls with interesting right hand sides.  */
   if (DECL_STRUCT_FUNCTION (decl))
     {
-      tree step;
-      for (step = DECL_STRUCT_FUNCTION (decl)->local_decls;
-	   step;
-	   step = TREE_CHAIN (step))
+      tree var;
+      unsigned ix;
+
+      FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (decl), ix, var)
 	{
-	  tree var = TREE_VALUE (step);
 	  if (TREE_CODE (var) == VAR_DECL
 	      && DECL_INITIAL (var)
 	      && !TREE_STATIC (var))
diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c
index 635cff4..47f40e2 100644
--- a/gcc/lto-streamer-in.c
+++ b/gcc/lto-streamer-in.c
@@ -1286,6 +1286,7 @@  input_function (tree fn_decl, struct data_in *data_in,
   struct bitpack_d bp;
   struct cgraph_node *node;
   tree args, narg, oarg;
+  int len;
 
   fn = DECL_STRUCT_FUNCTION (fn_decl);
   tag = input_record_start (ib);
@@ -1320,7 +1321,17 @@  input_function (tree fn_decl, struct data_in *data_in,
   fn->nonlocal_goto_save_area = lto_input_tree (ib, data_in);
 
   /* Read all the local symbols.  */
-  fn->local_decls = lto_input_tree (ib, data_in);
+  len = lto_input_sleb128 (ib);
+  if (len > 0)
+    {
+      int i;
+      VEC_safe_grow (tree, gc, fn->local_decls, len);
+      for (i = 0; i < len; i++)
+	{
+	  tree t = lto_input_tree (ib, data_in);
+	  VEC_replace (tree, fn->local_decls, i, t);
+	}
+    }
 
   /* Read all function arguments.  We need to re-map them here to the
      arguments of the merged function declaration.  */
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 6ee2eda..18d4b19 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -1840,6 +1840,8 @@  output_function (struct cgraph_node *node)
   struct function *fn;
   basic_block bb;
   struct output_block *ob;
+  unsigned i;
+  tree t;
 
   function = node->decl;
   fn = DECL_STRUCT_FUNCTION (function);
@@ -1886,7 +1888,9 @@  output_function (struct cgraph_node *node)
   lto_output_tree_ref (ob, fn->nonlocal_goto_save_area);
 
   /* Output all the local variables in the function.  */
-  lto_output_tree_ref (ob, fn->local_decls);
+  output_sleb128 (ob, VEC_length (tree, fn->local_decls));
+  for (i = 0; VEC_iterate (tree, fn->local_decls, i, t); i++)
+    lto_output_tree_ref (ob, t);
 
   /* Output the head of the arguments list.  */
   lto_output_tree_ref (ob, DECL_ARGUMENTS (function));
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 53309ad..293be2f 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -3127,20 +3127,18 @@  maybe_catch_exception (gimple_seq body)
 /* Chain all the DECLs in LIST by their TREE_CHAIN fields.  */
 
 static tree
-list2chain (tree list)
+vec2chain (VEC(tree,gc) *v)
 {
-  tree t;
+  tree chain = NULL_TREE, t;
+  unsigned ix;
 
-  for (t = list; t; t = TREE_CHAIN (t))
+  FOR_EACH_VEC_ELT_REVERSE (tree, v, ix, t)
     {
-      tree var = TREE_VALUE (t);
-      if (TREE_CHAIN (t))
-	TREE_CHAIN (var) = TREE_VALUE (TREE_CHAIN (t));
-      else
-	TREE_CHAIN (var) = NULL_TREE;
+      TREE_CHAIN (t) = chain;
+      chain = t;
     }
 
-  return list ? TREE_VALUE (list) : NULL_TREE;
+  return chain;
 }
 
 
@@ -3199,12 +3197,12 @@  remove_exit_barrier (struct omp_region *region)
 	    {
 	      gimple parallel_stmt = last_stmt (region->entry);
 	      tree child_fun = gimple_omp_parallel_child_fn (parallel_stmt);
-	      tree local_decls = DECL_STRUCT_FUNCTION (child_fun)->local_decls;
-	      tree block;
+	      tree local_decls, block, decl;
+	      unsigned ix;
 
 	      any_addressable_vars = 0;
-	      for (; local_decls; local_decls = TREE_CHAIN (local_decls))
-		if (TREE_ADDRESSABLE (TREE_VALUE (local_decls)))
+	      FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (child_fun), ix, decl)
+		if (TREE_ADDRESSABLE (decl))
 		  {
 		    any_addressable_vars = 1;
 		    break;
@@ -3323,7 +3321,7 @@  expand_omp_taskreg (struct omp_region *region)
 {
   basic_block entry_bb, exit_bb, new_bb;
   struct function *child_cfun;
-  tree child_fn, block, t, ws_args, *tp;
+  tree child_fn, block, t, ws_args;
   tree save_current;
   gimple_stmt_iterator gsi;
   gimple entry_stmt, stmt;
@@ -3369,6 +3367,8 @@  expand_omp_taskreg (struct omp_region *region)
     }
   else
     {
+      unsigned ix;
+
       /* If the parallel region needs data sent from the parent
 	 function, then the very first statement (except possible
 	 tree profile counter updates) of the parallel body
@@ -3446,7 +3446,7 @@  expand_omp_taskreg (struct omp_region *region)
 
       /* Declare local variables needed in CHILD_CFUN.  */
       block = DECL_INITIAL (child_fn);
-      BLOCK_VARS (block) = list2chain (child_cfun->local_decls);
+      BLOCK_VARS (block) = vec2chain (child_cfun->local_decls);
       /* The gimplifier could record temporaries in parallel/task block
 	 rather than in containing function's local_decls chain,
 	 which would mean cgraph missed finalizing them.  Do it now.  */
@@ -3504,11 +3504,11 @@  expand_omp_taskreg (struct omp_region *region)
 	single_succ_edge (new_bb)->flags = EDGE_FALLTHRU;
 
       /* Remove non-local VAR_DECLs from child_cfun->local_decls list.  */
-      for (tp = &child_cfun->local_decls; *tp; )
-	if (DECL_CONTEXT (TREE_VALUE (*tp)) != cfun->decl)
-	  tp = &TREE_CHAIN (*tp);
+      for (ix = 0; VEC_iterate (tree, child_cfun->local_decls, ix, t); )
+	if (DECL_CONTEXT (t) != cfun->decl)
+	  ix++;
 	else
-	  *tp = TREE_CHAIN (*tp);
+	  VEC_unordered_remove (tree, child_cfun->local_decls, ix);
 
       /* Inform the callgraph about the new function.  */
       DECL_STRUCT_FUNCTION (child_fn)->curr_properties
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index f28db37..64a7460 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -5538,7 +5538,7 @@  replace_by_duplicate_decl (tree *tp, struct pointer_map_t *vars_map,
       if (SSA_VAR_P (t))
 	{
 	  new_t = copy_var_decl (t, DECL_NAME (t), TREE_TYPE (t));
-	  f->local_decls = tree_cons (NULL_TREE, new_t, f->local_decls);
+	  add_local_decl (f, new_t);
 	}
       else
 	{
@@ -6258,7 +6258,7 @@  move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
 void
 dump_function_to_file (tree fn, FILE *file, int flags)
 {
-  tree arg, vars, var;
+  tree arg, var;
   struct function *dsf;
   bool ignore_topmost_bind = false, any_var = false;
   basic_block bb;
@@ -6298,15 +6298,14 @@  dump_function_to_file (tree fn, FILE *file, int flags)
 
   /* When GIMPLE is lowered, the variables are no longer available in
      BIND_EXPRs, so display them separately.  */
-  if (cfun && cfun->decl == fn && cfun->local_decls)
+  if (cfun && cfun->decl == fn && !VEC_empty (tree, cfun->local_decls))
     {
+      unsigned ix;
       ignore_topmost_bind = true;
 
       fprintf (file, "{\n");
-      for (vars = cfun->local_decls; vars; vars = TREE_CHAIN (vars))
+      FOR_EACH_LOCAL_DECL (cfun, ix, var)
 	{
-	  var = TREE_VALUE (vars);
-
 	  print_generic_decl (file, var, flags);
 	  if (flags & TDF_VERBOSE)
 	    print_node (file, "", var, 4);
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index f1470d7..bb82118 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -545,8 +545,7 @@  remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id)
 	  if (TREE_CODE (old_var) == VAR_DECL
 	      && ! DECL_EXTERNAL (old_var)
 	      && (var_ann (old_var) || !gimple_in_ssa_p (cfun)))
-	    cfun->local_decls = tree_cons (NULL_TREE, old_var,
-						   cfun->local_decls);
+	    add_local_decl (cfun, old_var);
 	  if ((!optimize || debug_info_level > DINFO_LEVEL_TERSE)
 	      && !DECL_IGNORED_P (old_var)
 	      && nonlocalized_list)
@@ -2839,9 +2838,7 @@  declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest)
     }
 
   DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
-  DECL_STRUCT_FUNCTION (caller)->local_decls
-    = tree_cons (NULL_TREE, var,
-		 DECL_STRUCT_FUNCTION (caller)->local_decls);
+  add_local_decl (DECL_STRUCT_FUNCTION (caller), var);
 
   /* Do not have the rest of GCC warn about this variable as it should
      not be visible to the user.  */
@@ -2900,7 +2897,8 @@  static const char *
 copy_forbidden (struct function *fun, tree fndecl)
 {
   const char *reason = fun->cannot_be_copied_reason;
-  tree step;
+  tree decl;
+  unsigned ix;
 
   /* Only examine the function once.  */
   if (fun->cannot_be_copied_set)
@@ -2919,23 +2917,19 @@  copy_forbidden (struct function *fun, tree fndecl)
       goto fail;
     }
 
-  for (step = fun->local_decls; step; step = TREE_CHAIN (step))
-    {
-      tree decl = TREE_VALUE (step);
-
-      if (TREE_CODE (decl) == VAR_DECL
-	  && TREE_STATIC (decl)
-	  && !DECL_EXTERNAL (decl)
-	  && DECL_INITIAL (decl)
-	  && walk_tree_without_duplicates (&DECL_INITIAL (decl),
-					   has_label_address_in_static_1,
-					   fndecl))
-	{
-	  reason = G_("function %q+F can never be copied because it saves "
-		      "address of local label in a static variable");
-	  goto fail;
-	}
-    }
+  FOR_EACH_LOCAL_DECL (fun, ix, decl)
+    if (TREE_CODE (decl) == VAR_DECL
+	&& TREE_STATIC (decl)
+	&& !DECL_EXTERNAL (decl)
+	&& DECL_INITIAL (decl)
+	&& walk_tree_without_duplicates (&DECL_INITIAL (decl),
+					 has_label_address_in_static_1,
+					 fndecl))
+      {
+	reason = G_("function %q+F can never be copied because it saves "
+		    "address of local label in a static variable");
+	goto fail;
+      }
 
  fail:
   fun->cannot_be_copied_reason = reason;
@@ -3722,6 +3716,26 @@  prepend_lexical_block (tree current_block, tree new_block)
   BLOCK_SUPERCONTEXT (new_block) = current_block;
 }
 
+/* Add local variables from CALLEE to CALLER.  */
+
+static inline void
+add_local_variables (struct function *callee, struct function *caller,
+		     copy_body_data *id, bool check_var_ann)
+{
+  tree var;
+  unsigned ix;
+
+  FOR_EACH_LOCAL_DECL (callee, ix, var)
+    if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
+      {
+	if (!check_var_ann
+	    || (var_ann (var) && add_referenced_var (var)))
+	  add_local_decl (caller, var);
+      }
+    else if (!can_be_nonlocal (var, id))
+      add_local_decl (caller, remap_decl (var, id));
+}
+
 /* Fetch callee declaration from the call graph edge going from NODE and
    associated with STMR call statement.  Return NULL_TREE if not found.  */
 static tree
@@ -3754,8 +3768,6 @@  expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
   gimple_stmt_iterator gsi, stmt_gsi;
   bool successfully_inlined = FALSE;
   bool purge_dead_abnormal_edges;
-  tree t_step;
-  tree var;
 
   /* Set input_location here so we get the right instantiation context
      if we call instantiate_decl from inlinable_function_p.  */
@@ -3959,20 +3971,7 @@  expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
   use_retvar = declare_return_variable (id, return_slot, modify_dest);
 
   /* Add local vars in this inlined callee to caller.  */
-  t_step = id->src_cfun->local_decls;
-  for (; t_step; t_step = TREE_CHAIN (t_step))
-    {
-      var = TREE_VALUE (t_step);
-      if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
-	{
-	  if (var_ann (var) && add_referenced_var (var))
-	    cfun->local_decls = tree_cons (NULL_TREE, var,
-					   cfun->local_decls);
-	}
-      else if (!can_be_nonlocal (var, id))
-	cfun->local_decls = tree_cons (NULL_TREE, remap_decl (var, id),
-				       cfun->local_decls);
-    }
+  add_local_variables (id->src_cfun, cfun, id, true);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
@@ -4714,7 +4713,7 @@  declare_inline_vars (tree block, tree vars)
     {
       DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
       gcc_assert (!TREE_STATIC (t) && !TREE_ASM_WRITTEN (t));
-      cfun->local_decls = tree_cons (NULL_TREE, t, cfun->local_decls);
+      add_local_decl (cfun, t);
     }
 
   if (block)
@@ -5045,7 +5044,6 @@  tree_function_versioning (tree old_decl, tree new_decl,
   basic_block old_entry_block, bb;
   VEC (gimple, heap) *init_stmts = VEC_alloc (gimple, heap, 10);
 
-  tree t_step;
   tree old_current_function_decl = current_function_decl;
   tree vars = NULL_TREE;
 
@@ -5170,19 +5168,9 @@  tree_function_versioning (tree old_decl, tree new_decl,
 
   declare_inline_vars (DECL_INITIAL (new_decl), vars);
 
-  if (DECL_STRUCT_FUNCTION (old_decl)->local_decls != NULL_TREE)
+  if (!VEC_empty (tree, DECL_STRUCT_FUNCTION (old_decl)->local_decls))
     /* Add local vars.  */
-    for (t_step = DECL_STRUCT_FUNCTION (old_decl)->local_decls;
-	 t_step; t_step = TREE_CHAIN (t_step))
-      {
-	tree var = TREE_VALUE (t_step);
-	if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
-	  cfun->local_decls = tree_cons (NULL_TREE, var, cfun->local_decls);
-	else if (!can_be_nonlocal (var, &id))
-	  cfun->local_decls =
-	    tree_cons (NULL_TREE, remap_decl (var, &id),
-		       cfun->local_decls);
-      }
+    add_local_variables (DECL_STRUCT_FUNCTION (old_decl), cfun, &id, false);
 
   /* Copy the Function's body.  */
   copy_body (&id, old_entry_block->count, REG_BR_PROB_BASE,
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index ca1b985..045d42c 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -663,10 +663,11 @@  void
 remove_unused_locals (void)
 {
   basic_block bb;
-  tree t, *cell;
+  tree var, t;
   referenced_var_iterator rvi;
   var_ann_t ann;
   bitmap global_unused_vars = NULL;
+  unsigned ix;
 
   /* Removing declarations from lexical blocks when not optimizing is
      not only a waste of time, it actually causes differences in stack
@@ -733,10 +734,8 @@  remove_unused_locals (void)
   cfun->has_local_explicit_reg_vars = false;
 
   /* Remove unmarked local vars from local_decls.  */
-  for (cell = &cfun->local_decls; *cell; )
+  for (ix = 0; VEC_iterate (tree, cfun->local_decls, ix, var); )
     {
-      tree var = TREE_VALUE (*cell);
-
       if (TREE_CODE (var) != FUNCTION_DECL
 	  && (!(ann = var_ann (var))
 	      || !ann->used))
@@ -749,7 +748,7 @@  remove_unused_locals (void)
 	    }
 	  else
 	    {
-	      *cell = TREE_CHAIN (*cell);
+	      VEC_unordered_remove (tree, cfun->local_decls, ix);
 	      continue;
 	    }
 	}
@@ -757,34 +756,29 @@  remove_unused_locals (void)
 	       && DECL_HARD_REGISTER (var)
 	       && !is_global_var (var))
 	cfun->has_local_explicit_reg_vars = true;
-      cell = &TREE_CHAIN (*cell);
+
+      ix++;
     }
 
   /* Remove unmarked global vars from local_decls.  */
   if (global_unused_vars != NULL)
     {
-      for (t = cfun->local_decls; t; t = TREE_CHAIN (t))
-	{
-	  tree var = TREE_VALUE (t);
-
-	  if (TREE_CODE (var) == VAR_DECL
-	      && is_global_var (var)
-	      && (ann = var_ann (var)) != NULL
-	      && ann->used)
-	    mark_all_vars_used (&DECL_INITIAL (var), global_unused_vars);
-	}
-
-      for (cell = &cfun->local_decls; *cell; )
-	{
-	  tree var = TREE_VALUE (*cell);
-
-	  if (TREE_CODE (var) == VAR_DECL
-	      && is_global_var (var)
-	      && bitmap_bit_p (global_unused_vars, DECL_UID (var)))
-	    *cell = TREE_CHAIN (*cell);
-	  else
-	    cell = &TREE_CHAIN (*cell);
-	}
+      tree var;
+      unsigned ix;
+      FOR_EACH_LOCAL_DECL (cfun, ix, var)
+	if (TREE_CODE (var) == VAR_DECL
+	    && is_global_var (var)
+	    && (ann = var_ann (var)) != NULL
+	    && ann->used)
+	  mark_all_vars_used (&DECL_INITIAL (var), global_unused_vars);
+
+      for (ix = 0; VEC_iterate (tree, cfun->local_decls, ix, var); )
+	if (TREE_CODE (var) == VAR_DECL
+	    && is_global_var (var)
+	    && bitmap_bit_p (global_unused_vars, DECL_UID (var)))
+	  VEC_unordered_remove (tree, cfun->local_decls, ix);
+	else
+	  ix++;
       BITMAP_FREE (global_unused_vars);
     }
 
diff --git a/gcc/tree.c b/gcc/tree.c
index de83e64..07c0662 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -4449,6 +4449,7 @@  free_lang_data_in_decl (tree decl)
       if (gimple_has_body_p (decl))
 	{
 	  tree t;
+	  unsigned ix;
 	  struct pointer_set_t *locals;
 
 	  /* If DECL has a gimple body, then the context for its
@@ -4465,14 +4466,13 @@  free_lang_data_in_decl (tree decl)
 
 	  /* Collect all the symbols declared in DECL.  */
 	  locals = pointer_set_create ();
-	  t = DECL_STRUCT_FUNCTION (decl)->local_decls;
-	  for (; t; t = TREE_CHAIN (t))
+	  FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (decl), ix, t)
 	    {
-	      pointer_set_insert (locals, TREE_VALUE (t));
+	      pointer_set_insert (locals, t);
 
 	      /* All the local symbols should have DECL as their
 		 context.  */
-	      DECL_CONTEXT (TREE_VALUE (t)) = decl;
+	      DECL_CONTEXT (t) = decl;
 	    }
 
 	  /* Get rid of any decl not in local_decls.  */
@@ -4824,6 +4824,7 @@  find_decls_types_in_node (struct cgraph_node *n, struct free_lang_data_d *fld)
 {
   basic_block bb;
   struct function *fn;
+  unsigned ix;
   tree t;
 
   find_decls_types (n->decl, fld);
@@ -4836,8 +4837,8 @@  find_decls_types_in_node (struct cgraph_node *n, struct free_lang_data_d *fld)
   fn = DECL_STRUCT_FUNCTION (n->decl);
 
   /* Traverse locals. */
-  for (t = fn->local_decls; t; t = TREE_CHAIN (t))
-    find_decls_types (TREE_VALUE (t), fld);
+  FOR_EACH_LOCAL_DECL (fn, ix, t)
+    find_decls_types (t, fld);
 
   /* Traverse EH regions in FN.  */
   {
diff --git a/gcc/vec.h b/gcc/vec.h
index 93a432d..f1ce1ea 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -190,6 +190,13 @@  along with GCC; see the file COPYING3.  If not see
 
 #define VEC_iterate(T,V,I,P)	(VEC_OP(T,base,iterate)(VEC_BASE(V),I,&(P)))
 
+/* Convenience macro for reverse iteration.  */
+
+#define FOR_EACH_VEC_ELT_REVERSE(T,V,I,P) \
+  for (I = VEC_length (T, (V)) - 1;           \
+       VEC_iterate (T, (V), (I), (P));	  \
+       (I)--)
+
 /* Allocate new vector.
    VEC(T,A) *VEC_T_A_alloc(int reserve);