Skip to content

Polymorphization #69749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
mir: unused_generic_params query
This commit implements the `unused_generic_params` query, an initial
version of polymorphization which detects when an item does not use
generic parameters and is being needlessly monomorphized as a result.

Signed-off-by: David Wood <[email protected]>
  • Loading branch information
davidtwco committed Jul 20, 2020
commit 2989fea88a489a01b3e2243bb84b0ec20b8a0e28
10 changes: 7 additions & 3 deletions src/librustc_codegen_llvm/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use log::debug;
use rustc_codegen_ssa::traits::*;

use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
use rustc_middle::ty::{Instance, TypeFoldable};
use rustc_middle::ty::{self, Instance, TypeFoldable};

/// Codegens a reference to a fn/method item, monomorphizing and
/// inlining as it goes.
Expand All @@ -29,14 +29,18 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value

assert!(!instance.substs.needs_infer());
assert!(!instance.substs.has_escaping_bound_vars());
assert!(!instance.substs.has_param_types_or_consts());

if let Some(&llfn) = cx.instances.borrow().get(&instance) {
return llfn;
}

let sym = tcx.symbol_name(instance).name;
debug!("get_fn({:?}: {:?}) => {}", instance, instance.monomorphic_ty(cx.tcx()), sym);
debug!(
"get_fn({:?}: {:?}) => {}",
instance,
instance.ty(cx.tcx(), ty::ParamEnv::reveal_all()),
sym
);

let fn_abi = FnAbi::of_instance(cx, instance, &[]);

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_llvm/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl CodegenCx<'ll, 'tcx> {
def_id
);

let ty = instance.monomorphic_ty(self.tcx);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let sym = self.tcx.symbol_name(instance).name;

debug!("get_static: sym={} instance={:?}", sym, instance);
Expand Down Expand Up @@ -361,7 +361,7 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> {
};

let instance = Instance::mono(self.tcx, def_id);
let ty = instance.monomorphic_ty(self.tcx);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let llty = self.layout_of(ty).llvm_type(self);
let g = if val_llty == llty {
g
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_llvm/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2481,7 +2481,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global
};

let is_local_to_unit = is_node_local_to_unit(cx, def_id);
let variable_type = Instance::mono(cx.tcx, def_id).monomorphic_ty(cx.tcx);
let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all());
let type_metadata = type_metadata(cx, variable_type, span);
let var_name = tcx.item_name(def_id).as_str();
let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_llvm/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
caller_instance: ty::Instance<'tcx>,
) {
let tcx = self.tcx;
let callee_ty = instance.monomorphic_ty(tcx);
let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());

let (def_id, substs) = match callee_ty.kind {
ty::FnDef(def_id, substs) => (def_id, substs),
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_codegen_llvm/mono_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
pub use rustc_middle::mir::mono::MonoItem;
use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::layout::FnAbiExt;
use rustc_middle::ty::{Instance, TypeFoldable};
use rustc_middle::ty::{self, Instance, TypeFoldable};
use rustc_target::abi::LayoutOf;

impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
Expand All @@ -22,7 +22,7 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
symbol_name: &str,
) {
let instance = Instance::mono(self.tcx, def_id);
let ty = instance.monomorphic_ty(self.tcx);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let llty = self.layout_of(ty).llvm_type(self);

let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {
Expand All @@ -47,7 +47,7 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
visibility: Visibility,
symbol_name: &str,
) {
assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types_or_consts());
assert!(!instance.substs.needs_infer());

let fn_abi = FnAbi::of_instance(self, instance, &[]);
let lldecl = self.declare_fn(symbol_name, &fn_abi);
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_codegen_ssa/meth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
def_id,
substs,
)
.unwrap(),
.unwrap()
.polymorphize(cx.tcx()),
)
})
});
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(
ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs)
.unwrap()
.unwrap(),
.unwrap()
.polymorphize(bx.tcx()),
),
None,
),
Expand Down
21 changes: 10 additions & 11 deletions src/librustc_codegen_ssa/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) {
bug!("reifying a fn ptr that requires const arguments");
}
OperandValue::Immediate(
bx.get_fn_addr(
ty::Instance::resolve_for_fn_ptr(
bx.tcx(),
ty::ParamEnv::reveal_all(),
def_id,
substs,
)
.unwrap(),
),
let instance = ty::Instance::resolve_for_fn_ptr(
bx.tcx(),
ty::ParamEnv::reveal_all(),
def_id,
substs,
)
.unwrap()
.polymorphize(bx.cx().tcx());
OperandValue::Immediate(bx.get_fn_addr(instance))
}
_ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty),
}
Expand All @@ -213,7 +211,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
def_id,
substs,
ty::ClosureKind::FnOnce,
);
)
.polymorphize(bx.cx().tcx());
OperandValue::Immediate(bx.cx().get_fn_addr(instance))
}
_ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty),
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_middle/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,13 @@ rustc_queries! {
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
desc { "codegen_unit" }
}
query unused_generic_params(key: DefId) -> u64 {
cache_on_disk_if { key.is_local() }
desc {
|tcx| "determining which generic parameters are unused by `{}`",
tcx.def_path_str(key)
}
}
query backend_optimization_level(_: CrateNum) -> OptLevel {
desc { "optimization level used by backend" }
}
Expand Down
66 changes: 40 additions & 26 deletions src/librustc_middle/ty/instance.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::subst::InternalSubsts;
use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable};
use rustc_errors::ErrorReported;
use rustc_hir::def::Namespace;
Expand Down Expand Up @@ -106,32 +107,9 @@ pub enum InstanceDef<'tcx> {
}

