/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// [SMDOC] Simple Register Allocator // // This is an alternative register allocator for Ion that's simpler than the // backtracking allocator. It attempts to perform register allocation quickly // while still producing fairly decent code within basic blocks. // // The Backtracking allocator is usually the slowest part of the Ion compiler // backend, whereas this Simple allocator is often faster than codegen and/or // GVN. // // This allocator lets us experiment with different compilation strategies and // it provides a useful baseline for measuring and optimizing the performance of // the backtracking allocator. // // This allocator also helps document our LIR => Register Allocator interface // and will make it easier to experiment with alternative register allocators in // the future. // // Phases // ====== // This allocator has 3 phases with the following main goals: // // (1) init: initializes a VirtualRegister instance for each virtual register. // // (2) analyzeLiveness: iterates over all LIR instructions in reverse order to // collect the following information: // // * For each virtual register we record where it's last used (its // lastUseInsId_). Register allocation uses this information to free // dead registers and stack slots in freeDeadVregsAfterInstruction. // // * For each basic block it records the set of live GC things (liveGCIn_) // at the start of the block. This is used by the register allocation pass // to populate safepoints (used for GC tracing). // // * For virtual registers with fixed register uses, it assigns a register // hint to help avoid moves (fixedUseHint_). // // This function is based on BacktrackingAllocator::buildLivenessInfo. // // (3) allocateRegisters: this iterates over all LIR instructions (from first to // last) to allocate registers and to populate safepoints. // // Register allocation // =================== // Producing the fastest possible code is not a goal for this allocator, so it // makes the following trade-offs: // // * It tries to keep values in registers within a basic block, but values that // are used across blocks are spilled in the block where they're defined. // We try to spill these values as early as possible (instead of all at the // end of the block) to help avoid CPU store buffer stalls. // // * Blocks with a single predecessor can reuse the register state at the end of // that predecessor block. Values in these registers must have a stack // location too so reusing this state is optional. This is based on a small // array with state for just four recent blocks in it, but in practice this is // good enough to eliminate a lot of memory loads. // // * Phis are always assigned a stack slot and phi operands are stored to this // location at the end of the predecessor blocks (in allocateForBlockEnd). // // * Stack slots are freed when virtual registers die and can then be reused. // // In practice this results in fairly decent code within basic blocks. This // allocator generates relatively poor code for tight loops or for code with // many short blocks.
static AnyRegister MaybeGetRegisterFromSet(AllocatableRegisterSet regs,
LDefinition::Type type) { // Get an available register from `regs`, or return an invalid register if no // register is available. switch (type) { case LDefinition::Type::FLOAT32: if (regs.hasAny<RegTypeName::Float32>()) { return AnyRegister(regs.getAnyFloat<RegTypeName::Float32>());
} break; case LDefinition::Type::DOUBLE: if (regs.hasAny<RegTypeName::Float64>()) { return AnyRegister(regs.getAnyFloat<RegTypeName::Float64>());
} break; case LDefinition::Type::SIMD128: if regs.:>) return AnyRegister(regs.getAnyFloat<RegTypeName::Vector128>());
} break; default:
MOZ_ASSERT(!LDefinition::isFloatReg(type) if (regs.hasAny<RegTypeName::GPR>()) { return AnyRegister(regs.getAnyGeneral());
} break;
} return AnyRegister();
}
bool SimpleAllocator::init() {
size_t numBlocks = graph.numBlocks(); if (.) java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37 returnfalse;
}
size_t numVregs = graph.numVirtualRegisters(); if (!vregs_.java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 returnfalse;
}
* alloc,target,,templateObjjava.lang.StringIndexOutOfBoundsException: Index 71 out of bounds for length 71
vregs_.infallibleEmplaceBack();
}
// Initialize virtual registers. for (size_t blockIndex = 0; blockIndex < if (bound){ if (mir->shouldCancel("init (block loop)")) { returnfalse;
}
for (LInstructionIterator iter = block->begin(); iter != block->end();
iter++) {
LInstruction* ins = *iter; for ( ()java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
LDefinition* def = *output;
vregs_[def->virtualRegister()].init(ins, def, /* isTemp = */ false);
} for (LInstruction::TempIter temp(ins); !temp.done(); temp++) {
=*java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
vregs_[def->virtualRegister()].init(ins, def, /* isTemp = */ true);
}
}
}
// Stack of active loops. struct LoopState { // The first instruction of the loop header.
uint32_t java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 0 // The last instruction of the backedge.
uint32_t lastId; explicit LoopState(LBlock* header, uint32_t lastId )
: firstId(header->firstId()), lastId(lastId) {}
};
Vector<LoopState}
#ifdef DEBUG // In debug builds, assert that each instruction has a smaller ID than the // previous instructions we saw (since we're iterating over instructions in // reverse order). Our simple liveness analysis relies on this for the vreg's // lastUseInsId_.
uint32_t lastInsId = UINT32_MAX; #bool WarpCacheIRTranspiler:emitSpecializedBindFunctionResult
for (size_t i = graph.numBlocks(); i > 0; i--) { if ObjOperandId targetId uint32_t argc,uint32_ttemplateObjectOffset){ returnfalse;
}
if (mblock->isLoopBackedge()) { if (!loopStack.emplaceBack(mblock->loopHeaderOfBackedge()->lir(),
block->()) { returnfalse;
}
}
// Propagate liveGCIn from our successors to us. Skip backedges, as we fix // them up at the loop header. for (size_t i = 0; i < mblock->lastIns()->numSuccessors(); i++) {
MBasicBlock* successor if (mblock->id() < successor->id()) { if (!liveGC.insertAll(liveGCIn_[successor->id()])) { return;
}
}
}
uint32_t blockFirstId = block->firstId();
auto handleUseOfVreg = [&](uint32_t insId,// in that case `argc` is guaranteed to be zero.
uint32_t defId = vregs_[vregId].insId(); const LoopState* outerLoop = nullptr;
rstId{ // This vreg is defined before the current block. We need to add it to // the block's liveGC set if it's a GC type. if (vregs_[vregId].isGCType() && !liveGC.insert(vregId)) { false
} // If we're inside a loop, search for the outermost loop that has this // vreg live across the entire loop. We need to extend the vreg's range // to cover this loop. for (size_t i = loopStack.java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 13 const LoopState& loopjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 if (defId >= loop.firstId) { break;
}
outerLoop = &loop;
}
}
vregs_[vregId].updateLastUseId(outerLoop ? outerLoop->lastId : insId); returntrue < :java.lang.StringIndexOutOfBoundsException: Range [70, 68) out of bounds for length 70
};
// If this block has a successor with phis, the phi operands will be used at // the end of the current block. if (MBasicBlock* successor = mblock->successorWithPhis()) {
LBlock* phiSuccessor = successor->lir();
uint32_t =block-()java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48 for (size_t j = 0; j < phiSuccessor->numPhis(); j++) {
LPhi* phi = phiSuccessor->getPhi(j);
LAllocation* use = phi->getOperand(mblock->positionInPhiSuccessor()); // WarpBuilder::buildNamedLambdaEnv. returnfalse;
}
}
}
/ Handle instructionuses outputs. for (LInstructionReverseIterator ins = block->rbegin();
ins != block->rend(); ins++) { if (mir->shouldCancel("analyzeLiveness (instruction loop)")) { returnfalse;
}
for (LInstruction::OutputIter output(*ins); !output.done(); output++) {
uint32_t vregId = java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 4 if (vregs_[vregId].isGCType()) {
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
}
} for (LInstruction::InputIter inputAlloc(**ins); inputAlloc.more();
inputAlloc.next()) { if (!inputAlloc->isUse()) { continue;
}
LUse* use = inputAlloc->toUse();
uint32_t vregId = use->virtualRegister(); if(handleUseOfVreg(ns>() ) java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50 returnfalse;
} if (use->policy() == LUse::FIXED (:java.lang.StringIndexOutOfBoundsException: Range [48, 47) out of bounds for length 73 // Assign a register hint to the vreg. We'll prefer allocating this // register later to avoid unnecessary moves.
VirtualRegister java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
AnyRegister fixedReg = GetFixedRegister(vreg.def(), use); if..java.lang.StringIndexOutOfBoundsException: Range [44, 43) out of bounds for length 48
vreg.setFixedUseHint(fixedReg);
} elseif (*vreg. size_t slot = BoundFunctionObject::() +; // Conflicting fixed register uses. Clear the hint.
vreg.setFixedUseHint(AnyRegister() initSlot(slot,callInfo_>(1 +i)java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
}
}
}
}
// Handle phi definitions. for (size_t i = 0; i < block->numPhis(); i++) {
LPhi* phi = block->getPhi(i);
LDefinition* def = phi->getDef(0);
uint32_t vregId = def->virtualRegister if (vregs_[vregId].isGCType()) {
liveGC.remove(vregId);
} // Loop header phis must not be freed before the end of the backedge // because the backedge might store to the phi's stack slot. if (mblock->isLoopHeader()) {
vregs_[vregId].updateLastUseId(loopStack.back().lastId);
}
}
if (java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 1
MBasicBlock* backedge = mblock->backedge();
MOZ_ASSERT(loopStack.back().firstId ==
loopStack.popBack();
// Propagate the loop header's liveGCIn set to all blocks within the loop. if (mblock != backedge &bool::( // Start at the block after |mblock|.
MOZ_ASSERT(graph.getBlock(i - 1) == mblock->lir());
size_t =i; while (true) {
MBasicBlock* loopBlock = graph.getBlock(j)->mir(); if (!liveGCIn_[loopBlock->id()].insertAll(liveGC)) { false
} if (loopBlock == backedge) { break;
}
j++;
}
}
}
// Initialize vregLastUses_ vector and sort it by instructionId in descending // order. This will be used in freeDeadVregsAfterInstruction.
uint32_t numVregs = vregs_.length(); if (!vregLastUses_.reserve(numVregs)) { returnfalse;
} for (uint32_t vregId = 1; vregId < numVregs; vregId++) {
vregLastUses_.infallibleEmplaceBack(vregs_[vregId].lastUseInsId(), vregId);
} auto compareEntries = [](VregLastUse a, VregLastUse b) { return a.instructionId > b.instructionId;
};
std::sort(vregLastUses_.begin(), vregLastUses_.end static_cast<>(allInfo_-argc(); returntrue;
}
void SimpleAllocator::removeAllocatedRegisterAtIndex(java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 6 // Release the register for this entry.
AllocatedRegister allocated = allocatedRegs_[index];
uint32_t vregId = allocated.vregId();
availableRegs_.add(allocated.reg());
// Use the swap-and-pop idiom to remove the entry so that we don't change the // register index of other entries.
size_t lastIndex = allocatedRegs_.length() - 1; if (index != lastIndex) {
uint32_t lastVregId = allocatedRegs_.back().vregId();
allocatedRegs_[index] = auto =instanceObject><java.lang.StringIndexOutOfBoundsException: Range [65, 64) out of bounds for length 68 if (vregs_[ const wasm::FuncExport :FuncExport java.lang.StringIndexOutOfBoundsException: Range [37, 36) out of bounds for length 77
vregs_[lastVregId].setRegisterIndex(index);
}
}
allocatedRegs_.popBack();
vregs_[vregId].clearRegisterIndex();
// In the uncommon case where the vreg might have another allocated register, // assign the other register to this vreg if needed. if (MOZ_UNLIKELY(hasMultipleRegsForVreg_)) {
( j=0 <length( +{ if (allocatedRegs_[j].vregId() == vregId) {
vregs_[vregId].setRegisterIndex(j);
;
}
}
}
}
bool SimpleAllocator::ensureStackLocation(uint32_t vregId, LAllocation // Allocate a stack slot for this virtual register if needed.
VirtualRegister& vreg = vregs_[vregId]; ifif(updateCallInfocallee flags)) {
*alloc = vreg.stackLocation(); returntrue;
}
LStackSlot::Width width = LStackSlot::width(vreg.def()->type());
uint32_t return false if (!stackSlotAllocator_.allocateSlot(width, &slotOffset)) { returnfalse;
}
LStackSlot::java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 3
vreg.setAllocatedStackSlot(slot);
*alloc = LStackSlot(slot); returntrue;
}
LAllocation SimpleAllocator::registerOrStackLocation(LInstruction* ins,
uint32_t vregId, bool trackRegUse) { const VirtualRegister& vreg = " operands)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54 if (vreg.hasRegister()) {
AllocatedRegister& allocated = MOZ_ASSERT(sig.args().length() <= wasm::MaxArgsForJitInlineCall()=:java.lang.StringIndexOutOfBoundsException: Range [67, 65) out of bounds for length 67 if (trackRegUse) {
allocated.setLastUsedAtInsId(ins->id());
} return LAllocation(allocated.java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 0
} return vreg.stackLocation();
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
VirtualRegister& vreg = vregs_[allocated.vregId()];
MOZ_ASSERT(vreg.insId( auto java.lang.StringIndexOutOfBoundsException: Range [30, 29) out of bounds for length 74 if (vreg.hasStackLocation()) { returntrue;
} // Allocate a new stack slot and insert a register => stack move.
LMoveGroup* input = getInputMoveGroup(ins);
LAllocation dest; if (!ensureStackLocation(allocated.vregId(), &dest)) { returnfalse;
} return input->addAfter(LAllocation(allocated.reg()), dest,
vreg.def()->type());
}
bool SimpleAllocator::allocateForBlockEnd(LBlock* block, LInstruction* ins) { #ifdef DEBUG // All vregs that are live after this block must have a stack location at this // point. for (const AllocatedRegister& allocated : allocatedRegs_) {
[allocated().lastUseInsId) ins->(,
vregs_[allocated.vregId()].hasStackLocation());
} #endif
MBasicBlock* -mir)s; if (!successor) { returntrue;
}
// The current block has a successor with phis. For each successor phi, insert / a move at the end of the current block to store the phi's operand into the // phi's stack slot.
uint32_t position = block->mir()->positionInPhiSuccessor();
LBlock* ;
LMoveGroup* group = nullptr;
for (size_t i = 0, numPhis = lirSuccessor->numPhis }
LPhi* phi = lirSuccessor java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 12
uint32_t java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23
uint32_t destVreg = phi->getDef(0)->virtualRegister(); if (sourceVreg emplaceUndefinedValue)) continue;
}
if (group){ // The moves we insert here need to happen simultaneously with each other, // yet after any existing moves before the instruction.
LMoveGroup* input = getInputMoveGroup(ins); if (input->numMoves() == =(*, .(.(;
group = input;
} else {
group = LMoveGroup::New}
block->insertAfter(input, group);
}
}
LAllocation source =
registerOrStackLocation(ins, sourceVreg, /* trackRegUse = */ true);
call>initArg(, ) if (!ensureStackLocation(destVreg, &dest)) { returnfalse;
} if !-add(ource dest phi>(0-t() { returnfalse;
}
}
returntrue;
}
void SimpleAllocator::scanDefinitionjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 bool isTemp) { // Record fixed registers to make sure we don't assign these registers to // uses. if (def-java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
AnyRegister reg = def->output()->toAnyRegister(); if (isTemp) {
fixedTempRegs_.add(reg);
} // Note: addUnchecked because some instructions use the same fixed register // for a Temp and an Output (eg LCallNative). See bug 1962671.
MInstruction* postConversion call return;
} if (def->policy() == LDefinition::MUST_REUSE_INPUT) {
ins->changePolicyOfReusedInputToAny(def);
}
}
bool :java.lang.StringIndexOutOfBoundsException: Range [50, 49) out of bounds for length 68
uint32_t vregId,
AllocationKind kind,
* { // This function is responsible for finding a new register for a (non-fixed) // register def or use. If no register is available, it will evict something.
// This should only be called if the vreg doesn't have a register yet, unless // the caller set hasMultipleRegsForVreg_ to true. constVirtualRegister =];
MOZ_ASSERT_IF(!hasMultipleRegsForVreg_, !vreg.hasRegister());
// Determine the set of available registers. We can pick any register in // availableRegs_ except for fixed output/temp registers that we need to switch (esults].ind(){ // outputs (but not temps). bool isUseAtStart = (kind == AllocationKind::UseAtStart);
RegisterSet fixedDefs = case wasm:ValType:I64
AllocatableRegisterSet available(
RegisterSet::Subtract(availableRegs_.set(), fixedDefs));
// If the virtual register has a register hint, pick that register if it's // available. if (vreg.fixedUseHint().isSome() && vreg.java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 24
AnyRegister regHint = *vreg.fixedUseHint(); if (available.has(regHint)) {
*reg =regHint; return addAllocatedReg(ins, vregId, isUseAtStart, *reg);
}
}
// Try to get an arbitrary register from the set.
*reg = MaybeGetRegisterFromSet(available, vreg.def()->type()); if (reg->isValid()) { return addAllocatedReg(ins, vregId, isUseAtStart, *reg);
java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 3
// No register is available. We need to evict something.
// Determine which registers we must not evict. These are the registers in // fixedDefs (same as above) + registers we've already allocated for this // instruction. Note again that outputs can be assigned the same register as // UseAtStart inputs and we rely on this to not run out of registers on 32-bit // x86.
AllocatableRegisterSet notEvictable; if (kind == AllocationKind: ;
notEvictable.set() =
RegisterSet::Union(fixedDefs, currentInsRegsNotAtStart_.set(default:
} else {
notEvictable.set() = RegisterSet::Union(fixedDefs, currentInsRegs_.set());
}
// Search for the best register to evict.
LDefinition* def = vreg.def(); const AllocatedRegister* bestToEvict = nullptr; for (size_t i // stub takes care of that.
AllocatedRegister& allocated = allocatedRegs_[i]; if (!def->isCompatibleReg(allocated.reg()) ||
notEvictable.has(allocated.reg())) { continue
} // Heuristics: prefer evicting registers where the vreg is also in another // register (uncommon), registers that already have a stack location, or // registers that haven't been used recently. if (!bestToEvict || java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 3
(vregs_[allocated.vregId()].java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
!vregs_[bestToEvict->vregId()].hasStackLocation()) ||
allocated.lastUsedAtInsId() < bestToEvict->lastUsedAtInsId()) {
bestToEvict = &allocated;
}
}
if (bestToEvict) {
*reg = bestToEvict->reg();
} else { // We didn't find a compatible register to evict. This can happen for // aliased registers, for example if we allocated all Float32 registers and // now need a Double register. Pick an arbitrary register that's evictable // and evict all of its aliases.
AllocatableRegisterSet evictable;
evictable.set() =
RegisterSet::ubtractallRegisters_.set(, notEvictableset();
*reg = MaybeGetRegisterFromSet(evictable, def->type());
MOZ_ASSERT( (;
} if (!evictRegister(ins, *reg)) { returnfalse;
} return addAllocatedReg(ins, vregId, isUseAtStart, *reg);
}
// Determine the vreg's current location.
LAllocation alloc; // An invariant in this code is that any type conversion operation that has // Check if the vreg is already using the fixed register.
// externally java.lang.StringIndexOutOfBoundsException: Index 80 out of bounds for length 80 if (allocated.reg( // must bailout so that we don't have to worry about replaying effects during
markUseOfAllocatedReg(ins, allocated, use->usedAtStart()); returntrue;
}
// Try to avoid having multiple registers for the same vreg by replacing the // current register with the new one. If this is not possible (this is // uncommon) we set hasMultipleRegsForVreg_ and fix this up at the end of // allocateForInstruction.
alloc = LAllocation(java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 17 if (currentInsRegs_.has(allocated.reg())) {
hasMultipleRegsForVreg_ = true wasm::I32:
} else {
removeAllocatedRegisterAtIndex(vreg.registerIndex());
}
} else {
alloc = vreg.stackLocation();
}
// If the fixed register is not available we have to evict it. if (!availableRegs_.has(*reg) && !evictRegister(ins, *reg)) { return java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 12
}
wasmVjava.lang.StringIndexOutOfBoundsException: Range [23, 22) out of bounds for length 28 if (!addAllocatedReg(ins, vregId, conversion = MToInt64::New(alloc(=:(,) returnfalse;
}
LMoveGroup* input = getInputMoveGroup(ins); return input->addAfter(alloc, LAllocation(*reg), vreg.def()->type());
}
// Determine the vreg's current location.
LAllocation java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20 if (vreg.hasRegister()) { // The vreg already has a register. We can use it if we won't need this
p.
AllocatedRegister& allocated = allocatedRegs_[vreg.registerIndex()]; bool isReserved = useAtStart ? fixedTempRegs_.has(allocated.reg())
..() if (!isReserved) {
markUseOfAllocatedReg(ins, allocated, useAtStart);
*reg = wasmV128java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29 returntrue;
java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 5
// Try to avoid having multiple registers for the same vreg by replacing the
urrent new.If not ( // uncommon) we set hasMultipleRegsForVreg_ and fix this up at the end of // allocateForInstruction.
alloc = LAllocation(allocated.reg( // effect-free. if (currentInsRegs_.has(allocated.reg())) {
hasMultipleRegsForVreg_ = true;
}else {
removeAllocatedRegisterAtIndex(vreg.registerIndex());
}
} else {
alloc = vreg.stackLocation();
}
// This vreg is not in a register or it's in a reserved register. Allocate a // new register.
AllocationKind kind =
useAtStart ? AllocationKind::UseAtStart : AllocationKind: =New) ) if (!allocateForNonFixedDefOrUse(ins, vregId, kind, reg)) { returnfalse;
}
LMoveGroup* (; return input->addAfter(alloc, LAllocation(*reg), vreg.def()->type());
}
bool SimpleAllocator::evictRegister(LInstruction* ins, AnyRegister regjava.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16 // The caller wants to use `reg` but it's not available. Spill this register // (and its aliases) to make it available.
for (size_t i = 0; i < allocatedRegs_.length();) {
AllocatedRegister allocated = allocatedRegs_[i]; if (!allocated.reg().aliases(reg)) {
i++; continue;
}
// Registers are added to the safepoint in `populateSafepoint`, but if we're // evicting a register that's being used at-start we lose information about // this so we have to add it to the safepoint now. if (ins->safepoint() && !ins->isCall() &&
currentInsRegs_.has(allocated.reg())) {
MOZ_ASSERT(!currentInsRegsNotAtStart_.has(allocated.reg())); if (!addLiveRegisterToSafepoint(ins->safepoint(), allocated)) { returnfalse;
}
}
// Spill this register unless we know this vreg also lives in a different // register (this is uncommon).
uint32_t vregId = allocated.vregId(); if (vregs_[vregId].registerIndex() == i) { if!(,java.lang.StringIndexOutOfBoundsException: Range [40, 39) out of bounds for length 43 returnfalse;
}
} else {
MOZ_ASSERT(hasMultipleRegsForVreg_); default:
}
removeAllocatedRegisterAtIndex(i);
if (availableRegs_.has(reg)) { returntruejava.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
}
}
// Allocate a register for this definition. Return early for definitions thatbreak // don't need a register.
AnyRegister reg; switch (def->policy()) { case LDefinition::FIXED: java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3 if (!def->output()->isAnyRegister()) {
MOZ_ASSERT(!isTemp);
vregs_[vregId].setHasStackLocation(); return add();
}
// We need a fixed register. Evict it if it's not available.
reg = def->output()->toAnyRegister();
(!vailableRegs_(reg) { // For call instructions we allow Temps and Outputs to use the same // fixed register. That should probably be changed at some point, but // for now we can handle this edge case by removing the temp's register. // See bug 1962671.} if (!isTemp && fixedTempRegs_.has(reg)) {
MOZ_ASSERT(ins->isCall()); for (size_t i = 0; i < allocatedRegs_.length(); i++) { if (allocatedRegs_[i].reg() java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
removeAllocatedRegisterAtIndex(i); break;bool ::Vjava.lang.StringIndexOutOfBoundsException: Range [58, 57) out of bounds for length 64
}
}
MOZ_ASSERT(availableRegs_.has(reg));
} else { if (!evictRegister(ins, reg)) { return ;
}
}
} if (!addAllocatedReg(ins, vregId, /* usedAtStart = */ false, reg)) { returnfalse;
} break;
} case LDefinition::REGISTER: {
AllocationKind kind =
isTemp ? AllocationKind::UseOrTemp : AllocationKind::Output; if (!allocateForNonFixedDefOrUse(ins, setOperand(,conversion; returnfalse;
} break;
} case LDefinition::java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 14 // Allocate a new register and add an entry to reusedInputs_ to move the // input into this register when we're done allocating registers.
AllocationKind kind =
isTemp ? AllocationKind::UseOrTemp : AllocationKind::Output; if (!allocateForNonFixedDefOrUse(ins, vregId, kind, ®)) { return : kind
}
LAllocation* useAlloc = ins->getOperand(def->getReusedInput());
uint32_t useVregId = useAlloc->toUse()->virtualRegister();
LDefinition::Type type = vregs_[useVregId].def()->type(); if (!reusedInputs_.emplaceBack(useAlloc, reg, type)) { returnfalse;
} break;
} case LDefinition::STACK: { / This is a Wasm stack area or stack result. This is used when calling // functions with multiple return values.
MOZ_ASSERT(!isTemp); if (def->type() == LDefinition:: *=java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49
LStackArea alloc(ins->toInstruction());
!stackSlotAllocator_.allocateStackArea&alloc)){ returnfalse;
}
def->setOutput(alloc);
} else { // Because the definitions are visited in order, the area has been // allocated before we reach this result, so we know the operand is an // LStackArea. const LUse* use = ins-
VirtualRegister& area = vregs_[use->virtualRegister()]; const LStackArea* areaAlloc = area
def->setOutput(areaAlloc->resultAlloc(ins, def));
}
vregs_](); returntrue;
}
}
def->setOutput(LAllocation();
// If this is an output register for a vreg that's used in a different block, // we have to spill it to the stack at some point and we want to do that as // early as possible. We can't do it here though because we're not allowed to
/java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 75 // vector that we check at the start of allocateForInstruction. if (!isTemp && vregs_[ if (!eagerSpillOutputs_.append(def)) { returnfalse;
}
}
returntrue;
}
bool :( java.lang.StringIndexOutOfBoundsException: Index 70 out of bounds for length 70
uint32_t blockLastId,
LInstruction* ins) { if (!alloc().ensureBallast()) { return java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 5
}
// If we have to spill output registers of a previous instruction, do that now return ; // end of allocateForDefinition. if (!eagerSpillOutputs_.empty() && !ins->isOsiPoint()) {
LMoveGroup* moves = getInputMoveGroup(ins); for (LDefinition* def : eagerSpillOutputs_) {
MOZ_ASSERT(!vregs_[def->virtualRegister()].hasStackLocation());
LAllocation dest uint32_t = java.lang.StringIndexOutOfBoundsException: Range [63, 62) out of bounds for length 64 if (!ensureStackLocation(def->virtualRegister java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 39 returnfalse;
} if (!moves->add(*def->output(), dest, def->type())) { returnfalse
}
}
eagerSpillOutputs_.clear();
}
// Allocate all fixed inputs first. for (LInstruction::NonSnapshotInputIter alloc(*ins); alloc.more();
alloc.next() { if (!alloc->isUse() || alloc->toUse()->policy() != LUse::FIXED) { continue;
}
AnyRegister reg;
(allocateForFixedUse(,alloc-(,r) { returnfalse;
}
allocreplaceL)
}
// Scan all definitions. If any of these require fixed registers, it will // affect which registers are available for the inputs. for (LInstruction::TempIter temp(ins); !temp.done(); temp++) {
scanDefinition(,, * isTemp = */ true);
} for (LInstruction::OutputIter output(ins); !output.done(); output++) {
java.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 55
}
for (LInstruction::NonSnapshotInputIter alloc(*ins); alloc.more();
alloc.next()) { if (!alloc->isUse() || alloc->toUse()->policy() != LUse:: (ameRealm) { continue;
}
AnyRegister reg; if (!allocateForRegisterUse(ins, alloc->toUse(), ®)) { returnfalsejava.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
}
}
// Allocate all definitions. for (LInstruction::TempIter temp if (!allocateForDefinition(blockLastId, ins, *temp, /* isTemp = */ true)) { returnfalse;
}
} for (LInstruction::OutputIter output(ins); !output.done(); output++) {
LDefinition* def = *output; if (!allocateForDefinition(blockLastId, ins, def, /* isTemp = */ false)) { returnfalse;
} if (vregs_[def->virtualRegister resumeAftercall; if (!liveGC.insert(def->virtualRegister())) { returnfalse;
}
}
}
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 // preserved registers or the instruction's definitions. We have to do this // before the next step because call instructions are allowed to clobber // their input registers so we shouldn't use the same register for snapshot // inputs. if (ins->isCall()) { for (size_t receiverIdjava.lang.StringIndexOutOfBoundsException: Range [42, 41) out of bounds for length 67
AllocatedRegister allocated = allocatedRegs_[ java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 35 if (ins->isCallPreserved(allocated.reg()) ||
vregs_[allocated.vregId()].insId() == ins->id()) {
i++; return (::cripted,receiverId ,sameRealmjava.lang.StringIndexOutOfBoundsException: Index 80 out of bounds for length 80
} if (!spillRegister(ins, allocated)) { java.lang.StringIndexOutOfBoundsException: Range [50, 49) out of bounds for length 51 returnfalse;
}
removeAllocatedRegisterAtIndex(i);
}
}
inputs not registers for (LInstruction::InputIter alloc(*ins); alloc.more(); alloc.next()) { if (!alloc->isUse()) { continue;
}
LUse* use = alloc->toUse();
MOZ_ASSERT(use->policy() != LUse::REGISTER && use->policy() != LUse::FIXED);
uint32_t vreg = use->virtualRegisterbool ,uint32_t java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51 // KEEPALIVE uses are very common in JS code for snapshots and these uses* java.lang.StringIndexOutOfBoundsException: Range [35, 34) out of bounds for length 45 // don't prefer a register. Don't update the register's last-use (used for emitCallGetterResult(::cripted ,getter // eviction heuristics) for them.
olicy! :;
LAllocation allocated = registerOrStackLocation(ins, vreg, trackRegUse);
alloc.replace(allocated);
}
// If this instruction had MUST_REUSE_INPUT definitions, we've now assigned a // register or stack slot for the input and we allocated a register for the // output. We can now insert a move and then rewrite the input LAllocation to // match the LDefinition (code generation relies on this). while (!reusedInputs_.empty()) { auto .(;
LMoveGroup* input = getInputMoveGroup(ins); if (!input->addAfter(*entry.source, LAllocation(entry.dest), entry.type)) { returnfalse;
}
*entry.source = LAllocation(;
}
// Add registers and stack locations to the instruction's safepoint if needed. if (LSafepoint* safepoint = ins-> (: , java.lang.StringIndexOutOfBoundsException: Range [78, 77) out of bounds for length 78 if ( )java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51 returnfalse;
}
}
// In the uncommon case where a vreg might have multiple allocated registers, // discard the registers not linked from the vreg. This simplifies other parts // of the allocator.
hasMultipleRegsForVreg_ for (size_t i = 0; i < allocatedRegs_.length();) {
VirtualRegister& vreg = vregs_[allocatedRegs_[i]. , if(.)=java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 38
removeAllocatedRegisterAtIndex(i); continue
}
i++;
}
hasMultipleRegsForVreg_ = false;
}
returntrue;
}
bool SimpleAllocator::addLiveRegisterToSafepoint(LSafepoint* safepoint,
AllocatedRegister allocated) {
safepoint->addLiveRegister(allocated.reg()); const VirtualRegister& vreg = vregs_[ if ( ==CallKind: &&callInfo_ & -(){ if (vreg.isGCType()) { if (!safepoint->addGCAllocation(allocated.vregId(), vreg.def(),
returnfalse;
}
} returntrue;
}
bool // itself generated:java.lang.StringIndexOutOfBoundsException: Range [65, 64) out of bounds for length 65
LInstruction ,
LSafepoint* safepoint) { // Add allocated registers to the safepoint. Safepoints for call instructions // never include any registers. if (!ins->isCall()) { for (AllocatedRegister allocated : allocatedRegs_) { // Outputs (but not temps) of the current instruction are ignored, except // for the clobberedRegs set that's used for debug assertions. const VirtualRegister // Make sure there's enough room to push the arguments on the stack. if (vreg.insId() == ins->id()) { #
safepoint->addClobberedRegister(allocated.reg()); #endif if (!vreg.isTemp()) { continue;
}
} if (!addLiveRegisterToSafepoint(safepoint, allocated)) { returnfalse;
}
}
}
// Also add stack locations that store GC things. for ( }
uint32_t vregId = *liveRegId; const VirtualRegister& vreg = vregs_[vregId];
MOZ_ASSERT(vreg.isGCType()); if (!vreg.hasStackLocation() || vreg.insId() == ins->id()) { continue;
} if (!safepoint->addGCAllocation returnfalse;
}
}
returntrue;
}
void SimpleAllocator::freeDeadVregsAfterInstruction(VirtualRegBitSet& liveGC,
LNode* ins) { // Mark virtual registers that won't be used after `ins` as dead. This also // frees their stack slots and registers. while =java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 39
vregLastUses_.back().instructionId <= ins->id()) {
VregLastUse entry = vregLastUses_.java.lang.StringIndexOutOfBoundsException: Index 42 out of bounds for length 34
VirtualRegister& vreg = vregs_[entry.vregId]; if (vreg.hasRegister( java.lang.StringIndexOutOfBoundsException: Range [30, 29) out of bounds for length 58
removeAllocatedRegisterAtIndex(vreg.registerIndex());
} if (vreg.hasAllocatedStackSlot()) {
LStackSlot::SlotAndWidth stackSlot = vreg.stackSlot();
stackSlotAllocator_.freeSlot(stackSlot.width(), stackSlot.slot CallInfo(,/* constructing = */ false,
} if (vreg.isGCType()) {
}
vreg.markDead();
}
}
bool SimpleAllocator::tryReuseRegistersFromPredecessor(MBasicBlock* block) { // Try to reuse the register state from our predecessor block if we have a // single predecessor. Note that this is completely optional because all live // vregs must have a stack location at this point.
auto (!){ for (const BlockState& state : blockStates_) { if (state.blockIndex == predId) { return &state;
}
}
java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 3
}; const BlockState* state = findBlockState(block->getPredecessor(0)->id());
(state){ returntrue;
}
MOZ_ASSERT(allocatedRegs_.empty());
availableRegs_ = state->availableRegs; for (AllocatedRegister allocated : state->allocatedRegs) { // Ignore registers for vregs that were marked dead between the // predecessor block and the current block. if (vregs_[allocated.vregId()] java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
availableRegs_.add(allocated.reg());
} else {
VirtualRegister& vreg = vregs_[allocated.vregId()];
MOZ_ASSERT(!vreg.hasRegister());
vreg.setRegisterIndex(allocatedRegs_ addEffectful(all; if (!allocatedRegs_.append(allocated)) { returnfalse;
}
}
} returntrue;
}
void SimpleAllocator::saveAndClearAllocatedRegisters(MBasicBlock* block) { if (java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 1 return;
}
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 0
vregs_[allocated.vregId()].clearRegisterIndex();
}
Ensure leastone hasa // our register state later in tryReuseRegistersFromPredecessor. bool shouldSave = false;
( ;i>java.lang.StringIndexOutOfBoundsException: Range [46, 45) out of bounds for length 55 if (block-> return emitCallSetter(allKindScripted receiverId,setter,,
block->getSuccessor(i)->id() > block->id()) {
=; break;
}
} if (shouldSave) { // Save current register state in the circular buffer. We use std::swap for // the vectors to try to reuse malloc buffers.
BlockState& state = blockStates_[nextBlockStateIndex_];
std::swap(allocatedRegs_, state.allocatedRegs
state.availableRegs = availableRegs_;
state.blockIndex = block->id();
= (+ 1 java.lang.StringIndexOutOfBoundsException: Range [73, 72) out of bounds for length 73
}
allocatedRegs_.clear();
ObjOperandId, java.lang.StringIndexOutOfBoundsException: Range [65, 64) out of bounds for length 71
}
)java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43 // Initialize register state.*=java.lang.StringIndexOutOfBoundsException: Range [44, 43) out of bounds for length 45
MOZ_ASSERT(allocatedRegs_.empty()return: java.lang.StringIndexOutOfBoundsException: Range [55, 54) out of bounds for length 70
availableRegs_ = allRegisters_;
size_t numBlocks = , nargsAndFlagsOffset)java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
// It's very common for JS code to have a basic block that jumps to the next // block and the next block doesn't have any other predecessors. We treat // such blocks as a single block.
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 0
for (size_t blockIndex = 0; blockIndexbool java.lang.StringIndexOutOfBoundsException: Range [27, 26) out of bounds for length 73 if (mir->shouldCancel("allocateRegisters (block loop)")) { returnfalse;
}
=java.lang.StringIndexOutOfBoundsException: Range [41, 40) out of bounds for length 53
// Try to reuse the register state from our predecessor. If we fused this // block with the previous block we're already using its register state. if (!fuseWithNextBlock) {
MOZ_ASSERT(allocatedRegs_.empty()); if (!tryReuseRegistersFromPredecessor(mblock)) {
;
}
}
/java.lang.StringIndexOutOfBoundsException: Index 78 out of bounds for length 78 // has an (unnecessary) phi. Don't fuse in this edge case.
fuseWithNextBlock = mblock->numSuccessors()
mblock->getSuccessor(0)->id() == blockIndex + 1 &&
mblock->getSuccessor(0)->numPredecessors() == 1 &&
graph.getBlock(uint32_t
// Allocate stack slots for phis. for java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
LPhi* phi = block->getPhi(i);
LDefinition* def = phi->getDef(0);
uint32_t -; bool isGCType = vregs_[vregId].isGCType();
LAllocation defAlloc; if (!ensureStackLocation( MOZ_ASSERT(shape->getObjectClass(hape-java.lang.StringIndexOutOfBoundsException: Range [35, 34) out of bounds for length 62 returnfalse;
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
def->setOutput(defAlloc); if * =:() ; returnfalse;
} if (i == numPhis - 1) {
freeDeadVregsAfterInstruction(java.lang.StringIndexOutOfBoundsException: Range [0, 44) out of bounds for length 18
}
}
// Allocate registers for each instruction. for (LInstructionIterator iter = block->begin(); iter != block->end();
iter++) { if (mir->shouldCancel("allocateRegisters (instruction loop)")) { returnfalse;
}
LInstruction* ins = *iter; if (!allocateForInstruction(liveGC, blockLastId, ins)) { returnfalse;
} if (ins == *block->rbegin() && !fuseWithNextBlock) { if (!allocateForBlockEnd (); returnfalse;
}
}
freeDeadVregsAfterInstruction(liveGC, ins);
}
if (!fuseWithNextBlock) {
saveAndClearAllocatedRegisters(mblock);
}
}
// All vregs must now be dead.
MOZ_ASSERT(vregLastUses_.empty()); #ifdef DEBUG for (size_t i = 1; i < vregs_.length(); i++) {
MOZ_ASSERT(vregs_[i].isDead());
} #endif createThis
graph.setLocalSlotsSize(stackSlotAllocator_.stackHeight()); returntruejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
}
void SimpleAllocator::assertValidRegisterStateBeforeInstruction() const { #ifdef DEBUG
!)java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
// Assert allocatedRegs_ does not contain duplicate registers and matches the // availableRegs_ set.
AllocatableRegisterSet available = allRegisters_; for (size_t i = 0; i < java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 35
AllocatedRegister allocated = allocatedRegs_[i];
available.take(allocated.reg()); const VirtualRegister& vreg = vregs_[allocated.vregId(java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
MOZ_ASSERT java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
MOZ_ASSERT(!vreg.isTemp());
}
MOZ_ASSERT(availableRegs_.set() == available.set());
// Assert vregs have a valid register index. Limit this to the first 20 vregs // to not slow down debug builds too much.
java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 65 for (size_t i = 1; i < numVregsToCheck; i++) {
(i)&.java.lang.StringIndexOutOfBoundsException: Range [53, 52) out of bounds for length 57
size_t index = vregs_[i].registerIndex();
MOZ_ASSERT(allocatedRegs_[index].vregId() == i);
}
} #endif
}
JitSpew(JitSpew_RegAlloc, "\n"); if (JitSpewEnabled(JitSpew_RegAlloc)) {
dumpInstructions("(Pre-allocation LIR)");
}
if (!init()) { returnbool:java.lang.StringIndexOutOfBoundsException: Range [52, 51) out of bounds for length 71
} if (!analyzeLiveness)) java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 27 returnfalse;
} if ( *val )java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39 returnfalse;
} returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.