Skip to content

Commit

Permalink
Auto merge of rust-lang#134282 - matthiaskrgr:rollup-txnn8yz, r=matth…
Browse files Browse the repository at this point in the history
…iaskrgr

Rollup of 7 pull requests

Successful merges:

 - rust-lang#132150 (Fix powerpc64 big-endian FreeBSD ABI)
 - rust-lang#133633 (don't show the full linker args unless `--verbose` is passed)
 - rust-lang#133942 (Clarify how to use `black_box()`)
 - rust-lang#134081 (Try to evaluate constants in legacy mangling)
 - rust-lang#134192 (Remove `Lexer`'s dependency on `Parser`.)
 - rust-lang#134208 (coverage: Tidy up creation of covmap and covfun records)
 - rust-lang#134211 (On Neutrino QNX, reduce the need to set archiver via environment variables)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Dec 14, 2024
2 parents 4a204be + 9668539 commit 354e098
Show file tree
Hide file tree
Showing 28 changed files with 453 additions and 263 deletions.
67 changes: 32 additions & 35 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {

// Encode all filenames referenced by coverage mappings in this CGU.
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);

let filenames_size = filenames_buffer.len();
let filenames_val = cx.const_bytes(&filenames_buffer);
let filenames_ref = llvm_cov::hash_bytes(&filenames_buffer);
// The `llvm-cov` tool uses this hash to associate each covfun record with
// its corresponding filenames table, since the final binary will typically
// contain multiple covmap records from different compilation units.
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);

let mut unused_function_names = Vec::new();

Expand All @@ -101,7 +101,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
for covfun in &covfun_records {
unused_function_names.extend(covfun.mangled_function_name_if_unused());

covfun::generate_covfun_record(cx, filenames_ref, covfun)
covfun::generate_covfun_record(cx, filenames_hash, covfun)
}

// For unused functions, we need to take their mangled names and store them
Expand All @@ -126,7 +126,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
// Generate the coverage map header, which contains the filenames used by
// this CGU's coverage mappings, and store it in a well-known global.
// (This is skipped if we returned early due to having no covfun records.)
generate_covmap_record(cx, covmap_version, filenames_size, filenames_val);
generate_covmap_record(cx, covmap_version, &filenames_buffer);
}

/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
Expand Down Expand Up @@ -225,38 +225,35 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
/// Generates the contents of the covmap record for this CGU, which mostly
/// consists of a header and a list of filenames. The record is then stored
/// as a global variable in the `__llvm_covmap` section.
fn generate_covmap_record<'ll>(
cx: &CodegenCx<'ll, '_>,
version: u32,
filenames_size: usize,
filenames_val: &'ll llvm::Value,
) {
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);

// Create the coverage data header (Note, fields 0 and 2 are now always zero,
// as of `llvm::coverage::CovMapVersion::Version4`.)
let zero_was_n_records_val = cx.const_u32(0);
let filenames_size_val = cx.const_u32(filenames_size as u32);
let zero_was_coverage_size_val = cx.const_u32(0);
let version_val = cx.const_u32(version);
let cov_data_header_val = cx.const_struct(
&[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
/*packed=*/ false,
fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) {
// A covmap record consists of four target-endian u32 values, followed by
// the encoded filenames table. Two of the header fields are unused in
// modern versions of the LLVM coverage mapping format, and are always 0.
// <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp`.
let covmap_header = cx.const_struct(
&[
cx.const_u32(0), // (unused)
cx.const_u32(filenames_buffer.len() as u32),
cx.const_u32(0), // (unused)
cx.const_u32(version),
],
/* packed */ false,
);

// Create the complete LLVM coverage data value to add to the LLVM IR
let covmap_data =
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false);

let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &llvm_cov::covmap_var_name());
llvm::set_initializer(llglobal, covmap_data);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::set_section(llglobal, &llvm_cov::covmap_section_name(cx.llmod));
let covmap_record = cx
.const_struct(&[covmap_header, cx.const_bytes(filenames_buffer)], /* packed */ false);

