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