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