Skip to content

Commit 22409f0

Browse files
committed
instance: polymorphize shims
This commit removes the restriction of `InstanceDef::Item` on polymorphization, so that shims can now be polymorphized. In addition, `unused_generic_params` now takes a `ty::InstanceDef` so that `instance_mir` can be invoked and actual shim MIR can be fetched (rather than whatever you get from the `instance.def_id()`'s MIR). Signed-off-by: David Wood <[email protected]>
1 parent fd2df74 commit 22409f0

File tree

7 files changed

+120
-60
lines changed

7 files changed

+120
-60
lines changed

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ impl IntoArgs for (CrateNum, DefId) {
8686
}
8787
}
8888

89+
impl IntoArgs for ty::InstanceDef<'tcx> {
90+
fn into_args(self) -> (DefId, DefId) {
91+
(self.def_id(), self.def_id())
92+
}
93+
}
94+
8995
provide! { <'tcx> tcx, def_id, other, cdata,
9096
type_of => { cdata.get_type(def_id.index, tcx) }
9197
generics_of => { cdata.get_generics(def_id.index, tcx.sess) }

compiler/rustc_metadata/src/rmeta/encoder.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,8 @@ impl EncodeContext<'a, 'tcx> {
11381138
debug!("EntryBuilder::encode_mir_for_ctfe({:?})", def_id);
11391139
record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id));
11401140

1141-
let unused = self.tcx.unused_generic_params(def_id);
1141+
let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
1142+
let unused = self.tcx.unused_generic_params(instance);
11421143
if !unused.is_empty() {
11431144
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
11441145
}
@@ -1153,7 +1154,8 @@ impl EncodeContext<'a, 'tcx> {
11531154
debug!("EntryBuilder::encode_optimized_mir({:?})", def_id);
11541155
record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
11551156

1156-
let unused = self.tcx.unused_generic_params(def_id);
1157+
let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
1158+
let unused = self.tcx.unused_generic_params(instance);
11571159
if !unused.is_empty() {
11581160
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
11591161
}

compiler/rustc_middle/src/query/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1469,11 +1469,11 @@ rustc_queries! {
14691469
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
14701470
desc { "codegen_unit" }
14711471
}
1472-
query unused_generic_params(key: DefId) -> FiniteBitSet<u32> {
1473-
cache_on_disk_if { key.is_local() }
1472+
query unused_generic_params(key: ty::InstanceDef<'tcx>) -> FiniteBitSet<u32> {
1473+
cache_on_disk_if { key.def_id().is_local() }
14741474
desc {
14751475
|tcx| "determining which generic parameters are unused by `{}`",
1476-
tcx.def_path_str(key)
1476+
tcx.def_path_str(key.def_id())
14771477
}
14781478
}
14791479
query backend_optimization_level(_: CrateNum) -> OptLevel {

compiler/rustc_middle/src/ty/instance.rs

+35-12
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ impl<'tcx> InstanceDef<'tcx> {
152152
}
153153
}
154154

155+
/// Returns the `DefId` of instances which might not require codegen locally.
156+
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
157+
match self {
158+
ty::InstanceDef::Item(def) => Some(def.did),
159+
ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id),
160+
ty::InstanceDef::VtableShim(..)
161+
| ty::InstanceDef::ReifyShim(..)
162+
| ty::InstanceDef::ClosureOnceShim { .. }
163+
| ty::InstanceDef::Virtual(..)
164+
| ty::InstanceDef::FnPtrShim(..)
165+
| ty::InstanceDef::DropGlue(..)
166+
| ty::InstanceDef::Intrinsic(_)
167+
| ty::InstanceDef::CloneShim(..) => None,
168+
}
169+
}
170+
155171
#[inline]
156172
pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
157173
match self {
@@ -505,29 +521,26 @@ impl<'tcx> Instance<'tcx> {
505521
return self;
506522
}
507523

508-
if let InstanceDef::Item(def) = self.def {
509-
let polymorphized_substs = polymorphize(tcx, def.did, self.substs);
510-
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
511-
Self { def: self.def, substs: polymorphized_substs }
512-
} else {
513-
self
514-
}
524+
let polymorphized_substs = polymorphize(tcx, self.def, self.substs);
525+
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
526+
Self { def: self.def, substs: polymorphized_substs }
515527
}
516528
}
517529

518530
fn polymorphize<'tcx>(
519531
tcx: TyCtxt<'tcx>,
520-
def_id: DefId,
532+
instance: ty::InstanceDef<'tcx>,
521533
substs: SubstsRef<'tcx>,
522534
) -> SubstsRef<'tcx> {
523-
debug!("polymorphize({:?}, {:?})", def_id, substs);
524-
let unused = tcx.unused_generic_params(def_id);
535+
debug!("polymorphize({:?}, {:?})", instance, substs);
536+
let unused = tcx.unused_generic_params(instance);
525537
debug!("polymorphize: unused={:?}", unused);
526538

