Skip to content

Commit 2989fea

Browse files
committed
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]>
1 parent 47756bb commit 2989fea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1627
-84
lines changed

src/librustc_codegen_llvm/callee.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use log::debug;
1313
use rustc_codegen_ssa::traits::*;
1414

1515
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
16-
use rustc_middle::ty::{Instance, TypeFoldable};
16+
use rustc_middle::ty::{self, Instance, TypeFoldable};
1717

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

3030
assert!(!instance.substs.needs_infer());
3131
assert!(!instance.substs.has_escaping_bound_vars());
32-
assert!(!instance.substs.has_param_types_or_consts());
3332

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

3837
let sym = tcx.symbol_name(instance).name;
39-
debug!("get_fn({:?}: {:?}) => {}", instance, instance.monomorphic_ty(cx.tcx()), sym);
38+
debug!(
39+
"get_fn({:?}: {:?}) => {}",
40+
instance,
41+
instance.ty(cx.tcx(), ty::ParamEnv::reveal_all()),
42+
sym
43+
);
4044

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

src/librustc_codegen_llvm/consts.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl CodegenCx<'ll, 'tcx> {
203203
def_id
204204
);
205205

206-
let ty = instance.monomorphic_ty(self.tcx);
206+
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
207207
let sym = self.tcx.symbol_name(instance).name;
208208

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

363363
let instance = Instance::mono(self.tcx, def_id);
364-
let ty = instance.monomorphic_ty(self.tcx);
364+
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
365365
let llty = self.layout_of(ty).llvm_type(self);
366366
let g = if val_llty == llty {
367367
g

src/librustc_codegen_llvm/debuginfo/metadata.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2481,7 +2481,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global
24812481
};
24822482

24832483
let is_local_to_unit = is_node_local_to_unit(cx, def_id);
2484-
let variable_type = Instance::mono(cx.tcx, def_id).monomorphic_ty(cx.tcx);
2484+
let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all());
24852485
let type_metadata = type_metadata(cx, variable_type, span);
24862486
let var_name = tcx.item_name(def_id).as_str();
24872487
let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name;

src/librustc_codegen_llvm/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
160160
caller_instance: ty::Instance<'tcx>,
161161
) {
162162
let tcx = self.tcx;
163-
let callee_ty = instance.monomorphic_ty(tcx);
163+
let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
164164

165165
let (def_id, substs) = match callee_ty.kind {
166166
ty::FnDef(def_id, substs) => (def_id, substs),

src/librustc_codegen_llvm/mono_item.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
1010
pub use rustc_middle::mir::mono::MonoItem;
1111
use rustc_middle::mir::mono::{Linkage, Visibility};
1212
use rustc_middle::ty::layout::FnAbiExt;
13-
use rustc_middle::ty::{Instance, TypeFoldable};
13+
use rustc_middle::ty::{self, Instance, TypeFoldable};
1414
use rustc_target::abi::LayoutOf;
1515

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

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

5252
let fn_abi = FnAbi::of_instance(self, instance, &[]);
5353
let lldecl = self.declare_fn(symbol_name, &fn_abi);

src/librustc_codegen_ssa/meth.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
9494
def_id,
9595
substs,
9696
)
97-
.unwrap(),
97+
.unwrap()
98+
.polymorphize(cx.tcx()),
9899
)
99100
})
100101
});

src/librustc_codegen_ssa/mir/block.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
543543
Some(
544544
ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs)
545545
.unwrap()
546-
.unwrap(),
546+
.unwrap()
547+
.polymorphize(bx.tcx()),
547548
),
548549
None,
549550
),

