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