@@ -118,6 +118,13 @@ pub struct Thread<'mir, 'tcx> {
118118 /// The virtual call stack.
119119 stack : Vec < Frame < ' mir , ' tcx , Provenance , FrameData < ' tcx > > > ,
120120
121+ /// The index of the topmost user-relevant frame in `stack`. This field must contain
122+ /// the value produced by `get_top_user_relevant_frame`.
123+ /// The `None` state here represents
124+ /// This field is a cache to reduce how often we call that method. The cache is manually
125+ /// maintained inside `MiriMachine::after_stack_push` and `MiriMachine::after_stack_pop`.
126+ top_user_relevant_frame : Option < usize > ,
127+
121128 /// The join status.
122129 join_status : ThreadJoinStatus ,
123130
@@ -147,6 +154,40 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
147154 fn thread_name ( & self ) -> & [ u8 ] {
148155 if let Some ( ref thread_name) = self . thread_name { thread_name } else { b"<unnamed>" }
149156 }
157+
158+ /// Return the top user-relevant frame, if there is one.
159+ /// Note that the choice to return `None` here when there is no user-relevant frame is part of
160+ /// justifying the optimization that only pushes of user-relevant frames require updating the
161+ /// `top_user_relevant_frame` field.
162+ fn compute_top_user_relevant_frame ( & self ) -> Option < usize > {
163+ self . stack
164+ . iter ( )
165+ . enumerate ( )
166+ . rev ( )
167+ . find_map ( |( idx, frame) | if frame. extra . is_user_relevant { Some ( idx) } else { None } )
168+ }
169+
170+ /// Re-compute the top user-relevant frame from scratch.
171+ pub fn recompute_top_user_relevant_frame ( & mut self ) {
172+ self . top_user_relevant_frame = self . compute_top_user_relevant_frame ( ) ;
173+ }
174+
175+ /// Set the top user-relevant frame to the given value. Must be equal to what
176+ /// `get_top_user_relevant_frame` would return!
177+ pub fn set_top_user_relevant_frame ( & mut self , frame_idx : usize ) {
178+ debug_assert_eq ! ( Some ( frame_idx) , self . compute_top_user_relevant_frame( ) ) ;
179+ self . top_user_relevant_frame = Some ( frame_idx) ;
180+ }
181+
182+ /// Returns the topmost frame that is considered user-relevant, or the
183+ /// top of the stack if there is no such frame, or `None` if the stack is empty.
184+ pub fn top_user_relevant_frame ( & self ) -> Option < usize > {
185+ debug_assert_eq ! ( self . top_user_relevant_frame, self . compute_top_user_relevant_frame( ) ) ;
186+ // This can be called upon creation of an allocation. We create allocations while setting up
187+ // parts of the Rust runtime when we do not have any stack frames yet, so we need to handle
188+ // empty stacks.
189+ self . top_user_relevant_frame . or_else ( || self . stack . len ( ) . checked_sub ( 1 ) )
190+ }
150191}
151192
152193impl < ' mir , ' tcx > std:: fmt:: Debug for Thread < ' mir , ' tcx > {
@@ -167,6 +208,7 @@ impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> {
167208 state : ThreadState :: Enabled ,
168209 thread_name : None ,
169210 stack : Vec :: new ( ) ,
211+ top_user_relevant_frame : None ,
170212 join_status : ThreadJoinStatus :: Joinable ,
171213 panic_payload : None ,
172214 last_error : None ,
@@ -184,8 +226,15 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
184226
185227impl VisitTags for Thread < ' _ , ' _ > {
186228 fn visit_tags ( & self , visit : & mut dyn FnMut ( SbTag ) ) {
187- let Thread { panic_payload, last_error, stack, state : _, thread_name : _, join_status : _ } =
188- self ;
229+ let Thread {
230+ panic_payload,
231+ last_error,
232+ stack,
233+ top_user_relevant_frame : _,
234+ state : _,
235+ thread_name : _,
236+ join_status : _,
237+ } = self ;
189238
190239 panic_payload. visit_tags ( visit) ;
191240 last_error. visit_tags ( visit) ;
@@ -414,7 +463,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
414463 }
415464
416465 /// Get a shared borrow of the currently active thread.
417- fn active_thread_ref ( & self ) -> & Thread < ' mir , ' tcx > {
466+ pub fn active_thread_ref ( & self ) -> & Thread < ' mir , ' tcx > {
418467 & self . threads [ self . active_thread ]
419468 }
420469
0 commit comments