blob: c38ff511c8bb2b1b4a8a07269a4ec9a55c21741f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Intel, Konstantin M. Anisimov, Igor V. Chebykin
*
*/
#include "IpfRegisterAllocator.h"
#include "IpfIrPrinter.h"
#include "IpfOpndManager.h"
#include <fstream>
namespace Jitrino {
namespace IPF {
bool greaterSpillCost(RegOpnd *op1, RegOpnd *op2) { return op1->getSpillCost() > op2->getSpillCost(); }
//========================================================================================//
// RegisterAllocator
//========================================================================================//
RegisterAllocator::RegisterAllocator(Cfg &cfg) :
mm(cfg.getMM()),
cfg(cfg),
opndManager(cfg.getOpndManager()),
liveManager(cfg),
allocSet(mm),
liveSet(liveManager.getLiveSet()) {
}
//----------------------------------------------------------------------------------------//
void RegisterAllocator::allocate() {
IPF_LOG << endl << " Build Interference Matrix" << endl;
buildInterferenceMatrix();
removeSelfDep();
IPF_LOG << endl << " Assign Locations (# - cross call site)" << endl;
assignLocations();
IPF_LOG << endl << " Remove Usless \"mov\" Instructions" << endl;
removeSameRegMoves();
}
//----------------------------------------------------------------------------------------//
// 1. for each opnd build list of opnds which alive during live range of the opnd (RegOpnd::depOpnds)
// 2. build list of opnds which need allocation (allocSet)
void RegisterAllocator::buildInterferenceMatrix() {
NodeVector &nodes = cfg.search(SEARCH_POST_ORDER);
for(uint16 i=0; i<nodes.size(); i++) { // iterate through CFG nodes
if(nodes[i]->isBb() == false) continue; // ignore non BB nodes
BbNode *node = (BbNode *)nodes[i];
liveManager.init(node);
U_32 execCounter = node->getExecCounter();
InstIterator currInst = node->getInsts().end()-1;
InstIterator firstInst = node->getInsts().begin()-1;
for (; currInst>firstInst; currInst--) {
Inst *inst = *currInst;
uint16 numDst = inst->getNumDst(); // number of dst opnds (qp has index 0)
OpndVector& opnds = inst->getOpnds(); // get inst's opnds
RegOpnd *qp = (RegOpnd *) opnds[0];
QpMask mask = liveManager.getLiveMask(qp);
checkCoalescing(execCounter, inst);
liveManager.def(inst); // remove dst opnds from live set
checkCallSite(inst, mask); // if currInst is "call" - all alive opnds cross call site
for(uint16 i=1; i<=numDst; i++) { // for each dst opnd
updateAllocSet(opnds[i], execCounter, mask); // insert in allocSet and add live set in dep list
}
liveManager.use(inst); // add src opnds in live set
updateAllocSet(opnds[0], execCounter, mask); // insert in allocSet pq opnd and add alive qps in dep list
for (uint16 i=numDst+1; i<opnds.size(); i++) { // for each src opnd
updateAllocSet(opnds[i], execCounter, mask); // insert in allocSet and add live set in dep list
}
}
}
}
//----------------------------------------------------------------------------------------//
void RegisterAllocator::checkCoalescing(U_32 execCounter, Inst *inst) {
if (inst->getInstCode() != INST_MOV) return; // if inst is not "mov" - ignore
Opnd *qp = inst->getOpnd(0); // get dst opnd of mov inst
Opnd *dst = inst->getOpnd(1); // get dst opnd of mov inst
Opnd *src = inst->getOpnd(2); // get src opnd of mov inst
if (qp->getValue() != 0) return; // if it is not p0 - ignore
if (dst->getOpndKind() != src->getOpndKind()) return; // if opnds have different reg types - ignore
if (src->isConstant() == true) return; // if src is constant (r0, f1 ...) - ignore
RegOpnd *dst_ = (RegOpnd *)dst;
RegOpnd *src_ = (RegOpnd *)src;
dst_->addCoalesceCand(execCounter, src_);
src_->addCoalesceCand(execCounter, dst_);
}
//----------------------------------------------------------------------------------------//
// 1. Remove opnd dependency on itself
// 2. Log out opnd dependencis
void RegisterAllocator::removeSelfDep() {
for (RegOpndSetIterator it=allocSet.begin(); it!=allocSet.end(); it++) {
(*it)->getDepOpnds().erase(*it);
}
if (LOG_ON) {
IPF_LOG << endl << " Opnd dependensies" << endl;
for (RegOpndSetIterator it=allocSet.begin(); it!=allocSet.end(); it++) {
RegOpnd *opnd = *it;
IPF_LOG << " " << setw(4) << left << IrPrinter::toString(opnd);
IPF_LOG << " depends on: " << IrPrinter::toString(opnd->getDepOpnds()) << endl;
}
}
}
//----------------------------------------------------------------------------------------//
// 1. Sort opnd list by Spill Cost
// 2. Assign locations to all not allocated opnds
void RegisterAllocator::assignLocations() {
RegOpndVector opndVector(mm);
opndVector.insert(opndVector.begin(), allocSet.begin(), allocSet.end()); // create vector of opns to be allocated
sort(opndVector.begin(), opndVector.end(), greaterSpillCost); // sort them by Spill Cost
for (uint16 i=0; i<opndVector.size(); i++) {
RegOpnd *opnd = opndVector[i];
if (opnd->getLocation() != LOCATION_INVALID) continue; // opnd has already had location
IPF_LOG << " " << left << setw(5) << IrPrinter::toString(opnd);
IPF_LOG << (opnd->isCrossCallSite() ? "#" : " ");
assignLocation(opnd); // assign location for current opnd
IPF_LOG << " after assignment " << left << setw(5) << IrPrinter::toString(opnd);
IPF_LOG << " spill cost: " << opnd->getSpillCost() << endl;
}
}
//----------------------------------------------------------------------------------------//
// remove useless move insts. Like this "mov r8 = r8"
void RegisterAllocator::removeSameRegMoves() {
NodeVector &nodes = cfg.search(SEARCH_POST_ORDER);
for(uint16 i=0; i<nodes.size(); i++) { // iterate through CFG nodes
if(nodes[i]->isBb() == false) continue; // ignore non BB nodes
BbNode *node = (BbNode *)nodes[i];
InstVector &insts = node->getInsts();
for (InstVector::iterator it=insts.begin(); it!=insts.end();) {
Inst *inst = *it;
if (inst->getInstCode() != INST_MOV) { it++; continue; }
Opnd *dst = inst->getOpnd(1); // get dst opnd of mov inst
Opnd *src = inst->getOpnd(2); // get src opnd of mov inst
if (dst->getOpndKind() != src->getOpndKind()) { it++; continue; } // if opnds have different reg types - ignore
if (dst->getValue() != src->getValue()) { it++; continue; } // if opnds allocated on different regs - ignore
it = insts.erase(it);
IPF_LOG << " node" << left << setw(4) << node->getId() << IrPrinter::toString(inst) << endl;
}
}
}
//----------------------------------------------------------------------------------------//
// find and assign location for target opnd
void RegisterAllocator::assignLocation(RegOpnd *target) {
OpndKind opndKind = target->getOpndKind();
DataKind dataKind = target->getDataKind();
bool isPreserved = target->isCrossCallSite();
RegBitSet usedMask; // mask of regs which already used by dep opnds
RegOpndSet &depOpnds = target->getDepOpnds(); // target can not be assigned on reg used by depOpnds
for (RegOpndSet::iterator it=depOpnds.begin(); it!=depOpnds.end(); it++) {
RegOpnd *opnd = *it;
I_32 location = opnd->getLocation(); // get location of dep opnd
if (location >= NUM_G_REG) continue; // if opnd is not assigned on reg - continue
usedMask[location] = true; // mark reg busy
}
Int2OpndMap &coalesceCands = target->getCoalesceCands(); // opnds used in inst like: move target = opnd
for (Int2OpndMap::iterator it=coalesceCands.begin(); it!=coalesceCands.end(); it++) {
RegOpnd *cls = it->second;
I_32 location = cls->getValue(); // get location of coalesce candidate
if (location > NUM_G_REG) continue; // opnd is not allocated (or allocated on stack)
if (isPreserved && !cls->isCrossCallSite()) continue; // target must be preserved, but cls is scratch
if (usedMask[location] == true) continue; // target can not be allocated on cls location
target->setLocation(location); // assign target new location
return;
}
I_32 location = opndManager->newLocation(opndKind, dataKind, usedMask, isPreserved);
target->setLocation(location); // assign target new location
}
//----------------------------------------------------------------------------------------//
void RegisterAllocator::updateAllocSet(Opnd *cand_, U_32 execCounter, QpMask mask) {
if (cand_->isReg() == false) return; // imm - it does not need allocation
if (cand_->isMem() == true) return; // mem stack - it does not need allocation
if (cand_->isConstant() == true) return; // constant - it does not need allocation
RegOpnd *cand = (RegOpnd *)cand_;
cand->incSpillCost(execCounter); // increase opnd spill cost
allocSet.insert(cand); // isert opnd in list for allocation
// add current live set in opnd dep list (they must be placed on different regs)
for (RegOpndSetIterator it=liveSet.begin(); it!=liveSet.end(); it++) {
RegOpnd *opnd = *it;
if (opnd->isAlive(mask) == false) continue;
cand->insertDepOpnd(opnd); // cand depends on curr opnd from live set
opnd->insertDepOpnd(cand); // curr opnd from live set depends on cand
}
}
//----------------------------------------------------------------------------------------//
// Check if current inst is "call" and mark all opnds in liveSet as crossing call site
void RegisterAllocator::checkCallSite(Inst *inst, QpMask mask) {
if(inst->isCall() == false) return; // it is not call site
for(RegOpndSet::iterator it=liveSet.begin(); it!=liveSet.end(); it++) {
RegOpnd *opnd = *it;
if (opnd->isAlive(mask) == false) continue;
opnd->setCrossCallSite(true);
}
}
} // IPF
} // Jitrino