static bool forward_propagate_addr_expr (tree name, tree rhs) { int stmt_loop_depth = gimple_bb (SSA_NAME_DEF_STMT (name))->loop_depth; imm_use_iterator iter; gimple use_stmt; bool all = true; bool single_use_p = has_single_use (name); FOR_EACH_IMM_USE_STMT (use_stmt, iter, name) { bool result; tree use_rhs; /* If the use is not in a simple assignment statement, then there is nothing we can do. */ if (gimple_code (use_stmt) != GIMPLE_ASSIGN) { if (!is_gimple_debug (use_stmt)) all = false; continue; } /* 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 (gimple_bb (use_stmt)->loop_depth > stmt_loop_depth) { all = false; continue; } { gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); result = forward_propagate_addr_expr_1 (name, rhs, &gsi, single_use_p); /* If the use has moved to a different statement adjust the update machinery for the old statement too. */ if (use_stmt != gsi_stmt (gsi)) { update_stmt (use_stmt); use_stmt = gsi_stmt (gsi); } update_stmt (use_stmt); } all &= result; /* Remove intermediate now unused copy and conversion chains. */ use_rhs = gimple_assign_rhs1 (use_stmt); if (result && TREE_CODE (gimple_assign_lhs (use_stmt)) == SSA_NAME && TREE_CODE (use_rhs) == SSA_NAME && has_zero_uses (gimple_assign_lhs (use_stmt))) { gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); release_defs (use_stmt); gsi_remove (&gsi, true); } }
void dump_immediate_uses_for (FILE *file, tree var) { imm_use_iterator iter; use_operand_p use_p; gcc_assert (var && TREE_CODE (var) == SSA_NAME); print_generic_expr (file, var, TDF_SLIM); fprintf (file, " : -->"); if (has_zero_uses (var)) fprintf (file, " no uses.\n"); else if (has_single_use (var)) fprintf (file, " single use.\n"); else fprintf (file, "%d uses.\n", num_imm_uses (var)); FOR_EACH_IMM_USE_FAST (use_p, iter, var) { if (use_p->loc.stmt == NULL && use_p->use == NULL) fprintf (file, "***end of stmt iterator marker***\n"); else if (!is_gimple_reg (USE_FROM_PTR (use_p))) print_gimple_stmt (file, USE_STMT (use_p), 0, TDF_VOPS|TDF_MEMSYMS); else print_gimple_stmt (file, USE_STMT (use_p), 0, TDF_SLIM); } fprintf(file, "\n"); }
/* Check whether G is a potential conditional compare candidate. */ static bool ccmp_candidate_p (gimple *g) { tree rhs = gimple_assign_rhs_to_tree (g); tree lhs, op0, op1; gimple *gs0, *gs1; enum tree_code tcode, tcode0, tcode1; tcode = TREE_CODE (rhs); if (tcode != BIT_AND_EXPR && tcode != BIT_IOR_EXPR) return false; lhs = gimple_assign_lhs (g); op0 = TREE_OPERAND (rhs, 0); op1 = TREE_OPERAND (rhs, 1); if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME) || !has_single_use (lhs)) return false; gs0 = get_gimple_for_ssa_name (op0); gs1 = get_gimple_for_ssa_name (op1); if (!gs0 || !gs1 || !is_gimple_assign (gs0) || !is_gimple_assign (gs1) /* g, gs0 and gs1 must be in the same basic block, since current stage is out-of-ssa. We can not guarantee the correctness when forwording the gs0 and gs1 into g whithout DATAFLOW analysis. */ || gimple_bb (gs0) != gimple_bb (gs1) || gimple_bb (gs0) != gimple_bb (g)) return false; if (!(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0))) || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0)))) || !(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1))) || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1))))) return false; tcode0 = gimple_assign_rhs_code (gs0); tcode1 = gimple_assign_rhs_code (gs1); if (TREE_CODE_CLASS (tcode0) == tcc_comparison && TREE_CODE_CLASS (tcode1) == tcc_comparison) return true; if (TREE_CODE_CLASS (tcode0) == tcc_comparison && ccmp_candidate_p (gs1)) return true; else if (TREE_CODE_CLASS (tcode1) == tcc_comparison && ccmp_candidate_p (gs0)) return true; /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since there is no way to set the CC flag. */ return false; }
static gimple get_prop_source_stmt (tree name, bool single_use_only, bool *single_use_p) { bool single_use = true; do { gimple def_stmt = SSA_NAME_DEF_STMT (name); if (!has_single_use (name)) { single_use = false; if (single_use_only) return NULL; } /* If name is defined by a PHI node or is the default def, bail out. */ if (gimple_code (def_stmt) != GIMPLE_ASSIGN) return NULL; /* If name is not a simple copy destination, we found it. */ if (!gimple_assign_copy_p (def_stmt) || TREE_CODE (gimple_assign_rhs1 (def_stmt)) != SSA_NAME) { tree rhs; if (!single_use_only && single_use_p) *single_use_p = single_use; /* We can look through pointer conversions in the search for a useful stmt for the comparison folding. */ rhs = gimple_assign_rhs1 (def_stmt); if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)) && TREE_CODE (rhs) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (gimple_assign_lhs (def_stmt))) && POINTER_TYPE_P (TREE_TYPE (rhs))) name = rhs; else return def_stmt; } else { /* Continue searching the def of the copy source name. */ name = gimple_assign_rhs1 (def_stmt); } } while (1); }
static tree get_name_for_bit_test (tree candidate) { /* Skip single-use names in favor of using the name from a non-widening conversion definition. */ if (TREE_CODE (candidate) == SSA_NAME && has_single_use (candidate)) { gimple def_stmt = SSA_NAME_DEF_STMT (candidate); if (is_gimple_assign (def_stmt) && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt))) { if (TYPE_PRECISION (TREE_TYPE (candidate)) <= TYPE_PRECISION (TREE_TYPE (gimple_assign_rhs1 (def_stmt)))) return gimple_assign_rhs1 (def_stmt); } } return candidate; }
static inline bool single_use (tree t) { return TREE_CODE (t) != SSA_NAME || has_zero_uses (t) || has_single_use (t); }
static tree rewrite_bittest (block_stmt_iterator *bsi) { tree stmt, lhs, rhs, var, name, use_stmt, stmt1, stmt2, t; use_operand_p use; stmt = bsi_stmt (*bsi); lhs = GENERIC_TREE_OPERAND (stmt, 0); rhs = GENERIC_TREE_OPERAND (stmt, 1); /* Verify that the single use of lhs is a comparison against zero. */ if (TREE_CODE (lhs) != SSA_NAME || !single_imm_use (lhs, &use, &use_stmt) || TREE_CODE (use_stmt) != COND_EXPR) return stmt; t = COND_EXPR_COND (use_stmt); if (TREE_OPERAND (t, 0) != lhs || (TREE_CODE (t) != NE_EXPR && TREE_CODE (t) != EQ_EXPR) || !integer_zerop (TREE_OPERAND (t, 1))) return stmt; /* Get at the operands of the shift. The rhs is TMP1 & 1. */ stmt1 = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 0)); if (TREE_CODE (stmt1) != GIMPLE_MODIFY_STMT) return stmt; /* There is a conversion in between possibly inserted by fold. */ t = GIMPLE_STMT_OPERAND (stmt1, 1); if (TREE_CODE (t) == NOP_EXPR || TREE_CODE (t) == CONVERT_EXPR) { t = TREE_OPERAND (t, 0); if (TREE_CODE (t) != SSA_NAME || !has_single_use (t)) return stmt; stmt1 = SSA_NAME_DEF_STMT (t); if (TREE_CODE (stmt1) != GIMPLE_MODIFY_STMT) return stmt; t = GIMPLE_STMT_OPERAND (stmt1, 1); } /* Verify that B is loop invariant but A is not. Verify that with all the stmt walking we are still in the same loop. */ if (TREE_CODE (t) == RSHIFT_EXPR && loop_containing_stmt (stmt1) == loop_containing_stmt (stmt) && outermost_invariant_loop_expr (TREE_OPERAND (t, 1), loop_containing_stmt (stmt1)) != NULL && outermost_invariant_loop_expr (TREE_OPERAND (t, 0), loop_containing_stmt (stmt1)) == NULL) { tree a = TREE_OPERAND (t, 0); tree b = TREE_OPERAND (t, 1); /* 1 << B */ var = create_tmp_var (TREE_TYPE (a), "shifttmp"); add_referenced_var (var); t = fold_build2 (LSHIFT_EXPR, TREE_TYPE (a), build_int_cst (TREE_TYPE (a), 1), b); stmt1 = build_gimple_modify_stmt (var, t); name = make_ssa_name (var, stmt1); GIMPLE_STMT_OPERAND (stmt1, 0) = name; /* A & (1 << B) */ t = fold_build2 (BIT_AND_EXPR, TREE_TYPE (a), a, name); stmt2 = build_gimple_modify_stmt (var, t); name = make_ssa_name (var, stmt2); GIMPLE_STMT_OPERAND (stmt2, 0) = name; /* Replace the SSA_NAME we compare against zero. Adjust the type of zero accordingly. */ SET_USE (use, name); TREE_OPERAND (COND_EXPR_COND (use_stmt), 1) = build_int_cst_type (TREE_TYPE (name), 0); bsi_insert_before (bsi, stmt1, BSI_SAME_STMT); bsi_replace (bsi, stmt2, true); return stmt1; } return stmt; }
static tree forward_propagate_into_cond_1 (tree cond, tree *test_var_p) { tree new_cond = NULL_TREE; enum tree_code cond_code = TREE_CODE (cond); tree test_var = NULL_TREE; tree def; tree def_rhs; /* If the condition is not a lone variable or an equality test of an SSA_NAME against an integral constant, then we do not have an optimizable case. Note these conditions also ensure the COND_EXPR has no virtual operands or other side effects. */ if (cond_code != SSA_NAME && !((cond_code == EQ_EXPR || cond_code == NE_EXPR) && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME && CONSTANT_CLASS_P (TREE_OPERAND (cond, 1)) && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (cond, 1))))) return NULL_TREE; /* Extract the single variable used in the test into TEST_VAR. */ if (cond_code == SSA_NAME) test_var = cond; else test_var = TREE_OPERAND (cond, 0); /* Now get the defining statement for TEST_VAR. Skip this case if it's not defined by some MODIFY_EXPR. */ def = SSA_NAME_DEF_STMT (test_var); if (TREE_CODE (def) != MODIFY_EXPR) return NULL_TREE; def_rhs = TREE_OPERAND (def, 1); /* If TEST_VAR is set by adding or subtracting a constant from an SSA_NAME, then it is interesting to us as we can adjust the constant in the conditional and thus eliminate the arithmetic operation. */ if (TREE_CODE (def_rhs) == PLUS_EXPR || TREE_CODE (def_rhs) == MINUS_EXPR) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); /* The first operand must be an SSA_NAME and the second operand must be a constant. */ if (TREE_CODE (op0) != SSA_NAME || !CONSTANT_CLASS_P (op1) || !INTEGRAL_TYPE_P (TREE_TYPE (op1))) return NULL_TREE; /* Don't propagate if the first operand occurs in an abnormal PHI. */ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) return NULL_TREE; if (has_single_use (test_var)) { enum tree_code new_code; tree t; /* If the variable was defined via X + C, then we must subtract C from the constant in the conditional. Otherwise we add C to the constant in the conditional. The result must fold into a valid gimple operand to be optimizable. */ new_code = (TREE_CODE (def_rhs) == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR); t = int_const_binop (new_code, TREE_OPERAND (cond, 1), op1, 0); if (!is_gimple_val (t)) return NULL_TREE; new_cond = build (cond_code, boolean_type_node, op0, t); } } /* These cases require comparisons of a naked SSA_NAME or comparison of an SSA_NAME against zero or one. */ else if (TREE_CODE (cond) == SSA_NAME || integer_zerop (TREE_OPERAND (cond, 1)) || integer_onep (TREE_OPERAND (cond, 1))) { /* If TEST_VAR is set from a relational operation between two SSA_NAMEs or a combination of an SSA_NAME and a constant, then it is interesting. */ if (COMPARISON_CLASS_P (def_rhs)) { tree op0 = TREE_OPERAND (def_rhs, 0); tree op1 = TREE_OPERAND (def_rhs, 1); /* Both operands of DEF_RHS must be SSA_NAMEs or constants. */ if ((TREE_CODE (op0) != SSA_NAME && !is_gimple_min_invariant (op0)) || (TREE_CODE (op1) != SSA_NAME && !is_gimple_min_invariant (op1))) return NULL_TREE; /* Don't propagate if the first operand occurs in an abnormal PHI. */ if (TREE_CODE (op0) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) return NULL_TREE; /* Don't propagate if the second operand occurs in an abnormal PHI. */ if (TREE_CODE (op1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op1)) return NULL_TREE; if (has_single_use (test_var)) { /* TEST_VAR was set from a relational operator. */ new_cond = build (TREE_CODE (def_rhs), boolean_type_node, op0, op1); /* Invert the conditional if necessary. */ if ((cond_code == EQ_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == NE_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) { new_cond = invert_truthvalue (new_cond); /* If we did not get a simple relational expression or bare SSA_NAME, then we can not optimize this case. */ if (!COMPARISON_CLASS_P (new_cond) && TREE_CODE (new_cond) != SSA_NAME) new_cond = NULL_TREE; } } } /* If TEST_VAR is set from a TRUTH_NOT_EXPR, then it is interesting. */ else if (TREE_CODE (def_rhs) == TRUTH_NOT_EXPR) { enum tree_code new_code; def_rhs = TREE_OPERAND (def_rhs, 0); /* DEF_RHS must be an SSA_NAME or constant. */ if (TREE_CODE (def_rhs) != SSA_NAME && !is_gimple_min_invariant (def_rhs)) return NULL_TREE; /* Don't propagate if the operand occurs in an abnormal PHI. */ if (TREE_CODE (def_rhs) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def_rhs)) return NULL_TREE; if (cond_code == SSA_NAME || (cond_code == NE_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == EQ_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) new_code = EQ_EXPR; else new_code = NE_EXPR; new_cond = build2 (new_code, boolean_type_node, def_rhs, fold_convert (TREE_TYPE (def_rhs), integer_zero_node)); } /* If TEST_VAR was set from a cast of an integer type to a boolean type or a cast of a boolean to an integral, then it is interesting. */ else if (TREE_CODE (def_rhs) == NOP_EXPR || TREE_CODE (def_rhs) == CONVERT_EXPR) { tree outer_type; tree inner_type; outer_type = TREE_TYPE (def_rhs); inner_type = TREE_TYPE (TREE_OPERAND (def_rhs, 0)); if ((TREE_CODE (outer_type) == BOOLEAN_TYPE && INTEGRAL_TYPE_P (inner_type)) || (TREE_CODE (inner_type) == BOOLEAN_TYPE && INTEGRAL_TYPE_P (outer_type))) ; else if (INTEGRAL_TYPE_P (outer_type) && INTEGRAL_TYPE_P (inner_type) && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME && ssa_name_defined_by_comparison_p (TREE_OPERAND (def_rhs, 0))) ; else return NULL_TREE; /* Don't propagate if the operand occurs in an abnormal PHI. */ if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (def_rhs, 0))) return NULL_TREE; if (has_single_use (test_var)) { enum tree_code new_code; tree new_arg; if (cond_code == SSA_NAME || (cond_code == NE_EXPR && integer_zerop (TREE_OPERAND (cond, 1))) || (cond_code == EQ_EXPR && integer_onep (TREE_OPERAND (cond, 1)))) new_code = NE_EXPR; else new_code = EQ_EXPR; new_arg = TREE_OPERAND (def_rhs, 0); new_cond = build2 (new_code, boolean_type_node, new_arg, fold_convert (TREE_TYPE (new_arg), integer_zero_node)); } } } *test_var_p = test_var; return new_cond; }
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