As discussed in bug 71104, the C++ P0145 proposal specifies the evaluation order of certain operations:

1. a.b
2. a->b
3. a->*b
4. a(b1, b2, b3)
5. b @= a
6. a[b]
7. a << b
8. a >> b

The second patch introduces a flag -fargs-in-order to control whether these orders are enforced on calls. -fargs-in-order=1 enforces all but the ordering between function arguments in #4.

The first patch implements #5 for the built-in assignment operator by changing the order of gimplification of MODIFY_EXPR in the back end, as richi was also thinking about doing to fix 71104. This runs into problems with DECL_VALUE_EXPR variables, where is_gimple_reg can be true before gimplification and false afterward, so he checks for this situation in rhs_predicate_for. richi, you said you were still working on 71104; is this patch OK to put in for now, or should I wait for something better?

For the moment the patch turns on full ordering by default so we can see what effect it has in routine benchmarking. This will probably back off by the time of the GCC 7 release.

In my own SPEC runs with an earlier version of the patch, most of the C++ tests did not change significantly, but xalancbmk slowed down about 1%. I'm running it again now with the current patch.

Tested x86_64-pc-linux-gnu, applying second patch to trunk, is the first patch OK as well?

Jason
commit ec26b9bc7d98fb127d0e38b1b935278e54bd62c4
Author: Jason Merrill <ja...@redhat.com>
Date:   Fri Jun 10 14:55:54 2016 -0400

    	Gimplify RHS of assignment first.
    
    	* gimplify.c (rhs_predicate_for): Check DECL_HAS_VALUE_EXPR_P.
    	(gimplify_modify_expr): Gimplify RHS first.

diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index ae8b4fc..8608569 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -3807,7 +3807,9 @@ gimplify_init_ctor_eval (tree object, vec<constructor_elt, va_gc> *elts,
 gimple_predicate
 rhs_predicate_for (tree lhs)
 {
-  if (is_gimple_reg (lhs))
+  if (is_gimple_reg (lhs)
+      && (!DECL_P (lhs)
+	  || !DECL_HAS_VALUE_EXPR_P (lhs)))
     return is_gimple_reg_rhs_or_call;
   else
     return is_gimple_mem_rhs_or_call;
@@ -4778,10 +4780,6 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
      that is what we must do here.  */
   maybe_with_size_expr (from_p);
 
-  ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
-  if (ret == GS_ERROR)
-    return ret;
-
   /* As a special case, we have to temporarily allow for assignments
      with a CALL_EXPR on the RHS.  Since in GIMPLE a function call is
      a toplevel statement, when gimplifying the GENERIC expression
@@ -4799,6 +4797,10 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   if (ret == GS_ERROR)
     return ret;
 
+  ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
+  if (ret == GS_ERROR)
+    return ret;
+
   /* In case of va_arg internal fn wrappped in a WITH_SIZE_EXPR, add the type
      size as argument to the call.  */
   if (TREE_CODE (*from_p) == WITH_SIZE_EXPR)
diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order3.C b/gcc/testsuite/g++.dg/cpp1z/eval-order3.C
index 15df903..d351219 100644
--- a/gcc/testsuite/g++.dg/cpp1z/eval-order3.C
+++ b/gcc/testsuite/g++.dg/cpp1z/eval-order3.C
@@ -84,7 +84,7 @@ template <class T> void f()
 
   // b @= a
   aref(19)=A(18);
-  //iref(21)=f(20);
+  iref(21)=f(20);
   aref(23)+=f(22);
   last = 0;
 
@@ -123,7 +123,7 @@ void g()
 
   // b @= a
   aref(19)=A(18);
-  //iref(21)=f(20);
+  iref(21)=f(20);
   aref(23)+=f(22);
   last = 0;
 
commit 0516df95f13b7d30b17d8412739afc3c8d18df6e
Author: Jason Merrill <ja...@redhat.com>
Date:   Mon Jun 6 16:50:48 2016 -0400

    	P0145R2: Refining Expression Order for C++.
    
    gcc/c-family/
    	* c.opt (fargs-in-order): New.
    	* c-opts.c (c_common_post_options): Adjust flag_args_in_order.
    gcc/cp/
    	* cp-tree.h (CALL_EXPR_OPERATOR_SYNTAX, CALL_EXPR_ORDERED_ARGS)
    	(CALL_EXPR_REVERSE_ARGS): New.
    	* call.c (build_new_op_1): Set them.
    	(extract_call_expr, op_is_ordered): New.
    	(build_over_call): Set CALL_EXPR_ORDERED_ARGS.
    	* cp-gimplify.c (cp_gimplify_expr) [CALL_EXPR]: Handle new flags.
    	* pt.c (tsubst_copy_and_build): Copy new flags.
    	* semantics.c (simplify_aggr_init_expr): Likewise.
    	* tree.c (build_aggr_init_expr): Likewise.
    	(build_min_non_dep_op_overload): Likewise.

diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index fec58bc..ff6339c 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -910,6 +910,12 @@ c_common_post_options (const char **pfilename)
   else if (warn_narrowing == -1)
     warn_narrowing = 0;
 
+  /* C++17 requires that function arguments be evaluated left-to-right even on
+     PUSH_ARGS_REVERSED targets.  */
+  if (c_dialect_cxx ()
+      && flag_args_in_order == -1)
+    flag_args_in_order = 2 /*(cxx_dialect >= cxx1z) ? 2 : 0*/;
+
   /* Global sized deallocation is new in C++14.  */
   if (flag_sized_deallocation == -1)
     flag_sized_deallocation = (cxx_dialect >= cxx14);
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 761a83b..83fd84c 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1043,6 +1043,14 @@ falt-external-templates
 C++ ObjC++ Ignore Warn(switch %qs is no longer supported)
 No longer supported.
 
+fargs-in-order
+C++ ObjC++ Alias(fargs-in-order=, 2, 0)
+Always evaluate function arguments in left-to-right order.
+
+fargs-in-order=
+C++ ObjC++ Var(flag_args_in_order) Joined UInteger Init(-1)
+Always evaluate function arguments in left-to-right order.
+
 fasm
 C ObjC C++ ObjC++ Var(flag_no_asm, 0)
 Recognize the \"asm\" keyword.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 729b7eb..e2b89b8 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5372,6 +5372,40 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
     }
 }
 
+/* Returns 1 if P0145R2 says that the LHS of operator CODE is evaluated first,
+   -1 if the RHS is evaluated first, or 0 if the order is unspecified.  */
+
+static int
+op_is_ordered (tree_code code)
+{
+  if (!flag_args_in_order)
+    return 0;
+
+  switch (code)
+    {
+      // 5. b @= a
+    case MODIFY_EXPR:
+      return -1;
+
+      // 1. a.b
+      // Not overloadable (yet).
+      // 2. a->b
+      // Only one argument.
+      // 3. a->*b
+    case MEMBER_REF:
+      // 6. a[b]
+    case ARRAY_REF:
+      // 7. a << b
+    case LSHIFT_EXPR:
+      // 8. a >> b
+    case RSHIFT_EXPR:
+      return 1;
+
+    default:
+      return 0;
+    }
+}
+
 static tree
 build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 		tree arg2, tree arg3, tree *overload, tsubst_flags_t complain)
@@ -5660,17 +5694,33 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 	  else
 	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
 
