Skip to content

Commit a2be769

Browse files
committed
Auto merge of #51833 - wesleywiser:faster_large_constant_arrays, r=oli-obk
Speed up compilation of large constant arrays This is a different approach to #51672 as suggested by @oli-obk. Rather than write each repeated value one-by-one, we write the first one and then copy its value directly into the remaining memory. With this change, the [toy program](https://github.com/rust-lang/rust/blob/c2f4744d2db4e162df824d0bd0b093ba4b351545/src/test/run-pass/mir_heavy_promoted.rs) goes from 63 seconds to 19 seconds on my machine. Edit: Inlining `Size::bytes()` saves an additional 6 seconds dropping the total time to 13 seconds on my machine. Edit2: Now down to 2.8 seconds. r? @oli-obk cc @nnethercote @eddyb
2 parents 6af9f91 + 46512e0 commit a2be769

File tree

6 files changed

+118
-25
lines changed

6 files changed

+118
-25
lines changed

src/librustc/mir/interpret/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,13 @@ impl UndefMask {
638638
}
639639
}
640640

641+
#[inline]
641642
pub fn get(&self, i: Size) -> bool {
642643
let (block, bit) = bit_index(i);
643644
(self.blocks[block] & 1 << bit) != 0
644645
}
645646

647+
#[inline]
646648
pub fn set(&mut self, i: Size, new_state: bool) {
647649
let (block, bit) = bit_index(i);
648650
if new_state {
@@ -667,6 +669,7 @@ impl UndefMask {
667669
}
668670
}
669671

672+
#[inline]
670673
fn bit_index(bits: Size) -> (usize, usize) {
671674
let bits = bits.bytes();
672675
let a = bits / BLOCK_SIZE;

src/librustc_mir/interpret/eval_context.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -579,10 +579,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
579579

580580
let (dest, dest_align) = self.force_allocation(dest)?.to_ptr_align();
581581

582-
// FIXME: speed up repeat filling
583-
for i in 0..length {
584-
let elem_dest = dest.ptr_offset(elem_size * i as u64, &self)?;
585-
self.write_value_to_ptr(value, elem_dest, dest_align, elem_ty)?;
582+
if length > 0 {
583+
//write the first value
584+
self.write_value_to_ptr(value, dest, dest_align, elem_ty)?;
585+
586+
if length > 1 {
587+
let rest = dest.ptr_offset(elem_size * 1 as u64, &self)?;
588+
self.memory.copy_repeatedly(dest, dest_align, rest, dest_align, elem_size, length - 1, false)?;
589+
}
586590
}
587591
}
588592

src/librustc_mir/interpret/memory.rs

+51-21
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
589589
dest_align: Align,
590590
size: Size,
591591
nonoverlapping: bool,
592+
) -> EvalResult<'tcx> {
593+
self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
594+
}
595+
596+
pub fn copy_repeatedly(
597+
&mut self,
598+
src: Scalar,
599+
src_align: Align,
600+
dest: Scalar,
601+
dest_align: Align,
602+
size: Size,
603+
length: u64,
604+
nonoverlapping: bool,
592605
) -> EvalResult<'tcx> {
593606
// Empty accesses don't need to be valid pointers, but they should still be aligned
594607
self.check_align(src, src_align)?;
@@ -603,16 +616,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
603616
// first copy the relocations to a temporary buffer, because
604617
// `get_bytes_mut` will clear the relocations, which is correct,
605618
// since we don't want to keep any relocations at the target.
606-
let relocations: Vec<_> = self.relocations(src, size)?
607-
.iter()
608-
.map(|&(offset, alloc_id)| {
609-
// Update relocation offsets for the new positions in the destination allocation.
610-
(offset + dest.offset - src.offset, alloc_id)
611-
})
612-
.collect();
619+
let relocations = {
620+
let relocations = self.relocations(src, size)?;
621+
let mut new_relocations = Vec::with_capacity(relocations.len() * (length as usize));
622+
for i in 0..length {
623+
new_relocations.extend(
624+
relocations
625+
.iter()
626+
.map(|&(offset, alloc_id)| {
627+
(offset + dest.offset - src.offset + (i * size * relocations.len() as u64), alloc_id)
628+
})
629+
);
630+
}
631+
632+
new_relocations
633+
};
613634

614635
let src_bytes = self.get_bytes_unchecked(src, size, src_align)?.as_ptr();
615-
let dest_bytes = self.get_bytes_mut(dest, size, dest_align)?.as_mut_ptr();
636+
let dest_bytes = self.get_bytes_mut(dest, size * length, dest_align)?.as_mut_ptr();
616637

617638
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
618639
// behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
@@ -629,13 +650,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
629650
));
630651
}
631652
}
632-
ptr::copy(src_bytes, dest_bytes, size.bytes() as usize);
653+
654+
for i in 0..length {
655+
ptr::copy(src_bytes, dest_bytes.offset((size.bytes() * i) as isize), size.bytes() as usize);
656+
}
633657
} else {
634-
ptr::copy_nonoverlapping(src_bytes, dest_bytes, size.bytes() as usize);
658+
for i in 0..length {
659+
ptr::copy_nonoverlapping(src_bytes, dest_bytes.offset((size.bytes() * i) as isize), size.bytes() as usize);
660+
}
635661
}
636662
}
637663

