Skip to content

Commit 2e6fc42

Browse files
committed
Auto merge of rust-lang#128002 - matthiaskrgr:rollup-21p0cue, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - rust-lang#127463 ( use precompiled rustdoc with CI rustc) - rust-lang#127779 (Add a hook for `should_codegen_locally`) - rust-lang#127843 (unix: document unsafety for std `sig{action,altstack}`) - rust-lang#127873 (kmc-solid: `#![forbid(unsafe_op_in_unsafe_fn)]`) - rust-lang#127917 (match lowering: Split `finalize_or_candidate` into more coherent methods) - rust-lang#127964 (run_make_support: skip rustfmt for lib.rs) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 73a2281 + 8963855 commit 2e6fc42

File tree

23 files changed

+370
-222
lines changed

23 files changed

+370
-222
lines changed

compiler/rustc_codegen_cranelift/src/abi/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ use std::mem;
1010
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
1111
use cranelift_codegen::isa::CallConv;
1212
use cranelift_module::ModuleError;
13+
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
1314
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
1415
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1516
use rustc_middle::ty::layout::FnAbiOf;
1617
use rustc_middle::ty::print::with_no_trimmed_paths;
1718
use rustc_middle::ty::TypeVisitableExt;
18-
use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization;
1919
use rustc_session::Session;
2020
use rustc_span::source_map::Spanned;
2121
use rustc_target::abi::call::{Conv, FnAbi, PassMode};

compiler/rustc_codegen_cranelift/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ use cranelift_codegen::CodegenError;
55
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
66
use cranelift_module::ModuleError;
77
use rustc_ast::InlineAsmOptions;
8+
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
89
use rustc_index::IndexVec;
910
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1011
use rustc_middle::ty::adjustment::PointerCoercion;
1112
use rustc_middle::ty::layout::FnAbiOf;
1213
use rustc_middle::ty::print::with_no_trimmed_paths;
1314
use rustc_middle::ty::TypeVisitableExt;
14-
use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization;
1515

1616
use crate::constant::ConstantCx;
1717
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};

compiler/rustc_codegen_cranelift/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ extern crate rustc_hir;
2424
extern crate rustc_incremental;
2525
extern crate rustc_index;
2626
extern crate rustc_metadata;
27-
extern crate rustc_monomorphize;
2827
extern crate rustc_session;
2928
extern crate rustc_span;
3029
extern crate rustc_target;

compiler/rustc_codegen_ssa/src/base.rs

+28
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,34 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
806806
ongoing_codegen
807807
}
808808

809+
/// Returns whether a call from the current crate to the [`Instance`] would produce a call
810+
/// from `compiler_builtins` to a symbol the linker must resolve.
811+
///
812+
/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some
813+
/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is
814+
/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any
815+
/// unlinkable calls.
816+
///
817+
/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker.
818+
pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>(
819+
tcx: TyCtxt<'tcx>,
820+
instance: Instance<'tcx>,
821+
) -> bool {
822+
fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
823+
if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name {
824+
name.as_str().starts_with("llvm.")
825+
} else {
826+
false
827+
}
828+
}
829+
830+
let def_id = instance.def_id();
831+
!def_id.is_local()
832+
&& tcx.is_compiler_builtins(LOCAL_CRATE)
833+
&& !is_llvm_intrinsic(tcx, def_id)
834+
&& !tcx.should_codegen_locally(instance)
835+
}
836+
809837
impl CrateInfo {
810838
pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
811839
let crate_types = tcx.crate_types().to_vec();

compiler/rustc_codegen_ssa/src/mir/block.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized};
33
use super::place::{PlaceRef, PlaceValue};
44
use super::{CachedLlbb, FunctionCx, LocalRef};
55

6-
use crate::base;
6+
use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization};
77
use crate::common::{self, IntPredicate};
88
use crate::errors::CompilerBuiltinsCannotCall;
99
use crate::meth;
@@ -18,7 +18,6 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
1818
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
1919
use rustc_middle::ty::{self, Instance, Ty};
2020
use rustc_middle::{bug, span_bug};
21-
use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization;
2221
use rustc_session::config::OptLevel;
2322
use rustc_span::{source_map::Spanned, sym, Span};
2423
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};