-	  if (processing_template_decl
-	      && result != NULL_TREE
-	      && result != error_mark_node
-	      && DECL_HIDDEN_FRIEND_P (cand->fn))
+	  if (trivial_fn_p (cand->fn))
+	    /* There won't be a CALL_EXPR.  */;
+	  else if (result && result != error_mark_node)
 	    {
-	      tree call = result;
-	      if (REFERENCE_REF_P (call))
-		call = TREE_OPERAND (call, 0);
-	      /* This prevents build_new_function_call from discarding this
-		 function during instantiation of the enclosing template.  */
-	      KOENIG_LOOKUP_P (call) = 1;
+	      tree call = extract_call_expr (result);
+	      CALL_EXPR_OPERATOR_SYNTAX (call) = true;
+
+	      if (processing_template_decl && DECL_HIDDEN_FRIEND_P (cand->fn))
+		/* This prevents build_new_function_call from discarding this
+		   function during instantiation of the enclosing template.  */
+		KOENIG_LOOKUP_P (call) = 1;
+
+	      /* Specify evaluation order as per P0145R2.  */
+	      CALL_EXPR_ORDERED_ARGS (call) = false;
+	      switch (op_is_ordered (code))
+		{
+		case -1:
+		  CALL_EXPR_REVERSE_ARGS (call) = true;
+		  break;
+
+		case 1:
+		  CALL_EXPR_ORDERED_ARGS (call) = true;
+		  break;
+
+		default:
+		  break;
+		}
 	    }
 	}
       else
@@ -5846,6 +5896,25 @@ build_new_op (location_t loc, enum tree_code code, int flags,
   return ret;
 }
 
+/* CALL was returned by some call-building function; extract the actual
+   CALL_EXPR from any bits that have been tacked on, e.g. by
+   convert_from_reference.  */
+
+tree
+extract_call_expr (tree call)
+{
+  while (TREE_CODE (call) == COMPOUND_EXPR)
+    call = TREE_OPERAND (call, 1);
+  if (REFERENCE_REF_P (call))
+    call = TREE_OPERAND (call, 0);
+  if (TREE_CODE (call) == TARGET_EXPR)
+    call = TARGET_EXPR_INITIAL (call);
+  gcc_assert (TREE_CODE (call) == CALL_EXPR
+	      || TREE_CODE (call) == AGGR_INIT_EXPR
+	      || call == error_mark_node);
+  return call;
+}
+
 /* Returns true if FN has two parameters, of which the second has type
    size_t.  */
 
@@ -7533,10 +7602,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
     }
 
   /* Ellipsis */
+  int magic = magic_varargs_p (fn);
   for (; arg_index < vec_safe_length (args); ++arg_index)
     {
       tree a = (*args)[arg_index];
-      int magic = magic_varargs_p (fn);
       if (magic == 2)
 	{
 	  /* Do no conversions for certain magic varargs.  */
@@ -7666,9 +7735,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       if (is_really_empty_class (type))
 	{
 	  /* Avoid copying empty classes.  */
-	  val = build2 (COMPOUND_EXPR, void_type_node, to, arg);
-	  TREE_NO_WARNING (val) = 1;
-	  val = build2 (COMPOUND_EXPR, type, val, to);
+	  val = build2 (COMPOUND_EXPR, type, arg, to);
 	  TREE_NO_WARNING (val) = 1;
 	}
       else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
@@ -7756,9 +7823,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
     }
 
   tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
-  if (TREE_CODE (call) == CALL_EXPR
-      && (cand->flags & LOOKUP_LIST_INIT_CTOR))
-    CALL_EXPR_LIST_INIT_P (call) = true;
+  if (call != error_mark_node
+      && !magic
+      && (flag_args_in_order > 1
+	  || (cand->flags & LOOKUP_LIST_INIT_CTOR)))
+    {
+      tree c = extract_call_expr (call);
+      /* build_new_op_1 will clear this when appropriate.  */
+      CALL_EXPR_ORDERED_ARGS (c) = true;
+    }
   return call;
 }
 
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index dcb0fa6..97b043a 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -565,6 +565,7 @@ int
 cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 {
   int saved_stmts_are_full_exprs_p = 0;
+  location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
   enum tree_code code = TREE_CODE (*expr_p);
   enum gimplify_status ret;
 
@@ -752,18 +753,26 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	  cilk_cp_gimplify_call_params_in_spawned_fn (expr_p, pre_p, post_p);
 	  return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
 	}
