Bug 1004363 - IonMonkey: A new value-numbering implementation based on a dom-tree DFS. r=nbp

This commit is contained in:
Dan Gohman 2014-06-27 10:38:44 -07:00
parent 8ae670ea14
commit 243266ba02
18 changed files with 961 additions and 775 deletions

View File

@ -479,6 +479,13 @@ class HashSet
impl.rekeyAndMaybeRehash(p, new_lookup, new_value);
}
// Infallibly rekey one entry with a new key that is equivalent.
void rekeyInPlace(Ptr p, const T &new_value)
{
MOZ_ASSERT(HashPolicy::match(*p, new_value));
impl.rekeyInPlace(p, new_value);
}
// HashSet is movable
HashSet(HashSet &&rhs) : impl(mozilla::Move(rhs.impl)) {}
void operator=(HashSet &&rhs) {
@ -1629,6 +1636,14 @@ class HashTable : private AllocPolicy
checkOverRemoved();
}
void rekeyInPlace(Ptr p, const Key &k)
{
MOZ_ASSERT(table);
mozilla::ReentrancyGuard g(*this);
MOZ_ASSERT(p.found());
HashPolicy::rekey(const_cast<Key &>(*p), const_cast<Key &>(k));
}
#undef METER
};

View File

@ -177,8 +177,7 @@ AliasAnalysis::analyze()
// Type analysis may have inserted new instructions. Since this pass depends
// on the instruction number ordering, all instructions are renumbered.
// We start with 1 because some passes use 0 to denote failure.
uint32_t newId = 1;
uint32_t newId = 0;
for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
if (mir->shouldCancel("Alias Analysis (main loop)"))

View File

