Quick compiler source reorganizatio - part 1
A step towards cleanup of the quick compiler source. In this
CL we rename all files to Art standards, combine some of the
old target-specific files that may have made sense in the
JIT, but no longer do. Also removed some codegen/<target>/
subdirectories, combined and deleted some existing files.
Still quite a bit of work to do in cleaning up header files,
getting some better consistency in what codegen functions
go where. That will happen in later CLs.
No logic changes in this CL - just renaming and moving stuff around
Change-Id: Ic172cd3b76d4c670f8e4d5fdd4a3e967db3f4c1e
diff --git a/src/compiler/codegen/ralloc_util.cc b/src/compiler/codegen/ralloc_util.cc
new file mode 100644
index 0000000..059d1c3
--- /dev/null
+++ b/src/compiler/codegen/ralloc_util.cc
@@ -0,0 +1,1264 @@
+/*
+ * 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. */
+
+#include "../compiler_utility.h"
+#include "../compiler_ir.h"
+#include "../dataflow.h"
+#include "ralloc.h"
+
+namespace art {
+
+/*
+ * 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;
+ }
+}
+
+void oatRecordCorePromotion(CompilationUnit* cUnit, int reg, int sReg)
+{
+ int pMapIdx = SRegToPMap(cUnit, sReg);
+ int vReg = SRegToVReg(cUnit, sReg);
+ oatGetRegInfo(cUnit, reg)->inUse = true;
+ cUnit->coreSpillMask |= (1 << reg);
+ // Include reg for later sort
+ cUnit->coreVmapTable.push_back(reg << VREG_NUM_WIDTH |
+ (vReg & ((1 << VREG_NUM_WIDTH) - 1)));
+ cUnit->numCoreSpills++;
+ cUnit->promotionMap[pMapIdx].coreLocation = kLocPhysReg;
+ cUnit->promotionMap[pMapIdx].coreReg = reg;
+}
+
+/* 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) {
+ res = coreRegs[i].reg;
+ oatRecordCorePromotion(cUnit, res, sReg);
+ break;
+ }
+ }
+ return res;
+}
+
+void oatRecordFpPromotion(CompilationUnit* cUnit, int reg, int sReg)
+{
+ int pMapIdx = SRegToPMap(cUnit, sReg);
+ int vReg = SRegToVReg(cUnit, sReg);
+ oatGetRegInfo(cUnit, reg)->inUse = true;
+ oatMarkPreservedSingle(cUnit, vReg, reg);
+ cUnit->promotionMap[pMapIdx].fpLocation = kLocPhysReg;
+ cUnit->promotionMap[pMapIdx].fpReg = reg;
+}
+
+/*
+ * 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) {
+ res = FPRegs[i].reg;
+ oatRecordFpPromotion(cUnit, res, sReg);
+ 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]];
+ return res;
+}
+extern RegLocation oatGetRawDest(CompilationUnit* cUnit, MIR* mir)
+{
+ DCHECK_GT(mir->ssaRep->numDefs, 0);
+ RegLocation res = cUnit->regLocation[mir->ssaRep->defs[0]];
+ 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;
+ }
+ }
+ }
+ }
+ if (cUnit->printMe) {
+ oatDumpPromotionMap(cUnit);
+ }
+}
+
+/* Returns sp-relative offset in bytes for a VReg */
+extern int oatVRegOffset(CompilationUnit* cUnit, int vReg)
+{
+ return StackVisitor::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