-      /* DR 1030 says that we need to evaluate the elements of an
-	 initializer-list in forward order even when it's used as arguments to
-	 a constructor.  So if the target wants to evaluate them in reverse
-	 order and there's more than one argument other than 'this', gimplify
-	 them in order.  */
       ret = GS_OK;
-      if (PUSH_ARGS_REVERSED && CALL_EXPR_LIST_INIT_P (*expr_p)
-	  && call_expr_nargs (*expr_p) > 2)
+      if (!CALL_EXPR_FN (*expr_p))
+	/* Internal function call.  */;
+      else if (CALL_EXPR_REVERSE_ARGS (*expr_p))
 	{
-	  int nargs = call_expr_nargs (*expr_p);
-	  location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
-	  for (int i = 1; i < nargs; ++i)
+	  /* This is a call to a (compound) assignment operator that used
+	     the operator syntax; gimplify the RHS first.  */
+	  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);
+	  if (t == GS_ERROR)
+	    ret = GS_ERROR;
+	}
+      else if (CALL_EXPR_ORDERED_ARGS (*expr_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;
+	  for (int i = 0; i < nargs; ++i)
 	    {
 	      enum gimplify_status t
 		= gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, loc);
@@ -771,6 +780,22 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 		ret = GS_ERROR;
 	    }
 	}
+      else if (flag_args_in_order == 1
+	       && !CALL_EXPR_OPERATOR_SYNTAX (*expr_p))
+	{
+	  /* If flag_args_in_order == 1, we don't force an order on all
+	     function arguments, but do evaluate the object argument first.  */
+	  tree fntype = TREE_TYPE (CALL_EXPR_FN (*expr_p));
+	  if (POINTER_TYPE_P (fntype))
+	    fntype = TREE_TYPE (fntype);
+	  if (TREE_CODE (fntype) == METHOD_TYPE)
+	    {
+	      enum gimplify_status t
+		= gimplify_arg (&CALL_EXPR_ARG (*expr_p, 0), pre_p, loc);
+	      if (t == GS_ERROR)
+		ret = GS_ERROR;
+	    }
+	}
       break;
 
     case RETURN_EXPR:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 14ba120..6c6ad10 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -179,19 +179,21 @@ operator == (const cp_expr &lhs, tree rhs)
       IDENTIFIER_CTOR_OR_DTOR_P (in IDENTIFIER_NODE)
       BIND_EXPR_BODY_BLOCK (in BIND_EXPR)
       DECL_NON_TRIVIALLY_INITIALIZED_P (in VAR_DECL)
-      CALL_EXPR_LIST_INIT_P (in CALL_EXPR, AGGR_INIT_EXPR)
+      CALL_EXPR_ORDERED_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
    4: TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
-	  or FIELD_DECL).
+	  CALL_EXPR, or FIELD_DECL).
       IDENTIFIER_TYPENAME_P (in IDENTIFIER_NODE)
       DECL_TINFO_P (in VAR_DECL)
       FUNCTION_REF_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
    5: C_IS_RESERVED_WORD (in IDENTIFIER_NODE)
       DECL_VTABLE_OR_VTT_P (in VAR_DECL)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
+      CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
    6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
       DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
       TYPE_MARKED_P (in _TYPE)
       RANGE_FOR_IVDEP (in RANGE_FOR_STMT)
+      CALL_EXPR_OPERATOR_SYNTAX (in CALL_EXPR, AGGR_INIT_EXPR)
 
    Usage of TYPE_LANG_FLAG_?:
    0: TYPE_DEPENDENT_P
@@ -3379,6 +3381,9 @@ extern void decl_shadowed_for_var_insert (tree, tree);
 #define DELETE_EXPR_USE_VEC(NODE) \
   TREE_LANG_FLAG_1 (DELETE_EXPR_CHECK (NODE))
 
