Skip to content

Rollup of 5 pull requests #136754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Feb 9, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -3287,13 +3287,6 @@ dependencies = [
"tikv-jemalloc-sys",
]

[[package]]
name = "rustc-perf-wrapper"
version = "0.1.0"
dependencies = [
"clap",
]

[[package]]
name = "rustc-rayon"
version = "0.5.1"
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -45,7 +45,6 @@ members = [
"src/tools/rustdoc-gui-test",
"src/tools/opt-dist",
"src/tools/coverage-dump",
"src/tools/rustc-perf-wrapper",
"src/tools/wasm-component-ld",
"src/tools/features-status-dump",
]
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
@@ -97,7 +97,11 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
// Android has the same issue (#22398)
llvm::add_module_flag_u32(
self.llmod,
llvm::ModuleFlagMergeBehavior::Warning,
// In the case where multiple CGUs with different dwarf version
// values are being merged together, such as with cross-crate
// LTO, then we want to use the highest version of dwarf
// we can. This matches Clang's behavior as well.
llvm::ModuleFlagMergeBehavior::Max,
"Dwarf Version",
sess.dwarf_version(),
);
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
@@ -272,7 +272,7 @@ impl<O> AssertKind<O> {
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
)
}
NullPointerDereference => write!(f, "\"null pointer dereference occured\""),
NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
write!(f, "\"coroutine resumed after completion\"")
}
2 changes: 2 additions & 0 deletions compiler/rustc_mir_transform/src/check_alignment.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_index::IndexVec;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::PlaceContext;
use rustc_middle::mir::*;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_session::Session;
@@ -44,6 +45,7 @@ fn insert_alignment_check<'tcx>(
tcx: TyCtxt<'tcx>,
pointer: Place<'tcx>,
pointee_ty: Ty<'tcx>,
_context: PlaceContext,
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
stmts: &mut Vec<Statement<'tcx>>,
source_info: SourceInfo,
69 changes: 46 additions & 23 deletions compiler/rustc_mir_transform/src/check_null.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_index::IndexVec;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext};
use rustc_middle::mir::*;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_session::Session;
@@ -26,6 +26,7 @@ fn insert_null_check<'tcx>(
tcx: TyCtxt<'tcx>,
pointer: Place<'tcx>,
pointee_ty: Ty<'tcx>,
context: PlaceContext,
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
stmts: &mut Vec<Statement<'tcx>>,
source_info: SourceInfo,
@@ -42,30 +43,51 @@ fn insert_null_check<'tcx>(
let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });

// Get size of the pointee (zero-sized reads and writes are allowed).
let rvalue = Rvalue::NullaryOp(NullOp::SizeOf, pointee_ty);
let sizeof_pointee =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
stmts.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((sizeof_pointee, rvalue))),
});

// Check that the pointee is not a ZST.
let zero = Operand::Constant(Box::new(ConstOperand {
span: source_info.span,
user_ty: None,
const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize),
const_: Const::Val(ConstValue::from_target_usize(0, &tcx), tcx.types.usize),
}));
let is_pointee_no_zst =
local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
stmts.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
is_pointee_no_zst,
Rvalue::BinaryOp(BinOp::Ne, Box::new((Operand::Copy(sizeof_pointee), zero.clone()))),
))),
});

let pointee_should_be_checked = match context {
// Borrows pointing to "null" are UB even if the pointee is a ZST.
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
// Pointer should be checked unconditionally.
Operand::Constant(Box::new(ConstOperand {
span: source_info.span,
user_ty: None,
const_: Const::Val(ConstValue::from_bool(true), tcx.types.bool),
}))
}
// Other usages of null pointers only are UB if the pointee is not a ZST.
_ => {
let rvalue = Rvalue::NullaryOp(NullOp::SizeOf, pointee_ty);
let sizeof_pointee =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
stmts.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((sizeof_pointee, rvalue))),
});

// Check that the pointee is not a ZST.
let is_pointee_not_zst =
local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
stmts.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
is_pointee_not_zst,
Rvalue::BinaryOp(
BinOp::Ne,
Box::new((Operand::Copy(sizeof_pointee), zero.clone())),
),
))),
});

