Skip to content

Commit 2b8f4a5

Browse files
committed
avoid phi node for pointers flowing into Vec appends
1 parent 3df0dc8 commit 2b8f4a5

3 files changed

Lines changed: 45 additions & 5 deletions

File tree

library/alloc/src/slice.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,13 +444,16 @@ impl<T> [T] {
444444
impl<T: TrivialClone> ConvertVec for T {
445445
#[inline]
446446
fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A> {
447-
let mut v = Vec::with_capacity_in(s.len(), alloc);
447+
let len = s.len();
448+
let mut v = Vec::with_capacity_in(len, alloc);
448449
// SAFETY:
449450
// allocated above with the capacity of `s`, and initialize to `s.len()` in
450451
// ptr::copy_to_non_overlapping below.
451-
unsafe {
452-
s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len());
453-
v.set_len(s.len());
452+
if len > 0 {
453+
unsafe {
454+
s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), len);
455+
v.set_len(len);
456+
}
454457
}
455458
v
456459
}

library/alloc/src/vec/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2818,7 +2818,11 @@ impl<T, A: Allocator> Vec<T, A> {
28182818
let count = other.len();
28192819
self.reserve(count);
28202820
let len = self.len();
2821-
unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) };
2821+
if count > 0 {
2822+
unsafe {
2823+
ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count)
2824+
};
2825+
}
28222826
self.len += count;
28232827
}
28242828

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ compile-flags: -O -Zmerge-functions=disabled
2+
//@ needs-deterministic-layouts
3+
//@ min-llvm-version: 21
4+
#![crate_type = "lib"]
5+
6+
//! Check that a temporary intermediate allocations can eliminated and replaced
7+
//! with memcpy forwarding.
8+
//! This requires Vec code to be structured in a way that avoids phi nodes from the
9+
//! zero-capacity length flowing into the memcpy arguments.
10+
11+
// CHECK-LABEL: @vec_append_with_temp_alloc
12+
// CHECK-SAME: ptr{{.*}}[[DST:%[a-z]+]]{{.*}}ptr{{.*}}[[SRC:%[a-z]+]]
13+
#[no_mangle]
14+
pub fn vec_append_with_temp_alloc(dst: &mut Vec<u8>, src: &[u8]) {
15+
// CHECK-NOT: call void @llvm.memcpy
16+
// CHECK: call void @llvm.memcpy.{{.*}}[[DST]].i{{.*}}[[SRC]]
17+
// CHECK-NOT: call void @llvm.memcpy
18+
let temp = src.to_vec();
19+
dst.extend(&temp);
20+
// CHECK: ret
21+
}
22+
23+
// CHECK-LABEL: @string_append_with_temp_alloc
24+
// CHECK-SAME: ptr{{.*}}[[DST:%[a-z]+]]{{.*}}ptr{{.*}}[[SRC:%[a-z]+]]
25+
#[no_mangle]
26+
pub fn string_append_with_temp_alloc(dst: &mut String, src: &str) {
27+
// CHECK-NOT: call void @llvm.memcpy
28+
// CHECK: call void @llvm.memcpy.{{.*}}[[DST]].i{{.*}}[[SRC]]
29+
// CHECK-NOT: call void @llvm.memcpy
30+
let temp = src.to_string();
31+
dst.push_str(&temp);
32+
// CHECK: ret
33+
}

0 commit comments

Comments
 (0)