diff options
-rw-r--r-- | gcc/cp/cp-gimplify.c | 65 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1z/eval-order11.C | 89 |
2 files changed, 151 insertions, 3 deletions
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index c86a5fe..c1691c3 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -398,6 +398,47 @@ gimplify_to_rvalue (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, return t; } +/* Like gimplify_arg, but if ORDERED is set (which should be set if + any of the arguments this argument is sequenced before has + TREE_SIDE_EFFECTS set, make sure expressions with is_gimple_reg_type type + are gimplified into SSA_NAME or a fresh temporary and for + non-is_gimple_reg_type we don't optimize away TARGET_EXPRs. */ + +static enum gimplify_status +cp_gimplify_arg (tree *arg_p, gimple_seq *pre_p, location_t call_location, + bool ordered) +{ + enum gimplify_status t; + if (ordered + && !is_gimple_reg_type (TREE_TYPE (*arg_p)) + && TREE_CODE (*arg_p) == TARGET_EXPR) + { + /* gimplify_arg would strip away the TARGET_EXPR, but + that can mean we don't copy the argument and some following + argument with side-effect could modify it. */ + protected_set_expr_location (*arg_p, call_location); + return gimplify_expr (arg_p, pre_p, NULL, is_gimple_lvalue, fb_either); + } + else + { + t = gimplify_arg (arg_p, pre_p, call_location); + if (t == GS_ERROR) + return GS_ERROR; + else if (ordered + && is_gimple_reg_type (TREE_TYPE (*arg_p)) + && is_gimple_variable (*arg_p) + && TREE_CODE (*arg_p) != SSA_NAME + /* No need to force references into register, references + can't be modified. */ + && !TYPE_REF_P (TREE_TYPE (*arg_p)) + /* And this can't be modified either. */ + && *arg_p != current_class_ptr) + *arg_p = get_initialized_tmp_var (*arg_p, pre_p); + return t; + } + +} + /* Do C++-specific gimplification. Args are as for gimplify_expr. */ int @@ -613,7 +654,8 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) gcc_assert (call_expr_nargs (*expr_p) == 2); gcc_assert (!CALL_EXPR_ORDERED_ARGS (*expr_p)); enum gimplify_status t - = gimplify_arg (&CALL_EXPR_ARG (*expr_p, 1), pre_p, loc); + = cp_gimplify_arg (&CALL_EXPR_ARG (*expr_p, 1), pre_p, loc, + TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, 0))); if (t == GS_ERROR) ret = GS_ERROR; } @@ -622,10 +664,18 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) /* Leave the last argument for gimplify_call_expr, to avoid problems with __builtin_va_arg_pack(). */ int nargs = call_expr_nargs (*expr_p) - 1; + int last_side_effects_arg = -1; + for (int i = nargs; i > 0; --i) + if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i))) + { + last_side_effects_arg = i; + break; + } for (int i = 0; i < nargs; ++i) { enum gimplify_status t - = gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, loc); + = cp_gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, loc, + i < last_side_effects_arg); if (t == GS_ERROR) ret = GS_ERROR; } @@ -639,8 +689,17 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) fntype = TREE_TYPE (fntype); if (TREE_CODE (fntype) == METHOD_TYPE) { + int nargs = call_expr_nargs (*expr_p); + bool side_effects = false; + for (int i = 1; i < nargs; ++i) + if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i))) + { + side_effects = true; + break; + } enum gimplify_status t - = gimplify_arg (&CALL_EXPR_ARG (*expr_p, 0), pre_p, loc); + = cp_gimplify_arg (&CALL_EXPR_ARG (*expr_p, 0), pre_p, loc, + side_effects); if (t == GS_ERROR) ret = GS_ERROR; } diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order11.C b/gcc/testsuite/g++.dg/cpp1z/eval-order11.C new file mode 100644 index 0000000..19a24c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/eval-order11.C @@ -0,0 +1,89 @@ +// PR c++/70796 +// { dg-do run { target c++11 } } +// { dg-options "-fstrong-eval-order" { target c++14_down } } + +struct A +{ + int x = 0; + A & operator ++ () { ++x; return *this; } +}; +struct B +{ + A first, second; + B (A x, A y) : first{x}, second{y} {} +}; +struct C +{ + int first, second; + C (int x, int y) : first{x}, second{y} {} +}; +struct D +{ + int d; + void foo (int x, D *y) + { + if (y != this + 1) + __builtin_abort (); + d = x; + } +}; +D d[2] = { { 1 }, { 2 } }; + +void +foo () +{ + int i = 0; + C p{++i, ++i}; + if (p.first != 1 || p.second != 2) + __builtin_abort (); +} + +void +bar () +{ + int i = 0; + C p{++i, ++i}; + if (p.first != 1 || p.second != 2) + __builtin_abort (); + int &j = i; + C q{++j, ++j}; + if (q.first != 3 || q.second != 4) + __builtin_abort (); +} + +void +baz () +{ + int i = 0; + C p{(int &) ++i, (int &) ++i}; + if (p.first != 1 || p.second != 2) + __builtin_abort (); +} + +void +qux () +{ + A i; + B p{++i, ++i}; + if (p.first.x != 1 || p.second.x != 2) + __builtin_abort (); +} + +void +corge () +{ + D *p = &d[0]; + p->foo (3, ++p); + if (d[0].d != 3 || d[1].d != 2) + __builtin_abort (); +} + +int +main () +{ + bar (); + baz (); + foo (); + qux (); + corge (); +} |