impl<'tcx> Instance<'tcx> {
/// Returns the `Ty` corresponding to this `Instance`,
/// with generic substitutions applied and lifetimes erased.
///
/// This method can only be called when the 'substs' for this Instance
/// are fully monomorphic (no `ty::Param`'s are present).
/// This is usually the case (e.g. during codegen).
/// However, during constant evaluation, we may want
/// to try to resolve a `Instance` using generic parameters
/// (e.g. when we are attempting to to do const-propagation).
/// In this case, `Instance.ty_env` should be used to provide
/// the `ParamEnv` for our generic context.
pub fn monomorphic_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
let ty = tcx.type_of(self.def.def_id());
// There shouldn't be any params - if there are, then
// Instance.ty_env should have been used to provide the proper
// ParamEnv
if self.substs.has_param_types_or_consts() {
bug!("Instance.ty called for type {:?} with params in substs: {:?}", ty, self.substs);
}
tcx.subst_and_normalize_erasing_regions(self.substs, ty::ParamEnv::reveal_all(), &ty)
}

/// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during
/// normalization. This method is only really useful during constant evaluation,
/// where we are dealing with potentially generic types.
pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
/// Returns the `Ty` corresponding to this `Instance`, with generic substitutions applied and
/// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization.
pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
let ty = tcx.type_of(self.def.def_id());
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty)
}
Expand Down Expand Up @@ -486,6 +464,42 @@ impl<'tcx> Instance<'tcx> {
| InstanceDef::VtableShim(..) => Some(self.substs),
}
}

/// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
/// identify parameters if they are determined to be unused in `instance.def`.
pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
debug!("polymorphize: running polymorphization analysis");
if !tcx.sess.opts.debugging_opts.polymorphize {
return self;
}

if let InstanceDef::Item(def) = self.def {
let results = tcx.unused_generic_params(def.did);

if results == 0 {
// Exit early if every parameter was used.
return self;
}

debug!("polymorphize: results={:064b}", results);
let polymorphized_substs =
InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind {
// If parameter is a const or type parameter..
ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
// ..and is within range and unused..
param.index < 64 && ((results >> param.index) & 1) == 1 =>
// ..then use the identity for this parameter.
tcx.mk_param_from_def(param),
// Otherwise, use the parameter as before.
_ => self.substs[param.index as usize],
});

debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
Self { def: self.def, substs: polymorphized_substs }
} else {
self
}
}
}

fn needs_fn_once_adapter_shim(
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_middle/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2299,7 +2299,8 @@ impl<'tcx> ty::Instance<'tcx> {
// or should go through `FnAbi` instead, to avoid losing any
// adjustments `FnAbi::of_instance` might be performing.
fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
let ty = self.monomorphic_ty(tcx);
// FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function.
let ty = self.ty(tcx, ty::ParamEnv::reveal_all());
match ty.kind {
ty::FnDef(..) |
// Shims currently have type FnPtr. Not sure this should remain.
Expand Down
1 change: 0 additions & 1 deletion src/librustc_middle/ty/normalize_erasing_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ impl<'tcx> TyCtxt<'tcx> {
where
T: TypeFoldable<'tcx>,
{
assert!(!value.needs_subst());
let value = self.erase_late_bound_regions(value);
self.normalize_erasing_regions(param_env, value)
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_middle/ty/print/obsolete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,14 @@ impl DefPathBasedNames<'tcx> {
let substs = substs.truncate_to(self.tcx, generics);
self.push_generic_params(substs, iter::empty(), output, debug);
}
ty::Param(_) => {
output.push_str(&t.to_string());
}
ty::Error(_)
| ty::Bound(..)
| ty::Infer(_)
| ty::Placeholder(..)
| ty::Projection(..)
| ty::Param(_)
| ty::GeneratorWitness(_)
| ty::Opaque(..) => {
if debug {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ pub fn const_eval_validated_provider<'tcx>(
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
let ty = key.value.instance.ty_env(tcx, key.param_env);
let ty = key.value.instance.ty(tcx, key.param_env);
let substs = match ty.kind {
ty::FnDef(_, substs) => substs,
_ => bug!("intrinsic with type {:?}", ty),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// ABI check
{
let callee_abi = {
let instance_ty = instance.ty_env(*self.tcx, self.param_env);
let instance_ty = instance.ty(*self.tcx, self.param_env);
match instance_ty.kind {
ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(),
ty::Closure(..) => Abi::RustCall,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// to determine the type.
let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?;
trace!("Found drop fn: {:?}", drop_instance);
let fn_sig = drop_instance.ty_env(*self.tcx, self.param_env).fn_sig(*self.tcx);
let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
// The drop function takes `*mut T` where `T` is the type being dropped, so get that.
let args = fn_sig.inputs();
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub fn provide(providers: &mut Providers) {
shim::provide(providers);
transform::provide(providers);
monomorphize::partitioning::provide(providers);
monomorphize::polymorphize::provide(providers);
providers.const_eval_validated = const_eval::const_eval_validated_provider;
providers.const_eval_raw = const_eval::const_eval_raw_provider;
providers.const_caller_location = const_eval::const_caller_location;
Expand Down
Loading