static void tree_if_convert_cond_expr (struct loop *loop, tree stmt, tree cond, block_stmt_iterator *bsi) { tree c, c2; edge true_edge, false_edge; gcc_assert (TREE_CODE (stmt) == COND_EXPR); c = COND_EXPR_COND (stmt); extract_true_false_edges_from_block (bb_for_stmt (stmt), &true_edge, &false_edge); /* Add new condition into destination's predicate list. */ /* If 'c' is true then TRUE_EDGE is taken. */ add_to_dst_predicate_list (loop, true_edge, cond, unshare_expr (c), bsi); /* If 'c' is false then FALSE_EDGE is taken. */ c2 = invert_truthvalue (unshare_expr (c)); add_to_dst_predicate_list (loop, false_edge, cond, c2, bsi); /* Now this conditional statement is redundant. Remove it. But, do not remove exit condition! Update exit condition using new condition. */ if (!bb_with_exit_edge_p (loop, bb_for_stmt (stmt))) { bsi_remove (bsi, true); cond = NULL_TREE; } return; }
void remove_phi_node (tree phi, tree prev) { tree *loc; if (prev) { loc = &PHI_CHAIN (prev); } else { for (loc = &(bb_for_stmt (phi)->phi_nodes); *loc != phi; loc = &PHI_CHAIN (*loc)) ; } /* Remove PHI from the chain. */ *loc = PHI_CHAIN (phi); /* If we are deleting the PHI node, then we should release the SSA_NAME node so that it can be reused. */ release_phi_node (phi); release_ssa_name (PHI_RESULT (phi)); }
void add_phi_arg (tree phi, tree def, edge e) { basic_block bb = e->dest; gcc_assert (bb == bb_for_stmt (phi)); /* We resize PHI nodes upon edge creation. We should always have enough room at this point. */ gcc_assert (PHI_NUM_ARGS (phi) <= PHI_ARG_CAPACITY (phi)); /* We resize PHI nodes upon edge creation. We should always have enough room at this point. */ gcc_assert (e->dest_idx < (unsigned int) PHI_NUM_ARGS (phi)); /* Copy propagation needs to know what object occur in abnormal PHI nodes. This is a convenient place to record such information. */ if (e->flags & EDGE_ABNORMAL) { SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def) = 1; SSA_NAME_OCCURS_IN_ABNORMAL_PHI (PHI_RESULT (phi)) = 1; } SET_PHI_ARG_DEF (phi, e->dest_idx, def); }
static bool determine_max_movement (tree stmt, bool must_preserve_exec) { basic_block bb = bb_for_stmt (stmt); struct loop *loop = bb->loop_father; struct loop *level; struct lim_aux_data *lim_data = LIM_DATA (stmt); tree val; ssa_op_iter iter; if (must_preserve_exec) level = ALWAYS_EXECUTED_IN (bb); else level = superloop_at_depth (loop, 1); lim_data->max_loop = level; FOR_EACH_SSA_TREE_OPERAND (val, stmt, iter, SSA_OP_USE) if (!add_dependency (val, lim_data, loop, true)) return false; FOR_EACH_SSA_TREE_OPERAND (val, stmt, iter, SSA_OP_VIRTUAL_USES) if (!add_dependency (val, lim_data, loop, false)) return false; lim_data->cost += stmt_cost (stmt); return true; }
static struct loop * outermost_invariant_loop (tree def, struct loop *loop) { tree def_stmt; basic_block def_bb; struct loop *max_loop; if (TREE_CODE (def) != SSA_NAME) return superloop_at_depth (loop, 1); def_stmt = SSA_NAME_DEF_STMT (def); def_bb = bb_for_stmt (def_stmt); if (!def_bb) return superloop_at_depth (loop, 1); max_loop = find_common_loop (loop, def_bb->loop_father); if (LIM_DATA (def_stmt) && LIM_DATA (def_stmt)->max_loop) max_loop = find_common_loop (max_loop, loop_outer (LIM_DATA (def_stmt)->max_loop)); if (max_loop == loop) return NULL; max_loop = superloop_at_depth (loop, loop_depth (max_loop) + 1); return max_loop; }
static void tidy_after_forward_propagate_addr (tree stmt) { mark_new_vars_to_rename (stmt); /* We may have turned a trapping insn into a non-trapping insn. */ if (maybe_clean_or_replace_eh_stmt (stmt, stmt) && tree_purge_dead_eh_edges (bb_for_stmt (stmt))) cfg_changed = true; if (TREE_CODE (TREE_OPERAND (stmt, 1)) == ADDR_EXPR) recompute_tree_invarant_for_addr_expr (TREE_OPERAND (stmt, 1)); update_stmt (stmt); }
static tree lhs_of_dominating_assert (tree op, basic_block bb, tree stmt) { imm_use_iterator imm_iter; tree use_stmt; use_operand_p use_p; FOR_EACH_IMM_USE_FAST (use_p, imm_iter, op) { use_stmt = USE_STMT (use_p); if (use_stmt != stmt && TREE_CODE (use_stmt) == MODIFY_EXPR && TREE_CODE (TREE_OPERAND (use_stmt, 1)) == ASSERT_EXPR && TREE_OPERAND (TREE_OPERAND (use_stmt, 1), 0) == op && dominated_by_p (CDI_DOMINATORS, bb, bb_for_stmt (use_stmt))) { return TREE_OPERAND (use_stmt, 0); } }
static void set_level (tree stmt, struct loop *orig_loop, struct loop *level) { struct loop *stmt_loop = bb_for_stmt (stmt)->loop_father; struct depend *dep; stmt_loop = find_common_loop (orig_loop, stmt_loop); if (LIM_DATA (stmt) && LIM_DATA (stmt)->tgt_loop) stmt_loop = find_common_loop (stmt_loop, loop_outer (LIM_DATA (stmt)->tgt_loop)); if (flow_loop_nested_p (stmt_loop, level)) return; gcc_assert (LIM_DATA (stmt)); gcc_assert (level == LIM_DATA (stmt)->max_loop || flow_loop_nested_p (LIM_DATA (stmt)->max_loop, level)); LIM_DATA (stmt)->tgt_loop = level; for (dep = LIM_DATA (stmt)->depends; dep; dep = dep->next) set_level (dep->stmt, orig_loop, level); }
static tree tree_may_unswitch_on (basic_block bb, struct loop *loop) { tree stmt, def, cond, use; basic_block def_bb; ssa_op_iter iter; /* BB must end in a simple conditional jump. */ stmt = last_stmt (bb); if (!stmt || TREE_CODE (stmt) != COND_EXPR) return NULL_TREE; /* Condition must be invariant. */ FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) { def = SSA_NAME_DEF_STMT (use); def_bb = bb_for_stmt (def); if (def_bb && flow_bb_inside_loop_p (loop, def_bb)) return NULL_TREE; }
static bool add_dependency (tree def, struct lim_aux_data *data, struct loop *loop, bool add_cost) { tree def_stmt = SSA_NAME_DEF_STMT (def); basic_block def_bb = bb_for_stmt (def_stmt); struct loop *max_loop; struct depend *dep; if (!def_bb) return true; max_loop = outermost_invariant_loop (def, loop); if (!max_loop) return false; if (flow_loop_nested_p (data->max_loop, max_loop)) data->max_loop = max_loop; if (!LIM_DATA (def_stmt)) return true; if (add_cost /* Only add the cost if the statement defining DEF is inside LOOP, i.e. if it is likely that by moving the invariants dependent on it, we will be able to avoid creating a new register for it (since it will be only used in these dependent invariants). */ && def_bb->loop_father == loop) data->cost += LIM_DATA (def_stmt)->cost; dep = XNEW (struct depend); dep->stmt = def_stmt; dep->next = data->depends; data->depends = dep; return true; }
static void set_profitable_level (tree stmt) { set_level (stmt, bb_for_stmt (stmt)->loop_father, LIM_DATA (stmt)->max_loop); }
static void mf_build_check_statement_for (tree base, tree limit, block_stmt_iterator *instr_bsi, location_t *locus, tree dirflag) { tree_stmt_iterator head, tsi; block_stmt_iterator bsi; basic_block cond_bb, then_bb, join_bb; edge e; tree cond, t, u, v; tree mf_base; tree mf_elem; tree mf_limit; /* We first need to split the current basic block, and start altering the CFG. This allows us to insert the statements we're about to construct into the right basic blocks. */ cond_bb = bb_for_stmt (bsi_stmt (*instr_bsi)); bsi = *instr_bsi; bsi_prev (&bsi); if (! bsi_end_p (bsi)) e = split_block (cond_bb, bsi_stmt (bsi)); else e = split_block_after_labels (cond_bb); cond_bb = e->src; join_bb = e->dest; /* A recap at this point: join_bb is the basic block at whose head is the gimple statement for which this check expression is being built. cond_bb is the (possibly new, synthetic) basic block the end of which will contain the cache-lookup code, and a conditional that jumps to the cache-miss code or, much more likely, over to join_bb. */ /* Create the bb that contains the cache-miss fallback block (mf_check). */ then_bb = create_empty_bb (cond_bb); make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE); make_single_succ_edge (then_bb, join_bb, EDGE_FALLTHRU); /* Mark the pseudo-fallthrough edge from cond_bb to join_bb. */ e = find_edge (cond_bb, join_bb); e->flags = EDGE_FALSE_VALUE; e->count = cond_bb->count; e->probability = REG_BR_PROB_BASE; /* Update dominance info. Note that bb_join's data was updated by split_block. */ if (dom_info_available_p (CDI_DOMINATORS)) { set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); set_immediate_dominator (CDI_DOMINATORS, join_bb, cond_bb); } /* Build our local variables. */ mf_elem = create_tmp_var (mf_cache_structptr_type, "__mf_elem"); mf_base = create_tmp_var (mf_uintptr_type, "__mf_base"); mf_limit = create_tmp_var (mf_uintptr_type, "__mf_limit"); /* Build: __mf_base = (uintptr_t) <base address expression>. */ t = build2 (MODIFY_EXPR, void_type_node, mf_base, convert (mf_uintptr_type, unshare_expr (base))); SET_EXPR_LOCUS (t, locus); gimplify_to_stmt_list (&t); head = tsi_start (t); tsi = tsi_last (t); /* Build: __mf_limit = (uintptr_t) <limit address expression>. */ t = build2 (MODIFY_EXPR, void_type_node, mf_limit, convert (mf_uintptr_type, unshare_expr (limit))); SET_EXPR_LOCUS (t, locus); gimplify_to_stmt_list (&t); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Build: __mf_elem = &__mf_lookup_cache [(__mf_base >> __mf_shift) & __mf_mask]. */ t = build2 (RSHIFT_EXPR, mf_uintptr_type, mf_base, (flag_mudflap_threads ? mf_cache_shift_decl : mf_cache_shift_decl_l)); t = build2 (BIT_AND_EXPR, mf_uintptr_type, t, (flag_mudflap_threads ? mf_cache_mask_decl : mf_cache_mask_decl_l)); t = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (mf_cache_array_decl)), mf_cache_array_decl, t, NULL_TREE, NULL_TREE); t = build1 (ADDR_EXPR, mf_cache_structptr_type, t); t = build2 (MODIFY_EXPR, void_type_node, mf_elem, t); SET_EXPR_LOCUS (t, locus); gimplify_to_stmt_list (&t); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Quick validity check. if (__mf_elem->low > __mf_base || (__mf_elem_high < __mf_limit)) { __mf_check (); ... and only if single-threaded: __mf_lookup_shift_1 = f...; __mf_lookup_mask_l = ...; } It is expected that this body of code is rarely executed so we mark the edge to the THEN clause of the conditional jump as unlikely. */ /* Construct t <-- '__mf_elem->low > __mf_base'. */ t = build3 (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TYPE_FIELDS (mf_cache_struct_type), NULL_TREE); t = build2 (GT_EXPR, boolean_type_node, t, mf_base); /* Construct '__mf_elem->high < __mf_limit'. First build: 1) u <-- '__mf_elem->high' 2) v <-- '__mf_limit'. Then build 'u <-- (u < v). */ u = build3 (COMPONENT_REF, mf_uintptr_type, build1 (INDIRECT_REF, mf_cache_struct_type, mf_elem), TREE_CHAIN (TYPE_FIELDS (mf_cache_struct_type)), NULL_TREE); v = mf_limit; u = build2 (LT_EXPR, boolean_type_node, u, v); /* Build the composed conditional: t <-- 't || u'. Then store the result of the evaluation of 't' in a temporary variable which we can use as the condition for the conditional jump. */ t = build2 (TRUTH_OR_EXPR, boolean_type_node, t, u); cond = create_tmp_var (boolean_type_node, "__mf_unlikely_cond"); t = build2 (MODIFY_EXPR, boolean_type_node, cond, t); gimplify_to_stmt_list (&t); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* Build the conditional jump. 'cond' is just a temporary so we can simply build a void COND_EXPR. We do need labels in both arms though. */ t = build3 (COND_EXPR, void_type_node, cond, build1 (GOTO_EXPR, void_type_node, tree_block_label (then_bb)), build1 (GOTO_EXPR, void_type_node, tree_block_label (join_bb))); SET_EXPR_LOCUS (t, locus); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); /* At this point, after so much hard work, we have only constructed the conditional jump, if (__mf_elem->low > __mf_base || (__mf_elem_high < __mf_limit)) The lowered GIMPLE tree representing this code is in the statement list starting at 'head'. We can insert this now in the current basic block, i.e. the one that the statement we're instrumenting was originally in. */ bsi = bsi_last (cond_bb); for (tsi = head; ! tsi_end_p (tsi); tsi_next (&tsi)) bsi_insert_after (&bsi, tsi_stmt (tsi), BSI_CONTINUE_LINKING); /* Now build up the body of the cache-miss handling: __mf_check(); refresh *_l vars. This is the body of the conditional. */ u = tree_cons (NULL_TREE, mf_file_function_line_tree (locus == NULL ? UNKNOWN_LOCATION : *locus), NULL_TREE); u = tree_cons (NULL_TREE, dirflag, u); /* NB: we pass the overall [base..limit] range to mf_check. */ u = tree_cons (NULL_TREE, fold_build2 (PLUS_EXPR, integer_type_node, fold_build2 (MINUS_EXPR, mf_uintptr_type, mf_limit, mf_base), integer_one_node), u); u = tree_cons (NULL_TREE, mf_base, u); t = build_function_call_expr (mf_check_fndecl, u); gimplify_to_stmt_list (&t); head = tsi_start (t); tsi = tsi_last (t); if (! flag_mudflap_threads) { t = build2 (MODIFY_EXPR, void_type_node, mf_cache_shift_decl_l, mf_cache_shift_decl); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); t = build2 (MODIFY_EXPR, void_type_node, mf_cache_mask_decl_l, mf_cache_mask_decl); tsi_link_after (&tsi, t, TSI_CONTINUE_LINKING); } /* Insert the check code in the THEN block. */ bsi = bsi_start (then_bb); for (tsi = head; ! tsi_end_p (tsi); tsi_next (&tsi)) bsi_insert_after (&bsi, tsi_stmt (tsi), BSI_CONTINUE_LINKING); *instr_bsi = bsi_start (join_bb); bsi_next (instr_bsi); }
static bool forward_propagate_addr_expr (tree stmt) { int stmt_loop_depth = bb_for_stmt (stmt)->loop_depth; tree name = TREE_OPERAND (stmt, 0); use_operand_p imm_use; tree use_stmt, lhs, rhs, array_ref; /* We require that the SSA_NAME holding the result of the ADDR_EXPR be used only once. That may be overly conservative in that we could propagate into multiple uses. However, that would effectively be un-cseing the ADDR_EXPR, which is probably not what we want. */ single_imm_use (name, &imm_use, &use_stmt); if (!use_stmt) return false; /* If the use is not in a simple assignment statement, then there is nothing we can do. */ if (TREE_CODE (use_stmt) != MODIFY_EXPR) return false; /* If the use is in a deeper loop nest, then we do not want to propagate the ADDR_EXPR into the loop as that is likely adding expression evaluations into the loop. */ if (bb_for_stmt (use_stmt)->loop_depth > stmt_loop_depth) return false; /* Strip away any outer COMPONENT_REF/ARRAY_REF nodes from the LHS. ADDR_EXPR will not appear on the LHS. */ lhs = TREE_OPERAND (use_stmt, 0); while (TREE_CODE (lhs) == COMPONENT_REF || TREE_CODE (lhs) == ARRAY_REF) lhs = TREE_OPERAND (lhs, 0); /* Now see if the LHS node is an INDIRECT_REF using NAME. If so, propagate the ADDR_EXPR into the use of NAME and fold the result. */ if (TREE_CODE (lhs) == INDIRECT_REF && TREE_OPERAND (lhs, 0) == name) { /* This should always succeed in creating gimple, so there is no need to save enough state to undo this propagation. */ TREE_OPERAND (lhs, 0) = unshare_expr (TREE_OPERAND (stmt, 1)); fold_stmt_inplace (use_stmt); tidy_after_forward_propagate_addr (use_stmt); return true; } /* Trivial case. The use statement could be a trivial copy. We go ahead and handle that case here since it's trivial and removes the need to run copy-prop before this pass to get the best results. Also note that by handling this case here we can catch some cascading effects, ie the single use is in a copy, and the copy is used later by a single INDIRECT_REF for example. */ if (TREE_CODE (lhs) == SSA_NAME && TREE_OPERAND (use_stmt, 1) == name) { TREE_OPERAND (use_stmt, 1) = unshare_expr (TREE_OPERAND (stmt, 1)); tidy_after_forward_propagate_addr (use_stmt); return true; } /* Strip away any outer COMPONENT_REF, ARRAY_REF or ADDR_EXPR nodes from the RHS. */ rhs = TREE_OPERAND (use_stmt, 1); while (TREE_CODE (rhs) == COMPONENT_REF || TREE_CODE (rhs) == ARRAY_REF || TREE_CODE (rhs) == ADDR_EXPR) rhs = TREE_OPERAND (rhs, 0); /* Now see if the RHS node is an INDIRECT_REF using NAME. If so, propagate the ADDR_EXPR into the use of NAME and fold the result. */ if (TREE_CODE (rhs) == INDIRECT_REF && TREE_OPERAND (rhs, 0) == name) { /* This should always succeed in creating gimple, so there is no need to save enough state to undo this propagation. */ TREE_OPERAND (rhs, 0) = unshare_expr (TREE_OPERAND (stmt, 1)); fold_stmt_inplace (use_stmt); tidy_after_forward_propagate_addr (use_stmt); return true; } /* The remaining cases are all for turning pointer arithmetic into array indexing. They only apply when we have the address of element zero in an array. If that is not the case then there is nothing to do. */ array_ref = TREE_OPERAND (TREE_OPERAND (stmt, 1), 0); if (TREE_CODE (array_ref) != ARRAY_REF || TREE_CODE (TREE_TYPE (TREE_OPERAND (array_ref, 0))) != ARRAY_TYPE || !integer_zerop (TREE_OPERAND (array_ref, 1))) return false; /* If the use of the ADDR_EXPR must be a PLUS_EXPR, or else there is nothing to do. */ if (TREE_CODE (rhs) != PLUS_EXPR) return false; /* Try to optimize &x[0] + C where C is a multiple of the size of the elements in X into &x[C/element size]. */ if (TREE_OPERAND (rhs, 0) == name && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST) { tree orig = unshare_expr (rhs); TREE_OPERAND (rhs, 0) = unshare_expr (TREE_OPERAND (stmt, 1)); /* If folding succeeds, then we have just exposed new variables in USE_STMT which will need to be renamed. If folding fails, then we need to put everything back the way it was. */ if (fold_stmt_inplace (use_stmt)) { tidy_after_forward_propagate_addr (use_stmt); return true; } else { TREE_OPERAND (use_stmt, 1) = orig; update_stmt (use_stmt); return false; } } /* Try to optimize &x[0] + OFFSET where OFFSET is defined by converting a multiplication of an index by the size of the array elements, then the result is converted into the proper type for the arithmetic. */ if (TREE_OPERAND (rhs, 0) == name && TREE_CODE (TREE_OPERAND (rhs, 1)) == SSA_NAME /* Avoid problems with IVopts creating PLUS_EXPRs with a different type than their operands. */ && lang_hooks.types_compatible_p (TREE_TYPE (name), TREE_TYPE (rhs))) { tree offset_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 1)); return forward_propagate_addr_into_variable_array_index (offset_stmt, lhs, stmt, use_stmt); } /* Same as the previous case, except the operands of the PLUS_EXPR were reversed. */ if (TREE_OPERAND (rhs, 1) == name && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME /* Avoid problems with IVopts creating PLUS_EXPRs with a different type than their operands. */ && lang_hooks.types_compatible_p (TREE_TYPE (name), TREE_TYPE (rhs))) { tree offset_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 0)); return forward_propagate_addr_into_variable_array_index (offset_stmt, lhs, stmt, use_stmt); } return false; }
if (!bsi_end_p (bsi)) return false; return true; } /* Replace PHI node element whose edge is E in block BB with variable NEW. Remove the edge from COND_BLOCK which does not lead to BB (COND_BLOCK is known to have two edges, one of which must reach BB). */ static void replace_phi_edge_with_variable (basic_block cond_block, edge e, tree phi, tree new) { basic_block bb = bb_for_stmt (phi); basic_block block_to_remove; block_stmt_iterator bsi; /* Change the PHI argument to new. */ SET_USE (PHI_ARG_DEF_PTR (phi, e->dest_idx), new); /* Remove the empty basic block. */ if (EDGE_SUCC (cond_block, 0)->dest == bb) { EDGE_SUCC (cond_block, 0)->flags |= EDGE_FALLTHRU; EDGE_SUCC (cond_block, 0)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE); EDGE_SUCC (cond_block, 0)->probability = REG_BR_PROB_BASE; EDGE_SUCC (cond_block, 0)->count += EDGE_SUCC (cond_block, 1)->count; block_to_remove = EDGE_SUCC (cond_block, 1)->dest;
static unsigned int get_rank (tree e) { operand_entry_t vr; /* Constants have rank 0. */ if (is_gimple_min_invariant (e)) return 0; /* SSA_NAME's have the rank of the expression they are the result of. For globals and uninitialized values, the rank is 0. For function arguments, use the pre-setup rank. For PHI nodes, stores, asm statements, etc, we use the rank of the BB. For simple operations, the rank is the maximum rank of any of its operands, or the bb_rank, whichever is less. I make no claims that this is optimal, however, it gives good results. */ if (TREE_CODE (e) == SSA_NAME) { tree stmt; tree rhs; unsigned int rank, maxrank; int i; if (TREE_CODE (SSA_NAME_VAR (e)) == PARM_DECL && e == default_def (SSA_NAME_VAR (e))) return find_operand_rank (e)->rank; stmt = SSA_NAME_DEF_STMT (e); if (bb_for_stmt (stmt) == NULL) return 0; if (TREE_CODE (stmt) != MODIFY_EXPR || !ZERO_SSA_OPERANDS (stmt, SSA_OP_VIRTUAL_DEFS)) return bb_rank[bb_for_stmt (stmt)->index]; /* If we already have a rank for this expression, use that. */ vr = find_operand_rank (e); if (vr) return vr->rank; /* Otherwise, find the maximum rank for the operands, or the bb rank, whichever is less. */ rank = 0; maxrank = bb_rank[bb_for_stmt(stmt)->index]; rhs = TREE_OPERAND (stmt, 1); if (TREE_CODE_LENGTH (TREE_CODE (rhs)) == 0) rank = MAX (rank, get_rank (rhs)); else { for (i = 0; i < TREE_CODE_LENGTH (TREE_CODE (rhs)) && TREE_OPERAND (rhs, i) && rank != maxrank; i++) rank = MAX(rank, get_rank (TREE_OPERAND (rhs, i))); } if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Rank for "); print_generic_expr (dump_file, e, 0); fprintf (dump_file, " is %d\n", (rank + 1)); } /* Note the rank in the hashtable so we don't recompute it. */ insert_operand_rank (e, (rank + 1)); return (rank + 1); } /* Globals, etc, are rank 0 */ return 0; }