https://gcc.gnu.org/g:797146f19c4aaf2b7cba5126f0ae07da6c17a3ff

commit r17-5436-g797146f19c4aaf2b7cba5126f0ae07da6c17a3ff
Author: Utkarsh Bahuguna <[email protected]>
Date:   Mon Oct 5 13:00:56 2026 +0530

    gccrs: Evaluate call arguments from left to right
    
    The gimplifier evaluates call arguments in the order the target pushes
    them, which is right to left on x86, but Rust wants left to right. Method
    calls and overloaded operators were affected too. When a call has side
    effects, save the callee and every argument but the last, in order,
    before the call.
    
    Fixes Rust-GCC/gccrs#4956
    
    gcc/rust/ChangeLog:
    
            * rust-gcc.cc (save_call_operands): New function.
            (call_expression): Use it to evaluate the callee and the arguments
            from left to right.
    
    gcc/testsuite/ChangeLog:
    
            * rust/execute/torture/eval-order-call-args.rs: New test.
    
    Signed-off-by: Utkarsh Bahuguna <[email protected]>

Diff:
---
 gcc/rust/rust-gcc.cc                               | 44 +++++++++++++++
 .../rust/execute/torture/eval-order-call-args.rs   | 66 ++++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc
index b3295a54f36e..bb9aee2c5581 100644
--- a/gcc/rust/rust-gcc.cc
+++ b/gcc/rust/rust-gcc.cc
@@ -1590,6 +1590,44 @@ slice_index_expression (tree slice_tree, tree 
index_tree, location_t location)
   return build1_loc (location, INDIRECT_REF, element_type, data_offset_expr);
 }
 
+// Rust evaluates the callee and then the arguments of a call from left to
+// right, but gimplify_call_expr evaluates the arguments in the order the
+// target pushes them, which is right to left on x86. When anything in the
+// call has side effects, wrap the callee and every argument but the last in
+// a SAVE_EXPR, and return those in order so that the caller can evaluate
+// them before the call. The call then only evaluates its last argument.
+
+static std::vector<tree>
+save_call_operands (tree &fn, tree *args, size_t nargs)
+{
+  std::vector<tree> saved;
+
+  bool has_side_effects = TREE_SIDE_EFFECTS (fn);
+  for (size_t i = 0; i < nargs && !has_side_effects; ++i)
+    has_side_effects = TREE_SIDE_EFFECTS (args[i]);
+
+  if (!has_side_effects || nargs == 0)
+    return saved;
+
+  if (TREE_SIDE_EFFECTS (fn))
+    {
+      fn = save_expr (fn);
+      saved.push_back (fn);
+    }
+
+  for (size_t i = 0; i + 1 < nargs; ++i)
+    {
+      if (VOID_TYPE_P (TREE_TYPE (args[i])) || really_constant_p (args[i]))
+       continue;
+
+      args[i] = save_expr (args[i]);
+      if (TREE_CODE (args[i]) == SAVE_EXPR)
+       saved.push_back (args[i]);
+    }
+
+  return saved;
+}
+
 // Create an expression for a call to FN_EXPR with FN_ARGS.
 tree
 call_expression (tree fn, const std::vector<tree> &fn_args, tree chain_expr,
@@ -1642,6 +1680,8 @@ call_expression (tree fn, const std::vector<tree> 
&fn_args, tree chain_expr,
        }
     }
 
+  std::vector<tree> saved = save_call_operands (fn, args, nargs);
+
   tree ret
     = build_call_array_loc (location,
                            excess_type != NULL_TREE ? excess_type : rettype,
@@ -1665,6 +1705,10 @@ call_expression (tree fn, const std::vector<tree> 
&fn_args, tree chain_expr,
       ret = build1_loc (location, NOP_EXPR, rettype, ret);
     }
 
+  // evaluate the saved operands in order before the call
+  for (auto it = saved.rbegin (); it != saved.rend (); ++it)
+    ret = build2_loc (location, COMPOUND_EXPR, TREE_TYPE (ret), *it, ret);
+
   delete[] args;
   return ret;
 }
diff --git a/gcc/testsuite/rust/execute/torture/eval-order-call-args.rs 
b/gcc/testsuite/rust/execute/torture/eval-order-call-args.rs
new file mode 100644
index 000000000000..506b69733cd7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/eval-order-call-args.rs
@@ -0,0 +1,66 @@
+// Operands of a call, a method call and an overloaded operator are
+// evaluated from left to right.
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "add"]
+trait Add<Rhs = Self> {
+    type Output;
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
+
+static mut N: i32 = 0;
+
+fn tick() -> i32 {
+    unsafe {
+        N += 1;
+        N
+    }
+}
+
+fn three(a: i32, b: i32, c: i32) -> i32 {
+    a * 100 + b * 10 + c
+}
+
+struct S;
+
+impl S {
+    fn two(&self, a: i32, b: i32) -> i32 {
+        a * 10 + b
+    }
+}
+
+struct W(i32);
+
+impl Add for W {
+    type Output = i32;
+    fn add(self, rhs: W) -> i32 {
+        self.0 * 10 + rhs.0
+    }
+}
+
+fn w() -> W {
+    W(tick())
+}
+
+fn main() -> i32 {
+    if three(tick(), tick(), tick()) != 123 {
+        return 1;
+    }
+
+    unsafe { N = 0 };
+    let s = S;
+    if s.two(tick(), tick()) != 12 {
+        return 2;
+    }
+
+    unsafe { N = 0 };
+    if w() + w() != 12 {
+        return 3;
+    }
+
+    0
+}

Reply via email to