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