compiler/rustc_middle/src/hooks/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ declare_hooks! {
103103

104104
/// Create a list-like THIR representation for debugging.
105105
hook thir_flat(key: LocalDefId) -> String;
106+
107+
/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
108+
/// can just link to the upstream crate and therefore don't need a mono item.
109+
hook should_codegen_locally(instance: crate::ty::Instance<'tcx>) -> bool;
106110
}
107111

108112
#[cold]

compiler/rustc_mir_build/src/build/matches/mod.rs

+129-91
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15981598
for subcandidate in candidate.subcandidates.iter_mut() {
15991599
expanded_candidates.push(subcandidate);
16001600
}
1601+
// Note that the subcandidates have been added to `expanded_candidates`,
1602+
// but `candidate` itself has not. If the last candidate has more match pairs,
1603+
// they are handled separately by `test_remaining_match_pairs_after_or`.
16011604
} else {
16021605
// A candidate that doesn't start with an or-pattern has nothing to
16031606
// expand, so it is included in the post-expansion list as-is.
@@ -1613,19 +1616,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16131616
expanded_candidates.as_mut_slice(),
16141617
);
16151618

1616-
// Simplify subcandidates and process any leftover match pairs.
1617-
for candidate in candidates_to_expand {
1619+
// Postprocess subcandidates, and process any leftover match pairs.
1620+
// (Only the last candidate can possibly have more match pairs.)
1621+
debug_assert!({
1622+
let mut all_except_last = candidates_to_expand.iter().rev().skip(1);
1623+
all_except_last.all(|candidate| candidate.match_pairs.is_empty())
1624+
});
1625+
for candidate in candidates_to_expand.iter_mut() {
16181626
if !candidate.subcandidates.is_empty() {
1619-
self.finalize_or_candidate(span, scrutinee_span, candidate);
1627+
self.merge_trivial_subcandidates(candidate);
1628+
self.remove_never_subcandidates(candidate);
16201629
}
16211630
}
1631+
if let Some(last_candidate) = candidates_to_expand.last_mut() {
1632+
self.test_remaining_match_pairs_after_or(span, scrutinee_span, last_candidate);
1633+
}
16221634

16231635
remainder_start.and(remaining_candidates)
16241636
}
16251637

16261638
/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
1627-
/// subcandidate. Any candidate that has been expanded that way should be passed to
1628-
/// `finalize_or_candidate` after its subcandidates have been processed.
1639+
/// subcandidate. Any candidate that has been expanded this way should also be postprocessed
1640+
/// at the end of [`Self::expand_and_match_or_candidates`].
16291641
fn create_or_subcandidates<'pat>(
16301642
&mut self,
16311643
candidate: &mut Candidate<'pat, 'tcx>,
@@ -1642,7 +1654,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16421654
candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block;
16431655
}
16441656