let covmap_global =
llvm::add_global(cx.llmod, cx.val_ty(covmap_record), &llvm_cov::covmap_var_name());
llvm::set_initializer(covmap_global, covmap_record);
llvm::set_global_constant(covmap_global, true);
llvm::set_linkage(covmap_global, llvm::Linkage::PrivateLinkage);
llvm::set_section(covmap_global, &llvm_cov::covmap_section_name(cx.llmod));
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT);
cx.add_used_global(llglobal);
llvm::set_alignment(covmap_global, Align::EIGHT);

cx.add_used_global(covmap_global);
}

/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
Expand Down
59 changes: 29 additions & 30 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ fn fill_region_tables<'tcx>(
/// as a global variable in the `__llvm_covfun` section.
pub(crate) fn generate_covfun_record<'tcx>(
cx: &CodegenCx<'_, 'tcx>,
filenames_ref: u64,
filenames_hash: u64,
covfun: &CovfunRecord<'tcx>,
) {
let &CovfunRecord {
Expand All @@ -155,46 +155,45 @@ pub(crate) fn generate_covfun_record<'tcx>(
regions,
);

// Concatenate the encoded coverage mappings
let coverage_mapping_size = coverage_mapping_buffer.len();
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);

// A covfun record consists of four target-endian integers, followed by the
// encoded mapping data in bytes. Note that the length field is 32 bits.
// <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp` and
// `COVMAP_V3` in `src/llvm-project/llvm/include/llvm/ProfileData/InstrProfData.inc`.
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
let func_name_hash_val = cx.const_u64(func_name_hash);
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
let source_hash_val = cx.const_u64(source_hash);
let filenames_ref_val = cx.const_u64(filenames_ref);
let func_record_val = cx.const_struct(
let covfun_record = cx.const_struct(
&[
func_name_hash_val,
coverage_mapping_size_val,
source_hash_val,
filenames_ref_val,
coverage_mapping_val,
cx.const_u64(func_name_hash),
cx.const_u32(coverage_mapping_buffer.len() as u32),
cx.const_u64(source_hash),
cx.const_u64(filenames_hash),
cx.const_bytes(&coverage_mapping_buffer),
],
/*packed=*/ true,
// This struct needs to be packed, so that the 32-bit length field
// doesn't have unexpected padding.
true,
);

// Choose a variable name to hold this function's covfun data.
// Functions that are used have a suffix ("u") to distinguish them from
// unused copies of the same function (from different CGUs), so that if a
// linker sees both it won't discard the used copy's data.
let func_record_var_name =
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
.unwrap();
debug!("function record var name: {:?}", func_record_var_name);

let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
llvm::set_initializer(llglobal, func_record_val);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
llvm::set_section(llglobal, cx.covfun_section_name());
let u = if is_used { "u" } else { "" };
let covfun_var_name = CString::new(format!("__covrec_{func_name_hash:X}{u}")).unwrap();
debug!("function record var name: {covfun_var_name:?}");

let covfun_global = llvm::add_global(cx.llmod, cx.val_ty(covfun_record), &covfun_var_name);
llvm::set_initializer(covfun_global, covfun_record);
llvm::set_global_constant(covfun_global, true);
llvm::set_linkage(covfun_global, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(covfun_global, llvm::Visibility::Hidden);
llvm::set_section(covfun_global, cx.covfun_section_name());
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT);
llvm::set_alignment(covfun_global, Align::EIGHT);
if cx.target_spec().supports_comdat() {
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
llvm::set_comdat(cx.llmod, covfun_global, &covfun_var_name);
}
cx.add_used_global(llglobal);

cx.add_used_global(covfun_global);
}
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -992,12 +992,12 @@ fn link_natively(
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
let escaped_output = escape_linker_output(&output, flavor);
// FIXME: Add UI tests for this error.
let err = errors::LinkingFailed {
linker_path: &linker_path,
exit_status: prog.status,
command: &cmd,
command: cmd,
escaped_output,
verbose: sess.opts.verbose,
};
sess.dcx().emit_err(err);
// If MSVC's `link.exe` was expected but the return code
Expand Down
70 changes: 66 additions & 4 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Errors emitted by codegen_ssa
use std::borrow::Cow;
use std::ffi::OsString;
use std::io::Error;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -345,21 +346,82 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper {
}

pub(crate) struct LinkingFailed<'a> {
pub linker_path: &'a PathBuf,
pub linker_path: &'a Path,
pub exit_status: ExitStatus,
pub command: &'a Command,
pub command: Command,
pub escaped_output: String,
pub verbose: bool,
}

impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
fn into_diag(mut self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_linking_failed);
diag.arg("linker_path", format!("{}", self.linker_path.display()));
diag.arg("exit_status", format!("{}", self.exit_status));

let contains_undefined_ref = self.escaped_output.contains("undefined reference to");

diag.note(format!("{:?}", self.command)).note(self.escaped_output);
if self.verbose {
diag.note(format!("{:?}", self.command));
} else {
enum ArgGroup {
Regular(OsString),
Objects(usize),
Rlibs(PathBuf, Vec<OsString>),
}

// Omit rust object files and fold rlibs in the error by default to make linker errors a
// bit less verbose.
let orig_args = self.command.take_args();
let mut args: Vec<ArgGroup> = vec![];
for arg in orig_args {
if arg.as_encoded_bytes().ends_with(b".rcgu.o") {
if let Some(ArgGroup::Objects(n)) = args.last_mut() {
*n += 1;
} else {
args.push(ArgGroup::Objects(1));
}
} else if arg.as_encoded_bytes().ends_with(b".rlib") {
let rlib_path = Path::new(&arg);
let dir = rlib_path.parent().unwrap();
let filename = rlib_path.file_name().unwrap().to_owned();
if let Some(ArgGroup::Rlibs(parent, rlibs)) = args.last_mut() {
if parent == dir {
rlibs.push(filename);
} else {
args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename]));
}
} else {
args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename]));
}
} else {
args.push(ArgGroup::Regular(arg));
}
}
self.command.args(args.into_iter().map(|arg_group| match arg_group {
ArgGroup::Regular(arg) => arg,
ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")),
ArgGroup::Rlibs(dir, rlibs) => {
let mut arg = dir.into_os_string();
arg.push("/{");
let mut first = true;
for rlib in rlibs {
if !first {
arg.push(",");
}
first = false;
arg.push(rlib);
}
arg.push("}");
arg
}
}));

diag.note(format!("{:?}", self.command));
diag.note("some arguments are omitted. use `--verbose` to show all linker arguments");
}

diag.note(self.escaped_output);

// Trying to match an error from OS linkers
// which by now we have no way to translate.
Expand Down
40 changes: 23 additions & 17 deletions compiler/rustc_parse/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,30 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
token: Token::dummy(),
diag_info: TokenTreeDiagInfo::default(),
};
let (_open_spacing, stream, res) = lexer.lex_token_trees(/* is_delimited */ false);
let unmatched_delims = lexer.diag_info.unmatched_delims;

if res.is_ok() && unmatched_delims.is_empty() {
Ok(stream)
} else {
// Return error if there are unmatched delimiters or unclosed delimiters.
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
// because the delimiter mismatch is more likely to be the root cause of error
let mut buffer: Vec<_> = unmatched_delims
.into_iter()
.filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
.collect();
if let Err(errs) = res {
// Add unclosing delimiter or diff marker errors
buffer.extend(errs);
let res = lexer.lex_token_trees(/* is_delimited */ false);

let mut unmatched_delims: Vec<_> = lexer
.diag_info
.unmatched_delims
.into_iter()
.filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
.collect();

match res {
Ok((_open_spacing, stream)) => {
if unmatched_delims.is_empty() {
Ok(stream)
} else {
// Return error if there are unmatched delimiters or unclosed delimiters.
Err(unmatched_delims)
}
}
Err(errs) => {
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
// because the delimiter mismatch is more likely to be the root cause of error
unmatched_delims.extend(errs);
Err(unmatched_delims)
}
Err(buffer)
}
}

Expand Down
Loading

0 comments on commit 354e098

Please sign in to comment.