@@ -7,10 +7,11 @@ use rustc_abi::{
77} ;
88use rustc_middle:: mir:: interpret:: { Pointer , Scalar , alloc_range} ;
99use rustc_middle:: mir:: { self , ConstValue } ;
10- use rustc_middle:: ty:: Ty ;
1110use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
11+ use rustc_middle:: ty:: { self , Ty } ;
1212use rustc_middle:: { bug, span_bug} ;
13- use rustc_session:: config:: OptLevel ;
13+ use rustc_session:: config:: { AnnotateMoves , DebugInfo , OptLevel } ;
14+ use rustc_span:: { Symbol , sym} ;
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,13 @@ 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).
857+ const MOVE_ANNOTATION_DEFAULT_LIMIT : u64 = 65 ;
858+
835859impl < ' a , ' tcx , V : CodegenObject > OperandValue < V > {
836860 /// Returns an `OperandValue` that's generally UB to use in any way.
837861 ///
@@ -961,7 +985,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
961985 abi:: Variants :: Single { index: vidx } ,
962986 ) ;
963987 let layout = o. layout . for_variant ( bx. cx ( ) , vidx) ;
964- o = OperandRef { val : o . val , layout }
988+ o = OperandRef { layout , ..o }
965989 }
966990 _ => return None ,
967991 }
@@ -1014,7 +1038,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10141038
10151039 match * operand {
10161040 mir:: Operand :: Copy ( ref place) | mir:: Operand :: Move ( ref place) => {
1017- self . codegen_consume ( bx, place. as_ref ( ) )
1041+ let kind = match operand {
1042+ mir:: Operand :: Move ( _) => sym:: compiler_move,
1043+ mir:: Operand :: Copy ( _) => sym:: compiler_copy,
1044+ _ => unreachable ! ( ) ,
1045+ } ;
1046+ let ty = self . monomorphized_place_ty ( place. as_ref ( ) ) ;
1047+ let layout = bx. cx ( ) . layout_of ( ty) ;
1048+
1049+ // Check if we should annotate this move/copy for profiling
1050+ let move_annotation = self . move_copy_annotation_instance ( bx, ty, layout, kind) ;
1051+
1052+ OperandRef { move_annotation, ..self . codegen_consume ( bx, place. as_ref ( ) ) }
10181053 }
10191054
10201055 mir:: Operand :: Constant ( ref constant) => {
@@ -1030,11 +1065,77 @@ 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,
1080+ /// `None` otherwise. The instance represents a monomorphized `compiler_move<T, SIZE>`
1081+ /// or `compiler_copy<T, SIZE>` function that can be used to create debug scopes.
1082+ ///
1083+ /// Annotation criteria:
1084+ /// - Codegen annotation flag must be enabled
1085+ /// - Debug info must be enabled
1086+ /// - Type must not be ZST
1087+ /// - Type size must meet threshold
1088+ /// - Type must not be scalar/scalar-pair/SIMD (no memcpy generated)
1089+ fn move_copy_annotation_instance (
1090+ & self ,
1091+ bx : & Bx ,
1092+ ty : Ty < ' tcx > ,
1093+ layout : TyAndLayout < ' tcx > ,
1094+ kind : Symbol ,
1095+ ) -> Option < ty:: Instance < ' tcx > > {
1096+ let tcx = bx. tcx ( ) ;
1097+ let sess = tcx. sess ;
1098+
1099+ // Check if annotation is enabled and get size limit
1100+ let size_limit = match sess. opts . unstable_opts . annotate_moves {
1101+ AnnotateMoves :: Disabled => return None ,
1102+ AnnotateMoves :: Enabled ( None ) => MOVE_ANNOTATION_DEFAULT_LIMIT ,
1103+ AnnotateMoves :: Enabled ( Some ( limit) ) => limit,
1104+ } ;
1105+ let size = layout. size . bytes ( ) ;
1106+ // Skip if we're not generating debug info, or if ty is zst, or the size limit isn't met, or
1107+ // if the layout is not moved by memcpy.
1108+ if sess. opts . debuginfo == DebugInfo :: None
1109+ || layout. is_zst ( )
1110+ || size < size_limit
1111+ || matches ! (
1112+ layout. backend_repr,
1113+ BackendRepr :: Scalar ( _)
1114+ | BackendRepr :: ScalarPair ( _, _)
1115+ | BackendRepr :: SimdVector { .. }
1116+ )
1117+ {
1118+ return None ;
1119+ }
1120+
1121+ // Look up the DefId for compiler_move or compiler_copy (skip if it's missing for some
1122+ // reason).
1123+ let def_id = tcx. get_diagnostic_item ( kind) ?;
1124+
1125+ // Create generic args: compiler_move<T, SIZE> or compiler_copy<T, SIZE>
1126+ let size_const = ty:: Const :: from_target_usize ( tcx, size) ;
1127+ let generic_args = tcx. mk_args ( & [ ty. into ( ) , size_const. into ( ) ] ) ;
1128+
1129+ // Create the Instance
1130+ let typing_env = self . mir . typing_env ( tcx) ;
1131+ let instance = ty:: Instance :: expect_resolve (
1132+ tcx,
1133+ typing_env,
1134+ def_id,
1135+ generic_args,
1136+ rustc_span:: DUMMY_SP , // span only used for error messages
1137+ ) ;
1138+
1139+ Some ( instance)
1140+ }
10401141}
0 commit comments