Introduce a fast-path for type_is_sized/type_moves_by_default

This seems to improve performance by the same 2-3% of my selection
fast-path.
This commit is contained in:
Ariel Ben-Yehuda 2015-06-05 03:50:49 +03:00
parent 52e530af4c
commit 595409df06
4 changed files with 119 additions and 68 deletions

View File

@ -60,7 +60,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> {
cmt: mc::cmt<'tcx>, cmt: mc::cmt<'tcx>,
_: euv::ConsumeMode) { _: euv::ConsumeMode) {
debug!("consume; cmt: {:?}; type: {}", *cmt, ty_to_string(self.tcx, cmt.ty)); debug!("consume; cmt: {:?}; type: {}", *cmt, ty_to_string(self.tcx, cmt.ty));
if !ty::type_is_sized(self.param_env, span, cmt.ty) { if !ty::type_is_sized(Some(self.param_env), self.tcx, span, cmt.ty) {
span_err!(self.tcx.sess, span, E0161, span_err!(self.tcx.sess, span, E0161,
"cannot move a value of type {0}: the size of {0} cannot be statically determined", "cannot move a value of type {0}: the size of {0} cannot be statically determined",
ty_to_string(self.tcx, cmt.ty)); ty_to_string(self.tcx, cmt.ty));

View File

@ -212,7 +212,7 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
debug!("with_each_combination: space={:?}, index={}, param_ty={}", debug!("with_each_combination: space={:?}, index={}, param_ty={}",
space, index, param_ty.repr(self.tcx)); space, index, param_ty.repr(self.tcx));
if !ty::type_is_sized(param_env, span, param_ty) { if !ty::type_is_sized(Some(param_env), self.tcx, span, param_ty) {
debug!("with_each_combination: param_ty is not known to be sized"); debug!("with_each_combination: param_ty is not known to be sized");
substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty; substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;

View File

@ -756,16 +756,6 @@ pub struct ctxt<'tcx> {
/// Caches the representation hints for struct definitions. /// Caches the representation hints for struct definitions.
pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>, pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
/// Caches whether types are known to impl Copy. Note that type
/// parameters are never placed into this cache, because their
/// results are dependent on the parameter environment.
pub type_impls_copy_cache: RefCell<HashMap<Ty<'tcx>,bool>>,
/// Caches whether types are known to impl Sized. Note that type
/// parameters are never placed into this cache, because their
/// results are dependent on the parameter environment.
pub type_impls_sized_cache: RefCell<HashMap<Ty<'tcx>,bool>>,
/// Maps Expr NodeId's to their constant qualification. /// Maps Expr NodeId's to their constant qualification.
pub const_qualif_map: RefCell<NodeMap<check_const::ConstQualif>>, pub const_qualif_map: RefCell<NodeMap<check_const::ConstQualif>>,
@ -827,6 +817,23 @@ bitflags! {
const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits |
TypeFlags::HAS_SELF.bits | TypeFlags::HAS_SELF.bits |
TypeFlags::HAS_REGIONS.bits, TypeFlags::HAS_REGIONS.bits,
// Flags representing the nominal content of a type,
// computed by FlagsComputetion
const NOMINAL_FLAGS = TypeFlags::HAS_PARAMS.bits |
TypeFlags::HAS_SELF.bits |
TypeFlags::HAS_TY_INFER.bits |
TypeFlags::HAS_RE_INFER.bits |
TypeFlags::HAS_RE_LATE_BOUND.bits |
TypeFlags::HAS_REGIONS.bits |
TypeFlags::HAS_TY_ERR.bits |
TypeFlags::HAS_PROJECTION.bits,
// Caches for type_is_sized, type_moves_by_default
const SIZEDNESS_CACHED = 1 << 16,
const IS_SIZED = 1 << 17,
const MOVENESS_CACHED = 1 << 18,
const MOVES_BY_DEFAULT = 1 << 19,
} }
} }
@ -859,8 +866,8 @@ macro_rules! sty_debug_print {
ty::ty_err => /* unimportant */ continue, ty::ty_err => /* unimportant */ continue,
$(ty::$variant(..) => &mut $variant,)* $(ty::$variant(..) => &mut $variant,)*
}; };
let region = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); let region = t.flags.get().intersects(ty::TypeFlags::HAS_RE_INFER);
let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER); let ty = t.flags.get().intersects(ty::TypeFlags::HAS_TY_INFER);
variant.total += 1; variant.total += 1;
total.total += 1; total.total += 1;
@ -908,7 +915,7 @@ impl<'tcx> ctxt<'tcx> {
#[derive(Debug)] #[derive(Debug)]
pub struct TyS<'tcx> { pub struct TyS<'tcx> {
pub sty: sty<'tcx>, pub sty: sty<'tcx>,
pub flags: TypeFlags, pub flags: Cell<TypeFlags>,
// the maximal depth of any bound regions appearing in this type. // the maximal depth of any bound regions appearing in this type.
region_depth: u32, region_depth: u32,
@ -964,23 +971,23 @@ impl<'tcx> Borrow<sty<'tcx>> for InternedTy<'tcx> {
} }
pub fn type_has_params(ty: Ty) -> bool { pub fn type_has_params(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_PARAMS) ty.flags.get().intersects(TypeFlags::HAS_PARAMS)
} }
pub fn type_has_self(ty: Ty) -> bool { pub fn type_has_self(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_SELF) ty.flags.get().intersects(TypeFlags::HAS_SELF)
} }
pub fn type_has_ty_infer(ty: Ty) -> bool { pub fn type_has_ty_infer(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_TY_INFER) ty.flags.get().intersects(TypeFlags::HAS_TY_INFER)
} }
pub fn type_needs_infer(ty: Ty) -> bool { pub fn type_needs_infer(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER) ty.flags.get().intersects(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER)
} }
pub fn type_has_projection(ty: Ty) -> bool { pub fn type_has_projection(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_PROJECTION) ty.flags.get().intersects(TypeFlags::HAS_PROJECTION)
} }
pub fn type_has_late_bound_regions(ty: Ty) -> bool { pub fn type_has_late_bound_regions(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_RE_LATE_BOUND) ty.flags.get().intersects(TypeFlags::HAS_RE_LATE_BOUND)
} }
/// An "escaping region" is a bound region whose binder is not part of `t`. /// An "escaping region" is a bound region whose binder is not part of `t`.
@ -2770,8 +2777,6 @@ pub fn mk_ctxt<'tcx>(s: Session,
stability: RefCell::new(stability), stability: RefCell::new(stability),
selection_cache: traits::SelectionCache::new(), selection_cache: traits::SelectionCache::new(),
repr_hint_cache: RefCell::new(DefIdMap()), repr_hint_cache: RefCell::new(DefIdMap()),
type_impls_copy_cache: RefCell::new(HashMap::new()),
type_impls_sized_cache: RefCell::new(HashMap::new()),
const_qualif_map: RefCell::new(NodeMap()), const_qualif_map: RefCell::new(NodeMap()),
custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
cast_kinds: RefCell::new(NodeMap()), cast_kinds: RefCell::new(NodeMap()),
@ -2871,7 +2876,7 @@ fn intern_ty<'tcx>(type_arena: &'tcx TypedArena<TyS<'tcx>>,
let ty = match () { let ty = match () {
() => type_arena.alloc(TyS { sty: st, () => type_arena.alloc(TyS { sty: st,
flags: flags.flags, flags: Cell::new(flags.flags),
region_depth: flags.depth, }), region_depth: flags.depth, }),
}; };
@ -2902,7 +2907,7 @@ impl FlagComputation {
} }
fn add_flags(&mut self, flags: TypeFlags) { fn add_flags(&mut self, flags: TypeFlags) {
self.flags = self.flags | flags; self.flags = self.flags | (flags & TypeFlags::NOMINAL_FLAGS);
} }
fn add_depth(&mut self, depth: u32) { fn add_depth(&mut self, depth: u32) {
@ -3008,7 +3013,7 @@ impl FlagComputation {
} }
fn add_ty(&mut self, ty: Ty) { fn add_ty(&mut self, ty: Ty) {
self.add_flags(ty.flags); self.add_flags(ty.flags.get());
self.add_depth(ty.region_depth); self.add_depth(ty.region_depth);
} }
@ -3389,11 +3394,11 @@ pub fn type_is_nil(ty: Ty) -> bool {
} }
pub fn type_is_error(ty: Ty) -> bool { pub fn type_is_error(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::HAS_TY_ERR) ty.flags.get().intersects(TypeFlags::HAS_TY_ERR)
} }
pub fn type_needs_subst(ty: Ty) -> bool { pub fn type_needs_subst(ty: Ty) -> bool {
ty.flags.intersects(TypeFlags::NEEDS_SUBST) ty.flags.get().intersects(TypeFlags::NEEDS_SUBST)
} }
pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool { pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool {
@ -3911,42 +3916,30 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
} }
} }
fn type_impls_bound<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, fn type_impls_bound<'a,'tcx>(param_env: Option<&ParameterEnvironment<'a,'tcx>>,
cache: &RefCell<HashMap<Ty<'tcx>,bool>>, tcx: &ty::ctxt<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
bound: ty::BuiltinBound, bound: ty::BuiltinBound,
span: Span) span: Span)
-> bool -> bool
{ {
assert!(!ty::type_needs_infer(ty)); let pe;
let param_env = match param_env {
if !type_has_params(ty) && !type_has_self(ty) { Some(e) => e,
match cache.borrow().get(&ty) { None => {
None => {} pe = empty_parameter_environment(tcx);
Some(&result) => { &pe
debug!("type_impls_bound({}, {:?}) = {:?} (cached)",
ty.repr(param_env.tcx),
bound,
result);
return result
}
} }
} };
let infcx = infer::new_infer_ctxt(tcx);
let infcx = infer::new_infer_ctxt(param_env.tcx);
let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound, span); let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound, span);
debug!("type_impls_bound({}, {:?}) = {:?}", debug!("type_impls_bound({}, {:?}) = {:?}",
ty.repr(param_env.tcx), ty.repr(tcx),
bound, bound,
is_impld); is_impld);
if !type_has_params(ty) && !type_has_self(ty) {
let old_value = cache.borrow_mut().insert(ty, is_impld);
assert!(old_value.is_none());
}
is_impld is_impld
} }
@ -3955,17 +3948,85 @@ pub fn type_moves_by_default<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>,
ty: Ty<'tcx>) ty: Ty<'tcx>)
-> bool -> bool
{ {
let tcx = param_env.tcx; if ty.flags.get().intersects(TypeFlags::MOVENESS_CACHED) {
!type_impls_bound(param_env, &tcx.type_impls_copy_cache, ty, ty::BoundCopy, span) return ty.flags.get().intersects(TypeFlags::MOVES_BY_DEFAULT);
}
assert!(!ty::type_needs_infer(ty));
// Fast-path for primitive types
let result = match ty.sty {
ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) |
ty_ptr(..) | ty_bare_fn(..) | ty_rptr(_, mt {
mutbl: ast::MutImmutable, ..
}) => Some(false),
ty_str | ty_uniq(..) | ty_rptr(_, mt {
mutbl: ast::MutMutable, ..
}) => Some(true),
ty_vec(..) | ty_trait(..) | ty_tup(..) |
ty_closure(..) | ty_enum(..) | ty_struct(..) |
ty_projection(..) | ty_param(..) | ty_infer(..) | ty_err => None
}.unwrap_or_else(|| !type_impls_bound(Some(param_env),
param_env.tcx,
ty,
ty::BoundCopy,
span));
if !type_has_params(ty) && !type_has_self(ty) {
ty.flags.set(ty.flags.get() | if result {
TypeFlags::MOVENESS_CACHED | TypeFlags::MOVES_BY_DEFAULT
} else {
TypeFlags::MOVENESS_CACHED
});
}
result
} }
pub fn type_is_sized<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, #[inline]
pub fn type_is_sized<'a,'tcx>(param_env: Option<&ParameterEnvironment<'a,'tcx>>,
tcx: &ty::ctxt<'tcx>,
span: Span, span: Span,
ty: Ty<'tcx>) ty: Ty<'tcx>)
-> bool -> bool
{ {
let tcx = param_env.tcx; if ty.flags.get().intersects(TypeFlags::SIZEDNESS_CACHED) {
type_impls_bound(param_env, &tcx.type_impls_sized_cache, ty, ty::BoundSized, span) let result = ty.flags.get().intersects(TypeFlags::IS_SIZED);
return result;
}
type_is_sized_uncached(param_env, tcx, span, ty)
}
fn type_is_sized_uncached<'a,'tcx>(param_env: Option<&ParameterEnvironment<'a,'tcx>>,
tcx: &ty::ctxt<'tcx>,
span: Span,
ty: Ty<'tcx>) -> bool {
assert!(!ty::type_needs_infer(ty));
// Fast-path for primitive types
let result = match ty.sty {
ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) |
ty_uniq(..) | ty_ptr(..) | ty_rptr(..) | ty_bare_fn(..) |
ty_vec(_, Some(..)) | ty_tup(..) | ty_closure(..) => Some(true),
ty_str | ty_trait(..) | ty_vec(_, None) => Some(false),
ty_enum(..) | ty_struct(..) | ty_projection(..) | ty_param(..) |
ty_infer(..) | ty_err => None
}.unwrap_or_else(|| type_impls_bound(param_env, tcx, ty, ty::BoundSized, span));
if !type_has_params(ty) && !type_has_self(ty) {
ty.flags.set(ty.flags.get() | if result {
TypeFlags::SIZEDNESS_CACHED | TypeFlags::IS_SIZED
} else {
TypeFlags::SIZEDNESS_CACHED
});
}
result
} }
pub fn is_ffi_safe<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn is_ffi_safe<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool {

View File

@ -118,19 +118,9 @@ pub fn erase_regions<'tcx,T>(cx: &ty::ctxt<'tcx>, value: &T) -> T
} }
} }
// Is the type's representation size known at compile time? /// Is the type's representation size known at compile time?
pub fn type_is_sized<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn type_is_sized<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
let param_env = ty::empty_parameter_environment(tcx); ty::type_is_sized(None, tcx, DUMMY_SP, ty)
// FIXME(#4287) This can cause errors due to polymorphic recursion,
// a better span should be provided, if available.
let err_count = tcx.sess.err_count();
let is_sized = ty::type_is_sized(&param_env, DUMMY_SP, ty);
// Those errors aren't fatal, but an incorrect result can later
// trip over asserts in both rustc's trans and LLVM.
if err_count < tcx.sess.err_count() {
tcx.sess.abort_if_errors();
}
is_sized
} }
pub fn type_is_fat_ptr<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn type_is_fat_ptr<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {