Skip to content

Commit d1a8efb

Browse files
committed
Auto merge of #134296 - matthiaskrgr:rollup-o0sxozj, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #132150 (Fix powerpc64 big-endian FreeBSD ABI) - #133942 (Clarify how to use `black_box()`) - #134081 (Try to evaluate constants in legacy mangling) - #134192 (Remove `Lexer`'s dependency on `Parser`.) - #134208 (coverage: Tidy up creation of covmap and covfun records) - #134211 (On Neutrino QNX, reduce the need to set archiver via environment variables) r? `@ghost` `@rustbot` modify labels: rollup
2 parents ed14192 + a942794 commit d1a8efb

File tree

20 files changed

+327
-249
lines changed

20 files changed

+327
-249
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+32-35
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
7575

7676
// Encode all filenames referenced by coverage mappings in this CGU.
7777
let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
78-
79-
let filenames_size = filenames_buffer.len();
80-
let filenames_val = cx.const_bytes(&filenames_buffer);
81-
let filenames_ref = llvm_cov::hash_bytes(&filenames_buffer);
78+
// The `llvm-cov` tool uses this hash to associate each covfun record with
79+
// its corresponding filenames table, since the final binary will typically
80+
// contain multiple covmap records from different compilation units.
81+
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
8282

8383
let mut unused_function_names = Vec::new();
8484

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

104-
covfun::generate_covfun_record(cx, filenames_ref, covfun)
104+
covfun::generate_covfun_record(cx, filenames_hash, covfun)
105105
}
106106

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

