Bug 1310155 - IonMonkey, part 1.0: Split graph creation from IonBuilder, r=jandem

This commit is contained in:
Hannes Verschore 2016-12-08 13:53:05 -10:00
Родитель 1daea96f16
Коммит d889fa48d3
14 изменённых файлов: 3712 добавлений и 2453 удалений

Просмотреть файл

@ -13,6 +13,7 @@
#include "jit/BaselineCompiler.h"
#include "jit/BaselineIC.h"
#include "jit/CompileInfo.h"
#include "jit/IonControlFlow.h"
#include "jit/JitCommon.h"
#include "jit/JitSpewer.h"
#include "vm/Debugger.h"
@ -76,7 +77,8 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
flags_(0),
inlinedBytecodeLength_(0),
maxInliningDepth_(UINT8_MAX),
pendingBuilder_(nullptr)
pendingBuilder_(nullptr),
controlFlowGraph_(nullptr)
{ }
static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
@ -499,6 +501,8 @@ BaselineScript::Destroy(FreeOp* fop, BaselineScript* script)
{
MOZ_ASSERT(!script->hasPendingIonBuilder());
fop->delete_(script->controlFlowGraph_);
script->controlFlowGraph_ = nullptr;
script->unlinkDependentWasmImports(fop);

Просмотреть файл

@ -24,6 +24,7 @@ namespace jit {
class StackValue;
class BaselineICEntry;
class ICStub;
class ControlFlowGraph;
class PCMappingSlotInfo
{
@ -239,6 +240,8 @@ struct BaselineScript
// An ion compilation that is ready, but isn't linked yet.
IonBuilder *pendingBuilder_;
ControlFlowGraph* controlFlowGraph_;
public:
// Do not call directly, use BaselineScript::New. This is public for cx->new_.
BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
@ -513,6 +516,14 @@ struct BaselineScript
script->setIonScript(nullptr, nullptr);
}
const ControlFlowGraph* controlFlowGraph() const {
return controlFlowGraph_;
}
void setControlFlowGraph(ControlFlowGraph* controlFlowGraph) {
controlFlowGraph_ = controlFlowGraph;
}
};
static_assert(sizeof(BaselineScript) % sizeof(uintptr_t) == 0,
"The data attached to the script must be aligned for fast JIT access.");

Просмотреть файл

@ -254,6 +254,10 @@ class InlineListNode : public InlineForwardListNode<T>
InlineListNode(const InlineListNode<T>&) = delete;
void operator=(const InlineListNode<T>&) = delete;
bool isInList() {
return prev != nullptr && this->next != nullptr;
}
protected:
friend class InlineList<T>;
friend class InlineListIterator<T>;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -15,6 +15,7 @@
#include "jit/BaselineInspector.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/IonAnalysis.h"
#include "jit/IonControlFlow.h"
#include "jit/IonOptimizationLevels.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
@ -39,173 +40,6 @@ 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,
@ -224,7 +58,6 @@ class IonBuilder
private:
MOZ_MUST_USE bool traverseBytecode();
ControlStatus snoopControlFlow(JSOp op);
MOZ_MUST_USE bool processIterators();
MOZ_MUST_USE bool inspectOpcode(JSOp op);
uint32_t readIndex(jsbytecode* pc);
@ -237,44 +70,9 @@ class IonBuilder
MOZ_MUST_USE bool getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
ObjectVector& targets, uint32_t maxTargets);
void popCfgStack();
DeferredEdge* filterDeadDeferredEdges(DeferredEdge* edge);
MOZ_MUST_USE 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);
MOZ_MUST_USE bool pushLoop(CFGState::State state, jsbytecode* stopAt, MBasicBlock* entry,
bool osr, jsbytecode* loopHead, jsbytecode* initialPc,
jsbytecode* bodyStart, jsbytecode* bodyEnd,
jsbytecode* exitpc, jsbytecode* continuepc);
MOZ_MUST_USE bool analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecode* end);
MOZ_MUST_USE bool analyzeNewLoopTypes(const CFGBlock* loopEntryBlock);
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);
@ -289,6 +87,18 @@ class IonBuilder
return newBlockAfter(at, nullptr, pc);
}
MOZ_MUST_USE bool visitBlock(const CFGBlock* hblock, MBasicBlock* mblock);
MOZ_MUST_USE bool visitControlInstruction(CFGControlInstruction* ins, bool* restarted);
MOZ_MUST_USE bool visitTest(CFGTest* test);
MOZ_MUST_USE bool visitCompare(CFGCompare* compare);
MOZ_MUST_USE bool visitLoopEntry(CFGLoopEntry* loopEntry);
MOZ_MUST_USE bool visitReturn(CFGControlInstruction* ins);
MOZ_MUST_USE bool visitGoto(CFGGoto* ins);
MOZ_MUST_USE bool visitBackEdge(CFGBackEdge* ins, bool* restarted);
MOZ_MUST_USE bool visitTry(CFGTry* test);
MOZ_MUST_USE bool visitThrow(CFGThrow* ins);
MOZ_MUST_USE bool visitTableSwitch(CFGTableSwitch* ins);
// 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
@ -297,18 +107,6 @@ class IonBuilder
// 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.
MOZ_MUST_USE bool addOsrValueTypeBarrier(uint32_t slot, MInstruction** def,
@ -317,15 +115,8 @@ class IonBuilder
// Restarts processing of a loop if the type information at its header was
// incomplete.
ControlStatus restartLoop(const 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);
bool restartLoop(const CFGBlock* header);
bool initLoopEntry();
// Please see the Big Honkin' Comment about how resume points work in
// IonBuilder.cpp, near the definition for this function.
@ -334,6 +125,8 @@ class IonBuilder
MOZ_MUST_USE bool resumeAfter(MInstruction* ins);
MOZ_MUST_USE bool maybeInsertResume();
bool blockIsOSREntry(const CFGBlock* block, const CFGBlock* predecessor);
void insertRecompileCheck();
MOZ_MUST_USE bool initParameters();
@ -708,10 +501,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_funapplyarray(uint32_t argc);
MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing);
MOZ_MUST_USE bool jsop_eval(uint32_t argc);
MOZ_MUST_USE bool jsop_ifeq(JSOp op);
MOZ_MUST_USE bool jsop_try();
MOZ_MUST_USE bool jsop_label();
MOZ_MUST_USE bool jsop_condswitch();
MOZ_MUST_USE bool jsop_andor(JSOp op);
MOZ_MUST_USE bool jsop_dup2();
MOZ_MUST_USE bool jsop_loophead(jsbytecode* pc);
@ -1193,6 +983,9 @@ class IonBuilder
jsbytecode* pc;
MBasicBlock* current;
uint32_t loopDepth_;
Vector<MBasicBlock*, 0, JitAllocPolicy> blockWorklist;
const CFGBlock* cfgCurrent;
const ControlFlowGraph* cfg;
Vector<BytecodeSite*, 0, JitAllocPolicy> trackedOptimizationSites_;
@ -1238,12 +1031,13 @@ class IonBuilder
{}
};
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_;
Vector<MBasicBlock*, 0, JitAllocPolicy> loopHeaderStack_;
#ifdef DEBUG
Vector<const CFGBlock*, 0, JitAllocPolicy> cfgLoopHeaderStack_;
#endif
BaselineInspector* inspector;
size_t inliningDepth_;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

