blob: 5f88b7ea31a0b1ad8c85320bbfa191e72c47eb34 [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 Sergey L. Ivashin
*/
#include "Ia32IRManager.h"
#include "Ia32RegAllocCheck.h"
#include "XTimer.h"
#include "Counter.h"
#include "Stl.h"
#ifdef PLATFORM_POSIX
#include <limits.h>
#endif
#ifdef _DEBUG_SPILLGEN
#include "Ia32SpillGenDbg.h"
#endif //#ifdef _DEBUG_SPILLGEN
namespace Jitrino
{
namespace Ia32
{
static const char* help =
" regs=ALL\n"
" regs=<comma-separated list of available registers, e.g EAX,ECX,XMM0,>\n"
;
struct SpillGen : public SessionAction
{
// Inter-blocks data
// -----------------
typedef U_32 RegMask;
typedef StlVector<Opnd*> Opnds;
MemoryManager mm; // this is private MemoryManager, not irm.getMemoryManager()
// Table of all available registers sets.
// Each set (GPReg, FPReg, XMMReg and so on) is represented by the corresponding
// Constraint object.
struct Registers : public StlVector<Constraint>
{
Registers (MemoryManager& mm) :StlVector<Constraint>(mm) {}
void parse (const char*);
// clear mask fields in all elements of the table
void clearMasks ();
void merge (const Registers&);
// register the new constraint (register) in the table.
// if table doesn't contain constraint of the specified kind, it will be ignored
// (add = false) or new table entry for the constraint will be created (add = true).
int merge (const Constraint&, bool add = false);
// returns table index of the constraint of the specified kind or -1
int getIndex (const Constraint&) const;
// returns pointer to the table element of the specified kind
Constraint* contains (const Constraint&);
int indexes[IRMaxRegKinds];
};
Registers registers;
size_t opndcount; // initial count of operands
size_t emitted; // extra instructions count
size_t fails; // if != 0, then SpillGen should be aborted
// mapping operand -> its memory location
// every operand that was splitted to memory is registered in this table
typedef ::std::pair<const Opnd*, Opnd*> TwoOpnds;
typedef StlVector<TwoOpnds> MemOpnds;
MemOpnds memopnds;
// Block-specific data
// -------------------
enum
{
RoleDef = 1,
RoleUse = 2,
RoleCond = 4, // if (RoleCond == true) RoleUse = true;
RoleEnd = 8
};
typedef char OpRole;
// extension of 'Inst' structure with some specific data
struct Instx
{
Instx () : nbopnds(0), evicts(0) {}
Inst* inst; // pointer to the corresponding instruction
size_t nbopnds; // max number of operands in instruction
OpRole* oproles; // array of nbopnds size
Opnds* evicts; // can be null, can contain null pointers
RegMask regusage[IRMaxRegKinds];// masks of all registers that are busy at this instruction
RegMask regpress[IRMaxRegKinds];
};
typedef StlVector<Instx> Instxs;
Instxs instxs;
// created for all problem (needed to be allocated) operands
struct Op
{
Instx* instx;
OpRole oprole;
};
struct Opline
{
Opline () : ops(0) {}
void clear (MemoryManager&);
void addOp (Instx*, OpRole);
// iterator methods, work with the current instruction only
void start ();
void forw ();
void back (const Instx*);
bool go () const {return op != 0;}
bool isDef () const {return op->instx == instx && (op->oprole & RoleDef) != 0;}
bool isUse () const {return op->instx == instx && (op->oprole & RoleUse) != 0;}
bool isEnd () const {return op->instx == instx && (op->oprole & RoleEnd) != 0;}
bool isProc() const {return op->instx == instx;}
// work with the specified instruction
bool isDef (const Inst*) const;
bool isUse (const Inst*) const;
bool at_start, // 'true' if this operand lives at the BB entry
at_exit, // 'true' if this operand lives at the BB exit
catched; // 'true' if this operand lives at entry to dispatch node
int idx; // -1 or index of the corresponding constraint in 'registers' array
Opnd* opnd; // original form of operand
Opnd* opnd_mem; // 0 or memory form of the operand (cached copy of the value from 'memopnds' array)
Instx* save_instx; // 0 or point where instruction to save operand should be inserted
bool save_before; // true if save must be before instruction (false is the default)
Opnd* save_opnd; // which operand should be saved
RegMask save_regmsk;
int save_regidx;
bool save_changed;
typedef StlVector<Op> Ops;
Ops* ops; // array of all instructions that change/use value of the operand
// current instruction
Op* op;
Instx* instx;
static bool smaller (const Opline& x, const Opline& y) {return x.weight < y.weight;}
Constraint initial;
int weight;
};
typedef StlVector<Opline> Oplines;
Oplines actives;
StlVector<Opline*> actsmap;
struct Evict;
typedef StlVector<Evict> Evicts;
// current context
Node* bblock; // the basic block being processed
BitSet* lives_start, // liveness info for start and end of the block
* lives_exit; //
BitSet* lives_catch; // 0 or mask of operands live at the corresponding dispatch node
bool evicts_known;
// Methods
// -------
SpillGen () : mm("SpillGen")
, registers(mm), memopnds(mm)
, instxs(mm), actives(mm), actsmap(mm)
, lives_start(0), lives_exit(0)
{}
U_32 getNeedInfo () const {return NeedInfo_LivenessInfo;}
U_32 getSideEffects () const {return SideEffect_InvalidatesLivenessInfo;}
void runImpl();
bool verify(bool force=false);
size_t pass0 ();
size_t pass1 ();
RegMask lookPreffered (Opline&);
bool tryRegister (Opline&, Constraint, RegMask);
bool tryMemory (Opline&, Constraint);
bool tryEvict (Opline&, Constraint);
bool tryRepair (Opline&, Constraint);
bool simplify (Inst*, Opnd*);
RegMask usedRegs (const Instx*, int idx, bool isuse) const;
RegMask callRegs (const Instx*, int idx) const;
int update (const Inst*, const Opnd*, Constraint&) const;
void assignReg (Opline&, Instx* begx, Instx* endx, RegName);
void assignMem (Opline&, Instx* begx, Instx* endx);
void saveOpnd (Opline&, Instx*, Opnd*);
void saveOpnd (Opline&, Instx*, Opnd*, RegMask, int, bool);
bool loadOpnd (Opline&, Instx*, Opnd*);
void loadOpndMem (Opline&);
Opnd* opndMem (Opline&);
Opnd* opndMem (const Opnd*);
Opnd* opndReg (const Opnd*, RegName) const;
void emitPushPop (bool before, Inst*, Opnd* opnd, bool push);
Inst* emitMove (bool before, Inst*, Opnd* dst, Opnd* src);
RegName findFree (RegMask usable, int idx, Instx* begx);
RegName findEvict (RegMask usable, int idx, Instx* begx, Instx*& endx);
void setupEvicts ();
Evict* pickEvict (Evicts&);
bool isEvict (const Opnd*, const Instx*) const;
void killEvict (const Opnd*, Instx*) const;
RegMask getRegMaskConstr (const Opnd*, Constraint) const;
};
static ActionFactory<SpillGen> _spillgen("spillgen", help);
#ifdef _DEBUG_SPILLGEN
#ifdef _MSC_VER
#pragma warning(disable : 4505) //unreferenced local function has been removed
#else
#endif //#ifdef _MSC_VER
#include "Ia32SpillGenDbgHead.h"
#else
#define DBGOUT(s)
#endif //#ifdef _DEBUG_SPILLGEN
// Utility class for (zero-terminated) strings parsing
//
class Tokens
{
public:
Tokens (const char* s) :src(s) {;}
void init (const char* s) {src = s;}
bool scan ();
bool isWord () const {return isw;}
const char* lex () const {return buff;}
protected:
const char* src;
char* dst;
char buff[64];
bool isw;
};
// Return 'false' if end of parsing string is reached and 'true' otherwise.
// isWord() will specify was it word or separator (one character).
// lex() will return zero-terminated lexem string scanned (for word or separator).
//
bool Tokens::scan ()
{
while (isspace(*src))
src++;
if (*src == 0)
return false;
dst = buff;
isw = isalnum(*src) != 0;
if (!isw)
*dst++ = *src++;
else
while (isalnum(*src))
{
assert(dst != &buff[sizeof(buff)-1]);
*dst++ = *src++;
}
*dst = 0;
return true;
}
const int MAXREGS = IRMaxRegNamesSameKind*IRMaxRegKinds;
// Temporary structure to describe operand that can be evicted.
// Used to choose the best operand to evict.
//
struct SpillGen::Evict
{
Opnd* opnd;
Instx* begx, * endx;
int weight;
bool operator < (const Evict& x) const {return weight < x.weight;}
};
//// Misc helpers
///// ----------------------
static size_t bitCount (SpillGen::RegMask mk)
{
size_t count = 0;
while (mk != 0)
{
if ((mk & 1) != 0)
++count;
mk >>= 1;
}
return count;
}
static void merge (Constraint& c, SpillGen::RegMask mk)
{
c.setMask(c.getMask() | mk);
}
// Parse input parameters (registers available) and build table of the regsiters
// available for allocalion ('registers').
//
void SpillGen::Registers::parse (const char* params)
{
if (params == 0 || strcmp(params, "ALL") == 0)
{
#ifdef HYX86_64
push_back(Constraint(RegName_R8)
|Constraint(RegName_RAX)
|Constraint(RegName_RCX)
|Constraint(RegName_RBX)
|Constraint(RegName_RDX)
|Constraint(RegName_RSI)
|Constraint(RegName_RDI)
|Constraint(RegName_R9)
|Constraint(RegName_R10)
|Constraint(RegName_R11)
|Constraint(RegName_R12));
#else
push_back(Constraint(RegName_EAX)
|Constraint(RegName_ECX)
|Constraint(RegName_EDX)
|Constraint(RegName_EBX)
|Constraint(RegName_ESI)
|Constraint(RegName_EDI)
|Constraint(RegName_EBP));
#endif
push_back(Constraint(RegName_XMM1)
|Constraint(RegName_XMM0)
|Constraint(RegName_XMM2)
|Constraint(RegName_XMM3)
|Constraint(RegName_XMM4)
|Constraint(RegName_XMM5)
|Constraint(RegName_XMM6)
|Constraint(RegName_XMM7));
push_back(Constraint(RegName_FP0));
}
else
{
Constraint c;
for (Tokens t(params); t.scan(); )
if (t.isWord())
{
RegName r = getRegName(t.lex());
if (r != RegName_Null)
c = Constraint(r);
merge(c, true);
}
}
assert(!empty());
for (size_t i = 0; i != IRMaxRegKinds; ++i)
indexes[i] = -1;
for (size_t i = 0; i != size(); ++i)
indexes[operator[](i).getKind()] = (int)i;
}
void SpillGen::Registers::clearMasks ()
{
for (size_t i = 0; i != size(); ++i)
operator[](i).setMask(0);
}
void SpillGen::Registers::merge (const SpillGen::Registers& x)
{
for (size_t i = 0; i != size(); ++i)
{
Constraint& r = operator[](i);
r.setMask(r.getMask() | x[i].getMask());
}
}
int SpillGen::Registers::merge (const Constraint& c, bool add)
{
if (c.getMask() != 0)
{
for (size_t i = 0; i != size(); ++i)
{
Constraint& r = operator[](i);
if (r.getKind() == c.getKind())
{
r.setMask(r.getMask() | c.getMask());
return (int)i;
}
}
if (add)
push_back(c);
}
return -1;
}
int SpillGen::Registers::getIndex (const Constraint& c) const
{
return indexes[c.getKind() & OpndKind_Reg];
}
Constraint* SpillGen::Registers::contains (const Constraint& c)
{
for (size_t i = 0; i != size(); ++i)
{
Constraint& r = operator[](i);
if (r.getKind() == c.getKind() && (r.getMask() & c.getMask()) != 0)
return &r;
}
return 0;
}
void SpillGen::Opline::clear (MemoryManager& mm)
{
if (ops == 0)
ops = new (mm) Ops(mm);
else
ops->clear();
opnd_mem = 0;
save_instx = 0;
}
void SpillGen::Opline::addOp (Instx* instx, OpRole oprole)
{
if (ops->empty() || ops->back().instx != instx)
{
Op tmp;
tmp.instx = instx;
tmp.oprole = oprole;
ops->push_back(tmp);
}
else
{
ops->back().oprole |= oprole;
}
op = 0;
}
void SpillGen::Opline::start ()
{
assert(!ops->empty());
op = &ops->back();
instx = op->instx;
}
void SpillGen::Opline::forw ()
{
assert(op != 0);
assert(!ops->empty());
if (op == &ops->front())
{
op = 0;
return;
}
assert(instx != ops->front().instx);
++instx;
Op* opx = op - 1;
if (opx->instx == instx)
op = opx;
}
void SpillGen::Opline::back (const Instx* x)
{
if (op == 0)
{
assert(!ops->empty());
op = &ops->front();
instx = op->instx;
}
while (instx != x)
{
assert(instx != ops->back().instx);
if (op->instx == instx)
++op;
--instx;
}
}
static bool operator == (const SpillGen::Op& op, const Inst* i)
{
return op.instx->inst == i;
}
bool SpillGen::Opline::isDef (const Inst* i) const
{
Ops::iterator end, ptr = find(ops->begin(), end = ops->end(), i);
return ptr != end && (ptr->oprole & RoleDef) != 0;
}
bool SpillGen::Opline::isUse (const Inst* i) const
{
Ops::iterator end, ptr = find(ops->begin(), end = ops->end(), i);
return ptr != end && (ptr->oprole & RoleUse) != 0;
}
///// SpillGen
///// ----------------------
void SpillGen::runImpl()
{
#ifdef _DEBUG_SPILLGEN
onConstruct(*this);
#endif
const bool* spill_flag = static_cast<const bool*>(getIRManager().getInfo("SpillGen"));
if (spill_flag != 0 && !*spill_flag)
{
DBGOUT("SpillGen skipped" << endl;);
return;
}
registers.parse(getArg("regs"));
DBGOUT("parameters: " << registers << endl;)
irManager->resetOpndConstraints();
fails = 0;
emitted = 0;
opndcount = irManager->getOpndCount();
lives_exit = new (mm) BitSet(mm, (U_32)opndcount);
actsmap.resize(opndcount);
for (size_t i = 0; i < opndcount; ++i)
actsmap[i] = 0;
const Nodes& nodes = irManager->getFlowGraph()->getNodes();
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
if (node->isBlockNode())
{
bblock = node;
if (bblock->isEmpty()) {
continue;
}
lives_start = irManager->getLiveAtEntry(bblock);
lives_exit->resize((U_32)opndcount);
irManager->getLiveAtExit(bblock, *lives_exit);
size_t bfails = 0;
for (;;)
{
if (pass0() == 0)
break;
#ifdef _DEBUG_SPILLGEN
if (Log::isEnabled())
outInstxs(Log::out(), *this);
#endif
size_t x;
if ((x = pass1()) == 0 || x == bfails)
{
bfails = x;
if (x != 0 && Log::isEnabled())
Log::out() << "SpillGen cannot assign operand(s)" << std::endl;
break;
}
bfails = x;
DBGOUT(endl << "reiterating BB#"<< bblock->getId() << endl;)
if (opndcount != irManager->getOpndCount())
{
size_t oldcount = opndcount;
opndcount = irManager->getOpndCount();
lives_exit->resize((U_32)opndcount);
irManager->fixLivenessInfo();
actsmap.resize(opndcount);
for (; oldcount < opndcount; ++oldcount)
actsmap[oldcount] = 0;
}
}
fails += bfails;
}
}
static Counter<size_t> count_emits("ia32:spillgen:emits", 0);
count_emits += emitted;
#ifdef _DEBUG_SPILLGEN
DBGOUT(endl << "Emitted movs :" << emitted << endl);
if (fails)
DBGOUT(endl << "FAILS: " << fails << endl);
#endif
assert(fails == 0);
if (fails != 0)
Jitrino::crash("SpillGen failed");
#ifdef _DEBUG_SPILLGEN
onDestruct(*this);
#endif
}
bool SpillGen::verify (bool force)
{
bool failed = false;
if (force || getVerificationLevel() >= 1)
{
irManager->packOpnds();
irManager->updateLivenessInfo();
if (!RegAllocCheck(*irManager).run(true))
failed = true;
if (!SessionAction::verify(true))
failed = true;
}
return !failed;
}
// Preprocessing phase.
// Scans all instructions in the basic block and create 'Instx' extension structure for
// every instruction with required information.
// Identifies all operands that needed to be allocated (problem operands) and fill 'Opline'
// structure for each.
//
size_t SpillGen::pass0 ()
{
static CountTime pass0Timer("ia32::spillgen:pass0");
AutoTimer tm(pass0Timer);
actives.resize(0);
evicts_known = false;
const size_t regkinds = registers.size();
assert(regkinds <= IRMaxRegKinds);
instxs.resize(bblock->getInstCount() + 1);
instxs[0].inst = 0;
if (instxs[0].evicts == 0)
instxs[0].evicts = new (mm) Opnds(mm);
else
instxs[0].evicts->resize(0);
Instxs::iterator instxp = instxs.end() - 1;
for (size_t i = 0; i < regkinds; ++i)
instxp->regusage [i] = 0;
#ifdef _DEBUG_SPILLGEN
DBGOUT(endl << "BB#"<< bblock->getId());
if (!instxs.empty())
DBGOUT (" [" << *(Inst*)bblock->getFirstInst() << " - " << *(Inst*)bblock->getLastInst() << "] ");
DBGOUT(instxs.size() - 1 << endl);
#endif
// calculate registers used at the block exit
BitSet lives_next = *lives_exit;
BitSet::IterB ib(*lives_exit);
for (int i = ib.getNext(); i != -1; i = ib.getNext())
{
Constraint loc = irManager->getOpnd(i)->getConstraint(Opnd::ConstraintKind_Location, OpndSize_Default);
int idx = registers.getIndex(loc);
if (!loc.isNull() && idx != -1)
{// this operand is already assigned to some location/register
instxp->regusage[idx] |= loc.getMask();
}
}
// iterate over instructions towards the top of the block
for (Inst* inst = (Inst*)bblock->getLastInst(); inst != 0; inst = inst->getPrevInst())
{
// Prepare processing of the next instruction
Instxs::iterator instxq = instxp - 1;
for (size_t i = 0; i < regkinds; ++i)
instxq->regusage [i] = instxp->regusage[i];
// Process current instruction
instxp->inst = inst;
size_t nbopnds = inst->getOpndCount(Inst::OpndRole_All);
if (instxp->nbopnds < nbopnds)
instxp->oproles = new (mm) OpRole[instxp->nbopnds = (nbopnds + 8) & ~7];
for (size_t i = 0; i < regkinds; ++i)
instxp->regpress[i] = 0;
Opnd* opnd;
size_t itx = 0;
Inst::Opnds opnds(inst, Inst::OpndRole_All);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it), ++itx)
{
opnd = inst->getOpnd(it);
U_32 roles = inst->getOpndRoles(it);
OpRole oprole = 0;
if ((roles & Inst::OpndRole_Def) != 0)
oprole |= RoleDef;
if ((roles & Inst::OpndRole_Use) != 0 || (inst->getProperties() & Inst::Properties_Conditional) != 0)
oprole |= RoleUse;
if ((inst->getProperties() & Inst::Properties_Conditional) != 0)
oprole |= RoleCond;
if (!lives_next.getBit(opnd->getId()))
oprole |= RoleEnd;
assert(itx < instxp->nbopnds);
instxp->oproles[itx] = oprole;
Constraint loc = opnd->getConstraint(Opnd::ConstraintKind_Location, OpndSize_Default);
if (!loc.isNull())
{// this operand is already assigned to some location/register
int idx;
if ((idx = registers.getIndex(loc)) != -1)
{
RegMask msk = loc.getMask();
assert(msk != 0);
RegMask was = instxp->regusage[idx];
instxq->regusage [idx] |= msk;
if ((oprole & RoleUse) != 0)
{
}
else if ((oprole & (RoleDef | RoleCond)) == RoleDef)
{
instxq->regusage[idx] &= ~msk;
if ((was & msk) == 0)
{
DBGOUT("dead def " << *inst << " " << *opnd << endl;)
instxp->regusage [idx] |= msk;
}
}
}
}
else
{// this operand is not allocated yet
unsigned mask = (1<<inst->getOpndCount())-1;
loc = ((Inst*)inst)->getConstraint(it, mask, OpndSize_Default);
int idx = registers.getIndex(loc);
Opline* opline = actsmap[opnd->getId()];
if (opline == 0)
{// Is this is a problem operand?
if (!(loc & Constraint(OpndKind_Memory)).isNull() || idx != -1)
{// operand can be assigned to memory or register
actives.resize(actives.size() + 1);
opline = &actives.back();
actsmap[opnd->getId()] = opline;
opline->clear(mm);
opline->opnd = opnd;
opline->at_start = lives_start->getBit(opnd->getId());
opline->at_exit = lives_exit->getBit(opnd->getId());
opline->initial = loc;
}
else
{
DBGOUT("Cannot assign " << *opnd << " @ " << *opline->instx->inst << endl;)
}
}
else
{
opline->initial.intersectWith(loc);
}
// this operand is tracked
if (opline != 0)
{
opline->addOp(&*instxp, oprole);
if ((loc & Constraint(OpndKind_Memory)).isNull())
{// Operand cannot assigned to memory
assert(idx != -1);
if (bitCount(loc.getMask()) == 1)
instxp->regpress[idx] |= loc.getMask();
}
}
}
if (inst->isLiveRangeEnd(it))
lives_next.setBit(opnd->getId(), false);
else if (inst->isLiveRangeStart(it))
lives_next.setBit(opnd->getId(), true);
}
instxp = instxq;
}
assert(instxp == instxs.begin());
return actives.size();
}
// Allocation phase.
// Allocate all problem operands for the current block.
//
size_t SpillGen::pass1 ()
{
static CountTime pass1Timer("ia32::spillgen:pass1");
AutoTimer tm(pass1Timer);
size_t fails = 0;
if (actives.size() > 1)
{
for (Oplines::iterator it = actives.begin(); it != actives.end(); ++it)
{
Opline& opline = *it;
opline.weight = (int)bitCount(opline.initial.getMask());
}
sort(actives.begin(), actives.end(), Opline::smaller);
}
lives_catch = 0;
Node* node = bblock->getExceptionEdgeTarget();
if (node != NULL)
if ((lives_catch = irManager->getLiveAtEntry(node))->isEmpty())
lives_catch = 0;
for (Oplines::reverse_iterator it = actives.rbegin(); it != actives.rend(); ++it)
{
Opline& opline = *it;
if (actsmap[opline.opnd->getId()] == 0)
continue;
actsmap[opline.opnd->getId()] = 0;
#ifdef _DEBUG_SPILLGEN
opruns.incr(opline.ops->size());
#endif
opline.opnd_mem = 0;
opline.save_instx = 0;
opline.save_changed = false;
opline.start();
// Is this operand lives at the corresponding Dispatch node entry ?
opline.catched = (lives_catch == 0) ? false : lives_catch->getBit(opline.opnd->getId());
DBGOUT(opline;)
// Begin-block processing
RegMask prefreg = 0;
// Is this operand lives at entry of this node ?
if (opline.at_start)
saveOpnd(opline, opline.instx, opndMem(opline));
else
prefreg = lookPreffered(opline);
// Process instructions that are using the operand
while (opline.go())
if (opline.isProc())
{
Constraint c(opline.opnd->getConstraint(Opnd::ConstraintKind_Initial));
update(opline.instx->inst, opline.opnd, c);
opline.idx = registers.getIndex(c);
if (!tryRegister(opline, c, prefreg))
if (!tryMemory(opline, c))
if (!tryEvict(opline, c))
if (!tryRepair(opline, c))
{// Cannot assign operand, so let it remains in the original form
DBGOUT("Cannot assign " << opline.opnd << " @ " << *opline.instx->inst << endl;)
++fails;
if (simplify(opline.instx->inst, opline.opnd))
{
loadOpndMem(opline);
break;
}
loadOpnd(opline, opline.instx, opline.opnd);
if (opline.isDef())
opline.save_changed = true;
saveOpnd(opline, opline.instx, opline.opnd);
opline.forw();
}
}
else
{
opline.forw();
}
// End-block processing
//assert(opline.instx == opline.ops->front().instx);
if (opline.at_exit)
loadOpndMem(opline);
}
return fails;
}
SpillGen::RegMask SpillGen::lookPreffered (Opline& opline)
{
Inst* inst = opline.instx->inst;
if (inst->getMnemonic() != Mnemonic_MOV || inst->getOpnd(0) != opline.opnd)
return 0;
if ((opline.instx->oproles[1] & RoleEnd) == 0)
return 0;
Opnd* opnd1 = inst->getOpnd(1);
RegMask msk;
Constraint loc = opnd1->getConstraint(Opnd::ConstraintKind_Location, OpndSize_Default);
if (!loc.isNull() && registers.getIndex(loc) != -1)
msk = loc.getMask();
else
return 0;
DBGOUT("COALESCE at " << *inst << " " << *opnd1 << endl;)
return msk;
}
// Attempt to allocate a register for the operand.
//
bool SpillGen::tryRegister (Opline& opline, Constraint c, RegMask prefreg)
{
if (opline.idx == -1)
return false;
Constraint cx = c.getAliasConstraint(OpndSize_Default) & registers[opline.idx];
Constraint cr((OpndKind)cx.getKind(), c.getSize(), cx.getMask());
// handle first instruction of the interval
RegMask mk = cr.getMask() & ~usedRegs(opline.instx, opline.idx, opline.isUse());
if (mk == 0)
{
DBGOUT(" -No reg [" << *opline.instx->inst << "]" << c << endl;)
return false;
}
RegMask mkpost = ~(RegMask)0;
if (!opline.isDef())
mkpost = callRegs(opline.instx, opline.idx);
// handle second and all others instructions
Instx* begx = opline.instx;
Instx* endx = begx;
int count = 0;
for (opline.forw(); opline.go(); opline.forw())
{
RegMask tmp = mk & mkpost;
if (opline.isEnd())
tmp &= ~opline.instx->regusage[opline.idx-1];
else
tmp &= ~opline.instx->regusage[opline.idx];
if (tmp == 0)
break;
int cnt = 0;
if (opline.isProc())
{
if (opline.isDef() && !opline.isUse())
break;
if ((cnt = update(opline.instx->inst, opline.opnd, cr)) != 0)
{
tmp &= cr.getMask();
if (tmp == 0)
break;
endx = opline.instx;
}
}
mk = tmp;
mkpost = callRegs(opline.instx, opline.idx);
count += cnt;
}
DBGOUT(" -reg [" << *begx->inst << " - " << *endx->inst
<< "] avail:" << RegMasks(cx, mk) << " count:" << count << endl;)
if (count == 0)
{
assert(begx == endx);
Constraint cm = c & OpndKind_Memory;
if (!cm.isNull())
{
DBGOUT(" -mem [" << *begx->inst << "]" << endl;)
assignMem(opline, begx, begx);
return true;
}
}
if ((mk & prefreg) != 0)
{
mk &= prefreg;
static Counter<size_t> count_preffer("ia32:spillgen:preffered", 0);
count_preffer++;
}
RegName rn = findFree(mk, opline.idx, begx);
assignReg(opline, begx, endx, rn);
return true;
}
// Attempt to assign operand to memory.
//
bool SpillGen::tryMemory (Opline& opline, Constraint c)
{
c.intersectWith(OpndKind_Memory);
if (c.isNull())
{
DBGOUT(" -No mem [" << *opline.instx->inst << "]" << endl;)
return false;
}
DBGOUT(" -mem [" << *opline.instx->inst << "]" << endl;)
assignMem(opline, opline.instx, opline.instx);
opline.forw();
return true;
}
// Attempt to find other operand that can be temporary saved to memory and use it's register
// for the operand processed.
//
bool SpillGen::tryEvict (Opline& opline, Constraint c)
{
if (opline.idx == -1)
return false;
Constraint cx = c.getAliasConstraint(OpndSize_Default) & registers[opline.idx];
Constraint cr((OpndKind)cx.getKind(), c.getSize(), cx.getMask());
Instx* begx = opline.instx;
Instx* endx = begx;
for (opline.forw(); opline.go(); opline.forw())
{
Constraint cx = cr;
if (update(opline.instx->inst, opline.opnd, cx))
if (cx.getMask() == 0)
break;
cr = cx;
endx = opline.instx;
}
RegName rn = findEvict(cr.getMask(), opline.idx, begx, endx);
if (rn == RegName_Null)
{
DBGOUT(" -No evict [" << *begx->inst << " - " << *endx->inst << "]" << endl;)
opline.back(begx);
return false;
}
DBGOUT(" -evict [" << *begx->inst << " - " << *endx->inst
<< "] " << getRegNameString(rn) << endl;)
static Counter<size_t> count_evicts("ia32:spillgen:evicts", 0);
count_evicts++;
assignReg(opline, begx, endx, rn);
opline.back(endx);
opline.forw();
return true;
}
// Attempt to change allocation that was previously made, i.e. reassign register that
// already used in the instruction for other operand because the processed operand
// requires already assigned register.
//
bool SpillGen::tryRepair (Opline& opline, Constraint c)
{
if (opline.idx == -1)
return false;
Constraint ca = c.getAliasConstraint(OpndSize_Default) & registers[opline.idx];
Constraint cr((OpndKind)ca.getKind(), c.getSize(), ca.getMask());
Inst* inst = opline.instx->inst;
RegMask mk = cr.getMask();
Opnd* ox = 0, *ox_out = 0;
RegName rx = RegName_Null;
bool need_load = false,
need_save = false;
size_t itx = 0;
Inst::Opnds opnds(inst, Inst::OpndRole_All);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it), ++itx)
{
ox = inst->getOpnd(it);
RegMask oxMask = ox->getConstraint(Opnd::ConstraintKind_Initial).getMask();
RegMask rm = getRegMaskConstr(ox, cr);
if ((mk & rm) != 0)
{
OpRole oprole = opline.instx->oproles[itx];
need_load = (oprole & RoleUse) != 0;
need_save = (oprole & RoleEnd) == 0;
unsigned mask = (1<<inst->getOpndCount())-1;
Constraint cx = ((Inst*)inst)->getConstraint(it, mask, OpndSize_Default);
RegMask used = usedRegs(opline.instx, opline.idx, need_load);
RegMask usable = (cx & registers[opline.idx]).getMask() & ~mk & oxMask;
if ((usable & ~used) != 0)
{
rx = findFree(usable & ~used, opline.idx, opline.instx);
ox_out = opndReg(ox, rx);
break;
}
else if (!(cx & OpndKind_Memory).isNull())
{
ox_out = opndMem(ox);
break;
}
else
{
rx = findEvict(usable, opline.idx, opline.instx, opline.instx);
if (rx != RegName_Null)
{
ox_out = opndReg(ox, rx);
break;
}
}
}
}
if (ox_out == 0)
return false;
// relocate operand 'ox' to new register 'rx'
DBGOUT(" -repair [" << *inst << "] " << *opline.opnd
<< " " << *ox << " " << *ox_out << endl;)
static Counter<size_t> count_repairs("ia32:spillgen:repairs", 0);
count_repairs++;
if (need_load)
emitMove(true, inst, ox_out, ox);
if (need_save)
emitMove(false, inst, ox, ox_out);
inst->replaceOpnd(ox, ox_out);
if (rx != RegName_Null /*&& need_save*/)
opline.instx->regusage[opline.idx] |= getRegMask(rx);
// assign the requsted operand to the register which was previosly assigned to ox
assignReg(opline, opline.instx, opline.instx, ox->getRegName());
opline.forw();
return true;
}
bool SpillGen::simplify (Inst* inst, Opnd* opnd)
{
Opnd* opndm;
if (inst->hasKind(Inst::Kind_LocalControlTransferInst) && inst->getOpndCount() == 1)
{
opndm = inst->getOpnd(0);
Opnd* opndx = irManager->newMemOpnd(opndm->getType(),
MemOpndKind_StackAutoLayout,
irManager->getRegOpnd(STACK_REG),
0);
DBGOUT("simplify jump " << *opndm << " -> mem " << *opndx << endl;);
emitMove(true, inst,opndx, opndm);
inst->replaceOpnd(opndm, opndx);
for (int so = 0; so != MemOpndSubOpndKind_Count; ++so)
{
Opnd* sub = opndm->getMemOpndSubOpnd(static_cast<MemOpndSubOpndKind>(so));
if (sub != 0)
actsmap[sub->getId()] = 0;
}
return true;
}
else if (inst->hasKind(Inst::Kind_CallInst) && (opndm = inst->getOpnd(0)) == opnd)
{
Opnd* opndx = irManager->newMemOpnd(opndm->getType(),
MemOpndKind_StackAutoLayout,
irManager->getRegOpnd(STACK_REG),
0);
DBGOUT("simplify call " << *opndm << " -> mem " << *opndx << endl;);
emitMove(true, inst,opndx, opndm);
inst->replaceOpnd(opndm, opndx);
for (int so = 0; so != MemOpndSubOpndKind_Count; ++so)
{
Opnd* sub = opndm->getMemOpndSubOpnd(static_cast<MemOpndSubOpndKind>(so));
if (sub != 0)
actsmap[sub->getId()] = 0;
}
return true;
}
return false;
}
// Return mask of all free registers at the instruction specified.
// If register will define value, set isuse=false.
// If regsiter will load value defined in some other instruction, set isuse=true
//
SpillGen::RegMask SpillGen::usedRegs (const Instx* instx, int idx, bool isuse) const
{
RegMask used = instx->regusage[idx];
if (isuse)
used |= (used ^ (instx-1)->regusage[idx]);
return used;
}
SpillGen::RegMask SpillGen::callRegs (const Instx* instx, int idx) const
{
if (instx->inst->getMnemonic() == Mnemonic_CALL)
{
OpndKind k = static_cast<OpndKind>(registers[idx].getKind());
Constraint ci = static_cast<CallInst*>(instx->inst)->getCalleeSaveRegs(k);
return ci.getMask();
}
return ~(RegMask)0;
}
// If currently handled operand is referenced by current instruction, then evaluate
// constraint of the operand imposed by this instruction and return 'true'.
// Otherwise, do nothing and return false.
//
int SpillGen::update (const Inst* inst, const Opnd* opnd, Constraint& constr) const
{
int count = 0;
Inst::Opnds opnds(inst, Inst::OpndRole_All);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it))
if ( inst->getOpnd(it) == opnd)
{
// if operand occures more than once then conservatively take
// the strong constraint for the operand
Constraint c;
c = ((Inst*)inst)->getConstraint(it, count?0xFFFFFFFF:0x7FFFFFFF, constr.getSize());
if (constr.isNull())
constr = c;
else
constr.intersectWith(c);
count++;
}
return count;
}
// Split operand and assign register 'rn' to it in the interval specified
// ('begx' - 'endx', inclusive)
//
void SpillGen::assignReg (Opline& opline, Instx* begx, Instx* endx, RegName rn)
{
assert(rn != RegName_Null);
RegMask mk = getRegMask(rn);
Opnd* opnd_reg = opndReg(opline.opnd, rn);
int reguses = 0;
DBGOUT("assignReg " << *opline.opnd << " [" << *begx->inst
<< " - " << *endx->inst << "] to " << *opnd_reg << endl;)
if (loadOpnd(opline, begx, opnd_reg))
(begx-1)->regusage[opline.idx] |= mk;
bool has_def = false;
Instx* savex = endx;
for (Instx* ptrx = begx; ptrx <= endx; ++ptrx)
{
if (opline.save_changed && opline.catched && ptrx->inst->hasKind(Inst::Kind_CallInst))
{
emitMove(true, ptrx->inst, opndMem(opline), opnd_reg);
opline.save_changed = false;
}
if (ptrx->inst->replaceOpnd(opline.opnd, opnd_reg))
{// the operand is used in the instruction
++reguses;
if (opline.isDef(ptrx->inst))
{
has_def = true;
opline.save_changed = true;
savex = ptrx;
}
}
else
{// the operand is not used in the instruction, so it can be evicted
if (ptrx->evicts != 0)
ptrx->evicts->push_back(opnd_reg);
}
// special processing for dead-definition (begx == endx && has_def)
if (ptrx != endx || (begx == endx && has_def))
ptrx->regusage[opline.idx] |= mk;
}
bool before = false;
if (savex->inst->getMnemonic() == Mnemonic_CALL && !opline.isDef(savex->inst))
{
Constraint ci = static_cast<CallInst*>(savex->inst)->getCalleeSaveRegs(getRegKind(rn));
before = (ci.getMask() & mk) == 0;
}
saveOpnd(opline, savex, opnd_reg, before ? 0 : mk, opline.idx, before);
#ifdef _DEBUG_SPILLGEN
assigns.incr(reguses);
#endif
}
// Split operand and assign memory location to it in the interval specified
// ('begx' - 'endx', inclusive)
//
void SpillGen::assignMem (Opline& opline, Instx* begx, Instx* endx)
{
opndMem(opline);
DBGOUT("assignMem " << *opline.opnd << " [" << *begx->inst
<< " - " << *endx->inst << "] to " << *opline.opnd_mem << endl;)
loadOpnd(opline, begx, opline.opnd_mem);
if (opline.opnd != opline.opnd_mem)
for (; begx <= endx; ++begx)
{
begx->inst->replaceOpnd(opline.opnd, opline.opnd_mem);
}
saveOpnd(opline, endx, opline.opnd_mem);
opline.save_changed = false;
}
// Lazy save 'opnd' to memory at the point 'instx'
//
void SpillGen::saveOpnd (Opline& opline, Instx* instx, Opnd* opnd)
{
opline.save_instx = instx;
opline.save_before = false;
opline.save_opnd = opnd;
opline.save_regmsk = 0;
opline.save_regidx = -1;
}
void SpillGen::saveOpnd (Opline& opline, Instx* instx, Opnd* opnd, RegMask regmsk, int regidx, bool before)
{
opline.save_instx = instx;
opline.save_before = before;
opline.save_opnd = opnd;
opline.save_regmsk = regmsk;
opline.save_regidx = regidx;
}
// Load operand from memory to 'opnd' at the poinr 'instx'
//
bool SpillGen::loadOpnd (Opline& opline, Instx* instx, Opnd* opnd)
{
if (opline.save_instx == 0 || !opline.isUse(instx->inst))
return false;
if (opline.save_opnd->hasAssignedPhysicalLocation())
{
opndMem(opline);
if (opline.save_changed)
{
emitMove(opline.save_before,
opline.save_instx->inst,
opline.opnd_mem,
opline.save_opnd);
if (opline.save_regmsk != 0)
opline.save_instx->regusage[opline.save_regidx] |= opline.save_regmsk;
opline.save_changed = false;
}
emitMove(true, instx->inst, opnd, opline.opnd_mem);
}
else
{
if (opnd->hasAssignedPhysicalLocation())
{// Assigned <- UnAssigned
emitMove(true, instx->inst, opnd, opline.save_opnd);
}
else
{// UnAssigned <- UnAssigned
assert(opnd == opline.save_opnd);
opline.save_opnd = opnd;
opline.save_instx = instx;
}
}
return true;
}
// Save operand to memory
//
void SpillGen::loadOpndMem (Opline& opline)
{
if (opline.save_instx == 0)
return;
if (opline.save_opnd->hasAssignedPhysicalLocation())
{
if (opline.save_changed)
{
emitMove(opline.save_before,
opline.save_instx->inst,
opndMem(opline),
opline.save_opnd);
if (opline.save_regmsk != 0)
opline.save_instx->regusage[opline.save_regidx] |= opline.save_regmsk;
opline.save_changed = false;
}
}
}
Opnd* SpillGen::opndMem (Opline& opline)
{
if (opline.opnd_mem == 0)
opline.opnd_mem = opndMem(opline.opnd);
return opline.opnd_mem;
}
static bool operator == (const SpillGen::TwoOpnds& p, const Opnd* o)
{
return p.first == o;
}
Opnd* SpillGen::opndMem (const Opnd* opnd)
{
Opnd* opnd_mem;
MemOpnds::iterator ptr, end;
if ((ptr = find(memopnds.begin(), end = memopnds.end(), opnd)) == end)
{
opnd_mem = irManager->newMemOpnd(opnd->getType(),
MemOpndKind_StackAutoLayout,
irManager->getRegOpnd(STACK_REG),
0);
memopnds.push_back(TwoOpnds(opnd, opnd_mem));
#ifdef _DEBUG_SPILLGEN
DBGOUT("split " << *opnd << " -> mem " << *opnd_mem << endl;)
//if (dbgraw != 0)
// dbgraw->split(opnd, opnd_mem);
#endif
}
else
{
opnd_mem = ptr->second;
}
return opnd_mem;
}
Opnd* SpillGen::opndReg (const Opnd* opnd, RegName rn) const
{
Opnd* opnd_reg = irManager->newRegOpnd(opnd->getType(), getRegName(getRegKind(rn), opnd->getSize(), getRegIndex(rn)));
#ifdef _DEBUG_SPILLGEN
DBGOUT("split " << *opnd << " -> reg " << *opnd_reg << endl;)
//if (dbgraw != 0)
// dbgraw->split(opnd, opnd_reg);
#endif
return opnd_reg;
}
void SpillGen::emitPushPop (bool before, Inst* inst, Opnd* opnd, bool push)
{
if (push)
emitMove(before, inst, opndMem(opnd), opnd);
else
emitMove(before, inst, opnd, opndMem(opnd));
}
// Emit 'mov' instruction before/after the instruction specified
//
Inst* SpillGen::emitMove (bool before, Inst* inst, Opnd* dst, Opnd* src)
{
if (dst == src)
return 0;
Inst* mov = irManager->newCopyPseudoInst(Mnemonic_MOV, dst, src);
if (before) {
mov->insertBefore(inst);
} else
{
// ensure we don't append after any branch instructions
// for example, after the last instruction in basic block
assert(!inst->hasKind(Inst::Kind_LocalControlTransferInst));
mov->insertAfter(inst);
}
DBGOUT((before?"before":" after") << " " << *inst << " "
<< *mov << " MOV " << *dst << " " << *src << endl;)
emitted++;
return mov;
}
RegName SpillGen::findFree (RegMask usable, int idx, Instx* instx)
{
Constraint c = registers[idx];
usable &= c.getMask(); // search only from available registers
Instx* begx = instxs.empty()? (Instx*)NULL:&*instxs.begin();
Instx* endx = begx + instxs.size() ;
++begx;
Instx* ptrx;
size_t xbest = INT_MAX,
xbest_free = INT_MAX,
xlgth_free = 0,
xbest_press = INT_MAX,
xlgth_press = 0;
size_t x, found;
RegMask msk;
for (x = 0, found = 0, msk = 1; usable != 0; ++x, msk <<= 1) {
if ((usable & msk) != 0)
{
usable ^= msk, ++found;
if (usable == 0 && found == 1)
{// this is the only register available - no need to choose the best
xbest = x;
break;
}
size_t lgth = 0;
bool press = (instx->regpress[idx] & msk) != 0;
for (ptrx = instx-1; ptrx >= begx && (ptrx->regusage[idx] & msk) == 0; --ptrx)
{
++lgth;
press |= (ptrx->regpress[idx] & msk) != 0;
}
for (ptrx = instx+1; ptrx < endx && (ptrx->regusage[idx] & msk) == 0; ++ptrx)
{
++lgth;
press |= (ptrx->regpress[idx] & msk) != 0;
}
if (press)
{
if (xbest_press == INT_MAX || xlgth_press >= lgth)
xbest_press = x,
xlgth_press = lgth;
}
else
{
if (xbest_free == INT_MAX || xlgth_free >= lgth)
xbest_free = x,
xlgth_free = lgth;
}
}
}
if (xbest == INT_MAX) {
xbest = xbest_free != INT_MAX ? xbest_free : xbest_press;
}
return xbest == INT_MAX ?
RegName_Null :
getRegName(static_cast<OpndKind>(c.getKind()), c.getSize(), (int)xbest);
}
RegName SpillGen::findEvict (RegMask usable, int idx, Instx* begx, Instx*& endx)
{
Constraint c = registers[idx];
Inst* inst = begx->inst;
Opnd* opnd;
RegMask used = 0;
Inst::Opnds opnds(inst, Inst::OpndRole_All);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it))
used |= getRegMaskConstr(inst->getOpnd(it), c);
usable &= ~used;
if (usable == 0)
return RegName_Null;
if (!evicts_known)
setupEvicts();
Evicts evicts(mm);
for (Opnds::iterator it = begx->evicts->begin(); it != begx->evicts->end(); ++it)
if ((opnd = *it) != 0 && (usable & getRegMaskConstr(opnd, c)) != 0)
{
Evict tmp;
tmp.opnd = opnd;
tmp.begx = begx;
tmp.endx = begx;
evicts.push_back(tmp);
}
if (evicts.empty())
return RegName_Null;
Evict* evict = pickEvict(evicts);
if (evict == 0)
return RegName_Null;
if (evict->endx->inst->hasKind(Inst::Kind_LocalControlTransferInst)) {
--(evict->endx);
if ((evict->endx < evict->begx) || (evict->endx < endx))
return RegName_Null;
}
if (endx > evict->endx)
endx = evict->endx;
// now evict the operand found
emitPushPop(true, evict->begx->inst, evict->opnd, true);
emitPushPop(false, evict->endx->inst, evict->opnd, false);
// update the 'instxs' table
RegName rn = evict->opnd->getRegName();
RegMask mk = getRegMask(rn);
assert((usable & mk));
Instx* ptrx;
for (ptrx = evict->begx; ptrx <= evict->endx; ++ptrx)
{
// mark register assigned to the operand as free
if (ptrx != evict->endx)
ptrx->regusage[idx] &= ~mk;
// clear the operand as a candidate to evict
killEvict(evict->opnd, ptrx);
}
return rn;
}
void SpillGen::setupEvicts ()
{
Opnd* opnd;
Constraint loc;
RegMask msk;
int idx;
// will be used to calculate all registers used in an instruction
Registers used(mm);
used = registers;
Registers* catched = 0;
BitSet ls = *lives_exit;
ls.resize(irManager->getOpndCount());
BitSet lsnext = ls;
Instxs::iterator instxp = instxs.end() - 1;
for (Inst* inst = (Inst*)bblock->getLastInst(); inst != 0; inst = inst->getPrevInst())
{
used.clearMasks(); // mask of all registers used in the current instruction
Inst::Opnds opnds(inst, Inst::OpndRole_All);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it))
{
opnd = inst->getOpnd(it);
loc = opnd->getConstraint(Opnd::ConstraintKind_Location, OpndSize_Default);
if ((msk = loc.getMask()) != 0 && (idx = registers.getIndex(loc)) != -1)
{// this operand is already assigned to some location/register
if (inst->isLiveRangeEnd(it))
lsnext.setBit(opnd->getId(), false);
else if (inst->isLiveRangeStart(it))
lsnext.setBit(opnd->getId(), true);
merge(used[idx], msk);
}
}
if (inst->hasKind(Inst::Kind_CallInst) && lives_catch != 0)
{
if (catched == 0)
{// construct the catched operand list - once per block
catched = new (mm) Registers(mm);
*catched = registers;
catched->clearMasks();
BitSet::IterB lives(*lives_catch);
for (int i = lives.getNext(); i != -1; i = lives.getNext())
{
opnd = irManager->getOpnd(i);
loc = opnd->getConstraint(Opnd::ConstraintKind_Location, OpndSize_Default);
if ((msk = loc.getMask()) != 0 && (idx = registers.getIndex(loc)) != -1)
merge((*catched)[idx], msk);
}
}
used.merge(*catched);
}
if (inst == instxp->inst)
{
if (instxp->evicts == 0)
instxp->evicts = new (mm) Opnds(mm);
else
instxp->evicts->resize(0);
BitSet::IterB lives(ls);
for (int i = lives.getNext(); i != -1; i = lives.getNext())
{
opnd = irManager->getOpnd(i);
loc = opnd->getConstraint(Opnd::ConstraintKind_Location, OpndSize_Default);
if (loc.getMask() != 0 && registers.contains(loc) && !used.contains(loc))
instxp->evicts->push_back(opnd);
}
--instxp;
}
ls = lsnext;
}
evicts_known = true;
}
SpillGen::Evict* SpillGen::pickEvict (Evicts& evicts)
{
Instx* begx = instxs.empty()? (Instx*)NULL:&*instxs.begin();
Instx* endx = begx + instxs.size();
++begx;
Instx* ptrx;
DBGOUT("Evicts" << endl;)
for (Evicts::iterator it = evicts.begin(); it != evicts.end(); ++it)
{
Evict& evict = *it;
for (ptrx = evict.begx-1; ptrx >= begx && isEvict(evict.opnd, ptrx); --ptrx)
evict.begx = ptrx;
for (ptrx = evict.endx+1; ptrx < endx && isEvict(evict.opnd, ptrx); ++ptrx)
evict.endx = ptrx;
evict.weight = int(evict.endx - evict.begx);
DBGOUT(" evict " << *evict.opnd << " [" << *evict.begx->inst
<< " - " << *evict.endx->inst << "] w:" << evict.weight << endl;)
}
assert(!evicts.empty());
return &*max_element(evicts.begin(), evicts.end());
}
bool SpillGen::isEvict (const Opnd* opnd, const Instx* ptrx) const
{
Opnds::iterator endx = ptrx->evicts->end();
return find(ptrx->evicts->begin(), endx, opnd) != endx;
}
void SpillGen:: killEvict (const Opnd* opnd, Instx* instx) const
{
Opnds::iterator end = instx->evicts->end();
Opnds::iterator it = find(instx->evicts->begin(), end, opnd);
if (it != end)
*it = 0;
}
SpillGen::RegMask SpillGen::getRegMaskConstr (const Opnd* opnd, Constraint c) const
{
return opnd->isPlacedIn(c.getAliasConstraint(OpndSize_Default)) ?
getRegMask(opnd->getRegName()) : 0;
}
#ifdef _DEBUG_SPILLGEN
#include "Ia32SpillGenDbgTail.h"
#endif
} //namespace Ia32
} //namespace Jitrino