Skip to content

Commit bbefc98

Browse files
committed
Auto merge of #118412 - matthiaskrgr:rollup-ghzhti2, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #118193 (Add missing period in `std::process::Command` docs) - #118222 (unify read_to_end and io::copy impls for reading into a Vec) - #118323 (give dev-friendly error message for incorrect config profiles) - #118378 (Perform LTO optimisations with wasm-ld + -Clinker-plugin-lto) - #118399 (Clean dead codes in miri) - #118410 (update test for new LLVM 18 codegen) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 5facb42 + 4ca038f commit bbefc98

File tree

8 files changed

+125
-136
lines changed

8 files changed

+125
-136
lines changed

Diff for: compiler/rustc_codegen_ssa/src/back/linker.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,8 @@ impl<'a> Linker for WasmLd<'a> {
13081308
}
13091309

13101310
fn optimize(&mut self) {
1311+
// The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and
1312+
// only differentiates -O0 and -O1. It does not apply to LTO.
13111313
self.cmd.arg(match self.sess.opts.optimize {
13121314
OptLevel::No => "-O0",
13131315
OptLevel::Less => "-O1",
@@ -1360,7 +1362,31 @@ impl<'a> Linker for WasmLd<'a> {
13601362
fn subsystem(&mut self, _subsystem: &str) {}
13611363

13621364
fn linker_plugin_lto(&mut self) {
1363-
// Do nothing for now
1365+
match self.sess.opts.cg.linker_plugin_lto {
1366+
LinkerPluginLto::Disabled => {
1367+
// Nothing to do
1368+
}
1369+
LinkerPluginLto::LinkerPluginAuto => {
1370+
self.push_linker_plugin_lto_args();
1371+
}
1372+
LinkerPluginLto::LinkerPlugin(_) => {
1373+
self.push_linker_plugin_lto_args();
1374+
}
1375+
}
1376+
}
1377+
}
1378+
1379+
impl<'a> WasmLd<'a> {
1380+
fn push_linker_plugin_lto_args(&mut self) {
1381+
let opt_level = match self.sess.opts.optimize {
1382+
config::OptLevel::No => "O0",
1383+
config::OptLevel::Less => "O1",
1384+
config::OptLevel::Default => "O2",
1385+
config::OptLevel::Aggressive => "O3",
1386+
// wasm-ld only handles integer LTO opt levels. Use O2
1387+
config::OptLevel::Size | config::OptLevel::SizeMin => "O2",
1388+
};
1389+
self.cmd.arg(&format!("--lto-{opt_level}"));
13641390
}
13651391
}
13661392

Diff for: library/std/src/io/copy.rs

+3-66
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use super::{BorrowedBuf, BufReader, BufWriter, Read, Result, Write, DEFAULT_BUF_SIZE};
22
use crate::alloc::Allocator;
33
use crate::cmp;
4-
use crate::cmp::min;
54
use crate::collections::VecDeque;
65
use crate::io::IoSlice;
76
use crate::mem::MaybeUninit;
@@ -256,79 +255,17 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
256255
}
257256
}
258257

259-
impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> {
258+
impl BufferedWriterSpec for Vec<u8> {
260259
fn buffer_size(&self) -> usize {
261260
cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len())
262261
}
263262

264263
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
265-
let mut bytes = 0;
266-
267-
// avoid inflating empty/small vecs before we have determined that there's anything to read
268-
if self.capacity() < DEFAULT_BUF_SIZE {
269-
let stack_read_limit = DEFAULT_BUF_SIZE as u64;
270-
bytes = stack_buffer_copy(&mut reader.take(stack_read_limit), self)?;
271-
// fewer bytes than requested -> EOF reached
272-
if bytes < stack_read_limit {
273-
return Ok(bytes);
274-
}
275-
}
276-
277-
// don't immediately offer the vec's whole spare capacity, otherwise
278-
// we might have to fully initialize it if the reader doesn't have a custom read_buf() impl
279-
let mut max_read_size = DEFAULT_BUF_SIZE;
280-
281-
loop {
282-
self.reserve(DEFAULT_BUF_SIZE);
283-
let mut initialized_spare_capacity = 0;
284-
285-
loop {
286-
let buf = self.spare_capacity_mut();
287-
let read_size = min(max_read_size, buf.len());
288-
let mut buf = BorrowedBuf::from(&mut buf[..read_size]);
289-
// SAFETY: init is either 0 or the init_len from the previous iteration.
290-
unsafe {
291-
buf.set_init(initialized_spare_capacity);
292-
}
293-
match reader.read_buf(buf.unfilled()) {
294-
Ok(()) => {
295-
let bytes_read = buf.len();
296-
297-
// EOF
298-
if bytes_read == 0 {
299-
return Ok(bytes);
300-
}
301-
302-
// the reader is returning short reads but it doesn't call ensure_init()
303-
if buf.init_len() < buf.capacity() {
304-
max_read_size = usize::MAX;
305-
}
306-
// the reader hasn't returned short reads so far
307-
if bytes_read == buf.capacity() {
308-
max_read_size *= 2;
309-
}
310-
311-
initialized_spare_capacity = buf.init_len() - bytes_read;
312-
bytes += bytes_read as u64;
313-
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
314-
// and the number of read bytes can't exceed the spare capacity since
315-
// that's what the buffer is borrowing from.
316-
unsafe { self.set_len(self.len() + bytes_read) };
317-
318-
// spare capacity full, reserve more
319-
if self.len() == self.capacity() {
320-
break;
321-
}
322-
}
323-
Err(e) if e.is_interrupted() => continue,
324-
Err(e) => return Err(e),
325-
}
326-
}
327-
}
264+
reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64"))
328265
}
329266
}
330267

