bool AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins, uint32_t vreg, LAllocation alloc, bool populateSafepoints) { LSafepoint* safepoint = ins->safepoint(); MOZ_ASSERT(safepoint); if (ins->isCall() && alloc.isRegister()) return true; if (alloc.isRegister()) { AnyRegister reg = alloc.toRegister(); if (populateSafepoints) safepoint->addLiveRegister(reg); MOZ_ASSERT(safepoint->liveRegs().has(reg)); } // The |this| argument slot is implicitly included in all safepoints. if (alloc.isArgument() && alloc.toArgument()->index() < THIS_FRAME_ARGSLOT + sizeof(Value)) return true; LDefinition::Type type = virtualRegisters[vreg] ? virtualRegisters[vreg]->type() : LDefinition::GENERAL; switch (type) { case LDefinition::OBJECT: if (populateSafepoints) { JitSpew(JitSpew_RegAlloc, "Safepoint object v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addGcPointer(alloc)) return false; } MOZ_ASSERT(safepoint->hasGcPointer(alloc)); break; case LDefinition::SLOTS: if (populateSafepoints) { JitSpew(JitSpew_RegAlloc, "Safepoint slots v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addSlotsOrElementsPointer(alloc)) return false; } MOZ_ASSERT(safepoint->hasSlotsOrElementsPointer(alloc)); break; #ifdef JS_NUNBOX32 // Do not assert that safepoint information for nunbox types is complete, // as if a vreg for a value's components are copied in multiple places // then the safepoint information may not reflect all copies. All copies // of payloads must be reflected, however, for generational GC. case LDefinition::TYPE: if (populateSafepoints) { JitSpew(JitSpew_RegAlloc, "Safepoint type v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addNunboxType(vreg, alloc)) return false; } break; case LDefinition::PAYLOAD: if (populateSafepoints) { JitSpew(JitSpew_RegAlloc, "Safepoint payload v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addNunboxPayload(vreg, alloc)) return false; } MOZ_ASSERT(safepoint->hasNunboxPayload(alloc)); break; #else case LDefinition::BOX: if (populateSafepoints) { JitSpew(JitSpew_RegAlloc, "Safepoint boxed value v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addBoxedValue(alloc)) return false; } MOZ_ASSERT(safepoint->hasBoxedValue(alloc)); break; #endif default: break; } return true; }
bool AllocationIntegrityState::check(bool populateSafepoints) { MOZ_ASSERT(!instructions.empty()); #ifdef DEBUG if (JitSpewEnabled(JitSpew_RegAlloc)) dump(); for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) { LBlock* block = graph.getBlock(blockIndex); // Check that all instruction inputs and outputs have been assigned an allocation. for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) { LInstruction* ins = *iter; for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) MOZ_ASSERT(!alloc->isUse()); for (size_t i = 0; i < ins->numDefs(); i++) { LDefinition* def = ins->getDef(i); MOZ_ASSERT(!def->output()->isUse()); LDefinition oldDef = instructions[ins->id()].outputs[i]; MOZ_ASSERT_IF(oldDef.policy() == LDefinition::MUST_REUSE_INPUT, *def->output() == *ins->getOperand(oldDef.getReusedInput())); } for (size_t i = 0; i < ins->numTemps(); i++) { LDefinition* temp = ins->getTemp(i); MOZ_ASSERT_IF(!temp->isBogusTemp(), temp->output()->isRegister()); LDefinition oldTemp = instructions[ins->id()].temps[i]; MOZ_ASSERT_IF(oldTemp.policy() == LDefinition::MUST_REUSE_INPUT, *temp->output() == *ins->getOperand(oldTemp.getReusedInput())); } } } #endif // Check that the register assignment and move groups preserve the original // semantics of the virtual registers. Each virtual register has a single // write (owing to the SSA representation), but the allocation may move the // written value around between registers and memory locations along // different paths through the script. // // For each use of an allocation, follow the physical value which is read // backward through the script, along all paths to the value's virtual // register's definition. for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) { LBlock* block = graph.getBlock(blockIndex); for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) { LInstruction* ins = *iter; const InstructionInfo& info = instructions[ins->id()]; LSafepoint* safepoint = ins->safepoint(); if (safepoint) { for (size_t i = 0; i < ins->numTemps(); i++) { if (ins->getTemp(i)->isBogusTemp()) continue; uint32_t vreg = info.temps[i].virtualRegister(); LAllocation* alloc = ins->getTemp(i)->output(); if (!checkSafepointAllocation(ins, vreg, *alloc, populateSafepoints)) return false; } MOZ_ASSERT_IF(ins->isCall() && !populateSafepoints, safepoint->liveRegs().emptyFloat() && safepoint->liveRegs().emptyGeneral()); } size_t inputIndex = 0; for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) { LAllocation oldInput = info.inputs[inputIndex++]; if (!oldInput.isUse()) continue; uint32_t vreg = oldInput.toUse()->virtualRegister(); if (safepoint && !oldInput.toUse()->usedAtStart()) { if (!checkSafepointAllocation(ins, vreg, **alloc, populateSafepoints)) return false; } // Start checking at the previous instruction, in case this // instruction reuses its input register for an output. LInstructionReverseIterator riter = block->rbegin(ins); riter++; checkIntegrity(block, *riter, vreg, **alloc, populateSafepoints); while (!worklist.empty()) { IntegrityItem item = worklist.popCopy(); checkIntegrity(item.block, *item.block->rbegin(), item.vreg, item.alloc, populateSafepoints); } } } } return true; }
bool AllocationIntegrityState::checkSafepointAllocation(LInstruction *ins, uint32_t vreg, LAllocation alloc, bool populateSafepoints) { LSafepoint *safepoint = ins->safepoint(); JS_ASSERT(safepoint); if (ins->isCall() && alloc.isRegister()) return true; if (alloc.isRegister()) { AnyRegister reg = alloc.toRegister(); if (populateSafepoints) safepoint->addLiveRegister(reg); JS_ASSERT(safepoint->liveRegs().has(reg)); } LDefinition::Type type = virtualRegisters[vreg] ? virtualRegisters[vreg]->type() : LDefinition::GENERAL; switch (type) { case LDefinition::OBJECT: if (populateSafepoints) { IonSpew(IonSpew_RegAlloc, "Safepoint object v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addGcPointer(alloc)) return false; } JS_ASSERT(safepoint->hasGcPointer(alloc)); break; #ifdef JS_NUNBOX32 // Do not assert that safepoint information for nunboxes is complete, // as if a vreg for a value's components are copied in multiple places // then the safepoint information may not reflect all copies. // See SafepointWriter::writeNunboxParts. case LDefinition::TYPE: if (populateSafepoints) { IonSpew(IonSpew_RegAlloc, "Safepoint type v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addNunboxType(vreg, alloc)) return false; } break; case LDefinition::PAYLOAD: if (populateSafepoints) { IonSpew(IonSpew_RegAlloc, "Safepoint payload v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addNunboxPayload(vreg, alloc)) return false; } break; #else case LDefinition::BOX: if (populateSafepoints) { IonSpew(IonSpew_RegAlloc, "Safepoint boxed value v%u i%u %s", vreg, ins->id(), alloc.toString()); if (!safepoint->addBoxedValue(alloc)) return false; } JS_ASSERT(safepoint->hasBoxedValue(alloc)); break; #endif default: break; } return true; }