560 lines
17 KiB
C++
560 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef jit_CompileInfo_h
|
|
#define jit_CompileInfo_h
|
|
|
|
#include "mozilla/Maybe.h"
|
|
|
|
#include "jsfun.h"
|
|
|
|
#include "jit/JitAllocPolicy.h"
|
|
#include "jit/JitFrames.h"
|
|
#include "jit/Registers.h"
|
|
#include "vm/EnvironmentObject.h"
|
|
|
|
namespace js {
|
|
namespace jit {
|
|
|
|
class TrackedOptimizations;
|
|
|
|
inline unsigned
|
|
StartArgSlot(JSScript* script)
|
|
{
|
|
// Reserved slots:
|
|
// Slot 0: Environment chain.
|
|
// Slot 1: Return value.
|
|
|
|
// When needed:
|
|
// Slot 2: Argumentsobject.
|
|
|
|
// Note: when updating this, please also update the assert in SnapshotWriter::startFrame
|
|
return 2 + (script->argumentsHasVarBinding() ? 1 : 0);
|
|
}
|
|
|
|
inline unsigned
|
|
CountArgSlots(JSScript* script, JSFunction* fun)
|
|
{
|
|
// Slot x + 0: This value.
|
|
// Slot x + 1: Argument 1.
|
|
// ...
|
|
// Slot x + n: Argument n.
|
|
|
|
// Note: when updating this, please also update the assert in SnapshotWriter::startFrame
|
|
return StartArgSlot(script) + (fun ? fun->nargs() + 1 : 0);
|
|
}
|
|
|
|
|
|
// The compiler at various points needs to be able to store references to the
|
|
// current inline path (the sequence of scripts and call-pcs that lead to the
|
|
// current function being inlined).
|
|
//
|
|
// To support this, the top-level IonBuilder keeps a tree that records the
|
|
// inlinings done during compilation.
|
|
class InlineScriptTree {
|
|
// InlineScriptTree for the caller
|
|
InlineScriptTree* caller_;
|
|
|
|
// PC in the caller corresponding to this script.
|
|
jsbytecode* callerPc_;
|
|
|
|
// Script for this entry.
|
|
JSScript* script_;
|
|
|
|
// Child entries (linked together by nextCallee pointer)
|
|
InlineScriptTree* children_;
|
|
InlineScriptTree* nextCallee_;
|
|
|
|
public:
|
|
InlineScriptTree(InlineScriptTree* caller, jsbytecode* callerPc, JSScript* script)
|
|
: caller_(caller), callerPc_(callerPc), script_(script),
|
|
children_(nullptr), nextCallee_(nullptr)
|
|
{}
|
|
|
|
static InlineScriptTree* New(TempAllocator* allocator, InlineScriptTree* caller,
|
|
jsbytecode* callerPc, JSScript* script);
|
|
|
|
InlineScriptTree* addCallee(TempAllocator* allocator, jsbytecode* callerPc,
|
|
JSScript* calleeScript);
|
|
|
|
InlineScriptTree* caller() const {
|
|
return caller_;
|
|
}
|
|
|
|
bool isOutermostCaller() const {
|
|
return caller_ == nullptr;
|
|
}
|
|
bool hasCaller() const {
|
|
return caller_ != nullptr;
|
|
}
|
|
InlineScriptTree* outermostCaller() {
|
|
if (isOutermostCaller())
|
|
return this;
|
|
return caller_->outermostCaller();
|
|
}
|
|
|
|
jsbytecode* callerPc() const {
|
|
return callerPc_;
|
|
}
|
|
|
|
JSScript* script() const {
|
|
return script_;
|
|
}
|
|
|
|
bool hasChildren() const {
|
|
return children_ != nullptr;
|
|
}
|
|
InlineScriptTree* firstChild() const {
|
|
MOZ_ASSERT(hasChildren());
|
|
return children_;
|
|
}
|
|
|
|
bool hasNextCallee() const {
|
|
return nextCallee_ != nullptr;
|
|
}
|
|
InlineScriptTree* nextCallee() const {
|
|
MOZ_ASSERT(hasNextCallee());
|
|
return nextCallee_;
|
|
}
|
|
|
|
unsigned depth() const {
|
|
if (isOutermostCaller())
|
|
return 1;
|
|
return 1 + caller_->depth();
|
|
}
|
|
};
|
|
|
|
class BytecodeSite : public TempObject
|
|
{
|
|
// InlineScriptTree identifying innermost active function at site.
|
|
InlineScriptTree* tree_;
|
|
|
|
// Bytecode address within innermost active function.
|
|
jsbytecode* pc_;
|
|
|
|
// Optimization information at the pc.
|
|
TrackedOptimizations* optimizations_;
|
|
|
|
public:
|
|
BytecodeSite()
|
|
: tree_(nullptr), pc_(nullptr), optimizations_(nullptr)
|
|
{}
|
|
|
|
BytecodeSite(InlineScriptTree* tree, jsbytecode* pc)
|
|
: tree_(tree), pc_(pc), optimizations_(nullptr)
|
|
{
|
|
MOZ_ASSERT(tree_ != nullptr);
|
|
MOZ_ASSERT(pc_ != nullptr);
|
|
}
|
|
|
|
InlineScriptTree* tree() const {
|
|
return tree_;
|
|
}
|
|
|
|
jsbytecode* pc() const {
|
|
return pc_;
|
|
}
|
|
|
|
JSScript* script() const {
|
|
return tree_ ? tree_->script() : nullptr;
|
|
}
|
|
|
|
bool hasOptimizations() const {
|
|
return !!optimizations_;
|
|
}
|
|
|
|
TrackedOptimizations* optimizations() const {
|
|
MOZ_ASSERT(hasOptimizations());
|
|
return optimizations_;
|
|
}
|
|
|
|
void setOptimizations(TrackedOptimizations* optimizations) {
|
|
optimizations_ = optimizations;
|
|
}
|
|
};
|
|
|
|
enum AnalysisMode {
|
|
/* JavaScript execution, not analysis. */
|
|
Analysis_None,
|
|
|
|
/*
|
|
* MIR analysis performed when invoking 'new' on a script, to determine
|
|
* definite properties. Used by the optimizing JIT.
|
|
*/
|
|
Analysis_DefiniteProperties,
|
|
|
|
/*
|
|
* MIR analysis performed when executing a script which uses its arguments,
|
|
* when it is not known whether a lazy arguments value can be used.
|
|
*/
|
|
Analysis_ArgumentsUsage
|
|
};
|
|
|
|
// Contains information about the compilation source for IR being generated.
|
|
class CompileInfo
|
|
{
|
|
public:
|
|
CompileInfo(JSScript* script, JSFunction* fun, jsbytecode* osrPc,
|
|
AnalysisMode analysisMode, bool scriptNeedsArgsObj,
|
|
InlineScriptTree* inlineScriptTree)
|
|
: script_(script), fun_(fun), osrPc_(osrPc),
|
|
analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
|
|
hadOverflowBailout_(script->hadOverflowBailout()),
|
|
mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
|
|
inlineScriptTree_(inlineScriptTree)
|
|
{
|
|
MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
|
|
|
|
// The function here can flow in from anywhere so look up the canonical
|
|
// function to ensure that we do not try to embed a nursery pointer in
|
|
// jit-code. Precisely because it can flow in from anywhere, it's not
|
|
// guaranteed to be non-lazy. Hence, don't access its script!
|
|
if (fun_) {
|
|
fun_ = fun_->nonLazyScript()->functionNonDelazifying();
|
|
MOZ_ASSERT(fun_->isTenured());
|
|
}
|
|
|
|
nimplicit_ = StartArgSlot(script) /* env chain and argument obj */
|
|
+ (fun ? 1 : 0); /* this */
|
|
nargs_ = fun ? fun->nargs() : 0;
|
|
nlocals_ = script->nfixed();
|
|
|
|
// An extra slot is needed for global scopes because INITGLEXICAL (stack
|
|
// depth 1) is compiled as a SETPROP (stack depth 2) on the global lexical
|
|
// scope.
|
|
uint32_t extra = script->isGlobalCode() ? 1 : 0;
|
|
nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize) + extra;
|
|
nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
|
|
|
|
// For derived class constructors, find and cache the frame slot for
|
|
// the .this binding. This slot is assumed to be always
|
|
// observable. See isObservableFrameSlot.
|
|
if (script->isDerivedClassConstructor()) {
|
|
MOZ_ASSERT(script->functionHasThisBinding());
|
|
CompileRuntime* runtime = GetJitContext()->runtime;
|
|
for (BindingIter bi(script); bi; bi++) {
|
|
if (bi.name() != runtime->names().dotThis)
|
|
continue;
|
|
BindingLocation loc = bi.location();
|
|
if (loc.kind() == BindingLocation::Kind::Frame) {
|
|
thisSlotForDerivedClassConstructor_ = mozilla::Some(localSlot(loc.slot()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
explicit CompileInfo(unsigned nlocals)
|
|
: script_(nullptr), fun_(nullptr), osrPc_(nullptr),
|
|
analysisMode_(Analysis_None), scriptNeedsArgsObj_(false),
|
|
mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr)
|
|
{
|
|
nimplicit_ = 0;
|
|
nargs_ = 0;
|
|
nlocals_ = nlocals;
|
|
nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */
|
|
nslots_ = nlocals_ + nstack_;
|
|
}
|
|
|
|
JSScript* script() const {
|
|
return script_;
|
|
}
|
|
bool compilingWasm() const {
|
|
return script() == nullptr;
|
|
}
|
|
JSFunction* funMaybeLazy() const {
|
|
return fun_;
|
|
}
|
|
ModuleObject* module() const {
|
|
return script_->module();
|
|
}
|
|
jsbytecode* osrPc() const {
|
|
return osrPc_;
|
|
}
|
|
InlineScriptTree* inlineScriptTree() const {
|
|
return inlineScriptTree_;
|
|
}
|
|
|
|
bool hasOsrAt(jsbytecode* pc) const {
|
|
MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
|
|
return pc == osrPc();
|
|
}
|
|
|
|
jsbytecode* startPC() const {
|
|
return script_->code();
|
|
}
|
|
jsbytecode* limitPC() const {
|
|
return script_->codeEnd();
|
|
}
|
|
|
|
const char* filename() const {
|
|
return script_->filename();
|
|
}
|
|
|
|
unsigned lineno() const {
|
|
return script_->lineno();
|
|
}
|
|
unsigned lineno(jsbytecode* pc) const {
|
|
return PCToLineNumber(script_, pc);
|
|
}
|
|
|
|
// Script accessors based on PC.
|
|
|
|
JSAtom* getAtom(jsbytecode* pc) const {
|
|
return script_->getAtom(GET_UINT32_INDEX(pc));
|
|
}
|
|
|
|
PropertyName* getName(jsbytecode* pc) const {
|
|
return script_->getName(GET_UINT32_INDEX(pc));
|
|
}
|
|
|
|
inline RegExpObject* getRegExp(jsbytecode* pc) const;
|
|
|
|
JSObject* getObject(jsbytecode* pc) const {
|
|
return script_->getObject(GET_UINT32_INDEX(pc));
|
|
}
|
|
|
|
inline JSFunction* getFunction(jsbytecode* pc) const;
|
|
|
|
const Value& getConst(jsbytecode* pc) const {
|
|
return script_->getConst(GET_UINT32_INDEX(pc));
|
|
}
|
|
|
|
jssrcnote* getNote(GSNCache& gsn, jsbytecode* pc) const {
|
|
return GetSrcNote(gsn, script(), pc);
|
|
}
|
|
|
|
// Total number of slots: args, locals, and stack.
|
|
unsigned nslots() const {
|
|
return nslots_;
|
|
}
|
|
|
|
// Number of slots needed for env chain, return value,
|
|
// maybe argumentsobject and this value.
|
|
unsigned nimplicit() const {
|
|
return nimplicit_;
|
|
}
|
|
// Number of arguments (without counting this value).
|
|
unsigned nargs() const {
|
|
return nargs_;
|
|
}
|
|
// Number of slots needed for all local variables. This includes "fixed
|
|
// vars" (see above) and also block-scoped locals.
|
|
unsigned nlocals() const {
|
|
return nlocals_;
|
|
}
|
|
unsigned ninvoke() const {
|
|
return nslots_ - nstack_;
|
|
}
|
|
|
|
uint32_t environmentChainSlot() const {
|
|
MOZ_ASSERT(script());
|
|
return 0;
|
|
}
|
|
uint32_t returnValueSlot() const {
|
|
MOZ_ASSERT(script());
|
|
return 1;
|
|
}
|
|
uint32_t argsObjSlot() const {
|
|
MOZ_ASSERT(hasArguments());
|
|
return 2;
|
|
}
|
|
uint32_t thisSlot() const {
|
|
MOZ_ASSERT(funMaybeLazy());
|
|
MOZ_ASSERT(nimplicit_ > 0);
|
|
return nimplicit_ - 1;
|
|
}
|
|
uint32_t firstArgSlot() const {
|
|
return nimplicit_;
|
|
}
|
|
uint32_t argSlotUnchecked(uint32_t i) const {
|
|
// During initialization, some routines need to get at arg
|
|
// slots regardless of how regular argument access is done.
|
|
MOZ_ASSERT(i < nargs_);
|
|
return nimplicit_ + i;
|
|
}
|
|
uint32_t argSlot(uint32_t i) const {
|
|
// This should only be accessed when compiling functions for
|
|
// which argument accesses don't need to go through the
|
|
// argument object.
|
|
MOZ_ASSERT(!argsObjAliasesFormals());
|
|
return argSlotUnchecked(i);
|
|
}
|
|
uint32_t firstLocalSlot() const {
|
|
return nimplicit_ + nargs_;
|
|
}
|
|
uint32_t localSlot(uint32_t i) const {
|
|
return firstLocalSlot() + i;
|
|
}
|
|
uint32_t firstStackSlot() const {
|
|
return firstLocalSlot() + nlocals();
|
|
}
|
|
uint32_t stackSlot(uint32_t i) const {
|
|
return firstStackSlot() + i;
|
|
}
|
|
|
|
uint32_t startArgSlot() const {
|
|
MOZ_ASSERT(script());
|
|
return StartArgSlot(script());
|
|
}
|
|
uint32_t endArgSlot() const {
|
|
MOZ_ASSERT(script());
|
|
return CountArgSlots(script(), funMaybeLazy());
|
|
}
|
|
|
|
uint32_t totalSlots() const {
|
|
MOZ_ASSERT(script() && funMaybeLazy());
|
|
return nimplicit() + nargs() + nlocals();
|
|
}
|
|
|
|
bool isSlotAliased(uint32_t index) const {
|
|
MOZ_ASSERT(index >= startArgSlot());
|
|
uint32_t arg = index - firstArgSlot();
|
|
if (arg < nargs())
|
|
return script()->formalIsAliased(arg);
|
|
return false;
|
|
}
|
|
|
|
bool hasArguments() const {
|
|
return script()->argumentsHasVarBinding();
|
|
}
|
|
bool argumentsAliasesFormals() const {
|
|
return script()->argumentsAliasesFormals();
|
|
}
|
|
bool needsArgsObj() const {
|
|
return scriptNeedsArgsObj_;
|
|
}
|
|
bool argsObjAliasesFormals() const {
|
|
return scriptNeedsArgsObj_ && script()->hasMappedArgsObj();
|
|
}
|
|
|
|
AnalysisMode analysisMode() const {
|
|
return analysisMode_;
|
|
}
|
|
|
|
bool isAnalysis() const {
|
|
return analysisMode_ != Analysis_None;
|
|
}
|
|
|
|
// Returns true if a slot can be observed out-side the current frame while
|
|
// the frame is active on the stack. This implies that these definitions
|
|
// would have to be executed and that they cannot be removed even if they
|
|
// are unused.
|
|
bool isObservableSlot(uint32_t slot) const {
|
|
if (isObservableFrameSlot(slot))
|
|
return true;
|
|
|
|
if (isObservableArgumentSlot(slot))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isObservableFrameSlot(uint32_t slot) const {
|
|
if (!funMaybeLazy())
|
|
return false;
|
|
|
|
// The |this| value must always be observable.
|
|
if (slot == thisSlot())
|
|
return true;
|
|
|
|
// The |this| frame slot in derived class constructors should never be
|
|
// optimized out, as a Debugger might need to perform TDZ checks on it
|
|
// via, e.g., an exceptionUnwind handler. The TDZ check is required
|
|
// for correctness if the handler decides to continue execution.
|
|
if (thisSlotForDerivedClassConstructor_ && *thisSlotForDerivedClassConstructor_ == slot)
|
|
return true;
|
|
|
|
if (funMaybeLazy()->needsSomeEnvironmentObject() && slot == environmentChainSlot())
|
|
return true;
|
|
|
|
// If the function may need an arguments object, then make sure to
|
|
// preserve the env chain, because it may be needed to construct the
|
|
// arguments object during bailout. If we've already created an
|
|
// arguments object (or got one via OSR), preserve that as well.
|
|
if (hasArguments() && (slot == environmentChainSlot() || slot == argsObjSlot()))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isObservableArgumentSlot(uint32_t slot) const {
|
|
if (!funMaybeLazy())
|
|
return false;
|
|
|
|
// Function.arguments can be used to access all arguments in non-strict
|
|
// scripts, so we can't optimize out any arguments.
|
|
if ((mayReadFrameArgsDirectly_ || !script()->strict()) &&
|
|
firstArgSlot() <= slot && slot - firstArgSlot() < nargs())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Returns true if a slot can be recovered before or during a bailout. A
|
|
// definition which can be observed and recovered, implies that this
|
|
// definition can be optimized away as long as we can compute its values.
|
|
bool isRecoverableOperand(uint32_t slot) const {
|
|
// If this script is not a function, then none of the slots are
|
|
// observable. If it this |slot| is not observable, thus we can always
|
|
// recover it.
|
|
if (!funMaybeLazy())
|
|
return true;
|
|
|
|
// The |this| and the |envChain| values can be recovered.
|
|
if (slot == thisSlot() || slot == environmentChainSlot())
|
|
return true;
|
|
|
|
if (isObservableFrameSlot(slot))
|
|
return false;
|
|
|
|
if (needsArgsObj() && isObservableArgumentSlot(slot))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check previous bailout states to prevent doing the same bailout in the
|
|
// next compilation.
|
|
bool hadOverflowBailout() const {
|
|
return hadOverflowBailout_;
|
|
}
|
|
bool mayReadFrameArgsDirectly() const {
|
|
return mayReadFrameArgsDirectly_;
|
|
}
|
|
|
|
private:
|
|
unsigned nimplicit_;
|
|
unsigned nargs_;
|
|
unsigned nlocals_;
|
|
unsigned nstack_;
|
|
unsigned nslots_;
|
|
mozilla::Maybe<unsigned> thisSlotForDerivedClassConstructor_;
|
|
JSScript* script_;
|
|
JSFunction* fun_;
|
|
jsbytecode* osrPc_;
|
|
AnalysisMode analysisMode_;
|
|
|
|
// Whether a script needs an arguments object is unstable over compilation
|
|
// since the arguments optimization could be marked as failed on the main
|
|
// thread, so cache a value here and use it throughout for consistency.
|
|
bool scriptNeedsArgsObj_;
|
|
|
|
// Record the state of previous bailouts in order to prevent compiling the
|
|
// same function identically the next time.
|
|
bool hadOverflowBailout_;
|
|
|
|
bool mayReadFrameArgsDirectly_;
|
|
|
|
InlineScriptTree* inlineScriptTree_;
|
|
};
|
|
|
|
} // namespace jit
|
|
} // namespace js
|
|
|
|
#endif /* jit_CompileInfo_h */
|