132132
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
@@ -225,38 +225,35 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
225225
/// Generates the contents of the covmap record for this CGU, which mostly
226226
/// consists of a header and a list of filenames. The record is then stored
227227
/// as a global variable in the `__llvm_covmap` section.
228-
fn generate_covmap_record<'ll>(
229-
cx: &CodegenCx<'ll, '_>,
230-
version: u32,
231-
filenames_size: usize,
232-
filenames_val: &'ll llvm::Value,
233-
) {
234-
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);
235-
236-
// Create the coverage data header (Note, fields 0 and 2 are now always zero,
237-
// as of `llvm::coverage::CovMapVersion::Version4`.)
238-
let zero_was_n_records_val = cx.const_u32(0);
239-
let filenames_size_val = cx.const_u32(filenames_size as u32);
240-
let zero_was_coverage_size_val = cx.const_u32(0);
241-
let version_val = cx.const_u32(version);
242-
let cov_data_header_val = cx.const_struct(
243-
&[zero_was_n_records_val, filenames_size_val, zero_was_coverage_size_val, version_val],
244-
/*packed=*/ false,
228+
fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) {
229+
// A covmap record consists of four target-endian u32 values, followed by
230+
// the encoded filenames table. Two of the header fields are unused in
231+
// modern versions of the LLVM coverage mapping format, and are always 0.
232+
// <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
233+
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp`.
234+
let covmap_header = cx.const_struct(
235+
&[
236+
cx.const_u32(0), // (unused)
237+
cx.const_u32(filenames_buffer.len() as u32),
238+
cx.const_u32(0), // (unused)
239+
cx.const_u32(version),
240+
],
241+
/* packed */ false,
245242
);
246-
247-
// Create the complete LLVM coverage data value to add to the LLVM IR
248-
let covmap_data =
249-
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false);
250-
251-
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &llvm_cov::covmap_var_name());
252-
llvm::set_initializer(llglobal, covmap_data);
253-
llvm::set_global_constant(llglobal, true);
254-
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
255-
llvm::set_section(llglobal, &llvm_cov::covmap_section_name(cx.llmod));
243+
let covmap_record = cx
244+
.const_struct(&[covmap_header, cx.const_bytes(filenames_buffer)], /* packed */ false);
245+
246+
let covmap_global =
247+
llvm::add_global(cx.llmod, cx.val_ty(covmap_record), &llvm_cov::covmap_var_name());
248+
llvm::set_initializer(covmap_global, covmap_record);
249+
llvm::set_global_constant(covmap_global, true);
250+
llvm::set_linkage(covmap_global, llvm::Linkage::PrivateLinkage);
251+
llvm::set_section(covmap_global, &llvm_cov::covmap_section_name(cx.llmod));
256252
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
257253
// <https://llvm.org/docs/CoverageMappingFormat.html>
258-
llvm::set_alignment(llglobal, Align::EIGHT);
259-
cx.add_used_global(llglobal);
254+
llvm::set_alignment(covmap_global, Align::EIGHT);
255+
256+
cx.add_used_global(covmap_global);
260257
}
261258

262259
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

+29-30
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ fn fill_region_tables<'tcx>(
136136
/// as a global variable in the `__llvm_covfun` section.
137137
pub(crate) fn generate_covfun_record<'tcx>(
138138
cx: &CodegenCx<'_, 'tcx>,
139-
filenames_ref: u64,
139+
filenames_hash: u64,
140140
covfun: &CovfunRecord<'tcx>,
141141
) {
142142
let &CovfunRecord {
@@ -155,46 +155,45 @@ pub(crate) fn generate_covfun_record<'tcx>(
155155
regions,
156156
);
157157

158-
// Concatenate the encoded coverage mappings
159-
let coverage_mapping_size = coverage_mapping_buffer.len();
160-
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);
161-
158+
// A covfun record consists of four target-endian integers, followed by the
159+
// encoded mapping data in bytes. Note that the length field is 32 bits.
160+
// <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
161+
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp` and
162+
// `COVMAP_V3` in `src/llvm-project/llvm/include/llvm/ProfileData/InstrProfData.inc`.
162163
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
163-
let func_name_hash_val = cx.const_u64(func_name_hash);
164-
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
165-
let source_hash_val = cx.const_u64(source_hash);
166-
let filenames_ref_val = cx.const_u64(filenames_ref);
167-
let func_record_val = cx.const_struct(
164+
let covfun_record = cx.const_struct(
168165
&[
169-
func_name_hash_val,
170-
coverage_mapping_size_val,
171-
source_hash_val,
172-
filenames_ref_val,
173-
coverage_mapping_val,
166+
cx.const_u64(func_name_hash),
167+
cx.const_u32(coverage_mapping_buffer.len() as u32),
168+
cx.const_u64(source_hash),
169+
cx.const_u64(filenames_hash),
170+
cx.const_bytes(&coverage_mapping_buffer),
174171
],
175-
/*packed=*/ true,
172+
// This struct needs to be packed, so that the 32-bit length field
173+
// doesn't have unexpected padding.
174+
true,
176175
);
177176

178177
// Choose a variable name to hold this function's covfun data.
179178
// Functions that are used have a suffix ("u") to distinguish them from
180179
// unused copies of the same function (from different CGUs), so that if a
181180
// linker sees both it won't discard the used copy's data.
182-
let func_record_var_name =
183-
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
184-
.unwrap();
185-
debug!("function record var name: {:?}", func_record_var_name);
186-
187-
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
188-
llvm::set_initializer(llglobal, func_record_val);
189-
llvm::set_global_constant(llglobal, true);
190-
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
191-
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
192-
llvm::set_section(llglobal, cx.covfun_section_name());
181+
let u = if is_used { "u" } else { "" };
182+
let covfun_var_name = CString::new(format!("__covrec_{func_name_hash:X}{u}")).unwrap();
183+
debug!("function record var name: {covfun_var_name:?}");
184+
185+
let covfun_global = llvm::add_global(cx.llmod, cx.val_ty(covfun_record), &covfun_var_name);
186+
llvm::set_initializer(covfun_global, covfun_record);
187+
llvm::set_global_constant(covfun_global, true);
188+
llvm::set_linkage(covfun_global, llvm::Linkage::LinkOnceODRLinkage);
189+
llvm::set_visibility(covfun_global, llvm::Visibility::Hidden);
190+
llvm::set_section(covfun_global, cx.covfun_section_name());
193191
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
194192
// <https://llvm.org/docs/CoverageMappingFormat.html>
195-
llvm::set_alignment(llglobal, Align::EIGHT);
193+
llvm::set_alignment(covfun_global, Align::EIGHT);
196194
if cx.target_spec().supports_comdat() {
197-
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
195+
llvm::set_comdat(cx.llmod, covfun_global, &covfun_var_name);
198196
}
199-
cx.add_used_global(llglobal);
197+
198+
cx.add_used_global(covfun_global);
200199
}

compiler/rustc_parse/src/lexer/mod.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,30 @@ pub(crate) fn lex_token_trees<'psess, 'src>(
6969
token: Token::dummy(),
7070
diag_info: TokenTreeDiagInfo::default(),
7171
};
72-
let (_open_spacing, stream, res) = lexer.lex_token_trees(/* is_delimited */ false);
73-
let unmatched_delims = lexer.diag_info.unmatched_delims;
74-
75-
if res.is_ok() && unmatched_delims.is_empty() {
76-
Ok(stream)
77-
} else {
78-
// Return error if there are unmatched delimiters or unclosed delimiters.
79-
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
80-
// because the delimiter mismatch is more likely to be the root cause of error
81-
let mut buffer: Vec<_> = unmatched_delims
82-
.into_iter()
83-
.filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
84-
.collect();
85-
if let Err(errs) = res {
86-
// Add unclosing delimiter or diff marker errors
87-
buffer.extend(errs);
72+
let res = lexer.lex_token_trees(/* is_delimited */ false);
73+
74+
let mut unmatched_delims: Vec<_> = lexer
75+
.diag_info
76+
.unmatched_delims
77+
.into_iter()
78+
.filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess))
79+
.collect();
80+
81+
match res {
82+
Ok((_open_spacing, stream)) => {
83+
if unmatched_delims.is_empty() {
84+
Ok(stream)
85+
} else {
86+
// Return error if there are unmatched delimiters or unclosed delimiters.
87+
Err(unmatched_delims)
88+
}
89+
}
90+
Err(errs) => {
91+
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
92+
// because the delimiter mismatch is more likely to be the root cause of error
93+
unmatched_delims.extend(errs);
94+
Err(unmatched_delims)
8895
}
89-
Err(buffer)
9096
}
9197
}
9298

