Skip to content

Commit 01c11f9

Browse files
RalfJungAaron1011
authored andcommitted
avoid the loop in unwinding stack popping
1 parent 499eb5d commit 01c11f9

File tree

5 files changed

+85
-107
lines changed

5 files changed

+85
-107
lines changed

src/librustc_mir/interpret/eval_context.rs

Lines changed: 71 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
6060
/// The span of the call site.
6161
pub span: source_map::Span,
6262

63+
/// Extra data for the machine.
64+
pub extra: Extra,
65+
6366
////////////////////////////////////////////////////////////////////////////////
6467
// Return place and locals
6568
////////////////////////////////////////////////////////////////////////////////
@@ -82,13 +85,12 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
8285
////////////////////////////////////////////////////////////////////////////////
8386
/// The block that is currently executed (or will be executed after the above call stacks
8487
/// return).
85-
pub block: mir::BasicBlock,
88+
/// If this is `None`, we are unwinding and this function doesn't need any clean-up.
89+
/// Just continue the same as with
90+
pub block: Option<mir::BasicBlock>,
8691

8792
/// The index of the currently evaluated statement.
8893
pub stmt: usize,
89-
90-
/// Extra data for the machine.
91-
pub extra: Extra,
9294
}
9395

9496
#[derive(Clone, Eq, PartialEq, Debug)] // Miri debug-prints these
@@ -491,7 +493,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
491493
let extra = M::stack_push(self)?;
492494
self.stack.push(Frame {
493495
body,
494-
block: mir::START_BLOCK,
496+
block: Some(mir::START_BLOCK),
495497
return_to_block,
496498
return_place,
497499
// empty local array, we fill it in below, after we are inside the stack frame and
@@ -549,51 +551,75 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
549551
}
550552
}
551553

552-
pub(super) fn pop_stack_frame_internal(
554+
pub(super) fn pop_stack_frame(
553555
&mut self,
554556
unwinding: bool
555-
) -> InterpResult<'tcx, (StackPopCleanup, StackPopInfo)> {
557+
) -> InterpResult<'tcx> {
556558
info!("LEAVING({}) {} (unwinding = {})",
557559
self.cur_frame(), self.frame().instance, unwinding);
558560

561+
// Sanity check `unwinding`.
562+
assert_eq!(
563+
unwinding,
564+
match self.frame().block {
565+
None => true,
566+
Some(block) => self.body().basic_blocks()[block].is_cleanup
567+
}
568+
);
569+
559570
::log_settings::settings().indentation -= 1;
560571
let frame = self.stack.pop().expect(
561572
"tried to pop a stack frame, but there were none",
562573
);
563574
let stack_pop_info = M::stack_pop(self, frame.extra)?;
575+
match (unwinding, stack_pop_info) {
576+
(true, StackPopInfo::StartUnwinding) =>
577+
bug!("Attempted to start unwinding while already unwinding!"),
578+
(false, StackPopInfo::StopUnwinding) =>
579+
bug!("Attempted to stop unwinding while there is no unwinding!"),
580+
_ => {}
581+
}
564582