1645-
/// Simplify subcandidates and process any leftover match pairs. The candidate should have been
1657+
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
1658+
/// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been
16461659
/// expanded with `create_or_subcandidates`.
16471660
///
16481661
/// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
@@ -1695,103 +1708,128 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16951708
/// |
16961709
/// ...
16971710
/// ```
1698-
fn finalize_or_candidate(
1699-
&mut self,
1700-
span: Span,
1701-
scrutinee_span: Span,
1702-
candidate: &mut Candidate<'_, 'tcx>,
1703-
) {
1704-
if candidate.subcandidates.is_empty() {
1711+
///
1712+
/// Note that this takes place _after_ the subcandidates have participated
1713+
/// in match tree lowering.
1714+
fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
1715+
assert!(!candidate.subcandidates.is_empty());
1716+
if candidate.has_guard {
1717+
// FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
17051718
return;
17061719
}
17071720

1708-
self.merge_trivial_subcandidates(candidate);
1721+
// FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
1722+
let can_merge = candidate.subcandidates.iter().all(|subcandidate| {
1723+
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
1724+
});
1725+
if !can_merge {
1726+
return;
1727+
}
17091728

1710-
if !candidate.match_pairs.is_empty() {
1711-
let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span);
1712-
let source_info = self.source_info(or_span);
1713-
// If more match pairs remain, test them after each subcandidate.
1714-
// We could add them to the or-candidates before the call to `test_or_pattern` but this
1715-
// would make it impossible to detect simplifiable or-patterns. That would guarantee
1716-
// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1717-
let mut last_otherwise = None;
1718-
candidate.visit_leaves(|leaf_candidate| {
1719-
last_otherwise = leaf_candidate.otherwise_block;
1720-
});
1721-
let remaining_match_pairs = mem::take(&mut candidate.match_pairs);
1722-
candidate.visit_leaves(|leaf_candidate| {
1723-
assert!(leaf_candidate.match_pairs.is_empty());
1724-
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
1725-
let or_start = leaf_candidate.pre_binding_block.unwrap();
1726-
let otherwise =
1727-
self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]);
1728-
// In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1729-
// R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1730-
// directly to `last_otherwise`. If there is a guard,
1731-
// `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
1732-
// can't skip `Q`.
1733-
let or_otherwise = if leaf_candidate.has_guard {
1734-
leaf_candidate.otherwise_block.unwrap()
1735-
} else {
1736-
last_otherwise.unwrap()
1737-
};
1738-
self.cfg.goto(otherwise, source_info, or_otherwise);
1739-
});
1729+
let mut last_otherwise = None;
1730+
let shared_pre_binding_block = self.cfg.start_new_block();
1731+
// This candidate is about to become a leaf, so unset `or_span`.
1732+
let or_span = candidate.or_span.take().unwrap();
1733+
let source_info = self.source_info(or_span);
1734+
1735+
if candidate.false_edge_start_block.is_none() {
1736+
candidate.false_edge_start_block = candidate.subcandidates[0].false_edge_start_block;
1737+
}
1738+
1739+
// Remove the (known-trivial) subcandidates from the candidate tree,
1740+
// so that they aren't visible after match tree lowering, and wire them
1741+
// all to join up at a single shared pre-binding block.
1742+
// (Note that the subcandidates have already had their part of the match
1743+
// tree lowered by this point, which is why we can add a goto to them.)
1744+
for subcandidate in mem::take(&mut candidate.subcandidates) {
1745+
let subcandidate_block = subcandidate.pre_binding_block.unwrap();
1746+
self.cfg.goto(subcandidate_block, source_info, shared_pre_binding_block);
1747+
last_otherwise = subcandidate.otherwise_block;
17401748
}
1749+
candidate.pre_binding_block = Some(shared_pre_binding_block);
1750+
assert!(last_otherwise.is_some());
1751+
candidate.otherwise_block = last_otherwise;
17411752
}
17421753