// Pointer needs to be checked only if pointee is not a ZST.
Operand::Copy(is_pointee_not_zst)
}
};

// Check whether the pointer is null.
let is_null = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
@@ -77,7 +99,8 @@ fn insert_null_check<'tcx>(
))),
});

// We want to throw an exception if the pointer is null and doesn't point to a ZST.
// We want to throw an exception if the pointer is null and the pointee is not unconditionally
// allowed (which for all non-borrow place uses, is when the pointee is ZST).
let should_throw_exception =
local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
stmts.push(Statement {
@@ -86,7 +109,7 @@ fn insert_null_check<'tcx>(
should_throw_exception,
Rvalue::BinaryOp(
BinOp::BitAnd,
Box::new((Operand::Copy(is_null), Operand::Copy(is_pointee_no_zst))),
Box::new((Operand::Copy(is_null), pointee_should_be_checked)),
),
))),
});
14 changes: 8 additions & 6 deletions compiler/rustc_mir_transform/src/check_pointers.rs
Original file line number Diff line number Diff line change
@@ -40,17 +40,18 @@ pub(crate) enum BorrowCheckMode {
/// success and fail the check otherwise.
/// This utility will insert a terminator block that asserts on the condition
/// and panics on failure.
pub(crate) fn check_pointers<'a, 'tcx, F>(
pub(crate) fn check_pointers<'tcx, F>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
excluded_pointees: &'a [Ty<'tcx>],
excluded_pointees: &[Ty<'tcx>],
on_finding: F,
borrow_check_mode: BorrowCheckMode,
) where
F: Fn(
/* tcx: */ TyCtxt<'tcx>,
/* pointer: */ Place<'tcx>,
/* pointee_ty: */ Ty<'tcx>,
/* context: */ PlaceContext,
/* local_decls: */ &mut IndexVec<Local, LocalDecl<'tcx>>,
/* stmts: */ &mut Vec<Statement<'tcx>>,
/* source_info: */ SourceInfo,
@@ -86,7 +87,7 @@ pub(crate) fn check_pointers<'a, 'tcx, F>(
);
finder.visit_statement(statement, location);

for (local, ty) in finder.into_found_pointers() {
for (local, ty, context) in finder.into_found_pointers() {
debug!("Inserting check for {:?}", ty);
let new_block = split_block(basic_blocks, location);

@@ -98,6 +99,7 @@ pub(crate) fn check_pointers<'a, 'tcx, F>(
tcx,
local,
ty,
context,
local_decls,
&mut block_data.statements,
source_info,
@@ -125,7 +127,7 @@ struct PointerFinder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
local_decls: &'a mut LocalDecls<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>,
excluded_pointees: &'a [Ty<'tcx>],
borrow_check_mode: BorrowCheckMode,
}
@@ -148,7 +150,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
}
}

fn into_found_pointers(self) -> Vec<(Place<'tcx>, Ty<'tcx>)> {
fn into_found_pointers(self) -> Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)> {
self.pointers
}

@@ -211,7 +213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
return;
}

self.pointers.push((pointer, pointee_ty));
self.pointers.push((pointer, pointee_ty, context));

self.super_place(place, context, location);
}
2 changes: 1 addition & 1 deletion compiler/stable_mir/src/mir/body.rs
Original file line number Diff line number Diff line change
@@ -307,7 +307,7 @@ impl AssertMessage {
AssertMessage::MisalignedPointerDereference { .. } => {
Ok("misaligned pointer dereference")
}
AssertMessage::NullPointerDereference => Ok("null pointer dereference occured"),
AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"),
}
}
}
2 changes: 1 addition & 1 deletion compiler/stable_mir/src/mir/pretty.rs
Original file line number Diff line number Diff line change
@@ -299,7 +299,7 @@ fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::R
)
}
AssertMessage::NullPointerDereference => {
write!(writer, "\"null pointer dereference occured.\"")
write!(writer, "\"null pointer dereference occurred\"")
}
AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
write!(writer, "{}", msg.description().unwrap())
2 changes: 1 addition & 1 deletion library/core/src/panicking.rs
Original file line number Diff line number Diff line change
@@ -302,7 +302,7 @@ fn panic_null_pointer_dereference() -> ! {
}