527539
// If this is a closure or generator then we need to handle the case where another closure
528540
// from the function is captured as an upvar and hasn't been polymorphized. In this case,
529541
// the unpolymorphized upvar closure would result in a polymorphized closure producing
530542
// multiple mono items (and eventually symbol clashes).
543+
let def_id = instance.def_id();
531544
let upvars_ty = if tcx.is_closure(def_id) {
532545
Some(substs.as_closure().tupled_upvars_ty())
533546
} else if tcx.type_of(def_id).is_generator() {
@@ -551,15 +564,25 @@ fn polymorphize<'tcx>(
551564
debug!("fold_ty: ty={:?}", ty);
552565
match ty.kind {
553566
ty::Closure(def_id, substs) => {
554-
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
567+
let polymorphized_substs = polymorphize(
568+
self.tcx,
569+
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)),
570+
substs,
571+
);
572+
555573
if substs == polymorphized_substs {
556574
ty
557575
} else {
558576
self.tcx.mk_closure(def_id, polymorphized_substs)
559577
}
560578
}
561579
ty::Generator(def_id, substs, movability) => {
562-
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
580+
let polymorphized_substs = polymorphize(
581+
self.tcx,
582+
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)),
583+
substs,
584+
);
585+
563586
if substs == polymorphized_substs {
564587
ty
565588
} else {

compiler/rustc_mir/src/interpret/util.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ where
4242
ty::Closure(def_id, substs)
4343
| ty::Generator(def_id, substs, ..)
4444
| ty::FnDef(def_id, substs) => {
45-
let unused_params = self.tcx.unused_generic_params(def_id);
45+
let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id));
46+
let unused_params = self.tcx.unused_generic_params(instance);
4647
for (index, subst) in substs.into_iter().enumerate() {
4748
let index = index
4849
.try_into()

compiler/rustc_mir/src/monomorphize/collector.rs

+6-14
Original file line numberDiff line numberDiff line change
@@ -788,21 +788,13 @@ fn visit_instance_use<'tcx>(
788788
}
789789
}
790790

