Bug 1493260 - Revise the SMDOC comment in js/src/jit/Bailouts.h. r=tcampbell
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 07 Dec 2018 14:40:20 +0000
changeset 508822 d341e1dcdfeff2ba187ada77af2e77f4b430604d
parent 508821 a7f0acc59459f4a2be7d2b5edd8436081b5bbcf2
child 508823 ffb2ef47c53699f188ab5f316f3285f8a012e743
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1493260
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1493260 - Revise the SMDOC comment in js/src/jit/Bailouts.h. r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D13742
js/src/jit/Bailouts.h
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -13,71 +13,102 @@
 #include "jit/JSJitFrameIter.h"
 #include "vm/Stack.h"
 
 namespace js {
 namespace jit {
 
 // [SMDOC] IonMonkey Bailouts
 //
-// A "bailout" is a condition in which we need to recover a baseline frame from
-// an IonFrame. Bailouts can happen for the following reasons:
-//   (1) A deoptimization guard, for example, an add overflows or a type check
-//       fails.
-//   (2) A check or assumption held by the JIT is invalidated by the VM, and
-//       JIT code must be thrown away. This includes the GC possibly deciding
-//       to evict live JIT code, or a Type Inference reflow.
+// A "bailout" is the process of recovering a baseline frame from an IonFrame.
+// Bailouts are implemented in js::jit::BailoutIonToBaseline, which has the
+// following callers:
+//
+// *   js::jit::Bailout - This is used when a guard fails in the Ion code
+//     itself; for example, an LGuardShape fails or an LAddI overflows. See
+//     callers of CodeGenerator::bailoutFrom() for more examples.
+//
+// *   js::jit::ExceptionHandlerBailout - Something called from Ion code
+//     failed. Ion doesn't implement `catch`; it handles all exceptions by
+//     bailing out.
 //
-// Note that bailouts as described here do not include normal Ion frame
-// inspection, for example, if an exception must be built or the GC needs to
-// scan an Ion frame for gcthings.
+// *   js::jit::InvalidationBailout - We returned to Ion code that was
+//     invalidated while it was on the stack. See "OSI" below. Ion code can be
+//     invalidated for several reasons: when GC evicts Ion code to save memory,
+//     for example, or when assumptions baked into the jitted code are
+//     invalidated by the VM (see callers of IonBuilder::constraints()).
+//
+// (Some stack inspection can be done without bailing out, including GC stack
+// marking, Error object construction, and Gecko profiler sampling.)
 //
-// The second type of bailout needs a different name - "deoptimization" or
-// "deep bailout". Here we are concerned with eager (or maybe "shallow")
-// bailouts, that happen from JIT code. These happen from guards, like:
+// Consider the first case. When an Ion guard fails, we can't continue in
+// Ion. There's no IC fallback case coming to save us; we've got a broken
+// assumption baked into the code we're running. So we jump to an out-of-line
+// code path that's responsible for abandoning Ion execution and resuming in
+// baseline: the bailout path.
 //
-//  cmp [obj + shape], 0x50M37TH1NG
-//  jmp _bailout
+// We were in the midst of optimized Ion code, so bits of program state may be
+// in registers or spilled to the native stack; values may be unboxed; some
+// objects may have been optimized away; thanks to inlining, whole call frames
+// may be missing. The bailout path must put all these pieces back together
+// into the structure the baseline code expects.
 //
-// The bailout target needs to somehow translate the Ion frame (whose state
-// will differ at each program point) to a baseline frame. This state is
-// captured into the IonScript's snapshot buffer, and for each bailout we know
-// which snapshot corresponds to its state.
+// The data structure that makes this possible is called a *snapshot*.
+// Snapshots are created during Ion codegen and associated with the IonScript;
+// they tell how to recover each value in a BaselineFrame from the current
+// machine state at a given point in the Ion JIT code. This is potentially
+// different at every place in an Ion script where we might bail out. (See
+// Snapshots.h.)
+//
+// The bailout path performs roughly the following steps:
+//
+// 1.  Push a snapshot index and the frame size to the native stack.
+// 2.  Spill all registers.
+// 3.  Call js::jit::Bailout to reconstruct the baseline frame(s).
+// 4.  memmove() those to the right place on the native stack.
+// 5.  Jump to baseline code.
+//
+// (This last step requires baseline JIT code to have an entry point at each pc
+// where an eventual Ion guard may be inserted.)
 //
-// Roughly, the following needs to happen at the bailout target.
-//   (1) Move snapshot ID into a known stack location (registers cannot be
-//       mutated).
-//   (2) Spill all registers to the stack.
-//   (3) Call a Bailout() routine, whose argument is the stack pointer.
-//   (4) Bailout() will find the IonScript on the stack, use the snapshot ID
-//       to find the structure of the frame, and then use the stack and spilled
-//       registers to perform frame conversion.
-//   (5) Bailout() returns, and the JIT must immediately return to the
-//       baseline JIT code (all frames are converted at once).
+// When C++ code invalidates Ion code, we do on-stack invalidation, or OSI, to
+// arrange for every affected Ion frame on the stack to bail out as soon as
+// control returns to it. OSI patches every instruction in the JIT code that's
+// at a return address currently on the stack. See InvalidateActivation.
+//
+//
+// ## Bailout path implementation details
+//
+// Ion code has a lot of guards, so each bailout path must be small. Steps 2
+// and 3 above are therefore implemented by a shared per-Runtime trampoline,
+// rt->jitRuntime()->getGenericBailoutHandler().
 //
-// (2) and (3) are implemented by a trampoline held in the compartment.
-// Naively, we could implement (1) like:
+// Naively, we could implement step 1 like:
 //
-//   _bailout_ID_1:
-//     push 1
-//     jmp _global_bailout_handler
-//   _bailout_ID_2:
-//     push 2
-//     jmp _global_bailout_handler
+//     _bailout_ID_1:
+//       push 1
+//       jmp _deopt
+//     _bailout_ID_2:
+//       push 2
+//       jmp _deopt
+//     ...
+//     _deopt:
+//       push imm(FrameSize)
+//       call _global_bailout_handler
 //
 // This takes about 10 extra bytes per guard. On some platforms, we can reduce
 // this overhead to 4 bytes by creating a global jump table, shared again in
 // the compartment:
 //
-//     call _global_bailout_handler
-//     call _global_bailout_handler
-//     call _global_bailout_handler
-//     call _global_bailout_handler
-//      ...
-//    _global_bailout_handler:
+//       call _global_bailout_handler
+//       call _global_bailout_handler
+//       call _global_bailout_handler
+//       call _global_bailout_handler
+//       ...
+//     _global_bailout_handler:
 //
 // In the bailout handler, we can recompute which entry in the table was
 // selected by subtracting the return addressed pushed by the call, from the
 // start of the table, and then dividing by the size of a (call X) entry in the
 // table. This gives us a number in [0, TableSize), which we call a
 // "BailoutId".
 //
 // Then, we can provide a per-script mapping from BailoutIds to snapshots,