Skip to content

Commit a80edce

Browse files
committed
Auto merge of rust-lang#139460 - Zalathar:rollup-s3a39sh, r=Zalathar
Rollup of 4 pull requests Successful merges: - rust-lang#138314 (fix usage of `autodiff` macro with inner functions) - rust-lang#138766 (coverage: Deal with unused functions and their names in one place) - rust-lang#139298 (Allow for missing invisible close delim when reparsing an expression.) - rust-lang#139426 (Make the UnifyKey and UnifyValue imports non-nightly) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 25a615b + 117c898 commit a80edce

File tree

13 files changed

+344
-217
lines changed

13 files changed

+344
-217
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4589,6 +4589,7 @@ version = "0.0.0"
45894589
dependencies = [
45904590
"bitflags",
45914591
"derive-where",
4592+
"ena",
45924593
"indexmap",
45934594
"rustc-hash 1.1.0",
45944595
"rustc_ast_ir",

compiler/rustc_builtin_macros/src/autodiff.rs

+77-48
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod llvm_enzyme {
1717
use rustc_ast::visit::AssocCtxt::*;
1818
use rustc_ast::{
1919
self as ast, AssocItemKind, BindingMode, ExprKind, FnRetTy, FnSig, Generics, ItemKind,
20-
MetaItemInner, PatKind, QSelf, TyKind,
20+
MetaItemInner, PatKind, QSelf, TyKind, Visibility,
2121
};
2222
use rustc_expand::base::{Annotatable, ExtCtxt};
2323
use rustc_span::{Ident, Span, Symbol, kw, sym};
@@ -72,6 +72,16 @@ mod llvm_enzyme {
7272
}
7373
}
7474

75+
// Get information about the function the macro is applied to
76+
fn extract_item_info(iitem: &P<ast::Item>) -> Option<(Visibility, FnSig, Ident)> {
77+
match &iitem.kind {
78+
ItemKind::Fn(box ast::Fn { sig, ident, .. }) => {
79+
Some((iitem.vis.clone(), sig.clone(), ident.clone()))
80+
}
81+
_ => None,
82+
}
83+
}
84+
7585
pub(crate) fn from_ast(
7686
ecx: &mut ExtCtxt<'_>,
7787
meta_item: &ThinVec<MetaItemInner>,
@@ -199,32 +209,26 @@ mod llvm_enzyme {
199209
return vec![item];
200210
}
201211
let dcx = ecx.sess.dcx();
202-
// first get the annotable item:
203-
let (primal, sig, is_impl): (Ident, FnSig, bool) = match &item {
204-
Annotatable::Item(iitem) => {
205-
let (ident, sig) = match &iitem.kind {
206-
ItemKind::Fn(box ast::Fn { ident, sig, .. }) => (ident, sig),
207-
_ => {
208-
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
209-
return vec![item];
210-
}
211-
};
212-
(*ident, sig.clone(), false)
213-
}
212+
213+
// first get information about the annotable item:
214+
let Some((vis, sig, primal)) = (match &item {
215+
Annotatable::Item(iitem) => extract_item_info(iitem),
216+
Annotatable::Stmt(stmt) => match &stmt.kind {
217+
ast::StmtKind::Item(iitem) => extract_item_info(iitem),
218+
_ => None,
219+
},
214220
Annotatable::AssocItem(assoc_item, Impl { of_trait: false }) => {
215-
let (ident, sig) = match &assoc_item.kind {
216-
ast::AssocItemKind::Fn(box ast::Fn { ident, sig, .. }) => (ident, sig),
217-
_ => {
218-
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
219-
return vec![item];
221+
match &assoc_item.kind {
222+
ast::AssocItemKind::Fn(box ast::Fn { sig, ident, .. }) => {
223+
Some((assoc_item.vis.clone(), sig.clone(), ident.clone()))
220224
}
221-
};
222-
(*ident, sig.clone(), true)
223-
}
224-
_ => {
225-
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
226-
return vec![item];
225+
_ => None,
226+
}
227227
}
228+
_ => None,
229+
}) else {
230+
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
231+
return vec![item];
228232
};
229233

230234
let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
@@ -238,15 +242,6 @@ mod llvm_enzyme {
238242
let has_ret = has_ret(&sig.decl.output);
239243
let sig_span = ecx.with_call_site_ctxt(sig.span);
240244

241-
let vis = match &item {
242-
Annotatable::Item(iitem) => iitem.vis.clone(),
243-
Annotatable::AssocItem(assoc_item, _) => assoc_item.vis.clone(),
244-
_ => {
245-
dcx.emit_err(errors::AutoDiffInvalidApplication { span: item.span() });
246-
return vec![item];
247-
}
248-
};
249-
250245
// create TokenStream from vec elemtents:
251246
// meta_item doesn't have a .tokens field
252247
let mut ts: Vec<TokenTree> = vec![];
@@ -379,6 +374,22 @@ mod llvm_enzyme {
379374
}
380375
Annotatable::AssocItem(assoc_item.clone(), i)
381376
}
377+
Annotatable::Stmt(ref mut stmt) => {
378+
match stmt.kind {
379+
ast::StmtKind::Item(ref mut iitem) => {
380+
if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) {
381+
iitem.attrs.push(attr);
382+
}
383+
if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind))
384+
{
385+
iitem.attrs.push(inline_never.clone());
386+
}
387+
}
388+
_ => unreachable!("stmt kind checked previously"),
389+
};
390+
391+
Annotatable::Stmt(stmt.clone())
392+
}
382393
_ => {
383394
unreachable!("annotatable kind checked previously")
384395
}
@@ -389,22 +400,40 @@ mod llvm_enzyme {
389400
delim: rustc_ast::token::Delimiter::Parenthesis,
390401
tokens: ts,
391402
});
403+
392404
let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span);
393-
let d_annotatable = if is_impl {
394-
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
395-
let d_fn = P(ast::AssocItem {
396-
attrs: thin_vec![d_attr, inline_never],
397-
id: ast::DUMMY_NODE_ID,
398-
span,
399-
vis,
400-
kind: assoc_item,
401-
tokens: None,
402-
});
403-
Annotatable::AssocItem(d_fn, Impl { of_trait: false })
404-
} else {
405-
let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
406-
d_fn.vis = vis;
407-
Annotatable::Item(d_fn)
405+
let d_annotatable = match &item {
406+
Annotatable::AssocItem(_, _) => {
407+
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
408+
let d_fn = P(ast::AssocItem {
409+
attrs: thin_vec![d_attr, inline_never],
410+
id: ast::DUMMY_NODE_ID,
411+
span,
412+
vis,
413+
kind: assoc_item,
414+
tokens: None,
415+
});
416+
Annotatable::AssocItem(d_fn, Impl { of_trait: false })
417+
}
418+
Annotatable::Item(_) => {
419+
let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
420+
d_fn.vis = vis;
421+
422+
Annotatable::Item(d_fn)
423+
}
424+
Annotatable::Stmt(_) => {
425+
let mut d_fn = ecx.item(span, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
426+
d_fn.vis = vis;
427+
428+
Annotatable::Stmt(P(ast::Stmt {
429+
id: ast::DUMMY_NODE_ID,
430+
kind: ast::StmtKind::Item(d_fn),
431+
span,
432+
}))
433+
}
434+
_ => {
435+
unreachable!("item kind checked previously")
436+
}
408437
};
409438

410439
return vec![orig_annotatable, d_annotatable];

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+8-153
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ use rustc_abi::Align;
55
use rustc_codegen_ssa::traits::{
66
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
77
};
8-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
9-
use rustc_hir::def_id::{DefId, LocalDefId};
8+
use rustc_data_structures::fx::FxIndexMap;
109
use rustc_index::IndexVec;
11-
use rustc_middle::mir;
12-
use rustc_middle::mir::mono::MonoItemPartitions;
13-
use rustc_middle::ty::{self, TyCtxt};
10+
use rustc_middle::ty::TyCtxt;
1411
use rustc_session::RemapFileNameExt;
1512
use rustc_session::config::RemapPathScopeComponents;
16-
use rustc_span::def_id::DefIdSet;
1713
use rustc_span::{SourceFile, StableSourceFileId};
1814
use tracing::debug;
1915

@@ -24,6 +20,7 @@ use crate::llvm;
2420

2521
mod covfun;
2622
mod spans;
23+
mod unused;
2724

2825
/// Generates and exports the coverage map, which is embedded in special
2926
/// linker sections in the final binary.
@@ -76,12 +73,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
7673
// In a single designated CGU, also prepare covfun records for functions
7774
// in this crate that were instrumented for coverage, but are unused.
7875
if cx.codegen_unit.is_code_coverage_dead_code_cgu() {
79-
let mut unused_instances = gather_unused_function_instances(cx);
80-
// Sort the unused instances by symbol name, for the same reason as the used ones.
81-
unused_instances.sort_by_cached_key(|&instance| tcx.symbol_name(instance).name);
82-
covfun_records.extend(unused_instances.into_iter().filter_map(|instance| {
83-
prepare_covfun_record(tcx, &mut global_file_table, instance, false)
84-
}));
76+
unused::prepare_covfun_records_for_unused_functions(
77+
cx,
78+
&mut global_file_table,
79+
&mut covfun_records,
80+
);
8581
}
8682

8783
// If there are no covfun records for this CGU, don't generate a covmap record.
@@ -100,33 +96,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
10096
// contain multiple covmap records from different compilation units.
10197
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
10298

103-
let mut unused_function_names = vec![];
104-
10599
for covfun in &covfun_records {
106-
unused_function_names.extend(covfun.mangled_function_name_if_unused());
107-
108100
covfun::generate_covfun_record(cx, filenames_hash, covfun)
109101
}
110102

111-
// For unused functions, we need to take their mangled names and store them
112-
// in a specially-named global array. LLVM's `InstrProfiling` pass will
113-
// detect this global and include those names in its `__llvm_prf_names`
114-
// section. (See `llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp`.)
115-
if !unused_function_names.is_empty() {
116-
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
117-
118-
let name_globals = unused_function_names
119-
.into_iter()
120-
.map(|mangled_function_name| cx.const_str(mangled_function_name).0)
121-
.collect::<Vec<_>>();
122-
let initializer = cx.const_array(cx.type_ptr(), &name_globals);
123-
124-
let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names");
125-
llvm::set_global_constant(array, true);
126-
llvm::set_linkage(array, llvm::Linkage::InternalLinkage);
127-
llvm::set_initializer(array, initializer);
128-
}
129-
130103
// Generate the coverage map header, which contains the filenames used by
131104
// this CGU's coverage mappings, and store it in a well-known global.
132105
// (This is skipped if we returned early due to having no covfun records.)
@@ -249,121 +222,3 @@ fn generate_covmap_record<'ll>(cx: &CodegenCx<'ll, '_>, version: u32, filenames_
249222

250223
cx.add_used_global(covmap_global);
251224
}
252-
253-
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
254-
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
255-
/// functions that were instrumented but are not participating in codegen.
256-
///
257-
/// These unused functions don't need to be codegenned, but we do need to add them to the function
258-
/// coverage map (in a single designated CGU) so that we still emit coverage mappings for them.
259-
/// We also end up adding their symbol names to a special global array that LLVM will include in
260-
/// its embedded coverage data.
261-
fn gather_unused_function_instances<'tcx>(cx: &CodegenCx<'_, 'tcx>) -> Vec<ty::Instance<'tcx>> {
262-
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
263-
264-
let tcx = cx.tcx;
265-
let usage = prepare_usage_sets(tcx);
266-
267-
let is_unused_fn = |def_id: LocalDefId| -> bool {
268-
// Usage sets expect `DefId`, so convert from `LocalDefId`.
269-
let d: DefId = LocalDefId::to_def_id(def_id);
270-
// To be potentially eligible for "unused function" mappings, a definition must:
271-
// - Be eligible for coverage instrumentation
272-
// - Not participate directly in codegen (or have lost all its coverage statements)
273-
// - Not have any coverage statements inlined into codegenned functions
274-
tcx.is_eligible_for_coverage(def_id)
275-
&& (!usage.all_mono_items.contains(&d) || usage.missing_own_coverage.contains(&d))
276-
&& !usage.used_via_inlining.contains(&d)
277-
};
278-
279-
// FIXME(#79651): Consider trying to filter out dummy instantiations of
280-
// unused generic functions from library crates, because they can produce
281-
// "unused instantiation" in coverage reports even when they are actually
282-
// used by some downstream crate in the same binary.
283-
284-
tcx.mir_keys(())
285-
.iter()
286-
.copied()
287-
.filter(|&def_id| is_unused_fn(def_id))
288-
.map(|def_id| make_dummy_instance(tcx, def_id))
289-
.collect::<Vec<_>>()
290-
}
291-
292-
struct UsageSets<'tcx> {
293-
all_mono_items: &'tcx DefIdSet,
294-
used_via_inlining: FxHashSet<DefId>,
295-
missing_own_coverage: FxHashSet<DefId>,
296-
}
297-
298-
/// Prepare sets of definitions that are relevant to deciding whether something
299-
/// is an "unused function" for coverage purposes.
300-
fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> {
301-
let MonoItemPartitions { all_mono_items, codegen_units, .. } =
302-
tcx.collect_and_partition_mono_items(());
303-
304-
// Obtain a MIR body for each function participating in codegen, via an
305-
// arbitrary instance.
306-
let mut def_ids_seen = FxHashSet::default();
307-
let def_and_mir_for_all_mono_fns = codegen_units
308-
.iter()
309-
.flat_map(|cgu| cgu.items().keys())
310-
.filter_map(|item| match item {
311-
mir::mono::MonoItem::Fn(instance) => Some(instance),
312-
mir::mono::MonoItem::Static(_) | mir::mono::MonoItem::GlobalAsm(_) => None,
313-
})
314-
// We only need one arbitrary instance per definition.
315-
.filter(move |instance| def_ids_seen.insert(instance.def_id()))
316-
.map(|instance| {
317-
// We don't care about the instance, just its underlying MIR.
318-
let body = tcx.instance_mir(instance.def);
319-
(instance.def_id(), body)
320-
});
321-
322-
// Functions whose coverage statements were found inlined into other functions.
323-
let mut used_via_inlining = FxHashSet::default();
324-
// Functions that were instrumented, but had all of their coverage statements
325-
// removed by later MIR transforms (e.g. UnreachablePropagation).
326-
let mut missing_own_coverage = FxHashSet::default();
327-
328-
for (def_id, body) in def_and_mir_for_all_mono_fns {
329-
let mut saw_own_coverage = false;
330-
331-
// Inspect every coverage statement in the function's MIR.
332-
for stmt in body
333-
.basic_blocks
334-
.iter()
335-
.flat_map(|block| &block.statements)
336-
.filter(|stmt| matches!(stmt.kind, mir::StatementKind::Coverage(_)))
337-
{
338-
if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) {
339-
// This coverage statement was inlined from another function.
340-
used_via_inlining.insert(inlined.def_id());
341-
} else {
342-
// Non-inlined coverage statements belong to the enclosing function.
343-
saw_own_coverage = true;
344-
}
345-
}
346-
347-
if !saw_own_coverage && body.function_coverage_info.is_some() {
348-
missing_own_coverage.insert(def_id);
349-
}
350-
}
351-
352-
UsageSets { all_mono_items, used_via_inlining, missing_own_coverage }
353-
}
354-
355-
fn make_dummy_instance<'tcx>(tcx: TyCtxt<'tcx>, local_def_id: LocalDefId) -> ty::Instance<'tcx> {
356-
let def_id = local_def_id.to_def_id();
357-
358-
// Make a dummy instance that fills in all generics with placeholders.
359-
ty::Instance::new(
360-
def_id,
361-
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
362-
if let ty::GenericParamDefKind::Lifetime = param.kind {
363-
tcx.lifetimes.re_erased.into()
364-
} else {
365-
tcx.mk_param_from_def(param)
366-
}
367-
}),
368-
)
369-
}

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

-8
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,6 @@ pub(crate) struct CovfunRecord<'tcx> {
3737
regions: ffi::Regions,
3838
}
3939

40-
impl<'tcx> CovfunRecord<'tcx> {
41-
/// FIXME(Zalathar): Make this the responsibility of the code that determines
42-
/// which functions are unused.
43-
pub(crate) fn mangled_function_name_if_unused(&self) -> Option<&'tcx str> {
44-
(!self.is_used).then_some(self.mangled_function_name)
45-
}
46-
}
47-
4840
pub(crate) fn prepare_covfun_record<'tcx>(
4941
tcx: TyCtxt<'tcx>,
5042
global_file_table: &mut GlobalFileTable,

0 commit comments

Comments
 (0)