compiler/rustc_parse/src/lexer/tokentrees.rs

+14-80
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
use rustc_ast::token::{self, Delimiter, Token};
22
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
33
use rustc_ast_pretty::pprust::token_to_string;
4-
use rustc_errors::{Applicability, Diag};
5-
use rustc_span::symbol::kw;
4+
use rustc_errors::Diag;
65

76
use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level};
87
use super::{Lexer, UnmatchedDelim};
9-
use crate::Parser;
108

119
impl<'psess, 'src> Lexer<'psess, 'src> {
1210
// Lex into a token stream. The `Spacing` in the result is that of the
1311
// opening delimiter.
1412
pub(super) fn lex_token_trees(
1513
&mut self,
1614
is_delimited: bool,
17-
) -> (Spacing, TokenStream, Result<(), Vec<Diag<'psess>>>) {
15+
) -> Result<(Spacing, TokenStream), Vec<Diag<'psess>>> {
1816
// Move past the opening delimiter.
1917
let open_spacing = self.bump_minimal();
2018

@@ -27,25 +25,25 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
2725
debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
2826
buf.push(match self.lex_token_tree_open_delim(delim) {
2927
Ok(val) => val,
30-
Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)),
28+
Err(errs) => return Err(errs),
3129
})
3230
}
3331
token::CloseDelim(delim) => {
3432
// Invisible delimiters cannot occur here because `TokenTreesReader` parses
3533
// code directly from strings, with no macro expansion involved.
3634
debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
37-
return (
38-
open_spacing,
39-
TokenStream::new(buf),
40-
if is_delimited { Ok(()) } else { Err(vec![self.close_delim_err(delim)]) },
41-
);
35+
return if is_delimited {
36+
Ok((open_spacing, TokenStream::new(buf)))
37+
} else {
38+
Err(vec![self.close_delim_err(delim)])
39+
};
4240
}
4341
token::Eof => {
44-
return (
45-
open_spacing,
46-
TokenStream::new(buf),
47-
if is_delimited { Err(vec![self.eof_err()]) } else { Ok(()) },
48-
);
42+
return if is_delimited {
43+
Err(vec![self.eof_err()])
44+
} else {
45+
Ok((open_spacing, TokenStream::new(buf)))
46+
};
4947
}
5048
_ => {
5149
// Get the next normal token.
@@ -107,10 +105,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
107105
// Lex the token trees within the delimiters.
108106
// We stop at any delimiter so we can try to recover if the user
109107
// uses an incorrect delimiter.
110-
let (open_spacing, tts, res) = self.lex_token_trees(/* is_delimited */ true);
111-
if let Err(errs) = res {
112-
return Err(self.unclosed_delim_err(tts, errs));
113-
}
108+
let (open_spacing, tts) = self.lex_token_trees(/* is_delimited */ true)?;
114109

115110
// Expand to cover the entire delimited token tree.
116111
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
@@ -247,67 +242,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
247242
this_spacing
248243
}
249244