src/librustc_codegen_ssa/mir/rvalue.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -190,17 +190,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
190190
if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) {
191191
bug!("reifying a fn ptr that requires const arguments");
192192
}
193-
OperandValue::Immediate(
194-
bx.get_fn_addr(
195-
ty::Instance::resolve_for_fn_ptr(
196-
bx.tcx(),
197-
ty::ParamEnv::reveal_all(),
198-
def_id,
199-
substs,
200-
)
201-
.unwrap(),
202-
),
193+
let instance = ty::Instance::resolve_for_fn_ptr(
194+
bx.tcx(),
195+
ty::ParamEnv::reveal_all(),
196+
def_id,
197+
substs,
203198
)
199+
.unwrap()
200+
.polymorphize(bx.cx().tcx());
201+
OperandValue::Immediate(bx.get_fn_addr(instance))
204202
}
205203
_ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty),
206204
}
@@ -213,7 +211,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
213211
def_id,
214212
substs,
215213
ty::ClosureKind::FnOnce,
216-
);
214+
)
215+
.polymorphize(bx.cx().tcx());
217216
OperandValue::Immediate(bx.cx().get_fn_addr(instance))
218217
}
219218
_ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty),

src/librustc_middle/query/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,13 @@ rustc_queries! {
13091309
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
13101310
desc { "codegen_unit" }
13111311
}
1312+
query unused_generic_params(key: DefId) -> u64 {
1313+
cache_on_disk_if { key.is_local() }
1314+
desc {
1315+
|tcx| "determining which generic parameters are unused by `{}`",
1316+
tcx.def_path_str(key)
1317+
}
1318+
}
13121319
query backend_optimization_level(_: CrateNum) -> OptLevel {
13131320
desc { "optimization level used by backend" }
13141321
}

src/librustc_middle/ty/instance.rs

+40-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
22
use crate::ty::print::{FmtPrinter, Printer};
3+
use crate::ty::subst::InternalSubsts;
34
use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable};
45
use rustc_errors::ErrorReported;
56
use rustc_hir::def::Namespace;
@@ -106,32 +107,9 @@ pub enum InstanceDef<'tcx> {
106107
}
107108

108109
impl<'tcx> Instance<'tcx> {
109-
/// Returns the `Ty` corresponding to this `Instance`,
110-
/// with generic substitutions applied and lifetimes erased.
111-
///
112-
/// This method can only be called when the 'substs' for this Instance
113-
/// are fully monomorphic (no `ty::Param`'s are present).
114-
/// This is usually the case (e.g. during codegen).
115-
/// However, during constant evaluation, we may want
116-
/// to try to resolve a `Instance` using generic parameters
117-
/// (e.g. when we are attempting to to do const-propagation).
118-
/// In this case, `Instance.ty_env` should be used to provide
119-
/// the `ParamEnv` for our generic context.
120-
pub fn monomorphic_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
121-
let ty = tcx.type_of(self.def.def_id());
122-
// There shouldn't be any params - if there are, then
123-
// Instance.ty_env should have been used to provide the proper
124-
// ParamEnv
125-
if self.substs.has_param_types_or_consts() {
126-
bug!("Instance.ty called for type {:?} with params in substs: {:?}", ty, self.substs);
127-
}
128-
tcx.subst_and_normalize_erasing_regions(self.substs, ty::ParamEnv::reveal_all(), &ty)
129-
}
130-
131-
/// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during
132-
/// normalization. This method is only really useful during constant evaluation,
133-
/// where we are dealing with potentially generic types.
134-
pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
110+
/// Returns the `Ty` corresponding to this `Instance`, with generic substitutions applied and
111+
/// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization.
112+
pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
135113
let ty = tcx.type_of(self.def.def_id());
136114
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty)
137115
}
@@ -486,6 +464,42 @@ impl<'tcx> Instance<'tcx> {
486464
| InstanceDef::VtableShim(..) => Some(self.substs),
487465
}
488466
}
467+
468+
/// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
469+
/// identify parameters if they are determined to be unused in `instance.def`.
470+
pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
471+
debug!("polymorphize: running polymorphization analysis");
472+
if !tcx.sess.opts.debugging_opts.polymorphize {
473+
return self;
474+
}
475+
476+
if let InstanceDef::Item(def) = self.def {
477+
let results = tcx.unused_generic_params(def.did);
478+
479+
if results == 0 {
480+
// Exit early if every parameter was used.
481+
return self;
482+
}
483+
484+
debug!("polymorphize: results={:064b}", results);
485+
let polymorphized_substs =
486+
InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind {
487+
// If parameter is a const or type parameter..
488+
ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
489+
// ..and is within range and unused..
490+
param.index < 64 && ((results >> param.index) & 1) == 1 =>
491+
// ..then use the identity for this parameter.
492+
tcx.mk_param_from_def(param),
493+
// Otherwise, use the parameter as before.
494+
_ => self.substs[param.index as usize],
495+
});
496+
497+
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
498+
Self { def: self.def, substs: polymorphized_substs }
499+
} else {
500+
self
501+
}
502+
}
489503
}
490504