791-
// Returns `true` if we should codegen an instance in the local crate.
792-
// Returns `false` if we can just link to the upstream crate and therefore don't
793-
// need a mono item.
791+
/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
792+
/// can just link to the upstream crate and therefore don't need a mono item.
794793
fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool {
795-
let def_id = match instance.def {
796-
ty::InstanceDef::Item(def) => def.did,
797-
ty::InstanceDef::DropGlue(def_id, Some(_)) => def_id,
798-
ty::InstanceDef::VtableShim(..)
799-
| ty::InstanceDef::ReifyShim(..)
800-
| ty::InstanceDef::ClosureOnceShim { .. }
801-
| ty::InstanceDef::Virtual(..)
802-
| ty::InstanceDef::FnPtrShim(..)
803-
| ty::InstanceDef::DropGlue(..)
804-
| ty::InstanceDef::Intrinsic(_)
805-
| ty::InstanceDef::CloneShim(..) => return true,
794+
let def_id = if let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() {
795+
def_id
796+
} else {
797+
return true;
806798
};
807799

808800
if tcx.is_foreign_item(def_id) {

compiler/rustc_mir/src/monomorphize/polymorphize.rs

+64-28
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_middle::ty::{
1616
fold::{TypeFoldable, TypeVisitor},
1717
query::Providers,
1818
subst::SubstsRef,
19-
Const, Ty, TyCtxt,
19+
Const, InstanceDef, Ty, TyCtxt,
2020
};
2121
use rustc_span::symbol::sym;
2222
use std::convert::TryInto;
@@ -27,21 +27,25 @@ pub fn provide(providers: &mut Providers) {
2727
providers.unused_generic_params = unused_generic_params;
2828
}
2929

30-
/// Determine which generic parameters are used by the function/method/closure represented by
31-
/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty`
30+
/// Determine which generic parameters are used by the `instance`.
31+
///
32+
/// Returns a bitset where bits representing unused parameters are set (`is_empty`
3233
/// indicates all parameters are used).
33-
fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
34-
debug!("unused_generic_params({:?})", def_id);
34+
fn unused_generic_params<'tcx>(
35+
tcx: TyCtxt<'tcx>,
36+
instance: InstanceDef<'tcx>,
37+
) -> FiniteBitSet<u32> {
38+
debug!("unused_generic_params({:?})", instance);
3539

40+
// If polymorphization disabled, then all parameters are used.
3641
if !tcx.sess.opts.debugging_opts.polymorphize {
37-
// If polymorphization disabled, then all parameters are used.
3842
return FiniteBitSet::new_empty();
3943
}
4044

41-
// Polymorphization results are stored in cross-crate metadata only when there are unused
42-
// parameters, so assume that non-local items must have only used parameters (else this query
43-
// would not be invoked, and the cross-crate metadata used instead).
44-
if !def_id.is_local() {
45+
// Exit early if this instance should not be polymorphized.
46+
let def_id = instance.def_id();
47+
if !should_polymorphize(tcx, def_id, instance) {
48+
debug!("unused_generic_params: skipping");
4549
return FiniteBitSet::new_empty();
4650
}
4751

@@ -53,36 +57,24 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
5357
return FiniteBitSet::new_empty();
5458
}
5559

56-
// Exit early when there is no MIR available.
57-
let context = tcx.hir().body_const_context(def_id.expect_local());
58-
match context {
59-
Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => {
60-
debug!("unused_generic_params: (no mir available) def_id={:?}", def_id);
61-
return FiniteBitSet::new_empty();
62-
}
63-
Some(_) if !tcx.is_ctfe_mir_available(def_id) => {
64-
debug!("unused_generic_params: (no ctfe mir available) def_id={:?}", def_id);
65-
return FiniteBitSet::new_empty();
66-
}
67-
_ => {}
68-
}
69-
7060
// Create a bitset with N rightmost ones for each parameter.
7161
let generics_count: u32 =
7262
generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
7363
let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
7464
unused_parameters.set_range(0..generics_count);
7565
debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters);
66+
7667
mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
7768
debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters);
7869

79-
// Visit MIR and accumululate used generic parameters.
80-
let body = match context {
70+
let body = match tcx.hir().body_const_context(def_id.expect_local()) {
8171
// Const functions are actually called and should thus be considered for polymorphization
82-
// via their runtime MIR
72+
// via their runtime MIR.
8373
Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id),
8474
Some(_) => tcx.mir_for_ctfe(def_id),
8575
};
76+
77+
// Visit MIR and accumululate used generic parameters.
8678
let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters };
8779
vis.visit_body(body);
8880
debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters);
@@ -98,6 +90,48 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
9890
unused_parameters
9991
}
10092

93+
/// Returns `true` if the `InstanceDef` should be polymorphized.
94+
fn should_polymorphize<'tcx>(
95+
tcx: TyCtxt<'tcx>,
96+
def_id: DefId,
97+
instance: ty::InstanceDef<'tcx>,
98+
) -> bool {
99+
// If a instance's MIR body is not polymorphic then the modified substitutions that are derived
100+
// from polymorphization's result won't make any difference.
101+
if !instance.has_polymorphic_mir_body() {
102+
return false;
103+
}
104+
105+
// Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic.
106+
if matches!(instance, ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::Virtual(..)) {
107+
return false;
108+
}
109+
110+
// Polymorphization results are stored in cross-crate metadata only when there are unused
111+
// parameters, so assume that non-local items must have only used parameters (else this query
112+
// would not be invoked, and the cross-crate metadata used instead).
113+
if !def_id.is_local() {
114+
return false;
115+
}
116+
117+
// Foreign items don't have a body to analyze.
118+
if tcx.is_foreign_item(def_id) {
119+
return false;
120+
}
121+
122+
// Without available MIR, polymorphization has nothing to analyze.
123+
match tcx.hir().body_const_context(def_id.expect_local()) {
124+
// FIXME(davidtwco): Disable polymorphization for any constant functions which, at the time
125+
// of writing, can result in an ICE from typeck in one test and a cycle error in another.
126+
Some(_) => false,
127+
None if !tcx.is_mir_available(def_id) => {
128+
debug!("should_polymorphize: (no mir available) def_id={:?}", def_id);
129+
false
130+
}
131+
None => true,
132+
}
133+
}
134+
101135
/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy
102136
/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should
103137
/// be `true` if the item that `unused_generic_params` was invoked on is a closure.
@@ -220,7 +254,9 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
220254
/// Invoke `unused_generic_params` on a body contained within the current item (e.g.
221255
/// a closure, generator or constant).
222256
fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
223-
let unused = self.tcx.unused_generic_params(def_id);
257+
let unused = self
258+
.tcx
259+
.unused_generic_params(ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)));
224260
debug!(
225261
"visit_child_body: unused_parameters={:?} unused={:?}",
226262
self.unused_parameters, unused

0 commit comments

Comments
 (0)