. */ if (TREE_CODE (addr) != ADDR_EXPR || DECL_P (TREE_OPERAND (addr, 0))) return fold_build2 (MEM_REF, type, addr, wide_int_to_tree (ptype, off)); } /* *(foo *)fooarrptr => (*fooarrptr)[0] */ if (TREE_CODE (TREE_TYPE (subtype)) == ARRAY_TYPE && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (subtype)))) == INTEGER_CST && useless_type_conversion_p (type, TREE_TYPE (TREE_TYPE (subtype)))) { tree type_domain; tree min_val = size_zero_node; tree osub = sub; sub = gimple_fold_indirect_ref (sub); if (! sub) sub = build1 (INDIRECT_REF, TREE_TYPE (subtype), osub); type_domain = TYPE_DOMAIN (TREE_TYPE (sub)); if (type_domain && TYPE_MIN_VALUE (type_domain)) min_val = TYPE_MIN_VALUE (type_domain); if (TREE_CODE (min_val) == INTEGER_CST) return build4 (ARRAY_REF, type, sub, min_val, NULL_TREE, NULL_TREE); } return NULL_TREE; } /* Return true if CODE is an operation that when operating on signed integer types involves undefined behavior on overflow and the operation can be expressed with unsigned arithmetic. */ bool arith_code_with_undefined_signed_overflow (tree_code code) { switch (code) { case PLUS_EXPR: case MINUS_EXPR: case MULT_EXPR: case NEGATE_EXPR: case POINTER_PLUS_EXPR: return true; default: return false; } } /* Rewrite STMT, an assignment with a signed integer or pointer arithmetic operation that can be transformed to unsigned arithmetic by converting its operand, carrying out the operation in the corresponding unsigned type and converting the result back to the original type. Returns a sequence of statements that replace STMT and also contain a modified form of STMT itself. */ gimple_seq rewrite_to_defined_overflow (gimple *stmt) { if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "rewriting stmt with undefined signed " "overflow "); print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); } tree lhs = gimple_assign_lhs (stmt); tree type = unsigned_type_for (TREE_TYPE (lhs)); gimple_seq stmts = NULL; for (unsigned i = 1; i < gimple_num_ops (stmt); ++i) { tree op = gimple_op (stmt, i); op = gimple_convert (&stmts, type, op); gimple_set_op (stmt, i, op); } gimple_assign_set_lhs (stmt, make_ssa_name (type, stmt)); if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) gimple_assign_set_rhs_code (stmt, PLUS_EXPR); gimple_seq_add_stmt (&stmts, stmt); gimple *cvt = gimple_build_assign (lhs, NOP_EXPR, gimple_assign_lhs (stmt)); gimple_seq_add_stmt (&stmts, cvt); return stmts; } /* The valueization hook we use for the gimple_build API simplification. This makes us match fold_buildN behavior by only combining with statements in the sequence(s) we are currently building. */ static tree gimple_build_valueize (tree op) { if (gimple_bb (SSA_NAME_DEF_STMT (op)) == NULL) return op; return NULL_TREE; } /* Build the expression CODE OP0 of type TYPE with location LOC, simplifying it first if possible. Returns the built expression value and appends statements possibly defining it to SEQ. */ tree gimple_build (gimple_seq *seq, location_t loc, enum tree_code code, tree type, tree op0) { tree res = gimple_simplify (code, type, op0, seq, gimple_build_valueize); if (!res) { res = create_tmp_reg_or_ssa_name (type); gimple *stmt; if (code == REALPART_EXPR || code == IMAGPART_EXPR || code == VIEW_CONVERT_EXPR) stmt = gimple_build_assign (res, code, build1 (code, type, op0)); else stmt = gimple_build_assign (res, code, op0); gimple_set_location (stmt, loc); gimple_seq_add_stmt_without_update (seq, stmt); } return res; } /* Build the expression OP0 CODE OP1 of type TYPE with location LOC, simplifying it first if possible. Returns the built expression value and appends statements possibly defining it to SEQ. */ tree gimple_build (gimple_seq *seq, location_t loc, enum tree_code code, tree type, tree op0, tree op1) { tree res = gimple_simplify (code, type, op0, op1, seq, gimple_build_valueize); if (!res) { res = create_tmp_reg_or_ssa_name (type); gimple *stmt = gimple_build_assign (res, code, op0, op1); gimple_set_location (stmt, loc); gimple_seq_add_stmt_without_update (seq, stmt); } return res; } /* Build the expression (CODE OP0 OP1 OP2) of type TYPE with location LOC, simplifying it first if possible. Returns the built expression value and appends statements possibly defining it to SEQ. */ tree gimple_build (gimple_seq *seq, location_t loc, enum tree_code code, tree type, tree op0, tree op1, tree op2) { tree res = gimple_simplify (code, type, op0, op1, op2, seq, gimple_build_valueize); if (!res) { res = create_tmp_reg_or_ssa_name (type); gimple *stmt; if (code == BIT_FIELD_REF) stmt = gimple_build_assign (res, code, build3 (code, type, op0, op1, op2)); else stmt = gimple_build_assign (res, code, op0, op1, op2); gimple_set_location (stmt, loc); gimple_seq_add_stmt_without_update (seq, stmt); } return res; } /* Build the call FN (ARG0) with a result of type TYPE (or no result if TYPE is void) with location LOC, simplifying it first if possible. Returns the built expression value (or NULL_TREE if TYPE is void) and appends statements possibly defining it to SEQ. */ tree gimple_build (gimple_seq *seq, location_t loc, enum built_in_function fn, tree type, tree arg0) { tree res = gimple_simplify (fn, type, arg0, seq, gimple_build_valueize); if (!res) { tree decl = builtin_decl_implicit (fn); gimple *stmt = gimple_build_call (decl, 1, arg0); if (!VOID_TYPE_P (type)) { res = create_tmp_reg_or_ssa_name (type); gimple_call_set_lhs (stmt, res); } gimple_set_location (stmt, loc); gimple_seq_add_stmt_without_update (seq, stmt); } return res; } /* Build the call FN (ARG0, ARG1) with a result of type TYPE (or no result if TYPE is void) with location LOC, simplifying it first if possible. Returns the built expression value (or NULL_TREE if TYPE is void) and appends statements possibly defining it to SEQ. */ tree gimple_build (gimple_seq *seq, location_t loc, enum built_in_function fn, tree type, tree arg0, tree arg1) { tree res = gimple_simplify (fn, type, arg0, arg1, seq, gimple_build_valueize); if (!res) { tree decl = builtin_decl_implicit (fn); gimple *stmt = gimple_build_call (decl, 2, arg0, arg1); if (!VOID_TYPE_P (type)) { res = create_tmp_reg_or_ssa_name (type); gimple_call_set_lhs (stmt, res); } gimple_set_location (stmt, loc); gimple_seq_add_stmt_without_update (seq, stmt); } return res; } /* Build the call FN (ARG0, ARG1, ARG2) with a result of type TYPE (or no result if TYPE is void) with location LOC, simplifying it first if possible. Returns the built expression value (or NULL_TREE if TYPE is void) and appends statements possibly defining it to SEQ. */ tree gimple_build (gimple_seq *seq, location_t loc, enum built_in_function fn, tree type, tree arg0, tree arg1, tree arg2) { tree res = gimple_simplify (fn, type, arg0, arg1, arg2, seq, gimple_build_valueize); if (!res) { tree decl = builtin_decl_implicit (fn); gimple *stmt = gimple_build_call (decl, 3, arg0, arg1, arg2); if (!VOID_TYPE_P (type)) { res = create_tmp_reg_or_ssa_name (type); gimple_call_set_lhs (stmt, res); } gimple_set_location (stmt, loc); gimple_seq_add_stmt_without_update (seq, stmt); } return res; } /* Build the conversion (TYPE) OP with a result of type TYPE with location LOC if such conversion is neccesary in GIMPLE, simplifying it first. Returns the built expression value and appends statements possibly defining it to SEQ. */ tree gimple_convert (gimple_seq *seq, location_t loc, tree type, tree op) { if (useless_type_conversion_p (type, TREE_TYPE (op))) return op; return gimple_build (seq, loc, NOP_EXPR, type, op); } /* Build the conversion (ptrofftype) OP with a result of a type compatible with ptrofftype with location LOC if such conversion is neccesary in GIMPLE, simplifying it first. Returns the built expression value and appends statements possibly defining it to SEQ. */ tree gimple_convert_to_ptrofftype (gimple_seq *seq, location_t loc, tree op) { if (ptrofftype_p (TREE_TYPE (op))) return op; return gimple_convert (seq, loc, sizetype, op); } /* Return true if the result of assignment STMT is known to be non-negative. If the return value is based on the assumption that signed overflow is undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change *STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */ static bool gimple_assign_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p, int depth) { enum tree_code code = gimple_assign_rhs_code (stmt); switch (get_gimple_rhs_class (code)) { case GIMPLE_UNARY_RHS: return tree_unary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt), gimple_expr_type (stmt), gimple_assign_rhs1 (stmt), strict_overflow_p, depth); case GIMPLE_BINARY_RHS: return tree_binary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt), gimple_expr_type (stmt), gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt), strict_overflow_p, depth); case GIMPLE_TERNARY_RHS: return false; case GIMPLE_SINGLE_RHS: return tree_single_nonnegative_warnv_p (gimple_assign_rhs1 (stmt), strict_overflow_p, depth); case GIMPLE_INVALID_RHS: break; } gcc_unreachable (); } /* Return true if return value of call STMT is known to be non-negative. If the return value is based on the assumption that signed overflow is undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change *STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */ static bool gimple_call_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p, int depth) { tree arg0 = gimple_call_num_args (stmt) > 0 ? gimple_call_arg (stmt, 0) : NULL_TREE; tree arg1 = gimple_call_num_args (stmt) > 1 ? gimple_call_arg (stmt, 1) : NULL_TREE; return tree_call_nonnegative_warnv_p (gimple_expr_type (stmt), gimple_call_combined_fn (stmt), arg0, arg1, strict_overflow_p, depth); } /* Return true if return value of call STMT is known to be non-negative. If the return value is based on the assumption that signed overflow is undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change *STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */ static bool gimple_phi_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p, int depth) { for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i) { tree arg = gimple_phi_arg_def (stmt, i); if (!tree_single_nonnegative_warnv_p (arg, strict_overflow_p, depth + 1)) return false; } return true; } /* Return true if STMT is known to compute a non-negative value. If the return value is based on the assumption that signed overflow is undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change *STRICT_OVERFLOW_P. DEPTH is the current nesting depth of the query. */ bool gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p, int depth) { switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: return gimple_assign_nonnegative_warnv_p (stmt, strict_overflow_p, depth); case GIMPLE_CALL: return gimple_call_nonnegative_warnv_p (stmt, strict_overflow_p, depth); case GIMPLE_PHI: return gimple_phi_nonnegative_warnv_p (stmt, strict_overflow_p, depth); default: return false; } } /* Return true if the floating-point value computed by assignment STMT is known to have an integer value. We also allow +Inf, -Inf and NaN to be considered integer values. Return false for signaling NaN. DEPTH is the current nesting depth of the query. */ static bool gimple_assign_integer_valued_real_p (gimple *stmt, int depth) { enum tree_code code = gimple_assign_rhs_code (stmt); switch (get_gimple_rhs_class (code)) { case GIMPLE_UNARY_RHS: return integer_valued_real_unary_p (gimple_assign_rhs_code (stmt), gimple_assign_rhs1 (stmt), depth); case GIMPLE_BINARY_RHS: return integer_valued_real_binary_p (gimple_assign_rhs_code (stmt), gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt), depth); case GIMPLE_TERNARY_RHS: return false; case GIMPLE_SINGLE_RHS: return integer_valued_real_single_p (gimple_assign_rhs1 (stmt), depth); case GIMPLE_INVALID_RHS: break; } gcc_unreachable (); } /* Return true if the floating-point value computed by call STMT is known to have an integer value. We also allow +Inf, -Inf and NaN to be considered integer values. Return false for signaling NaN. DEPTH is the current nesting depth of the query. */ static bool gimple_call_integer_valued_real_p (gimple *stmt, int depth) { tree arg0 = (gimple_call_num_args (stmt) > 0 ? gimple_call_arg (stmt, 0) : NULL_TREE); tree arg1 = (gimple_call_num_args (stmt) > 1 ? gimple_call_arg (stmt, 1) : NULL_TREE); return integer_valued_real_call_p (gimple_call_combined_fn (stmt), arg0, arg1, depth); } /* Return true if the floating-point result of phi STMT is known to have an integer value. We also allow +Inf, -Inf and NaN to be considered integer values. Return false for signaling NaN. DEPTH is the current nesting depth of the query. */ static bool gimple_phi_integer_valued_real_p (gimple *stmt, int depth) { for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i) { tree arg = gimple_phi_arg_def (stmt, i); if (!integer_valued_real_single_p (arg, depth + 1)) return false; } return true; } /* Return true if the floating-point value computed by STMT is known to have an integer value. We also allow +Inf, -Inf and NaN to be considered integer values. Return false for signaling NaN. DEPTH is the current nesting depth of the query. */ bool gimple_stmt_integer_valued_real_p (gimple *stmt, int depth) { switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: return gimple_assign_integer_valued_real_p (stmt, depth); case GIMPLE_CALL: return gimple_call_integer_valued_real_p (stmt, depth); case GIMPLE_PHI: return gimple_phi_integer_valued_real_p (stmt, depth); default: return false; } }