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