807
js/src/jit/IonControlFlow.h Normal file
Просмотреть файл

@ -0,0 +1,807 @@
/* -*- 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_IonControlFlow_h
#define jit_IonControlFlow_h
#include "mozilla/Array.h"
#include "jsbytecode.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/FixedList.h"
#include "jit/JitAllocPolicy.h"
#include "js/TypeDecls.h"
namespace js {
namespace jit {
class CFGControlInstruction;
// Adds MFoo::New functions which are mirroring the arguments of the
// constructors. Opcodes which are using this macro can be called with a
// TempAllocator, or the fallible version of the TempAllocator.
#define TRIVIAL_CFG_NEW_WRAPPERS \
template <typename... Args> \
static CFGThisOpcode* New(TempAllocator& alloc, Args&&... args) { \
return new(alloc) CFGThisOpcode(mozilla::Forward<Args>(args)...); \
} \
template <typename... Args> \
static CFGThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args) \
{ \
return new(alloc) CFGThisOpcode(mozilla::Forward<Args>(args)...); \
}
class CFGBlock : public TempObject
{
size_t id_;
jsbytecode* start;
jsbytecode* stop;
CFGControlInstruction* end;
bool inWorkList;
public:
explicit CFGBlock(jsbytecode* start)
: id_(-1),
start(start),
stop(nullptr),
end(nullptr),
inWorkList(false)
{}
static CFGBlock* New(TempAllocator& alloc, jsbytecode* start) {
return new(alloc) CFGBlock(start);
}
void operator=(const CFGBlock&) = delete;
jsbytecode* startPc() const {
return start;
}
jsbytecode* stopPc() const {
MOZ_ASSERT(stop);
return stop;
}
void setStopPc(jsbytecode* stopPc) {
stop = stopPc;
}
CFGControlInstruction* stopIns() const {
MOZ_ASSERT(end);
return end;
}
void setStopIns(CFGControlInstruction* stopIns) {
end = stopIns;
}
bool isInWorkList() const {
return inWorkList;
}
void setInWorklist() {
MOZ_ASSERT(!inWorkList);
inWorkList = true;
}
void clearInWorkList() {
MOZ_ASSERT(inWorkList);
inWorkList = false;
}
size_t id() const {
return id_;
}
void setId(size_t id) {
id_ = id;
}
};
#define CFG_CONTROL_OPCODE_LIST(_) \
_(Test) \
_(Compare) \
_(Goto) \
_(Return) \
_(RetRVal) \
_(LoopEntry) \
_(BackEdge) \
_(TableSwitch) \
_(Try) \
_(Throw)
// Forward declarations of MIR types.
#define FORWARD_DECLARE(type) class CFG##type;
CFG_CONTROL_OPCODE_LIST(FORWARD_DECLARE)
#undef FORWARD_DECLARE
#define CFG_CONTROL_HEADER(type_name) \
static const Type classOpcode = CFGControlInstruction::Type_##type_name; \
using CFGThisOpcode = CFG##type_name; \
Type type() const override { \
return classOpcode; \
} \
const char* Name() const override { \
return #type_name; \
} \
class CFGControlInstruction : public TempObject
{
public:
enum Type {
# define DEFINE_TYPES(type) Type_##type,
CFG_CONTROL_OPCODE_LIST(DEFINE_TYPES)
# undef DEFINE_TYPES
};
virtual size_t numSuccessors() const = 0;
virtual CFGBlock* getSuccessor(size_t i) const = 0;
virtual void replaceSuccessor(size_t i, CFGBlock* successor) = 0;
virtual Type type() const = 0;
virtual const char* Name() const = 0;
template<typename CFGType> bool is() const {
return type() == CFGType::classOpcode;
}
template<typename CFGType> CFGType* to() {
MOZ_ASSERT(this->is<CFGType>());
return static_cast<CFGType*>(this);
}
template<typename CFGType> const CFGType* to() const {
MOZ_ASSERT(this->is<CFGType>());
return static_cast<const CFGType*>(this);
}
# define TYPE_CASTS(type) \
bool is##type() const { \
return this->is<CFG##type>(); \
} \
CFG##type* to##type() { \
return this->to<CFG##type>(); \
} \
const CFG##type* to##type() const { \
return this->to<CFG##type>(); \
}
CFG_CONTROL_OPCODE_LIST(TYPE_CASTS)
# undef TYPE_CASTS
};
template <size_t Successors>
class CFGAryControlInstruction : public CFGControlInstruction
{
mozilla::Array<CFGBlock*, Successors> successors_;
public:
size_t numSuccessors() const final override {
return Successors;
}
CFGBlock* getSuccessor(size_t i) const final override {
return successors_[i];
}
void replaceSuccessor(size_t i, CFGBlock* succ) final override {
successors_[i] = succ;
}
};
class CFGTry : public CFGControlInstruction
{
CFGBlock* tryBlock_;
jsbytecode* catchStartPc_;
CFGBlock* mergePoint_;
CFGTry(CFGBlock* successor, jsbytecode* catchStartPc, CFGBlock* mergePoint = nullptr)
: tryBlock_(successor),
catchStartPc_(catchStartPc),
mergePoint_(mergePoint)
{ }
public:
CFG_CONTROL_HEADER(Try)
TRIVIAL_CFG_NEW_WRAPPERS
size_t numSuccessors() const final override {
return mergePoint_ ? 2 : 1;
}
CFGBlock* getSuccessor(size_t i) const final override {
MOZ_ASSERT(i < numSuccessors());
return (i == 0) ? tryBlock_ : mergePoint_;
}
void replaceSuccessor(size_t i, CFGBlock* succ) final override {
MOZ_ASSERT(i < numSuccessors());
if (i == 0)
tryBlock_ = succ;
else
mergePoint_ = succ;
}
CFGBlock* tryBlock() const {
return getSuccessor(0);
}
jsbytecode* catchStartPc() const {
return catchStartPc_;
}
CFGBlock* afterTryCatchBlock() const {
return getSuccessor(1);
}
bool codeAfterTryCatchReachable() const {
return !!mergePoint_;
}
};
class CFGTableSwitch : public CFGControlInstruction
{
Vector<CFGBlock*, 4, JitAllocPolicy> successors_;
size_t low_;
size_t high_;
CFGTableSwitch(TempAllocator& alloc, size_t low, size_t high)
: successors_(alloc),
low_(low),
high_(high)
{}
public:
CFG_CONTROL_HEADER(TableSwitch);
static CFGTableSwitch* New(TempAllocator& alloc, size_t low, size_t high) {
return new(alloc) CFGTableSwitch(alloc, low, high);
}
size_t numSuccessors() const final override {
return successors_.length();
}
CFGBlock* getSuccessor(size_t i) const final override {
MOZ_ASSERT(i < numSuccessors());
return successors_[i];
}
void replaceSuccessor(size_t i, CFGBlock* succ) final override {
MOZ_ASSERT(i < numSuccessors());
successors_[i] = succ;
}
bool addDefault(CFGBlock* defaultCase) {
MOZ_ASSERT(successors_.length() == 0);
return successors_.append(defaultCase);
}
bool addCase(CFGBlock* caseBlock) {
MOZ_ASSERT(successors_.length() > 0);
return successors_.append(caseBlock);
}
CFGBlock* defaultCase() const {
return getSuccessor(0);
}
CFGBlock* getCase(size_t i) const {
return getSuccessor(i + 1);
}
size_t high() const {
return high_;
}
size_t low() const {
return low_;
}
};
/**
* CFGCompare
*
* POP
* PEEK
* STRICTEQ JUMP succ1
* STRICTNEQ JUMP succ2
*/
class CFGCompare : public CFGAryControlInstruction<2>
{
CFGCompare(CFGBlock* succ1, CFGBlock* succ2)
{
replaceSuccessor(0, succ1);
replaceSuccessor(1, succ2);
}
public:
CFG_CONTROL_HEADER(Compare);
TRIVIAL_CFG_NEW_WRAPPERS
CFGBlock* trueBranch() const {
return getSuccessor(0);
}
CFGBlock* falseBranch() const {
return getSuccessor(1);
}
};
/**
* CFGTest
*
* POP / PEEK (depending on keepCondition_)
* IFEQ JUMP succ1
* IFNEQ JUMP succ2
*
*/
class CFGTest : public CFGAryControlInstruction<2>
{
// By default the condition gets popped. This variable
// keeps track if we want to keep the condition.
bool keepCondition_;
CFGTest(CFGBlock* succ1, CFGBlock* succ2)
: keepCondition_(false)
{
replaceSuccessor(0, succ1);
replaceSuccessor(1, succ2);
}
CFGTest(CFGBlock* succ1, CFGBlock* succ2, bool keepCondition)
: keepCondition_(keepCondition)
{
replaceSuccessor(0, succ1);
replaceSuccessor(1, succ2);
}
public:
CFG_CONTROL_HEADER(Test);
TRIVIAL_CFG_NEW_WRAPPERS
CFGBlock* trueBranch() const {
return getSuccessor(0);
}
CFGBlock* falseBranch() const {
return getSuccessor(1);
}
void keepCondition() {
keepCondition_ = true;
}
bool mustKeepCondition() const {
return keepCondition_;
}
};
/**
* CFGReturn
*
* POP
* RETURN popped value
*
*/
class CFGReturn : public CFGAryControlInstruction<0>
{
public:
CFG_CONTROL_HEADER(Return);
TRIVIAL_CFG_NEW_WRAPPERS
};
/**
* CFGRetRVal
*
* RETURN the value in the return value slot
*
*/
class CFGRetRVal : public CFGAryControlInstruction<0>
{
public:
CFG_CONTROL_HEADER(RetRVal);
TRIVIAL_CFG_NEW_WRAPPERS
};
/**
* CFGThrow
*
* POP
* THROW popped value
*
*/
class CFGThrow : public CFGAryControlInstruction<0>
{
public:
CFG_CONTROL_HEADER(Throw);
TRIVIAL_CFG_NEW_WRAPPERS
};
class CFGUnaryControlInstruction : public CFGAryControlInstruction<1>
{
public:
explicit CFGUnaryControlInstruction(CFGBlock* block) {
MOZ_ASSERT(block);
replaceSuccessor(0, block);
}
CFGBlock* successor() const {
return getSuccessor(0);
}
};
/**
* CFGGOTO
*
* POP (x popAmount)
* JMP block
*
*/
class CFGGoto : public CFGUnaryControlInstruction
{
size_t popAmount_;
explicit CFGGoto(CFGBlock* block)
: CFGUnaryControlInstruction(block),
popAmount_(0)
{}
CFGGoto(CFGBlock* block, size_t popAmount_)
: CFGUnaryControlInstruction(block),
popAmount_(popAmount_)
{}
public:
CFG_CONTROL_HEADER(Goto);
TRIVIAL_CFG_NEW_WRAPPERS
size_t popAmount() const {
return popAmount_;
}
};
/**
* CFGBackEdge
*
* Jumps back to the start of the loop.
*
* JMP block
*
*/
class CFGBackEdge : public CFGUnaryControlInstruction
{
explicit CFGBackEdge(CFGBlock* block)
: CFGUnaryControlInstruction(block)
{}
public:
CFG_CONTROL_HEADER(BackEdge);
TRIVIAL_CFG_NEW_WRAPPERS
};
/**
* CFGLOOPENTRY
*
* Indicates the jumping block is the start of a loop.
* That block is the only block allowed to have a backedge.
*
* JMP block
*
*/
class CFGLoopEntry : public CFGUnaryControlInstruction
{
bool canOsr_;
size_t stackPhiCount_;
jsbytecode* loopStopPc_;
CFGLoopEntry(CFGBlock* block, size_t stackPhiCount)
: CFGUnaryControlInstruction(block),
canOsr_(false),
stackPhiCount_(stackPhiCount),
loopStopPc_(nullptr)
{}
CFGLoopEntry(CFGBlock* block, bool canOsr, size_t stackPhiCount, jsbytecode* loopStopPc)
: CFGUnaryControlInstruction(block),
canOsr_(canOsr),
stackPhiCount_(stackPhiCount),
loopStopPc_(loopStopPc)
{}
public:
CFG_CONTROL_HEADER(LoopEntry);
TRIVIAL_CFG_NEW_WRAPPERS
void setCanOsr() {
canOsr_ = true;
}
bool canOsr() const {
return canOsr_;
}
size_t stackPhiCount() const {
return stackPhiCount_;
}
jsbytecode* loopStopPc() const {
MOZ_ASSERT(loopStopPc_);
return loopStopPc_;
}
void setLoopStopPc(jsbytecode* loopStopPc) {
loopStopPc_ = loopStopPc;
}
};
typedef Vector<CFGBlock*, 4, JitAllocPolicy> CFGBlockVector;
class ControlFlowGraph
{
// Keeps the data alive until destruction.
LifoAlloc lifoAlloc_;
TempAllocator alloc_;
// A list of blocks in RPO, containing per block a pc-range and
// a control instruction.
Vector<CFGBlock, 4, JitAllocPolicy> blocks_;
public:
ControlFlowGraph()
: lifoAlloc_(TempAllocator::PreferredLifoChunkSize),
alloc_(&lifoAlloc_),
blocks_(alloc_)
{}
ControlFlowGraph(const ControlFlowGraph&) = delete;
void operator=(const ControlFlowGraph&) = delete;
void dump(GenericPrinter& print, JSScript* script);
bool init(const CFGBlockVector& blocks);
TempAllocator& alloc() {
return alloc_;
}
const CFGBlock* block(size_t i) const {
return &blocks_[i];
}
size_t numBlocks() const {
return blocks_.length();
}
};
class ControlFlowGenerator
{
static int CmpSuccessors(const void* a, const void* b);
JSScript* script;
CFGBlock* current;
jsbytecode* pc;
GSNCache gsn;
TempAllocator& alloc_;
CFGBlockVector blocks_;
public:
ControlFlowGenerator(const ControlFlowGenerator&) = delete;
void operator=(const ControlFlowGenerator&) = delete;
TempAllocator& alloc() {
return alloc_;
}
enum class ControlStatus {
Error,
Abort,
Ended, // There is no continuation/join point.
Joined, // Created a join node.
Jumped, // Parsing another branch at the same level.
None // No control flow.
};
struct DeferredEdge : public TempObject
{
CFGBlock* block;
DeferredEdge* next;
DeferredEdge(CFGBlock* 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 {
CFGBlock* ifFalse;
jsbytecode* falseEnd;
CFGBlock* ifTrue; // Set when the end of the true path is reached.
CFGTest* test;
} branch;
struct {
// Common entry point.
CFGBlock* entry;
// Position of where the loop body starts and ends.
jsbytecode* bodyStart;
jsbytecode* bodyEnd;
// pc immediately after the loop exits.
jsbytecode* exitpc;
// Common exit point. Created lazily, so it may be nullptr.
CFGBlock* 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 {
// Vector of body blocks to process after the cases.
FixedList<CFGBlock*>* 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;
} switch_;
struct {
DeferredEdge* breaks;
} label;
struct {
CFGBlock* 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, CFGTest* test);
static CFGState IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, CFGTest* test);
static CFGState AndOr(jsbytecode* join, CFGBlock* lhs);
static CFGState TableSwitch(TempAllocator& alloc, jsbytecode* exitpc);
static CFGState CondSwitch(TempAllocator& alloc, jsbytecode* exitpc,
jsbytecode* defaultTarget);
static CFGState Label(jsbytecode* exitpc);
static CFGState Try(jsbytecode* exitpc, CFGBlock* successor);
};
Vector<CFGState, 8, JitAllocPolicy> cfgStack_;
Vector<ControlFlowInfo, 4, JitAllocPolicy> loops_;
Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
BytecodeAnalysis analysis_;
bool aborted_;
public:
ControlFlowGenerator(TempAllocator& alloc, JSScript* script);
MOZ_MUST_USE bool init();
MOZ_MUST_USE bool traverseBytecode();
MOZ_MUST_USE bool addBlock(CFGBlock* block);
ControlFlowGraph* getGraph() {
ControlFlowGraph* cfg = js_new<ControlFlowGraph>();
if (!cfg)
return nullptr;
if (!cfg->init(blocks_)) {
js_delete(cfg);
return nullptr;
}
return cfg;
}
bool aborted() const {
return aborted_;
}
private:
void popCfgStack();
MOZ_MUST_USE bool processDeferredContinues(CFGState& state);
ControlStatus processControlEnd();
ControlStatus processCfgStack();
ControlStatus processCfgEntry(CFGState& state);
ControlStatus processIfStart(JSOp op);
ControlStatus processIfEnd(CFGState& state);
ControlStatus processIfElseTrueEnd(CFGState& state);
ControlStatus processIfElseFalseEnd(CFGState& state);
ControlStatus processDoWhileLoop(JSOp op, jssrcnote* sn);
ControlStatus processDoWhileBodyEnd(CFGState& state);
ControlStatus processDoWhileCondEnd(CFGState& state);
ControlStatus processWhileCondEnd(CFGState& state);
ControlStatus processWhileBodyEnd(CFGState& state);
ControlStatus processForLoop(JSOp op, jssrcnote* sn);
ControlStatus processForCondEnd(CFGState& state);
ControlStatus processForBodyEnd(CFGState& state);
ControlStatus processForUpdateEnd(CFGState& state);
ControlStatus processWhileOrForInLoop(jssrcnote* sn);
ControlStatus processNextTableSwitchCase(CFGState& state);
ControlStatus processCondSwitch();
ControlStatus processCondSwitchCase(CFGState& state);
ControlStatus processCondSwitchDefault(CFGState& state);
ControlStatus processCondSwitchBody(CFGState& state);
ControlStatus processSwitchBreak(JSOp op);
ControlStatus processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc);
ControlStatus processTry();
ControlStatus processTryEnd(CFGState& state);
ControlStatus processThrow();
ControlStatus processTableSwitch(JSOp op, jssrcnote* sn);
ControlStatus processContinue(JSOp op);
ControlStatus processBreak(JSOp op, jssrcnote* sn);
ControlStatus processReturn(JSOp op);
ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
ControlStatus snoopControlFlow(JSOp op);
ControlStatus processBrokenLoop(CFGState& state);
ControlStatus finishLoop(CFGState& state, CFGBlock* successor);
ControlStatus processAndOr(JSOp op);
ControlStatus processAndOrEnd(CFGState& state);
ControlStatus processLabel();
ControlStatus processLabelEnd(CFGState& state);
MOZ_MUST_USE bool pushLoop(CFGState::State state, jsbytecode* stopAt, CFGBlock* entry,
jsbytecode* loopHead, jsbytecode* initialPc,
jsbytecode* bodyStart, jsbytecode* bodyEnd,
jsbytecode* exitpc, jsbytecode* continuepc);
void endCurrentBlock(CFGControlInstruction* ins);
CFGBlock* createBreakCatchBlock(DeferredEdge* edge, jsbytecode* pc);
};
} // namespace jit
} // namespace js
#endif /* jit_IonControlFlow_h */