565-
// Abort early if we do not want to clean up: We also avoid validation in that case,
583+
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
584+
// In that case, we return early. We also avoid validation in that case,
566585
// because this is CTFE and the final value will be thoroughly validated anyway.
567-
match frame.return_to_block {
568-
StackPopCleanup::Goto{ .. } => {},
586+
let cleanup = unwinding || match frame.return_to_block {
587+
StackPopCleanup::Goto{ .. } => true,
569588
StackPopCleanup::None { cleanup, .. } => {
570-
assert!(!unwinding, "Encountered StackPopCleanup::None while unwinding");
571-
572-
if !cleanup {
573-
assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked");
574-
// Leak the locals, skip validation.
575-
return Ok((frame.return_to_block, stack_pop_info));
576-
}
589+
cleanup
577590
}
591+
};
592+
if !cleanup {
593+
assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked");
594+
// Leak the locals, skip validation.
595+
return Ok(());
578596
}
579-
// Deallocate all locals that are backed by an allocation.
597+
598+
// Cleanup: deallocate all locals that are backed by an allocation.
580599
for local in frame.locals {
581600
self.deallocate_local(local.value)?;
582601
}
583602

584-
// If we're popping frames due to unwinding, and we didn't just exit
585-
// unwinding, we skip a bunch of validation and cleanup logic (including
586-
// jumping to the regular return block specified in the StackPopCleanup)
587-
let cur_unwinding = unwinding && stack_pop_info != StackPopInfo::StopUnwinding;
603+
// Now where do we jump next?
588604

589-
info!("StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
605+
// Determine if we leave this function normally or via unwinding.
606+
let cur_unwinding = unwinding && stack_pop_info != StackPopInfo::StopUnwinding;
607+
trace!("StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}",
590608
frame.return_to_block, stack_pop_info, cur_unwinding);
591-
592-
593-
// When we're popping a stack frame for unwinding purposes,
594-
// we don't care at all about returning-related stuff (i.e. return_place
595-
// and return_to_block), because we're not performing a return from this frame.
596-
if !cur_unwinding {
609+
if cur_unwinding {
610+
// Follow the unwind edge.
611+
match frame.return_to_block {
612+
StackPopCleanup::Goto { unwind, .. } => {
613+
let next_frame = self.frame_mut();
614+
// If `unwind` is `None`, we'll leave that function immediately again.
615+
next_frame.block = unwind;
616+
next_frame.stmt = 0;
617+
},
618+
StackPopCleanup::None { .. } =>
619+
bug!("Encountered StackPopCleanup::None while unwinding"),
620+
}
621+
} else {
622+
// Follow the normal return edge.
597623
// Validate the return value. Do this after deallocating so that we catch dangling
598624
// references.
599625
if let Some(return_place) = frame.return_place {
@@ -625,70 +651,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
625651
}
626652
}
627653

628-
629-
Ok((frame.return_to_block, stack_pop_info))
630-
}
631-
632-
pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> {
633-
let (mut cleanup, mut stack_pop_info) = self.pop_stack_frame_internal(unwinding)?;
634-
635-
// There are two cases where we want to unwind the stack:
636-
// * The caller explicitly told us (i.e. we hit a Resume terminator)
637-
// * The machine indicated that we've just started unwinding (i.e.
638-
// a panic has just occured)
639-
if unwinding || stack_pop_info == StackPopInfo::StartUnwinding {
640-
trace!("unwinding: starting stack unwind...");
641-
// Overwrite our current stack_pop_info, so that the check
642-
// below doesn't fail.
643-
stack_pop_info = StackPopInfo::Normal;
644-
// There are three posible ways that we can exit the loop:
645-
// 1) We find an unwind block - we jump to it to allow cleanup
646-
// to occur for that frame
647-
// 2) pop_stack_frame_internal reports that we're no longer unwinding
648-
// - this means that the panic has been caught, and that execution
649-
// should continue as normal
650-
// 3) We pop all of our frames off the stack - this should never happen.
651-
while !self.stack.is_empty() {
652-
match stack_pop_info {
653-
// We tried to start unwinding while we were already
654-
// unwinding. Note that this **is not** the same thing
655-
// as a double panic, which will be intercepted by
656-
// libcore/libstd before we actually attempt to unwind.
657-
StackPopInfo::StartUnwinding => {
658-
throw_ub_format!("Attempted to start unwinding while already unwinding!");
659-
},
660-
StackPopInfo::StopUnwinding => {
661-
trace!("unwinding: no longer unwinding!");
662-
break;
663-
}
664-
StackPopInfo::Normal => {}
665-
}
666-
667-
match cleanup {
668-
StackPopCleanup::Goto { unwind, .. } if unwind.is_some() => {
669-
670-
info!("unwind: found cleanup block {:?}", unwind);
671-
self.goto_block(unwind)?;
672-
break;
673-
},
674-
_ => {}
675-
}
676-
677-
info!("unwinding: popping frame!");
678-
let res = self.pop_stack_frame_internal(true)?;
679-
cleanup = res.0;
680-
stack_pop_info = res.1;
681-
}
682-
if self.stack.is_empty() {
683-
// We should never get here:
684-
// The 'start_fn' lang item should always install a panic handler
685-
throw_ub!(Unreachable);
686-
}
687-
688-
}
689-
690654
if self.stack.len() > 0 {
691-
info!("CONTINUING({}) {}", self.cur_frame(), self.frame().instance);
655+
info!("CONTINUING({}) {} (unwinding = {})",
656+
self.cur_frame(), self.frame().instance, cur_unwinding);
692657
}
693658

694659
Ok(())
@@ -833,16 +798,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
833798
} else {
834799
last_span = Some(span);
835800
}
836-
let block = &body.basic_blocks()[block];
837-
let source_info = if stmt < block.statements.len() {
838-
block.statements[stmt].source_info
839-
} else {
840-
block.terminator().source_info
841-
};
842-
let lint_root = match body.source_scope_local_data {
843-
mir::ClearCrossCrate::Set(ref ivs) => Some(ivs[source_info.scope].lint_root),
844-
mir::ClearCrossCrate::Clear => None,
845-
};
801+
802+
let lint_root = block.and_then(|block| {
803+
let block = &body.basic_blocks()[block];
804+
let source_info = if stmt < block.statements.len() {
805+
block.statements[stmt].source_info
806+
} else {
807+
block.terminator().source_info
808+
};
809+
match body.source_scope_local_data {
810+
mir::ClearCrossCrate::Set(ref ivs) => Some(ivs[source_info.scope].lint_root),
811+
mir::ClearCrossCrate::Clear => None,
812+
}
813+
});
814+
846815
frames.push(FrameInfo { call_site: span, instance, lint_root });
847816
}
848817
trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);

