blob: 72b480ddd132c8cf94b28ff153a945ea844f2f81 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "jsfun.h"
#include "jit/JitAllocPolicy.h"
#include "jit/JitFrames.h"
#include "jit/Registers.h"
#include "vm/ScopeObject.h"
namespace js {
namespace jit {
class TrackedOptimizations;
inline unsigned
StartArgSlot(JSScript* script)
{
// Reserved slots:
// Slot 0: Scope 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, bool constructing,
AnalysisMode analysisMode, bool scriptNeedsArgsObj,
InlineScriptTree* inlineScriptTree)
: script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
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());
}
osrStaticScope_ = osrPc ? script->getStaticBlockScope(osrPc) : nullptr;
nimplicit_ = StartArgSlot(script) /* scope chain and argument obj */
+ (fun ? 1 : 0); /* this */
nargs_ = fun ? fun->nargs() : 0;
nbodyfixed_ = script->nbodyfixed();
nlocals_ = script->nfixed();
fixedLexicalBegin_ = script->fixedLexicalBegin();
nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize);
nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
}
explicit CompileInfo(unsigned nlocals)
: script_(nullptr), fun_(nullptr), osrPc_(nullptr), osrStaticScope_(nullptr),
constructing_(false), analysisMode_(Analysis_None), scriptNeedsArgsObj_(false),
mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr)
{
nimplicit_ = 0;
nargs_ = 0;
nbodyfixed_ = 0;
nlocals_ = nlocals;
nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */
nslots_ = nlocals_ + nstack_;
fixedLexicalBegin_ = nlocals;
}
JSScript* script() const {
return script_;
}
bool compilingAsmJS() const {
return script() == nullptr;
}
JSFunction* funMaybeLazy() const {
return fun_;
}
ModuleObject* module() const {
return script_->module();
}
bool constructing() const {
return constructing_;
}
jsbytecode* osrPc() const {
return osrPc_;
}
NestedScopeObject* osrStaticScope() const {
return osrStaticScope_;
}
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 Scope 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 fixed body-level bindings. Note that this
// is only non-zero for function code.
unsigned nbodyfixed() const {
return nbodyfixed_;
}
// 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_;
}
// The slot number at which fixed lexicals begin.
unsigned fixedLexicalBegin() const {
return fixedLexicalBegin_;
}
uint32_t scopeChainSlot() 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, NestedScopeObject* staticScope) const {
MOZ_ASSERT(index >= startArgSlot());
if (funMaybeLazy() && index == thisSlot())
return false;
uint32_t arg = index - firstArgSlot();
if (arg < nargs())
return script()->formalIsAliased(arg);
uint32_t local = index - firstLocalSlot();
if (local < nlocals()) {
// First, check if this local is body-level. If we have a slot for
// it, it is by definition unaliased. Aliased body-level locals do
// not have fixed slots on the frame and live in the CallObject.
//
// Note that this is not true for lexical (block-scoped)
// bindings. Such bindings, even when aliased, may be considered
// part of the "fixed" part (< nlocals()) of the frame.
if (local < nbodyfixed())
return false;
// Otherwise, it might be part of a block scope.
for (; staticScope; staticScope = staticScope->enclosingNestedScope()) {
if (!staticScope->is<StaticBlockObject>())
continue;
StaticBlockObject& blockObj = staticScope->as<StaticBlockObject>();
if (blockObj.localOffset() < local) {
if (local - blockObj.localOffset() < blockObj.numVariables())
return blockObj.isAliased(local - blockObj.localOffset());
return false;
}
}
// In this static scope, this var is dead.
return false;
}
MOZ_ASSERT(index >= firstStackSlot());
return false;
}
bool isSlotAliasedAtEntry(uint32_t index) const {
return isSlotAliased(index, nullptr);
}
bool isSlotAliasedAtOsr(uint32_t index) const {
return isSlotAliased(index, osrStaticScope());
}
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;
if (funMaybeLazy()->needsCallObject() && slot == scopeChainSlot())
return true;
// If the function may need an arguments object, then make sure to
// preserve the scope 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 == scopeChainSlot() || 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 ((hasArguments() || !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 |scopeChain| values can be recovered.
if (slot == thisSlot() || slot == scopeChainSlot())
return true;
if (isObservableFrameSlot(slot))
return false;
if (needsArgsObj() && isObservableArgumentSlot(slot))
return false;
return true;
}
bool mayReadFrameArgsDirectly() const {
return mayReadFrameArgsDirectly_;
}
private:
unsigned nimplicit_;
unsigned nargs_;
unsigned nbodyfixed_;
unsigned nlocals_;
unsigned nstack_;
unsigned nslots_;
unsigned fixedLexicalBegin_;
JSScript* script_;
JSFunction* fun_;
jsbytecode* osrPc_;
NestedScopeObject* osrStaticScope_;
bool constructing_;
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_;
bool mayReadFrameArgsDirectly_;
InlineScriptTree* inlineScriptTree_;
};
} // namespace jit
} // namespace js
#endif /* jit_CompileInfo_h */