@@ -5,12 +5,13 @@ use rustc_abi as abi;
55use rustc_abi:: {
66 Align , BackendRepr , FIRST_VARIANT , FieldIdx , Primitive , Size , TagEncoding , VariantIdx , Variants ,
77} ;
8+ use rustc_hir:: LangItem ;
89use rustc_middle:: mir:: interpret:: { Pointer , Scalar , alloc_range} ;
910use rustc_middle:: mir:: { self , ConstValue } ;
10- use rustc_middle:: ty:: Ty ;
1111use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
12+ use rustc_middle:: ty:: { self , Ty } ;
1213use rustc_middle:: { bug, span_bug} ;
13- use rustc_session:: config:: OptLevel ;
14+ use rustc_session:: config:: { AnnotateMoves , DebugInfo , OptLevel } ;
1415use tracing:: { debug, instrument} ;
1516
1617use super :: place:: { PlaceRef , PlaceValue } ;
@@ -131,6 +132,10 @@ pub struct OperandRef<'tcx, V> {
131132
132133 /// The layout of value, based on its Rust type.
133134 pub layout : TyAndLayout < ' tcx > ,
135+
136+ /// Annotation for profiler visibility of move/copy operations.
137+ /// When set, the store operation should appear as an inlined call to this function.
138+ pub move_annotation : Option < ty:: Instance < ' tcx > > ,
134139}
135140
136141impl < V : CodegenObject > fmt:: Debug for OperandRef < ' _ , V > {
@@ -142,7 +147,7 @@ impl<V: CodegenObject> fmt::Debug for OperandRef<'_, V> {
142147impl < ' a , ' tcx , V : CodegenObject > OperandRef < ' tcx , V > {
143148 pub fn zero_sized ( layout : TyAndLayout < ' tcx > ) -> OperandRef < ' tcx , V > {
144149 assert ! ( layout. is_zst( ) ) ;
145- OperandRef { val : OperandValue :: ZeroSized , layout }
150+ OperandRef { val : OperandValue :: ZeroSized , layout, move_annotation : None }
146151 }
147152
148153 pub ( crate ) fn from_const < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
@@ -180,7 +185,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
180185 }
181186 } ;
182187
183- OperandRef { val, layout }
188+ OperandRef { val, layout, move_annotation : None }
184189 }
185190
186191 fn from_const_alloc < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
@@ -214,7 +219,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
214219 let size = s. size ( bx) ;
215220 assert_eq ! ( size, layout. size, "abi::Scalar size does not match layout size" ) ;
216221 let val = read_scalar ( offset, size, s, bx. immediate_backend_type ( layout) ) ;
217- OperandRef { val : OperandValue :: Immediate ( val) , layout }
222+ OperandRef { val : OperandValue :: Immediate ( val) , layout, move_annotation : None }
218223 }
219224 BackendRepr :: ScalarPair (
220225 a @ abi:: Scalar :: Initialized { .. } ,
@@ -235,7 +240,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
235240 b,
236241 bx. scalar_pair_element_backend_type ( layout, 1 , true ) ,
237242 ) ;
238- OperandRef { val : OperandValue :: Pair ( a_val, b_val) , layout }
243+ OperandRef { val : OperandValue :: Pair ( a_val, b_val) , layout, move_annotation : None }
239244 }
240245 _ if layout. is_zst ( ) => OperandRef :: zero_sized ( layout) ,
241246 _ => {
@@ -285,6 +290,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
285290 self . val . deref ( layout. align . abi ) . with_type ( layout)
286291 }
287292
293+ /// Store this operand into a place, applying move/copy annotation if present.
294+ ///
295+ /// This is the preferred method for storing operands, as it automatically
296+ /// applies profiler annotations for tracked move/copy operations.
297+ pub fn store_with_annotation < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
298+ self ,
299+ bx : & mut Bx ,
300+ dest : PlaceRef < ' tcx , V > ,
301+ ) {
302+ if let Some ( instance) = self . move_annotation {
303+ bx. with_move_annotation ( instance, |bx| self . val . store ( bx, dest) )
304+ } else {
305+ self . val . store ( bx, dest)
306+ }
307+ }
308+
288309 /// If this operand is a `Pair`, we return an aggregate with the two values.
289310 /// For other cases, see `immediate`.
290311 pub fn immediate_or_packed_pair < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
@@ -320,7 +341,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
320341 } else {
321342 OperandValue :: Immediate ( llval)
322343 } ;
323- OperandRef { val, layout }
344+ OperandRef { val, layout, move_annotation : None }
324345 }
325346
326347 pub ( crate ) fn extract_field < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
@@ -388,7 +409,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
388409 } )
389410 } ;
390411
391- OperandRef { val, layout : field }
412+ OperandRef { val, layout : field, move_annotation : None }
392413 }
393414
394415 /// Obtain the actual discriminant of a value.
@@ -828,10 +849,15 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> {
828849 }
829850 } ,
830851 } ;
831- OperandRef { val, layout }
852+ OperandRef { val, layout, move_annotation : None }
832853 }
833854}
834855
856+ /// Default size limit for move/copy annotations (in bytes). 64 bytes is a common size of a cache
857+ /// line, and the assumption is that anything this size or below is very cheap to move/copy, so only
858+ /// annotate copies larger than this.
859+ const MOVE_ANNOTATION_DEFAULT_LIMIT : u64 = 65 ;
860+
835861impl < ' a , ' tcx , V : CodegenObject > OperandValue < V > {
836862 /// Returns an `OperandValue` that's generally UB to use in any way.
837863 ///
@@ -961,7 +987,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
961987 abi:: Variants :: Single { index: vidx } ,
962988 ) ;
963989 let layout = o. layout . for_variant ( bx. cx ( ) , vidx) ;
964- o = OperandRef { val : o . val , layout }
990+ o = OperandRef { layout , ..o }
965991 }
966992 _ => return None ,
967993 }
@@ -1014,7 +1040,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10141040
10151041 match * operand {
10161042 mir:: Operand :: Copy ( ref place) | mir:: Operand :: Move ( ref place) => {
1017- self . codegen_consume ( bx, place. as_ref ( ) )
1043+ let kind = match operand {
1044+ mir:: Operand :: Move ( _) => LangItem :: CompilerMove ,
1045+ mir:: Operand :: Copy ( _) => LangItem :: CompilerCopy ,
1046+ _ => unreachable ! ( ) ,
1047+ } ;
1048+
1049+ // Check if we should annotate this move/copy for profiling
1050+ let move_annotation = self . move_copy_annotation_instance ( bx, place. as_ref ( ) , kind) ;
1051+
1052+ OperandRef { move_annotation, ..self . codegen_consume ( bx, place. as_ref ( ) ) }
10181053 }
10191054
10201055 mir:: Operand :: Constant ( ref constant) => {
@@ -1030,11 +1065,76 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10301065 return OperandRef {
10311066 val : OperandValue :: Immediate ( llval) ,
10321067 layout : bx. layout_of ( ty) ,
1068+ move_annotation : None ,
10331069 } ;
10341070 }
10351071 }
10361072 self . eval_mir_constant_to_operand ( bx, constant)
10371073 }
10381074 }
10391075 }
1076+
1077+ /// Creates an `Instance` for annotating a move/copy operation at codegen time.
1078+ ///
1079+ /// Returns `Some(instance)` if the operation should be annotated with debug info, `None`
1080+ /// otherwise. The instance represents a monomorphized `compiler_move<T, SIZE>` or
1081+ /// `compiler_copy<T, SIZE>` function that can be used to create debug scopes.
1082+ ///
1083+ /// There are a number of conditions that must be met for an annotation to be created, but aside
1084+ /// from the basics (annotation is enabled, we're generating debuginfo), the primary concern is
1085+ /// moves/copies which could result in a real `memcpy`. So we check for the size limit, but also
1086+ /// that the underlying representation of the type is in memory.
1087+ fn move_copy_annotation_instance (
1088+ & self ,
1089+ bx : & Bx ,
1090+ place : mir:: PlaceRef < ' tcx > ,
1091+ kind : LangItem ,
1092+ ) -> Option < ty:: Instance < ' tcx > > {
1093+ let tcx = bx. tcx ( ) ;
1094+ let sess = tcx. sess ;
1095+
1096+ // Skip if we're not generating debuginfo
1097+ if sess. opts . debuginfo == DebugInfo :: None {
1098+ return None ;
1099+ }
1100+
1101+ // Check if annotation is enabled and get size limit (otherwise skip)
1102+ let size_limit = match sess. opts . unstable_opts . annotate_moves {
1103+ AnnotateMoves :: Disabled => return None ,
1104+ AnnotateMoves :: Enabled ( None ) => MOVE_ANNOTATION_DEFAULT_LIMIT ,
1105+ AnnotateMoves :: Enabled ( Some ( limit) ) => limit,
1106+ } ;
1107+
1108+ let ty = self . monomorphized_place_ty ( place) ;
1109+ let layout = bx. cx ( ) . layout_of ( ty) ;
1110+ let ty_size = layout. size . bytes ( ) ;
1111+
1112+ // Only annotate if type has a memory representation and exceeds size limit (and has a
1113+ // non-zero size)
1114+ if layout. is_zst ( )
1115+ || ty_size < size_limit
1116+ || !matches ! ( layout. backend_repr, BackendRepr :: Memory { .. } )
1117+ {
1118+ return None ;
1119+ }
1120+
1121+ // Look up the DefId for compiler_move or compiler_copy lang item
1122+ let def_id = tcx. lang_items ( ) . get ( kind) ?;
1123+
1124+ // Create generic args: compiler_move<T, SIZE> or compiler_copy<T, SIZE>
1125+ let size_const = ty:: Const :: from_target_usize ( tcx, ty_size) ;
1126+ let generic_args = tcx. mk_args ( & [ ty. into ( ) , size_const. into ( ) ] ) ;
1127+
1128+ // Create the Instance
1129+ let typing_env = self . mir . typing_env ( tcx) ;
1130+ let instance = ty:: Instance :: expect_resolve (
1131+ tcx,
1132+ typing_env,
1133+ def_id,
1134+ generic_args,
1135+ rustc_span:: DUMMY_SP , // span only used for error messages
1136+ ) ;
1137+
1138+ Some ( instance)
1139+ }
10401140}
0 commit comments