Просмотреть файл

@ -441,6 +441,7 @@ jit::CheckLogging()
" profiling Profiling-related information\n"
" trackopts Optimization tracking information\n"
" dump-mir-expr Dump the MIR expressions\n"
" cfg Control flow graph generation\n"
" all Everything\n"
"\n"
" bl-aborts Baseline compiler abort messages\n"
@ -517,6 +518,8 @@ jit::CheckLogging()
EnableChannel(JitSpew_OptimizationTracking);
if (ContainsFlag(env, "dump-mir-expr"))
EnableChannel(JitSpew_MIRExpressions);
if (ContainsFlag(env, "cfg"))
EnableChannel(JitSpew_CFG);
if (ContainsFlag(env, "all"))
LoggingBits = uint64_t(-1);

Просмотреть файл

@ -66,6 +66,8 @@ namespace jit {
_(CacheFlush) \
/* Output a list of MIR expressions */ \
_(MIRExpressions) \
/* Print control flow graph */ \
_(CFG) \
\
/* BASELINE COMPILER SPEW */ \
\

Просмотреть файл

@ -4141,7 +4141,10 @@ MResumePoint::dump(GenericPrinter& out) const
switch (mode()) {
case MResumePoint::ResumeAt:
out.printf("At");
if (instruction_)
out.printf("At(%d)", instruction_->id());
else
out.printf("At");
break;
case MResumePoint::ResumeAfter:
out.printf("After");

Просмотреть файл

@ -2841,13 +2841,11 @@ class MTableSwitch final
{
// The successors of the tableswitch
// - First successor = the default case
// - Successor 2 and higher = the cases sorted on case index.
// - Successors 2 and higher = the cases
Vector<MBasicBlock*, 0, JitAllocPolicy> successors_;
// Index into successors_ sorted on case index
Vector<size_t, 0, JitAllocPolicy> cases_;
// Contains the blocks/cases that still need to get build
Vector<MBasicBlock*, 0, JitAllocPolicy> blocks_;
MUse operand_;
int32_t low_;
int32_t high_;
@ -2861,7 +2859,6 @@ class MTableSwitch final
int32_t low, int32_t high)
: successors_(alloc),
cases_(alloc),
blocks_(alloc),
low_(low),
high_(high)
{
@ -2904,14 +2901,6 @@ class MTableSwitch final
successors_[i] = successor;
}
MBasicBlock** blocks() {
return &blocks_[0];
}
size_t numBlocks() const {
return blocks_.length();
}
int32_t low() const {
return low_;
}
@ -2928,10 +2917,6 @@ class MTableSwitch final
return getSuccessor(cases_[i]);
}
size_t numCases() const {
return high() - low() + 1;
}
MOZ_MUST_USE bool addDefault(MBasicBlock* block, size_t* index = nullptr) {
MOZ_ASSERT(successors_.empty());
if (index)
@ -2943,13 +2928,8 @@ class MTableSwitch final
return cases_.append(successorIndex);
}
MBasicBlock* getBlock(size_t i) const {
MOZ_ASSERT(i < numBlocks());
return blocks_[i];
}
MOZ_MUST_USE bool addBlock(MBasicBlock* block) {
return blocks_.append(block);
size_t numCases() const {
return high() - low() + 1;
}
MDefinition* getOperand(size_t index) const override {

Просмотреть файл

@ -141,20 +141,77 @@ MIRGraph::renumberBlocksAfter(MBasicBlock* at)
iter->setId(++id);
}
void
MIRGraph::removeBlocksAfter(MBasicBlock* start)
bool
MIRGraph::removeSuccessorBlocks(MBasicBlock* start)
{
MBasicBlockIterator iter(begin());
iter++;
while (iter != end()) {
MBasicBlock* block = *iter;
iter++;
if (!start->hasLastIns())
return true;
if (block->id() <= start->id())
start->mark();
// Mark all successors.
Vector<MBasicBlock*, 4, SystemAllocPolicy> blocks;
for (size_t i = 0; i < start->numSuccessors(); i++) {
if (start->getSuccessor(i)->isMarked())
continue;
if (!blocks.append(start->getSuccessor(i)))
return false;
start->getSuccessor(i)->mark();
}
for (size_t i = 0; i < blocks.length(); i++) {
MBasicBlock* block = blocks[i];
if (!block->hasLastIns())
continue;
removeBlock(block);
for (size_t j = 0; j < block->numSuccessors(); j++) {
if (block->getSuccessor(j)->isMarked())
continue;
if (!blocks.append(block->getSuccessor(j)))
return false;
block->getSuccessor(j)->mark();
}
}
if (osrBlock()) {
if (osrBlock()->getSuccessor(0)->isMarked())
osrBlock()->mark();
}
// Remove blocks.
// If they don't have any predecessor
for (size_t i = 0; i < blocks.length(); i++) {
MBasicBlock* block = blocks[i];
bool allMarked = true;
for (size_t i = 0; i < block->numPredecessors(); i++) {
if (block->getPredecessor(i)->isMarked())
continue;
allMarked = false;
break;
}
if (allMarked) {
removeBlock(block);
} else {
MOZ_ASSERT(block != osrBlock());
for (size_t j = 0; j < block->numPredecessors(); j++) {
if (!block->getPredecessor(j)->isMarked())
continue;
block->removePredecessor(block->getPredecessor(j));
}
// This shouldn't have any instructions yet.
MOZ_ASSERT(block->begin() == block->end());
}
}
if (osrBlock()) {
if (osrBlock()->getSuccessor(0)->isDead())
removeBlock(osrBlock());
}
for (size_t i = 0; i < blocks.length(); i++)
blocks[i]->unmark();
start->unmark();
return true;
}
void
@ -175,18 +232,13 @@ MIRGraph::removeBlock(MBasicBlock* block)
}
}
block->discardAllInstructions();
block->discardAllResumePoints();
// Note: phis are disconnected from the rest of the graph, but are not
// removed entirely. If the block being removed is a loop header then
// IonBuilder may need to access these phis to more quickly converge on the
// possible types in the graph. See IonBuilder::analyzeNewLoopTypes.
block->discardAllPhiOperands();
block->clear();
block->markAsDead();
blocks_.remove(block);
numBlocks_--;
if (block->isInList()) {
blocks_.remove(block);
numBlocks_--;
}
}
void
@ -403,6 +455,7 @@ MBasicBlock::New(MIRGraph& graph, const CompileInfo& info, MBasicBlock* pred, Ki
MBasicBlock::MBasicBlock(MIRGraph& graph, const CompileInfo& info, BytecodeSite* site, Kind kind)
: unreachable_(false),
specialized_(false),
graph_(graph),
info_(info),
predecessors_(graph.alloc()),
@ -1005,6 +1058,19 @@ MBasicBlock::discardAllResumePoints(bool discardEntry)
#endif
}
void
MBasicBlock::clear()
{
discardAllInstructions();
discardAllResumePoints();
// Note: phis are disconnected from the rest of the graph, but are not
// removed entirely. If the block being removed is a loop header then
// IonBuilder may need to access these phis to more quickly converge on the
// possible types in the graph. See IonBuilder::analyzeNewLoopTypes.
discardAllPhiOperands();
}
void
MBasicBlock::insertBefore(MInstruction* at, MInstruction* ins)
{
@ -1552,6 +1618,10 @@ MBasicBlock::inheritPhisFromBackedge(TempAllocator& alloc, MBasicBlock* backedge
bool
MBasicBlock::specializePhis(TempAllocator& alloc)
{
if (specialized_)
return true;
specialized_ = true;
for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
MPhi* phi = *iter;
if (!phi->specializeType(alloc))
@ -1590,7 +1660,6 @@ MBasicBlock::immediateDominatorBranch(BranchDirection* pdirection)
MBasicBlock::BackupPoint::BackupPoint(MBasicBlock* current)
: current_(current),
lastBlock_(nullptr),
lastIns_(current->hasAnyIns() ? *current->rbegin() : nullptr),
stackPosition_(current->stackDepth()),
slots_()
@ -1605,17 +1674,6 @@ MBasicBlock::BackupPoint::BackupPoint(MBasicBlock* current)
{
// The block is not yet jumping into a block of an inlined function yet.
MOZ_ASSERT(current->outerResumePoint_ == nullptr);
// The CFG reconstruction might add blocks and move them around.
uint32_t lastBlockId = 0;
PostorderIterator e = current->graph().poEnd();
for (PostorderIterator b = current->graph().poBegin(); b != e; ++b) {
if (lastBlockId <= b->id()) {
lastBlock_ = *b;
lastBlockId = b->id();
}
}
MOZ_ASSERT(lastBlock_);
}
bool
@ -1663,6 +1721,10 @@ MBasicBlock::BackupPoint::restore()
MOZ_ASSERT_IF(lastIns_, lastIns_->block() == current_);
MOZ_ASSERT_IF(lastIns_, !lastIns_->isDiscarded());
if (!current_->graph().removeSuccessorBlocks(current_))
return nullptr;
MInstructionIterator lastIns(lastIns_ ? ++(current_->begin(lastIns_)) : current_->begin());
current_->discardAllInstructionsStartingAt(lastIns);
current_->clearOuterResumePoint();
@ -1679,8 +1741,6 @@ MBasicBlock::BackupPoint::restore()
MOZ_ASSERT(current_->callerResumePoint() == callerResumePoint_);
MOZ_ASSERT(current_->entryResumePoint() == entryResumePoint_);
current_->graph().removeBlocksAfter(lastBlock_);
return current_;
}

Просмотреть файл

@ -57,6 +57,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
// This block cannot be reached by any means.
bool unreachable_;
// Keeps track if the phis has been type specialized already.
bool specialized_;
// Pushes a copy of a local variable or argument.
void pushVariable(uint32_t slot);
@ -304,6 +307,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
void discardAllPhiOperands();
void discardAllPhis();
void discardAllResumePoints(bool discardEntry = true);
void clear();
// Same as |void discard(MInstruction* ins)| but assuming that
// all operands are already discarded.
@ -662,7 +666,6 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
friend MBasicBlock;
MBasicBlock* current_;
MBasicBlock* lastBlock_;
MInstruction* lastIns_;
uint32_t stackPosition_;
FixedList<MDefinition*> slots_;
@ -839,12 +842,12 @@ class MIRGraph
ReversePostorderIterator rpoEnd() {
return blocks_.end();
}
void removeBlocksAfter(MBasicBlock* block);
MOZ_MUST_USE bool removeSuccessorBlocks(MBasicBlock* block);
void removeBlock(MBasicBlock* block);
void removeBlockIncludingPhis(MBasicBlock* block);
void moveBlockToEnd(MBasicBlock* block) {
MOZ_ASSERT(block->id());
blocks_.remove(block);
MOZ_ASSERT_IF(!blocks_.empty(), block->id());
blocks_.pushBack(block);
}
void moveBlockBefore(MBasicBlock* at, MBasicBlock* block) {
@ -852,6 +855,10 @@ class MIRGraph
blocks_.remove(block);
blocks_.insertBefore(at, block);
}
void removeBlockFromList(MBasicBlock* block) {
blocks_.remove(block);
numBlocks_--;
}
size_t numBlocks() const {
return numBlocks_;
}

Просмотреть файл

@ -236,6 +236,7 @@ UNIFIED_SOURCES += [
'jit/IonAnalysis.cpp',
'jit/IonBuilder.cpp',
'jit/IonCaches.cpp',
'jit/IonControlFlow.cpp',
'jit/IonOptimizationLevels.cpp',
'jit/JitcodeMap.cpp',
'jit/JitFrames.cpp',