mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-11-05 05:57:30 +09:00
232 lines
9.8 KiB
C++
232 lines
9.8 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/Sink.h"
|
|
|
|
#include "mozilla/Vector.h"
|
|
|
|
#include "jit/IonAnalysis.h"
|
|
#include "jit/JitSpewer.h"
|
|
#include "jit/MIR.h"
|
|
#include "jit/MIRGenerator.h"
|
|
#include "jit/MIRGraph.h"
|
|
|
|
namespace js {
|
|
namespace jit {
|
|
|
|
// Given the last found common dominator and a new definition to dominate, the
|
|
// CommonDominator function returns the basic block which dominate the last
|
|
// common dominator and the definition. If no such block exists, then this
|
|
// functions return null.
|
|
static MBasicBlock*
|
|
CommonDominator(MBasicBlock* commonDominator, MBasicBlock* defBlock)
|
|
{
|
|
// This is the first instruction visited, record its basic block as being
|
|
// the only interesting one.
|
|
if (!commonDominator)
|
|
return defBlock;
|
|
|
|
// Iterate on immediate dominators of the known common dominator to find a
|
|
// block which dominates all previous uses as well as this instruction.
|
|
while (!commonDominator->dominates(defBlock)) {
|
|
MBasicBlock* nextBlock = commonDominator->immediateDominator();
|
|
// All uses are dominated, so, this cannot happen unless the graph
|
|
// coherency is not respected.
|
|
MOZ_ASSERT(commonDominator != nextBlock);
|
|
commonDominator = nextBlock;
|
|
}
|
|
|
|
return commonDominator;
|
|
}
|
|
|
|
bool
|
|
Sink(MIRGenerator* mir, MIRGraph& graph)
|
|
{
|
|
TempAllocator& alloc = graph.alloc();
|
|
bool sinkEnabled = mir->optimizationInfo().sinkEnabled();
|
|
|
|
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
|
|
if (mir->shouldCancel("Sink"))
|
|
return false;
|
|
|
|
for (MInstructionReverseIterator iter = block->rbegin(); iter != block->rend(); ) {
|
|
MInstruction* ins = *iter++;
|
|
|
|
// Only instructions which can be recovered on bailout can be moved
|
|
// into the bailout paths.
|
|
if (ins->isGuard() || ins->isGuardRangeBailouts() ||
|
|
ins->isRecoveredOnBailout() || !ins->canRecoverOnBailout())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Compute a common dominator for all uses of the current
|
|
// instruction.
|
|
bool hasLiveUses = false;
|
|
bool hasUses = false;
|
|
MBasicBlock* usesDominator = nullptr;
|
|
for (MUseIterator i(ins->usesBegin()), e(ins->usesEnd()); i != e; i++) {
|
|
hasUses = true;
|
|
MNode* consumerNode = (*i)->consumer();
|
|
if (consumerNode->isResumePoint())
|
|
continue;
|
|
|
|
MDefinition* consumer = consumerNode->toDefinition();
|
|
if (consumer->isRecoveredOnBailout())
|
|
continue;
|
|
|
|
hasLiveUses = true;
|
|
|
|
// If the instruction is a Phi, then we should dominate the
|
|
// predecessor from which the value is coming from.
|
|
MBasicBlock* consumerBlock = consumer->block();
|
|
if (consumer->isPhi())
|
|
consumerBlock = consumerBlock->getPredecessor(consumer->indexOf(*i));
|
|
|
|
usesDominator = CommonDominator(usesDominator, consumerBlock);
|
|
if (usesDominator == *block)
|
|
break;
|
|
}
|
|
|
|
// Leave this instruction for DCE.
|
|
if (!hasUses)
|
|
continue;
|
|
|
|
// We have no uses, so sink this instruction in all the bailout
|
|
// paths.
|
|
if (!hasLiveUses) {
|
|
MOZ_ASSERT(!usesDominator);
|
|
ins->setRecoveredOnBailout();
|
|
JitSpewDef(JitSpew_Sink, " No live uses, recover the instruction on bailout\n", ins);
|
|
continue;
|
|
}
|
|
|
|
// This guard is temporarly moved here as the above code deals with
|
|
// Dead Code elimination, which got moved into this Sink phase, as
|
|
// the Dead Code elimination used to move instructions with no-live
|
|
// uses to the bailout path.
|
|
if (!sinkEnabled)
|
|
continue;
|
|
|
|
// To move an effectful instruction, we would have to verify that the
|
|
// side-effect is not observed. In the mean time, we just inhibit
|
|
// this optimization on effectful instructions.
|
|
if (ins->isEffectful())
|
|
continue;
|
|
|
|
// If all the uses are under a loop, we might not want to work
|
|
// against LICM by moving everything back into the loop, but if the
|
|
// loop is it-self inside an if, then we still want to move the
|
|
// computation under this if statement.
|
|
while (block->loopDepth() < usesDominator->loopDepth()) {
|
|
MOZ_ASSERT(usesDominator != usesDominator->immediateDominator());
|
|
usesDominator = usesDominator->immediateDominator();
|
|
}
|
|
|
|
// Only move instructions if there is a branch between the dominator
|
|
// of the uses and the original instruction. This prevent moving the
|
|
// computation of the arguments into an inline function if there is
|
|
// no major win.
|
|
MBasicBlock* lastJoin = usesDominator;
|
|
while (*block != lastJoin && lastJoin->numPredecessors() == 1) {
|
|
MOZ_ASSERT(lastJoin != lastJoin->immediateDominator());
|
|
MBasicBlock* next = lastJoin->immediateDominator();
|
|
if (next->numSuccessors() > 1)
|
|
break;
|
|
lastJoin = next;
|
|
}
|
|
if (*block == lastJoin)
|
|
continue;
|
|
|
|
// Skip to the next instruction if we cannot find a common dominator
|
|
// for all the uses of this instruction, or if the common dominator
|
|
// correspond to the block of the current instruction.
|
|
if (!usesDominator || usesDominator == *block)
|
|
continue;
|
|
|
|
// Only instruction which can be recovered on bailout and which are
|
|
// sinkable can be moved into blocks which are below while filling
|
|
// the resume points with a clone which is recovered on bailout.
|
|
|
|
// If the instruction has live uses and if it is clonable, then we
|
|
// can clone the instruction for all non-dominated uses and move the
|
|
// instruction into the block which is dominating all live uses.
|
|
if (!ins->canClone())
|
|
continue;
|
|
|
|
// If the block is a split-edge block, which is created for folding
|
|
// test conditions, then the block has no resume point and has
|
|
// multiple predecessors. In such case, we cannot safely move
|
|
// bailing instruction to these blocks as we have no way to bailout.
|
|
if (!usesDominator->entryResumePoint() && usesDominator->numPredecessors() != 1)
|
|
continue;
|
|
|
|
JitSpewDef(JitSpew_Sink, " Can Clone & Recover, sink instruction\n", ins);
|
|
JitSpew(JitSpew_Sink, " into Block %u", usesDominator->id());
|
|
|
|
// Copy the arguments and clone the instruction.
|
|
MDefinitionVector operands(alloc);
|
|
for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
|
|
if (!operands.append(ins->getOperand(i)))
|
|
return false;
|
|
}
|
|
|
|
MInstruction* clone = ins->clone(alloc, operands);
|
|
ins->block()->insertBefore(ins, clone);
|
|
clone->setRecoveredOnBailout();
|
|
|
|
// We should not update the producer of the entry resume point, as
|
|
// it cannot refer to any instruction within the basic block excepts
|
|
// for Phi nodes.
|
|
MResumePoint* entry = usesDominator->entryResumePoint();
|
|
|
|
// Replace the instruction by its clone in all the resume points /
|
|
// recovered-on-bailout instructions which are not in blocks which
|
|
// are dominated by the usesDominator block.
|
|
for (MUseIterator i(ins->usesBegin()), e(ins->usesEnd()); i != e; ) {
|
|
MUse* use = *i++;
|
|
MNode* consumer = use->consumer();
|
|
|
|
// If the consumer is a Phi, then we look for the index of the
|
|
// use to find the corresponding predecessor block, which is
|
|
// then used as the consumer block.
|
|
MBasicBlock* consumerBlock = consumer->block();
|
|
if (consumer->isDefinition() && consumer->toDefinition()->isPhi()) {
|
|
consumerBlock = consumerBlock->getPredecessor(
|
|
consumer->toDefinition()->toPhi()->indexOf(use));
|
|
}
|
|
|
|
// Keep the current instruction for all dominated uses, except
|
|
// for the entry resume point of the block in which the
|
|
// instruction would be moved into.
|
|
if (usesDominator->dominates(consumerBlock) &&
|
|
(!consumer->isResumePoint() || consumer->toResumePoint() != entry))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
use->replaceProducer(clone);
|
|
}
|
|
|
|
// As we move this instruction in a different block, we should
|
|
// verify that we do not carry over a resume point which would refer
|
|
// to an outdated state of the control flow.
|
|
if (ins->resumePoint())
|
|
ins->clearResumePoint();
|
|
|
|
// Now, that all uses which are not dominated by usesDominator are
|
|
// using the cloned instruction, we can safely move the instruction
|
|
// into the usesDominator block.
|
|
MInstruction* at = usesDominator->safeInsertTop(nullptr, MBasicBlock::IgnoreRecover);
|
|
block->moveBefore(at, ins);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace jit
|
|
} // namespace js
|