about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-02-20 16:58:34 -0800
committerbors <bors@rust-lang.org>2013-02-20 16:58:34 -0800
commit8f8f0ec2c6761b096eea1f8dceb42c2618f5a196 (patch)
tree34ff06ab322c6cd89332b3b1dd4b5e3270e9ac4f
parent62f2b4943a223ff7dc168d8fed5ebc50f34150db (diff)
parent423843e54bdbd6e32a0a75b7502904458e20c480 (diff)
downloadrust-8f8f0ec2c6761b096eea1f8dceb42c2618f5a196.tar.gz
rust-8f8f0ec2c6761b096eea1f8dceb42c2618f5a196.zip
auto merge of #5043 : brson/rust/swap, r=brson
r?
-rw-r--r--src/librustc/middle/trans/expr.rs26
-rw-r--r--src/test/run-pass/swap-overlapping.rs45
2 files changed, 67 insertions, 4 deletions
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 6af6adbf68d..5b57ccc9ad4 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -491,11 +491,29 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
         ast::expr_swap(dst, src) => {
             let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
             let src_datum = unpack_datum!(bcx, trans_lvalue(bcx, src));
-            let scratch = scratch_datum(bcx, dst_datum.ty, false);
 
-            let bcx = dst_datum.move_to_datum(bcx, INIT, scratch);
-            let bcx = src_datum.move_to_datum(bcx, INIT, dst_datum);
-            return scratch.move_to_datum(bcx, INIT, src_datum);
+            // If the source and destination are the same, then don't swap.
+            // Avoids performing an overlapping memcpy
+            let dst_datum_ref = dst_datum.to_ref_llval(bcx);
+            let src_datum_ref = src_datum.to_ref_llval(bcx);
+            let cmp = ICmp(bcx, lib::llvm::IntEQ,
+                           src_datum_ref,
+                           dst_datum_ref);
+
+            let swap_cx = base::sub_block(bcx, ~"swap");
+            let next_cx = base::sub_block(bcx, ~"next");
+
+            CondBr(bcx, cmp, next_cx.llbb, swap_cx.llbb);
+
+            let scratch = scratch_datum(swap_cx, dst_datum.ty, false);
+
+            let swap_cx = dst_datum.move_to_datum(swap_cx, INIT, scratch);
+            let swap_cx = src_datum.move_to_datum(swap_cx, INIT, dst_datum);
+            let swap_cx = scratch.move_to_datum(swap_cx, INIT, src_datum);
+
+            Br(swap_cx, next_cx.llbb);
+
+            return next_cx;
         }
         ast::expr_assign_op(op, dst, src) => {
             return trans_assign_op(bcx, expr, op, dst, src);
diff --git a/src/test/run-pass/swap-overlapping.rs b/src/test/run-pass/swap-overlapping.rs
new file mode 100644
index 00000000000..90b2ceef71a
--- /dev/null
+++ b/src/test/run-pass/swap-overlapping.rs
@@ -0,0 +1,45 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Issue #5041 - avoid overlapping memcpy when src and dest of a swap are the same
+
+pub fn main() {
+    let mut test = TestDescAndFn {
+        desc: TestDesc {
+            name: DynTestName(~"test"),
+            should_fail: false
+        },
+        testfn: DynTestFn(|| ()),
+    };
+    do_swap(&mut test);
+}
+
+fn do_swap(test: &mut TestDescAndFn) {
+    *test <-> *test;
+}
+
+pub enum TestName {
+    DynTestName(~str)
+}
+
+pub enum TestFn {
+    DynTestFn(~fn()),
+    DynBenchFn(~fn(&mut int))
+}
+
+pub struct TestDesc {
+    name: TestName,
+    should_fail: bool
+}
+
+pub struct TestDescAndFn {
+    desc: TestDesc,
+    testfn: TestFn,
+}