Performance improvements jit

1322724: IonMonkey - Add the hit count information on the extra false branch blocks

1322932: IonMonkey - Only iterate the backedge of the inner-loop when it has already be visited

1329901 - Remove expensive isObservableOperand() loop guards.

1330667: IonMonkey - Create a new constant for every optimized arguments use.

1342016 - Fast-path for isObservableSlot().

1388045 - Branch Pruning: Check the compile info associated with the resume point.
This commit is contained in:
win7-7 2025-12-22 15:54:09 +02:00 committed by wuggy
commit f4b5f122a6
4 changed files with 186 additions and 38 deletions

View file

@ -442,14 +442,19 @@ 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.
bool isObservableSlot(uint32_t slot) const {
if (isObservableFrameSlot(slot))
return true;
inline bool isObservableSlot(uint32_t slot) const {
if (slot >= firstLocalSlot()) {
// The |this| slot for a derived class constructor is a local slot.
if (thisSlotForDerivedClassConstructor_)
return *thisSlotForDerivedClassConstructor_ == slot;
return false;
}
if (isObservableArgumentSlot(slot))
return true;
if (slot < firstArgSlot())
return isObservableFrameSlot(slot);
return false;
return isObservableArgumentSlot(slot);
}
bool isObservableFrameSlot(uint32_t slot) const {

View file

@ -196,6 +196,8 @@ FlagPhiInputsAsHavingRemovedUses(MIRGenerator* mir, MBasicBlock* block, MBasicBl
static bool
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++) {
@ -210,13 +212,10 @@ FlagAllOperandsAsHavingRemovedUses(MIRGenerator* mir, MBasicBlock* block)
if (MResumePoint* rp = ins->resumePoint()) {
// Note: no need to iterate over the caller's of the resume point as
// this is the same as the entry resume point.
MOZ_ASSERT(&rp->block()->info() == &info);
for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
if (mir->shouldCancel("FlagAllOperandsAsHavingRemovedUses inner loop"))
return false;
if (!rp->isObservableOperand(i))
continue;
rp->getOperand(i)->setUseRemovedUnchecked();
if (info.isObservableSlot(i))
rp->getOperand(i)->setUseRemovedUnchecked();
}
}
}
@ -227,10 +226,10 @@ FlagAllOperandsAsHavingRemovedUses(MIRGenerator* mir, MBasicBlock* block)
if (mir->shouldCancel("FlagAllOperandsAsHavingRemovedUses loop 2"))
return false;
const CompileInfo& info = rp->block()->info();
for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
if (!rp->isObservableOperand(i))
continue;
rp->getOperand(i)->setUseRemovedUnchecked();
if (info.isObservableSlot(i))
rp->getOperand(i)->setUseRemovedUnchecked();
}
rp = rp->caller();
}

View file