331-
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
268+
pub fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
332269
reader: &mut R,
333270
writer: &mut W,
334271
) -> Result<u64> {

Diff for: library/std/src/io/copy/tests.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,16 @@ fn copy_specializes_bufreader() {
8282

8383
#[test]
8484
fn copy_specializes_to_vec() {
85-
let cap = 123456;
86-
let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
85+
let cap = DEFAULT_BUF_SIZE * 10;
86+
let mut source = ShortReader { cap, observed_buffer: 0, read_size: DEFAULT_BUF_SIZE };
8787
let mut sink = Vec::new();
88-
assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap());
88+
let copied = io::copy(&mut source, &mut sink).unwrap();
89+
assert_eq!(cap as u64, copied);
90+
assert_eq!(sink.len() as u64, copied);
8991
assert!(
9092
source.observed_buffer > DEFAULT_BUF_SIZE,
91-
"expected a large buffer to be provided to the reader"
93+
"expected a large buffer to be provided to the reader, got {}",
94+
source.observed_buffer
9295
);
9396
}
9497

Diff for: library/std/src/io/mod.rs

+81-37
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,16 @@ where
397397
}
398398
}
399399

400-
// This uses an adaptive system to extend the vector when it fills. We want to
401-
// avoid paying to allocate and zero a huge chunk of memory if the reader only
402-
// has 4 bytes while still making large reads if the reader does have a ton
403-
// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every
404-
// time is 4,500 times (!) slower than a default reservation size of 32 if the
405-
// reader has a very small amount of data to return.
400+
// Here we must serve many masters with conflicting goals:
401+
//
402+
// - avoid allocating unless necessary
403+
// - avoid overallocating if we know the exact size (#89165)
404+
// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820)
405+
// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads
406+
// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems
407+
// at the same time, i.e. small reads suffer from syscall overhead, all reads incur initialization cost
408+
// proportional to buffer size (#110650)
409+
//
406410
pub(crate) fn default_read_to_end<R: Read + ?Sized>(
407411
r: &mut R,
408412
buf: &mut Vec<u8>,
@@ -412,20 +416,58 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
412416
let start_cap = buf.capacity();
413417
// Optionally limit the maximum bytes read on each iteration.
414418
// This adds an arbitrary fiddle factor to allow for more data than we expect.
415-
let max_read_size =
416-
size_hint.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE));
419+
let mut max_read_size = size_hint
420+
.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
421+
.unwrap_or(DEFAULT_BUF_SIZE);
417422

