1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 "jsfun.h"
11 
12 #include "jit/JitAllocPolicy.h"
13 #include "jit/JitFrames.h"
14 #include "jit/Registers.h"
15 #include "vm/ScopeObject.h"
16 
17 namespace js {
18 namespace jit {
19 
20 class TrackedOptimizations;
21 
22 inline unsigned
StartArgSlot(JSScript * script)23 StartArgSlot(JSScript* script)
24 {
25     // Reserved slots:
26     // Slot 0: Scope chain.
27     // Slot 1: Return value.
28 
29     // When needed:
30     // Slot 2: Argumentsobject.
31 
32     // Note: when updating this, please also update the assert in SnapshotWriter::startFrame
33     return 2 + (script->argumentsHasVarBinding() ? 1 : 0);
34 }
35 
36 inline unsigned
CountArgSlots(JSScript * script,JSFunction * fun)37 CountArgSlots(JSScript* script, JSFunction* fun)
38 {
39     // Slot x + 0: This value.
40     // Slot x + 1: Argument 1.
41     // ...
42     // Slot x + n: Argument n.
43 
44     // Note: when updating this, please also update the assert in SnapshotWriter::startFrame
45     return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0);
46 }
47 
48 
49 // The compiler at various points needs to be able to store references to the
50 // current inline path (the sequence of scripts and call-pcs that lead to the
51 // current function being inlined).
52 //
53 // To support this, the top-level IonBuilder keeps a tree that records the
54 // inlinings done during compilation.
55 class InlineScriptTree {
56     // InlineScriptTree for the caller
57     InlineScriptTree* caller_;
58 
59     // PC in the caller corresponding to this script.
60     jsbytecode* callerPc_;
61 
62     // Script for this entry.
63     JSScript* script_;
64 
65     // Child entries (linked together by nextCallee pointer)
66     InlineScriptTree* children_;
67     InlineScriptTree* nextCallee_;
68 
69   public:
InlineScriptTree(InlineScriptTree * caller,jsbytecode * callerPc,JSScript * script)70     InlineScriptTree(InlineScriptTree* caller, jsbytecode* callerPc, JSScript* script)
71       : caller_(caller), callerPc_(callerPc), script_(script),
72         children_(nullptr), nextCallee_(nullptr)
73     {}
74 
75     static InlineScriptTree* New(TempAllocator* allocator, InlineScriptTree* caller,
76                                  jsbytecode* callerPc, JSScript* script);
77 
78     InlineScriptTree* addCallee(TempAllocator* allocator, jsbytecode* callerPc,
79                                 JSScript* calleeScript);
80 
caller()81     InlineScriptTree* caller() const {
82         return caller_;
83     }
84 
isOutermostCaller()85     bool isOutermostCaller() const {
86         return caller_ == nullptr;
87     }
hasCaller()88     bool hasCaller() const {
89         return caller_ != nullptr;
90     }
outermostCaller()91     InlineScriptTree* outermostCaller() {
92         if (isOutermostCaller())
93             return this;
94         return caller_->outermostCaller();
95     }
96 
callerPc()97     jsbytecode* callerPc() const {
98         return callerPc_;
99     }
100 
script()101     JSScript* script() const {
102         return script_;
103     }
104 
hasChildren()105     bool hasChildren() const {
106         return children_ != nullptr;
107     }
firstChild()108     InlineScriptTree* firstChild() const {
109         MOZ_ASSERT(hasChildren());
110         return children_;
111     }
112 
hasNextCallee()113     bool hasNextCallee() const {
114         return nextCallee_ != nullptr;
115     }
nextCallee()116     InlineScriptTree* nextCallee() const {
117         MOZ_ASSERT(hasNextCallee());
118         return nextCallee_;
119     }
120 
depth()121     unsigned depth() const {
122         if (isOutermostCaller())
123             return 1;
124         return 1 + caller_->depth();
125     }
126 };
127 
128 class BytecodeSite : public TempObject
129 {
130     // InlineScriptTree identifying innermost active function at site.
131     InlineScriptTree* tree_;
132 
133     // Bytecode address within innermost active function.
134     jsbytecode* pc_;
135 
136     // Optimization information at the pc.
137     TrackedOptimizations* optimizations_;
138 
139   public:
BytecodeSite()140     BytecodeSite()
141       : tree_(nullptr), pc_(nullptr), optimizations_(nullptr)
142     {}
143 
BytecodeSite(InlineScriptTree * tree,jsbytecode * pc)144     BytecodeSite(InlineScriptTree* tree, jsbytecode* pc)
145       : tree_(tree), pc_(pc), optimizations_(nullptr)
146     {
147         MOZ_ASSERT(tree_ != nullptr);
148         MOZ_ASSERT(pc_ != nullptr);
149     }
150 
tree()151     InlineScriptTree* tree() const {
152         return tree_;
153     }
154 
pc()155     jsbytecode* pc() const {
156         return pc_;
157     }
158 
script()159     JSScript* script() const {
160         return tree_ ? tree_->script() : nullptr;
161     }
162 
hasOptimizations()163     bool hasOptimizations() const {
164         return !!optimizations_;
165     }
166 
optimizations()167     TrackedOptimizations* optimizations() const {
168         MOZ_ASSERT(hasOptimizations());
169         return optimizations_;
170     }
171 
setOptimizations(TrackedOptimizations * optimizations)172     void setOptimizations(TrackedOptimizations* optimizations) {
173         optimizations_ = optimizations;
174     }
175 };
176 
177 enum AnalysisMode {
178     /* JavaScript execution, not analysis. */
179     Analysis_None,
180 
181     /*
182      * MIR analysis performed when invoking 'new' on a script, to determine
183      * definite properties. Used by the optimizing JIT.
184      */
185     Analysis_DefiniteProperties,
186 
187     /*
188      * MIR analysis performed when executing a script which uses its arguments,
189      * when it is not known whether a lazy arguments value can be used.
190      */
191     Analysis_ArgumentsUsage
192 };
193 
194 // Contains information about the compilation source for IR being generated.
195 class CompileInfo
196 {
197   public:
CompileInfo(JSScript * script,JSFunction * fun,jsbytecode * osrPc,bool constructing,AnalysisMode analysisMode,bool scriptNeedsArgsObj,InlineScriptTree * inlineScriptTree)198     CompileInfo(JSScript* script, JSFunction* fun, jsbytecode* osrPc, bool constructing,
199                 AnalysisMode analysisMode, bool scriptNeedsArgsObj,
200                 InlineScriptTree* inlineScriptTree)
201       : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
202         analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
203         mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
204         inlineScriptTree_(inlineScriptTree)
205     {
206         MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
207 
208         // The function here can flow in from anywhere so look up the canonical
209         // function to ensure that we do not try to embed a nursery pointer in
210         // jit-code. Precisely because it can flow in from anywhere, it's not
211         // guaranteed to be non-lazy. Hence, don't access its script!
212         if (fun_) {
213             fun_ = fun_->nonLazyScript()->functionNonDelazifying();
214             MOZ_ASSERT(fun_->isTenured());
215         }
216 
217         osrStaticScope_ = osrPc ? script->getStaticBlockScope(osrPc) : nullptr;
218 
219         nimplicit_ = StartArgSlot(script)                   /* scope chain and argument obj */
220                    + (fun ? 1 : 0);                         /* this */
221         nargs_ = fun ? fun->nargs() : 0;
222         nbodyfixed_ = script->nbodyfixed();
223         nlocals_ = script->nfixed();
224         fixedLexicalBegin_ = script->fixedLexicalBegin();
225         nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize);
226         nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
227     }
228 
CompileInfo(unsigned nlocals)229     explicit CompileInfo(unsigned nlocals)
230       : script_(nullptr), fun_(nullptr), osrPc_(nullptr), osrStaticScope_(nullptr),
231         constructing_(false), analysisMode_(Analysis_None), scriptNeedsArgsObj_(false),
232         mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr)
233     {
234         nimplicit_ = 0;
235         nargs_ = 0;
236         nbodyfixed_ = 0;
237         nlocals_ = nlocals;
238         nstack_ = 1;  /* For FunctionCompiler::pushPhiInput/popPhiOutput */
239         nslots_ = nlocals_ + nstack_;
240         fixedLexicalBegin_ = nlocals;
241     }
242 
script()243     JSScript* script() const {
244         return script_;
245     }
compilingAsmJS()246     bool compilingAsmJS() const {
247         return script() == nullptr;
248     }
funMaybeLazy()249     JSFunction* funMaybeLazy() const {
250         return fun_;
251     }
module()252     ModuleObject* module() const {
253         return script_->module();
254     }
constructing()255     bool constructing() const {
256         return constructing_;
257     }
osrPc()258     jsbytecode* osrPc() const {
259         return osrPc_;
260     }
osrStaticScope()261     NestedScopeObject* osrStaticScope() const {
262         return osrStaticScope_;
263     }
inlineScriptTree()264     InlineScriptTree* inlineScriptTree() const {
265         return inlineScriptTree_;
266     }
267 
hasOsrAt(jsbytecode * pc)268     bool hasOsrAt(jsbytecode* pc) const {
269         MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
270         return pc == osrPc();
271     }
272 
startPC()273     jsbytecode* startPC() const {
274         return script_->code();
275     }
limitPC()276     jsbytecode* limitPC() const {
277         return script_->codeEnd();
278     }
279 
filename()280     const char* filename() const {
281         return script_->filename();
282     }
283 
lineno()284     unsigned lineno() const {
285         return script_->lineno();
286     }
lineno(jsbytecode * pc)287     unsigned lineno(jsbytecode* pc) const {
288         return PCToLineNumber(script_, pc);
289     }
290 
291     // Script accessors based on PC.
292 
getAtom(jsbytecode * pc)293     JSAtom* getAtom(jsbytecode* pc) const {
294         return script_->getAtom(GET_UINT32_INDEX(pc));
295     }
296 
getName(jsbytecode * pc)297     PropertyName* getName(jsbytecode* pc) const {
298         return script_->getName(GET_UINT32_INDEX(pc));
299     }
300 
301     inline RegExpObject* getRegExp(jsbytecode* pc) const;
302 
getObject(jsbytecode * pc)303     JSObject* getObject(jsbytecode* pc) const {
304         return script_->getObject(GET_UINT32_INDEX(pc));
305     }
306 
307     inline JSFunction* getFunction(jsbytecode* pc) const;
308 
getConst(jsbytecode * pc)309     const Value& getConst(jsbytecode* pc) const {
310         return script_->getConst(GET_UINT32_INDEX(pc));
311     }
312 
getNote(GSNCache & gsn,jsbytecode * pc)313     jssrcnote* getNote(GSNCache& gsn, jsbytecode* pc) const {
314         return GetSrcNote(gsn, script(), pc);
315     }
316 
317     // Total number of slots: args, locals, and stack.
nslots()318     unsigned nslots() const {
319         return nslots_;
320     }
321 
322     // Number of slots needed for Scope chain, return value,
323     // maybe argumentsobject and this value.
nimplicit()324     unsigned nimplicit() const {
325         return nimplicit_;
326     }
327     // Number of arguments (without counting this value).
nargs()328     unsigned nargs() const {
329         return nargs_;
330     }
331     // Number of slots needed for fixed body-level bindings.  Note that this
332     // is only non-zero for function code.
nbodyfixed()333     unsigned nbodyfixed() const {
334         return nbodyfixed_;
335     }
336     // Number of slots needed for all local variables.  This includes "fixed
337     // vars" (see above) and also block-scoped locals.
nlocals()338     unsigned nlocals() const {
339         return nlocals_;
340     }
ninvoke()341     unsigned ninvoke() const {
342         return nslots_ - nstack_;
343     }
344     // The slot number at which fixed lexicals begin.
fixedLexicalBegin()345     unsigned fixedLexicalBegin() const {
346         return fixedLexicalBegin_;
347     }
348 
scopeChainSlot()349     uint32_t scopeChainSlot() const {
350         MOZ_ASSERT(script());
351         return 0;
352     }
returnValueSlot()353     uint32_t returnValueSlot() const {
354         MOZ_ASSERT(script());
355         return 1;
356     }
argsObjSlot()357     uint32_t argsObjSlot() const {
358         MOZ_ASSERT(hasArguments());
359         return 2;
360     }
thisSlot()361     uint32_t thisSlot() const {
362         MOZ_ASSERT(funMaybeLazy());
363         MOZ_ASSERT(nimplicit_ > 0);
364         return nimplicit_ - 1;
365     }
firstArgSlot()366     uint32_t firstArgSlot() const {
367         return nimplicit_;
368     }
argSlotUnchecked(uint32_t i)369     uint32_t argSlotUnchecked(uint32_t i) const {
370         // During initialization, some routines need to get at arg
371         // slots regardless of how regular argument access is done.
372         MOZ_ASSERT(i < nargs_);
373         return nimplicit_ + i;
374     }
argSlot(uint32_t i)375     uint32_t argSlot(uint32_t i) const {
376         // This should only be accessed when compiling functions for
377         // which argument accesses don't need to go through the
378         // argument object.
379         MOZ_ASSERT(!argsObjAliasesFormals());
380         return argSlotUnchecked(i);
381     }
firstLocalSlot()382     uint32_t firstLocalSlot() const {
383         return nimplicit_ + nargs_;
384     }
localSlot(uint32_t i)385     uint32_t localSlot(uint32_t i) const {
386         return firstLocalSlot() + i;
387     }
firstStackSlot()388     uint32_t firstStackSlot() const {
389         return firstLocalSlot() + nlocals();
390     }
stackSlot(uint32_t i)391     uint32_t stackSlot(uint32_t i) const {
392         return firstStackSlot() + i;
393     }
394 
startArgSlot()395     uint32_t startArgSlot() const {
396         MOZ_ASSERT(script());
397         return StartArgSlot(script());
398     }
endArgSlot()399     uint32_t endArgSlot() const {
400         MOZ_ASSERT(script());
401         return CountArgSlots(script(), funMaybeLazy());
402     }
403 
totalSlots()404     uint32_t totalSlots() const {
405         MOZ_ASSERT(script() && funMaybeLazy());
406         return nimplicit() + nargs() + nlocals();
407     }
408 
isSlotAliased(uint32_t index,NestedScopeObject * staticScope)409     bool isSlotAliased(uint32_t index, NestedScopeObject* staticScope) const {
410         MOZ_ASSERT(index >= startArgSlot());
411 
412         if (funMaybeLazy() && index == thisSlot())
413             return false;
414 
415         uint32_t arg = index - firstArgSlot();
416         if (arg < nargs())
417             return script()->formalIsAliased(arg);
418 
419         uint32_t local = index - firstLocalSlot();
420         if (local < nlocals()) {
421             // First, check if this local is body-level. If we have a slot for
422             // it, it is by definition unaliased. Aliased body-level locals do
423             // not have fixed slots on the frame and live in the CallObject.
424             //
425             // Note that this is not true for lexical (block-scoped)
426             // bindings. Such bindings, even when aliased, may be considered
427             // part of the "fixed" part (< nlocals()) of the frame.
428             if (local < nbodyfixed())
429                 return false;
430 
431             // Otherwise, it might be part of a block scope.
432             for (; staticScope; staticScope = staticScope->enclosingNestedScope()) {
433                 if (!staticScope->is<StaticBlockObject>())
434                     continue;
435                 StaticBlockObject& blockObj = staticScope->as<StaticBlockObject>();
436                 if (blockObj.localOffset() < local) {
437                     if (local - blockObj.localOffset() < blockObj.numVariables())
438                         return blockObj.isAliased(local - blockObj.localOffset());
439                     return false;
440                 }
441             }
442 
443             // In this static scope, this var is dead.
444             return false;
445         }
446 
447         MOZ_ASSERT(index >= firstStackSlot());
448         return false;
449     }
450 
isSlotAliasedAtEntry(uint32_t index)451     bool isSlotAliasedAtEntry(uint32_t index) const {
452         return isSlotAliased(index, nullptr);
453     }
isSlotAliasedAtOsr(uint32_t index)454     bool isSlotAliasedAtOsr(uint32_t index) const {
455         return isSlotAliased(index, osrStaticScope());
456     }
457 
hasArguments()458     bool hasArguments() const {
459         return script()->argumentsHasVarBinding();
460     }
argumentsAliasesFormals()461     bool argumentsAliasesFormals() const {
462         return script()->argumentsAliasesFormals();
463     }
needsArgsObj()464     bool needsArgsObj() const {
465         return scriptNeedsArgsObj_;
466     }
argsObjAliasesFormals()467     bool argsObjAliasesFormals() const {
468         return scriptNeedsArgsObj_ && script()->hasMappedArgsObj();
469     }
470 
analysisMode()471     AnalysisMode analysisMode() const {
472         return analysisMode_;
473     }
474 
isAnalysis()475     bool isAnalysis() const {
476         return analysisMode_ != Analysis_None;
477     }
478 
479     // Returns true if a slot can be observed out-side the current frame while
480     // the frame is active on the stack.  This implies that these definitions
481     // would have to be executed and that they cannot be removed even if they
482     // are unused.
isObservableSlot(uint32_t slot)483     bool isObservableSlot(uint32_t slot) const {
484         if (isObservableFrameSlot(slot))
485             return true;
486 
487         if (isObservableArgumentSlot(slot))
488             return true;
489 
490         return false;
491     }
492 
isObservableFrameSlot(uint32_t slot)493     bool isObservableFrameSlot(uint32_t slot) const {
494         if (!funMaybeLazy())
495             return false;
496 
497         // The |this| value must always be observable.
498         if (slot == thisSlot())
499             return true;
500 
501         if (funMaybeLazy()->needsCallObject() && slot == scopeChainSlot())
502             return true;
503 
504         // If the function may need an arguments object, then make sure to
505         // preserve the scope chain, because it may be needed to construct the
506         // arguments object during bailout. If we've already created an
507         // arguments object (or got one via OSR), preserve that as well.
508         if (hasArguments() && (slot == scopeChainSlot() || slot == argsObjSlot()))
509             return true;
510 
511         return false;
512     }
513 
isObservableArgumentSlot(uint32_t slot)514     bool isObservableArgumentSlot(uint32_t slot) const {
515         if (!funMaybeLazy())
516             return false;
517 
518         // Function.arguments can be used to access all arguments in non-strict
519         // scripts, so we can't optimize out any arguments.
520         if ((hasArguments() || !script()->strict()) &&
521             firstArgSlot() <= slot && slot - firstArgSlot() < nargs())
522         {
523             return true;
524         }
525 
526         return false;
527     }
528 
529     // Returns true if a slot can be recovered before or during a bailout.  A
530     // definition which can be observed and recovered, implies that this
531     // definition can be optimized away as long as we can compute its values.
isRecoverableOperand(uint32_t slot)532     bool isRecoverableOperand(uint32_t slot) const {
533         // If this script is not a function, then none of the slots are
534         // observable.  If it this |slot| is not observable, thus we can always
535         // recover it.
536         if (!funMaybeLazy())
537             return true;
538 
539         // The |this| and the |scopeChain| values can be recovered.
540         if (slot == thisSlot() || slot == scopeChainSlot())
541             return true;
542 
543         if (isObservableFrameSlot(slot))
544             return false;
545 
546         if (needsArgsObj() && isObservableArgumentSlot(slot))
547             return false;
548 
549         return true;
550     }
551 
mayReadFrameArgsDirectly()552     bool mayReadFrameArgsDirectly() const {
553         return mayReadFrameArgsDirectly_;
554     }
555 
556   private:
557     unsigned nimplicit_;
558     unsigned nargs_;
559     unsigned nbodyfixed_;
560     unsigned nlocals_;
561     unsigned nstack_;
562     unsigned nslots_;
563     unsigned fixedLexicalBegin_;
564     JSScript* script_;
565     JSFunction* fun_;
566     jsbytecode* osrPc_;
567     NestedScopeObject* osrStaticScope_;
568     bool constructing_;
569     AnalysisMode analysisMode_;
570 
571     // Whether a script needs an arguments object is unstable over compilation
572     // since the arguments optimization could be marked as failed on the main
573     // thread, so cache a value here and use it throughout for consistency.
574     bool scriptNeedsArgsObj_;
575 
576     bool mayReadFrameArgsDirectly_;
577 
578     InlineScriptTree* inlineScriptTree_;
579 };
580 
581 } // namespace jit
582 } // namespace js
583 
584 #endif /* jit_CompileInfo_h */
585