1743-
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
1744-
/// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been
1745-
/// expanded with `create_or_subcandidates`.
1746-
fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
1747-
if candidate.subcandidates.is_empty() || candidate.has_guard {
1748-
// FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
1754+
/// Never subcandidates may have a set of bindings inconsistent with their siblings,
1755+
/// which would break later code. So we filter them out. Note that we can't filter out
1756+
/// top-level candidates this way.
1757+
fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
1758+
if candidate.subcandidates.is_empty() {
17491759
return;
17501760
}
17511761

1752-
// FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
1753-
let can_merge = candidate.subcandidates.iter().all(|subcandidate| {
1754-
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
1755-
});
1756-
if can_merge {
1757-
let mut last_otherwise = None;
1758-
let any_matches = self.cfg.start_new_block();
1759-
let or_span = candidate.or_span.take().unwrap();
1760-
let source_info = self.source_info(or_span);
1761-
if candidate.false_edge_start_block.is_none() {
1762-
candidate.false_edge_start_block =
1763-
candidate.subcandidates[0].false_edge_start_block;
1764-
}
1765-
for subcandidate in mem::take(&mut candidate.subcandidates) {
1766-
let or_block = subcandidate.pre_binding_block.unwrap();
1767-
self.cfg.goto(or_block, source_info, any_matches);
1768-
last_otherwise = subcandidate.otherwise_block;
1769-
}
1770-
candidate.pre_binding_block = Some(any_matches);
1771-
assert!(last_otherwise.is_some());
1772-
candidate.otherwise_block = last_otherwise;
1773-
} else {
1774-
// Never subcandidates may have a set of bindings inconsistent with their siblings,
1775-
// which would break later code. So we filter them out. Note that we can't filter out
1776-
// top-level candidates this way.
1777-
candidate.subcandidates.retain_mut(|candidate| {
1778-
if candidate.extra_data.is_never {
1779-
candidate.visit_leaves(|subcandidate| {
1780-
let block = subcandidate.pre_binding_block.unwrap();
1781-
// That block is already unreachable but needs a terminator to make the MIR well-formed.
1782-
let source_info = self.source_info(subcandidate.extra_data.span);
1783-
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
1784-
});
1785-
false
1786-
} else {
1787-
true
1788-
}
1789-
});
1790-
if candidate.subcandidates.is_empty() {
1791-
// If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`.
1792-
candidate.pre_binding_block = Some(self.cfg.start_new_block());
1762+
candidate.subcandidates.retain_mut(|candidate| {
1763+
if candidate.extra_data.is_never {
1764+
candidate.visit_leaves(|subcandidate| {
1765+
let block = subcandidate.pre_binding_block.unwrap();
1766+
// That block is already unreachable but needs a terminator to make the MIR well-formed.
1767+
let source_info = self.source_info(subcandidate.extra_data.span);
1768+
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
1769+
});
1770+
false
1771+
} else {
1772+
true
17931773
}
1774+
});
1775+
if candidate.subcandidates.is_empty() {
1776+
// If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`.
1777+
candidate.pre_binding_block = Some(self.cfg.start_new_block());
1778+
}
1779+
}
1780+
1781+
/// If more match pairs remain, test them after each subcandidate.
1782+
/// We could have added them to the or-candidates during or-pattern expansion, but that
1783+
/// would make it impossible to detect simplifiable or-patterns. That would guarantee
1784+
/// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1785+
fn test_remaining_match_pairs_after_or(
1786+
&mut self,
1787+
span: Span,
1788+
scrutinee_span: Span,
1789+
candidate: &mut Candidate<'_, 'tcx>,
1790+
) {
1791+
if candidate.match_pairs.is_empty() {
1792+
return;
17941793
}
1794+
1795+
let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span);
1796+
let source_info = self.source_info(or_span);
1797+
let mut last_otherwise = None;
1798+
candidate.visit_leaves(|leaf_candidate| {
1799+
last_otherwise = leaf_candidate.otherwise_block;
1800+
});
1801+
1802+
let remaining_match_pairs = mem::take(&mut candidate.match_pairs);
1803+
// We're testing match pairs that remained after an `Or`, so the remaining
1804+
// pairs should all be `Or` too, due to the sorting invariant.
1805+
debug_assert!(
1806+
remaining_match_pairs
1807+
.iter()
1808+
.all(|match_pair| matches!(match_pair.test_case, TestCase::Or { .. }))
1809+
);
1810+
1811+
candidate.visit_leaves(|leaf_candidate| {
1812+
// At this point the leaf's own match pairs have all been lowered
1813+
// and removed, so `extend` and assignment are equivalent,
1814+
// but extending can also recycle any existing vector capacity.
1815+
assert!(leaf_candidate.match_pairs.is_empty());
1816+
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
1817+
1818+
let or_start = leaf_candidate.pre_binding_block.unwrap();
1819+
let otherwise =
1820+
self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]);
1821+
// In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1822+
// R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1823+
// directly to `last_otherwise`. If there is a guard,
1824+
// `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
1825+
// can't skip `Q`.
1826+
let or_otherwise = if leaf_candidate.has_guard {
1827+
leaf_candidate.otherwise_block.unwrap()
1828+
} else {
1829+
last_otherwise.unwrap()
1830+
};
1831+
self.cfg.goto(otherwise, source_info, or_otherwise);
1832+
});
17951833
}
17961834

17971835
/// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at

0 commit comments

Comments
 (0)