Skip to content

Commit dfc15e6

Browse files
committed
Reapply "avoid phi node for pointers flowing into Vec appends rust-lang#130998"
This reverts commit cd79ff2.
1 parent c9af9c1 commit dfc15e6

4 files changed

Lines changed: 50 additions & 5 deletions

File tree

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
517517
to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
518518
// applies to argument place instead of function place
519519
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
520-
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
520+
let attrs: &[_] = if llvm_util::get_version() >= (21, 0, 0) {
521+
// "Does not capture provenance" means "if the function call stashes the pointer somewhere,
522+
// accessing that pointer after the function returns is UB". That is definitely the case here since
523+
// freeing will destroy the provenance.
524+
let captures_addr = AttributeKind::CapturesAddress.create_attr(cx.llcx);
525+
&[allocated_pointer, captures_addr]
526+
} else {
527+
&[allocated_pointer]
528+
};
529+
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), attrs);
521530
}
522531
if let Some(align) = codegen_fn_attrs.alignment {
523532
llvm::set_alignment(llfn, align);

library/alloc/src/slice.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -448,9 +448,11 @@ impl<T> [T] {
448448
// SAFETY:
449449
// allocated above with the capacity of `s`, and initialize to `s.len()` in
450450
// 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());
451+
if s.len() > 0 {
452+
unsafe {
453+
s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len());
454+
v.set_len(s.len());
455+
}
454456
}
455457
v
456458
}

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

0 commit comments

Comments
 (0)