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 #include "vm/JitActivation.h"
8
9 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_RELEASE_ASSERT
10
11 #include <stddef.h> // size_t
12 #include <stdint.h> // uint8_t, uint32_t
13 #include <utility> // std::move
14
15 #include "debugger/DebugAPI.h" // js::DebugAPI
16 #include "jit/RematerializedFrame.h" // js::jit::RematerializedFrame
17 #include "js/AllocPolicy.h" // js::ReportOutOfMemory
18 #include "vm/EnvironmentObject.h" // js::DebugEnvironments
19 #include "vm/FrameIter.h" // js::jit::InlineFrameIterator
20 #include "vm/JSContext.h" // JSContext
21 #include "vm/Realm.h" // js::AutoRealmUnchecked
22 #include "wasm/WasmCode.h" // js::wasm::Code
23 #include "wasm/WasmConstants.h" // js::wasm::Trap
24 #include "wasm/WasmFrameIter.h" // js::wasm::{RegisterState,StartUnwinding,UnwindState}
25 #include "wasm/WasmInstance.h" // js::wasm::Instance
26 #include "wasm/WasmProcess.h" // js::wasm::LookupCode
27 #include "wasm/WasmTlsData.h" // js::wasm::TlsData
28
29 #include "vm/Realm-inl.h" // js::~AutoRealm
30
31 class JS_PUBLIC_API JSTracer;
32
JitActivation(JSContext * cx)33 js::jit::JitActivation::JitActivation(JSContext* cx)
34 : Activation(cx, Jit),
35 packedExitFP_(nullptr),
36 encodedWasmExitReason_(0),
37 prevJitActivation_(cx->jitActivation),
38 rematerializedFrames_(),
39 ionRecovery_(cx),
40 bailoutData_(nullptr),
41 lastProfilingFrame_(nullptr),
42 lastProfilingCallSite_(nullptr) {
43 cx->jitActivation = this;
44 registerProfiling();
45 }
46
~JitActivation()47 js::jit::JitActivation::~JitActivation() {
48 if (isProfiling()) {
49 unregisterProfiling();
50 }
51 cx_->jitActivation = prevJitActivation_;
52
53 // All reocvered value are taken from activation during the bailout.
54 MOZ_ASSERT(ionRecovery_.empty());
55
56 // The BailoutFrameInfo should have unregistered itself from the
57 // JitActivations.
58 MOZ_ASSERT(!bailoutData_);
59
60 // Traps get handled immediately.
61 MOZ_ASSERT(!isWasmTrapping());
62
63 clearRematerializedFrames();
64 }
65
setBailoutData(jit::BailoutFrameInfo * bailoutData)66 void js::jit::JitActivation::setBailoutData(
67 jit::BailoutFrameInfo* bailoutData) {
68 MOZ_ASSERT(!bailoutData_);
69 bailoutData_ = bailoutData;
70 }
71
cleanBailoutData()72 void js::jit::JitActivation::cleanBailoutData() {
73 MOZ_ASSERT(bailoutData_);
74 bailoutData_ = nullptr;
75 }
76
removeRematerializedFrame(uint8_t * top)77 void js::jit::JitActivation::removeRematerializedFrame(uint8_t* top) {
78 if (!rematerializedFrames_) {
79 return;
80 }
81
82 if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
83 rematerializedFrames_->remove(p);
84 }
85 }
86
clearRematerializedFrames()87 void js::jit::JitActivation::clearRematerializedFrames() {
88 if (!rematerializedFrames_) {
89 return;
90 }
91
92 for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty();
93 e.popFront()) {
94 e.removeFront();
95 }
96 }
97
getRematerializedFrame(JSContext * cx,const JSJitFrameIter & iter,size_t inlineDepth)98 js::jit::RematerializedFrame* js::jit::JitActivation::getRematerializedFrame(
99 JSContext* cx, const JSJitFrameIter& iter, size_t inlineDepth) {
100 MOZ_ASSERT(iter.activation() == this);
101 MOZ_ASSERT(iter.isIonScripted());
102
103 if (!rematerializedFrames_) {
104 rematerializedFrames_ = cx->make_unique<RematerializedFrameTable>(cx);
105 if (!rematerializedFrames_) {
106 return nullptr;
107 }
108 }
109
110 uint8_t* top = iter.fp();
111 RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
112 if (!p) {
113 RematerializedFrameVector frames(cx);
114
115 // The unit of rematerialization is an uninlined frame and its inlined
116 // frames. Since inlined frames do not exist outside of snapshots, it
117 // is impossible to synchronize their rematerialized copies to
118 // preserve identity. Therefore, we always rematerialize an uninlined
119 // frame and all its inlined frames at once.
120 InlineFrameIterator inlineIter(cx, &iter);
121 MaybeReadFallback recover(cx, this, &iter);
122
123 // Frames are often rematerialized with the cx inside a Debugger's
124 // realm. To recover slots and to create CallObjects, we need to
125 // be in the script's realm.
126 AutoRealmUnchecked ar(cx, iter.script()->realm());
127
128 if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter,
129 recover, frames)) {
130 return nullptr;
131 }
132
133 if (!rematerializedFrames_->add(p, top, std::move(frames))) {
134 ReportOutOfMemory(cx);
135 return nullptr;
136 }
137
138 // See comment in unsetPrevUpToDateUntil.
139 DebugEnvironments::unsetPrevUpToDateUntil(cx,
140 p->value()[inlineDepth].get());
141 }
142
143 return p->value()[inlineDepth].get();
144 }
145
lookupRematerializedFrame(uint8_t * top,size_t inlineDepth)146 js::jit::RematerializedFrame* js::jit::JitActivation::lookupRematerializedFrame(
147 uint8_t* top, size_t inlineDepth) {
148 if (!rematerializedFrames_) {
149 return nullptr;
150 }
151 if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
152 return inlineDepth < p->value().length() ? p->value()[inlineDepth].get()
153 : nullptr;
154 }
155 return nullptr;
156 }
157
removeRematerializedFramesFromDebugger(JSContext * cx,uint8_t * top)158 void js::jit::JitActivation::removeRematerializedFramesFromDebugger(
159 JSContext* cx, uint8_t* top) {
160 // Ion bailout can fail due to overrecursion and OOM. In such cases we
161 // cannot honor any further Debugger hooks on the frame, and need to
162 // ensure that its Debugger.Frame entry is cleaned up.
163 if (!cx->realm()->isDebuggee() || !rematerializedFrames_) {
164 return;
165 }
166 if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
167 for (uint32_t i = 0; i < p->value().length(); i++) {
168 DebugAPI::handleUnrecoverableIonBailoutError(cx, p->value()[i].get());
169 }
170 rematerializedFrames_->remove(p);
171 }
172 }
173
traceRematerializedFrames(JSTracer * trc)174 void js::jit::JitActivation::traceRematerializedFrames(JSTracer* trc) {
175 if (!rematerializedFrames_) {
176 return;
177 }
178 for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty();
179 e.popFront()) {
180 e.front().value().trace(trc);
181 }
182 }
183
registerIonFrameRecovery(RInstructionResults && results)184 bool js::jit::JitActivation::registerIonFrameRecovery(
185 RInstructionResults&& results) {
186 // Check that there is no entry in the vector yet.
187 MOZ_ASSERT(!maybeIonFrameRecovery(results.frame()));
188 if (!ionRecovery_.append(std::move(results))) {
189 return false;
190 }
191
192 return true;
193 }
194
maybeIonFrameRecovery(JitFrameLayout * fp)195 js::jit::RInstructionResults* js::jit::JitActivation::maybeIonFrameRecovery(
196 JitFrameLayout* fp) {
197 for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end();
198 it++) {
199 if (it->frame() == fp) {
200 return it;
201 }
202 }
203
204 return nullptr;
205 }
206
removeIonFrameRecovery(JitFrameLayout * fp)207 void js::jit::JitActivation::removeIonFrameRecovery(JitFrameLayout* fp) {
208 RInstructionResults* elem = maybeIonFrameRecovery(fp);
209 if (!elem) {
210 return;
211 }
212
213 ionRecovery_.erase(elem);
214 }
215
traceIonRecovery(JSTracer * trc)216 void js::jit::JitActivation::traceIonRecovery(JSTracer* trc) {
217 for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end();
218 it++) {
219 it->trace(trc);
220 }
221 }
222
startWasmTrap(wasm::Trap trap,uint32_t bytecodeOffset,const wasm::RegisterState & state)223 void js::jit::JitActivation::startWasmTrap(wasm::Trap trap,
224 uint32_t bytecodeOffset,
225 const wasm::RegisterState& state) {
226 MOZ_ASSERT(!isWasmTrapping());
227
228 bool unwound;
229 wasm::UnwindState unwindState;
230 MOZ_RELEASE_ASSERT(wasm::StartUnwinding(state, &unwindState, &unwound));
231 MOZ_ASSERT(unwound == (trap == wasm::Trap::IndirectCallBadSig));
232
233 void* pc = unwindState.pc;
234 const wasm::Frame* fp = wasm::Frame::fromUntaggedWasmExitFP(unwindState.fp);
235
236 const wasm::Code& code = wasm::GetNearestEffectiveTls(fp)->instance->code();
237 MOZ_RELEASE_ASSERT(&code == wasm::LookupCode(pc));
238
239 // If the frame was unwound, the bytecodeOffset must be recovered from the
240 // callsite so that it is accurate.
241 if (unwound) {
242 bytecodeOffset = code.lookupCallSite(pc)->lineOrBytecode();
243 }
244
245 setWasmExitFP(fp);
246 wasmTrapData_.emplace();
247 wasmTrapData_->resumePC =
248 ((uint8_t*)state.pc) + jit::WasmTrapInstructionLength;
249 wasmTrapData_->unwoundPC = pc;
250 wasmTrapData_->trap = trap;
251 wasmTrapData_->bytecodeOffset = bytecodeOffset;
252
253 MOZ_ASSERT(isWasmTrapping());
254 }
255
finishWasmTrap()256 void js::jit::JitActivation::finishWasmTrap() {
257 MOZ_ASSERT(isWasmTrapping());
258 packedExitFP_ = nullptr;
259 wasmTrapData_.reset();
260 MOZ_ASSERT(!isWasmTrapping());
261 }
262