Skip to content

Commit 4d91323

Browse files
authored
Auto merge of #36355 - bluss:vec-extend-from-slice-aliasing-workaround, r=alexcrichton
Work around pointer aliasing issue in Vec::extend_from_slice, extend_with_element Due to missing noalias annotations for &mut T in general (issue #31681), in larger programs extend_from_slice and extend_with_element may both compile very poorly. What is observed is that the .set_len() calls are not lifted out of the loop, even for `Vec<u8>`. Use a local length variable for the Vec length instead, and use a scope guard to write this value back to self.len when the scope ends or on panic. Then the alias analysis is easy. This affects extend_from_slice, extend_with_element, the vec![x; n] macro, Write impls for Vec<u8>, BufWriter, etc (but may / may not have triggered since inlining can be enough for the compiler to get it right). Fixes #32155 Fixes #33518 Closes #17844
2 parents 0f5f325 + 765700b commit 4d91323

File tree

1 file changed

+55
-13
lines changed

1 file changed

+55
-13
lines changed

src/libcollections/vec.rs

+55-13
Original file line numberDiff line numberDiff line change
@@ -1046,21 +1046,27 @@ impl<T: Clone> Vec<T> {
10461046
self.reserve(n);
10471047

10481048
unsafe {
1049-
let len = self.len();
1050-
let mut ptr = self.as_mut_ptr().offset(len as isize);
1049+
let mut ptr = self.as_mut_ptr().offset(self.len() as isize);
1050+
// Use SetLenOnDrop to work around bug where compiler
1051+
// may not realize the store through `ptr` trough self.set_len()
1052+
// don't alias.
1053+
let mut local_len = SetLenOnDrop::new(&mut self.len);
1054+
10511055
// Write all elements except the last one
1052-
for i in 1..n {
1056+
for _ in 1..n {
10531057
ptr::write(ptr, value.clone());
10541058
ptr = ptr.offset(1);
10551059
// Increment the length in every step in case clone() panics
1056-
self.set_len(len + i);
1060+
local_len.increment_len(1);
10571061
}
10581062

10591063
if n > 0 {
10601064
// We can write the last element directly without cloning needlessly
10611065
ptr::write(ptr, value);
1062-
self.set_len(len + n);
1066+
local_len.increment_len(1);
10631067
}
1068+
1069+
// len set by scope guard
10641070
}
10651071
}
10661072

@@ -1085,20 +1091,56 @@ impl<T: Clone> Vec<T> {
10851091
pub fn extend_from_slice(&mut self, other: &[T]) {
10861092
self.reserve(other.len());
10871093

1088-
for i in 0..other.len() {
1094+
// Unsafe code so this can be optimised to a memcpy (or something
1095+
// similarly fast) when T is Copy. LLVM is easily confused, so any
1096+
// extra operations during the loop can prevent this optimisation.
1097+
unsafe {
10891098
let len = self.len();
1090-
1091-
// Unsafe code so this can be optimised to a memcpy (or something
1092-
// similarly fast) when T is Copy. LLVM is easily confused, so any
1093-
// extra operations during the loop can prevent this optimisation.
1094-
unsafe {
1095-
ptr::write(self.get_unchecked_mut(len), other.get_unchecked(i).clone());
1096-
self.set_len(len + 1);
1099+
let ptr = self.get_unchecked_mut(len) as *mut T;
1100+
// Use SetLenOnDrop to work around bug where compiler
1101+
// may not realize the store through `ptr` trough self.set_len()
1102+
// don't alias.
1103+
let mut local_len = SetLenOnDrop::new(&mut self.len);
1104+
1105+
for i in 0..other.len() {
1106+
ptr::write(ptr.offset(i as isize), other.get_unchecked(i).clone());
1107+
local_len.increment_len(1);
10971108
}
1109+
1110+
// len set by scope guard
10981111
}
10991112
}
11001113
}
11011114

1115+
// Set the length of the vec when the `SetLenOnDrop` value goes out of scope.
1116+
//
1117+
// The idea is: The length field in SetLenOnDrop is a local variable
1118+
// that the optimizer will see does not alias with any stores through the Vec's data
1119+
// pointer. This is a workaround for alias analysis issue #32155
1120+
struct SetLenOnDrop<'a> {
1121+
len: &'a mut usize,
1122+
local_len: usize,
1123+
}
1124+
1125+
impl<'a> SetLenOnDrop<'a> {
1126+
#[inline]
1127+
fn new(len: &'a mut usize) -> Self {
1128+
SetLenOnDrop { local_len: *len, len: len }
1129+
}
1130+
1131+
#[inline]
1132+
fn increment_len(&mut self, increment: usize) {
1133+
self.local_len += increment;
1134+
}
1135+
}
1136+
1137+
impl<'a> Drop for SetLenOnDrop<'a> {
1138+
#[inline]
1139+
fn drop(&mut self) {
1140+
*self.len = self.local_len;
1141+
}
1142+
}
1143+
11021144
impl<T: PartialEq> Vec<T> {
11031145
/// Removes consecutive repeated elements in the vector.
11041146
///

0 commit comments

Comments
 (0)