michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/RegisterAllocator.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: bool michael@0: AllocationIntegrityState::record() michael@0: { michael@0: // Ignore repeated record() calls. michael@0: if (!instructions.empty()) michael@0: return true; michael@0: michael@0: if (!instructions.appendN(InstructionInfo(), graph.numInstructions())) michael@0: return false; michael@0: michael@0: if (!virtualRegisters.appendN((LDefinition *)nullptr, graph.numVirtualRegisters())) michael@0: return false; michael@0: michael@0: if (!blocks.reserve(graph.numBlocks())) michael@0: return false; michael@0: for (size_t i = 0; i < graph.numBlocks(); i++) { michael@0: blocks.infallibleAppend(BlockInfo()); michael@0: LBlock *block = graph.getBlock(i); michael@0: JS_ASSERT(block->mir()->id() == i); michael@0: michael@0: BlockInfo &blockInfo = blocks[i]; michael@0: if (!blockInfo.phis.reserve(block->numPhis())) michael@0: return false; michael@0: michael@0: for (size_t j = 0; j < block->numPhis(); j++) { michael@0: blockInfo.phis.infallibleAppend(InstructionInfo()); michael@0: InstructionInfo &info = blockInfo.phis[j]; michael@0: LPhi *phi = block->getPhi(j); michael@0: JS_ASSERT(phi->numDefs() == 1); michael@0: uint32_t vreg = phi->getDef(0)->virtualRegister(); michael@0: virtualRegisters[vreg] = phi->getDef(0); michael@0: if (!info.outputs.append(*phi->getDef(0))) michael@0: return false; michael@0: for (size_t k = 0, kend = phi->numOperands(); k < kend; k++) { michael@0: if (!info.inputs.append(*phi->getOperand(k))) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) { michael@0: LInstruction *ins = *iter; michael@0: InstructionInfo &info = instructions[ins->id()]; michael@0: michael@0: for (size_t k = 0; k < ins->numTemps(); k++) { michael@0: uint32_t vreg = ins->getTemp(k)->virtualRegister(); michael@0: virtualRegisters[vreg] = ins->getTemp(k); michael@0: if (!info.temps.append(*ins->getTemp(k))) michael@0: return false; michael@0: } michael@0: for (size_t k = 0; k < ins->numDefs(); k++) { michael@0: uint32_t vreg = ins->getDef(k)->virtualRegister(); michael@0: virtualRegisters[vreg] = ins->getDef(k); michael@0: if (!info.outputs.append(*ins->getDef(k))) michael@0: return false; michael@0: } michael@0: for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) { michael@0: if (!info.inputs.append(**alloc)) michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return seen.init(); michael@0: } michael@0: michael@0: bool michael@0: AllocationIntegrityState::check(bool populateSafepoints) michael@0: { michael@0: JS_ASSERT(!instructions.empty()); michael@0: michael@0: #ifdef DEBUG michael@0: if (IonSpewEnabled(IonSpew_RegAlloc)) michael@0: dump(); michael@0: michael@0: for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) { michael@0: LBlock *block = graph.getBlock(blockIndex); michael@0: michael@0: // Check that all instruction inputs and outputs have been assigned an allocation. michael@0: for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) { michael@0: LInstruction *ins = *iter; michael@0: michael@0: for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) michael@0: JS_ASSERT(!alloc->isUse()); michael@0: michael@0: for (size_t i = 0; i < ins->numDefs(); i++) { michael@0: LDefinition *def = ins->getDef(i); michael@0: JS_ASSERT_IF(def->policy() != LDefinition::PASSTHROUGH, !def->output()->isUse()); michael@0: michael@0: LDefinition oldDef = instructions[ins->id()].outputs[i]; michael@0: JS_ASSERT_IF(oldDef.policy() == LDefinition::MUST_REUSE_INPUT, michael@0: *def->output() == *ins->getOperand(oldDef.getReusedInput())); michael@0: } michael@0: michael@0: for (size_t i = 0; i < ins->numTemps(); i++) { michael@0: LDefinition *temp = ins->getTemp(i); michael@0: JS_ASSERT_IF(!temp->isBogusTemp(), temp->output()->isRegister()); michael@0: michael@0: LDefinition oldTemp = instructions[ins->id()].temps[i]; michael@0: JS_ASSERT_IF(oldTemp.policy() == LDefinition::MUST_REUSE_INPUT, michael@0: *temp->output() == *ins->getOperand(oldTemp.getReusedInput())); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Check that the register assignment and move groups preserve the original michael@0: // semantics of the virtual registers. Each virtual register has a single michael@0: // write (owing to the SSA representation), but the allocation may move the michael@0: // written value around between registers and memory locations along michael@0: // different paths through the script. michael@0: // michael@0: // For each use of an allocation, follow the physical value which is read michael@0: // backward through the script, along all paths to the value's virtual michael@0: // register's definition. michael@0: for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) { michael@0: LBlock *block = graph.getBlock(blockIndex); michael@0: for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) { michael@0: LInstruction *ins = *iter; michael@0: const InstructionInfo &info = instructions[ins->id()]; michael@0: michael@0: LSafepoint *safepoint = ins->safepoint(); michael@0: if (safepoint) { michael@0: for (size_t i = 0; i < ins->numTemps(); i++) { michael@0: uint32_t vreg = info.temps[i].virtualRegister(); michael@0: LAllocation *alloc = ins->getTemp(i)->output(); michael@0: if (!checkSafepointAllocation(ins, vreg, *alloc, populateSafepoints)) michael@0: return false; michael@0: } michael@0: JS_ASSERT_IF(ins->isCall() && !populateSafepoints, michael@0: safepoint->liveRegs().empty(true) && michael@0: safepoint->liveRegs().empty(false)); michael@0: } michael@0: michael@0: size_t inputIndex = 0; michael@0: for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) { michael@0: LAllocation oldInput = info.inputs[inputIndex++]; michael@0: if (!oldInput.isUse()) michael@0: continue; michael@0: michael@0: uint32_t vreg = oldInput.toUse()->virtualRegister(); michael@0: michael@0: if (safepoint && !oldInput.toUse()->usedAtStart()) { michael@0: if (!checkSafepointAllocation(ins, vreg, **alloc, populateSafepoints)) michael@0: return false; michael@0: } michael@0: michael@0: // Start checking at the previous instruction, in case this michael@0: // instruction reuses its input register for an output. michael@0: LInstructionReverseIterator riter = block->rbegin(ins); michael@0: riter++; michael@0: checkIntegrity(block, *riter, vreg, **alloc, populateSafepoints); michael@0: michael@0: while (!worklist.empty()) { michael@0: IntegrityItem item = worklist.popCopy(); michael@0: checkIntegrity(item.block, *item.block->rbegin(), item.vreg, item.alloc, populateSafepoints); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AllocationIntegrityState::checkIntegrity(LBlock *block, LInstruction *ins, michael@0: uint32_t vreg, LAllocation alloc, bool populateSafepoints) michael@0: { michael@0: for (LInstructionReverseIterator iter(block->rbegin(ins)); iter != block->rend(); iter++) { michael@0: ins = *iter; michael@0: michael@0: // Follow values through assignments in move groups. All assignments in michael@0: // a move group are considered to happen simultaneously, so stop after michael@0: // the first matching move is found. michael@0: if (ins->isMoveGroup()) { michael@0: LMoveGroup *group = ins->toMoveGroup(); michael@0: for (int i = group->numMoves() - 1; i >= 0; i--) { michael@0: if (*group->getMove(i).to() == alloc) { michael@0: alloc = *group->getMove(i).from(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: const InstructionInfo &info = instructions[ins->id()]; michael@0: michael@0: // Make sure the physical location being tracked is not clobbered by michael@0: // another instruction, and that if the originating vreg definition is michael@0: // found that it is writing to the tracked location. michael@0: michael@0: for (size_t i = 0; i < ins->numDefs(); i++) { michael@0: LDefinition *def = ins->getDef(i); michael@0: if (def->policy() == LDefinition::PASSTHROUGH) michael@0: continue; michael@0: if (info.outputs[i].virtualRegister() == vreg) { michael@0: JS_ASSERT(*def->output() == alloc); michael@0: michael@0: // Found the original definition, done scanning. michael@0: return true; michael@0: } else { michael@0: JS_ASSERT(*def->output() != alloc); michael@0: } michael@0: } michael@0: michael@0: for (size_t i = 0; i < ins->numTemps(); i++) { michael@0: LDefinition *temp = ins->getTemp(i); michael@0: if (!temp->isBogusTemp()) michael@0: JS_ASSERT(*temp->output() != alloc); michael@0: } michael@0: michael@0: if (ins->safepoint()) { michael@0: if (!checkSafepointAllocation(ins, vreg, alloc, populateSafepoints)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Phis are effectless, but change the vreg we are tracking. Check if there michael@0: // is one which produced this vreg. We need to follow back through the phi michael@0: // inputs as it is not guaranteed the register allocator filled in physical michael@0: // allocations for the inputs and outputs of the phis. michael@0: for (size_t i = 0; i < block->numPhis(); i++) { michael@0: const InstructionInfo &info = blocks[block->mir()->id()].phis[i]; michael@0: LPhi *phi = block->getPhi(i); michael@0: if (info.outputs[0].virtualRegister() == vreg) { michael@0: for (size_t j = 0, jend = phi->numOperands(); j < jend; j++) { michael@0: uint32_t newvreg = info.inputs[j].toUse()->virtualRegister(); michael@0: LBlock *predecessor = graph.getBlock(block->mir()->getPredecessor(j)->id()); michael@0: if (!addPredecessor(predecessor, newvreg, alloc)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // No phi which defined the vreg we are tracking, follow back through all michael@0: // predecessors with the existing vreg. michael@0: for (size_t i = 0, iend = block->mir()->numPredecessors(); i < iend; i++) { michael@0: LBlock *predecessor = graph.getBlock(block->mir()->getPredecessor(i)->id()); michael@0: if (!addPredecessor(predecessor, vreg, alloc)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AllocationIntegrityState::checkSafepointAllocation(LInstruction *ins, michael@0: uint32_t vreg, LAllocation alloc, michael@0: bool populateSafepoints) michael@0: { michael@0: LSafepoint *safepoint = ins->safepoint(); michael@0: JS_ASSERT(safepoint); michael@0: michael@0: if (ins->isCall() && alloc.isRegister()) michael@0: return true; michael@0: michael@0: if (alloc.isRegister()) { michael@0: AnyRegister reg = alloc.toRegister(); michael@0: if (populateSafepoints) michael@0: safepoint->addLiveRegister(reg); michael@0: JS_ASSERT(safepoint->liveRegs().has(reg)); michael@0: } michael@0: michael@0: LDefinition::Type type = virtualRegisters[vreg] michael@0: ? virtualRegisters[vreg]->type() michael@0: : LDefinition::GENERAL; michael@0: michael@0: switch (type) { michael@0: case LDefinition::OBJECT: michael@0: if (populateSafepoints) { michael@0: IonSpew(IonSpew_RegAlloc, "Safepoint object v%u i%u %s", michael@0: vreg, ins->id(), alloc.toString()); michael@0: if (!safepoint->addGcPointer(alloc)) michael@0: return false; michael@0: } michael@0: JS_ASSERT(safepoint->hasGcPointer(alloc)); michael@0: break; michael@0: case LDefinition::SLOTS: michael@0: if (populateSafepoints) { michael@0: IonSpew(IonSpew_RegAlloc, "Safepoint slots v%u i%u %s", michael@0: vreg, ins->id(), alloc.toString()); michael@0: if (!safepoint->addSlotsOrElementsPointer(alloc)) michael@0: return false; michael@0: } michael@0: JS_ASSERT(safepoint->hasSlotsOrElementsPointer(alloc)); michael@0: break; michael@0: #ifdef JS_NUNBOX32 michael@0: // Do not assert that safepoint information for nunbox types is complete, michael@0: // as if a vreg for a value's components are copied in multiple places michael@0: // then the safepoint information may not reflect all copies. All copies michael@0: // of payloads must be reflected, however, for generational GC. michael@0: case LDefinition::TYPE: michael@0: if (populateSafepoints) { michael@0: IonSpew(IonSpew_RegAlloc, "Safepoint type v%u i%u %s", michael@0: vreg, ins->id(), alloc.toString()); michael@0: if (!safepoint->addNunboxType(vreg, alloc)) michael@0: return false; michael@0: } michael@0: break; michael@0: case LDefinition::PAYLOAD: michael@0: if (populateSafepoints) { michael@0: IonSpew(IonSpew_RegAlloc, "Safepoint payload v%u i%u %s", michael@0: vreg, ins->id(), alloc.toString()); michael@0: if (!safepoint->addNunboxPayload(vreg, alloc)) michael@0: return false; michael@0: } michael@0: JS_ASSERT(safepoint->hasNunboxPayload(alloc)); michael@0: break; michael@0: #else michael@0: case LDefinition::BOX: michael@0: if (populateSafepoints) { michael@0: IonSpew(IonSpew_RegAlloc, "Safepoint boxed value v%u i%u %s", michael@0: vreg, ins->id(), alloc.toString()); michael@0: if (!safepoint->addBoxedValue(alloc)) michael@0: return false; michael@0: } michael@0: JS_ASSERT(safepoint->hasBoxedValue(alloc)); michael@0: break; michael@0: #endif michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AllocationIntegrityState::addPredecessor(LBlock *block, uint32_t vreg, LAllocation alloc) michael@0: { michael@0: // There is no need to reanalyze if we have already seen this predecessor. michael@0: // We share the seen allocations across analysis of each use, as there will michael@0: // likely be common ground between different uses of the same vreg. michael@0: IntegrityItem item; michael@0: item.block = block; michael@0: item.vreg = vreg; michael@0: item.alloc = alloc; michael@0: item.index = seen.count(); michael@0: michael@0: IntegrityItemSet::AddPtr p = seen.lookupForAdd(item); michael@0: if (p) michael@0: return true; michael@0: if (!seen.add(p, item)) michael@0: return false; michael@0: michael@0: return worklist.append(item); michael@0: } michael@0: michael@0: void michael@0: AllocationIntegrityState::dump() michael@0: { michael@0: #ifdef DEBUG michael@0: fprintf(stderr, "Register Allocation:\n"); michael@0: michael@0: for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) { michael@0: LBlock *block = graph.getBlock(blockIndex); michael@0: MBasicBlock *mir = block->mir(); michael@0: michael@0: fprintf(stderr, "\nBlock %lu", static_cast(blockIndex)); michael@0: for (size_t i = 0; i < mir->numSuccessors(); i++) michael@0: fprintf(stderr, " [successor %u]", mir->getSuccessor(i)->id()); michael@0: fprintf(stderr, "\n"); michael@0: michael@0: for (size_t i = 0; i < block->numPhis(); i++) { michael@0: const InstructionInfo &info = blocks[blockIndex].phis[i]; michael@0: LPhi *phi = block->getPhi(i); michael@0: CodePosition output(phi->id(), CodePosition::OUTPUT); michael@0: michael@0: // Don't print the inputOf for phi nodes, since it's never used. michael@0: fprintf(stderr, "[,%u Phi [def v%u %s] <-", michael@0: output.pos(), michael@0: info.outputs[0].virtualRegister(), michael@0: phi->getDef(0)->output()->toString()); michael@0: for (size_t j = 0; j < phi->numOperands(); j++) michael@0: fprintf(stderr, " %s", info.inputs[j].toString()); michael@0: fprintf(stderr, "]\n"); michael@0: } michael@0: michael@0: for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) { michael@0: LInstruction *ins = *iter; michael@0: const InstructionInfo &info = instructions[ins->id()]; michael@0: michael@0: CodePosition input(ins->id(), CodePosition::INPUT); michael@0: CodePosition output(ins->id(), CodePosition::OUTPUT); michael@0: michael@0: fprintf(stderr, "["); michael@0: if (input != CodePosition::MIN) michael@0: fprintf(stderr, "%u,%u ", input.pos(), output.pos()); michael@0: fprintf(stderr, "%s]", ins->opName()); michael@0: michael@0: if (ins->isMoveGroup()) { michael@0: LMoveGroup *group = ins->toMoveGroup(); michael@0: for (int i = group->numMoves() - 1; i >= 0; i--) { michael@0: // Use two printfs, as LAllocation::toString is not reentant. michael@0: fprintf(stderr, " [%s", group->getMove(i).from()->toString()); michael@0: fprintf(stderr, " -> %s]", group->getMove(i).to()->toString()); michael@0: } michael@0: fprintf(stderr, "\n"); michael@0: continue; michael@0: } michael@0: michael@0: for (size_t i = 0; i < ins->numTemps(); i++) { michael@0: LDefinition *temp = ins->getTemp(i); michael@0: if (!temp->isBogusTemp()) michael@0: fprintf(stderr, " [temp v%u %s]", info.temps[i].virtualRegister(), michael@0: temp->output()->toString()); michael@0: } michael@0: michael@0: for (size_t i = 0; i < ins->numDefs(); i++) { michael@0: LDefinition *def = ins->getDef(i); michael@0: fprintf(stderr, " [def v%u %s]", info.outputs[i].virtualRegister(), michael@0: def->output()->toString()); michael@0: } michael@0: michael@0: size_t index = 0; michael@0: for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) { michael@0: fprintf(stderr, " [use %s", info.inputs[index++].toString()); michael@0: fprintf(stderr, " %s]", alloc->toString()); michael@0: } michael@0: michael@0: fprintf(stderr, "\n"); michael@0: } michael@0: } michael@0: michael@0: fprintf(stderr, "\nIntermediate Allocations:\n\n"); michael@0: michael@0: // Print discovered allocations at the ends of blocks, in the order they michael@0: // were discovered. michael@0: michael@0: Vector seenOrdered; michael@0: seenOrdered.appendN(IntegrityItem(), seen.count()); michael@0: michael@0: for (IntegrityItemSet::Enum iter(seen); !iter.empty(); iter.popFront()) { michael@0: IntegrityItem item = iter.front(); michael@0: seenOrdered[item.index] = item; michael@0: } michael@0: michael@0: for (size_t i = 0; i < seenOrdered.length(); i++) { michael@0: IntegrityItem item = seenOrdered[i]; michael@0: fprintf(stderr, "block %u reg v%u alloc %s\n", michael@0: item.block->mir()->id(), item.vreg, item.alloc.toString()); michael@0: } michael@0: michael@0: fprintf(stderr, "\n"); michael@0: #endif michael@0: } michael@0: michael@0: const CodePosition CodePosition::MAX(UINT_MAX); michael@0: const CodePosition CodePosition::MIN(0); michael@0: michael@0: bool michael@0: RegisterAllocator::init() michael@0: { michael@0: if (!insData.init(mir, graph.numInstructions())) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < graph.numBlocks(); i++) { michael@0: LBlock *block = graph.getBlock(i); michael@0: for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) michael@0: insData[*ins].init(*ins, block); michael@0: for (size_t j = 0; j < block->numPhis(); j++) { michael@0: LPhi *phi = block->getPhi(j); michael@0: insData[phi].init(phi, block); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: LMoveGroup * michael@0: RegisterAllocator::getInputMoveGroup(uint32_t ins) michael@0: { michael@0: InstructionData *data = &insData[ins]; michael@0: JS_ASSERT(!data->ins()->isPhi()); michael@0: JS_ASSERT(!data->ins()->isLabel()); michael@0: michael@0: if (data->inputMoves()) michael@0: return data->inputMoves(); michael@0: michael@0: LMoveGroup *moves = LMoveGroup::New(alloc()); michael@0: data->setInputMoves(moves); michael@0: data->block()->insertBefore(data->ins(), moves); michael@0: michael@0: return moves; michael@0: } michael@0: michael@0: LMoveGroup * michael@0: RegisterAllocator::getMoveGroupAfter(uint32_t ins) michael@0: { michael@0: InstructionData *data = &insData[ins]; michael@0: JS_ASSERT(!data->ins()->isPhi()); michael@0: michael@0: if (data->movesAfter()) michael@0: return data->movesAfter(); michael@0: michael@0: LMoveGroup *moves = LMoveGroup::New(alloc()); michael@0: data->setMovesAfter(moves); michael@0: michael@0: if (data->ins()->isLabel()) michael@0: data->block()->insertAfter(data->block()->getEntryMoveGroup(alloc()), moves); michael@0: else michael@0: data->block()->insertAfter(data->ins(), moves); michael@0: return moves; michael@0: }