Auto merge of #151337 - the8472:bail-before-memcpy2, r=Mark-Simulacrum
optimize `vec.extend(slice.to_vec())`, take 2 Redoing https://github.com/rust-lang/rust/pull/130998 It was reverted in https://github.com/rust-lang/rust/pull/151150 due to flakiness. I have traced this to layout randomization perturbing the test (the failure reproduces locally with layout randomization), which is now excluded.
This commit is contained in:
commit
873d4682c7
4 changed files with 55 additions and 6 deletions
|
|
@ -516,7 +516,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
|
||||||
to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
|
to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
|
||||||
// applies to argument place instead of function place
|
// applies to argument place instead of function place
|
||||||
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
|
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
|
||||||
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
|
let attrs: &[_] = if llvm_util::get_version() >= (21, 0, 0) {
|
||||||
|
// "Does not capture provenance" means "if the function call stashes the pointer somewhere,
|
||||||
|
// accessing that pointer after the function returns is UB". That is definitely the case here since
|
||||||
|
// freeing will destroy the provenance.
|
||||||
|
let captures_addr = AttributeKind::CapturesAddress.create_attr(cx.llcx);
|
||||||
|
&[allocated_pointer, captures_addr]
|
||||||
|
} else {
|
||||||
|
&[allocated_pointer]
|
||||||
|
};
|
||||||
|
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), attrs);
|
||||||
}
|
}
|
||||||
if let Some(align) = codegen_fn_attrs.alignment {
|
if let Some(align) = codegen_fn_attrs.alignment {
|
||||||
llvm::set_alignment(llfn, align);
|
llvm::set_alignment(llfn, align);
|
||||||
|
|
|
||||||
|
|
@ -444,13 +444,16 @@ impl<T> [T] {
|
||||||
impl<T: TrivialClone> ConvertVec for T {
|
impl<T: TrivialClone> ConvertVec for T {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A> {
|
fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A> {
|
||||||
let mut v = Vec::with_capacity_in(s.len(), alloc);
|
let len = s.len();
|
||||||
|
let mut v = Vec::with_capacity_in(len, alloc);
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// allocated above with the capacity of `s`, and initialize to `s.len()` in
|
// allocated above with the capacity of `s`, and initialize to `s.len()` in
|
||||||
// ptr::copy_to_non_overlapping below.
|
// ptr::copy_to_non_overlapping below.
|
||||||
unsafe {
|
if len > 0 {
|
||||||
s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len());
|
unsafe {
|
||||||
v.set_len(s.len());
|
s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), len);
|
||||||
|
v.set_len(len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2818,7 +2818,11 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||||
let count = other.len();
|
let count = other.len();
|
||||||
self.reserve(count);
|
self.reserve(count);
|
||||||
let len = self.len();
|
let len = self.len();
|
||||||
unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) };
|
if count > 0 {
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count)
|
||||||
|
};
|
||||||
|
}
|
||||||
self.len += count;
|
self.len += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
33
tests/codegen-llvm/lib-optimizations/append-elements.rs
Normal file
33
tests/codegen-llvm/lib-optimizations/append-elements.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
//@ compile-flags: -O -Zmerge-functions=disabled
|
||||||
|
//@ needs-deterministic-layouts
|
||||||
|
//@ min-llvm-version: 21
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
//! Check that a temporary intermediate allocations can eliminated and replaced
|
||||||
|
//! with memcpy forwarding.
|
||||||
|
//! This requires Vec code to be structured in a way that avoids phi nodes from the
|
||||||
|
//! zero-capacity length flowing into the memcpy arguments.
|
||||||
|
|
||||||
|
// CHECK-LABEL: @vec_append_with_temp_alloc
|
||||||
|
// CHECK-SAME: ptr{{.*}}[[DST:%[a-z]+]]{{.*}}ptr{{.*}}[[SRC:%[a-z]+]]
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn vec_append_with_temp_alloc(dst: &mut Vec<u8>, src: &[u8]) {
|
||||||
|
// CHECK-NOT: call void @llvm.memcpy
|
||||||
|
// CHECK: call void @llvm.memcpy.{{.*}}[[DST]].i{{.*}}[[SRC]]
|
||||||
|
// CHECK-NOT: call void @llvm.memcpy
|
||||||
|
let temp = src.to_vec();
|
||||||
|
dst.extend(&temp);
|
||||||
|
// CHECK: ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @string_append_with_temp_alloc
|
||||||
|
// CHECK-SAME: ptr{{.*}}[[DST:%[a-z]+]]{{.*}}ptr{{.*}}[[SRC:%[a-z]+]]
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn string_append_with_temp_alloc(dst: &mut String, src: &str) {
|
||||||
|
// CHECK-NOT: call void @llvm.memcpy
|
||||||
|
// CHECK: call void @llvm.memcpy.{{.*}}[[DST]].i{{.*}}[[SRC]]
|
||||||
|
// CHECK-NOT: call void @llvm.memcpy
|
||||||
|
let temp = src.to_string();
|
||||||
|
dst.push_str(&temp);
|
||||||
|
// CHECK: ret
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue