1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef vm_JitActivation_h
8 #define vm_JitActivation_h
9 
10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
11 #include "mozilla/Atomics.h"     // mozilla::Atomic, mozilla::Relaxed
12 #include "mozilla/Maybe.h"       // mozilla::Maybe
13 
14 #include <stddef.h>  // size_t
15 #include <stdint.h>  // uint8_t, uint32_t, uintptr_t
16 
17 #include "jstypes.h"  // JS_PUBLIC_API
18 
19 #include "jit/IonTypes.h"        // CHECK_OSIPOINT_REGISTERS
20 #include "jit/JSJitFrameIter.h"  // js::jit::{JSJitFrameIter,RInstructionResults}
21 #ifdef CHECK_OSIPOINT_REGISTERS
22 #  include "jit/Registers.h"  // js::jit::RegisterDump
23 #endif
24 #include "jit/RematerializedFrame.h"  // js::jit::RematerializedFrame
25 #include "js/GCVector.h"              // JS::GCVector
26 #include "js/HashTable.h"             // js::HashMap
27 #include "js/UniquePtr.h"             // js::UniquePtr
28 #include "vm/Activation.h"            // js::Activation
29 #include "wasm/WasmCodegenTypes.h"    // js::wasm::TrapData
30 #include "wasm/WasmConstants.h"       // js::wasm::Trap
31 #include "wasm/WasmFrame.h"           // js::wasm::Frame
32 #include "wasm/WasmFrameIter.h"  // js::wasm::{ExitReason,RegisterState,WasmFrameIter}
33 
34 struct JS_PUBLIC_API JSContext;
35 class JS_PUBLIC_API JSTracer;
36 
37 namespace js {
38 
39 namespace jit {
40 
41 class BailoutFrameInfo;
42 
43 // A JitActivation is used for frames running in Baseline or Ion.
44 class JitActivation : public Activation {
45   // If Baseline, Ion or Wasm code is on the stack, and has called into C++,
46   // this will be aligned to an ExitFrame. The last bit indicates if it's a
47   // wasm frame (bit set to wasm::ExitOrJitEntryFPTag) or not
48   // (bit set to ~wasm::ExitOrJitEntryFPTag).
49   uint8_t* packedExitFP_;
50 
51   // When hasWasmExitFP(), encodedWasmExitReason_ holds ExitReason.
52   uint32_t encodedWasmExitReason_;
53 
54   JitActivation* prevJitActivation_;
55 
56   // Rematerialized Ion frames which has info copied out of snapshots. Maps
57   // frame pointers (i.e. packedExitFP_) to a vector of rematerializations of
58   // all inline frames associated with that frame.
59   //
60   // This table is lazily initialized by calling getRematerializedFrame.
61   using RematerializedFrameVector =
62       JS::GCVector<js::UniquePtr<RematerializedFrame>>;
63   using RematerializedFrameTable =
64       js::HashMap<uint8_t*, RematerializedFrameVector>;
65   js::UniquePtr<RematerializedFrameTable> rematerializedFrames_;
66 
67   // This vector is used to remember the outcome of the evaluation of recover
68   // instructions.
69   //
70   // RInstructionResults are appended into this vector when Snapshot values
71   // have to be read, or when the evaluation has to run before some mutating
72   // code.  Each RInstructionResults belongs to one frame which has to bailout
73   // as soon as we get back to it.
74   using IonRecoveryMap = Vector<RInstructionResults, 1>;
75   IonRecoveryMap ionRecovery_;
76 
77   // If we are bailing out from Ion, then this field should be a non-null
78   // pointer which references the BailoutFrameInfo used to walk the inner
79   // frames. This field is used for all newly constructed JSJitFrameIters to
80   // read the innermost frame information from this bailout data instead of
81   // reading it from the stack.
82   BailoutFrameInfo* bailoutData_;
83 
84   // When profiling is enabled, these fields will be updated to reflect the
85   // last pushed frame for this activation, and if that frame has been
86   // left for a call, the native code site of the call.
87   mozilla::Atomic<void*, mozilla::Relaxed> lastProfilingFrame_;
88   mozilla::Atomic<void*, mozilla::Relaxed> lastProfilingCallSite_;
89   static_assert(sizeof(mozilla::Atomic<void*, mozilla::Relaxed>) ==
90                     sizeof(void*),
91                 "Atomic should have same memory format as underlying type.");
92 
93   // When wasm traps, the signal handler records some data for unwinding
94   // purposes. Wasm code can't trap reentrantly.
95   mozilla::Maybe<wasm::TrapData> wasmTrapData_;
96 
97   void clearRematerializedFrames();
98 
99 #ifdef CHECK_OSIPOINT_REGISTERS
100  protected:
101   // Used to verify that live registers don't change between a VM call and
102   // the OsiPoint that follows it. Protected to silence Clang warning.
103   uint32_t checkRegs_ = 0;
104   RegisterDump regs_;
105 #endif
106 
107  public:
108   explicit JitActivation(JSContext* cx);
109   ~JitActivation();
110 
isProfiling()111   bool isProfiling() const {
112     // All JitActivations can be profiled.
113     return true;
114   }
115 
prevJitActivation()116   JitActivation* prevJitActivation() const { return prevJitActivation_; }
offsetOfPrevJitActivation()117   static size_t offsetOfPrevJitActivation() {
118     return offsetof(JitActivation, prevJitActivation_);
119   }
120 
hasExitFP()121   bool hasExitFP() const { return !!packedExitFP_; }
jsOrWasmExitFP()122   uint8_t* jsOrWasmExitFP() const {
123     if (hasWasmExitFP()) {
124       return wasm::Frame::toJitEntryCaller(packedExitFP_);
125     }
126     return packedExitFP_;
127   }
offsetOfPackedExitFP()128   static size_t offsetOfPackedExitFP() {
129     return offsetof(JitActivation, packedExitFP_);
130   }
131 
hasJSExitFP()132   bool hasJSExitFP() const { return !hasWasmExitFP(); }
133 
jsExitFP()134   uint8_t* jsExitFP() const {
135     MOZ_ASSERT(hasJSExitFP());
136     return packedExitFP_;
137   }
setJSExitFP(uint8_t * fp)138   void setJSExitFP(uint8_t* fp) { packedExitFP_ = fp; }
139 
140 #ifdef CHECK_OSIPOINT_REGISTERS
setCheckRegs(bool check)141   void setCheckRegs(bool check) { checkRegs_ = check; }
offsetOfCheckRegs()142   static size_t offsetOfCheckRegs() {
143     return offsetof(JitActivation, checkRegs_);
144   }
offsetOfRegs()145   static size_t offsetOfRegs() { return offsetof(JitActivation, regs_); }
146 #endif
147 
148   // Look up a rematerialized frame keyed by the fp, rematerializing the
149   // frame if one doesn't already exist. A frame can only be rematerialized
150   // if an IonFrameIterator pointing to the nearest uninlined frame can be
151   // provided, as values need to be read out of snapshots.
152   //
153   // The inlineDepth must be within bounds of the frame pointed to by iter.
154   RematerializedFrame* getRematerializedFrame(JSContext* cx,
155                                               const JSJitFrameIter& iter,
156                                               size_t inlineDepth = 0);
157 
158   // Look up a rematerialized frame by the fp. If inlineDepth is out of
159   // bounds of what has been rematerialized, nullptr is returned.
160   RematerializedFrame* lookupRematerializedFrame(uint8_t* top,
161                                                  size_t inlineDepth = 0);
162 
163   // Remove all rematerialized frames associated with the fp top from the
164   // Debugger.
165   void removeRematerializedFramesFromDebugger(JSContext* cx, uint8_t* top);
166 
167   bool hasRematerializedFrame(uint8_t* top, size_t inlineDepth = 0) {
168     return !!lookupRematerializedFrame(top, inlineDepth);
169   }
170 
171   // Remove a previous rematerialization by fp.
172   void removeRematerializedFrame(uint8_t* top);
173 
174   void traceRematerializedFrames(JSTracer* trc);
175 
176   // Register the results of on Ion frame recovery.
177   bool registerIonFrameRecovery(RInstructionResults&& results);
178 
179   // Return the pointer to the Ion frame recovery, if it is already registered.
180   RInstructionResults* maybeIonFrameRecovery(JitFrameLayout* fp);
181 
182   // If an Ion frame recovery exists for the |fp| frame exists, then remove it
183   // from the activation.
184   void removeIonFrameRecovery(JitFrameLayout* fp);
185 
186   void traceIonRecovery(JSTracer* trc);
187 
188   // Return the bailout information if it is registered.
bailoutData()189   const BailoutFrameInfo* bailoutData() const { return bailoutData_; }
190 
191   // Register the bailout data when it is constructed.
192   void setBailoutData(BailoutFrameInfo* bailoutData);
193 
194   // Unregister the bailout data when the frame is reconstructed.
195   void cleanBailoutData();
196 
offsetOfLastProfilingFrame()197   static size_t offsetOfLastProfilingFrame() {
198     return offsetof(JitActivation, lastProfilingFrame_);
199   }
lastProfilingFrame()200   void* lastProfilingFrame() { return lastProfilingFrame_; }
setLastProfilingFrame(void * ptr)201   void setLastProfilingFrame(void* ptr) { lastProfilingFrame_ = ptr; }
202 
offsetOfLastProfilingCallSite()203   static size_t offsetOfLastProfilingCallSite() {
204     return offsetof(JitActivation, lastProfilingCallSite_);
205   }
lastProfilingCallSite()206   void* lastProfilingCallSite() { return lastProfilingCallSite_; }
setLastProfilingCallSite(void * ptr)207   void setLastProfilingCallSite(void* ptr) { lastProfilingCallSite_ = ptr; }
208 
209   // WebAssembly specific attributes.
hasWasmExitFP()210   bool hasWasmExitFP() const {
211     return wasm::Frame::isExitOrJitEntryFP(packedExitFP_);
212   }
wasmExitFP()213   wasm::Frame* wasmExitFP() const {
214     MOZ_ASSERT(hasWasmExitFP());
215     return reinterpret_cast<wasm::Frame*>(
216         wasm::Frame::toJitEntryCaller(packedExitFP_));
217   }
wasmExitTls()218   wasm::TlsData* wasmExitTls() const {
219     return wasm::GetNearestEffectiveTls(wasmExitFP());
220   }
setWasmExitFP(const wasm::Frame * fp)221   void setWasmExitFP(const wasm::Frame* fp) {
222     if (fp) {
223       MOZ_ASSERT(!wasm::Frame::isExitOrJitEntryFP(fp));
224       packedExitFP_ = wasm::Frame::addExitOrJitEntryFPTag(fp);
225       MOZ_ASSERT(hasWasmExitFP());
226     } else {
227       packedExitFP_ = nullptr;
228     }
229   }
wasmExitReason()230   wasm::ExitReason wasmExitReason() const {
231     MOZ_ASSERT(hasWasmExitFP());
232     return wasm::ExitReason::Decode(encodedWasmExitReason_);
233   }
offsetOfEncodedWasmExitReason()234   static size_t offsetOfEncodedWasmExitReason() {
235     return offsetof(JitActivation, encodedWasmExitReason_);
236   }
237 
238   void startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset,
239                      const wasm::RegisterState& state);
240   void finishWasmTrap();
isWasmTrapping()241   bool isWasmTrapping() const { return !!wasmTrapData_; }
wasmTrapData()242   const wasm::TrapData& wasmTrapData() { return *wasmTrapData_; }
243 };
244 
245 // A filtering of the ActivationIterator to only stop at JitActivations.
246 class JitActivationIterator : public ActivationIterator {
settle()247   void settle() {
248     while (!done() && !activation_->isJit()) {
249       ActivationIterator::operator++();
250     }
251   }
252 
253  public:
JitActivationIterator(JSContext * cx)254   explicit JitActivationIterator(JSContext* cx) : ActivationIterator(cx) {
255     settle();
256   }
257 
258   JitActivationIterator& operator++() {
259     ActivationIterator::operator++();
260     settle();
261     return *this;
262   }
263 };
264 
265 }  // namespace jit
266 
267 }  // namespace js
268 
269 #endif  // vm_JitActivation_h
270