mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 22:53:28 +00:00
polymorphize: polymorphize shims
This commit removes the restriction of `InstanceDef::Item` on polymorphization, so that shims can now be polymorphized. Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
parent
4528b8e581
commit
76b05531ca
@ -35,7 +35,8 @@ where
|
|||||||
ty::Closure(def_id, substs)
|
ty::Closure(def_id, substs)
|
||||||
| ty::Generator(def_id, substs, ..)
|
| ty::Generator(def_id, substs, ..)
|
||||||
| ty::FnDef(def_id, substs) => {
|
| ty::FnDef(def_id, substs) => {
|
||||||
let unused_params = self.tcx.unused_generic_params(def_id);
|
let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id));
|
||||||
|
let unused_params = self.tcx.unused_generic_params(instance);
|
||||||
for (index, subst) in substs.into_iter().enumerate() {
|
for (index, subst) in substs.into_iter().enumerate() {
|
||||||
let index = index
|
let index = index
|
||||||
.try_into()
|
.try_into()
|
||||||
|
@ -84,6 +84,12 @@ impl IntoArgs for (CrateNum, DefId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoArgs for ty::InstanceDef<'tcx> {
|
||||||
|
fn into_args(self) -> (DefId, DefId) {
|
||||||
|
(self.def_id(), self.def_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
provide! { <'tcx> tcx, def_id, other, cdata,
|
provide! { <'tcx> tcx, def_id, other, cdata,
|
||||||
type_of => { cdata.get_type(def_id.index, tcx) }
|
type_of => { cdata.get_type(def_id.index, tcx) }
|
||||||
generics_of => { cdata.get_generics(def_id.index, tcx.sess) }
|
generics_of => { cdata.get_generics(def_id.index, tcx.sess) }
|
||||||
|
@ -1320,7 +1320,9 @@ impl EncodeContext<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
|
record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id));
|
||||||
|
|
||||||
let unused = self.tcx.unused_generic_params(def_id);
|
let instance =
|
||||||
|
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
|
||||||
|
let unused = self.tcx.unused_generic_params(instance);
|
||||||
if !unused.is_empty() {
|
if !unused.is_empty() {
|
||||||
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
|
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
|
||||||
}
|
}
|
||||||
|
@ -1551,11 +1551,11 @@ rustc_queries! {
|
|||||||
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
|
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
|
||||||
desc { "codegen_unit" }
|
desc { "codegen_unit" }
|
||||||
}
|
}
|
||||||
query unused_generic_params(key: DefId) -> FiniteBitSet<u32> {
|
query unused_generic_params(key: ty::InstanceDef<'tcx>) -> FiniteBitSet<u32> {
|
||||||
cache_on_disk_if { key.is_local() }
|
cache_on_disk_if { key.def_id().is_local() }
|
||||||
desc {
|
desc {
|
||||||
|tcx| "determining which generic parameters are unused by `{}`",
|
|tcx| "determining which generic parameters are unused by `{}`",
|
||||||
tcx.def_path_str(key)
|
tcx.def_path_str(key.def_id())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
query backend_optimization_level(_: ()) -> OptLevel {
|
query backend_optimization_level(_: ()) -> OptLevel {
|
||||||
|
@ -152,6 +152,22 @@ impl<'tcx> InstanceDef<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the `DefId` of instances which might not require codegen locally.
|
||||||
|
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
|
||||||
|
match self {
|
||||||
|
ty::InstanceDef::Item(def) => Some(def.did),
|
||||||
|
ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id),
|
||||||
|
InstanceDef::VtableShim(..)
|
||||||
|
| InstanceDef::ReifyShim(..)
|
||||||
|
| InstanceDef::FnPtrShim(..)
|
||||||
|
| InstanceDef::Virtual(..)
|
||||||
|
| InstanceDef::Intrinsic(..)
|
||||||
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
|
| InstanceDef::DropGlue(..)
|
||||||
|
| InstanceDef::CloneShim(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
|
pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
|
||||||
match self {
|
match self {
|
||||||
@ -567,29 +583,26 @@ impl<'tcx> Instance<'tcx> {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let InstanceDef::Item(def) = self.def {
|
let polymorphized_substs = polymorphize(tcx, self.def, self.substs);
|
||||||
let polymorphized_substs = polymorphize(tcx, def.did, self.substs);
|
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
|
||||||
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
|
Self { def: self.def, substs: polymorphized_substs }
|
||||||
Self { def: self.def, substs: polymorphized_substs }
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn polymorphize<'tcx>(
|
fn polymorphize<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
def_id: DefId,
|
instance: ty::InstanceDef<'tcx>,
|
||||||
substs: SubstsRef<'tcx>,
|
substs: SubstsRef<'tcx>,
|
||||||
) -> SubstsRef<'tcx> {
|
) -> SubstsRef<'tcx> {
|
||||||
debug!("polymorphize({:?}, {:?})", def_id, substs);
|
debug!("polymorphize({:?}, {:?})", instance, substs);
|
||||||
let unused = tcx.unused_generic_params(def_id);
|
let unused = tcx.unused_generic_params(instance);
|
||||||
debug!("polymorphize: unused={:?}", unused);
|
debug!("polymorphize: unused={:?}", unused);
|
||||||
|
|
||||||
// If this is a closure or generator then we need to handle the case where another closure
|
// If this is a closure or generator then we need to handle the case where another closure
|
||||||
// from the function is captured as an upvar and hasn't been polymorphized. In this case,
|
// from the function is captured as an upvar and hasn't been polymorphized. In this case,
|
||||||
// the unpolymorphized upvar closure would result in a polymorphized closure producing
|
// the unpolymorphized upvar closure would result in a polymorphized closure producing
|
||||||
// multiple mono items (and eventually symbol clashes).
|
// multiple mono items (and eventually symbol clashes).
|
||||||
|
let def_id = instance.def_id();
|
||||||
let upvars_ty = if tcx.is_closure(def_id) {
|
let upvars_ty = if tcx.is_closure(def_id) {
|
||||||
Some(substs.as_closure().tupled_upvars_ty())
|
Some(substs.as_closure().tupled_upvars_ty())
|
||||||
} else if tcx.type_of(def_id).is_generator() {
|
} else if tcx.type_of(def_id).is_generator() {
|
||||||
@ -613,7 +626,11 @@ fn polymorphize<'tcx>(
|
|||||||
debug!("fold_ty: ty={:?}", ty);
|
debug!("fold_ty: ty={:?}", ty);
|
||||||
match ty.kind {
|
match ty.kind {
|
||||||
ty::Closure(def_id, substs) => {
|
ty::Closure(def_id, substs) => {
|
||||||
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
|
let polymorphized_substs = polymorphize(
|
||||||
|
self.tcx,
|
||||||
|
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)),
|
||||||
|
substs,
|
||||||
|
);
|
||||||
if substs == polymorphized_substs {
|
if substs == polymorphized_substs {
|
||||||
ty
|
ty
|
||||||
} else {
|
} else {
|
||||||
@ -621,7 +638,11 @@ fn polymorphize<'tcx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::Generator(def_id, substs, movability) => {
|
ty::Generator(def_id, substs, movability) => {
|
||||||
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
|
let polymorphized_substs = polymorphize(
|
||||||
|
self.tcx,
|
||||||
|
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)),
|
||||||
|
substs,
|
||||||
|
);
|
||||||
if substs == polymorphized_substs {
|
if substs == polymorphized_substs {
|
||||||
ty
|
ty
|
||||||
} else {
|
} else {
|
||||||
|
@ -936,21 +936,13 @@ fn visit_instance_use<'tcx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns `true` if we should codegen an instance in the local crate.
|
/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
|
||||||
// Returns `false` if we can just link to the upstream crate and therefore don't
|
/// can just link to the upstream crate and therefore don't need a mono item.
|
||||||
// need a mono item.
|
|
||||||
fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool {
|
fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool {
|
||||||
let def_id = match instance.def {
|
let def_id = if let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() {
|
||||||
ty::InstanceDef::Item(def) => def.did,
|
def_id
|
||||||
ty::InstanceDef::DropGlue(def_id, Some(_)) => def_id,
|
} else {
|
||||||
ty::InstanceDef::VtableShim(..)
|
return true;
|
||||||
| ty::InstanceDef::ReifyShim(..)
|
|
||||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
|
||||||
| ty::InstanceDef::Virtual(..)
|
|
||||||
| ty::InstanceDef::FnPtrShim(..)
|
|
||||||
| ty::InstanceDef::DropGlue(..)
|
|
||||||
| ty::InstanceDef::Intrinsic(_)
|
|
||||||
| ty::InstanceDef::CloneShim(..) => return true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if tcx.is_foreign_item(def_id) {
|
if tcx.is_foreign_item(def_id) {
|
||||||
|
@ -27,20 +27,23 @@ pub fn provide(providers: &mut Providers) {
|
|||||||
providers.unused_generic_params = unused_generic_params;
|
providers.unused_generic_params = unused_generic_params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which generic parameters are used by the function/method/closure represented by
|
/// Determine which generic parameters are used by the instance.
|
||||||
/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty`
|
///
|
||||||
/// indicates all parameters are used).
|
/// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all
|
||||||
|
/// parameters are used).
|
||||||
#[instrument(level = "debug", skip(tcx))]
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
|
fn unused_generic_params<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
instance: ty::InstanceDef<'tcx>,
|
||||||
|
) -> FiniteBitSet<u32> {
|
||||||
if !tcx.sess.opts.debugging_opts.polymorphize {
|
if !tcx.sess.opts.debugging_opts.polymorphize {
|
||||||
// If polymorphization disabled, then all parameters are used.
|
// If polymorphization disabled, then all parameters are used.
|
||||||
return FiniteBitSet::new_empty();
|
return FiniteBitSet::new_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polymorphization results are stored in cross-crate metadata only when there are unused
|
let def_id = instance.def_id();
|
||||||
// parameters, so assume that non-local items must have only used parameters (else this query
|
// Exit early if this instance should not be polymorphized.
|
||||||
// would not be invoked, and the cross-crate metadata used instead).
|
if !should_polymorphize(tcx, def_id, instance) {
|
||||||
if !def_id.is_local() {
|
|
||||||
return FiniteBitSet::new_empty();
|
return FiniteBitSet::new_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,38 +55,20 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
|
|||||||
return FiniteBitSet::new_empty();
|
return FiniteBitSet::new_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit early for foreign items, these have no bodies to analyze.
|
|
||||||
if tcx.is_foreign_item(def_id) {
|
|
||||||
return FiniteBitSet::new_empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit early when there is no MIR available.
|
|
||||||
let context = tcx.hir().body_const_context(def_id.expect_local());
|
|
||||||
match context {
|
|
||||||
Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => {
|
|
||||||
debug!("no mir available");
|
|
||||||
return FiniteBitSet::new_empty();
|
|
||||||
}
|
|
||||||
Some(_) if !tcx.is_ctfe_mir_available(def_id) => {
|
|
||||||
debug!("no ctfe mir available");
|
|
||||||
return FiniteBitSet::new_empty();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a bitset with N rightmost ones for each parameter.
|
// Create a bitset with N rightmost ones for each parameter.
|
||||||
let generics_count: u32 =
|
let generics_count: u32 =
|
||||||
generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
|
generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
|
||||||
let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
|
let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
|
||||||
unused_parameters.set_range(0..generics_count);
|
unused_parameters.set_range(0..generics_count);
|
||||||
debug!(?unused_parameters, "(start)");
|
debug!(?unused_parameters, "(start)");
|
||||||
|
|
||||||
mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
|
mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
|
||||||
debug!(?unused_parameters, "(after default)");
|
debug!(?unused_parameters, "(after default)");
|
||||||
|
|
||||||
// Visit MIR and accumululate used generic parameters.
|
// Visit MIR and accumululate used generic parameters.
|
||||||
let body = match context {
|
let body = match tcx.hir().body_const_context(def_id.expect_local()) {
|
||||||
// Const functions are actually called and should thus be considered for polymorphization
|
// Const functions are actually called and should thus be considered for polymorphization
|
||||||
// via their runtime MIR
|
// via their runtime MIR.
|
||||||
Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id),
|
Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id),
|
||||||
Some(_) => tcx.mir_for_ctfe(def_id),
|
Some(_) => tcx.mir_for_ctfe(def_id),
|
||||||
};
|
};
|
||||||
@ -99,6 +84,49 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
|
|||||||
unused_parameters
|
unused_parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the instance should be polymorphized.
|
||||||
|
fn should_polymorphize<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
def_id: DefId,
|
||||||
|
instance: ty::InstanceDef<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
// If an instance's MIR body is not polymorphic then the modified substitutions that are
|
||||||
|
// derived from polymorphization's result won't make any difference.
|
||||||
|
if !instance.has_polymorphic_mir_body() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic.
|
||||||
|
if matches!(instance, ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::Virtual(..)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Polymorphization results are stored in cross-crate metadata only when there are unused
|
||||||
|
// parameters, so assume that non-local items must have only used parameters (else this query
|
||||||
|
// would not be invoked, and the cross-crate metadata used instead).
|
||||||
|
if !def_id.is_local() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreign items have no bodies to analyze.
|
||||||
|
if tcx.is_foreign_item(def_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there is MIR available.
|
||||||
|
match tcx.hir().body_const_context(def_id.expect_local()) {
|
||||||
|
Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => {
|
||||||
|
debug!("no mir available");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Some(_) if !tcx.is_ctfe_mir_available(def_id) => {
|
||||||
|
debug!("no ctfe mir available");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy
|
/// Some parameters are considered used-by-default, such as non-generic parameters and the dummy
|
||||||
/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should
|
/// generic parameters from closures, this function marks them as used. `leaf_is_closure` should
|
||||||
/// be `true` if the item that `unused_generic_params` was invoked on is a closure.
|
/// be `true` if the item that `unused_generic_params` was invoked on is a closure.
|
||||||
@ -207,7 +235,8 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
|
|||||||
/// a closure, generator or constant).
|
/// a closure, generator or constant).
|
||||||
#[instrument(level = "debug", skip(self, def_id, substs))]
|
#[instrument(level = "debug", skip(self, def_id, substs))]
|
||||||
fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
|
fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
|
||||||
let unused = self.tcx.unused_generic_params(def_id);
|
let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id));
|
||||||
|
let unused = self.tcx.unused_generic_params(instance);
|
||||||
debug!(?self.unused_parameters, ?unused);
|
debug!(?self.unused_parameters, ?unused);
|
||||||
for (i, arg) in substs.iter().enumerate() {
|
for (i, arg) in substs.iter().enumerate() {
|
||||||
let i = i.try_into().unwrap();
|
let i = i.try_into().unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user