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); }