mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-09 11:29:39 +00:00
1626 lines
44 KiB
C++
1626 lines
44 KiB
C++
/* -*- 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/. */
|
|
|
|
#include "jit/MIRGraph.h"
|
|
|
|
#include "asmjs/AsmJSValidate.h"
|
|
#include "jit/BytecodeAnalysis.h"
|
|
#include "jit/Ion.h"
|
|
#include "jit/JitSpewer.h"
|
|
#include "jit/MIR.h"
|
|
#include "jit/MIRGenerator.h"
|
|
|
|
using namespace js;
|
|
using namespace js::jit;
|
|
using mozilla::Swap;
|
|
|
|
MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
|
|
TempAllocator* alloc, MIRGraph* graph, const CompileInfo* info,
|
|
const OptimizationInfo* optimizationInfo,
|
|
bool usesSignalHandlersForAsmJSOOB)
|
|
: compartment(compartment),
|
|
info_(info),
|
|
optimizationInfo_(optimizationInfo),
|
|
alloc_(alloc),
|
|
graph_(graph),
|
|
abortReason_(AbortReason_NoAbort),
|
|
shouldForceAbort_(false),
|
|
abortedPreliminaryGroups_(*alloc_),
|
|
error_(false),
|
|
pauseBuild_(nullptr),
|
|
cancelBuild_(false),
|
|
maxAsmJSStackArgBytes_(0),
|
|
performsCall_(false),
|
|
usesSimd_(false),
|
|
usesSimdCached_(false),
|
|
modifiesFrameArguments_(false),
|
|
instrumentedProfiling_(false),
|
|
instrumentedProfilingIsCached_(false),
|
|
safeForMinorGC_(true),
|
|
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
|
usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB),
|
|
#endif
|
|
options(options),
|
|
gs_(alloc)
|
|
{ }
|
|
|
|
bool
|
|
MIRGenerator::usesSimd()
|
|
{
|
|
if (usesSimdCached_)
|
|
return usesSimd_;
|
|
|
|
usesSimdCached_ = true;
|
|
for (ReversePostorderIterator block = graph_->rpoBegin(),
|
|
end = graph_->rpoEnd();
|
|
block != end;
|
|
block++)
|
|
{
|
|
// It's fine to use MInstructionIterator here because we don't have to
|
|
// worry about Phis, since any reachable phi (or phi cycle) will have at
|
|
// least one instruction as an input.
|
|
for (MInstructionIterator inst = block->begin(); inst != block->end(); inst++) {
|
|
// Instructions that have SIMD inputs but not a SIMD type are fine
|
|
// to ignore, as their inputs are also reached at some point. By
|
|
// induction, at least one instruction with a SIMD type is reached
|
|
// at some point.
|
|
if (IsSimdType(inst->type())) {
|
|
MOZ_ASSERT(SupportsSimd);
|
|
usesSimd_ = true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
usesSimd_ = false;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
MIRGenerator::abortFmt(const char* message, va_list ap)
|
|
{
|
|
JitSpewVA(JitSpew_IonAbort, message, ap);
|
|
error_ = true;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
MIRGenerator::abort(const char* message, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, message);
|
|
abortFmt(message, ap);
|
|
va_end(ap);
|
|
return false;
|
|
}
|
|
|
|
void
|
|
MIRGenerator::addAbortedPreliminaryGroup(ObjectGroup* group)
|
|
{
|
|
for (size_t i = 0; i < abortedPreliminaryGroups_.length(); i++) {
|
|
if (group == abortedPreliminaryGroups_[i])
|
|
return;
|
|
}
|
|
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
if (!abortedPreliminaryGroups_.append(group))
|
|
oomUnsafe.crash("addAbortedPreliminaryGroup");
|
|
}
|
|
|
|
bool
|
|
MIRGenerator::needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const
|
|
{
|
|
// A heap access needs a bounds-check branch if we're not relying on signal
|
|
// handlers to catch errors, and if it's not proven to be within bounds.
|
|
// We use signal-handlers on x64, but on x86 there isn't enough address
|
|
// space for a guard region. Also, on x64 the atomic loads and stores
|
|
// can't (yet) use the signal handlers.
|
|
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
|
if (usesSignalHandlersForAsmJSOOB_ && !access->isAtomicAccess())
|
|
return false;
|
|
#endif
|
|
return access->needsBoundsCheck();
|
|
}
|
|
|
|
size_t
|
|
MIRGenerator::foldableOffsetRange(const MAsmJSHeapAccess* access) const
|
|
{
|
|
// This determines whether it's ok to fold up to AsmJSImmediateSize
|
|
// offsets, instead of just AsmJSCheckedImmediateSize.
|
|
|
|
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
|
// With signal-handler OOB handling, we reserve guard space for the full
|
|
// immediate size.
|
|
if (usesSignalHandlersForAsmJSOOB_)
|
|
return AsmJSImmediateRange;
|
|
#endif
|
|
|
|
// On 32-bit platforms, if we've proven the access is in bounds after
|
|
// 32-bit wrapping, we can fold full offsets because they're added with
|
|
// 32-bit arithmetic.
|
|
if (sizeof(intptr_t) == sizeof(int32_t) && !access->needsBoundsCheck())
|
|
return AsmJSImmediateRange;
|
|
|
|
// Otherwise, only allow the checked size. This is always less than the
|
|
// minimum heap length, and allows explicit bounds checks to fold in the
|
|
// offset without overflow.
|
|
return AsmJSCheckedImmediateRange;
|
|
}
|
|
|
|
void
|
|
MIRGraph::addBlock(MBasicBlock* block)
|
|
{
|
|
MOZ_ASSERT(block);
|
|
block->setId(blockIdGen_++);
|
|
blocks_.pushBack(block);
|
|
numBlocks_++;
|
|
}
|
|
|
|
void
|
|
MIRGraph::insertBlockAfter(MBasicBlock* at, MBasicBlock* block)
|
|
{
|
|
block->setId(blockIdGen_++);
|
|
blocks_.insertAfter(at, block);
|
|
numBlocks_++;
|
|
}
|
|
|
|
void
|
|
MIRGraph::insertBlockBefore(MBasicBlock* at, MBasicBlock* block)
|
|
{
|
|
block->setId(blockIdGen_++);
|
|
blocks_.insertBefore(at, block);
|
|
numBlocks_++;
|
|
}
|
|
|
|
void
|
|
MIRGraph::renumberBlocksAfter(MBasicBlock* at)
|
|
{
|
|
MBasicBlockIterator iter = begin(at);
|
|
iter++;
|
|
|
|
uint32_t id = at->id();
|
|
for (; iter != end(); iter++)
|
|
iter->setId(++id);
|
|
}
|
|
|
|
void
|
|
MIRGraph::removeBlocksAfter(MBasicBlock* start)
|
|
{
|
|
MBasicBlockIterator iter(begin());
|
|
iter++;
|
|
while (iter != end()) {
|
|
MBasicBlock* block = *iter;
|
|
iter++;
|
|
|
|
if (block->id() <= start->id())
|
|
continue;
|
|
|
|
removeBlock(block);
|
|
}
|
|
}
|
|
|
|
void
|
|
MIRGraph::removeBlock(MBasicBlock* block)
|
|
{
|
|
// Remove a block from the graph. It will also cleanup the block.
|
|
|
|
if (block == osrBlock_)
|
|
osrBlock_ = nullptr;
|
|
|
|
if (returnAccumulator_) {
|
|
size_t i = 0;
|
|
while (i < returnAccumulator_->length()) {
|
|
if ((*returnAccumulator_)[i] == block)
|
|
returnAccumulator_->erase(returnAccumulator_->begin() + i);
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
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->markAsDead();
|
|
blocks_.remove(block);
|
|
numBlocks_--;
|
|
}
|
|
|
|
void
|
|
MIRGraph::removeBlockIncludingPhis(MBasicBlock* block)
|
|
{
|
|
// removeBlock doesn't clear phis because of IonBuilder constraints. Here,
|
|
// we want to totally clear everything.
|
|
removeBlock(block);
|
|
block->discardAllPhis();
|
|
}
|
|
|
|
void
|
|
MIRGraph::unmarkBlocks()
|
|
{
|
|
for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++)
|
|
i->unmark();
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::New(MIRGraph& graph, BytecodeAnalysis* analysis, const CompileInfo& info,
|
|
MBasicBlock* pred, BytecodeSite* site, Kind kind)
|
|
{
|
|
MOZ_ASSERT(site->pc() != nullptr);
|
|
|
|
MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
|
|
if (!block->init())
|
|
return nullptr;
|
|
|
|
if (!block->inherit(graph.alloc(), analysis, pred, 0))
|
|
return nullptr;
|
|
|
|
return block;
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::NewPopN(MIRGraph& graph, const CompileInfo& info,
|
|
MBasicBlock* pred, BytecodeSite* site, Kind kind, uint32_t popped)
|
|
{
|
|
MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
|
|
if (!block->init())
|
|
return nullptr;
|
|
|
|
if (!block->inherit(graph.alloc(), nullptr, pred, popped))
|
|
return nullptr;
|
|
|
|
return block;
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::NewWithResumePoint(MIRGraph& graph, const CompileInfo& info,
|
|
MBasicBlock* pred, BytecodeSite* site,
|
|
MResumePoint* resumePoint)
|
|
{
|
|
MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL);
|
|
|
|
MOZ_ASSERT(!resumePoint->instruction());
|
|
resumePoint->block()->discardResumePoint(resumePoint, RefType_None);
|
|
resumePoint->block_ = block;
|
|
block->addResumePoint(resumePoint);
|
|
block->entryResumePoint_ = resumePoint;
|
|
|
|
if (!block->init())
|
|
return nullptr;
|
|
|
|
if (!block->inheritResumePoint(pred))
|
|
return nullptr;
|
|
|
|
return block;
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::NewPendingLoopHeader(MIRGraph& graph, const CompileInfo& info,
|
|
MBasicBlock* pred, BytecodeSite* site,
|
|
unsigned stackPhiCount)
|
|
{
|
|
MOZ_ASSERT(site->pc() != nullptr);
|
|
|
|
MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, PENDING_LOOP_HEADER);
|
|
if (!block->init())
|
|
return nullptr;
|
|
|
|
if (!block->inherit(graph.alloc(), nullptr, pred, 0, stackPhiCount))
|
|
return nullptr;
|
|
|
|
return block;
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::NewSplitEdge(MIRGraph& graph, const CompileInfo& info, MBasicBlock* pred)
|
|
{
|
|
return pred->pc()
|
|
? MBasicBlock::New(graph, nullptr, info, pred,
|
|
new(graph.alloc()) BytecodeSite(pred->trackedTree(), pred->pc()),
|
|
SPLIT_EDGE)
|
|
: MBasicBlock::NewAsmJS(graph, info, pred, SPLIT_EDGE);
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::NewAsmJS(MIRGraph& graph, const CompileInfo& info, MBasicBlock* pred, Kind kind)
|
|
{
|
|
BytecodeSite* site = new(graph.alloc()) BytecodeSite();
|
|
MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
|
|
if (!block->init())
|
|
return nullptr;
|
|
|
|
if (pred) {
|
|
block->stackPosition_ = pred->stackPosition_;
|
|
|
|
if (block->kind_ == PENDING_LOOP_HEADER) {
|
|
size_t nphis = block->stackPosition_;
|
|
|
|
size_t nfree = graph.phiFreeListLength();
|
|
|
|
TempAllocator& alloc = graph.alloc();
|
|
MPhi* phis = nullptr;
|
|
if (nphis > nfree) {
|
|
phis = alloc.allocateArray<MPhi>(nphis - nfree);
|
|
if (!phis)
|
|
return nullptr;
|
|
}
|
|
|
|
// Note: Phis are inserted in the same order as the slots.
|
|
for (size_t i = 0; i < nphis; i++) {
|
|
MDefinition* predSlot = pred->getSlot(i);
|
|
|
|
MOZ_ASSERT(predSlot->type() != MIRType_Value);
|
|
|
|
MPhi* phi;
|
|
if (i < nfree)
|
|
phi = graph.takePhiFromFreeList();
|
|
else
|
|
phi = phis + (i - nfree);
|
|
new(phi) MPhi(alloc, predSlot->type());
|
|
|
|
phi->addInput(predSlot);
|
|
|
|
// Add append Phis in the block.
|
|
block->addPhi(phi);
|
|
block->setSlot(i, phi);
|
|
}
|
|
} else {
|
|
block->copySlots(pred);
|
|
}
|
|
|
|
if (!block->predecessors_.append(pred))
|
|
return nullptr;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
MBasicBlock::MBasicBlock(MIRGraph& graph, const CompileInfo& info, BytecodeSite* site, Kind kind)
|
|
: unreachable_(false),
|
|
graph_(graph),
|
|
info_(info),
|
|
predecessors_(graph.alloc()),
|
|
stackPosition_(info_.firstStackSlot()),
|
|
numDominated_(0),
|
|
pc_(site->pc()),
|
|
lir_(nullptr),
|
|
callerResumePoint_(nullptr),
|
|
entryResumePoint_(nullptr),
|
|
outerResumePoint_(nullptr),
|
|
successorWithPhis_(nullptr),
|
|
positionInPhiSuccessor_(0),
|
|
loopDepth_(0),
|
|
kind_(kind),
|
|
mark_(false),
|
|
immediatelyDominated_(graph.alloc()),
|
|
immediateDominator_(nullptr),
|
|
trackedSite_(site),
|
|
hitCount_(0),
|
|
hitState_(HitState::NotDefined)
|
|
#if defined (JS_ION_PERF)
|
|
, lineno_(0u),
|
|
columnIndex_(0u)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::init()
|
|
{
|
|
return slots_.init(graph_.alloc(), info_.nslots());
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::increaseSlots(size_t num)
|
|
{
|
|
return slots_.growBy(graph_.alloc(), num);
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::ensureHasSlots(size_t num)
|
|
{
|
|
size_t depth = stackDepth() + num;
|
|
if (depth > nslots()) {
|
|
if (!increaseSlots(depth - nslots()))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::copySlots(MBasicBlock* from)
|
|
{
|
|
MOZ_ASSERT(stackPosition_ <= from->stackPosition_);
|
|
|
|
MDefinition** thisSlots = slots_.begin();
|
|
MDefinition** fromSlots = from->slots_.begin();
|
|
for (size_t i = 0, e = stackPosition_; i < e; ++i)
|
|
thisSlots[i] = fromSlots[i];
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::inherit(TempAllocator& alloc, BytecodeAnalysis* analysis, MBasicBlock* pred,
|
|
uint32_t popped, unsigned stackPhiCount)
|
|
{
|
|
if (pred) {
|
|
stackPosition_ = pred->stackPosition_;
|
|
MOZ_ASSERT(stackPosition_ >= popped);
|
|
stackPosition_ -= popped;
|
|
if (kind_ != PENDING_LOOP_HEADER)
|
|
copySlots(pred);
|
|
} else {
|
|
uint32_t stackDepth = analysis->info(pc()).stackDepth;
|
|
stackPosition_ = info().firstStackSlot() + stackDepth;
|
|
MOZ_ASSERT(stackPosition_ >= popped);
|
|
stackPosition_ -= popped;
|
|
}
|
|
|
|
MOZ_ASSERT(info_.nslots() >= stackPosition_);
|
|
MOZ_ASSERT(!entryResumePoint_);
|
|
|
|
// Propagate the caller resume point from the inherited block.
|
|
callerResumePoint_ = pred ? pred->callerResumePoint() : nullptr;
|
|
|
|
// Create a resume point using our initial stack state.
|
|
entryResumePoint_ = new(alloc) MResumePoint(this, pc(), MResumePoint::ResumeAt);
|
|
if (!entryResumePoint_->init(alloc))
|
|
return false;
|
|
|
|
if (pred) {
|
|
if (!predecessors_.append(pred))
|
|
return false;
|
|
|
|
if (kind_ == PENDING_LOOP_HEADER) {
|
|
size_t i = 0;
|
|
for (i = 0; i < info().firstStackSlot(); i++) {
|
|
MPhi* phi = MPhi::New(alloc);
|
|
phi->addInput(pred->getSlot(i));
|
|
addPhi(phi);
|
|
setSlot(i, phi);
|
|
entryResumePoint()->initOperand(i, phi);
|
|
}
|
|
|
|
MOZ_ASSERT(stackPhiCount <= stackDepth());
|
|
MOZ_ASSERT(info().firstStackSlot() <= stackDepth() - stackPhiCount);
|
|
|
|
// Avoid creating new phis for stack values that aren't part of the
|
|
// loop. Note that for loop headers that can OSR, all values on the
|
|
// stack are part of the loop.
|
|
for (; i < stackDepth() - stackPhiCount; i++) {
|
|
MDefinition* val = pred->getSlot(i);
|
|
setSlot(i, val);
|
|
entryResumePoint()->initOperand(i, val);
|
|
}
|
|
|
|
for (; i < stackDepth(); i++) {
|
|
MPhi* phi = MPhi::New(alloc);
|
|
phi->addInput(pred->getSlot(i));
|
|
addPhi(phi);
|
|
setSlot(i, phi);
|
|
entryResumePoint()->initOperand(i, phi);
|
|
}
|
|
} else {
|
|
for (size_t i = 0; i < stackDepth(); i++)
|
|
entryResumePoint()->initOperand(i, getSlot(i));
|
|
}
|
|
} else {
|
|
/*
|
|
* Don't leave the operands uninitialized for the caller, as it may not
|
|
* initialize them later on.
|
|
*/
|
|
for (size_t i = 0; i < stackDepth(); i++)
|
|
entryResumePoint()->clearOperand(i);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::inheritResumePoint(MBasicBlock* pred)
|
|
{
|
|
// Copy slots from the resume point.
|
|
stackPosition_ = entryResumePoint_->stackDepth();
|
|
for (uint32_t i = 0; i < stackPosition_; i++)
|
|
slots_[i] = entryResumePoint_->getOperand(i);
|
|
|
|
MOZ_ASSERT(info_.nslots() >= stackPosition_);
|
|
MOZ_ASSERT(kind_ != PENDING_LOOP_HEADER);
|
|
MOZ_ASSERT(pred != nullptr);
|
|
|
|
callerResumePoint_ = pred->callerResumePoint();
|
|
|
|
if (!predecessors_.append(pred))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::inheritSlots(MBasicBlock* parent)
|
|
{
|
|
stackPosition_ = parent->stackPosition_;
|
|
copySlots(parent);
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::initEntrySlots(TempAllocator& alloc)
|
|
{
|
|
// Remove the previous resume point.
|
|
discardResumePoint(entryResumePoint_);
|
|
|
|
// Create a resume point using our initial stack state.
|
|
entryResumePoint_ = MResumePoint::New(alloc, this, pc(), MResumePoint::ResumeAt);
|
|
if (!entryResumePoint_)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
MDefinition*
|
|
MBasicBlock::getSlot(uint32_t index)
|
|
{
|
|
MOZ_ASSERT(index < stackPosition_);
|
|
return slots_[index];
|
|
}
|
|
|
|
void
|
|
MBasicBlock::initSlot(uint32_t slot, MDefinition* ins)
|
|
{
|
|
slots_[slot] = ins;
|
|
if (entryResumePoint())
|
|
entryResumePoint()->initOperand(slot, ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::shimmySlots(int discardDepth)
|
|
{
|
|
// Move all slots above the given depth down by one,
|
|
// overwriting the MDefinition at discardDepth.
|
|
|
|
MOZ_ASSERT(discardDepth < 0);
|
|
MOZ_ASSERT(stackPosition_ + discardDepth >= info_.firstStackSlot());
|
|
|
|
for (int i = discardDepth; i < -1; i++)
|
|
slots_[stackPosition_ + i] = slots_[stackPosition_ + i + 1];
|
|
|
|
--stackPosition_;
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::linkOsrValues(MStart* start)
|
|
{
|
|
MOZ_ASSERT(start->startType() == MStart::StartType_Osr);
|
|
|
|
MResumePoint* res = start->resumePoint();
|
|
|
|
for (uint32_t i = 0; i < stackDepth(); i++) {
|
|
MDefinition* def = slots_[i];
|
|
MInstruction* cloneRp = nullptr;
|
|
if (i == info().scopeChainSlot()) {
|
|
if (def->isOsrScopeChain())
|
|
cloneRp = def->toOsrScopeChain();
|
|
} else if (i == info().returnValueSlot()) {
|
|
if (def->isOsrReturnValue())
|
|
cloneRp = def->toOsrReturnValue();
|
|
} else if (info().hasArguments() && i == info().argsObjSlot()) {
|
|
MOZ_ASSERT(def->isConstant() || def->isOsrArgumentsObject());
|
|
MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
|
|
if (def->isOsrArgumentsObject())
|
|
cloneRp = def->toOsrArgumentsObject();
|
|
} else {
|
|
MOZ_ASSERT(def->isOsrValue() || def->isGetArgumentsObjectArg() || def->isConstant() ||
|
|
def->isParameter());
|
|
|
|
// A constant Undefined can show up here for an argument slot when
|
|
// the function has an arguments object, but the argument in
|
|
// question is stored on the scope chain.
|
|
MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
|
|
|
|
if (def->isOsrValue())
|
|
cloneRp = def->toOsrValue();
|
|
else if (def->isGetArgumentsObjectArg())
|
|
cloneRp = def->toGetArgumentsObjectArg();
|
|
else if (def->isParameter())
|
|
cloneRp = def->toParameter();
|
|
}
|
|
|
|
if (cloneRp) {
|
|
MResumePoint* clone = MResumePoint::Copy(graph().alloc(), res);
|
|
if (!clone)
|
|
return false;
|
|
cloneRp->setResumePoint(clone);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setSlot(uint32_t slot, MDefinition* ins)
|
|
{
|
|
slots_[slot] = ins;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setVariable(uint32_t index)
|
|
{
|
|
MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
|
|
setSlot(index, slots_[stackPosition_ - 1]);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setArg(uint32_t arg)
|
|
{
|
|
setVariable(info_.argSlot(arg));
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setLocal(uint32_t local)
|
|
{
|
|
setVariable(info_.localSlot(local));
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setSlot(uint32_t slot)
|
|
{
|
|
setVariable(slot);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::rewriteSlot(uint32_t slot, MDefinition* ins)
|
|
{
|
|
setSlot(slot, ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::rewriteAtDepth(int32_t depth, MDefinition* ins)
|
|
{
|
|
MOZ_ASSERT(depth < 0);
|
|
MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
|
|
rewriteSlot(stackPosition_ + depth, ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::push(MDefinition* ins)
|
|
{
|
|
MOZ_ASSERT(stackPosition_ < nslots());
|
|
slots_[stackPosition_++] = ins;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::pushVariable(uint32_t slot)
|
|
{
|
|
push(slots_[slot]);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::pushArg(uint32_t arg)
|
|
{
|
|
pushVariable(info_.argSlot(arg));
|
|
}
|
|
|
|
void
|
|
MBasicBlock::pushLocal(uint32_t local)
|
|
{
|
|
pushVariable(info_.localSlot(local));
|
|
}
|
|
|
|
void
|
|
MBasicBlock::pushSlot(uint32_t slot)
|
|
{
|
|
pushVariable(slot);
|
|
}
|
|
|
|
MDefinition*
|
|
MBasicBlock::pop()
|
|
{
|
|
MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
|
|
return slots_[--stackPosition_];
|
|
}
|
|
|
|
void
|
|
MBasicBlock::popn(uint32_t n)
|
|
{
|
|
MOZ_ASSERT(stackPosition_ - n >= info_.firstStackSlot());
|
|
MOZ_ASSERT(stackPosition_ >= stackPosition_ - n);
|
|
stackPosition_ -= n;
|
|
}
|
|
|
|
MDefinition*
|
|
MBasicBlock::scopeChain()
|
|
{
|
|
return getSlot(info().scopeChainSlot());
|
|
}
|
|
|
|
MDefinition*
|
|
MBasicBlock::argumentsObject()
|
|
{
|
|
return getSlot(info().argsObjSlot());
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setScopeChain(MDefinition* scopeObj)
|
|
{
|
|
setSlot(info().scopeChainSlot(), scopeObj);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setArgumentsObject(MDefinition* argsObj)
|
|
{
|
|
setSlot(info().argsObjSlot(), argsObj);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::pick(int32_t depth)
|
|
{
|
|
// pick take an element and move it to the top.
|
|
// pick(-2):
|
|
// A B C D E
|
|
// A B D C E [ swapAt(-2) ]
|
|
// A B D E C [ swapAt(-1) ]
|
|
for (; depth < 0; depth++)
|
|
swapAt(depth);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::swapAt(int32_t depth)
|
|
{
|
|
uint32_t lhsDepth = stackPosition_ + depth - 1;
|
|
uint32_t rhsDepth = stackPosition_ + depth;
|
|
|
|
MDefinition* temp = slots_[lhsDepth];
|
|
slots_[lhsDepth] = slots_[rhsDepth];
|
|
slots_[rhsDepth] = temp;
|
|
}
|
|
|
|
MDefinition*
|
|
MBasicBlock::peek(int32_t depth)
|
|
{
|
|
MOZ_ASSERT(depth < 0);
|
|
MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
|
|
return getSlot(stackPosition_ + depth);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardLastIns()
|
|
{
|
|
discard(lastIns());
|
|
}
|
|
|
|
MConstant*
|
|
MBasicBlock::optimizedOutConstant(TempAllocator& alloc)
|
|
{
|
|
// If the first instruction is a MConstant(MagicValue(JS_OPTIMIZED_OUT))
|
|
// then reuse it.
|
|
MInstruction* ins = *begin();
|
|
if (ins->type() == MIRType_MagicOptimizedOut)
|
|
return ins->toConstant();
|
|
|
|
MConstant* constant = MConstant::New(alloc, MagicValue(JS_OPTIMIZED_OUT));
|
|
insertBefore(ins, constant);
|
|
return constant;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::addFromElsewhere(MInstruction* ins)
|
|
{
|
|
MOZ_ASSERT(ins->block() != this);
|
|
|
|
// Remove |ins| from its containing block.
|
|
ins->block()->instructions_.remove(ins);
|
|
|
|
// Add it to this block.
|
|
add(ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::moveBefore(MInstruction* at, MInstruction* ins)
|
|
{
|
|
// Remove |ins| from the current block.
|
|
MOZ_ASSERT(ins->block() == this);
|
|
instructions_.remove(ins);
|
|
|
|
// Insert into new block, which may be distinct.
|
|
// Uses and operands are untouched.
|
|
ins->setBlock(at->block());
|
|
at->block()->instructions_.insertBefore(at, ins);
|
|
ins->setTrackedSite(at->trackedSite());
|
|
}
|
|
|
|
MInstruction*
|
|
MBasicBlock::safeInsertTop(MDefinition* ins, IgnoreTop ignore)
|
|
{
|
|
// Beta nodes and interrupt checks are required to be located at the
|
|
// beginnings of basic blocks, so we must insert new instructions after any
|
|
// such instructions.
|
|
MInstructionIterator insertIter = !ins || ins->isPhi()
|
|
? begin()
|
|
: begin(ins->toInstruction());
|
|
while (insertIter->isBeta() ||
|
|
insertIter->isInterruptCheck() ||
|
|
insertIter->isConstant() ||
|
|
(!(ignore & IgnoreRecover) && insertIter->isRecoveredOnBailout()))
|
|
{
|
|
insertIter++;
|
|
}
|
|
|
|
return *insertIter;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardResumePoint(MResumePoint* rp, ReferencesType refType /* = RefType_Default */)
|
|
{
|
|
if (refType & RefType_DiscardOperands)
|
|
rp->releaseUses();
|
|
#ifdef DEBUG
|
|
MResumePointIterator iter = resumePointsBegin();
|
|
while (*iter != rp) {
|
|
// We should reach it before reaching the end.
|
|
MOZ_ASSERT(iter != resumePointsEnd());
|
|
iter++;
|
|
}
|
|
resumePoints_.removeAt(iter);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MBasicBlock::prepareForDiscard(MInstruction* ins, ReferencesType refType /* = RefType_Default */)
|
|
{
|
|
// Only remove instructions from the same basic block. This is needed for
|
|
// correctly removing the resume point if any.
|
|
MOZ_ASSERT(ins->block() == this);
|
|
|
|
MResumePoint* rp = ins->resumePoint();
|
|
if ((refType & RefType_DiscardResumePoint) && rp)
|
|
discardResumePoint(rp, refType);
|
|
|
|
// We need to assert that instructions have no uses after removing the their
|
|
// resume points operands as they could be captured by their own resume
|
|
// point.
|
|
MOZ_ASSERT_IF(refType & RefType_AssertNoUses, !ins->hasUses());
|
|
|
|
const uint32_t InstructionOperands = RefType_DiscardOperands | RefType_DiscardInstruction;
|
|
if ((refType & InstructionOperands) == InstructionOperands) {
|
|
for (size_t i = 0, e = ins->numOperands(); i < e; i++)
|
|
ins->releaseOperand(i);
|
|
}
|
|
|
|
ins->setDiscarded();
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discard(MInstruction* ins)
|
|
{
|
|
prepareForDiscard(ins);
|
|
instructions_.remove(ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardIgnoreOperands(MInstruction* ins)
|
|
{
|
|
#ifdef DEBUG
|
|
for (size_t i = 0, e = ins->numOperands(); i < e; i++)
|
|
MOZ_ASSERT(!ins->hasOperand(i));
|
|
#endif
|
|
|
|
prepareForDiscard(ins, RefType_IgnoreOperands);
|
|
instructions_.remove(ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardDef(MDefinition* at)
|
|
{
|
|
if (at->isPhi())
|
|
at->block_->discardPhi(at->toPhi());
|
|
else
|
|
at->block_->discard(at->toInstruction());
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardAllInstructions()
|
|
{
|
|
MInstructionIterator iter = begin();
|
|
discardAllInstructionsStartingAt(iter);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardAllInstructionsStartingAt(MInstructionIterator iter)
|
|
{
|
|
while (iter != end()) {
|
|
// Discard operands and resume point operands and flag the instruction
|
|
// as discarded. Also we do not assert that we have no uses as blocks
|
|
// might be removed in reverse post order.
|
|
MInstruction* ins = *iter++;
|
|
prepareForDiscard(ins, RefType_DefaultNoAssert);
|
|
instructions_.remove(ins);
|
|
}
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardAllPhiOperands()
|
|
{
|
|
for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++)
|
|
iter->removeAllOperands();
|
|
|
|
for (MBasicBlock** pred = predecessors_.begin(); pred != predecessors_.end(); pred++)
|
|
(*pred)->clearSuccessorWithPhis();
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardAllPhis()
|
|
{
|
|
discardAllPhiOperands();
|
|
phis_.clear();
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardAllResumePoints(bool discardEntry)
|
|
{
|
|
if (outerResumePoint_)
|
|
clearOuterResumePoint();
|
|
|
|
if (discardEntry && entryResumePoint_)
|
|
clearEntryResumePoint();
|
|
|
|
#ifdef DEBUG
|
|
if (!entryResumePoint()) {
|
|
MOZ_ASSERT(resumePointsEmpty());
|
|
} else {
|
|
MResumePointIterator iter(resumePointsBegin());
|
|
MOZ_ASSERT(iter != resumePointsEnd());
|
|
iter++;
|
|
MOZ_ASSERT(iter == resumePointsEnd());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MBasicBlock::insertBefore(MInstruction* at, MInstruction* ins)
|
|
{
|
|
MOZ_ASSERT(at->block() == this);
|
|
ins->setBlock(this);
|
|
graph().allocDefinitionId(ins);
|
|
instructions_.insertBefore(at, ins);
|
|
ins->setTrackedSite(at->trackedSite());
|
|
}
|
|
|
|
void
|
|
MBasicBlock::insertAfter(MInstruction* at, MInstruction* ins)
|
|
{
|
|
MOZ_ASSERT(at->block() == this);
|
|
ins->setBlock(this);
|
|
graph().allocDefinitionId(ins);
|
|
instructions_.insertAfter(at, ins);
|
|
ins->setTrackedSite(at->trackedSite());
|
|
}
|
|
|
|
void
|
|
MBasicBlock::insertAtEnd(MInstruction* ins)
|
|
{
|
|
if (hasLastIns())
|
|
insertBefore(lastIns(), ins);
|
|
else
|
|
add(ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::add(MInstruction* ins)
|
|
{
|
|
MOZ_ASSERT(!hasLastIns());
|
|
ins->setBlock(this);
|
|
graph().allocDefinitionId(ins);
|
|
instructions_.pushBack(ins);
|
|
ins->setTrackedSite(trackedSite_);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::end(MControlInstruction* ins)
|
|
{
|
|
MOZ_ASSERT(!hasLastIns()); // Existing control instructions should be removed first.
|
|
MOZ_ASSERT(ins);
|
|
add(ins);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::addPhi(MPhi* phi)
|
|
{
|
|
phis_.pushBack(phi);
|
|
phi->setBlock(this);
|
|
graph().allocDefinitionId(phi);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::discardPhi(MPhi* phi)
|
|
{
|
|
MOZ_ASSERT(!phis_.empty());
|
|
|
|
phi->removeAllOperands();
|
|
phi->setDiscarded();
|
|
|
|
phis_.remove(phi);
|
|
|
|
if (phis_.empty()) {
|
|
for (MBasicBlock* pred : predecessors_)
|
|
pred->clearSuccessorWithPhis();
|
|
}
|
|
}
|
|
|
|
void
|
|
MBasicBlock::flagOperandsOfPrunedBranches(MInstruction* ins)
|
|
{
|
|
// Find the previous resume point which would be used for bailing out.
|
|
MResumePoint* rp = nullptr;
|
|
for (MInstructionReverseIterator iter = rbegin(ins); iter != rend(); iter++) {
|
|
rp = iter->resumePoint();
|
|
if (rp)
|
|
break;
|
|
}
|
|
|
|
// If none, take the entry resume point.
|
|
if (!rp)
|
|
rp = entryResumePoint();
|
|
|
|
// The only blocks which do not have any entryResumePoint in Ion, are the
|
|
// SplitEdge blocks. SplitEdge blocks only have a Goto instruction before
|
|
// Range Analysis phase. In adjustInputs, we are manipulating instructions
|
|
// which have a TypePolicy. So, as a Goto has no operand and no type
|
|
// policy, the entry resume point should exists.
|
|
MOZ_ASSERT(rp);
|
|
|
|
// Flag all operand as being potentially used.
|
|
while (rp) {
|
|
for (size_t i = 0, end = rp->numOperands(); i < end; i++)
|
|
rp->getOperand(i)->setUseRemovedUnchecked();
|
|
rp = rp->caller();
|
|
}
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::addPredecessor(TempAllocator& alloc, MBasicBlock* pred)
|
|
{
|
|
return addPredecessorPopN(alloc, pred, 0);
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::addPredecessorPopN(TempAllocator& alloc, MBasicBlock* pred, uint32_t popped)
|
|
{
|
|
MOZ_ASSERT(pred);
|
|
MOZ_ASSERT(predecessors_.length() > 0);
|
|
|
|
// Predecessors must be finished, and at the correct stack depth.
|
|
MOZ_ASSERT(pred->hasLastIns());
|
|
MOZ_ASSERT(pred->stackPosition_ == stackPosition_ + popped);
|
|
|
|
for (uint32_t i = 0, e = stackPosition_; i < e; ++i) {
|
|
MDefinition* mine = getSlot(i);
|
|
MDefinition* other = pred->getSlot(i);
|
|
|
|
if (mine != other) {
|
|
// If the current instruction is a phi, and it was created in this
|
|
// basic block, then we have already placed this phi and should
|
|
// instead append to its operands.
|
|
if (mine->isPhi() && mine->block() == this) {
|
|
MOZ_ASSERT(predecessors_.length());
|
|
if (!mine->toPhi()->addInputSlow(other))
|
|
return false;
|
|
} else {
|
|
// Otherwise, create a new phi node.
|
|
MPhi* phi;
|
|
if (mine->type() == other->type())
|
|
phi = MPhi::New(alloc, mine->type());
|
|
else
|
|
phi = MPhi::New(alloc);
|
|
addPhi(phi);
|
|
|
|
// Prime the phi for each predecessor, so input(x) comes from
|
|
// predecessor(x).
|
|
if (!phi->reserveLength(predecessors_.length() + 1))
|
|
return false;
|
|
|
|
for (size_t j = 0, numPreds = predecessors_.length(); j < numPreds; ++j) {
|
|
MOZ_ASSERT(predecessors_[j]->getSlot(i) == mine);
|
|
phi->addInput(mine);
|
|
}
|
|
phi->addInput(other);
|
|
|
|
setSlot(i, phi);
|
|
if (entryResumePoint())
|
|
entryResumePoint()->replaceOperand(i, phi);
|
|
}
|
|
}
|
|
}
|
|
|
|
return predecessors_.append(pred);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::addPredecessorSameInputsAs(MBasicBlock* pred, MBasicBlock* existingPred)
|
|
{
|
|
MOZ_ASSERT(pred);
|
|
MOZ_ASSERT(predecessors_.length() > 0);
|
|
|
|
// Predecessors must be finished, and at the correct stack depth.
|
|
MOZ_ASSERT(pred->hasLastIns());
|
|
MOZ_ASSERT(!pred->successorWithPhis());
|
|
|
|
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
|
|
if (!phisEmpty()) {
|
|
size_t existingPosition = indexForPredecessor(existingPred);
|
|
for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
|
|
if (!iter->addInputSlow(iter->getOperand(existingPosition)))
|
|
oomUnsafe.crash("MBasicBlock::addPredecessorAdjustPhis");
|
|
}
|
|
}
|
|
|
|
if (!predecessors_.append(pred))
|
|
oomUnsafe.crash("MBasicBlock::addPredecessorAdjustPhis");
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::addPredecessorWithoutPhis(MBasicBlock* pred)
|
|
{
|
|
// Predecessors must be finished.
|
|
MOZ_ASSERT(pred && pred->hasLastIns());
|
|
return predecessors_.append(pred);
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::addImmediatelyDominatedBlock(MBasicBlock* child)
|
|
{
|
|
return immediatelyDominated_.append(child);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::removeImmediatelyDominatedBlock(MBasicBlock* child)
|
|
{
|
|
for (size_t i = 0; ; ++i) {
|
|
MOZ_ASSERT(i < immediatelyDominated_.length(),
|
|
"Dominated block to remove not present");
|
|
if (immediatelyDominated_[i] == child) {
|
|
immediatelyDominated_[i] = immediatelyDominated_.back();
|
|
immediatelyDominated_.popBack();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MBasicBlock::assertUsesAreNotWithin(MUseIterator use, MUseIterator end)
|
|
{
|
|
#ifdef DEBUG
|
|
for (; use != end; use++) {
|
|
MOZ_ASSERT_IF(use->consumer()->isDefinition(),
|
|
use->consumer()->toDefinition()->block()->id() < id());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
AbortReason
|
|
MBasicBlock::setBackedge(MBasicBlock* pred)
|
|
{
|
|
// Predecessors must be finished, and at the correct stack depth.
|
|
MOZ_ASSERT(hasLastIns());
|
|
MOZ_ASSERT(pred->hasLastIns());
|
|
MOZ_ASSERT(pred->stackDepth() == entryResumePoint()->stackDepth());
|
|
|
|
// We must be a pending loop header
|
|
MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
|
|
|
|
bool hadTypeChange = false;
|
|
|
|
// Add exit definitions to each corresponding phi at the entry.
|
|
if (!inheritPhisFromBackedge(pred, &hadTypeChange))
|
|
return AbortReason_Alloc;
|
|
|
|
if (hadTypeChange) {
|
|
for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++)
|
|
phi->removeOperand(phi->numOperands() - 1);
|
|
return AbortReason_Disable;
|
|
}
|
|
|
|
// We are now a loop header proper
|
|
kind_ = LOOP_HEADER;
|
|
|
|
if (!predecessors_.append(pred))
|
|
return AbortReason_Alloc;
|
|
|
|
return AbortReason_NoAbort;
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::setBackedgeAsmJS(MBasicBlock* pred)
|
|
{
|
|
// Predecessors must be finished, and at the correct stack depth.
|
|
MOZ_ASSERT(hasLastIns());
|
|
MOZ_ASSERT(pred->hasLastIns());
|
|
MOZ_ASSERT(stackDepth() == pred->stackDepth());
|
|
|
|
// We must be a pending loop header
|
|
MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
|
|
|
|
// Add exit definitions to each corresponding phi at the entry.
|
|
// Note: Phis are inserted in the same order as the slots. (see
|
|
// MBasicBlock::NewAsmJS)
|
|
size_t slot = 0;
|
|
for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++, slot++) {
|
|
MPhi* entryDef = *phi;
|
|
MDefinition* exitDef = pred->getSlot(slot);
|
|
|
|
// Assert that we already placed phis for each slot.
|
|
MOZ_ASSERT(entryDef->block() == this);
|
|
|
|
// Assert that the phi already has the correct type.
|
|
MOZ_ASSERT(entryDef->type() == exitDef->type());
|
|
MOZ_ASSERT(entryDef->type() != MIRType_Value);
|
|
|
|
if (entryDef == exitDef) {
|
|
// If the exit def is the same as the entry def, make a redundant
|
|
// phi. Since loop headers have exactly two incoming edges, we
|
|
// know that that's just the first input.
|
|
//
|
|
// Note that we eliminate later rather than now, to avoid any
|
|
// weirdness around pending continue edges which might still hold
|
|
// onto phis.
|
|
exitDef = entryDef->getOperand(0);
|
|
}
|
|
|
|
// Phis always have room for 2 operands, so we can use addInput.
|
|
entryDef->addInput(exitDef);
|
|
|
|
MOZ_ASSERT(slot < pred->stackDepth());
|
|
setSlot(slot, entryDef);
|
|
}
|
|
|
|
// We are now a loop header proper
|
|
kind_ = LOOP_HEADER;
|
|
|
|
return predecessors_.append(pred);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::clearLoopHeader()
|
|
{
|
|
MOZ_ASSERT(isLoopHeader());
|
|
kind_ = NORMAL;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::setLoopHeader(MBasicBlock* newBackedge)
|
|
{
|
|
MOZ_ASSERT(!isLoopHeader());
|
|
kind_ = LOOP_HEADER;
|
|
|
|
size_t numPreds = numPredecessors();
|
|
MOZ_ASSERT(numPreds != 0);
|
|
|
|
size_t lastIndex = numPreds - 1;
|
|
size_t oldIndex = 0;
|
|
for (; ; ++oldIndex) {
|
|
MOZ_ASSERT(oldIndex < numPreds);
|
|
MBasicBlock* pred = getPredecessor(oldIndex);
|
|
if (pred == newBackedge)
|
|
break;
|
|
}
|
|
|
|
// Set the loop backedge to be the last element in predecessors_.
|
|
Swap(predecessors_[oldIndex], predecessors_[lastIndex]);
|
|
|
|
// If we have phis, reorder their operands accordingly.
|
|
if (!phisEmpty()) {
|
|
getPredecessor(oldIndex)->setSuccessorWithPhis(this, oldIndex);
|
|
getPredecessor(lastIndex)->setSuccessorWithPhis(this, lastIndex);
|
|
for (MPhiIterator iter(phisBegin()), end(phisEnd()); iter != end; ++iter) {
|
|
MPhi* phi = *iter;
|
|
MDefinition* last = phi->getOperand(oldIndex);
|
|
MDefinition* old = phi->getOperand(lastIndex);
|
|
phi->replaceOperand(oldIndex, old);
|
|
phi->replaceOperand(lastIndex, last);
|
|
}
|
|
}
|
|
|
|
MOZ_ASSERT(newBackedge->loopHeaderOfBackedge() == this);
|
|
MOZ_ASSERT(backedge() == newBackedge);
|
|
}
|
|
|
|
size_t
|
|
MBasicBlock::numSuccessors() const
|
|
{
|
|
MOZ_ASSERT(lastIns());
|
|
return lastIns()->numSuccessors();
|
|
}
|
|
|
|
MBasicBlock*
|
|
MBasicBlock::getSuccessor(size_t index) const
|
|
{
|
|
MOZ_ASSERT(lastIns());
|
|
return lastIns()->getSuccessor(index);
|
|
}
|
|
|
|
size_t
|
|
MBasicBlock::getSuccessorIndex(MBasicBlock* block) const
|
|
{
|
|
MOZ_ASSERT(lastIns());
|
|
for (size_t i = 0; i < numSuccessors(); i++) {
|
|
if (getSuccessor(i) == block)
|
|
return i;
|
|
}
|
|
MOZ_CRASH("Invalid successor");
|
|
}
|
|
|
|
size_t
|
|
MBasicBlock::getPredecessorIndex(MBasicBlock* block) const
|
|
{
|
|
for (size_t i = 0, e = numPredecessors(); i < e; ++i) {
|
|
if (getPredecessor(i) == block)
|
|
return i;
|
|
}
|
|
MOZ_CRASH("Invalid predecessor");
|
|
}
|
|
|
|
void
|
|
MBasicBlock::replaceSuccessor(size_t pos, MBasicBlock* split)
|
|
{
|
|
MOZ_ASSERT(lastIns());
|
|
|
|
// Note, during split-critical-edges, successors-with-phis is not yet set.
|
|
// During PAA, this case is handled before we enter.
|
|
MOZ_ASSERT_IF(successorWithPhis_, successorWithPhis_ != getSuccessor(pos));
|
|
|
|
lastIns()->replaceSuccessor(pos, split);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::replacePredecessor(MBasicBlock* old, MBasicBlock* split)
|
|
{
|
|
for (size_t i = 0; i < numPredecessors(); i++) {
|
|
if (getPredecessor(i) == old) {
|
|
predecessors_[i] = split;
|
|
|
|
#ifdef DEBUG
|
|
// The same block should not appear twice in the predecessor list.
|
|
for (size_t j = i; j < numPredecessors(); j++)
|
|
MOZ_ASSERT(predecessors_[j] != old);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
MOZ_CRASH("predecessor was not found");
|
|
}
|
|
|
|
void
|
|
MBasicBlock::clearDominatorInfo()
|
|
{
|
|
setImmediateDominator(nullptr);
|
|
immediatelyDominated_.clear();
|
|
numDominated_ = 0;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::removePredecessorWithoutPhiOperands(MBasicBlock* pred, size_t predIndex)
|
|
{
|
|
// If we're removing the last backedge, this is no longer a loop.
|
|
if (isLoopHeader() && hasUniqueBackedge() && backedge() == pred)
|
|
clearLoopHeader();
|
|
|
|
// Adjust phis. Note that this can leave redundant phis behind.
|
|
// Don't adjust successorWithPhis() if we haven't constructed this
|
|
// information yet.
|
|
if (pred->successorWithPhis()) {
|
|
MOZ_ASSERT(pred->positionInPhiSuccessor() == predIndex);
|
|
pred->clearSuccessorWithPhis();
|
|
for (size_t j = predIndex+1; j < numPredecessors(); j++)
|
|
getPredecessor(j)->setSuccessorWithPhis(this, j - 1);
|
|
}
|
|
|
|
// Remove from pred list.
|
|
predecessors_.erase(predecessors_.begin() + predIndex);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::removePredecessor(MBasicBlock* pred)
|
|
{
|
|
size_t predIndex = getPredecessorIndex(pred);
|
|
|
|
// Remove the phi operands.
|
|
for (MPhiIterator iter(phisBegin()), end(phisEnd()); iter != end; ++iter)
|
|
iter->removeOperand(predIndex);
|
|
|
|
// Now we can call the underlying function, which expects that phi
|
|
// operands have been removed.
|
|
removePredecessorWithoutPhiOperands(pred, predIndex);
|
|
}
|
|
|
|
void
|
|
MBasicBlock::inheritPhis(MBasicBlock* header)
|
|
{
|
|
MResumePoint* headerRp = header->entryResumePoint();
|
|
size_t stackDepth = headerRp->stackDepth();
|
|
for (size_t slot = 0; slot < stackDepth; slot++) {
|
|
MDefinition* exitDef = getSlot(slot);
|
|
MDefinition* loopDef = headerRp->getOperand(slot);
|
|
if (loopDef->block() != header) {
|
|
MOZ_ASSERT(loopDef->block()->id() < header->id());
|
|
MOZ_ASSERT(loopDef == exitDef);
|
|
continue;
|
|
}
|
|
|
|
// Phis are allocated by NewPendingLoopHeader.
|
|
MPhi* phi = loopDef->toPhi();
|
|
MOZ_ASSERT(phi->numOperands() == 2);
|
|
|
|
// The entry definition is always the leftmost input to the phi.
|
|
MDefinition* entryDef = phi->getOperand(0);
|
|
|
|
if (entryDef != exitDef)
|
|
continue;
|
|
|
|
// If the entryDef is the same as exitDef, then we must propagate the
|
|
// phi down to this successor. This chance was missed as part of
|
|
// setBackedge() because exits are not captured in resume points.
|
|
setSlot(slot, phi);
|
|
}
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::inheritPhisFromBackedge(MBasicBlock* backedge, bool* hadTypeChange)
|
|
{
|
|
// We must be a pending loop header
|
|
MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
|
|
|
|
size_t stackDepth = entryResumePoint()->stackDepth();
|
|
for (size_t slot = 0; slot < stackDepth; slot++) {
|
|
// Get the value stack-slot of the back edge.
|
|
MDefinition* exitDef = backedge->getSlot(slot);
|
|
|
|
// Get the value of the loop header.
|
|
MDefinition* loopDef = entryResumePoint()->getOperand(slot);
|
|
if (loopDef->block() != this) {
|
|
// If we are finishing a pending loop header, then we need to ensure
|
|
// that all operands are phis. This is usualy the case, except for
|
|
// object/arrays build with generators, in which case we share the
|
|
// same allocations across all blocks.
|
|
MOZ_ASSERT(loopDef->block()->id() < id());
|
|
MOZ_ASSERT(loopDef == exitDef);
|
|
continue;
|
|
}
|
|
|
|
// Phis are allocated by NewPendingLoopHeader.
|
|
MPhi* entryDef = loopDef->toPhi();
|
|
MOZ_ASSERT(entryDef->block() == this);
|
|
|
|
if (entryDef == exitDef) {
|
|
// If the exit def is the same as the entry def, make a redundant
|
|
// phi. Since loop headers have exactly two incoming edges, we
|
|
// know that that's just the first input.
|
|
//
|
|
// Note that we eliminate later rather than now, to avoid any
|
|
// weirdness around pending continue edges which might still hold
|
|
// onto phis.
|
|
exitDef = entryDef->getOperand(0);
|
|
}
|
|
|
|
bool typeChange = false;
|
|
|
|
if (!entryDef->addInputSlow(exitDef))
|
|
return false;
|
|
if (!entryDef->checkForTypeChange(exitDef, &typeChange))
|
|
return false;
|
|
*hadTypeChange |= typeChange;
|
|
setSlot(slot, entryDef);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MBasicBlock::specializePhis()
|
|
{
|
|
for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
|
|
MPhi* phi = *iter;
|
|
if (!phi->specializeType())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MTest*
|
|
MBasicBlock::immediateDominatorBranch(BranchDirection* pdirection)
|
|
{
|
|
*pdirection = FALSE_BRANCH;
|
|
|
|
if (numPredecessors() != 1)
|
|
return nullptr;
|
|
|
|
MBasicBlock* dom = immediateDominator();
|
|
if (dom != getPredecessor(0))
|
|
return nullptr;
|
|
|
|
// Look for a trailing MTest branching to this block.
|
|
MInstruction* ins = dom->lastIns();
|
|
if (ins->isTest()) {
|
|
MTest* test = ins->toTest();
|
|
|
|
MOZ_ASSERT(test->ifTrue() == this || test->ifFalse() == this);
|
|
if (test->ifTrue() == this && test->ifFalse() == this)
|
|
return nullptr;
|
|
|
|
*pdirection = (test->ifTrue() == this) ? TRUE_BRANCH : FALSE_BRANCH;
|
|
return test;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
MBasicBlock::dumpStack(GenericPrinter& out)
|
|
{
|
|
#ifdef DEBUG
|
|
out.printf(" %-3s %-16s %-6s %-10s\n", "#", "name", "copyOf", "first/next");
|
|
out.printf("-------------------------------------------\n");
|
|
for (uint32_t i = 0; i < stackPosition_; i++) {
|
|
out.printf(" %-3d", i);
|
|
out.printf(" %-16p\n", (void*)slots_[i]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MBasicBlock::dumpStack()
|
|
{
|
|
Fprinter out(stderr);
|
|
dumpStack(out);
|
|
out.finish();
|
|
}
|
|
|
|
void
|
|
MIRGraph::dump(GenericPrinter& out)
|
|
{
|
|
#ifdef DEBUG
|
|
for (MBasicBlockIterator iter(begin()); iter != end(); iter++) {
|
|
iter->dump(out);
|
|
out.printf("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MIRGraph::dump()
|
|
{
|
|
Fprinter out(stderr);
|
|
dump(out);
|
|
out.finish();
|
|
}
|
|
|
|
void
|
|
MBasicBlock::dump(GenericPrinter& out)
|
|
{
|
|
#ifdef DEBUG
|
|
out.printf("block%u:%s%s%s\n", id(),
|
|
isLoopHeader() ? " (loop header)" : "",
|
|
unreachable() ? " (unreachable)" : "",
|
|
isMarked() ? " (marked)" : "");
|
|
if (MResumePoint* resume = entryResumePoint())
|
|
resume->dump(out);
|
|
for (MPhiIterator iter(phisBegin()); iter != phisEnd(); iter++)
|
|
iter->dump(out);
|
|
for (MInstructionIterator iter(begin()); iter != end(); iter++)
|
|
iter->dump(out);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MBasicBlock::dump()
|
|
{
|
|
Fprinter out(stderr);
|
|
dump(out);
|
|
out.finish();
|
|
}
|