panic_nounwind_fmt(
format_args!("null pointer dereference occured"),
format_args!("null pointer dereference occurred"),
/* force_no_backtrace */ false,
)
}
4 changes: 2 additions & 2 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
@@ -2307,8 +2307,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
///
/// # Platform-specific behavior
///
/// This function currently corresponds to the `unlink` function on Unix
/// and the `DeleteFile` function on Windows.
/// This function currently corresponds to the `unlink` function on Unix.
/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files.
/// Note that, this [may change in the future][changes].
///
/// [changes]: io#platform-specific-behavior
2 changes: 1 addition & 1 deletion library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
@@ -1384,7 +1384,7 @@ fn file_try_clone() {
}

#[test]
#[cfg(not(windows))]
#[cfg(not(target_vendor = "win7"))]
fn unlink_readonly() {
let tmpdir = tmpdir();
let path = tmpdir.join("file");
18 changes: 17 additions & 1 deletion library/std/src/sys/pal/unix/fs.rs
Original file line number Diff line number Diff line change
@@ -9,9 +9,12 @@ use libc::c_char;
#[cfg(any(
all(target_os = "linux", not(target_env = "musl")),
target_os = "android",
target_os = "fuchsia",
target_os = "hurd"
))]
use libc::dirfd;
#[cfg(target_os = "fuchsia")]
use libc::fstatat as fstatat64;
#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
use libc::fstatat64;
#[cfg(any(
@@ -848,7 +851,6 @@ impl Drop for Dir {
target_os = "vita",
target_os = "hurd",
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "vxworks",
target_os = "rtems",
@@ -880,6 +882,7 @@ impl DirEntry {
any(
all(target_os = "linux", not(target_env = "musl")),
target_os = "android",
target_os = "fuchsia",
target_os = "hurd"
),
not(miri) // no dirfd on Miri
@@ -908,6 +911,7 @@ impl DirEntry {
not(any(
all(target_os = "linux", not(target_env = "musl")),
target_os = "android",
target_os = "fuchsia",
target_os = "hurd",
)),
miri
@@ -1211,6 +1215,7 @@ impl File {
}
#[cfg(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "android",
target_os = "netbsd",
@@ -1223,6 +1228,7 @@ impl File {
}
#[cfg(not(any(
target_os = "android",
target_os = "fuchsia",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
@@ -1238,6 +1244,7 @@ impl File {

#[cfg(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1249,6 +1256,7 @@ impl File {

#[cfg(not(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1259,6 +1267,7 @@ impl File {

#[cfg(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1270,6 +1279,7 @@ impl File {

#[cfg(not(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1280,6 +1290,7 @@ impl File {

#[cfg(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1297,6 +1308,7 @@ impl File {

#[cfg(not(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1307,6 +1319,7 @@ impl File {

#[cfg(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1324,6 +1337,7 @@ impl File {

#[cfg(not(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1334,6 +1348,7 @@ impl File {

#[cfg(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
@@ -1345,6 +1360,7 @@ impl File {

#[cfg(not(any(
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_vendor = "apple",
26 changes: 24 additions & 2 deletions library/std/src/sys/pal/windows/fs.rs
Original file line number Diff line number Diff line change
@@ -296,6 +296,10 @@ impl OpenOptions {
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
let path = maybe_verbatim(path)?;
Self::open_native(&path, opts)
}

fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> {
let creation = opts.get_creation_mode()?;
let handle = unsafe {
c::CreateFileW(
@@ -1226,8 +1230,26 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {

pub fn unlink(p: &Path) -> io::Result<()> {
let p_u16s = maybe_verbatim(p)?;
cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
Ok(())
if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 {
let err = api::get_last_error();
// if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove
// the file while ignoring the readonly attribute.
// This is accomplished by calling the `posix_delete` function on an open file handle.
if err == WinError::ACCESS_DENIED {
let mut opts = OpenOptions::new();
opts.access_mode(c::DELETE);
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);
if let Ok(f) = File::open_native(&p_u16s, &opts) {
if f.posix_delete().is_ok() {
return Ok(());
}
}
}
// return the original error if any of the above fails.
Err(io::Error::from_raw_os_error(err.code as i32))
} else {
Ok(())
}
}

pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
8 changes: 8 additions & 0 deletions library/std/tests/win_delete_self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![cfg(windows)]

/// Attempting to delete a running binary should return an error on Windows.
#[test]
fn win_delete_self() {
let path = std::env::current_exe().unwrap();
assert!(std::fs::remove_file(path).is_err());
}
220 changes: 208 additions & 12 deletions src/bootstrap/src/core/build_steps/perf.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,231 @@
use std::fmt::{Display, Formatter};

use crate::core::build_steps::compile::{Std, Sysroot};
use crate::core::build_steps::tool::{RustcPerf, Tool};
use crate::core::build_steps::tool::{RustcPerf, Rustdoc};
use crate::core::builder::Builder;
use crate::core::config::DebuginfoLevel;
use crate::utils::exec::{BootstrapCommand, command};

#[derive(Debug, Clone, clap::Parser)]
pub struct PerfArgs {
#[clap(subcommand)]
cmd: PerfCommand,
}

#[derive(Debug, Clone, clap::Parser)]
enum PerfCommand {
/// Run `profile_local eprintln`.
/// This executes the compiler on the given benchmarks and stores its stderr output.
Eprintln {
#[clap(flatten)]
opts: SharedOpts,
},
/// Run `profile_local samply`
/// This executes the compiler on the given benchmarks and profiles it with `samply`.
/// You need to install `samply`, e.g. using `cargo install samply`.
Samply {
#[clap(flatten)]
opts: SharedOpts,
},
/// Run `profile_local cachegrind`.
/// This executes the compiler on the given benchmarks under `Cachegrind`.
Cachegrind {
#[clap(flatten)]
opts: SharedOpts,
},
/// Run compile benchmarks with a locally built compiler.
Benchmark {
/// Identifier to associate benchmark results with
#[clap(name = "benchmark-id")]
id: String,

#[clap(flatten)]
opts: SharedOpts,
},
/// Compare the results of two previously executed benchmark runs.
Compare {
/// The name of the base artifact to be compared.
base: String,

/// The name of the modified artifact to be compared.
modified: String,
},
}

impl PerfCommand {
fn shared_opts(&self) -> Option<&SharedOpts> {
match self {
PerfCommand::Eprintln { opts, .. }
| PerfCommand::Samply { opts, .. }
| PerfCommand::Cachegrind { opts, .. }
| PerfCommand::Benchmark { opts, .. } => Some(opts),
PerfCommand::Compare { .. } => None,
}
}
}

#[derive(Debug, Clone, clap::Parser)]
struct SharedOpts {
/// Select the benchmarks that you want to run (separated by commas).
/// If unspecified, all benchmarks will be executed.
#[clap(long, global = true, value_delimiter = ',')]
include: Vec<String>,

/// Select the benchmarks matching a prefix in this comma-separated list that you don't want to run.
#[clap(long, global = true, value_delimiter = ',')]
exclude: Vec<String>,

/// Select the scenarios that should be benchmarked.
#[clap(
long,
global = true,
value_delimiter = ',',
default_value = "Full,IncrFull,IncrUnchanged,IncrPatched"
)]
scenarios: Vec<Scenario>,
/// Select the profiles that should be benchmarked.
#[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")]
profiles: Vec<Profile>,
}

#[derive(Clone, Copy, Debug, PartialEq, clap::ValueEnum)]
#[value(rename_all = "PascalCase")]
pub enum Profile {
Check,
Debug,
Doc,
Opt,
Clippy,
}

impl Display for Profile {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = match self {
Profile::Check => "Check",
Profile::Debug => "Debug",
Profile::Doc => "Doc",
Profile::Opt => "Opt",
Profile::Clippy => "Clippy",
};
f.write_str(name)
}
}

#[derive(Clone, Copy, Debug, clap::ValueEnum)]
#[value(rename_all = "PascalCase")]
pub enum Scenario {
Full,
IncrFull,
IncrUnchanged,
IncrPatched,
}

impl Display for Scenario {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = match self {
Scenario::Full => "Full",
Scenario::IncrFull => "IncrFull",
Scenario::IncrUnchanged => "IncrUnchanged",
Scenario::IncrPatched => "IncrPatched",
};
f.write_str(name)
}
}

/// Performs profiling using `rustc-perf` on a built version of the compiler.
pub fn perf(builder: &Builder<'_>) {
pub fn perf(builder: &Builder<'_>, args: &PerfArgs) {
let collector = builder.ensure(RustcPerf {
compiler: builder.compiler(0, builder.config.build),
target: builder.config.build,
});

if builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None {
let is_profiling = match &args.cmd {
PerfCommand::Eprintln { .. }
| PerfCommand::Samply { .. }
| PerfCommand::Cachegrind { .. } => true,
PerfCommand::Benchmark { .. } | PerfCommand::Compare { .. } => false,
};
if is_profiling && builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None {
builder.info(r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful.
Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
}

let compiler = builder.compiler(builder.top_stage, builder.config.build);
builder.ensure(Std::new(compiler, builder.config.build));

if let Some(opts) = args.cmd.shared_opts() {
if opts.profiles.contains(&Profile::Doc) {
builder.ensure(Rustdoc { compiler });
}
}

let sysroot = builder.ensure(Sysroot::new(compiler));
let rustc = sysroot.join("bin/rustc");

let rustc_perf_dir = builder.build.tempdir().join("rustc-perf");
let profile_results_dir = rustc_perf_dir.join("results");
let results_dir = rustc_perf_dir.join("results");
builder.create_dir(&results_dir);

let mut cmd = command(collector);

// We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory
// with compile-time benchmarks.
cmd.current_dir(builder.src.join("src/tools/rustc-perf"));

let db_path = results_dir.join("results.db");

match &args.cmd {
PerfCommand::Eprintln { opts }
| PerfCommand::Samply { opts }
| PerfCommand::Cachegrind { opts } => {
cmd.arg("profile_local");
cmd.arg(match &args.cmd {
PerfCommand::Eprintln { .. } => "eprintln",
PerfCommand::Samply { .. } => "samply",
PerfCommand::Cachegrind { .. } => "cachegrind",
_ => unreachable!(),
});

// We need to take args passed after `--` and pass them to `rustc-perf-wrapper`
let args = std::env::args().skip_while(|a| a != "--").skip(1);
cmd.arg("--out-dir").arg(&results_dir);
cmd.arg(rustc);

let mut cmd = builder.tool_cmd(Tool::RustcPerfWrapper);
cmd.env("RUSTC_REAL", rustc)
.env("PERF_COLLECTOR", collector)
.env("PERF_RESULT_DIR", profile_results_dir)
.args(args);
cmd.run(builder);
apply_shared_opts(&mut cmd, opts);
cmd.run(builder);

println!("You can find the results at `{}`", &results_dir.display());
}
PerfCommand::Benchmark { id, opts } => {
cmd.arg("bench_local");
cmd.arg("--db").arg(&db_path);
cmd.arg("--id").arg(id);
cmd.arg(rustc);

apply_shared_opts(&mut cmd, opts);
cmd.run(builder);
}
PerfCommand::Compare { base, modified } => {
cmd.arg("bench_cmp");
cmd.arg("--db").arg(&db_path);
cmd.arg(base).arg(modified);

cmd.run(builder);
}
}
}

fn apply_shared_opts(cmd: &mut BootstrapCommand, opts: &SharedOpts) {
if !opts.include.is_empty() {
cmd.arg("--include").arg(opts.include.join(","));
}
if !opts.exclude.is_empty() {
cmd.arg("--exclude").arg(opts.exclude.join(","));
}
if !opts.profiles.is_empty() {
cmd.arg("--profiles")
.arg(opts.profiles.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
}
if !opts.scenarios.is_empty() {
cmd.arg("--scenarios")
.arg(opts.scenarios.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
}
}
1 change: 0 additions & 1 deletion src/bootstrap/src/core/build_steps/tool.rs
Original file line number Diff line number Diff line change
@@ -362,7 +362,6 @@ bootstrap_tool!(
GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test";
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
8 changes: 3 additions & 5 deletions src/bootstrap/src/core/config/flags.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ use clap::{CommandFactory, Parser, ValueEnum};
#[cfg(feature = "tracing")]
use tracing::instrument;

use crate::core::build_steps::perf::PerfArgs;
use crate::core::build_steps::setup::Profile;
use crate::core::builder::{Builder, Kind};
use crate::core::config::{Config, TargetSelectionList, target_selection_list};
@@ -481,11 +482,8 @@ Arguments:
#[arg(long)]
versioned_dirs: bool,
},
/// Perform profiling and benchmarking of the compiler using the
/// `rustc-perf-wrapper` tool.
///
/// You need to pass arguments after `--`, e.g.`x perf -- cachegrind`.
Perf {},
/// Perform profiling and benchmarking of the compiler using `rustc-perf`.
Perf(PerfArgs),
}

impl Subcommand {
4 changes: 2 additions & 2 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
@@ -571,8 +571,8 @@ impl Build {
Subcommand::Suggest { run } => {
return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
}
Subcommand::Perf { .. } => {
return core::build_steps::perf::perf(&builder::Builder::new(self));
Subcommand::Perf(args) => {
return core::build_steps::perf::perf(&builder::Builder::new(self), args);
}
_cmd => {
debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");
4 changes: 1 addition & 3 deletions src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md
Original file line number Diff line number Diff line change
@@ -7,9 +7,7 @@ However, using the suite manually can be a bit cumbersome. To make this easier f
the compiler build system (`bootstrap`) also provides built-in integration with the benchmarking suite,
which will download and build the suite for you, build a local compiler toolchain and let you profile it using a simplified command-line interface.

You can use the `./x perf -- <command> [options]` command to use this integration.

> Note that you need to specify arguments after `--` in the `x perf` command! You will not be able to pass arguments without the double dashes.
You can use the `./x perf <command> [options]` command to use this integration.

You can use normal bootstrap flags for this command, such as `--stage 1` or `--stage 2`, for example to modify the stage of the created sysroot. It might also be useful to configure `config.toml` to better support profiling, e.g. set `rust.debuginfo-level = 1` to add source line information to the built compiler.

250 changes: 216 additions & 34 deletions src/etc/completions/x.fish

Large diffs are not rendered by default.

219 changes: 218 additions & 1 deletion src/etc/completions/x.ps1

Large diffs are not rendered by default.

250 changes: 216 additions & 34 deletions src/etc/completions/x.py.fish

Large diffs are not rendered by default.

219 changes: 218 additions & 1 deletion src/etc/completions/x.py.ps1

Large diffs are not rendered by default.

1,010 changes: 1,009 additions & 1 deletion src/etc/completions/x.py.sh

Large diffs are not rendered by default.

273 changes: 271 additions & 2 deletions src/etc/completions/x.py.zsh

Large diffs are not rendered by default.

1,010 changes: 1,009 additions & 1 deletion src/etc/completions/x.sh

Large diffs are not rendered by default.

273 changes: 271 additions & 2 deletions src/etc/completions/x.zsh

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions src/tools/rustc-perf-wrapper/Cargo.toml

This file was deleted.

3 changes: 0 additions & 3 deletions src/tools/rustc-perf-wrapper/README.md

This file was deleted.

45 changes: 0 additions & 45 deletions src/tools/rustc-perf-wrapper/src/config.rs

This file was deleted.

178 changes: 0 additions & 178 deletions src/tools/rustc-perf-wrapper/src/main.rs

This file was deleted.

5 changes: 5 additions & 0 deletions tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4

pub fn check_is_even(number: &u64) -> bool {
number % 2 == 0
}
19 changes: 19 additions & 0 deletions tests/assembly/dwarf-mixed-versions-lto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// This test ensures that if LTO occurs between crates with different DWARF versions, we
// will choose the highest DWARF version for the final binary. This matches Clang's behavior.

//@ only-linux
//@ aux-build:dwarf-mixed-versions-lto-aux.rs
//@ compile-flags: -C lto -g -Zdwarf-version=5
//@ assembly-output: emit-asm
//@ no-prefer-dynamic

extern crate dwarf_mixed_versions_lto_aux;

fn main() {
dwarf_mixed_versions_lto_aux::check_is_even(&0);
}

// CHECK: .section .debug_info
// CHECK-NOT: {{\.(short|hword)}} 2
// CHECK-NOT: {{\.(short|hword)}} 4
// CHECK: {{\.(short|hword)}} 5
5 changes: 5 additions & 0 deletions tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4

pub fn say_hi() {
println!("hello there")
}
15 changes: 15 additions & 0 deletions tests/ui/lto/dwarf-mixed-versions-lto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This test verifies that we do not produce a warning when performing LTO on a
// crate graph that contains a mix of different DWARF version settings. This
// matches Clang's behavior.

//@ ignore-msvc Platform must use DWARF
//@ aux-build:dwarf-mixed-versions-lto-aux.rs
//@ compile-flags: -C lto -g -Zdwarf-version=5
//@ no-prefer-dynamic
//@ build-pass

extern crate dwarf_mixed_versions_lto_aux;

fn main() {
dwarf_mixed_versions_lto_aux::say_hi();
}
2 changes: 1 addition & 1 deletion tests/ui/mir/null/borrowed_mut_null.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ run-fail
//@ compile-flags: -C debug-assertions
//@ error-pattern: null pointer dereference occured
//@ error-pattern: null pointer dereference occurred

fn main() {
let ptr: *mut u32 = std::ptr::null_mut();
2 changes: 1 addition & 1 deletion tests/ui/mir/null/borrowed_null.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ run-fail
//@ compile-flags: -C debug-assertions
//@ error-pattern: null pointer dereference occured
//@ error-pattern: null pointer dereference occurred

fn main() {
let ptr: *const u32 = std::ptr::null();
8 changes: 8 additions & 0 deletions tests/ui/mir/null/borrowed_null_zst.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//@ run-fail
//@ compile-flags: -C debug-assertions
//@ error-pattern: null pointer dereference occurred

fn main() {
let ptr: *const () = std::ptr::null();
let _ptr: &() = unsafe { &*ptr };
}
2 changes: 1 addition & 1 deletion tests/ui/mir/null/null_lhs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ run-fail
//@ compile-flags: -C debug-assertions
//@ error-pattern: null pointer dereference occured
//@ error-pattern: null pointer dereference occurred

fn main() {
let ptr: *mut u32 = std::ptr::null_mut();
2 changes: 1 addition & 1 deletion tests/ui/mir/null/null_rhs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ run-fail
//@ compile-flags: -C debug-assertions
//@ error-pattern: null pointer dereference occured
//@ error-pattern: null pointer dereference occurred

fn main() {
let ptr: *mut u32 = std::ptr::null_mut();
1 change: 1 addition & 0 deletions tests/ui/mir/null/place_without_read.rs
Original file line number Diff line number Diff line change
@@ -6,5 +6,6 @@ fn main() {
let ptr: *const u16 = std::ptr::null();
unsafe {
let _ = *ptr;
let _ = &raw const *ptr;
}
}
11 changes: 11 additions & 0 deletions tests/ui/mir/null/place_without_read_zst.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Make sure that we don't insert a check for places that do not read.
//@ run-pass
//@ compile-flags: -C debug-assertions

fn main() {
let ptr: *const () = std::ptr::null();
unsafe {
let _ = *ptr;
let _ = &raw const *ptr;
}
}
2 changes: 1 addition & 1 deletion tests/ui/mir/null/two_pointers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ run-fail
//@ compile-flags: -C debug-assertions
//@ error-pattern: null pointer dereference occured
//@ error-pattern: null pointer dereference occurred

fn main() {
let ptr = std::ptr::null();
1 change: 0 additions & 1 deletion triagebot.toml
Original file line number Diff line number Diff line change
@@ -394,7 +394,6 @@ trigger_files = [
"src/tools/tidy",
"src/tools/rustdoc-gui-test",
"src/tools/libcxx-version",
"src/tools/rustc-perf-wrapper",
"x.py",
"x",
"x.ps1"