Unsurprisingly, this merge was quite painful. Particularly
adapting to all the new EH changes. The main changes I needed to
do:
- The master_clone field is gone from cgraph_node. Some things
had to be handled differently when reading/writing cgraph
nodes.
- There was a bug when emitting/reading EH tables. We were not
handling region aliases (the aka bitmap). EH regions are
shared, so I added a new LTO_eh_shared_region marker to
indicate that on the IL files.
- When resetting the context on function arguments, we only need to
reset it on functions that have a gimple body. With the new FE
changes, the FE is generating different decls for the same
function and depending on the order in which those
FUNCTION_DECLs are processed, we were setting DECL_CONTEXT to
the wrong function (PARM_DECLs are shared, so once set in one
function, it is set in all of them). I fixed this to only
reset DECL_CONTEXT on the FUNCTION_DECL that has a gimple body,
since that's really the only one that matters.
Bootstrapped and tested on x86_64.
Mainline merge @145453.
* configure.ac (acx_pkgversion): update revision merge string.
* configure: regenerate.
* Makefile.in (lto-cgraph.o): Add dependency on lto-utils.h.
* cgraph.c (initialize_inline_failed): Check that E has a call
(cgraph_is_master_clone): Remove.
(cgraph_master_clone): Remove.
* except.c (debug_eh_tree): New.
* except.h (debug_eh_tree): Declare.
* lto-function-in.c (input_bitmap): New.
(input_eh_region): Handle LTO_eh_table_shared_region. Return
a previously created EH region corresponding to the number
read.
(fixup_eh_region_pointers): Remove argument LAST_REGION.
Change type of ROOT_REGION to HOST_WIDE_INT.
Update all users.
Handle shared and NULL regions.
(input_eh_regions): Read size of EH region table before
reading the array.
(input_bb): Assert that CFUN is properly set.
(input_node): Rename WROTE_MASTER_P to CLONE_P.
(input_function): Look for the first clone in NODE's list
of clones.
* lto-cgraph.c (output_node): Rename WROTE_MASTER to
WRITTEN_DECLS.
(output_cgraph_verify_node): Remove.
(output_cgraph): Rename WROTE_MASTER to WRITTEN_DECLS.
* lto-function-out.c (output_bitmap): New.
(output_eh_region): For shared regions, output the marker
LTO_eh_table_shared_region and the region number of the shared
region.
(output_eh_regions): Output the AKA bitmap.
If there is no EH region tree, output -1 to mark its absence.
Call output_eh_region for every region, even NULL ones.
(output_parm_decl): Only check DECL_CONTEXT of DECL if FN has
a gimple body.
* lto-tags.h (enum LTO_tags): Add LTO_eh_table_shared_region.
* tree.c (free_lang_data_in_type): After building a qualified
type for an argument type, call free_lang_data_in_type on the
new type.
(free_lang_data_in_decl): Only reset DECL_CONTEXT on
FUNCTION_DECL arguments when the function has a gimple body.
lto/ChangeLog
* lto-lang.c (lto_post_options): Set flag_excess_precision_cmdline.
* lto.c (read_cgraph_and_symbols): Set cgraph_function_flags_ready.
(lto_add_all_inlinees): Tidy.
--- Makefile.in (revision 144738)
+++ Makefile.in (working copy)
@@ -2084,7 +2084,7 @@ lto-cgraph.o: lto-cgraph.c $(CONFIG_H) $
tree-pass.h tree-flow.h $(CGRAPH_H) $(FUNCTION_H) $(GGC_H) $(DIAGNOSTIC_H) \
except.h debug.h $(TIMEVAR_H) $(LTO_TAGS_H) $(LTO_SECTION_IN_H) \
$(LTO_SECTION_OUT_H) output.h dwarf2asm.h dwarf2out.h pointer-set.h \
- $(LTO_TREE_IN_H)
+ $(LTO_TREE_IN_H) lto-utils.h
lto-function-in.o: lto-function-in.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(TOPLEV_H) $(EXPR_H) $(FLAGS_H) $(PARAMS_H) input.h $(VARRAY_H) \
$(HASHTAB_H) langhooks.h $(BASIC_BLOCK_H) tree-iterator.h tree-pass.h \
--- cgraph.c 2009/04/03 11:46:06 1.3
+++ cgraph.c 2009/04/04 02:04:43
@@ -666,7 +666,7 @@ initialize_inline_failed (struct cgraph_
e->inline_failed = CIF_REDEFINED_EXTERN_INLINE;
else if (!callee->local.inlinable)
e->inline_failed = CIF_FUNCTION_NOT_INLINABLE;
- else if (gimple_call_cannot_inline_p (e->call_stmt))
+ else if (e->call_stmt && gimple_call_cannot_inline_p (e->call_stmt))
e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
else
e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
@@ -690,7 +690,7 @@ cgraph_create_edge (struct cgraph_node *
hashtable. */
gcc_assert (!cgraph_edge (caller, call_stmt));
#endif
-
+
gcc_assert (is_gimple_call (call_stmt));
}
@@ -729,7 +729,6 @@ cgraph_create_edge (struct cgraph_node *
edge->indirect_call = 0;
edge->call_stmt_cannot_inline_p =
(call_stmt ? gimple_call_cannot_inline_p (call_stmt) : false);
- edge->uid = cgraph_edge_max_uid++;
if (call_stmt && caller->call_site_hash)
{
void **slot;
@@ -1430,32 +1417,6 @@ cgraph_is_clone_node (struct cgraph_node
return n->next_clone || n->prev_clone;
}
-/* Return true if N is an master_clone, (see cgraph_master_clone). */
-
-bool
-cgraph_is_master_clone (struct cgraph_node *n, bool check_overwrite)
-{
- return (n == cgraph_master_clone (n, check_overwrite));
-}
-
-
-/* Return the master clone node of N if it is available and if
- CHECK_OVERWRITE is true, not overwritable. */
-
-struct cgraph_node *
-cgraph_master_clone (struct cgraph_node *n, bool check_overwrite)
-{
- enum availability avail = cgraph_function_body_availability (n);
-
- if (avail == AVAIL_NOT_AVAILABLE ||
- (check_overwrite && (avail == AVAIL_OVERWRITABLE)))
- return NULL;
-
- if (!n->master_clone)
- n->master_clone = cgraph_node (n->decl);
-
- return n->master_clone;
-}
/* NODE is no longer nested function; update cgraph accordingly. */
void
--- except.c 2009/04/03 11:46:06 1.3
+++ except.c 2009/04/05 16:02:29
@@ -4049,6 +4049,15 @@ dump_eh_tree (FILE * out, struct functio
}
}
+/* Dump the EH tree for FN on stderr. */
+
+void
+debug_eh_tree (struct function *fn)
+{
+ dump_eh_tree (stderr, fn);
+}
+
+
/* Verify some basic invariants on EH datastructures. Could be extended to
catch more. */
void
--- except.h 2009/04/03 11:46:06 1.2
+++ except.h 2009/04/05 16:02:43
@@ -204,6 +204,7 @@ extern void collect_eh_region_array (voi
extern void expand_resx_expr (tree);
extern void verify_eh_tree (struct function *);
extern void dump_eh_tree (FILE *, struct function *);
+void debug_eh_tree (struct function *);
extern bool eh_region_outer_p (struct function *, int, int);
extern int eh_region_outermost (struct function *, int, int);
extern void add_type_for_runtime (tree);
--- lto-function-in.c 2009/04/03 11:46:06 1.3
+++ lto-function-in.c 2009/04/05 18:13:53
@@ -169,6 +169,36 @@ input_string_cst (struct data_in *data_i
return build_string (len, ptr);
}
+
+/* Read a bitmap from input block IB. If GC_P is true, allocate the
+ bitmap in GC memory. Otherwise, allocate it on OBSTACK. If
+ OBSTACK is NULL, it is allocated in the default bitmap obstack. */
+
+static bitmap
+input_bitmap (struct lto_input_block *ib, bitmap_obstack *obstack, bool gc_p)
+{
+ unsigned long num_bits, i;
+ bitmap b;
+
+ num_bits = lto_input_uleb128 (ib);
+ if (num_bits == 0)
+ return NULL;
+
+ if (gc_p)
+ b = BITMAP_GGC_ALLOC ();
+ else
+ b = BITMAP_ALLOC (obstack);
+
+ for (i = 0; i < num_bits; i++)
+ {
+ unsigned long bit = lto_input_uleb128 (ib);
+ bitmap_set_bit (b, bit);
+ }
+
+ return b;
+}
+
+
/* Read an IDENTIFIER from the string table in DATA_IN. */
static tree
@@ -1350,13 +1380,32 @@ input_eh_region (struct lto_input_block
enum LTO_tags tag;
eh_region r;
- r = GGC_CNEW (struct eh_region);
- r->region_number = region_number;
-
/* Read the region header. */
tag = input_record_start (ib);
+ if (tag == 0)
+ return NULL;
+
+ /* If TAG indicates that this is a shared region, then return the
+ original region read earlier. */
+ if (tag == LTO_eh_table_shared_region)
+ {
+ eh_region orig;
+ int orig_rn;
+
+ orig_rn = lto_input_sleb128 (ib);
+ orig = VEC_index (eh_region, fn->eh->region_array, orig_rn);
+ /* The region that we are trying to read must exist in the AKA
+ set of the original EH region. */
+ gcc_assert (orig->aka && bitmap_bit_p (orig->aka, region_number));
+
+ return orig;
+ }
+
+ r = GGC_CNEW (struct eh_region);
r->region_number = lto_input_sleb128 (ib);
+ r->aka = input_bitmap (ib, NULL, true);
+
gcc_assert (r->region_number == region_number);
/* Read all the region pointers as region numbers. We'll fix up
@@ -1444,23 +1493,38 @@ input_eh_region (struct lto_input_block
/* After reading the EH regions, pointers to peer and children regions
are region numbers. This converts all these region numbers into
- real pointers into the rematerialized regions for FN. */
+ real pointers into the rematerialized regions for FN. ROOT_REGION
+ is the region number for the root EH region in FN. */
static void
-fixup_eh_region_pointers (struct function *fn, unsigned root_region,
- unsigned last_region)
+fixup_eh_region_pointers (struct function *fn, HOST_WIDE_INT root_region)
{
unsigned i;
VEC(eh_region,gc) *array = fn->eh->region_array;
+ eh_region r;
#define fixup_region(r) (r) = VEC_index (eh_region, array, \
(HOST_WIDE_INT) (intptr_t) (r))
- fn->eh->region_tree = VEC_index (eh_region, array, root_region);
+ gcc_assert (array);
- for (i = 1; i <= last_region; i++)
+ /* A root region with value -1 means that there is not a region tree
+ for this function. However, we may still have an EH table with
+ statements in it. FIXME, this is a bug in the generic EH code. */
+ if (root_region >= 0)
+ fn->eh->region_tree = VEC_index (eh_region, array, root_region);
+
+ for (i = 0; VEC_iterate (eh_region, array, i, r); i++)
{
- eh_region r = VEC_index (eh_region, array, i);
+ if (r == NULL)
+ continue;
+
+ /* If R is a shared EH region, then its region number will be
+ that of its original EH region. Skip these, since they only
+ need to be fixed up when processing the original region. */
+ if (i != (unsigned) r->region_number)
+ continue;
+
fixup_region (r->outer);
fixup_region (r->inner);
fixup_region (r->next_peer);
@@ -1529,7 +1593,7 @@ static void
input_eh_regions (struct lto_input_block *ib, struct data_in *data_in,
struct function *fn)
{
- HOST_WIDE_INT i, last_region, root_region;
+ HOST_WIDE_INT i, last_region, root_region, len;
enum LTO_tags tag;
tag = input_record_start (ib);
@@ -1549,26 +1613,30 @@ input_eh_regions (struct lto_input_block
gcc_assert (fn->eh);
last_region = lto_input_sleb128 (ib);
- VEC_safe_grow (eh_region, gc, fn->eh->region_array, last_region + 1);
fn->eh->last_region_number = last_region;
- VEC_replace (eh_region, fn->eh->region_array, 0, NULL);
root_region = lto_input_sleb128 (ib);
/* Fill in the EH region array. */
- for (i = 1; i <= last_region; i++)
+ len = lto_input_sleb128 (ib);
+ if (len > 0)
{
- eh_region r = input_eh_region (ib, data_in, fn, i);
- VEC_replace (eh_region, fn->eh->region_array, i, r);
- }
+ VEC_safe_grow (eh_region, gc, fn->eh->region_array, len);
+ for (i = 0; i < len; i++)
+ {
+ eh_region r = input_eh_region (ib, data_in, fn, i);
+ VEC_replace (eh_region, fn->eh->region_array, i, r);
+ }
- /* Reconstruct the EH region tree by fixing up the peer/children
- pointers. */
- fixup_eh_region_pointers (fn, root_region, last_region);
+ /* Reconstruct the EH region tree by fixing up the
+ peer/children pointers. */
+ fixup_eh_region_pointers (fn, root_region);
+ }
LTO_DEBUG_UNDENT ();
- input_record_start (ib);
+ tag = input_record_start (ib);
+ gcc_assert (tag == LTO_null);
}
}
@@ -1857,6 +1925,10 @@ input_bb (struct lto_input_block *ib, en
gimple_stmt_iterator bsi;
HOST_WIDE_INT curr_eh_region;
+ /* This routine assumes that CFUN is set to FN, as it needs to call
+ basic GIMPLE routines that use CFUN. */
+ gcc_assert (cfun == fn);
+
LTO_DEBUG_TOKEN ("bbindex");
index = lto_input_uleb128 (ib);
bb = BASIC_BLOCK_FOR_FUNCTION (fn, index);
@@ -1894,7 +1966,10 @@ input_bb (struct lto_input_block *ib, en
}
if (curr_eh_region >= 0)
- add_stmt_to_eh_region (stmt, curr_eh_region);
+ {
+ gcc_assert (curr_eh_region <= num_eh_regions ());
+ add_stmt_to_eh_region (stmt, curr_eh_region);
+ }
LTO_DEBUG_INDENT_TOKEN ("stmt");
tag = input_record_start (ib);
--- lto-function-out.c 2009/04/03 11:46:06 1.3
+++ lto-function-out.c 2009/04/05 18:06:09
@@ -255,8 +255,6 @@ destroy_output_block (struct output_bloc
free (ob);
}
-
-
/* Output STRING of LEN characters to the string
table in OB. The string might or might not include a trailing '\0'.
Then put the index onto the INDEX_STREAM. */
@@ -436,6 +434,31 @@ output_integer (struct output_block *ob,
}
+/* Output bitmap B to OB. */
+
+static void
+output_bitmap (struct output_block *ob, bitmap b)
+{
+ bitmap_iterator bi;
+ unsigned i;
+
+ if (b == NULL)
+ {
+ output_zero (ob);
+ return;
+ }
+
+ /* Indicate how many set bits B has. */
+ output_uleb128 (ob, bitmap_count_bits (b));
+
+ /* FIXME lto. For now, emit a sequence of all the bit positions
+ that are set in B. This could be compacted by packing multiple
+ bits in one word. */
+ EXECUTE_IF_SET_IN_BITMAP (b, 0, i, bi)
+ output_uleb128 (ob, i);
+}
+
+
/* Build a densely packed word that contains only the flags that are
used for this type of tree EXPR and write the word in uleb128 to
the OB. IF CODE is 0 (ERROR_MARK), put the flags anyway.
@@ -740,13 +763,38 @@ output_record_start (struct output_block
}
-/* Output EH region R to OB. */
+/* Output EH region R in function FN to OB. CURR_RN is the slot index
+ that is being emitted in FN->EH->REGION_ARRAY. This is used to
+ detect EH region sharing. */
static void
-output_eh_region (struct output_block *ob, eh_region r)
+output_eh_region (struct output_block *ob, struct function *fn,
+ eh_region r, int curr_rn)
{
enum LTO_tags tag;
+ if (r == NULL)
+ {
+ output_zero (ob);
+ return;
+ }
+
+ /* If R has a different region number than CURR_RN it means that
+ CURR_RN is an alias for the original region R. In this case,
+ instead of wasting space emitting all of R again, only emit the
+ integer R->REGION_NUMBER so that we can share the EH array slots
+ on the reading side. */
+ if (r->region_number != curr_rn)
+ {
+ /* Make sure the EH regions are indeed shared. */
+ gcc_assert (VEC_index (eh_region, fn->eh->region_array, r->region_number)
+ == VEC_index (eh_region, fn->eh->region_array, curr_rn));
+
+ output_record_start (ob, NULL, NULL, LTO_eh_table_shared_region);
+ output_sleb128 (ob, r->region_number);
+ return;
+ }
+
if (r->type == ERT_CLEANUP)
tag = LTO_eh_table_cleanup0;
else if (r->type == ERT_TRY)
@@ -768,6 +816,7 @@ output_eh_region (struct output_block *o
output_record_start (ob, NULL, NULL, tag);
output_sleb128 (ob, r->region_number);
+ output_bitmap (ob, r->aka);
if (r->outer)
output_uleb128 (ob, r->outer->region_number);
else
@@ -864,17 +913,25 @@ output_eh_regions (struct output_block *
{
eh_region curr;
- if (fn->eh->region_array)
+ if (fn->eh)
{
unsigned i;
output_record_start (ob, NULL, NULL, LTO_eh_table);
output_sleb128 (ob, fn->eh->last_region_number);
- output_sleb128 (ob, fn->eh->region_tree->region_number);
+ /* If the EH regions were optimized, there may not be a region
+ tree. FIXME, if there is no region tree we should not be
+ removing all statements from the EH tables. This is a bug in
+ the generic EH code. */
+ if (fn->eh->region_tree)
+ output_sleb128 (ob, fn->eh->region_tree->region_number);
+ else
+ output_sleb128 (ob, -1);
+
+ output_sleb128 (ob, VEC_length (eh_region, fn->eh->region_array));
for (i = 0; VEC_iterate (eh_region, fn->eh->region_array, i, curr); i++)
- if (curr)
- output_eh_region (ob, curr);
+ output_eh_region (ob, fn, curr, i);
LTO_DEBUG_UNDENT ();
}
@@ -2681,7 +2738,17 @@ output_parm_decl (struct output_block *o
/* uid and locus are handled specially */
output_tree (ob, decl->decl_minimal.name);
- gcc_assert (decl->decl_minimal.context == fn);
+
+ /* If FN has a gimple body, DECL's context must bu FN. Otherwise,
+ it doesn't really matter, as we will not be emitting any code for
+ FN. In general, there may be other instances of FN created by
+ the front end and since PARM_DECLs are generally shared, their
+ DECL_CONTEXT changes as the replicas of FN are created. The only
+ time where DECL_CONTEXT is important is for the FNs that have a
+ gimple body (since the PARM_DECL will be used in the function's
+ body). */
+ if (gimple_has_body_p (fn))
+ gcc_assert (DECL_CONTEXT (decl) == fn);
output_tree (ob, decl->common.type);
--- lto-tags.h 2009/02/24 20:26:31 1.2
+++ lto-tags.h 2009/04/05 17:58:08
@@ -494,6 +494,7 @@ enum LTO_tags {
LTO_eh_table_must_not_throw1,
LTO_eh_table_throw0,
LTO_eh_table_throw1,
+ LTO_eh_table_shared_region,
/* Base info, e.g., for C++ */
LTO_tree_binfo,
--- tree.c 2009/04/03 11:46:06 1.3
+++ tree.c 2009/04/06 14:51:58
@@ -3851,6 +3851,7 @@ free_lang_data_in_type (tree type)
& ~TYPE_QUAL_CONST
& ~TYPE_QUAL_VOLATILE;
TREE_VALUE (p) = build_qualified_type (arg_type, quals);
+ free_lang_data_in_type (TREE_VALUE (p));
}
}
}
@@ -4042,8 +4043,19 @@ free_lang_data_in_decl (tree decl)
if (context && TREE_CODE (context) == FUNCTION_DECL)
DECL_CONTEXT (decl) = NULL_TREE;
- for (t = DECL_ARGUMENTS (decl); t ; t = TREE_CHAIN (t))
- DECL_CONTEXT (t) = decl;
+
+ /* If DECL has a gimple body, then the context for its arguments
+ must be DECL. Otherwise, it doesn't really matter, as we
+ will not be emitting any code for DECL. In general, there
+ may be other instances of DECL created by the front end and
+ since PARM_DECLs are generally shared, their DECL_CONTEXT
+ changes as the replicas of DECL are created. The only time
+ where DECL_CONTEXT is important is for the FUNCTION_DECLs
+ that have a gimple body (since the PARM_DECL will be used in
+ the function's body). */
+ if (gimple_has_body_p (decl))
+ for (t = DECL_ARGUMENTS (decl); t ; t = TREE_CHAIN (t))
+ DECL_CONTEXT (t) = decl;
}
else if (TREE_CODE (decl) == VAR_DECL)
{
@@ -4265,7 +4277,6 @@ find_decls_types_in_node (struct cgraph_
fn = DECL_STRUCT_FUNCTION (n->decl);
/* Traverse locals. */
-
for (t = fn->local_decls; t; t = TREE_CHAIN (t))
{
tree *tp = &TREE_VALUE (t);
--- lto-lang.c 2009/04/03 11:46:19 1.3
+++ lto-lang.c 2009/04/04 01:57:57
@@ -728,6 +728,10 @@ lto_post_options (const char **pfilename
if (flag_wpa)
flag_generate_lto = 1;
+ /* Excess precision other than "fast" requires front-end
+ support. */
+ flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+
lto_read_all_file_options ();
/* Initialize the compiler back end. */
--- lto/lto.c (revision 145604)
+++ lto/lto.c (working copy)
@@ -608,7 +607,7 @@ lto_add_all_inlinees (cgraph_node_set se
bitmap inlined_decls = lto_bitmap_alloc();
bool changed;
- /* We are going to iterate SET will adding to it, mark all original
+ /* We are going to iterate SET while adding to it, mark all original
nodes so that we only add node inlined to original nodes. */
for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
{
@@ -616,7 +615,8 @@ lto_add_all_inlinees (cgraph_node_set se
bitmap_set_bit (original_decls, DECL_UID (csi_node (csi)->decl));
}
- /* Some of the original nodes might not be needed anymore. Remove them. */
+ /* Some of the original nodes might not be needed anymore.
+ Remove them. */
do
{
changed = false;
@@ -625,13 +625,13 @@ lto_add_all_inlinees (cgraph_node_set se
struct cgraph_node *inlined_to;
node = csi_node (csi);
- /* NODE was not inlined. We still need it. */
+ /* NODE was not inlined. We still need it. */
if (!node->global.inlined_to)
continue;
inlined_to = node->global.inlined_to;
- /* NODE should have only one caller */
+ /* NODE should have only one caller. */
gcc_assert (!node->callers->next_caller);
if (!bitmap_bit_p (original_nodes, inlined_to->uid))
@@ -641,7 +641,8 @@ lto_add_all_inlinees (cgraph_node_set se
changed = true;
}
}
- } while (changed);
+ }
+ while (changed);
for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
{
@@ -652,6 +653,7 @@ lto_add_all_inlinees (cgraph_node_set se
lto_bitmap_free (original_nodes);
lto_bitmap_free (original_decls);
+
return inlined_decls;
}
@@ -780,7 +782,7 @@ lto_scan_statics_in_cgraph_node (struct
{
struct lto_in_decl_state *state;
- /* Return if NODE has no function body or is not the master clone. */
+ /* Do nothing if NODE has no function body. */
if (!node->analyzed)
return;
@@ -1559,6 +1561,9 @@ read_cgraph_and_symbols (unsigned nfiles
lto_materialize_constructors_and_inits (file_data);
}
+ /* Indicate that the cgraph is built and ready. */
+ cgraph_function_flags_ready = true;
+
timevar_pop (TV_IPA_LTO_DECL_IO);
}