[PATCH 3/6] Implement fork-based parallelism engine

Jan Hubicka hubicka@ucw.cz
Thu Aug 27 15:37:26 GMT 2020


> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index c0b45795059..22405098dc5 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -226,6 +226,22 @@ cgraph_node::delete_function_version_by_decl (tree decl)
>    decl_node->remove ();
>  }
>  
> +/* Release function dominator info if present.  */
> +
> +void
> +cgraph_node::maybe_release_dominators (void)
> +{
> +  struct function *fun = DECL_STRUCT_FUNCTION (decl);
> +
> +  if (fun && fun->cfg)
> +    {
> +      if (dom_info_available_p (fun, CDI_DOMINATORS))
> +	free_dominance_info (fun, CDI_DOMINATORS);
> +      if (dom_info_available_p (fun, CDI_POST_DOMINATORS))
> +	free_dominance_info (fun, CDI_POST_DOMINATORS);
> +    }
> +}

I am not sure if that needs to be member function, but if so we want to
merge it with other places in cgraph.c and cgraphunit.c where dominators
are freed.  I do not think you need to check avalability.
> +
>  /* Record that DECL1 and DECL2 are semantically identical function
>     versions.  */
>  void
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index b4a7871bd3d..72ac19f9672 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -463,6 +463,15 @@ public:
>       Return NULL if there's no such node.  */
>    static symtab_node *get_for_asmname (const_tree asmname);
>  
> +  /* Get symtab node by order.  */
> +  static symtab_node *find_by_order (int order);

This is quadratic and moreover seems unused. Why do you add it?
> +
> +  /* Get symtab_node by its name.  */
> +  static symtab_node *find_by_name (const char *);

Similarly here, note that names are not really very meaningful as lookup
things, since they get duplicated.
> +
> +  /* Get symtab_node by its ASM name.  */
> +  static symtab_node *find_by_asm_name (const char *);

For this we have get_for_asmname (which also populates asm name hash as
needed and is not quadratic)
> diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
> index d10d635e942..73e4bed3b61 100644
> --- a/gcc/cgraphunit.c
> +++ b/gcc/cgraphunit.c
> @@ -2258,6 +2258,11 @@ cgraph_node::expand (void)
>  {
>    location_t saved_loc;
>  
> +  /* FIXME: Find out why body-removed nodes are marked for output.  */
> +  if (body_removed)
> +    return;

Indeed, we should know :)
> +
> +
>    /* We ought to not compile any inline clones.  */
>    gcc_assert (!inlined_to);
>  
> @@ -2658,6 +2663,7 @@ ipa_passes (void)
>  
>        execute_ipa_summary_passes
>  	((ipa_opt_pass_d *) passes->all_regular_ipa_passes);
> +
This seems accidental.
>      }
>  
>    /* Some targets need to handle LTO assembler output specially.  */
> @@ -2687,10 +2693,17 @@ ipa_passes (void)
>    if (flag_generate_lto || flag_generate_offload)
>      targetm.asm_out.lto_end ();
>  
> -  if (!flag_ltrans
> +  if (split_outputs)
> +    flag_ltrans = true;
> +
> +  if ((!flag_ltrans || split_outputs)
>        && ((in_lto_p && flag_incremental_link != INCREMENTAL_LINK_LTO)
>  	  || !flag_lto || flag_fat_lto_objects))
>      execute_ipa_pass_list (passes->all_regular_ipa_passes);
> +
> +  if (split_outputs)
> +    flag_ltrans = false;
> +
>    invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL);
>  
>    bitmap_obstack_release (NULL);
> @@ -2742,6 +2755,185 @@ symbol_table::output_weakrefs (void)
>        }
>  }
>  
> +static bool is_number (const char *str)
> +{
> +  while (*str != '\0')
> +    switch (*str++)
> +      {
> +	case '0':
> +	case '1':
> +	case '2':
> +	case '3':
> +	case '4':
> +	case '5':
> +	case '6':
> +	case '7':
> +	case '8':
> +	case '9':
> +	  continue;
> +	default:
> +	  return false;
> +      }
> +
> +  return true;
> +}