@ -154,7 +154,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)
{
@ -915,11 +917,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();
@ -1088,10 +1090,10 @@ IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoi
// +2 for the env 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();
@ -1376,11 +1378,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;
}
@ -1534,6 +1539,47 @@ IonBuilder::traverseBytecode()
if (!current)
return true;
}
}
#ifdef DEBUG
MOZ_ASSERT(graph().numBlocks() >= blockWorklist.length());
for (i = 0; i < cfg->numBlocks(); i++) {
MOZ_ASSERT(blockWorklist[i]);
MOZ_ASSERT(!blockWorklist[i]->isDead());
MOZ_ASSERT_IF(i != 0, blockWorklist[i]->id() != 0);
}
#endif
cfg = nullptr;
blockWorklist.clear();
return Ok();
}
AbortReasonOr<Ok>
IonBuilder::visitBlock(const CFGBlock* cfgblock, MBasicBlock* mblock)
{
mblock->setLoopDepth(loopDepth_);
cfgCurrent = cfgblock;
pc = cfgblock->startPc();
if (mblock->pc() && script()->hasScriptCounts())
mblock->setHitCount(script()->getHitCount(mblock->pc()));
// Optimization to move a predecessor that only has this block as successor
// just before this block.
if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1) {
graph().removeBlockFromList(mblock->getPredecessor(0));
graph().addBlock(mblock->getPredecessor(0));
}
MOZ_TRY(setCurrentAndSpecializePhis(mblock));
graph().addBlock(mblock);
while (pc < cfgblock->stopPc()) {
if (!alloc().ensureBallast())
return abort(AbortReason::Alloc);
#ifdef DEBUG
// In debug builds, after compiling this op, check that all values
@ -4594,7 +4640,33 @@ IonBuilder::jsop_ifeq(JSOp op)
if (!improveTypesAtTest(test->getOperand(0), test->ifTrue() == current, test))
return false;
return true;
MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
blockWorklist[test->trueBranch()->id()] = ifTrue;
// Filter the types in the false branch.
// Note: sometimes the false branch is used as merge point. As a result
// reuse the ifFalse block as a type improvement block and create a new
// ifFalse which we can use for the merge.
MBasicBlock* filterBlock = ifFalse;
ifFalse = nullptr;
graph().addBlock(filterBlock);
MOZ_TRY(setCurrentAndSpecializePhis(filterBlock));
MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
MOZ_TRY_VAR(ifFalse, newBlock(filterBlock, test->falseBranch()->startPc()));
filterBlock->end(MGoto::New(alloc(), ifFalse));
if (filterBlock->pc() && script()->hasScriptCounts())
filterBlock->setHitCount(script()->getHitCount(filterBlock->pc()));
blockWorklist[test->falseBranch()->id()] = ifFalse;
current = nullptr;
return Ok();
}
bool
@ -4755,9 +4827,78 @@ IonBuilder::processThrow()
MThrow* ins = MThrow::New(alloc(), def);
current->end(ins);
// Make sure no one tries to use this block now.
setCurrent(nullptr);
return processControlEnd();
return Ok();
}
AbortReasonOr<Ok>
IonBuilder::visitTableSwitch(CFGTableSwitch* cfgIns)
{
// Pop input.
MDefinition* ins = current->pop();
// Create MIR instruction
MTableSwitch* tableswitch = MTableSwitch::New(alloc(), ins, cfgIns->low(), cfgIns->high());
#ifdef DEBUG
MOZ_ASSERT(cfgIns->defaultCase() == cfgIns->getSuccessor(0));
for (size_t i = 1; i < cfgIns->numSuccessors(); i++) {
MOZ_ASSERT(cfgIns->getCase(i-1) == cfgIns->getSuccessor(i));
}
#endif
// Create the cases
for (size_t i = 0; i < cfgIns->numSuccessors(); i++) {
const CFGBlock* cfgblock = cfgIns->getSuccessor(i);
MBasicBlock* caseBlock;
MOZ_TRY_VAR(caseBlock, newBlock(current, cfgblock->startPc()));
blockWorklist[cfgblock->id()] = caseBlock;
size_t index;
if (i == 0) {
if (!tableswitch->addDefault(caseBlock, &index))
return abort(AbortReason::Alloc);
} else {
if (!tableswitch->addSuccessor(caseBlock, &index))
return abort(AbortReason::Alloc);
if (!tableswitch->addCase(index))
return abort(AbortReason::Alloc);
// If this is an actual case statement, optimize by replacing the
// input to the switch case with the actual number of the case.
MConstant* constant = MConstant::New(alloc(), Int32Value(i - 1 + tableswitch->low()));
caseBlock->add(constant);
for (uint32_t j = 0; j < caseBlock->stackDepth(); j++) {
if (ins != caseBlock->getSlot(j))
continue;
constant->setDependency(ins);
caseBlock->setSlot(j, constant);
}
graph().addBlock(caseBlock);
if (caseBlock->pc() && script()->hasScriptCounts())
caseBlock->setHitCount(script()->getHitCount(caseBlock->pc()));
MBasicBlock* merge;
MOZ_TRY_VAR(merge, newBlock(caseBlock, cfgblock->startPc()));
if (!merge)
return abort(AbortReason::Alloc);
caseBlock->end(MGoto::New(alloc(), merge));
blockWorklist[cfgblock->id()] = merge;
}
MOZ_ASSERT(index == i);
}
// Save the MIR instruction as last instruction of this block.
current->end(tableswitch);
return Ok();
}
void
@ -11015,9 +11156,11 @@ IonBuilder::jsop_arguments()
current->push(current->argumentsObject());
return true;
}
MOZ_ASSERT(lazyArguments_);
current->push(lazyArguments_);
return true;
MOZ_ASSERT(hasLazyArguments_);
MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
current->add(lazyArg);
current->push(lazyArg);
return Ok();
}
bool

View file

@ -1245,9 +1245,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_;