static bool stmt_may_generate_copy (gimple stmt) { if (gimple_code (stmt) == GIMPLE_PHI) return !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_phi_result (stmt)); if (gimple_code (stmt) != GIMPLE_ASSIGN) return false; /* If the statement has volatile operands, it won't generate a useful copy. */ if (gimple_has_volatile_ops (stmt)) return false; /* Statements with loads and/or stores will never generate a useful copy. */ if (gimple_vuse (stmt)) return false; /* Otherwise, the only statements that generate useful copies are assignments whose RHS is just an SSA name that doesn't flow through abnormal edges. */ return ((gimple_assign_rhs_code (stmt) == SSA_NAME && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (stmt))) || is_gimple_min_invariant (gimple_assign_rhs1 (stmt))); }
edge ssa_redirect_edge (edge e, basic_block dest) { gphi_iterator gsi; gphi *phi; redirect_edge_var_map_clear (e); /* Remove the appropriate PHI arguments in E's destination block. */ for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) { tree def; source_location locus ; phi = gsi.phi (); def = gimple_phi_arg_def (phi, e->dest_idx); locus = gimple_phi_arg_location (phi, e->dest_idx); if (def == NULL_TREE) continue; redirect_edge_var_map_add (e, gimple_phi_result (phi), def, locus); } e = redirect_edge_succ_nodup (e, dest); return e; }
static bool stmt_has_scalar_dependences_outside_loop (gimple stmt) { tree name; switch (gimple_code (stmt)) { case GIMPLE_CALL: case GIMPLE_ASSIGN: name = gimple_get_lhs (stmt); break; case GIMPLE_PHI: name = gimple_phi_result (stmt); break; default: return false; } return (name && TREE_CODE (name) == SSA_NAME && ssa_name_has_uses_outside_loop_p (name, loop_containing_stmt (stmt))); }
static unsigned get_stmt_uid (gimple stmt) { if (gimple_code (stmt) == GIMPLE_PHI) return SSA_NAME_VERSION (gimple_phi_result (stmt)) + gimple_stmt_max_uid (cfun); return gimple_uid (stmt); }
void output_bb (struct output_block *ob, basic_block bb, struct function *fn) { gimple_stmt_iterator bsi = gsi_start_bb (bb); streamer_write_record_start (ob, (!gsi_end_p (bsi)) || phi_nodes (bb) ? LTO_bb1 : LTO_bb0); streamer_write_uhwi (ob, bb->index); streamer_write_gcov_count (ob, bb->count); streamer_write_hwi (ob, bb->frequency); streamer_write_hwi (ob, bb->flags); if (!gsi_end_p (bsi) || phi_nodes (bb)) { /* Output the statements. The list of statements is terminated with a zero. */ for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi)) { int region; gimple stmt = gsi_stmt (bsi); output_gimple_stmt (ob, stmt); /* Emit the EH region holding STMT. */ region = lookup_stmt_eh_lp_fn (fn, stmt); if (region != 0) { streamer_write_record_start (ob, LTO_eh_region); streamer_write_hwi (ob, region); } else streamer_write_record_start (ob, LTO_null); } streamer_write_record_start (ob, LTO_null); for (gphi_iterator psi = gsi_start_phis (bb); !gsi_end_p (psi); gsi_next (&psi)) { gphi *phi = psi.phi (); /* Only emit PHIs for gimple registers. PHI nodes for .MEM will be filled in on reading when the SSA form is updated. */ if (!virtual_operand_p (gimple_phi_result (phi))) output_phi (ob, phi); } streamer_write_record_start (ob, LTO_null); } }
static bool generate_loops_for_partition (struct loop *loop, bitmap partition, bool copy_p) { unsigned i, x; gimple_stmt_iterator bsi; basic_block *bbs; if (copy_p) { loop = copy_loop_before (loop); create_preheader (loop, CP_SIMPLE_PREHEADERS); create_bb_after_loop (loop); } if (loop == NULL) return false; /* Remove stmts not in the PARTITION bitmap. The order in which we visit the phi nodes and the statements is exactly as in stmts_from_loop. */ bbs = get_loop_body_in_dom_order (loop); for (x = 0, i = 0; i < loop->num_nodes; i++) { basic_block bb = bbs[i]; for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi);) if (!bitmap_bit_p (partition, x++)) { gimple phi = gsi_stmt (bsi); if (!is_gimple_reg (gimple_phi_result (phi))) mark_virtual_phi_result_for_renaming (phi); remove_phi_node (&bsi, true); } else gsi_next (&bsi); for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi);) { gimple stmt = gsi_stmt (bsi); if (gimple_code (gsi_stmt (bsi)) != GIMPLE_LABEL && !bitmap_bit_p (partition, x++)) { unlink_stmt_vdef (stmt); gsi_remove (&bsi, true); release_defs (stmt); } else gsi_next (&bsi); } } free (bbs); return true; }
void remove_phi_node (gimple_stmt_iterator *gsi, bool release_lhs_p) { gimple phi = gsi_stmt (*gsi); gsi_remove (gsi, false); /* 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); if (release_lhs_p) release_ssa_name (gimple_phi_result (phi)); }
static void init_copy_prop (void) { basic_block bb; n_copy_of = num_ssa_names; copy_of = XCNEWVEC (prop_value_t, n_copy_of); FOR_EACH_BB_FN (bb, cfun) { for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple *stmt = gsi_stmt (si); ssa_op_iter iter; tree def; /* The only statements that we care about are those that may generate useful copies. We also need to mark conditional jumps so that their outgoing edges are added to the work lists of the propagator. */ if (stmt_ends_bb_p (stmt)) prop_set_simulate_again (stmt, true); else if (stmt_may_generate_copy (stmt)) prop_set_simulate_again (stmt, true); else prop_set_simulate_again (stmt, false); /* Mark all the outputs of this statement as not being the copy of anything. */ FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS) if (!prop_simulate_again_p (stmt)) set_copy_of_val (def, def); } for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) { gphi *phi = si.phi (); tree def; def = gimple_phi_result (phi); if (virtual_operand_p (def)) prop_set_simulate_again (phi, false); else prop_set_simulate_again (phi, true); if (!prop_simulate_again_p (phi)) set_copy_of_val (def, def); } } }
void backprop::process_block (basic_block bb) { for (gimple_stmt_iterator gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi)) { tree lhs = gimple_get_lhs (gsi_stmt (gsi)); if (lhs && TREE_CODE (lhs) == SSA_NAME) process_var (lhs); } for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); gsi_next (&gpi)) process_var (gimple_phi_result (gpi.phi ())); }
DEBUG_FUNCTION void verify_ssaname_freelists (struct function *fun) { if (!gimple_in_ssa_p (fun)) return; bitmap names_in_il = BITMAP_ALLOC (NULL); /* Walk the entire IL noting every SSA_NAME we see. */ basic_block bb; FOR_EACH_BB_FN (bb, fun) { tree t; /* First note the result and arguments of PHI nodes. */ for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gphi *phi = gsi.phi (); t = gimple_phi_result (phi); bitmap_set_bit (names_in_il, SSA_NAME_VERSION (t)); for (unsigned int i = 0; i < gimple_phi_num_args (phi); i++) { t = gimple_phi_arg_def (phi, i); if (TREE_CODE (t) == SSA_NAME) bitmap_set_bit (names_in_il, SSA_NAME_VERSION (t)); } } /* Then note the operands of each statement. */ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { ssa_op_iter iter; gimple *stmt = gsi_stmt (gsi); FOR_EACH_SSA_TREE_OPERAND (t, stmt, iter, SSA_OP_ALL_OPERANDS) bitmap_set_bit (names_in_il, SSA_NAME_VERSION (t)); } }
static void eliminate_build (elim_graph g) { tree Ti; int p0, pi; gimple_stmt_iterator gsi; clear_elim_graph (g); for (gsi = gsi_start_phis (g->e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple phi = gsi_stmt (gsi); source_location locus; p0 = var_to_partition (g->map, gimple_phi_result (phi)); /* Ignore results which are not in partitions. */ if (p0 == NO_PARTITION) continue; Ti = PHI_ARG_DEF (phi, g->e->dest_idx); locus = gimple_phi_arg_location_from_edge (phi, g->e); /* If this argument is a constant, or a SSA_NAME which is being left in SSA form, just queue a copy to be emitted on this edge. */ if (!phi_ssa_name_p (Ti) || (TREE_CODE (Ti) == SSA_NAME && var_to_partition (g->map, Ti) == NO_PARTITION)) { /* Save constant copies until all other copies have been emitted on this edge. */ VEC_safe_push (int, heap, g->const_dests, p0); VEC_safe_push (tree, heap, g->const_copies, Ti); VEC_safe_push (source_location, heap, g->copy_locus, locus); } else {
void reserve_phi_args_for_new_edge (basic_block bb) { size_t len = EDGE_COUNT (bb->preds); size_t cap = ideal_phi_node_len (len + 4); gimple_stmt_iterator gsi; for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple_statement_phi *stmt = as_a <gimple_statement_phi> (gsi_stmt (gsi)); if (len > gimple_phi_capacity (stmt)) { gimple_statement_phi *new_phi = resize_phi_node (stmt, cap); /* The result of the PHI is defined by this PHI node. */ SSA_NAME_DEF_STMT (gimple_phi_result (new_phi)) = new_phi; gsi_set_stmt (&gsi, new_phi); release_phi_node (stmt); stmt = new_phi; } /* We represent a "missing PHI argument" by placing NULL_TREE in the corresponding slot. If PHI arguments were added immediately after an edge is created, this zeroing would not be necessary, but unfortunately this is not the case. For example, the loop optimizer duplicates several basic blocks, redirects edges, and then fixes up PHI arguments later in batch. */ SET_PHI_ARG_DEF (stmt, len - 1, NULL_TREE); gimple_phi_arg_set_location (stmt, len - 1, UNKNOWN_LOCATION); stmt->nargs++; } }
static enum ssa_prop_result copy_prop_visit_phi_node (gimple phi) { enum ssa_prop_result retval; unsigned i; prop_value_t phi_val = { NULL_TREE }; tree lhs = gimple_phi_result (phi); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nVisiting PHI node: "); print_gimple_stmt (dump_file, phi, 0, dump_flags); } for (i = 0; i < gimple_phi_num_args (phi); i++) { prop_value_t *arg_val; tree arg_value; tree arg = gimple_phi_arg_def (phi, i); edge e = gimple_phi_arg_edge (phi, i); /* We don't care about values flowing through non-executable edges. */ if (!(e->flags & EDGE_EXECUTABLE)) continue; /* Names that flow through abnormal edges cannot be used to derive copies. */ if (TREE_CODE (arg) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (arg)) { phi_val.value = lhs; break; } if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\tArgument #%d: ", i); dump_copy_of (dump_file, arg); fprintf (dump_file, "\n"); } if (TREE_CODE (arg) == SSA_NAME) { arg_val = get_copy_of_val (arg); /* If we didn't visit the definition of arg yet treat it as UNDEFINED. This also handles PHI arguments that are the same as lhs. We'll come here again. */ if (!arg_val->value) continue; arg_value = arg_val->value; } else arg_value = valueize_val (arg); /* Avoid copy propagation from an inner into an outer loop. Otherwise, this may move loop variant variables outside of their loops and prevent coalescing opportunities. If the value was loop invariant, it will be hoisted by LICM and exposed for copy propagation. ??? The value will be always loop invariant. In loop-closed SSA form do not copy-propagate through PHI nodes in blocks with a loop exit edge predecessor. */ if (current_loops && TREE_CODE (arg_value) == SSA_NAME && (loop_depth_of_name (arg_value) > loop_depth_of_name (lhs) || (loops_state_satisfies_p (LOOP_CLOSED_SSA) && loop_exit_edge_p (e->src->loop_father, e)))) { phi_val.value = lhs; break; } /* If the LHS didn't have a value yet, make it a copy of the first argument we find. */ if (phi_val.value == NULL_TREE) { phi_val.value = arg_value; continue; } /* If PHI_VAL and ARG don't have a common copy-of chain, then this PHI node cannot be a copy operation. */ if (phi_val.value != arg_value && !operand_equal_p (phi_val.value, arg_value, 0)) { phi_val.value = lhs; break; } } if (phi_val.value && may_propagate_copy (lhs, phi_val.value) && set_copy_of_val (lhs, phi_val.value)) retval = (phi_val.value != lhs) ? SSA_PROP_INTERESTING : SSA_PROP_VARYING; else retval = SSA_PROP_NOT_INTERESTING; if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "PHI node "); dump_copy_of (dump_file, lhs); fprintf (dump_file, "\nTelling the propagator to "); if (retval == SSA_PROP_INTERESTING) fprintf (dump_file, "add SSA edges out of this PHI and continue."); else if (retval == SSA_PROP_VARYING) fprintf (dump_file, "add SSA edges out of this PHI and never visit again."); else fprintf (dump_file, "do nothing with SSA edges and keep iterating."); fprintf (dump_file, "\n\n"); } return retval; }
static void init_copy_prop (void) { basic_block bb; copy_of = XCNEWVEC (prop_value_t, num_ssa_names); cached_last_copy_of = XCNEWVEC (tree, num_ssa_names); FOR_EACH_BB (bb) { gimple_stmt_iterator si; int depth = bb->loop_depth; for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple stmt = gsi_stmt (si); ssa_op_iter iter; tree def; /* The only statements that we care about are those that may generate useful copies. We also need to mark conditional jumps so that their outgoing edges are added to the work lists of the propagator. Avoid copy propagation from an inner into an outer loop. Otherwise, this may move loop variant variables outside of their loops and prevent coalescing opportunities. If the value was loop invariant, it will be hoisted by LICM and exposed for copy propagation. */ if (stmt_ends_bb_p (stmt)) prop_set_simulate_again (stmt, true); else if (stmt_may_generate_copy (stmt) /* Since we are iterating over the statements in BB, not the phi nodes, STMT will always be an assignment. */ && loop_depth_of_name (gimple_assign_rhs1 (stmt)) <= depth) prop_set_simulate_again (stmt, true); else prop_set_simulate_again (stmt, false); /* Mark all the outputs of this statement as not being the copy of anything. */ FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS) if (!prop_simulate_again_p (stmt)) set_copy_of_val (def, def); else cached_last_copy_of[SSA_NAME_VERSION (def)] = def; } for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) { gimple phi = gsi_stmt (si); tree def; def = gimple_phi_result (phi); if (!is_gimple_reg (def) /* In loop-closed SSA form do not copy-propagate through PHI nodes. Technically this is only needed for loop exit PHIs, but this is difficult to query. */ || (current_loops && gimple_phi_num_args (phi) == 1 && loops_state_satisfies_p (LOOP_CLOSED_SSA))) prop_set_simulate_again (phi, false); else prop_set_simulate_again (phi, true); if (!prop_simulate_again_p (phi)) set_copy_of_val (def, def); else cached_last_copy_of[SSA_NAME_VERSION (def)] = def; } } }
static enum ssa_prop_result copy_prop_visit_phi_node (gimple phi) { enum ssa_prop_result retval; unsigned i; prop_value_t phi_val = { 0, NULL_TREE }; tree lhs = gimple_phi_result (phi); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nVisiting PHI node: "); print_gimple_stmt (dump_file, phi, 0, dump_flags); fprintf (dump_file, "\n\n"); } for (i = 0; i < gimple_phi_num_args (phi); i++) { prop_value_t *arg_val; tree arg = gimple_phi_arg_def (phi, i); edge e = gimple_phi_arg_edge (phi, i); /* We don't care about values flowing through non-executable edges. */ if (!(e->flags & EDGE_EXECUTABLE)) continue; /* Constants in the argument list never generate a useful copy. Similarly, names that flow through abnormal edges cannot be used to derive copies. */ if (TREE_CODE (arg) != SSA_NAME || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (arg)) { phi_val.value = lhs; break; } /* Avoid copy propagation from an inner into an outer loop. Otherwise, this may move loop variant variables outside of their loops and prevent coalescing opportunities. If the value was loop invariant, it will be hoisted by LICM and exposed for copy propagation. Not a problem for virtual operands though. */ if (is_gimple_reg (lhs) && loop_depth_of_name (arg) > loop_depth_of_name (lhs)) { phi_val.value = lhs; break; } /* If the LHS appears in the argument list, ignore it. It is irrelevant as a copy. */ if (arg == lhs || get_last_copy_of (arg) == lhs) continue; if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\tArgument #%d: ", i); dump_copy_of (dump_file, arg); fprintf (dump_file, "\n"); } arg_val = get_copy_of_val (arg); /* If the LHS didn't have a value yet, make it a copy of the first argument we find. Notice that while we make the LHS be a copy of the argument itself, we take the memory reference from the argument's value so that we can compare it to the memory reference of all the other arguments. */ if (phi_val.value == NULL_TREE) { phi_val.value = arg_val->value ? arg_val->value : arg; continue; } /* If PHI_VAL and ARG don't have a common copy-of chain, then this PHI node cannot be a copy operation. Also, if we are copy propagating stores and these two arguments came from different memory references, they cannot be considered copies. */ if (get_last_copy_of (phi_val.value) != get_last_copy_of (arg)) { phi_val.value = lhs; break; } } if (phi_val.value && may_propagate_copy (lhs, phi_val.value) && set_copy_of_val (lhs, phi_val.value)) retval = (phi_val.value != lhs) ? SSA_PROP_INTERESTING : SSA_PROP_VARYING; else retval = SSA_PROP_NOT_INTERESTING; if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nPHI node "); dump_copy_of (dump_file, lhs); fprintf (dump_file, "\nTelling the propagator to "); if (retval == SSA_PROP_INTERESTING) fprintf (dump_file, "add SSA edges out of this PHI and continue."); else if (retval == SSA_PROP_VARYING) fprintf (dump_file, "add SSA edges out of this PHI and never visit again."); else fprintf (dump_file, "do nothing with SSA edges and keep iterating."); fprintf (dump_file, "\n\n"); } return retval; }
static bool init_dont_simulate_again (void) { basic_block bb; gimple_stmt_iterator gsi; gimple phi; bool saw_a_complex_op = false; FOR_EACH_BB (bb) { for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { phi = gsi_stmt (gsi); prop_set_simulate_again (phi, is_complex_reg (gimple_phi_result (phi))); } for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple stmt; tree op0, op1; bool sim_again_p; stmt = gsi_stmt (gsi); op0 = op1 = NULL_TREE; /* Most control-altering statements must be initially simulated, else we won't cover the entire cfg. */ sim_again_p = stmt_ends_bb_p (stmt); switch (gimple_code (stmt)) { case GIMPLE_CALL: if (gimple_call_lhs (stmt)) sim_again_p = is_complex_reg (gimple_call_lhs (stmt)); break; case GIMPLE_ASSIGN: sim_again_p = is_complex_reg (gimple_assign_lhs (stmt)); if (gimple_assign_rhs_code (stmt) == REALPART_EXPR || gimple_assign_rhs_code (stmt) == IMAGPART_EXPR) op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0); else op0 = gimple_assign_rhs1 (stmt); if (gimple_num_ops (stmt) > 2) op1 = gimple_assign_rhs2 (stmt); break; case GIMPLE_COND: op0 = gimple_cond_lhs (stmt); op1 = gimple_cond_rhs (stmt); break; default: break; } if (op0 || op1) switch (gimple_expr_code (stmt)) { case EQ_EXPR: case NE_EXPR: case PLUS_EXPR: case MINUS_EXPR: case MULT_EXPR: case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: case FLOOR_DIV_EXPR: case ROUND_DIV_EXPR: case RDIV_EXPR: if (TREE_CODE (TREE_TYPE (op0)) == COMPLEX_TYPE || TREE_CODE (TREE_TYPE (op1)) == COMPLEX_TYPE) saw_a_complex_op = true; break; case NEGATE_EXPR: case CONJ_EXPR: if (TREE_CODE (TREE_TYPE (op0)) == COMPLEX_TYPE) saw_a_complex_op = true; break; case REALPART_EXPR: case IMAGPART_EXPR: /* The total store transformation performed during gimplification creates such uninitialized loads and we need to lower the statement to be able to fix things up. */ if (TREE_CODE (op0) == SSA_NAME && ssa_undefined_value_p (op0)) saw_a_complex_op = true; break; default: break; } prop_set_simulate_again (stmt, sim_again_p); } } return saw_a_complex_op; }
tree avail_exprs_stack::lookup_avail_expr (gimple *stmt, bool insert, bool tbaa_p) { expr_hash_elt **slot; tree lhs; /* Get LHS of phi, assignment, or call; else NULL_TREE. */ if (gimple_code (stmt) == GIMPLE_PHI) lhs = gimple_phi_result (stmt); else lhs = gimple_get_lhs (stmt); class expr_hash_elt element (stmt, lhs); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "LKUP "); element.print (dump_file); } /* Don't bother remembering constant assignments and copy operations. Constants and copy operations are handled by the constant/copy propagator in optimize_stmt. */ if (element.expr()->kind == EXPR_SINGLE && (TREE_CODE (element.expr()->ops.single.rhs) == SSA_NAME || is_gimple_min_invariant (element.expr()->ops.single.rhs))) return NULL_TREE; /* Finally try to find the expression in the main expression hash table. */ slot = m_avail_exprs->find_slot (&element, (insert ? INSERT : NO_INSERT)); if (slot == NULL) { return NULL_TREE; } else if (*slot == NULL) { class expr_hash_elt *element2 = new expr_hash_elt (element); *slot = element2; record_expr (element2, NULL, '2'); return NULL_TREE; } /* If we found a redundant memory operation do an alias walk to check if we can re-use it. */ if (gimple_vuse (stmt) != (*slot)->vop ()) { tree vuse1 = (*slot)->vop (); tree vuse2 = gimple_vuse (stmt); /* If we have a load of a register and a candidate in the hash with vuse1 then try to reach its stmt by walking up the virtual use-def chain using walk_non_aliased_vuses. But don't do this when removing expressions from the hash. */ ao_ref ref; if (!(vuse1 && vuse2 && gimple_assign_single_p (stmt) && TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME && (ao_ref_init (&ref, gimple_assign_rhs1 (stmt)), ref.base_alias_set = ref.ref_alias_set = tbaa_p ? -1 : 0, true) && walk_non_aliased_vuses (&ref, vuse2, vuse_eq, NULL, NULL, vuse1) != NULL)) { if (insert) { class expr_hash_elt *element2 = new expr_hash_elt (element); /* Insert the expr into the hash by replacing the current entry and recording the value to restore in the avail_exprs_stack. */ record_expr (element2, *slot, '2'); *slot = element2; } return NULL_TREE; } } /* Extract the LHS of the assignment so that it can be used as the current definition of another variable. */ lhs = (*slot)->lhs (); /* Valueize the result. */ if (TREE_CODE (lhs) == SSA_NAME) { tree tem = SSA_NAME_VALUE (lhs); if (tem) lhs = tem; } if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "FIND: "); print_generic_expr (dump_file, lhs, 0); fprintf (dump_file, "\n"); } return lhs; }
expr_hash_elt::expr_hash_elt (gimple *stmt, tree orig_lhs) { enum gimple_code code = gimple_code (stmt); struct hashable_expr *expr = this->expr (); if (code == GIMPLE_ASSIGN) { enum tree_code subcode = gimple_assign_rhs_code (stmt); switch (get_gimple_rhs_class (subcode)) { case GIMPLE_SINGLE_RHS: expr->kind = EXPR_SINGLE; expr->type = TREE_TYPE (gimple_assign_rhs1 (stmt)); expr->ops.single.rhs = gimple_assign_rhs1 (stmt); break; case GIMPLE_UNARY_RHS: expr->kind = EXPR_UNARY; expr->type = TREE_TYPE (gimple_assign_lhs (stmt)); if (CONVERT_EXPR_CODE_P (subcode)) subcode = NOP_EXPR; expr->ops.unary.op = subcode; expr->ops.unary.opnd = gimple_assign_rhs1 (stmt); break; case GIMPLE_BINARY_RHS: expr->kind = EXPR_BINARY; expr->type = TREE_TYPE (gimple_assign_lhs (stmt)); expr->ops.binary.op = subcode; expr->ops.binary.opnd0 = gimple_assign_rhs1 (stmt); expr->ops.binary.opnd1 = gimple_assign_rhs2 (stmt); break; case GIMPLE_TERNARY_RHS: expr->kind = EXPR_TERNARY; expr->type = TREE_TYPE (gimple_assign_lhs (stmt)); expr->ops.ternary.op = subcode; expr->ops.ternary.opnd0 = gimple_assign_rhs1 (stmt); expr->ops.ternary.opnd1 = gimple_assign_rhs2 (stmt); expr->ops.ternary.opnd2 = gimple_assign_rhs3 (stmt); break; default: gcc_unreachable (); } } else if (code == GIMPLE_COND) { expr->type = boolean_type_node; expr->kind = EXPR_BINARY; expr->ops.binary.op = gimple_cond_code (stmt); expr->ops.binary.opnd0 = gimple_cond_lhs (stmt); expr->ops.binary.opnd1 = gimple_cond_rhs (stmt); } else if (gcall *call_stmt = dyn_cast <gcall *> (stmt)) { size_t nargs = gimple_call_num_args (call_stmt); size_t i; gcc_assert (gimple_call_lhs (call_stmt)); expr->type = TREE_TYPE (gimple_call_lhs (call_stmt)); expr->kind = EXPR_CALL; expr->ops.call.fn_from = call_stmt; if (gimple_call_flags (call_stmt) & (ECF_CONST | ECF_PURE)) expr->ops.call.pure = true; else expr->ops.call.pure = false; expr->ops.call.nargs = nargs; expr->ops.call.args = XCNEWVEC (tree, nargs); for (i = 0; i < nargs; i++) expr->ops.call.args[i] = gimple_call_arg (call_stmt, i); } else if (gswitch *swtch_stmt = dyn_cast <gswitch *> (stmt)) { expr->type = TREE_TYPE (gimple_switch_index (swtch_stmt)); expr->kind = EXPR_SINGLE; expr->ops.single.rhs = gimple_switch_index (swtch_stmt); } else if (code == GIMPLE_GOTO) { expr->type = TREE_TYPE (gimple_goto_dest (stmt)); expr->kind = EXPR_SINGLE; expr->ops.single.rhs = gimple_goto_dest (stmt); } else if (code == GIMPLE_PHI) { size_t nargs = gimple_phi_num_args (stmt); size_t i; expr->type = TREE_TYPE (gimple_phi_result (stmt)); expr->kind = EXPR_PHI; expr->ops.phi.nargs = nargs; expr->ops.phi.args = XCNEWVEC (tree, nargs); for (i = 0; i < nargs; i++) expr->ops.phi.args[i] = gimple_phi_arg_def (stmt, i); } else gcc_unreachable (); m_lhs = orig_lhs; m_vop = gimple_vuse (stmt); m_hash = avail_expr_hash (this); m_stamp = this; }
static unsigned int rename_ssa_copies (void) { var_map map; basic_block bb; gimple_stmt_iterator gsi; tree var, part_var; gimple stmt, phi; unsigned x; FILE *debug; bool updated = false; if (dump_file && (dump_flags & TDF_DETAILS)) debug = dump_file; else debug = NULL; map = init_var_map (num_ssa_names); FOR_EACH_BB (bb) { /* Scan for real copies. */ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { stmt = gsi_stmt (gsi); if (gimple_assign_ssa_name_copy_p (stmt)) { tree lhs = gimple_assign_lhs (stmt); tree rhs = gimple_assign_rhs1 (stmt); updated |= copy_rename_partition_coalesce (map, lhs, rhs, debug); } } } FOR_EACH_BB (bb) { /* Treat PHI nodes as copies between the result and each argument. */ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { size_t i; tree res; phi = gsi_stmt (gsi); res = gimple_phi_result (phi); /* Do not process virtual SSA_NAMES. */ if (!is_gimple_reg (SSA_NAME_VAR (res))) continue; for (i = 0; i < gimple_phi_num_args (phi); i++) { tree arg = gimple_phi_arg (phi, i)->def; if (TREE_CODE (arg) == SSA_NAME) updated |= copy_rename_partition_coalesce (map, res, arg, debug); } } } if (debug) dump_var_map (debug, map); /* Now one more pass to make all elements of a partition share the same root variable. */ for (x = 1; x < num_ssa_names; x++) { part_var = partition_to_var (map, x); if (!part_var) continue; var = ssa_name (x); if (debug) { if (SSA_NAME_VAR (var) != SSA_NAME_VAR (part_var)) { fprintf (debug, "Coalesced "); print_generic_expr (debug, var, TDF_SLIM); fprintf (debug, " to "); print_generic_expr (debug, part_var, TDF_SLIM); fprintf (debug, "\n"); } } replace_ssa_name_symbol (var, SSA_NAME_VAR (part_var)); } delete_var_map (map); return updated ? TODO_remove_unused_locals : 0; }
/* Look for PHI nodes which feed statements in the same block where the value of the PHI node implies the statement is erroneous. For example, a NULL PHI arg value which then feeds a pointer dereference. When found isolate and optimize the path associated with the PHI argument feeding the erroneous statement. */ static void find_implicit_erroneous_behaviour (void) { basic_block bb; FOR_EACH_BB_FN (bb, cfun) { gimple_stmt_iterator si; /* Out of an abundance of caution, do not isolate paths to a block where the block has any abnormal outgoing edges. We might be able to relax this in the future. We have to detect when we have to split the block with the NULL dereference and the trap we insert. We have to preserve abnormal edges out of the isolated block which in turn means updating PHIs at the targets of those abnormal outgoing edges. */ if (has_abnormal_or_eh_outgoing_edge_p (bb)) continue; /* First look for a PHI which sets a pointer to NULL and which is then dereferenced within BB. This is somewhat overly conservative, but probably catches most of the interesting cases. */ for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) { gimple phi = gsi_stmt (si); tree lhs = gimple_phi_result (phi); /* If the result is not a pointer, then there is no need to examine the arguments. */ if (!POINTER_TYPE_P (TREE_TYPE (lhs))) continue; /* PHI produces a pointer result. See if any of the PHI's arguments are NULL. When we remove an edge, we want to reprocess the current index, hence the ugly way we update I for each iteration. */ basic_block duplicate = NULL; for (unsigned i = 0, next_i = 0; i < gimple_phi_num_args (phi); i = next_i) { tree op = gimple_phi_arg_def (phi, i); next_i = i + 1; if (!integer_zerop (op)) continue; edge e = gimple_phi_arg_edge (phi, i); imm_use_iterator iter; gimple use_stmt; /* We've got a NULL PHI argument. Now see if the PHI's result is dereferenced within BB. */ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) { /* We only care about uses in BB. Catching cases in in other blocks would require more complex path isolation code. */ if (gimple_bb (use_stmt) != bb) continue; if (infer_nonnull_range (use_stmt, lhs, flag_isolate_erroneous_paths_dereference, flag_isolate_erroneous_paths_attribute)) { duplicate = isolate_path (bb, duplicate, e, use_stmt, lhs); /* When we remove an incoming edge, we need to reprocess the Ith element. */ next_i = i; cfg_altered = true; } } } } }
static void shrink_wrap_one_built_in_call_with_conds (gcall *bi_call, vec <gimple *> conds, unsigned int nconds) { gimple_stmt_iterator bi_call_bsi; basic_block bi_call_bb, join_tgt_bb, guard_bb; edge join_tgt_in_edge_from_call, join_tgt_in_edge_fall_thru; edge bi_call_in_edge0, guard_bb_in_edge; unsigned tn_cond_stmts; unsigned ci; gimple *cond_expr = NULL; gimple *cond_expr_start; /* The cfg we want to create looks like this: [guard n-1] <- guard_bb (old block) | \ | [guard n-2] } | / \ } | / ... } new blocks | / [guard 0] } | / / | } [ call ] | <- bi_call_bb } | \ | | \ | | [ join ] <- join_tgt_bb (old iff call must end bb) | possible EH edges (only if [join] is old) When [join] is new, the immediate dominators for these blocks are: 1. [guard n-1]: unchanged 2. [call]: [guard n-1] 3. [guard m]: [guard m+1] for 0 <= m <= n-2 4. [join]: [guard n-1] We punt for the more complex case case of [join] being old and simply free the dominance info. We also punt on postdominators, which aren't expected to be available at this point anyway. */ bi_call_bb = gimple_bb (bi_call); /* Now find the join target bb -- split bi_call_bb if needed. */ if (stmt_ends_bb_p (bi_call)) { /* We checked that there was a fallthrough edge in can_guard_call_p. */ join_tgt_in_edge_from_call = find_fallthru_edge (bi_call_bb->succs); gcc_assert (join_tgt_in_edge_from_call); /* We don't want to handle PHIs. */ if (EDGE_COUNT (join_tgt_in_edge_from_call->dest->preds) > 1) join_tgt_bb = split_edge (join_tgt_in_edge_from_call); else { join_tgt_bb = join_tgt_in_edge_from_call->dest; /* We may have degenerate PHIs in the destination. Propagate those out. */ for (gphi_iterator i = gsi_start_phis (join_tgt_bb); !gsi_end_p (i);) { gphi *phi = i.phi (); replace_uses_by (gimple_phi_result (phi), gimple_phi_arg_def (phi, 0)); remove_phi_node (&i, true); } } } else { join_tgt_in_edge_from_call = split_block (bi_call_bb, bi_call); join_tgt_bb = join_tgt_in_edge_from_call->dest; } bi_call_bsi = gsi_for_stmt (bi_call); /* Now it is time to insert the first conditional expression into bi_call_bb and split this bb so that bi_call is shrink-wrapped. */ tn_cond_stmts = conds.length (); cond_expr = NULL; cond_expr_start = conds[0]; for (ci = 0; ci < tn_cond_stmts; ci++) { gimple *c = conds[ci]; gcc_assert (c || ci != 0); if (!c) break; gsi_insert_before (&bi_call_bsi, c, GSI_SAME_STMT); cond_expr = c; } ci++; gcc_assert (cond_expr && gimple_code (cond_expr) == GIMPLE_COND); typedef std::pair<edge, edge> edge_pair; auto_vec<edge_pair, 8> edges; bi_call_in_edge0 = split_block (bi_call_bb, cond_expr); bi_call_in_edge0->flags &= ~EDGE_FALLTHRU; bi_call_in_edge0->flags |= EDGE_FALSE_VALUE; guard_bb = bi_call_bb; bi_call_bb = bi_call_in_edge0->dest; join_tgt_in_edge_fall_thru = make_edge (guard_bb, join_tgt_bb, EDGE_TRUE_VALUE); edges.reserve (nconds); edges.quick_push (edge_pair (bi_call_in_edge0, join_tgt_in_edge_fall_thru)); /* Code generation for the rest of the conditions */ for (unsigned int i = 1; i < nconds; ++i) { unsigned ci0; edge bi_call_in_edge; gimple_stmt_iterator guard_bsi = gsi_for_stmt (cond_expr_start); ci0 = ci; cond_expr_start = conds[ci0]; for (; ci < tn_cond_stmts; ci++) { gimple *c = conds[ci]; gcc_assert (c || ci != ci0); if (!c) break; gsi_insert_before (&guard_bsi, c, GSI_SAME_STMT); cond_expr = c; } ci++; gcc_assert (cond_expr && gimple_code (cond_expr) == GIMPLE_COND); guard_bb_in_edge = split_block (guard_bb, cond_expr); guard_bb_in_edge->flags &= ~EDGE_FALLTHRU; guard_bb_in_edge->flags |= EDGE_TRUE_VALUE; bi_call_in_edge = make_edge (guard_bb, bi_call_bb, EDGE_FALSE_VALUE); edges.quick_push (edge_pair (bi_call_in_edge, guard_bb_in_edge)); } /* Now update the probability and profile information, processing the guards in order of execution. There are two approaches we could take here. On the one hand we could assign a probability of X to the call block and distribute that probability among its incoming edges. On the other hand we could assign a probability of X to each individual call edge. The choice only affects calls that have more than one condition. In those cases, the second approach would give the call block a greater probability than the first. However, the difference is only small, and our chosen X is a pure guess anyway. Here we take the second approach because it's slightly simpler and because it's easy to see that it doesn't lose profile counts. */ bi_call_bb->count = profile_count::zero (); while (!edges.is_empty ()) { edge_pair e = edges.pop (); edge call_edge = e.first; edge nocall_edge = e.second; basic_block src_bb = call_edge->src; gcc_assert (src_bb == nocall_edge->src); call_edge->probability = profile_probability::very_unlikely (); nocall_edge->probability = profile_probability::always () - call_edge->probability; bi_call_bb->count += call_edge->count (); if (nocall_edge->dest != join_tgt_bb) nocall_edge->dest->count = src_bb->count - bi_call_bb->count; } if (dom_info_available_p (CDI_DOMINATORS)) { /* The split_blocks leave [guard 0] as the immediate dominator of [call] and [call] as the immediate dominator of [join]. Fix them up. */ set_immediate_dominator (CDI_DOMINATORS, bi_call_bb, guard_bb); set_immediate_dominator (CDI_DOMINATORS, join_tgt_bb, guard_bb); } if (dump_file && (dump_flags & TDF_DETAILS)) { location_t loc; loc = gimple_location (bi_call); fprintf (dump_file, "%s:%d: note: function call is shrink-wrapped" " into error conditions.\n", LOCATION_FILE (loc), LOCATION_LINE (loc)); } }
static void init_copy_prop (void) { basic_block bb; copy_of = XCNEWVEC (prop_value_t, num_ssa_names); FOR_EACH_BB (bb) { gimple_stmt_iterator si; int depth = bb_loop_depth (bb); for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple stmt = gsi_stmt (si); ssa_op_iter iter; tree def; /* The only statements that we care about are those that may generate useful copies. We also need to mark conditional jumps so that their outgoing edges are added to the work lists of the propagator. Avoid copy propagation from an inner into an outer loop. Otherwise, this may move loop variant variables outside of their loops and prevent coalescing opportunities. If the value was loop invariant, it will be hoisted by LICM and exposed for copy propagation. ??? This doesn't make sense. */ if (stmt_ends_bb_p (stmt)) prop_set_simulate_again (stmt, true); else if (stmt_may_generate_copy (stmt) /* Since we are iterating over the statements in BB, not the phi nodes, STMT will always be an assignment. */ && loop_depth_of_name (gimple_assign_rhs1 (stmt)) <= depth) prop_set_simulate_again (stmt, true); else prop_set_simulate_again (stmt, false); /* Mark all the outputs of this statement as not being the copy of anything. */ FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS) if (!prop_simulate_again_p (stmt)) set_copy_of_val (def, def); } for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) { gimple phi = gsi_stmt (si); tree def; def = gimple_phi_result (phi); if (virtual_operand_p (def)) prop_set_simulate_again (phi, false); else prop_set_simulate_again (phi, true); if (!prop_simulate_again_p (phi)) set_copy_of_val (def, def); } } }
static unsigned int rename_ssa_copies (void) { var_map map; basic_block bb; gimple_stmt_iterator gsi; tree var, part_var; gimple stmt, phi; unsigned x; FILE *debug; bool updated = false; memset (&stats, 0, sizeof (stats)); if (dump_file && (dump_flags & TDF_DETAILS)) debug = dump_file; else debug = NULL; map = init_var_map (num_ssa_names); FOR_EACH_BB (bb) { /* Scan for real copies. */ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { stmt = gsi_stmt (gsi); if (gimple_assign_ssa_name_copy_p (stmt)) { tree lhs = gimple_assign_lhs (stmt); tree rhs = gimple_assign_rhs1 (stmt); updated |= copy_rename_partition_coalesce (map, lhs, rhs, debug); } } } FOR_EACH_BB (bb) { /* Treat PHI nodes as copies between the result and each argument. */ for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { size_t i; tree res; phi = gsi_stmt (gsi); res = gimple_phi_result (phi); /* Do not process virtual SSA_NAMES. */ if (virtual_operand_p (res)) continue; /* Make sure to only use the same partition for an argument as the result but never the other way around. */ if (SSA_NAME_VAR (res) && !DECL_IGNORED_P (SSA_NAME_VAR (res))) for (i = 0; i < gimple_phi_num_args (phi); i++) { tree arg = PHI_ARG_DEF (phi, i); if (TREE_CODE (arg) == SSA_NAME) updated |= copy_rename_partition_coalesce (map, res, arg, debug); } /* Else if all arguments are in the same partition try to merge it with the result. */ else { int all_p_same = -1; int p = -1; for (i = 0; i < gimple_phi_num_args (phi); i++) { tree arg = PHI_ARG_DEF (phi, i); if (TREE_CODE (arg) != SSA_NAME) { all_p_same = 0; break; } else if (all_p_same == -1) { p = partition_find (map->var_partition, SSA_NAME_VERSION (arg)); all_p_same = 1; } else if (all_p_same == 1 && p != partition_find (map->var_partition, SSA_NAME_VERSION (arg))) { all_p_same = 0; break; } } if (all_p_same == 1) updated |= copy_rename_partition_coalesce (map, res, PHI_ARG_DEF (phi, 0), debug); } } } if (debug) dump_var_map (debug, map); /* Now one more pass to make all elements of a partition share the same root variable. */ for (x = 1; x < num_ssa_names; x++) { part_var = partition_to_var (map, x); if (!part_var) continue; var = ssa_name (x); if (SSA_NAME_VAR (var) == SSA_NAME_VAR (part_var)) continue; if (debug) { fprintf (debug, "Coalesced "); print_generic_expr (debug, var, TDF_SLIM); fprintf (debug, " to "); print_generic_expr (debug, part_var, TDF_SLIM); fprintf (debug, "\n"); } stats.coalesced++; replace_ssa_name_symbol (var, SSA_NAME_VAR (part_var)); } statistics_counter_event (cfun, "copies coalesced", stats.coalesced); delete_var_map (map); return updated ? TODO_remove_unused_locals : 0; }
static bool factor_out_conditional_conversion (edge e0, edge e1, gphi *phi, tree arg0, tree arg1) { gimple arg0_def_stmt = NULL, arg1_def_stmt = NULL, new_stmt; tree new_arg0 = NULL_TREE, new_arg1 = NULL_TREE; tree temp, result; gphi *newphi; gimple_stmt_iterator gsi, gsi_for_def; source_location locus = gimple_location (phi); enum tree_code convert_code; /* Handle only PHI statements with two arguments. TODO: If all other arguments to PHI are INTEGER_CST or if their defining statement have the same unary operation, we can handle more than two arguments too. */ if (gimple_phi_num_args (phi) != 2) return false; /* First canonicalize to simplify tests. */ if (TREE_CODE (arg0) != SSA_NAME) { std::swap (arg0, arg1); std::swap (e0, e1); } if (TREE_CODE (arg0) != SSA_NAME || (TREE_CODE (arg1) != SSA_NAME && TREE_CODE (arg1) != INTEGER_CST)) return false; /* Check if arg0 is an SSA_NAME and the stmt which defines arg0 is a conversion. */ arg0_def_stmt = SSA_NAME_DEF_STMT (arg0); if (!is_gimple_assign (arg0_def_stmt) || !gimple_assign_cast_p (arg0_def_stmt)) return false; /* Use the RHS as new_arg0. */ convert_code = gimple_assign_rhs_code (arg0_def_stmt); new_arg0 = gimple_assign_rhs1 (arg0_def_stmt); if (convert_code == VIEW_CONVERT_EXPR) new_arg0 = TREE_OPERAND (new_arg0, 0); if (TREE_CODE (arg1) == SSA_NAME) { /* Check if arg1 is an SSA_NAME and the stmt which defines arg1 is a conversion. */ arg1_def_stmt = SSA_NAME_DEF_STMT (arg1); if (!is_gimple_assign (arg1_def_stmt) || gimple_assign_rhs_code (arg1_def_stmt) != convert_code) return false; /* Use the RHS as new_arg1. */ new_arg1 = gimple_assign_rhs1 (arg1_def_stmt); if (convert_code == VIEW_CONVERT_EXPR) new_arg1 = TREE_OPERAND (new_arg1, 0); } else { /* If arg1 is an INTEGER_CST, fold it to new type. */ if (INTEGRAL_TYPE_P (TREE_TYPE (new_arg0)) && int_fits_type_p (arg1, TREE_TYPE (new_arg0))) { if (gimple_assign_cast_p (arg0_def_stmt)) new_arg1 = fold_convert (TREE_TYPE (new_arg0), arg1); else return false; } else return false; } /* If arg0/arg1 have > 1 use, then this transformation actually increases the number of expressions evaluated at runtime. */ if (!has_single_use (arg0) || (arg1_def_stmt && !has_single_use (arg1))) return false; /* If types of new_arg0 and new_arg1 are different bailout. */ if (!types_compatible_p (TREE_TYPE (new_arg0), TREE_TYPE (new_arg1))) return false; /* Create a new PHI stmt. */ result = PHI_RESULT (phi); temp = make_ssa_name (TREE_TYPE (new_arg0), NULL); newphi = create_phi_node (temp, gimple_bb (phi)); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "PHI "); print_generic_expr (dump_file, gimple_phi_result (phi), 0); fprintf (dump_file, " changed to factor conversion out from COND_EXPR.\n"); fprintf (dump_file, "New stmt with CAST that defines "); print_generic_expr (dump_file, result, 0); fprintf (dump_file, ".\n"); } /* Remove the old cast(s) that has single use. */ gsi_for_def = gsi_for_stmt (arg0_def_stmt); gsi_remove (&gsi_for_def, true); if (arg1_def_stmt) { gsi_for_def = gsi_for_s