index: introduce and use FiniteBitSet

This commit introduces a `FiniteBitSet` type which replaces the manual
bit manipulation which was being performed in polymorphization.

Signed-off-by: David Wood <david@davidtw.co>
This commit is contained in:
David Wood 2020-07-17 12:28:23 +01:00
parent b1f8bd6356
commit 4b99699c84
No known key found for this signature in database
GPG Key ID: 2592E76C87381FD9
10 changed files with 211 additions and 73 deletions

View File

@ -469,6 +469,15 @@ impl<R: vec::Idx, C: vec::Idx, CTX> HashStable<CTX> for bit_set::BitMatrix<R, C>
}
}
impl<T, CTX> HashStable<CTX> for bit_set::FiniteBitSet<T>
where
T: HashStable<CTX> + bit_set::FiniteBitSetTy,
{
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
self.0.hash_stable(hcx, hasher);
}
}
impl_stable_hash_via_hash!(::std::path::Path);
impl_stable_hash_via_hash!(::std::path::PathBuf);

View File

@ -4,6 +4,7 @@ use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not, Range, Shl};
use std::slice;
#[cfg(test)]
@ -1001,3 +1002,137 @@ fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) {
let mask = 1 << (elem % WORD_BITS);
(word_index, mask)
}
/// Integral type used to represent the bit set.
pub trait FiniteBitSetTy:
BitAnd<Output = Self>
+ BitAndAssign
+ BitOrAssign
+ Clone
+ Copy
+ Shl
+ Not<Output = Self>
+ PartialEq
+ Sized
{
/// Size of the domain representable by this type, e.g. 64 for `u64`.
const DOMAIN_SIZE: u32;
/// Value which represents the `FiniteBitSet` having every bit set.
const FILLED: Self;
/// Value which represents the `FiniteBitSet` having no bits set.
const EMPTY: Self;
/// Value for one as the integral type.
const ONE: Self;
/// Value for zero as the integral type.
const ZERO: Self;
/// Perform a checked left shift on the integral type.
fn checked_shl(self, rhs: u32) -> Option<Self>;
/// Perform a checked right shift on the integral type.
fn checked_shr(self, rhs: u32) -> Option<Self>;
}
impl FiniteBitSetTy for u64 {
const DOMAIN_SIZE: u32 = 64;
const FILLED: Self = Self::MAX;
const EMPTY: Self = Self::MIN;
const ONE: Self = 1u64;
const ZERO: Self = 0u64;
fn checked_shl(self, rhs: u32) -> Option<Self> {
self.checked_shl(rhs)
}
fn checked_shr(self, rhs: u32) -> Option<Self> {
self.checked_shr(rhs)
}
}
impl std::fmt::Debug for FiniteBitSet<u64> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:064b}", self.0)
}
}
impl FiniteBitSetTy for u128 {
const DOMAIN_SIZE: u32 = 128;
const FILLED: Self = Self::MAX;
const EMPTY: Self = Self::MIN;
const ONE: Self = 1u128;
const ZERO: Self = 0u128;
fn checked_shl(self, rhs: u32) -> Option<Self> {
self.checked_shl(rhs)
}
fn checked_shr(self, rhs: u32) -> Option<Self> {
self.checked_shr(rhs)
}
}
impl std::fmt::Debug for FiniteBitSet<u128> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:0128b}", self.0)
}
}
/// A fixed-sized bitset type represented by an integer type. Indices outwith than the range
/// representable by `T` are considered set.
#[derive(Copy, Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)]
pub struct FiniteBitSet<T: FiniteBitSetTy>(pub T);
impl<T: FiniteBitSetTy> FiniteBitSet<T> {
/// Creates a new, empty bitset.
pub fn new_empty() -> Self {
Self(T::EMPTY)
}
/// Sets the `index`th bit.
pub fn set(&mut self, index: u32) {
self.0 |= T::ONE.checked_shl(index).unwrap_or(T::ZERO);
}
/// Unsets the `index`th bit.
pub fn clear(&mut self, index: u32) {
self.0 &= !T::ONE.checked_shl(index).unwrap_or(T::ZERO);
}
/// Sets the `i`th to `j`th bits.
pub fn set_range(&mut self, range: Range<u32>) {
let bits = T::FILLED
.checked_shl(range.end - range.start)
.unwrap_or(T::ZERO)
.not()
.checked_shl(range.start)
.unwrap_or(T::ZERO);
self.0 |= bits;
}
/// Is the set empty?
pub fn is_empty(&self) -> bool {
self.0 == T::EMPTY
}
/// Returns the domain size of the bitset.
pub fn within_domain(&self, index: u32) -> bool {
index < T::DOMAIN_SIZE
}
/// Returns if the `index`th bit is set.
pub fn contains(&self, index: u32) -> Option<bool> {
self.within_domain(index)
.then(|| ((self.0.checked_shr(index).unwrap_or(T::ONE)) & T::ONE) == T::ONE)
}
}
impl<T: FiniteBitSetTy> Default for FiniteBitSet<T> {
fn default() -> Self {
Self::new_empty()
}
}

