Skip to content

Commit 0fb2e37

Browse files
committed
Auto merge of #131193 - EFanZh:asserts-vec-len, r=<try>
Asserts the maximum value that can be returned from `Vec::len` Currently, casting `Vec<i32>` to `Vec<u32>` takes O(1) time: ```rust // See <https://godbolt.org/z/hxq3hnYKG> for assembly output. pub fn cast(vec: Vec<i32>) -> Vec<u32> { vec.into_iter().map(|e| e as _).collect() } ``` But the generated assembly is not the same as the identity function, which prevents us from casting `Vec<Vec<i32>>` to `Vec<Vec<u32>>` within O(1) time: ```rust // See <https://godbolt.org/z/7n48bxd9f> for assembly output. pub fn cast(vec: Vec<Vec<i32>>) -> Vec<Vec<u32>> { vec.into_iter() .map(|e| e.into_iter().map(|e| e as _).collect()) .collect() } ``` This change tries to fix the problem. You can see the comparison here: <https://godbolt.org/z/jdManrKvx>.
2 parents f7c8928 + 6f0772b commit 0fb2e37

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

Diff for: library/alloc/src/vec/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -2630,6 +2630,14 @@ impl<T, A: Allocator> Vec<T, A> {
26302630
#[stable(feature = "rust1", since = "1.0.0")]
26312631
#[rustc_confusables("length", "size")]
26322632
pub fn len(&self) -> usize {
2633+
if !T::IS_ZST {
2634+
// SAFETY: The maximum capacity of `Vec<T>` is `isize::MAX` bytes, so if `T` non-ZST,
2635+
// `Vec<T>` can have at most `isize::MAX as usize / mem::size_of::<T>()` elements.
2636+
unsafe {
2637+
core::hint::assert_unchecked(self.len <= isize::MAX as usize / mem::size_of::<T>());
2638+
}
2639+
}
2640+
26332641
self.len
26342642
}
26352643

Diff for: tests/codegen/vec-in-place.rs

+34
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub struct Baz {
3737
#[no_mangle]
3838
pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
3939
// CHECK-NOT: loop
40+
// CHECK: call
41+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
4042
// CHECK-NOT: call
4143
vec.into_iter().map(|e| e as u8).collect()
4244
}
@@ -45,14 +47,35 @@ pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
4547
#[no_mangle]
4648
pub fn vec_iterator_cast_wrapper(vec: Vec<u8>) -> Vec<Wrapper<u8>> {
4749
// CHECK-NOT: loop
50+
// CHECK: call
51+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
4852
// CHECK-NOT: call
4953
vec.into_iter().map(|e| Wrapper(e)).collect()
5054
}
5155

56+
// CHECK-LABEL: @vec_iterator_cast_signed
57+
#[no_mangle]
58+
pub fn vec_iterator_cast_signed(vec: Vec<i32>) -> Vec<u32> {
59+
// CHECK-NOT: and i{{[0-9]+}} %{{.*}}, {{[0-9]+}}
60+
vec.into_iter().map(|e| u32::from_ne_bytes(e.to_ne_bytes())).collect()
61+
}
62+
63+
// CHECK-LABEL: @vec_iterator_cast_signed_nested
64+
#[no_mangle]
65+
pub fn vec_iterator_cast_signed_nested(vec: Vec<Vec<i32>>) -> Vec<Vec<u32>> {
66+
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
67+
// CHECK-NOT: %{{.*}} = udiv
68+
vec.into_iter()
69+
.map(|e| e.into_iter().map(|e| u32::from_ne_bytes(e.to_ne_bytes())).collect())
70+
.collect()
71+
}
72+
5273
// CHECK-LABEL: @vec_iterator_cast_unwrap
5374
#[no_mangle]
5475
pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
5576
// CHECK-NOT: loop
77+
// CHECK: call
78+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
5679
// CHECK-NOT: call
5780
vec.into_iter().map(|e| e.0).collect()
5881
}
@@ -61,6 +84,8 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
6184
#[no_mangle]
6285
pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> {
6386
// CHECK-NOT: loop
87+
// CHECK: call
88+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
6489
// CHECK-NOT: call
6590
vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect()
6691
}
@@ -69,6 +94,8 @@ pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> {
6994
#[no_mangle]
7095
pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> {
7196
// CHECK-NOT: loop
97+
// CHECK: call
98+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
7299
// CHECK-NOT: call
73100

74101
// Safety: For the purpose of this test we assume that Bar layout matches [u64; 4].
@@ -82,6 +109,8 @@ pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> {
82109
#[no_mangle]
83110
pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> {
84111
// CHECK-NOT: loop
112+
// CHECK: call
113+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
85114
// CHECK-NOT: call
86115

87116
// Safety: For the purpose of this test we assume that Bar layout matches [u64; 4].
@@ -95,6 +124,8 @@ pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> {
95124
#[no_mangle]
96125
pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> {
97126
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
127+
// CHECK: call
128+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
98129
// CHECK-NOT: call
99130
// CHECK-NOT: %{{.*}} = mul
100131
// CHECK-NOT: %{{.*}} = udiv
@@ -106,9 +137,12 @@ pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> {
106137
#[no_mangle]
107138
pub fn vec_iterator_cast_wrap_drop(vec: Vec<String>) -> Vec<Wrapper<String>> {
108139
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
140+
// CHECK: call
141+
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
109142
// CHECK-NOT: call
110143
// CHECK-NOT: %{{.*}} = mul
111144
// CHECK-NOT: %{{.*}} = udiv
145+
// CHECK: ret void
112146

113147
vec.into_iter().map(Wrapper).collect()
114148
}

Diff for: tests/codegen/vec_pop_push_noop.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ pub fn noop(v: &mut Vec<u8>) {
99
// CHECK-NOT: call
1010
// CHECK: tail call void @llvm.assume
1111
// CHECK-NOT: grow_one
12-
// CHECK-NOT: call
1312
// CHECK: ret
1413
if let Some(x) = v.pop() {
1514
v.push(x)

0 commit comments

Comments
 (0)