@@ -159,8 +159,7 @@ pub enum StackPopCleanup {
159
159
pub struct LocalState < ' tcx , Prov : Provenance = AllocId > {
160
160
pub value : LocalValue < Prov > ,
161
161
/// Don't modify if `Some`, this is only used to prevent computing the layout twice.
162
- /// Layout needs to be computed lazily because ConstProp wants to run on frames where we can't
163
- /// compute the layout of all locals.
162
+ /// Avoids computing the layout of locals that are never actually initialized.
164
163
pub layout : Cell < Option < TyAndLayout < ' tcx > > > ,
165
164
}
166
165
@@ -919,15 +918,72 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
919
918
) -> InterpResult < ' tcx > {
920
919
trace ! ( "{:?} is now live" , local) ;
921
920
922
- let layout = self . layout_of_local ( self . frame ( ) , local, None ) ?;
923
- let local_val = LocalValue :: Live ( if layout. is_sized ( ) {
924
- assert ! ( matches!( meta, MemPlaceMeta :: None ) ) ; // we're dropping the metadata
925
- // Just make this an efficient immediate.
926
- Operand :: Immediate ( Immediate :: Uninit )
921
+ // We avoid `ty.is_trivially_sized` since that (a) cannot assume WF, so it recurses through
922
+ // all fields of a tuple, and (b) does something expensive for ADTs.
923
+ fn is_very_trivially_sized ( ty : Ty < ' _ > ) -> bool {
924
+ match ty. kind ( ) {
925
+ ty:: Infer ( ty:: IntVar ( _) | ty:: FloatVar ( _) )
926
+ | ty:: Uint ( _)
927
+ | ty:: Int ( _)
928
+ | ty:: Bool
929
+ | ty:: Float ( _)
930
+ | ty:: FnDef ( ..)
931
+ | ty:: FnPtr ( _)
932
+ | ty:: RawPtr ( ..)
933
+ | ty:: Char
934
+ | ty:: Ref ( ..)
935
+ | ty:: Generator ( ..)
936
+ | ty:: GeneratorWitness ( ..)
937
+ | ty:: GeneratorWitnessMIR ( ..)
938
+ | ty:: Array ( ..)
939
+ | ty:: Closure ( ..)
940
+ | ty:: Never
941
+ | ty:: Error ( _) => true ,
942
+
943
+ ty:: Str | ty:: Slice ( _) | ty:: Dynamic ( ..) | ty:: Foreign ( ..) => false ,
944
+
945
+ ty:: Tuple ( tys) => tys. last ( ) . iter ( ) . all ( |ty| is_very_trivially_sized ( * * ty) ) ,
946
+
947
+ // We don't want to do any queries, so there is not much we can do with ADTs.
948
+ ty:: Adt ( ..) => false ,
949
+
950
+ ty:: Alias ( ..) | ty:: Param ( _) | ty:: Placeholder ( ..) => false ,
951
+
952
+ ty:: Infer ( ty:: TyVar ( _) ) => false ,
953
+
954
+ ty:: Bound ( ..)
955
+ | ty:: Infer ( ty:: FreshTy ( _) | ty:: FreshIntTy ( _) | ty:: FreshFloatTy ( _) ) => {
956
+ bug ! ( "`is_very_trivially_sized` applied to unexpected type: {:?}" , ty)
957
+ }
958
+ }
959
+ }
960
+
961
+ // This is a hot function, we avoid computing the layout when possible.
962
+ // `unsized_` will be `None` for sized types and `Some(layout)` for unsized types.
963
+ let unsized_ = if is_very_trivially_sized ( self . body ( ) . local_decls [ local] . ty ) {
964
+ None
927
965
} else {
928
- // Need to allocate some memory.
966
+ // We need the layout.
967
+ let layout = self . layout_of_local ( self . frame ( ) , local, None ) ?;
968
+ if layout. is_sized ( ) { None } else { Some ( layout) }
969
+ } ;
970
+
971
+ let local_val = LocalValue :: Live ( if let Some ( layout) = unsized_ {
972
+ if !meta. has_meta ( ) {
973
+ throw_unsup ! ( UnsizedLocal ) ;
974
+ }
975
+ // Need to allocate some memory, since `Immediate::Uninit` cannot be unsized.
929
976
let dest_place = self . allocate_dyn ( layout, MemoryKind :: Stack , meta) ?;
930
977
Operand :: Indirect ( * dest_place)
978
+ } else {
979
+ assert ! ( !meta. has_meta( ) ) ; // we're dropping the metadata
980
+ // Just make this an efficient immediate.
981
+ // Note that not calling `layout_of` here does have one real consequence:
982
+ // if the type is too big, we'll only notice this when the local is actually initialized,
983
+ // which is a bit too late -- we should ideally notice this alreayd here, when the memory
984
+ // is conceptually allocated. But given how rare that error is and that this is a hot function,
985
+ // we accept this downside for now.
986
+ Operand :: Immediate ( Immediate :: Uninit )
931
987
} ) ;
932
988
933
989
// StorageLive expects the local to be dead, and marks it live.
@@ -939,13 +995,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
939
995
}
940
996
941
997
/// Mark a storage as live, killing the previous content.
998
+ #[ inline( always) ]
942
999
pub fn storage_live ( & mut self , local : mir:: Local ) -> InterpResult < ' tcx > {
943
- trace ! ( "{:?} is now live" , local) ;
944
-
945
- if self . layout_of_local ( self . frame ( ) , local, None ) ?. is_unsized ( ) {
946
- throw_unsup ! ( UnsizedLocal ) ;
947
- }
948
-
949
1000
self . storage_live_dyn ( local, MemPlaceMeta :: None )
950
1001
}
951
1002
0 commit comments