491505
fn needs_fn_once_adapter_shim(

src/librustc_middle/ty/layout.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2299,7 +2299,8 @@ impl<'tcx> ty::Instance<'tcx> {
22992299
// or should go through `FnAbi` instead, to avoid losing any
23002300
// adjustments `FnAbi::of_instance` might be performing.
23012301
fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> {
2302-
let ty = self.monomorphic_ty(tcx);
2302+
// FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function.
2303+
let ty = self.ty(tcx, ty::ParamEnv::reveal_all());
23032304
match ty.kind {
23042305
ty::FnDef(..) |
23052306
// Shims currently have type FnPtr. Not sure this should remain.

src/librustc_middle/ty/normalize_erasing_regions.rs

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ impl<'tcx> TyCtxt<'tcx> {
5454
where
5555
T: TypeFoldable<'tcx>,
5656
{
57-
assert!(!value.needs_subst());
5857
let value = self.erase_late_bound_regions(value);
5958
self.normalize_erasing_regions(param_env, value)
6059
}

src/librustc_middle/ty/print/obsolete.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,14 @@ impl DefPathBasedNames<'tcx> {
144144
let substs = substs.truncate_to(self.tcx, generics);
145145
self.push_generic_params(substs, iter::empty(), output, debug);
146146
}
147+
ty::Param(_) => {
148+
output.push_str(&t.to_string());
149+
}
147150
ty::Error(_)
148151
| ty::Bound(..)
149152
| ty::Infer(_)
150153
| ty::Placeholder(..)
151154
| ty::Projection(..)
152-
| ty::Param(_)
153155
| ty::GeneratorWitness(_)
154156
| ty::Opaque(..) => {
155157
if debug {

src/librustc_mir/const_eval/eval_queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub fn const_eval_validated_provider<'tcx>(
240240
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
241241
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
242242
if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
243-
let ty = key.value.instance.ty_env(tcx, key.param_env);
243+
let ty = key.value.instance.ty(tcx, key.param_env);
244244
let substs = match ty.kind {
245245
ty::FnDef(_, substs) => substs,
246246
_ => bug!("intrinsic with type {:?}", ty),

src/librustc_mir/interpret/terminator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
221221
// ABI check
222222
{
223223
let callee_abi = {
224-
let instance_ty = instance.ty_env(*self.tcx, self.param_env);
224+
let instance_ty = instance.ty(*self.tcx, self.param_env);
225225
match instance_ty.kind {
226226
ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(),
227227
ty::Closure(..) => Abi::RustCall,

src/librustc_mir/interpret/traits.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
142142
// to determine the type.
143143
let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?;
144144
trace!("Found drop fn: {:?}", drop_instance);
145-
let fn_sig = drop_instance.ty_env(*self.tcx, self.param_env).fn_sig(*self.tcx);
145+
let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
146146
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
147147
// The drop function takes `*mut T` where `T` is the type being dropped, so get that.
148148
let args = fn_sig.inputs();

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub fn provide(providers: &mut Providers) {
5151
shim::provide(providers);
5252
transform::provide(providers);
5353
monomorphize::partitioning::provide(providers);
54+
monomorphize::polymorphize::provide(providers);
5455
providers.const_eval_validated = const_eval::const_eval_validated_provider;
5556
providers.const_eval_raw = const_eval::const_eval_raw_provider;
5657
providers.const_caller_location = const_eval::const_caller_location;

0 commit comments

Comments
 (0)