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/. */
7 #ifndef jit_CompileInfo_h
8 #define jit_CompileInfo_h
10 #include "mozilla/Maybe.h"
14 #include "jit/JitAllocPolicy.h"
15 #include "jit/JitFrames.h"
16 #include "jit/Registers.h"
17 #include "vm/EnvironmentObject.h"
18 #include "vm/JSFunction.h"
23 class TrackedOptimizations
;
25 inline unsigned StartArgSlot(JSScript
* script
) {
27 // Slot 0: Environment chain.
28 // Slot 1: Return value.
31 // Slot 2: Argumentsobject.
33 // Note: when updating this, please also update the assert in
34 // SnapshotWriter::startFrame
35 return 2 + (script
->argumentsHasVarBinding() ? 1 : 0);
38 inline unsigned CountArgSlots(JSScript
* script
, JSFunction
* fun
) {
39 // Slot x + 0: This value.
40 // Slot x + 1: Argument 1.
42 // Slot x + n: Argument n.
44 // Note: when updating this, please also update the assert in
45 // SnapshotWriter::startFrame
46 return StartArgSlot(script
) + (fun
? fun
->nargs() + 1 : 0);
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).
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_
;
59 // PC in the caller corresponding to this script.
60 jsbytecode
* callerPc_
;
62 // Script for this entry.
65 // Child entries (linked together by nextCallee pointer)
66 InlineScriptTree
* children_
;
67 InlineScriptTree
* nextCallee_
;
70 InlineScriptTree(InlineScriptTree
* caller
, jsbytecode
* callerPc
,
76 nextCallee_(nullptr) {}
78 static InlineScriptTree
* New(TempAllocator
* allocator
,
79 InlineScriptTree
* caller
, jsbytecode
* callerPc
,
82 InlineScriptTree
* addCallee(TempAllocator
* allocator
, jsbytecode
* callerPc
,
83 JSScript
* calleeScript
);
84 void removeCallee(InlineScriptTree
* callee
);
86 InlineScriptTree
* caller() const { return caller_
; }
88 bool isOutermostCaller() const { return caller_
== nullptr; }
89 bool hasCaller() const { return caller_
!= nullptr; }
90 InlineScriptTree
* outermostCaller() {
91 if (isOutermostCaller()) {
94 return caller_
->outermostCaller();
97 jsbytecode
* callerPc() const { return callerPc_
; }
99 JSScript
* script() const { return script_
; }
101 bool hasChildren() const { return children_
!= nullptr; }
102 InlineScriptTree
* firstChild() const {
103 MOZ_ASSERT(hasChildren());
107 bool hasNextCallee() const { return nextCallee_
!= nullptr; }
108 InlineScriptTree
* nextCallee() const {
109 MOZ_ASSERT(hasNextCallee());
113 unsigned depth() const {
114 if (isOutermostCaller()) {
117 return 1 + caller_
->depth();
121 class BytecodeSite
: public TempObject
{
122 // InlineScriptTree identifying innermost active function at site.
123 InlineScriptTree
* tree_
;
125 // Bytecode address within innermost active function.
129 BytecodeSite() : tree_(nullptr), pc_(nullptr) {}
131 BytecodeSite(InlineScriptTree
* tree
, jsbytecode
* pc
) : tree_(tree
), pc_(pc
) {
132 MOZ_ASSERT(tree_
!= nullptr);
133 MOZ_ASSERT(pc_
!= nullptr);
136 InlineScriptTree
* tree() const { return tree_
; }
138 jsbytecode
* pc() const { return pc_
; }
140 JSScript
* script() const { return tree_
? tree_
->script() : nullptr; }
144 /* JavaScript execution, not analysis. */
148 * MIR analysis performed when invoking 'new' on a script, to determine
149 * definite properties. Used by the optimizing JIT.
151 Analysis_DefiniteProperties
,
154 * MIR analysis performed when executing a script which uses its arguments,
155 * when it is not known whether a lazy arguments value can be used.
157 Analysis_ArgumentsUsage
160 // Contains information about the compilation source for IR being generated.
163 CompileInfo(CompileRuntime
* runtime
, JSScript
* script
, JSFunction
* fun
,
164 jsbytecode
* osrPc
, AnalysisMode analysisMode
,
165 bool scriptNeedsArgsObj
, InlineScriptTree
* inlineScriptTree
)
169 analysisMode_(analysisMode
),
170 scriptNeedsArgsObj_(scriptNeedsArgsObj
),
171 hadOverflowBailout_(script
->hadOverflowBailout()),
172 hadFrequentBailouts_(script
->hadFrequentBailouts()),
173 mayReadFrameArgsDirectly_(script
->mayReadFrameArgsDirectly()),
174 inlineScriptTree_(inlineScriptTree
) {
175 MOZ_ASSERT_IF(osrPc
, JSOp(*osrPc
) == JSOp::LoopHead
);
177 // The function here can flow in from anywhere so look up the canonical
178 // function to ensure that we do not try to embed a nursery pointer in
179 // jit-code. Precisely because it can flow in from anywhere, it's not
180 // guaranteed to be non-lazy. Hence, don't access its script!
182 fun_
= fun_
->baseScript()->function();
183 MOZ_ASSERT(fun_
->isTenured());
186 nimplicit_
= StartArgSlot(script
) /* env chain and argument obj */
187 + (fun
? 1 : 0); /* this */
188 nargs_
= fun
? fun
->nargs() : 0;
189 nlocals_
= script
->nfixed();
191 // An extra slot is needed for global scopes because InitGLexical (stack
192 // depth 1) is compiled as a SetProp (stack depth 2) on the global lexical
194 uint32_t extra
= script
->isGlobalCode() ? 1 : 0;
195 nstack_
= std::max
<unsigned>(script
->nslots() - script
->nfixed(),
198 nslots_
= nimplicit_
+ nargs_
+ nlocals_
+ nstack_
;
200 // For derived class constructors, find and cache the frame slot for
201 // the .this binding. This slot is assumed to be always
202 // observable. See isObservableFrameSlot.
203 if (script
->isDerivedClassConstructor()) {
204 MOZ_ASSERT(script
->functionHasThisBinding());
205 for (BindingIter
bi(script
); bi
; bi
++) {
206 if (bi
.name() != runtime
->names().dotThis
) {
209 BindingLocation loc
= bi
.location();
210 if (loc
.kind() == BindingLocation::Kind::Frame
) {
211 thisSlotForDerivedClassConstructor_
=
212 mozilla::Some(localSlot(loc
.slot()));
218 // If the script uses an environment in body, the environment chain
219 // will need to be observable.
220 needsBodyEnvironmentObject_
= script
->needsBodyEnvironment();
221 funNeedsSomeEnvironmentObject_
=
222 fun
? fun
->needsSomeEnvironmentObject() : false;
225 explicit CompileInfo(unsigned nlocals
)
229 analysisMode_(Analysis_None
),
230 scriptNeedsArgsObj_(false),
231 hadOverflowBailout_(false),
232 hadFrequentBailouts_(false),
233 mayReadFrameArgsDirectly_(false),
234 inlineScriptTree_(nullptr),
235 needsBodyEnvironmentObject_(false),
236 funNeedsSomeEnvironmentObject_(false) {
240 nstack_
= 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */
241 nslots_
= nlocals_
+ nstack_
;
244 JSScript
* script() const { return script_
; }
245 bool compilingWasm() const { return script() == nullptr; }
246 JSFunction
* funMaybeLazy() const { return fun_
; }
247 ModuleObject
* module() const { return script_
->module(); }
248 jsbytecode
* osrPc() const { return osrPc_
; }
249 InlineScriptTree
* inlineScriptTree() const { return inlineScriptTree_
; }
251 bool hasOsrAt(jsbytecode
* pc
) const {
252 MOZ_ASSERT(JSOp(*pc
) == JSOp::LoopHead
);
253 return pc
== osrPc();
256 jsbytecode
* startPC() const { return script_
->code(); }
257 jsbytecode
* limitPC() const { return script_
->codeEnd(); }
259 const char* filename() const { return script_
->filename(); }
261 unsigned lineno() const { return script_
->lineno(); }
262 unsigned lineno(jsbytecode
* pc
) const { return PCToLineNumber(script_
, pc
); }
264 // Script accessors based on PC.
266 JSAtom
* getAtom(jsbytecode
* pc
) const { return script_
->getAtom(pc
); }
268 PropertyName
* getName(jsbytecode
* pc
) const { return script_
->getName(pc
); }
270 inline RegExpObject
* getRegExp(jsbytecode
* pc
) const;
272 JSObject
* getObject(jsbytecode
* pc
) const { return script_
->getObject(pc
); }
274 inline JSFunction
* getFunction(jsbytecode
* pc
) const;
276 BigInt
* getBigInt(jsbytecode
* pc
) const { return script_
->getBigInt(pc
); }
278 // Total number of slots: args, locals, and stack.
279 unsigned nslots() const { return nslots_
; }
281 // Number of slots needed for env chain, return value,
282 // maybe argumentsobject and this value.
283 unsigned nimplicit() const { return nimplicit_
; }
284 // Number of arguments (without counting this value).
285 unsigned nargs() const { return nargs_
; }
286 // Number of slots needed for all local variables. This includes "fixed
287 // vars" (see above) and also block-scoped locals.
288 unsigned nlocals() const { return nlocals_
; }
289 unsigned ninvoke() const { return nslots_
- nstack_
; }
291 uint32_t environmentChainSlot() const {
292 MOZ_ASSERT(script());
295 uint32_t returnValueSlot() const {
296 MOZ_ASSERT(script());
299 uint32_t argsObjSlot() const {
300 MOZ_ASSERT(hasArguments());
303 uint32_t thisSlot() const {
304 MOZ_ASSERT(funMaybeLazy());
305 MOZ_ASSERT(nimplicit_
> 0);
306 return nimplicit_
- 1;
308 uint32_t firstArgSlot() const { return nimplicit_
; }
309 uint32_t argSlotUnchecked(uint32_t i
) const {
310 // During initialization, some routines need to get at arg
311 // slots regardless of how regular argument access is done.
312 MOZ_ASSERT(i
< nargs_
);
313 return nimplicit_
+ i
;
315 uint32_t argSlot(uint32_t i
) const {
316 // This should only be accessed when compiling functions for
317 // which argument accesses don't need to go through the
319 MOZ_ASSERT(!argsObjAliasesFormals());
320 return argSlotUnchecked(i
);
322 uint32_t firstLocalSlot() const { return nimplicit_
+ nargs_
; }
323 uint32_t localSlot(uint32_t i
) const { return firstLocalSlot() + i
; }
324 uint32_t firstStackSlot() const { return firstLocalSlot() + nlocals(); }
325 uint32_t stackSlot(uint32_t i
) const { return firstStackSlot() + i
; }
327 uint32_t startArgSlot() const {
328 MOZ_ASSERT(script());
329 return StartArgSlot(script());
331 uint32_t endArgSlot() const {
332 MOZ_ASSERT(script());
333 return CountArgSlots(script(), funMaybeLazy());
336 uint32_t totalSlots() const {
337 MOZ_ASSERT(script() && funMaybeLazy());
338 return nimplicit() + nargs() + nlocals();
341 bool isSlotAliased(uint32_t index
) const {
342 MOZ_ASSERT(index
>= startArgSlot());
343 uint32_t arg
= index
- firstArgSlot();
345 return script()->formalIsAliased(arg
);
350 bool hasArguments() const { return script()->argumentsHasVarBinding(); }
351 bool argumentsAliasesFormals() const {
352 return script()->argumentsAliasesFormals();
354 bool hasMappedArgsObj() const { return script()->hasMappedArgsObj(); }
355 bool needsArgsObj() const { return scriptNeedsArgsObj_
; }
356 bool argsObjAliasesFormals() const {
357 return scriptNeedsArgsObj_
&& script()->hasMappedArgsObj();
360 AnalysisMode
analysisMode() const { return analysisMode_
; }
362 bool isAnalysis() const { return analysisMode_
!= Analysis_None
; }
364 bool needsBodyEnvironmentObject() const {
365 return needsBodyEnvironmentObject_
;
368 enum class SlotObservableKind
{
369 // This slot must be preserved because it's observable outside SSA uses.
370 // It can't be recovered before or during bailout.
371 ObservableNotRecoverable
,
373 // This slot must be preserved because it's observable, but it can be
375 ObservableRecoverable
,
377 // This slot is not observable outside SSA uses.
381 inline SlotObservableKind
getSlotObservableKind(uint32_t slot
) const {
382 // Locals and expression stack slots.
383 if (slot
>= firstLocalSlot()) {
384 // The |this| slot for a derived class constructor is a local slot.
385 // It should never be optimized out, as a Debugger might need to perform
386 // TDZ checks on it via, e.g., an exceptionUnwind handler. The TDZ check
387 // is required for correctness if the handler decides to continue
389 if (thisSlotForDerivedClassConstructor_
&&
390 *thisSlotForDerivedClassConstructor_
== slot
) {
391 return SlotObservableKind::ObservableNotRecoverable
;
393 return SlotObservableKind::NotObservable
;
396 // Formal argument slots.
397 if (slot
>= firstArgSlot()) {
398 MOZ_ASSERT(funMaybeLazy());
399 MOZ_ASSERT(slot
- firstArgSlot() < nargs());
401 // Preserve formal arguments if they might be read when creating a rest or
402 // arguments object. In non-strict scripts, Function.arguments can create
403 // an arguments object dynamically so we always preserve the arguments.
404 if (mayReadFrameArgsDirectly_
|| !script()->strict()) {
405 return SlotObservableKind::ObservableRecoverable
;
407 return SlotObservableKind::NotObservable
;
410 // |this| slot is observable but it can be recovered.
411 if (funMaybeLazy() && slot
== thisSlot()) {
412 return SlotObservableKind::ObservableRecoverable
;
415 // Environment chain slot.
416 if (slot
== environmentChainSlot()) {
417 // If environments can be added in the body (after the prologue) we need
418 // to preserve the environment chain slot. It can't be recovered.
419 if (needsBodyEnvironmentObject()) {
420 return SlotObservableKind::ObservableNotRecoverable
;
422 // If the function may need an arguments object, also preserve the
423 // environment chain because it may be needed to reconstruct the arguments
424 // object during bailout.
425 if (funNeedsSomeEnvironmentObject_
|| hasArguments()) {
426 return SlotObservableKind::ObservableRecoverable
;
428 return SlotObservableKind::NotObservable
;
431 // The arguments object is observable and not recoverable.
432 if (hasArguments() && slot
== argsObjSlot()) {
433 MOZ_ASSERT(funMaybeLazy());
434 return SlotObservableKind::ObservableNotRecoverable
;
437 MOZ_ASSERT(slot
== returnValueSlot());
438 return SlotObservableKind::NotObservable
;
441 // Returns true if a slot can be observed out-side the current frame while
442 // the frame is active on the stack. This implies that these definitions
443 // would have to be executed and that they cannot be removed even if they
445 inline bool isObservableSlot(uint32_t slot
) const {
446 SlotObservableKind kind
= getSlotObservableKind(slot
);
447 return (kind
== SlotObservableKind::ObservableNotRecoverable
||
448 kind
== SlotObservableKind::ObservableRecoverable
);
451 // Returns true if a slot can be recovered before or during a bailout. A
452 // definition which can be observed and recovered, implies that this
453 // definition can be optimized away as long as we can compute its values.
454 bool isRecoverableOperand(uint32_t slot
) const {
455 SlotObservableKind kind
= getSlotObservableKind(slot
);
456 return (kind
== SlotObservableKind::ObservableRecoverable
||
457 kind
== SlotObservableKind::NotObservable
);
460 // Check previous bailout states to prevent doing the same bailout in the
462 bool hadOverflowBailout() const { return hadOverflowBailout_
; }
463 bool hadFrequentBailouts() const { return hadFrequentBailouts_
; }
464 bool mayReadFrameArgsDirectly() const { return mayReadFrameArgsDirectly_
; }
472 mozilla::Maybe
<unsigned> thisSlotForDerivedClassConstructor_
;
476 AnalysisMode analysisMode_
;
478 // Whether a script needs an arguments object is unstable over compilation
479 // since the arguments optimization could be marked as failed on the active
480 // thread, so cache a value here and use it throughout for consistency.
481 bool scriptNeedsArgsObj_
;
483 // Record the state of previous bailouts in order to prevent compiling the
484 // same function identically the next time.
485 bool hadOverflowBailout_
;
486 bool hadFrequentBailouts_
;
488 bool mayReadFrameArgsDirectly_
;
490 InlineScriptTree
* inlineScriptTree_
;
492 // Whether a script needs environments within its body. This informs us
493 // that the environment chain is not easy to reconstruct.
494 bool needsBodyEnvironmentObject_
;
495 bool funNeedsSomeEnvironmentObject_
;
501 #endif /* jit_CompileInfo_h */