diff options
author | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-20 08:38:32 +0000 |
---|---|---|
committer | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-20 08:38:32 +0000 |
commit | 3e41a3c4063d1014118aafa8c609e2f5741bf2da (patch) | |
tree | d1d81473ac0df66d33973cb168258176e1ca34bf /gcc | |
parent | 94cd0238acdaea5ab220faaa3a6b7ecc0ec0c512 (diff) | |
download | gcc-3e41a3c4063d1014118aafa8c609e2f5741bf2da.tar.gz |
2011-10-19 Basile Starynkevitch <basile@starynkevitch.net>
MELT branch merged with trunk rev 180237 using svnmerge.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@180238 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
64 files changed, 2734 insertions, 1218 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d8f5a8292d0..9ef7e64c2c8 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,266 @@ +2011-10-20 Richard Guenther <rguenther@suse.de> + + * tree-ssa-loop-im.c (stmt_cost): Add WIDEN_*, FMA_EXPR + and rotates to the set of expensive operations. + +2011-10-19 David S. Miller <davem@davemloft.net> + + * config/sparc/sparc.c (sparc_expand_move): Use + can_create_pseudo_p. + (sparc_emit_set_const32): Likewise. + (sparc_emit_set_const64_longway): Likewise. + (sparc_emit_set_const64): Likewise. + (sparc_legitimize_pic_address): Likewise. + (memory_ok_for_ldd): Likewise. + +2011-10-20 Dehao Chen <dehao@google.com> + + * profile.c (compute_branch_probabilities): Compute and dump the + overlap between the static estimation and the instrumentation profile. + (OVERLAP_BASE): New macro. + (compute_frequency_overlap): New function + +2011-10-19 Jakub Jelinek <jakub@redhat.com> + + * config/i386/i386.c (expand_vec_perm_vpshufb2_vpermq_even_odd): Use + d->op1 instead of d->op0 for the second vpshufb. + (expand_vec_perm_even_odd_1): For V8SImode fix vpshufd immediates. + (ix86_expand_vec_perm_const): If mask indicates two operands are + needed, but both are the same and expanding them as d.op0 == d.op1 + failed, retry with d.op0 != d.op1. + (ix86_expand_vec_perm_builtin): Likewise. Handle sorry printing + also for d.nelt == 32. + + PR middle-end/50754 + * cfgexpand.c (expand_debug_expr): Handle WIDEN_LSHIFT_EXPR, ignore + VEC_PERM_EXPR. + +2011-10-19 Bernd Schmidt <bernds@codesourcery.com> + + * regrename.h: New file. + * regrename.c: Include it. Also include "emit-rtl.h". + (struct du_head, struct du_chain, du_head_p DEF_VEC and + DEF_VEC_ALLOC_P): Move to regrename.h. + (do_replace): Remove declaration. + (insn_rr): New variable. + (cur_operand): New static variable. + (regrename_chain_from_id): Renamed from chain_from_id and no longer + static. All callers changed. + (record_operand_use): New static function. + (scan_rtx_reg): Use it. + (find_best_rename_reg): New function, broken out of rename_chains. + (rename_chains): Use it. Don't update chain regno and nregs here, ... + (regrename_do_replace): ... do it here instead. Renamed from + do_replace, and no longer static. All callers changed. + (regrename_analyze): No longer static. New arg bb_mask. + All callers changed. If bb_mask is nonzero, use it to limit the + number of basic blocks we analyze. If we failed to analyze a block, + clear insn operand data. + (record_out_operands): New arg insn_info. Update cur_operand if it is + nonnull. + (build_def_use): If insn_rr is nonnull, pass an insn_info to + record_out_operands, and update cur_operand here as well. + (regrename_init, regrename_finish): New functions. + (regrename_optimize): Use them. + * Makefile.in (regrename.o): Adjust dependencies. + +2011-10-19 Tom de Vries <tom@codesourcery.com> + + PR tree-optimization/50769 + * tree-ssa-tail-merge.c (replace_block_by): Calculate phi_vuse2 + unconditionally. Handle case that phi_vuse2 is not an SSA_NAME. Add + dummy argument .MEM to phi when increasing number of arguments of phi by + redirecting edges to the block with phi. + +2011-10-19 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR bootstrap/50777 + * configure.ac: Save and restore CXXFLAGS around + gcc_AC_CHECK_DECLS uses. + Check for madvise() declaration with g++ if --enable-build-with-cxx. + * configure: Regenerate. + * config.in: Regenerate. + * ggc-page.c (USING_MADVISE): Also check HAVE_DECL_MADVISE. + +2011-10-19 Alexandre Oliva <aoliva@redhat.com> + + PR debug/49310 + * var-tracking.c (loc_exp_dep, onepart_aux): New structs. + (variable_part): Replace offset with union. + (enum onepart_enum, onepart_enum_t): New. + (variable_def): Drop cur_loc_changed, add onepart. + (value_chain_def, const_value_chain): Remove. + (VAR_PART_OFFSET, VAR_LOC_1PAUX): New macros, with checking. + (VAR_LOC_DEP_LST, VAR_LOC_DEP_LSTP): New macros. + (VAR_LOC_FROM, VAR_LOC_DEPTH, VAR_LOC_DEP_VEC): Likewise. + (value_chain_pool, value_chains): Remove. + (dropped_values): New. + (struct parm_reg): Only if HAVE_window_save. + (vt_stack_adjustments): Don't record register arguments. + (dv_as_rtx): New. + (dv_onepart_p): Return a onepart_enum_t. + (onepart_pool): New. + (dv_pool): Remove. + (dv_from_rtx): New. + (variable_htab_free): Release onepart aux data. Reset flags. + (value_chain_htab_hash, value_chain_htab_eq): Remove. + (unshare_variable): Use onepart field. Propagate onepart aux + data or offset. Drop cur_loc_changed. + (val_store): Cope with NULL insn. Rephrase dump output. Check + for unsuitable locs. Add FIXME on using cselib locs. + (val_reset): Remove FIXME of unfounded concerns. + (val_resolve): Check for unsuitable locs. Add FIXME on using + cselib locs. + (variable_union): Use onepart field, adjust access to offset. + (NO_LOC_P): New. + (VALUE_CHANGED, DECL_CHANGED): Update doc. + (set_dv_changed): Clear NO_LOC_P when changed. + (find_loc_in_1pdv): Use onepart field. + (intersect_loc_chains): Likewise. + (unsuitable_loc): New. + (loc_cmp): Keep ENTRY_VALUEs at the end of the loc list. + (add_value_chain, add_value_chains): Remove. + (add_cselib_value_chains, remove_value_chain): Likewise. + (remove_value_chains, remove_cselib_value_chains): Likewise. + (canonicalize_loc_order_check): Use onepart. Drop cur_loc_changed. + (canonicalize_values_star, canonicalize_vars_star): Use onepart. + (variable_merge_over_cur): Likewise. Adjust access to offset. + Drop cur_loc_changed. + (variable_merge_over_src): Use onepart field. + (remove_duplicate_values): Likewise. + (variable_post_merge_new_vals): Likewise. + (find_mem_expr_in_1pdv): Likewise. + (dataflow_set_preserve_mem_locs): Likewise. Drop cur_loc_changed + and value chains. + (dataflow_set_remove_mem_locs): Likewise. Use VAR_LOC_FROM. + (variable_different_p): Use onepart field. Move onepart test out + of the loop. + (argument_reg_set): Drop. + (add_uses, add_stores): Preserve but do not record in dynamic + tables equivalences for ENTRY_VALUEs and CFA_based addresses. + Avoid unsuitable address expressions. + (EXPR_DEPTH): Unlimit. + (EXPR_USE_DEPTH): Repurpose PARAM_MAX_VARTRACK_EXPR_DEPTH. + (prepare_call_arguments): Use DECL_RTL_IF_SET. + (dump_var): Adjust access to offset. + (variable_from_dropped, recover_dropped_1paux): New. + (variable_was_changed): Drop cur_loc_changed. Use onepart. + Preserve onepart aux in empty_var. Recover empty_var and onepart + aux from dropped_values. + (find_variable_location_part): Special-case onepart. Adjust + access to offset. + (set_slot_part): Use onepart. Drop cur_loc_changed. Adjust + access to offset. Initialize onepaux. Drop value chains. + (delete_slot_part): Drop value chains. Use VAR_LOC_FROM. + (VEC (variable, heap), VEC (rtx, stack)): Define. + (expand_loc_callback_data): Drop dummy, cur_loc_changed, + ignore_cur_loc. Add expanding, pending, depth. + (loc_exp_dep_alloc, loc_exp_dep_clear): New. + (loc_exp_dep_insert, loc_exp_dep_set): New. + (notify_dependents_of_resolved_value): New. + (update_depth, vt_expand_var_loc_chain): New. + (vt_expand_loc_callback): Revamped. + (resolve_expansions_pending_recursion): New. + (INIT_ELCD, FINI_ELCD): New. + (vt_expand_loc): Use the new macros above. Drop ignore_cur_loc + parameter, adjust all callers. + (vt_expand_loc_dummy): Drop. + (vt_expand_1pvar): New. + (emit_note_insn_var_location): Operate on non-debug decls only. + Revamp multi-part cur_loc recomputation and one-part expansion. + Drop cur_loc_changed. Adjust access to offset. + (VEC (variable, heap)): Drop. + (changed_variables_stack, changed_values_stack): Drop. + (check_changed_vars_0, check_changed_vars_1): Remove. + (check_changed_vars_2, check_changed_vars_3): Remove. + (values_to_stack, remove_value_from_changed_variables): New. + (notify_dependents_of_changed_value, process_changed_values): New. + (emit_notes_for_changes): Revamp onepart updates. + (emit_notes_for_differences_1): Use onepart. Drop cur_loc_changed + and value chains. Propagate onepaux. Recover empty_var and onepaux + from dropped_values. + (emit_notes_for_differences_2): Drop value chains. + (emit_notes_in_bb): Adjust. + (vt_emit_notes): Drop value chains, changed_variables_stack. + Initialize and release dropped_values. + (create_entry_value): Revamp. + (vt_add_function_parameter): Use new interface. + (note_register_arguments): Remove. + (vt_initialize): Drop value chains and register arguments. + (vt_finalize): Drop value chains. Release windowed_parm_regs only + if HAVE_window_save. + * rtl.h: Document various pass-local uses of RTL flags. + * tree.h (DECL_RTL_KNOWN_SET): New. + * doc/invoke.texi (param max-vartrack-expr-depth): Update + description and default. + +2011-10-19 Georg-Johann Lay <avr@gjlay.de> + + PR target/50447 + * config/avr/avr.md (cc): New alternative out_plus_noclobber. + (adjust_len): Ditto. + (addhi3): Don't pipe through short; use gen_int_mode instead. + Prior to reload, expand to gen_addhi3_clobber. + (*addhi3): Use avr_out_plus_noclobber if applicable, use + out_plus_noclobber in cc and adjust_len attribute. + (addhi3_clobber): 2 new RTL peepholes. + (addhi3_clobber): New insn. + * config/avr/avr-protos.h: (avr_out_plus_noclobber): New prototype. + * config/avr/avr.c (avr_out_plus_noclobber): New function. + (notice_update_cc): Handle CC_OUT_PLUS_NOCLOBBER. + (avr_out_plus_1): Tweak if only MSB is +/-1 and other bytes are 0. + Set cc0 to set_zn for adiw on 16-bit values. + (adjust_insn_length): Handle ADJUST_LEN_OUT_PLUS_NOCLOBBER. + (expand_epilogue): No need to add 0 to frame_pointer_rtx. + +2011-10-19 Richard Guenther <rguenther@suse.de> + + PR middle-end/50780 + * tree-ssa-forwprop.c (forward_propagate_into_cond): Verify + the condition is properly gimple before using it. + * tree-eh (stmt_could_throw_1_p): Properly extract the + operation type from comparisons. + +2011-10-19 Roland Stigge <stigge@antcom.de> + + PR translation/48638 + * plugin.c (add_new_plugin): Fix typo in fatal_error message. + +2011-10-19 Roland Stigge <stigge@antcom.de> + + PR translation/49517 + * config/rx/rx.c (rx_print_operand): Fix typo in warning message. + +2011-10-19 Richard Guenther <rguenther@suse.de> + + PR middle-end/50768 + * gimple-fold.c (gimplify_and_update_call_from_tree): Rewrite. + +2011-10-19 Andrey Belevantsev <abel@ispras.ru> + + PR rtl-optimization/50340 + * sel-sched-ir.c (update_target_availability): LHS register + availability is not known if the unavailable LHS of the other + expression is a different register. + +2011-10-19 Ulrich Weigand <Ulrich.Weigand@de.ibm.com> + + PR target/50310 + * config/spu/spu.c (spu_emit_vector_compare): Support unordered + floating-point comparisons. + +2011-10-19 Jan Hubicka <jh@suse.cz> + + * cgraphunit.c (handle_alias_pairs): Also handle wekref with destination + declared. + (output_weakrefs): New function. + * varpool.c (varpool_create_variable_alias): Handle external aliases. + +2011-10-19 Jakub Jelinek <jakub@redhat.com> + + * dwarf2out.c (loc_descriptor): For SUBREG pass SUBREG_REG's mode as + second argument instead of mode. + 2011-10-18 Jakub Jelinek <jakub@redhat.com> * config/i386/i386.c (ix86_expand_vec_perm): In merge_two use diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index a8885cf9df5..2f6b55c3ea3 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20111019 +20111020 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index f9d0b07baa2..10a37616400 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -3588,7 +3588,8 @@ regcprop.o : regcprop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ regrename.o : regrename.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_ERROR_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h \ output.h $(RECOG_H) $(FUNCTION_H) $(OBSTACK_H) $(FLAGS_H) $(TM_P_H) \ - addresses.h reload.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(TARGET_H) + addresses.h reload.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(TARGET_H) \ + regrename.h $(EMIT_RTL_H) ifcvt.o : ifcvt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(REGS_H) $(DIAGNOSTIC_CORE_H) $(FLAGS_H) insn-config.h $(FUNCTION_H) $(RECOG_H) \ $(TARGET_H) $(BASIC_BLOCK_H) $(EXPR_H) output.h $(EXCEPT_H) $(TM_P_H) \ diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index f839cfb222e..ff41f8e5f1a 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,4 +1,4 @@ -2011-10-15 Tom Tromey <tromey@redhat.com> +2011-10-16 Tom Tromey <tromey@redhat.com> Dodji Seketeli <dodji@redhat.com> * gcc-interface/trans.c (gigi, Sloc_to_locus): Adjust to use the @@ -6,7 +6,7 @@ 2011-10-16 Tristan Gingold <gingold@adacore.com> - * link.c (_AIX): Add support for GNU ld. + * link.c (_AIX): Add support for GNU ld. 2011-10-16 Fedor Rybin <frybin@adacore.com> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 045c2e28813..6fb9ee0c5a1 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -3267,6 +3267,7 @@ expand_debug_expr (tree exp) case VEC_WIDEN_MULT_LO_EXPR: case VEC_WIDEN_LSHIFT_HI_EXPR: case VEC_WIDEN_LSHIFT_LO_EXPR: + case VEC_PERM_EXPR: return NULL; /* Misc codes. */ @@ -3321,6 +3322,7 @@ expand_debug_expr (tree exp) return NULL; case WIDEN_SUM_EXPR: + case WIDEN_LSHIFT_EXPR: if (SCALAR_INT_MODE_P (GET_MODE (op0)) && SCALAR_INT_MODE_P (mode)) { @@ -3329,7 +3331,8 @@ expand_debug_expr (tree exp) 0))) ? ZERO_EXTEND : SIGN_EXTEND, mode, op0, inner_mode); - return simplify_gen_binary (PLUS, mode, op0, op1); + return simplify_gen_binary (TREE_CODE (exp) == WIDEN_LSHIFT_EXPR + ? ASHIFT : PLUS, mode, op0, op1); } return NULL; diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 96c910c1051..ef71b5bed7c 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -1219,7 +1219,6 @@ handle_alias_pairs (void) for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p);) { if (TREE_CODE (p->decl) == FUNCTION_DECL - && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) && (target_node = cgraph_node_for_asm (p->target)) != NULL) { src_node = cgraph_get_node (p->decl); @@ -1231,12 +1230,12 @@ handle_alias_pairs (void) However for weakref we insist on EXTERNAL flag being set. See gcc.dg/attr-alias-5.c */ if (DECL_EXTERNAL (p->decl)) - DECL_EXTERNAL (p->decl) = 0; + DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref", + DECL_ATTRIBUTES (p->decl)) != NULL; cgraph_create_function_alias (p->decl, target_node->decl); VEC_unordered_remove (alias_pair, alias_pairs, i); } else if (TREE_CODE (p->decl) == VAR_DECL - && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) && (target_vnode = varpool_node_for_asm (p->target)) != NULL) { /* Normally EXTERNAL flag is used to mark external inlines, @@ -1245,7 +1244,8 @@ handle_alias_pairs (void) However for weakref we insist on EXTERNAL flag being set. See gcc.dg/attr-alias-5.c */ if (DECL_EXTERNAL (p->decl)) - DECL_EXTERNAL (p->decl) = 0; + DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref", + DECL_ATTRIBUTES (p->decl)) != NULL; varpool_create_variable_alias (p->decl, target_vnode->decl); VEC_unordered_remove (alias_pair, alias_pairs, i); } @@ -2064,6 +2064,26 @@ ipa_passes (void) bitmap_obstack_release (NULL); } +/* Weakrefs may be associated to external decls and thus not output + at expansion time. Emit all neccesary aliases. */ + +static void +output_weakrefs (void) +{ + struct cgraph_node *node; + struct varpool_node *vnode; + for (node = cgraph_nodes; node; node = node->next) + if (node->alias && node->thunk.alias && DECL_EXTERNAL (node->decl) + && !TREE_ASM_WRITTEN (node->decl)) + assemble_alias (node->decl, + DECL_ASSEMBLER_NAME (node->thunk.alias)); + for (vnode = varpool_nodes; vnode; vnode = vnode->next) + if (vnode->alias && vnode->alias_of && DECL_EXTERNAL (vnode->decl) + && !TREE_ASM_WRITTEN (vnode->decl)) + assemble_alias (vnode->decl, + DECL_ASSEMBLER_NAME (vnode->alias_of)); +} + /* Perform simple optimizations based on callgraph. */ @@ -2150,6 +2170,8 @@ cgraph_optimize (void) varpool_assemble_pending_decls (); } + + output_weakrefs (); cgraph_process_new_functions (); cgraph_state = CGRAPH_STATE_FINISHED; diff --git a/gcc/config.in b/gcc/config.in index e8148b6d571..3950e289b7c 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -753,6 +753,13 @@ #endif +/* Define to 1 if we found a declaration for 'madvise', otherwise define to 0. + */ +#ifndef USED_FOR_TARGET +#undef HAVE_DECL_MADVISE +#endif + + /* Define to 1 if we found a declaration for 'malloc', otherwise define to 0. */ #ifndef USED_FOR_TARGET diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index a799fb2a937..dd8ba3a3318 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -83,6 +83,7 @@ extern void avr_output_addr_vec_elt (FILE *stream, int value); extern const char *avr_out_sbxx_branch (rtx insn, rtx operands[]); extern const char* avr_out_bitop (rtx, rtx*, int*); extern const char* avr_out_plus (rtx*, int*, int*); +extern const char* avr_out_plus_noclobber (rtx*, int*, int*); extern const char* avr_out_addto_sp (rtx*, int*); extern bool avr_popcount_each_byte (rtx, int, int); diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index c0ce6f9ace0..94bc30af3d9 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -1051,9 +1051,10 @@ expand_epilogue (bool sibcall_p) if (frame_pointer_needed) { /* Get rid of frame. */ - emit_move_insn(frame_pointer_rtx, - gen_rtx_PLUS (HImode, frame_pointer_rtx, - gen_int_mode (size, HImode))); + if (size) + emit_move_insn (frame_pointer_rtx, + gen_rtx_PLUS (HImode, frame_pointer_rtx, + gen_int_mode (size, HImode))); } else { @@ -1682,14 +1683,19 @@ notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn) break; case CC_OUT_PLUS: + case CC_OUT_PLUS_NOCLOBBER: { rtx *op = recog_data.operand; int len_dummy, icc; /* Extract insn's operands. */ extract_constrain_insn_cached (insn); + + if (CC_OUT_PLUS == cc) + avr_out_plus (op, &len_dummy, &icc); + else + avr_out_plus_noclobber (op, &len_dummy, &icc); - avr_out_plus (op, &len_dummy, &icc); cc = (enum attr_cc) icc; break; @@ -4773,7 +4779,8 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc) /* Value to add. There are two ways to add VAL: R += VAL and R -= -VAL. */ rtx xval = xop[2]; - /* Addition does not set cc0 in a usable way. */ + /* Except in the case of ADIW with 16-bit register (see below) + addition does not set cc0 in a usable way. */ *pcc = (MINUS == code) ? CC_SET_CZN : CC_CLOBBER; @@ -4821,6 +4828,9 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc) started = true; avr_asm_len (code == PLUS ? "adiw %0,%1" : "sbiw %0,%1", op, plen, 1); + + if (n_bytes == 2 && PLUS == code) + *pcc = CC_SET_ZN; } i++; @@ -4836,6 +4846,14 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc) op, plen, 1); continue; } + else if ((val8 == 1 || val8 == 0xff) + && !started + && i == n_bytes - 1) + { + avr_asm_len ((code == PLUS) ^ (val8 == 1) ? "dec %0" : "inc %0", + op, plen, 1); + break; + } switch (code) { @@ -4924,6 +4942,22 @@ avr_out_plus (rtx *xop, int *plen, int *pcc) } +/* Same as above but XOP has just 3 entries. + Supply a dummy 4th operand. */ + +const char* +avr_out_plus_noclobber (rtx *xop, int *plen, int *pcc) +{ + rtx op[4]; + + op[0] = xop[0]; + op[1] = xop[1]; + op[2] = xop[2]; + op[3] = NULL_RTX; + + return avr_out_plus (op, plen, pcc); +} + /* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile time constant XOP[2]: @@ -5308,6 +5342,8 @@ adjust_insn_length (rtx insn, int len) case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break; case ADJUST_LEN_OUT_PLUS: avr_out_plus (op, &len, NULL); break; + case ADJUST_LEN_OUT_PLUS_NOCLOBBER: + avr_out_plus_noclobber (op, &len, NULL); break; case ADJUST_LEN_ADDTO_SP: avr_out_addto_sp (op, &len); break; diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index aafdc554a10..35d4bdc1717 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -78,7 +78,7 @@ ;; Condition code settings. (define_attr "cc" "none,set_czn,set_zn,set_n,compare,clobber, - out_plus" + out_plus, out_plus_noclobber" (const_string "none")) (define_attr "type" "branch,branch1,arith,xcall" @@ -125,7 +125,8 @@ ;; Otherwise do special processing depending on the attribute. (define_attr "adjust_len" - "out_bitop, out_plus, addto_sp, tsthi, tstsi, compare, call, + "out_bitop, out_plus, out_plus_noclobber, addto_sp, + tsthi, tstsi, compare, call, mov8, mov16, mov32, reload_in16, reload_in32, ashlqi, ashrqi, lshrqi, ashlhi, ashrhi, lshrhi, @@ -759,14 +760,22 @@ (plus:HI (match_operand:HI 1 "register_operand" "") (match_operand:HI 2 "nonmemory_operand" "")))] "" - " -{ - if (GET_CODE (operands[2]) == CONST_INT) - { - short tmp = INTVAL (operands[2]); - operands[2] = GEN_INT(tmp); - } -}") + { + if (CONST_INT_P (operands[2])) + { + operands[2] = gen_int_mode (INTVAL (operands[2]), HImode); + + if (can_create_pseudo_p() + && !stack_register_operand (operands[0], HImode) + && !stack_register_operand (operands[1], HImode) + && !d_register_operand (operands[0], HImode) + && !d_register_operand (operands[1], HImode)) + { + emit_insn (gen_addhi3_clobber (operands[0], operands[1], operands[2])); + DONE; + } + } + }) (define_insn "*addhi3_zero_extend" @@ -803,20 +812,77 @@ (set_attr "adjust_len" "addto_sp")]) (define_insn "*addhi3" - [(set (match_operand:HI 0 "register_operand" "=r,!w,!w,d,r,r") - (plus:HI - (match_operand:HI 1 "register_operand" "%0,0,0,0,0,0") - (match_operand:HI 2 "nonmemory_operand" "r,I,J,i,P,N")))] + [(set (match_operand:HI 0 "register_operand" "=r,d,d") + (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0") + (match_operand:HI 2 "nonmemory_operand" "r,s,n")))] "" - "@ - add %A0,%A2\;adc %B0,%B2 - adiw %A0,%2 - sbiw %A0,%n2 - subi %A0,lo8(-(%2))\;sbci %B0,hi8(-(%2)) - sec\;adc %A0,__zero_reg__\;adc %B0,__zero_reg__ - sec\;sbc %A0,__zero_reg__\;sbc %B0,__zero_reg__" - [(set_attr "length" "2,1,1,2,3,3") - (set_attr "cc" "set_n,set_czn,set_czn,set_czn,set_n,set_n")]) + { + static const char * const asm_code[] = + { + "add %A0,%A2\;adc %B0,%B2", + "subi %A0,lo8(-(%2))\;sbci %B0,hi8(-(%2))", + "" + }; + + if (*asm_code[which_alternative]) + return asm_code[which_alternative]; + + return avr_out_plus_noclobber (operands, NULL, NULL); + } + [(set_attr "length" "2,2,2") + (set_attr "adjust_len" "*,*,out_plus_noclobber") + (set_attr "cc" "set_n,set_czn,out_plus_noclobber")]) + +;; Adding a constant to NO_LD_REGS might have lead to a reload of +;; that constant to LD_REGS. We don't add a scratch to *addhi3 +;; itself because that insn is special to reload. + +(define_peephole2 ; addhi3_clobber + [(set (match_operand:HI 0 "d_register_operand" "") + (match_operand:HI 1 "const_int_operand" "")) + (set (match_operand:HI 2 "l_register_operand" "") + (plus:HI (match_dup 2) + (match_dup 0)))] + "peep2_reg_dead_p (2, operands[0])" + [(parallel [(set (match_dup 2) + (plus:HI (match_dup 2) + (match_dup 1))) + (clobber (match_dup 3))])] + { + operands[3] = simplify_gen_subreg (QImode, operands[0], HImode, 0); + }) + +;; Same, but with reload to NO_LD_REGS +;; Combine *reload_inhi with *addhi3 + +(define_peephole2 ; addhi3_clobber + [(parallel [(set (match_operand:HI 0 "l_register_operand" "") + (match_operand:HI 1 "const_int_operand" "")) + (clobber (match_operand:QI 2 "d_register_operand" ""))]) + (set (match_operand:HI 3 "l_register_operand" "") + (plus:HI (match_dup 3) + (match_dup 0)))] + "peep2_reg_dead_p (2, operands[0])" + [(parallel [(set (match_dup 3) + (plus:HI (match_dup 3) + (match_dup 1))) + (clobber (match_dup 2))])]) + +(define_insn "addhi3_clobber" + [(set (match_operand:HI 0 "register_operand" "=d,l") + (plus:HI (match_operand:HI 1 "register_operand" "%0,0") + (match_operand:HI 2 "const_int_operand" "n,n"))) + (clobber (match_scratch:QI 3 "=X,&d"))] + "" + { + gcc_assert (REGNO (operands[0]) == REGNO (operands[1])); + + return avr_out_plus (operands, NULL, NULL); + } + [(set_attr "length" "4") + (set_attr "adjust_len" "out_plus") + (set_attr "cc" "out_plus")]) + (define_insn "addsi3" [(set (match_operand:SI 0 "register_operand" "=r,d ,d,r") @@ -3606,12 +3672,8 @@ (if_then_else (match_test "!AVR_HAVE_JMP_CALL") (const_int 1) (const_int 2)) - (if_then_else (and (ge (minus (pc) - (match_dup 0)) - (const_int -2047)) - (le (minus (pc) - (match_dup 0)) - (const_int 2047))) + (if_then_else (and (ge (minus (pc) (match_dup 0)) (const_int -2047)) + (le (minus (pc) (match_dup 0)) (const_int 2047))) (const_int 1) (const_int 2)))) (set_attr "cc" "none")]) diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index ec9d39b4830..4af4e5958b7 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -35992,7 +35992,7 @@ expand_vec_perm_vpshufb2_vpermq_even_odd (struct expand_vec_perm_d *d) vperm = force_reg (V32QImode, vperm); h = gen_reg_rtx (V32QImode); - op = gen_lowpart (V32QImode, d->op0); + op = gen_lowpart (V32QImode, d->op1); emit_insn (gen_avx2_pshufbv32qi3 (h, op, vperm)); ior = gen_reg_rtx (V32QImode); @@ -36154,9 +36154,9 @@ expand_vec_perm_even_odd_1 (struct expand_vec_perm_d *d, unsigned odd) /* Swap the 2nd and 3rd position in each lane into { 0 2 1 3 8 a 9 b } and { 4 6 5 7 c e d f }. */ emit_insn (gen_avx2_pshufdv3 (t1, t1, - GEN_INT (2 * 2 + 1 * 16 + 3 * 64))); + GEN_INT (2 * 4 + 1 * 16 + 3 * 64))); emit_insn (gen_avx2_pshufdv3 (t2, t2, - GEN_INT (2 * 2 + 1 * 16 + 3 * 64))); + GEN_INT (2 * 4 + 1 * 16 + 3 * 64))); /* Now an vpunpck[lh]qdq will produce { 0 2 4 6 8 a c e } resp. { 1 3 5 7 9 b d f }. */ @@ -36498,6 +36498,7 @@ ix86_expand_vec_perm_builtin (tree exp) { struct expand_vec_perm_d d; tree arg0, arg1, arg2; + bool maybe_retry = false; arg0 = CALL_EXPR_ARG (exp, 0); arg1 = CALL_EXPR_ARG (exp, 1); @@ -36543,6 +36544,7 @@ ix86_expand_vec_perm_builtin (tree exp) for (i = 0; i < nelt; ++i) if (d.perm[i] >= nelt) d.perm[i] -= nelt; + maybe_retry = true; } /* FALLTHRU */ @@ -36563,6 +36565,28 @@ ix86_expand_vec_perm_builtin (tree exp) if (ix86_expand_vec_perm_builtin_1 (&d)) return d.target; + /* If the mask says both arguments are needed, but they are the same, + the above tried to expand with d.op0 == d.op1. If that didn't work, + retry with d.op0 != d.op1 as that is what testing has been done with. */ + if (maybe_retry) + { + rtx seq; + bool ok; + + extract_vec_perm_cst (&d, arg2); + d.op1 = gen_reg_rtx (d.vmode); + start_sequence (); + ok = ix86_expand_vec_perm_builtin_1 (&d); + seq = get_insns (); + end_sequence (); + if (ok) + { + emit_move_insn (d.op1, d.op0); + emit_insn (seq); + return d.target; + } + } + /* For compiler generated permutations, we should never got here, because the compiler should also be checking the ok hook. But since this is a builtin the user has access too, so don't abort. */ @@ -36588,6 +36612,19 @@ ix86_expand_vec_perm_builtin (tree exp) d.perm[8], d.perm[9], d.perm[10], d.perm[11], d.perm[12], d.perm[13], d.perm[14], d.perm[15]); break; + case 32: + sorry ("vector permutation " + "(%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d " + "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d)", + d.perm[0], d.perm[1], d.perm[2], d.perm[3], + d.perm[4], d.perm[5], d.perm[6], d.perm[7], + d.perm[8], d.perm[9], d.perm[10], d.perm[11], + d.perm[12], d.perm[13], d.perm[14], d.perm[15], + d.perm[16], d.perm[17], d.perm[18], d.perm[19], + d.perm[20], d.perm[21], d.perm[22], d.perm[23], + d.perm[24], d.perm[25], d.perm[26], d.perm[27], + d.perm[28], d.perm[29], d.perm[30], d.perm[31]); + break; default: gcc_unreachable (); } @@ -36599,6 +36636,7 @@ bool ix86_expand_vec_perm_const (rtx operands[4]) { struct expand_vec_perm_d d; + unsigned char perm[MAX_VECT_LEN]; int i, nelt, which; rtx sel; @@ -36614,6 +36652,7 @@ ix86_expand_vec_perm_const (rtx operands[4]) gcc_assert (GET_CODE (sel) == CONST_VECTOR); gcc_assert (XVECLEN (sel, 0) == nelt); + gcc_checking_assert (sizeof (d.perm) == sizeof (perm)); for (i = which = 0; i < nelt; ++i) { @@ -36622,6 +36661,7 @@ ix86_expand_vec_perm_const (rtx operands[4]) which |= (ei < nelt ? 1 : 2); d.perm[i] = ei; + perm[i] = ei; } switch (which) @@ -36653,7 +36693,32 @@ ix86_expand_vec_perm_const (rtx operands[4]) break; } - return ix86_expand_vec_perm_builtin_1 (&d); + if (ix86_expand_vec_perm_builtin_1 (&d)) + return true; + + /* If the mask says both arguments are needed, but they are the same, + the above tried to expand with d.op0 == d.op1. If that didn't work, + retry with d.op0 != d.op1 as that is what testing has been done with. */ + if (which == 3 && d.op0 == d.op1) + { + rtx seq; + bool ok; + + memcpy (d.perm, perm, sizeof (perm)); + d.op1 = gen_reg_rtx (d.vmode); + start_sequence (); + ok = ix86_expand_vec_perm_builtin_1 (&d); + seq = get_insns (); + end_sequence (); + if (ok) + { + emit_move_insn (d.op1, d.op0); + emit_insn (seq); + return true; + } + } + + return false; } /* Implement targetm.vectorize.builtin_vec_perm_ok. */ diff --git a/gcc/config/rx/rx.c b/gcc/config/rx/rx.c index d25175d5d0e..30574306982 100644 --- a/gcc/config/rx/rx.c +++ b/gcc/config/rx/rx.c @@ -637,7 +637,7 @@ rx_print_operand (FILE * file, rtx op, int letter) case 0xb: fprintf (file, "fintv"); break; case 0xc: fprintf (file, "intb"); break; default: - warning (0, "unreocgnized control register number: %d - using 'psw'", + warning (0, "unrecognized control register number: %d - using 'psw'", (int) INTVAL (op)); fprintf (file, "psw"); break; diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index a6601def3be..a6809ddaae3 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -1316,7 +1316,7 @@ sparc_expand_move (enum machine_mode mode, rtx *operands) && (mode == SFmode /* And any DF constant in integer registers. */ || (mode == DFmode - && (reload_completed || reload_in_progress)))) + && ! can_create_pseudo_p ()))) return false; operands[1] = force_const_mem (mode, operands[1]); @@ -1362,11 +1362,9 @@ static void sparc_emit_set_const32 (rtx op0, rtx op1) { enum machine_mode mode = GET_MODE (op0); - rtx temp; + rtx temp = op0; - if (reload_in_progress || reload_completed) - temp = op0; - else + if (can_create_pseudo_p ()) temp = gen_reg_rtx (mode); if (GET_CODE (op1) == CONST_INT) @@ -1739,11 +1737,9 @@ sparc_emit_set_const64_longway (rtx op0, rtx temp, unsigned HOST_WIDE_INT high_bits, unsigned HOST_WIDE_INT low_bits) { - rtx sub_temp; + rtx sub_temp = op0; - if (reload_in_progress || reload_completed) - sub_temp = op0; - else + if (can_create_pseudo_p ()) sub_temp = gen_reg_rtx (DImode); if ((high_bits & 0xfffffc00) != 0) @@ -1762,7 +1758,7 @@ sparc_emit_set_const64_longway (rtx op0, rtx temp, sub_temp = temp; } - if (!reload_in_progress && !reload_completed) + if (can_create_pseudo_p ()) { rtx temp2 = gen_reg_rtx (DImode); rtx temp3 = gen_reg_rtx (DImode); @@ -1970,7 +1966,7 @@ sparc_emit_set_const64 (rtx op0, rtx op1) && (GET_CODE (op0) == SUBREG || (REG_P (op0) && ! SPARC_FP_REG_P (REGNO (op0))))); - if (reload_in_progress || reload_completed) + if (! can_create_pseudo_p ()) temp = op0; if (GET_CODE (op1) != CONST_INT) @@ -3685,7 +3681,7 @@ sparc_legitimize_pic_address (rtx orig, rtx reg) if (reg == 0) { - gcc_assert (! reload_in_progress && ! reload_completed); + gcc_assert (can_create_pseudo_p ()); reg = gen_reg_rtx (Pmode); } @@ -3694,7 +3690,7 @@ sparc_legitimize_pic_address (rtx orig, rtx reg) /* If not during reload, allocate another temp reg here for loading in the address, so that these instructions can be optimized properly. */ - rtx temp_reg = ((reload_in_progress || reload_completed) + rtx temp_reg = (! can_create_pseudo_p () ? reg : gen_reg_rtx (Pmode)); /* Must put the SYMBOL_REF inside an UNSPEC here so that cse @@ -3753,7 +3749,7 @@ sparc_legitimize_pic_address (rtx orig, rtx reg) if (reg == 0) { - gcc_assert (! reload_in_progress && ! reload_completed); + gcc_assert (can_create_pseudo_p ()); reg = gen_reg_rtx (Pmode); } @@ -3766,7 +3762,7 @@ sparc_legitimize_pic_address (rtx orig, rtx reg) { if (SMALL_INT (offset)) return plus_constant (base, INTVAL (offset)); - else if (! reload_in_progress && ! reload_completed) + else if (can_create_pseudo_p ()) offset = force_reg (Pmode, offset); else /* If we reach here, then something is seriously wrong. */ @@ -7900,7 +7896,7 @@ memory_ok_for_ldd (rtx op) if (TARGET_ARCH32 && !mem_min_alignment (op, 8)) return 0; - if ((reload_in_progress || reload_completed) + if (! can_create_pseudo_p () && !strict_memory_address_p (Pmode, XEXP (op, 0))) return 0; } diff --git a/gcc/config/spu/spu.c b/gcc/config/spu/spu.c index 3baa2ebc925..734c2beec4c 100644 --- a/gcc/config/spu/spu.c +++ b/gcc/config/spu/spu.c @@ -6415,13 +6415,24 @@ spu_emit_vector_compare (enum rtx_code rcode, try_again = true; break; case NE: + case UNEQ: + case UNLE: + case UNLT: + case UNGE: + case UNGT: + case UNORDERED: /* Treat A != B as ~(A==B). */ { + enum rtx_code rev_code; enum insn_code nor_code; - rtx eq_rtx = spu_emit_vector_compare (EQ, op0, op1, dest_mode); + rtx rev_mask; + + rev_code = reverse_condition_maybe_unordered (rcode); + rev_mask = spu_emit_vector_compare (rev_code, op0, op1, dest_mode); + nor_code = optab_handler (one_cmpl_optab, dest_mode); gcc_assert (nor_code != CODE_FOR_nothing); - emit_insn (GEN_FCN (nor_code) (mask, eq_rtx)); + emit_insn (GEN_FCN (nor_code) (mask, rev_mask)); if (dmode != dest_mode) { rtx temp = gen_reg_rtx (dest_mode); @@ -6466,6 +6477,48 @@ spu_emit_vector_compare (enum rtx_code rcode, return mask; } break; + case LTGT: + /* Try LT OR GT */ + { + rtx lt_rtx, gt_rtx; + enum insn_code ior_code; + + lt_rtx = spu_emit_vector_compare (LT, op0, op1, dest_mode); + gt_rtx = spu_emit_vector_compare (GT, op0, op1, dest_mode); + + ior_code = optab_handler (ior_optab, dest_mode); + gcc_assert (ior_code != CODE_FOR_nothing); + emit_insn (GEN_FCN (ior_code) (mask, lt_rtx, gt_rtx)); + if (dmode != dest_mode) + { + rtx temp = gen_reg_rtx (dest_mode); + convert_move (temp, mask, 0); + return temp; + } + return mask; + } + break; + case ORDERED: + /* Implement as (A==A) & (B==B) */ + { + rtx a_rtx, b_rtx; + enum insn_code and_code; + + a_rtx = spu_emit_vector_compare (EQ, op0, op0, dest_mode); + b_rtx = spu_emit_vector_compare (EQ, op1, op1, dest_mode); + + and_code = optab_handler (and_optab, dest_mode); + gcc_assert (and_code != CODE_FOR_nothing); + emit_insn (GEN_FCN (and_code) (mask, a_rtx, b_rtx)); + if (dmode != dest_mode) + { + rtx temp = gen_reg_rtx (dest_mode); + convert_move (temp, mask, 0); + return temp; + } + return mask; + } + break; default: gcc_unreachable (); } diff --git a/gcc/configure b/gcc/configure index 32542dd455d..0540430dc20 100755 --- a/gcc/configure +++ b/gcc/configure @@ -10415,6 +10415,8 @@ $as_echo "#define HAVE_LANGINFO_CODESET 1" >>confdefs.h # We will need to find libiberty.h and ansidecl.h saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -I${srcdir} -I${srcdir}/../include" +saved_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS -I${srcdir} -I${srcdir}/../include" for ac_func in getenv atol asprintf sbrk abort atof getcwd getwd \ strsignal strstr strverscmp \ errno snprintf vsnprintf vasprintf malloc realloc calloc \ @@ -10719,6 +10721,127 @@ fi done +# g++ on Solaris 10+ defines _XOPEN_SOURCE=600, which hides the madvise() +# prototype. +if test "$ENABLE_BUILD_WITH_CXX" = "yes"; then : + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + for ac_func in madvise +do + ac_tr_decl=`$as_echo "HAVE_DECL_$ac_func" | $as_tr_cpp` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $ac_func is declared" >&5 +$as_echo_n "checking whether $ac_func is declared... " >&6; } +if { as_var=gcc_cv_have_decl_$ac_func; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#undef $ac_tr_decl +#define $ac_tr_decl 1 + + #include "ansidecl.h" + #include "system.h" + + +int +main () +{ +#ifndef $ac_func +char *(*pfn) = (char *(*)) $ac_func ; +#endif + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "gcc_cv_have_decl_$ac_func=yes" +else + eval "gcc_cv_have_decl_$ac_func=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +if eval "test \"`echo '$gcc_cv_have_decl_'$ac_func`\" = yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } ; cat >>confdefs.h <<_ACEOF +#define $ac_tr_decl 1 +_ACEOF + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } ; cat >>confdefs.h <<_ACEOF +#define $ac_tr_decl 0 +_ACEOF + +fi + +done + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + for ac_func in madvise +do + ac_tr_decl=`$as_echo "HAVE_DECL_$ac_func" | $as_tr_cpp` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $ac_func is declared" >&5 +$as_echo_n "checking whether $ac_func is declared... " >&6; } +if { as_var=gcc_cv_have_decl_$ac_func; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#undef $ac_tr_decl +#define $ac_tr_decl 1 + + #include "ansidecl.h" + #include "system.h" + + +int +main () +{ +#ifndef $ac_func +char *(*pfn) = (char *(*)) $ac_func ; +#endif + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "gcc_cv_have_decl_$ac_func=yes" +else + eval "gcc_cv_have_decl_$ac_func=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +if eval "test \"`echo '$gcc_cv_have_decl_'$ac_func`\" = yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } ; cat >>confdefs.h <<_ACEOF +#define $ac_tr_decl 1 +_ACEOF + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } ; cat >>confdefs.h <<_ACEOF +#define $ac_tr_decl 0 +_ACEOF + +fi + +done + + +fi + # More time-related stuff. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct tms" >&5 $as_echo_n "checking for struct tms... " >&6; } @@ -10795,8 +10918,9 @@ $as_echo "#define HAVE_CLOCK_T 1" >>confdefs.h fi -# Restore CFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. +# Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. CFLAGS="$saved_CFLAGS" +CXXFLAGS="$saved_CXXFLAGS" # Check whether --enable-initfini-array was given. if test "${enable_initfini_array+set}" = set; then : @@ -17948,7 +18072,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 17951 "configure" +#line 18075 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -18054,7 +18178,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 18057 "configure" +#line 18181 "configure" #include "confdefs.h" #if HAVE_DLFCN_H diff --git a/gcc/configure.ac b/gcc/configure.ac index d19ca077b2e..d63acea68f5 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -1094,6 +1094,8 @@ AM_LANGINFO_CODESET # We will need to find libiberty.h and ansidecl.h saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -I${srcdir} -I${srcdir}/../include" +saved_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS -I${srcdir} -I${srcdir}/../include" gcc_AC_CHECK_DECLS(getenv atol asprintf sbrk abort atof getcwd getwd \ strsignal strstr strverscmp \ errno snprintf vsnprintf vasprintf malloc realloc calloc \ @@ -1146,6 +1148,21 @@ gcc_AC_CHECK_DECLS(sigaltstack, , ,[ #include <signal.h> ]) +# g++ on Solaris 10+ defines _XOPEN_SOURCE=600, which hides the madvise() +# prototype. +AS_IF([test "$ENABLE_BUILD_WITH_CXX" = "yes"], + [AC_LANG_PUSH([C++]) + gcc_AC_CHECK_DECLS(madvise, , ,[ + #include "ansidecl.h" + #include "system.h" + ]) + AC_LANG_POP([C++])], + [gcc_AC_CHECK_DECLS(madvise, , ,[ + #include "ansidecl.h" + #include "system.h" + ]) +]) + # More time-related stuff. AC_CACHE_CHECK(for struct tms, ac_cv_struct_tms, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @@ -1172,8 +1189,9 @@ if test $gcc_cv_type_clock_t = yes; then [Define if <time.h> defines clock_t.]) fi -# Restore CFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. +# Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. CFLAGS="$saved_CFLAGS" +CXXFLAGS="$saved_CXXFLAGS" gcc_AC_INITFINI_ARRAY diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 402f5b56c34..a32a7b9fca0 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,29 @@ +2011-10-19 Paolo Carlini <paolo.carlini@oracle.com> + + PR c++/13657 + * class.c (instantiate_type): Fix error message. + +2011-10-19 Jason Merrill <jason@redhat.com> + + PR c++/50793 + * tree.c (bot_manip): Propagate AGGR_INIT_ZERO_FIRST. + +2011-10-19 Roland Stigge <stigge@antcom.de> + + PR translation/49704 + * semantics.c (potential_constant_expression_1): Use "AST" instead of + "ast" in sorry message. + +2011-10-19 Paolo Carlini <paolo.carlini@oracle.com> + + PR c++/38761 + PR c++/40872 + * decl.c (duplicate_decls, make_typename_type, grokdeclarator): Use + G_() in error message strings to facilitate translation. + * semantics.c (finish_id_expression): Likewise. + * parser.c (cp_parser_nested_name_specifier_opt, + cp_parser_parameter_declaration): Likewise. + 2011-10-18 Jason Merrill <jason@redhat.com> PR c++/50531 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index c8efc7e3b71..ee6ca049d3f 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -6867,8 +6867,8 @@ instantiate_type (tree lhstype, tree rhs, tsubst_flags_t flags) else { if (flags & tf_error) - error ("argument of type %qT does not match %qT", - TREE_TYPE (rhs), lhstype); + error ("cannot convert %qE from type %qT to type %qT", + rhs, TREE_TYPE (rhs), lhstype); return error_mark_node; } } diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 4b5b6c81226..a21cf461aab 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1542,8 +1542,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) error_at (DECL_SOURCE_LOCATION (newdecl), errmsg, newdecl); if (DECL_NAME (olddecl) != NULL_TREE) error ((DECL_INITIAL (olddecl) && namespace_bindings_p ()) - ? "%q+#D previously defined here" - : "%q+#D previously declared here", olddecl); + ? G_("%q+#D previously defined here") + : G_("%q+#D previously declared here"), olddecl); return error_mark_node; } else if (TREE_CODE (olddecl) == FUNCTION_DECL @@ -3236,8 +3236,8 @@ make_typename_type (tree context, tree name, enum tag_types tag_type, if (!t) { if (complain & tf_error) - error (want_template ? "no class template named %q#T in %q#T" - : "no type named %q#T in %q#T", name, context); + error (want_template ? G_("no class template named %q#T in %q#T") + : G_("no type named %q#T in %q#T"), name, context); return error_mark_node; } @@ -9143,13 +9143,13 @@ grokdeclarator (const cp_declarator *declarator, virtual. A constructor may not be static. */ if (staticp == 2) error ((flags == DTOR_FLAG) - ? "destructor cannot be static member function" - : "constructor cannot be static member function"); + ? G_("destructor cannot be static member function") + : G_("constructor cannot be static member function")); if (memfn_quals) { error ((flags == DTOR_FLAG) - ? "destructors may not be cv-qualified" - : "constructors may not be cv-qualified"); + ? G_("destructors may not be cv-qualified") + : G_("constructors may not be cv-qualified")); memfn_quals = TYPE_UNQUALIFIED; } @@ -9502,8 +9502,10 @@ grokdeclarator (const cp_declarator *declarator, && (!friendp || funcdef_flag)) { error (funcdef_flag - ? "cannot define member function %<%T::%s%> within %<%T%>" - : "cannot declare member function %<%T::%s%> within %<%T%>", + ? G_("cannot define member function %<%T::%s%> " + "within %<%T%>") + : G_("cannot declare member function %<%T::%s%> " + "within %<%T%>"), ctype, name, current_class_type); return error_mark_node; } @@ -10223,8 +10225,8 @@ grokdeclarator (const cp_declarator *declarator, || sfk == sfk_destructor) { error (funcdef_flag - ? "%qs defined in a non-class scope" - : "%qs declared in a non-class scope", name); + ? G_("%qs defined in a non-class scope") + : G_("%qs declared in a non-class scope"), name); sfk = sfk_none; } } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index a237b870edb..8d138fb7b53 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -4752,8 +4752,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope)) == TEMPLATE_ID_EXPR))) permerror (input_location, TYPE_P (new_scope) - ? "%qT is not a template" - : "%qD is not a template", + ? G_("%qT is not a template") + : G_("%qD is not a template"), new_scope); /* If it is a class scope, try to complete it; we are about to be looking up names inside the class. */ @@ -16810,17 +16810,20 @@ cp_parser_parameter_declaration (cp_parser *parser, if (id_declarator && id_declarator->kind == cdk_id) error_at (declarator_token_start->location, - template_parm_p - ? "template parameter pack %qD" - " cannot have a default argument" - : "parameter pack %qD cannot have a default argument", + template_parm_p + ? G_("template parameter pack %qD " + "cannot have a default argument") + : G_("parameter pack %qD cannot have " + "a default argument"), id_declarator->u.id.unqualified_name); else error_at (declarator_token_start->location, - template_parm_p - ? "template parameter pack cannot have a default argument" - : "parameter pack cannot have a default argument"); - + template_parm_p + ? G_("template parameter pack cannot have " + "a default argument") + : G_("parameter pack cannot have a " + "default argument")); + default_argument = NULL_TREE; } } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 3e847fa5ce5..42195be4a81 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-objc.h" #include "tree-inline.h" #include "tree-mudflap.h" +#include "intl.h" #include "toplev.h" #include "flags.h" #include "output.h" @@ -2985,8 +2986,8 @@ finish_id_expression (tree id_expression, else { error (TREE_CODE (decl) == VAR_DECL - ? "use of %<auto%> variable from containing function" - : "use of parameter from containing function"); + ? G_("use of %<auto%> variable from containing function") + : G_("use of parameter from containing function")); error (" %q+#D declared here", decl); return error_mark_node; } @@ -8340,7 +8341,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags) return false; default: - sorry ("unexpected ast of kind %s", tree_code_name[TREE_CODE (t)]); + sorry ("unexpected AST of kind %s", tree_code_name[TREE_CODE (t)]); gcc_unreachable(); return false; } diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 75aa2650300..d023eb86cb8 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -1879,8 +1879,12 @@ bot_manip (tree* tp, int* walk_subtrees, void* data) tree u; if (TREE_CODE (TREE_OPERAND (t, 1)) == AGGR_INIT_EXPR) - u = build_cplus_new (TREE_TYPE (t), TREE_OPERAND (t, 1), - tf_warning_or_error); + { + u = build_cplus_new (TREE_TYPE (t), TREE_OPERAND (t, 1), + tf_warning_or_error); + if (AGGR_INIT_ZERO_FIRST (TREE_OPERAND (t, 1))) + AGGR_INIT_ZERO_FIRST (TREE_OPERAND (u, 1)) = true; + } else u = build_target_expr_with_type (TREE_OPERAND (t, 1), TREE_TYPE (t), tf_warning_or_error); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index c20219ab09d..0d33667942b 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -9087,8 +9087,7 @@ compile time for more complete debug information. If this is set too low, value expressions that are available and could be represented in debug information may end up not being used; setting this higher may enable the compiler to find more complex debug expressions, but compile -time may grow exponentially, and even then, it may fail to find more -usable expressions. The default is 10. +time and memory use may grow. The default is 12. @item min-nondebug-insn-uid Use uids starting at this parameter for nondebug insns. The range below diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index c11f666bfbf..e328201a82a 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -12502,7 +12502,8 @@ loc_descriptor (rtx rtl, enum machine_mode mode, legitimate to make the Dwarf info refer to the whole register which contains the given subreg. */ if (REG_P (SUBREG_REG (rtl)) && subreg_lowpart_p (rtl)) - loc_result = loc_descriptor (SUBREG_REG (rtl), mode, initialized); + loc_result = loc_descriptor (SUBREG_REG (rtl), + GET_MODE (SUBREG_REG (rtl)), initialized); else goto do_default; break; diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index f7a793f056c..a350ff29a38 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,10 @@ +2011-10-19 Janus Weil <janus@gcc.gnu.org> + + PR fortran/47023 + * check.c (gfc_check_sizeof): Reject procedures as argument of SIZEOF. + * intrinsinc.texi (SIZEOF): Document it. + (STORAGE_SIZE): Fix special characters. Fix line breaks. + 2011-10-18 Mikael Morin <mikael@gcc.gnu.org> PR fortran/50420 diff --git a/gcc/fortran/check.c b/gcc/fortran/check.c index eb8b3e1b2a2..bf4559203b1 100644 --- a/gcc/fortran/check.c +++ b/gcc/fortran/check.c @@ -3444,8 +3444,15 @@ gfc_check_size (gfc_expr *array, gfc_expr *dim, gfc_expr *kind) gfc_try -gfc_check_sizeof (gfc_expr *arg ATTRIBUTE_UNUSED) +gfc_check_sizeof (gfc_expr *arg) { + if (arg->ts.type == BT_PROCEDURE) + { + gfc_error ("'%s' argument of '%s' intrinsic at %L may not be a procedure", + gfc_current_intrinsic_arg[0]->name, gfc_current_intrinsic, + &arg->where); + return FAILURE; + } return SUCCESS; } diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi index 11f87a57308..084cd15ea6f 100644 --- a/gcc/fortran/intrinsic.texi +++ b/gcc/fortran/intrinsic.texi @@ -11459,7 +11459,8 @@ number of bytes occupied by the argument. If the argument has the to is returned. If the argument is of a derived type with @code{POINTER} or @code{ALLOCATABLE} components, the return value doesn't account for the sizes of the data pointed to by these components. If the argument is -polymorphic, the size according to the declared type is returned. +polymorphic, the size according to the declared type is returned. The argument +may not be a procedure or procedure pointer. @item @emph{Example}: @smallexample @@ -11816,8 +11817,10 @@ Inquiry function @end multitable @item @emph{Return Value}: -The result is a scalar integer with the kind type parameter specified by KIND (or default integer type if KIND is missing). The result value is the size expressed in bits for an element of an array that -has the dynamic type and type parameters of A. +The result is a scalar integer with the kind type parameter specified by KIND +(or default integer type if KIND is missing). The result value is the size +expressed in bits for an element of an array that has the dynamic type and type +parameters of A. @item @emph{See also}: @ref{C_SIZEOF}, @ref{SIZEOF} diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c index 2da99db9cf8..617a49348d7 100644 --- a/gcc/ggc-page.c +++ b/gcc/ggc-page.c @@ -1,6 +1,6 @@ /* "Bag-of-pages" garbage collector for the GNU compiler. Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, - 2010 Free Software Foundation, Inc. + 2010, 2011 Free Software Foundation, Inc. This file is part of GCC. @@ -50,7 +50,8 @@ along with GCC; see the file COPYING3. If not see #define USING_MALLOC_PAGE_GROUPS #endif -#if defined(HAVE_MADVISE) && defined(MADV_DONTNEED) && defined(USING_MMAP) +#if defined(HAVE_MADVISE) && HAVE_DECL_MADVISE && defined(MADV_DONTNEED) \ + && defined(USING_MMAP) # define USING_MADVISE #endif diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 7df7e694aa2..aa67d248c53 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -534,25 +534,22 @@ void gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr) { tree lhs; - tree tmp = NULL_TREE; /* Silence warning. */ gimple stmt, new_stmt; gimple_stmt_iterator i; gimple_seq stmts = gimple_seq_alloc(); struct gimplify_ctx gctx; - gimple last = NULL; - gimple laststore = NULL; + gimple last; + gimple laststore; tree reaching_vuse; stmt = gsi_stmt (*si_p); gcc_assert (is_gimple_call (stmt)); - lhs = gimple_call_lhs (stmt); - reaching_vuse = gimple_vuse (stmt); - push_gimplify_context (&gctx); gctx.into_ssa = gimple_in_ssa_p (cfun); + lhs = gimple_call_lhs (stmt); if (lhs == NULL_TREE) { gimplify_and_add (expr, &stmts); @@ -571,105 +568,83 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr) } } else - tmp = get_initialized_tmp_var (expr, &stmts, NULL); + { + tree tmp = get_initialized_tmp_var (expr, &stmts, NULL); + new_stmt = gimple_build_assign (lhs, tmp); + i = gsi_last (stmts); + gsi_insert_after_without_update (&i, new_stmt, + GSI_CONTINUE_LINKING); + } pop_gimplify_context (NULL); if (gimple_has_location (stmt)) annotate_all_with_location (stmts, gimple_location (stmt)); - /* The replacement can expose previously unreferenced variables. */ + /* First iterate over the replacement statements backward, assigning + virtual operands to their defining statements. */ + laststore = NULL; + for (i = gsi_last (stmts); !gsi_end_p (i); gsi_prev (&i)) + { + new_stmt = gsi_stmt (i); + if (gimple_assign_single_p (new_stmt) + && !is_gimple_reg (gimple_assign_lhs (new_stmt))) + { + tree vdef; + if (!laststore) + vdef = gimple_vdef (stmt); + else + vdef = make_ssa_name (gimple_vop (cfun), new_stmt); + gimple_set_vdef (new_stmt, vdef); + if (TREE_CODE (vdef) == SSA_NAME) + SSA_NAME_DEF_STMT (vdef) = new_stmt; + laststore = new_stmt; + } + } + + /* Second iterate over the statements forward, assigning virtual + operands to their uses. */ + last = NULL; + reaching_vuse = gimple_vuse (stmt); for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i)) { + /* Do not insert the last stmt in this loop but remember it + for replacing the original statement. */ if (last) { gsi_insert_before (si_p, last, GSI_NEW_STMT); gsi_next (si_p); } new_stmt = gsi_stmt (i); + /* The replacement can expose previously unreferenced variables. */ if (gimple_in_ssa_p (cfun)) find_new_referenced_vars (new_stmt); /* If the new statement possibly has a VUSE, update it with exact SSA name we know will reach this one. */ if (gimple_has_mem_ops (new_stmt)) - { - /* If we've also seen a previous store create a new VDEF for - the latter one, and make that the new reaching VUSE. */ - if (laststore) - { - reaching_vuse = make_ssa_name (gimple_vop (cfun), laststore); - gimple_set_vdef (laststore, reaching_vuse); - update_stmt (laststore); - laststore = NULL; - } - gimple_set_vuse (new_stmt, reaching_vuse); - gimple_set_modified (new_stmt, true); - } - if (gimple_assign_single_p (new_stmt) - && !is_gimple_reg (gimple_assign_lhs (new_stmt))) - { - laststore = new_stmt; - } + gimple_set_vuse (new_stmt, reaching_vuse); + gimple_set_modified (new_stmt, true); + if (gimple_vdef (new_stmt)) + reaching_vuse = gimple_vdef (new_stmt); last = new_stmt; } - if (lhs == NULL_TREE) + /* If the new sequence does not do a store release the virtual + definition of the original statement. */ + if (reaching_vuse + && reaching_vuse == gimple_vuse (stmt)) { - /* If we replace a call without LHS that has a VDEF and our new - sequence ends with a store we must make that store have the same - vdef in order not to break the sequencing. This can happen - for instance when folding memcpy calls into assignments. */ - if (gimple_vdef (stmt) && laststore) - { - gimple_set_vdef (laststore, gimple_vdef (stmt)); - if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME) - SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = laststore; - update_stmt (laststore); - } - else if (gimple_in_ssa_p (cfun)) + tree vdef = gimple_vdef (stmt); + if (vdef + && TREE_CODE (vdef) == SSA_NAME) { unlink_stmt_vdef (stmt); - release_defs (stmt); - } - new_stmt = last; - } - else - { - if (last) - { - gsi_insert_before (si_p, last, GSI_NEW_STMT); - gsi_next (si_p); - } - if (laststore && is_gimple_reg (lhs)) - { - gimple_set_vdef (laststore, gimple_vdef (stmt)); - update_stmt (laststore); - if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME) - SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = laststore; - laststore = NULL; - } - else if (laststore) - { - reaching_vuse = make_ssa_name (gimple_vop (cfun), laststore); - gimple_set_vdef (laststore, reaching_vuse); - update_stmt (laststore); - laststore = NULL; - } - new_stmt = gimple_build_assign (lhs, tmp); - if (!is_gimple_reg (tmp)) - gimple_set_vuse (new_stmt, reaching_vuse); - if (!is_gimple_reg (lhs)) - { - gimple_set_vdef (new_stmt, gimple_vdef (stmt)); - if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME) - SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = new_stmt; + release_ssa_name (vdef); } - else if (reaching_vuse == gimple_vuse (stmt)) - unlink_stmt_vdef (stmt); } - gimple_set_location (new_stmt, gimple_location (stmt)); - gsi_replace (si_p, new_stmt, false); + /* Finally replace rhe original statement with the last. */ + gsi_replace (si_p, last, false); } /* Return the string length, maximum string length or maximum value of diff --git a/gcc/plugin.c b/gcc/plugin.c index 3906bffc68b..63d1cae4ede 100644 --- a/gcc/plugin.c +++ b/gcc/plugin.c @@ -1,5 +1,5 @@ /* Support for GCC plugin mechanism. - Copyright (C) 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. This file is part of GCC. @@ -149,7 +149,7 @@ add_new_plugin (const char* plugin_name) plugin_name, ".so", NULL); if (access (plugin_name, R_OK)) fatal_error - ("inacessible plugin file %s expanded from short plugin name %s: %m", + ("inaccessible plugin file %s expanded from short plugin name %s: %m", plugin_name, base_name); } else diff --git a/gcc/profile.c b/gcc/profile.c index 893e2cd17c7..41108394e98 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -437,6 +437,39 @@ read_profile_edge_counts (gcov_type *exec_counts) return num_edges; } +#define OVERLAP_BASE 10000 + +/* Compare the static estimated profile to the actual profile, and + return the "degree of overlap" measure between them. + + Degree of overlap is a number between 0 and OVERLAP_BASE. It is + the sum of each basic block's minimum relative weights between + two profiles. And overlap of OVERLAP_BASE means two profiles are + identical. */ + +static int +compute_frequency_overlap (void) +{ + gcov_type count_total = 0, freq_total = 0; + int overlap = 0; + basic_block bb; + + FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) + { + count_total += bb->count; + freq_total += bb->frequency; + } + + if (count_total == 0 || freq_total == 0) + return 0; + + FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) + overlap += MIN (bb->count * OVERLAP_BASE / count_total, + bb->frequency * OVERLAP_BASE / freq_total); + + return overlap; +} + /* Compute the branch probabilities for the various branches. Annotate them accordingly. @@ -607,7 +640,13 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum) } } if (dump_file) - dump_flow_info (dump_file, dump_flags); + { + int overlap = compute_frequency_overlap (); + dump_flow_info (dump_file, dump_flags); + fprintf (dump_file, "Static profile overlap: %d.%d%%\n", + overlap / (OVERLAP_BASE / 100), + overlap % (OVERLAP_BASE / 100)); + } total_num_passes += passes; if (dump_file) diff --git a/gcc/regrename.c b/gcc/regrename.c index bfff1033018..1823558f451 100644 --- a/gcc/regrename.c +++ b/gcc/regrename.c @@ -39,6 +39,8 @@ #include "tree-pass.h" #include "df.h" #include "target.h" +#include "emit-rtl.h" +#include "regrename.h" /* This file implements the RTL register renaming pass of the compiler. It is a semi-local pass whose goal is to maximize the usage of the register file @@ -73,47 +75,6 @@ #error "Use a different bitmap implementation for untracked_operands." #endif -/* We keep linked lists of DU_HEAD structures, each of which describes - a chain of occurrences of a reg. */ -struct du_head -{ - /* The next chain. */ - struct du_head *next_chain; - /* The first and last elements of this chain. */ - struct du_chain *first, *last; - /* Describe the register being tracked, register number and count. */ - unsigned regno; - int nregs; - - /* A unique id to be used as an index into the conflicts bitmaps. */ - unsigned id; - /* A bitmap to record conflicts with other chains. */ - bitmap_head conflicts; - /* Conflicts with untracked hard registers. */ - HARD_REG_SET hard_conflicts; - - /* Nonzero if the chain crosses a call. */ - unsigned int need_caller_save_reg:1; - /* Nonzero if the register is used in a way that prevents renaming, - such as the SET_DEST of a CALL_INSN or an asm operand that used - to be a hard register. */ - unsigned int cannot_rename:1; -}; - -/* This struct describes a single occurrence of a register. */ -struct du_chain -{ - /* Links to the next occurrence of the register. */ - struct du_chain *next_use; - - /* The insn where the register appears. */ - rtx insn; - /* The location inside the insn. */ - rtx *loc; - /* The register class required by the insn at this location. */ - ENUM_BITFIELD(reg_class) cl : 16; -}; - enum scan_actions { terminate_write, @@ -144,15 +105,14 @@ static int this_tick = 0; static struct obstack rename_obstack; -static void do_replace (struct du_head *, int); +/* If nonnull, the code calling into the register renamer requested + information about insn operands, and we store it here. */ +VEC(insn_rr_info, heap) *insn_rr; + static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions, enum op_type); static bool build_def_use (basic_block); -typedef struct du_head *du_head_p; -DEF_VEC_P (du_head_p); -DEF_VEC_ALLOC_P (du_head_p, heap); - /* The id to be given to the next opened chain. */ static unsigned current_id; @@ -173,10 +133,15 @@ static HARD_REG_SET live_in_chains; between this and live_in_chains is empty. */ static HARD_REG_SET live_hard_regs; +/* Set while scanning RTL if INSN_RR is nonnull, i.e. if the current analysis + is for a caller that requires operand data. Used in + record_operand_use. */ +static operand_rr_info *cur_operand; + /* Return the chain corresponding to id number ID. Take into account that chains may have been merged. */ -static du_head_p -chain_from_id (unsigned int id) +du_head_p +regrename_chain_from_id (unsigned int id) { du_head_p first_chain = VEC_index (du_head_p, id_to_chain, id); du_head_p chain = first_chain; @@ -237,6 +202,19 @@ mark_conflict (struct du_head *chains, unsigned id) } } +/* Examine cur_operand, and if it is nonnull, record information about the + use THIS_DU which is part of the chain HEAD. */ + +static void +record_operand_use (struct du_head *head, struct du_chain *this_du) +{ + if (cur_operand == NULL) + return; + gcc_assert (cur_operand->n_chains < MAX_REGS_PER_ADDRESS); + cur_operand->heads[cur_operand->n_chains] = head; + cur_operand->chains[cur_operand->n_chains++] = this_du; +} + /* Create a new chain for THIS_NREGS registers starting at THIS_REGNO, and record its occurrence in *LOC, which is being written to in INSN. This access requires a register of class CL. */ @@ -299,6 +277,7 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc, this_du->loc = loc; this_du->insn = insn; this_du->cl = cl; + record_operand_use (head, this_du); return head; } @@ -313,7 +292,7 @@ merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head) IOR_HARD_REG_SET (*pset, head->hard_conflicts); EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi) { - du_head_p other = chain_from_id (i); + du_head_p other = regrename_chain_from_id (i); unsigned j = other->nregs; gcc_assert (other != head); while (j-- > 0) @@ -368,6 +347,69 @@ check_new_reg_p (int reg ATTRIBUTE_UNUSED, int new_reg, return true; } +/* For the chain THIS_HEAD, compute and return the best register to + rename to. SUPER_CLASS is the superunion of register classes in + the chain. UNAVAILABLE is a set of registers that cannot be used. + OLD_REG is the register currently used for the chain. */ + +int +find_best_rename_reg (du_head_p this_head, enum reg_class super_class, + HARD_REG_SET *unavailable, int old_reg) +{ + bool has_preferred_class; + enum reg_class preferred_class; + int pass; + int best_new_reg = old_reg; + + /* Further narrow the set of registers we can use for renaming. + If the chain needs a call-saved register, mark the call-used + registers as unavailable. */ + if (this_head->need_caller_save_reg) + IOR_HARD_REG_SET (*unavailable, call_used_reg_set); + + /* Mark registers that overlap this chain's lifetime as unavailable. */ + merge_overlapping_regs (unavailable, this_head); + + /* Compute preferred rename class of super union of all the classes + in the chain. */ + preferred_class + = (enum reg_class) targetm.preferred_rename_class (super_class); + + /* If PREFERRED_CLASS is not NO_REGS, we iterate in the first pass + over registers that belong to PREFERRED_CLASS and try to find the + best register within the class. If that failed, we iterate in + the second pass over registers that don't belong to the class. + If PREFERRED_CLASS is NO_REGS, we iterate over all registers in + ascending order without any preference. */ + has_preferred_class = (preferred_class != NO_REGS); + for (pass = (has_preferred_class ? 0 : 1); pass < 2; pass++) + { + int new_reg; + for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++) + { + if (has_preferred_class + && (pass == 0) + != TEST_HARD_REG_BIT (reg_class_contents[preferred_class], + new_reg)) + continue; + + /* In the first pass, we force the renaming of registers that + don't belong to PREFERRED_CLASS to registers that do, even + though the latters were used not very long ago. */ + if (check_new_reg_p (old_reg, new_reg, this_head, + *unavailable) + && ((pass == 0 + && !TEST_HARD_REG_BIT (reg_class_contents[preferred_class], + best_new_reg)) + || tick[best_new_reg] > tick[new_reg])) + best_new_reg = new_reg; + } + if (pass == 0 && best_new_reg != old_reg) + break; + } + return best_new_reg; +} + /* Perform register renaming on the current function. */ static void rename_chains (void) @@ -390,22 +432,16 @@ rename_chains (void) FOR_EACH_VEC_ELT (du_head_p, id_to_chain, i, this_head) { - int new_reg, best_new_reg, best_nregs; + int best_new_reg; int n_uses; struct du_chain *tmp; HARD_REG_SET this_unavailable; int reg = this_head->regno; - int pass; enum reg_class super_class = NO_REGS; - enum reg_class preferred_class; - bool has_preferred_class; if (this_head->cannot_rename) continue; - best_new_reg = reg; - best_nregs = this_head->nregs; - if (fixed_regs[reg] || global_regs[reg] #if !HARD_FRAME_POINTER_IS_FRAME_POINTER || (frame_pointer_needed && reg == HARD_FRAME_POINTER_REGNUM) @@ -437,56 +473,8 @@ rename_chains (void) if (n_uses < 2) continue; - /* Further narrow the set of registers we can use for renaming. - If the chain needs a call-saved register, mark the call-used - registers as unavailable. */ - if (this_head->need_caller_save_reg) - IOR_HARD_REG_SET (this_unavailable, call_used_reg_set); - - /* And mark registers that overlap its lifetime as unavailable. */ - merge_overlapping_regs (&this_unavailable, this_head); - - /* Compute preferred rename class of super union of all the classes - in the chain. */ - preferred_class - = (enum reg_class) targetm.preferred_rename_class (super_class); - - /* If PREFERRED_CLASS is not NO_REGS, we iterate in the first pass - over registers that belong to PREFERRED_CLASS and try to find the - best register within the class. If that failed, we iterate in - the second pass over registers that don't belong to the class. - If PREFERRED_CLASS is NO_REGS, we iterate over all registers in - ascending order without any preference. */ - has_preferred_class = (preferred_class != NO_REGS); - for (pass = (has_preferred_class ? 0 : 1); pass < 2; pass++) - { - for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++) - { - if (has_preferred_class - && ((pass == 0) != TEST_HARD_REG_BIT - (reg_class_contents[preferred_class], new_reg))) - continue; - - /* In the first pass, we force the renaming of registers that - don't belong to PREFERRED_CLASS to registers that do, even - though the latters were used not very long ago. */ - if (check_new_reg_p (reg, new_reg, this_head, - this_unavailable) - && ((pass == 0 - && (!TEST_HARD_REG_BIT - (reg_class_contents[preferred_class], - best_new_reg))) - || tick[best_new_reg] > tick[new_reg])) - { - enum machine_mode mode - = GET_MODE (*this_head->first->loc); - best_new_reg = new_reg; - best_nregs = hard_regno_nregs[new_reg][mode]; - } - } - if (pass == 0 && best_new_reg != reg) - break; - } + best_new_reg = find_best_rename_reg (this_head, super_class, + &this_unavailable, reg); if (dump_file) { @@ -507,9 +495,7 @@ rename_chains (void) if (dump_file) fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]); - do_replace (this_head, best_new_reg); - this_head->regno = best_new_reg; - this_head->nregs = best_nregs; + regrename_do_replace (this_head, best_new_reg); tick[best_new_reg] = ++this_tick; df_set_regs_ever_live (best_new_reg, true); } @@ -675,8 +661,8 @@ merge_chains (du_head_p c1, du_head_p c2) /* Analyze the current function and build chains for renaming. */ -static void -regrename_analyze (void) +void +regrename_analyze (bitmap bb_mask) { struct bb_rename_info *rename_info; int i; @@ -694,7 +680,10 @@ regrename_analyze (void) { struct bb_rename_info *ri = rename_info + i; ri->bb = bb; - bb->aux = ri; + if (bb_mask != NULL && !bitmap_bit_p (bb_mask, bb->index)) + bb->aux = NULL; + else + bb->aux = ri; i++; } @@ -735,6 +724,16 @@ regrename_analyze (void) current_id = old_length; bitmap_clear (&this_info->incoming_open_chains_set); open_chains = NULL; + if (insn_rr != NULL) + { + rtx insn; + FOR_BB_INSNS (bb1, insn) + { + insn_rr_info *p = VEC_index (insn_rr_info, insn_rr, + INSN_UID (insn)); + p->op_info = NULL; + } + } continue; } @@ -798,7 +797,7 @@ regrename_analyze (void) { edge e; edge_iterator ei; - struct du_head *chain = chain_from_id (j); + struct du_head *chain = regrename_chain_from_id (j); int n_preds_used = 0, n_preds_joined = 0; FOR_EACH_EDGE (e, ei, bb->preds) @@ -825,7 +824,7 @@ regrename_analyze (void) EXECUTE_IF_SET_IN_BITMAP (&src_ri->open_chains_set, 0, k, bi2) { - struct du_head *outgoing_chain = chain_from_id (k); + struct du_head *outgoing_chain = regrename_chain_from_id (k); if (outgoing_chain->regno == chain->regno && outgoing_chain->nregs == chain->nregs) @@ -863,7 +862,7 @@ regrename_analyze (void) { edge e; edge_iterator ei; - struct du_head *chain = chain_from_id (j); + struct du_head *chain = regrename_chain_from_id (j); int n_succs_used = 0, n_succs_joined = 0; FOR_EACH_EDGE (e, ei, bb->succs) @@ -888,7 +887,7 @@ regrename_analyze (void) EXECUTE_IF_SET_IN_BITMAP (&dest_ri->incoming_open_chains_set, 0, k, bi2) { - struct du_head *incoming_chain = chain_from_id (k); + struct du_head *incoming_chain = regrename_chain_from_id (k); if (incoming_chain->regno == chain->regno && incoming_chain->nregs == chain->nregs) @@ -928,11 +927,12 @@ regrename_analyze (void) bb->aux = NULL; } -static void -do_replace (struct du_head *head, int reg) +void +regrename_do_replace (struct du_head *head, int reg) { struct du_chain *chain; unsigned int base_regno = head->regno; + enum machine_mode mode; for (chain = head->first; chain; chain = chain->next_use) { @@ -953,6 +953,10 @@ do_replace (struct du_head *head, int reg) df_insn_rescan (chain->insn); } + + mode = GET_MODE (*head->first->loc); + head->regno = reg; + head->nregs = hard_regno_nregs[reg][mode]; } @@ -1106,6 +1110,7 @@ scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action, head->first = this_du; else head->last->next_use = this_du; + record_operand_use (head, this_du); head->last = this_du; } /* Avoid adding the same location in a DEBUG_INSN multiple times, @@ -1468,10 +1473,11 @@ restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups) /* For each output operand of INSN, call scan_rtx to create a new open chain. Do this only for normal or earlyclobber outputs, - depending on EARLYCLOBBER. */ + depending on EARLYCLOBBER. If INSN_INFO is nonnull, use it to + record information about the operands in the insn. */ static void -record_out_operands (rtx insn, bool earlyclobber) +record_out_operands (rtx insn, bool earlyclobber, insn_rr_info *insn_info) { int n_ops = recog_data.n_operands; int alt = which_alternative; @@ -1493,6 +1499,9 @@ record_out_operands (rtx insn, bool earlyclobber) || recog_op_alt[opn][alt].earlyclobber != earlyclobber) continue; + if (insn_info) + cur_operand = insn_info->op_info + i; + prev_open = open_chains; scan_rtx (insn, loc, cl, mark_write, OP_OUT); @@ -1510,6 +1519,7 @@ record_out_operands (rtx insn, bool earlyclobber) open_chains->cannot_rename = 1; } } + cur_operand = NULL; } /* Build def/use chain. */ @@ -1535,6 +1545,7 @@ build_def_use (basic_block bb) int predicated; enum rtx_code set_code = SET; enum rtx_code clobber_code = CLOBBER; + insn_rr_info *insn_info = NULL; /* Process the insn, determining its effect on the def-use chains and live hard registers. We perform the following @@ -1567,6 +1578,15 @@ build_def_use (basic_block bb) n_ops = recog_data.n_operands; untracked_operands = 0; + if (insn_rr != NULL) + { + insn_info = VEC_index (insn_rr_info, insn_rr, INSN_UID (insn)); + insn_info->op_info = XOBNEWVEC (&rename_obstack, operand_rr_info, + recog_data.n_operands); + memset (insn_info->op_info, 0, + sizeof (operand_rr_info) * recog_data.n_operands); + } + /* Simplify the code below by rewriting things to reflect matching constraints. Also promote OP_OUT to OP_INOUT in predicated instructions, but only for register operands @@ -1625,7 +1645,7 @@ build_def_use (basic_block bb) /* Step 1b: Begin new chains for earlyclobbered writes inside operands. */ - record_out_operands (insn, true); + record_out_operands (insn, true, insn_info); /* Step 2: Mark chains for which we have reads outside operands as unrenamable. @@ -1674,11 +1694,14 @@ build_def_use (basic_block bb) || untracked_operands & (1 << opn)) continue; + if (insn_info) + cur_operand = i == opn ? insn_info->op_info + i : NULL; if (recog_op_alt[opn][alt].is_address) scan_rtx_address (insn, loc, cl, mark_read, VOIDmode); else scan_rtx (insn, loc, cl, mark_read, type); } + cur_operand = NULL; /* Step 3B: Record updates for regs in REG_INC notes, and source regs in REG_FRAME_RELATED_EXPR notes. */ @@ -1729,7 +1752,7 @@ build_def_use (basic_block bb) restore_operands (insn, n_ops, old_operands, old_dups); /* Step 6b: Begin new chains for writes inside operands. */ - record_out_operands (insn, false); + record_out_operands (insn, false, insn_info); /* Step 6c: Record destination regs in REG_FRAME_RELATED_EXPR notes for update. */ @@ -1766,6 +1789,26 @@ build_def_use (basic_block bb) return true; } +/* Initialize the register renamer. If INSN_INFO is true, ensure that + insn_rr is nonnull. */ +void +regrename_init (bool insn_info) +{ + gcc_obstack_init (&rename_obstack); + insn_rr = NULL; + if (insn_info) + VEC_safe_grow_cleared (insn_rr_info, heap, insn_rr, get_max_uid ()); +} + +/* Free all global data used by the register renamer. */ +void +regrename_finish (void) +{ + VEC_free (insn_rr_info, heap, insn_rr); + free_chain_data (); + obstack_free (&rename_obstack, NULL); +} + /* Perform register renaming on the current function. */ static unsigned int @@ -1776,14 +1819,13 @@ regrename_optimize (void) df_analyze (); df_set_flags (DF_DEFER_INSN_RESCAN); - gcc_obstack_init (&rename_obstack); + regrename_init (false); - regrename_analyze (); + regrename_analyze (NULL); rename_chains (); - free_chain_data (); - obstack_free (&rename_obstack, NULL); + regrename_finish (); return 0; } diff --git a/gcc/regrename.h b/gcc/regrename.h new file mode 100644 index 00000000000..f3969a14fc2 --- /dev/null +++ b/gcc/regrename.h @@ -0,0 +1,101 @@ +/* This file contains definitions for the register renamer. + Copyright (C) 2011 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_REGRENAME_H +#define GCC_REGRENAME_H + +/* We keep linked lists of DU_HEAD structures, each of which describes + a chain of occurrences of a reg. */ +struct du_head +{ + /* The next chain. */ + struct du_head *next_chain; + /* The first and last elements of this chain. */ + struct du_chain *first, *last; + /* Describes the register being tracked. */ + unsigned regno; + int nregs; + + /* A unique id to be used as an index into the conflicts bitmaps. */ + unsigned id; + /* A bitmap to record conflicts with other chains. */ + bitmap_head conflicts; + /* Conflicts with untracked hard registers. */ + HARD_REG_SET hard_conflicts; + + /* Nonzero if the chain crosses a call. */ + unsigned int need_caller_save_reg:1; + /* Nonzero if the register is used in a way that prevents renaming, + such as the SET_DEST of a CALL_INSN or an asm operand that used + to be a hard register. */ + unsigned int cannot_rename:1; +}; + +typedef struct du_head *du_head_p; +DEF_VEC_P (du_head_p); +DEF_VEC_ALLOC_P (du_head_p, heap); + +/* This struct describes a single occurrence of a register. */ +struct du_chain +{ + /* Links to the next occurrence of the register. */ + struct du_chain *next_use; + + /* The insn where the register appears. */ + rtx insn; + /* The location inside the insn. */ + rtx *loc; + /* The register class required by the insn at this location. */ + ENUM_BITFIELD(reg_class) cl : 16; +}; + +/* This struct describes data gathered during regrename_analyze about + a single operand of an insn. */ +typedef struct +{ + /* The number of chains recorded for this operand. */ + int n_chains; + /* Holds either the chain for the operand itself, or for the registers in + a memory operand. */ + struct du_chain *chains[MAX_REGS_PER_ADDRESS]; + struct du_head *heads[MAX_REGS_PER_ADDRESS]; +} operand_rr_info; + +/* A struct to hold a vector of operand_rr_info structures describing the + operands of an insn. */ +typedef struct +{ + operand_rr_info *op_info; +} insn_rr_info; + +DEF_VEC_O (insn_rr_info); +DEF_VEC_ALLOC_O (insn_rr_info, heap); + +extern VEC(insn_rr_info, heap) *insn_rr; + +extern void regrename_init (bool); +extern void regrename_finish (void); +extern void regrename_analyze (bitmap); +extern du_head_p regrename_chain_from_id (unsigned int); +extern int find_best_rename_reg (du_head_p, enum reg_class, HARD_REG_SET *, + int); +extern void regrename_do_replace (du_head_p, int); + +#endif diff --git a/gcc/rtl.h b/gcc/rtl.h index f13485e200a..1ffc7c26d7f 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -265,7 +265,8 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), when we access a component. 1 in a CALL_INSN if it is a sibling call. 1 in a SET that is for a return. - In a CODE_LABEL, part of the two-bit alternate entry field. */ + In a CODE_LABEL, part of the two-bit alternate entry field. + 1 in a CONCAT is VAL_EXPR_IS_COPIED in var-tracking.c. */ unsigned int jump : 1; /* In a CODE_LABEL, part of the two-bit alternate entry field. 1 in a MEM if it cannot trap. @@ -278,7 +279,9 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), constants pool. 1 in a CALL_INSN logically equivalent to ECF_CONST and TREE_READONLY. 1 in a NOTE, or EXPR_LIST for a const call. - 1 in a JUMP_INSN of an annulling branch. */ + 1 in a JUMP_INSN of an annulling branch. + 1 in a CONCAT is VAL_EXPR_IS_CLOBBERED in var-tracking.c. + 1 in a preserved VALUE is PRESERVED_VALUE_P in cselib.c. */ unsigned int unchanging : 1; /* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile. 1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL, BARRIER, or NOTE @@ -290,7 +293,8 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), non-local label. In a SYMBOL_REF, this flag is used for machine-specific purposes. In a PREFETCH, this flag indicates that it should be considered a scheduling - barrier. */ + barrier. + 1 in a CONCAT is VAL_NEEDS_RESOLUTION in var-tracking.c. */ unsigned int volatil : 1; /* 1 in a MEM referring to a field of an aggregate. 0 if the MEM was a variable or the result of a * operator in C; @@ -311,19 +315,24 @@ struct GTY((chain_next ("RTX_NEXT (&%h)"), In a REG, this is not needed for that purpose, and used instead in `leaf_renumber_regs_insn'. 1 in a SYMBOL_REF, means that emit_library_call - has used it as the function. */ + has used it as the function. + 1 in a CONCAT is VAL_HOLDS_TRACK_EXPR in var-tracking.c. + 1 in a VALUE or DEBUG_EXPR is VALUE_RECURSED_INTO in var-tracking.c. */ unsigned int used : 1; /* 1 in an INSN or a SET if this rtx is related to the call frame, either changing how we compute the frame address or saving and restoring registers in the prologue and epilogue. 1 in a REG or MEM if it is a pointer. 1 in a SYMBOL_REF if it addresses something in the per-function - constant string pool. */ + constant string pool. + 1 in a VALUE is VALUE_CHANGED in var-tracking.c. */ unsigned frame_related : 1; /* 1 in a REG or PARALLEL that is the current function's return value. 1 in a MEM if it refers to a scalar. 1 in a SYMBOL_REF for a weak symbol. - 1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P. */ + 1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P. + 1 in a CONCAT is VAL_EXPR_HAS_REVERSE in var-tracking.c. + 1 in a VALUE or DEBUG_EXPR is NO_LOC_P in var-tracking.c. */ unsigned return_val : 1; /* The first element of the operands of this rtx. diff --git a/gcc/sel-sched-ir.c b/gcc/sel-sched-ir.c index 4878460f882..dacee0b9e9b 100644 --- a/gcc/sel-sched-ir.c +++ b/gcc/sel-sched-ir.c @@ -1745,6 +1745,11 @@ update_target_availability (expr_t to, expr_t from, insn_t split_point) else EXPR_TARGET_AVAILABLE (to) = -1; } + else if (EXPR_TARGET_AVAILABLE (from) == 0 + && EXPR_LHS (from) + && REG_P (EXPR_LHS (from)) + && REGNO (EXPR_LHS (to)) != REGNO (EXPR_LHS (from))) + EXPR_TARGET_AVAILABLE (to) = -1; else EXPR_TARGET_AVAILABLE (to) &= EXPR_TARGET_AVAILABLE (from); } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2ed54293ab4..709fae4848c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,60 @@ +2011-10-19 Paolo Carlini <paolo.carlini@oracle.com> + + PR c++/13657 + * g++.dg/parse/error42.C: New. + * g++.old-deja/g++.other/ptrmem7.C: Tweak dg-errors. + +2011-10-19 Jason Merrill <jason@redhat.com> + + PR c++/50793 + * g++.dg/init/value9.C: New. + +2011-10-19 Jakub Jelinek <jakub@redhat.com> + + * gcc.dg/torture/vshuf-32.inc: Add interleave permutations. + * gcc.dg/torture/vshuf-16.inc: Likewise. + * gcc.dg/torture/vshuf-8.inc: Likewise. + * gcc.dg/torture/vshuf-4.inc: Likewise. + +2011-10-19 Janus Weil <janus@gcc.gnu.org> + + PR fortran/47023 + * gfortran.dg/sizeof_proc.f90: New. + +2011-10-19 Joseph Myers <joseph@codesourcery.com> + + * g++.dg/compat/struct-layout-1_generate.c: Also pass -mno-mmx + -Wno-abi for i?86-*-mingw32* x86_64-*-mingw32* i?86-*-cygwin*. + +2011-10-19 Uros Bizjak <ubizjak@gmail.com> + + PR testsuite/50796 + * gcc.dg/plugin/plugindir?.c Update dg_prune_output. + +2011-10-19 Jason Merrill <jason@redhat.com> + + PR c++/50787 + * g++.dg/init/ref20.C: New. + +2011-10-19 Kai Tietz <ktietz@redhat.com> + + PR middle-end/50795 + * gcc.dg/tree-ssa/builtin-expect-1.c: Adjust test. + * gcc.dg/tree-ssa/builtin-expect-2.c: Adjust test. + * gcc.dg/tree-ssa/builtin-expect-3.c: Adjust test. + * gcc.dg/tree-ssa/builtin-expect-4.c: Adjust test. + * gcc.dg/tree-ssa/builtin-expect-5.c: Adjust test. + +2011-10-19 Richard Guenther <rguenther@suse.de> + + PR middle-end/50768 + * gcc.dg/torture/pr50768.c: New testcase. + +2011-10-19 Alexander Monakov <amonakov@ispras.ru> + + PR rtl-optimization/50340 + * gcc.dg/pr50340.c: New. + 2011-10-18 Andrew Stubbs <ams@codesourcery.com> PR tree-optimization/50717 diff --git a/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c b/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c index db0e973a7a0..5ad3f4409f6 100644 --- a/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c +++ b/gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c @@ -47,7 +47,7 @@ const char *dg_options[] = { "/* { dg-options \"%s-I%s\" } */\n", "/* { dg-options \"%s-I%s -mno-mmx -Wno-abi\" { target i?86-*-* x86_64-*-* } } */\n", "/* { dg-options \"%s-I%s -fno-common\" { target alpha*-dec-osf* hppa*-*-hpux* powerpc*-*-darwin* *-*-mingw32* *-*-cygwin* } } */\n", -"/* { dg-options \"%s-I%s -mno-mmx -fno-common -Wno-abi\" { target i?86-*-darwin* x86_64-*-darwin* } } */\n", +"/* { dg-options \"%s-I%s -mno-mmx -fno-common -Wno-abi\" { target i?86-*-darwin* x86_64-*-darwin* i?86-*-mingw32* x86_64-*-mingw32* i?86-*-cygwin* } } */\n", "/* { dg-options \"%s-I%s -mno-base-addresses\" { target mmix-*-* } } */\n", "/* { dg-options \"%s-I%s -mlongcalls -mtext-section-literals\" { target xtensa*-*-* } } */\n" #define NDG_OPTIONS (sizeof (dg_options) / sizeof (dg_options[0])) diff --git a/gcc/testsuite/g++.dg/init/ref20.C b/gcc/testsuite/g++.dg/init/ref20.C new file mode 100644 index 00000000000..b64cb57e09d --- /dev/null +++ b/gcc/testsuite/g++.dg/init/ref20.C @@ -0,0 +1,8 @@ +// PR c++/50787 +// { dg-do run } + +int main() { + const int Ci = 0; + const int &rCi = Ci; + if (!(&Ci == &rCi)) __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/init/value9.C b/gcc/testsuite/g++.dg/init/value9.C new file mode 100644 index 00000000000..4899bd81001 --- /dev/null +++ b/gcc/testsuite/g++.dg/init/value9.C @@ -0,0 +1,32 @@ +// PR c++/50793 +// { dg-do run } + +struct NonTrivial +{ + NonTrivial() { } +}; + +struct S +{ + NonTrivial nt; + int i; +}; + +int f(S s) +{ + s.i = 0xdeadbeef; + return s.i; +} + +int g(S s = S()) +{ + return s.i; +} + +int main() +{ + f(S()); // make stack dirty + + if ( g() ) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/parse/error42.C b/gcc/testsuite/g++.dg/parse/error42.C new file mode 100644 index 00000000000..5e4c5bbdd2d --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/error42.C @@ -0,0 +1,4 @@ +// PR c++/13657 + +class C { public: int (*f())(); int bar(); }; +int (*C::f())() { return C::bar; } // { dg-error "cannot convert 'C::bar'" } diff --git a/gcc/testsuite/g++.old-deja/g++.other/ptrmem7.C b/gcc/testsuite/g++.old-deja/g++.other/ptrmem7.C index b04f7e2657c..987c0445a67 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/ptrmem7.C +++ b/gcc/testsuite/g++.old-deja/g++.other/ptrmem7.C @@ -31,9 +31,9 @@ void A::foo () int (*ptr8) (short) = ns; int (A::*ptr11) (int) = &A::single; - int (A::*ptr12) (int) = A::single; // { dg-error "match" } + int (A::*ptr12) (int) = A::single; // { dg-error "cannot convert" } int (A::*ptr13) (int) = &single; // { dg-error "pointer to member" } - int (A::*ptr14) (int) = single; // { dg-error "match" } + int (A::*ptr14) (int) = single; // { dg-error "cannot convert" } int (A::*ptr20) (int) = &(A::ns); // { dg-error "pointer to member" } int (A::*ptr21) (int) = &(A::single); // { dg-error "pointer to member" } diff --git a/gcc/testsuite/gcc.dg/plugin/plugindir1.c b/gcc/testsuite/gcc.dg/plugin/plugindir1.c index a973ec0c54c..72821ac9852 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugindir1.c +++ b/gcc/testsuite/gcc.dg/plugin/plugindir1.c @@ -1,4 +1,4 @@ /* { dg-do compile } */ /* { dg-options "-c -fplugin=foo" } */ -/* { dg-prune-output ".*inacessible plugin file.*foo\.so expanded from short plugin name.*" } */ +/* { dg-prune-output ".*inaccessible plugin file.*foo\.so expanded from short plugin name.*" } */ diff --git a/gcc/testsuite/gcc.dg/plugin/plugindir2.c b/gcc/testsuite/gcc.dg/plugin/plugindir2.c index 58c1505501a..063b9d9e594 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugindir2.c +++ b/gcc/testsuite/gcc.dg/plugin/plugindir2.c @@ -1,4 +1,4 @@ /* { dg-do compile } */ /* { dg-options "-save-temps -c -fplugin=foo" } */ -/* { dg-prune-output ".*inacessible plugin file.*foo\.so expanded from short plugin name.*" } */ +/* { dg-prune-output ".*inaccessible plugin file.*foo\.so expanded from short plugin name.*" } */ diff --git a/gcc/testsuite/gcc.dg/plugin/plugindir3.c b/gcc/testsuite/gcc.dg/plugin/plugindir3.c index 41037534a26..9b1c004cfd1 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugindir3.c +++ b/gcc/testsuite/gcc.dg/plugin/plugindir3.c @@ -1,4 +1,4 @@ /* { dg-do preprocess } */ /* { dg-options "-fplugin=foo" } */ -/* { dg-prune-output ".*inacessible plugin file.*foo\.so expanded from short plugin name.*" } */ +/* { dg-prune-output ".*inaccessible plugin file.*foo\.so expanded from short plugin name.*" } */ diff --git a/gcc/testsuite/gcc.dg/plugin/plugindir4.c b/gcc/testsuite/gcc.dg/plugin/plugindir4.c index 27b2f24aaf9..8f1cb801621 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugindir4.c +++ b/gcc/testsuite/gcc.dg/plugin/plugindir4.c @@ -1,4 +1,4 @@ /* { dg-do preprocess } */ /* { dg-options "-iplugindir=my-plugindir -fplugin=foo" } */ -/* { dg-prune-output ".*inacessible plugin file.*my-plugindir/foo\.so expanded from short plugin name.*" } */ +/* { dg-prune-output ".*inaccessible plugin file.*my-plugindir/foo\.so expanded from short plugin name.*" } */ diff --git a/gcc/testsuite/gcc.dg/pr50340.c b/gcc/testsuite/gcc.dg/pr50340.c new file mode 100644 index 00000000000..18430366fe1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr50340.c @@ -0,0 +1,46 @@ +/* { dg-do compile { target powerpc*-*-* ia64-*-* i?86-*-* x86_64-*-* } } */ +/* { dg-options "-O3 -fselective-scheduling2 -funroll-loops" } */ +extern int mode_size[]; +typedef unsigned HARD_REG_SET[ ((64 + 32 - 1) / 32) ]; +enum reload_type { + RELOAD_FOR_INPUT, + RELOAD_FOR_OUTPUT, + RELOAD_FOR_INSN, + RELOAD_FOR_INPUT_ADDRESS, + RELOAD_FOR_OUTPUT_ADDRESS, + RELOAD_FOR_OPERAND_ADDRESS, + RELOAD_FOR_OPADDR_ADDR, + RELOAD_OTHER, + RELOAD_FOR_OTHER_ADDRESS +}; +static HARD_REG_SET reload_reg_used; +static HARD_REG_SET reload_reg_used_in_input_addr[10]; +static HARD_REG_SET reload_reg_used_in_output_addr[10]; +static HARD_REG_SET reload_reg_used_in_input[10]; +static HARD_REG_SET reload_reg_used_in_output[10]; +static HARD_REG_SET reload_reg_used_in_op_addr; +static HARD_REG_SET reload_reg_used_in_op_addr_reload; +static HARD_REG_SET reload_reg_used_in_insn; +static HARD_REG_SET reload_reg_used_in_other_addr; +static HARD_REG_SET reload_reg_used_at_all; +void __attribute__((cold)) mark_reload_reg_in_use (regno, opnum, type, mode) +{ + int nregs = regno ? 1 : mode_size[mode]; + int i; + for (i = regno; i < nregs + regno; i++) + { + switch (type) + { + case RELOAD_OTHER: reload_reg_used[i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_INPUT_ADDRESS: reload_reg_used_in_input_addr[opnum][i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_OUTPUT_ADDRESS: reload_reg_used_in_output_addr[opnum][i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_OPERAND_ADDRESS: reload_reg_used_in_op_addr[i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_OPADDR_ADDR: reload_reg_used_in_op_addr_reload[i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_OTHER_ADDRESS: reload_reg_used_in_other_addr[i / 32u] |= 1; break; + case RELOAD_FOR_INPUT: reload_reg_used_in_input[opnum][i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_OUTPUT: reload_reg_used_in_output[opnum][i / 32u] |= 1 << i % 32u; break; + case RELOAD_FOR_INSN: reload_reg_used_in_insn[i / 32u] |= 1 << i % 32u; + } + reload_reg_used_at_all[i / 32u] |= 1 << i; + } +} diff --git a/gcc/testsuite/gcc.dg/torture/pr50768.c b/gcc/testsuite/gcc.dg/torture/pr50768.c new file mode 100644 index 00000000000..351b2f96618 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr50768.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-ftracer" } */ + +char data[8]; +int l1; + +void +test1 (void) +{ + char buf[8]; + __builtin___mempcpy_chk (buf, data, l1 ? sizeof (buf) : 4, + __builtin_object_size (buf, 0)); + if (__builtin___memmove_chk + (buf, data, l1 ? sizeof (buf) : 4, + __builtin_object_size (buf, 0)) != buf) + __builtin_abort (); +} diff --git a/gcc/testsuite/gcc.dg/torture/vshuf-16.inc b/gcc/testsuite/gcc.dg/torture/vshuf-16.inc index 722160377b5..1464774c62f 100644 --- a/gcc/testsuite/gcc.dg/torture/vshuf-16.inc +++ b/gcc/testsuite/gcc.dg/torture/vshuf-16.inc @@ -15,7 +15,9 @@ T (11, 15, 9, 14, 10, 8, 12, 13, 11, 7, 1, 2, 0, 4, 5, 6, 3) \ T (12, 2, 5, 24, 23, 17, 22, 20, 21, 12, 14, 13, 8, 6, 20, 10, 18) \ T (13, 23, 11, 15, 9, 0, 14, 8, 12, 10, 13, 19, 11, 2, 26, 24, 30) \ T (14, 25, 5, 17, 1, 9, 15, 21, 7, 28, 2, 18, 13, 30, 14, 10, 4) \ -T (15, 1, 30, 27, 31, 9, 18, 25, 12, 7, 4, 2, 16, 25, 20, 10, 3) +T (15, 1, 30, 27, 31, 9, 18, 25, 12, 7, 4, 2, 16, 25, 20, 10, 3) \ +T (16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30) \ +T (17, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31) #define EXPTESTS \ T (116, 28, 13, 27, 11, 21, 1, 5, 22, 29, 14, 15, 6, 3, 10, 16, 30) \ T (117, 22, 26, 1, 13, 29, 3, 18, 18, 11, 21, 12, 28, 19, 5, 7, 4) \ diff --git a/gcc/testsuite/gcc.dg/torture/vshuf-32.inc b/gcc/testsuite/gcc.dg/torture/vshuf-32.inc index 5cd077b5045..8410e48310d 100644 --- a/gcc/testsuite/gcc.dg/torture/vshuf-32.inc +++ b/gcc/testsuite/gcc.dg/torture/vshuf-32.inc @@ -15,7 +15,9 @@ T (11, 13, 40, 7, 33, 51, 21, 59, 46, 47, 32, 9, 34, 63, 35, 41, 17, 24, 1, 8, 2 T (12, 39, 43, 54, 27, 53, 39, 27, 30, 2, 17, 13, 33, 7, 52, 40, 15, 36, 57, 10, 28, 22, 23, 25, 24, 41, 47, 8, 20, 5, 3, 4, 0) \ T (13, 7, 51, 13, 61, 25, 4, 19, 58, 35, 33, 29, 15, 40, 2, 39, 16, 38, 3, 54, 63, 15, 6, 48, 21, 14, 52, 17, 50, 34, 55, 57, 50) \ T (14, 22, 53, 28, 42, 45, 38, 49, 13, 54, 61, 21, 52, 7, 16, 34, 9, 1, 43, 62, 43, 35, 50, 47, 58, 20, 3, 30, 15, 37, 53, 43, 36) \ -T (15, 2, 43, 49, 34, 28, 35, 29, 36, 51, 9, 17, 48, 10, 37, 45, 21, 52, 19, 25, 33, 60, 31, 30, 42, 12, 26, 27, 46, 5, 40, 14, 36) +T (15, 2, 43, 49, 34, 28, 35, 29, 36, 51, 9, 17, 48, 10, 37, 45, 21, 52, 19, 25, 33, 60, 31, 30, 42, 12, 26, 27, 46, 5, 40, 14, 36) \ +T (16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62) \ +T (17, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63) #define EXPTESTS \ T (116, 13, 38, 47, 3, 17, 8, 38, 20, 59, 61, 39, 26, 7, 49, 63, 43, 57, 16, 40, 19, 4, 32, 27, 7, 52, 19, 46, 55, 36, 41, 48, 6) \ T (117, 39, 35, 59, 20, 56, 18, 58, 63, 57, 14, 2, 16, 5, 61, 35, 4, 53, 9, 52, 51, 27, 33, 61, 12, 3, 35, 36, 40, 37, 7, 45, 42) \ diff --git a/gcc/testsuite/gcc.dg/torture/vshuf-4.inc b/gcc/testsuite/gcc.dg/torture/vshuf-4.inc index a7a852b80ab..2044e365dec 100644 --- a/gcc/testsuite/gcc.dg/torture/vshuf-4.inc +++ b/gcc/testsuite/gcc.dg/torture/vshuf-4.inc @@ -15,7 +15,9 @@ T (11, 1, 4, 0, 7) \ T (12, 1, 5, 7, 2) \ T (13, 2, 3, 0, 4) \ T (14, 7, 6, 4, 2) \ -T (15, 6, 1, 3, 4) +T (15, 6, 1, 3, 4) \ +T (16, 0, 2, 4, 6) \ +T (17, 1, 3, 5, 7) #define EXPTESTS \ T (116, 1, 2, 4, 3) \ T (117, 7, 3, 3, 0) \ diff --git a/gcc/testsuite/gcc.dg/torture/vshuf-8.inc b/gcc/testsuite/gcc.dg/torture/vshuf-8.inc index cd841c29efe..24db545ef26 100644 --- a/gcc/testsuite/gcc.dg/torture/vshuf-8.inc +++ b/gcc/testsuite/gcc.dg/torture/vshuf-8.inc @@ -15,7 +15,9 @@ T (11, 5, 11, 12, 6, 3, 2, 4, 15) \ T (12, 5, 13, 14, 8, 4, 10, 4, 12) \ T (13, 14, 8, 12, 3, 13, 9, 5, 4) \ T (14, 15, 3, 13, 6, 14, 12, 10, 0) \ -T (15, 0, 5, 11, 7, 4, 6, 14, 1) +T (15, 0, 5, 11, 7, 4, 6, 14, 1) \ +T (16, 0, 2, 4, 6, 8, 10, 12, 14) \ +T (17, 1, 3, 5, 7, 9, 11, 13, 15) #define EXPTESTS \ T (116, 9, 3, 9, 4, 7, 0, 0, 6) \ T (117, 4, 14, 12, 8, 9, 6, 0, 10) \ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-1.c index 42d4b0dd874..4c3888fd237 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-1.c @@ -1,9 +1,9 @@ /* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-gimple" } */ -f (int i, float j) +f (int i, float j, int i2, float j2) { - if (__builtin_expect (i > 0 && j, 0)) + if (__builtin_expect ((i * i2) > 0 && (j * j2), 0)) g (); } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-2.c index ff175b975a9..d8f344f7826 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-2.c @@ -1,9 +1,9 @@ /* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-gimple" } */ -f (int i, float j) +f (int i, float j, int i2, float j2) { - if (__builtin_expect (i > 0 || j, 0)) + if (__builtin_expect ((i * i2) > 0 || (j * j2), 0)) ; else g (); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-3.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-3.c index 8fcc4e2bd40..b9e6cfb8ff9 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-3.c @@ -1,9 +1,9 @@ /* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-gimple" } */ -f (int i, float j) +f (int i, float j, int i2, float j2) { - if (__builtin_expect (i > 0 && j, 0)) + if (__builtin_expect ((i * i2) > 0 && (j * j2), 0)) a (); else b (); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-4.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-4.c index 1fecf1b980c..2d2b206683b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-4.c @@ -1,9 +1,9 @@ /* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-gimple" } */ -f (int i, float j) +f (int i, float j, int i2, float j2) { - if (__builtin_expect (i > 0 || j, 0)) + if (__builtin_expect ((i * i2) > 0 || (j * j2), 0)) a (); else b (); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-5.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-5.c index ed53b287b2a..d0b251db17b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-5.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-expect-5.c @@ -1,9 +1,9 @@ /* { dg-do compile } */ /* { dg-options "-O2 -fdump-tree-forwprop" } */ -f (int i, float j) +f (int i, float j, int i2, float j2) { - if (__builtin_expect (i > 0 && __builtin_expect (j != 0, 1), 0)) + if (__builtin_expect ((i * i2) > 0 && __builtin_expect ((j * j2) != 0, 1), 0)) a (); else b (); diff --git a/gcc/testsuite/gfortran.dg/sizeof_proc.f90 b/gcc/testsuite/gfortran.dg/sizeof_proc.f90 new file mode 100644 index 00000000000..b4a2d732003 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/sizeof_proc.f90 @@ -0,0 +1,28 @@ +! { dg-do compile } +! +! PR 47023: C_Sizeof: Rejects valid code +! +! Contributed by Janus Weil <janus@gcc.gnu.org> + +use iso_c_binding +procedure(real) :: proc +procedure(real), pointer :: pp +pp => sin + +print *,sizeof(proc) ! { dg-error "may not be a procedure" } +print *,sizeof(pp) ! { dg-error "may not be a procedure" } +print *,sizeof(pp(0.)) +print *,sizeof(sub) ! { dg-error "may not be a procedure" } +print *,sizeof(func) ! { dg-error "may not be a procedure" } +print *,sizeof(func()) + +contains + + subroutine sub + end subroutine + + real function func() + func = 0. + end function + +end diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index f751d8d98a2..c1896be228d 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -2512,7 +2512,13 @@ stmt_could_throw_1_p (gimple stmt) || TREE_CODE_CLASS (code) == tcc_unary || TREE_CODE_CLASS (code) == tcc_binary) { - t = gimple_expr_type (stmt); + if (is_gimple_assign (stmt) + && TREE_CODE_CLASS (code) == tcc_comparison) + t = TREE_TYPE (gimple_assign_rhs1 (stmt)); + else if (gimple_code (stmt) == GIMPLE_COND) + t = TREE_TYPE (gimple_cond_lhs (stmt)); + else + t = gimple_expr_type (stmt); fp_operation = FLOAT_TYPE_P (t); if (fp_operation) { diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c index 1db93a65a65..d707db520e3 100644 --- a/gcc/tree-ssa-forwprop.c +++ b/gcc/tree-ssa-forwprop.c @@ -597,7 +597,8 @@ forward_propagate_into_cond (gimple_stmt_iterator *gsi_p) } } - if (tmp) + if (tmp + && is_gimple_condexpr (tmp)) { if (dump_file && tmp) { diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c index cb527913d4f..4bc9ffd0eff 100644 --- a/gcc/tree-ssa-loop-im.c +++ b/gcc/tree-ssa-loop-im.c @@ -549,6 +549,11 @@ stmt_cost (gimple stmt) switch (gimple_assign_rhs_code (stmt)) { case MULT_EXPR: + case WIDEN_MULT_EXPR: + case WIDEN_MULT_PLUS_EXPR: + case WIDEN_MULT_MINUS_EXPR: + case DOT_PROD_EXPR: + case FMA_EXPR: case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: case FLOOR_DIV_EXPR: @@ -565,6 +570,9 @@ stmt_cost (gimple stmt) case LSHIFT_EXPR: case RSHIFT_EXPR: + case WIDEN_LSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: cost += 20; break; diff --git a/gcc/tree-ssa-tail-merge.c b/gcc/tree-ssa-tail-merge.c index 529388c1b22..2a47dc6b0a9 100644 --- a/gcc/tree-ssa-tail-merge.c +++ b/gcc/tree-ssa-tail-merge.c @@ -1508,11 +1508,14 @@ replace_block_by (basic_block bb1, basic_block bb2, bool update_vops) edge e; edge_iterator ei; + phi_vuse2 = vop_at_entry (bb2); + if (phi_vuse2 != NULL_TREE && TREE_CODE (phi_vuse2) != SSA_NAME) + phi_vuse2 = NULL_TREE; + if (update_vops) { /* Find the vops at entry of bb1 and bb2. */ phi_vuse1 = vop_at_entry (bb1); - phi_vuse2 = vop_at_entry (bb2); /* If one of the 2 not found, it means there's no need to update. */ update_vops = phi_vuse1 != NULL_TREE && phi_vuse2 != NULL_TREE; @@ -1545,6 +1548,9 @@ replace_block_by (basic_block bb1, basic_block bb2, bool update_vops) gcc_assert (pred_edge != NULL); if (update_vops) VEC_safe_push (edge, heap, redirected_edges, pred_edge); + else if (phi_vuse2 && gimple_bb (SSA_NAME_DEF_STMT (phi_vuse2)) == bb2) + add_phi_arg (SSA_NAME_DEF_STMT (phi_vuse2), SSA_NAME_VAR (phi_vuse2), + pred_edge, UNKNOWN_LOCATION); } /* Update the vops. */ diff --git a/gcc/tree.h b/gcc/tree.h index 18fdd07fe4e..64ccdb03417 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2967,6 +2967,17 @@ extern void decl_value_expr_insert (tree, tree); /* The DECL_RTL for NODE, if it is set, or NULL, if it is not set. */ #define DECL_RTL_IF_SET(NODE) (DECL_RTL_SET_P (NODE) ? DECL_RTL (NODE) : NULL) +#if (GCC_VERSION >= 2007) +#define DECL_RTL_KNOWN_SET(decl) __extension__ \ +({ tree const __d = (decl); \ + gcc_checking_assert (DECL_RTL_SET_P (__d)); \ + /* Dereference it so the compiler knows it can't be NULL even \ + without assertion checking. */ \ + &*DECL_RTL_IF_SET (__d); }) +#else +#define DECL_RTL_KNOWN_SET(decl) (&*DECL_RTL_IF_SET (decl)) +#endif + /* In VAR_DECL and PARM_DECL nodes, nonzero means declared `register'. */ #define DECL_REGISTER(NODE) (DECL_WRTL_CHECK (NODE)->decl_common.decl_flag_0) diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index 214f58a8ac2..8a64000b616 100644 --- a/gcc/var-tracking.c +++ b/gcc/var-tracking.c @@ -301,6 +301,48 @@ typedef struct location_chain_def enum var_init_status init; } *location_chain; +/* A vector of loc_exp_dep holds the active dependencies of a one-part + DV on VALUEs, i.e., the VALUEs expanded so as to form the current + location of DV. Each entry is also part of VALUE' s linked-list of + backlinks back to DV. */ +typedef struct loc_exp_dep_s +{ + /* The dependent DV. */ + decl_or_value dv; + /* The dependency VALUE or DECL_DEBUG. */ + rtx value; + /* The next entry in VALUE's backlinks list. */ + struct loc_exp_dep_s *next; + /* A pointer to the pointer to this entry (head or prev's next) in + the doubly-linked list. */ + struct loc_exp_dep_s **pprev; +} loc_exp_dep; + +DEF_VEC_O (loc_exp_dep); + +/* This data structure is allocated for one-part variables at the time + of emitting notes. */ +struct onepart_aux +{ + /* Doubly-linked list of dependent DVs. These are DVs whose cur_loc + computation used the expansion of this variable, and that ought + to be notified should this variable change. If the DV's cur_loc + expanded to NULL, all components of the loc list are regarded as + active, so that any changes in them give us a chance to get a + location. Otherwise, only components of the loc that expanded to + non-NULL are regarded as active dependencies. */ + loc_exp_dep *backlinks; + /* This holds the LOC that was expanded into cur_loc. We need only + mark a one-part variable as changed if the FROM loc is removed, + or if it has no known location and a loc is added, or if it gets + a change notification from any of its active dependencies. */ + rtx from; + /* The depth of the cur_loc expression. */ + int depth; + /* Dependencies actively used when expand FROM into cur_loc. */ + VEC (loc_exp_dep, none) deps; +}; + /* Structure describing one part of variable. */ typedef struct variable_part_def { @@ -310,13 +352,33 @@ typedef struct variable_part_def /* Location which was last emitted to location list. */ rtx cur_loc; - /* The offset in the variable. */ - HOST_WIDE_INT offset; + union variable_aux + { + /* The offset in the variable, if !var->onepart. */ + HOST_WIDE_INT offset; + + /* Pointer to auxiliary data, if var->onepart and emit_notes. */ + struct onepart_aux *onepaux; + } aux; } variable_part; /* Maximum number of location parts. */ #define MAX_VAR_PARTS 16 +/* Enumeration type used to discriminate various types of one-part + variables. */ +typedef enum onepart_enum +{ + /* Not a one-part variable. */ + NOT_ONEPART = 0, + /* A one-part DECL that is not a DEBUG_EXPR_DECL. */ + ONEPART_VDECL = 1, + /* A DEBUG_EXPR_DECL. */ + ONEPART_DEXPR = 2, + /* A VALUE. */ + ONEPART_VALUE = 3 +} onepart_enum_t; + /* Structure describing where the variable is located. */ typedef struct variable_def { @@ -330,10 +392,8 @@ typedef struct variable_def /* Number of variable parts. */ char n_var_parts; - /* True if this variable changed (any of its) cur_loc fields - during the current emit_notes_for_changes resp. - emit_notes_for_differences call. */ - bool cur_loc_changed; + /* What type of DV this is, according to enum onepart_enum. */ + ENUM_BITFIELD (onepart_enum) onepart : CHAR_BIT; /* True if this variable_def struct is currently in the changed_variables hash table. */ @@ -344,29 +404,48 @@ typedef struct variable_def } *variable; typedef const struct variable_def *const_variable; -/* Structure for chaining backlinks from referenced VALUEs to - DVs that are referencing them. */ -typedef struct value_chain_def -{ - /* Next value_chain entry. */ - struct value_chain_def *next; - - /* The declaration of the variable, or an RTL value - being handled like a declaration, whose var_parts[0].loc_chain - references the VALUE owning this value_chain. */ - decl_or_value dv; - - /* Reference count. */ - int refcount; -} *value_chain; -typedef const struct value_chain_def *const_value_chain; - /* Pointer to the BB's information specific to variable tracking pass. */ #define VTI(BB) ((variable_tracking_info) (BB)->aux) /* Macro to access MEM_OFFSET as an HOST_WIDE_INT. Evaluates MEM twice. */ #define INT_MEM_OFFSET(mem) (MEM_OFFSET_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0) +#if ENABLE_CHECKING && (GCC_VERSION >= 2007) + +/* Access VAR's Ith part's offset, checking that it's not a one-part + variable. */ +#define VAR_PART_OFFSET(var, i) __extension__ \ +(*({ variable const __v = (var); \ + gcc_checking_assert (!__v->onepart); \ + &__v->var_part[(i)].aux.offset; })) + +/* Access VAR's one-part auxiliary data, checking that it is a + one-part variable. */ +#define VAR_LOC_1PAUX(var) __extension__ \ +(*({ variable const __v = (var); \ + gcc_checking_assert (__v->onepart); \ + &__v->var_part[0].aux.onepaux; })) + +#else +#define VAR_PART_OFFSET(var, i) ((var)->var_part[(i)].aux.offset) +#define VAR_LOC_1PAUX(var) ((var)->var_part[0].aux.onepaux) +#endif + +/* These are accessor macros for the one-part auxiliary data. When + convenient for users, they're guarded by tests that the data was + allocated. */ +#define VAR_LOC_DEP_LST(var) (VAR_LOC_1PAUX (var) \ + ? VAR_LOC_1PAUX (var)->backlinks \ + : NULL) +#define VAR_LOC_DEP_LSTP(var) (VAR_LOC_1PAUX (var) \ + ? &VAR_LOC_1PAUX (var)->backlinks \ + : NULL) +#define VAR_LOC_FROM(var) (VAR_LOC_1PAUX (var)->from) +#define VAR_LOC_DEPTH(var) (VAR_LOC_1PAUX (var)->depth) +#define VAR_LOC_DEP_VEC(var) (VAR_LOC_1PAUX (var) \ + ? &VAR_LOC_1PAUX (var)->deps \ + : NULL) + /* Alloc pool for struct attrs_def. */ static alloc_pool attrs_pool; @@ -382,24 +461,24 @@ static alloc_pool loc_chain_pool; /* Alloc pool for struct shared_hash_def. */ static alloc_pool shared_hash_pool; -/* Alloc pool for struct value_chain_def. */ -static alloc_pool value_chain_pool; - /* Changed variables, notes will be emitted for them. */ static htab_t changed_variables; -/* Links from VALUEs to DVs referencing them in their current loc_chains. */ -static htab_t value_chains; - /* Shall notes be emitted? */ static bool emit_notes; +/* Values whose dynamic location lists have gone empty, but whose + cselib location lists are still usable. Use this to hold the + current location, the backlinks, etc, during emit_notes. */ +static htab_t dropped_values; + /* Empty shared hashtable. */ static shared_hash empty_shared_hash; /* Scratch register bitmap used by cselib_expand_value_rtx. */ static bitmap scratch_regs = NULL; +#ifdef HAVE_window_save typedef struct GTY(()) parm_reg { rtx outgoing; rtx incoming; @@ -410,6 +489,7 @@ DEF_VEC_ALLOC_O(parm_reg_t, gc); /* Vector of windowed parameter registers, if any. */ static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL; +#endif /* Variable used to tell whether cselib_process_insn called our hook. */ static bool cselib_hook_called; @@ -420,7 +500,6 @@ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *, HOST_WIDE_INT *); static bool vt_stack_adjustments (void); -static void note_register_arguments (rtx); static hashval_t variable_htab_hash (const void *); static int variable_htab_eq (const void *, const void *); static void variable_htab_free (void *); @@ -476,6 +555,7 @@ static void dump_vars (htab_t); static void dump_dataflow_set (dataflow_set *); static void dump_dataflow_sets (void); +static void set_dv_changed (decl_or_value, bool); static void variable_was_changed (variable, dataflow_set *); static void **set_slot_part (dataflow_set *, rtx, void **, decl_or_value, HOST_WIDE_INT, @@ -672,15 +752,11 @@ vt_stack_adjustments (void) for (insn = BB_HEAD (dest); insn != NEXT_INSN (BB_END (dest)); insn = NEXT_INSN (insn)) - { - if (INSN_P (insn)) - { - insn_stack_adjust_offset_pre_post (insn, &pre, &post); - offset += pre + post; - } - if (CALL_P (insn)) - note_register_arguments (insn); - } + if (INSN_P (insn)) + { + insn_stack_adjust_offset_pre_post (insn, &pre, &post); + offset += pre + post; + } VTI (dest)->out.stack_adjust = offset; @@ -1133,6 +1209,21 @@ dv_as_value (decl_or_value dv) return (rtx)dv; } +/* Return the DEBUG_EXPR of a DEBUG_EXPR_DECL or the VALUE in DV. */ +static inline rtx +dv_as_rtx (decl_or_value dv) +{ + tree decl; + + if (dv_is_value_p (dv)) + return dv_as_value (dv); + + decl = dv_as_decl (dv); + + gcc_checking_assert (TREE_CODE (decl) == DEBUG_EXPR_DECL); + return DECL_RTL_KNOWN_SET (decl); +} + /* Return the opaque pointer in the decl_or_value. */ static inline void * dv_as_opaque (decl_or_value dv) @@ -1140,36 +1231,36 @@ dv_as_opaque (decl_or_value dv) return dv; } -/* Return true if a decl_or_value must not have more than one variable - part. */ -static inline bool +/* Return nonzero if a decl_or_value must not have more than one + variable part. The returned value discriminates among various + kinds of one-part DVs ccording to enum onepart_enum. */ +static inline onepart_enum_t dv_onepart_p (decl_or_value dv) { tree decl; if (!MAY_HAVE_DEBUG_INSNS) - return false; + return NOT_ONEPART; if (dv_is_value_p (dv)) - return true; + return ONEPART_VALUE; decl = dv_as_decl (dv); - if (!decl) - return true; - if (TREE_CODE (decl) == DEBUG_EXPR_DECL) - return true; + return ONEPART_DEXPR; - return (target_for_debug_bind (decl) != NULL_TREE); + if (target_for_debug_bind (decl) != NULL_TREE) + return ONEPART_VDECL; + + return NOT_ONEPART; } -/* Return the variable pool to be used for dv, depending on whether it - can have multiple parts or not. */ +/* Return the variable pool to be used for a dv of type ONEPART. */ static inline alloc_pool -dv_pool (decl_or_value dv) +onepart_pool (onepart_enum_t onepart) { - return dv_onepart_p (dv) ? valvar_pool : var_pool; + return onepart ? valvar_pool : var_pool; } /* Build a decl_or_value out of a decl. */ @@ -1192,6 +1283,30 @@ dv_from_value (rtx value) return dv; } +/* Return a value or the decl of a debug_expr as a decl_or_value. */ +static inline decl_or_value +dv_from_rtx (rtx x) +{ + decl_or_value dv; + + switch (GET_CODE (x)) + { + case DEBUG_EXPR: + dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x)); + gcc_checking_assert (DECL_RTL_KNOWN_SET (DEBUG_EXPR_TREE_DECL (x)) == x); + break; + + case VALUE: + dv = dv_from_value (x); + break; + + default: + gcc_unreachable (); + } + + return dv; +} + extern void debug_dv (decl_or_value dv); DEBUG_FUNCTION void @@ -1254,6 +1369,8 @@ variable_htab_eq (const void *x, const void *y) return (dv_as_opaque (v->dv) == dv_as_opaque (dv)); } +static void loc_exp_dep_clear (variable var); + /* Free the element of VARIABLE_HTAB (its type is struct variable_def). */ static void @@ -1278,29 +1395,18 @@ variable_htab_free (void *elem) } var->var_part[i].loc_chain = NULL; } - pool_free (dv_pool (var->dv), var); -} - -/* The hash function for value_chains htab, computes the hash value - from the VALUE. */ - -static hashval_t -value_chain_htab_hash (const void *x) -{ - const_value_chain const v = (const_value_chain) x; - - return dv_htab_hash (v->dv); -} - -/* Compare the VALUE X with VALUE Y. */ - -static int -value_chain_htab_eq (const void *x, const void *y) -{ - const_value_chain const v = (const_value_chain) x; - decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y); - - return dv_as_opaque (v->dv) == dv_as_opaque (dv); + if (var->onepart && VAR_LOC_1PAUX (var)) + { + loc_exp_dep_clear (var); + if (VAR_LOC_DEP_LST (var)) + VAR_LOC_DEP_LST (var)->pprev = NULL; + XDELETE (VAR_LOC_1PAUX (var)); + /* These may be reused across functions, so reset + e.g. NO_LOC_P. */ + if (var->onepart == ONEPART_DEXPR) + set_dv_changed (var->dv, true); + } + pool_free (onepart_pool (var->onepart), var); } /* Initialize the set (array) SET of attrs to empty lists. */ @@ -1569,13 +1675,12 @@ unshare_variable (dataflow_set *set, void **slot, variable var, variable new_var; int i; - new_var = (variable) pool_alloc (dv_pool (var->dv)); + new_var = (variable) pool_alloc (onepart_pool (var->onepart)); new_var->dv = var->dv; new_var->refcount = 1; var->refcount--; new_var->n_var_parts = var->n_var_parts; - new_var->cur_loc_changed = var->cur_loc_changed; - var->cur_loc_changed = false; + new_var->onepart = var->onepart; new_var->in_changed_variables = false; if (! flag_var_tracking_uninit) @@ -1586,7 +1691,18 @@ unshare_variable (dataflow_set *set, void **slot, variable var, location_chain node; location_chain *nextp; - new_var->var_part[i].offset = var->var_part[i].offset; + if (i == 0 && var->onepart) + { + /* One-part auxiliary data is only used while emitting + notes, so propagate it to the new variable in the active + dataflow set. If we're not emitting notes, this will be + a no-op. */ + gcc_checking_assert (!VAR_LOC_1PAUX (var) || emit_notes); + VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (var); + VAR_LOC_1PAUX (var) = NULL; + } + else + VAR_PART_OFFSET (new_var, i) = VAR_PART_OFFSET (var, i); nextp = &new_var->var_part[i].loc_chain; for (node = var->var_part[i].loc_chain; node; node = node->next) { @@ -1891,6 +2007,26 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber) delete_variable_part (set, loc, dv_from_decl (decl), offset); } +/* Return true if LOC should not be expanded for location expressions, + or used in them. */ + +static inline bool +unsuitable_loc (rtx loc) +{ + switch (GET_CODE (loc)) + { + case PC: + case SCRATCH: + case CC0: + case ASM_INPUT: + case ASM_OPERANDS: + return true; + + default: + return false; + } +} + /* Bind a value to a location it was just stored in. If MODIFIED holds, assume the location was modified, detaching it from any values bound to it. */ @@ -1904,10 +2040,10 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified) if (dump_file) { - fprintf (dump_file, "%i: ", INSN_UID (insn)); - print_inline_rtx (dump_file, val, 0); - fprintf (dump_file, " stored in "); + fprintf (dump_file, "%i: ", insn ? INSN_UID (insn) : 0); print_inline_rtx (dump_file, loc, 0); + fprintf (dump_file, " evaluates to "); + print_inline_rtx (dump_file, val, 0); if (v->locs) { struct elt_loc_list *l; @@ -1920,6 +2056,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified) fprintf (dump_file, "\n"); } + gcc_checking_assert (!unsuitable_loc (loc)); + if (REG_P (loc)) { if (modified) @@ -1931,6 +2069,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified) var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED, dv_from_value (val), 0, NULL_RTX, INSERT); else + /* ??? Ideally we wouldn't get these, and use them from the static + cselib loc list. */ set_variable_part (set, loc, dv_from_value (val), 0, VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT); } @@ -1997,13 +2137,6 @@ val_reset (dataflow_set *set, decl_or_value dv) delete_variable_part (set, dv_as_value (dv), dv_from_value (cval), 0); clobber_variable_part (set, NULL, dv, 0, NULL); - - /* ??? Should we make sure there aren't other available values or - variables whose values involve this one other than by - equivalence? E.g., at the very least we should reset MEMs, those - shouldn't be too hard to find cselib-looking up the value as an - address, then locating the resulting value in our own hash - table. */ } /* Find the values in a given location and map the val to another @@ -2029,6 +2162,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn) val_reset (set, dv); + gcc_checking_assert (!unsuitable_loc (loc)); + if (REG_P (loc)) { attrs node, found = NULL; @@ -2061,6 +2196,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn) var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED, dv_from_value (val), 0, NULL_RTX, INSERT); else + /* ??? Ideally we wouldn't get these, and use them from the static + cselib loc list. */ /* ??? Merge equivalent expressions. */ set_variable_part (set, loc, dv_from_value (val), 0, VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT); @@ -2176,10 +2313,11 @@ variable_union (variable src, dataflow_set *set) dst = (variable) *dstp; gcc_assert (src->n_var_parts); + gcc_checking_assert (src->onepart == dst->onepart); /* We can combine one-part variables very efficiently, because their entries are in canonical order. */ - if (dv_onepart_p (src->dv)) + if (src->onepart) { location_chain *nodep, dnode, snode; @@ -2233,16 +2371,18 @@ variable_union (variable src, dataflow_set *set) return 1; } + gcc_checking_assert (!src->onepart); + /* Count the number of location parts, result is K. */ for (i = 0, j = 0, k = 0; i < src->n_var_parts && j < dst->n_var_parts; k++) { - if (src->var_part[i].offset == dst->var_part[j].offset) + if (VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j)) { i++; j++; } - else if (src->var_part[i].offset < dst->var_part[j].offset) + else if (VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j)) i++; else j++; @@ -2252,7 +2392,7 @@ variable_union (variable src, dataflow_set *set) /* We track only variables whose size is <= MAX_VAR_PARTS bytes thus there are at most MAX_VAR_PARTS different offsets. */ - gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS); + gcc_checking_assert (dst->onepart ? k == 1 : k <= MAX_VAR_PARTS); if (dst->n_var_parts != k && shared_var_p (dst, set->vars)) { @@ -2269,7 +2409,7 @@ variable_union (variable src, dataflow_set *set) location_chain node, node2; if (i >= 0 && j >= 0 - && src->var_part[i].offset == dst->var_part[j].offset) + && VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j)) { /* Compute the "sorted" union of the chains, i.e. the locations which are in both chains go first, they are sorted by the sum of @@ -2317,7 +2457,7 @@ variable_union (variable src, dataflow_set *set) /* The most common case, much simpler, no qsort is needed. */ location_chain dstnode = dst->var_part[j].loc_chain; dst->var_part[k].loc_chain = dstnode; - dst->var_part[k].offset = dst->var_part[j].offset; + VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET(dst, j); node2 = dstnode; for (node = src->var_part[i].loc_chain; node; node = node->next) if (!((REG_P (dstnode->loc) @@ -2455,20 +2595,20 @@ variable_union (variable src, dataflow_set *set) dst->var_part[k].loc_chain = vui[0].lc; } - dst->var_part[k].offset = dst->var_part[j].offset; + VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (dst, j); } i--; j--; } else if ((i >= 0 && j >= 0 - && src->var_part[i].offset < dst->var_part[j].offset) + && VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j)) || i < 0) { dst->var_part[k] = dst->var_part[j]; j--; } else if ((i >= 0 && j >= 0 - && src->var_part[i].offset > dst->var_part[j].offset) + && VAR_PART_OFFSET (src, i) > VAR_PART_OFFSET (dst, j)) || j < 0) { location_chain *nextp; @@ -2492,7 +2632,7 @@ variable_union (variable src, dataflow_set *set) nextp = &new_lc->next; } - dst->var_part[k].offset = src->var_part[i].offset; + VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (src, i); i--; } dst->var_part[k].cur_loc = NULL; @@ -2543,25 +2683,46 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src) /* Whether the value is currently being expanded. */ #define VALUE_RECURSED_INTO(x) \ (RTL_FLAG_CHECK2 ("VALUE_RECURSED_INTO", (x), VALUE, DEBUG_EXPR)->used) -/* Whether the value is in changed_variables hash table. */ + +/* Whether no expansion was found, saving useless lookups. + It must only be set when VALUE_CHANGED is clear. */ +#define NO_LOC_P(x) \ + (RTL_FLAG_CHECK2 ("NO_LOC_P", (x), VALUE, DEBUG_EXPR)->return_val) + +/* Whether cur_loc in the value needs to be (re)computed. */ #define VALUE_CHANGED(x) \ (RTL_FLAG_CHECK1 ("VALUE_CHANGED", (x), VALUE)->frame_related) -/* Whether the decl is in changed_variables hash table. */ +/* Whether cur_loc in the decl needs to be (re)computed. */ #define DECL_CHANGED(x) TREE_VISITED (x) -/* Record that DV has been added into resp. removed from changed_variables - hashtable. */ +/* Record (if NEWV) that DV needs to have its cur_loc recomputed. For + user DECLs, this means they're in changed_variables. Values and + debug exprs may be left with this flag set if no user variable + requires them to be evaluated. */ static inline void set_dv_changed (decl_or_value dv, bool newv) { - if (dv_is_value_p (dv)) - VALUE_CHANGED (dv_as_value (dv)) = newv; - else - DECL_CHANGED (dv_as_decl (dv)) = newv; + switch (dv_onepart_p (dv)) + { + case ONEPART_VALUE: + if (newv) + NO_LOC_P (dv_as_value (dv)) = false; + VALUE_CHANGED (dv_as_value (dv)) = newv; + break; + + case ONEPART_DEXPR: + if (newv) + NO_LOC_P (DECL_RTL_KNOWN_SET (dv_as_decl (dv))) = false; + /* Fall through... */ + + default: + DECL_CHANGED (dv_as_decl (dv)) = newv; + break; + } } -/* Return true if DV is present in changed_variables hash table. */ +/* Return true if DV needs to have its cur_loc recomputed. */ static inline bool dv_changed_p (decl_or_value dv) @@ -2585,12 +2746,11 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars) if (!var) return NULL; - gcc_checking_assert (dv_onepart_p (var->dv)); + gcc_checking_assert (var->onepart); if (!var->n_var_parts) return NULL; - gcc_checking_assert (var->var_part[0].offset == 0); gcc_checking_assert (loc != dv_as_opaque (var->dv)); loc_code = GET_CODE (loc); @@ -2639,6 +2799,8 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars) return find_loc_in_1pdv (loc, rvar, vars); } + /* ??? Gotta look in cselib_val locations too. */ + return NULL; } @@ -2683,7 +2845,7 @@ insert_into_intersection (location_chain *nodep, rtx loc, *nodep = node; } -/* Insert in DEST the intersection the locations present in both +/* Insert in DEST the intersection of the locations present in both S1NODE and S2VAR, directly or indirectly. S1NODE is from a variable in DSM->cur, whereas S2VAR is from DSM->src. dvar is in DSM->dst. */ @@ -2700,11 +2862,10 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm, { location_chain s2node; - gcc_checking_assert (dv_onepart_p (s2var->dv)); + gcc_checking_assert (s2var->onepart); if (s2var->n_var_parts) { - gcc_checking_assert (s2var->var_part[0].offset == 0); s2node = s2var->var_part[0].loc_chain; for (; s1node && s2node; @@ -2750,6 +2911,8 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm, } } + /* ??? gotta look in cselib_val locations too. */ + /* ??? if the location is equivalent to any location in src, searched recursively @@ -2839,6 +3002,18 @@ loc_cmp (rtx x, rtx y) if (GET_CODE (y) == VALUE) return 1; + /* Entry value is the least preferable kind of expression. */ + if (GET_CODE (x) == ENTRY_VALUE) + { + if (GET_CODE (y) != ENTRY_VALUE) + return 1; + gcc_assert (GET_MODE (x) == GET_MODE (y)); + return loc_cmp (XEXP (x, 0), XEXP (y, 0)); + } + + if (GET_CODE (y) == ENTRY_VALUE) + return -1; + if (GET_CODE (x) == GET_CODE (y)) /* Compare operands below. */; else if (GET_CODE (x) < GET_CODE (y)) @@ -2933,186 +3108,23 @@ loc_cmp (rtx x, rtx y) return 0; } -/* If decl or value DVP refers to VALUE from *LOC, add backlinks - from VALUE to DVP. */ - -static int -add_value_chain (rtx *loc, void *dvp) -{ - decl_or_value dv, ldv; - value_chain vc, nvc; - void **slot; - - if (GET_CODE (*loc) == VALUE) - ldv = dv_from_value (*loc); - else if (GET_CODE (*loc) == DEBUG_EXPR) - ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc)); - else - return 0; - - if (dv_as_opaque (ldv) == dvp) - return 0; - - dv = (decl_or_value) dvp; - slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv), - INSERT); - if (!*slot) - { - vc = (value_chain) pool_alloc (value_chain_pool); - vc->dv = ldv; - vc->next = NULL; - vc->refcount = 0; - *slot = (void *) vc; - } - else - { - for (vc = ((value_chain) *slot)->next; vc; vc = vc->next) - if (dv_as_opaque (vc->dv) == dv_as_opaque (dv)) - break; - if (vc) - { - vc->refcount++; - return 0; - } - } - vc = (value_chain) *slot; - nvc = (value_chain) pool_alloc (value_chain_pool); - nvc->dv = dv; - nvc->next = vc->next; - nvc->refcount = 1; - vc->next = nvc; - return 0; -} - -/* If decl or value DVP refers to VALUEs from within LOC, add backlinks - from those VALUEs to DVP. */ - -static void -add_value_chains (decl_or_value dv, rtx loc) -{ - if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR) - { - add_value_chain (&loc, dv_as_opaque (dv)); - return; - } - if (REG_P (loc)) - return; - if (MEM_P (loc)) - loc = XEXP (loc, 0); - for_each_rtx (&loc, add_value_chain, dv_as_opaque (dv)); -} - -/* If CSELIB_VAL_PTR of value DV refer to VALUEs, add backlinks from those - VALUEs to DV. Add the same time get rid of ASM_OPERANDS from locs list, - that is something we never can express in .debug_info and can prevent - reverse ops from being used. */ - -static void -add_cselib_value_chains (decl_or_value dv) -{ - struct elt_loc_list **l; - - for (l = &CSELIB_VAL_PTR (dv_as_value (dv))->locs; *l;) - if (GET_CODE ((*l)->loc) == ASM_OPERANDS) - *l = (*l)->next; - else - { - for_each_rtx (&(*l)->loc, add_value_chain, dv_as_opaque (dv)); - l = &(*l)->next; - } -} - -/* If decl or value DVP refers to VALUE from *LOC, remove backlinks - from VALUE to DVP. */ - -static int -remove_value_chain (rtx *loc, void *dvp) -{ - decl_or_value dv, ldv; - value_chain vc; - void **slot; - - if (GET_CODE (*loc) == VALUE) - ldv = dv_from_value (*loc); - else if (GET_CODE (*loc) == DEBUG_EXPR) - ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc)); - else - return 0; - - if (dv_as_opaque (ldv) == dvp) - return 0; - - dv = (decl_or_value) dvp; - slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv), - NO_INSERT); - for (vc = (value_chain) *slot; vc->next; vc = vc->next) - if (dv_as_opaque (vc->next->dv) == dv_as_opaque (dv)) - { - value_chain dvc = vc->next; - gcc_assert (dvc->refcount > 0); - if (--dvc->refcount == 0) - { - vc->next = dvc->next; - pool_free (value_chain_pool, dvc); - if (vc->next == NULL && vc == (value_chain) *slot) - { - pool_free (value_chain_pool, vc); - htab_clear_slot (value_chains, slot); - } - } - return 0; - } - gcc_unreachable (); -} - -/* If decl or value DVP refers to VALUEs from within LOC, remove backlinks - from those VALUEs to DVP. */ - -static void -remove_value_chains (decl_or_value dv, rtx loc) -{ - if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR) - { - remove_value_chain (&loc, dv_as_opaque (dv)); - return; - } - if (REG_P (loc)) - return; - if (MEM_P (loc)) - loc = XEXP (loc, 0); - for_each_rtx (&loc, remove_value_chain, dv_as_opaque (dv)); -} - #if ENABLE_CHECKING -/* If CSELIB_VAL_PTR of value DV refer to VALUEs, remove backlinks from those - VALUEs to DV. */ - -static void -remove_cselib_value_chains (decl_or_value dv) -{ - struct elt_loc_list *l; - - for (l = CSELIB_VAL_PTR (dv_as_value (dv))->locs; l; l = l->next) - for_each_rtx (&l->loc, remove_value_chain, dv_as_opaque (dv)); -} - /* Check the order of entries in one-part variables. */ static int canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED) { variable var = (variable) *slot; - decl_or_value dv = var->dv; location_chain node, next; #ifdef ENABLE_RTL_CHECKING int i; for (i = 0; i < var->n_var_parts; i++) gcc_assert (var->var_part[0].cur_loc == NULL); - gcc_assert (!var->cur_loc_changed && !var->in_changed_variables); + gcc_assert (!var->in_changed_variables); #endif - if (!dv_onepart_p (dv)) + if (!var->onepart) return 1; gcc_assert (var->n_var_parts == 1); @@ -3186,7 +3198,7 @@ canonicalize_values_star (void **slot, void *data) bool has_value; bool has_marks; - if (!dv_onepart_p (dv)) + if (!var->onepart) return 1; gcc_checking_assert (var->n_var_parts == 1); @@ -3408,7 +3420,7 @@ canonicalize_vars_star (void **slot, void *data) variable cvar; location_chain cnode; - if (!dv_onepart_p (dv) || dv_is_value_p (dv)) + if (!var->onepart || var->onepart == ONEPART_VALUE) return 1; gcc_assert (var->n_var_parts == 1); @@ -3461,7 +3473,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) void **dstslot; variable s2var, dvar = NULL; decl_or_value dv = s1var->dv; - bool onepart = dv_onepart_p (dv); + onepart_enum_t onepart = s1var->onepart; rtx val; hashval_t dvhash; location_chain node, *nodep; @@ -3475,8 +3487,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) if (!onepart) return variable_union (s1var, dst); - gcc_checking_assert (s1var->n_var_parts == 1 - && s1var->var_part[0].offset == 0); + gcc_checking_assert (s1var->n_var_parts == 1); dvhash = dv_htab_hash (dv); if (dv_is_value_p (dv)) @@ -3493,16 +3504,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) dsm->src_onepart_cnt--; gcc_assert (s2var->var_part[0].loc_chain - && s2var->n_var_parts == 1 - && s2var->var_part[0].offset == 0); + && s2var->onepart == onepart + && s2var->n_var_parts == 1); dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash); if (dstslot) { dvar = (variable)*dstslot; gcc_assert (dvar->refcount == 1 - && dvar->n_var_parts == 1 - && dvar->var_part[0].offset == 0); + && dvar->onepart == onepart + && dvar->n_var_parts == 1); nodep = &dvar->var_part[0].loc_chain; } else @@ -3529,15 +3540,18 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) { if (node) { - dvar = (variable) pool_alloc (dv_pool (dv)); + dvar = (variable) pool_alloc (onepart_pool (onepart)); dvar->dv = dv; dvar->refcount = 1; dvar->n_var_parts = 1; - dvar->cur_loc_changed = false; + dvar->onepart = onepart; dvar->in_changed_variables = false; - dvar->var_part[0].offset = 0; dvar->var_part[0].loc_chain = node; dvar->var_part[0].cur_loc = NULL; + if (onepart) + VAR_LOC_1PAUX (dvar) = NULL; + else + VAR_PART_OFFSET (dvar, 0) = 0; dstslot = shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash, @@ -3662,15 +3676,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm) INSERT); if (!*slot) { - variable var = (variable) pool_alloc (dv_pool (dv)); + variable var = (variable) pool_alloc (onepart_pool + (ONEPART_VALUE)); var->dv = dv; var->refcount = 1; var->n_var_parts = 1; - var->cur_loc_changed = false; + var->onepart = ONEPART_VALUE; var->in_changed_variables = false; - var->var_part[0].offset = 0; var->var_part[0].loc_chain = NULL; var->var_part[0].cur_loc = NULL; + VAR_LOC_1PAUX (var) = NULL; *slot = var; } @@ -3717,9 +3732,8 @@ variable_merge_over_src (variable s2var, struct dfset_merge *dsm) { dataflow_set *dst = dsm->dst; decl_or_value dv = s2var->dv; - bool onepart = dv_onepart_p (dv); - if (!onepart) + if (!s2var->onepart) { void **dstp = shared_hash_find_slot (dst->vars, dv); *dstp = s2var; @@ -3864,7 +3878,7 @@ remove_duplicate_values (variable var) { location_chain node, *nodep; - gcc_assert (dv_onepart_p (var->dv)); + gcc_assert (var->onepart); gcc_assert (var->n_var_parts == 1); gcc_assert (var->refcount == 1); @@ -3915,7 +3929,7 @@ variable_post_merge_new_vals (void **slot, void *info) variable var = (variable)*slot; location_chain node; - if (!dv_onepart_p (var->dv) || !var->n_var_parts) + if (!var->onepart || !var->n_var_parts) return 1; gcc_assert (var->n_var_parts == 1); @@ -4146,13 +4160,11 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars) if (!var) return NULL; - gcc_assert (dv_onepart_p (var->dv)); + gcc_assert (var->onepart); if (!var->n_var_parts) return NULL; - gcc_assert (var->var_part[0].offset == 0); - VALUE_RECURSED_INTO (val) = true; for (node = var->var_part[0].loc_chain; node; node = node->next) @@ -4206,7 +4218,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data) dataflow_set *set = (dataflow_set *) data; variable var = (variable) *slot; - if (dv_is_decl_p (var->dv) && dv_onepart_p (var->dv)) + if (var->onepart == ONEPART_VDECL || var->onepart == ONEPART_DEXPR) { tree decl = dv_as_decl (var->dv); location_chain loc, *locp; @@ -4277,10 +4289,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data) { changed = true; var->var_part[0].cur_loc = NULL; - var->cur_loc_changed = true; } - add_value_chains (var->dv, loc->loc); - remove_value_chains (var->dv, old_loc); } locp = &loc->next; continue; @@ -4288,12 +4297,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data) if (emit_notes) { - remove_value_chains (var->dv, old_loc); if (old_loc == var->var_part[0].cur_loc) { changed = true; var->var_part[0].cur_loc = NULL; - var->cur_loc_changed = true; } } *locp = loc->next; @@ -4321,10 +4328,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data) dataflow_set *set = (dataflow_set *) data; variable var = (variable) *slot; - if (dv_is_value_p (var->dv)) + if (var->onepart == ONEPART_VALUE) { location_chain loc, *locp; bool changed = false; + rtx cur_loc; gcc_assert (var->n_var_parts == 1); @@ -4343,6 +4351,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data) gcc_assert (var->n_var_parts == 1); } + if (VAR_LOC_1PAUX (var)) + cur_loc = VAR_LOC_FROM (var); + else + cur_loc = var->var_part[0].cur_loc; + for (locp = &var->var_part[0].loc_chain, loc = *locp; loc; loc = *locp) { @@ -4353,17 +4366,16 @@ dataflow_set_remove_mem_locs (void **slot, void *data) continue; } - if (emit_notes) - remove_value_chains (var->dv, loc->loc); *locp = loc->next; /* If we have deleted the location which was last emitted we have to emit new location so add the variable to set of changed variables. */ - if (var->var_part[0].cur_loc == loc->loc) + if (cur_loc == loc->loc) { changed = true; var->var_part[0].cur_loc = NULL; - var->cur_loc_changed = true; + if (VAR_LOC_1PAUX (var)) + VAR_LOC_FROM (var) = NULL; } pool_free (loc_chain_pool, loc); } @@ -4467,20 +4479,24 @@ variable_different_p (variable var1, variable var2) if (var1 == var2) return false; + if (var1->onepart != var2->onepart) + return true; + if (var1->n_var_parts != var2->n_var_parts) return true; + if (var1->onepart && var1->n_var_parts) + { + gcc_checking_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv) + && var1->n_var_parts == 1); + /* One-part values have locations in a canonical order. */ + return onepart_variable_different_p (var1, var2); + } + for (i = 0; i < var1->n_var_parts; i++) { - if (var1->var_part[i].offset != var2->var_part[i].offset) + if (VAR_PART_OFFSET (var1, i) != VAR_PART_OFFSET (var2, i)) return true; - /* One-part values have locations in a canonical order. */ - if (i == 0 && var1->var_part[i].offset == 0 && dv_onepart_p (var1->dv)) - { - gcc_assert (var1->n_var_parts == 1 - && dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv)); - return onepart_variable_different_p (var1, var2); - } if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i])) return true; if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i])) @@ -5020,9 +5036,6 @@ log_op_type (rtx x, basic_block bb, rtx insn, /* All preserved VALUEs. */ static VEC (rtx, heap) *preserved_values; -/* Registers used in the current function for passing parameters. */ -static HARD_REG_SET argument_reg_set; - /* Ensure VAL is preserved and remember it in a vector for vt_emit_notes. */ static void @@ -5089,11 +5102,7 @@ add_uses (rtx *ploc, void *data) if (MEM_P (vloc) && !REG_P (XEXP (vloc, 0)) - && !MEM_P (XEXP (vloc, 0)) - && GET_CODE (XEXP (vloc, 0)) != ENTRY_VALUE - && (GET_CODE (XEXP (vloc, 0)) != PLUS - || XEXP (XEXP (vloc, 0), 0) != cfa_base_rtx - || !CONST_INT_P (XEXP (XEXP (vloc, 0), 1)))) + && !MEM_P (XEXP (vloc, 0))) { rtx mloc = vloc; enum machine_mode address_mode = get_address_mode (mloc); @@ -5105,16 +5114,24 @@ add_uses (rtx *ploc, void *data) { micro_operation moa; preserve_value (val); - mloc = cselib_subst_to_values (XEXP (mloc, 0), - GET_MODE (mloc)); - moa.type = MO_VAL_USE; - moa.insn = cui->insn; - moa.u.loc = gen_rtx_CONCAT (address_mode, - val->val_rtx, mloc); - if (dump_file && (dump_flags & TDF_DETAILS)) - log_op_type (moa.u.loc, cui->bb, cui->insn, - moa.type, dump_file); - VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa); + + if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE + && (GET_CODE (XEXP (mloc, 0)) != PLUS + || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx + || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1)))) + { + mloc = cselib_subst_to_values (XEXP (mloc, 0), + GET_MODE (mloc)); + moa.type = MO_VAL_USE; + moa.insn = cui->insn; + moa.u.loc = gen_rtx_CONCAT (address_mode, + val->val_rtx, mloc); + if (dump_file && (dump_flags & TDF_DETAILS)) + log_op_type (moa.u.loc, cui->bb, cui->insn, + moa.type, dump_file); + VEC_safe_push (micro_operation, heap, VTI (bb)->mos, + &moa); + } } } @@ -5122,7 +5139,7 @@ add_uses (rtx *ploc, void *data) && (GET_CODE (vloc) != CONST || for_each_rtx (&vloc, non_suitable_const, NULL))) /* For constants don't look up any value. */; - else if (!VAR_LOC_UNKNOWN_P (vloc) + else if (!VAR_LOC_UNKNOWN_P (vloc) && !unsuitable_loc (vloc) && (val = find_use_val (vloc, GET_MODE (oloc), cui))) { enum machine_mode mode2; @@ -5168,32 +5185,36 @@ add_uses (rtx *ploc, void *data) if (MEM_P (oloc) && !REG_P (XEXP (oloc, 0)) - && !MEM_P (XEXP (oloc, 0)) - && GET_CODE (XEXP (oloc, 0)) != ENTRY_VALUE - && (GET_CODE (XEXP (oloc, 0)) != PLUS - || XEXP (XEXP (oloc, 0), 0) != cfa_base_rtx - || !CONST_INT_P (XEXP (XEXP (oloc, 0), 1)))) + && !MEM_P (XEXP (oloc, 0))) { rtx mloc = oloc; enum machine_mode address_mode = get_address_mode (mloc); cselib_val *val = cselib_lookup (XEXP (mloc, 0), address_mode, 0, - GET_MODE (mloc)); + GET_MODE (mloc)); if (val && !cselib_preserved_value_p (val)) { micro_operation moa; preserve_value (val); - mloc = cselib_subst_to_values (XEXP (mloc, 0), - GET_MODE (mloc)); - moa.type = MO_VAL_USE; - moa.insn = cui->insn; - moa.u.loc = gen_rtx_CONCAT (address_mode, - val->val_rtx, mloc); - if (dump_file && (dump_flags & TDF_DETAILS)) - log_op_type (moa.u.loc, cui->bb, cui->insn, - moa.type, dump_file); - VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa); + + if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE + && (GET_CODE (XEXP (mloc, 0)) != PLUS + || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx + || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1)))) + { + mloc = cselib_subst_to_values (XEXP (mloc, 0), + GET_MODE (mloc)); + moa.type = MO_VAL_USE; + moa.insn = cui->insn; + moa.u.loc = gen_rtx_CONCAT (address_mode, + val->val_rtx, mloc); + if (dump_file && (dump_flags & TDF_DETAILS)) + log_op_type (moa.u.loc, cui->bb, cui->insn, + moa.type, dump_file); + VEC_safe_push (micro_operation, heap, VTI (bb)->mos, + &moa); + } } } @@ -5255,7 +5276,18 @@ add_uses_1 (rtx *x, void *cui) for_each_rtx (x, add_uses, cui); } -#define EXPR_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH)) +/* This is the value used during expansion of locations. We want it + to be unbounded, so that variables expanded deep in a recursion + nest are fully evaluated, so that their values are cached + correctly. We avoid recursion cycles through other means, and we + don't unshare RTL, so excess complexity is not a problem. */ +#define EXPR_DEPTH (INT_MAX) +/* We use this to keep too-complex expressions from being emitted as + location notes, and then to debug information. Users can trade + compile time for ridiculously complex expressions, although they're + seldom useful, and they may often have to be discarded as not + representable anyway. */ +#define EXPR_USE_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH)) /* Attempt to reverse the EXPR operation in the debug info. Say for reg1 = reg2 + 6 even when reg2 is no longer live we @@ -5382,10 +5414,8 @@ add_stores (rtx loc, const_rtx expr, void *cuip) mo.u.loc = loc; if (GET_CODE (expr) == SET && SET_DEST (expr) == loc - && REGNO (loc) < FIRST_PSEUDO_REGISTER - && TEST_HARD_REG_BIT (argument_reg_set, REGNO (loc)) - && find_use_val (loc, mode, cui) - && GET_CODE (SET_SRC (expr)) != ASM_OPERANDS) + && !unsuitable_loc (SET_SRC (expr)) + && find_use_val (loc, mode, cui)) { gcc_checking_assert (type == MO_VAL_SET); mo.u.loc = gen_rtx_SET (VOIDmode, loc, SET_SRC (expr)); @@ -5422,11 +5452,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip) { if (MEM_P (loc) && type == MO_VAL_SET && !REG_P (XEXP (loc, 0)) - && !MEM_P (XEXP (loc, 0)) - && GET_CODE (XEXP (loc, 0)) != ENTRY_VALUE - && (GET_CODE (XEXP (loc, 0)) != PLUS - || XEXP (XEXP (loc, 0), 0) != cfa_base_rtx - || !CONST_INT_P (XEXP (XEXP (loc, 0), 1)))) + && !MEM_P (XEXP (loc, 0))) { rtx mloc = loc; enum machine_mode address_mode = get_address_mode (mloc); @@ -5437,15 +5463,23 @@ add_stores (rtx loc, const_rtx expr, void *cuip) if (val && !cselib_preserved_value_p (val)) { preserve_value (val); - mo.type = MO_VAL_USE; - mloc = cselib_subst_to_values (XEXP (mloc, 0), - GET_MODE (mloc)); - mo.u.loc = gen_rtx_CONCAT (address_mode, val->val_rtx, mloc); - mo.insn = cui->insn; - if (dump_file && (dump_flags & TDF_DETAILS)) - log_op_type (mo.u.loc, cui->bb, cui->insn, - mo.type, dump_file); - VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo); + + if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE + && (GET_CODE (XEXP (mloc, 0)) != PLUS + || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx + || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1)))) + { + mloc = cselib_subst_to_values (XEXP (mloc, 0), + GET_MODE (mloc)); + mo.type = MO_VAL_USE; + mo.insn = cui->insn; + mo.u.loc = gen_rtx_CONCAT (address_mode, + val->val_rtx, mloc); + if (dump_file && (dump_flags & TDF_DETAILS)) + log_op_type (mo.u.loc, cui->bb, cui->insn, + mo.type, dump_file); + VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo); + } } } @@ -5893,7 +5927,7 @@ prepare_call_arguments (basic_block bb, rtx insn) tree dtemp = VEC_index (tree, *debug_args, ix + 1); enum machine_mode mode = DECL_MODE (dtemp); item = gen_rtx_DEBUG_PARAMETER_REF (mode, param); - item = gen_rtx_CONCAT (mode, item, DECL_RTL (dtemp)); + item = gen_rtx_CONCAT (mode, item, DECL_RTL_KNOWN_SET (dtemp)); call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item, call_arguments); } @@ -6705,7 +6739,7 @@ dump_var (variable var) for (i = 0; i < var->n_var_parts; i++) { fprintf (dump_file, " offset %ld\n", - (long) var->var_part[i].offset); + (long)(var->onepart ? 0 : VAR_PART_OFFSET (var, i))); for (node = var->var_part[i].loc_chain; node; node = node->next) { fprintf (dump_file, " "); @@ -6766,6 +6800,73 @@ dump_dataflow_sets (void) } } +/* Return the variable for DV in dropped_values, inserting one if + requested with INSERT. */ + +static inline variable +variable_from_dropped (decl_or_value dv, enum insert_option insert) +{ + void **slot; + variable empty_var; + onepart_enum_t onepart; + + slot = htab_find_slot_with_hash (dropped_values, dv, dv_htab_hash (dv), + insert); + + if (!slot) + return NULL; + + if (*slot) + return (variable) *slot; + + gcc_checking_assert (insert == INSERT); + + onepart = dv_onepart_p (dv); + + gcc_checking_assert (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR); + + empty_var = (variable) pool_alloc (onepart_pool (onepart)); + empty_var->dv = dv; + empty_var->refcount = 1; + empty_var->n_var_parts = 0; + empty_var->onepart = onepart; + empty_var->in_changed_variables = false; + empty_var->var_part[0].loc_chain = NULL; + empty_var->var_part[0].cur_loc = NULL; + VAR_LOC_1PAUX (empty_var) = NULL; + set_dv_changed (dv, true); + + *slot = empty_var; + + return empty_var; +} + +/* Recover the one-part aux from dropped_values. */ + +static struct onepart_aux * +recover_dropped_1paux (variable var) +{ + variable dvar; + + gcc_checking_assert (var->onepart); + + if (VAR_LOC_1PAUX (var)) + return VAR_LOC_1PAUX (var); + + if (var->onepart == ONEPART_VDECL) + return NULL; + + dvar = variable_from_dropped (var->dv, NO_INSERT); + + if (!dvar) + return NULL; + + VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (dvar); + VAR_LOC_1PAUX (dvar) = NULL; + + return VAR_LOC_1PAUX (var); +} + /* Add variable VAR to the hash table of changed variables and if it has no locations delete it from SET's hash table. */ @@ -6777,7 +6878,6 @@ variable_was_changed (variable var, dataflow_set *set) if (emit_notes) { void **slot; - bool old_cur_loc_changed = false; /* Remember this decl or VALUE has been added to changed_variables. */ set_dv_changed (var->dv, true); @@ -6791,30 +6891,76 @@ variable_was_changed (variable var, dataflow_set *set) variable old_var = (variable) *slot; gcc_assert (old_var->in_changed_variables); old_var->in_changed_variables = false; - old_cur_loc_changed = old_var->cur_loc_changed; + if (var != old_var && var->onepart) + { + /* Restore the auxiliary info from an empty variable + previously created for changed_variables, so it is + not lost. */ + gcc_checking_assert (!VAR_LOC_1PAUX (var)); + VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (old_var); + VAR_LOC_1PAUX (old_var) = NULL; + } variable_htab_free (*slot); } + if (set && var->n_var_parts == 0) { - variable empty_var; + onepart_enum_t onepart = var->onepart; + variable empty_var = NULL; + void **dslot = NULL; - empty_var = (variable) pool_alloc (dv_pool (var->dv)); - empty_var->dv = var->dv; - empty_var->refcount = 1; - empty_var->n_var_parts = 0; - empty_var->cur_loc_changed = true; + if (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR) + { + dslot = htab_find_slot_with_hash (dropped_values, var->dv, + dv_htab_hash (var->dv), + INSERT); + empty_var = (variable) *dslot; + + if (empty_var) + { + gcc_checking_assert (!empty_var->in_changed_variables); + if (!VAR_LOC_1PAUX (var)) + { + VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (empty_var); + VAR_LOC_1PAUX (empty_var) = NULL; + } + else + gcc_checking_assert (!VAR_LOC_1PAUX (empty_var)); + } + } + + if (!empty_var) + { + empty_var = (variable) pool_alloc (onepart_pool (onepart)); + empty_var->dv = var->dv; + empty_var->refcount = 1; + empty_var->n_var_parts = 0; + empty_var->onepart = onepart; + if (dslot) + { + empty_var->refcount++; + *dslot = empty_var; + } + } + else + empty_var->refcount++; empty_var->in_changed_variables = true; *slot = empty_var; + if (onepart) + { + empty_var->var_part[0].loc_chain = NULL; + empty_var->var_part[0].cur_loc = NULL; + VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (var); + VAR_LOC_1PAUX (var) = NULL; + } goto drop_var; } else { + if (var->onepart && !VAR_LOC_1PAUX (var)) + recover_dropped_1paux (var); var->refcount++; var->in_changed_variables = true; - /* If within processing one uop a variable is deleted - and then readded, we need to assume it has changed. */ - if (old_cur_loc_changed) - var->cur_loc_changed = true; *slot = var; } } @@ -6849,13 +6995,24 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset, { int pos, low, high; + if (var->onepart) + { + if (offset != 0) + return -1; + + if (insertion_point) + *insertion_point = 0; + + return var->n_var_parts - 1; + } + /* Find the location part. */ low = 0; high = var->n_var_parts; while (low != high) { pos = (low + high) / 2; - if (var->var_part[pos].offset < offset) + if (VAR_PART_OFFSET (var, pos) < offset) low = pos + 1; else high = pos; @@ -6865,7 +7022,7 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset, if (insertion_point) *insertion_point = pos; - if (pos < var->n_var_parts && var->var_part[pos].offset == offset) + if (pos < var->n_var_parts && VAR_PART_OFFSET (var, pos) == offset) return pos; return -1; @@ -6880,26 +7037,34 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, location_chain node, next; location_chain *nextp; variable var; - bool onepart = dv_onepart_p (dv); - - gcc_assert (offset == 0 || !onepart); - gcc_assert (loc != dv_as_opaque (dv)); + onepart_enum_t onepart; var = (variable) *slot; + if (var) + onepart = var->onepart; + else + onepart = dv_onepart_p (dv); + + gcc_checking_assert (offset == 0 || !onepart); + gcc_checking_assert (loc != dv_as_opaque (dv)); + if (! flag_var_tracking_uninit) initialized = VAR_INIT_STATUS_INITIALIZED; if (!var) { /* Create new variable information. */ - var = (variable) pool_alloc (dv_pool (dv)); + var = (variable) pool_alloc (onepart_pool (onepart)); var->dv = dv; var->refcount = 1; var->n_var_parts = 1; - var->cur_loc_changed = false; + var->onepart = onepart; var->in_changed_variables = false; - var->var_part[0].offset = offset; + if (var->onepart) + VAR_LOC_1PAUX (var) = NULL; + else + VAR_PART_OFFSET (var, 0) = offset; var->var_part[0].loc_chain = NULL; var->var_part[0].cur_loc = NULL; *slot = var; @@ -7054,7 +7219,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, /* We track only variables whose size is <= MAX_VAR_PARTS bytes thus there are at most MAX_VAR_PARTS different offsets. */ gcc_assert (var->n_var_parts < MAX_VAR_PARTS - && (!var->n_var_parts || !dv_onepart_p (var->dv))); + && (!var->n_var_parts || !onepart)); /* We have to move the elements of array starting at index inspos to the next position. */ @@ -7062,7 +7227,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, var->var_part[pos] = var->var_part[pos - 1]; var->n_var_parts++; - var->var_part[pos].offset = offset; + gcc_checking_assert (!onepart); + VAR_PART_OFFSET (var, pos) = offset; var->var_part[pos].loc_chain = NULL; var->var_part[pos].cur_loc = NULL; } @@ -7083,10 +7249,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, if (node->set_src != NULL && set_src == NULL) set_src = node->set_src; if (var->var_part[pos].cur_loc == node->loc) - { - var->var_part[pos].cur_loc = NULL; - var->cur_loc_changed = true; - } + var->var_part[pos].cur_loc = NULL; pool_free (loc_chain_pool, node); *nextp = next; break; @@ -7106,9 +7269,6 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot, node->next = *nextp; *nextp = node; - if (onepart && emit_notes) - add_value_chains (var->dv, loc); - /* If no location was emitted do so. */ if (var->var_part[pos].cur_loc == NULL) variable_was_changed (var, set); @@ -7238,6 +7398,7 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, location_chain node, next; location_chain *nextp; bool changed; + rtx cur_loc; if (shared_var_p (var, set->vars)) { @@ -7258,6 +7419,11 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, } } + if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var)) + cur_loc = VAR_LOC_FROM (var); + else + cur_loc = var->var_part[pos].cur_loc; + /* Delete the location part. */ changed = false; nextp = &var->var_part[pos].loc_chain; @@ -7268,16 +7434,15 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, && REGNO (node->loc) == REGNO (loc)) || rtx_equal_p (node->loc, loc)) { - if (emit_notes && pos == 0 && dv_onepart_p (var->dv)) - remove_value_chains (var->dv, node->loc); /* If we have deleted the location which was last emitted we have to emit new location so add the variable to set of changed variables. */ - if (var->var_part[pos].cur_loc == node->loc) + if (cur_loc == node->loc) { changed = true; var->var_part[pos].cur_loc = NULL; - var->cur_loc_changed = true; + if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var)) + VAR_LOC_FROM (var) = NULL; } pool_free (loc_chain_pool, node); *nextp = next; @@ -7291,8 +7456,6 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot, { changed = true; var->n_var_parts--; - if (emit_notes) - var->cur_loc_changed = true; while (pos < var->n_var_parts) { var->var_part[pos] = var->var_part[pos + 1]; @@ -7321,6 +7484,12 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv, delete_slot_part (set, loc, slot, offset); } +DEF_VEC_P (variable); +DEF_VEC_ALLOC_P (variable, heap); + +DEF_VEC_ALLOC_P_STACK (rtx); +#define VEC_rtx_stack_alloc(alloc) VEC_stack_alloc (rtx, alloc) + /* Structure for passing some other parameters to function vt_expand_loc_callback. */ struct expand_loc_callback_data @@ -7328,56 +7497,368 @@ struct expand_loc_callback_data /* The variables and values active at this point. */ htab_t vars; - /* True in vt_expand_loc_dummy calls, no rtl should be allocated. - Non-NULL should be returned if vt_expand_loc would return - non-NULL in that case, NULL otherwise. cur_loc_changed should be - computed and cur_loc recomputed when possible (but just once - per emit_notes_for_changes call). */ - bool dummy; - - /* True if expansion of subexpressions had to recompute some - VALUE/DEBUG_EXPR_DECL's cur_loc or used a VALUE/DEBUG_EXPR_DECL - whose cur_loc has been already recomputed during current - emit_notes_for_changes call. */ - bool cur_loc_changed; - - /* True if cur_loc should be ignored and any possible location - returned. */ - bool ignore_cur_loc; + /* Stack of values and debug_exprs under expansion, and their + children. */ + VEC (rtx, stack) *expanding; + + /* Stack of values and debug_exprs whose expansion hit recursion + cycles. They will have VALUE_RECURSED_INTO marked when added to + this list. This flag will be cleared if any of its dependencies + resolves to a valid location. So, if the flag remains set at the + end of the search, we know no valid location for this one can + possibly exist. */ + VEC (rtx, stack) *pending; + + /* The maximum depth among the sub-expressions under expansion. + Zero indicates no expansion so far. */ + int depth; }; +/* Allocate the one-part auxiliary data structure for VAR, with enough + room for COUNT dependencies. */ + +static void +loc_exp_dep_alloc (variable var, int count) +{ + size_t allocsize; + + gcc_checking_assert (var->onepart); + + /* We can be called with COUNT == 0 to allocate the data structure + without any dependencies, e.g. for the backlinks only. However, + if we are specifying a COUNT, then the dependency list must have + been emptied before. It would be possible to adjust pointers or + force it empty here, but this is better done at an earlier point + in the algorithm, so we instead leave an assertion to catch + errors. */ + gcc_checking_assert (!count + || VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var))); + + if (VAR_LOC_1PAUX (var) + && VEC_space (loc_exp_dep, VAR_LOC_DEP_VEC (var), count)) + return; + + allocsize = offsetof (struct onepart_aux, deps) + + VEC_embedded_size (loc_exp_dep, count); + + if (VAR_LOC_1PAUX (var)) + { + VAR_LOC_1PAUX (var) = XRESIZEVAR (struct onepart_aux, + VAR_LOC_1PAUX (var), allocsize); + /* If the reallocation moves the onepaux structure, the + back-pointer to BACKLINKS in the first list member will still + point to its old location. Adjust it. */ + if (VAR_LOC_DEP_LST (var)) + VAR_LOC_DEP_LST (var)->pprev = VAR_LOC_DEP_LSTP (var); + } + else + { + VAR_LOC_1PAUX (var) = XNEWVAR (struct onepart_aux, allocsize); + *VAR_LOC_DEP_LSTP (var) = NULL; + VAR_LOC_FROM (var) = NULL; + VAR_LOC_DEPTH (var) = 0; + } + VEC_embedded_init (loc_exp_dep, VAR_LOC_DEP_VEC (var), count); +} + +/* Remove all entries from the vector of active dependencies of VAR, + removing them from the back-links lists too. */ + +static void +loc_exp_dep_clear (variable var) +{ + while (!VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var))) + { + loc_exp_dep *led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var)); + if (led->next) + led->next->pprev = led->pprev; + if (led->pprev) + *led->pprev = led->next; + VEC_pop (loc_exp_dep, VAR_LOC_DEP_VEC (var)); + } +} + +/* Insert an active dependency from VAR on X to the vector of + dependencies, and add the corresponding back-link to X's list of + back-links in VARS. */ + +static void +loc_exp_insert_dep (variable var, rtx x, htab_t vars) +{ + decl_or_value dv; + variable xvar; + loc_exp_dep *led; + + dv = dv_from_rtx (x); + + /* ??? Build a vector of variables parallel to EXPANDING, to avoid + an additional look up? */ + xvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv)); + + if (!xvar) + { + xvar = variable_from_dropped (dv, NO_INSERT); + gcc_checking_assert (xvar); + } + + /* No point in adding the same backlink more than once. This may + arise if say the same value appears in two complex expressions in + the same loc_list, or even more than once in a single + expression. */ + if (VAR_LOC_DEP_LST (xvar) && VAR_LOC_DEP_LST (xvar)->dv == var->dv) + return; + + VEC_quick_push (loc_exp_dep, VAR_LOC_DEP_VEC (var), NULL); + led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var)); + led->dv = var->dv; + led->value = x; + + loc_exp_dep_alloc (xvar, 0); + led->pprev = VAR_LOC_DEP_LSTP (xvar); + led->next = *led->pprev; + if (led->next) + led->next->pprev = &led->next; + *led->pprev = led; +} + +/* Create active dependencies of VAR on COUNT values starting at + VALUE, and corresponding back-links to the entries in VARS. Return + true if we found any pending-recursion results. */ + +static bool +loc_exp_dep_set (variable var, rtx result, rtx *value, int count, htab_t vars) +{ + bool pending_recursion = false; + + gcc_checking_assert (VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var))); + + /* Set up all dependencies from last_child (as set up at the end of + the loop above) to the end. */ + loc_exp_dep_alloc (var, count); + + while (count--) + { + rtx x = *value++; + + if (!pending_recursion) + pending_recursion = !result && VALUE_RECURSED_INTO (x); + + loc_exp_insert_dep (var, x, vars); + } + + return pending_recursion; +} + +/* Notify the back-links of IVAR that are pending recursion that we + have found a non-NIL value for it, so they are cleared for another + attempt to compute a current location. */ + +static void +notify_dependents_of_resolved_value (variable ivar, htab_t vars) +{ + loc_exp_dep *led, *next; + + for (led = VAR_LOC_DEP_LST (ivar); led; led = next) + { + decl_or_value dv = led->dv; + variable var; + + next = led->next; + + if (dv_is_value_p (dv)) + { + rtx value = dv_as_value (dv); + + /* If we have already resolved it, leave it alone. */ + if (!VALUE_RECURSED_INTO (value)) + continue; + + /* Check that VALUE_RECURSED_INTO, true from the test above, + implies NO_LOC_P. */ + gcc_checking_assert (NO_LOC_P (value)); + + /* We won't notify variables that are being expanded, + because their dependency list is cleared before + recursing. */ + VALUE_RECURSED_INTO (value) = false; + + gcc_checking_assert (dv_changed_p (dv)); + } + else if (!dv_changed_p (dv)) + continue; + + var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv)); + + if (!var) + var = variable_from_dropped (dv, NO_INSERT); + + if (var) + notify_dependents_of_resolved_value (var, vars); + + if (next) + next->pprev = led->pprev; + if (led->pprev) + *led->pprev = next; + led->next = NULL; + led->pprev = NULL; + } +} + +static rtx vt_expand_loc_callback (rtx x, bitmap regs, + int max_depth, void *data); + +/* Return the combined depth, when one sub-expression evaluated to + BEST_DEPTH and the previous known depth was SAVED_DEPTH. */ + +static inline int +update_depth (int saved_depth, int best_depth) +{ + /* If we didn't find anything, stick with what we had. */ + if (!best_depth) + return saved_depth; + + /* If we found hadn't found anything, use the depth of the current + expression. Do NOT add one extra level, we want to compute the + maximum depth among sub-expressions. We'll increment it later, + if appropriate. */ + if (!saved_depth) + return best_depth; + + if (saved_depth < best_depth) + return best_depth; + else + return saved_depth; +} + +/* Expand VAR to a location RTX, updating its cur_loc. Use REGS and + DATA for cselib expand callback. If PENDRECP is given, indicate in + it whether any sub-expression couldn't be fully evaluated because + it is pending recursion resolution. */ + +static inline rtx +vt_expand_var_loc_chain (variable var, bitmap regs, void *data, bool *pendrecp) +{ + struct expand_loc_callback_data *elcd + = (struct expand_loc_callback_data *) data; + location_chain loc, next; + rtx result = NULL; + int first_child, result_first_child, last_child; + bool pending_recursion; + rtx loc_from = NULL; + struct elt_loc_list *cloc = NULL; + int depth, saved_depth = elcd->depth; + + /* Clear all backlinks pointing at this, so that we're not notified + while we're active. */ + loc_exp_dep_clear (var); + + if (var->onepart == ONEPART_VALUE) + { + cselib_val *val = CSELIB_VAL_PTR (dv_as_value (var->dv)); + + gcc_checking_assert (cselib_preserved_value_p (val)); + + cloc = val->locs; + } + + first_child = result_first_child = last_child + = VEC_length (rtx, elcd->expanding); + + /* Attempt to expand each available location in turn. */ + for (next = loc = var->n_var_parts ? var->var_part[0].loc_chain : NULL; + loc || cloc; loc = next) + { + result_first_child = last_child; + + if (!loc || (GET_CODE (loc->loc) == ENTRY_VALUE && cloc)) + { + loc_from = cloc->loc; + next = loc; + cloc = cloc->next; + if (unsuitable_loc (loc_from)) + continue; + } + else + { + loc_from = loc->loc; + next = loc->next; + } + + gcc_checking_assert (!unsuitable_loc (loc_from)); + + elcd->depth = 0; + result = cselib_expand_value_rtx_cb (loc_from, regs, EXPR_DEPTH, + vt_expand_loc_callback, data); + last_child = VEC_length (rtx, elcd->expanding); + + if (result) + { + depth = elcd->depth; + + gcc_checking_assert (depth || result_first_child == last_child); + + if (last_child - result_first_child != 1) + depth++; + + if (depth <= EXPR_USE_DEPTH) + break; + + result = NULL; + } + + /* Set it up in case we leave the loop. */ + depth = 0; + loc_from = NULL; + result_first_child = first_child; + } + + /* Register all encountered dependencies as active. */ + pending_recursion = loc_exp_dep_set + (var, result, VEC_address (rtx, elcd->expanding) + result_first_child, + last_child - result_first_child, elcd->vars); + + VEC_truncate (rtx, elcd->expanding, first_child); + + /* Record where the expansion came from. */ + gcc_checking_assert (!result || !pending_recursion); + VAR_LOC_FROM (var) = loc_from; + VAR_LOC_DEPTH (var) = depth; + + elcd->depth = update_depth (saved_depth, depth); + + /* Indicate whether any of the dependencies are pending recursion + resolution. */ + if (pendrecp) + *pendrecp = pending_recursion; + + if (!pendrecp || !pending_recursion) + var->var_part[0].cur_loc = result; + + return result; +} + /* Callback for cselib_expand_value, that looks for expressions holding the value in the var-tracking hash tables. Return X for standard processing, anything else is to be used as-is. */ static rtx -vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data) +vt_expand_loc_callback (rtx x, bitmap regs, + int max_depth ATTRIBUTE_UNUSED, + void *data) { struct expand_loc_callback_data *elcd = (struct expand_loc_callback_data *) data; - bool dummy = elcd->dummy; - bool cur_loc_changed = elcd->cur_loc_changed; - rtx cur_loc; decl_or_value dv; variable var; - location_chain loc; - rtx result, subreg, xret; + rtx result, subreg; + bool pending_recursion = false; + bool from_empty = false; switch (GET_CODE (x)) { case SUBREG: - if (dummy) - { - if (cselib_dummy_expand_value_rtx_cb (SUBREG_REG (x), regs, - max_depth - 1, - vt_expand_loc_callback, data)) - return pc_rtx; - else - return NULL; - } - subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs, - max_depth - 1, + EXPR_DEPTH, vt_expand_loc_callback, data); if (!subreg) @@ -7395,148 +7876,172 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data) return result; case DEBUG_EXPR: - dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x)); - xret = NULL; - break; - case VALUE: - dv = dv_from_value (x); - xret = x; + dv = dv_from_rtx (x); break; default: return x; } - if (VALUE_RECURSED_INTO (x)) + VEC_safe_push (rtx, stack, elcd->expanding, x); + + /* Check that VALUE_RECURSED_INTO implies NO_LOC_P. */ + gcc_checking_assert (!VALUE_RECURSED_INTO (x) || NO_LOC_P (x)); + + if (NO_LOC_P (x)) return NULL; var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv)); if (!var) { - if (dummy && dv_changed_p (dv)) - elcd->cur_loc_changed = true; - return xret; + from_empty = true; + var = variable_from_dropped (dv, INSERT); } - if (var->n_var_parts == 0) + gcc_checking_assert (var); + + if (!dv_changed_p (dv)) { - if (dummy) - elcd->cur_loc_changed = true; - return xret; - } + gcc_checking_assert (!NO_LOC_P (x)); + gcc_checking_assert (var->var_part[0].cur_loc); + gcc_checking_assert (VAR_LOC_1PAUX (var)); + gcc_checking_assert (VAR_LOC_1PAUX (var)->depth); - gcc_assert (var->n_var_parts == 1); + elcd->depth = update_depth (elcd->depth, VAR_LOC_1PAUX (var)->depth); + + return var->var_part[0].cur_loc; + } VALUE_RECURSED_INTO (x) = true; - result = NULL; + /* This is tentative, but it makes some tests simpler. */ + NO_LOC_P (x) = true; + + gcc_checking_assert (var->n_var_parts == 1 || from_empty); + + result = vt_expand_var_loc_chain (var, regs, data, &pending_recursion); - if (var->var_part[0].cur_loc && !elcd->ignore_cur_loc) + if (pending_recursion) { - if (dummy) - { - if (cselib_dummy_expand_value_rtx_cb (var->var_part[0].cur_loc, regs, - max_depth, - vt_expand_loc_callback, data)) - result = pc_rtx; - } - else - result = cselib_expand_value_rtx_cb (var->var_part[0].cur_loc, regs, - max_depth, - vt_expand_loc_callback, data); - if (result) - set_dv_changed (dv, false); - cur_loc = var->var_part[0].cur_loc; + gcc_checking_assert (!result); + VEC_safe_push (rtx, stack, elcd->pending, x); } else - cur_loc = NULL_RTX; - if (!result && (dv_changed_p (dv) || elcd->ignore_cur_loc)) - { - if (!elcd->ignore_cur_loc) - set_dv_changed (dv, false); - for (loc = var->var_part[0].loc_chain; loc; loc = loc->next) - if (loc->loc == cur_loc) - continue; - else if (dummy) - { - elcd->cur_loc_changed = cur_loc_changed; - if (cselib_dummy_expand_value_rtx_cb (loc->loc, regs, max_depth, - vt_expand_loc_callback, - data)) - { - result = pc_rtx; - break; - } - } - else - { - result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth, - vt_expand_loc_callback, data); - if (result) - break; - } - if (dummy && (result || var->var_part[0].cur_loc)) - var->cur_loc_changed = true; - if (!elcd->ignore_cur_loc) - var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX; - } - if (dummy) { - if (var->cur_loc_changed) - elcd->cur_loc_changed = true; - else if (!result && var->var_part[0].cur_loc == NULL_RTX) - elcd->cur_loc_changed = cur_loc_changed; + NO_LOC_P (x) = !result; + VALUE_RECURSED_INTO (x) = false; + set_dv_changed (dv, false); + + if (result) + notify_dependents_of_resolved_value (var, elcd->vars); } - VALUE_RECURSED_INTO (x) = false; - if (result) - return result; - else - return xret; + return result; } -/* Expand VALUEs in LOC, using VARS as well as cselib's equivalence - tables. */ +/* While expanding variables, we may encounter recursion cycles + because of mutual (possibly indirect) dependencies between two + particular variables (or values), say A and B. If we're trying to + expand A when we get to B, which in turn attempts to expand A, if + we can't find any other expansion for B, we'll add B to this + pending-recursion stack, and tentatively return NULL for its + location. This tentative value will be used for any other + occurrences of B, unless A gets some other location, in which case + it will notify B that it is worth another try at computing a + location for it, and it will use the location computed for A then. + At the end of the expansion, the tentative NULL locations become + final for all members of PENDING that didn't get a notification. + This function performs this finalization of NULL locations. */ + +static void +resolve_expansions_pending_recursion (VEC (rtx, stack) *pending) +{ + while (!VEC_empty (rtx, pending)) + { + rtx x = VEC_pop (rtx, pending); + decl_or_value dv; + + if (!VALUE_RECURSED_INTO (x)) + continue; + + gcc_checking_assert (NO_LOC_P (x)); + VALUE_RECURSED_INTO (x) = false; + dv = dv_from_rtx (x); + gcc_checking_assert (dv_changed_p (dv)); + set_dv_changed (dv, false); + } +} + +/* Initialize expand_loc_callback_data D with variable hash table V. + It must be a macro because of alloca (VEC stack). */ +#define INIT_ELCD(d, v) \ + do \ + { \ + (d).vars = (v); \ + (d).expanding = VEC_alloc (rtx, stack, 4); \ + (d).pending = VEC_alloc (rtx, stack, 4); \ + (d).depth = 0; \ + } \ + while (0) +/* Finalize expand_loc_callback_data D, resolved to location L. */ +#define FINI_ELCD(d, l) \ + do \ + { \ + resolve_expansions_pending_recursion ((d).pending); \ + VEC_free (rtx, stack, (d).pending); \ + VEC_free (rtx, stack, (d).expanding); \ + \ + if ((l) && MEM_P (l)) \ + (l) = targetm.delegitimize_address (l); \ + } \ + while (0) + +/* Expand VALUEs and DEBUG_EXPRs in LOC to a location, using the + equivalences in VARS, updating their CUR_LOCs in the process. */ static rtx -vt_expand_loc (rtx loc, htab_t vars, bool ignore_cur_loc) +vt_expand_loc (rtx loc, htab_t vars) { struct expand_loc_callback_data data; + rtx result; if (!MAY_HAVE_DEBUG_INSNS) return loc; - data.vars = vars; - data.dummy = false; - data.cur_loc_changed = false; - data.ignore_cur_loc = ignore_cur_loc; - loc = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH, - vt_expand_loc_callback, &data); + INIT_ELCD (data, vars); - if (loc && MEM_P (loc)) - loc = targetm.delegitimize_address (loc); - return loc; + result = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH, + vt_expand_loc_callback, &data); + + FINI_ELCD (data, result); + + return result; } -/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc - would succeed or not, without actually allocating new rtxes. */ +/* Expand the one-part VARiable to a location, using the equivalences + in VARS, updating their CUR_LOCs in the process. */ -static bool -vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed) +static rtx +vt_expand_1pvar (variable var, htab_t vars) { struct expand_loc_callback_data data; - bool ret; - - gcc_assert (MAY_HAVE_DEBUG_INSNS); - data.vars = vars; - data.dummy = true; - data.cur_loc_changed = false; - data.ignore_cur_loc = false; - ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH, - vt_expand_loc_callback, &data); - *pcur_loc_changed = data.cur_loc_changed; - return ret; + rtx loc; + + gcc_checking_assert (var->onepart && var->n_var_parts == 1); + + if (!dv_changed_p (var->dv)) + return var->var_part[0].cur_loc; + + INIT_ELCD (data, vars); + + loc = vt_expand_var_loc_chain (var, scratch_regs, &data, NULL); + + gcc_checking_assert (VEC_empty (rtx, data.expanding)); + + FINI_ELCD (data, loc); + + return loc; } /* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains @@ -7561,49 +8066,57 @@ emit_note_insn_var_location (void **varp, void *data) tree decl; location_chain lc; - if (dv_is_value_p (var->dv)) - goto value_or_debug_decl; + gcc_checking_assert (var->onepart == NOT_ONEPART + || var->onepart == ONEPART_VDECL); decl = dv_as_decl (var->dv); - if (TREE_CODE (decl) == DEBUG_EXPR_DECL) - goto value_or_debug_decl; - complete = true; last_limit = 0; n_var_parts = 0; - if (!MAY_HAVE_DEBUG_INSNS) - { - for (i = 0; i < var->n_var_parts; i++) - if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain) - { - var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc; - var->cur_loc_changed = true; - } - if (var->n_var_parts == 0) - var->cur_loc_changed = true; - } - if (!var->cur_loc_changed) - goto clear; + if (!var->onepart) + for (i = 0; i < var->n_var_parts; i++) + if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain) + var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc; for (i = 0; i < var->n_var_parts; i++) { enum machine_mode mode, wider_mode; rtx loc2; + HOST_WIDE_INT offset; - if (last_limit < var->var_part[i].offset) + if (i == 0 && var->onepart) { - complete = false; - break; + gcc_checking_assert (var->n_var_parts == 1); + offset = 0; + initialized = VAR_INIT_STATUS_INITIALIZED; + loc2 = vt_expand_1pvar (var, vars); } - else if (last_limit > var->var_part[i].offset) - continue; - offsets[n_var_parts] = var->var_part[i].offset; - if (!var->var_part[i].cur_loc) + else { - complete = false; - continue; + if (last_limit < VAR_PART_OFFSET (var, i)) + { + complete = false; + break; + } + else if (last_limit > VAR_PART_OFFSET (var, i)) + continue; + offset = VAR_PART_OFFSET (var, i); + if (!var->var_part[i].cur_loc) + { + complete = false; + continue; + } + for (lc = var->var_part[i].loc_chain; lc; lc = lc->next) + if (var->var_part[i].cur_loc == lc->loc) + { + initialized = lc->init; + break; + } + gcc_assert (lc); + loc2 = var->var_part[i].cur_loc; } - loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars, false); + + offsets[n_var_parts] = offset; if (!loc2) { complete = false; @@ -7611,29 +8124,22 @@ emit_note_insn_var_location (void **varp, void *data) } loc[n_var_parts] = loc2; mode = GET_MODE (var->var_part[i].cur_loc); - if (mode == VOIDmode && dv_onepart_p (var->dv)) + if (mode == VOIDmode && var->onepart) mode = DECL_MODE (decl); - for (lc = var->var_part[i].loc_chain; lc; lc = lc->next) - if (var->var_part[i].cur_loc == lc->loc) - { - initialized = lc->init; - break; - } - gcc_assert (lc); last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode); /* Attempt to merge adjacent registers or memory. */ wider_mode = GET_MODE_WIDER_MODE (mode); for (j = i + 1; j < var->n_var_parts; j++) - if (last_limit <= var->var_part[j].offset) + if (last_limit <= VAR_PART_OFFSET (var, j)) break; if (j < var->n_var_parts && wider_mode != VOIDmode && var->var_part[j].cur_loc && mode == GET_MODE (var->var_part[j].cur_loc) && (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts])) - && last_limit == var->var_part[j].offset - && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars, false)) + && last_limit == (var->onepart ? 0 : VAR_PART_OFFSET (var, j)) + && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars)) && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2)) { rtx new_loc = NULL; @@ -7746,152 +8252,152 @@ emit_note_insn_var_location (void **varp, void *data) } NOTE_VAR_LOCATION (note) = note_vl; - clear: set_dv_changed (var->dv, false); - var->cur_loc_changed = false; gcc_assert (var->in_changed_variables); var->in_changed_variables = false; htab_clear_slot (changed_variables, varp); /* Continue traversing the hash table. */ return 1; - - value_or_debug_decl: - if (dv_changed_p (var->dv) && var->n_var_parts) - { - location_chain lc; - bool cur_loc_changed; - - if (var->var_part[0].cur_loc - && vt_expand_loc_dummy (var->var_part[0].cur_loc, vars, - &cur_loc_changed)) - goto clear; - for (lc = var->var_part[0].loc_chain; lc; lc = lc->next) - if (lc->loc != var->var_part[0].cur_loc - && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed)) - break; - var->var_part[0].cur_loc = lc ? lc->loc : NULL_RTX; - } - goto clear; } -DEF_VEC_P (variable); -DEF_VEC_ALLOC_P (variable, heap); - -/* Stack of variable_def pointers that need processing with - check_changed_vars_2. */ +/* While traversing changed_variables, push onto DATA (a stack of RTX + values) entries that aren't user variables. */ -static VEC (variable, heap) *changed_variables_stack; - -/* VALUEs with no variables that need set_dv_changed (val, false) - called before check_changed_vars_3. */ +static int +values_to_stack (void **slot, void *data) +{ + VEC (rtx, stack) **changed_values_stack = (VEC (rtx, stack) **)data; + variable var = (variable) *slot; -static VEC (rtx, heap) *changed_values_stack; + if (var->onepart == ONEPART_VALUE) + VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_value (var->dv)); + else if (var->onepart == ONEPART_DEXPR) + VEC_safe_push (rtx, stack, *changed_values_stack, + DECL_RTL_KNOWN_SET (dv_as_decl (var->dv))); -/* Helper function for check_changed_vars_1 and check_changed_vars_2. */ + return 1; +} +/* Remove from changed_variables the entry whose DV corresponds to + value or debug_expr VAL. */ static void -check_changed_vars_0 (decl_or_value dv, htab_t htab) +remove_value_from_changed_variables (rtx val) { - value_chain vc - = (value_chain) htab_find_with_hash (value_chains, dv, dv_htab_hash (dv)); + decl_or_value dv = dv_from_rtx (val); + void **slot; + variable var; - if (vc == NULL) - return; - for (vc = vc->next; vc; vc = vc->next) - if (!dv_changed_p (vc->dv)) - { - variable vcvar - = (variable) htab_find_with_hash (htab, vc->dv, - dv_htab_hash (vc->dv)); - if (vcvar) - { - set_dv_changed (vc->dv, true); - VEC_safe_push (variable, heap, changed_variables_stack, vcvar); - } - else if (dv_is_value_p (vc->dv)) - { - set_dv_changed (vc->dv, true); - VEC_safe_push (rtx, heap, changed_values_stack, - dv_as_value (vc->dv)); - check_changed_vars_0 (vc->dv, htab); - } - } + slot = htab_find_slot_with_hash (changed_variables, + dv, dv_htab_hash (dv), NO_INSERT); + var = (variable) *slot; + var->in_changed_variables = false; + htab_clear_slot (changed_variables, slot); } -/* Populate changed_variables_stack with variable_def pointers - that need variable_was_changed called on them. */ +/* If VAL (a value or debug_expr) has backlinks to variables actively + dependent on it in HTAB or in CHANGED_VARIABLES, mark them as + changed, adding to CHANGED_VALUES_STACK any dependencies that may + have dependencies of their own to notify. */ -static int -check_changed_vars_1 (void **slot, void *data) +static void +notify_dependents_of_changed_value (rtx val, htab_t htab, + VEC (rtx, stack) **changed_values_stack) { - variable var = (variable) *slot; - htab_t htab = (htab_t) data; + void **slot; + variable var; + loc_exp_dep *led; + decl_or_value dv = dv_from_rtx (val); - if (dv_is_value_p (var->dv) - || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL) - check_changed_vars_0 (var->dv, htab); - return 1; -} + slot = htab_find_slot_with_hash (changed_variables, + dv, dv_htab_hash (dv), NO_INSERT); + if (!slot) + slot = htab_find_slot_with_hash (htab, + dv, dv_htab_hash (dv), NO_INSERT); + if (!slot) + slot = htab_find_slot_with_hash (dropped_values, + dv, dv_htab_hash (dv), NO_INSERT); + var = (variable) *slot; -/* Add VAR to changed_variables and also for VALUEs add recursively - all DVs that aren't in changed_variables yet but reference the - VALUE from its loc_chain. */ + while ((led = VAR_LOC_DEP_LST (var))) + { + decl_or_value ldv = led->dv; + void **islot; + variable ivar; -static void -check_changed_vars_2 (variable var, htab_t htab) -{ - variable_was_changed (var, NULL); - if (dv_is_value_p (var->dv) - || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL) - check_changed_vars_0 (var->dv, htab); + /* Deactivate and remove the backlink, as it was “used up”. It + makes no sense to attempt to notify the same entity again: + either it will be recomputed and re-register an active + dependency, or it will still have the changed mark. */ + if (led->next) + led->next->pprev = led->pprev; + if (led->pprev) + *led->pprev = led->next; + led->next = NULL; + led->pprev = NULL; + + if (dv_changed_p (ldv)) + continue; + + switch (dv_onepart_p (ldv)) + { + case ONEPART_VALUE: + case ONEPART_DEXPR: + set_dv_changed (ldv, true); + VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_rtx (ldv)); + break; + + default: + islot = htab_find_slot_with_hash (htab, ldv, dv_htab_hash (ldv), + NO_INSERT); + ivar = (variable) *islot; + gcc_checking_assert (!VAR_LOC_DEP_LST (ivar)); + variable_was_changed (ivar, NULL); + break; + } + } } -/* For each changed decl (except DEBUG_EXPR_DECLs) recompute - cur_loc if needed (and cur_loc of all VALUEs and DEBUG_EXPR_DECLs - it needs and are also in changed variables) and track whether - cur_loc (or anything it uses to compute location) had to change - during the current emit_notes_for_changes call. */ +/* Take out of changed_variables any entries that don't refer to use + variables. Back-propagate change notifications from values and + debug_exprs to their active dependencies in HTAB or in + CHANGED_VARIABLES. */ -static int -check_changed_vars_3 (void **slot, void *data) +static void +process_changed_values (htab_t htab) { - variable var = (variable) *slot; - htab_t vars = (htab_t) data; - int i; - location_chain lc; - bool cur_loc_changed; + int i, n; + rtx val; + VEC (rtx, stack) *changed_values_stack = VEC_alloc (rtx, stack, 20); - if (dv_is_value_p (var->dv) - || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL) - return 1; + /* Move values from changed_variables to changed_values_stack. */ + htab_traverse (changed_variables, values_to_stack, &changed_values_stack); - for (i = 0; i < var->n_var_parts; i++) + /* Back-propagate change notifications in values while popping + them from the stack. */ + for (n = i = VEC_length (rtx, changed_values_stack); + i > 0; i = VEC_length (rtx, changed_values_stack)) { - if (var->var_part[i].cur_loc - && vt_expand_loc_dummy (var->var_part[i].cur_loc, vars, - &cur_loc_changed)) + val = VEC_pop (rtx, changed_values_stack); + notify_dependents_of_changed_value (val, htab, &changed_values_stack); + + /* This condition will hold when visiting each of the entries + originally in changed_variables. We can't remove them + earlier because this could drop the backlinks before we got a + chance to use them. */ + if (i == n) { - if (cur_loc_changed) - var->cur_loc_changed = true; - continue; + remove_value_from_changed_variables (val); + n--; } - for (lc = var->var_part[i].loc_chain; lc; lc = lc->next) - if (lc->loc != var->var_part[i].cur_loc - && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed)) - break; - if (lc || var->var_part[i].cur_loc) - var->cur_loc_changed = true; - var->var_part[i].cur_loc = lc ? lc->loc : NULL_RTX; } - if (var->n_var_parts == 0) - var->cur_loc_changed = true; - return 1; + + VEC_free (rtx, stack, changed_values_stack); } /* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain - CHANGED_VARIABLES and delete this chain. WHERE specifies whether the notes - shall be emitted before of after instruction INSN. */ + CHANGED_VARIABLES and delete this chain. WHERE specifies whether + the notes shall be emitted before of after instruction INSN. */ static void emit_notes_for_changes (rtx insn, enum emit_note_where where, @@ -7904,19 +8410,7 @@ emit_notes_for_changes (rtx insn, enum emit_note_where where, return; if (MAY_HAVE_DEBUG_INSNS) - { - /* Unfortunately this has to be done in two steps, because - we can't traverse a hashtab into which we are inserting - through variable_was_changed. */ - htab_traverse (changed_variables, check_changed_vars_1, htab); - while (VEC_length (variable, changed_variables_stack) > 0) - check_changed_vars_2 (VEC_pop (variable, changed_variables_stack), - htab); - while (VEC_length (rtx, changed_values_stack) > 0) - set_dv_changed (dv_from_value (VEC_pop (rtx, changed_values_stack)), - false); - htab_traverse (changed_variables, check_changed_vars_3, htab); - } + process_changed_values (htab); data.insn = insn; data.where = where; @@ -7941,78 +8435,59 @@ emit_notes_for_differences_1 (void **slot, void *data) if (!new_var) { /* Variable has disappeared. */ - variable empty_var; - - empty_var = (variable) pool_alloc (dv_pool (old_var->dv)); - empty_var->dv = old_var->dv; - empty_var->refcount = 0; - empty_var->n_var_parts = 0; - empty_var->cur_loc_changed = false; - empty_var->in_changed_variables = false; - if (dv_onepart_p (old_var->dv)) - { - location_chain lc; + variable empty_var = NULL; - gcc_assert (old_var->n_var_parts == 1); - for (lc = old_var->var_part[0].loc_chain; lc; lc = lc->next) - remove_value_chains (old_var->dv, lc->loc); - } - variable_was_changed (empty_var, NULL); - /* Continue traversing the hash table. */ - return 1; - } - if (variable_different_p (old_var, new_var)) - { - if (dv_onepart_p (old_var->dv)) + if (old_var->onepart == ONEPART_VALUE + || old_var->onepart == ONEPART_DEXPR) { - location_chain lc1, lc2; - - gcc_assert (old_var->n_var_parts == 1 - && new_var->n_var_parts == 1); - lc1 = old_var->var_part[0].loc_chain; - lc2 = new_var->var_part[0].loc_chain; - while (lc1 - && lc2 - && ((REG_P (lc1->loc) && REG_P (lc2->loc)) - || rtx_equal_p (lc1->loc, lc2->loc))) + empty_var = variable_from_dropped (old_var->dv, NO_INSERT); + if (empty_var) { - lc1 = lc1->next; - lc2 = lc2->next; + gcc_checking_assert (!empty_var->in_changed_variables); + if (!VAR_LOC_1PAUX (old_var)) + { + VAR_LOC_1PAUX (old_var) = VAR_LOC_1PAUX (empty_var); + VAR_LOC_1PAUX (empty_var) = NULL; + } + else + gcc_checking_assert (!VAR_LOC_1PAUX (empty_var)); } - for (; lc2; lc2 = lc2->next) - add_value_chains (old_var->dv, lc2->loc); - for (; lc1; lc1 = lc1->next) - remove_value_chains (old_var->dv, lc1->loc); } - variable_was_changed (new_var, NULL); - } - /* Update cur_loc. */ - if (old_var != new_var) - { - int i; - for (i = 0; i < new_var->n_var_parts; i++) + + if (!empty_var) { - new_var->var_part[i].cur_loc = NULL; - if (old_var->n_var_parts != new_var->n_var_parts - || old_var->var_part[i].offset != new_var->var_part[i].offset) - new_var->cur_loc_changed = true; - else if (old_var->var_part[i].cur_loc != NULL) - { - location_chain lc; - rtx cur_loc = old_var->var_part[i].cur_loc; + empty_var = (variable) pool_alloc (onepart_pool (old_var->onepart)); + empty_var->dv = old_var->dv; + empty_var->refcount = 0; + empty_var->n_var_parts = 0; + empty_var->onepart = old_var->onepart; + empty_var->in_changed_variables = false; + } - for (lc = new_var->var_part[i].loc_chain; lc; lc = lc->next) - if (lc->loc == cur_loc - || rtx_equal_p (cur_loc, lc->loc)) - { - new_var->var_part[i].cur_loc = lc->loc; - break; - } - if (lc == NULL) - new_var->cur_loc_changed = true; - } + if (empty_var->onepart) + { + /* Propagate the auxiliary data to (ultimately) + changed_variables. */ + empty_var->var_part[0].loc_chain = NULL; + empty_var->var_part[0].cur_loc = NULL; + VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (old_var); + VAR_LOC_1PAUX (old_var) = NULL; } + variable_was_changed (empty_var, NULL); + /* Continue traversing the hash table. */ + return 1; + } + /* Update cur_loc and one-part auxiliary data, before new_var goes + through variable_was_changed. */ + if (old_var != new_var && new_var->onepart) + { + gcc_checking_assert (VAR_LOC_1PAUX (new_var) == NULL); + VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (old_var); + VAR_LOC_1PAUX (old_var) = NULL; + new_var->var_part[0].cur_loc = old_var->var_part[0].cur_loc; } + if (variable_different_p (old_var, new_var)) + variable_was_changed (new_var, NULL); /* Continue traversing the hash table. */ return 1; @@ -8033,15 +8508,6 @@ emit_notes_for_differences_2 (void **slot, void *data) if (!old_var) { int i; - /* Variable has appeared. */ - if (dv_onepart_p (new_var->dv)) - { - location_chain lc; - - gcc_assert (new_var->n_var_parts == 1); - for (lc = new_var->var_part[0].loc_chain; lc; lc = lc->next) - add_value_chains (new_var->dv, lc->loc); - } for (i = 0; i < new_var->n_var_parts; i++) new_var->var_part[i].cur_loc = NULL; variable_was_changed (new_var, NULL); @@ -8111,7 +8577,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set) { XEXP (XEXP (*p, 0), 1) = vt_expand_loc (XEXP (XEXP (*p, 0), 1), - shared_hash_htab (set->vars), true); + shared_hash_htab (set->vars)); /* If expansion is successful, keep it in the list. */ if (XEXP (XEXP (*p, 0), 1)) p = &XEXP (*p, 1); @@ -8412,15 +8878,9 @@ vt_emit_notes (void) emit_notes = true; if (MAY_HAVE_DEBUG_INSNS) - { - unsigned int i; - rtx val; - - FOR_EACH_VEC_ELT (rtx, preserved_values, i, val) - add_cselib_value_chains (dv_from_value (val)); - changed_variables_stack = VEC_alloc (variable, heap, 40); - changed_values_stack = VEC_alloc (rtx, heap, 40); - } + dropped_values = htab_create (cselib_get_next_uid () * 2, + variable_htab_hash, variable_htab_eq, + variable_htab_free); dataflow_set_init (&cur); @@ -8441,23 +8901,11 @@ vt_emit_notes (void) htab_traverse (shared_hash_htab (cur.vars), emit_notes_for_differences_1, shared_hash_htab (empty_shared_hash)); - if (MAY_HAVE_DEBUG_INSNS) - { - unsigned int i; - rtx val; - - FOR_EACH_VEC_ELT (rtx, preserved_values, i, val) - remove_cselib_value_chains (dv_from_value (val)); - gcc_assert (htab_elements (value_chains) == 0); - } #endif dataflow_set_destroy (&cur); if (MAY_HAVE_DEBUG_INSNS) - { - VEC_free (variable, heap, changed_variables_stack); - VEC_free (rtx, heap, changed_values_stack); - } + htab_delete (dropped_values); emit_notes = false; } @@ -8489,36 +8937,27 @@ vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp) return false; } -/* Helper function for vt_add_function_parameter. RTL is - the expression and VAL corresponding cselib_val pointer - for which ENTRY_VALUE should be created. */ +/* Mark the value for the ENTRY_VALUE of RTL as equivalent to EQVAL in + OUT. */ static void -create_entry_value (rtx rtl, cselib_val *val) -{ - cselib_val *val2; - struct elt_loc_list *el; - el = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el)); - el->loc = gen_rtx_ENTRY_VALUE (GET_MODE (rtl)); - ENTRY_VALUE_EXP (el->loc) = rtl; - val2 = cselib_lookup_from_insn (el->loc, GET_MODE (rtl), true, - VOIDmode, get_insns ()); - el->next = val->locs; - el->setting_insn = get_insns (); - val->locs = el; - if (val2 - && val2 != val - && val2->locs - && rtx_equal_p (val2->locs->loc, el->loc)) - { - struct elt_loc_list *el2; - - preserve_value (val2); - el2 = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el2)); - el2->next = val2->locs; - el2->loc = val->val_rtx; - el2->setting_insn = get_insns (); - val2->locs = el2; +create_entry_value (dataflow_set *out, rtx eqval, rtx rtl) +{ + rtx ev = gen_rtx_ENTRY_VALUE (GET_MODE (rtl)); + cselib_val *val; + + ENTRY_VALUE_EXP (ev) = rtl; + + val = cselib_lookup_from_insn (ev, GET_MODE (ev), true, + VOIDmode, get_insns ()); + + if (val->val_rtx != eqval) + { + preserve_value (val); + set_variable_part (out, val->val_rtx, dv_from_value (eqval), 0, + VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT); + set_variable_part (out, eqval, dv_from_value (val->val_rtx), 0, + VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT); } } @@ -8678,20 +9117,22 @@ vt_add_function_parameter (tree parm) VAR_INIT_STATUS_INITIALIZED, NULL, INSERT); if (dv_is_value_p (dv)) { - cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv)); - create_entry_value (incoming, val); + create_entry_value (out, dv_as_value (dv), incoming); if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm)))) { enum machine_mode indmode = TYPE_MODE (TREE_TYPE (TREE_TYPE (parm))); rtx mem = gen_rtx_MEM (indmode, incoming); - val = cselib_lookup_from_insn (mem, indmode, true, - VOIDmode, get_insns ()); + cselib_val *val = cselib_lookup_from_insn (mem, indmode, true, + VOIDmode, + get_insns ()); if (val) { preserve_value (val); - create_entry_value (mem, val); + set_variable_part (out, mem, dv_from_value (val->val_rtx), 0, + VAR_INIT_STATUS_INITIALIZED, NULL, INSERT); + create_entry_value (out, val->val_rtx, mem); } } } @@ -8755,23 +9196,6 @@ fp_setter (rtx insn) return false; } -/* Gather all registers used for passing arguments to other functions - called from the current routine. */ - -static void -note_register_arguments (rtx insn) -{ - rtx link, x; - - for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) - if (GET_CODE (XEXP (link, 0)) == USE) - { - x = XEXP (XEXP (link, 0), 0); - if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER) - SET_HARD_REG_BIT (argument_reg_set, REGNO (x)); - } -} - /* Initialize cfa_base_rtx, create a preserved VALUE for it and ensure it isn't flushed during cselib_reset_table. Can be called only if frame_pointer_rtx resp. arg_pointer_rtx @@ -8843,14 +9267,6 @@ vt_initialize (void) variable_htab_free); changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq, variable_htab_free); - if (MAY_HAVE_DEBUG_INSNS) - { - value_chain_pool = create_alloc_pool ("value_chain_def pool", - sizeof (struct value_chain_def), - 1024); - value_chains = htab_create (32, value_chain_htab_hash, - value_chain_htab_eq, NULL); - } /* Init the IN and OUT sets. */ FOR_ALL_BB (bb) @@ -8876,8 +9292,6 @@ vt_initialize (void) valvar_pool = NULL; } - CLEAR_HARD_REG_SET (argument_reg_set); - /* In order to factor out the adjustments made to the stack pointer or to the hard frame pointer and thus be able to use DW_OP_fbreg operations instead of individual location lists, we're going to rewrite MEMs based @@ -8962,14 +9376,6 @@ vt_initialize (void) } } - if (frame_pointer_needed) - { - rtx insn; - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (CALL_P (insn)) - note_register_arguments (insn); - } - hard_frame_pointer_adjustment = -1; vt_add_function_parameters (); @@ -9155,8 +9561,6 @@ vt_finalize (void) if (MAY_HAVE_DEBUG_INSNS) { - htab_delete (value_chains); - free_alloc_pool (value_chain_pool); free_alloc_pool (valvar_pool); VEC_free (rtx, heap, preserved_values); cselib_finish (); @@ -9164,7 +9568,9 @@ vt_finalize (void) scratch_regs = NULL; } +#ifdef HAVE_window_save VEC_free (parm_reg_t, gc, windowed_parm_regs); +#endif if (vui_vec) XDELETEVEC (vui_vec); diff --git a/gcc/varpool.c b/gcc/varpool.c index d223779a5c1..e342a9109d9 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -703,9 +703,11 @@ varpool_create_variable_alias (tree alias, tree decl) gcc_assert (TREE_CODE (alias) == VAR_DECL); alias_node = varpool_node (alias); alias_node->alias = 1; - alias_node->finalized = 1; + if (!DECL_EXTERNAL (alias)) + alias_node->finalized = 1; alias_node->alias_of = decl; - if (decide_is_variable_needed (alias_node, alias) + if ((!DECL_EXTERNAL (alias) + && decide_is_variable_needed (alias_node, alias)) || alias_node->needed) varpool_mark_needed_node (alias_node); return alias_node; |