From becfd6e57bd90a45e5ab409db85f2e69e5945213 Mon Sep 17 00:00:00 2001 From: Kenneth Zadeck Date: Wed, 7 May 2008 20:48:07 +0000 Subject: [PATCH] [multiple changes] 2008-05-07 Kenneth Zadeck PATCH rtl/7335 PATCH rtl/33826 * see.c (see_copy_insn): Copy new pure const attributes for new call. * c-decl.c (merge_decls): Ditto. * postreload.c (record_opr_changes): Change CONST_OR_PURE_CALL_P to RTL_CONST_OR_PURE_CALL_P. * tree.c (define_local_buitin): Rename DECL_IS_PURE to DECL_PURE_P. Initialized DECL_LOOPING_CONST_PURE. (process_call_operands): Set tree_side_effects properly. * tree.h (TREE_READONLY_DECL_P): Removed. (DECL_IS_PURE): Renamed to DECL_PURE_P. (DECL_LOOPING_OR_CONST_P): New macro. (struct tree_function_decl): Added looping_const_or_pure_p. (ECF_*) Renumbered. (ECF_LOOPING_OR_CONST_P): New macro, * rtlanal.c (pure_const_p): Removed. * builtins.c (expand_builtin): Rename DECL_IS_PURE to DECL_PURE_P. * reorg.c (delete_prior_computation) Changed CONST_OR_PURE_CALL_P to RTL_CONST_CALL_P. * ipa-pure-const.c (pure_const_state_e): Added looping field. (check_decl, check_tree, check_call, scan_function): Initialize looping. (analyze_function): Rename DECL_IS_PURE to DECL_PURE_P. (static_execute): Set looping true for recursive functions. Undo setting state to IPA_NEITHER for recursive functions. * cse.c (cse_insn): * ifcvt.c (noce_can_store_speculate_p): Changed CONST_OR_PURE_CALL_P and pure_call_p to RTL_CONST_CALL_P or RTL_CONST_OR_PURE_CALL_P. * dse.c (scan_insn): Ditto. * local-alloc.c (validate_equiv_mem, memref_used_between_p): Ditto. * gcse.c (oprs_not_seen_p) Changed CONST_OR_PURE_CALL_P to RTL_CONST_OR_PURE_CALL_P. (store_killed_in_insn): Changed CONST_OR_PURE_CALL_P and pure_call_p to RTL_CONST_CALL_P. * gimplify.c (gimplify_call_expr): Clear side effects for non-looping pure and constant calls. * calls.c (emit_call_1): Set rtl flags from ecf flags. (flags_from_decl_or_type): Set ecf flags from decl flags. (initialize_argument_information): Turn off ECF_LOOPING_CONST_OR_PURE when turning off ECF_CONST. Change const to pure if callee_copies is true rather than just turning off const. (expand_call): Turn off ECF_LOOPING_PURE_CONST_CALL and remove old way of marking pure calls. (emit_library_call_value_1): Turn off ECF_LOOPING_PURE_CONST_CALL. Remove hack that was supposed to fix pr7335 and remove old way of marking pure calls. * emit-rtl.c (emit_copy_of_insn_after): Copy RTL_CONST_CALL_P, RTL_PURE_CALL_P, RTL_LOOPING_CONST_OR_PURE_CALL_P. * cselib.c (cselib_process_insn): Changed CONST_OR_PURE_CALL_P to RTL_CONST_OR_PURE_CALL_P. * tree-ssa-pre.c (can_value_number_call): Fixed spacing. * loop-invariant.c (find_exits, find_invariant_bb): Changed CONST_OR_PURE_CALL_P to RTL_CONST_OR_PURE_CALL_P. * sched-deps.c (schedule_analyze): Ditto. * rtl.h (struct rtx_def): Use call field, unchanging field, and return_val field of calls to represent pure and const function info. (CONST_OR_PURE_CALL_P): Deleted macro. (RTL_CONST_CALL_P, RTL_PURE_CALL_P, RTL_LOOPING_CONST_OR_PURE_CALL_P, RTL_CONST_OR_PURE_P): New macros. * tree-inline.c (copy_body_r): Changed TREE_READONLY_DECL_P to TREE_READONLY. * tree-optimize.c (execute_fixup_cfg): Added test for ECF_LOOPING_CONST_OR_PURE. * c-common.c (handle_pure_attribute): Changed DECL_IS_PURE to DECL_PURE_P. * tree-cfg.c (update_call_expr_flags): Do not clear tree side effects for looping pure or const calls. (verify_gimple_expr): Added verification code. * config/alpha/alpha.c (alpha_legitimize_address, alpha_emit_xfloating_libcall): Changed CONST_OR_PURE_CALL_P to RTL_CONST_CALL_P. * config/s390/s390.c (s390_emit_tls_call_insn): Ditto. * config/rs6000/rs6000.c (rs6000_legitimize_tls_address): Ditto. * config/mips/mips.c (mips_call_tls_get_addr): Ditto. * cfgrtl.c (need_fake_edge_p): Changed CONST_OR_PURE_CALL_P to RTL_CONST_OR_PURE_CALL_P. * dce.c (deletable_insn_p): Allow non looping, non sibling, pure and const calls to be deleted. java: 2008-05-07 Kenneth Zadeck * decl.c (java_init_decl_processing): Change DECL_IS_PURE to DECL_PURE_P. cp: 2008-05-07 Kenneth Zadeck * decl.c (duplicate_decls): Merge in DECL_PURE_P, TREE_READONLY, DECL_LOOPING_CONST_OR_PURE_P attributes. * rtti.c (build_dynamic_cast_1): Rename DECL_IS_PURE to DECL_PURE_P. gfortran: 2008-05-07 Kenneth Zadeck * trans-decl.c (gfc_get_extern_function_decl, build_function_decl): Rename DECL_IS_PURE to DECL_PURE_P. From-SVN: r135053 --- gcc/ChangeLog | 86 +++++++++++++++++++++++++++++++++++++ gcc/builtins.c | 2 +- gcc/c-common.c | 2 +- gcc/c-decl.c | 4 +- gcc/calls.c | 87 +++++++++++++++++++------------------- gcc/cfgrtl.c | 2 +- gcc/config/alpha/alpha.c | 6 +-- gcc/config/i386/i386.c | 4 +- gcc/config/mips/mips.c | 2 +- gcc/config/rs6000/rs6000.c | 4 +- gcc/config/s390/s390.c | 2 +- gcc/cp/ChangeLog | 8 ++++ gcc/cp/decl.c | 6 ++- gcc/cp/rtti.c | 2 +- gcc/cse.c | 2 +- gcc/cselib.c | 6 ++- gcc/dce.c | 9 ++++ gcc/dse.c | 2 +- gcc/emit-rtl.c | 5 ++- gcc/fortran/ChangeLog | 5 +++ gcc/fortran/trans-decl.c | 4 +- gcc/gcse.c | 4 +- gcc/gimplify.c | 12 ++++-- gcc/ifcvt.c | 4 +- gcc/ipa-pure-const.c | 67 ++++++++++++++++------------- gcc/java/ChangeLog | 5 +++ gcc/java/decl.c | 6 +-- gcc/local-alloc.c | 6 +-- gcc/loop-invariant.c | 6 ++- gcc/postreload-gcse.c | 2 +- gcc/reorg.c | 2 +- gcc/rtl.h | 31 +++++++++++--- gcc/rtlanal.c | 23 ---------- gcc/sched-deps.c | 3 +- gcc/see.c | 5 ++- gcc/tree-cfg.c | 18 +++++++- gcc/tree-inline.c | 2 +- gcc/tree-optimize.c | 3 +- gcc/tree-ssa-pre.c | 2 +- gcc/tree.c | 6 ++- gcc/tree.h | 59 ++++++++++++++++---------- 41 files changed, 338 insertions(+), 178 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 32ee2a6983c..1104f77b256 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,89 @@ +2008-05-07 Kenneth Zadeck + + PATCH rtl/7335 + PATCH rtl/33826 + * see.c (see_copy_insn): Copy new pure const attributes for new + call. + * c-decl.c (merge_decls): Ditto. + * postreload.c (record_opr_changes): Change CONST_OR_PURE_CALL_P + to RTL_CONST_OR_PURE_CALL_P. + * tree.c (define_local_buitin): Rename DECL_IS_PURE to + DECL_PURE_P. Initialized DECL_LOOPING_CONST_PURE. + (process_call_operands): Set tree_side_effects properly. + * tree.h (TREE_READONLY_DECL_P): Removed. + (DECL_IS_PURE): Renamed to DECL_PURE_P. + (DECL_LOOPING_OR_CONST_P): New macro. + (struct tree_function_decl): Added looping_const_or_pure_p. + (ECF_*) Renumbered. + (ECF_LOOPING_OR_CONST_P): New macro, + * rtlanal.c (pure_const_p): Removed. + * builtins.c (expand_builtin): Rename DECL_IS_PURE to DECL_PURE_P. + * reorg.c (delete_prior_computation) Changed CONST_OR_PURE_CALL_P + to RTL_CONST_CALL_P. + * ipa-pure-const.c (pure_const_state_e): Added looping field. + (check_decl, check_tree, check_call, scan_function): Initialize + looping. + (analyze_function): Rename DECL_IS_PURE to DECL_PURE_P. + (static_execute): Set looping true for recursive functions. + Undo setting state to IPA_NEITHER for recursive functions. + * cse.c (cse_insn): + * ifcvt.c (noce_can_store_speculate_p): Changed + CONST_OR_PURE_CALL_P and pure_call_p to RTL_CONST_CALL_P or + RTL_CONST_OR_PURE_CALL_P. + * dse.c (scan_insn): Ditto. + * local-alloc.c (validate_equiv_mem, memref_used_between_p): Ditto. + * gcse.c (oprs_not_seen_p) Changed CONST_OR_PURE_CALL_P to + RTL_CONST_OR_PURE_CALL_P. + (store_killed_in_insn): Changed CONST_OR_PURE_CALL_P and + pure_call_p to RTL_CONST_CALL_P. + * gimplify.c (gimplify_call_expr): Clear side effects for + non-looping pure and constant calls. + * calls.c (emit_call_1): Set rtl flags from ecf flags. + (flags_from_decl_or_type): Set ecf flags from decl flags. + (initialize_argument_information): Turn off + ECF_LOOPING_CONST_OR_PURE when turning off ECF_CONST. + Change const to pure if callee_copies is true rather than just + turning off const. + (expand_call): Turn off ECF_LOOPING_PURE_CONST_CALL and remove old + way of marking pure calls. + (emit_library_call_value_1): Turn off ECF_LOOPING_PURE_CONST_CALL. + Remove hack that was supposed to fix pr7335 and remove old + way of marking pure calls. + * emit-rtl.c (emit_copy_of_insn_after): Copy RTL_CONST_CALL_P, + RTL_PURE_CALL_P, RTL_LOOPING_CONST_OR_PURE_CALL_P. + * cselib.c (cselib_process_insn): Changed CONST_OR_PURE_CALL_P to + RTL_CONST_OR_PURE_CALL_P. + * tree-ssa-pre.c (can_value_number_call): Fixed spacing. + * loop-invariant.c (find_exits, find_invariant_bb): Changed + CONST_OR_PURE_CALL_P to RTL_CONST_OR_PURE_CALL_P. + * sched-deps.c (schedule_analyze): Ditto. + * rtl.h (struct rtx_def): Use call field, unchanging field, and + return_val field of calls to represent pure and const function + info. + (CONST_OR_PURE_CALL_P): Deleted macro. + (RTL_CONST_CALL_P, RTL_PURE_CALL_P, + RTL_LOOPING_CONST_OR_PURE_CALL_P, RTL_CONST_OR_PURE_P): New macros. + * tree-inline.c (copy_body_r): Changed TREE_READONLY_DECL_P to + TREE_READONLY. + * tree-optimize.c (execute_fixup_cfg): Added test for + ECF_LOOPING_CONST_OR_PURE. + * c-common.c (handle_pure_attribute): Changed DECL_IS_PURE to + DECL_PURE_P. + * tree-cfg.c (update_call_expr_flags): Do not clear tree side + effects for looping pure or const calls. + (verify_gimple_expr): Added verification code. + * config/alpha/alpha.c (alpha_legitimize_address, + alpha_emit_xfloating_libcall): Changed CONST_OR_PURE_CALL_P to + RTL_CONST_CALL_P. + * config/s390/s390.c (s390_emit_tls_call_insn): Ditto. + * config/rs6000/rs6000.c (rs6000_legitimize_tls_address): Ditto. + * config/mips/mips.c (mips_call_tls_get_addr): Ditto. + * cfgrtl.c (need_fake_edge_p): Changed CONST_OR_PURE_CALL_P to + RTL_CONST_OR_PURE_CALL_P. + * dce.c (deletable_insn_p): Allow non looping, non sibling, pure + and const calls to be deleted. + + 2008-05-08 Uros Bizjak PR target/35714 diff --git a/gcc/builtins.c b/gcc/builtins.c index 2cb8fa03f9e..61b42722056 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -6098,7 +6098,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode, none of its arguments are volatile, we can avoid expanding the built-in call and just evaluate the arguments for side-effects. */ if (target == const0_rtx - && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl))) + && (DECL_PURE_P (fndecl) || TREE_READONLY (fndecl))) { bool volatilep = false; tree arg; diff --git a/gcc/c-common.c b/gcc/c-common.c index 58585239680..0c27b3e6eba 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -6019,7 +6019,7 @@ handle_pure_attribute (tree *node, tree name, tree ARG_UNUSED (args), int ARG_UNUSED (flags), bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) - DECL_IS_PURE (*node) = 1; + DECL_PURE_P (*node) = 1; /* ??? TODO: Support types. */ else { diff --git a/gcc/c-decl.c b/gcc/c-decl.c index a51f76e8a0c..9abb4dfc65f 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -1729,10 +1729,10 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl) |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl); TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl); - TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl); DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl); - DECL_IS_PURE (newdecl) |= DECL_IS_PURE (olddecl); + TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); + DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl); DECL_IS_NOVOPS (newdecl) |= DECL_IS_NOVOPS (olddecl); } diff --git a/gcc/calls.c b/gcc/calls.c index 126037d4a42..429ccb4ed23 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -359,21 +359,20 @@ emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED, /* Find the call we just emitted. */ call_insn = last_call_insn (); - /* Mark memory as used for "pure" function call. */ - if (ecf_flags & ECF_PURE) - call_fusage - = gen_rtx_EXPR_LIST - (VOIDmode, - gen_rtx_USE (VOIDmode, - gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))), - call_fusage); - /* Put the register usage information there. */ add_function_usage_to (call_insn, call_fusage); /* If this is a const call, then set the insn's unchanging bit. */ - if (ecf_flags & (ECF_CONST | ECF_PURE)) - CONST_OR_PURE_CALL_P (call_insn) = 1; + if (ecf_flags & ECF_CONST) + RTL_CONST_CALL_P (call_insn) = 1; + + /* If this is a pure call, then set the insn's unchanging bit. */ + if (ecf_flags & ECF_PURE) + RTL_PURE_CALL_P (call_insn) = 1; + + /* If this is a const call, then set the insn's unchanging bit. */ + if (ecf_flags & ECF_LOOPING_CONST_OR_PURE) + RTL_LOOPING_CONST_OR_PURE_CALL_P (call_insn) = 1; /* If this call can't throw, attach a REG_EH_REGION reg note to that effect. */ @@ -580,9 +579,13 @@ flags_from_decl_or_type (const_tree exp) if (DECL_IS_RETURNS_TWICE (exp)) flags |= ECF_RETURNS_TWICE; - /* The function exp may have the `pure' attribute. */ - if (DECL_IS_PURE (exp)) + /* Process the pure and const attributes. */ + if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp)) + flags |= ECF_CONST; + if (DECL_PURE_P (exp)) flags |= ECF_PURE; + if (DECL_LOOPING_CONST_OR_PURE_P (exp)) + flags |= ECF_LOOPING_CONST_OR_PURE; if (DECL_IS_NOVOPS (exp)) flags |= ECF_NOVOPS; @@ -590,9 +593,6 @@ flags_from_decl_or_type (const_tree exp) if (TREE_NOTHROW (exp)) flags |= ECF_NOTHROW; - if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp)) - flags |= ECF_CONST; - flags = special_function_p (exp, flags); } else if (TYPE_P (exp) && TYPE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp)) @@ -1038,7 +1038,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, args[i].tree_value = build_fold_addr_expr (args[i].tree_value); type = TREE_TYPE (args[i].tree_value); - *ecf_flags &= ~(ECF_CONST | ECF_LIBCALL_BLOCK); + if (*ecf_flags & ECF_CONST) + *ecf_flags &= ~(ECF_CONST | ECF_LOOPING_CONST_OR_PURE); + *ecf_flags &= ~ECF_LIBCALL_BLOCK; } else { @@ -1073,10 +1075,19 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, store_expr (args[i].tree_value, copy, 0, false); - if (callee_copies) - *ecf_flags &= ~(ECF_CONST | ECF_LIBCALL_BLOCK); - else - *ecf_flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK); + *ecf_flags &= ~(ECF_LIBCALL_BLOCK); + + /* Just change the const function to pure and then let + the next test clear the pure based on + callee_copies. */ + if (*ecf_flags & ECF_CONST) + { + *ecf_flags &= ~ECF_CONST; + *ecf_flags |= ECF_PURE; + } + + if (!callee_copies && *ecf_flags & ECF_PURE) + *ecf_flags &= ~(ECF_PURE | ECF_LOOPING_CONST_OR_PURE); args[i].tree_value = build_fold_addr_expr (make_tree (type, copy)); @@ -2022,10 +2033,12 @@ expand_call (tree exp, rtx target, int ignore) if (AGGREGATE_TYPE_P (TREE_TYPE (exp))) warning (OPT_Waggregate_return, "function call has aggregate value"); - /* If the result of a pure or const function call is ignored (or void), - and none of its arguments are volatile, we can avoid expanding the - call and just evaluate the arguments for side-effects. */ + /* If the result of a non looping pure or const function call is + ignored (or void), and none of its arguments are volatile, we can + avoid expanding the call and just evaluate the arguments for + side-effects. */ if ((flags & (ECF_CONST | ECF_PURE)) + && (!(flags & ECF_LOOPING_CONST_OR_PURE)) && (ignore || target == const0_rtx || TYPE_MODE (TREE_TYPE (exp)) == VOIDmode)) { @@ -2061,7 +2074,8 @@ expand_call (tree exp, rtx target, int ignore) if (aggregate_value_p (exp, fndecl)) { /* This call returns a big structure. */ - flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK); + flags &= ~(ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE + | ECF_LIBCALL_BLOCK); #ifdef PCC_STATIC_STRUCT_RETURN { @@ -2852,13 +2866,6 @@ expand_call (tree exp, rtx target, int ignore) note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note); note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note); - - if (flags & ECF_PURE) - note = gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_USE (VOIDmode, - gen_rtx_MEM (BLKmode, - gen_rtx_SCRATCH (VOIDmode))), - note); } emit_libcall_block (insns, temp, valreg, note); @@ -3369,7 +3376,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, mem_value = assign_temp (tfom, 0, 1, 1); #endif /* This call returns a big structure. */ - flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK); + flags &= ~(ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE + | ECF_LIBCALL_BLOCK); } } else @@ -3472,10 +3480,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, end_sequence (); emit_insn (insns); } - flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK); - /* If this was a CONST function, it is now PURE since - it now reads memory. */ + /* If this was a CONST function, it is now PURE since it now + reads memory. */ if (flags & ECF_CONST) { flags &= ~ECF_CONST; @@ -3901,14 +3908,6 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, insns = get_insns (); end_sequence (); - - if (flags & ECF_PURE) - note = gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_USE (VOIDmode, - gen_rtx_MEM (BLKmode, - gen_rtx_SCRATCH (VOIDmode))), - note); - emit_libcall_block (insns, temp, valreg, note); valreg = temp; diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c index 4c17fe5af2f..994fb16fc47 100644 --- a/gcc/cfgrtl.c +++ b/gcc/cfgrtl.c @@ -2745,7 +2745,7 @@ need_fake_edge_p (const_rtx insn) if ((CALL_P (insn) && !SIBLING_CALL_P (insn) && !find_reg_note (insn, REG_NORETURN, NULL) - && !CONST_OR_PURE_CALL_P (insn))) + && !(RTL_CONST_OR_PURE_CALL_P (insn)))) return true; return ((GET_CODE (PATTERN (insn)) == ASM_OPERANDS diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index ed44a88d9ec..21b0590d66a 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -986,7 +986,7 @@ alpha_legitimize_address (rtx x, rtx scratch, emit_insn (gen_movdi_er_tlsgd (r16, pic_offset_table_rtx, x, seq)); insn = gen_call_value_osf_tlsgd (r0, tga, seq); insn = emit_call_insn (insn); - CONST_OR_PURE_CALL_P (insn) = 1; + RTL_CONST_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16); insn = get_insns (); @@ -1007,7 +1007,7 @@ alpha_legitimize_address (rtx x, rtx scratch, emit_insn (gen_movdi_er_tlsldm (r16, pic_offset_table_rtx, seq)); insn = gen_call_value_osf_tlsldm (r0, tga, seq); insn = emit_call_insn (insn); - CONST_OR_PURE_CALL_P (insn) = 1; + RTL_CONST_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16); insn = get_insns (); @@ -3013,7 +3013,7 @@ alpha_emit_xfloating_libcall (rtx func, rtx target, rtx operands[], tmp = emit_call_insn (GEN_CALL_VALUE (reg, tmp, const0_rtx, const0_rtx, const0_rtx)); CALL_INSN_FUNCTION_USAGE (tmp) = usage; - CONST_OR_PURE_CALL_P (tmp) = 1; + RTL_CONST_CALL_P (tmp) = 1; tmp = get_insns (); end_sequence (); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 825bf3d3994..7d254fec8cb 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -7859,7 +7859,7 @@ legitimize_tls_address (rtx x, enum tls_model model, int for_mov) insns = get_insns (); end_sequence (); - CONST_OR_PURE_CALL_P (insns) = 1; + RTL_CONST_CALL_P (insns) = 1; emit_libcall_block (insns, dest, rax, x); } else if (TARGET_64BIT && TARGET_GNU2_TLS) @@ -7890,7 +7890,7 @@ legitimize_tls_address (rtx x, enum tls_model model, int for_mov) note = gen_rtx_EXPR_LIST (VOIDmode, const0_rtx, NULL); note = gen_rtx_EXPR_LIST (VOIDmode, ix86_tls_get_addr (), note); - CONST_OR_PURE_CALL_P (insns) = 1; + RTL_CONST_CALL_P (insns) = 1; emit_libcall_block (insns, base, rax, note); } else if (TARGET_64BIT && TARGET_GNU2_TLS) diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 86072acf4ab..de2e42a33c7 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -2371,7 +2371,7 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) emit_insn (gen_rtx_SET (Pmode, a0, gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc))); insn = mips_expand_call (v0, mips_tls_symbol, const0_rtx, const0_rtx, false); - CONST_OR_PURE_CALL_P (insn) = 1; + RTL_CONST_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); insn = get_insns (); diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 20cfe95b95d..2e0031a67c1 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -3902,7 +3902,7 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model) tga = gen_rtx_MEM (Pmode, rs6000_tls_get_addr ()); insn = gen_call_value (r3, tga, const0_rtx, const0_rtx); insn = emit_call_insn (insn); - CONST_OR_PURE_CALL_P (insn) = 1; + RTL_CONST_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3); insn = get_insns (); end_sequence (); @@ -3920,7 +3920,7 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model) tga = gen_rtx_MEM (Pmode, rs6000_tls_get_addr ()); insn = gen_call_value (r3, tga, const0_rtx, const0_rtx); insn = emit_call_insn (insn); - CONST_OR_PURE_CALL_P (insn) = 1; + RTL_CONST_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3); insn = get_insns (); end_sequence (); diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 6dab692c2a6..95fee4d63c4 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -3178,7 +3178,7 @@ s390_emit_tls_call_insn (rtx result_reg, rtx tls_call) gen_rtx_REG (Pmode, RETURN_REGNUM)); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg); - CONST_OR_PURE_CALL_P (insn) = 1; + RTL_CONST_CALL_P (insn) = 1; } /* ADDR contains a thread-local SYMBOL_REF. Generate code to compute diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 5bbeda665d9..473db145650 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2008-05-07 Kenneth Zadeck + + * decl.c (duplicate_decls): Merge in DECL_PURE_P, TREE_READONLY, + DECL_LOOPING_CONST_OR_PURE_P attributes. + * rtti.c (build_dynamic_cast_1): Rename DECL_IS_PURE to + DECL_PURE_P. + + 2008-05-02 Simon Baldwin PR bootstrap/36108 diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 221b30040d7..8b9b2114417 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1802,11 +1802,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl); DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl); TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl); - TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl); DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl); DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl); - DECL_IS_PURE (newdecl) |= DECL_IS_PURE (olddecl); + DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl); + TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); + DECL_LOOPING_CONST_OR_PURE_P (newdecl) + |= DECL_LOOPING_CONST_OR_PURE_P (olddecl); /* Keep the old RTL. */ COPY_DECL_RTL (olddecl, newdecl); } diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index 2d2ef5b84e1..1dcd785371a 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -707,7 +707,7 @@ build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain) (NULL_TREE, ptrdiff_type_node, void_list_node)))); tmp = build_function_type (ptr_type_node, tmp); dcast_fn = build_library_fn_ptr (name, tmp); - DECL_IS_PURE (dcast_fn) = 1; + DECL_PURE_P (dcast_fn) = 1; pop_abi_namespace (); dynamic_cast_node = dcast_fn; } diff --git a/gcc/cse.c b/gcc/cse.c index ef135e99b8f..23b61349455 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -5249,7 +5249,7 @@ cse_insn (rtx insn, rtx libcall_insn) if (CALL_P (insn)) { - if (! CONST_OR_PURE_CALL_P (insn)) + if (!(RTL_CONST_OR_PURE_CALL_P (insn))) invalidate_memory (); invalidate_for_call (); } diff --git a/gcc/cselib.c b/gcc/cselib.c index c4c77c2c5ef..5d490941df8 100644 --- a/gcc/cselib.c +++ b/gcc/cselib.c @@ -1693,7 +1693,11 @@ cselib_process_insn (rtx insn) GET_MODE (REG_VALUES (i)->elt->val_rtx)))) cselib_invalidate_regno (i, reg_raw_mode[i]); - if (! CONST_OR_PURE_CALL_P (insn)) + /* Since it is not clear how cselib is going to be used, be + conservative here and treat looping pure or const functions + as if they were regular functions. */ + if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn) + || !(RTL_CONST_OR_PURE_CALL_P (insn))) cselib_invalidate_mem (callmem); } diff --git a/gcc/dce.c b/gcc/dce.c index 7b2ffe92581..403d09937e2 100644 --- a/gcc/dce.c +++ b/gcc/dce.c @@ -99,6 +99,15 @@ deletable_insn_p (rtx insn, bool fast) rtx body, x; int i; + /* We can delete dead const or pure calls as long as they do not + infinite loop and are not sibling calls. The problem with + sibling calls is that it is hard to see the result. */ + if (CALL_P (insn) + && (!SIBLING_CALL_P (insn)) + && (RTL_CONST_OR_PURE_CALL_P (insn) + && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))) + return true; + if (!NONJUMP_INSN_P (insn)) return false; diff --git a/gcc/dse.c b/gcc/dse.c index 6984e19bd95..c2289384905 100644 --- a/gcc/dse.c +++ b/gcc/dse.c @@ -1967,7 +1967,7 @@ scan_insn (bb_info_t bb_info, rtx insn) /* Const functions cannot do anything bad i.e. read memory, however, they can read their parameters which may have been pushed onto the stack. */ - if (CONST_OR_PURE_CALL_P (insn) && !pure_call_p (insn)) + if (RTL_CONST_CALL_P (insn)) { insn_info_t i_ptr = active_local_stores; insn_info_t last = NULL; diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 2fc2f92126a..aec01517b6d 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -5464,7 +5464,10 @@ emit_copy_of_insn_after (rtx insn, rtx after) CALL_INSN_FUNCTION_USAGE (new) = copy_insn (CALL_INSN_FUNCTION_USAGE (insn)); SIBLING_CALL_P (new) = SIBLING_CALL_P (insn); - CONST_OR_PURE_CALL_P (new) = CONST_OR_PURE_CALL_P (insn); + RTL_CONST_CALL_P (new) = RTL_CONST_CALL_P (insn); + RTL_PURE_CALL_P (new) = RTL_PURE_CALL_P (insn); + RTL_LOOPING_CONST_OR_PURE_CALL_P (new) + = RTL_LOOPING_CONST_OR_PURE_CALL_P (insn); break; default: diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 66873c0723c..4906bbe414e 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,8 @@ +2008-05-07 Kenneth Zadeck + + * trans-decl.c (gfc_get_extern_function_decl, build_function_decl): + Rename DECL_IS_PURE to DECL_PURE_P. + 2008-05-06 Francois-Xavier Coudert * arith.c: (gfc_arith_concat, gfc_compare_string, diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c index d204579c75f..6e0b5425a91 100644 --- a/gcc/fortran/trans-decl.c +++ b/gcc/fortran/trans-decl.c @@ -1197,7 +1197,7 @@ gfc_get_extern_function_decl (gfc_symbol * sym) if (sym->attr.pure || sym->attr.elemental) { if (sym->attr.function && !gfc_return_by_reference (sym)) - DECL_IS_PURE (fndecl) = 1; + DECL_PURE_P (fndecl) = 1; /* TODO: check if pure SUBROUTINEs don't have INTENT(OUT) parameters and don't use alternate returns (is this allowed?). In that case, calls to them are meaningless, and @@ -1324,7 +1324,7 @@ build_function_decl (gfc_symbol * sym) including an alternate return. In that case it can also be marked as PURE. See also in gfc_get_extern_function_decl(). */ if (attr.function && !gfc_return_by_reference (sym)) - DECL_IS_PURE (fndecl) = 1; + DECL_PURE_P (fndecl) = 1; TREE_SIDE_EFFECTS (fndecl) = 0; } diff --git a/gcc/gcse.c b/gcc/gcse.c index 477d4d3ef07..e881e86764e 100644 --- a/gcc/gcse.c +++ b/gcc/gcse.c @@ -2309,7 +2309,7 @@ oprs_not_set_p (const_rtx x, const_rtx insn) static void mark_call (rtx insn) { - if (! CONST_OR_PURE_CALL_P (insn)) + if (! RTL_CONST_OR_PURE_CALL_P (insn)) record_last_mem_set_info (insn); } @@ -5987,7 +5987,7 @@ store_killed_in_insn (const_rtx x, const_rtx x_regs, const_rtx insn, int after) { /* A normal or pure call might read from pattern, but a const call will not. */ - if (! CONST_OR_PURE_CALL_P (insn) || pure_call_p (insn)) + if (RTL_CONST_CALL_P (insn)) return true; /* But even a const call reads its parameters. Check whether the diff --git a/gcc/gimplify.c b/gcc/gimplify.c index e36d5db93c8..fc90fa35a20 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -2274,10 +2274,14 @@ gimplify_call_expr (tree *expr_p, tree *pre_p, bool want_value) /* If the function is "const" or "pure", then clear TREE_SIDE_EFFECTS on its decl. This allows us to eliminate redundant or useless calls to "const" functions. */ - if (TREE_CODE (*expr_p) == CALL_EXPR - && (call_expr_flags (*expr_p) & (ECF_CONST | ECF_PURE))) - TREE_SIDE_EFFECTS (*expr_p) = 0; - + if (TREE_CODE (*expr_p) == CALL_EXPR) + { + int flags = call_expr_flags (*expr_p); + if (flags & (ECF_CONST | ECF_PURE) + /* An infinite loop is considered a side effect. */ + && !(flags & (ECF_LOOPING_CONST_OR_PURE))) + TREE_SIDE_EFFECTS (*expr_p) = 0; + } return ret; } diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 98b707a3e77..26ff9680baf 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -2168,9 +2168,7 @@ noce_can_store_speculate_p (basic_block top_bb, const_rtx mem) unconditionally before the barrier. */ if (INSN_P (insn) && (volatile_insn_p (PATTERN (insn)) - || (CALL_P (insn) - && (!CONST_OR_PURE_CALL_P (insn) - || pure_call_p (insn))))) + || (CALL_P (insn) && (!RTL_CONST_CALL_P (insn))))) return false; if (memory_modified_in_insn_p (mem, insn)) diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index c180e35d6c1..a2c920601ac 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -19,7 +19,8 @@ along with GCC; see the file COPYING3. If not see . */ /* This file mark functions as being either const (TREE_READONLY) or - pure (DECL_IS_PURE). + pure (DECL_PURE_P). It can also set the a variant of these that + are allowed to infinite loop (DECL_LOOPING_CONST_PURE_P). This must be run after inlining decisions have been made since otherwise, the local sets will not contain information that is @@ -69,6 +70,7 @@ enum pure_const_state_e struct funct_state_d { enum pure_const_state_e pure_const_state; + bool looping; bool state_set_in_source; }; @@ -95,6 +97,7 @@ check_decl (funct_state local, if (lookup_attribute ("used", DECL_ATTRIBUTES (t))) { local->pure_const_state = IPA_NEITHER; + local->looping = false; return; } @@ -103,6 +106,7 @@ check_decl (funct_state local, if (TREE_THIS_VOLATILE (t)) { local->pure_const_state = IPA_NEITHER; + local->looping = false; return; } @@ -116,6 +120,7 @@ check_decl (funct_state local, if (checking_write) { local->pure_const_state = IPA_NEITHER; + local->looping = false; return; } @@ -174,6 +179,7 @@ check_tree (funct_state local, tree t, bool checking_write) if (TREE_THIS_VOLATILE (t)) { local->pure_const_state = IPA_NEITHER; + local->looping = false; return; } @@ -199,6 +205,7 @@ check_tree (funct_state local, tree t, bool checking_write) if (checking_write) { local->pure_const_state = IPA_NEITHER; + local->looping = false; return; } else if (local->pure_const_state == IPA_CONST) @@ -346,7 +353,10 @@ check_call (funct_state local, tree call_expr) /* When bad things happen to bad functions, they cannot be const or pure. */ if (setjmp_call_p (callee_t)) - local->pure_const_state = IPA_NEITHER; + { + local->pure_const_state = IPA_NEITHER; + local->looping = false; + } if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL) switch (DECL_FUNCTION_CODE (callee_t)) @@ -354,6 +364,7 @@ check_call (funct_state local, tree call_expr) case BUILT_IN_LONGJMP: case BUILT_IN_NONLOCAL_GOTO: local->pure_const_state = IPA_NEITHER; + local->looping = false; break; default: break; @@ -480,7 +491,10 @@ scan_function (tree *tp, case LABEL_EXPR: if (DECL_NONLOCAL (TREE_OPERAND (t, 0))) /* Target of long jump. */ - local->pure_const_state = IPA_NEITHER; + { + local->pure_const_state = IPA_NEITHER; + local->looping = false; + } break; case CALL_EXPR: @@ -513,6 +527,10 @@ analyze_function (struct cgraph_node *fn) l->pure_const_state = IPA_CONST; l->state_set_in_source = false; + if (DECL_LOOPING_CONST_OR_PURE_P (decl)) + l->looping = true; + else + l->looping = false; /* If this function does not return normally or does not bind local, do not touch this unless it has been marked as const or pure by the @@ -529,7 +547,7 @@ analyze_function (struct cgraph_node *fn) l->pure_const_state = IPA_CONST; l->state_set_in_source = true; } - if (DECL_IS_PURE (decl)) + if (DECL_PURE_P (decl)) { l->pure_const_state = IPA_PURE; l->state_set_in_source = true; @@ -644,6 +662,7 @@ static_execute (void) for (i = 0; i < order_pos; i++ ) { enum pure_const_state_e pure_const_state = IPA_CONST; + bool looping = false; int count = 0; node = order[i]; @@ -655,6 +674,9 @@ static_execute (void) if (pure_const_state < w_l->pure_const_state) pure_const_state = w_l->pure_const_state; + if (w_l->looping) + looping = true; + if (pure_const_state == IPA_NEITHER) break; @@ -663,24 +685,8 @@ static_execute (void) struct cgraph_edge *e; count++; - /* FIXME!!! Because of pr33826, we cannot have either - immediate or transitive recursive functions marked as - pure or const because dce can delete a function that - is in reality an infinite loop. A better solution - than just outlawing them is to add another bit the - functions to distinguish recursive from non recursive - pure and const function. This would allow the - recursive ones to be cse'd but not dce'd. In this - same vein, we could allow functions with loops to - also be cse'd but not dce'd. - - Unfortunately we are late in stage 3, and the fix - described above is is not appropriate. */ if (count > 1) - { - pure_const_state = IPA_NEITHER; - break; - } + looping = true; for (e = w->callees; e; e = e->next_callee) { @@ -688,13 +694,8 @@ static_execute (void) /* Only look at the master nodes and skip external nodes. */ y = cgraph_master_clone (y); - /* Check for immediate recursive functions. See the - FIXME above. */ if (w == y) - { - pure_const_state = IPA_NEITHER; - break; - } + looping = true; if (y) { funct_state y_l = get_function_state (y); @@ -702,6 +703,8 @@ static_execute (void) pure_const_state = y_l->pure_const_state; if (pure_const_state == IPA_NEITHER) break; + if (y_l->looping) + looping = true; } } } @@ -724,15 +727,19 @@ static_execute (void) { case IPA_CONST: TREE_READONLY (w->decl) = 1; + DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping; if (dump_file) - fprintf (dump_file, "Function found to be const: %s\n", + fprintf (dump_file, "Function found to be %sconst: %s\n", + looping ? "looping " : "", lang_hooks.decl_printable_name(w->decl, 2)); break; case IPA_PURE: - DECL_IS_PURE (w->decl) = 1; + DECL_PURE_P (w->decl) = 1; + DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping; if (dump_file) - fprintf (dump_file, "Function found to be pure: %s\n", + fprintf (dump_file, "Function found to be %spure: %s\n", + looping ? "looping " : "", lang_hooks.decl_printable_name(w->decl, 2)); break; diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog index a9487a9be12..411806af5a8 100644 --- a/gcc/java/ChangeLog +++ b/gcc/java/ChangeLog @@ -1,3 +1,8 @@ +2008-05-07 Kenneth Zadeck + + * decl.c (java_init_decl_processing): Change DECL_IS_PURE to + DECL_PURE_P. + 2008-04-23 Paolo Bonzini * class.c (build_utf8_ref): Don't set TREE_INVARIANT. diff --git a/gcc/java/decl.c b/gcc/java/decl.c index d449e39b539..fcc1c126e11 100644 --- a/gcc/java/decl.c +++ b/gcc/java/decl.c @@ -906,7 +906,7 @@ java_init_decl_processing (void) = add_builtin_function ("_Jv_ResolvePoolEntry", build_function_type (ptr_type_node, t), 0,NOT_BUILT_IN, NULL, NULL_TREE); - DECL_IS_PURE (soft_resolvepoolentry_node) = 1; + DECL_PURE_P (soft_resolvepoolentry_node) = 1; throw_node = add_builtin_function ("_Jv_Throw", build_function_type (void_type_node, t), 0, NOT_BUILT_IN, NULL, NULL_TREE); @@ -1000,7 +1000,7 @@ java_init_decl_processing (void) = add_builtin_function ("_Jv_IsInstanceOf", build_function_type (boolean_type_node, t), 0, NOT_BUILT_IN, NULL, NULL_TREE); - DECL_IS_PURE (soft_instanceof_node) = 1; + DECL_PURE_P (soft_instanceof_node) = 1; t = tree_cons (NULL_TREE, object_ptr_type_node, tree_cons (NULL_TREE, object_ptr_type_node, endlink)); soft_checkarraystore_node @@ -1014,7 +1014,7 @@ java_init_decl_processing (void) = add_builtin_function ("_Jv_LookupInterfaceMethodIdx", build_function_type (ptr_type_node, t), 0, NOT_BUILT_IN, NULL, NULL_TREE); - DECL_IS_PURE (soft_lookupinterfacemethod_node) = 1; + DECL_PURE_P (soft_lookupinterfacemethod_node) = 1; t = tree_cons (NULL_TREE, ptr_type_node, tree_cons (NULL_TREE, ptr_type_node, tree_cons (NULL_TREE, ptr_type_node, endlink))); diff --git a/gcc/local-alloc.c b/gcc/local-alloc.c index af97caf0173..9194d118691 100644 --- a/gcc/local-alloc.c +++ b/gcc/local-alloc.c @@ -505,7 +505,7 @@ validate_equiv_mem (rtx start, rtx reg, rtx memref) return 1; if (CALL_P (insn) && ! MEM_READONLY_P (memref) - && ! CONST_OR_PURE_CALL_P (insn)) + && ! RTL_CONST_OR_PURE_CALL_P (insn)) return 0; note_stores (PATTERN (insn), validate_equiv_mem_from_store, NULL); @@ -781,9 +781,7 @@ memref_used_between_p (rtx memref, rtx start, rtx end) return 1; /* Nonconst functions may access memory. */ - if (CALL_P (insn) - && (! CONST_OR_PURE_CALL_P (insn) - || pure_call_p (insn))) + if (CALL_P (insn) && (! RTL_CONST_CALL_P (insn))) return 1; } diff --git a/gcc/loop-invariant.c b/gcc/loop-invariant.c index f043884b15a..c586cf94804 100644 --- a/gcc/loop-invariant.c +++ b/gcc/loop-invariant.c @@ -563,7 +563,8 @@ find_exits (struct loop *loop, basic_block *body, FOR_BB_INSNS (body[i], insn) { if (CALL_P (insn) - && !CONST_OR_PURE_CALL_P (insn)) + && (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn) + || !RTL_CONST_OR_PURE_CALL_P (insn))) { has_call = true; bitmap_set_bit (may_exit, i); @@ -904,7 +905,8 @@ find_invariants_bb (basic_block bb, bool always_reached, bool always_executed) if (always_reached && CALL_P (insn) - && !CONST_OR_PURE_CALL_P (insn)) + && (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn) + || ! RTL_CONST_OR_PURE_CALL_P (insn))) always_reached = false; } } diff --git a/gcc/postreload-gcse.c b/gcc/postreload-gcse.c index ef78fde2179..4dc312d91cf 100644 --- a/gcc/postreload-gcse.c +++ b/gcc/postreload-gcse.c @@ -758,7 +758,7 @@ record_opr_changes (rtx insn) } } - if (! CONST_OR_PURE_CALL_P (insn)) + if (! RTL_CONST_OR_PURE_CALL_P (insn)) record_last_mem_set_info (insn); } } diff --git a/gcc/reorg.c b/gcc/reorg.c index 773950909b3..c20c337d944 100644 --- a/gcc/reorg.c +++ b/gcc/reorg.c @@ -3155,7 +3155,7 @@ delete_prior_computation (rtx note, rtx insn) /* If we reach a CALL which is not calling a const function or the callee pops the arguments, then give up. */ if (CALL_P (our_prev) - && (! CONST_OR_PURE_CALL_P (our_prev) + && (! RTL_CONST_CALL_P (our_prev) || GET_CODE (pat) != SET || GET_CODE (SET_SRC (pat)) != CALL)) break; diff --git a/gcc/rtl.h b/gcc/rtl.h index aaa1a925a9d..b747a27f592 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -253,14 +253,17 @@ struct rtx_def GTY((chain_next ("RTX_NEXT (&%h)"), In a CODE_LABEL, part of the two-bit alternate entry field. */ unsigned int jump : 1; /* In a CODE_LABEL, part of the two-bit alternate entry field. - 1 in a MEM if it cannot trap. */ + 1 in a MEM if it cannot trap. + 1 in a CALL_INSN logically equivalent to + ECF_LOOPING_CONST_OR_PURE and DECL_LOOPING_CONST_OR_PURE_P. */ unsigned int call : 1; /* 1 in a REG, MEM, or CONCAT if the value is set at most once, anywhere. 1 in a SUBREG if it references an unsigned object whose mode has been from a promoted to a wider mode. 1 in a SYMBOL_REF if it addresses something in the per-function constants pool. - 1 in a CALL_INSN, NOTE, or EXPR_LIST for a const or pure call. + 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, CALL_INSN, or INSN of an annulling branch. */ unsigned int unchanging : 1; /* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile. @@ -303,7 +306,8 @@ struct rtx_def GTY((chain_next ("RTX_NEXT (&%h)"), 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 SYMBOL_REF for a weak symbol. + 1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P. */ unsigned return_val : 1; /* The first element of the operands of this rtx. @@ -765,10 +769,24 @@ extern void rtl_check_failed_flag (const char *, const_rtx, const char *, (RTL_FLAG_CHECK6("INSN_DELETED_P", (RTX), INSN, CALL_INSN, JUMP_INSN, \ CODE_LABEL, BARRIER, NOTE)->volatil) +/* 1 if RTX is a call to a const function. Built from ECF_CONST and + TREE_READONLY. */ +#define RTL_CONST_CALL_P(RTX) \ + (RTL_FLAG_CHECK1("RTL_CONST_CALL_P", (RTX), CALL_INSN)->unchanging) + +/* 1 if RTX is a call to a pure function. Built from ECF_PURE and + DECL_PURE_P. */ +#define RTL_PURE_CALL_P(RTX) \ + (RTL_FLAG_CHECK1("RTL_PURE_CALL_P", (RTX), CALL_INSN)->return_val) + /* 1 if RTX is a call to a const or pure function. */ -#define CONST_OR_PURE_CALL_P(RTX) \ - (RTL_FLAG_CHECK3("CONST_OR_PURE_CALL_P", (RTX), CALL_INSN, NOTE, \ - EXPR_LIST)->unchanging) +#define RTL_CONST_OR_PURE_CALL_P(RTX) \ + (RTL_CONST_CALL_P(RTX) || RTL_PURE_CALL_P(RTX)) + +/* 1 if RTX is a call to a looping const or pure function. Built from + ECF_LOOPING_CONST_OR_PURE and DECL_LOOPING_CONST_OR_PURE_P. */ +#define RTL_LOOPING_CONST_OR_PURE_CALL_P(RTX) \ + (RTL_FLAG_CHECK1("CONST_OR_PURE_CALL_P", (RTX), CALL_INSN)->call) /* 1 if RTX is a call_insn for a sibling call. */ #define SIBLING_CALL_P(RTX) \ @@ -1733,7 +1751,6 @@ extern rtx find_reg_equal_equiv_note (const_rtx); extern rtx find_constant_src (const_rtx); extern int find_reg_fusage (const_rtx, enum rtx_code, const_rtx); extern int find_regno_fusage (const_rtx, enum rtx_code, unsigned int); -extern int pure_call_p (const_rtx); extern void remove_note (rtx, const_rtx); extern void remove_reg_equal_equiv_notes (rtx); extern int side_effects_p (const_rtx); diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 93833a3e951..f0dc512fcdf 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -1846,29 +1846,6 @@ find_regno_fusage (const_rtx insn, enum rtx_code code, unsigned int regno) return 0; } -/* Return true if INSN is a call to a pure function. */ - -int -pure_call_p (const_rtx insn) -{ - const_rtx link; - - if (!CALL_P (insn) || ! CONST_OR_PURE_CALL_P (insn)) - return 0; - - /* Look for the note that differentiates const and pure functions. */ - for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) - { - rtx u, m; - - if (GET_CODE (u = XEXP (link, 0)) == USE - && MEM_P (m = XEXP (u, 0)) && GET_MODE (m) == BLKmode - && GET_CODE (XEXP (m, 0)) == SCRATCH) - return 1; - } - - return 0; -} /* Remove register note NOTE from the REG_NOTES of INSN. */ diff --git a/gcc/sched-deps.c b/gcc/sched-deps.c index 1f85781ebce..b62797f6a56 100644 --- a/gcc/sched-deps.c +++ b/gcc/sched-deps.c @@ -2304,7 +2304,8 @@ sched_analyze (struct deps *deps, rtx head, rtx tail) all pending reads and writes, and start new dependencies starting from here. But only flush writes for constant calls (which may be passed a pointer to something we haven't written yet). */ - flush_pending_lists (deps, insn, true, !CONST_OR_PURE_CALL_P (insn)); + flush_pending_lists (deps, insn, true, + ! RTL_CONST_OR_PURE_CALL_P (insn)); /* Remember the last function call for limiting lifetimes. */ free_INSN_LIST_list (&deps->last_function_call); diff --git a/gcc/see.c b/gcc/see.c index c6f584cc653..6e5260b995a 100644 --- a/gcc/see.c +++ b/gcc/see.c @@ -2430,7 +2430,10 @@ see_copy_insn (rtx insn) CALL_INSN_FUNCTION_USAGE (ret) = copy_rtx (CALL_INSN_FUNCTION_USAGE (insn)); SIBLING_CALL_P (ret) = SIBLING_CALL_P (insn); - CONST_OR_PURE_CALL_P (ret) = CONST_OR_PURE_CALL_P (insn); + RTL_CONST_CALL_P (ret) = RTL_CONST_CALL_P (insn); + RTL_PURE_CALL_P (ret) = RTL_PURE_CALL_P (insn); + RTL_LOOPING_CONST_OR_PURE_CALL_P (ret) + = RTL_LOOPING_CONST_OR_PURE_CALL_P (insn); } else gcc_unreachable (); diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index add7362d7d3..f559030fc58 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -1792,9 +1792,11 @@ static void update_call_expr_flags (tree call) { tree decl = get_callee_fndecl (call); + int flags; if (!decl) return; - if (call_expr_flags (call) & (ECF_CONST | ECF_PURE)) + flags = call_expr_flags (call); + if (flags & (ECF_CONST | ECF_PURE) && !(flags & ECF_LOOPING_CONST_OR_PURE)) TREE_SIDE_EFFECTS (call) = 0; if (TREE_NOTHROW (decl)) TREE_NOTHROW (call) = 1; @@ -3906,7 +3908,19 @@ verify_gimple_expr (tree expr) case CALL_EXPR: /* FIXME. The C frontend passes unpromoted arguments in case it didn't see a function declaration before the call. */ - return false; + { + tree decl = CALL_EXPR_FN (expr); + + if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_LOOPING_CONST_OR_PURE_P (decl) + && (!DECL_PURE_P (decl)) + && (!TREE_READONLY (decl))) + { + error ("invalid pure const state for function"); + return true; + } + return false; + } case OBJ_TYPE_REF: /* FIXME. */ diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 687ed95c964..28727ccec0b 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -671,7 +671,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) { value = *n; STRIP_TYPE_NOPS (value); - if (TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value)) + if (TREE_CONSTANT (value) || TREE_READONLY (value)) { *tp = build_empty_stmt (); return copy_body_r (tp, walk_subtrees, data); diff --git a/gcc/tree-optimize.c b/gcc/tree-optimize.c index 8c53e6f28b6..270353601da 100644 --- a/gcc/tree-optimize.c +++ b/gcc/tree-optimize.c @@ -299,7 +299,8 @@ execute_fixup_cfg (void) tree call = get_call_expr_in (stmt); tree decl = call ? get_callee_fndecl (call) : NULL; - if (decl && call_expr_flags (call) & (ECF_CONST | ECF_PURE) + if (decl && call_expr_flags (call) & (ECF_CONST | ECF_PURE + | ECF_LOOPING_CONST_OR_PURE) && TREE_SIDE_EFFECTS (call)) { if (gimple_in_ssa_p (cfun)) diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 13c4e979b7d..a8db47dc06d 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -2077,7 +2077,7 @@ can_value_number_call (tree stmt) { tree call = get_call_expr_in (stmt); - if (call_expr_flags (call) & (ECF_PURE | ECF_CONST)) + if (call_expr_flags (call) & (ECF_PURE | ECF_CONST)) return true; return false; } diff --git a/gcc/tree.c b/gcc/tree.c index 73adaa10b03..182d86a5a1d 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -7373,7 +7373,9 @@ local_define_builtin (const char *name, tree type, enum built_in_function code, if (ecf_flags & ECF_CONST) TREE_READONLY (decl) = 1; if (ecf_flags & ECF_PURE) - DECL_IS_PURE (decl) = 1; + DECL_PURE_P (decl) = 1; + if (ecf_flags & ECF_LOOPING_CONST_OR_PURE) + DECL_LOOPING_CONST_OR_PURE_P (decl) = 1; if (ecf_flags & ECF_NORETURN) TREE_THIS_VOLATILE (decl) = 1; if (ecf_flags & ECF_NOTHROW) @@ -7766,7 +7768,7 @@ process_call_operands (tree t) /* Calls have side-effects, except those to const or pure functions. */ i = call_expr_flags (t); - if (!(i & (ECF_CONST | ECF_PURE))) + if ((i & ECF_LOOPING_CONST_OR_PURE) || !(i & (ECF_CONST | ECF_PURE))) side_effects = 1; } TREE_SIDE_EFFECTS (t) = side_effects; diff --git a/gcc/tree.h b/gcc/tree.h index d52c76095fb..0b4b443311d 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -788,7 +788,7 @@ enum tree_node_structure_enum { &__t->phi.a[__i]; })) #define OMP_CLAUSE_ELT_CHECK(T, I) __extension__ \ -(*({__typeof (T) const __t = (T); \ +(*({__typeof (T) const __t = (T); \ const int __i = (I); \ if (TREE_CODE (__t) != OMP_CLAUSE) \ tree_check_failed (__t, __FILE__, __LINE__, __FUNCTION__, \ @@ -1281,13 +1281,11 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, #define TREE_THIS_NOTRAP(NODE) ((NODE)->base.nothrow_flag) /* In a VAR_DECL, PARM_DECL or FIELD_DECL, or any kind of ..._REF node, - nonzero means it may not be the lhs of an assignment. */ + nonzero means it may not be the lhs of an assignment. + Nonzero in a FUNCTION_DECL means this function should be treated + as "const" function (can only read its arguments). */ #define TREE_READONLY(NODE) (NON_TYPE_CHECK (NODE)->base.readonly_flag) -/* Nonzero if NODE is a _DECL with TREE_READONLY set. */ -#define TREE_READONLY_DECL_P(NODE)\ - (DECL_P (NODE) && TREE_READONLY (NODE)) - /* Value of expression is constant. Always on in all ..._CST nodes. May also appear in an expression or decl where the value is constant. */ #define TREE_CONSTANT(NODE) (NON_TYPE_CHECK (NODE)->base.constant_flag) @@ -3256,7 +3254,16 @@ struct tree_decl_non_common GTY(()) /* Nonzero in a FUNCTION_DECL means this function should be treated as "pure" function (like const function, but may read global memory). */ -#define DECL_IS_PURE(NODE) (FUNCTION_DECL_CHECK (NODE)->function_decl.pure_flag) +#define DECL_PURE_P(NODE) (FUNCTION_DECL_CHECK (NODE)->function_decl.pure_flag) + +/* Nonzero only if one of TREE_READONLY or DECL_PURE_P is nonzero AND + the const or pure function may not terminate. When this is nonzero + for a const or pure function, it can be dealt with by cse passes + but cannot be removed by dce passes since you are not allowed to + change an infinite looping program into one that terminates without + error. */ +#define DECL_LOOPING_CONST_OR_PURE_P(NODE) \ + (FUNCTION_DECL_CHECK (NODE)->function_decl.looping_const_or_pure_flag) /* Nonzero in a FUNCTION_DECL means this function should be treated as "novops" function (function that does not read global memory, @@ -3354,7 +3361,6 @@ struct tree_function_decl GTY(()) unsigned returns_twice_flag : 1; unsigned malloc_flag : 1; unsigned operator_new_flag : 1; - unsigned pure_flag : 1; unsigned declared_inline_flag : 1; unsigned regdecl_flag : 1; @@ -3362,8 +3368,11 @@ struct tree_function_decl GTY(()) unsigned no_instrument_function_entry_exit : 1; unsigned no_limit_stack : 1; unsigned disregard_inline_limits : 1; + unsigned pure_flag : 1; + unsigned looping_const_or_pure_flag : 1; + - /* 4 bits left */ + /* 3 bits left */ }; /* For a TYPE_DECL, holds the "original" type. (TREE_TYPE has the copy.) */ @@ -4987,28 +4996,34 @@ extern tree build_duplicate_type (tree); /* Nonzero if this is a call to a function whose return value depends solely on its arguments, has no side effects, and does not read - global memory. */ -#define ECF_CONST 1 + global memory. This corresponds to TREE_READONLY for function + decls. */ +#define ECF_CONST (1 << 0) +/* Nonzero if this is a call to "pure" function (like const function, + but may read memory. This corresponds to DECL_PURE_P for function + decls. */ +#define ECF_PURE (1 << 1) +/* Nonzero if this is ECF_CONST or ECF_PURE but cannot be proven to no + infinite loop. This corresponds to DECL_LOOPING_CONST_OR_PURE_P + for function decls.*/ +#define ECF_LOOPING_CONST_OR_PURE (1 << 2) /* Nonzero if this call will never return. */ -#define ECF_NORETURN 2 +#define ECF_NORETURN (1 << 3) /* Nonzero if this is a call to malloc or a related function. */ -#define ECF_MALLOC 4 +#define ECF_MALLOC (1 << 4) /* Nonzero if it is plausible that this is a call to alloca. */ -#define ECF_MAY_BE_ALLOCA 8 +#define ECF_MAY_BE_ALLOCA (1 << 5) /* Nonzero if this is a call to a function that won't throw an exception. */ -#define ECF_NOTHROW 16 +#define ECF_NOTHROW (1 << 6) /* Nonzero if this is a call to setjmp or a related function. */ -#define ECF_RETURNS_TWICE 32 +#define ECF_RETURNS_TWICE (1 << 7) /* Nonzero if this call replaces the current stack frame. */ -#define ECF_SIBCALL 64 -/* Nonzero if this is a call to "pure" function (like const function, - but may read memory. */ -#define ECF_PURE 128 +#define ECF_SIBCALL (1 << 8) /* Create libcall block around the call. */ -#define ECF_LIBCALL_BLOCK 256 +#define ECF_LIBCALL_BLOCK (1 << 9) /* Function does not read or write memory (but may have side effects, so it does not necessarily fit ECF_CONST). */ -#define ECF_NOVOPS 512 +#define ECF_NOVOPS (1 << 10) extern int flags_from_decl_or_type (const_tree); extern int call_expr_flags (const_tree); -- 2.30.2