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 jit_CompileInfo_h
8 #define jit_CompileInfo_h
9
10 #include "mozilla/Assertions.h" // MOZ_ASSERT
11 #include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some
12
13 #include <algorithm> // std::max
14 #include <stdint.h> // uint32_t
15
16 #include "jit/CompileWrappers.h" // CompileRuntime
17 #include "jit/JitFrames.h" // MinJITStackSize
18 #include "js/TypeDecls.h" // jsbytecode
19 #include "vm/BindingKind.h" // BindingLocation
20 #include "vm/BytecodeUtil.h" // JSOp
21 #include "vm/JSAtomState.h" // JSAtomState
22 #include "vm/JSFunction.h" // JSFunction
23 #include "vm/JSScript.h" // JSScript
24 #include "vm/Scope.h" // BindingIter
25
26 namespace js {
27
28 class ModuleObject;
29
30 namespace jit {
31
32 class InlineScriptTree;
33
StartArgSlot(JSScript * script)34 inline unsigned StartArgSlot(JSScript* script) {
35 // Reserved slots:
36 // Slot 0: Environment chain.
37 // Slot 1: Return value.
38
39 // When needed:
40 // Slot 2: Argumentsobject.
41
42 // Note: when updating this, please also update the assert in
43 // SnapshotWriter::startFrame
44 return 2 + (script->needsArgsObj() ? 1 : 0);
45 }
46
CountArgSlots(JSScript * script,JSFunction * fun)47 inline unsigned CountArgSlots(JSScript* script, JSFunction* fun) {
48 // Slot x + 0: This value.
49 // Slot x + 1: Argument 1.
50 // ...
51 // Slot x + n: Argument n.
52
53 // Note: when updating this, please also update the assert in
54 // SnapshotWriter::startFrame
55 return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0);
56 }
57
58 // Contains information about the compilation source for IR being generated.
59 class CompileInfo {
60 public:
CompileInfo(CompileRuntime * runtime,JSScript * script,JSFunction * fun,jsbytecode * osrPc,bool scriptNeedsArgsObj,InlineScriptTree * inlineScriptTree)61 CompileInfo(CompileRuntime* runtime, JSScript* script, JSFunction* fun,
62 jsbytecode* osrPc, bool scriptNeedsArgsObj,
63 InlineScriptTree* inlineScriptTree)
64 : script_(script),
65 fun_(fun),
66 osrPc_(osrPc),
67 scriptNeedsArgsObj_(scriptNeedsArgsObj),
68 hadEagerTruncationBailout_(script->hadEagerTruncationBailout()),
69 hadSpeculativePhiBailout_(script->hadSpeculativePhiBailout()),
70 hadLICMInvalidation_(script->hadLICMInvalidation()),
71 hadReorderingBailout_(script->hadReorderingBailout()),
72 hadBoundsCheckBailout_(script->failedBoundsCheck()),
73 hadUnboxFoldingBailout_(script->hadUnboxFoldingBailout()),
74 mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
75 anyFormalIsForwarded_(script->anyFormalIsForwarded()),
76 isDerivedClassConstructor_(script->isDerivedClassConstructor()),
77 inlineScriptTree_(inlineScriptTree) {
78 MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOp::LoopHead);
79
80 // The function here can flow in from anywhere so look up the canonical
81 // function to ensure that we do not try to embed a nursery pointer in
82 // jit-code. Precisely because it can flow in from anywhere, it's not
83 // guaranteed to be non-lazy. Hence, don't access its script!
84 if (fun_) {
85 fun_ = fun_->baseScript()->function();
86 MOZ_ASSERT(fun_->isTenured());
87 }
88
89 nimplicit_ = StartArgSlot(script) /* env chain and argument obj */
90 + (fun ? 1 : 0); /* this */
91 nargs_ = fun ? fun->nargs() : 0;
92 nlocals_ = script->nfixed();
93
94 // An extra slot is needed for global scopes because InitGLexical (stack
95 // depth 1) is compiled as a SetProp (stack depth 2) on the global lexical
96 // scope.
97 uint32_t extra = script->isGlobalCode() ? 1 : 0;
98 nstack_ = std::max<unsigned>(script->nslots() - script->nfixed(),
99 MinJITStackSize) +
100 extra;
101 nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
102
103 // For derived class constructors, find and cache the frame slot for
104 // the .this binding. This slot is assumed to be always
105 // observable. See isObservableFrameSlot.
106 if (script->isDerivedClassConstructor()) {
107 MOZ_ASSERT(script->functionHasThisBinding());
108 for (BindingIter bi(script); bi; bi++) {
109 if (bi.name() != runtime->names().dotThis) {
110 continue;
111 }
112 BindingLocation loc = bi.location();
113 if (loc.kind() == BindingLocation::Kind::Frame) {
114 thisSlotForDerivedClassConstructor_ =
115 mozilla::Some(localSlot(loc.slot()));
116 break;
117 }
118 }
119 }
120
121 // If the script uses an environment in body, the environment chain
122 // will need to be observable.
123 needsBodyEnvironmentObject_ = script->needsBodyEnvironment();
124 funNeedsSomeEnvironmentObject_ =
125 fun ? fun->needsSomeEnvironmentObject() : false;
126 }
127
CompileInfo(unsigned nlocals)128 explicit CompileInfo(unsigned nlocals)
129 : script_(nullptr),
130 fun_(nullptr),
131 osrPc_(nullptr),
132 scriptNeedsArgsObj_(false),
133 hadEagerTruncationBailout_(false),
134 hadSpeculativePhiBailout_(false),
135 hadLICMInvalidation_(false),
136 hadReorderingBailout_(false),
137 hadBoundsCheckBailout_(false),
138 hadUnboxFoldingBailout_(false),
139 mayReadFrameArgsDirectly_(false),
140 anyFormalIsForwarded_(false),
141 inlineScriptTree_(nullptr),
142 needsBodyEnvironmentObject_(false),
143 funNeedsSomeEnvironmentObject_(false) {
144 nimplicit_ = 0;
145 nargs_ = 0;
146 nlocals_ = nlocals;
147 nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */
148 nslots_ = nlocals_ + nstack_;
149 }
150
script()151 JSScript* script() const { return script_; }
compilingWasm()152 bool compilingWasm() const { return script() == nullptr; }
funMaybeLazy()153 JSFunction* funMaybeLazy() const { return fun_; }
module()154 ModuleObject* module() const { return script_->module(); }
osrPc()155 jsbytecode* osrPc() const { return osrPc_; }
inlineScriptTree()156 InlineScriptTree* inlineScriptTree() const { return inlineScriptTree_; }
157
filename()158 const char* filename() const { return script_->filename(); }
159
lineno()160 unsigned lineno() const { return script_->lineno(); }
161
162 // Total number of slots: args, locals, and stack.
nslots()163 unsigned nslots() const { return nslots_; }
164
165 // Number of slots needed for env chain, return value,
166 // maybe argumentsobject and this value.
nimplicit()167 unsigned nimplicit() const { return nimplicit_; }
168 // Number of arguments (without counting this value).
nargs()169 unsigned nargs() const { return nargs_; }
170 // Number of slots needed for all local variables. This includes "fixed
171 // vars" (see above) and also block-scoped locals.
nlocals()172 unsigned nlocals() const { return nlocals_; }
ninvoke()173 unsigned ninvoke() const { return nslots_ - nstack_; }
174
environmentChainSlot()175 uint32_t environmentChainSlot() const {
176 MOZ_ASSERT(script());
177 return 0;
178 }
returnValueSlot()179 uint32_t returnValueSlot() const {
180 MOZ_ASSERT(script());
181 return 1;
182 }
argsObjSlot()183 uint32_t argsObjSlot() const {
184 MOZ_ASSERT(needsArgsObj());
185 return 2;
186 }
thisSlot()187 uint32_t thisSlot() const {
188 MOZ_ASSERT(funMaybeLazy());
189 MOZ_ASSERT(nimplicit_ > 0);
190 return nimplicit_ - 1;
191 }
firstArgSlot()192 uint32_t firstArgSlot() const { return nimplicit_; }
argSlotUnchecked(uint32_t i)193 uint32_t argSlotUnchecked(uint32_t i) const {
194 // During initialization, some routines need to get at arg
195 // slots regardless of how regular argument access is done.
196 MOZ_ASSERT(i < nargs_);
197 return nimplicit_ + i;
198 }
argSlot(uint32_t i)199 uint32_t argSlot(uint32_t i) const {
200 // This should only be accessed when compiling functions for
201 // which argument accesses don't need to go through the
202 // argument object.
203 MOZ_ASSERT(!argsObjAliasesFormals());
204 return argSlotUnchecked(i);
205 }
firstLocalSlot()206 uint32_t firstLocalSlot() const { return nimplicit_ + nargs_; }
localSlot(uint32_t i)207 uint32_t localSlot(uint32_t i) const { return firstLocalSlot() + i; }
firstStackSlot()208 uint32_t firstStackSlot() const { return firstLocalSlot() + nlocals(); }
stackSlot(uint32_t i)209 uint32_t stackSlot(uint32_t i) const { return firstStackSlot() + i; }
210
totalSlots()211 uint32_t totalSlots() const {
212 MOZ_ASSERT(script() && funMaybeLazy());
213 return nimplicit() + nargs() + nlocals();
214 }
215
hasMappedArgsObj()216 bool hasMappedArgsObj() const { return script()->hasMappedArgsObj(); }
needsArgsObj()217 bool needsArgsObj() const { return scriptNeedsArgsObj_; }
argsObjAliasesFormals()218 bool argsObjAliasesFormals() const {
219 return scriptNeedsArgsObj_ && script()->hasMappedArgsObj();
220 }
221
needsBodyEnvironmentObject()222 bool needsBodyEnvironmentObject() const {
223 return needsBodyEnvironmentObject_;
224 }
225
226 enum class SlotObservableKind {
227 // This slot must be preserved because it's observable outside SSA uses.
228 // It can't be recovered before or during bailout.
229 ObservableNotRecoverable,
230
231 // This slot must be preserved because it's observable, but it can be
232 // recovered.
233 ObservableRecoverable,
234
235 // This slot is not observable outside SSA uses.
236 NotObservable,
237 };
238
getSlotObservableKind(uint32_t slot)239 inline SlotObservableKind getSlotObservableKind(uint32_t slot) const {
240 // Locals and expression stack slots.
241 if (slot >= firstLocalSlot()) {
242 // The |this| slot for a derived class constructor is a local slot.
243 // It should never be optimized out, as a Debugger might need to perform
244 // TDZ checks on it via, e.g., an exceptionUnwind handler. The TDZ check
245 // is required for correctness if the handler decides to continue
246 // execution.
247 if (thisSlotForDerivedClassConstructor_ &&
248 *thisSlotForDerivedClassConstructor_ == slot) {
249 return SlotObservableKind::ObservableNotRecoverable;
250 }
251 return SlotObservableKind::NotObservable;
252 }
253
254 // Formal argument slots.
255 if (slot >= firstArgSlot()) {
256 MOZ_ASSERT(funMaybeLazy());
257 MOZ_ASSERT(slot - firstArgSlot() < nargs());
258
259 // Preserve formal arguments if they might be read when creating a rest or
260 // arguments object. In non-strict scripts, Function.arguments can create
261 // an arguments object dynamically so we always preserve the arguments.
262 if (mayReadFrameArgsDirectly_ || !script()->strict()) {
263 return SlotObservableKind::ObservableRecoverable;
264 }
265 return SlotObservableKind::NotObservable;
266 }
267
268 // |this| slot is observable but it can be recovered.
269 if (funMaybeLazy() && slot == thisSlot()) {
270 return SlotObservableKind::ObservableRecoverable;
271 }
272
273 // Environment chain slot.
274 if (slot == environmentChainSlot()) {
275 // If environments can be added in the body (after the prologue) we need
276 // to preserve the environment chain slot. It can't be recovered.
277 if (needsBodyEnvironmentObject()) {
278 return SlotObservableKind::ObservableNotRecoverable;
279 }
280 // If the function may need an arguments object, also preserve the
281 // environment chain because it may be needed to reconstruct the arguments
282 // object during bailout.
283 if (funNeedsSomeEnvironmentObject_ || needsArgsObj()) {
284 return SlotObservableKind::ObservableRecoverable;
285 }
286 return SlotObservableKind::NotObservable;
287 }
288
289 // The arguments object is observable. If it does not escape, it can
290 // be recovered.
291 if (needsArgsObj() && slot == argsObjSlot()) {
292 MOZ_ASSERT(funMaybeLazy());
293 return SlotObservableKind::ObservableRecoverable;
294 }
295
296 MOZ_ASSERT(slot == returnValueSlot());
297 return SlotObservableKind::NotObservable;
298 }
299
300 // Returns true if a slot can be observed out-side the current frame while
301 // the frame is active on the stack. This implies that these definitions
302 // would have to be executed and that they cannot be removed even if they
303 // are unused.
isObservableSlot(uint32_t slot)304 inline bool isObservableSlot(uint32_t slot) const {
305 SlotObservableKind kind = getSlotObservableKind(slot);
306 return (kind == SlotObservableKind::ObservableNotRecoverable ||
307 kind == SlotObservableKind::ObservableRecoverable);
308 }
309
310 // Returns true if a slot can be recovered before or during a bailout. A
311 // definition which can be observed and recovered, implies that this
312 // definition can be optimized away as long as we can compute its values.
isRecoverableOperand(uint32_t slot)313 bool isRecoverableOperand(uint32_t slot) const {
314 SlotObservableKind kind = getSlotObservableKind(slot);
315 return (kind == SlotObservableKind::ObservableRecoverable ||
316 kind == SlotObservableKind::NotObservable);
317 }
318
319 // Check previous bailout states to prevent doing the same bailout in the
320 // next compilation.
hadEagerTruncationBailout()321 bool hadEagerTruncationBailout() const { return hadEagerTruncationBailout_; }
hadSpeculativePhiBailout()322 bool hadSpeculativePhiBailout() const { return hadSpeculativePhiBailout_; }
hadLICMInvalidation()323 bool hadLICMInvalidation() const { return hadLICMInvalidation_; }
hadReorderingBailout()324 bool hadReorderingBailout() const { return hadReorderingBailout_; }
hadBoundsCheckBailout()325 bool hadBoundsCheckBailout() const { return hadBoundsCheckBailout_; }
hadUnboxFoldingBailout()326 bool hadUnboxFoldingBailout() const { return hadUnboxFoldingBailout_; }
327
mayReadFrameArgsDirectly()328 bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_; }
anyFormalIsForwarded()329 bool anyFormalIsForwarded() const { return anyFormalIsForwarded_; }
330
isDerivedClassConstructor()331 bool isDerivedClassConstructor() const { return isDerivedClassConstructor_; }
332
333 private:
334 unsigned nimplicit_;
335 unsigned nargs_;
336 unsigned nlocals_;
337 unsigned nstack_;
338 unsigned nslots_;
339 mozilla::Maybe<unsigned> thisSlotForDerivedClassConstructor_;
340 JSScript* script_;
341 JSFunction* fun_;
342 jsbytecode* osrPc_;
343
344 bool scriptNeedsArgsObj_;
345
346 // Record the state of previous bailouts in order to prevent compiling the
347 // same function identically the next time.
348 bool hadEagerTruncationBailout_;
349 bool hadSpeculativePhiBailout_;
350 bool hadLICMInvalidation_;
351 bool hadReorderingBailout_;
352 bool hadBoundsCheckBailout_;
353 bool hadUnboxFoldingBailout_;
354
355 bool mayReadFrameArgsDirectly_;
356 bool anyFormalIsForwarded_;
357
358 bool isDerivedClassConstructor_;
359
360 InlineScriptTree* inlineScriptTree_;
361
362 // Whether a script needs environments within its body. This informs us
363 // that the environment chain is not easy to reconstruct.
364 bool needsBodyEnvironmentObject_;
365 bool funNeedsSomeEnvironmentObject_;
366 };
367
368 } // namespace jit
369 } // namespace js
370
371 #endif /* jit_CompileInfo_h */
372