From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 27791 invoked by alias); 13 Nov 2008 20:08:38 -0000 Received: (qmail 25747 invoked by uid 22791); 13 Nov 2008 20:08:28 -0000 X-Spam-Check-By: sourceware.org Received: from yx-out-1718.google.com (HELO yx-out-1718.google.com) (74.125.44.152) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 13 Nov 2008 20:07:49 +0000 Received: by yx-out-1718.google.com with SMTP id 36so463939yxh.26 for ; Thu, 13 Nov 2008 12:07:45 -0800 (PST) Received: by 10.100.253.7 with SMTP id a7mr68758ani.159.1226606865610; Thu, 13 Nov 2008 12:07:45 -0800 (PST) Received: from ?9.2.34.21? (yktgi01e0-s5.watson.ibm.com [129.34.20.19]) by mx.google.com with ESMTPS id b32sm19537638ana.54.2008.11.13.12.07.43 (version=TLSv1/SSLv3 cipher=RC4-MD5); Thu, 13 Nov 2008 12:07:44 -0800 (PST) Message-ID: <491C890F.2030403@naturalbridge.com> Date: Thu, 13 Nov 2008 20:50:00 -0000 From: Kenneth Zadeck User-Agent: Thunderbird 2.0.0.17 (X11/20080922) MIME-Version: 1.0 To: Jan Hubicka CC: gcc-patches@gcc.gnu.org, dnovillo@google.com Subject: Re: Rewrite of pure/const pass and nothrow stuff References: <20081113190834.GA11360@kam.mff.cuni.cz> In-Reply-To: <20081113190834.GA11360@kam.mff.cuni.cz> Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2008-11/txt/msg00599.txt.bz2 Jan Hubicka wrote: > Hi, > this patch teach pure/const about nothrow as discussed earlier. I eneded up > rewriting the local analysis from scratch as after tuplification it ended up remarkably > ugly. > > There are several new flasing features: > 1) Local analysis should be more aggressive (I hope I did not miss anything important) > 2) There is local variant of the pass. It can be done during early optimization > and at the place original RTL pass executed. Especially in early > optimization we are effective. Main motivation for doing so early is to strip down EH > and dead calls to get early inliner more informed; main motivation for late pass is to avoid > dwarf2out from unnecesarily outputting unwind info and being stupid. > IPA pass is the only able to handle non-trivial recursion cycles. > > On tramp3d 4828 functions are marked nothrow in early pass, 0 in IPA pass, 36 in late pass. > 7386 functions are found const in early pass, 283 in IPA pass, 45 in late pass. > 1462 functions are found pure in early pass, 516 in IPA pass, 22 in late pass. > > Old implementation found 15 const and 12 pure functions. > > 3) There is readable debug output showing operation of local scanner > 4) fixup_cfg is now pass that can be scheduled as needed that should help LTO too. > > Bootstrapping/regtesting x86_64-linux, will commit it once finished to pretty-ipa. > > Assuming that this all checks out with the regression tests, this is also ok whenever stage 1 opens again. kenny > Honza > > * tree-pass.h (pass_fixup_cfg, pass_local_pure_const): Declare. > * ipa-pure-const.c (funct_state_d): Add can throw field; make > state_set_in_source enum > (check_decl): Ignore memory tags; do not set fake looping flags; > dump diagnostics. > (check_operand, check_tree, check_rhs_var, check_lhs_var, > get_asm_expr_operands, scan_function_op, scan_function_stmt): Remove. > (check_call, analyze_function): Rewrite. > (check_stmt): New. > (add_new_function): Update call of analyze_function. > (generate_summary): Add call of analyze_function. > (propagate): Propagate can_throw; handle state_set_in_source correctly. > (local_pure_const): New function. > (pass_local_pure_const): New pass. > * ipa-inline.c (inline_transform): Set after_inlining. > * tree-eh.c (stmt_can_throw_external): New. > * tree-optimize.c (execute_fixup_cfg): Do not set after_inlining; > work with aliasing built. > * tree-flow.h (stmt_can_throw_external): New. > * passes.c (init_optimization_passes): Schedule fixup_cfg pass early; > and local pure/const pass in early and late optimization queue. > Index: tree-pass.h > =================================================================== > *** tree-pass.h (revision 141798) > --- tree-pass.h (working copy) > *************** extern struct gimple_opt_pass pass_build > *** 308,313 **** > --- 308,314 ---- > extern struct gimple_opt_pass pass_tree_profile; > extern struct gimple_opt_pass pass_early_tree_profile; > extern struct gimple_opt_pass pass_cleanup_cfg; > + extern struct gimple_opt_pass pass_fixup_cfg; > extern struct gimple_opt_pass pass_referenced_vars; > extern struct gimple_opt_pass pass_sra; > extern struct gimple_opt_pass pass_sra_early; > *************** extern struct gimple_opt_pass pass_reass > *** 388,393 **** > --- 389,395 ---- > extern struct gimple_opt_pass pass_rebuild_cgraph_edges; > extern struct gimple_opt_pass pass_build_cgraph_edges; > extern struct gimple_opt_pass pass_reset_cc_flags; > + extern struct gimple_opt_pass pass_local_pure_const; > > /* IPA Passes */ > extern struct ipa_opt_pass pass_ipa_inline; > Index: ipa-pure-const.c > =================================================================== > *** ipa-pure-const.c (revision 141798) > --- ipa-pure-const.c (working copy) > *************** struct funct_state_d > *** 71,76 **** > --- 71,78 ---- > { > /* See above. */ > enum pure_const_state_e pure_const_state; > + /* What user set here; we can be always sure about this. */ > + enum pure_const_state_e state_set_in_source; > > /* True if the function could possibly infinite loop. There are a > lot of ways that this could be determined. We are pretty > *************** struct funct_state_d > *** 80,89 **** > a behavioral change. */ > bool looping; > > ! /* If the state of the function was set in the source, then assume > ! that it was done properly even if the analysis we do would be > ! more pessimestic. */ > ! bool state_set_in_source; > }; > > typedef struct funct_state_d * funct_state; > --- 82,88 ---- > a behavioral change. */ > bool looping; > > ! bool can_throw; > }; > > typedef struct funct_state_d * funct_state; > *************** static inline void > *** 141,152 **** > check_decl (funct_state local, > tree t, bool checking_write) > { > /* Do not want to do anything with volatile except mark any > function that uses one to be not const or pure. */ > if (TREE_THIS_VOLATILE (t)) > { > local->pure_const_state = IPA_NEITHER; > ! local->looping = false; > return; > } > > --- 140,154 ---- > check_decl (funct_state local, > tree t, bool checking_write) > { > + if (MTAG_P (t)) > + return; > /* Do not want to do anything with volatile except mark any > function that uses one to be not const or pure. */ > if (TREE_THIS_VOLATILE (t)) > { > local->pure_const_state = IPA_NEITHER; > ! if (dump_file) > ! fprintf (dump_file, " Volatile operand is not const/pure"); > return; > } > > *************** check_decl (funct_state local, > *** 159,165 **** > if (lookup_attribute ("used", DECL_ATTRIBUTES (t))) > { > local->pure_const_state = IPA_NEITHER; > ! local->looping = false; > return; > } > > --- 161,168 ---- > if (lookup_attribute ("used", DECL_ATTRIBUTES (t))) > { > local->pure_const_state = IPA_NEITHER; > ! if (dump_file) > ! fprintf (dump_file, " Used static/global variable is not const/pure\n"); > return; > } > > *************** check_decl (funct_state local, > *** 169,175 **** > if (checking_write) > { > local->pure_const_state = IPA_NEITHER; > ! local->looping = false; > return; > } > > --- 172,179 ---- > if (checking_write) > { > local->pure_const_state = IPA_NEITHER; > ! if (dump_file) > ! fprintf (dump_file, " static/global memory write is not const/pure\n"); > return; > } > > *************** check_decl (funct_state local, > *** 177,333 **** > { > /* Readonly reads are safe. */ > if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t))) > ! ; /* Read of a constant, do not change the function state. */ > else > { > /* Just a regular read. */ > if (local->pure_const_state == IPA_CONST) > local->pure_const_state = IPA_PURE; > } > } > ! > ! /* Compilation level statics can be read if they are readonly > ! variables. */ > ! if (TREE_READONLY (t)) > ! return; > ! > ! /* Just a regular read. */ > ! if (local->pure_const_state == IPA_CONST) > ! local->pure_const_state = IPA_PURE; > } > > - /* If T is a VAR_DECL check to see if it is an allowed reference. */ > - > - static void > - check_operand (funct_state local, > - tree t, bool checking_write) > - { > - if (!t) return; > - > - if (TREE_CODE (t) == VAR_DECL) > - check_decl (local, t, checking_write); > - } > > ! /* Examine tree T for references. */ > > ! static void > ! check_tree (funct_state local, tree t, bool checking_write) > { > ! if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR) > ! || TREE_CODE (t) == SSA_NAME) > return; > ! > ! /* Any tree which is volatile disqualifies this function from being > ! const or pure. */ > ! if (TREE_THIS_VOLATILE (t)) > { > ! local->pure_const_state = IPA_NEITHER; > ! local->looping = false; > ! return; > ! } > ! > ! while (TREE_CODE (t) == REALPART_EXPR > ! || TREE_CODE (t) == IMAGPART_EXPR > ! || handled_component_p (t)) > ! { > ! if (TREE_CODE (t) == ARRAY_REF) > ! check_operand (local, TREE_OPERAND (t, 1), false); > ! t = TREE_OPERAND (t, 0); > ! } > ! > ! /* The bottom of an indirect reference can only be read, not > ! written. */ > ! if (INDIRECT_REF_P (t)) > ! { > ! check_tree (local, TREE_OPERAND (t, 0), false); > ! > ! /* Any indirect reference that occurs on the lhs > ! disqualifies the function from being pure or const. Any > ! indirect reference that occurs on the rhs disqualifies the > ! function from being const. */ > ! if (checking_write) > ! { > local->pure_const_state = IPA_NEITHER; > ! local->looping = false; > return; > } > ! else if (local->pure_const_state == IPA_CONST) > ! local->pure_const_state = IPA_PURE; > ! } > ! > ! if (SSA_VAR_P (t)) > ! check_operand (local, t, checking_write); > ! } > ! > ! /* Check to see if T is a read or address of operation on a var we are > ! interested in analyzing. LOCAL is passed in to get access to its > ! bit vectors. */ > ! > ! static void > ! check_rhs_var (funct_state local, tree t) > ! { > ! check_tree(local, t, false); > ! } > ! > ! /* Check to see if T is an assignment to a var we are interested in > ! analyzing. LOCAL is passed in to get access to its bit vectors. */ > ! > ! static void > ! check_lhs_var (funct_state local, tree t) > ! { > ! check_tree(local, t, true); > ! } > ! > ! /* This is a scaled down version of get_asm_expr_operands from > ! tree_ssa_operands.c. The version there runs much later and assumes > ! that aliasing information is already available. Here we are just > ! trying to find if the set of inputs and outputs contain references > ! or address of operations to local static variables. STMT is the > ! actual asm statement. */ > ! > ! static void > ! get_asm_expr_operands (funct_state local, gimple stmt) > ! { > ! size_t noutputs = gimple_asm_noutputs (stmt); > ! const char **oconstraints > ! = (const char **) alloca ((noutputs) * sizeof (const char *)); > ! size_t i; > ! tree op; > ! const char *constraint; > ! bool allows_mem, allows_reg, is_inout; > ! > ! for (i = 0; i < noutputs; i++) > ! { > ! op = gimple_asm_output_op (stmt, i); > ! oconstraints[i] = constraint > ! = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op))); > ! parse_output_constraint (&constraint, i, 0, 0, > ! &allows_mem, &allows_reg, &is_inout); > ! > ! check_lhs_var (local, TREE_VALUE (op)); > ! } > ! > ! for (i = 0; i < gimple_asm_ninputs (stmt); i++) > ! { > ! op = gimple_asm_input_op (stmt, i); > ! constraint > ! = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op))); > ! parse_input_constraint (&constraint, 0, 0, noutputs, 0, > ! oconstraints, &allows_mem, &allows_reg); > ! > ! check_rhs_var (local, TREE_VALUE (op)); > ! } > ! > ! for (i = 0; i < gimple_asm_nclobbers (stmt); i++) > ! { > ! op = gimple_asm_clobber_op (stmt, i); > ! if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) > ! /* Abandon all hope, ye who enter here. */ > ! local->pure_const_state = IPA_NEITHER; > } > - > - if (gimple_asm_volatile_p (stmt)) > - local->pure_const_state = IPA_NEITHER; > } > > /* Check the parameters of a function call to CALL_EXPR to see if > --- 181,247 ---- > { > /* Readonly reads are safe. */ > if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t))) > ! return; /* Read of a constant, do not change the function state. */ > else > { > + if (dump_file) > + fprintf (dump_file, " global memory read is not const\n"); > /* Just a regular read. */ > if (local->pure_const_state == IPA_CONST) > local->pure_const_state = IPA_PURE; > } > } > ! else > ! { > ! /* Compilation level statics can be read if they are readonly > ! variables. */ > ! if (TREE_READONLY (t)) > ! return; > ! > ! if (dump_file) > ! fprintf (dump_file, " static memory read is not const\n"); > ! /* Just a regular read. */ > ! if (local->pure_const_state == IPA_CONST) > ! local->pure_const_state = IPA_PURE; > ! } > } > > > ! /* Check to see if the use (or definition when CHECKING_WRITE is true) > ! variable T is legal in a function that is either pure or const. */ > > ! static inline void > ! check_op (funct_state local, > ! tree t, bool checking_write) > { > ! while (t && handled_component_p (t)) > ! t = TREE_OPERAND (t, 0); > ! if (!t) > return; > ! if (TREE_CODE (t) == INDIRECT_REF || TREE_CODE (t) == TARGET_MEM_REF) > { > ! if (TREE_THIS_VOLATILE (t)) > ! { > local->pure_const_state = IPA_NEITHER; > ! if (dump_file) > ! fprintf (dump_file, " Volatile indirect ref is not const/pure\n"); > return; > } > ! else if (checking_write) > ! { > ! local->pure_const_state = IPA_NEITHER; > ! if (dump_file) > ! fprintf (dump_file, " Indirect ref write is not const/pure\n"); > ! return; > ! } > ! else > ! { > ! if (dump_file) > ! fprintf (dump_file, " Indirect ref read is not const\n"); > ! if (local->pure_const_state == IPA_CONST) > ! local->pure_const_state = IPA_PURE; > ! } > } > } > > /* Check the parameters of a function call to CALL_EXPR to see if > *************** get_asm_expr_operands (funct_state local > *** 338,357 **** > the entire call expression. */ > > static void > ! check_call (funct_state local, gimple call) > { > int flags = gimple_call_flags (call); > ! tree lhs, callee_t = gimple_call_fndecl (call); > struct cgraph_node* callee; > enum availability avail = AVAIL_NOT_AVAILABLE; > ! size_t i; > ! > ! lhs = gimple_call_lhs (call); > ! if (lhs) > ! check_lhs_var (local, lhs); > > ! for (i = 0; i < gimple_call_num_args (call); i++) > ! check_rhs_var (local, gimple_call_arg (call, i)); > > /* The const and pure flags are set by a variety of places in the > compiler (including here). If someone has already set the flags > --- 252,286 ---- > the entire call expression. */ > > static void > ! check_call (funct_state local, gimple call, bool ipa) > { > int flags = gimple_call_flags (call); > ! tree callee_t = gimple_call_fndecl (call); > struct cgraph_node* callee; > enum availability avail = AVAIL_NOT_AVAILABLE; > ! bool possibly_throws_externally = stmt_can_throw_external (call); > ! bool possibly_throws = stmt_could_throw_p (call); > > ! if (possibly_throws) > ! { > ! unsigned int i; > ! for (i = 0; i < gimple_num_ops (call); i++) > ! if (stmt_could_throw_p (call)) > ! { > ! if (possibly_throws && flag_non_call_exceptions) > ! { > ! if (dump_file) > ! fprintf (dump_file, " operand can throw; looping"); > ! local->looping = true; > ! } > ! if (possibly_throws_externally) > ! { > ! if (dump_file) > ! fprintf (dump_file, " operand can throw externally"); > ! local->can_throw = true; > ! } > ! } > ! } > > /* The const and pure flags are set by a variety of places in the > compiler (including here). If someone has already set the flags > *************** check_call (funct_state local, gimple ca > *** 372,379 **** > or pure. */ > if (setjmp_call_p (callee_t)) > { > local->pure_const_state = IPA_NEITHER; > - local->looping = false; > } > > if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL) > --- 301,310 ---- > or pure. */ > if (setjmp_call_p (callee_t)) > { > + if (dump_file) > + fprintf (dump_file, " setjmp is not const/pure\n"); > + local->looping = true; > local->pure_const_state = IPA_NEITHER; > } > > if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL) > *************** check_call (funct_state local, gimple ca > *** 381,647 **** > { > case BUILT_IN_LONGJMP: > case BUILT_IN_NONLOCAL_GOTO: > local->pure_const_state = IPA_NEITHER; > ! local->looping = false; > break; > default: > break; > } > } > > /* The callee is either unknown (indirect call) or there is just no > scannable code for it (external call) . We look to see if there > are any bits available for the callee (such as by declaration or > because it is builtin) and process solely on the basis of those > bits. */ > ! if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE) > { > ! if (flags & ECF_PURE) > ! { > if (local->pure_const_state == IPA_CONST) > local->pure_const_state = IPA_PURE; > } > else > - local->pure_const_state = IPA_NEITHER; > - } > - else > - { > - /* We have the code and we will scan it for the effects. */ > - if (flags & ECF_PURE) > { > ! if (local->pure_const_state == IPA_CONST) > ! local->pure_const_state = IPA_PURE; > } > } > } > > ! /* TP is the part of the tree currently under the microscope. > ! WALK_SUBTREES is part of the walk_tree api but is unused here. > ! DATA is cgraph_node of the function being walked. */ > ! > ! /* FIXME: When this is converted to run over SSA form, this code > ! should be converted to use the operand scanner. */ > ! > ! static tree > ! scan_function_op (tree *tp, int *walk_subtrees, void *data) > ! { > ! struct walk_stmt_info *wi = (struct walk_stmt_info *) data; > ! struct cgraph_node *fn = (struct cgraph_node *) wi->info; > ! tree t = *tp; > ! funct_state local = get_function_state (fn); > ! > ! switch (TREE_CODE (t)) > ! { > ! case VAR_DECL: > ! if (DECL_INITIAL (t)) > ! walk_tree (&DECL_INITIAL (t), scan_function_op, data, visited_nodes); > ! *walk_subtrees = 0; > ! break; > ! > ! case ADDR_EXPR: > ! /* This case is here to find addresses on rhs of constructors in > ! decl_initial of static variables. */ > ! check_rhs_var (local, t); > ! *walk_subtrees = 0; > ! break; > ! > ! default: > ! break; > ! } > ! return NULL; > ! } > ! > ! static tree > ! scan_function_stmt (gimple_stmt_iterator *gsi_p, > ! bool *handled_ops_p, > ! struct walk_stmt_info *wi) > { > ! struct cgraph_node *fn = (struct cgraph_node *) wi->info; > ! gimple stmt = gsi_stmt (*gsi_p); > ! funct_state local = get_function_state (fn); > > switch (gimple_code (stmt)) > { > case GIMPLE_ASSIGN: > ! { > ! /* First look on the lhs and see what variable is stored to */ > ! tree lhs = gimple_assign_lhs (stmt); > ! tree rhs1 = gimple_assign_rhs1 (stmt); > ! tree rhs2 = gimple_assign_rhs2 (stmt); > ! enum tree_code code = gimple_assign_rhs_code (stmt); > ! > ! check_lhs_var (local, lhs); > ! > ! /* For the purposes of figuring out what the cast affects */ > ! > ! /* Next check the operands on the rhs to see if they are ok. */ > ! switch (TREE_CODE_CLASS (code)) > ! { > ! case tcc_binary: > ! { > ! check_rhs_var (local, rhs1); > ! check_rhs_var (local, rhs2); > ! } > ! break; > ! case tcc_unary: > ! { > ! check_rhs_var (local, rhs1); > ! } > ! > ! break; > ! case tcc_reference: > ! check_rhs_var (local, rhs1); > ! break; > ! case tcc_declaration: > ! check_rhs_var (local, rhs1); > ! break; > ! case tcc_expression: > ! switch (code) > ! { > ! case ADDR_EXPR: > ! check_rhs_var (local, rhs1); > ! break; > ! default: > ! break; > ! } > ! break; > ! default: > ! break; > ! } > ! *handled_ops_p = true; > ! } > break; > - > case GIMPLE_LABEL: > if (DECL_NONLOCAL (gimple_label_label (stmt))) > /* Target of long jump. */ > { > local->pure_const_state = IPA_NEITHER; > - local->looping = false; > } > break; > - > - case GIMPLE_CALL: > - check_call (local, stmt); > - *handled_ops_p = true; > - break; > - > case GIMPLE_ASM: > ! get_asm_expr_operands (local, stmt); > ! *handled_ops_p = true; > ! break; > ! > default: > break; > } > ! return NULL; > } > > > /* This is the main routine for finding the reference patterns for > global variables within a function FN. */ > > ! static void > ! analyze_function (struct cgraph_node *fn) > { > tree decl = fn->decl; > ! funct_state l = XCNEW (struct funct_state_d); > ! > ! if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE) > ! return; > > ! set_function_state (fn, l); > > 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 > ! front end. */ > ! if (TREE_THIS_VOLATILE (decl) > ! || !targetm.binds_local_p (decl)) > { > ! l->pure_const_state = IPA_NEITHER; > ! return; > } > ! > ! if (TREE_READONLY (decl)) > { > ! l->pure_const_state = IPA_CONST; > ! l->state_set_in_source = true; > } > ! if (DECL_PURE_P (decl)) > { > ! l->pure_const_state = IPA_PURE; > ! l->state_set_in_source = true; > } > > ! if (dump_file) > { > ! fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ", > ! cgraph_node_name (fn), > ! l->pure_const_state); > } > ! > ! if (!l->state_set_in_source) > { > ! struct function *this_cfun = DECL_STRUCT_FUNCTION (decl); > ! basic_block this_block; > ! > ! FOR_EACH_BB_FN (this_block, this_cfun) > ! { > ! gimple_stmt_iterator gsi; > ! struct walk_stmt_info wi; > ! > ! memset (&wi, 0, sizeof(wi)); > ! for (gsi = gsi_start_bb (this_block); > ! !gsi_end_p (gsi); > ! gsi_next (&gsi)) > ! { > ! wi.info = fn; > ! wi.pset = visited_nodes; > ! walk_gimple_stmt (&gsi, scan_function_stmt, scan_function_op, > ! &wi); > ! if (l->pure_const_state == IPA_NEITHER) > ! goto end; > ! } > ! } > ! > ! if (l->pure_const_state != IPA_NEITHER) > ! { > ! tree old_decl = current_function_decl; > ! /* Const functions cannot have back edges (an > ! indication of possible infinite loop side > ! effect. */ > ! > ! current_function_decl = fn->decl; > ! > ! /* The C++ front end, has a tendency to some times jerk away > ! a function after it has created it. This should have > ! been fixed. */ > ! gcc_assert (DECL_STRUCT_FUNCTION (fn->decl)); > ! > ! push_cfun (DECL_STRUCT_FUNCTION (fn->decl)); > ! > ! if (mark_dfs_back_edges ()) > ! l->pure_const_state = IPA_NEITHER; > ! > ! current_function_decl = old_decl; > ! pop_cfun (); > ! } > } > > ! end: > if (dump_file) > { > ! fprintf (dump_file, "after local analysis of %s with initial value = %d\n ", > ! cgraph_node_name (fn), > ! l->pure_const_state); > } > } > > /* Called when new function is inserted to callgraph late. */ > --- 312,551 ---- > { > case BUILT_IN_LONGJMP: > case BUILT_IN_NONLOCAL_GOTO: > + if (dump_file) > + fprintf (dump_file, " longjmp and nonlocal goto is not const/pure\n"); > local->pure_const_state = IPA_NEITHER; > ! local->looping = true; > break; > default: > break; > } > } > > + /* When not in IPA mode, we can still handle self recursion. */ > + if (!ipa && callee_t == current_function_decl) > + local->looping = true; > /* The callee is either unknown (indirect call) or there is just no > scannable code for it (external call) . We look to see if there > are any bits available for the callee (such as by declaration or > because it is builtin) and process solely on the basis of those > bits. */ > ! else if (avail <= AVAIL_OVERWRITABLE || !ipa) > { > ! if (possibly_throws && flag_non_call_exceptions) > ! { > ! if (dump_file) > ! fprintf (dump_file, " can throw; looping"); > ! local->looping = true; > ! } > ! if (possibly_throws_externally) > ! { > ! if (dump_file) > ! fprintf (dump_file, " can throw externally"); > ! local->can_throw = true; > ! } > ! if (flags & ECF_CONST) > ! { > ! if (callee_t && DECL_LOOPING_CONST_OR_PURE_P (callee_t)) > ! local->looping = true; > ! } > ! else if (flags & ECF_PURE) > ! { > ! if (callee_t && DECL_LOOPING_CONST_OR_PURE_P (callee_t)) > ! local->looping = true; > ! if (dump_file) > ! fprintf (dump_file, " pure function call in not const\n"); > if (local->pure_const_state == IPA_CONST) > local->pure_const_state = IPA_PURE; > } > else > { > ! if (dump_file) > ! fprintf (dump_file, " uknown function call is not const/pure\n"); > ! local->pure_const_state = IPA_NEITHER; > ! local->looping = true; > } > } > + /* Direct functions calls are handled by IPA propagation. */ > } > > ! static void > ! check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa) > { > ! gimple stmt = gsi_stmt (*gsip); > ! unsigned int i = 0; > ! bitmap_iterator bi; > > + if (dump_file) > + { > + fprintf (dump_file, " scanning: "); > + print_gimple_stmt (dump_file, stmt, 0, 0); > + } > + if (gimple_loaded_syms (stmt)) > + EXECUTE_IF_SET_IN_BITMAP (gimple_loaded_syms (stmt), 0, i, bi) > + check_decl (local, referenced_var_lookup (i), false); > + if (gimple_stored_syms (stmt)) > + EXECUTE_IF_SET_IN_BITMAP (gimple_stored_syms (stmt), 0, i, bi) > + check_decl (local, referenced_var_lookup (i), true); > + > + if (gimple_code (stmt) != GIMPLE_CALL > + && stmt_could_throw_p (stmt)) > + { > + if (flag_non_call_exceptions) > + { > + if (dump_file) > + fprintf (dump_file, " can throw; looping"); > + local->looping = true; > + } > + if (stmt_can_throw_external (stmt)) > + { > + if (dump_file) > + fprintf (dump_file, " can throw externally"); > + local->can_throw = true; > + } > + } > switch (gimple_code (stmt)) > { > case GIMPLE_ASSIGN: > ! check_op (local, gimple_assign_lhs (stmt), true); > ! i = 1; > ! break; > ! case GIMPLE_CALL: > ! check_op (local, gimple_call_lhs (stmt), true); > ! i = 1; > ! check_call (local, stmt, ipa); > break; > case GIMPLE_LABEL: > if (DECL_NONLOCAL (gimple_label_label (stmt))) > /* Target of long jump. */ > { > + if (dump_file) > + fprintf (dump_file, " nonlocal label is not const/pure"); > local->pure_const_state = IPA_NEITHER; > } > break; > case GIMPLE_ASM: > ! for (i = 0; i < gimple_asm_noutputs (stmt); i++) > ! check_op (local, gimple_asm_output_op (stmt, i), true); > ! for (i = 0; i < gimple_asm_ninputs (stmt); i++) > ! check_op (local, gimple_asm_input_op (stmt, i), false); > ! for (i = 0; i < gimple_asm_nclobbers (stmt); i++) > ! { > ! tree op = gimple_asm_clobber_op (stmt, i); > ! if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) > ! { > ! if (dump_file) > ! fprintf (dump_file, " memory asm clobber is not const/pure"); > ! /* Abandon all hope, ye who enter here. */ > ! local->pure_const_state = IPA_NEITHER; > ! } > ! } > ! if (gimple_asm_volatile_p (stmt)) > ! { > ! if (dump_file) > ! fprintf (dump_file, " volatile is not const/pure"); > ! /* Abandon all hope, ye who enter here. */ > ! local->pure_const_state = IPA_NEITHER; > ! local->looping = true; > ! } > ! return; > default: > break; > } > ! > ! for (; i < gimple_num_ops (stmt); i++) > ! check_op (local, gimple_op (stmt, i), false); > } > > > /* This is the main routine for finding the reference patterns for > global variables within a function FN. */ > > ! static funct_state > ! analyze_function (struct cgraph_node *fn, bool ipa) > { > tree decl = fn->decl; > ! tree old_decl = current_function_decl; > ! funct_state l; > ! basic_block this_block; > > ! if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE) > ! return NULL; > > + l = XCNEW (struct funct_state_d); > l->pure_const_state = IPA_CONST; > ! l->state_set_in_source = IPA_NEITHER; > ! l->looping = false; > ! l->can_throw = false; > > ! if (dump_file) > { > ! fprintf (dump_file, "\n\n local analysis of %s\n ", > ! cgraph_node_name (fn)); > } > ! > ! push_cfun (DECL_STRUCT_FUNCTION (decl)); > ! current_function_decl = decl; > ! > ! FOR_EACH_BB (this_block) > { > ! gimple_stmt_iterator gsi; > ! struct walk_stmt_info wi; > ! > ! memset (&wi, 0, sizeof(wi)); > ! for (gsi = gsi_start_bb (this_block); > ! !gsi_end_p (gsi); > ! gsi_next (&gsi)) > ! { > ! check_stmt (&gsi, l, ipa); > ! if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw) > ! goto end; > ! } > } > ! > ! end: > ! if (l->pure_const_state != IPA_NEITHER) > { > ! /* Const functions cannot have back edges (an > ! indication of possible infinite loop side > ! effect. */ > ! if (mark_dfs_back_edges ()) > ! l->looping = true; > ! > } > > ! if (TREE_READONLY (decl)) > { > ! l->pure_const_state = IPA_CONST; > ! l->state_set_in_source = IPA_CONST; > ! if (!DECL_LOOPING_CONST_OR_PURE_P (decl)) > ! l->looping = false; > } > ! if (DECL_PURE_P (decl)) > { > ! if (l->pure_const_state != IPA_CONST) > ! l->pure_const_state = IPA_PURE; > ! l->state_set_in_source = IPA_PURE; > ! if (!DECL_LOOPING_CONST_OR_PURE_P (decl)) > ! l->looping = false; > } > + if (TREE_NOTHROW (decl)) > + l->can_throw = false; > > ! pop_cfun (); > ! current_function_decl = old_decl; > if (dump_file) > { > ! if (l->looping) > ! fprintf (dump_file, "Function is locally looping.\n"); > ! if (l->can_throw) > ! fprintf (dump_file, "Function is locally throwing.\n"); > ! if (l->pure_const_state == IPA_CONST) > ! fprintf (dump_file, "Function is locally const.\n"); > ! if (l->pure_const_state == IPA_PURE) > ! fprintf (dump_file, "Function is locally pure.\n"); > } > + return l; > } > > /* Called when new function is inserted to callgraph late. */ > *************** add_new_function (struct cgraph_node *no > *** 655,661 **** > since all we would be interested in are the addressof > operations. */ > visited_nodes = pointer_set_create (); > ! analyze_function (node); > pointer_set_destroy (visited_nodes); > visited_nodes = NULL; > } > --- 559,565 ---- > since all we would be interested in are the addressof > operations. */ > visited_nodes = pointer_set_create (); > ! set_function_state (node, analyze_function (node, true)); > pointer_set_destroy (visited_nodes); > visited_nodes = NULL; > } > *************** generate_summary (void) > *** 716,722 **** > */ > for (node = cgraph_nodes; node; node = node->next) > if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE) > ! analyze_function (node); > > pointer_set_destroy (visited_nodes); > visited_nodes = NULL; > --- 620,626 ---- > */ > for (node = cgraph_nodes; node; node = node->next) > if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE) > ! set_function_state (node, analyze_function (node, true)); > > pointer_set_destroy (visited_nodes); > visited_nodes = NULL; > *************** propagate (void) > *** 756,761 **** > --- 660,666 ---- > { > enum pure_const_state_e pure_const_state = IPA_CONST; > bool looping = false; > + bool can_throw = false; > int count = 0; > node = order[i]; > > *************** propagate (void) > *** 763,800 **** > w = node; > while (w) > { > funct_state w_l = get_function_state (w); > 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; > > ! if (!w_l->state_set_in_source) > { > ! struct cgraph_edge *e; > ! count++; > > ! if (count > 1) > ! looping = true; > ! > ! for (e = w->callees; e; e = e->next_callee) > { > ! struct cgraph_node *y = e->callee; > ! > ! if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE) > ! { > ! funct_state y_l = get_function_state (y); > ! if (pure_const_state < y_l->pure_const_state) > ! pure_const_state = y_l->pure_const_state; > ! if (pure_const_state == IPA_NEITHER) > ! break; > ! if (y_l->looping) > ! looping = true; > ! } > } > } > w_info = (struct ipa_dfs_info *) w->aux; > --- 668,709 ---- > w = node; > while (w) > { > + struct cgraph_edge *e; > funct_state w_l = get_function_state (w); > if (pure_const_state < w_l->pure_const_state) > pure_const_state = w_l->pure_const_state; > > + if (w_l->can_throw) > + can_throw = true; > if (w_l->looping) > looping = true; > > if (pure_const_state == IPA_NEITHER) > break; > > ! count++; > ! > ! if (count > 1) > ! looping = true; > ! > ! for (e = w->callees; e; e = e->next_callee) > { > ! struct cgraph_node *y = e->callee; > > ! if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE) > { > ! funct_state y_l = get_function_state (y); > ! if (pure_const_state < y_l->pure_const_state) > ! pure_const_state = y_l->pure_const_state; > ! if (pure_const_state == IPA_NEITHER) > ! break; > ! if (y_l->looping) > ! looping = true; > ! if (y_l->can_throw && !TREE_NOTHROW (w->decl) > ! /* FIXME: We should check that the throw can get external. > ! We also should handle only loops formed by can throw external > ! edges. */) > ! can_throw = true; > } > } > w_info = (struct ipa_dfs_info *) w->aux; > *************** propagate (void) > *** 807,842 **** > while (w) > { > funct_state w_l = get_function_state (w); > > ! /* All nodes within a cycle share the same info. */ > ! if (!w_l->state_set_in_source) > { > ! w_l->pure_const_state = pure_const_state; > ! w_l->looping = looping; > > ! switch (pure_const_state) > ! { > ! 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 %sconst: %s\n", > ! looping ? "looping " : "", > ! lang_hooks.decl_printable_name(w->decl, 2)); > ! break; > ! > ! case IPA_PURE: > ! 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 %spure: %s\n", > ! looping ? "looping " : "", > ! lang_hooks.decl_printable_name(w->decl, 2)); > ! break; > ! > ! default: > ! break; > ! } > } > w_info = (struct ipa_dfs_info *) w->aux; > w = w_info->next_cycle; > --- 716,763 ---- > while (w) > { > funct_state w_l = get_function_state (w); > + enum pure_const_state_e this_state = pure_const_state; > + bool this_looping = looping; > > ! if (w_l->state_set_in_source != IPA_NEITHER) > { > ! if (this_state > w_l->state_set_in_source) > ! this_state = w_l->state_set_in_source; > ! this_looping = false; > ! } > > ! /* All nodes within a cycle share the same info. */ > ! w_l->pure_const_state = this_state; > ! w_l->looping = this_looping; > ! > ! switch (this_state) > ! { > ! case IPA_CONST: > ! if (!TREE_READONLY (w->decl) && dump_file) > ! fprintf (dump_file, "Function found to be %sconst: %s\n", > ! this_looping ? "looping " : "", > ! cgraph_node_name (w)); > ! TREE_READONLY (w->decl) = 1; > ! DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping; > ! break; > ! > ! case IPA_PURE: > ! if (!DECL_PURE_P (w->decl) && dump_file) > ! fprintf (dump_file, "Function found to be %spure: %s\n", > ! this_looping ? "looping " : "", > ! cgraph_node_name (w)); > ! DECL_PURE_P (w->decl) = 1; > ! DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping; > ! break; > ! > ! default: > ! break; > ! } > ! if (!can_throw && !TREE_NOTHROW (w->decl)) > ! { > ! if (dump_file) > ! fprintf (dump_file, "Function found to be nothrow: %s\n", > ! cgraph_node_name (w)); > } > w_info = (struct ipa_dfs_info *) w->aux; > w = w_info->next_cycle; > *************** struct ipa_opt_pass pass_ipa_pure_const > *** 896,898 **** > --- 817,917 ---- > NULL, /* function_transform */ > NULL /* variable_transform */ > }; > + > + static unsigned int > + local_pure_const (void) > + { > + bool changed = false; > + funct_state l = > + analyze_function (cgraph_node (current_function_decl), false); > + if (!l) > + return 0; > + > + switch (l->pure_const_state) > + { > + case IPA_CONST: > + if (!TREE_READONLY (current_function_decl)) > + { > + TREE_READONLY (current_function_decl) = 1; > + DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = l->looping; > + changed = true; > + if (dump_file) > + fprintf (dump_file, "Function found to be %sconst: %s\n", > + l->looping ? "looping " : "", > + lang_hooks.decl_printable_name (current_function_decl, > + 2)); > + } > + else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) > + && !l->looping) > + { > + DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = false; > + changed = true; > + if (dump_file) > + fprintf (dump_file, "Function found to be non-looping: %s\n", > + lang_hooks.decl_printable_name (current_function_decl, > + 2)); > + } > + break; > + > + case IPA_PURE: > + if (!TREE_READONLY (current_function_decl)) > + { > + DECL_PURE_P (current_function_decl) = 1; > + DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = l->looping; > + changed = true; > + if (dump_file) > + fprintf (dump_file, "Function found to be %spure: %s\n", > + l->looping ? "looping " : "", > + lang_hooks.decl_printable_name (current_function_decl, > + 2)); > + } > + else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) > + && !l->looping) > + { > + DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = false; > + changed = true; > + if (dump_file) > + fprintf (dump_file, "Function found to be non-looping: %s\n", > + lang_hooks.decl_printable_name (current_function_decl, > + 2)); > + } > + break; > + > + default: > + break; > + } > + if (!l->can_throw && !TREE_NOTHROW (current_function_decl)) > + { > + TREE_NOTHROW (current_function_decl) = 1; > + changed = true; > + if (dump_file) > + fprintf (dump_file, "Function found to be nothrow: %s\n", > + lang_hooks.decl_printable_name (current_function_decl, > + 2)); > + } > + if (l) > + free (l); > + if (changed) > + return execute_fixup_cfg (); > + else > + return 0; > + } > + > + struct gimple_opt_pass pass_local_pure_const = > + { > + { > + GIMPLE_PASS, > + "local-pure-const", /* name */ > + gate_pure_const, /* gate */ > + local_pure_const, /* execute */ > + NULL, /* sub */ > + NULL, /* next */ > + 0, /* static_pass_number */ > + TV_IPA_PURE_CONST, /* tv_id */ > + 0, /* properties_required */ > + 0, /* properties_provided */ > + 0, /* properties_destroyed */ > + 0, /* todo_flags_start */ > + 0 /* todo_flags_finish */ > + } > + }; > Index: ipa-inline.c > =================================================================== > *** ipa-inline.c (revision 141798) > --- ipa-inline.c (working copy) > *************** inline_transform (struct cgraph_node *no > *** 1732,1737 **** > --- 1732,1738 ---- > todo = optimize_inline_calls (current_function_decl); > timevar_pop (TV_INTEGRATION); > } > + cfun->after_inlining = true; > return todo | execute_fixup_cfg (); > } > > Index: tree-eh.c > =================================================================== > *** tree-eh.c (revision 141798) > --- tree-eh.c (working copy) > *************** tree_could_throw_p (tree t) > *** 2384,2389 **** > --- 2384,2411 ---- > return false; > } > > + /* Return true if STMT can throw an exception that is not caught within > + the current function (CFUN). */ > + > + bool > + stmt_can_throw_external (gimple stmt) > + { > + int region_nr; > + bool is_resx = false; > + > + if (gimple_code (stmt) == GIMPLE_RESX) > + { > + region_nr = gimple_resx_region (stmt); > + is_resx = true; > + } > + else > + region_nr = lookup_stmt_eh_region (stmt); > + > + if (region_nr < 0) > + return true; > + > + return can_throw_external_1 (region_nr, is_resx); > + } > > /* Return true if STMT can throw an exception that is caught within > the current function (CFUN). */ > Index: tree-optimize.c > =================================================================== > *** tree-optimize.c (revision 141798) > --- tree-optimize.c (working copy) > *************** execute_fixup_cfg (void) > *** 292,299 **** > gimple_stmt_iterator gsi; > int todo = gimple_in_ssa_p (cfun) ? TODO_verify_ssa : 0; > > - cfun->after_inlining = true; > - > if (cfun->eh) > FOR_EACH_BB (bb) > { > --- 292,297 ---- > *************** execute_fixup_cfg (void) > *** 312,322 **** > if (gimple_in_ssa_p (cfun)) > { > todo |= TODO_update_ssa | TODO_cleanup_cfg; > update_stmt (stmt); > } > } > > ! if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt)) > remove_stmt_from_eh_region (stmt); > } > > --- 310,321 ---- > if (gimple_in_ssa_p (cfun)) > { > todo |= TODO_update_ssa | TODO_cleanup_cfg; > + mark_symbols_for_renaming (stmt); > update_stmt (stmt); > } > } > > ! if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt) != -2) > remove_stmt_from_eh_region (stmt); > } > > *************** execute_fixup_cfg (void) > *** 331,336 **** > --- 330,355 ---- > return todo; > } > > + struct gimple_opt_pass pass_fixup_cfg = > + { > + { > + GIMPLE_PASS, > + NULL, /* name */ > + NULL, /* gate */ > + execute_fixup_cfg, /* execute */ > + NULL, /* sub */ > + NULL, /* next */ > + 0, /* static_pass_number */ > + 0, /* tv_id */ > + PROP_cfg, /* properties_required */ > + 0, /* properties_provided */ > + 0, /* properties_destroyed */ > + 0, /* todo_flags_start */ > + 0 /* todo_flags_finish */ > + } > + }; > + > + > /* Do the actions required to initialize internal data structures used > in tree-ssa optimization passes. */ > > Index: tree-flow.h > =================================================================== > *** tree-flow.h (revision 141798) > --- tree-flow.h (working copy) > *************** extern bool operation_could_trap_p (enum > *** 1071,1076 **** > --- 1071,1077 ---- > extern bool stmt_could_throw_p (gimple); > extern bool tree_could_throw_p (tree); > extern bool stmt_can_throw_internal (gimple); > + extern bool stmt_can_throw_external (gimple); > extern void add_stmt_to_eh_region (gimple, int); > extern bool remove_stmt_from_eh_region (gimple); > extern bool maybe_clean_or_replace_eh_stmt (gimple, gimple); > Index: passes.c > =================================================================== > *** passes.c (revision 141798) > --- passes.c (working copy) > *************** init_optimization_passes (void) > *** 536,541 **** > --- 536,542 ---- > NEXT_PASS (pass_early_local_passes); > { > struct opt_pass **p = &pass_early_local_passes.pass.sub; > + NEXT_PASS (pass_fixup_cfg); > NEXT_PASS (pass_tree_profile); > NEXT_PASS (pass_cleanup_cfg); > NEXT_PASS (pass_init_datastructures); > *************** init_optimization_passes (void) > *** 562,567 **** > --- 563,569 ---- > NEXT_PASS (pass_tail_recursion); > NEXT_PASS (pass_convert_switch); > NEXT_PASS (pass_profile); > + NEXT_PASS (pass_local_pure_const); > } > NEXT_PASS (pass_release_ssa_names); > NEXT_PASS (pass_rebuild_cgraph_edges); > *************** init_optimization_passes (void) > *** 701,706 **** > --- 703,709 ---- > NEXT_PASS (pass_tail_calls); > NEXT_PASS (pass_rename_ssa_copies); > NEXT_PASS (pass_uncprop); > + NEXT_PASS (pass_local_pure_const); > } > NEXT_PASS (pass_del_ssa); > NEXT_PASS (pass_nrv); >