This looks odd, we have other places where we parse number from command
line :)
> +
> +/* If forked, which child am I?  */
> +
> +static int childno = -1;
> +
> +static bool
> +maybe_compile_in_parallel (void)
> +{
> +  struct symtab_node *node;
> +  int partitions, i, j;
> +  int *pids;
> +
> +  bool promote_statics = param_promote_statics;
> +  bool balance = param_balance_partitions;
> +  bool jobserver = false;
> +  bool job_auto = false;
> +  int num_jobs = -1;
> +
> +  if (!flag_parallel_jobs || !split_outputs)
> +    return false;
> +
> +  if (!strcmp (flag_parallel_jobs, "auto"))
> +    {
> +      jobserver = jobserver_initialize ();
> +      job_auto = true;
> +    }
> +  else if (!strcmp (flag_parallel_jobs, "jobserver"))
> +    jobserver = jobserver_initialize ();
> +  else if (is_number (flag_parallel_jobs))
> +    num_jobs = atoi (flag_parallel_jobs);
> +  else
> +    gcc_unreachable ();
> +
> +  if (job_auto && !jobserver)
> +    {
> +      num_jobs = sysconf (_SC_NPROCESSORS_CONF);
> +      if (num_jobs > 2)
> +	num_jobs = 2;
> +    }
> +
> +  if (num_jobs < 0 && !jobserver)
> +    {
> +      inform (UNKNOWN_LOCATION,
> +	      "-fparallel-jobs=jobserver, but no GNU Jobserver found");
> +      return false;
> +    }
> +
> +  if (jobserver)
> +    num_jobs = 2;
> +
> +  if (num_jobs == 0)
> +    {
> +      inform (UNKNOWN_LOCATION, "-fparallel-jobs=0 makes no sense");
> +      return false;
> +    }
> +
> +  /* Trick the compiler to think that we are in WPA.  */
> +  flag_wpa = "";
> +  symtab_node::checking_verify_symtab_nodes ();

Messing with WPA/ltrans flags is not good idea.  You already have
split_output for parallel build.  I sort of see why you set ltrans, but
why WPA?
> +
> +  /* Partition the program so that COMDATs get mapped to the same
> +     partition.  If promote_statics is true, it also maps statics
> +     to the same partition.  If balance is true, try to balance the
> +     partitions for compilation performance.  */
> +  lto_merge_comdat_map (balance, promote_statics, num_jobs);
> +
> +  /* AUX pointers are used by partitioning code to bookkeep number of
> +     partitions symbol is in.  This is no longer needed.  */
> +  FOR_EACH_SYMBOL (node)
> +    node->aux = NULL;
> +
> +  /* We decided that partitioning is a bad idea.  In this case, just
> +     proceed with the default compilation method.  */
> +  if (ltrans_partitions.length () <= 1)
> +    {
> +      flag_wpa = NULL;
> +      jobserver_finalize ();
> +      return false;
> +    }
> +
> +  /* Find out statics that need to be promoted
> +     to globals with hidden visibility because they are accessed from
> +     multiple partitions.  */
> +  lto_promote_cross_file_statics (promote_statics);
> +
> +  /* Check if we have variables being referenced across partitions.  */
> +  lto_check_usage_from_other_partitions ();
> +
> +  /* Trick the compiler to think we are not in WPA anymore.  */
> +  flag_wpa = NULL;
> +
> +  partitions = ltrans_partitions.length ();
> +  pids = XALLOCAVEC (pid_t, partitions);
> +
> +  /* There is no point in launching more jobs than we have partitions.  */
> +  if (num_jobs > partitions)
> +    num_jobs = partitions;
> +
> +  /* Trick the compiler to think we are in LTRANS mode.  */
> +  flag_ltrans = true;
> +
> +  init_additional_asm_names_file ();
> +
> +  /* Flush asm file, so we don't get repeated output as we fork.  */
> +  fflush (asm_out_file);
> +
> +  /* Insert a token for child to consume.  */
> +  if (jobserver)
> +    {
> +      num_jobs = partitions;
> +      jobserver_return_token ('p');
> +    }
> +
> +  /* Spawn processes.  Spawn as soon as there is a free slot.  */
> +  for (j = 0, i = -num_jobs; i < partitions; i++, j++)
> +    {
> +      if (i >= 0)
> +	{
> +	  int wstatus, ret;
> +	  ret = waitpid (pids[i], &wstatus, 0);
> +
> +	  if (ret < 0)
> +	    internal_error ("Unable to wait child %d to finish", i);
> +	  else if (WIFEXITED (wstatus))
> +	    {
> +	      if (WEXITSTATUS (wstatus) != 0)
> +		error ("Child %d exited with error", i);
> +	    }
> +	  else if (WIFSIGNALED (wstatus))
> +	    error ("Child %d aborted with error", i);
> +	}
> +
> +      if (j < partitions)
> +	{
> +	  gcc_assert (ltrans_partitions[j]->symbols > 0);
> +
> +	  if (jobserver)
> +	    jobserver_get_token ();
> +
> +	  pids[j] = fork ();
> +	  if (pids[j] == 0)
> +	    {
> +	      childno = j;
> +	      lto_apply_partition_mask (ltrans_partitions[j]);
> +	      return true;
> +	    }
> +	}
> +    }
> +
> +  /* Get the token which parent inserted for the childs, which they returned by
> +     now.  */
> +  if (jobserver)
> +    jobserver_get_token ();
> +  exit (0);
> +}
> +
> +
>  /* Perform simple optimizations based on callgraph.  */
>  
>  void
> @@ -2768,6 +2960,7 @@ symbol_table::compile (void)
>    {
>      timevar_start (TV_CGRAPH_IPA_PASSES);
>      ipa_passes ();
> +    maybe_compile_in_parallel ();
>      timevar_stop (TV_CGRAPH_IPA_PASSES);
>    }
>    /* Do nothing else if any IPA pass found errors or if we are just streaming LTO.  */
> @@ -2790,6 +2983,9 @@ symbol_table::compile (void)
>    timevar_pop (TV_CGRAPHOPT);
>  
>    /* Output everything.  */
> +  if (split_outputs)
> +    handle_additional_asm (childno);
What this is doin?
> +
>    switch_to_section (text_section);
>    (*debug_hooks->assembly_start) ();
>    if (!quiet_flag)
> diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
> index 2cfab40156e..bc500df4853 100644
> --- a/gcc/ipa-fnsummary.c
> +++ b/gcc/ipa-fnsummary.c
> @@ -4610,7 +4610,7 @@ public:
>        gcc_assert (n == 0);
>        small_p = param;
>      }
> -  virtual bool gate (function *) { return true; }
> +  virtual bool gate (function *) { return !(flag_ltrans && split_outputs); }
>    virtual unsigned int execute (function *)
>      {
>        ipa_free_fn_summary ();
> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
> index 069de9d82fb..6a5657c7507 100644
> --- a/gcc/ipa-icf.c
> +++ b/gcc/ipa-icf.c
> @@ -2345,7 +2345,8 @@ sem_item_optimizer::filter_removed_items (void)
>          {
>  	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
>  
> -	  if (in_lto_p && (cnode->alias || cnode->body_removed))
> +	  if ((in_lto_p || split_outputs)
> +	      && (cnode->alias || cnode->body_removed))


And I wonder why you need these. IPA passes are run before we split,
right?
>  	    remove_item (item);
>  	  else
>  	    filtered.safe_push (item);
> diff --git a/gcc/ipa-visibility.c b/gcc/ipa-visibility.c
> index 7c854f471e8..4d9e11482d3 100644
> --- a/gcc/ipa-visibility.c
> +++ b/gcc/ipa-visibility.c
> @@ -540,7 +540,8 @@ optimize_weakref (symtab_node *node)
>  static void
>  localize_node (bool whole_program, symtab_node *node)
>  {
> -  gcc_assert (whole_program || in_lto_p || !TREE_PUBLIC (node->decl));
> +  gcc_assert (split_outputs || whole_program || in_lto_p
> +	      || !TREE_PUBLIC (node->decl));
>  
>    /* It is possible that one comdat group contains both hidden and non-hidden
>       symbols.  In this case we can privatize all hidden symbol but we need
> diff --git a/gcc/ipa.c b/gcc/ipa.c
> index 288b58cf73d..b397ea2fed8 100644
> --- a/gcc/ipa.c
> +++ b/gcc/ipa.c
> @@ -350,7 +350,7 @@ symbol_table::remove_unreachable_nodes (FILE *file)
>  
>    /* Mark variables that are obviously needed.  */
>    FOR_EACH_DEFINED_VARIABLE (vnode)
> -    if (!vnode->can_remove_if_no_refs_p()
> +    if (!vnode->can_remove_if_no_refs_p ()
>  	&& !vnode->in_other_partition)
>        {
>  	reachable.add (vnode);
> @@ -564,7 +564,7 @@ symbol_table::remove_unreachable_nodes (FILE *file)
>  	}
>        else
>  	gcc_assert (node->clone_of || !node->has_gimple_body_p ()
> -		    || in_lto_p || DECL_RESULT (node->decl));
> +		    || in_lto_p || split_outputs || DECL_RESULT (node->decl));
>      }
>  
>    /* Inline clones might be kept around so their materializing allows further
> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
> index 93a99f3465b..12be8546d9c 100644
> --- a/gcc/lto-cgraph.c
> +++ b/gcc/lto-cgraph.c
> @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "omp-offload.h"
>  #include "stringpool.h"
>  #include "attribs.h"
> +#include "lto-partition.h"
>  
>  /* True when asm nodes has been output.  */
>  bool asm_nodes_output = false;
> @@ -2065,3 +2066,174 @@ input_cgraph_opt_summary (vec<symtab_node *> nodes)
>  	input_cgraph_opt_section (file_data, data, len, nodes);
>      }
>  }
> +
> +/* When analysing function for removal, we have mainly three states, as
> +   defined below.  */
> +
> +enum node_partition_state
> +{
> +  CAN_REMOVE,		/* This node can be removed, or is still to be
> +			   analysed.  */
> +  IN_CURRENT_PARTITION, /* This node is in current partition and should not be
> +			   touched.  */
> +  IN_BOUNDARY,		/* This node is in boundary, therefore being in other
> +			   partition or is a external symbol, and its body can
> +			   be released.  */
> +  IN_BOUNDARY_KEEP_BODY /* This symbol is in other partition but we may need its
> +			   body for inlining, for instance.  */
> +};
> +
> +/* Handle node that are in the LTRANS boundary, releasing its body and
> +   other informations if necessary.  */
> +
> +static void
> +handle_node_in_boundary (symtab_node *node, bool keep_body)
> +{
> +  if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
> +    {
> +      if (cnode->inlined_to && cnode->inlined_to->aux2 != IN_CURRENT_PARTITION)
> +	{
> +	  /* If marked to be inlined into a node not in current partition,
> +	     then undo the inline.  */
> +
> +	  if (cnode->callers) /* This edge could be removed.  */
> +	    cnode->callers->inline_failed = CIF_UNSPECIFIED;
> +	  cnode->inlined_to = NULL;
> +	}
> +
> +      if (cnode->has_gimple_body_p ())
> +	{
> +	  if (!keep_body)
> +	    {
> +	      cnode->maybe_release_dominators ();
> +	      cnode->remove_callees ();
> +	      cnode->remove_all_references ();
> +
> +	      /* FIXME: Releasing body of clones can release bodies of functions
> +		 in current partition.  */
> +
> +	      /* cnode->release_body ();  */
> +	      cnode->body_removed = true;
> +	      cnode->definition = false;
> +	      cnode->analyzed = false;
> +	    }
> +	  cnode->cpp_implicit_alias = false;
> +	  cnode->alias = false;
> +	  cnode->transparent_alias = false;
> +	  cnode->thunk.thunk_p = false;
> +	  cnode->weakref = false;
> +	  /* After early inlining we drop always_inline attributes on
> +	     bodies of functions that are still referenced (have their
> +	     address taken).  */
> +	  DECL_ATTRIBUTES (cnode->decl)
> +	    = remove_attribute ("always_inline",
> +				DECL_ATTRIBUTES (node->decl));
> +
> +	  cnode->in_other_partition = true;
> +	}
> +    }
> +  else if (is_a <varpool_node *> (node) && !DECL_EXTERNAL (node->decl))
> +    {
> +      DECL_EXTERNAL (node->decl) = true;
> +      node->in_other_partition = true;
> +    }
> +}
> +
> +/* Check the boundary and expands it if necessary, including more nodes or
> +   promoting then to a state where their body is required.  */
> +
> +static void
> +compute_boundary (ltrans_partition partition)
> +{
> +  vec<lto_encoder_entry> &nodes = partition->encoder->nodes;
> +  symtab_node *node;
> +  cgraph_node *cnode;
> +  auto_vec<symtab_node *, 16> mark_to_remove;
> +  unsigned int i;
> +
> +  FOR_EACH_SYMBOL (node)
> +    node->aux2 = CAN_REMOVE;

There is boundary computation in lto-cgraph.c so we should merge the
logic...
If you keep the lto-partition datastructures it will compute boundary
for you and you can just remove the rest (I got that working at one
point).

I am also not sure about copy-on-write effect of this. It may be better
to keep things around and just teach late passes to not compile things
in other partitions, but that is definitly for incremental change.
> +void
> +init_additional_asm_names_file (void)
> +{
> +  gcc_assert (split_outputs);
> +
> +  additional_asm_filenames = fopen (split_outputs, "w");
> +  if (!additional_asm_filenames)
> +    error ("Unable to create a temporary write-only file.");
> +
> +  fclose (additional_asm_filenames);
> +}

Aha, that is what it does :)
I wonder if creating the file conditionally (and late) can not lead to
race condition where we will use same tmp file name for other build
executed in parallel by make.
> +
> +/* Reinitialize the assembler file and store it in the additional asm file.  */
> +
> +void
> +handle_additional_asm (int childno)
> +{
> +  gcc_assert (split_outputs);
> +
> +  if (childno < 0)
> +    return;
> +
> +  const char *temp_asm_name = make_temp_file (".s");
> +  asm_file_name = temp_asm_name;
> +
> +  if (asm_out_file == stdout)
> +    fatal_error (UNKNOWN_LOCATION, "Unexpected asm output to stdout");
> +
> +  fclose (asm_out_file);
> +
> +  asm_out_file = fopen (temp_asm_name, "w");
> +  if (!asm_out_file)
> +    fatal_error (UNKNOWN_LOCATION, "Unable to create asm output file");
> +
> +  /* Reopen file as append mode.  Here we assume that write to append file is
> +     atomic, as it is in Linux.  */
> +  additional_asm_filenames = fopen (split_outputs, "a");
> +  if (!additional_asm_filenames)
> +    fatal_error (UNKNOWN_LOCATION,
> +		 "Unable to open the temporary asm files container");
> +
> +  fprintf (additional_asm_filenames, "%d %s\n", childno, asm_file_name);
> +  fclose (additional_asm_filenames);
> +}
> +
>  /* A helper function; used as the reallocator function for cpp's line
>     table.  */
>  static void *
> @@ -2311,7 +2359,7 @@ do_compile ()
>  
>            timevar_stop (TV_PHASE_SETUP);
>  
> -          compile_file ();
> +	  compile_file ();
>          }
>        else
>          {
> @@ -2477,6 +2525,12 @@ toplev::main (int argc, char **argv)
>  
>    finalize_plugins ();
>  
> +  if (jobserver_initialized)
> +    {
> +      jobserver_return_token (JOBSERVER_NULL_TOKEN);
> +      jobserver_finalize ();
> +    }
> +
>    after_memory_report = true;
>  
>    if (seen_error () || werrorcount)
> diff --git a/gcc/toplev.h b/gcc/toplev.h
> index d6c316962b0..3abbf74cd02 100644
> --- a/gcc/toplev.h
> +++ b/gcc/toplev.h
> @@ -103,4 +103,7 @@ extern void parse_alignment_opts (void);
>  
>  extern void initialize_rtl (void);
>  
> +extern void init_additional_asm_names_file (void);
> +extern void handle_additional_asm (int);
> +
>  #endif /* ! GCC_TOPLEV_H */
> diff --git a/gcc/varasm.c b/gcc/varasm.c
> index 4070f9c17e8..84df52013d7 100644
> --- a/gcc/varasm.c
> +++ b/gcc/varasm.c
> @@ -110,7 +110,7 @@ static void decode_addr_const (tree, class addr_const *);
>  static hashval_t const_hash_1 (const tree);
>  static int compare_constant (const tree, const tree);
>  static void output_constant_def_contents (rtx);
> -static void output_addressed_constants (tree);
> +static void output_addressed_constants (tree, int);
>  static unsigned HOST_WIDE_INT output_constant (tree, unsigned HOST_WIDE_INT,
>  					       unsigned int, bool, bool);
>  static void globalize_decl (tree);
> @@ -2272,7 +2272,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
>  
>    /* Output any data that we will need to use the address of.  */
>    if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node)
> -    output_addressed_constants (DECL_INITIAL (decl));
> +    output_addressed_constants (DECL_INITIAL (decl), 0);
>  
>    /* dbxout.c needs to know this.  */
>    if (sect && (sect->common.flags & SECTION_CODE) != 0)
> @@ -3426,11 +3426,11 @@ build_constant_desc (tree exp)
>     already have labels.  */
>  
>  static constant_descriptor_tree *
> -add_constant_to_table (tree exp)
> +add_constant_to_table (tree exp, int defer)
>  {
>    /* The hash table methods may call output_constant_def for addressed
>       constants, so handle them first.  */
> -  output_addressed_constants (exp);
> +  output_addressed_constants (exp, defer);
>  
>    /* Sanity check to catch recursive insertion.  */
>    static bool inserting;
> @@ -3474,7 +3474,7 @@ add_constant_to_table (tree exp)
>  rtx
>  output_constant_def (tree exp, int defer)
>  {
> -  struct constant_descriptor_tree *desc = add_constant_to_table (exp);
> +  struct constant_descriptor_tree *desc = add_constant_to_table (exp, defer);
>    maybe_output_constant_def_contents (desc, defer);
>    return desc->rtl;
>  }
> @@ -3544,7 +3544,7 @@ output_constant_def_contents (rtx symbol)
>  
>    /* Make sure any other constants whose addresses appear in EXP
>       are assigned label numbers.  */
> -  output_addressed_constants (exp);
> +  output_addressed_constants (exp, 0);
>  
>    /* We are no longer deferring this constant.  */
>    TREE_ASM_WRITTEN (decl) = TREE_ASM_WRITTEN (exp) = 1;
> @@ -3608,7 +3608,7 @@ lookup_constant_def (tree exp)
>  tree
>  tree_output_constant_def (tree exp)
>  {
> -  struct constant_descriptor_tree *desc = add_constant_to_table (exp);
> +  struct constant_descriptor_tree *desc = add_constant_to_table (exp, 1);
>    tree decl = SYMBOL_REF_DECL (XEXP (desc->rtl, 0));
>    varpool_node::finalize_decl (decl);
>    return decl;
> @@ -4327,7 +4327,7 @@ compute_reloc_for_constant (tree exp)
>     Indicate whether an ADDR_EXPR has been encountered.  */
>  
>  static void
> -output_addressed_constants (tree exp)
> +output_addressed_constants (tree exp, int defer)
>  {
>    tree tem;
>  
> @@ -4347,21 +4347,21 @@ output_addressed_constants (tree exp)
>  	tem = DECL_INITIAL (tem);
>  
>        if (CONSTANT_CLASS_P (tem) || TREE_CODE (tem) == CONSTRUCTOR)
> -	output_constant_def (tem, 0);
> +	output_constant_def (tem, defer);
>  
>        if (TREE_CODE (tem) == MEM_REF)
> -	output_addressed_constants (TREE_OPERAND (tem, 0));
> +	output_addressed_constants (TREE_OPERAND (tem, 0), defer);
>        break;
>  
>      case PLUS_EXPR:
>      case POINTER_PLUS_EXPR:
>      case MINUS_EXPR:
> -      output_addressed_constants (TREE_OPERAND (exp, 1));
> +      output_addressed_constants (TREE_OPERAND (exp, 1), defer);
>        gcc_fallthrough ();
>  
>      CASE_CONVERT:
>      case VIEW_CONVERT_EXPR:
> -      output_addressed_constants (TREE_OPERAND (exp, 0));
> +      output_addressed_constants (TREE_OPERAND (exp, 0), defer);
>        break;
>  
>      case CONSTRUCTOR:
> @@ -4369,7 +4369,7 @@ output_addressed_constants (tree exp)
>  	unsigned HOST_WIDE_INT idx;
>  	FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
>  	  if (tem != 0)
> -	    output_addressed_constants (tem);
> +	    output_addressed_constants (tem, defer);
>        }
>        break;

Nice job :)
Honza


More information about the Gcc-patches mailing list