blob: d786f33a6dc79673da2f89c650c215ece58ba1a9 [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_IonBuilder_h
#define jit_IonBuilder_h
// This file declares the data structures for building a MIRGraph from a
// JSScript.
#include "mozilla/LinkedList.h"
#include "jit/BaselineInspector.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/IonAnalysis.h"
#include "jit/IonOptimizationLevels.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "jit/OptimizationTracking.h"
namespace js {
namespace jit {
class CodeGenerator;
class CallInfo;
class BaselineFrameInspector;
enum class InlinableNative : uint16_t;
// Records information about a baseline frame for compilation that is stable
// when later used off thread.
BaselineFrameInspector*
NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame, CompileInfo* info);
class IonBuilder
: public MIRGenerator,
public mozilla::LinkedListElement<IonBuilder>
{
enum ControlStatus {
ControlStatus_Error,
ControlStatus_Abort,
ControlStatus_Ended, // There is no continuation/join point.
ControlStatus_Joined, // Created a join node.
ControlStatus_Jumped, // Parsing another branch at the same level.
ControlStatus_None // No control flow.
};
struct DeferredEdge : public TempObject
{
MBasicBlock* block;
DeferredEdge* next;
DeferredEdge(MBasicBlock* block, DeferredEdge* next)
: block(block), next(next)
{ }
};
struct ControlFlowInfo {
// Entry in the cfgStack.
uint32_t cfgEntry;
// Label that continues go to.
jsbytecode* continuepc;
ControlFlowInfo(uint32_t cfgEntry, jsbytecode* continuepc)
: cfgEntry(cfgEntry),
continuepc(continuepc)
{ }
};
// To avoid recursion, the bytecode analyzer uses a stack where each entry
// is a small state machine. As we encounter branches or jumps in the
// bytecode, we push information about the edges on the stack so that the
// CFG can be built in a tree-like fashion.
struct CFGState {
enum State {
IF_TRUE, // if() { }, no else.
IF_TRUE_EMPTY_ELSE, // if() { }, empty else
IF_ELSE_TRUE, // if() { X } else { }
IF_ELSE_FALSE, // if() { } else { X }
DO_WHILE_LOOP_BODY, // do { x } while ()
DO_WHILE_LOOP_COND, // do { } while (x)
WHILE_LOOP_COND, // while (x) { }
WHILE_LOOP_BODY, // while () { x }
FOR_LOOP_COND, // for (; x;) { }
FOR_LOOP_BODY, // for (; ;) { x }
FOR_LOOP_UPDATE, // for (; ; x) { }
TABLE_SWITCH, // switch() { x }
COND_SWITCH_CASE, // switch() { case X: ... }
COND_SWITCH_BODY, // switch() { case ...: X }
AND_OR, // && x, || x
LABEL, // label: x
TRY // try { x } catch(e) { }
};
State state; // Current state of this control structure.
jsbytecode* stopAt; // Bytecode at which to stop the processing loop.
// For if structures, this contains branch information.
union {
struct {
MBasicBlock* ifFalse;
jsbytecode* falseEnd;
MBasicBlock* ifTrue; // Set when the end of the true path is reached.
MTest* test;
} branch;
struct {
// Common entry point.
MBasicBlock* entry;
// Whether OSR is being performed for this loop.
bool osr;
// Position of where the loop body starts and ends.
jsbytecode* bodyStart;
jsbytecode* bodyEnd;
// pc immediately after the loop exits.
jsbytecode* exitpc;
// pc for 'continue' jumps.
jsbytecode* continuepc;
// Common exit point. Created lazily, so it may be nullptr.
MBasicBlock* successor;
// Deferred break and continue targets.
DeferredEdge* breaks;
DeferredEdge* continues;
// Initial state, in case loop processing is restarted.
State initialState;
jsbytecode* initialPc;
jsbytecode* initialStopAt;
jsbytecode* loopHead;
// For-loops only.
jsbytecode* condpc;
jsbytecode* updatepc;
jsbytecode* updateEnd;
} loop;
struct {
// pc immediately after the switch.
jsbytecode* exitpc;
// Deferred break and continue targets.
DeferredEdge* breaks;
// MIR instruction
MTableSwitch* ins;
// The number of current successor that get mapped into a block.
uint32_t currentBlock;
} tableswitch;
struct {
// Vector of body blocks to process after the cases.
FixedList<MBasicBlock*>* bodies;
// When processing case statements, this counter points at the
// last uninitialized body. When processing bodies, this
// counter targets the next body to process.
uint32_t currentIdx;
// Remember the block index of the default case.
jsbytecode* defaultTarget;
uint32_t defaultIdx;
// Block immediately after the switch.
jsbytecode* exitpc;
DeferredEdge* breaks;
} condswitch;
struct {
DeferredEdge* breaks;
} label;
struct {
MBasicBlock* successor;
} try_;
};
inline bool isLoop() const {
switch (state) {
case DO_WHILE_LOOP_COND:
case DO_WHILE_LOOP_BODY:
case WHILE_LOOP_COND:
case WHILE_LOOP_BODY:
case FOR_LOOP_COND:
case FOR_LOOP_BODY:
case FOR_LOOP_UPDATE:
return true;
default:
return false;
}
}
static CFGState If(jsbytecode* join, MTest* test);
static CFGState IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, MTest* test);
static CFGState AndOr(jsbytecode* join, MBasicBlock* lhs);
static CFGState TableSwitch(jsbytecode* exitpc, MTableSwitch* ins);
static CFGState CondSwitch(IonBuilder* builder, jsbytecode* exitpc, jsbytecode* defaultTarget);
static CFGState Label(jsbytecode* exitpc);
static CFGState Try(jsbytecode* exitpc, MBasicBlock* successor);
};
static int CmpSuccessors(const void* a, const void* b);
public:
IonBuilder(JSContext* analysisContext, CompileCompartment* comp,
const JitCompileOptions& options, TempAllocator* temp,
MIRGraph* graph, CompilerConstraintList* constraints,
BaselineInspector* inspector, CompileInfo* info,
const OptimizationInfo* optimizationInfo, BaselineFrameInspector* baselineFrame,
size_t inliningDepth = 0, uint32_t loopDepth = 0);
// Callers of build() and buildInline() should always check whether the
// call overrecursed, if false is returned. Overrecursion is not
// signaled as OOM and will not in general be caught by OOM paths.
bool build();
bool buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoint,
CallInfo& callInfo);
private:
bool traverseBytecode();
ControlStatus snoopControlFlow(JSOp op);
bool processIterators();
bool inspectOpcode(JSOp op);
uint32_t readIndex(jsbytecode* pc);
JSAtom* readAtom(jsbytecode* pc);
bool abort(const char* message, ...);
void trackActionableAbort(const char* message);
void spew(const char* message);
JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes);
bool getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
ObjectVector& targets, uint32_t maxTargets);
void popCfgStack();
DeferredEdge* filterDeadDeferredEdges(DeferredEdge* edge);
bool processDeferredContinues(CFGState& state);
ControlStatus processControlEnd();
ControlStatus processCfgStack();
ControlStatus processCfgEntry(CFGState& state);
ControlStatus processIfEnd(CFGState& state);
ControlStatus processIfElseTrueEnd(CFGState& state);
ControlStatus processIfElseFalseEnd(CFGState& state);
ControlStatus processDoWhileBodyEnd(CFGState& state);
ControlStatus processDoWhileCondEnd(CFGState& state);
ControlStatus processWhileCondEnd(CFGState& state);
ControlStatus processWhileBodyEnd(CFGState& state);
ControlStatus processForCondEnd(CFGState& state);
ControlStatus processForBodyEnd(CFGState& state);
ControlStatus processForUpdateEnd(CFGState& state);
ControlStatus processNextTableSwitchCase(CFGState& state);
ControlStatus processCondSwitchCase(CFGState& state);
ControlStatus processCondSwitchBody(CFGState& state);
ControlStatus processSwitchBreak(JSOp op);
ControlStatus processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc);
ControlStatus processAndOrEnd(CFGState& state);
ControlStatus processLabelEnd(CFGState& state);
ControlStatus processTryEnd(CFGState& state);
ControlStatus processReturn(JSOp op);
ControlStatus processThrow();
ControlStatus processContinue(JSOp op);
ControlStatus processBreak(JSOp op, jssrcnote* sn);
ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
bool pushLoop(CFGState::State state, jsbytecode* stopAt, MBasicBlock* entry, bool osr,
jsbytecode* loopHead, jsbytecode* initialPc,
jsbytecode* bodyStart, jsbytecode* bodyEnd,
jsbytecode* exitpc, jsbytecode* continuepc);
bool analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecode* end);
MBasicBlock* addBlock(MBasicBlock* block, uint32_t loopDepth);
MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc);
MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, uint32_t loopDepth);
MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint);
MBasicBlock* newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped);
MBasicBlock* newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc);
MBasicBlock* newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry,
jsbytecode* beforeLoopEntry);
MBasicBlock* newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
unsigned stackPhiCount);
MBasicBlock* newBlock(jsbytecode* pc) {
return newBlock(nullptr, pc);
}
MBasicBlock* newBlockAfter(MBasicBlock* at, jsbytecode* pc) {
return newBlockAfter(at, nullptr, pc);
}
// We want to make sure that our MTest instructions all check whether the
// thing being tested might emulate undefined. So we funnel their creation
// through this method, to make sure that happens. We don't want to just do
// the check in MTest::New, because that can run on background compilation
// threads, and we're not sure it's safe to touch that part of the typeset
// from a background thread.
MTest* newTest(MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse);
// Given a list of pending breaks, creates a new block and inserts a Goto
// linking each break to the new block.
MBasicBlock* createBreakCatchBlock(DeferredEdge* edge, jsbytecode* pc);
// Finishes loops that do not actually loop, containing only breaks and
// returns or a do while loop with a condition that is constant false.
ControlStatus processBrokenLoop(CFGState& state);
// Computes loop phis, places them in all successors of a loop, then
// handles any pending breaks.
ControlStatus finishLoop(CFGState& state, MBasicBlock* successor);
// Incorporates a type/typeSet into an OSR value for a loop, after the loop
// body has been processed.
bool addOsrValueTypeBarrier(uint32_t slot, MInstruction** def,
MIRType type, TemporaryTypeSet* typeSet);
bool maybeAddOsrTypeBarriers();
// Restarts processing of a loop if the type information at its header was
// incomplete.
ControlStatus restartLoop(CFGState state);
void assertValidLoopHeadOp(jsbytecode* pc);
ControlStatus forLoop(JSOp op, jssrcnote* sn);
ControlStatus whileOrForInLoop(jssrcnote* sn);
ControlStatus doWhileLoop(JSOp op, jssrcnote* sn);
ControlStatus tableSwitch(JSOp op, jssrcnote* sn);
ControlStatus condSwitch(JSOp op, jssrcnote* sn);
// Please see the Big Honkin' Comment about how resume points work in
// IonBuilder.cpp, near the definition for this function.
bool resume(MInstruction* ins, jsbytecode* pc, MResumePoint::Mode mode);
bool resumeAt(MInstruction* ins, jsbytecode* pc);
bool resumeAfter(MInstruction* ins);
bool maybeInsertResume();
void insertRecompileCheck();
void initParameters();
void initLocals();
void rewriteParameter(uint32_t slotIdx, MDefinition* param, int32_t argIndex);
void rewriteParameters();
bool initScopeChain(MDefinition* callee = nullptr);
bool initArgumentsObject();
bool pushConstant(const Value& v);
MConstant* constant(const Value& v);
MConstant* constantInt(int32_t i);
MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
JSValueType unboxedType);
MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count);
// Improve the type information at tests
bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test);
bool improveTypesAtCompare(MCompare* ins, bool trueBranch, MTest* test);
bool improveTypesAtNullOrUndefinedCompare(MCompare* ins, bool trueBranch, MTest* test);
bool improveTypesAtTypeOfCompare(MCompare* ins, bool trueBranch, MTest* test);
// Used to detect triangular structure at test.
bool detectAndOrStructure(MPhi* ins, bool* branchIsTrue);
bool replaceTypeSet(MDefinition* subject, TemporaryTypeSet* type, MTest* test);
// Add a guard which ensure that the set of type which goes through this
// generated code correspond to the observed types for the bytecode.
MDefinition* addTypeBarrier(MDefinition* def, TemporaryTypeSet* observed,
BarrierKind kind, MTypeBarrier** pbarrier = nullptr);
bool pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind);
// As pushTypeBarrier, but will compute the needBarrier boolean itself based
// on observed and the JSFunction that we're planning to call. The
// JSFunction must be a DOM method or getter.
bool pushDOMTypeBarrier(MInstruction* ins, TemporaryTypeSet* observed, JSFunction* func);
// If definiteType is not known or def already has the right type, just
// returns def. Otherwise, returns an MInstruction that has that definite
// type, infallibly unboxing ins as needed. The new instruction will be
// added to |current| in this case.
MDefinition* ensureDefiniteType(MDefinition* def, MIRType definiteType);
// Creates a MDefinition based on the given def improved with type as TypeSet.
MDefinition* ensureDefiniteTypeSet(MDefinition* def, TemporaryTypeSet* types);
void maybeMarkEmpty(MDefinition* ins);
JSObject* getSingletonPrototype(JSFunction* target);
MDefinition* createThisScripted(MDefinition* callee, MDefinition* newTarget);
MDefinition* createThisScriptedSingleton(JSFunction* target, MDefinition* callee);
MDefinition* createThisScriptedBaseline(MDefinition* callee);
MDefinition* createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget);
MInstruction* createDeclEnvObject(MDefinition* callee, MDefinition* scopeObj);
MInstruction* createCallObject(MDefinition* callee, MDefinition* scopeObj);
MDefinition* walkScopeChain(unsigned hops);
MInstruction* addConvertElementsToDoubles(MDefinition* elements);
MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind);
MInstruction* addSharedTypedArrayGuard(MDefinition* obj);
MInstruction*
addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers);
MDefinition* convertShiftToMaskForStaticTypedArray(MDefinition* id,
Scalar::Type viewType);
bool invalidatedIdempotentCache();
bool hasStaticScopeObject(ScopeCoordinate sc, JSObject** pcall);
bool loadSlot(MDefinition* obj, size_t slot, size_t nfixed, MIRType rvalType,
BarrierKind barrier, TemporaryTypeSet* types);
bool loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType,
BarrierKind barrier, TemporaryTypeSet* types);
bool storeSlot(MDefinition* obj, size_t slot, size_t nfixed,
MDefinition* value, bool needsBarrier,
MIRType slotType = MIRType_None);
bool storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier,
MIRType slotType = MIRType_None);
bool shouldAbortOnPreliminaryGroups(MDefinition *obj);
MDefinition* tryInnerizeWindow(MDefinition* obj);
MDefinition* maybeUnboxForPropertyAccess(MDefinition* def);
// jsop_getprop() helpers.
bool checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs);
bool getPropTryInferredConstant(bool* emitted, MDefinition* obj, PropertyName* name,
TemporaryTypeSet* types);
bool getPropTryArgumentsLength(bool* emitted, MDefinition* obj);
bool getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyName* name);
bool getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types);
bool getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
TemporaryTypeSet* types);
bool getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
bool getPropTrySimdGetter(bool* emitted, MDefinition* obj, PropertyName* name);
bool getPropTryTypedObject(bool* emitted, MDefinition* obj, PropertyName* name);
bool getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj,
int32_t fieldOffset,
TypedObjectPrediction fieldTypeReprs);
bool getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj,
int32_t fieldOffset,
TypedObjectPrediction fieldPrediction,
PropertyName* name);
bool getPropTryComplexPropOfTypedObject(bool* emitted, MDefinition* typedObj,
int32_t fieldOffset,
TypedObjectPrediction fieldTypeReprs,
size_t fieldIndex);
bool getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* name,
TemporaryTypeSet* types);
bool getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
bool getPropTrySharedStub(bool* emitted, MDefinition* obj);
// jsop_setprop() helpers.
bool setPropTryCommonSetter(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value);
bool setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
MDefinition* value, JSFunction* setter,
TemporaryTypeSet* objTypes);
bool setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
bool barrier, TemporaryTypeSet* objTypes);
bool setPropTryUnboxed(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
bool barrier, TemporaryTypeSet* objTypes);
bool setPropTryInlineAccess(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
bool barrier, TemporaryTypeSet* objTypes);
bool setPropTryTypedObject(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value);
bool setPropTryReferencePropOfTypedObject(bool* emitted,
MDefinition* obj,
int32_t fieldOffset,
MDefinition* value,
TypedObjectPrediction fieldPrediction,
PropertyName* name);
bool setPropTryScalarPropOfTypedObject(bool* emitted,
MDefinition* obj,
int32_t fieldOffset,
MDefinition* value,
TypedObjectPrediction fieldTypeReprs);
bool setPropTryCache(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
bool barrier, TemporaryTypeSet* objTypes);
// jsop_binary_arith helpers.
MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
bool arithTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
// jsop_bitnot helpers.
bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
// jsop_compare helpes.
bool compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
bool compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
bool compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
MDefinition* right);
bool compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
// binary data lookup helpers.
TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
bool typedObjectHasField(MDefinition* typedObj,
PropertyName* name,
size_t* fieldOffset,
TypedObjectPrediction* fieldTypeReprs,
size_t* fieldIndex);
MDefinition* loadTypedObjectType(MDefinition* value);
void loadTypedObjectData(MDefinition* typedObj,
MDefinition** owner,
LinearSum* ownerOffset);
void loadTypedObjectElements(MDefinition* typedObj,
const LinearSum& byteOffset,
int32_t scale,
MDefinition** ownerElements,
MDefinition** ownerScaledOffset,
int32_t* ownerByteAdjustment);
MDefinition* typeObjectForElementFromArrayStructType(MDefinition* typedObj);
MDefinition* typeObjectForFieldFromStructType(MDefinition* type,
size_t fieldIndex);
bool storeReferenceTypedObjectValue(MDefinition* typedObj,
const LinearSum& byteOffset,
ReferenceTypeDescr::Type type,
MDefinition* value,
PropertyName* name);
bool storeScalarTypedObjectValue(MDefinition* typedObj,
const LinearSum& byteOffset,
ScalarTypeDescr::Type type,
MDefinition* value);
bool checkTypedObjectIndexInBounds(int32_t elemSize,
MDefinition* obj,
MDefinition* index,
TypedObjectPrediction objTypeDescrs,
LinearSum* indexAsByteOffset);
bool pushDerivedTypedObject(bool* emitted,
MDefinition* obj,
const LinearSum& byteOffset,
TypedObjectPrediction derivedTypeDescrs,
MDefinition* derivedTypeObj);
bool pushScalarLoadFromTypedObject(MDefinition* obj,
const LinearSum& byteoffset,
ScalarTypeDescr::Type type);
bool pushReferenceLoadFromTypedObject(MDefinition* typedObj,
const LinearSum& byteOffset,
ReferenceTypeDescr::Type type,
PropertyName* name);
MDefinition* neuterCheck(MDefinition* obj);
JSObject* getStaticTypedArrayObject(MDefinition* obj, MDefinition* index);
// jsop_setelem() helpers.
bool setElemTryTypedArray(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
bool setElemTryTypedObject(bool* emitted, MDefinition* obj,
MDefinition* index, MDefinition* value);
bool setElemTryTypedStatic(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
bool setElemTryDense(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value, bool writeHole);
bool setElemTryArguments(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
bool setElemTryCache(bool* emitted, MDefinition* object,
MDefinition* index, MDefinition* value);
bool setElemTryReferenceElemOfTypedObject(bool* emitted,
MDefinition* obj,
MDefinition* index,
TypedObjectPrediction objPrediction,
MDefinition* value,
TypedObjectPrediction elemPrediction);
bool setElemTryScalarElemOfTypedObject(bool* emitted,
MDefinition* obj,
MDefinition* index,
TypedObjectPrediction objTypeReprs,
MDefinition* value,
TypedObjectPrediction elemTypeReprs,
int32_t elemSize);
bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
JSValueType unboxedType,
bool addResumePointAndIncrementInitializedLength);
// jsop_getelem() helpers.
bool getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index);
bool getElemTryScalarElemOfTypedObject(bool* emitted,
MDefinition* obj,
MDefinition* index,
TypedObjectPrediction objTypeReprs,
TypedObjectPrediction elemTypeReprs,
int32_t elemSize);
bool getElemTryReferenceElemOfTypedObject(bool* emitted,
MDefinition* obj,
MDefinition* index,
TypedObjectPrediction objPrediction,
TypedObjectPrediction elemPrediction);
bool getElemTryComplexElemOfTypedObject(bool* emitted,
MDefinition* obj,
MDefinition* index,
TypedObjectPrediction objTypeReprs,
TypedObjectPrediction elemTypeReprs,
int32_t elemSize);
TemporaryTypeSet* computeHeapType(const TemporaryTypeSet* objTypes, const jsid id);
enum BoundsChecking { DoBoundsCheck, SkipBoundsCheck };
// Add instructions to compute a typed array's length and data. Also
// optionally convert |*index| into a bounds-checked definition, if
// requested.
//
// If you only need the array's length, use addTypedArrayLength below.
void addTypedArrayLengthAndData(MDefinition* obj,
BoundsChecking checking,
MDefinition** index,
MInstruction** length, MInstruction** elements);
// Add an instruction to compute a typed array's length to the current
// block. If you also need the typed array's data, use the above method
// instead.
MInstruction* addTypedArrayLength(MDefinition* obj) {
MInstruction* length;
addTypedArrayLengthAndData(obj, SkipBoundsCheck, nullptr, &length, nullptr);
return length;
}
bool improveThisTypesForCall();
MDefinition* getCallee();
MDefinition* getAliasedVar(ScopeCoordinate sc);
MDefinition* addLexicalCheck(MDefinition* input);
bool tryFoldInstanceOf(MDefinition* lhs, JSObject* protoObject);
bool hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto);
bool jsop_add(MDefinition* left, MDefinition* right);
bool jsop_bitnot();
bool jsop_bitop(JSOp op);
bool jsop_binary_arith(JSOp op);
bool jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right);
bool jsop_pow();
bool jsop_pos();
bool jsop_neg();
bool jsop_tostring();
bool jsop_setarg(uint32_t arg);
bool jsop_defvar(uint32_t index);
bool jsop_deflexical(uint32_t index);
bool jsop_deffun(uint32_t index);
bool jsop_notearg();
bool jsop_throwsetconst();
bool jsop_checklexical();
bool jsop_checkaliasedlet(ScopeCoordinate sc);
bool jsop_funcall(uint32_t argc);
bool jsop_funapply(uint32_t argc);
bool jsop_funapplyarguments(uint32_t argc);
bool jsop_funapplyarray(uint32_t argc);
bool jsop_call(uint32_t argc, bool constructing);
bool jsop_eval(uint32_t argc);
bool jsop_ifeq(JSOp op);
bool jsop_try();
bool jsop_label();
bool jsop_condswitch();
bool jsop_andor(JSOp op);
bool jsop_dup2();
bool jsop_loophead(jsbytecode* pc);
bool jsop_compare(JSOp op);
bool jsop_compare(JSOp op, MDefinition* left, MDefinition* right);
bool getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
MDefinition* lexicalCheck = nullptr);
bool setStaticName(JSObject* staticObject, PropertyName* name);
bool jsop_getgname(PropertyName* name);
bool jsop_getname(PropertyName* name);
bool jsop_intrinsic(PropertyName* name);
bool jsop_getimport(PropertyName* name);
bool jsop_bindname(PropertyName* name);
bool jsop_getelem();
bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType);
bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType);
bool jsop_setelem();
bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
MDefinition* object, MDefinition* index, MDefinition* value,
JSValueType unboxedType, bool writeHole);
bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
MDefinition* object, MDefinition* index, MDefinition* value);
bool jsop_length();
bool jsop_length_fastPath();
bool jsop_arguments();
bool jsop_arguments_getelem();
bool jsop_runonce();
bool jsop_rest();
bool jsop_not();
bool jsop_getprop(PropertyName* name);
bool jsop_setprop(PropertyName* name);
bool jsop_delprop(PropertyName* name);
bool jsop_delelem();
bool jsop_newarray(uint32_t length);
bool jsop_newarray_copyonwrite();
bool jsop_newobject();
bool jsop_initelem();
bool jsop_initelem_array();
bool jsop_initelem_getter_setter();
bool jsop_mutateproto();
bool jsop_initprop(PropertyName* name);
bool jsop_initprop_getter_setter(PropertyName* name);
bool jsop_regexp(RegExpObject* reobj);
bool jsop_object(JSObject* obj);
bool jsop_lambda(JSFunction* fun);
bool jsop_lambda_arrow(JSFunction* fun);
bool jsop_functionthis();
bool jsop_globalthis();
bool jsop_typeof();
bool jsop_toid();
bool jsop_iter(uint8_t flags);
bool jsop_itermore();
bool jsop_isnoiter();
bool jsop_iterend();
bool jsop_in();
bool jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType);
bool jsop_instanceof();
bool jsop_getaliasedvar(ScopeCoordinate sc);
bool jsop_setaliasedvar(ScopeCoordinate sc);
bool jsop_debugger();
bool jsop_newtarget();
bool jsop_checkobjcoercible();
/* Inlining. */
enum InliningStatus
{
InliningStatus_Error,
InliningStatus_NotInlined,
InliningStatus_WarmUpCountTooLow,
InliningStatus_Inlined
};
enum InliningDecision
{
InliningDecision_Error,
InliningDecision_Inline,
InliningDecision_DontInline,
InliningDecision_WarmUpCountTooLow
};
static InliningDecision DontInline(JSScript* targetScript, const char* reason);
// Helper function for canInlineTarget
bool hasCommonInliningPath(const JSScript* scriptToInline);
// Oracles.
InliningDecision canInlineTarget(JSFunction* target, CallInfo& callInfo);
InliningDecision makeInliningDecision(JSObject* target, CallInfo& callInfo);
bool selectInliningTargets(const ObjectVector& targets, CallInfo& callInfo,
BoolVector& choiceSet, uint32_t* numInlineable);
// Native inlining helpers.
// The typeset for the return value of our function. These are
// the types it's been observed returning in the past.
TemporaryTypeSet* getInlineReturnTypeSet();
// The known MIR type of getInlineReturnTypeSet.
MIRType getInlineReturnType();
// Array natives.
InliningStatus inlineArray(CallInfo& callInfo);
InliningStatus inlineArrayIsArray(CallInfo& callInfo);
InliningStatus inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode);
InliningStatus inlineArrayPush(CallInfo& callInfo);
InliningStatus inlineArrayConcat(CallInfo& callInfo);
InliningStatus inlineArraySlice(CallInfo& callInfo);
InliningStatus inlineArrayJoin(CallInfo& callInfo);
InliningStatus inlineArraySplice(CallInfo& callInfo);
// Math natives.
InliningStatus inlineMathAbs(CallInfo& callInfo);
InliningStatus inlineMathFloor(CallInfo& callInfo);
InliningStatus inlineMathCeil(CallInfo& callInfo);
InliningStatus inlineMathClz32(CallInfo& callInfo);
InliningStatus inlineMathRound(CallInfo& callInfo);
InliningStatus inlineMathSqrt(CallInfo& callInfo);
InliningStatus inlineMathAtan2(CallInfo& callInfo);
InliningStatus inlineMathHypot(CallInfo& callInfo);
InliningStatus inlineMathMinMax(CallInfo& callInfo, bool max);
InliningStatus inlineMathPow(CallInfo& callInfo);
InliningStatus inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType);
InliningStatus inlineMathRandom(CallInfo& callInfo);
InliningStatus inlineMathImul(CallInfo& callInfo);
InliningStatus inlineMathFRound(CallInfo& callInfo);
InliningStatus inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);
// String natives.
InliningStatus inlineStringObject(CallInfo& callInfo);
InliningStatus inlineConstantStringSplit(CallInfo& callInfo);
InliningStatus inlineStringSplit(CallInfo& callInfo);
InliningStatus inlineStrCharCodeAt(CallInfo& callInfo);
InliningStatus inlineConstantCharCodeAt(CallInfo& callInfo);
InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
InliningStatus inlineStrCharAt(CallInfo& callInfo);
InliningStatus inlineStrReplace(CallInfo& callInfo);
// RegExp natives.
InliningStatus inlineRegExpExec(CallInfo& callInfo);
InliningStatus inlineRegExpTest(CallInfo& callInfo);
// Object natives and intrinsics.
InliningStatus inlineObjectCreate(CallInfo& callInfo);
InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
// Atomics natives.
InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
InliningStatus inlineAtomicsExchange(CallInfo& callInfo);
InliningStatus inlineAtomicsLoad(CallInfo& callInfo);
InliningStatus inlineAtomicsStore(CallInfo& callInfo);
InliningStatus inlineAtomicsFence(CallInfo& callInfo);
InliningStatus inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target);
InliningStatus inlineAtomicsIsLockFree(CallInfo& callInfo);
// Slot intrinsics.
InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo);
InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo,
MIRType knownValueType);
// TypedArray intrinsics.
enum WrappingBehavior { AllowWrappedTypedArrays, RejectWrappedTypedArrays };
InliningStatus inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior);
InliningStatus inlineIsTypedArray(CallInfo& callInfo);
InliningStatus inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo);
InliningStatus inlineTypedArrayLength(CallInfo& callInfo);
InliningStatus inlineSetDisjointTypedElements(CallInfo& callInfo);
// TypedObject intrinsics and natives.
InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo);
InliningStatus inlineSetTypedObjectOffset(CallInfo& callInfo);
InliningStatus inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target);
// SIMD intrinsics and natives.
InliningStatus inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* target);
// helpers
static MIRType SimdTypeDescrToMIRType(SimdTypeDescr::Type type);
bool checkInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
unsigned numArgs, InlineTypedObject** templateObj);
IonBuilder::InliningStatus boxSimd(CallInfo& callInfo, MInstruction* ins,
InlineTypedObject* templateObj);
InliningStatus inlineSimdInt32x4(CallInfo& callInfo, JSNative native);
InliningStatus inlineSimdFloat32x4(CallInfo& callInfo, JSNative native);
template <typename T>
InliningStatus inlineBinarySimd(CallInfo& callInfo, JSNative native,
typename T::Operation op, SimdTypeDescr::Type type);
InliningStatus inlineCompSimd(CallInfo& callInfo, JSNative native,
MSimdBinaryComp::Operation op, SimdTypeDescr::Type compType);
InliningStatus inlineUnarySimd(CallInfo& callInfo, JSNative native,
MSimdUnaryArith::Operation op, SimdTypeDescr::Type type);
InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native,
SimdTypeDescr::Type type);
InliningStatus inlineSimdReplaceLane(CallInfo& callInfo, JSNative native,
SimdTypeDescr::Type type);
InliningStatus inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type);
InliningStatus inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
unsigned numVectors, unsigned numLanes);
InliningStatus inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type);
InliningStatus inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast,
SimdTypeDescr::Type from, SimdTypeDescr::Type to);
InliningStatus inlineSimdSelect(CallInfo& callInfo, JSNative native, bool isElementWise,
SimdTypeDescr::Type type);
bool prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, MInstruction** elements,
MDefinition** index, Scalar::Type* arrayType);
InliningStatus inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
unsigned numElems);
InliningStatus inlineSimdStore(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
unsigned numElems);
// Utility intrinsics.
InliningStatus inlineIsCallable(CallInfo& callInfo);
InliningStatus inlineIsObject(CallInfo& callInfo);
InliningStatus inlineToObject(CallInfo& callInfo);
InliningStatus inlineToInteger(CallInfo& callInfo);
InliningStatus inlineToString(CallInfo& callInfo);
InliningStatus inlineDump(CallInfo& callInfo);
InliningStatus inlineHasClass(CallInfo& callInfo, const Class* clasp,
const Class* clasp2 = nullptr,
const Class* clasp3 = nullptr,
const Class* clasp4 = nullptr);
InliningStatus inlineIsConstructing(CallInfo& callInfo);
InliningStatus inlineSubstringKernel(CallInfo& callInfo);
// Testing functions.
InliningStatus inlineBailout(CallInfo& callInfo);
InliningStatus inlineAssertFloat32(CallInfo& callInfo);
InliningStatus inlineAssertRecoveredOnBailout(CallInfo& callInfo);
// Bind function.
InliningStatus inlineBoundFunction(CallInfo& callInfo, JSFunction* target);
// Main inlining functions
InliningStatus inlineNativeCall(CallInfo& callInfo, JSFunction* target);
InliningStatus inlineNativeGetter(CallInfo& callInfo, JSFunction* target);
InliningStatus inlineNonFunctionCall(CallInfo& callInfo, JSObject* target);
bool inlineScriptedCall(CallInfo& callInfo, JSFunction* target);
InliningStatus inlineSingleCall(CallInfo& callInfo, JSObject* target);
// Call functions
InliningStatus inlineCallsite(const ObjectVector& targets, CallInfo& callInfo);
bool inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVector& choiceSet,
MGetPropertyCache* maybeCache);
// Inlining helpers.
bool inlineGenericFallback(JSFunction* target, CallInfo& callInfo, MBasicBlock* dispatchBlock);
bool inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchBlock,
MObjectGroupDispatch* dispatch, MGetPropertyCache* cache,
MBasicBlock** fallbackTarget);
enum AtomicCheckResult {
DontCheckAtomicResult,
DoCheckAtomicResult
};
bool atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayElementType,
bool* requiresDynamicCheck,
AtomicCheckResult checkResult=DoCheckAtomicResult);
void atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index);
bool testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo);
MCall* makeCallHelper(JSFunction* target, CallInfo& callInfo);
bool makeCall(JSFunction* target, CallInfo& callInfo);
MDefinition* patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom);
MDefinition* patchInlinedReturns(CallInfo& callInfo, MIRGraphReturns& returns,
MBasicBlock* bottom);
MDefinition* specializeInlinedReturn(MDefinition* rdef, MBasicBlock* exit);
bool objectsHaveCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
bool isGetter, JSObject* foundProto, bool* guardGlobal);
void freezePropertiesForCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
JSObject* foundProto, bool allowEmptyTypesForGlobal = false);
/*
* Callers must pass a non-null globalGuard if they pass a non-null globalShape.
*/
bool testCommonGetterSetter(TemporaryTypeSet* types, PropertyName* name,
bool isGetter, JSObject* foundProto, Shape* lastProperty,
JSFunction* getterOrSetter,
MDefinition** guard, Shape* globalShape = nullptr,
MDefinition** globalGuard = nullptr);
bool testShouldDOMCall(TypeSet* inTypes,
JSFunction* func, JSJitInfo::OpType opType);
MDefinition*
addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
const BaselineInspector::ReceiverVector& receivers,
const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
bool isOwnProperty);
bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
MGetPropertyCache* getPropCache, TemporaryTypeSet* objTypes,
TemporaryTypeSet* pushedTypes);
MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo);
JSObject* testGlobalLexicalBinding(PropertyName* name);
JSObject* testSingletonProperty(JSObject* obj, jsid id);
JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
MDefinition* convertUnboxedObjects(MDefinition* obj);
MDefinition* convertUnboxedObjects(MDefinition* obj,
const BaselineInspector::ObjectGroupVector& list);
uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
JSValueType* punboxedType);
MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
BarrierKind barrier, TemporaryTypeSet* types);
MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
MDefinition* scaledOffset, JSValueType unboxedType,
BarrierKind barrier, TemporaryTypeSet* types);
MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
MDefinition* value);
MInstruction* storeUnboxedValue(MDefinition* obj,
MDefinition* elements, int32_t elementsOffset,
MDefinition* scaledOffset, JSValueType unboxedType,
MDefinition* value, bool preBarrier = true);
bool checkPreliminaryGroups(MDefinition *obj);
bool freezePropTypeSets(TemporaryTypeSet* types,
JSObject* foundProto, PropertyName* name);
bool canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers);
TemporaryTypeSet* bytecodeTypes(jsbytecode* pc);
// Use one of the below methods for updating the current block, rather than
// updating |current| directly. setCurrent() should only be used in cases
// where the block cannot have phis whose type needs to be computed.
bool setCurrentAndSpecializePhis(MBasicBlock* block) {
if (block) {
if (!block->specializePhis())
return false;
}
setCurrent(block);
return true;
}
void setCurrent(MBasicBlock* block) {
current = block;
}
// A builder is inextricably tied to a particular script.
JSScript* script_;
// script->hasIonScript() at the start of the compilation. Used to avoid
// calling hasIonScript() from background compilation threads.
bool scriptHasIonScript_;
// If off thread compilation is successful, the final code generator is
// attached here. Code has been generated, but not linked (there is not yet
// an IonScript). This is heap allocated, and must be explicitly destroyed,
// performed by FinishOffThreadBuilder().
CodeGenerator* backgroundCodegen_;
// Some aborts are actionable (e.g., using an unsupported bytecode). When
// optimization tracking is enabled, the location and message of the abort
// are recorded here so they may be propagated to the script's
// corresponding JitcodeGlobalEntry::BaselineEntry.
JSScript* actionableAbortScript_;
jsbytecode* actionableAbortPc_;
const char* actionableAbortMessage_;
public:
void clearForBackEnd();
JSObject* checkNurseryObject(JSObject* obj);
JSScript* script() const { return script_; }
bool scriptHasIonScript() const { return scriptHasIonScript_; }
CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; }
void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; }
CompilerConstraintList* constraints() {
return constraints_;
}
bool isInlineBuilder() const {
return callerBuilder_ != nullptr;
}
const JSAtomState& names() { return compartment->runtime()->names(); }
bool hadActionableAbort() const {
MOZ_ASSERT(!actionableAbortScript_ ||
(actionableAbortPc_ && actionableAbortMessage_));
return actionableAbortScript_ != nullptr;
}
void actionableAbortLocationAndMessage(JSScript** abortScript, jsbytecode** abortPc,
const char** abortMessage)
{
MOZ_ASSERT(hadActionableAbort());
*abortScript = actionableAbortScript_;
*abortPc = actionableAbortPc_;
*abortMessage = actionableAbortMessage_;
}
private:
bool init();
JSContext* analysisContext;
BaselineFrameInspector* baselineFrame_;
// Constraints for recording dependencies on type information.
CompilerConstraintList* constraints_;
// Basic analysis information about the script.
BytecodeAnalysis analysis_;
BytecodeAnalysis& analysis() {
return analysis_;
}
TemporaryTypeSet* thisTypes;
TemporaryTypeSet* argTypes;
TemporaryTypeSet* typeArray;
uint32_t typeArrayHint;
uint32_t* bytecodeTypeMap;
GSNCache gsn;
ScopeCoordinateNameCache scopeCoordinateNameCache;
jsbytecode* pc;
MBasicBlock* current;
uint32_t loopDepth_;
Vector<BytecodeSite*, 0, JitAllocPolicy> trackedOptimizationSites_;
BytecodeSite* bytecodeSite(jsbytecode* pc) {
MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc));
// See comment in maybeTrackedOptimizationSite.
if (isOptimizationTrackingEnabled()) {
if (BytecodeSite* site = maybeTrackedOptimizationSite(pc))
return site;
}
return new(alloc()) BytecodeSite(info().inlineScriptTree(), pc);
}
BytecodeSite* maybeTrackedOptimizationSite(jsbytecode* pc);
MDefinition* lexicalCheck_;
void setLexicalCheck(MDefinition* lexical) {
MOZ_ASSERT(!lexicalCheck_);
lexicalCheck_ = lexical;
}
MDefinition* takeLexicalCheck() {
MDefinition* lexical = lexicalCheck_;
lexicalCheck_ = nullptr;
return lexical;
}
/* Information used for inline-call builders. */
MResumePoint* callerResumePoint_;
jsbytecode* callerPC() {
return callerResumePoint_ ? callerResumePoint_->pc() : nullptr;
}
IonBuilder* callerBuilder_;
IonBuilder* outermostBuilder();
bool oom() {
abortReason_ = AbortReason_Alloc;
return false;
}
struct LoopHeader {
jsbytecode* pc;
MBasicBlock* header;
LoopHeader(jsbytecode* pc, MBasicBlock* header)
: pc(pc), header(header)
{}
};
Vector<CFGState, 8, JitAllocPolicy> cfgStack_;
Vector<ControlFlowInfo, 4, JitAllocPolicy> loops_;
Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
Vector<MInstruction*, 2, JitAllocPolicy> iterators_;
Vector<LoopHeader, 0, JitAllocPolicy> loopHeaders_;
BaselineInspector* inspector;
size_t inliningDepth_;
// Total bytecode length of all inlined scripts. Only tracked for the
// outermost builder.
size_t inlinedBytecodeLength_;
// Cutoff to disable compilation if excessive time is spent reanalyzing
// loop bodies to compute a fixpoint of the types for loop variables.
static const size_t MAX_LOOP_RESTARTS = 40;
size_t numLoopRestarts_;
// True if script->failedBoundsCheck is set for the current script or
// an outer script.
bool failedBoundsCheck_;
// True if script->failedShapeGuard is set for the current script or
// an outer script.
bool failedShapeGuard_;
// True if script->failedLexicalCheck_ is set for the current script or
// an outer script.
bool failedLexicalCheck_;
// Has an iterator other than 'for in'.
bool nonStringIteration_;
// If this script can use a lazy arguments object, it will be pre-created
// here.
MInstruction* lazyArguments_;
// If this is an inline builder, the call info for the builder.
const CallInfo* inlineCallInfo_;
// When compiling a call with multiple targets, we are first creating a
// MGetPropertyCache. This MGetPropertyCache is following the bytecode, and
// is used to recover the JSFunction. In some cases, the Type of the object
// which own the property is enough for dispatching to the right function.
// In such cases we do not have read the property, except when the type
// object is unknown.
//
// As an optimization, we can dispatch a call based on the object group,
// without doing the MGetPropertyCache. This is what is achieved by
// |IonBuilder::inlineCalls|. As we might not know all the functions, we
// are adding a fallback path, where this MGetPropertyCache would be moved
// into.
//
// In order to build the fallback path, we have to capture a resume point
// ahead, for the potential fallback path. This resume point is captured
// while building MGetPropertyCache. It is capturing the state of Baseline
// before the execution of the MGetPropertyCache, such as we can safely do
// it in the fallback path.
//
// This field is used to discard the resume point if it is not used for
// building a fallback path.
// Discard the prior resume point while setting a new MGetPropertyCache.
void replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache);
// Discard the MGetPropertyCache if it is handled by WrapMGetPropertyCache.
void keepFallbackFunctionGetter(MGetPropertyCache* cache) {
if (cache == maybeFallbackFunctionGetter_)
maybeFallbackFunctionGetter_ = nullptr;
}
MGetPropertyCache* maybeFallbackFunctionGetter_;
// Used in tracking outcomes of optimization strategies for devtools.
void startTrackingOptimizations();
// The track* methods below are called often. Do not combine them with the
// unchecked variants, despite the unchecked variants having no other
// callers.
void trackTypeInfo(JS::TrackedTypeSite site, MIRType mirType,
TemporaryTypeSet* typeSet)
{
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackTypeInfoUnchecked(site, mirType, typeSet);
}
void trackTypeInfo(JS::TrackedTypeSite site, JSObject* obj) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackTypeInfoUnchecked(site, obj);
}
void trackTypeInfo(CallInfo& callInfo) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackTypeInfoUnchecked(callInfo);
}
void trackOptimizationAttempt(JS::TrackedStrategy strategy) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackOptimizationAttemptUnchecked(strategy);
}
void amendOptimizationAttempt(uint32_t index) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
amendOptimizationAttemptUnchecked(index);
}
void trackOptimizationOutcome(JS::TrackedOutcome outcome) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackOptimizationOutcomeUnchecked(outcome);
}
void trackOptimizationSuccess() {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackOptimizationSuccessUnchecked();
}
void trackInlineSuccess(InliningStatus status = InliningStatus_Inlined) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackInlineSuccessUnchecked(status);
}
bool forceInlineCaches() {
return MOZ_UNLIKELY(JitOptions.forceInlineCaches);
}
// Out-of-line variants that don't check if optimization tracking is
// enabled.
void trackTypeInfoUnchecked(JS::TrackedTypeSite site, MIRType mirType,
TemporaryTypeSet* typeSet);
void trackTypeInfoUnchecked(JS::TrackedTypeSite site, JSObject* obj);
void trackTypeInfoUnchecked(CallInfo& callInfo);
void trackOptimizationAttemptUnchecked(JS::TrackedStrategy strategy);
void amendOptimizationAttemptUnchecked(uint32_t index);
void trackOptimizationOutcomeUnchecked(JS::TrackedOutcome outcome);
void trackOptimizationSuccessUnchecked();
void trackInlineSuccessUnchecked(InliningStatus status);
};
class CallInfo
{
MDefinition* fun_;
MDefinition* thisArg_;
MDefinition* newTargetArg_;
MDefinitionVector args_;
bool constructing_;
bool setter_;
public:
CallInfo(TempAllocator& alloc, bool constructing)
: fun_(nullptr),
thisArg_(nullptr),
newTargetArg_(nullptr),
args_(alloc),
constructing_(constructing),
setter_(false)
{ }
bool init(CallInfo& callInfo) {
MOZ_ASSERT(constructing_ == callInfo.constructing());
fun_ = callInfo.fun();
thisArg_ = callInfo.thisArg();
if (constructing())
newTargetArg_ = callInfo.getNewTarget();
if (!args_.appendAll(callInfo.argv()))
return false;
return true;
}
bool init(MBasicBlock* current, uint32_t argc) {
MOZ_ASSERT(args_.empty());
// Get the arguments in the right order
if (!args_.reserve(argc))
return false;
if (constructing())
setNewTarget(current->pop());
for (int32_t i = argc; i > 0; i--)
args_.infallibleAppend(current->peek(-i));
current->popn(argc);
// Get |this| and |fun|
setThis(current->pop());
setFun(current->pop());
return true;
}
void popFormals(MBasicBlock* current) {
current->popn(numFormals());
}
void pushFormals(MBasicBlock* current) {
current->push(fun());
current->push(thisArg());
for (uint32_t i = 0; i < argc(); i++)
current->push(getArg(i));
if (constructing())
current->push(getNewTarget());
}
uint32_t argc() const {
return args_.length();
}
uint32_t numFormals() const {
return argc() + 2 + constructing();
}
bool setArgs(const MDefinitionVector& args) {
MOZ_ASSERT(args_.empty());
return args_.appendAll(args);
}
MDefinitionVector& argv() {
return args_;
}
const MDefinitionVector& argv() const {
return args_;
}
MDefinition* getArg(uint32_t i) const {
MOZ_ASSERT(i < argc());
return args_[i];
}
MDefinition* getArgWithDefault(uint32_t i, MDefinition* defaultValue) const {
if (i < argc())
return args_[i];
return defaultValue;
}
void setArg(uint32_t i, MDefinition* def) {
MOZ_ASSERT(i < argc());
args_[i] = def;
}
MDefinition* thisArg() const {
MOZ_ASSERT(thisArg_);
return thisArg_;
}
void setThis(MDefinition* thisArg) {
thisArg_ = thisArg;
}
bool constructing() const {
return constructing_;
}
void setNewTarget(MDefinition* newTarget) {
MOZ_ASSERT(constructing());
newTargetArg_ = newTarget;
}
MDefinition* getNewTarget() const {
MOZ_ASSERT(newTargetArg_);
return newTargetArg_;
}
bool isSetter() const {
return setter_;
}
void markAsSetter() {
setter_ = true;
}
MDefinition* fun() const {
MOZ_ASSERT(fun_);
return fun_;
}
void setFun(MDefinition* fun) {
fun_ = fun;
}
void setImplicitlyUsedUnchecked() {
fun_->setImplicitlyUsedUnchecked();
thisArg_->setImplicitlyUsedUnchecked();
if (newTargetArg_)
newTargetArg_->setImplicitlyUsedUnchecked();
for (uint32_t i = 0; i < argc(); i++)
getArg(i)->setImplicitlyUsedUnchecked();
}
};
bool NeedsPostBarrier(MDefinition* value);
} // namespace jit
} // namespace js
#endif /* jit_IonBuilder_h */