blob: e2f2cd683bb47a2f9e24c21625b429a368ff43e8 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed 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.
*/
/*
* This file contains register alloction support and is intended to be
* included by:
*
* Codegen-$(TARGET_ARCH_VARIANT).c
*
*/
#include "../CompilerUtility.h"
#include "../CompilerIR.h"
#include "../Dataflow.h"
#include "Ralloc.h"
namespace art {
#define SREG(c, s) ((c)->regLocation[(s)].sRegLow)
/*
* Get the "real" sreg number associated with an sReg slot. In general,
* sReg values passed through codegen are the SSA names created by
* dataflow analysis and refer to slot numbers in the cUnit->regLocation
* array. However, renaming is accomplished by simply replacing RegLocation
* entries in the cUnit->reglocation[] array. Therefore, when location
* records for operands are first created, we need to ask the locRecord
* identified by the dataflow pass what it's new name is.
*/
/*
* Free all allocated temps in the temp pools. Note that this does
* not affect the "liveness" of a temp register, which will stay
* live until it is either explicitly killed or reallocated.
*/
extern void oatResetRegPool(CompilationUnit* cUnit)
{
int i;
for (i=0; i < cUnit->regPool->numCoreRegs; i++) {
if (cUnit->regPool->coreRegs[i].isTemp)
cUnit->regPool->coreRegs[i].inUse = false;
}
for (i=0; i < cUnit->regPool->numFPRegs; i++) {
if (cUnit->regPool->FPRegs[i].isTemp)
cUnit->regPool->FPRegs[i].inUse = false;
}
}
/*
* Set up temp & preserved register pools specialized by target.
* Note: numRegs may be zero.
*/
extern void oatInitPool(RegisterInfo* regs, int* regNums, int num)
{
int i;
for (i=0; i < num; i++) {
regs[i].reg = regNums[i];
regs[i].inUse = false;
regs[i].isTemp = false;
regs[i].pair = false;
regs[i].live = false;
regs[i].dirty = false;
regs[i].sReg = INVALID_SREG;
}
}
void dumpRegPool(RegisterInfo* p, int numRegs)
{
LOG(INFO) << "================================================";
for (int i = 0; i < numRegs; i++) {
LOG(INFO) << StringPrintf(
"R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
p[i].reg, p[i].isTemp, p[i].inUse, p[i].pair, p[i].partner,
p[i].live, p[i].dirty, p[i].sReg,(int)p[i].defStart,
(int)p[i].defEnd);
}
LOG(INFO) << "================================================";
}
void oatDumpCoreRegPool(CompilationUnit* cUnit)
{
dumpRegPool(cUnit->regPool->coreRegs, cUnit->regPool->numCoreRegs);
}
void oatDumpFpRegPool(CompilationUnit* cUnit)
{
dumpRegPool(cUnit->regPool->FPRegs, cUnit->regPool->numFPRegs);
}
/* Mark a temp register as dead. Does not affect allocation state. */
static inline void clobberBody(CompilationUnit *cUnit, RegisterInfo* p)
{
if (p->isTemp) {
DCHECK(!(p->live && p->dirty)) << "Live & dirty temp in clobber";
p->live = false;
p->sReg = INVALID_SREG;
p->defStart = NULL;
p->defEnd = NULL;
if (p->pair) {
p->pair = false;
oatClobber(cUnit, p->partner);
}
}
}
/* Mark a temp register as dead. Does not affect allocation state. */
void oatClobber(CompilationUnit* cUnit, int reg)
{
clobberBody(cUnit, oatGetRegInfo(cUnit, reg));
}
void clobberSRegBody(RegisterInfo* p, int numRegs, int sReg)
{
int i;
for (i=0; i< numRegs; i++) {
if (p[i].sReg == sReg) {
if (p[i].isTemp) {
p[i].live = false;
}
p[i].defStart = NULL;
p[i].defEnd = NULL;
}
}
}
/* Clobber any temp associated with an sReg. Could be in either class */
extern void oatClobberSReg(CompilationUnit* cUnit, int sReg)
{
#ifndef NDEBUG
/* Reset live temp tracking sanity checker */
if (sReg == cUnit->liveSReg) {
cUnit->liveSReg = INVALID_SREG;
}
#endif
clobberSRegBody(cUnit->regPool->coreRegs, cUnit->regPool->numCoreRegs, sReg);
clobberSRegBody(cUnit->regPool->FPRegs, cUnit->regPool->numFPRegs, sReg);
}
/*
* SSA names associated with the initial definitions of Dalvik
* registers are the same as the Dalvik register number (and
* thus take the same position in the promotionMap. However,
* the special Method* and compiler temp resisters use negative
* vReg numbers to distinguish them and can have an arbitrary
* ssa name (above the last original Dalvik register). This function
* maps SSA names to positions in the promotionMap array.
*/
int SRegToPMap(CompilationUnit* cUnit, int sReg)
{
DCHECK_LT(sReg, cUnit->numSSARegs);
DCHECK_GE(sReg, 0);
int vReg = SRegToVReg(cUnit, sReg);
if (vReg >= 0) {
DCHECK_LT(vReg, cUnit->numDalvikRegisters);
return vReg;
} else {
int pos = std::abs(vReg) - std::abs(SSA_METHOD_BASEREG);
DCHECK_LE(pos, cUnit->numCompilerTemps);
return cUnit->numDalvikRegisters + pos;
}
}
/* Reserve a callee-save register. Return -1 if none available */
extern int oatAllocPreservedCoreReg(CompilationUnit* cUnit, int sReg)
{
int res = -1;
RegisterInfo* coreRegs = cUnit->regPool->coreRegs;
for (int i = 0; i < cUnit->regPool->numCoreRegs; i++) {
if (!coreRegs[i].isTemp && !coreRegs[i].inUse) {
int vReg = SRegToVReg(cUnit, sReg);
int pMapIdx = SRegToPMap(cUnit, sReg);
res = coreRegs[i].reg;
coreRegs[i].inUse = true;
cUnit->coreSpillMask |= (1 << res);
cUnit->coreVmapTable.push_back(vReg);
cUnit->numCoreSpills++;
cUnit->promotionMap[pMapIdx].coreLocation = kLocPhysReg;
cUnit->promotionMap[pMapIdx].coreReg = res;
break;
}
}
return res;
}
/*
* Reserve a callee-save fp single register. Try to fullfill request for
* even/odd allocation, but go ahead and allocate anything if not
* available. If nothing's available, return -1.
*/
int allocPreservedSingle(CompilationUnit* cUnit, int sReg, bool even)
{
int res = -1;
RegisterInfo* FPRegs = cUnit->regPool->FPRegs;
for (int i = 0; i < cUnit->regPool->numFPRegs; i++) {
if (!FPRegs[i].isTemp && !FPRegs[i].inUse &&
((FPRegs[i].reg & 0x1) == 0) == even) {
int vReg = SRegToVReg(cUnit, sReg);
int pMapIdx = SRegToPMap(cUnit, sReg);
res = FPRegs[i].reg;
FPRegs[i].inUse = true;
oatMarkPreservedSingle(cUnit, vReg, res);
cUnit->promotionMap[pMapIdx].fpLocation = kLocPhysReg;
cUnit->promotionMap[pMapIdx].fpReg = res;
break;
}
}
return res;
}
/*
* Somewhat messy code here. We want to allocate a pair of contiguous
* physical single-precision floating point registers starting with
* an even numbered reg. It is possible that the paired sReg (sReg+1)
* has already been allocated - try to fit if possible. Fail to
* allocate if we can't meet the requirements for the pair of
* sReg<=sX[even] & (sReg+1)<= sX+1.
*/
int allocPreservedDouble(CompilationUnit* cUnit, int sReg)
{
int res = -1; // Assume failure
int vReg = SRegToVReg(cUnit, sReg);
int pMapIdx = SRegToPMap(cUnit, sReg);
if (cUnit->promotionMap[pMapIdx+1].fpLocation == kLocPhysReg) {
// Upper reg is already allocated. Can we fit?
int highReg = cUnit->promotionMap[pMapIdx+1].fpReg;
if ((highReg & 1) == 0) {
// High reg is even - fail.
return res;
}
// Is the low reg of the pair free?
RegisterInfo* p = oatGetRegInfo(cUnit, highReg-1);
if (p->inUse || p->isTemp) {
// Already allocated or not preserved - fail.
return res;
}
// OK - good to go.
res = p->reg;
p->inUse = true;
DCHECK_EQ((res & 1), 0);
oatMarkPreservedSingle(cUnit, vReg, res);
} else {
RegisterInfo* FPRegs = cUnit->regPool->FPRegs;
for (int i = 0; i < cUnit->regPool->numFPRegs; i++) {
if (!FPRegs[i].isTemp && !FPRegs[i].inUse &&
((FPRegs[i].reg & 0x1) == 0x0) &&
!FPRegs[i+1].isTemp && !FPRegs[i+1].inUse &&
((FPRegs[i+1].reg & 0x1) == 0x1) &&
(FPRegs[i].reg + 1) == FPRegs[i+1].reg) {
res = FPRegs[i].reg;
FPRegs[i].inUse = true;
oatMarkPreservedSingle(cUnit, vReg, res);
FPRegs[i+1].inUse = true;
DCHECK_EQ(res + 1, FPRegs[i+1].reg);
oatMarkPreservedSingle(cUnit, vReg+1, res+1);
break;
}
}
}
if (res != -1) {
cUnit->promotionMap[pMapIdx].fpLocation = kLocPhysReg;
cUnit->promotionMap[pMapIdx].fpReg = res;
cUnit->promotionMap[pMapIdx+1].fpLocation = kLocPhysReg;
cUnit->promotionMap[pMapIdx+1].fpReg = res + 1;
}
return res;
}
/*
* Reserve a callee-save fp register. If this register can be used
* as the first of a double, attempt to allocate an even pair of fp
* single regs (but if can't still attempt to allocate a single, preferring
* first to allocate an odd register.
*/
extern int oatAllocPreservedFPReg(CompilationUnit* cUnit, int sReg,
bool doubleStart)
{
int res = -1;
if (doubleStart) {
res = allocPreservedDouble(cUnit, sReg);
}
if (res == -1) {
res = allocPreservedSingle(cUnit, sReg, false /* try odd # */);
}
if (res == -1)
res = allocPreservedSingle(cUnit, sReg, true /* try even # */);
return res;
}
int allocTempBody(CompilationUnit* cUnit, RegisterInfo* p, int numRegs,
int* nextTemp, bool required)
{
int i;
int next = *nextTemp;
for (i=0; i< numRegs; i++) {
if (next >= numRegs)
next = 0;
if (p[next].isTemp && !p[next].inUse && !p[next].live) {
oatClobber(cUnit, p[next].reg);
p[next].inUse = true;
p[next].pair = false;
*nextTemp = next + 1;
return p[next].reg;
}
next++;
}
next = *nextTemp;
for (i=0; i< numRegs; i++) {
if (next >= numRegs)
next = 0;
if (p[next].isTemp && !p[next].inUse) {
oatClobber(cUnit, p[next].reg);
p[next].inUse = true;
p[next].pair = false;
*nextTemp = next + 1;
return p[next].reg;
}
next++;
}
if (required) {
oatCodegenDump(cUnit);
dumpRegPool(cUnit->regPool->coreRegs,
cUnit->regPool->numCoreRegs);
LOG(FATAL) << "No free temp registers";
}
return -1; // No register available
}
//REDO: too many assumptions.
extern int oatAllocTempDouble(CompilationUnit* cUnit)
{
RegisterInfo* p = cUnit->regPool->FPRegs;
int numRegs = cUnit->regPool->numFPRegs;
/* Start looking at an even reg */
int next = cUnit->regPool->nextFPReg & ~0x1;
// First try to avoid allocating live registers
for (int i=0; i < numRegs; i+=2) {
if (next >= numRegs)
next = 0;
if ((p[next].isTemp && !p[next].inUse && !p[next].live) &&
(p[next+1].isTemp && !p[next+1].inUse && !p[next+1].live)) {
oatClobber(cUnit, p[next].reg);
oatClobber(cUnit, p[next+1].reg);
p[next].inUse = true;
p[next+1].inUse = true;
DCHECK_EQ((p[next].reg+1), p[next+1].reg);
DCHECK_EQ((p[next].reg & 0x1), 0);
cUnit->regPool->nextFPReg = next + 2;
if (cUnit->regPool->nextFPReg >= numRegs) {
cUnit->regPool->nextFPReg = 0;
}
return p[next].reg;
}
next += 2;
}
next = cUnit->regPool->nextFPReg & ~0x1;
// No choice - find a pair and kill it.
for (int i=0; i < numRegs; i+=2) {
if (next >= numRegs)
next = 0;
if (p[next].isTemp && !p[next].inUse && p[next+1].isTemp &&
!p[next+1].inUse) {
oatClobber(cUnit, p[next].reg);
oatClobber(cUnit, p[next+1].reg);
p[next].inUse = true;
p[next+1].inUse = true;
DCHECK_EQ((p[next].reg+1), p[next+1].reg);
DCHECK_EQ((p[next].reg & 0x1), 0);
cUnit->regPool->nextFPReg = next + 2;
if (cUnit->regPool->nextFPReg >= numRegs) {
cUnit->regPool->nextFPReg = 0;
}
return p[next].reg;
}
next += 2;
}
LOG(FATAL) << "No free temp registers (pair)";
return -1;
}
/* Return a temp if one is available, -1 otherwise */
extern int oatAllocFreeTemp(CompilationUnit* cUnit)
{
return allocTempBody(cUnit, cUnit->regPool->coreRegs,
cUnit->regPool->numCoreRegs,
&cUnit->regPool->nextCoreReg, true);
}
extern int oatAllocTemp(CompilationUnit* cUnit)
{
return allocTempBody(cUnit, cUnit->regPool->coreRegs,
cUnit->regPool->numCoreRegs,
&cUnit->regPool->nextCoreReg, true);
}
extern int oatAllocTempFloat(CompilationUnit* cUnit)
{
return allocTempBody(cUnit, cUnit->regPool->FPRegs,
cUnit->regPool->numFPRegs,
&cUnit->regPool->nextFPReg, true);
}
RegisterInfo* allocLiveBody(RegisterInfo* p, int numRegs, int sReg)
{
int i;
if (sReg == -1)
return NULL;
for (i=0; i < numRegs; i++) {
if (p[i].live && (p[i].sReg == sReg)) {
if (p[i].isTemp)
p[i].inUse = true;
return &p[i];
}
}
return NULL;
}
RegisterInfo* allocLive(CompilationUnit* cUnit, int sReg, int regClass)
{
RegisterInfo* res = NULL;
switch (regClass) {
case kAnyReg:
res = allocLiveBody(cUnit->regPool->FPRegs,
cUnit->regPool->numFPRegs, sReg);
if (res)
break;
/* Intentional fallthrough */
case kCoreReg:
res = allocLiveBody(cUnit->regPool->coreRegs,
cUnit->regPool->numCoreRegs, sReg);
break;
case kFPReg:
res = allocLiveBody(cUnit->regPool->FPRegs,
cUnit->regPool->numFPRegs, sReg);
break;
default:
LOG(FATAL) << "Invalid register type";
}
return res;
}
extern void oatFreeTemp(CompilationUnit* cUnit, int reg)
{
RegisterInfo* p = cUnit->regPool->coreRegs;
int numRegs = cUnit->regPool->numCoreRegs;
int i;
for (i=0; i< numRegs; i++) {
if (p[i].reg == reg) {
if (p[i].isTemp) {
p[i].inUse = false;
}
p[i].pair = false;
return;
}
}
p = cUnit->regPool->FPRegs;
numRegs = cUnit->regPool->numFPRegs;
for (i=0; i< numRegs; i++) {
if (p[i].reg == reg) {
if (p[i].isTemp) {
p[i].inUse = false;
}
p[i].pair = false;
return;
}
}
LOG(FATAL) << "Tried to free a non-existant temp: r" << reg;
}
extern RegisterInfo* oatIsLive(CompilationUnit* cUnit, int reg)
{
RegisterInfo* p = cUnit->regPool->coreRegs;
int numRegs = cUnit->regPool->numCoreRegs;
int i;
for (i=0; i< numRegs; i++) {
if (p[i].reg == reg) {
return p[i].live ? &p[i] : NULL;
}
}
p = cUnit->regPool->FPRegs;
numRegs = cUnit->regPool->numFPRegs;
for (i=0; i< numRegs; i++) {
if (p[i].reg == reg) {
return p[i].live ? &p[i] : NULL;
}
}
return NULL;
}
extern RegisterInfo* oatIsTemp(CompilationUnit* cUnit, int reg)
{
RegisterInfo* p = oatGetRegInfo(cUnit, reg);
return (p->isTemp) ? p : NULL;
}
extern RegisterInfo* oatIsPromoted(CompilationUnit* cUnit, int reg)
{
RegisterInfo* p = oatGetRegInfo(cUnit, reg);
return (p->isTemp) ? NULL : p;
}
extern bool oatIsDirty(CompilationUnit* cUnit, int reg)
{
RegisterInfo* p = oatGetRegInfo(cUnit, reg);
return p->dirty;
}
/*
* Similar to oatAllocTemp(), but forces the allocation of a specific
* register. No check is made to see if the register was previously
* allocated. Use with caution.
*/
extern void oatLockTemp(CompilationUnit* cUnit, int reg)
{
RegisterInfo* p = cUnit->regPool->coreRegs;
int numRegs = cUnit->regPool->numCoreRegs;
int i;
for (i=0; i< numRegs; i++) {
if (p[i].reg == reg) {
DCHECK(p[i].isTemp);
p[i].inUse = true;
p[i].live = false;
return;
}
}
p = cUnit->regPool->FPRegs;
numRegs = cUnit->regPool->numFPRegs;
for (i=0; i< numRegs; i++) {
if (p[i].reg == reg) {
DCHECK(p[i].isTemp);
p[i].inUse = true;
p[i].live = false;
return;
}
}
LOG(FATAL) << "Tried to lock a non-existant temp: r" << reg;
}
static inline void resetDefBody(RegisterInfo* p)
{
p->defStart = NULL;
p->defEnd = NULL;
}
extern void oatResetDef(CompilationUnit* cUnit, int reg)
{
resetDefBody(oatGetRegInfo(cUnit, reg));
}
void nullifyRange(CompilationUnit* cUnit, LIR *start, LIR *finish,
int sReg1, int sReg2)
{
if (start && finish) {
LIR *p;
DCHECK_EQ(sReg1, sReg2);
for (p = start; ;p = p->next) {
oatNopLIR(p);
if (p == finish)
break;
}
}
}
/*
* Mark the beginning and end LIR of a def sequence. Note that
* on entry start points to the LIR prior to the beginning of the
* sequence.
*/
extern void oatMarkDef(CompilationUnit* cUnit, RegLocation rl,
LIR *start, LIR *finish)
{
DCHECK(!rl.wide);
DCHECK(start && start->next);
DCHECK(finish);
RegisterInfo* p = oatGetRegInfo(cUnit, rl.lowReg);
p->defStart = start->next;
p->defEnd = finish;
}
/*
* Mark the beginning and end LIR of a def sequence. Note that
* on entry start points to the LIR prior to the beginning of the
* sequence.
*/
extern void oatMarkDefWide(CompilationUnit* cUnit, RegLocation rl,
LIR *start, LIR *finish)
{
DCHECK(rl.wide);
DCHECK(start && start->next);
DCHECK(finish);
RegisterInfo* p = oatGetRegInfo(cUnit, rl.lowReg);
oatResetDef(cUnit, rl.highReg); // Only track low of pair
p->defStart = start->next;
p->defEnd = finish;
}
extern RegLocation oatWideToNarrow(CompilationUnit* cUnit, RegLocation rl)
{
DCHECK(rl.wide);
if (rl.location == kLocPhysReg) {
RegisterInfo* infoLo = oatGetRegInfo(cUnit, rl.lowReg);
RegisterInfo* infoHi = oatGetRegInfo(cUnit, rl.highReg);
if (infoLo->isTemp) {
infoLo->pair = false;
infoLo->defStart = NULL;
infoLo->defEnd = NULL;
}
if (infoHi->isTemp) {
infoHi->pair = false;
infoHi->defStart = NULL;
infoHi->defEnd = NULL;
}
}
rl.wide = false;
return rl;
}
extern void oatResetDefLoc(CompilationUnit* cUnit, RegLocation rl)
{
DCHECK(!rl.wide);
RegisterInfo* p = oatIsTemp(cUnit, rl.lowReg);
if (p && !(cUnit->disableOpt & (1 << kSuppressLoads))) {
DCHECK(!p->pair);
nullifyRange(cUnit, p->defStart, p->defEnd, p->sReg, rl.sRegLow);
}
oatResetDef(cUnit, rl.lowReg);
}
extern void oatResetDefLocWide(CompilationUnit* cUnit, RegLocation rl)
{
DCHECK(rl.wide);
RegisterInfo* pLow = oatIsTemp(cUnit, rl.lowReg);
RegisterInfo* pHigh = oatIsTemp(cUnit, rl.highReg);
if (pLow && !(cUnit->disableOpt & (1 << kSuppressLoads))) {
DCHECK(pLow->pair);
nullifyRange(cUnit, pLow->defStart, pLow->defEnd, pLow->sReg, rl.sRegLow);
}
if (pHigh && !(cUnit->disableOpt & (1 << kSuppressLoads))) {
DCHECK(pHigh->pair);
}
oatResetDef(cUnit, rl.lowReg);
oatResetDef(cUnit, rl.highReg);
}
extern void oatResetDefTracking(CompilationUnit* cUnit)
{
int i;
for (i=0; i< cUnit->regPool->numCoreRegs; i++) {
resetDefBody(&cUnit->regPool->coreRegs[i]);
}
for (i=0; i< cUnit->regPool->numFPRegs; i++) {
resetDefBody(&cUnit->regPool->FPRegs[i]);
}
}
extern void oatClobberAllRegs(CompilationUnit* cUnit)
{
int i;
for (i=0; i< cUnit->regPool->numCoreRegs; i++) {
clobberBody(cUnit, &cUnit->regPool->coreRegs[i]);
}
for (i=0; i< cUnit->regPool->numFPRegs; i++) {
clobberBody(cUnit, &cUnit->regPool->FPRegs[i]);
}
}
// Make sure nothing is live and dirty
void flushAllRegsBody(CompilationUnit* cUnit, RegisterInfo* info,
int numRegs)
{
int i;
for (i=0; i < numRegs; i++) {
if (info[i].live && info[i].dirty) {
if (info[i].pair) {
oatFlushRegWide(cUnit, info[i].reg, info[i].partner);
} else {
oatFlushReg(cUnit, info[i].reg);
}
}
}
}
extern void oatFlushAllRegs(CompilationUnit* cUnit)
{
flushAllRegsBody(cUnit, cUnit->regPool->coreRegs,
cUnit->regPool->numCoreRegs);
flushAllRegsBody(cUnit, cUnit->regPool->FPRegs,
cUnit->regPool->numFPRegs);
oatClobberAllRegs(cUnit);
}
//TUNING: rewrite all of this reg stuff. Probably use an attribute table
bool regClassMatches(int regClass, int reg)
{
if (regClass == kAnyReg) {
return true;
} else if (regClass == kCoreReg) {
return !oatIsFpReg(reg);
} else {
return oatIsFpReg(reg);
}
}
extern void oatMarkLive(CompilationUnit* cUnit, int reg, int sReg)
{
RegisterInfo* info = oatGetRegInfo(cUnit, reg);
if ((info->reg == reg) && (info->sReg == sReg) && info->live) {
return; /* already live */
} else if (sReg != INVALID_SREG) {
oatClobberSReg(cUnit, sReg);
if (info->isTemp) {
info->live = true;
}
} else {
/* Can't be live if no associated sReg */
DCHECK(info->isTemp);
info->live = false;
}
info->sReg = sReg;
}
extern void oatMarkTemp(CompilationUnit* cUnit, int reg)
{
RegisterInfo* info = oatGetRegInfo(cUnit, reg);
info->isTemp = true;
}
extern void oatUnmarkTemp(CompilationUnit* cUnit, int reg)
{
RegisterInfo* info = oatGetRegInfo(cUnit, reg);
info->isTemp = false;
}
extern void oatMarkPair(CompilationUnit* cUnit, int lowReg, int highReg)
{
RegisterInfo* infoLo = oatGetRegInfo(cUnit, lowReg);
RegisterInfo* infoHi = oatGetRegInfo(cUnit, highReg);
infoLo->pair = infoHi->pair = true;
infoLo->partner = highReg;
infoHi->partner = lowReg;
}
extern void oatMarkClean(CompilationUnit* cUnit, RegLocation loc)
{
RegisterInfo* info = oatGetRegInfo(cUnit, loc.lowReg);
info->dirty = false;
if (loc.wide) {
info = oatGetRegInfo(cUnit, loc.highReg);
info->dirty = false;
}
}
extern void oatMarkDirty(CompilationUnit* cUnit, RegLocation loc)
{
if (loc.home) {
// If already home, can't be dirty
return;
}
RegisterInfo* info = oatGetRegInfo(cUnit, loc.lowReg);
info->dirty = true;
if (loc.wide) {
info = oatGetRegInfo(cUnit, loc.highReg);
info->dirty = true;
}
}
extern void oatMarkInUse(CompilationUnit* cUnit, int reg)
{
RegisterInfo* info = oatGetRegInfo(cUnit, reg);
info->inUse = true;
}
void copyRegInfo(CompilationUnit* cUnit, int newReg, int oldReg)
{
RegisterInfo* newInfo = oatGetRegInfo(cUnit, newReg);
RegisterInfo* oldInfo = oatGetRegInfo(cUnit, oldReg);
// Target temp status must not change
bool isTemp = newInfo->isTemp;
*newInfo = *oldInfo;
// Restore target's temp status
newInfo->isTemp = isTemp;
newInfo->reg = newReg;
}
/*
* Return an updated location record with current in-register status.
* If the value lives in live temps, reflect that fact. No code
* is generated. If the live value is part of an older pair,
* clobber both low and high.
* TUNING: clobbering both is a bit heavy-handed, but the alternative
* is a bit complex when dealing with FP regs. Examine code to see
* if it's worthwhile trying to be more clever here.
*/
extern RegLocation oatUpdateLoc(CompilationUnit* cUnit, RegLocation loc)
{
DCHECK(!loc.wide);
DCHECK(oatCheckCorePoolSanity(cUnit));
if (loc.location != kLocPhysReg) {
DCHECK((loc.location == kLocDalvikFrame) ||
(loc.location == kLocCompilerTemp));
RegisterInfo* infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
if (infoLo) {
if (infoLo->pair) {
oatClobber(cUnit, infoLo->reg);
oatClobber(cUnit, infoLo->partner);
oatFreeTemp(cUnit, infoLo->reg);
} else {
loc.lowReg = infoLo->reg;
loc.location = kLocPhysReg;
}
}
}
return loc;
}
bool oatCheckCorePoolSanity(CompilationUnit* cUnit)
{
for (static int i = 0; i < cUnit->regPool->numCoreRegs; i++) {
if (cUnit->regPool->coreRegs[i].pair) {
static int myReg = cUnit->regPool->coreRegs[i].reg;
static int mySreg = cUnit->regPool->coreRegs[i].sReg;
static int partnerReg = cUnit->regPool->coreRegs[i].partner;
static RegisterInfo* partner = oatGetRegInfo(cUnit, partnerReg);
DCHECK(partner != NULL);
DCHECK(partner->pair);
DCHECK_EQ(myReg, partner->partner);
static int partnerSreg = partner->sReg;
if (mySreg == INVALID_SREG) {
DCHECK_EQ(partnerSreg, INVALID_SREG);
} else {
int diff = mySreg - partnerSreg;
DCHECK((diff == -1) || (diff == 1));
}
}
if (!cUnit->regPool->coreRegs[i].live) {
DCHECK(cUnit->regPool->coreRegs[i].defStart == NULL);
DCHECK(cUnit->regPool->coreRegs[i].defEnd == NULL);
}
}
return true;
}
/* see comments for updateLoc */
extern RegLocation oatUpdateLocWide(CompilationUnit* cUnit, RegLocation loc)
{
DCHECK(loc.wide);
DCHECK(oatCheckCorePoolSanity(cUnit));
if (loc.location != kLocPhysReg) {
DCHECK((loc.location == kLocDalvikFrame) ||
(loc.location == kLocCompilerTemp));
// Are the dalvik regs already live in physical registers?
RegisterInfo* infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
RegisterInfo* infoHi = allocLive(cUnit,
oatSRegHi(loc.sRegLow), kAnyReg);
bool match = true;
match = match && (infoLo != NULL);
match = match && (infoHi != NULL);
// Are they both core or both FP?
match = match && (oatIsFpReg(infoLo->reg) == oatIsFpReg(infoHi->reg));
// If a pair of floating point singles, are they properly aligned?
if (match && oatIsFpReg(infoLo->reg)) {
match &= ((infoLo->reg & 0x1) == 0);
match &= ((infoHi->reg - infoLo->reg) == 1);
}
// If previously used as a pair, it is the same pair?
if (match && (infoLo->pair || infoHi->pair)) {
match = (infoLo->pair == infoHi->pair);
match &= ((infoLo->reg == infoHi->partner) &&
(infoHi->reg == infoLo->partner));
}
if (match) {
// Can reuse - update the register usage info
loc.lowReg = infoLo->reg;
loc.highReg = infoHi->reg;
loc.location = kLocPhysReg;
oatMarkPair(cUnit, loc.lowReg, loc.highReg);
DCHECK(!oatIsFpReg(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
return loc;
}
// Can't easily reuse - clobber and free any overlaps
if (infoLo) {
oatClobber(cUnit, infoLo->reg);
oatFreeTemp(cUnit, infoLo->reg);
if (infoLo->pair)
oatClobber(cUnit, infoLo->partner);
}
if (infoHi) {
oatClobber(cUnit, infoHi->reg);
oatFreeTemp(cUnit, infoHi->reg);
if (infoHi->pair)
oatClobber(cUnit, infoHi->partner);
}
}
return loc;
}
/* For use in cases we don't know (or care) width */
extern RegLocation oatUpdateRawLoc(CompilationUnit* cUnit, RegLocation loc)
{
if (loc.wide)
return oatUpdateLocWide(cUnit, loc);
else
return oatUpdateLoc(cUnit, loc);
}
RegLocation evalLocWide(CompilationUnit* cUnit, RegLocation loc,
int regClass, bool update)
{
DCHECK(loc.wide);
int newRegs;
int lowReg;
int highReg;
loc = oatUpdateLocWide(cUnit, loc);
/* If already in registers, we can assume proper form. Right reg class? */
if (loc.location == kLocPhysReg) {
DCHECK_EQ(oatIsFpReg(loc.lowReg), oatIsFpReg(loc.highReg));
DCHECK(!oatIsFpReg(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
if (!regClassMatches(regClass, loc.lowReg)) {
/* Wrong register class. Reallocate and copy */
newRegs = oatAllocTypedTempPair(cUnit, loc.fp, regClass);
lowReg = newRegs & 0xff;
highReg = (newRegs >> 8) & 0xff;
oatRegCopyWide(cUnit, lowReg, highReg, loc.lowReg,
loc.highReg);
copyRegInfo(cUnit, lowReg, loc.lowReg);
copyRegInfo(cUnit, highReg, loc.highReg);
oatClobber(cUnit, loc.lowReg);
oatClobber(cUnit, loc.highReg);
loc.lowReg = lowReg;
loc.highReg = highReg;
oatMarkPair(cUnit, loc.lowReg, loc.highReg);
DCHECK(!oatIsFpReg(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
}
return loc;
}
DCHECK_NE(loc.sRegLow, INVALID_SREG);
DCHECK_NE(oatSRegHi(loc.sRegLow), INVALID_SREG);
newRegs = oatAllocTypedTempPair(cUnit, loc.fp, regClass);
loc.lowReg = newRegs & 0xff;
loc.highReg = (newRegs >> 8) & 0xff;
oatMarkPair(cUnit, loc.lowReg, loc.highReg);
if (update) {
loc.location = kLocPhysReg;
oatMarkLive(cUnit, loc.lowReg, loc.sRegLow);
oatMarkLive(cUnit, loc.highReg, oatSRegHi(loc.sRegLow));
}
DCHECK(!oatIsFpReg(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
return loc;
}
extern RegLocation oatEvalLoc(CompilationUnit* cUnit, RegLocation loc,
int regClass, bool update)
{
int newReg;
if (loc.wide)
return evalLocWide(cUnit, loc, regClass, update);
loc = oatUpdateLoc(cUnit, loc);
if (loc.location == kLocPhysReg) {
if (!regClassMatches(regClass, loc.lowReg)) {
/* Wrong register class. Realloc, copy and transfer ownership */
newReg = oatAllocTypedTemp(cUnit, loc.fp, regClass);
oatRegCopy(cUnit, newReg, loc.lowReg);
copyRegInfo(cUnit, newReg, loc.lowReg);
oatClobber(cUnit, loc.lowReg);
loc.lowReg = newReg;
}
return loc;
}
DCHECK_NE(loc.sRegLow, INVALID_SREG);
newReg = oatAllocTypedTemp(cUnit, loc.fp, regClass);
loc.lowReg = newReg;
if (update) {
loc.location = kLocPhysReg;
oatMarkLive(cUnit, loc.lowReg, loc.sRegLow);
}
return loc;
}
extern RegLocation oatGetRawSrc(CompilationUnit* cUnit, MIR* mir, int num)
{
DCHECK(num < mir->ssaRep->numUses);
RegLocation res = cUnit->regLocation[mir->ssaRep->uses[num]];
DCHECK(!res.wide || num < (mir->ssaRep->numUses - 1));
return res;
}
extern RegLocation oatGetRawDest(CompilationUnit* cUnit, MIR* mir)
{
DCHECK(mir->ssaRep->numDefs > 0);
RegLocation res = cUnit->regLocation[mir->ssaRep->defs[0]];
DCHECK(!res.wide || mir->ssaRep->numDefs == 2);
return res;
}
extern RegLocation oatGetDest(CompilationUnit* cUnit, MIR* mir)
{
RegLocation res = oatGetRawDest(cUnit, mir);
DCHECK(!res.wide);
return res;
}
extern RegLocation oatGetSrc(CompilationUnit* cUnit, MIR* mir, int num)
{
RegLocation res = oatGetRawSrc(cUnit, mir, num);
DCHECK(!res.wide);
return res;
}
extern RegLocation oatGetDestWide(CompilationUnit* cUnit, MIR* mir)
{
RegLocation res = oatGetRawDest(cUnit, mir);
DCHECK(res.wide);
return res;
}
extern RegLocation oatGetSrcWide(CompilationUnit* cUnit, MIR* mir,
int low)
{
RegLocation res = oatGetRawSrc(cUnit, mir, low);
DCHECK(res.wide);
return res;
}
/* USE SSA names to count references of base Dalvik vRegs. */
void oatCountRefs(CompilationUnit *cUnit, BasicBlock* bb,
RefCounts* coreCounts, RefCounts* fpCounts)
{
if ((cUnit->disableOpt & (1 << kPromoteRegs)) ||
!((bb->blockType == kEntryBlock) || (bb->blockType == kExitBlock) ||
(bb->blockType == kDalvikByteCode))) {
return;
}
for (int i = 0; i < cUnit->numSSARegs;) {
RegLocation loc = cUnit->regLocation[i];
RefCounts* counts = loc.fp ? fpCounts : coreCounts;
int pMapIdx = SRegToPMap(cUnit, loc.sRegLow);
if (loc.defined) {
counts[pMapIdx].count += cUnit->useCounts.elemList[i];
}
if (loc.wide) {
if (loc.defined) {
if (loc.fp) {
counts[pMapIdx].doubleStart = true;
counts[pMapIdx+1].count += cUnit->useCounts.elemList[i+1];
}
}
i += 2;
} else {
i++;
}
}
}
/* qsort callback function, sort descending */
int oatSortCounts(const void *val1, const void *val2)
{
const RefCounts* op1 = (const RefCounts*)val1;
const RefCounts* op2 = (const RefCounts*)val2;
return (op1->count == op2->count) ? 0 : (op1->count < op2->count ? 1 : -1);
}
void oatDumpCounts(const RefCounts* arr, int size, const char* msg)
{
LOG(INFO) << msg;
for (int i = 0; i < size; i++) {
LOG(INFO) << "sReg[" << arr[i].sReg << "]: " << arr[i].count;
}
}
/*
* Note: some portions of this code required even if the kPromoteRegs
* optimization is disabled.
*/
extern void oatDoPromotion(CompilationUnit* cUnit)
{
int regBias = cUnit->numCompilerTemps + 1;
int dalvikRegs = cUnit->numDalvikRegisters;
int numRegs = dalvikRegs + regBias;
const int promotionThreshold = 2;
// Allow target code to add any special registers
oatAdjustSpillMask(cUnit);
/*
* Simple register promotion. Just do a static count of the uses
* of Dalvik registers. Note that we examine the SSA names, but
* count based on original Dalvik register name. Count refs
* separately based on type in order to give allocation
* preference to fp doubles - which must be allocated sequential
* physical single fp registers started with an even-numbered
* reg.
* TUNING: replace with linear scan once we have the ability
* to describe register live ranges for GC.
*/
RefCounts *coreRegs = (RefCounts *)
oatNew(cUnit, sizeof(RefCounts) * numRegs, true, kAllocRegAlloc);
RefCounts *fpRegs = (RefCounts *)
oatNew(cUnit, sizeof(RefCounts) * numRegs, true, kAllocRegAlloc);
// Set ssa names for original Dalvik registers
for (int i = 0; i < dalvikRegs; i++) {
coreRegs[i].sReg = fpRegs[i].sReg = i;
}
// Set ssa name for Method*
coreRegs[dalvikRegs].sReg = cUnit->methodSReg;
fpRegs[dalvikRegs].sReg = cUnit->methodSReg; // For consistecy
// Set ssa names for compilerTemps
for (int i = 1; i <= cUnit->numCompilerTemps; i++) {
CompilerTemp* ct = (CompilerTemp*)cUnit->compilerTemps.elemList[i];
coreRegs[dalvikRegs + i].sReg = ct->sReg;
fpRegs[dalvikRegs + i].sReg = ct->sReg;
}
GrowableListIterator iterator;
oatGrowableListIteratorInit(&cUnit->blockList, &iterator);
while (true) {
BasicBlock* bb;
bb = (BasicBlock*)oatGrowableListIteratorNext(&iterator);
if (bb == NULL) break;
oatCountRefs(cUnit, bb, coreRegs, fpRegs);
}
/*
* Ideally, we'd allocate doubles starting with an even-numbered
* register. Bias the counts to try to allocate any vreg that's
* used as the start of a pair first.
*/
for (int i = 0; i < numRegs; i++) {
if (fpRegs[i].doubleStart) {
fpRegs[i].count *= 2;
}
}
// Sort the count arrays
qsort(coreRegs, numRegs, sizeof(RefCounts), oatSortCounts);
qsort(fpRegs, numRegs, sizeof(RefCounts), oatSortCounts);
if (cUnit->printMe) {
oatDumpCounts(coreRegs, numRegs, "Core regs after sort");
oatDumpCounts(fpRegs, numRegs, "Fp regs after sort");
}
if (!(cUnit->disableOpt & (1 << kPromoteRegs))) {
// Promote fpRegs
for (int i = 0; (i < numRegs) &&
(fpRegs[i].count >= promotionThreshold ); i++) {
int pMapIdx = SRegToPMap(cUnit, fpRegs[i].sReg);
if (cUnit->promotionMap[pMapIdx].fpLocation != kLocPhysReg) {
int reg = oatAllocPreservedFPReg(cUnit, fpRegs[i].sReg,
fpRegs[i].doubleStart);
if (reg < 0) {
break; // No more left
}
}
}
// Promote core regs
for (int i = 0; (i < numRegs) &&
(coreRegs[i].count > promotionThreshold); i++) {
int pMapIdx = SRegToPMap(cUnit, coreRegs[i].sReg);
if (cUnit->promotionMap[pMapIdx].coreLocation !=
kLocPhysReg) {
int reg = oatAllocPreservedCoreReg(cUnit, coreRegs[i].sReg);
if (reg < 0) {
break; // No more left
}
}
}
} else if (cUnit->qdMode) {
oatAllocPreservedCoreReg(cUnit, cUnit->methodSReg);
for (int i = 0; i < numRegs; i++) {
int reg = oatAllocPreservedCoreReg(cUnit, i);
if (reg < 0) {
break; // No more left
}
}
}
// Now, update SSA names to new home locations
for (int i = 0; i < cUnit->numSSARegs; i++) {
RegLocation *curr = &cUnit->regLocation[i];
int pMapIdx = SRegToPMap(cUnit, curr->sRegLow);
if (!curr->wide) {
if (curr->fp) {
if (cUnit->promotionMap[pMapIdx].fpLocation == kLocPhysReg) {
curr->location = kLocPhysReg;
curr->lowReg = cUnit->promotionMap[pMapIdx].fpReg;
curr->home = true;
}
} else {
if (cUnit->promotionMap[pMapIdx].coreLocation == kLocPhysReg) {
curr->location = kLocPhysReg;
curr->lowReg = cUnit->promotionMap[pMapIdx].coreReg;
curr->home = true;
}
}
curr->highReg = INVALID_REG;
} else {
if (curr->highWord) {
continue;
}
if (curr->fp) {
if ((cUnit->promotionMap[pMapIdx].fpLocation == kLocPhysReg) &&
(cUnit->promotionMap[pMapIdx+1].fpLocation ==
kLocPhysReg)) {
int lowReg = cUnit->promotionMap[pMapIdx].fpReg;
int highReg = cUnit->promotionMap[pMapIdx+1].fpReg;
// Doubles require pair of singles starting at even reg
if (((lowReg & 0x1) == 0) && ((lowReg + 1) == highReg)) {
curr->location = kLocPhysReg;
curr->lowReg = lowReg;
curr->highReg = highReg;
curr->home = true;
}
}
} else {
if ((cUnit->promotionMap[pMapIdx].coreLocation == kLocPhysReg)
&& (cUnit->promotionMap[pMapIdx+1].coreLocation ==
kLocPhysReg)) {
curr->location = kLocPhysReg;
curr->lowReg = cUnit->promotionMap[pMapIdx].coreReg;
curr->highReg = cUnit->promotionMap[pMapIdx+1].coreReg;
curr->home = true;
}
}
}
}
}
/* Returns sp-relative offset in bytes for a VReg */
extern int oatVRegOffset(CompilationUnit* cUnit, int vReg)
{
return Frame::GetVRegOffset(cUnit->code_item, cUnit->coreSpillMask,
cUnit->fpSpillMask, cUnit->frameSize, vReg);
}
/* Returns sp-relative offset in bytes for a SReg */
extern int oatSRegOffset(CompilationUnit* cUnit, int sReg)
{
return oatVRegOffset(cUnit, SRegToVReg(cUnit, sReg));
}
} // namespace art