closes #376: M1329901 M1330667 M1342016 (modified) M1304081 M1259476

This commit is contained in:
Cameron Kaiser 2018-07-06 17:27:11 -07:00
parent 1fe2d3921c
commit 8a97adc59a
10 changed files with 166 additions and 49 deletions

View File

@ -0,0 +1,17 @@
/* This is supposed to be jit-test --ion-pgo=on */
try {
x = evalcx('');
x.__proto__ = 0;
} catch (e) {}
(function() {
for (var i = 0; i < 1; ++i) {
if (i % 5 == 0) {
for (let z of[0, 0, new Boolean(false), new Boolean(false),
new Boolean(false), new Boolean(false)]) {
this.x;
}
}
}
})()

View File

@ -391,6 +391,9 @@ BacktrackingAllocator::init()
LBlock* block = graph.getBlock(i);
for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
if (mir->shouldCancel("Create data structures (inner loop 1)"))
return false;
for (size_t j = 0; j < ins->numDefs(); j++) {
LDefinition* def = ins->getDef(j);
if (def->isBogusTemp())
@ -828,7 +831,7 @@ BacktrackingAllocator::go()
return false;
QueueItem item = allocationQueue.removeHighest();
if (!processBundle(item.bundle))
if (!processBundle(mir, item.bundle))
return false;
}
JitSpew(JitSpew_RegAlloc, "Main allocation loop complete");
@ -1221,7 +1224,7 @@ BacktrackingAllocator::tryAllocateNonFixed(LiveBundle* bundle,
}
bool
BacktrackingAllocator::processBundle(LiveBundle* bundle)
BacktrackingAllocator::processBundle(MIRGenerator* mir, LiveBundle* bundle)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, "Allocating %s [priority %lu] [weight %lu]",
@ -1256,6 +1259,9 @@ BacktrackingAllocator::processBundle(LiveBundle* bundle)
bool fixed;
LiveBundleVector conflicting;
for (size_t attempt = 0;; attempt++) {
if (mir->shouldCancel("Backtracking Allocation (processBundle loop)"))
return false;
if (canAllocate) {
bool success = false;
fixed = false;
@ -1719,7 +1725,7 @@ BacktrackingAllocator::resolveControlFlow()
for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
VirtualRegister& reg = vregs[i];
if (mir->shouldCancel("Backtracking Resolve Control Flow (vreg loop)"))
if (mir->shouldCancel("Backtracking Resolve Control Flow (vreg outer loop)"))
return false;
if (!alloc().ensureBallast())
@ -1728,6 +1734,9 @@ BacktrackingAllocator::resolveControlFlow()
for (LiveRange::RegisterLinkIterator iter = reg.rangesBegin(); iter; ) {
LiveRange* range = LiveRange::get(*iter);
if (mir->shouldCancel("Backtracking Resolve Control Flow (vreg inner loop)"))
return false;
// Remove ranges which will never be used.
if (deadRange(range)) {
reg.removeRangeAndIncrement(iter);

View File

@ -663,7 +663,7 @@ class BacktrackingAllocator : protected RegisterAllocator
bool* success, bool* pfixed, LiveBundleVector& conflicting);
bool tryAllocateNonFixed(LiveBundle* bundle, Requirement requirement, Requirement hint,
bool* success, bool* pfixed, LiveBundleVector& conflicting);
bool processBundle(LiveBundle* bundle);
bool processBundle(MIRGenerator* mir, LiveBundle* bundle);
bool computeRequirement(LiveBundle* bundle, Requirement *prequirement, Requirement *phint);
bool tryAllocateRegister(PhysicalRegister& r, LiveBundle* bundle,
bool* success, bool* pfixed, LiveBundleVector& conflicting);

View File

@ -480,6 +480,7 @@ class CompileInfo
// the frame is active on the stack. This implies that these definitions
// would have to be executed and that they cannot be removed even if they
// are unused.
#if(0)
bool isObservableSlot(uint32_t slot) const {
if (isObservableFrameSlot(slot))
return true;
@ -489,6 +490,17 @@ class CompileInfo
return false;
}
#else // bug 1342016 attachment 8849618 for our older code
inline bool isObservableSlot(uint32_t slot) const {
if (slot >= firstLocalSlot())
return false;
if (slot < firstArgSlot())
return isObservableFrameSlot(slot);
return isObservableArgumentSlot(slot);
}
#endif
bool isObservableFrameSlot(uint32_t slot) const {
if (!funMaybeLazy())

View File

@ -24,9 +24,10 @@ EdgeCaseAnalysis::analyzeLate()
uint32_t nextId = 0;
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
if (mir->shouldCancel("Analyze Late (first loop)"))
return false;
for (MDefinitionIterator iter(*block); iter; iter++) {
if (mir->shouldCancel("Analyze Late (first loop)"))
return false;
iter->setId(nextId++);
iter->analyzeEdgeCasesForward();
}
@ -34,10 +35,12 @@ EdgeCaseAnalysis::analyzeLate()
}
for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); block++) {
if (mir->shouldCancel("Analyze Late (second loop)"))
return false;
for (MInstructionReverseIterator riter(block->rbegin()); riter != block->rend(); riter++)
for (MInstructionReverseIterator riter(block->rbegin()); riter != block->rend(); riter++) {
if (mir->shouldCancel("Analyze Late (second loop)"))
return false;
riter->analyzeEdgeCasesBackward();
}
}
return true;

View File

@ -1506,17 +1506,20 @@ OptimizeMIR(MIRGenerator* mir)
else
logger = TraceLoggerForCurrentThread();
if (mir->shouldCancel("Start"))
return false;
if (!mir->compilingAsmJS()) {
if (!MakeMRegExpHoistable(graph))
if (!MakeMRegExpHoistable(mir, graph))
return false;
if (mir->shouldCancel("Make MRegExp Hoistable"))
return false;
}
gs.spewPass("BuildSSA");
AssertBasicGraphCoherency(graph);
if (mir->shouldCancel("Start"))
return false;
if (!JitOptions.disablePgo && !mir->compilingAsmJS()) {
AutoTraceLog log(logger, TraceLogger_PruneUnusedBranches);
if (!PruneUnusedBranches(mir, graph))

View File

@ -30,7 +30,8 @@ using mozilla::DebugOnly;
typedef Vector<MPhi*, 16, SystemAllocPolicy> MPhiVector;
static bool
FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVector& worklist)
FlagPhiInputsAsHavingRemovedUses(MIRGenerator* mir, MBasicBlock* block, MBasicBlock* succ,
MPhiVector& worklist)
{
// When removing an edge between 2 blocks, we might remove the ability of
// later phases to figure out that the uses of a Phi should be considered as
@ -103,6 +104,9 @@ FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVect
for (; it != end; it++) {
MPhi* phi = *it;
if (mir->shouldCancel("FlagPhiInputsAsHavingRemovedUses outer loop"))
return false;
// We are looking to mark the Phi inputs which are used across the edge
// between the |block| and its successor |succ|.
MDefinition* def = phi->getOperand(predIndex);
@ -120,9 +124,23 @@ FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVect
bool isUsed = false;
for (size_t idx = 0; !isUsed && idx < worklist.length(); idx++) {
phi = worklist[idx];
if (mir->shouldCancel("FlagPhiInputsAsHavingRemovedUses inner loop 1"))
return false;
if (phi->isUseRemoved() || phi->isImplicitlyUsed()) {
// The phi is implicitly used.
isUsed = true;
break;
}
MUseIterator usesEnd(phi->usesEnd());
for (MUseIterator use(phi->usesBegin()); use != usesEnd; use++) {
MNode* consumer = (*use)->consumer();
if (mir->shouldCancel("FlagPhiInputsAsHavingRemovedUses inner loop 2"))
return false;
if (consumer->isResumePoint()) {
MResumePoint* rp = consumer->toResumePoint();
if (rp->isObservableOperand(*use)) {
@ -144,12 +162,6 @@ FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVect
if (phi->isInWorklist())
continue;
if (phi->isUseRemoved() || phi->isImplicitlyUsed()) {
// The phi is implicitly used.
isUsed = true;
break;
}
phi->setInWorklist();
if (!worklist.append(phi))
return false;
@ -177,11 +189,16 @@ FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVect
}
static bool
FlagAllOperandsAsHavingRemovedUses(MBasicBlock* block)
FlagAllOperandsAsHavingRemovedUses(MIRGenerator* mir, MBasicBlock* block)
{
const CompileInfo& info = block->info();
// Flag all instructions operands as having removed uses.
MInstructionIterator end = block->end();
for (MInstructionIterator it = block->begin(); it != end; it++) {
if (mir->shouldCancel("FlagAllOperandsAsHavingRemovedUses loop 1"))
return false;
MInstruction* ins = *it;
for (size_t i = 0, e = ins->numOperands(); i < e; i++)
ins->getOperand(i)->setUseRemovedUnchecked();
@ -191,9 +208,17 @@ FlagAllOperandsAsHavingRemovedUses(MBasicBlock* block)
// Note: no need to iterate over the caller's of the resume point as
// this is the same as the entry resume point.
for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
#if(0) // bug 1329901, bug 1342016
if (mir->shouldCancel("FlagAllOperandsAsHavingRemovedUses inner loop"))
return false;
if (!rp->isObservableOperand(i))
continue;
rp->getOperand(i)->setUseRemovedUnchecked();
#else
if (info.isObservableSlot(i))
rp->getOperand(i)->setUseRemovedUnchecked();
#endif
}
}
}
@ -201,10 +226,18 @@ FlagAllOperandsAsHavingRemovedUses(MBasicBlock* block)
// Flag observable operands of the entry resume point as having removed uses.
MResumePoint* rp = block->entryResumePoint();
while (rp) {
if (mir->shouldCancel("FlagAllOperandsAsHavingRemovedUses loop 2"))
return false;
for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
#if(0) // bug 1329901, bug 1342016
if (!rp->isObservableOperand(i))
continue;
rp->getOperand(i)->setUseRemovedUnchecked();
#else
if (info.isObservableSlot(i))
rp->getOperand(i)->setUseRemovedUnchecked();
#endif
}
rp = rp->caller();
}
@ -212,7 +245,10 @@ FlagAllOperandsAsHavingRemovedUses(MBasicBlock* block)
// Flag Phi inputs of the successors has having removed uses.
MPhiVector worklist;
for (size_t i = 0, e = block->numSuccessors(); i < e; i++) {
if (!FlagPhiInputsAsHavingRemovedUses(block, block->getSuccessor(i), worklist))
if (mir->shouldCancel("FlagAllOperandsAsHavingRemovedUses loop 3"))
return false;
if (!FlagPhiInputsAsHavingRemovedUses(mir, block, block->getSuccessor(i), worklist))
return false;
}
@ -261,6 +297,9 @@ jit::PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph)
// unreachable if all predecessors are flagged as bailing or unreachable.
bool someUnreachable = false;
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
if (mir->shouldCancel("Prune unused branches (main loop)"))
return false;
JitSpew(JitSpew_Prune, "Investigate Block %d:", block->id());
// Do not touch entry basic blocks.
@ -274,6 +313,9 @@ jit::PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph)
size_t numPred = block->numPredecessors();
size_t i = 0;
for (; i < numPred; i++) {
if (mir->shouldCancel("Prune unused branches (inner loop 1)"))
return false;
MBasicBlock* pred = block->getPredecessor(i);
// The backedge is visited after the loop header, but if the loop
@ -304,6 +346,9 @@ jit::PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph)
size_t predCount = 0;
bool isLoopExit = false;
while (p--) {
if (mir->shouldCancel("Prune unused branches (inner loop 2)"))
return false;
MBasicBlock* pred = block->getPredecessor(p);
if (pred->getHitState() == MBasicBlock::HitState::Count)
predCount += pred->getHitCount();
@ -369,16 +414,22 @@ jit::PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph)
// As we are going to remove edges and basic block, we have to mark
// instructions which would be needed by baseline if we were to bailout.
for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
if (mir->shouldCancel("Prune unused branches (marking loop)"))
return false;
MBasicBlock* block = *it++;
if (!block->isMarked() && !block->unreachable())
continue;
FlagAllOperandsAsHavingRemovedUses(block);
FlagAllOperandsAsHavingRemovedUses(mir, block);
}
// Remove the blocks in post-order such that consumers are visited before
// the predecessors, the only exception being the Phi nodes of loop headers.
for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
if (mir->shouldCancel("Prune unused branches (removal loop)"))
return false;
MBasicBlock* block = *it++;
if (!block->isMarked() && !block->unreachable())
continue;
@ -1071,13 +1122,13 @@ jit::EliminatePhis(MIRGenerator* mir, MIRGraph& graph,
// Add all observable phis to a worklist. We use the "in worklist" bit to
// mean "this phi is live".
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
if (mir->shouldCancel("Eliminate Phis (populate loop)"))
return false;
MPhiIterator iter = block->phisBegin();
while (iter != block->phisEnd()) {
MPhi* phi = *iter++;
if (mir->shouldCancel("Eliminate Phis (populate loop)"))
return false;
// Flag all as unused, only observable phis would be marked as used
// when processed by the work list.
phi->setUnused();
@ -1357,6 +1408,9 @@ TypeAnalyzer::specializePhis()
return false;
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
if (mir->shouldCancel("Specialize Phis (inner loop)"))
return false;
bool hasInputsWithEmptyTypes;
MIRType type = GuessPhiType(*phi, &hasInputsWithEmptyTypes);
phi->specialize(type);
@ -1739,10 +1793,10 @@ bool
TypeAnalyzer::graphContainsFloat32()
{
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
if (mir->shouldCancel("Ensure Float32 commutativity - Graph contains Float32"))
return false;
for (MDefinitionIterator def(*block); def; def++) {
if (mir->shouldCancel("Ensure Float32 commutativity - Graph contains Float32"))
return false;
if (def->type() == MIRType_Float32)
return true;
}
@ -1822,7 +1876,7 @@ jit::ApplyTypeInformation(MIRGenerator* mir, MIRGraph& graph)
}
bool
jit::MakeMRegExpHoistable(MIRGraph& graph)
jit::MakeMRegExpHoistable(MIRGenerator* mir, MIRGraph& graph)
{
// If we are compiling try blocks, regular expressions may be observable
// from catch blocks (which Ion does not compile). For now just disable the
@ -1831,7 +1885,13 @@ jit::MakeMRegExpHoistable(MIRGraph& graph)
return true;
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
if (mir->shouldCancel("MakeMRegExpHoistable outer loop"))
return false;
for (MDefinitionIterator iter(*block); iter; iter++) {
if (mir->shouldCancel("MakeMRegExpHoistable inner loop"))
return false;
if (!iter->isRegExp())
continue;
@ -1840,6 +1900,9 @@ jit::MakeMRegExpHoistable(MIRGraph& graph)
// Test if MRegExp is hoistable by looking at all uses.
bool hoistable = true;
for (MUseIterator i = regexp->usesBegin(); i != regexp->usesEnd(); i++) {
if (mir->shouldCancel("IsRegExpHoistable inner loop"))
return false;
// Ignore resume points. At this point all uses are listed.
// No DCE or GVN or something has happened.
if (i->consumer()->isResumePoint())
@ -1939,7 +2002,7 @@ jit::RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarked
if (!block->isMarked())
continue;
FlagAllOperandsAsHavingRemovedUses(block);
FlagAllOperandsAsHavingRemovedUses(mir, block);
}
// Find unmarked blocks and remove them.

View File

@ -54,7 +54,7 @@ bool
ApplyTypeInformation(MIRGenerator* mir, MIRGraph& graph);
bool
MakeMRegExpHoistable(MIRGraph& graph);
MakeMRegExpHoistable(MIRGenerator* mir, MIRGraph& graph);
bool
RenumberBlocks(MIRGraph& graph);

View File

@ -158,7 +158,9 @@ IonBuilder::IonBuilder(JSContext* analysisContext, CompileCompartment* comp,
failedShapeGuard_(info->script()->failedShapeGuard()),
failedLexicalCheck_(info->script()->failedLexicalCheck()),
nonStringIteration_(false),
lazyArguments_(nullptr),
#ifdef DEBUG
hasLazyArguments_(false),
#endif
inlineCallInfo_(nullptr),
maybeFallbackFunctionGetter_(nullptr)
{
@ -908,11 +910,11 @@ IonBuilder::build()
ins->setResumePoint(entryRpCopy);
}
#ifdef DEBUG
// lazyArguments should never be accessed in |argsObjAliasesFormals| scripts.
if (info().hasArguments() && !info().argsObjAliasesFormals()) {
lazyArguments_ = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
current->add(lazyArguments_);
}
if (info().hasArguments() && !info().argsObjAliasesFormals())
hasLazyArguments_ = true;
#endif
insertRecompileCheck();
@ -1080,10 +1082,10 @@ IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoi
// +2 for the scope chain and |this|, maybe another +1 for arguments object slot.
MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
if (script_->argumentsHasVarBinding()) {
lazyArguments_ = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
current->add(lazyArguments_);
}
#ifdef DEBUG
if (script_->argumentsHasVarBinding())
hasLazyArguments_ = true;
#endif
insertRecompileCheck();
@ -1349,10 +1351,14 @@ IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction** def_,
}
case MIRType_MagicOptimizedArguments:
MOZ_ASSERT(lazyArguments_);
osrBlock->rewriteSlot(slot, lazyArguments_);
def = lazyArguments_;
{
MOZ_ASSERT(hasLazyArguments_);
MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
osrBlock->insertBefore(osrBlock->lastIns(), lazyArg);
osrBlock->rewriteSlot(slot, lazyArg);
def = lazyArg;
break;
}
default:
break;
@ -10195,8 +10201,11 @@ IonBuilder::jsop_arguments()
current->push(current->argumentsObject());
return true;
}
MOZ_ASSERT(lazyArguments_);
current->push(lazyArguments_);
MOZ_ASSERT(hasLazyArguments_);
MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
current->add(lazyArg);
current->push(lazyArg);
return true;
}

View File

@ -1196,9 +1196,10 @@ class IonBuilder
// 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_;
#ifdef DEBUG
// If this script uses the lazy arguments object.
bool hasLazyArguments_;
#endif
// If this is an inline builder, the call info for the builder.
const CallInfo* inlineCallInfo_;