View File

@ -1,4 +1,5 @@
#![feature(allow_internal_unstable)]
#![feature(bool_to_option)]
#![feature(const_fn)]
#![feature(const_panic)]
#![feature(extend_one)]

View File

@ -1132,7 +1132,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
.decode((self, tcx))
}
fn get_unused_generic_params(&self, id: DefIndex) -> u64 {
fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet<u64> {
self.root
.tables
.unused_generic_params

View File

@ -9,7 +9,7 @@ use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::{DefId, DefIndex};
use rustc_hir::lang_items;
use rustc_index::vec::IndexVec;
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
use rustc_middle::hir::exports::Export;
use rustc_middle::middle::cstore::{DepKind, ForeignModule, LinkagePreference, NativeLib};
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
@ -277,7 +277,7 @@ define_tables! {
super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
unused_generic_params: Table<DefIndex, Lazy<u64>>,
unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u64>>>,
}
#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]

View File

@ -1309,7 +1309,7 @@ rustc_queries! {
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
desc { "codegen_unit" }
}
query unused_generic_params(key: DefId) -> u64 {
query unused_generic_params(key: DefId) -> FiniteBitSet<u64> {
cache_on_disk_if { key.is_local() }
desc {
|tcx| "determining which generic parameters are unused by `{}`",

View File

@ -474,20 +474,20 @@ impl<'tcx> Instance<'tcx> {
}
if let InstanceDef::Item(def) = self.def {
let results = tcx.unused_generic_params(def.did);
let unused = tcx.unused_generic_params(def.did);
if results == 0 {
if unused.is_empty() {
// Exit early if every parameter was used.
return self;
}
debug!("polymorphize: results={:064b}", results);
debug!("polymorphize: unused={:?}", unused);
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 =>
unused.contains(param.index).unwrap_or(false) =>
// ..then use the identity for this parameter.
tcx.mk_param_from_def(param),
// Otherwise, use the parameter as before.

View File

@ -44,7 +44,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
use rustc_hir::lang_items::{LangItem, LanguageItems};
use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate};
use rustc_index::vec::IndexVec;
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
use rustc_session::utils::NativeLibKind;
use rustc_session::CrateDisambiguator;

View File

@ -6,6 +6,7 @@
//! for their size, offset of a field, etc.).
use rustc_hir::{def::DefKind, def_id::DefId};
use rustc_index::bit_set::FiniteBitSet;
use rustc_middle::mir::{
visit::{TyContext, Visitor},
Local, LocalDecl, Location,
@ -25,14 +26,14 @@ pub fn provide(providers: &mut Providers) {
}
/// Determine which generic parameters are used by the function/method/closure represented by
/// `def_id`. Returns a `u64` where a bit is set if the parameter with that index is unused (ie.
/// a value of zero indicates that all parameters are used).
fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> u64 {
/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty`
/// indicates all parameters are used).
fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u64> {
debug!("unused_generic_params({:?})", def_id);
if !tcx.sess.opts.debugging_opts.polymorphize {
// If polymorphization disabled, then all parameters are used.
return 0;
return FiniteBitSet::new_empty();
}
let generics = tcx.generics_of(def_id);
@ -40,63 +41,42 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> u64 {
// Exit early when there are no parameters to be unused.
if generics.count() == 0 {
return 0;
return FiniteBitSet::new_empty();
}
// Exit early when there is no MIR available.
if !tcx.is_mir_available(def_id) {
debug!("unused_generic_params: (no mir available) def_id={:?}", def_id);
return 0;
return FiniteBitSet::new_empty();
}
// Use a `u64` as a bitset. Starting with all ones, shift left by the number of parameters,
// leaving N zeros for each parameter. When a parameter is marked as used, the bit (from the
// left) corresponding to the parameter index will be flipped. This is the opposite of what
// will be returned.
// Create a bitset with N rightmost ones for each parameter.
let generics_count: u32 =
generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
let mut used_parameters = u64::max_value().checked_shl(generics_count).unwrap_or(0);
debug!("unused_generic_params: (start) used_parameters={:064b}", used_parameters);
mark_used_by_default_parameters(tcx, def_id, generics, &mut used_parameters);
debug!("unused_generic_params: (after default) used_parameters={:064b}", used_parameters);
let mut unused_parameters = FiniteBitSet::<u64>::new_empty();
unused_parameters.set_range(0..generics_count);
debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters);
mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters);
// Visit MIR and accumululate used generic parameters.
let body = tcx.optimized_mir(def_id);
let mut vis =
UsedGenericParametersVisitor { tcx, def_id, used_parameters: &mut used_parameters };
UsedGenericParametersVisitor { tcx, def_id, unused_parameters: &mut unused_parameters };
vis.visit_body(body);
debug!("unused_generic_params: (after visitor) used_parameters={:064b}", used_parameters);
debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters);
mark_used_by_predicates(tcx, def_id, &mut used_parameters);
debug!("unused_generic_params: (after predicates) used_parameters={:064b}", used_parameters);
// Invert the u64 so that used is 0 and unused is 1. This makes checking if all parameters are
// used easy - just compare with zero.
debug!("unused_generic_params: (end) used_parameters={:064b}", used_parameters);
let unused_parameters: u64 = !used_parameters;
debug!("unused_generic_params: (flipped) unused_parameters={:064b}", unused_parameters);
mark_used_by_predicates(tcx, def_id, &mut unused_parameters);
debug!("unused_generic_params: (end) unused_parameters={:?}", unused_parameters);
// Emit errors for debugging and testing if enabled.
let is_full = unused_parameters == 0;
if !is_full {
emit_unused_generic_params_error(tcx, def_id, generics, unused_parameters);
if !unused_parameters.is_empty() {
emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters);
}
unused_parameters
}
/// Checks if the `param_index`th bit is set (or out-of-range).
fn is_bit_set(parameters: u64, param_index: u32) -> bool {
param_index >= 64 || ((parameters.checked_shr(param_index).unwrap_or(1)) & 1) == 1
}
/// Flips the bit corresponding to the parameter index.
fn set_bit(used_parameters: &mut u64, param_index: u32) {
debug!("set_bit: used_parameters={:064b} param_index={:?}", used_parameters, param_index);
*used_parameters |= 1u64.checked_shl(param_index).unwrap_or(0);
debug!("set_bit: used_parameters={:064b}", used_parameters);
}
/// 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
/// be `true` if the item that `unused_generic_params` was invoked on is a closure.
@ -104,43 +84,47 @@ fn mark_used_by_default_parameters<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
generics: &'tcx ty::Generics,
used_parameters: &mut u64,
unused_parameters: &mut FiniteBitSet<u64>,
) {
if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) {
for param in &generics.params {
debug!("mark_used_by_default_parameters: (closure/gen) param={:?}", param);
set_bit(used_parameters, param.index);
unused_parameters.clear(param.index);
}
} else {
for param in &generics.params {
debug!("mark_used_by_default_parameters: (other) param={:?}", param);
if let ty::GenericParamDefKind::Lifetime = param.kind {
set_bit(used_parameters, param.index);
unused_parameters.clear(param.index);
}
}
}
if let Some(parent) = generics.parent {
mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), used_parameters);
mark_used_by_default_parameters(tcx, parent, tcx.generics_of(parent), unused_parameters);
}
}
/// Search the predicates on used generic parameters for any unused generic parameters, and mark
/// those as used.
fn mark_used_by_predicates<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, used_parameters: &mut u64) {
fn mark_used_by_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
unused_parameters: &mut FiniteBitSet<u64>,
) {
let def_id = tcx.closure_base_def_id(def_id);
let is_self_ty_used = |used_parameters: &mut u64, self_ty: Ty<'tcx>| {
let is_self_ty_used = |unused_parameters: &mut FiniteBitSet<u64>, self_ty: Ty<'tcx>| {
debug!("unused_generic_params: self_ty={:?}", self_ty);
if let ty::Param(param) = self_ty.kind {
is_bit_set(*used_parameters, param.index)
!unused_parameters.contains(param.index).unwrap_or(false)
} else {
false
}
};
let mark_ty = |used_parameters: &mut u64, ty: Ty<'tcx>| {
let mut vis = UsedGenericParametersVisitor { tcx, def_id, used_parameters };
let mark_ty = |unused_parameters: &mut FiniteBitSet<u64>, ty: Ty<'tcx>| {
let mut vis = UsedGenericParametersVisitor { tcx, def_id, unused_parameters };
ty.visit_with(&mut vis);
};
@ -150,19 +134,19 @@ fn mark_used_by_predicates<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, used_paramete
match predicate.kind() {
ty::PredicateKind::Trait(predicate, ..) => {
let trait_ref = predicate.skip_binder().trait_ref;
if is_self_ty_used(used_parameters, trait_ref.self_ty()) {
if is_self_ty_used(unused_parameters, trait_ref.self_ty()) {
for ty in trait_ref.substs.types() {
debug!("unused_generic_params: (trait) ty={:?}", ty);
mark_ty(used_parameters, ty);
mark_ty(unused_parameters, ty);
}
}
}
ty::PredicateKind::Projection(predicate, ..) => {
let self_ty = predicate.skip_binder().projection_ty.self_ty();
if is_self_ty_used(used_parameters, self_ty) {
if is_self_ty_used(unused_parameters, self_ty) {
let ty = predicate.ty();
debug!("unused_generic_params: (projection) ty={:?}", ty);
mark_ty(used_parameters, ty.skip_binder());
mark_ty(unused_parameters, ty.skip_binder());
}
}
_ => (),
@ -176,7 +160,7 @@ fn emit_unused_generic_params_error<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
generics: &'tcx ty::Generics,
unused_parameters: u64,
unused_parameters: &FiniteBitSet<u64>,
) {
debug!("emit_unused_generic_params_error: def_id={:?}", def_id);
let base_def_id = tcx.closure_base_def_id(def_id);
@ -184,7 +168,7 @@ fn emit_unused_generic_params_error<'tcx>(
return;
}
debug!("emit_unused_generic_params_error: unused_parameters={:064b}", unused_parameters);
debug!("emit_unused_generic_params_error: unused_parameters={:?}", unused_parameters);
let fn_span = match tcx.opt_item_name(def_id) {
Some(ident) => ident.span,
_ => tcx.def_span(def_id),
@ -195,7 +179,7 @@ fn emit_unused_generic_params_error<'tcx>(
let mut next_generics = Some(generics);
while let Some(generics) = next_generics {
for param in &generics.params {
if is_bit_set(unused_parameters, param.index) {
if unused_parameters.contains(param.index).unwrap_or(false) {
debug!("emit_unused_generic_params_error: param={:?}", param);
let def_span = tcx.def_span(param.def_id);
err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name));
@ -212,7 +196,7 @@ fn emit_unused_generic_params_error<'tcx>(
struct UsedGenericParametersVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
def_id: DefId,
used_parameters: &'a mut u64,
unused_parameters: &'a mut FiniteBitSet<u64>,
}
impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
@ -252,7 +236,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
match c.val {
ty::ConstKind::Param(param) => {
debug!("visit_const: param={:?}", param);
set_bit(self.used_parameters, param.index);
self.unused_parameters.clear(param.index);
false
}
_ => c.super_visit_with(self),
@ -277,21 +261,22 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
// parent.
let unused = self.tcx.unused_generic_params(def_id);
debug!(
"visit_ty: used_parameters={:064b} unused={:064b}",
self.used_parameters, unused
"visit_ty: unused_parameters={:?} unused={:?}",
self.unused_parameters, unused
);
for (i, arg) in substs.iter().enumerate() {
if !is_bit_set(unused, i.try_into().unwrap()) {
let i = i.try_into().unwrap();
if !unused.contains(i).unwrap_or(false) {
arg.visit_with(self);
}
}
debug!("visit_ty: used_parameters={:064b}", self.used_parameters);
debug!("visit_ty: unused_parameters={:?}", self.unused_parameters);
false
}
ty::Param(param) => {
debug!("visit_ty: param={:?}", param);
set_bit(self.used_parameters, param.index);
self.unused_parameters.clear(param.index);
false
}
_ => ty.super_visit_with(self),

View File

@ -7,7 +7,7 @@
#[rustc_polymorphize_error]
fn bar<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA,
AB, AC, AD, AE, AF, AG, AH, AI, AJ, AK, AL, AM, AN, AO, AP, AQ, AR, AS, AT, AU, AV, AW,
AX, AY, AZ, BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL>()
AX, AY, AZ, BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL, BM>()
{
let _: Option<A> = None;
let _: Option<B> = None;
@ -60,6 +60,7 @@ fn bar<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
let _: Option<AW> = None;
let _: Option<AX> = None;
let _: Option<AY> = None;
let _: Option<AZ> = None;
let _: Option<BA> = None;
let _: Option<BB> = None;
let _: Option<BC> = None;
@ -72,6 +73,13 @@ fn bar<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
let _: Option<BJ> = None;
let _: Option<BK> = None;
let _: Option<BL> = None;
let _: Option<BM> = None;
}
fn main() { }
fn main() {
bar::<u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32,
u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32,
u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32,
u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32,
u32>();
}