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