418423
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
424+
425+
const PROBE_SIZE: usize = 32;
426+
427+
fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> {
428+
let mut probe = [0u8; PROBE_SIZE];
429+
430+
loop {
431+
match r.read(&mut probe) {
432+
Ok(n) => {
433+
buf.extend_from_slice(&probe[..n]);
434+
return Ok(n);
435+
}
436+
Err(ref e) if e.is_interrupted() => continue,
437+
Err(e) => return Err(e),
438+
}
439+
}
440+
}
441+
442+
// avoid inflating empty/small vecs before we have determined that there's anything to read
443+
if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE {
444+
let read = small_probe_read(r, buf)?;
445+
446+
if read == 0 {
447+
return Ok(0);
448+
}
449+
}
450+
419451
loop {
452+
if buf.len() == buf.capacity() && buf.capacity() == start_cap {
453+
// The buffer might be an exact fit. Let's read into a probe buffer
454+
// and see if it returns `Ok(0)`. If so, we've avoided an
455+
// unnecessary doubling of the capacity. But if not, append the
456+
// probe buffer to the primary buffer and let its capacity grow.
457+
let read = small_probe_read(r, buf)?;
458+
459+
if read == 0 {
460+
return Ok(buf.len() - start_len);
461+
}
462+
}
463+
420464
if buf.len() == buf.capacity() {
421-
buf.reserve(32); // buf is full, need more space
465+
buf.reserve(PROBE_SIZE); // buf is full, need more space
422466
}
423467

424468
let mut spare = buf.spare_capacity_mut();
425-
if let Some(size) = max_read_size {
426-
let len = cmp::min(spare.len(), size);
427-
spare = &mut spare[..len]
428-
}
469+
let buf_len = cmp::min(spare.len(), max_read_size);
470+
spare = &mut spare[..buf_len];
429471
let mut read_buf: BorrowedBuf<'_> = spare.into();
430472

431473
// SAFETY: These bytes were initialized but not filled in the previous loop
@@ -434,42 +476,44 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
434476
}
435477

