https://gcc.gnu.org/g:05bb1a71fb052d9145c010e4431d8bc01fb44115
commit 05bb1a71fb052d9145c010e4431d8bc01fb44115 Author: Mikael Morin <mik...@gcc.gnu.org> Date: Thu Jul 4 15:24:36 2024 +0200 match: Unwrap non-lvalue as unary or binary operand Regression tested on x86_64-linux. OK for master? -- 8< -- This change makes the binary and unary folding functions return a tree whose operands have their non-lvalue wrapper stripped (if they had one). It only makes a difference if the function hasn't found any simplification and would return NULL_TREE. It moves all early NULL_TREE return to the end of the function where a last resort common simplification attempt is made. That attempt checks whether the implicit simplifications contained in the stripped operands are worth keeping, and returns a new tree based on the stripped operands in that case. The testcases are best effort; for some operators the fortran frontend generates a temporary variable, so the simplification doesn't happen. Those cases are not tested. gcc/ChangeLog: * fold-const.cc (maybe_lvalue_p): New overload, split from the original function. (stripped_converted_equals_original): New predicate function. (fold_binary_loc): Check whether the initial stripping of operands was a simplification worth keeping before returning NULL_TREE, and rebuild a new tree based on the stripped operands in that case. (fold_unary_loc): Likewise. gcc/testsuite/ChangeLog: * gfortran.dg/non_lvalue_2.f90: New test. * gfortran.dg/non_lvalue_3.f90: New test. Diff: --- gcc/fold-const.cc | 228 ++++++++++++++++++----------- gcc/testsuite/gfortran.dg/non_lvalue_2.f90 | 58 ++++++++ gcc/testsuite/gfortran.dg/non_lvalue_3.f90 | 172 ++++++++++++++++++++++ 3 files changed, 375 insertions(+), 83 deletions(-) diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc index 014f42187932..867ef7017b0d 100644 --- a/gcc/fold-const.cc +++ b/gcc/fold-const.cc @@ -2790,56 +2790,65 @@ fold_convert_loc (location_t loc, tree type, tree arg) return tem; } -/* Return false if expr can be assumed not to be an lvalue, true - otherwise. */ +/* Return false if a tree whose code is CODE can be assumed not to represent an + lvalue, true otherwise. */ static bool -maybe_lvalue_p (const_tree x) +maybe_lvalue_p (enum tree_code code) { /* We only need to wrap lvalue tree codes. */ - switch (TREE_CODE (x)) - { - case VAR_DECL: - case PARM_DECL: - case RESULT_DECL: - case LABEL_DECL: - case FUNCTION_DECL: - case SSA_NAME: - case COMPOUND_LITERAL_EXPR: - - case COMPONENT_REF: - case MEM_REF: - case INDIRECT_REF: - case ARRAY_REF: - case ARRAY_RANGE_REF: - case BIT_FIELD_REF: - case OBJ_TYPE_REF: - - case REALPART_EXPR: - case IMAGPART_EXPR: - case PREINCREMENT_EXPR: - case PREDECREMENT_EXPR: - case SAVE_EXPR: - case TRY_CATCH_EXPR: - case WITH_CLEANUP_EXPR: - case COMPOUND_EXPR: - case MODIFY_EXPR: - case TARGET_EXPR: - case COND_EXPR: - case BIND_EXPR: - case VIEW_CONVERT_EXPR: - break; + switch (code) + { + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + case LABEL_DECL: + case FUNCTION_DECL: + case SSA_NAME: + case COMPOUND_LITERAL_EXPR: + + case COMPONENT_REF: + case MEM_REF: + case INDIRECT_REF: + case ARRAY_REF: + case ARRAY_RANGE_REF: + case BIT_FIELD_REF: + case OBJ_TYPE_REF: - default: - /* Assume the worst for front-end tree codes. */ - if ((int)TREE_CODE (x) >= NUM_TREE_CODES) + case REALPART_EXPR: + case IMAGPART_EXPR: + case PREINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case SAVE_EXPR: + case TRY_CATCH_EXPR: + case WITH_CLEANUP_EXPR: + case COMPOUND_EXPR: + case MODIFY_EXPR: + case TARGET_EXPR: + case COND_EXPR: + case BIND_EXPR: + case VIEW_CONVERT_EXPR: break; - return false; - } + + default: + /* Assume the worst for front-end tree codes. */ + if ((int)code >= NUM_TREE_CODES) + break; + return false; + } return true; } +/* Return false if expr can be assumed not to be an lvalue, true + otherwise. */ + +static bool +maybe_lvalue_p (const_tree x) +{ + return maybe_lvalue_p (TREE_CODE (x)); +} + /* Return an expr equal to X but certainly not valid as an lvalue. */ tree @@ -9181,6 +9190,21 @@ build_fold_addr_expr_loc (location_t loc, tree t) return build_fold_addr_expr_with_type_loc (loc, t, ptrtype); } +/* Tells whether tree ORIGINAL is equal to what would be produced if + converting its nop-stripped subtree STRIPPED to its type. */ + +static inline bool +stripped_converted_equals_original (const_tree stripped, const_tree original) +{ + if (stripped == original) + return true; + + if (TREE_TYPE (stripped) == TREE_TYPE (original)) + return false; + + return TREE_OPERAND (original, 0) == stripped; +} + /* Fold a unary expression of code CODE and type TYPE with operand OP0. Return the folded expression if folding is successful. Otherwise, return NULL_TREE. */ @@ -9299,7 +9323,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) case NON_LVALUE_EXPR: if (!maybe_lvalue_p (op0)) return fold_convert_loc (loc, type, op0); - return NULL_TREE; + break; CASE_CONVERT: case FLOAT_EXPR: @@ -9437,7 +9461,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) && sanitize_flags_p (SANITIZE_ALIGNMENT) && (min_align_of_type (TREE_TYPE (type)) > min_align_of_type (TREE_TYPE (TREE_TYPE (arg00))))) - return NULL_TREE; + break; /* Similarly, avoid this optimization in GENERIC for -fsanitize=null when type is a reference type and arg00's type is not, @@ -9447,7 +9471,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) && !in_gimple_form && sanitize_flags_p (SANITIZE_NULL) && TREE_CODE (TREE_TYPE (arg00)) != REFERENCE_TYPE) - return NULL_TREE; + break; arg00 = fold_convert_loc (loc, type, arg00); return fold_build_pointer_plus_loc (loc, arg00, arg01); @@ -9496,7 +9520,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) } } - return NULL_TREE; + break; case VIEW_CONVERT_EXPR: if (TREE_CODE (op0) == MEM_REF) @@ -9509,13 +9533,13 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) return tem; } - return NULL_TREE; + break; case NEGATE_EXPR: tem = fold_negate_expr (loc, arg0); if (tem) return fold_convert_loc (loc, type, tem); - return NULL_TREE; + break; case ABS_EXPR: /* Convert fabs((double)float) into (double)fabsf(float). */ @@ -9529,7 +9553,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) TREE_TYPE (targ0), targ0)); } - return NULL_TREE; + break; case BIT_NOT_EXPR: /* Convert ~(X ^ Y) to ~X ^ Y or X ^ ~Y if ~X or ~Y simplify. */ @@ -9548,7 +9572,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) fold_convert_loc (loc, type, TREE_OPERAND (arg0, 0)), tem); - return NULL_TREE; + break; case TRUTH_NOT_EXPR: /* Note that the operand of this must be an int @@ -9557,7 +9581,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) but we don't handle values other than 1 correctly yet.) */ tem = fold_truth_not_expr (loc, arg0); if (!tem) - return NULL_TREE; + break; return fold_convert_loc (loc, type, tem); case INDIRECT_REF: @@ -9571,11 +9595,22 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) && !TREE_READONLY (op00)) return op00; } - return NULL_TREE; + break; default: - return NULL_TREE; + break; } /* switch (code) */ + + if (kind == tcc_unary + && !stripped_converted_equals_original (arg0, op0)) + { + op0 = fold_convert_loc (EXPR_HAS_LOCATION (op0) + ? EXPR_LOCATION (op0) : loc, + TREE_TYPE (op0), arg0); + return fold_build1_loc (loc, code, type, op0); + } + + return NULL_TREE; } @@ -11144,14 +11179,14 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, base = get_addr_base_and_unit_offset (TREE_OPERAND (arg0, 0), &coffset); if (!base) - return NULL_TREE; + break; return fold_build2 (MEM_REF, type, build1 (ADDR_EXPR, TREE_TYPE (arg0), base), int_const_binop (PLUS_EXPR, arg1, size_int (coffset))); } - return NULL_TREE; + break; case POINTER_PLUS_EXPR: /* INT +p INT -> (PTR)(INT + INT). Stripping types allows for this. */ @@ -11164,7 +11199,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, fold_convert_loc (loc, sizetype, arg0))); - return NULL_TREE; + break; case PLUS_EXPR: if (INTEGRAL_TYPE_P (type) || VECTOR_INTEGER_TYPE_P (type)) @@ -11449,8 +11484,9 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, &lit1, &minus_lit1, code == MINUS_EXPR); /* Recombine MINUS_EXPR operands by using PLUS_EXPR. */ + enum tree_code assoc_code = code; if (code == MINUS_EXPR) - code = PLUS_EXPR; + assoc_code = PLUS_EXPR; /* With undefined overflow prefer doing association in a type which wraps on overflow, if that is one of the operand types. */ @@ -11532,15 +11568,15 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, int lit0_origin = (lit0 != 0) + 2 * (lit1 != 0); int minus_lit0_origin = (minus_lit0 != 0) + 2 * (minus_lit1 != 0); - var0 = associate_trees (loc, var0, var1, code, atype); + var0 = associate_trees (loc, var0, var1, assoc_code, atype); minus_var0 = associate_trees (loc, minus_var0, minus_var1, - code, atype); - con0 = associate_trees (loc, con0, con1, code, atype); + assoc_code, atype); + con0 = associate_trees (loc, con0, con1, assoc_code, atype); minus_con0 = associate_trees (loc, minus_con0, minus_con1, - code, atype); - lit0 = associate_trees (loc, lit0, lit1, code, atype); + assoc_code, atype); + lit0 = associate_trees (loc, lit0, lit1, assoc_code, atype); minus_lit0 = associate_trees (loc, minus_lit0, minus_lit1, - code, atype); + assoc_code, atype); if (minus_var0 && var0) { @@ -11591,14 +11627,14 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, /* Don't introduce overflows through reassociation. */ if ((lit0 && TREE_OVERFLOW_P (lit0)) || (minus_lit0 && TREE_OVERFLOW_P (minus_lit0))) - return NULL_TREE; + break; /* Eliminate lit0 and minus_lit0 to con0 and minus_con0. */ con0_origin |= lit0_origin; - con0 = associate_trees (loc, con0, lit0, code, atype); + con0 = associate_trees (loc, con0, lit0, assoc_code, atype); minus_con0_origin |= minus_lit0_origin; minus_con0 = associate_trees (loc, minus_con0, minus_lit0, - code, atype); + assoc_code, atype); /* Eliminate minus_con0. */ if (minus_con0) @@ -11637,15 +11673,15 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, least one of the operands, otherwise we risk infinite recursion. See PR114084. */ if (var0_origin != 3 && con0_origin != 3) - return NULL_TREE; + break; return fold_convert_loc (loc, type, associate_trees (loc, var0, con0, - code, atype)); + assoc_code, atype)); } } - return NULL_TREE; + break; case POINTER_DIFF_EXPR: case MINUS_EXPR: @@ -11666,7 +11702,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, /* Further transformations are not for pointers. */ if (code == POINTER_DIFF_EXPR) - return NULL_TREE; + break; /* (-A) - B -> (-B) - A where B is easily negated and we can swap. */ if (TREE_CODE (arg0) == NEGATE_EXPR @@ -11974,7 +12010,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, if (TREE_CODE (arg1) == REAL_CST && !MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (arg1))) && real_zerop (arg1)) - return NULL_TREE; + break; /* (-A) / (-B) -> A / B */ if (TREE_CODE (arg0) == NEGATE_EXPR && negate_expr_p (arg1)) @@ -11985,7 +12021,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, return fold_build2_loc (loc, RDIV_EXPR, type, negate_expr (arg0), TREE_OPERAND (arg1, 0)); - return NULL_TREE; + break; case TRUNC_DIV_EXPR: /* Fall through */ @@ -12023,7 +12059,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, case CEIL_DIV_EXPR: case EXACT_DIV_EXPR: if (integer_zerop (arg1)) - return NULL_TREE; + break; /* Convert -A / -B to A / B when the type is signed and overflow is undefined. */ @@ -12080,7 +12116,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, return fold_convert_loc (loc, type, tem); } - return NULL_TREE; + break; case CEIL_MOD_EXPR: case FLOOR_MOD_EXPR: @@ -12098,7 +12134,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, return fold_convert_loc (loc, type, tem); } - return NULL_TREE; + break; case LROTATE_EXPR: case RROTATE_EXPR: @@ -12107,7 +12143,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, /* Since negative shift count is not well-defined, don't try to compute it in the compiler. */ if (TREE_CODE (arg1) == INTEGER_CST && tree_int_cst_sgn (arg1) < 0) - return NULL_TREE; + break; prec = element_precision (type); @@ -12129,7 +12165,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, arg01, arg1)); } - return NULL_TREE; + break; case MIN_EXPR: case MAX_EXPR: @@ -12193,7 +12229,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, != NULL_TREE) return tem; - return NULL_TREE; + break; case TRUTH_ORIF_EXPR: /* Note that the operands of this must be ints @@ -12255,7 +12291,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, != NULL_TREE) return tem; - return NULL_TREE; + break; case TRUTH_XOR_EXPR: /* If the second arg is constant zero, drop it. */ @@ -12281,7 +12317,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, && operand_equal_p (arg0, TREE_OPERAND (arg1, 0), 0)) return omit_one_operand_loc (loc, type, integer_one_node, arg0); - return NULL_TREE; + break; case EQ_EXPR: case NE_EXPR: @@ -12564,7 +12600,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, } } - return NULL_TREE; + break; case LT_EXPR: case GT_EXPR: @@ -12701,7 +12737,7 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, build_zero_cst (TREE_TYPE (arg0))); } - return NULL_TREE; + break; case UNORDERED_EXPR: case ORDERED_EXPR: @@ -12727,21 +12763,47 @@ fold_binary_loc (location_t loc, enum tree_code code, tree type, fold_convert_loc (loc, newtype, targ1)); } - return NULL_TREE; + break; case COMPOUND_EXPR: /* When pedantic, a compound expression can be neither an lvalue nor an integer constant expression. */ if (TREE_SIDE_EFFECTS (arg0) || TREE_CONSTANT (arg1)) - return NULL_TREE; + break; /* Don't let (0, 0) be null pointer constant. */ tem = integer_zerop (arg1) ? build1_loc (loc, NOP_EXPR, type, arg1) : fold_convert_loc (loc, type, arg1); return tem; default: - return NULL_TREE; + break; } /* switch (code) */ + + if (kind == tcc_comparison || kind == tcc_binary) + { + bool any_changed_op = false; + if (!stripped_converted_equals_original (arg0, op0)) + + { + op0 = fold_convert_loc (EXPR_HAS_LOCATION (op0) + ? EXPR_LOCATION (op0) : loc, + TREE_TYPE (op0), arg0); + any_changed_op = true; + } + + if (!stripped_converted_equals_original (arg1, op1)) + { + op1 = fold_convert_loc (EXPR_HAS_LOCATION (op1) + ? EXPR_LOCATION (op1) : loc, + TREE_TYPE (op1), arg1); + any_changed_op = true; + } + + if (any_changed_op) + return fold_build2_loc (loc, code, type, op0, op1); + } + + return NULL_TREE; } /* For constants M and N, if M == (1LL << cst) - 1 && (N & M) == M, diff --git a/gcc/testsuite/gfortran.dg/non_lvalue_2.f90 b/gcc/testsuite/gfortran.dg/non_lvalue_2.f90 new file mode 100644 index 000000000000..24eff12ea4d5 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/non_lvalue_2.f90 @@ -0,0 +1,58 @@ +! { dg-do compile } +! { dg-additional-options "-funsigned -fdump-tree-original" } +! +! Check the unwrapping of the NON_LVALUE_EXPR that may com from an earlier +! simplification, if it is used in a unary operator context. + +! The NON_LVALUE_EXPR is dropped if it's used as argument to an absolute value +! operator. +function f1 (f1_arg1) + integer, value :: f1_arg1 + integer :: f1 + f1 = abs(f1_arg1 + 0) +end function +! { dg-final { scan-tree-dump {__result_f1 = ABS_EXPR <f1_arg1>;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a complement +! operator. +function f2 (f2_arg1) + integer, value :: f2_arg1 + integer :: f2 + f2 = not(f2_arg1 + 0) +end function +! { dg-final { scan-tree-dump {__result_f2 = ~f2_arg1;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a conjugate +! operator. +function f3 (f3_arg1) + complex, value :: f3_arg1 + complex :: f3 + f3 = conjg(conjg(conjg(f3_arg1))) +end function +! { dg-final { scan-tree-dump {__result_f3 = CONJ_EXPR <f3_arg1>;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a type conversion +! operator. +function f4 (f4_arg1) + integer(kind=4), value :: f4_arg1 + integer(kind=8) :: f4 + f4 = f4_arg1 + 0 +end function +! { dg-final { scan-tree-dump {__result_f4 = \(integer\(kind=8\)\) f4_arg1;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a float conversion +! operator. +function f5 (f5_arg1) + integer, value :: f5_arg1 + real :: f5 + f5 = f5_arg1 + 0 +end function +! { dg-final { scan-tree-dump {__result_f5 = \(real\(kind=4\)\) f5_arg1;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a negate operator. +function f6 (f6_arg1) + integer, value :: f6_arg1 + integer :: f6 + f6 = -not(not(f6_arg1)) +end function +! { dg-final { scan-tree-dump {__result_f6 = -f6_arg1;} "original" } } diff --git a/gcc/testsuite/gfortran.dg/non_lvalue_3.f90 b/gcc/testsuite/gfortran.dg/non_lvalue_3.f90 new file mode 100644 index 000000000000..280859c133b8 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/non_lvalue_3.f90 @@ -0,0 +1,172 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-original" } +! +! Check the unwrapping of the NON_LVALUE_EXPR that may come from an earlier +! simplification, if it is used in a binary operator context. + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a LT logical +! operator +subroutine f01 (f01_res1, f01_res2, f01_arg1, f01_arg2) + integer, value :: f01_arg1, f01_arg2 + logical, intent(out) :: f01_res1, f01_res2 + f01_res1 = f01_arg1 + 0 .lt. f01_arg2 + f01_res2 = f01_arg1 .lt. f01_arg2 + 0 +end subroutine +! { dg-final { scan-tree-dump {\*f01_res1 = f01_arg1 < f01_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f01_res2 = f01_arg1 < f01_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a LE logical +! operator +subroutine f02 (f02_res1, f02_res2, f02_arg1, f02_arg2) + integer, value :: f02_arg1, f02_arg2 + logical, intent(out) :: f02_res1, f02_res2 + f02_res1 = f02_arg1 + 0 .le. f02_arg2 + f02_res2 = f02_arg1 .le. f02_arg2 + 0 +end subroutine +! { dg-final { scan-tree-dump {\*f02_res1 = f02_arg1 <= f02_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f02_res2 = f02_arg1 <= f02_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a GT logical +! operator +subroutine f03 (f03_res1, f03_res2, f03_arg1, f03_arg2) + integer, value :: f03_arg1, f03_arg2 + logical, intent(out) :: f03_res1, f03_res2 + f03_res1 = f03_arg1 + 0 .gt. f03_arg2 + f03_res2 = f03_arg1 .gt. f03_arg2 + 0 +end subroutine +! { dg-final { scan-tree-dump {\*f03_res1 = f03_arg1 > f03_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f03_res2 = f03_arg1 > f03_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a GE logical +! operator +subroutine f04 (f04_res1, f04_res2, f04_arg1, f04_arg2) + integer, value :: f04_arg1, f04_arg2 + logical, intent(out) :: f04_res1, f04_res2 + f04_res1 = f04_arg1 + 0 .ge. f04_arg2 + f04_res2 = f04_arg1 .ge. f04_arg2 + 0 +end subroutine +! { dg-final { scan-tree-dump {\*f04_res1 = f04_arg1 >= f04_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f04_res2 = f04_arg1 >= f04_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a LTGT logical +! operator +subroutine f05 (f05_res1, f05_res2, f05_arg1, f05_arg2) + real, value :: f05_arg1, f05_arg2 + logical, intent(out) :: f05_res1, f05_res2 + f05_res1 = f05_arg1 * 1 .lt. f05_arg2 .or. f05_arg1 * 1 .gt. f05_arg2 + f05_res2 = f05_arg1 .lt. f05_arg2 * 1 .or. f05_arg1 .gt. f05_arg2 * 1 +end subroutine +! { dg-final { scan-tree-dump {\*f05_res1 = f05_arg1 <> f05_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f05_res2 = f05_arg1 <> f05_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a EQ logical +! operator +subroutine f06 (f06_res1, f06_res2, f06_arg1, f06_arg2) + integer, value :: f06_arg1, f06_arg2 + logical, intent(out) :: f06_res1, f06_res2 + f06_res1 = f06_arg1 + 0 .eq. f06_arg2 + f06_res2 = f06_arg1 .eq. f06_arg2 + 0 +end subroutine +! { dg-final { scan-tree-dump {\*f06_res1 = f06_arg1 == f06_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f06_res2 = f06_arg1 == f06_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a NE logical +! operator +subroutine f07 (f07_res1, f07_res2, f07_arg1, f07_arg2) + integer, value :: f07_arg1, f07_arg2 + logical, intent(out) :: f07_res1, f07_res2 + f07_res1 = f07_arg1 + 0 .ne. f07_arg2 + f07_res2 = f07_arg1 .ne. f07_arg2 + 0 +end subroutine +! { dg-final { scan-tree-dump {\*f07_res1 = f07_arg1 != f07_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f07_res2 = f07_arg1 != f07_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a PLUS operator +subroutine f08 (f08_res1, f08_res2, f08_arg1, f08_arg2) + integer, value :: f08_arg1, f08_arg2 + integer, intent(out) :: f08_res1, f08_res2 + f08_res1 = f08_arg1 * 1 + f08_arg2 + f08_res2 = f08_arg1 + f08_arg2 * 1 +end subroutine +! { dg-final { scan-tree-dump {\*f08_res1 = f08_arg1 \+ f08_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f08_res2 = f08_arg1 \+ f08_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a MINUS operator +subroutine f09 (f09_res1, f09_res2, f09_arg1, f09_arg2) + integer, value :: f09_arg1, f09_arg2 + integer, intent(out) :: f09_res1, f09_res2 + f09_res1 = f09_arg1 * 1 - f09_arg2 + f09_res2 = f09_arg1 - f09_arg2 * 1 +end subroutine +! { dg-final { scan-tree-dump {\*f09_res1 = f09_arg1 - f09_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f09_res2 = f09_arg1 - f09_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a MULT operator +subroutine f10 (f10_res1, f10_res2, f10_arg1, f10_arg2) + integer, value :: f10_arg1, f10_arg2 + integer, intent(out) :: f10_res1, f10_res2 + f10_res1 = not(not(f10_arg1)) * f10_arg2 + f10_res2 = f10_arg1 * not(not(f10_arg2)) +end subroutine +! { dg-final { scan-tree-dump {\*f10_res1 = f10_arg1 \* f10_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f10_res2 = f10_arg1 \* f10_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a DIV operator +subroutine f11 (f11_res1, f11_res2, f11_arg1, f11_arg2) + integer, value :: f11_arg1, f11_arg2 + integer, intent(out) :: f11_res1, f11_res2 + f11_res1 = not(not(f11_arg1)) / f11_arg2 + f11_res2 = f11_arg1 / not(not(f11_arg2)) +end subroutine +! { dg-final { scan-tree-dump {\*f11_res1 = f11_arg1 / f11_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f11_res2 = f11_arg1 / f11_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a MOD operator +subroutine f12 (f12_res1, f12_res2, f12_arg1, f12_arg2) + integer, value :: f12_arg1, f12_arg2 + integer, intent(out) :: f12_res1, f12_res2 + f12_res1 = mod(f12_arg1 + 0, f12_arg2) + f12_res2 = mod(f12_arg1, f12_arg2 + 0) +end subroutine +! { dg-final { scan-tree-dump {\*f12_res1 = f12_arg1 % f12_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f12_res2 = f12_arg1 % f12_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to an OR operator +subroutine f13 (f13_res1, f13_res2, f13_arg1, f13_arg2) + integer, value :: f13_arg1, f13_arg2 + integer, intent(out) :: f13_res1, f13_res2 + f13_res1 = ior(f13_arg1 + 0, f13_arg2) + f13_res2 = ior(f13_arg1, f13_arg2 + 0) +end subroutine +! { dg-final { scan-tree-dump {\*f13_res1 = f13_arg1 \| f13_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f13_res2 = f13_arg1 \| f13_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a XOR operator +subroutine f14 (f14_res1, f14_res2, f14_arg1, f14_arg2) + integer, value :: f14_arg1, f14_arg2 + integer, intent(out) :: f14_res1, f14_res2 + f14_res1 = ieor(f14_arg1 + 0, f14_arg2) + f14_res2 = ieor(f14_arg1, f14_arg2 + 0) +end subroutine +! { dg-final { scan-tree-dump {\*f14_res1 = f14_arg1 \^ f14_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f14_res2 = f14_arg1 \^ f14_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to an AND operator +subroutine f15 (f15_res1, f15_res2, f15_arg1, f15_arg2) + integer, value :: f15_arg1, f15_arg2 + integer, intent(out) :: f15_res1, f15_res2 + f15_res1 = iand(f15_arg1 + 0, f15_arg2) + f15_res2 = iand(f15_arg1, f15_arg2 + 0) +end subroutine +! { dg-final { scan-tree-dump {\*f15_res1 = f15_arg1 & f15_arg2;} "original" } } +! { dg-final { scan-tree-dump {\*f15_res2 = f15_arg1 & f15_arg2;} "original" } } + +! The NON_LVALUE_EXPR is dropped if it's used as argument to a complex constructor +subroutine f16 (f16_res1, f16_res2, f16_arg1, f16_arg2) + real, value :: f16_arg1, f16_arg2 + complex, intent(out) :: f16_res1, f16_res2 + f16_res1 = cmplx(f16_arg1 * 1, f16_arg2) + f16_res2 = cmplx(f16_arg1, f16_arg2 * 1) +end subroutine +! { dg-final { scan-tree-dump {\*f16_res1 = COMPLEX_EXPR <f16_arg1, f16_arg2>;} "original" } } +! { dg-final { scan-tree-dump {\*f16_res2 = COMPLEX_EXPR <f16_arg1, f16_arg2>;} "original" } }