From: Marc Glisse Date: Tue, 21 Nov 2017 18:23:56 +0000 (+0100) Subject: New POINTER_DIFF_EXPR X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1af4ebf5985ef2aaac13862654044d84a3cd7ae4;p=gcc.git New POINTER_DIFF_EXPR 2017-11-21 Marc Glisse gcc/c/ * c-fold.c (c_fully_fold_internal): Handle POINTER_DIFF_EXPR. * c-typeck.c (pointer_diff): Use POINTER_DIFF_EXPR. gcc/c-family/ * c-pretty-print.c (pp_c_additive_expression, c_pretty_printer::expression): Handle POINTER_DIFF_EXPR. gcc/cp/ * constexpr.c (cxx_eval_constant_expression, potential_constant_expression_1): Handle POINTER_DIFF_EXPR. * cp-gimplify.c (cp_fold): Likewise. * error.c (dump_expr): Likewise. * typeck.c (pointer_diff): Use POINTER_DIFF_EXPR. gcc/ * doc/generic.texi: Document POINTER_DIFF_EXPR, update POINTER_PLUS_EXPR. * cfgexpand.c (expand_debug_expr): Handle POINTER_DIFF_EXPR. * expr.c (expand_expr_real_2): Likewise. * fold-const.c (const_binop, fold_addr_of_array_ref_difference, fold_binary_loc): Likewise. * match.pd (X-X, P+(Q-P), &D-P, (P+N)-P, P-(P+N), (P+M)-(P+N), P-Q==0, -(A-B), X-Z + + * doc/generic.texi: Document POINTER_DIFF_EXPR, update + POINTER_PLUS_EXPR. + * cfgexpand.c (expand_debug_expr): Handle POINTER_DIFF_EXPR. + * expr.c (expand_expr_real_2): Likewise. + * fold-const.c (const_binop, fold_addr_of_array_ref_difference, + fold_binary_loc): Likewise. + * match.pd (X-X, P+(Q-P), &D-P, (P+N)-P, P-(P+N), (P+M)-(P+N), + P-Q==0, -(A-B), X-Z * config/i386/i386.md (*bswap2_movbe): Add diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 308a617e616..08d8cec9443 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2017-11-21 Marc Glisse + + * c-pretty-print.c (pp_c_additive_expression, + c_pretty_printer::expression): Handle POINTER_DIFF_EXPR. + 2017-11-21 Jakub Jelinek * c-common.c (get_nonnull_operand): Use tree_to_uhwi. diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c index 0f48b9e958a..15e13ea5a2f 100644 --- a/gcc/c-family/c-pretty-print.c +++ b/gcc/c-family/c-pretty-print.c @@ -1876,6 +1876,7 @@ pp_c_additive_expression (c_pretty_printer *pp, tree e) { case POINTER_PLUS_EXPR: case PLUS_EXPR: + case POINTER_DIFF_EXPR: case MINUS_EXPR: pp_c_additive_expression (pp, TREE_OPERAND (e, 0)); pp_c_whitespace (pp); @@ -2292,6 +2293,7 @@ c_pretty_printer::expression (tree e) case POINTER_PLUS_EXPR: case PLUS_EXPR: + case POINTER_DIFF_EXPR: case MINUS_EXPR: pp_c_additive_expression (this, e); break; diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 95b7718791a..a2773b0956f 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,8 @@ +2017-11-21 Marc Glisse + + * c-fold.c (c_fully_fold_internal): Handle POINTER_DIFF_EXPR. + * c-typeck.c (pointer_diff): Use POINTER_DIFF_EXPR. + 2017-11-20 David Malcolm PR c/81404 diff --git a/gcc/c/c-fold.c b/gcc/c/c-fold.c index 6a6c7163bbe..88954627159 100644 --- a/gcc/c/c-fold.c +++ b/gcc/c/c-fold.c @@ -306,6 +306,7 @@ c_fully_fold_internal (tree expr, bool in_init, bool *maybe_const_operands, case MINUS_EXPR: case MULT_EXPR: case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: case FLOOR_DIV_EXPR: diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 8a09ea2ea46..7f85c6b216e 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -3778,7 +3778,7 @@ parser_build_binary_op (location_t location, enum tree_code code, } /* Return a tree for the difference of pointers OP0 and OP1. - The resulting tree has type int. */ + The resulting tree has type ptrdiff_t. */ static tree pointer_diff (location_t loc, tree op0, tree op1) @@ -3810,7 +3810,7 @@ pointer_diff (location_t loc, tree op0, tree op1) op1 = convert (common_type, op1); } - /* Determine integer type to perform computations in. This will usually + /* Determine integer type result of the subtraction. This will usually be the same as the result type (ptrdiff_t), but may need to be a wider type if pointers for the address space are wider than ptrdiff_t. */ if (TYPE_PRECISION (restype) < TYPE_PRECISION (TREE_TYPE (op0))) @@ -3825,14 +3825,21 @@ pointer_diff (location_t loc, tree op0, tree op1) pedwarn (loc, OPT_Wpointer_arith, "pointer to a function used in subtraction"); - /* First do the subtraction as integers; - then drop through to build the divide operator. - Do not do default conversions on the minus operator - in case restype is a short type. */ + /* First do the subtraction, then build the divide operator + and only convert at the very end. + Do not do default conversions in case restype is a short type. */ + + /* POINTER_DIFF_EXPR requires a signed integer type of the same size as + pointers. If some platform cannot provide that, or has a larger + ptrdiff_type to support differences larger than half the address + space, cast the pointers to some larger integer type and do the + computations in that type. */ + if (TYPE_PRECISION (inttype) > TYPE_PRECISION (TREE_TYPE (op0))) + op0 = build_binary_op (loc, MINUS_EXPR, convert (inttype, op0), + convert (inttype, op1), false); + else + op0 = build2_loc (loc, POINTER_DIFF_EXPR, inttype, op0, op1); - op0 = build_binary_op (loc, - MINUS_EXPR, convert (inttype, op0), - convert (inttype, op1), false); /* This generates an error if op1 is pointer to incomplete type. */ if (!COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (TREE_TYPE (orig_op1)))) error_at (loc, "arithmetic on pointer to an incomplete type"); diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index ca5d8dbdfd6..a63b414520f 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -4623,6 +4623,7 @@ expand_debug_expr (tree exp) return simplify_gen_binary (PLUS, mode, op0, op1); case MINUS_EXPR: + case POINTER_DIFF_EXPR: return simplify_gen_binary (MINUS, mode, op0, op1); case MULT_EXPR: diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index db9403455c4..8a6328a8bff 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2017-11-21 Marc Glisse + + * constexpr.c (cxx_eval_constant_expression, + potential_constant_expression_1): Handle POINTER_DIFF_EXPR. + * cp-gimplify.c (cp_fold): Likewise. + * error.c (dump_expr): Likewise. + * typeck.c (pointer_diff): Use POINTER_DIFF_EXPR. + 2017-11-21 Jakub Jelinek P0428R2 - familiar template syntax for generic lambdas diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 5eac64bd8a3..34a9c6b9d50 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -4336,6 +4336,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, break; case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: case PLUS_EXPR: case MINUS_EXPR: case MULT_EXPR: @@ -5590,6 +5591,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return true; } + case POINTER_DIFF_EXPR: case MINUS_EXPR: want_rval = true; goto binary; diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 201a5950591..d597ed99009 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -2227,6 +2227,7 @@ cp_fold (tree x) /* FALLTHRU */ case POINTER_PLUS_EXPR: case PLUS_EXPR: + case POINTER_DIFF_EXPR: case MINUS_EXPR: case MULT_EXPR: case TRUNC_DIV_EXPR: diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 2537713b5c9..7125b88e29e 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -2227,6 +2227,10 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) dump_binary_op (pp, "+", t, flags); break; + case POINTER_DIFF_EXPR: + dump_binary_op (pp, "-", t, flags); + break; + case INIT_EXPR: case MODIFY_EXPR: dump_binary_op (pp, OVL_OP_INFO (true, NOP_EXPR)->name, t, flags); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index cb93cc33561..8ba6a124aa9 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -5396,7 +5396,7 @@ static tree pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, tsubst_flags_t complain) { - tree result; + tree result, inttype; tree restype = ptrdiff_type_node; tree target_type = TREE_TYPE (ptrtype); @@ -5428,14 +5428,31 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, return error_mark_node; } - /* First do the subtraction as integers; - then drop through to build the divide operator. */ - - op0 = cp_build_binary_op (loc, - MINUS_EXPR, - cp_convert (restype, op0, complain), - cp_convert (restype, op1, complain), - complain); + /* Determine integer type result of the subtraction. This will usually + be the same as the result type (ptrdiff_t), but may need to be a wider + type if pointers for the address space are wider than ptrdiff_t. */ + if (TYPE_PRECISION (restype) < TYPE_PRECISION (TREE_TYPE (op0))) + inttype = c_common_type_for_size (TYPE_PRECISION (TREE_TYPE (op0)), 0); + else + inttype = restype; + + /* First do the subtraction, then build the divide operator + and only convert at the very end. + Do not do default conversions in case restype is a short type. */ + + /* POINTER_DIFF_EXPR requires a signed integer type of the same size as + pointers. If some platform cannot provide that, or has a larger + ptrdiff_type to support differences larger than half the address + space, cast the pointers to some larger integer type and do the + computations in that type. */ + if (TYPE_PRECISION (inttype) > TYPE_PRECISION (TREE_TYPE (op0))) + op0 = cp_build_binary_op (loc, + MINUS_EXPR, + cp_convert (inttype, op0, complain), + cp_convert (inttype, op1, complain), + complain); + else + op0 = build2_loc (loc, POINTER_DIFF_EXPR, inttype, op0, op1); /* This generates an error if op1 is a pointer to an incomplete type. */ if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (op1)))) @@ -5461,9 +5478,9 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, /* Do the division. */ - result = build2_loc (loc, EXACT_DIV_EXPR, restype, op0, - cp_convert (restype, op1, complain)); - return result; + result = build2_loc (loc, EXACT_DIV_EXPR, inttype, op0, + cp_convert (inttype, op1, complain)); + return cp_convert (restype, result, complain); } /* Construct and perhaps optimize a tree representation diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi index a51cfd6e955..b03970f74cc 100644 --- a/gcc/doc/generic.texi +++ b/gcc/doc/generic.texi @@ -1224,6 +1224,7 @@ the byte offset of the field, but should not be used directly; call @tindex TRUTH_OR_EXPR @tindex TRUTH_XOR_EXPR @tindex POINTER_PLUS_EXPR +@tindex POINTER_DIFF_EXPR @tindex PLUS_EXPR @tindex MINUS_EXPR @tindex MULT_EXPR @@ -1413,8 +1414,16 @@ always of @code{BOOLEAN_TYPE} or @code{INTEGER_TYPE}. @item POINTER_PLUS_EXPR This node represents pointer arithmetic. The first operand is always a pointer/reference type. The second operand is always an unsigned -integer type compatible with sizetype. This is the only binary -arithmetic operand that can operate on pointer types. +integer type compatible with sizetype. This and POINTER_DIFF_EXPR are +the only binary arithmetic operators that can operate on pointer types. + +@item POINTER_DIFF_EXPR +This node represents pointer subtraction. The two operands always +have pointer/reference type. It returns a signed integer of the same +precision as the pointers. The behavior is undefined if the difference +of the two pointers, seen as infinite precision non-negative integers, +does not fit in the result type. The result does not depend on the +pointer type, it is not divided by the size of the pointed-to type. @item PLUS_EXPR @itemx MINUS_EXPR diff --git a/gcc/expr.c b/gcc/expr.c index c93d9f6754c..3341e94d6f0 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -8555,6 +8555,7 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode, return REDUCE_BIT_FIELD (simplify_gen_binary (PLUS, mode, op0, op1)); case MINUS_EXPR: + case POINTER_DIFF_EXPR: do_minus: /* For initializers, we are allowed to return a MINUS of two symbolic constants. Here we handle all cases when both operands diff --git a/gcc/fold-const.c b/gcc/fold-const.c index e9cd968887f..9fe346243dd 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -1483,6 +1483,16 @@ const_binop (enum tree_code code, tree type, tree arg1, tree arg2) return build_complex (type, arg1, arg2); return NULL_TREE; + case POINTER_DIFF_EXPR: + if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg2) == INTEGER_CST) + { + offset_int res = wi::sub (wi::to_offset (arg1), + wi::to_offset (arg2)); + return force_fit_type (type, res, 1, + TREE_OVERFLOW (arg1) | TREE_OVERFLOW (arg2)); + } + return NULL_TREE; + case VEC_PACK_TRUNC_EXPR: case VEC_PACK_FIX_TRUNC_EXPR: { @@ -8801,7 +8811,8 @@ fold_vec_perm (tree type, tree arg0, tree arg1, vec_perm_indices sel) static tree fold_addr_of_array_ref_difference (location_t loc, tree type, - tree aref0, tree aref1) + tree aref0, tree aref1, + bool use_pointer_diff) { tree base0 = TREE_OPERAND (aref0, 0); tree base1 = TREE_OPERAND (aref1, 0); @@ -8813,14 +8824,20 @@ fold_addr_of_array_ref_difference (location_t loc, tree type, if ((TREE_CODE (base0) == ARRAY_REF && TREE_CODE (base1) == ARRAY_REF && (base_offset - = fold_addr_of_array_ref_difference (loc, type, base0, base1))) + = fold_addr_of_array_ref_difference (loc, type, base0, base1, + use_pointer_diff))) || (INDIRECT_REF_P (base0) && INDIRECT_REF_P (base1) && (base_offset - = fold_binary_loc (loc, MINUS_EXPR, type, - fold_convert (type, TREE_OPERAND (base0, 0)), - fold_convert (type, - TREE_OPERAND (base1, 0))))) + = use_pointer_diff + ? fold_binary_loc (loc, POINTER_DIFF_EXPR, type, + TREE_OPERAND (base0, 0), + TREE_OPERAND (base1, 0)) + : fold_binary_loc (loc, MINUS_EXPR, type, + fold_convert (type, + TREE_OPERAND (base0, 0)), + fold_convert (type, + TREE_OPERAND (base1, 0))))) || operand_equal_p (base0, base1, OEP_ADDRESS_OF)) { tree op0 = fold_convert_loc (loc, type, TREE_OPERAND (aref0, 1)); @@ -9694,7 +9711,27 @@ fold_binary_loc (location_t loc, return NULL_TREE; + case POINTER_DIFF_EXPR: case MINUS_EXPR: + /* Fold &a[i] - &a[j] to i-j. */ + if (TREE_CODE (arg0) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (arg0, 0)) == ARRAY_REF + && TREE_CODE (arg1) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (arg1, 0)) == ARRAY_REF) + { + tree tem = fold_addr_of_array_ref_difference (loc, type, + TREE_OPERAND (arg0, 0), + TREE_OPERAND (arg1, 0), + code + == POINTER_DIFF_EXPR); + if (tem) + return tem; + } + + /* Further transformations are not for pointers. */ + if (code == POINTER_DIFF_EXPR) + return NULL_TREE; + /* (-A) - B -> (-B) - A where B is easily negated and we can swap. */ if (TREE_CODE (arg0) == NEGATE_EXPR && negate_expr_p (op1)) @@ -9752,19 +9789,6 @@ fold_binary_loc (location_t loc, fold_convert_loc (loc, type, arg0), negate_expr (op1)); - /* Fold &a[i] - &a[j] to i-j. */ - if (TREE_CODE (arg0) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (arg0, 0)) == ARRAY_REF - && TREE_CODE (arg1) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (arg1, 0)) == ARRAY_REF) - { - tree tem = fold_addr_of_array_ref_difference (loc, type, - TREE_OPERAND (arg0, 0), - TREE_OPERAND (arg1, 0)); - if (tem) - return tem; - } - /* Handle (A1 * C1) - (A2 * C2) with A1, A2 or C1, C2 being the same or one. Make sure the type is not saturating and has the signedness of the stripped operands, as fold_plusminus_mult_expr will re-associate. diff --git a/gcc/match.pd b/gcc/match.pd index 0e21a0d8d8f..0949a9bcd2c 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -124,6 +124,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (minus @0 @0) (if (!FLOAT_TYPE_P (type) || !HONOR_NANS (type)) { build_zero_cst (type); })) +(simplify + (pointer_diff @@0 @0) + { build_zero_cst (type); }) (simplify (mult @0 integer_zerop@1) @@ -1040,6 +1043,10 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && !HONOR_SIGN_DEPENDENT_ROUNDING (type) && !HONOR_SIGNED_ZEROS (type))) (minus @1 @0))) +(simplify + (negate (pointer_diff @0 @1)) + (if (TYPE_OVERFLOW_UNDEFINED (type)) + (pointer_diff @1 @0))) /* A - B -> A + (-B) if B is easily negatable. */ (simplify @@ -1342,6 +1349,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)) || TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))) (op @0 @1)))) +/* And for pointers... */ +(for op (simple_comparison) + (simplify + (op (pointer_diff@3 @0 @2) (pointer_diff @1 @2)) + (if (!TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@2))) + (op @0 @1)))) +(simplify + (minus (pointer_diff@3 @0 @2) (pointer_diff @1 @2)) + (if (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@3)) + && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@2))) + (pointer_diff @0 @1))) /* Z - X < Z - Y is the same as Y < X when there is no overflow. */ (for op (lt le ge gt) @@ -1358,6 +1376,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)) || TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))) (op @1 @0)))) +/* And for pointers... */ +(for op (simple_comparison) + (simplify + (op (pointer_diff@3 @2 @0) (pointer_diff @2 @1)) + (if (!TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@2))) + (op @1 @0)))) +(simplify + (minus (pointer_diff@3 @2 @0) (pointer_diff @2 @1)) + (if (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@3)) + && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@2))) + (pointer_diff @1 @0))) /* X + Y < Y is the same as X < 0 when there is no overflow. */ (for op (lt le gt ge) @@ -1506,6 +1535,10 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && ((GIMPLE && useless_type_conversion_p (type, TREE_TYPE (@1))) || (GENERIC && type == TREE_TYPE (@1)))) @1)) +(simplify + (pointer_plus @0 (convert?@2 (pointer_diff@3 @1 @@0))) + (if (TYPE_PRECISION (TREE_TYPE (@2)) >= TYPE_PRECISION (TREE_TYPE (@3))) + (convert @1))) /* Pattern match tem = (sizetype) ptr; @@ -1532,6 +1565,20 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (with { HOST_WIDE_INT diff; } (if (ptr_difference_const (@0, @1, &diff)) { build_int_cst_type (type, diff); })))) +(simplify + (pointer_diff (convert?@2 ADDR_EXPR@0) (convert?@3 @1)) + (if (tree_nop_conversion_p (TREE_TYPE(@2), TREE_TYPE (@0)) + && tree_nop_conversion_p (TREE_TYPE(@3), TREE_TYPE (@1))) + (with { HOST_WIDE_INT diff; } + (if (ptr_difference_const (@0, @1, &diff)) + { build_int_cst_type (type, diff); })))) +(simplify + (pointer_diff (convert?@2 @0) (convert?@3 ADDR_EXPR@1)) + (if (tree_nop_conversion_p (TREE_TYPE(@2), TREE_TYPE (@0)) + && tree_nop_conversion_p (TREE_TYPE(@3), TREE_TYPE (@1))) + (with { HOST_WIDE_INT diff; } + (if (ptr_difference_const (@0, @1, &diff)) + { build_int_cst_type (type, diff); })))) /* If arg0 is derived from the address of an object or function, we may be able to fold this expression using the object or function's @@ -1643,6 +1690,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (simplify (plus:c (minus @0 @1) (minus @2 @0)) (minus @2 @1)) + (simplify + (plus:c (pointer_diff @0 @1) (pointer_diff @2 @0)) + (if (TYPE_OVERFLOW_UNDEFINED (type) + && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0))) + (pointer_diff @2 @1))) (simplify (minus (plus:c @0 @1) (minus @0 @2)) (plus @1 @2)) @@ -1748,6 +1800,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && TREE_CODE (@1) == INTEGER_CST && tree_int_cst_sign_bit (@1) == 0)) (convert @1)))) + (simplify + (pointer_diff (pointer_plus @@0 @1) @0) + /* The second argument of pointer_plus must be interpreted as signed, and + thus sign-extended if necessary. */ + (with { tree stype = signed_type_for (TREE_TYPE (@1)); } + (convert (convert:stype @1)))) /* (T)P - (T)(P + A) -> -(T) A */ (for add (plus pointer_plus) @@ -1772,6 +1830,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && TREE_CODE (@1) == INTEGER_CST && tree_int_cst_sign_bit (@1) == 0)) (negate (convert @1))))) + (simplify + (pointer_diff @0 (pointer_plus @@0 @1)) + /* The second argument of pointer_plus must be interpreted as signed, and + thus sign-extended if necessary. */ + (with { tree stype = signed_type_for (TREE_TYPE (@1)); } + (negate (convert (convert:stype @1))))) /* (T)(P + A) - (T)(P + B) -> (T)A - (T)B */ (for add (plus pointer_plus) @@ -1798,6 +1862,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && TREE_CODE (@2) == INTEGER_CST && tree_int_cst_sign_bit (@2) == 0)) (minus (convert @1) (convert @2))))))) + (simplify + (pointer_diff (pointer_plus @@0 @1) (pointer_plus @0 @2)) + /* The second argument of pointer_plus must be interpreted as signed, and + thus sign-extended if necessary. */ + (with { tree stype = signed_type_for (TREE_TYPE (@1)); } + (minus (convert (convert:stype @1)) (convert (convert:stype @2))))) /* Simplifications of MIN_EXPR, MAX_EXPR, fmin() and fmax(). */ @@ -2797,10 +2867,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) with the transformation in fold_cond_expr_with_comparison which attempts to synthetize ABS_EXPR. */ (for cmp (eq ne) - (simplify - (cmp (minus@2 @0 @1) integer_zerop) - (if (single_use (@2)) - (cmp @0 @1)))) + (for sub (minus pointer_diff) + (simplify + (cmp (sub@2 @0 @1) integer_zerop) + (if (single_use (@2)) + (cmp @0 @1))))) /* Transform comparisons of the form X * C1 CMP 0 to X CMP 0 in the signed arithmetic case. That form is created by the compiler diff --git a/gcc/optabs-tree.c b/gcc/optabs-tree.c index c183b14c819..47a2aefd63e 100644 --- a/gcc/optabs-tree.c +++ b/gcc/optabs-tree.c @@ -223,6 +223,7 @@ optab_for_tree_code (enum tree_code code, const_tree type, return TYPE_UNSIGNED (type) ? usadd_optab : ssadd_optab; return trapv ? addv_optab : add_optab; + case POINTER_DIFF_EXPR: case MINUS_EXPR: if (TYPE_SATURATING (type)) return TYPE_UNSIGNED (type) ? ussub_optab : sssub_optab; diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index bdcb04f8c4a..b06c3f3b964 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -3142,6 +3142,25 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) CHECK_OP (1, "invalid operand to binary operator"); break; + case POINTER_DIFF_EXPR: + if (!POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0))) + || !POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 1)))) + { + error ("invalid operand to pointer diff, operand is not a pointer"); + return t; + } + if (TREE_CODE (TREE_TYPE (t)) != INTEGER_TYPE + || TYPE_UNSIGNED (TREE_TYPE (t)) + || (TYPE_PRECISION (TREE_TYPE (t)) + != TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0))))) + { + error ("invalid type for pointer diff"); + return t; + } + CHECK_OP (0, "invalid operand to pointer diff"); + CHECK_OP (1, "invalid operand to pointer diff"); + break; + case POINTER_PLUS_EXPR: /* Check to make sure the first operand is a pointer or reference type. */ if (!POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0)))) @@ -3977,6 +3996,25 @@ verify_gimple_assign_binary (gassign *stmt) return false; } + case POINTER_DIFF_EXPR: + { + if (!POINTER_TYPE_P (rhs1_type) + || !POINTER_TYPE_P (rhs2_type) + || !types_compatible_p (rhs1_type, rhs2_type) + || TREE_CODE (lhs_type) != INTEGER_TYPE + || TYPE_UNSIGNED (lhs_type) + || TYPE_PRECISION (lhs_type) != TYPE_PRECISION (rhs1_type)) + { + error ("type mismatch in pointer diff expression"); + debug_generic_stmt (lhs_type); + debug_generic_stmt (rhs1_type); + debug_generic_stmt (rhs2_type); + return true; + } + + return false; + } + case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: case TRUTH_AND_EXPR: diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 2da1745ede4..b51f13cc076 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -3819,6 +3819,7 @@ estimate_operator_cost (enum tree_code code, eni_weights *weights, case PLUS_EXPR: case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: case MINUS_EXPR: case MULT_EXPR: case MULT_HIGHPART_EXPR: diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 169f9724eb1..373332d97dd 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -2308,6 +2308,7 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags, case MULT_HIGHPART_EXPR: case PLUS_EXPR: case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: case MINUS_EXPR: case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: @@ -3553,6 +3554,7 @@ op_code_prio (enum tree_code code) case WIDEN_SUM_EXPR: case PLUS_EXPR: case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: case MINUS_EXPR: return 12; @@ -3739,6 +3741,7 @@ op_symbol_code (enum tree_code code) case NEGATE_EXPR: case MINUS_EXPR: + case POINTER_DIFF_EXPR: return "-"; case BIT_NOT_EXPR: diff --git a/gcc/tree-vect-stmts.c b/gcc/tree-vect-stmts.c index 61a4cbd6df6..0b9f4e60fe1 100644 --- a/gcc/tree-vect-stmts.c +++ b/gcc/tree-vect-stmts.c @@ -5265,10 +5265,12 @@ vectorizable_operation (gimple *stmt, gimple_stmt_iterator *gsi, code = gimple_assign_rhs_code (stmt); - /* For pointer addition, we should use the normal plus for - the vector addition. */ + /* For pointer addition and subtraction, we should use the normal + plus and minus for the vector operation. */ if (code == POINTER_PLUS_EXPR) code = PLUS_EXPR; + if (code == POINTER_DIFF_EXPR) + code = MINUS_EXPR; /* Support only unary or binary operations. */ op_type = TREE_CODE_LENGTH (code); diff --git a/gcc/tree.def b/gcc/tree.def index 3d2bd95d666..56558589337 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -676,6 +676,14 @@ DEFTREECODE (MULT_EXPR, "mult_expr", tcc_binary, 2) second operand is an integer of type sizetype. */ DEFTREECODE (POINTER_PLUS_EXPR, "pointer_plus_expr", tcc_binary, 2) +/* Pointer subtraction. The two arguments are pointers, and the result + is a signed integer of the same precision. Pointers are interpreted + as unsigned, the difference is computed as if in infinite signed + precision. Behavior is undefined if the difference does not fit in + the result type. The result does not depend on the pointer type, + it is not divided by the size of the pointed-to type. */ +DEFTREECODE (POINTER_DIFF_EXPR, "pointer_diff_expr", tcc_binary, 2) + /* Highpart multiplication. For an integral type with precision B, returns bits [2B-1, B] of the full 2*B product. */ DEFTREECODE (MULT_HIGHPART_EXPR, "mult_highpart_expr", tcc_binary, 2) diff --git a/gcc/varasm.c b/gcc/varasm.c index 1b35a99d688..923399d71a4 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -4615,6 +4615,7 @@ initializer_constant_valid_p_1 (tree value, tree endtype, tree *cache) } return ret; + case POINTER_DIFF_EXPR: case MINUS_EXPR: if (TREE_CODE (endtype) == REAL_TYPE) return NULL_TREE; diff --git a/gcc/vr-values.c b/gcc/vr-values.c index 2d1186198f7..7b9c5ad6c4b 100644 --- a/gcc/vr-values.c +++ b/gcc/vr-values.c @@ -850,7 +850,7 @@ vr_values::extract_range_from_binary_expr (value_range *vr, can derive a non-null range. This happens often for pointer subtraction. */ if (vr->type == VR_VARYING - && code == MINUS_EXPR + && (code == MINUS_EXPR || code == POINTER_DIFF_EXPR) && TREE_CODE (op0) == SSA_NAME && ((vr0.type == VR_ANTI_RANGE && vr0.min == op1 @@ -858,7 +858,7 @@ vr_values::extract_range_from_binary_expr (value_range *vr, || (vr1.type == VR_ANTI_RANGE && vr1.min == op0 && vr1.min == vr1.max))) - set_value_range_to_nonnull (vr, TREE_TYPE (op0)); + set_value_range_to_nonnull (vr, expr_type); } /* Extract range information from a unary expression CODE OP0 based on