436478
let mut cursor = read_buf.unfilled();
437-
match r.read_buf(cursor.reborrow()) {
438-
Ok(()) => {}
439-
Err(e) if e.is_interrupted() => continue,
440-
Err(e) => return Err(e),
479+
loop {
480+
match r.read_buf(cursor.reborrow()) {
481+
Ok(()) => break,
482+
Err(e) if e.is_interrupted() => continue,
483+
Err(e) => return Err(e),
484+
}
441485
}
442486

443-
if cursor.written() == 0 {
487+
let unfilled_but_initialized = cursor.init_ref().len();
488+
let bytes_read = cursor.written();
489+
let was_fully_initialized = read_buf.init_len() == buf_len;
490+
491+
if bytes_read == 0 {
444492
return Ok(buf.len() - start_len);
445493
}
446494

447495
// store how much was initialized but not filled
448-
initialized = cursor.init_ref().len();
496+
initialized = unfilled_but_initialized;
449497

450498
// SAFETY: BorrowedBuf's invariants mean this much memory is initialized.
451499
unsafe {
452-
let new_len = read_buf.filled().len() + buf.len();
500+
let new_len = bytes_read + buf.len();
453501
buf.set_len(new_len);
454502
}
455503

456-
if buf.len() == buf.capacity() && buf.capacity() == start_cap {
457-
// The buffer might be an exact fit. Let's read into a probe buffer
458-
// and see if it returns `Ok(0)`. If so, we've avoided an
459-
// unnecessary doubling of the capacity. But if not, append the
460-
// probe buffer to the primary buffer and let its capacity grow.
461-
let mut probe = [0u8; 32];
462-
463-
loop {
464-
match r.read(&mut probe) {
465-
Ok(0) => return Ok(buf.len() - start_len),
466-
Ok(n) => {
467-
buf.extend_from_slice(&probe[..n]);
468-
break;
469-
}
470-
Err(ref e) if e.is_interrupted() => continue,
471-
Err(e) => return Err(e),
472-
}
504+
// Use heuristics to determine the max read size if no initial size hint was provided
505+
if size_hint.is_none() {
506+
// The reader is returning short reads but it doesn't call ensure_init().
507+
// In that case we no longer need to restrict read sizes to avoid
508+
// initialization costs.
509+
if !was_fully_initialized {
510+
max_read_size = usize::MAX;
511+
}
512+
513+
// we have passed a larger buffer than previously and the
514+
// reader still hasn't returned a short read
515+
if buf_len >= max_read_size && bytes_read == buf_len {
516+
max_read_size = max_read_size.saturating_mul(2);
473517
}
474518
}
475519
}

Diff for: library/std/src/process.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@ impl fmt::Debug for Command {
11081108
///
11091109
/// The default format approximates a shell invocation of the program along with its
11101110
/// arguments. It does not include most of the other command properties. The output is not guaranteed to work
1111-
/// (e.g. due to lack of shell-escaping or differences in path resolution)
1111+
/// (e.g. due to lack of shell-escaping or differences in path resolution).
11121112
/// On some platforms you can use [the alternate syntax] to show more fields.
11131113
///
11141114
/// Note that the debug implementation is platform-specific.

Diff for: src/bootstrap/bootstrap.py

+5
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,11 @@ def bootstrap(args):
10831083
include_file = 'config.{}.toml'.format(profile_aliases.get(profile) or profile)
10841084
include_dir = os.path.join(rust_root, 'src', 'bootstrap', 'defaults')
10851085
include_path = os.path.join(include_dir, include_file)
1086+
1087+
if not os.path.exists(include_path):
1088+
raise Exception("Unrecognized config profile '{}'. Check src/bootstrap/defaults"
1089+
" for available options.".format(profile))
1090+
10861091
# HACK: This works because `self.get_toml()` returns the first match it finds for a
10871092
# specific key, so appending our defaults at the end allows the user to override them
10881093
with open(include_path) as included_toml:

Diff for: src/tools/miri/src/shims/unix/fs.rs

-26
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,6 @@ pub trait FileDescriptor: std::fmt::Debug + Any {
6666
fn is_tty(&self, _communicate_allowed: bool) -> bool {
6767
false
6868
}
69-
70-
#[cfg(unix)]
71-
fn as_unix_host_fd(&self) -> Option<i32> {
72-
None
73-
}
7469
}
7570

7671
impl dyn FileDescriptor {
@@ -150,12 +145,6 @@ impl FileDescriptor for FileHandle {
150145
Ok(Box::new(FileHandle { file: duplicated, writable: self.writable }))
151146
}
152147

153-
#[cfg(unix)]
154-
fn as_unix_host_fd(&self) -> Option<i32> {
155-
use std::os::unix::io::AsRawFd;
156-
Some(self.file.as_raw_fd())
157-
}
158-
159148
fn is_tty(&self, communicate_allowed: bool) -> bool {
160149
communicate_allowed && self.file.is_terminal()
161150
}
@@ -183,11 +172,6 @@ impl FileDescriptor for io::Stdin {
183172
Ok(Box::new(io::stdin()))
184173
}
185174

186-
#[cfg(unix)]
187-
fn as_unix_host_fd(&self) -> Option<i32> {
188-
Some(libc::STDIN_FILENO)
189-
}
190-
191175
fn is_tty(&self, communicate_allowed: bool) -> bool {
192176
communicate_allowed && self.is_terminal()
193177
}
@@ -220,11 +204,6 @@ impl FileDescriptor for io::Stdout {
220204
Ok(Box::new(io::stdout()))
221205
}
222206

223-
#[cfg(unix)]
224-
fn as_unix_host_fd(&self) -> Option<i32> {
225-
Some(libc::STDOUT_FILENO)
226-
}
227-
228207
fn is_tty(&self, communicate_allowed: bool) -> bool {
229208
communicate_allowed && self.is_terminal()
230209
}
@@ -250,11 +229,6 @@ impl FileDescriptor for io::Stderr {
250229
Ok(Box::new(io::stderr()))
251230
}
252231

253-
#[cfg(unix)]
254-
fn as_unix_host_fd(&self) -> Option<i32> {
255-
Some(libc::STDERR_FILENO)
256-
}
257-
258232
fn is_tty(&self, communicate_allowed: bool) -> bool {
259233
communicate_allowed && self.is_terminal()
260234
}

0 commit comments

Comments
 (0)