638-
self.copy_undef_mask(src, dest, size)?;
664+
self.copy_undef_mask(src, dest, size, length)?;
639665
// copy back the relocations
640666
self.get_mut(dest.alloc_id)?.relocations.insert_presorted(relocations);
641667

@@ -856,21 +882,25 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
856882
src: Pointer,
857883
dest: Pointer,
858884
size: Size,
885+
repeat: u64,
859886
) -> EvalResult<'tcx> {
860887
// The bits have to be saved locally before writing to dest in case src and dest overlap.
861888
assert_eq!(size.bytes() as usize as u64, size.bytes());
862-
let mut v = Vec::with_capacity(size.bytes() as usize);
889+
890+
let undef_mask = self.get(src.alloc_id)?.undef_mask.clone();
891+
let dest_allocation = self.get_mut(dest.alloc_id)?;
892+
863893
for i in 0..size.bytes() {
864-
let defined = self.get(src.alloc_id)?.undef_mask.get(src.offset + Size::from_bytes(i));
865-
v.push(defined);
866-
}
867-
for (i, defined) in v.into_iter().enumerate() {
868-
self.get_mut(dest.alloc_id)?.undef_mask.set(
869-
dest.offset +
870-
Size::from_bytes(i as u64),
871-
defined,
872-
);
894+
let defined = undef_mask.get(src.offset + Size::from_bytes(i));
895+
896+
for j in 0..repeat {
897+
dest_allocation.undef_mask.set(
898+
dest.offset + Size::from_bytes(i + (size.bytes() * j)),
899+
defined
900+
);
901+
}
873902
}
903+
874904
Ok(())
875905
}
876906

src/librustc_target/abi/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -229,37 +229,44 @@ pub struct Size {
229229
impl Size {
230230
pub const ZERO: Size = Self::from_bytes(0);
231231

232+
#[inline]
232233
pub fn from_bits(bits: u64) -> Size {
233234
// Avoid potential overflow from `bits + 7`.
234235
Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8)
235236
}
236237

238+
#[inline]
237239
pub const fn from_bytes(bytes: u64) -> Size {
238240
Size {
239241
raw: bytes
240242
}
241243
}
242244

245+
#[inline]
243246
pub fn bytes(self) -> u64 {
244247
self.raw
245248
}
246249

250+
#[inline]
247251
pub fn bits(self) -> u64 {
248252
self.bytes().checked_mul(8).unwrap_or_else(|| {
249253
panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes())
250254
})
251255
}
252256

257+
#[inline]
253258
pub fn abi_align(self, align: Align) -> Size {
254259
let mask = align.abi() - 1;
255260
Size::from_bytes((self.bytes() + mask) & !mask)
256261
}
257262

263+
#[inline]
258264
pub fn is_abi_aligned(self, align: Align) -> bool {
259265
let mask = align.abi() - 1;
260266
self.bytes() & mask == 0
261267
}
262268

269+
#[inline]
263270
pub fn checked_add<C: HasDataLayout>(self, offset: Size, cx: C) -> Option<Size> {
264271
let dl = cx.data_layout();
265272

@@ -272,6 +279,7 @@ impl Size {
272279
}
273280
}
274281

282+
#[inline]
275283
pub fn checked_mul<C: HasDataLayout>(self, count: u64, cx: C) -> Option<Size> {
276284
let dl = cx.data_layout();
277285

@@ -289,6 +297,7 @@ impl Size {
289297

290298
impl Add for Size {
291299
type Output = Size;
300+
#[inline]
292301
fn add(self, other: Size) -> Size {
293302
Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| {
294303
panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes())
@@ -298,6 +307,7 @@ impl Add for Size {
298307

299308
impl Sub for Size {
300309
type Output = Size;
310+
#[inline]
301311
fn sub(self, other: Size) -> Size {
302312
Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| {
303313
panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes())
@@ -307,13 +317,15 @@ impl Sub for Size {
307317

308318
impl Mul<Size> for u64 {
309319
type Output = Size;
320+
#[inline]
310321
fn mul(self, size: Size) -> Size {
311322
size * self
312323
}
313324
}
314325

315326
impl Mul<u64> for Size {
316327
type Output = Size;
328+
#[inline]
317329
fn mul(self, count: u64) -> Size {
318330
match self.bytes().checked_mul(count) {
319331
Some(bytes) => Size::from_bytes(bytes),
@@ -325,6 +337,7 @@ impl Mul<u64> for Size {
325337
}
326338

327339
impl AddAssign for Size {
340+
#[inline]
328341
fn add_assign(&mut self, other: Size) {
329342
*self = *self + other;
330343
}

src/test/compile-fail/const-err4.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[derive(Copy, Clone)]
12+
union Foo {
13+
a: isize,
14+
b: (),
15+
}
16+
17+
enum Bar {
18+
Boo = [unsafe { Foo { b: () }.a }; 4][3],
19+
//~^ ERROR constant evaluation of enum discriminant resulted in non-integer
20+
}
21+
22+
fn main() {
23+
assert_ne!(Bar::Boo as isize, 0);
24+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
const FOO: isize = 42;
12+
13+
enum Bar {
14+
Boo = *[&FOO; 4][3],
15+
}
16+
17+
fn main() {
18+
assert_eq!(Bar::Boo as isize, 42);
19+
}

0 commit comments

Comments
 (0)