+#define CALL_OR_AGGR_INIT_CHECK(NODE) \
+  TREE_CHECK2 ((NODE), CALL_EXPR, AGGR_INIT_EXPR)
+
 /* Indicates that this is a non-dependent COMPOUND_EXPR which will
    resolve to a function call.  */
 #define COMPOUND_EXPR_OVERLOADED(NODE) \
@@ -3388,9 +3393,20 @@ extern void decl_shadowed_for_var_insert (tree, tree);
    should be performed at instantiation time.  */
 #define KOENIG_LOOKUP_P(NODE) TREE_LANG_FLAG_0 (CALL_EXPR_CHECK (NODE))
 
-/* True if CALL_EXPR expresses list-initialization of an object.  */
-#define CALL_EXPR_LIST_INIT_P(NODE) \
-  TREE_LANG_FLAG_3 (TREE_CHECK2 ((NODE),CALL_EXPR,AGGR_INIT_EXPR))
+/* True if the arguments to NODE should be evaluated in left-to-right
+   order regardless of PUSH_ARGS_REVERSED.  */
+#define CALL_EXPR_ORDERED_ARGS(NODE) \
+  TREE_LANG_FLAG_3 (CALL_OR_AGGR_INIT_CHECK (NODE))
+
+/* True if the arguments to NODE should be evaluated in right-to-left
+   order regardless of PUSH_ARGS_REVERSED.  */
+#define CALL_EXPR_REVERSE_ARGS(NODE) \
+  TREE_LANG_FLAG_5 (CALL_OR_AGGR_INIT_CHECK (NODE))
+
+/* True if CALL_EXPR was written as an operator expression, not a function
+   call.  */
+#define CALL_EXPR_OPERATOR_SYNTAX(NODE) \
+  TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE))
 
 /* Indicates whether a string literal has been parenthesized. Such
    usages are disallowed in certain circumstances.  */
@@ -5542,6 +5558,7 @@ extern bool null_ptr_cst_p			(tree);
 extern bool null_member_pointer_value_p		(tree);
 extern bool sufficient_parms_p			(const_tree);
 extern tree type_decays_to			(tree);
+extern tree extract_call_expr			(tree);
 extern tree build_user_type_conversion		(tree, tree, int,
 						 tsubst_flags_t);
 extern tree build_new_function_call		(tree, vec<tree, va_gc> **, bool, 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3a3d9b8..11b5d82 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -16652,6 +16652,20 @@ tsubst_copy_and_build (tree t,
 
 	release_tree_vector (call_args);
 
+	if (ret != error_mark_node)
+	  {
+	    bool op = CALL_EXPR_OPERATOR_SYNTAX (t);
+	    bool ord = CALL_EXPR_ORDERED_ARGS (t);
+	    bool rev = CALL_EXPR_REVERSE_ARGS (t);
+	    if (op || ord || rev)
+	      {
+		function = extract_call_expr (ret);
+		CALL_EXPR_OPERATOR_SYNTAX (function) = op;
+		CALL_EXPR_ORDERED_ARGS (function) = ord;
+		CALL_EXPR_REVERSE_ARGS (function) = rev;
+	      }
+	  }
+
 	RETURN (ret);
       }
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5365091..9b0cff8 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4057,8 +4057,11 @@ simplify_aggr_init_expr (tree *tp)
 				    aggr_init_expr_nargs (aggr_init_expr),
 				    AGGR_INIT_EXPR_ARGP (aggr_init_expr));
   TREE_NOTHROW (call_expr) = TREE_NOTHROW (aggr_init_expr);
-  CALL_EXPR_LIST_INIT_P (call_expr) = CALL_EXPR_LIST_INIT_P (aggr_init_expr);
   CALL_FROM_THUNK_P (call_expr) = AGGR_INIT_FROM_THUNK_P (aggr_init_expr);
+  CALL_EXPR_OPERATOR_SYNTAX (call_expr)
+    = CALL_EXPR_OPERATOR_SYNTAX (aggr_init_expr);
+  CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (aggr_init_expr);
+  CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (aggr_init_expr);
 
   if (style == ctor)
     {
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index abda6e4..9ab964d 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -524,7 +524,9 @@ build_aggr_init_expr (tree type, tree init)
       TREE_SIDE_EFFECTS (rval) = 1;
       AGGR_INIT_VIA_CTOR_P (rval) = is_ctor;
       TREE_NOTHROW (rval) = TREE_NOTHROW (init);
-      CALL_EXPR_LIST_INIT_P (rval) = CALL_EXPR_LIST_INIT_P (init);
+      CALL_EXPR_OPERATOR_SYNTAX (rval) = CALL_EXPR_OPERATOR_SYNTAX (init);
+      CALL_EXPR_ORDERED_ARGS (rval) = CALL_EXPR_ORDERED_ARGS (init);
+      CALL_EXPR_REVERSE_ARGS (rval) = CALL_EXPR_REVERSE_ARGS (init);
     }
   else
     rval = init;
@@ -2854,8 +2856,7 @@ build_min_non_dep_op_overload (enum tree_code op,
   tree fn, call;
   vec<tree, va_gc> *args;
 
-  if (REFERENCE_REF_P (non_dep))
-    non_dep = TREE_OPERAND (non_dep, 0);
+  non_dep = extract_call_expr (non_dep);
 
   nargs = call_expr_nargs (non_dep);
 
@@ -2897,10 +2898,11 @@ build_min_non_dep_op_overload (enum tree_code op,
   call = build_min_non_dep_call_vec (non_dep, fn, args);
   release_tree_vector (args);
 
-  tree call_expr = call;
-  if (REFERENCE_REF_P (call_expr))
-    call_expr = TREE_OPERAND (call_expr, 0);
+  tree call_expr = extract_call_expr (call);
   KOENIG_LOOKUP_P (call_expr) = KOENIG_LOOKUP_P (non_dep);
+  CALL_EXPR_OPERATOR_SYNTAX (call_expr) = true;
+  CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (non_dep);
+  CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (non_dep);
 
   return call;
 }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 223fd86..2105351 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -189,7 +189,8 @@ in the following sections.
 
 @item C++ Language Options
 @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
-@gccoptlist{-fabi-version=@var{n}  -fno-access-control  -fcheck-new @gol
+@gccoptlist{-fabi-version=@var{n}  -fno-access-control @gol
+-fargs-in-order=@var{n} -fcheck-new @gol
 -fconstexpr-depth=@var{n}  -ffriend-injection @gol
 -fno-elide-constructors @gol
 -fno-enforce-eh-specs @gol
@@ -2233,6 +2234,14 @@ option is used for the warning.
 Turn off all access checking.  This switch is mainly useful for working
 around bugs in the access control code.
 
+@item -fargs-in-order
+@opindex fargs-in-order
+Evaluate function arguments and operands of some binary expressions in
+left-to-right order, and evaluate the right side of an assignment
+before the left side, as proposed in P0145R2.  Enabled by default with
+@option{-std=c++1z}.  @option{-fargs-in-order=1} implements all of the
+ordering requirements except function arguments.
+
 @item -fcheck-new
 @opindex fcheck-new
 Check that the pointer returned by @code{operator new} is non-null
diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order1.C b/gcc/testsuite/g++.dg/cpp1z/eval-order1.C
new file mode 100644
index 0000000..278990d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/eval-order1.C
@@ -0,0 +1,21 @@
+// P0145R2: Refining Expression Order for C++
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+extern "C" int printf (const char *, ...);
+void sink(...) { }
+
+int last = 0;
+int f(int i)
+{
+  if (i < last)
+    __builtin_abort ();
+  last = i;
+  return i;
+}
+
+int main()
+{
+  sink(f(1), f(2));
+  sink(f(3), f(4), f(5));
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order2.C b/gcc/testsuite/g++.dg/cpp1z/eval-order2.C
new file mode 100644
index 0000000..2a741d6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/eval-order2.C
@@ -0,0 +1,15 @@
+// P0145R2: Refining Expression Order for C++
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+#include <string>
+#define assert(X) if (!(X)) __builtin_abort();
+
+int main()
+{
+  std::string s = "but I have heard it works even if you don't believe in it" ;
+  s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
+    .replace( s.find( " don't" ), 6, "" );
+
+  assert( s == "I have heard it works only if you believe in it" ) ;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/eval-order3.C b/gcc/testsuite/g++.dg/cpp1z/eval-order3.C
new file mode 100644
index 0000000..15df903
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/eval-order3.C
@@ -0,0 +1,150 @@
+// P0145R2: Refining Expression Order for C++
+// { dg-do run }
+// { dg-options "-std=c++1z -fargs-in-order=1" }
+
+extern "C" int printf (const char *, ...);
+void sink(...) { }
+
+int last = 0;
+int f(int i)
+{
+  if (i < last)
+    __builtin_abort ();
+  last = i;
+  return i;
+}
+
+int& g(int i)
+{
+  static int dummy;
+  f(i);
+  return dummy;
+}
+
+struct A
+{
+  int _i;
+  A(int i): _i(f(i)) { }
+  A& memfn(int i, int j) { f(j); return *this; }
+  int operator<<(int i) { }
+  A& operator=(const A&) { return *this; }
+  A& operator+=(int i) { return *this; }
+};
+
+int operator>>(A&, int i) { }
+
+A a(0);
+A* afn(int i)
+{
+  f(i);
+  return &a;
+}
+
+A& aref(int i)
+{
+  f(i);
+  return a;
+}
+
+static int si;
+int* ip (int i)
+{
+  f(i);
+  return &si;
+}
+
+int& iref(int i)
+{
+  f(i);
+  return si;
+}
+
+auto pmff(int i) {
+  f(i);
+  return &A::memfn;
+}
+
+template <class T> void f()
+{
+  // a.b
+  A(1).memfn(f(2),3).memfn(f(4),5);
+  aref(6).memfn(f(7),8);
+  (aref(9).*pmff(10))(f(11),12);
+  last = 0;
+
+  // a->b
+  afn(12)->memfn(f(13),14);
+
+  // a->*b
+  (afn(15)->*pmff(16))(f(17),18);
+  last = 0;
+
+  // a(b)
+  // covered in eval-order1.C
+
+  // b @= a
+  aref(19)=A(18);
+  //iref(21)=f(20);
+  aref(23)+=f(22);
+  last = 0;
+
+  // a[b]
+  afn(20)[f(21)-21].memfn(f(22),23);
+  ip(24)[f(25)-25] = 0;
+  last=0;
+
+  // a << b
+  aref(24) << f(25);
+  iref(26) << f(27);
+  last=0;
+
+  // a >> b
+  aref(26) >> f(27);
+  iref(28) >> f(29);
+}
+
+void g()
+{
+  // a.b
+  A(1).memfn(f(2),3).memfn(f(4),5);
+  aref(6).memfn(f(7),8);
+  (aref(9).*pmff(10))(f(11),12);
+  last = 0;
+
+  // a->b
+  afn(12)->memfn(f(13),14);
+
+  // a->*b
+  (afn(15)->*pmff(16))(f(17),18);
+  last = 0;
+
+  // a(b)
+  // covered in eval-order1.C
+
+  // b @= a
+  aref(19)=A(18);
+  //iref(21)=f(20);
+  aref(23)+=f(22);
+  last = 0;
+
+  // a[b]
+  afn(20)[f(21)-21].memfn(f(22),23);
+  ip(24)[f(25)-25] = 0;
+  last=0;
+
+  // a << b
+  aref(24) << f(25);
+  iref(26) << f(27);
+  last=0;
+
+  // a >> b
+  aref(26) >> f(27);
+  iref(28) >> f(29);
+}
+
+int main()
+{
+  g();
+  last = 0;
+  f<int>();
+}

Reply via email to