@ -21,7 +21,7 @@ bool
EdgeCaseAnalysis::analyzeLate()
{
// Renumber definitions for NeedNegativeZeroCheck under analyzeEdgeCasesBackward.
uint32_t nextId = 1;
uint32_t nextId = 0;
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
if (mir->shouldCancel("Analyze Late (first loop)"))

View File

@ -1395,8 +1395,8 @@ OptimizeMIR(MIRGenerator *mir)
if (mir->optimizationInfo().gvnEnabled()) {
AutoTraceLog log(logger, TraceLogger::GVN);
ValueNumberer gvn(mir, graph, mir->optimizationInfo().gvnKind() == GVN_Optimistic);
if (!gvn.analyze())
ValueNumberer gvn(mir, graph);
if (!gvn.run(ValueNumberer::UpdateAliasAnalysis))
return false;
IonSpewPass("GVN");
AssertExtendedGraphCoherency(graph);

View File

@ -6,6 +6,7 @@
#include "jit/IonAnalysis.h"
#include "jit/AliasAnalysis.h"
#include "jit/BaselineInspector.h"
#include "jit/BaselineJIT.h"
#include "jit/Ion.h"
@ -1091,6 +1092,59 @@ jit::RenumberBlocks(MIRGraph &graph)
return true;
}
// A utility for code which deletes blocks. Renumber the remaining blocks,
// recompute dominators, and optionally recompute AliasAnalysis dependencies.
bool
jit::AccountForCFGChanges(MIRGenerator *mir, MIRGraph &graph, bool updateAliasAnalysis)
{
// Renumber the blocks and clear out the old dominator info.
size_t id = 0;
for (ReversePostorderIterator i(graph.rpoBegin()), e(graph.rpoEnd()); i != e; ++i) {
i->clearDominatorInfo();
i->setId(id++);
}
// Recompute dominator info.
if (!BuildDominatorTree(graph))
return false;
// If needed, update alias analysis dependencies.
if (updateAliasAnalysis) {
if (!AliasAnalysis(mir, graph).analyze())
return false;
}
AssertExtendedGraphCoherency(graph);
return true;
}
// Remove all blocks not marked with isMarked(). Unmark all remaining blocks.
// Alias analysis dependencies may be invalid after calling this function.
bool
jit::RemoveUnmarkedBlocks(MIRGenerator *mir, MIRGraph &graph, uint32_t numMarkedBlocks)
{
// If all blocks are marked, the CFG is unmodified. Just clear the marks.
if (numMarkedBlocks == graph.numBlocks()) {
graph.unmarkBlocks();
return true;
}
for (ReversePostorderIterator iter(graph.rpoBegin()); iter != graph.rpoEnd();) {
MBasicBlock *block = *iter++;
if (block->isMarked()) {
block->unmark();
continue;
}
for (size_t i = 0, e = block->numSuccessors(); i != e; ++i)
block->getSuccessor(i)->removePredecessor(block);
graph.removeBlockIncludingPhis(block);
}
return AccountForCFGChanges(mir, graph, /*updateAliasAnalysis=*/false);
}
// A Simple, Fast Dominance Algorithm by Cooper et al.
// Modified to support empty intersections for OSR, and in RPO.
static MBasicBlock *
@ -1559,7 +1613,13 @@ BoundsCheckHashIgnoreOffset(MBoundsCheck *check)
static MBoundsCheck *
FindDominatingBoundsCheck(BoundsCheckMap &checks, MBoundsCheck *check, size_t index)
{
// See the comment in ValueNumberer::findDominatingDef.
// Since we are traversing the dominator tree in pre-order, when we
// are looking at the |index|-th block, the next numDominated() blocks
// we traverse are precisely the set of blocks that are dominated.
//
// So, this value is visible in all blocks if:
// index <= index + ins->block->numDominated()
// and becomes invalid after that.
HashNumber hash = BoundsCheckHashIgnoreOffset(check);
BoundsCheckMap::Ptr p = checks.lookup(hash);
if (!p || index >= p->value().validEnd) {

View File

@ -55,6 +55,12 @@ MakeMRegExpHoistable(MIRGraph &graph);
bool
RenumberBlocks(MIRGraph &graph);
bool
AccountForCFGChanges(MIRGenerator *mir, MIRGraph &graph, bool updateAliasAnalysis);
bool
RemoveUnmarkedBlocks(MIRGenerator *mir, MIRGraph &graph, uint32_t numMarkedBlocks);
bool
BuildDominatorTree(MIRGraph &graph);

View File

@ -29,7 +29,6 @@ OptimizationInfo::initNormalOptimizationInfo()
inlineInterpreted_ = true;
inlineNative_ = true;
gvn_ = true;
gvnKind_ = GVN_Optimistic;
licm_ = true;
uce_ = true;
rangeAnalysis_ = true;

View File

@ -66,9 +66,6 @@ class OptimizationInfo
// Toggles whether global value numbering is used.
bool gvn_;
// Toggles whether global value numbering is optimistic or pessimistic.
IonGvnKind gvnKind_;
// Toggles whether loop invariant code motion is performed.
bool licm_;
@ -162,12 +159,6 @@ class OptimizationInfo
return eliminateRedundantChecks_;
}
IonGvnKind gvnKind() const {
if (!js_JitOptions.forceGvnKind)
return gvnKind_;
return js_JitOptions.forcedGvnKind;
}
IonRegisterAllocator registerAllocator() const {
if (!js_JitOptions.forceRegisterAllocator)
return registerAllocator_;

View File

@ -66,11 +66,6 @@ JitOptions::JitOptions()
forceDefaultIonUsesBeforeCompile = false;
forcedDefaultIonUsesBeforeCompile = 1000;
// Force the GVN kind to be optimistic or pessimistic instead of letting
// the optimization pass decide.
forceGvnKind = false;
forcedGvnKind = GVN_Optimistic;
// Force the used register allocator instead of letting the
// optimization pass decide.
forceRegisterAllocator = false;

View File

@ -28,11 +28,6 @@ enum IonRegisterAllocator {
RegisterAllocator_Stupid
};
enum IonGvnKind {
GVN_Optimistic,
GVN_Pessimistic
};
struct JitOptions
{
bool checkGraphConsistency;
@ -51,8 +46,6 @@ struct JitOptions
bool eagerCompilation;
bool forceDefaultIonUsesBeforeCompile;
uint32_t forcedDefaultIonUsesBeforeCompile;
bool forceGvnKind;
IonGvnKind forcedGvnKind;
bool forceRegisterAllocator;
IonRegisterAllocator forcedRegisterAllocator;
bool limitScriptSize;

View File

@ -69,15 +69,6 @@ MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op)
fprintf(fp, "%c", tolower(name[i]));
}
static inline bool
EqualValues(bool useGVN, MDefinition *left, MDefinition *right)
{
if (useGVN)
return left->valueNumber() == right->valueNumber();
return left == right;
}
static MConstant *
EvaluateConstantOperands(TempAllocator &alloc, MBinaryInstruction *ins, bool *ptypeChange = nullptr)
{
@ -148,9 +139,6 @@ MDefinition::printName(FILE *fp) const
{
PrintOpcodeName(fp, op());
fprintf(fp, "%u", id());
if (valueNumber() != 0)
fprintf(fp, "-vn%u", valueNumber());
}
HashNumber
@ -158,7 +146,7 @@ MDefinition::valueHash() const
{
HashNumber out = op();
for (size_t i = 0, e = numOperands(); i < e; i++) {
uint32_t valueNumber = getOperand(i)->valueNumber();
uint32_t valueNumber = getOperand(i)->id();
out = valueNumber + (out << 6) + (out << 16) - out;
}
return out;
@ -180,7 +168,7 @@ MDefinition::congruentIfOperandsEqual(const MDefinition *ins) const
return false;
for (size_t i = 0, e = numOperands(); i < e; i++) {
if (getOperand(i)->valueNumber() != ins->getOperand(i)->valueNumber())
if (getOperand(i) != ins->getOperand(i))
return false;
}
@ -188,7 +176,7 @@ MDefinition::congruentIfOperandsEqual(const MDefinition *ins) const
}
MDefinition *
MDefinition::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MDefinition::foldsTo(TempAllocator &alloc)
{
// In the default case, there are no constants to fold.
return this;
@ -246,7 +234,7 @@ MTest::cacheOperandMightEmulateUndefined()
}
MDefinition *
MTest::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MTest::foldsTo(TempAllocator &alloc)
{
MDefinition *op = getOperand(0);
@ -808,7 +796,7 @@ MApplyArgs::New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, MDef
}
MDefinition*
MStringLength::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MStringLength::foldsTo(TempAllocator &alloc)
{
if ((type() == MIRType_Int32) && (string()->isConstant())) {
Value value = string()->toConstant()->value();
@ -949,22 +937,31 @@ MPhi::removeAllOperands()
}
MDefinition *
MPhi::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MPhi::operandIfRedundant()
{
JS_ASSERT(!inputs_.empty());
JS_ASSERT(inputs_.length() != 0);
// If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
// returns the operand that it will always be equal to (a, in
// those two cases).
MDefinition *first = getOperand(0);
for (size_t i = 1; i < inputs_.length(); i++) {
// Phis need dominator information to fold based on value numbers. For
// simplicity, we only compare SSA names right now (bug 714727).
if (!EqualValues(false, getOperand(i), first))
return this;
for (size_t i = 1, e = numOperands(); i < e; i++) {
MDefinition *op = getOperand(i);
if (op != first && op != this)
return nullptr;
}
return first;
}
MDefinition *
MPhi::foldsTo(TempAllocator &alloc)
{
if (MDefinition *def = operandIfRedundant())
return def;
return this;
}
bool
MPhi::congruentTo(const MDefinition *ins) const
{
@ -1224,7 +1221,7 @@ IsConstant(MDefinition *def, double v)
}
MDefinition *
MBinaryBitwiseInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MBinaryBitwiseInstruction::foldsTo(TempAllocator &alloc)
{
if (specialization_ != MIRType_Int32)
return this;
@ -1259,7 +1256,7 @@ MBinaryBitwiseInstruction::foldUnnecessaryBitop()
if (IsConstant(rhs, -1))
return foldIfNegOne(1);
if (EqualValues(false, lhs, rhs))
if (lhs == rhs)
return foldIfEqual();
return this;
@ -1416,7 +1413,7 @@ NeedNegativeZeroCheck(MDefinition *def)
}
MDefinition *
MBinaryArithInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MBinaryArithInstruction::foldsTo(TempAllocator &alloc)
{
if (specialization_ == MIRType_None)
return this;
@ -1483,7 +1480,7 @@ MAbs::trySpecializeFloat32(TempAllocator &alloc)
}
MDefinition *
MDiv::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MDiv::foldsTo(TempAllocator &alloc)
{
if (specialization_ == MIRType_None)
return this;
@ -1540,7 +1537,7 @@ MDiv::fallible() const
}
MDefinition *
MMod::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MMod::foldsTo(TempAllocator &alloc)
{
if (specialization_ == MIRType_None)
return this;
@ -1612,16 +1609,16 @@ MSub::fallible() const
}
MDefinition *
MMul::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MMul::foldsTo(TempAllocator &alloc)
{
MDefinition *out = MBinaryArithInstruction::foldsTo(alloc, useValueNumbers);
MDefinition *out = MBinaryArithInstruction::foldsTo(alloc);
if (out != this)
return out;
if (specialization() != MIRType_Int32)
return this;
if (EqualValues(useValueNumbers, lhs(), rhs()))
if (lhs() == rhs())
setCanBeNegativeZero(false);
return this;
@ -2086,7 +2083,7 @@ MBitNot::NewAsmJS(TempAllocator &alloc, MDefinition *input)
}
MDefinition *
MBitNot::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MBitNot::foldsTo(TempAllocator &alloc)
{
if (specialization_ != MIRType_Int32)
return this;
@ -2107,7 +2104,7 @@ MBitNot::foldsTo(TempAllocator &alloc, bool useValueNumbers)
}
MDefinition *
MTypeOf::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MTypeOf::foldsTo(TempAllocator &alloc)
{
// Note: we can't use input->type() here, type analysis has
// boxed the input.
@ -2335,7 +2332,7 @@ MResumePoint::isObservableOperand(size_t index) const
}
MDefinition *
MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MToInt32::foldsTo(TempAllocator &alloc)
{
MDefinition *input = getOperand(0);
if (input->type() == MIRType_Int32)
@ -2351,7 +2348,7 @@ MToInt32::analyzeEdgeCasesBackward()
}
MDefinition *
MTruncateToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MTruncateToInt32::foldsTo(TempAllocator &alloc)
{
MDefinition *input = getOperand(0);
if (input->type() == MIRType_Int32)
@ -2367,7 +2364,7 @@ MTruncateToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
}
MDefinition *
MToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MToDouble::foldsTo(TempAllocator &alloc)
{
MDefinition *in = input();
if (in->type() == MIRType_Double)
@ -2385,7 +2382,7 @@ MToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers)
}
MDefinition *
MToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MToFloat32::foldsTo(TempAllocator &alloc)
{
if (input()->type() == MIRType_Float32)
return input();
@ -2407,7 +2404,7 @@ MToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
}
MDefinition *
MToString::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MToString::foldsTo(TempAllocator &alloc)
{
MDefinition *in = input();
if (in->type() == MIRType_String)
@ -2416,7 +2413,7 @@ MToString::foldsTo(TempAllocator &alloc, bool useValueNumbers)
}
MDefinition *
MClampToUint8::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MClampToUint8::foldsTo(TempAllocator &alloc)
{
if (input()->isConstant()) {
const Value &v = input()->toConstant()->value();
@ -2637,7 +2634,7 @@ MCompare::evaluateConstantOperands(bool *result)
}
MDefinition *
MCompare::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MCompare::foldsTo(TempAllocator &alloc)
{
bool result;
@ -2709,7 +2706,7 @@ MNot::cacheOperandMightEmulateUndefined()
}
MDefinition *
MNot::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MNot::foldsTo(TempAllocator &alloc)
{
// Fold if the input is constant
if (operand()->isConstant()) {
@ -3007,7 +3004,7 @@ MGetPropertyCache::updateForReplacement(MDefinition *ins) {
}
MDefinition *
MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc)
{
if (input()->isConstant()) {
const Value &v = input()->toConstant()->value();
@ -3019,7 +3016,7 @@ MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers)
}
MDefinition *
MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc)
{
if (input()->isConstant()) {
const Value &v = input()->toConstant()->value();

View File

@ -32,7 +32,6 @@ class StringObject;
namespace jit {
class BaselineInspector;
class ValueNumberData;
class Range;
static inline
@ -323,7 +322,6 @@ class MDefinition : public MNode
InlineList<MUse> uses_; // Use chain.
uint32_t id_; // Instruction ID, which after block re-ordering
// is sorted within a basic block.
ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use)
Range *range_; // Any computed range for this def.
MIRType resultType_; // Representation of result type.
types::TemporaryTypeSet *resultTypeSet_; // Optional refinement of the result type.
@ -365,7 +363,6 @@ class MDefinition : public MNode
public:
MDefinition()
: id_(0),
valueNumber_(nullptr),
range_(nullptr),
resultType_(MIRType_None),
resultTypeSet_(nullptr),
@ -448,7 +445,7 @@ class MDefinition : public MNode
return false;
}
bool congruentIfOperandsEqual(const MDefinition *ins) const;
virtual MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
virtual MDefinition *foldsTo(TempAllocator &alloc);
virtual void analyzeEdgeCasesForward();
virtual void analyzeEdgeCasesBackward();
@ -511,18 +508,6 @@ class MDefinition : public MNode
id_ = id;
}
uint32_t valueNumber() const;
void setValueNumber(uint32_t vn);
ValueNumberData *valueNumberData() {
return valueNumber_;
}
void clearValueNumberData() {
valueNumber_ = nullptr;
}
void setValueNumberData(ValueNumberData *vn) {
JS_ASSERT(valueNumber_ == nullptr);
valueNumber_ = vn;
}
#define FLAG_ACCESSOR(flag) \
bool is##flag() const {\
return hasFlags(1 << flag);\
@ -537,6 +522,9 @@ class MDefinition : public MNode
}\
void set##flag##Unchecked() {\
setFlags(1 << flag);\
} \
void setNot##flag##Unchecked() {\
removeFlags(1 << flag);\
}
MIR_FLAG_LIST(FLAG_ACCESSOR)
@ -869,7 +857,7 @@ class MBinaryInstruction : public MAryInstruction<2>
MDefinition *lhs = getOperand(0);
MDefinition *rhs = getOperand(1);
return op() + lhs->valueNumber() + rhs->valueNumber();
return op() + lhs->id() + rhs->id();
}
void swapOperands() {
MDefinition *temp = getOperand(0);
@ -892,7 +880,7 @@ class MBinaryInstruction : public MAryInstruction<2>
const MDefinition *right = getOperand(1);
const MDefinition *tmp;
if (isCommutative() && left->valueNumber() > right->valueNumber()) {
if (isCommutative() && left->id() > right->id()) {
tmp = right;
right = left;
left = tmp;
@ -901,14 +889,14 @@ class MBinaryInstruction : public MAryInstruction<2>
const MBinaryInstruction *bi = static_cast<const MBinaryInstruction *>(ins);
const MDefinition *insLeft = bi->getOperand(0);
const MDefinition *insRight = bi->getOperand(1);
if (isCommutative() && insLeft->valueNumber() > insRight->valueNumber()) {
if (isCommutative() && insLeft->id() > insRight->id()) {
tmp = insRight;
insRight = insLeft;
insLeft = tmp;
}
return (left->valueNumber() == insLeft->valueNumber()) &&
(right->valueNumber() == insRight->valueNumber());
return left == insLeft &&
right == insRight;
}
// Return true if the operands to this instruction are both unsigned,
@ -934,7 +922,7 @@ class MTernaryInstruction : public MAryInstruction<3>
MDefinition *second = getOperand(1);
MDefinition *third = getOperand(2);
return op() + first->valueNumber() + second->valueNumber() + third->valueNumber();
return op() + first->id() + second->id() + third->id();
}
};
@ -958,8 +946,8 @@ class MQuaternaryInstruction : public MAryInstruction<4>
MDefinition *third = getOperand(2);
MDefinition *fourth = getOperand(3);
return op() + first->valueNumber() + second->valueNumber() +
third->valueNumber() + fourth->valueNumber();
return op() + first->id() + second->id() +
third->id() + fourth->id();
}
};
@ -1479,7 +1467,7 @@ class MTest
// to check whether the operand might do this. If this method is never
// called, we'll assume our operand can emulate undefined.
void cacheOperandMightEmulateUndefined();
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined,
bool *filtersNull);
@ -2557,7 +2545,7 @@ class MCompare
bool tryFold(bool *result);
bool evaluateConstantOperands(bool *result);
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined,
bool *filtersNull);
@ -3203,7 +3191,7 @@ class MToDouble
return this;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
bool congruentTo(const MDefinition *ins) const {
if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion())
return false;
@ -3276,7 +3264,7 @@ class MToFloat32
return this;
}
virtual MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
virtual MDefinition *foldsTo(TempAllocator &alloc);
bool congruentTo(const MDefinition *ins) const {
if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion())
return false;
@ -3309,7 +3297,7 @@ class MAsmJSUnsignedToDouble
return new(alloc) MAsmJSUnsignedToDouble(def);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
bool congruentTo(const MDefinition *ins) const {
return congruentIfOperandsEqual(ins);
}
@ -3335,7 +3323,7 @@ class MAsmJSUnsignedToFloat32
return new(alloc) MAsmJSUnsignedToFloat32(def);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
bool congruentTo(const MDefinition *ins) const {
return congruentIfOperandsEqual(ins);
}
@ -3378,7 +3366,7 @@ class MToInt32
return new(alloc) MToInt32(def, conversion);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
// this only has backwards information flow.
void analyzeEdgeCasesBackward();
@ -3437,7 +3425,7 @@ class MTruncateToInt32 : public MUnaryInstruction
return new(alloc) MTruncateToInt32(def);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
bool congruentTo(const MDefinition *ins) const {
return congruentIfOperandsEqual(ins);
@ -3474,7 +3462,7 @@ class MToString :
return new(alloc) MToString(def);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
TypePolicy *typePolicy() {
return this;
@ -3514,7 +3502,7 @@ class MBitNot
return this;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void infer();
bool congruentTo(const MDefinition *ins) const {
@ -3562,7 +3550,7 @@ class MTypeOf
return inputType_;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void cacheInputMaybeCallableOrEmulatesUndefined();
bool inputMaybeCallableOrEmulatesUndefined() const {
@ -3631,7 +3619,7 @@ class MBinaryBitwiseInstruction
return this;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
MDefinition *foldUnnecessaryBitop();
virtual MDefinition *foldIfZero(size_t operand) = 0;
virtual MDefinition *foldIfNegOne(size_t operand) = 0;
@ -3869,7 +3857,7 @@ class MBinaryArithInstruction
return specialization_;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
virtual double getIdentity() = 0;
@ -4467,7 +4455,7 @@ class MMul : public MBinaryArithInstruction
return new(alloc) MMul(left, right, type, mode);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void analyzeEdgeCasesForward();
void analyzeEdgeCasesBackward();
void collectRangeInfoPreTrunc();
@ -4562,7 +4550,7 @@ class MDiv : public MBinaryArithInstruction
return div;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void analyzeEdgeCasesForward();
void analyzeEdgeCasesBackward();
@ -4658,7 +4646,7 @@ class MMod : public MBinaryArithInstruction
return mod;
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
double getIdentity() {
MOZ_ASSUME_UNREACHABLE("not used");
@ -4959,7 +4947,7 @@ class MLoadArrowThis
}
};
class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
class MPhi MOZ_FINAL : public MDefinition, public InlineListNode<MPhi>
{
js::Vector<MUse, 2, IonAllocPolicy> inputs_;
@ -5056,7 +5044,7 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
// Prefer reserveLength() and addInput() instead, where possible.
bool addInputSlow(MDefinition *ins, bool *ptypeChange = nullptr);
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
bool congruentTo(const MDefinition *ins) const;
@ -5072,17 +5060,7 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
}
void computeRange(TempAllocator &alloc);
MDefinition *operandIfRedundant() {
// If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
// returns the operand that it will always be equal to (a, in
// those two cases).
MDefinition *first = getOperand(0);
for (size_t i = 1, e = numOperands(); i < e; i++) {
if (getOperand(i) != first && getOperand(i) != this)
return nullptr;
}
return first;
}
MDefinition *operandIfRedundant();
bool canProduceFloat32() const {
return canProduceFloat32_;
@ -6210,7 +6188,7 @@ class MNot
INSTRUCTION_HEADER(Not);
void cacheOperandMightEmulateUndefined();
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
void markOperandCantEmulateUndefined() {
operandMightEmulateUndefined_ = false;
@ -7153,7 +7131,7 @@ class MClampToUint8
return new(alloc) MClampToUint8(input);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
TypePolicy *typePolicy() {
return this;
@ -8900,7 +8878,7 @@ class MStringLength
return new(alloc) MStringLength(string);
}
MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
MDefinition *foldsTo(TempAllocator &alloc);
TypePolicy *typePolicy() {
return this;

View File

@ -118,6 +118,15 @@ MIRGraph::removeBlock(MBasicBlock *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()
{
@ -930,6 +939,20 @@ 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)
{

View File

@ -26,7 +26,7 @@ class MDefinitionIterator;
typedef InlineListIterator<MInstruction> MInstructionIterator;
typedef InlineListReverseIterator<MInstruction> MInstructionReverseIterator;
typedef InlineForwardListIterator<MPhi> MPhiIterator;
typedef InlineListIterator<MPhi> MPhiIterator;
typedef InlineForwardListIterator<MResumePoint> MResumePointIterator;
class LBlock;
@ -189,9 +189,8 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
void replacePredecessor(MBasicBlock *old, MBasicBlock *split);
void replaceSuccessor(size_t pos, MBasicBlock *split);
// Removes `pred` from the predecessor list. `pred` should not be
// the final predecessor. If this block defines phis, removes the
// entry for `pred` and updates the indices of later entries.
// Removes `pred` from the predecessor list. If this block defines phis,
// removes the entry for `pred` and updates the indices of later entries.
// This may introduce redundant phis if the new block has fewer
// than two predecessors.
void removePredecessor(MBasicBlock *pred);
@ -292,6 +291,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
MPhiIterator phisBegin() const {
return phis_.begin();
}
MPhiIterator phisBegin(MPhi *at) const {
return phis_.begin(at);
}
MPhiIterator phisEnd() const {
return phis_.end();
}
@ -420,8 +422,12 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
numDominated_ += n;
}
// Add |child| to this block's immediately-dominated set.
bool addImmediatelyDominatedBlock(MBasicBlock *child);
// Remove |child| from this block's immediately-dominated set.
void removeImmediatelyDominatedBlock(MBasicBlock *child);
// This function retrieves the internal instruction associated with a
// slot, and should not be used for normal stack operations. It is an
// internal helper that is also used to enhance spew.
@ -506,7 +512,7 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
CompileInfo &info_; // Each block originates from a particular script.
InlineList<MInstruction> instructions_;
Vector<MBasicBlock *, 1, IonAllocPolicy> predecessors_;
InlineForwardList<MPhi> phis_;
InlineList<MPhi> phis_;
InlineForwardList<MResumePoint> resumePoints_;
FixedList<MDefinition *> slots_;
uint32_t stackPosition_;
@ -565,7 +571,7 @@ class MIRGraph
: alloc_(alloc),
returnAccumulator_(nullptr),
blockIdGen_(0),
idGen_(1),
idGen_(0),
osrBlock_(nullptr),
osrStart_(nullptr),
numBlocks_(0),
@ -627,6 +633,7 @@ class MIRGraph
}
void removeBlocksAfter(MBasicBlock *block);
void removeBlock(MBasicBlock *block);
void removeBlockIncludingPhis(MBasicBlock *block);
void moveBlockToEnd(MBasicBlock *block) {
JS_ASSERT(block->id());
blocks_.remove(block);

View File

@ -83,8 +83,10 @@ UnreachableCodeElimination::removeUnmarkedBlocksAndCleanup()
// Pass 5: It's important for optimizations to re-run GVN (and in
// turn alias analysis) after UCE if we eliminated branches.
if (rerunAliasAnalysis_ && mir_->optimizationInfo().gvnEnabled()) {
ValueNumberer gvn(mir_, graph_, mir_->optimizationInfo().gvnKind() == GVN_Optimistic);
if (!gvn.clear() || !gvn.analyze())
ValueNumberer gvn(mir_, graph_);
if (!gvn.run(rerunAliasAnalysis_
? ValueNumberer::UpdateAliasAnalysis
: ValueNumberer::DontUpdateAliasAnalysis))
return false;
IonSpewPass("GVN-after-UCE");
AssertExtendedGraphCoherency(graph_);

File diff suppressed because it is too large Load Diff

View File

@ -7,131 +7,101 @@
#ifndef jit_ValueNumbering_h
#define jit_ValueNumbering_h
#include "jit/MIR.h"
#include "jit/IonAllocPolicy.h"
#include "js/HashTable.h"
namespace js {
namespace jit {
class MDefinition;
class MBasicBlock;
class MIRGraph;
class MPhi;
class MInstruction;
class MIRGenerator;
class ValueNumberer
{
protected:
struct ValueHasher
// Value numbering data.
class VisibleValues
{
typedef MDefinition * Lookup;
typedef MDefinition * Key;
static HashNumber hash(const Lookup &ins) {
return ins->valueHash();
}
// Hash policy for ValueSet.
struct ValueHasher
{
typedef const MDefinition *Lookup;
typedef MDefinition *Key;
static HashNumber hash(Lookup ins);
static bool match(Key k, Lookup l);
static void rekey(Key &k, Key newKey);
};
static bool match(const Key &k, const Lookup &l) {
// If one of the instructions depends on a store, and the
// other instruction does not depend on the same store,
// the instructions are not congruent.
if (k->dependency() != l->dependency())
return false;
return k->congruentTo(l);
}
typedef HashSet<MDefinition *, ValueHasher, IonAllocPolicy> ValueSet;
ValueSet set_; // Set of visible values
public:
explicit VisibleValues(TempAllocator &alloc);
bool init();
typedef ValueSet::Ptr Ptr;
typedef ValueSet::AddPtr AddPtr;
Ptr findLeader(const MDefinition *def) const;
AddPtr findLeaderForAdd(MDefinition *def);
bool insert(AddPtr p, MDefinition *def);
void overwrite(AddPtr p, MDefinition *def);
void forget(const MDefinition *def);
void clear();
};
typedef HashMap<MDefinition *,
uint32_t,
ValueHasher,
IonAllocPolicy> ValueMap;
typedef Vector<MBasicBlock *, 4, IonAllocPolicy> BlockWorklist;
typedef Vector<MDefinition *, 4, IonAllocPolicy> DefWorklist;
struct DominatingValue
{
MDefinition *def;
uint32_t validEnd;
};
typedef HashMap<uint32_t,
DominatingValue,
DefaultHasher<uint32_t>,
IonAllocPolicy> InstructionMap;
protected:
TempAllocator &alloc() const;
uint32_t lookupValue(MDefinition *ins);
MDefinition *findDominatingDef(InstructionMap &defs, MDefinition *ins, size_t index);
MDefinition *simplify(MDefinition *def, bool useValueNumbers);
MControlInstruction *simplifyControlInstruction(MControlInstruction *def);
bool eliminateRedundancies();
bool computeValueNumbers();
inline bool isMarked(MDefinition *def) {
return pessimisticPass_ || def->isInWorklist();
}
void markDefinition(MDefinition *def);
void unmarkDefinition(MDefinition *def);
void markConsumers(MDefinition *def);
void markBlock(MBasicBlock *block);
void setClass(MDefinition *toSet, MDefinition *representative);
public:
static MDefinition *findSplit(MDefinition *);
void breakClass(MDefinition*);
protected:
MIRGenerator *mir;
MIRGenerator *const mir_;
MIRGraph &graph_;
ValueMap values;
bool pessimisticPass_;
size_t count_;
VisibleValues values_; // Numbered values
DefWorklist deadDefs_; // Worklist for deleting values
BlockWorklist unreachableBlocks_; // Worklist for unreachable blocks
BlockWorklist remainingBlocks_; // Blocks remaining with fewer preds
size_t numBlocksDeleted_; // Num deleted blocks in current tree
bool rerun_; // Should we run another GVN iteration?
bool blocksRemoved_; // Have any blocks been removed?
bool updateAliasAnalysis_; // Do we care about AliasAnalysis?
bool dependenciesBroken_; // Have we broken AliasAnalysis?
bool deleteDefsRecursively(MDefinition *def);
bool pushDeadPhiOperands(MPhi *phi, const MBasicBlock *phiBlock);
bool pushDeadInsOperands(MInstruction *ins);
bool processDeadDefs();
bool removePredecessor(MBasicBlock *block, MBasicBlock *pred);
bool removeBlocksRecursively(MBasicBlock *block, const MBasicBlock *root);
MDefinition *simplified(MDefinition *def) const;
MDefinition *leader(MDefinition *def);
bool hasLeader(const MPhi *phi, const MBasicBlock *phiBlock) const;
bool loopHasOptimizablePhi(MBasicBlock *backedge) const;
bool visitDefinition(MDefinition *def);
bool visitControlInstruction(MBasicBlock *block, const MBasicBlock *root);
bool visitBlock(MBasicBlock *block, const MBasicBlock *root);
bool visitDominatorTree(MBasicBlock *root, size_t *totalNumVisited);
bool visitGraph();
public:
ValueNumberer(MIRGenerator *mir, MIRGraph &graph, bool optimistic);
bool analyze();
bool clear();
ValueNumberer(MIRGenerator *mir, MIRGraph &graph);
enum UpdateAliasAnalysisFlag {
DontUpdateAliasAnalysis,
UpdateAliasAnalysis,
};
// Optimize the graph, performing expression simplification and
// canonicalization, eliminating statically fully-redundant expressions,
// deleting dead instructions, and removing unreachable blocks.
bool run(UpdateAliasAnalysisFlag updateAliasAnalysis);
};
class ValueNumberData : public TempObject {
friend void ValueNumberer::breakClass(MDefinition*);
friend MDefinition *ValueNumberer::findSplit(MDefinition*);
uint32_t number;
MDefinition *classNext;
MDefinition *classPrev;
public:
ValueNumberData() : number(0), classNext(nullptr), classPrev(nullptr) {}
void setValueNumber(uint32_t number_) {
number = number_;
}
uint32_t valueNumber() {
return number;
}
// Set the class of this to the given representative value.
void setClass(MDefinition *thisDef, MDefinition *rep) {
JS_ASSERT(thisDef->valueNumberData() == this);
// If we are attempting to insert ourself, then nothing needs to be done.
// However, if the definition to be inserted already has the correct value number,
// it still needs to be inserted, since the value number needs to be updated lazily.
// this updating tactic can leave the world in a state where thisDef is not in the
// equivalence class of rep, but it has the same value number. Defs in this state
// need to be re-processed.
if (this == rep->valueNumberData())
return;
if (classNext)
classNext->valueNumberData()->classPrev = classPrev;
if (classPrev)
classPrev->valueNumberData()->classNext = classNext;
classPrev = rep;
classNext = rep->valueNumberData()->classNext;
if (rep->valueNumberData()->classNext)
rep->valueNumberData()->classNext->valueNumberData()->classPrev = thisDef;
rep->valueNumberData()->classNext = thisDef;
}
};
} // namespace jit
} // namespace js

View File

@ -5980,13 +5980,12 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op)
if (const char *str = op.getStringOption("ion-gvn")) {
if (strcmp(str, "off") == 0) {
jit::js_JitOptions.disableGvn = true;
} else if (strcmp(str, "pessimistic") == 0) {
jit::js_JitOptions.forceGvnKind = true;
jit::js_JitOptions.forcedGvnKind = jit::GVN_Pessimistic;
} else if (strcmp(str, "optimistic") == 0) {
jit::js_JitOptions.forceGvnKind = true;
jit::js_JitOptions.forcedGvnKind = jit::GVN_Optimistic;
} else {
} else if (strcmp(str, "on") != 0 &&
strcmp(str, "optimistic") != 0 &&
strcmp(str, "pessimistic") != 0)
{
// We accept "pessimistic" and "optimistic" as synonyms for "on"
// for backwards compatibility.
return OptionFailure("ion-gvn", str);
}
}
@ -6269,8 +6268,7 @@ main(int argc, char **argv, char **envp)
|| !op.addStringOption('\0', "ion-gvn", "[mode]",
"Specify Ion global value numbering:\n"
" off: disable GVN\n"
" pessimistic: use pessimistic GVN\n"
" optimistic: (default) use optimistic GVN")
" on: enable GVN (default)\n")
|| !op.addStringOption('\0', "ion-licm", "on/off",
"Loop invariant code motion (default: on, off to disable)")
|| !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",