tree chrec_fold_multiply (tree type, tree op0, tree op1) { if (automatically_generated_chrec_p (op0) || automatically_generated_chrec_p (op1)) return chrec_fold_automatically_generated_operands (op0, op1); switch (TREE_CODE (op0)) { case POLYNOMIAL_CHREC: gcc_checking_assert (!chrec_contains_symbols_defined_in_loop (op0, CHREC_VARIABLE (op0))); switch (TREE_CODE (op1)) { case POLYNOMIAL_CHREC: gcc_checking_assert (!chrec_contains_symbols_defined_in_loop (op1, CHREC_VARIABLE (op1))); return chrec_fold_multiply_poly_poly (type, op0, op1); CASE_CONVERT: if (tree_contains_chrecs (op1, NULL)) return chrec_dont_know; default: if (integer_onep (op1)) return op0; if (integer_zerop (op1)) return build_int_cst (type, 0); return build_polynomial_chrec (CHREC_VARIABLE (op0), chrec_fold_multiply (type, CHREC_LEFT (op0), op1), chrec_fold_multiply (type, CHREC_RIGHT (op0), op1)); } CASE_CONVERT: if (tree_contains_chrecs (op0, NULL)) return chrec_dont_know; default: if (integer_onep (op0)) return op1; if (integer_zerop (op0)) return build_int_cst (type, 0); switch (TREE_CODE (op1)) { case POLYNOMIAL_CHREC: gcc_checking_assert (!chrec_contains_symbols_defined_in_loop (op1, CHREC_VARIABLE (op1))); return build_polynomial_chrec (CHREC_VARIABLE (op1), chrec_fold_multiply (type, CHREC_LEFT (op1), op0), chrec_fold_multiply (type, CHREC_RIGHT (op1), op0)); CASE_CONVERT: if (tree_contains_chrecs (op1, NULL)) return chrec_dont_know; default: if (integer_onep (op1)) return op0; if (integer_zerop (op1)) return build_int_cst (type, 0); return fold_build2 (MULT_EXPR, type, op0, op1); } } }
tree chrec_apply (unsigned var, tree chrec, tree x) { tree type = chrec_type (chrec); tree res = chrec_dont_know; if (automatically_generated_chrec_p (chrec) || automatically_generated_chrec_p (x) /* When the symbols are defined in an outer loop, it is possible to symbolically compute the apply, since the symbols are constants with respect to the varying loop. */ || chrec_contains_symbols_defined_in_loop (chrec, var)) return chrec_dont_know; if (dump_file && (dump_flags & TDF_SCEV)) fprintf (dump_file, "(chrec_apply \n"); if (TREE_CODE (x) == INTEGER_CST && SCALAR_FLOAT_TYPE_P (type)) x = build_real_from_int_cst (type, x); switch (TREE_CODE (chrec)) { case POLYNOMIAL_CHREC: if (evolution_function_is_affine_p (chrec)) { if (CHREC_VARIABLE (chrec) != var) return build_polynomial_chrec (CHREC_VARIABLE (chrec), chrec_apply (var, CHREC_LEFT (chrec), x), chrec_apply (var, CHREC_RIGHT (chrec), x)); /* "{a, +, b} (x)" -> "a + b*x". */ x = chrec_convert_rhs (type, x, NULL); res = chrec_fold_multiply (TREE_TYPE (x), CHREC_RIGHT (chrec), x); res = chrec_fold_plus (type, CHREC_LEFT (chrec), res); } else if (TREE_CODE (x) == INTEGER_CST && tree_int_cst_sgn (x) == 1) /* testsuite/.../ssa-chrec-38.c. */ res = chrec_evaluate (var, chrec, x, 0); else res = chrec_dont_know; break; CASE_CONVERT: res = chrec_convert (TREE_TYPE (chrec), chrec_apply (var, TREE_OPERAND (chrec, 0), x), NULL); break; default: res = chrec; break; } if (dump_file && (dump_flags & TDF_SCEV)) { fprintf (dump_file, " (varying_loop = %d\n", var); fprintf (dump_file, ")\n (chrec = "); print_generic_expr (dump_file, chrec, 0); fprintf (dump_file, ")\n (x = "); print_generic_expr (dump_file, x, 0); fprintf (dump_file, ")\n (res = "); print_generic_expr (dump_file, res, 0); fprintf (dump_file, "))\n"); } return res; }
static tree chrec_fold_plus_1 (enum tree_code code, tree type, tree op0, tree op1) { if (automatically_generated_chrec_p (op0) || automatically_generated_chrec_p (op1)) return chrec_fold_automatically_generated_operands (op0, op1); switch (TREE_CODE (op0)) { case POLYNOMIAL_CHREC: gcc_checking_assert (!chrec_contains_symbols_defined_in_loop (op0, CHREC_VARIABLE (op0))); switch (TREE_CODE (op1)) { case POLYNOMIAL_CHREC: gcc_checking_assert (!chrec_contains_symbols_defined_in_loop (op1, CHREC_VARIABLE (op1))); return chrec_fold_plus_poly_poly (code, type, op0, op1); CASE_CONVERT: if (tree_contains_chrecs (op1, NULL)) return chrec_dont_know; default: if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (op0), chrec_fold_plus (type, CHREC_LEFT (op0), op1), CHREC_RIGHT (op0)); else return build_polynomial_chrec (CHREC_VARIABLE (op0), chrec_fold_minus (type, CHREC_LEFT (op0), op1), CHREC_RIGHT (op0)); } CASE_CONVERT: if (tree_contains_chrecs (op0, NULL)) return chrec_dont_know; default: switch (TREE_CODE (op1)) { case POLYNOMIAL_CHREC: gcc_checking_assert (!chrec_contains_symbols_defined_in_loop (op1, CHREC_VARIABLE (op1))); if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (op1), chrec_fold_plus (type, op0, CHREC_LEFT (op1)), CHREC_RIGHT (op1)); else return build_polynomial_chrec (CHREC_VARIABLE (op1), chrec_fold_minus (type, op0, CHREC_LEFT (op1)), chrec_fold_multiply (type, CHREC_RIGHT (op1), SCALAR_FLOAT_TYPE_P (type) ? build_real (type, dconstm1) : build_int_cst_type (type, -1))); CASE_CONVERT: if (tree_contains_chrecs (op1, NULL)) return chrec_dont_know; default: { int size = 0; if ((tree_contains_chrecs (op0, &size) || tree_contains_chrecs (op1, &size)) && size < PARAM_VALUE (PARAM_SCEV_MAX_EXPR_SIZE)) return build2 (code, type, op0, op1); else if (size < PARAM_VALUE (PARAM_SCEV_MAX_EXPR_SIZE)) { if (code == POINTER_PLUS_EXPR) return fold_build_pointer_plus (fold_convert (type, op0), op1); else return fold_build2 (code, type, fold_convert (type, op0), fold_convert (type, op1)); } else return chrec_dont_know; } } } }
static tree chrec_convert_1 (tree type, tree chrec, gimple *at_stmt, bool use_overflow_semantics) { tree ct, res; tree base, step; struct loop *loop; if (automatically_generated_chrec_p (chrec)) return chrec; ct = chrec_type (chrec); if (useless_type_conversion_p (type, ct)) return chrec; if (!evolution_function_is_affine_p (chrec)) goto keep_cast; loop = get_chrec_loop (chrec); base = CHREC_LEFT (chrec); step = CHREC_RIGHT (chrec); if (convert_affine_scev (loop, type, &base, &step, at_stmt, use_overflow_semantics)) return build_polynomial_chrec (loop->num, base, step); /* If we cannot propagate the cast inside the chrec, just keep the cast. */ keep_cast: /* Fold will not canonicalize (long)(i - 1) to (long)i - 1 because that may be more expensive. We do want to perform this optimization here though for canonicalization reasons. */ if (use_overflow_semantics && (TREE_CODE (chrec) == PLUS_EXPR || TREE_CODE (chrec) == MINUS_EXPR) && TREE_CODE (type) == INTEGER_TYPE && TREE_CODE (ct) == INTEGER_TYPE && TYPE_PRECISION (type) > TYPE_PRECISION (ct) && TYPE_OVERFLOW_UNDEFINED (ct)) res = fold_build2 (TREE_CODE (chrec), type, fold_convert (type, TREE_OPERAND (chrec, 0)), fold_convert (type, TREE_OPERAND (chrec, 1))); /* Similar perform the trick that (signed char)((int)x + 2) can be narrowed to (signed char)((unsigned char)x + 2). */ else if (use_overflow_semantics && TREE_CODE (chrec) == POLYNOMIAL_CHREC && TREE_CODE (ct) == INTEGER_TYPE && TREE_CODE (type) == INTEGER_TYPE && TYPE_OVERFLOW_UNDEFINED (type) && TYPE_PRECISION (type) < TYPE_PRECISION (ct)) { tree utype = unsigned_type_for (type); res = build_polynomial_chrec (CHREC_VARIABLE (chrec), fold_convert (utype, CHREC_LEFT (chrec)), fold_convert (utype, CHREC_RIGHT (chrec))); res = chrec_convert_1 (type, res, at_stmt, use_overflow_semantics); } else res = fold_convert (type, chrec); /* Don't propagate overflows. */ if (CONSTANT_CLASS_P (res)) TREE_OVERFLOW (res) = 0; /* But reject constants that don't fit in their type after conversion. This can happen if TYPE_MIN_VALUE or TYPE_MAX_VALUE are not the natural values associated with TYPE_PRECISION and TYPE_UNSIGNED, and can cause problems later when computing niters of loops. Note that we don't do the check before converting because we don't want to reject conversions of negative chrecs to unsigned types. */ if (TREE_CODE (res) == INTEGER_CST && TREE_CODE (type) == INTEGER_TYPE && !int_fits_type_p (res, type)) res = chrec_dont_know; return res; }
static inline tree chrec_fold_plus_poly_poly (enum tree_code code, tree type, tree poly0, tree poly1) { tree left, right; struct loop *loop0 = get_chrec_loop (poly0); struct loop *loop1 = get_chrec_loop (poly1); tree rtype = code == POINTER_PLUS_EXPR ? chrec_type (poly1) : type; gcc_assert (poly0); gcc_assert (poly1); gcc_assert (TREE_CODE (poly0) == POLYNOMIAL_CHREC); gcc_assert (TREE_CODE (poly1) == POLYNOMIAL_CHREC); if (POINTER_TYPE_P (chrec_type (poly0))) gcc_assert (ptrofftype_p (chrec_type (poly1))); else gcc_assert (chrec_type (poly0) == chrec_type (poly1)); gcc_assert (type == chrec_type (poly0)); /* {a, +, b}_1 + {c, +, d}_2 -> {{a, +, b}_1 + c, +, d}_2, {a, +, b}_2 + {c, +, d}_1 -> {{c, +, d}_1 + a, +, b}_2, {a, +, b}_x + {c, +, d}_x -> {a+c, +, b+d}_x. */ if (flow_loop_nested_p (loop0, loop1)) { if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (poly1), chrec_fold_plus (type, poly0, CHREC_LEFT (poly1)), CHREC_RIGHT (poly1)); else return build_polynomial_chrec (CHREC_VARIABLE (poly1), chrec_fold_minus (type, poly0, CHREC_LEFT (poly1)), chrec_fold_multiply (type, CHREC_RIGHT (poly1), SCALAR_FLOAT_TYPE_P (type) ? build_real (type, dconstm1) : build_int_cst_type (type, -1))); } if (flow_loop_nested_p (loop1, loop0)) { if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (poly0), chrec_fold_plus (type, CHREC_LEFT (poly0), poly1), CHREC_RIGHT (poly0)); else return build_polynomial_chrec (CHREC_VARIABLE (poly0), chrec_fold_minus (type, CHREC_LEFT (poly0), poly1), CHREC_RIGHT (poly0)); } /* This function should never be called for chrecs of loops that do not belong to the same loop nest. */ gcc_assert (loop0 == loop1); if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR) { left = chrec_fold_plus (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1)); right = chrec_fold_plus (rtype, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1)); } else { left = chrec_fold_minus (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1)); right = chrec_fold_minus (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1)); } if (chrec_zerop (right)) return left; else return build_polynomial_chrec (CHREC_VARIABLE (poly0), left, right); }
static inline tree chrec_fold_plus_poly_poly (enum tree_code code, tree type, tree poly0, tree poly1) { tree left, right; gcc_assert (poly0); gcc_assert (poly1); gcc_assert (TREE_CODE (poly0) == POLYNOMIAL_CHREC); gcc_assert (TREE_CODE (poly1) == POLYNOMIAL_CHREC); gcc_assert (chrec_type (poly0) == chrec_type (poly1)); gcc_assert (type == chrec_type (poly0)); /* {a, +, b}_1 + {c, +, d}_2 -> {{a, +, b}_1 + c, +, d}_2, {a, +, b}_2 + {c, +, d}_1 -> {{c, +, d}_1 + a, +, b}_2, {a, +, b}_x + {c, +, d}_x -> {a+c, +, b+d}_x. */ if (CHREC_VARIABLE (poly0) < CHREC_VARIABLE (poly1)) { if (code == PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (poly1), chrec_fold_plus (type, poly0, CHREC_LEFT (poly1)), CHREC_RIGHT (poly1)); else return build_polynomial_chrec (CHREC_VARIABLE (poly1), chrec_fold_minus (type, poly0, CHREC_LEFT (poly1)), chrec_fold_multiply (type, CHREC_RIGHT (poly1), SCALAR_FLOAT_TYPE_P (type) ? build_real (type, dconstm1) : build_int_cst_type (type, -1))); } if (CHREC_VARIABLE (poly0) > CHREC_VARIABLE (poly1)) { if (code == PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (poly0), chrec_fold_plus (type, CHREC_LEFT (poly0), poly1), CHREC_RIGHT (poly0)); else return build_polynomial_chrec (CHREC_VARIABLE (poly0), chrec_fold_minus (type, CHREC_LEFT (poly0), poly1), CHREC_RIGHT (poly0)); } if (code == PLUS_EXPR) { left = chrec_fold_plus (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1)); right = chrec_fold_plus (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1)); } else { left = chrec_fold_minus (type, CHREC_LEFT (poly0), CHREC_LEFT (poly1)); right = chrec_fold_minus (type, CHREC_RIGHT (poly0), CHREC_RIGHT (poly1)); } if (chrec_zerop (right)) return left; else return build_polynomial_chrec (CHREC_VARIABLE (poly0), left, right); }
static tree chrec_fold_plus_1 (enum tree_code code, tree type, tree op0, tree op1) { if (automatically_generated_chrec_p (op0) || automatically_generated_chrec_p (op1)) return chrec_fold_automatically_generated_operands (op0, op1); switch (TREE_CODE (op0)) { case POLYNOMIAL_CHREC: switch (TREE_CODE (op1)) { case POLYNOMIAL_CHREC: return chrec_fold_plus_poly_poly (code, type, op0, op1); default: if (code == PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (op0), chrec_fold_plus (type, CHREC_LEFT (op0), op1), CHREC_RIGHT (op0)); else return build_polynomial_chrec (CHREC_VARIABLE (op0), chrec_fold_minus (type, CHREC_LEFT (op0), op1), CHREC_RIGHT (op0)); } default: switch (TREE_CODE (op1)) { case POLYNOMIAL_CHREC: if (code == PLUS_EXPR) return build_polynomial_chrec (CHREC_VARIABLE (op1), chrec_fold_plus (type, op0, CHREC_LEFT (op1)), CHREC_RIGHT (op1)); else return build_polynomial_chrec (CHREC_VARIABLE (op1), chrec_fold_minus (type, op0, CHREC_LEFT (op1)), chrec_fold_multiply (type, CHREC_RIGHT (op1), build_int_cst_type (type, -1))); default: { int size = 0; if ((tree_contains_chrecs (op0, &size) || tree_contains_chrecs (op1, &size)) && size < PARAM_VALUE (PARAM_SCEV_MAX_EXPR_SIZE)) return build2 (code, type, op0, op1); else if (size < PARAM_VALUE (PARAM_SCEV_MAX_EXPR_SIZE)) return fold (build2 (code, type, op0, op1)); else return chrec_dont_know; } } } }