src/librustc_mir/interpret/machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use super::{
1818

1919
/// Data returned by Machine::stack_pop,
2020
/// to provide further control over the popping of the stack frame
21-
#[derive(Eq, PartialEq, Debug)]
21+
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
2222
pub enum StackPopInfo {
2323
/// Indicates that we have just started unwinding
2424
/// as the result of panic

src/librustc_mir/interpret/snapshot.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ struct FrameSnapshot<'a, 'tcx> {
326326
return_to_block: &'a StackPopCleanup,
327327
return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
328328
locals: IndexVec<mir::Local, LocalValue<(), AllocIdSnapshot<'a>>>,
329-
block: &'a mir::BasicBlock,
329+
block: Option<mir::BasicBlock>,
330330
stmt: usize,
331331
}
332332

@@ -364,7 +364,7 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
364364
instance: *instance,
365365
span: *span,
366366
return_to_block,
367-
block,
367+
block: *block,
368368
stmt: *stmt,
369369
return_place: return_place.map(|r| r.snapshot(ctx)),
370370
locals: locals.iter().map(|local| local.snapshot(ctx)).collect(),

src/librustc_mir/interpret/step.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
4949
return Ok(false);
5050
}
5151

52-
let block = self.frame().block;
52+
let block = match self.frame().block {
53+
Some(block) => block,
54+
None => {
55+
// We are unwinding and this fn has no cleanup code.
56+
// Just go on unwinding.
57+
trace!("unwinding: skipping frame");
58+
self.pop_stack_frame(/* unwinding */ true)?;
59+
return Ok(true)
60+
}
61+
};
5362
let stmt_id = self.frame().stmt;
5463
let body = self.body();
5564
let basic_block = &body.basic_blocks()[block];

src/librustc_mir/interpret/terminator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1515
#[inline]
1616
pub fn goto_block(&mut self, target: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
1717
if let Some(target) = target {
18-
self.frame_mut().block = target;
18+
self.frame_mut().block = Some(target);
1919
self.frame_mut().stmt = 0;
2020
Ok(())
2121
} else {

0 commit comments

Comments
 (0)