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