250-
fn unclosed_delim_err(
251-
&mut self,
252-
tts: TokenStream,
253-
mut errs: Vec<Diag<'psess>>,
254-
) -> Vec<Diag<'psess>> {
255-
// If there are unclosed delims, see if there are diff markers and if so, point them
256-
// out instead of complaining about the unclosed delims.
257-
let mut parser = Parser::new(self.psess, tts, None);
258-
let mut diff_errs = vec![];
259-
// Suggest removing a `{` we think appears in an `if`/`while` condition.
260-
// We want to suggest removing a `{` only if we think we're in an `if`/`while` condition,
261-
// but we have no way of tracking this in the lexer itself, so we piggyback on the parser.
262-
let mut in_cond = false;
263-
while parser.token != token::Eof {
264-
if let Err(diff_err) = parser.err_vcs_conflict_marker() {
265-
diff_errs.push(diff_err);
266-
} else if parser.is_keyword_ahead(0, &[kw::If, kw::While]) {
267-
in_cond = true;
268-
} else if matches!(
269-
parser.token.kind,
270-
token::CloseDelim(Delimiter::Brace) | token::FatArrow
271-
) {
272-
// End of the `if`/`while` body, or the end of a `match` guard.
273-
in_cond = false;
274-
} else if in_cond && parser.token == token::OpenDelim(Delimiter::Brace) {
275-
// Store the `&&` and `let` to use their spans later when creating the diagnostic
276-
let maybe_andand = parser.look_ahead(1, |t| t.clone());
277-
let maybe_let = parser.look_ahead(2, |t| t.clone());
278-
if maybe_andand == token::OpenDelim(Delimiter::Brace) {
279-
// This might be the beginning of the `if`/`while` body (i.e., the end of the
280-
// condition).
281-
in_cond = false;
282-
} else if maybe_andand == token::AndAnd && maybe_let.is_keyword(kw::Let) {
283-
let mut err = parser.dcx().struct_span_err(
284-
parser.token.span,
285-
"found a `{` in the middle of a let-chain",
286-
);
287-
err.span_suggestion(
288-
parser.token.span,
289-
"consider removing this brace to parse the `let` as part of the same chain",
290-
"",
291-
Applicability::MachineApplicable,
292-
);
293-
err.span_label(
294-
maybe_andand.span.to(maybe_let.span),
295-
"you might have meant to continue the let-chain here",
296-
);
297-
errs.push(err);
298-
}
299-
}
300-
parser.bump();
301-
}
302-
if !diff_errs.is_empty() {
303-
for err in errs {
304-
err.cancel();
305-
}
306-
return diff_errs;
307-
}
308-
errs
309-
}
310-
311245
fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> {
312246
// An unexpected closing delimiter (i.e., there is no matching opening delimiter).
313247
let token_str = token_to_string(&self.token);

0 commit comments

Comments
 (0)