/*
* Copyright (C) 2012 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.
*/
/*! \file AnalysisO1.cpp
\brief This file implements register allocator, constant folding
*/
#include "libdex/DexOpcodes.h"
#include "libdex/DexFile.h"
#include "Lower.h"
#include "interp/InterpState.h"
#include "interp/InterpDefs.h"
#include "libdex/Leb128.h"
/* compilation flags to turn on debug printout */
//#define DEBUG_COMPILE_TABLE
//#define DEBUG_ALLOC_CONSTRAINT
//#define DEBUG_REGALLOC
//#define DEBUG_REG_USED
//#define DEBUG_REFCOUNT
//#define DEBUG_REACHING_DEF2
//#define DEBUG_REACHING_DEF
//#define DEBUG_LIVE_RANGE
//#define DEBUG_MOVE_OPT
//#define DEBUG_SPILL
//#define DEBUG_ENDOFBB
//#define DEBUG_CONST
/*
#define DEBUG_XFER_POINTS
#define DEBUG_DSE
#define DEBUG_CFG
#define DEBUG_GLOBALTYPE
#define DEBUG_STATE
#define DEBUG_COMPILE_TABLE
#define DEBUG_VIRTUAL_INFO
#define DEBUG_MOVE_OPT
#define DEBUG_MERGE_ENTRY
#define DEBUG_INVALIDATE
*/
#include "AnalysisO1.h"
void dumpCompileTable();
/* There are 3 kinds of variables that are handled in this file:
1> virtual register (isVirtualReg())
2> temporary (!isVirtualReg() && regNum < PhysicalReg_GLUE_DVMDEX)
3> glue variables: regNum >= PhysicalReg_GLUE_DVMDEX
*/
/** check whether a variable is a virtual register
*/
bool isVirtualReg(int type) {
if((type & LowOpndRegType_virtual) != 0) return true;
return false;
}
bool isTemporary(int type, int regNum) {
if(!isVirtualReg(type) && regNum < PhysicalReg_GLUE_DVMDEX) return true;
return false;
}
/** convert type defined in lowering module to type defined in register allocator
in lowering module <type, isPhysical>
in register allocator: LowOpndRegType_hard LowOpndRegType_virtual LowOpndRegType_scratch
*/
int convertType(int type, int reg, bool isPhysical) {
int newType = type;
if(isPhysical) newType |= LowOpndRegType_hard;
if(isVirtualReg(type)) newType |= LowOpndRegType_virtual;
else {
/* reg number for a VR can exceed PhysicalReg_SCRATCH_1 */
if(reg >= PhysicalReg_SCRATCH_1 && reg < PhysicalReg_GLUE_DVMDEX)
newType |= LowOpndRegType_scratch;
}
return newType;
}
/** return the size of a variable
*/
OpndSize getRegSize(int type) {
if((type & MASK_FOR_TYPE) == LowOpndRegType_xmm) return OpndSize_64;
if((type & MASK_FOR_TYPE) == LowOpndRegType_fs) return OpndSize_64;
/* for type _gp, _fs_s, _ss */
return OpndSize_32;
}
/*
Overlapping cases between two variables A and B
layout for A,B isAPartiallyOverlapB isBPartiallyOverlapA
1> |__| |____| OVERLAP_ALIGN OVERLAP_B_COVER_A
|__| |____|
2> |____| OVERLAP_B_IS_LOW_OF_A OVERLAP_B_COVER_LOW_OF_A
|__|
3> |____| OVERLAP_B_IS_HIGH_OF_A OVERLAP_B_COVER_HIGH_OF_A
|__|
4> |____| OVERLAP_LOW_OF_A_IS_HIGH_OF_B OVERLAP_B_COVER_LOW_OF_A
|____|
5> |____| OVERLAP_HIGH_OF_A_IS_LOW_OF_B OVERLAP_B_COVER_HIGH_OF_A
|____|
6> |__| OVERLAP_A_IS_LOW_OF_B OVERLAP_B_COVER_A
|____|
7> |__| OVERLAP_A_IS_HIGH_OF_B OVERLAP_B_COVER_A
|____|
*/
/** determine the overlapping between variable B and A
*/
OverlapCase getBPartiallyOverlapA(int regB, LowOpndRegType tB, int regA, LowOpndRegType tA) {
if(getRegSize(tA) == getRegSize(tB) && regA == regB) return OVERLAP_B_COVER_A;
if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regA == regB) return OVERLAP_B_COVER_LOW_OF_A;
if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regB == regA + 1) return OVERLAP_B_COVER_HIGH_OF_A;
if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && (regA == regB || regA == regB+1)) return OVERLAP_B_COVER_A;
if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regA == regB+1) return OVERLAP_B_COVER_LOW_OF_A;
if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regB == regA+1) return OVERLAP_B_COVER_HIGH_OF_A;
return OVERLAP_NO;
}
/** determine overlapping between variable A and B
*/
OverlapCase getAPartiallyOverlapB(int regA, LowOpndRegType tA, int regB, LowOpndRegType tB) {
if(getRegSize(tA) == getRegSize(tB) && regA == regB) return OVERLAP_ALIGN;
if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regA == regB)
return OVERLAP_B_IS_LOW_OF_A;
if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regB == regA+1)
return OVERLAP_B_IS_HIGH_OF_A;
if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regA == regB+1)
return OVERLAP_LOW_OF_A_IS_HIGH_OF_B;
if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regB == regA+1)
return OVERLAP_HIGH_OF_A_IS_LOW_OF_B;
if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && regA == regB)
return OVERLAP_A_IS_LOW_OF_B;
if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && regA == regB+1)
return OVERLAP_A_IS_HIGH_OF_B;
return OVERLAP_NO;
}
/** determine whether variable A fully covers B
*/
bool isAFullyCoverB(int regA, LowOpndRegType tA, int regB, LowOpndRegType tB) {
if(getRegSize(tB) == OpndSize_32) return true;
if(getRegSize(tA) == getRegSize(tB) && regA == regB) return true;
return false;
}
/*
RegAccessType accessType
1> DefOrUse.accessType
can only be D(VR), L(low part of VR), H(high part of VR), N(none)
for def, it means which part of the VR is live
for use, it means which part of the VR comes from the def
2> VirtualRegInfo.accessType
for currentInfo, it can only be a combination of U & D
for entries in infoBasicBlock, it can be a combination of U & D|L|H
*/
/*
Key data structures used:
1> BasicBlock_O1
VirtualRegInfo infoBasicBlock[]
DefUsePair* defUseTable
XferPoint xferPoints[]
2> MemoryVRInfo memVRTable[]
LiveRange* ranges
3> compileTableEntry compileTable[]
4> VirtualRegInfo
DefOrUse reachingDefs[3]
5> DefUsePair, LiveRange
*/
//! one entry for each variable used
//! a variable can be virtual register, or a temporary (can be hard-coded)
compileTableEntry compileTable[COMPILE_TABLE_SIZE];
int num_compile_entries;
//! tables to save the states of register allocation
regAllocStateEntry1 stateTable1_1[COMPILE_TABLE_SIZE];
regAllocStateEntry1 stateTable1_2[COMPILE_TABLE_SIZE];
regAllocStateEntry1 stateTable1_3[COMPILE_TABLE_SIZE];
regAllocStateEntry1 stateTable1_4[COMPILE_TABLE_SIZE];
regAllocStateEntry2 stateTable2_1[COMPILE_TABLE_SIZE];
regAllocStateEntry2 stateTable2_2[COMPILE_TABLE_SIZE];
regAllocStateEntry2 stateTable2_3[COMPILE_TABLE_SIZE];
regAllocStateEntry2 stateTable2_4[COMPILE_TABLE_SIZE];
//! array of VirtualRegInfo to store VRs accessed by a single bytecode
VirtualRegInfo infoByteCode[MAX_REG_PER_BYTECODE];
int num_regs_per_bytecode;
//! array of TempRegInfo to store temporaries accessed by a single bytecode
TempRegInfo infoByteCodeTemp[MAX_TEMP_REG_PER_BYTECODE];
int num_temp_regs_per_bytecode;
//! array of MemoryVRInfo to store whether a VR is in memory
#define NUM_MEM_VR_ENTRY 140
MemoryVRInfo memVRTable[NUM_MEM_VR_ENTRY];
int num_memory_vr;
CompilationUnit* currentUnit = NULL;
//! the current basic block
BasicBlock_O1* currentBB = NULL;
//! array of RegisterInfo for all the physical registers
RegisterInfo allRegs[PhysicalReg_GLUE+1]; //initialized in codeGen
VirtualRegInfo currentInfo;
VirtualRegInfo tmpInfo;
//! this array says whether a spill location is used (0 means not used, 1 means used)
int spillIndexUsed[MAX_SPILL_JIT_IA];
int indexForGlue = -1;
int num_bbs_for_method;
//! array of basic blocks in a method in program order
BasicBlock_O1* method_bbs_sorted[MAX_NUM_BBS_PER_METHOD];
//! the entry basic block
BasicBlock_O1* bb_entry;
int pc_start = -1;
int pc_end = -1;
//!array of PCs for exception handlers
int exceptionHandlers[10];
int num_exception_handlers;
bool canSpillReg[PhysicalReg_Null]; //physical registers that should not be spilled
int inGetVR_num = -1;
int inGetVR_type;
///////////////////////////////////////////////////////////////////////////////
// FORWARD FUNCTION DECLARATION
void addExceptionHandler(s4 tmp);
int createCFG(Method* method);
int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb);
void dumpVirtualInfoOfBasicBlock(BasicBlock_O1* bb);
void setTypeOfVR();
void insertGlueReg();
void dumpVirtualInfoOfMethod();
int codeGenBasicBlock(const Method* method, BasicBlock_O1* bb);
//used in collectInfoOfBasicBlock: getVirtualRegInfo
int mergeEntry2(BasicBlock_O1* bb);
int sortAllocConstraint(RegAllocConstraint* allocConstraints,
RegAllocConstraint* allocConstraintsSorted, bool fromHighToLow);
//used in codeGenBasicBlock
void insertFromVirtualInfo(BasicBlock_O1* bb, int k); //update compileTable
void insertFromTempInfo(int k); //update compileTable
int updateXferPoints();
void updateLiveTable();
void printDefUseTable();
bool isFirstOfHandler(BasicBlock_O1* bb);
//used in mergeEntry2
//following functions will not update global data structure
RegAccessType mergeAccess2(RegAccessType A, RegAccessType B, OverlapCase isBPartiallyOverlapA);
RegAccessType updateAccess1(RegAccessType A, OverlapCase isAPartiallyOverlapB); //will not update global data structure
RegAccessType updateAccess2(RegAccessType C1, RegAccessType C2);
RegAccessType updateAccess3(RegAccessType C, RegAccessType B);
void updateDefUseTable();
void updateReachingDefA(int indexToA, OverlapCase isBPartiallyOverlapA);
void updateReachingDefB1(int indexToA);
void updateReachingDefB2();
void updateReachingDefB3();
RegAccessType insertAUse(DefUsePair* ptr, int offsetPC, int regNum, LowOpndRegType physicalType);
DefUsePair* insertADef(int offsetPC, int regNum, LowOpndRegType pType, RegAccessType rType);
RegAccessType insertDefUsePair(int reachingDefIndex);
//used in updateXferPoints
int fakeUsageAtEndOfBB(BasicBlock_O1* bb);
void insertLoadXfer(int offset, int regNum, LowOpndRegType pType);
int searchMemTable(int regNum);
void mergeLiveRange(int tableIndex, int rangeStart, int rangeEnd);
//used in updateLiveTable
RegAccessType setAccessTypeOfUse(OverlapCase isDefPartiallyOverlapUse, RegAccessType reachingDefLive);
DefUsePair* searchDefUseTable(int offsetPC, int regNum, LowOpndRegType pType);
void insertAccess(int tableIndex, LiveRange* startP, int rangeStart);
//register allocation
int spillLogicalReg(int spill_index, bool updateTable);
/** check whether the current bytecode is IF or GOTO or SWITCH
*/
bool isCurrentByteCodeJump() {
u2 inst_op = INST_INST(inst);
if(inst_op == OP_IF_EQ || inst_op == OP_IF_NE || inst_op == OP_IF_LT ||
inst_op == OP_IF_GE || inst_op == OP_IF_GT || inst_op == OP_IF_LE) return true;
if(inst_op == OP_IF_EQZ || inst_op == OP_IF_NEZ || inst_op == OP_IF_LTZ ||
inst_op == OP_IF_GEZ || inst_op == OP_IF_GTZ || inst_op == OP_IF_LEZ) return true;
if(inst_op == OP_GOTO || inst_op == OP_GOTO_16 || inst_op == OP_GOTO_32) return true;
if(inst_op == OP_PACKED_SWITCH || inst_op == OP_SPARSE_SWITCH) return true;
return false;
}
/* this function is called before code generation of basic blocks
initialize data structure allRegs, which stores information for each physical register,
whether it is used, when it was last freed, whether it is callee-saved */
void initializeAllRegs() {
int k;
for(k = PhysicalReg_EAX; k <= PhysicalReg_EBP; k++) {
allRegs[k].physicalReg = (PhysicalReg) k;
if(k == PhysicalReg_EDI || k == PhysicalReg_ESP || k == PhysicalReg_EBP)
allRegs[k].isUsed = true;
else {
allRegs[k].isUsed = false;
allRegs[k].freeTimeStamp = -1;
}
if(k == PhysicalReg_EBX || k == PhysicalReg_EBP || k == PhysicalReg_ESI || k == PhysicalReg_EDI)
allRegs[k].isCalleeSaved = true;
else
allRegs[k].isCalleeSaved = false;
}
for(k = PhysicalReg_XMM0; k <= PhysicalReg_XMM7; k++) {
allRegs[k].physicalReg = (PhysicalReg) k;
allRegs[k].isUsed = false;
allRegs[k].freeTimeStamp = -1;
allRegs[k].isCalleeSaved = false;
}
}
/** sync up allRegs (isUsed & freeTimeStamp) with compileTable
global data: RegisterInfo allRegs[PhysicalReg_Null]
update allRegs[EAX to XMM7] except EDI,ESP,EBP
update RegisterInfo.isUsed & RegisterInfo.freeTimeStamp
if the physical register was used and is not used now
*/
void syncAllRegs() {
int k, k2;
for(k = PhysicalReg_EAX; k <= PhysicalReg_XMM7; k++) {
if(k == PhysicalReg_EDI || k == PhysicalReg_ESP || k == PhysicalReg_EBP)
continue;
//check whether the physical register is used by any logical register
bool stillUsed = false;
for(k2 = 0; k2 < num_compile_entries; k2++) {
if(compileTable[k2].physicalReg == k) {
stillUsed = true;
break;
}
}
if(stillUsed && !allRegs[k].isUsed) {
allRegs[k].isUsed = true;
}
if(!stillUsed && allRegs[k].isUsed) {
allRegs[k].isUsed = false;
allRegs[k].freeTimeStamp = lowOpTimeStamp;
}
}
return;
}
//!sync up spillIndexUsed with compileTable
//!
void updateSpillIndexUsed() {
int k;
for(k = 0; k <= MAX_SPILL_JIT_IA-1; k++) spillIndexUsed[k] = 0;
for(k = 0; k < num_compile_entries; k++) {
if(isVirtualReg(compileTable[k].physicalType)) continue;
if(compileTable[k].spill_loc_index >= 0) {
if(compileTable[k].spill_loc_index > 4*(MAX_SPILL_JIT_IA-1))
ALOGE("spill_loc_index is wrong for entry %d: %d",
k, compileTable[k].spill_loc_index);
spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 1;
}
}
}
/* free memory used in all basic blocks */
void freeCFG() {
int k;
for(k = 0; k < num_bbs_for_method; k++) {
/* free defUseTable for method_bbs_sorted[k] */
DefUsePair* ptr = method_bbs_sorted[k]->defUseTable;
while(ptr != NULL) {
DefUsePair* tmp = ptr->next;
/* free ptr->uses */
DefOrUseLink* ptrUse = ptr->uses;
while(ptrUse != NULL) {
DefOrUseLink* tmp2 = ptrUse->next;
free(ptrUse);
ptrUse = tmp2;
}
free(ptr);
ptr = tmp;
}
free(method_bbs_sorted[k]);
}
}
/* update compileTable.physicalReg, compileTable.spill_loc_index & allRegs.isUsed
for glue-related variables, they do not exist
not in a physical register (physicalReg is Null)
not in a spilled memory location (spill_loc_index is -1)
*/
void initializeRegStateOfBB(BasicBlock_O1* bb) {
//for GLUE variables, do not exist
int k;
for(k = 0; k < num_compile_entries; k++) {
/* trace-based JIT: there is no VR with GG type */
if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType == GLOBALTYPE_GG) {
if(bb->bb_index > 0) { //non-entry block
if(isFirstOfHandler(bb)) {
/* at the beginning of an exception handler, GG VR is in the interpreted stack */
compileTable[k].physicalReg = PhysicalReg_Null;
#ifdef DEBUG_COMPILE_TABLE
ALOGI("at the first basic block of an exception handler, GG VR %d type %d is in memory",
compileTable[k].regNum, compileTable[k].physicalType);
#endif
} else {
if(compileTable[k].physicalReg == PhysicalReg_Null) {
/* GG VR is in a specific physical register */
compileTable[k].physicalReg = compileTable[k].physicalReg_prev;
}
int tReg = compileTable[k].physicalReg;
allRegs[tReg].isUsed = true;
#ifdef DEBUG_REG_USED
ALOGI("REGALLOC: physical reg %d is used by a GG VR %d %d at beginning of BB", tReg, compileTable[k].regNum, compileTable[k].physicalType);
#endif
}
} //non-entry block
} //if GG VR
if(compileTable[k].regNum != PhysicalReg_GLUE &&
compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX) {
/* glue related registers */
compileTable[k].physicalReg = PhysicalReg_Null;
compileTable[k].spill_loc_index = -1;
}
}
}
/* update memVRTable[].nullCheckDone */
void initializeNullCheck(int indexToMemVR) {
bool found = false;
#ifdef GLOBAL_NULLCHECK_OPT
/* search nullCheck_inB of the current Basic Block */
for(k = 0; k < nullCheck_inSize[currentBB->bb_index2]; k++) {
if(nullCheck_inB[currentBB->bb_index2][k] == memVRTable[indexToMemVR].regNum) {
found = true;
break;
}
}
#endif
memVRTable[indexToMemVR].nullCheckDone = found;
}
/* initialize memVRTable */
void initializeMemVRTable() {
num_memory_vr = 0;
int k;
for(k = 0; k < num_compile_entries; k++) {
if(!isVirtualReg(compileTable[k].physicalType)) continue;
/* VRs in compileTable */
bool setToInMemory = (compileTable[k].physicalReg == PhysicalReg_Null);
int regNum = compileTable[k].regNum;
OpndSize sizeVR = getRegSize(compileTable[k].physicalType);
/* search memVRTable for the VR in compileTable */
int kk;
int indexL = -1;
int indexH = -1;
for(kk = 0; kk < num_memory_vr; kk++) {
if(memVRTable[kk].regNum == regNum) {
indexL = kk;
continue;
}
if(memVRTable[kk].regNum == regNum+1 && sizeVR == OpndSize_64) {
indexH = kk;
continue;
}
}
if(indexL < 0) {
/* the low half of VR is not in memVRTable
add an entry for the low half in memVRTable */
if(num_memory_vr >= NUM_MEM_VR_ENTRY) {
ALOGE("exceeds size of memVRTable");
dvmAbort();
}
memVRTable[num_memory_vr].regNum = regNum;
memVRTable[num_memory_vr].inMemory = setToInMemory;
initializeNullCheck(num_memory_vr); //set nullCheckDone
memVRTable[num_memory_vr].boundCheck.checkDone = false;
memVRTable[num_memory_vr].num_ranges = 0;
memVRTable[num_memory_vr].ranges = NULL;
memVRTable[num_memory_vr].delayFreeFlags = VRDELAY_NONE;
num_memory_vr++;
}
if(sizeVR == OpndSize_64 && indexH < 0) {
/* the high half of VR is not in memVRTable
add an entry for the high half in memVRTable */
if(num_memory_vr >= NUM_MEM_VR_ENTRY) {
ALOGE("exceeds size of memVRTable");
dvmAbort();
}
memVRTable[num_memory_vr].regNum = regNum+1;
memVRTable[num_memory_vr].inMemory = setToInMemory;
initializeNullCheck(num_memory_vr);
memVRTable[num_memory_vr].boundCheck.checkDone = false;
memVRTable[num_memory_vr].num_ranges = 0;
memVRTable[num_memory_vr].ranges = NULL;
memVRTable[num_memory_vr].delayFreeFlags = VRDELAY_NONE;
num_memory_vr++;
}
}
}
/* create a O1 basic block from basic block constructed in JIT MIR */
BasicBlock_O1* createBasicBlockO1(BasicBlock* bb) {
BasicBlock_O1* bb1 = createBasicBlock(0, -1);
bb1->jitBasicBlock = bb;
return bb1;
}
/* a basic block in JIT MIR can contain bytecodes that are not in program order
for example, a "goto" bytecode will be followed by the goto target */
void preprocessingBB(BasicBlock* bb) {
currentBB = createBasicBlockO1(bb);
/* initialize currentBB->allocConstraints */
int ii;
for(ii = 0; ii < 8; ii++) {
currentBB->allocConstraints[ii].physicalReg = (PhysicalReg)ii;
currentBB->allocConstraints[ii].count = 0;
}
collectInfoOfBasicBlock(currentMethod, currentBB);
#ifdef DEBUG_COMPILE_TABLE
dumpVirtualInfoOfBasicBlock(currentBB);
#endif
currentBB = NULL;
}
void preprocessingTrace() {
int k, k2, k3, jj;
/* this is a simplified verson of setTypeOfVR()
all VRs are assumed to be GL, no VR will be GG
*/
for(k = 0; k < num_bbs_for_method; k++)
for(jj = 0; jj < method_bbs_sorted[k]->num_regs; jj++)
method_bbs_sorted[k]->infoBasicBlock[jj].gType = GLOBALTYPE_GL;
/* insert a glue-related register GLUE_DVMDEX to compileTable */
insertGlueReg();
int compile_entries_old = num_compile_entries;
for(k2 = 0; k2 < num_bbs_for_method; k2++) {
currentBB = method_bbs_sorted[k2];
/* update compileTable with virtual register from currentBB */
for(k3 = 0; k3 < currentBB->num_regs; k3++) {
insertFromVirtualInfo(currentBB, k3);
}
/* for each GL|GG type VR, insert fake usage at end of basic block to keep it live */
int offsetPC_back = offsetPC;
offsetPC = PC_FOR_END_OF_BB;
for(k = 0; k < num_compile_entries; k++) {
currentInfo.regNum = compileTable[k].regNum;
currentInfo.physicalType = (LowOpndRegType)compileTable[k].physicalType;
if(isVirtualReg(compileTable[k].physicalType) &&
compileTable[k].gType == GLOBALTYPE_GL) {
/* update defUseTable by assuming a fake usage at END of a basic block for variable @ currentInfo */
fakeUsageAtEndOfBB(currentBB);
}
if(isVirtualReg(compileTable[k].physicalType) &&
compileTable[k].gType == GLOBALTYPE_GG) {
fakeUsageAtEndOfBB(currentBB);
}
}
offsetPC = offsetPC_back;
num_compile_entries = compile_entries_old;
}
/* initialize data structure allRegs */
initializeAllRegs();
#ifdef DEBUG_COMPILE_TABLE
dumpCompileTable();
#endif
currentBB = NULL;
}
void printJitTraceInfoAtRunTime(const Method* method, int offset) {
ALOGI("execute trace for %s%s at offset %x", method->clazz->descriptor, method->name, offset);
}
void startOfTraceO1(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId, CompilationUnit *cUnit) {
num_exception_handlers = 0;
num_compile_entries = 0;
currentBB = NULL;
pc_start = -1;
bb_entry = NULL;
num_bbs_for_method = 0;
currentUnit = cUnit;
lowOpTimeStamp = 0;
// dumpDebuggingInfo is gone in CompilationUnit struct
#if 0
/* add code to dump debugging information */
if(cUnit->dumpDebuggingInfo) {
move_imm_to_mem(OpndSize_32, cUnit->startOffset, -4, PhysicalReg_ESP, true); //2nd argument: offset
move_imm_to_mem(OpndSize_32, (int)currentMethod, -8, PhysicalReg_ESP, true); //1st argument: method
load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
typedef void (*vmHelper)(const Method*, int);
vmHelper funcPtr = printJitTraceInfoAtRunTime;
move_imm_to_reg(OpndSize_32, (int)funcPtr, PhysicalReg_ECX, true);
call_reg(PhysicalReg_ECX, true);
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
}
#endif
}
/* Code generation for a basic block defined for JIT
We have two data structures for a basic block:
BasicBlock defined in vm/compiler by JIT
BasicBlock_O1 defined in o1 */
int codeGenBasicBlockJit(const Method* method, BasicBlock* bb) {
/* search method_bbs_sorted to find the O1 basic block corresponding to bb */
int k;
for(k = 0; k < num_bbs_for_method; k++) {
if(method_bbs_sorted[k]->jitBasicBlock == bb) {
lowOpTimeStamp = 0; //reset time stamp at start of a basic block
currentBB = method_bbs_sorted[k];
int cg_ret = codeGenBasicBlock(method, currentBB);
currentBB = NULL;
return cg_ret;
}
}
ALOGE("can't find the corresponding O1 basic block for id %d type %d",
bb->id, bb->blockType);
return -1;
}
void endOfBasicBlock(BasicBlock* bb) {
isScratchPhysical = true;
currentBB = NULL;
}
void endOfTraceO1() {
freeCFG();
}
/** entry point to collect information about virtual registers used in a basic block
Initialize data structure BasicBlock_O1
The usage information of virtual registers is stoerd in bb->infoBasicBlock
Global variables accessed: offsetPC, rPC
*/
int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb) {
bb->num_regs = 0;
bb->num_defs = 0;
bb->defUseTable = NULL;
bb->defUseTail = NULL;
u2* rPC_start = (u2*)method->insns;
int kk;
bb->endsWithReturn = false;
bb->hasAccessToGlue = false;
MIR* mir;
int seqNum = 0;
/* traverse the MIR in basic block
sequence number is used to make sure next bytecode will have a larger sequence number */
for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
offsetPC = seqNum;
mir->seqNum = seqNum++;
rPC = rPC_start + mir->offset;
#ifdef WITH_JIT_INLINING
if(mir->dalvikInsn.opcode >= kMirOpFirst &&
mir->dalvikInsn.opcode != kMirOpCheckInlinePrediction) continue;
if(ir->dalvikInsn.opcode == kMirOpCheckInlinePrediction) { //TODO
}
#else
if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) continue;
#endif
inst = FETCH(0);
u2 inst_op = INST_INST(inst);
/* update bb->hasAccessToGlue */
if((inst_op >= OP_MOVE_RESULT && inst_op <= OP_RETURN_OBJECT) ||
(inst_op >= OP_MONITOR_ENTER && inst_op <= OP_INSTANCE_OF) ||
(inst_op == OP_FILLED_NEW_ARRAY) ||
(inst_op == OP_FILLED_NEW_ARRAY_RANGE) ||
(inst_op == OP_THROW) ||
(inst_op >= OP_INVOKE_VIRTUAL && inst_op <= OP_INVOKE_INTERFACE_RANGE) ||
(inst_op >= OP_THROW_VERIFICATION_ERROR &&
inst_op <= OP_EXECUTE_INLINE_RANGE) ||
(inst_op >= OP_INVOKE_VIRTUAL_QUICK && inst_op <= OP_INVOKE_SUPER_QUICK_RANGE))
bb->hasAccessToGlue = true;
/* update bb->endsWithReturn */
if(inst_op == OP_RETURN_VOID || inst_op == OP_RETURN || inst_op == OP_RETURN_VOID_BARRIER ||
inst_op == OP_RETURN_OBJECT || inst_op == OP_RETURN_WIDE)
bb->endsWithReturn = true;
/* get virtual register usage in current bytecode */
getVirtualRegInfo(infoByteCode);
int num_regs = num_regs_per_bytecode;
for(kk = 0; kk < num_regs; kk++) {
currentInfo = infoByteCode[kk];
#ifdef DEBUG_MERGE_ENTRY
ALOGI("call mergeEntry2 at offsetPC %x kk %d VR %d %d", offsetPC, kk,
currentInfo.regNum, currentInfo.physicalType);
#endif
mergeEntry2(bb); //update defUseTable of the basic block
}
//dumpVirtualInfoOfBasicBlock(bb);
}//for each bytecode
bb->pc_end = seqNum;
//sort allocConstraints of each basic block
for(kk = 0; kk < bb->num_regs; kk++) {
#ifdef DEBUG_ALLOC_CONSTRAINT
ALOGI("sort virtual reg %d type %d -------", bb->infoBasicBlock[kk].regNum,
bb->infoBasicBlock[kk].physicalType);
#endif
sortAllocConstraint(bb->infoBasicBlock[kk].allocConstraints,
bb->infoBasicBlock[kk].allocConstraintsSorted, true);
}
#ifdef DEBUG_ALLOC_CONSTRAINT
ALOGI("sort constraints for BB %d --------", bb->bb_index);
#endif
sortAllocConstraint(bb->allocConstraints, bb->allocConstraintsSorted, false);
return 0;
}
/** entry point to generate native code for a O1 basic block
There are 3 kinds of virtual registers in a O1 basic block:
1> L VR: local within the basic block
2> GG VR: is live in other basic blocks,
its content is in a pre-defined GPR at the beginning of a basic block
3> GL VR: is live in other basic blocks,
its content is in the interpreted stack at the beginning of a basic block
compileTable is updated with infoBasicBlock at the start of the basic block;
Before lowering each bytecode, compileTable is updated with infoByteCodeTemp;
At end of the basic block, right before the jump instruction, handles constant VRs and GG VRs
*/
int codeGenBasicBlock(const Method* method, BasicBlock_O1* bb) {
/* we assume at the beginning of each basic block,
all GL VRs reside in memory and all GG VRs reside in predefined physical registers,
so at the end of a basic block, recover a spilled GG VR, store a GL VR to memory */
/* update compileTable with entries in bb->infoBasicBlock */
int k;
for(k = 0; k < bb->num_regs; k++) {
insertFromVirtualInfo(bb, k);
}
updateXferPoints(); //call fakeUsageAtEndOfBB
#ifdef DEBUG_REACHING_DEF
printDefUseTable();
#endif
#ifdef DSE_OPT
removeDeadDefs();
printDefUseTable();
#endif
//clear const section of compileTable
for(k = 0; k < num_compile_entries; k++) compileTable[k].isConst = false;
num_const_vr = 0;
#ifdef DEBUG_COMPILE_TABLE
ALOGI("At start of basic block %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
dumpCompileTable();
#endif
initializeRegStateOfBB(bb);
initializeMemVRTable();
updateLiveTable();
freeReg(true); //before code gen of a basic block, also called at end of a basic block?
#ifdef DEBUG_COMPILE_TABLE
ALOGI("At start of basic block %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
#endif
u2* rPC_start = (u2*)method->insns;
bool lastByteCodeIsJump = false;
MIR* mir;
for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
offsetPC = mir->seqNum;
rPC = rPC_start + mir->offset;
#ifdef WITH_JIT_INLINING
if(mir->dalvikInsn.opcode >= kMirOpFirst &&
mir->dalvikInsn.opcode != kMirOpCheckInlinePrediction) {
#else
if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) {
#endif
handleExtendedMIR(currentUnit, mir);
continue;
}
inst = FETCH(0);
//before handling a bytecode, import info of temporary registers to compileTable including refCount
num_temp_regs_per_bytecode = getTempRegInfo(infoByteCodeTemp);
for(k = 0; k < num_temp_regs_per_bytecode; k++) {
if(infoByteCodeTemp[k].versionNum > 0) continue;
insertFromTempInfo(k);
}
startNativeCode(-1, -1);
for(k = 0; k <= MAX_SPILL_JIT_IA-1; k++) spillIndexUsed[k] = 0;
//update spillIndexUsed if a glue variable was spilled
for(k = 0; k < num_compile_entries; k++) {
if(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX) {
if(compileTable[k].spill_loc_index >= 0)
spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 1;
}
}
#ifdef DEBUG_COMPILE_TABLE
ALOGI("compile table size after importing temporary info %d", num_compile_entries);
ALOGI("before one bytecode %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
#endif
//set isConst to true for CONST & MOVE MOVE_OBJ?
//clear isConst to true for MOVE, MOVE_OBJ, MOVE_RESULT, MOVE_EXCEPTION ...
bool isConst = getConstInfo(bb); //will reset isConst if a VR is updated by the bytecode
bool isDeadStmt = false;
#ifdef DSE_OPT
for(k = 0; k < num_dead_pc; k++) {
if(deadPCs[k] == offsetPC) {
isDeadStmt = true;
break;
}
}
#endif
getVirtualRegInfo(infoByteCode);
//call something similar to mergeEntry2, but only update refCount
//clear refCount
for(k = 0; k < num_regs_per_bytecode; k++) {
int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
infoByteCode[k].regNum);
if(indexT >= 0)
compileTable[indexT].refCount = 0;
}
for(k = 0; k < num_regs_per_bytecode; k++) {
int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
infoByteCode[k].regNum);
if(indexT >= 0)
compileTable[indexT].refCount += infoByteCode[k].refCount;
} //for k
#ifdef DSE_OPT
if(isDeadStmt) { //search compileTable
getVirtualRegInfo(infoByteCode);
#ifdef DEBUG_DSE
ALOGI("DSE: stmt at offsetPC %d is dead", offsetPC);
#endif
for(k = 0; k < num_regs_per_bytecode; k++) {
int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
infoByteCode[k].regNum);
if(indexT >= 0)
compileTable[indexT].refCount -= infoByteCode[k].refCount;
}
}
#endif
lastByteCodeIsJump = false;
if(!isConst && !isDeadStmt) //isDeadStmt is false when DSE_OPT is not enabled
{
#ifdef DEBUG_COMPILE_TABLE
dumpCompileTable();
#endif
globalShortMap = NULL;
if(isCurrentByteCodeJump()) lastByteCodeIsJump = true;
//lowerByteCode will call globalVREndOfBB if it is jump
int retCode = lowerByteCodeJit(method, rPC, mir);
if(gDvmJit.codeCacheByteUsed + (stream - streamStart) +
CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
ALOGE("JIT code cache full");
gDvmJit.codeCacheFull = true;
return -1;
}
if (retCode == 1) {
ALOGE("JIT couldn't compile %s%s dex_pc=%d", method->clazz->descriptor, method->name, offsetPC);
return -1;
}
updateConstInfo(bb);
freeShortMap();
if(retCode < 0) {
ALOGE("error in lowering the bytecode");
return retCode;
}
freeReg(true); //may dump GL VR to memory (this is necessary)
//after each bytecode, make sure non-VRs have refCount of zero
for(k = 0; k < num_compile_entries; k++) {
if(isTemporary(compileTable[k].physicalType, compileTable[k].regNum)) {
#ifdef PRINT_WARNING
if(compileTable[k].refCount > 0) {
ALOGW("refCount for a temporary reg %d %d is %d after a bytecode", compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
}
#endif
compileTable[k].refCount = 0;
}
}
} else { //isConst || isDeadStmt
//if this bytecode is the target of a jump, the mapFromBCtoNCG should be updated
offsetNCG = stream - streamMethodStart;
mapFromBCtoNCG[offsetPC] = offsetNCG;
#ifdef DEBUG_COMPILE_TABLE
ALOGI("this bytecode generates a constant and has no side effect");
#endif
freeReg(true); //may dump GL VR to memory (this is necessary)
}
#ifdef DEBUG_COMPILE_TABLE
ALOGI("after one bytecode BB %d (num of VRs %d)", bb->bb_index, bb->num_regs);
#endif
}//for each bytecode
#ifdef DEBUG_COMPILE_TABLE
dumpCompileTable();
#endif
if(!lastByteCodeIsJump) constVREndOfBB();
//at end of a basic block, get spilled GG VR & dump GL VR
if(!lastByteCodeIsJump) globalVREndOfBB(method);
//remove entries for temporary registers, L VR and GL VR
int jj;
for(k = 0; k < num_compile_entries; ) {
bool removeEntry = false;
if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType != GLOBALTYPE_GG) {
removeEntry = true;
}
if(isTemporary(compileTable[k].physicalType, compileTable[k].regNum))
removeEntry = true;
if(removeEntry) {
#ifdef PRINT_WARNING
if(compileTable[k].refCount > 0)
ALOGW("refCount for REG %d %d is %d at end of a basic block", compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
#endif
compileTable[k].refCount = 0;
for(jj = k+1; jj < num_compile_entries; jj++) {
compileTable[jj-1] = compileTable[jj];
}
num_compile_entries--;
} else {
k++;
}
}
freeReg(true);
//free LIVE TABLE
for(k = 0; k < num_memory_vr; k++) {
LiveRange* ptr2 = memVRTable[k].ranges;
while(ptr2 != NULL) {
LiveRange* tmpP = ptr2->next;
free(ptr2->accessPC);
free(ptr2);
ptr2 = tmpP;
}
}
#ifdef DEBUG_COMPILE_TABLE
ALOGI("At end of basic block -------");
dumpCompileTable();
#endif
return 0;
}
/** update infoBasicBlock & defUseTable
input: currentInfo
side effect: update currentInfo.reachingDefs
update entries in infoBasicBlock by calling updateReachingDefA
if there is no entry in infoBasicBlock for B, an entry will be created and inserted to infoBasicBlock
defUseTable is updated to account for the access at currentInfo
if accessType of B is U or UD, we call updateReachingDefB to update currentInfo.reachingDefs
in order to correctly insert the usage to defUseTable
*/
int mergeEntry2(BasicBlock_O1* bb) {
LowOpndRegType typeB = currentInfo.physicalType;
int regB = currentInfo.regNum;
int jj, k;
int jjend = bb->num_regs;
bool isMerged = false;
bool hasAlias = false;
OverlapCase isBPartiallyOverlapA, isAPartiallyOverlapB;
RegAccessType tmpType = REGACCESS_N;
currentInfo.num_reaching_defs = 0;
/* traverse variable A in infoBasicBlock */
for(jj = 0; jj < jjend; jj++) {
int regA = bb->infoBasicBlock[jj].regNum;
LowOpndRegType typeA = bb->infoBasicBlock[jj].physicalType;
isBPartiallyOverlapA = getBPartiallyOverlapA(regB, typeB, regA, typeA);
isAPartiallyOverlapB = getAPartiallyOverlapB(regA, typeA, regB, typeB);
if(regA == regB && typeA == typeB) {
/* variable A and B are aligned */
bb->infoBasicBlock[jj].accessType = mergeAccess2(bb->infoBasicBlock[jj].accessType, currentInfo.accessType,
OVERLAP_B_COVER_A);
bb->infoBasicBlock[jj].refCount += currentInfo.refCount;
/* copy reaching defs of variable B from variable A */
currentInfo.num_reaching_defs = bb->infoBasicBlock[jj].num_reaching_defs;
for(k = 0; k < currentInfo.num_reaching_defs; k++)
currentInfo.reachingDefs[k] = bb->infoBasicBlock[jj].reachingDefs[k];
updateDefUseTable(); //use currentInfo to update defUseTable
updateReachingDefA(jj, OVERLAP_B_COVER_A); //update reachingDefs of A
isMerged = true;
hasAlias = true;
if(typeB == LowOpndRegType_gp) {
//merge allocConstraints
for(k = 0; k < 8; k++) {
bb->infoBasicBlock[jj].allocConstraints[k].count += currentInfo.allocConstraints[k].count;
}
}
}
else if(isBPartiallyOverlapA != OVERLAP_NO) {
tmpType = updateAccess2(tmpType, updateAccess1(bb->infoBasicBlock[jj].accessType, isAPartiallyOverlapB));
bb->infoBasicBlock[jj].accessType = mergeAccess2(bb->infoBasicBlock[jj].accessType, currentInfo.accessType,
isBPartiallyOverlapA);
#ifdef DEBUG_MERGE_ENTRY
ALOGI("update accessType in case 2: VR %d %d accessType %d", regA, typeA, bb->infoBasicBlock[jj].accessType);
#endif
hasAlias = true;
if(currentInfo.accessType == REGACCESS_U || currentInfo.accessType == REGACCESS_UD) {
/* update currentInfo.reachingDefs */
updateReachingDefB1(jj);
updateReachingDefB2();
}
updateReachingDefA(jj, isBPartiallyOverlapA);
}
else {
//even if B does not overlap with A, B can affect the reaching defs of A
//for example, B is a def of "v0", A is "v1"
// B can kill some reaching defs of A or affect the accessType of a reaching def
updateReachingDefA(jj, OVERLAP_NO); //update reachingDefs of A
}
}//for each variable A in infoBasicBlock
if(!isMerged) {
/* create a new entry in infoBasicBlock */
bb->infoBasicBlock[bb->num_regs].refCount = currentInfo.refCount;
bb->infoBasicBlock[bb->num_regs].physicalType = typeB;
if(hasAlias)
bb->infoBasicBlock[bb->num_regs].accessType = updateAccess3(tmpType, currentInfo.accessType);
else
bb->infoBasicBlock[bb->num_regs].accessType = currentInfo.accessType;
#ifdef DEBUG_MERGE_ENTRY
ALOGI("update accessType in case 3: VR %d %d accessType %d", regB, typeB, bb->infoBasicBlock[bb->num_regs].accessType);
#endif
bb->infoBasicBlock[bb->num_regs].regNum = regB;
for(k = 0; k < 8; k++)
bb->infoBasicBlock[bb->num_regs].allocConstraints[k] = currentInfo.allocConstraints[k];
#ifdef DEBUG_MERGE_ENTRY
ALOGI("isMerged is false, call updateDefUseTable");
#endif
updateDefUseTable(); //use currentInfo to update defUseTable
updateReachingDefB3(); //update currentInfo.reachingDefs if currentInfo defines variable B
//copy from currentInfo.reachingDefs to bb->infoBasicBlock[bb->num_regs]
bb->infoBasicBlock[bb->num_regs].num_reaching_defs = currentInfo.num_reaching_defs;
for(k = 0; k < currentInfo.num_reaching_defs; k++)
bb->infoBasicBlock[bb->num_regs].reachingDefs[k] = currentInfo.reachingDefs[k];
#ifdef DEBUG_MERGE_ENTRY
ALOGI("try to update reaching defs for VR %d %d", regB, typeB);
for(k = 0; k < bb->infoBasicBlock[bb->num_regs].num_reaching_defs; k++)
ALOGI("reaching def %d @ %d for VR %d %d access %d", k, currentInfo.reachingDefs[k].offsetPC,
currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType,
currentInfo.reachingDefs[k].accessType);
#endif
bb->num_regs++;
if(bb->num_regs >= MAX_REG_PER_BASICBLOCK) {
ALOGE("too many VRs in a basic block");
dvmAbort();
}
return -1;
}
return 0;
}
//!update reaching defs for infoBasicBlock[indexToA]
//!use currentInfo.reachingDefs to update reaching defs for variable A
void updateReachingDefA(int indexToA, OverlapCase isBPartiallyOverlapA) {
if(indexToA < 0) return;
int k, k2;
OverlapCase isBPartiallyOverlapDef;
if(currentInfo.accessType == REGACCESS_U) {
return; //no update to reachingDefs of the VR
}
/* access in currentInfo is DU, D, or UD */
if(isBPartiallyOverlapA == OVERLAP_B_COVER_A) {
/* from this point on, the reachingDefs for variable A is a single def to currentInfo at offsetPC */
currentBB->infoBasicBlock[indexToA].num_reaching_defs = 1;
currentBB->infoBasicBlock[indexToA].reachingDefs[0].offsetPC = offsetPC;
currentBB->infoBasicBlock[indexToA].reachingDefs[0].regNum = currentInfo.regNum;
currentBB->infoBasicBlock[indexToA].reachingDefs[0].physicalType = currentInfo.physicalType;
currentBB->infoBasicBlock[indexToA].reachingDefs[0].accessType = REGACCESS_D;
#ifdef DEBUG_REACHING_DEF
ALOGI("single reaching def @ %d for VR %d %d", offsetPC, currentInfo.regNum, currentInfo.physicalType);
#endif
return;
}
/* update reachingDefs for variable A to get rid of dead defs */
/* Bug fix: it is possible that more than one reaching defs need to be removed
after one reaching def is removed, num_reaching_defs--, but k should not change
*/
for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; ) {
/* remove one reaching def in one interation of the loop */
//check overlapping between def & B
isBPartiallyOverlapDef = getBPartiallyOverlapA(currentInfo.regNum, currentInfo.physicalType,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType);
#ifdef DEBUG_REACHING_DEF
ALOGI("DEBUG B %d %d def %d %d %d", currentInfo.regNum, currentInfo.physicalType,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType);
#endif
/* cases where one def nees to be removed:
if B fully covers def, def is removed
if B overlaps high half of def & def's accessType is H, def is removed
if B overlaps low half of def & def's accessType is L, def is removed
*/
if((isBPartiallyOverlapDef == OVERLAP_B_COVER_HIGH_OF_A &&
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType == REGACCESS_H) ||
(isBPartiallyOverlapDef == OVERLAP_B_COVER_LOW_OF_A &&
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType == REGACCESS_L) ||
isBPartiallyOverlapDef == OVERLAP_B_COVER_A
) { //remove def
//shift from k+1 to end
for(k2 = k+1; k2 < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k2++)
currentBB->infoBasicBlock[indexToA].reachingDefs[k2-1] = currentBB->infoBasicBlock[indexToA].reachingDefs[k2];
currentBB->infoBasicBlock[indexToA].num_reaching_defs--;
}
/*
if B overlaps high half of def & def's accessType is not H --> update accessType of def
*/
else if(isBPartiallyOverlapDef == OVERLAP_B_COVER_HIGH_OF_A &&
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType != REGACCESS_H) {
//low half is still valid
if(getRegSize(currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType) == OpndSize_32)
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_D;
else
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_L;
#ifdef DEBUG_REACHING_DEF
ALOGI("DEBUG: set accessType of def to L");
#endif
k++;
}
/*
if B overlaps low half of def & def's accessType is not L --> update accessType of def
*/
else if(isBPartiallyOverlapDef == OVERLAP_B_COVER_LOW_OF_A &&
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType != REGACCESS_L) {
//high half of def is still valid
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_H;
#ifdef DEBUG_REACHING_DEF
ALOGI("DEBUG: set accessType of def to H");
#endif
k++;
}
else {
k++;
}
}//for k
if(isBPartiallyOverlapA != OVERLAP_NO) {
//insert the def to variable @ currentInfo
k = currentBB->infoBasicBlock[indexToA].num_reaching_defs;
if(k >= 3) {
ALOGE("more than 3 reaching defs");
}
currentBB->infoBasicBlock[indexToA].reachingDefs[k].offsetPC = offsetPC;
currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum = currentInfo.regNum;
currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType = currentInfo.physicalType;
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_D;
currentBB->infoBasicBlock[indexToA].num_reaching_defs++;
}
#ifdef DEBUG_REACHING_DEF2
ALOGI("IN updateReachingDefA for VR %d %d", currentBB->infoBasicBlock[indexToA].regNum,
currentBB->infoBasicBlock[indexToA].physicalType);
for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k++)
ALOGI("reaching def %d @ %d for VR %d %d access %d", k,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].offsetPC,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType);
#endif
}
/** Given a variable B @currentInfo,
updates its reaching defs by checking reaching defs of variable A @currentBB->infoBasicBlock[indexToA]
The result is stored in tmpInfo.reachingDefs
*/
void updateReachingDefB1(int indexToA) {
if(indexToA < 0) return;
int k;
tmpInfo.num_reaching_defs = 0;
for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k++) {
/* go through reachingDefs of variable A @currentBB->infoBasicBlock[indexToA]
for each def, check whether it overlaps with variable B @currentInfo
if the def overlaps with variable B, insert it to tmpInfo.reachingDefs
*/
OverlapCase isDefPartiallyOverlapB = getAPartiallyOverlapB(
currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
currentInfo.regNum, currentInfo.physicalType
);
bool insert1 = false; //whether to insert the def to tmpInfo.reachingDefs
if(isDefPartiallyOverlapB == OVERLAP_ALIGN ||
isDefPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B ||
isDefPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B) {
/* B aligns with def */
/* def is low half of B, def is high half of B
in these two cases, def is 32 bits */
insert1 = true;
}
RegAccessType deftype = currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType;
if(isDefPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A ||
isDefPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B) {
/* B is the low half of def */
/* the low half of def is the high half of B */
if(deftype != REGACCESS_H) insert1 = true;
}
if(isDefPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A ||
isDefPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B) {
/* B is the high half of def */
/* the high half of def is the low half of B */
if(deftype != REGACCESS_L) insert1 = true;
}
if(insert1) {
if(tmpInfo.num_reaching_defs >= 3) {
ALOGE("more than 3 reaching defs for tmpInfo");
}
tmpInfo.reachingDefs[tmpInfo.num_reaching_defs] = currentBB->infoBasicBlock[indexToA].reachingDefs[k];
tmpInfo.num_reaching_defs++;
#ifdef DEBUG_REACHING_DEF2
ALOGI("insert from entry %d %d: index %d", currentBB->infoBasicBlock[indexToA].regNum,
currentBB->infoBasicBlock[indexToA].physicalType, k);
#endif
}
}
}
/** update currentInfo.reachingDefs by merging currentInfo.reachingDefs with tmpInfo.reachingDefs
*/
void updateReachingDefB2() {
int k, k2;
for(k2 = 0; k2 < tmpInfo.num_reaching_defs; k2++ ) {
bool merged = false;
for(k = 0; k < currentInfo.num_reaching_defs; k++) {
/* check whether it is the same def, if yes, do nothing */
if(currentInfo.reachingDefs[k].regNum == tmpInfo.reachingDefs[k2].regNum &&
currentInfo.reachingDefs[k].physicalType == tmpInfo.reachingDefs[k2].physicalType) {
merged = true;
if(currentInfo.reachingDefs[k].offsetPC != tmpInfo.reachingDefs[k2].offsetPC) {
ALOGE("defs on the same VR %d %d with different offsetPC %d vs %d",
currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType,
currentInfo.reachingDefs[k].offsetPC, tmpInfo.reachingDefs[k2].offsetPC);
}
if(currentInfo.reachingDefs[k].accessType != tmpInfo.reachingDefs[k2].accessType)
ALOGE("defs on the same VR %d %d with different accessType",
currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType);
break;
}
}
if(!merged) {
if(currentInfo.num_reaching_defs >= 3) {
ALOGE("more than 3 reaching defs for currentInfo");
}
currentInfo.reachingDefs[currentInfo.num_reaching_defs] = tmpInfo.reachingDefs[k2];
currentInfo.num_reaching_defs++;
}
}
}
//!update currentInfo.reachingDefs with currentInfo if variable is defined in currentInfo
//!
void updateReachingDefB3() {
if(currentInfo.accessType == REGACCESS_U) {
return; //no need to update currentInfo.reachingDefs
}
currentInfo.num_reaching_defs = 1;
currentInfo.reachingDefs[0].regNum = currentInfo.regNum;
currentInfo.reachingDefs[0].physicalType = currentInfo.physicalType;
currentInfo.reachingDefs[0].offsetPC = offsetPC;
currentInfo.reachingDefs[0].accessType = REGACCESS_D;
}
/** update defUseTable by checking currentInfo
*/
void updateDefUseTable() {
/* no access */
if(currentInfo.accessType == REGACCESS_N) return;
/* define then use, or define only */
if(currentInfo.accessType == REGACCESS_DU || currentInfo.accessType == REGACCESS_D) {
/* insert a definition at offsetPC to variable @ currentInfo */
DefUsePair* ptr = insertADef(offsetPC, currentInfo.regNum, currentInfo.physicalType, REGACCESS_D);
if(currentInfo.accessType != REGACCESS_D) {
/* if access is define then use, insert a use at offsetPC */
insertAUse(ptr, offsetPC, currentInfo.regNum, currentInfo.physicalType);
}
return;
}
/* use only or use then define
check the reaching defs for the usage */
int k;
bool isLCovered = false, isHCovered = false, isDCovered = false;
for(k = 0; k < currentInfo.num_reaching_defs; k++) {
/* insert a def currentInfo.reachingDefs[k] and a use of variable at offsetPC */
RegAccessType useType = insertDefUsePair(k);
if(useType == REGACCESS_D) isDCovered = true;
if(useType == REGACCESS_L) isLCovered = true;
if(useType == REGACCESS_H) isHCovered = true;
}
OpndSize useSize = getRegSize(currentInfo.physicalType);
if((!isDCovered) && (!isLCovered)) {
/* the low half of variable is not defined in the basic block
so insert a def to the low half at START of the basic block */
insertDefUsePair(-1);
}
if(useSize == OpndSize_64 && (!isDCovered) && (!isHCovered)) {
/* the high half of variable is not defined in the basic block
so insert a def to the high half at START of the basic block */
insertDefUsePair(-2);
}
if(currentInfo.accessType == REGACCESS_UD) {
/* insert a def at offsetPC to variable @ currentInfo */
insertADef(offsetPC, currentInfo.regNum, currentInfo.physicalType, REGACCESS_D);
return;
}
}
//! insert a use at offsetPC of given variable at end of DefUsePair
//!
RegAccessType insertAUse(DefUsePair* ptr, int offsetPC, int regNum, LowOpndRegType physicalType) {
DefOrUseLink* tLink = (DefOrUseLink*)malloc(sizeof(DefOrUseLink));
if(tLink == NULL) {
ALOGE("Memory allocation failed");
return REGACCESS_UNKNOWN;
}
tLink->offsetPC = offsetPC;
tLink->regNum = regNum;
tLink->physicalType = physicalType;
tLink->next = NULL;
if(ptr->useTail != NULL)
ptr->useTail->next = tLink;
ptr->useTail = tLink;
if(ptr->uses == NULL)
ptr->uses = tLink;
ptr->num_uses++;
//check whether the def is partially overlapping with the variable
OverlapCase isDefPartiallyOverlapB = getBPartiallyOverlapA(ptr->def.regNum,
ptr->def.physicalType,
regNum, physicalType);
RegAccessType useType = setAccessTypeOfUse(isDefPartiallyOverlapB, ptr->def.accessType);
tLink->accessType = useType;
return useType;
}
//! insert a def to currentBB->defUseTable
//! update currentBB->defUseTail if necessary
DefUsePair* insertADef(int offsetPC, int regNum, LowOpndRegType pType, RegAccessType rType) {
DefUsePair* ptr = (DefUsePair*)malloc(sizeof(DefUsePair));
if(ptr == NULL) {
ALOGE("Memory allocation failed");
return NULL;
}
ptr->next = NULL;
ptr->def.offsetPC = offsetPC;
ptr->def.regNum = regNum;
ptr->def.physicalType = pType;
ptr->def.accessType = rType;
ptr->num_uses = 0;
ptr->useTail = NULL;
ptr->uses = NULL;
if(currentBB->defUseTail != NULL) {
currentBB->defUseTail->next = ptr;
}
currentBB->defUseTail = ptr;
if(currentBB->defUseTable == NULL)
currentBB->defUseTable = ptr;
currentBB->num_defs++;
#ifdef DEBUG_REACHING_DEF
ALOGI("insert a def at %d to defUseTable for VR %d %d", offsetPC,
regNum, pType);
#endif
return ptr;
}
/** insert a def to defUseTable, then insert a use of variable @ currentInfo
if reachingDefIndex >= 0, the def is currentInfo.reachingDefs[index]
if reachingDefIndex is -1, the low half is defined at START of the basic block
if reachingDefIndex is -2, the high half is defined at START of the basic block
*/
RegAccessType insertDefUsePair(int reachingDefIndex) {
int k = reachingDefIndex;
DefUsePair* tableIndex = NULL;
DefOrUse theDef;
theDef.regNum = 0;
if(k < 0) {
/* def at start of the basic blcok */
theDef.offsetPC = PC_FOR_START_OF_BB;
theDef.accessType = REGACCESS_D;
if(k == -1) //low half of variable
theDef.regNum = currentInfo.regNum;
if(k == -2) //high half of variable
theDef.regNum = currentInfo.regNum+1;
theDef.physicalType = LowOpndRegType_gp;
}
else {
theDef = currentInfo.reachingDefs[k];
}
tableIndex = searchDefUseTable(theDef.offsetPC, theDef.regNum, theDef.physicalType);
if(tableIndex == NULL) //insert an entry
tableIndex = insertADef(theDef.offsetPC, theDef.regNum, theDef.physicalType, theDef.accessType);
else
tableIndex->def.accessType = theDef.accessType;
RegAccessType useType = insertAUse(tableIndex, offsetPC, currentInfo.regNum, currentInfo.physicalType);
return useType;
}
//! insert a XFER_MEM_TO_XMM to currentBB->xferPoints
//!
void insertLoadXfer(int offset, int regNum, LowOpndRegType pType) {
//check whether it is already in currentBB->xferPoints
int k;
for(k = 0; k < currentBB->num_xfer_points; k++) {
if(currentBB->xferPoints[k].xtype == XFER_MEM_TO_XMM &&
currentBB->xferPoints[k].offsetPC == offset &&
currentBB->xferPoints[k].regNum == regNum &&
currentBB->xferPoints[k].physicalType == pType)
return;
}
currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_MEM_TO_XMM;
currentBB->xferPoints[currentBB->num_xfer_points].regNum = regNum;
currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = offset;
currentBB->xferPoints[currentBB->num_xfer_points].physicalType = pType;
#ifdef DEBUG_XFER_POINTS
ALOGI("insert to xferPoints %d: XFER_MEM_TO_XMM of VR %d %d at %d", currentBB->num_xfer_points, regNum, pType, offset);
#endif
currentBB->num_xfer_points++;
if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
ALOGE("too many xfer points");
dvmAbort();
}
}
/** update defUseTable by assuming a fake usage at END of a basic block for variable @ currentInfo
create a fake usage at end of a basic block for variable B (currentInfo.physicalType, currentInfo.regNum)
get reaching def info for variable B and store the info in currentInfo.reachingDefs
for each virtual register (variable A) accessed in the basic block
update reaching defs of B by checking reaching defs of variable A
update defUseTable
*/
int fakeUsageAtEndOfBB(BasicBlock_O1* bb) {
currentInfo.accessType = REGACCESS_U;
LowOpndRegType typeB = currentInfo.physicalType;
int regB = currentInfo.regNum;
int jj, k;
currentInfo.num_reaching_defs = 0;
for(jj = 0; jj < bb->num_regs; jj++) {
int regA = bb->infoBasicBlock[jj].regNum;
LowOpndRegType typeA = bb->infoBasicBlock[jj].physicalType;
OverlapCase isBPartiallyOverlapA = getBPartiallyOverlapA(regB, typeB, regA, typeA);
if(regA == regB && typeA == typeB) {
/* copy reachingDefs from variable A */
currentInfo.num_reaching_defs = bb->infoBasicBlock[jj].num_reaching_defs;
for(k = 0; k < currentInfo.num_reaching_defs; k++)
currentInfo.reachingDefs[k] = bb->infoBasicBlock[jj].reachingDefs[k];
break;
}
else if(isBPartiallyOverlapA != OVERLAP_NO) {
/* B overlaps with A */
/* update reaching defs of variable B by checking reaching defs of bb->infoBasicBlock[jj] */
updateReachingDefB1(jj);
updateReachingDefB2(); //merge currentInfo with tmpInfo
}
}
/* update defUseTable by checking currentInfo */
updateDefUseTable();
return 0;
}
/** update xferPoints of currentBB
Traverse currentBB->defUseTable
*/
int updateXferPoints() {
int k = 0;
currentBB->num_xfer_points = 0;
DefUsePair* ptr = currentBB->defUseTable;
DefOrUseLink* ptrUse = NULL;
/* traverse the def use chain of the basic block */
while(ptr != NULL) {
LowOpndRegType defType = ptr->def.physicalType;
//if definition is for a variable of 32 bits
if(getRegSize(defType) == OpndSize_32) {
/* check usages of the definition, whether it reaches a GPR, a XMM, a FS, or a SS */
bool hasGpUsage = false;
bool hasGpUsage2 = false; //not a fake usage
bool hasXmmUsage = false;
bool hasFSUsage = false;
bool hasSSUsage = false;
ptrUse = ptr->uses;
while(ptrUse != NULL) {
if(ptrUse->physicalType == LowOpndRegType_gp) {
hasGpUsage = true;
if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
hasGpUsage2 = true;
}
if(ptrUse->physicalType == LowOpndRegType_ss) hasSSUsage = true;
if(ptrUse->physicalType == LowOpndRegType_fs ||
ptrUse->physicalType == LowOpndRegType_fs_s)
hasFSUsage = true;
if(ptrUse->physicalType == LowOpndRegType_xmm) {
hasXmmUsage = true;
}
if(ptrUse->physicalType == LowOpndRegType_xmm ||
ptrUse->physicalType == LowOpndRegType_ss) {
/* if a 32-bit definition reaches a xmm usage or a SS usage,
insert a XFER_MEM_TO_XMM */
insertLoadXfer(ptrUse->offsetPC,
ptrUse->regNum, LowOpndRegType_xmm);
}
ptrUse = ptrUse->next;
}
if(((hasXmmUsage || hasFSUsage || hasSSUsage) && defType == LowOpndRegType_gp) ||
(hasGpUsage && defType == LowOpndRegType_fs) ||
(defType == LowOpndRegType_ss && (hasGpUsage || hasXmmUsage || hasFSUsage))) {
/* insert a transfer if def is on a GPR, usage is on a XMM, FS or SS
if def is on a FS, usage is on a GPR
if def is on a SS, usage is on a GPR, XMM or FS
transfer type is XFER_DEF_TO_GP_MEM if a real GPR usage exisits
transfer type is XFER_DEF_TO_GP otherwise*/
currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = ptr->def.offsetPC;
currentBB->xferPoints[currentBB->num_xfer_points].regNum = ptr->def.regNum;
currentBB->xferPoints[currentBB->num_xfer_points].physicalType = ptr->def.physicalType;
if(hasGpUsage2) { //create an entry XFER_DEF_TO_GP_MEM
currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_TO_GP_MEM;
}
else { //create an entry XFER_DEF_TO_MEM
currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_TO_MEM;
}
currentBB->xferPoints[currentBB->num_xfer_points].tableIndex = k;
#ifdef DEBUG_XFER_POINTS
ALOGI("insert XFER %d at def %d: V%d %d", currentBB->num_xfer_points, ptr->def.offsetPC, ptr->def.regNum, defType);
#endif
currentBB->num_xfer_points++;
if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
ALOGE("too many xfer points");
dvmAbort();
}
}
}
else { /* def is on 64 bits */
bool hasGpUsageOfL = false; //exist a GPR usage of the low half
bool hasGpUsageOfH = false; //exist a GPR usage of the high half
bool hasGpUsageOfL2 = false;
bool hasGpUsageOfH2 = false;
bool hasMisaligned = false;
bool hasAligned = false;
bool hasFSUsage = false;
bool hasSSUsage = false;
ptrUse = ptr->uses;
while(ptrUse != NULL) {
if(ptrUse->physicalType == LowOpndRegType_gp &&
ptrUse->regNum == ptr->def.regNum) {
hasGpUsageOfL = true;
if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
hasGpUsageOfL2 = true;
}
if(ptrUse->physicalType == LowOpndRegType_gp &&
ptrUse->regNum == ptr->def.regNum + 1) {
hasGpUsageOfH = true;
if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
hasGpUsageOfH2 = true;
}
if(ptrUse->physicalType == LowOpndRegType_xmm &&
ptrUse->regNum == ptr->def.regNum) {
hasAligned = true;
/* if def is on FS and use is on XMM, insert a XFER_MEM_TO_XMM */
if(defType == LowOpndRegType_fs)
insertLoadXfer(ptrUse->offsetPC,
ptrUse->regNum, LowOpndRegType_xmm);
}
if(ptrUse->physicalType == LowOpndRegType_fs ||
ptrUse->physicalType == LowOpndRegType_fs_s)
hasFSUsage = true;
if(ptrUse->physicalType == LowOpndRegType_xmm &&
ptrUse->regNum != ptr->def.regNum) {
hasMisaligned = true;
/* if use is on XMM and use and def are misaligned, insert a XFER_MEM_TO_XMM */
insertLoadXfer(ptrUse->offsetPC,
ptrUse->regNum, LowOpndRegType_xmm);
}
if(ptrUse->physicalType == LowOpndRegType_ss) {
hasSSUsage = true;
/* if use is on SS, insert a XFER_MEM_TO_XMM */
insertLoadXfer(ptrUse->offsetPC,
ptrUse->regNum, LowOpndRegType_ss);
}
ptrUse = ptrUse->next;
}
if(defType == LowOpndRegType_fs && !hasGpUsageOfL && !hasGpUsageOfH) {
ptr = ptr->next;
continue;
}
if(defType == LowOpndRegType_xmm && !hasFSUsage &&
!hasGpUsageOfL && !hasGpUsageOfH && !hasMisaligned && !hasSSUsage) {
ptr = ptr->next;
continue;
}
/* insert a XFER_DEF_IS_XMM */
currentBB->xferPoints[currentBB->num_xfer_points].regNum = ptr->def.regNum;
currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = ptr->def.offsetPC;
currentBB->xferPoints[currentBB->num_xfer_points].physicalType = ptr->def.physicalType;
currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_IS_XMM;
currentBB->xferPoints[currentBB->num_xfer_points].vr_gpl = -1;
currentBB->xferPoints[currentBB->num_xfer_points].vr_gph = -1;
if(hasGpUsageOfL2) currentBB->xferPoints[currentBB->num_xfer_points].vr_gpl = ptr->def.regNum;
if(hasGpUsageOfH2) currentBB->xferPoints[currentBB->num_xfer_points].vr_gph = ptr->def.regNum+1;
currentBB->xferPoints[currentBB->num_xfer_points].dumpToMem = true;
currentBB->xferPoints[currentBB->num_xfer_points].dumpToXmm = false; //not used in updateVirtualReg
if(hasAligned) currentBB->xferPoints[currentBB->num_xfer_points].dumpToXmm = true;
currentBB->xferPoints[currentBB->num_xfer_points].tableIndex = k;
#ifdef DEBUG_XFER_POINTS
ALOGI("insert XFER %d at def %d: V%d %d", currentBB->num_xfer_points, ptr->def.offsetPC, ptr->def.regNum, defType);
#endif
currentBB->num_xfer_points++;
if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
ALOGE("too many xfer points");
dvmAbort();
}
}
ptr = ptr->next;
} //while ptr
#ifdef DEBUG_XFER_POINTS
ALOGI("XFER points for current basic block ------");
for(k = 0; k < currentBB->num_xfer_points; k++) {
ALOGI(" at offset %x, VR %d %d: type %d, vr_gpl %d, vr_gph %d, dumpToMem %d, dumpToXmm %d",
currentBB->xferPoints[k].offsetPC, currentBB->xferPoints[k].regNum,
currentBB->xferPoints[k].physicalType, currentBB->xferPoints[k].xtype,
currentBB->xferPoints[k].vr_gpl, currentBB->xferPoints[k].vr_gph,
currentBB->xferPoints[k].dumpToMem, currentBB->xferPoints[k].dumpToXmm);
}
#endif
return -1;
}
//! update memVRTable[].ranges by browsing the defUseTable
//! each virtual register has a list of live ranges, and each live range has a list of PCs that access the VR
void updateLiveTable() {
DefUsePair* ptr = currentBB->defUseTable;
while(ptr != NULL) {
bool updateUse = false;
if(ptr->num_uses == 0) {
ptr->num_uses = 1;
ptr->uses = (DefOrUseLink*)malloc(sizeof(DefOrUseLink));
if(ptr->uses == NULL) {
ALOGE("Memory allocation failed");
return;
}
ptr->uses->accessType = REGACCESS_D;
ptr->uses->regNum = ptr->def.regNum;
ptr->uses->offsetPC = ptr->def.offsetPC;
ptr->uses->physicalType = ptr->def.physicalType;
ptr->uses->next = NULL;
ptr->useTail = ptr->uses;
updateUse = true;
}
DefOrUseLink* ptrUse = ptr->uses;
while(ptrUse != NULL) {
RegAccessType useType = ptrUse->accessType;
if(useType == REGACCESS_L || useType == REGACCESS_D) {
int indexL = searchMemTable(ptrUse->regNum);
if(indexL >= 0)
mergeLiveRange(indexL, ptr->def.offsetPC,
ptrUse->offsetPC); //tableIndex, start PC, end PC
}
if(getRegSize(ptrUse->physicalType) == OpndSize_64 &&
(useType == REGACCESS_H || useType == REGACCESS_D)) {
int indexH = searchMemTable(ptrUse->regNum+1);
if(indexH >= 0)
mergeLiveRange(indexH, ptr->def.offsetPC,
ptrUse->offsetPC);
}
ptrUse = ptrUse->next;
}//while ptrUse
if(updateUse) {
ptr->num_uses = 0;
free(ptr->uses);
ptr->uses = NULL;
ptr->useTail = NULL;
}
ptr = ptr->next;
}//while ptr
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVE TABLE");
for(int k = 0; k < num_memory_vr; k++) {
ALOGI("VR %d live ", memVRTable[k].regNum);
LiveRange* ptr = memVRTable[k].ranges;
while(ptr != NULL) {
ALOGI("[%x %x] (", ptr->start, ptr->end);
for(int k3 = 0; k3 < ptr->num_access; k3++)
ALOGI("%x ", ptr->accessPC[k3]);
ALOGI(") ");
ptr = ptr->next;
}
ALOGI("");
}
#endif
}
//!add a live range [rangeStart, rangeEnd] to ranges of memVRTable, merge to existing live ranges if necessary
//!ranges are in increasing order of startPC
void mergeLiveRange(int tableIndex, int rangeStart, int rangeEnd) {
if(rangeStart == PC_FOR_START_OF_BB) rangeStart = currentBB->pc_start;
if(rangeEnd == PC_FOR_END_OF_BB) rangeEnd = currentBB->pc_end;
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVERANGE call mergeLiveRange on tableIndex %d with [%x %x]", tableIndex, rangeStart, rangeEnd);
#endif
int startIndex = -1, endIndex = -1;
bool startBeforeRange = false, endBeforeRange = false; //before the index or in the range
bool startDone = false, endDone = false;
LiveRange* ptr = memVRTable[tableIndex].ranges;
LiveRange* ptrStart = NULL;
LiveRange* ptrStart_prev = NULL;
LiveRange* ptrEnd = NULL;
LiveRange* ptrEnd_prev = NULL;
int k = 0;
while(ptr != NULL) {
if(!startDone) {
if(ptr->start <= rangeStart &&
ptr->end >= rangeStart) {
startIndex = k;
ptrStart = ptr;
startBeforeRange = false;
startDone = true;
}
else if(ptr->start > rangeStart) {
startIndex = k;
ptrStart = ptr;
startBeforeRange = true;
startDone = true;
}
}
if(!startDone) ptrStart_prev = ptr;
if(!endDone) {
if(ptr->start <= rangeEnd &&
ptr->end >= rangeEnd) {
endIndex = k;
ptrEnd = ptr;
endBeforeRange = false;
endDone = true;
}
else if(ptr->start > rangeEnd) {
endIndex = k;
ptrEnd = ptr;
endBeforeRange = true;
endDone = true;
}
}
if(!endDone) ptrEnd_prev = ptr;
ptr = ptr->next;
k++;
} //while
if(!startDone) { //both can be NULL
startIndex = memVRTable[tableIndex].num_ranges;
ptrStart = NULL; //ptrStart_prev should be the last live range
startBeforeRange = true;
}
//if endDone, ptrEnd is not NULL, ptrEnd_prev can be NULL
if(!endDone) { //both can be NULL
endIndex = memVRTable[tableIndex].num_ranges;
ptrEnd = NULL;
endBeforeRange = true;
}
if(startIndex == endIndex && startBeforeRange && endBeforeRange) { //insert at startIndex
//3 cases depending on BeforeRange when startIndex == endIndex
//insert only if both true
//merge otherwise
/////////// insert before ptrStart
LiveRange* currRange = (LiveRange *)malloc(sizeof(LiveRange));
if(ptrStart_prev == NULL) {
currRange->next = memVRTable[tableIndex].ranges;
memVRTable[tableIndex].ranges = currRange;
} else {
currRange->next = ptrStart_prev->next;
ptrStart_prev->next = currRange;
}
currRange->start = rangeStart;
currRange->end = rangeEnd;
currRange->accessPC = (int *)malloc(sizeof(int) * NUM_ACCESS_IN_LIVERANGE);
currRange->num_alloc = NUM_ACCESS_IN_LIVERANGE;
if(rangeStart != rangeEnd) {
currRange->num_access = 2;
currRange->accessPC[0] = rangeStart;
currRange->accessPC[1] = rangeEnd;
} else {
currRange->num_access = 1;
currRange->accessPC[0] = rangeStart;
}
memVRTable[tableIndex].num_ranges++;
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVERANGE insert one live range [%x %x] to tableIndex %d", rangeStart, rangeEnd, tableIndex);
#endif
return;
}
if(!endBeforeRange) { //here ptrEnd is not NULL
endIndex++; //next
ptrEnd_prev = ptrEnd; //ptrEnd_prev is not NULL
ptrEnd = ptrEnd->next; //ptrEnd can be NULL
}
if(endIndex < startIndex+1) ALOGE("mergeLiveRange endIndex %d startIndex %d", endIndex, startIndex);
///////// use ptrStart & ptrEnd_prev
if(ptrStart == NULL || ptrEnd_prev == NULL) {
ALOGE("mergeLiveRange ptr is NULL");
return;
}
//endIndex > startIndex (merge the ranges between startIndex and endIndex-1)
//update ptrStart
if(ptrStart->start > rangeStart)
ptrStart->start = rangeStart; //min of old start & rangeStart
ptrStart->end = ptrEnd_prev->end; //max of old end & rangeEnd
if(rangeEnd > ptrStart->end)
ptrStart->end = rangeEnd;
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVERANGE merge entries for tableIndex %d from %d to %d", tableIndex, startIndex+1, endIndex-1);
#endif
if(ptrStart->num_access <= 0) ALOGE("mergeLiveRange number of access");
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVERANGE tableIndex %d startIndex %d num_access %d (", tableIndex, startIndex, ptrStart->num_access);
for(k = 0; k < ptrStart->num_access; k++)
ALOGI("%x ", ptrStart->accessPC[k]);
ALOGI(")");
#endif
///// go through pointers from ptrStart->next to ptrEnd
//from startIndex+1 to endIndex-1
ptr = ptrStart->next;
while(ptr != NULL && ptr != ptrEnd) {
int k2;
for(k2 = 0; k2 < ptr->num_access; k2++) { //merge to startIndex
insertAccess(tableIndex, ptrStart, ptr->accessPC[k2]);
}//k2
ptr = ptr->next;
}
insertAccess(tableIndex, ptrStart, rangeStart);
insertAccess(tableIndex, ptrStart, rangeEnd);
//remove startIndex+1 to endIndex-1
if(startIndex+1 < endIndex) {
ptr = ptrStart->next;
while(ptr != NULL && ptr != ptrEnd) {
LiveRange* tmpP = ptr->next;
free(ptr->accessPC);
free(ptr);
ptr = tmpP;
}
ptrStart->next = ptrEnd;
}
memVRTable[tableIndex].num_ranges -= (endIndex - startIndex - 1);
#ifdef DEBUG_LIVE_RANGE
ALOGI("num_ranges for VR %d: %d", memVRTable[tableIndex].regNum, memVRTable[tableIndex].num_ranges);
#endif
}
//! insert an access to a given live range, in order
//!
void insertAccess(int tableIndex, LiveRange* startP, int rangeStart) {
int k3, k4;
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVERANGE insertAccess %d %x", tableIndex, rangeStart);
#endif
int insertIndex = -1;
for(k3 = 0; k3 < startP->num_access; k3++) {
if(startP->accessPC[k3] == rangeStart) {
return;
}
if(startP->accessPC[k3] > rangeStart) {
insertIndex = k3;
break;
}
}
//insert here
k3 = insertIndex;
if(insertIndex == -1) {
k3 = startP->num_access;
}
if(startP->num_access == startP->num_alloc) {
int currentAlloc = startP->num_alloc;
startP->num_alloc += NUM_ACCESS_IN_LIVERANGE;
int* tmpPtr = (int *)malloc(sizeof(int) * startP->num_alloc);
for(k4 = 0; k4 < currentAlloc; k4++)
tmpPtr[k4] = startP->accessPC[k4];
free(startP->accessPC);
startP->accessPC = tmpPtr;
}
//insert accessPC
for(k4 = startP->num_access-1; k4 >= k3; k4--)
startP->accessPC[k4+1] = startP->accessPC[k4];
startP->accessPC[k3] = rangeStart;
#ifdef DEBUG_LIVE_RANGE
ALOGI("LIVERANGE insert %x to tableIndex %d", rangeStart, tableIndex);
#endif
startP->num_access++;
return;
}
/////////////////////////////////////////////////////////////////////
bool isInMemory(int regNum, OpndSize size);
void setVRToMemory(int regNum, OpndSize size);
bool isVRLive(int vA);
int getSpillIndex(bool isGLUE, OpndSize size);
void clearVRToMemory(int regNum, OpndSize size);
void clearVRNullCheck(int regNum, OpndSize size);
inline int getSpillLocDisp(int offset) {
#ifdef SPILL_IN_THREAD
return offset+offsetof(Thread, spillRegion);;
#else
return offset+offEBP_spill;
#endif
}
#if 0
/* used if we keep self pointer in a physical register */
inline int getSpillLocReg(int offset) {
return PhysicalReg_Glue;
}
#endif
#ifdef SPILL_IN_THREAD
inline void loadFromSpillRegion_with_self(OpndSize size, int reg_self, bool selfPhysical, int reg, int offset) {
/* only 1 instruction is generated by move_mem_to_reg_noalloc */
move_mem_to_reg_noalloc(size,
getSpillLocDisp(offset), reg_self, selfPhysical,
MemoryAccess_SPILL, offset,
reg, true);
}
inline void loadFromSpillRegion(OpndSize size, int reg, int offset) {
get_self_pointer(C_SCRATCH_1, isScratchPhysical);
int reg_self = registerAlloc(LowOpndRegType_scratch, C_SCRATCH_1, isScratchPhysical, false);
/* only 1 instruction is generated by move_mem_to_reg_noalloc */
move_mem_to_reg_noalloc(size,
getSpillLocDisp(offset), reg_self, true,
MemoryAccess_SPILL, offset,
reg, true);
}
inline void saveToSpillRegion_with_self(OpndSize size, int selfReg, bool selfPhysical, int reg, int offset) {
move_reg_to_mem_noalloc(size,
reg, true,
getSpillLocDisp(offset), selfReg, selfPhysical,
MemoryAccess_SPILL, offset);
}
inline void saveToSpillRegion(OpndSize size, int reg, int offset) {
get_self_pointer(C_SCRATCH_1, isScratchPhysical);
int reg_self = registerAlloc(LowOpndRegType_scratch, C_SCRATCH_1, isScratchPhysical, false);
move_reg_to_mem_noalloc(size,
reg, true,
getSpillLocDisp(offset), reg_self, true,
MemoryAccess_SPILL, offset);
}
#else
inline void loadFromSpillRegion(OpndSize size, int reg, int offset) {
/* only 1 instruction is generated by move_mem_to_reg_noalloc */
move_mem_to_reg_noalloc(size,
getSpillLocDisp(offset), PhysicalReg_EBP, true,
MemoryAccess_SPILL, offset,
reg, true);
}
inline void saveToSpillRegion(OpndSize size, int reg, int offset) {
move_reg_to_mem_noalloc(size,
reg, true,
getSpillLocDisp(offset), PhysicalReg_EBP, true,
MemoryAccess_SPILL, offset);
}
#endif
//! dump an immediate to memory, set inMemory to true
//!
void dumpImmToMem(int vrNum, OpndSize size, int value) {
if(isInMemory(vrNum, size)) {
#ifdef DEBUG_SPILL
ALOGI("Skip dumpImmToMem vA %d size %d", vrNum, size);
#endif
return;
}
set_VR_to_imm_noalloc(vrNum, size, value);
setVRToMemory(vrNum, size);
}
//! dump content of a VR to memory, set inMemory to true
//!
void dumpToMem(int vrNum, LowOpndRegType type, int regAll) { //ss,gp,xmm
if(isInMemory(vrNum, getRegSize(type))) {
#ifdef DEBUG_SPILL
ALOGI("Skip dumpToMem vA %d type %d", vrNum, type);
#endif
return;
}
if(type == LowOpndRegType_gp || type == LowOpndRegType_xmm)
set_virtual_reg_noalloc(vrNum, getRegSize(type), regAll, true);
if(type == LowOpndRegType_ss)
move_ss_reg_to_mem_noalloc(regAll, true,
4*vrNum, PhysicalReg_FP, true,
MemoryAccess_VR, vrNum);
setVRToMemory(vrNum, getRegSize(type));
}
//! dump part of a 64-bit VR to memory and update inMemory
//! isLow tells whether low half or high half is dumped
void dumpPartToMem(int reg /*xmm physical reg*/, int vA, bool isLow) {
if(isLow) {
if(isInMemory(vA, OpndSize_32)) {
#ifdef DEBUG_SPILL
ALOGI("Skip dumpPartToMem isLow %d vA %d", isLow, vA);
#endif
return;
}
}
else {
if(isInMemory(vA+1, OpndSize_32)) {
#ifdef DEBUG_SPILL
ALOGI("Skip dumpPartToMem isLow %d vA %d", isLow, vA);
#endif
return;
}
}
if(isLow) {
if(!isVRLive(vA)) return;
}
else {
if(!isVRLive(vA+1)) return;
}
//move part to vA or vA+1
if(isLow) {
move_ss_reg_to_mem_noalloc(reg, true,
4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
} else {
int k = getSpillIndex(false, OpndSize_64);
//H, L in 4*k+4 & 4*k
#ifdef SPILL_IN_THREAD
get_self_pointer(PhysicalReg_SCRATCH_1, isScratchPhysical);
saveToSpillRegion_with_self(OpndSize_64, PhysicalReg_SCRATCH_1, isScratchPhysical, reg, 4*k);
//update low 32 bits of xmm reg from 4*k+4
move_ss_mem_to_reg(NULL,
getSpillLocDisp(4*k+4), PhysicalReg_SCRATCH_1, isScratchPhysical,
reg, true);
#else
saveToSpillRegion(OpndSize_64, reg, 4*k);
//update low 32 bits of xmm reg from 4*k+4
move_ss_mem_to_reg_noalloc(
getSpillLocDisp(4*k+4), PhysicalReg_EBP, true,
MemoryAccess_SPILL, 4*k+4,
reg, true);
#endif
//move low 32 bits of xmm reg to vA+1
move_ss_reg_to_mem_noalloc(reg, true, 4*(vA+1), PhysicalReg_FP, true, MemoryAccess_VR, vA+1);
}
if(isLow)
setVRToMemory(vA, OpndSize_32);
else
setVRToMemory(vA+1, OpndSize_32);
}
void clearVRBoundCheck(int regNum, OpndSize size);
//! the content of a VR is no longer in memory or in physical register if the latest content of a VR is constant
//! clear nullCheckDone; if another VR is overlapped with the given VR, the content of that VR is no longer in physical register
void invalidateVRDueToConst(int reg, OpndSize size) {
clearVRToMemory(reg, size); //memory content is out-dated
clearVRNullCheck(reg, size);
clearVRBoundCheck(reg, size);
//check reg,gp reg,ss reg,xmm reg-1,xmm
//if size is 64: check reg+1,gp|ss reg+1,xmm
int index;
//if VR is xmm, check whether we need to dump part of VR to memory
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_xmm);
#endif
if(size == OpndSize_32)
dumpPartToMem(compileTable[index].physicalReg, reg, false); //dump high of xmm to memory
compileTable[index].physicalReg = PhysicalReg_Null;
}
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg-1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg-1, LowOpndRegType_xmm);
#endif
dumpPartToMem(compileTable[index].physicalReg, reg-1, true); //dump low of xmm to memory
compileTable[index].physicalReg = PhysicalReg_Null;
}
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_gp);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_ss);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
if(size == OpndSize_64) {
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg+1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_xmm);
#endif
dumpPartToMem(compileTable[index].physicalReg, reg+1, false); //dump high of xmm to memory
compileTable[index].physicalReg = PhysicalReg_Null;
}
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg+1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_gp);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg+1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_ss);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
}
//! check which physical registers hold out-dated content if there is a def
//! if another VR is overlapped with the given VR, the content of that VR is no longer in physical register
//! should we update inMemory?
void invalidateVR(int reg, LowOpndRegType pType) {
//def at fs: content of xmm & gp & ss are out-dated (reg-1,xmm reg,xmm reg+1,xmm) (reg,gp|ss reg+1,gp|ss)
//def at xmm: content of misaligned xmm & gp are out-dated (reg-1,xmm reg+1,xmm) (reg,gp|ss reg+1,gp|ss)
//def at fs_s: content of xmm & gp are out-dated (reg-1,xmm reg,xmm) (reg,gp|ss)
//def at gp: content of xmm is out-dated (reg-1,xmm reg,xmm) (reg,ss)
//def at ss: content of xmm & gp are out-dated (reg-1,xmm reg,xmm) (reg,gp)
int index;
if(pType != LowOpndRegType_xmm) { //check xmm @reg
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_xmm);
#endif
if(getRegSize(pType) == OpndSize_32)
dumpPartToMem(compileTable[index].physicalReg, reg, false); //dump high of xmm to memory
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
//check misaligned xmm @ reg-1
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg-1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg-1, LowOpndRegType_xmm);
#endif
dumpPartToMem(compileTable[index].physicalReg, reg-1, true); //dump low of xmm to memory
compileTable[index].physicalReg = PhysicalReg_Null;
}
//check misaligned xmm @ reg+1
if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
//check reg+1,xmm
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg+1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_xmm);
#endif
dumpPartToMem(compileTable[index].physicalReg, reg+1, false); //dump high of xmm to memory
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
if(pType != LowOpndRegType_gp) {
//check reg,gp
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_gp);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
//check reg+1,gp
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg+1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_gp);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
if(pType != LowOpndRegType_ss) {
//check reg,ss
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_ss);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
//check reg+1,ss
index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg+1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_INVALIDATE
ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_ss);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
}
}
}
//! bookkeeping when a VR is updated
//! invalidate contents of some physical registers, clear nullCheckDone, and update inMemory;
//! check whether there exist tranfer points for this bytecode, if yes, perform the transfer
int updateVirtualReg(int reg, LowOpndRegType pType) {
int k;
OpndSize size = getRegSize(pType);
//WAS only invalidate xmm VRs for the following cases:
//if def reaches a use of vA,xmm and (the def is not xmm or is misaligned xmm)
// invalidate "vA,xmm"
invalidateVR(reg, pType);
clearVRNullCheck(reg, size);
clearVRBoundCheck(reg, size);
if(pType == LowOpndRegType_fs || pType == LowOpndRegType_fs_s)
setVRToMemory(reg, size);
else {
clearVRToMemory(reg, size);
}
for(k = 0; k < currentBB->num_xfer_points; k++) {
if(currentBB->xferPoints[k].offsetPC == offsetPC &&
currentBB->xferPoints[k].regNum == reg &&
currentBB->xferPoints[k].physicalType == pType &&
currentBB->xferPoints[k].xtype != XFER_MEM_TO_XMM) {
//perform the corresponding action for the def
PhysicalReg regAll;
if(currentBB->xferPoints[k].xtype == XFER_DEF_IS_XMM) {
//def at fs: content of xmm is out-dated
//def at xmm: content of misaligned xmm is out-dated
//invalidateXmmVR(currentBB->xferPoints[k].tableIndex);
#ifdef DEBUG_XFER_POINTS
if(currentBB->xferPoints[k].dumpToXmm) ALOGI("XFER set_virtual_reg to xmm: xmm VR %d", reg);
#endif
if(pType == LowOpndRegType_xmm) {
#ifdef DEBUG_XFER_POINTS
ALOGI("XFER set_virtual_reg to memory: xmm VR %d", reg);
#endif
PhysicalReg regAll = (PhysicalReg)checkVirtualReg(reg, LowOpndRegType_xmm, 0 /* do not update*/);
dumpToMem(reg, LowOpndRegType_xmm, regAll);
}
if(currentBB->xferPoints[k].vr_gpl >= 0) { //
}
if(currentBB->xferPoints[k].vr_gph >= 0) {
}
}
if((pType == LowOpndRegType_gp || pType == LowOpndRegType_ss) &&
(currentBB->xferPoints[k].xtype == XFER_DEF_TO_MEM ||
currentBB->xferPoints[k].xtype == XFER_DEF_TO_GP_MEM)) {
//the defined gp VR already in register
//invalidateXmmVR(currentBB->xferPoints[k].tableIndex);
regAll = (PhysicalReg)checkVirtualReg(reg, pType, 0 /* do not update*/);
dumpToMem(reg, pType, regAll);
#ifdef DEBUG_XFER_POINTS
ALOGI("XFER set_virtual_reg to memory: gp VR %d", reg);
#endif
}
if((pType == LowOpndRegType_fs_s || pType == LowOpndRegType_ss) &&
currentBB->xferPoints[k].xtype == XFER_DEF_TO_GP_MEM) {
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////
//REGISTER ALLOCATION
int spillForHardReg(int regNum, int type);
void decreaseRefCount(int index);
int getFreeReg(int type, int reg, int indexToCompileTable);
PhysicalReg spillForLogicalReg(int type, int reg, int indexToCompileTable);
int unspillLogicalReg(int spill_index, int physicalReg);
int searchVirtualInfoOfBB(LowOpndRegType type, int regNum, BasicBlock_O1* bb);
bool isTemp8Bit(int type, int reg);
bool matchType(int typeA, int typeB);
int getNextAccess(int compileIndex);
void dumpCompileTable();
//! allocate a register for a variable
//!if no physical register is free, call spillForLogicalReg to free up a physical register;
//!if the variable is a temporary and it was spilled, call unspillLogicalReg to load from spill location to the allocated physical register;
//!if updateRefCount is true, reduce reference count of the variable by 1
int registerAlloc(int type, int reg, bool isPhysical, bool updateRefCount) {
#ifdef DEBUG_REGALLOC
ALOGI("%p: try to allocate register %d type %d isPhysical %d", currentBB, reg, type, isPhysical);
#endif
if(currentBB == NULL) {
if(type & LowOpndRegType_virtual) return PhysicalReg_Null;
if(isPhysical) return reg; //for helper functions
return PhysicalReg_Null;
}
//ignore EDI, ESP, EBP (glue)
if(isPhysical && (reg == PhysicalReg_EDI || reg == PhysicalReg_ESP ||
reg == PhysicalReg_EBP || reg == PhysicalReg_Null))
return reg;
int newType = convertType(type, reg, isPhysical);
if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
int tIndex = searchCompileTable(newType, reg);
if(tIndex < 0) {
ALOGE("reg %d type %d not found in registerAlloc", reg, newType);
return PhysicalReg_Null;
}
//physical register
if(isPhysical) {
if(allRegs[reg].isUsed) { //if used by a non hard-coded register
spillForHardReg(reg, newType);
}
allRegs[reg].isUsed = true;
#ifdef DEBUG_REG_USED
ALOGI("REGALLOC: allocate a reg %d", reg);
#endif
compileTable[tIndex].physicalReg = reg;
if(updateRefCount)
decreaseRefCount(tIndex);
#ifdef DEBUG_REGALLOC
ALOGI("REGALLOC: allocate register %d for logical register %d %d",
compileTable[tIndex].physicalReg, reg, newType);
#endif
return reg;
}
//already allocated
if(compileTable[tIndex].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_REGALLOC
ALOGI("already allocated to physical register %d", compileTable[tIndex].physicalReg);
#endif
if(updateRefCount)
decreaseRefCount(tIndex);
return compileTable[tIndex].physicalReg;
}
//at this point, the logical register is not hard-coded and is mapped to Reg_Null
//first check whether there is a free reg
//if not, call spillForLogicalReg
int index = getFreeReg(newType, reg, tIndex);
if(index >= 0 && index < PhysicalReg_Null) {
//update compileTable & allRegs
compileTable[tIndex].physicalReg = allRegs[index].physicalReg;
allRegs[index].isUsed = true;
#ifdef DEBUG_REG_USED
ALOGI("REGALLOC: register %d is free", allRegs[index].physicalReg);
#endif
} else {
PhysicalReg allocR = spillForLogicalReg(newType, reg, tIndex);
compileTable[tIndex].physicalReg = allocR;
}
if(compileTable[tIndex].spill_loc_index >= 0) {
unspillLogicalReg(tIndex, compileTable[tIndex].physicalReg);
}
if(updateRefCount)
decreaseRefCount(tIndex);
#ifdef DEBUG_REGALLOC
ALOGI("REGALLOC: allocate register %d for logical register %d %d",
compileTable[tIndex].physicalReg, reg, newType);
#endif
return compileTable[tIndex].physicalReg;
}
//!a variable will use a physical register allocated for another variable
//!This is used when MOVE_OPT is on, it tries to alias a virtual register with a temporary to remove a move
int registerAllocMove(int reg, int type, bool isPhysical, int srcReg) {
if(srcReg == PhysicalReg_EDI || srcReg == PhysicalReg_ESP || srcReg == PhysicalReg_EBP)
ALOGE("can't move from srcReg EDI or ESP or EBP");
#ifdef DEBUG_REGALLOC
ALOGI("in registerAllocMove: reg %d type %d srcReg %d", reg, type, srcReg);
#endif
int newType = convertType(type, reg, isPhysical);
if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
int index = searchCompileTable(newType, reg);
if(index < 0) {
ALOGE("reg %d type %d not found in registerAllocMove", reg, newType);
return -1;
}
decreaseRefCount(index);
compileTable[index].physicalReg = srcReg;
#ifdef DEBUG_REGALLOC
ALOGI("REGALLOC: registerAllocMove %d for logical register %d %d",
compileTable[index].physicalReg, reg, newType);
#endif
return srcReg;
}
//! check whether a physical register is available to be used by a variable
//! data structures accessed:
//! 1> currentBB->infoBasicBlock[index].allocConstraintsSorted
//! sorted from high count to low count
//! 2> currentBB->allocConstraintsSorted
//! sorted from low count to high count
//! 3> allRegs: whether a physical register is available, indexed by PhysicalReg
//! NOTE: if a temporary variable is 8-bit, only %eax, %ebx, %ecx, %edx can be used
int getFreeReg(int type, int reg, int indexToCompileTable) {
syncAllRegs();
/* handles requests for xmm or ss registers */
int k;
if(((type & MASK_FOR_TYPE) == LowOpndRegType_xmm) ||
((type & MASK_FOR_TYPE) == LowOpndRegType_ss)) {
for(k = PhysicalReg_XMM0; k <= PhysicalReg_XMM7; k++) {
if(!allRegs[k].isUsed) return k;
}
return -1;
}
#ifdef DEBUG_REGALLOC
ALOGI("USED registers: ");
for(k = 0; k < 8; k++)
ALOGI("%d used: %d time freed: %d callee-saveld: %d", k, allRegs[k].isUsed,
allRegs[k].freeTimeStamp, allRegs[k].isCalleeSaved);
ALOGI("");
#endif
/* a VR is requesting a physical register */
if(isVirtualReg(type)) { //find a callee-saved register
/* if VR is type GG, check the pre-allocated physical register first */
bool isGGVR = compileTable[indexToCompileTable].gType == GLOBALTYPE_GG;
if(isGGVR) {
int regCandidateT = compileTable[indexToCompileTable].physicalReg_prev;
if(!allRegs[regCandidateT].isUsed) return regCandidateT;
}
int index = searchVirtualInfoOfBB((LowOpndRegType)(type&MASK_FOR_TYPE), reg, currentBB);
if(index < 0) {
ALOGE("VR %d %d not found in infoBasicBlock of currentBB %d (num of VRs %d)",
reg, type, currentBB->bb_index, currentBB->num_regs);
dvmAbort();
}
/* check allocConstraints for this VR,
return an available physical register with the highest constraint > 0 */
for(k = 0; k < 8; k++) {
if(currentBB->infoBasicBlock[index].allocConstraintsSorted[k].count == 0) break;
int regCandidateT = currentBB->infoBasicBlock[index].allocConstraintsSorted[k].physicalReg;
assert(regCandidateT < PhysicalReg_Null);
if(!allRegs[regCandidateT].isUsed) return regCandidateT;
}
/* WAS: return an available physical register with the lowest constraint
NOW: consider a new factor (freeTime) when there is a tie
if 2 available physical registers have the same number of constraints
choose the one with smaller free time stamp */
int currentCount = -1;
int index1 = -1;
int smallestTime = -1;
for(k = 0; k < 8; k++) {
int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
assert(regCandidateT < PhysicalReg_Null);
if(index1 >= 0 && currentBB->allocConstraintsSorted[k].count > currentCount)
break; //candidate has higher count than index1
if(!allRegs[regCandidateT].isUsed) {
if(index1 < 0) {
index1 = k;
currentCount = currentBB->allocConstraintsSorted[k].count;
smallestTime = allRegs[regCandidateT].freeTimeStamp;
} else if(allRegs[regCandidateT].freeTimeStamp < smallestTime) {
index1 = k;
smallestTime = allRegs[regCandidateT].freeTimeStamp;
}
}
}
if(index1 >= 0) return currentBB->allocConstraintsSorted[index1].physicalReg;
return -1;
}
/* handle request from a temporary variable or a glue variable */
else {
bool is8Bit = isTemp8Bit(type, reg);
/* if the temporary variable is linked to a VR and
the VR is not yet allocated to any physical register */
int vr_num = compileTable[indexToCompileTable].linkageToVR;
if(vr_num >= 0) {
int index3 = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_virtual, vr_num);
if(index3 < 0) {
ALOGE("2 in tracing linkage to VR %d", vr_num);
dvmAbort();
}
if(compileTable[index3].physicalReg == PhysicalReg_Null) {
int index2 = searchVirtualInfoOfBB(LowOpndRegType_gp, vr_num, currentBB);
if(index2 < 0) {
ALOGE("1 in tracing linkage to VR %d", vr_num);
dvmAbort();
}
#ifdef DEBUG_REGALLOC
ALOGI("in getFreeReg for temporary reg %d, trace the linkage to VR %d",
reg, vr_num);
#endif
/* check allocConstraints on the VR
return an available physical register with the highest constraint > 0
*/
for(k = 0; k < 8; k++) {
if(currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].count == 0) break;
int regCandidateT = currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].physicalReg;
#ifdef DEBUG_REGALLOC
ALOGI("check register %d with count %d", regCandidateT,
currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].count);
#endif
/* if the requesting variable is 8 bit */
if(is8Bit && regCandidateT > PhysicalReg_EDX) continue;
assert(regCandidateT < PhysicalReg_Null);
if(!allRegs[regCandidateT].isUsed) return regCandidateT;
}
}
}
/* check allocConstraints of the basic block
if 2 available physical registers have the same constraint count,
return the non callee-saved physical reg */
/* enhancement: record the time when a register is freed (freeTimeStamp)
the purpose is to reduce false dependency
priority: constraint count, non callee-saved, time freed
let x be the lowest constraint count
set A be available callee-saved physical registers with count == x
set B be available non callee-saved physical registers with count == x
if set B is not null, return the one with smallest free time
otherwise, return the one in A with smallest free time
To ignore whether it is callee-saved, add all candidates to set A
*/
int setAIndex[8];
int num_A = 0;
int setBIndex[8];
int num_B = 0;
int index1 = -1; //points to an available physical reg with lowest count
int currentCount = -1;
for(k = 0; k < 8; k++) {
int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
if(is8Bit && regCandidateT > PhysicalReg_EDX) continue;
if(index1 >= 0 && currentBB->allocConstraintsSorted[k].count > currentCount)
break; //candidate has higher count than index1
assert(regCandidateT < PhysicalReg_Null);
if(!allRegs[regCandidateT].isUsed) {
/*To ignore whether it is callee-saved, add all candidates to set A */
if(false) {//!allRegs[regCandidateT].isCalleeSaved) { //add to set B
setBIndex[num_B++] = k;
} else { //add to set A
setAIndex[num_A++] = k;
}
if(index1 < 0) {
/* index1 points to a physical reg with lowest count */
index1 = k;
currentCount = currentBB->allocConstraintsSorted[k].count;
}
}
}
int kk;
int smallestTime = -1;
index1 = -1;
for(kk = 0; kk < num_B; kk++) {
k = setBIndex[kk];
int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
assert(regCandidateT < PhysicalReg_Null);
if(kk == 0 || allRegs[regCandidateT].freeTimeStamp < smallestTime) {
index1 = k;
smallestTime = allRegs[regCandidateT].freeTimeStamp;
}
}
if(index1 >= 0)
return currentBB->allocConstraintsSorted[index1].physicalReg;
index1 = -1;
for(kk = 0; kk < num_A; kk++) {
k = setAIndex[kk];
int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
if(kk == 0 || allRegs[regCandidateT].freeTimeStamp < smallestTime) {
index1 = k;
smallestTime = allRegs[regCandidateT].freeTimeStamp;
}
}
if(index1 >= 0) return currentBB->allocConstraintsSorted[index1].physicalReg;
return -1;
}
return -1;
}
//! find a candidate physical register for a variable and spill all variables that are mapped to the candidate
//!
PhysicalReg spillForLogicalReg(int type, int reg, int indexToCompileTable) {
//choose a used register to spill
//when a GG virtual register is spilled, write it to interpretd stack, set physicalReg to Null
// at end of the basic block, load spilled GG VR to physical reg
//when other types of VR is spilled, write it to interpreted stack, set physicalReg to Null
//when a temporary (non-virtual) register is spilled, write it to stack, set physicalReg to Null
//can we spill a hard-coded temporary register? YES
int k, k2;
PhysicalReg allocR;
//do not try to free a physical reg that is used by more than one logical registers
//fix on sep 28, 2009
//do not try to spill a hard-coded logical register
//do not try to free a physical reg that is outside of the range for 8-bit logical reg
/* for each physical register,
collect number of non-hardcode entries that are mapped to the physical register */
int numOfUses[PhysicalReg_Null];
for(k = PhysicalReg_EAX; k < PhysicalReg_Null; k++)
numOfUses[k] = 0;
for(k = 0; k < num_compile_entries; k++) {
if((compileTable[k].physicalReg != PhysicalReg_Null) &&
matchType(type, compileTable[k].physicalType) &&
(compileTable[k].physicalType & LowOpndRegType_hard) == 0) {
numOfUses[compileTable[k].physicalReg]++;
}
}
/* candidates: all non-hardcode entries that are mapped to
a physical register that is used by only one entry*/
bool is8Bit = isTemp8Bit(type, reg);
int candidates[COMPILE_TABLE_SIZE];
int num_cand = 0;
for(k = 0; k < num_compile_entries; k++) {
if(matchType(type, compileTable[k].physicalType) &&
compileTable[k].physicalReg != PhysicalReg_Null) {
if(is8Bit && compileTable[k].physicalReg > PhysicalReg_EDX) continue; //not a candidate
if(!canSpillReg[compileTable[k].physicalReg]) continue; //not a candidate
if((compileTable[k].physicalType & LowOpndRegType_hard) == 0 &&
numOfUses[compileTable[k].physicalReg] <= 1) {
candidates[num_cand++] = k;
}
}
}
/* go through all candidates:
first check GLUE-related entries */
int spill_index = -1;
for(k2 = 0; k2 < num_cand; k2++) {
k = candidates[k2];
if((compileTable[k].physicalReg != PhysicalReg_Null) &&
matchType(type, compileTable[k].physicalType) &&
(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
compileTable[k].regNum != PhysicalReg_GLUE)) {
allocR = (PhysicalReg)spillLogicalReg(k, true);
#ifdef DEBUG_REGALLOC
ALOGI("SPILL register used by num %d type %d it is a GLUE register with refCount %d",
compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
#endif
return allocR;
}
}
/* out of the candates, find a VR that has the furthest next use */
int furthestUse = offsetPC;
for(k2 = 0; k2 < num_cand; k2++) {
k = candidates[k2];
if((compileTable[k].physicalReg != PhysicalReg_Null) &&
matchType(type, compileTable[k].physicalType) &&
isVirtualReg(compileTable[k].physicalType)) {
int nextUse = getNextAccess(k);
if(spill_index < 0 || nextUse > furthestUse) {
spill_index = k;
furthestUse = nextUse;
}
}
}
/* spill the VR with the furthest next use */
if(spill_index >= 0) {
allocR = (PhysicalReg)spillLogicalReg(spill_index, true);
return allocR; //the register is still being used
}
/* spill an entry with the smallest refCount */
int baseLeftOver = 0;
int index = -1;
for(k2 = 0; k2 < num_cand; k2++) {
k = candidates[k2];
if(k != indexForGlue &&
(compileTable[k].physicalReg != PhysicalReg_Null) &&
(compileTable[k].physicalType & LowOpndRegType_hard) == 0 && //not hard-coded
matchType(type, compileTable[k].physicalType)) {
if((index < 0) || (compileTable[k].refCount < baseLeftOver)) {
baseLeftOver = compileTable[k].refCount;
index = k;
}
}
}
if(index < 0) {
dumpCompileTable();
ALOGE("no register to spill for logical %d %d", reg, type);
dvmAbort();
}
allocR = (PhysicalReg)spillLogicalReg(index, true);
#ifdef DEBUG_REGALLOC
ALOGI("SPILL register used by num %d type %d it is a temporary register with refCount %d",
compileTable[index].regNum, compileTable[index].physicalType, compileTable[index].refCount);
#endif
return allocR;
}
//!spill a variable to memory, the variable is specified by an index to compileTable
//!If the variable is a temporary, get a spill location that is not in use and spill the content to the spill location;
//!If updateTable is true, set physicalReg to Null;
//!Return the physical register that was allocated to the variable
int spillLogicalReg(int spill_index, bool updateTable) {
if((compileTable[spill_index].physicalType & LowOpndRegType_hard) != 0) {
ALOGE("can't spill a hard-coded register");
dvmAbort();
}
int physicalReg = compileTable[spill_index].physicalReg;
if(!canSpillReg[physicalReg]) {
#ifdef PRINT_WARNING
ALOGW("can't spill register %d", physicalReg);
#endif
//dvmAbort(); //this happens in get_virtual_reg where VR is allocated to the same reg as the hardcoded temporary
}
if(isVirtualReg(compileTable[spill_index].physicalType)) {
//spill back to memory
dumpToMem(compileTable[spill_index].regNum,
(LowOpndRegType)(compileTable[spill_index].physicalType&MASK_FOR_TYPE),
compileTable[spill_index].physicalReg);
}
else {
//update spill_loc_index
int k = getSpillIndex(spill_index == indexForGlue,
getRegSize(compileTable[spill_index].physicalType));
compileTable[spill_index].spill_loc_index = 4*k;
if(k >= 0)
spillIndexUsed[k] = 1;
saveToSpillRegion(getRegSize(compileTable[spill_index].physicalType),
compileTable[spill_index].physicalReg, 4*k);
}
//compileTable[spill_index].physicalReg_prev = compileTable[spill_index].physicalReg;
#ifdef DEBUG_REGALLOC
ALOGI("REGALLOC: SPILL logical reg %d %d with refCount %d allocated to %d",
compileTable[spill_index].regNum,
compileTable[spill_index].physicalType, compileTable[spill_index].refCount,
compileTable[spill_index].physicalReg);
#endif
if(!updateTable) return PhysicalReg_Null;
int allocR = compileTable[spill_index].physicalReg;
compileTable[spill_index].physicalReg = PhysicalReg_Null;
return allocR;
}
//! load a varible from memory to physical register, the variable is specified with an index to compileTable
//!If the variable is a temporary, load from spill location and set the flag for the spill location to not used
int unspillLogicalReg(int spill_index, int physicalReg) {
//can't un-spill to %eax in afterCall!!!
//what if GG VR is allocated to %eax!!!
if(isVirtualReg(compileTable[spill_index].physicalType)) {
get_virtual_reg_noalloc(compileTable[spill_index].regNum,
getRegSize(compileTable[spill_index].physicalType),
physicalReg, true);
}
else {
loadFromSpillRegion(getRegSize(compileTable[spill_index].physicalType),
physicalReg, compileTable[spill_index].spill_loc_index);
spillIndexUsed[compileTable[spill_index].spill_loc_index >> 2] = 0;
compileTable[spill_index].spill_loc_index = -1;
}
#ifdef DEBUG_REGALLOC
ALOGI("REGALLOC: UNSPILL logical reg %d %d with refCount %d", compileTable[spill_index].regNum,
compileTable[spill_index].physicalType, compileTable[spill_index].refCount);
#endif
return PhysicalReg_Null;
}
//!spill a virtual register to memory
//!if the current value of a VR is constant, write immediate to memory;
//!if the current value of a VR is in a physical register, call spillLogicalReg to dump content of the physical register to memory;
//!ifupdateTable is true, set the physical register for VR to Null and decrease reference count of the virtual register
int spillVirtualReg(int vrNum, LowOpndRegType type, bool updateTable) {
int index = searchCompileTable(type | LowOpndRegType_virtual, vrNum);
if(index < 0) {
ALOGE("can't find VR %d %d in spillVirtualReg", vrNum, type);
return -1;
}
//check whether it is const
int value[2];
int isConst = isVirtualRegConstant(vrNum, type, value, false); //do not update refCount
if(isConst == 1 || isConst == 3) {
dumpImmToMem(vrNum, OpndSize_32, value[0]);
}
if(getRegSize(type) == OpndSize_64 && (isConst == 2 || isConst == 3)) {
dumpImmToMem(vrNum+1, OpndSize_32, value[1]);
}
if(isConst != 3 && compileTable[index].physicalReg != PhysicalReg_Null)
spillLogicalReg(index, updateTable);
if(updateTable) decreaseRefCount(index);
return -1;
}
//! spill variables that are mapped to physical register (regNum)
//!
int spillForHardReg(int regNum, int type) {
//find an entry that uses the physical register
int spill_index = -1;
int k;
for(k = 0; k < num_compile_entries; k++) {
if(k != indexForGlue &&
compileTable[k].physicalReg == regNum &&
matchType(type, compileTable[k].physicalType)) {
spill_index = k;
if(compileTable[k].regNum == regNum && compileTable[k].physicalType == type)
continue;
if(inGetVR_num >= 0 && compileTable[k].regNum == inGetVR_num && compileTable[k].physicalType == (type | LowOpndRegType_virtual))
continue;
#ifdef DEBUG_REGALLOC
ALOGI("SPILL logical reg %d %d to free hard-coded reg %d %d",
compileTable[spill_index].regNum, compileTable[spill_index].physicalType,
regNum, type);
if(compileTable[spill_index].physicalType & LowOpndRegType_hard) dumpCompileTable();
#endif
assert(spill_index < COMPILE_TABLE_SIZE);
spillLogicalReg(spill_index, true);
}
}
return regNum;
}
////////////////////////////////////////////////////////////////
//! update allocConstraints of the current basic block
//! allocConstraints specify how many times a hardcoded register is used in this basic block
void updateCurrentBBWithConstraints(PhysicalReg reg) {
if(reg > PhysicalReg_EBP) {
ALOGE("register %d out of range in updateCurrentBBWithConstraints", reg);
}
currentBB->allocConstraints[reg].count++;
}
//! sort allocConstraints and save the result in allocConstraintsSorted
//! allocConstraints specify how many times a virtual register is linked to a hardcode register
//! it is updated in getVirtualRegInfo and merged by mergeEntry2
int sortAllocConstraint(RegAllocConstraint* allocConstraints,
RegAllocConstraint* allocConstraintsSorted, bool fromHighToLow) {
int ii, jj;
int num_sorted = 0;
for(jj = 0; jj < 8; jj++) {
//figure out where to insert allocConstraints[jj]
int count = allocConstraints[jj].count;
int regT = allocConstraints[jj].physicalReg;
assert(regT < PhysicalReg_Null);
int insertIndex = -1;
for(ii = 0; ii < num_sorted; ii++) {
int regT2 = allocConstraintsSorted[ii].physicalReg;
assert(regT2 < PhysicalReg_Null);
if(allRegs[regT].isCalleeSaved &&
count == allocConstraintsSorted[ii].count) {
insertIndex = ii;
break;
}
if((!allRegs[regT].isCalleeSaved) &&
count == allocConstraintsSorted[ii].count &&
(!allRegs[regT2].isCalleeSaved)) { //skip until found one that is not callee-saved
insertIndex = ii;
break;
}
if((fromHighToLow && count > allocConstraintsSorted[ii].count) ||
((!fromHighToLow) && count < allocConstraintsSorted[ii].count)) {
insertIndex = ii;
break;
}
}
if(insertIndex < 0) {
allocConstraintsSorted[num_sorted].physicalReg = (PhysicalReg)regT;
allocConstraintsSorted[num_sorted].count = count;
num_sorted++;
} else {
for(ii = num_sorted-1; ii >= insertIndex; ii--) {
allocConstraintsSorted[ii+1] = allocConstraintsSorted[ii];
}
allocConstraintsSorted[insertIndex] = allocConstraints[jj];
num_sorted++;
}
} //for jj
#ifdef DEBUG_ALLOC_CONSTRAINT
for(jj = 0; jj < 8; jj++) {
if(allocConstraintsSorted[jj].count > 0)
ALOGI("%d: register %d has count %d", jj, allocConstraintsSorted[jj].physicalReg, allocConstraintsSorted[jj].count);
}
#endif
return 0;
}
//! find the entry for a given virtual register in compileTable
//!
int findVirtualRegInTable(u2 vA, LowOpndRegType type, bool printError) {
int k = searchCompileTable(type | LowOpndRegType_virtual, vA);
if(k < 0 && printError) {
ALOGE("findVirtualRegInTable virtual register %d type %d", vA, type);
dvmAbort();
}
return k;
}
//! check whether a virtual register is constant
//! the value of the constant is stored in valuePtr; if updateRefCount is true & the VR is constant, reference count for the VR will be reduced by 1
int isVirtualRegConstant(int regNum, LowOpndRegType type, int* valuePtr, bool updateRefCount) {
OpndSize size = getRegSize(type);
int k;
int indexL = -1;
int indexH = -1;
for(k = 0; k < num_const_vr; k++) {
#ifdef DEBUG_CONST
ALOGI("constVRTable VR %d isConst %d value %x", constVRTable[k].regNum, constVRTable[k].isConst, constVRTable[k].value);
#endif
if(constVRTable[k].regNum == regNum) {
indexL = k;
continue;
}
if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
indexH = k;
continue;
}
}
bool isConstL = false;
bool isConstH = false;
if(indexL >= 0) {
isConstL = constVRTable[indexL].isConst;
}
if(size == OpndSize_64 && indexH >= 0) {
isConstH = constVRTable[indexH].isConst;
}
if((isConstL || isConstH)) {
if(size == OpndSize_64 && isConstH)
valuePtr[1] = constVRTable[indexH].value;
if(isConstL)
*valuePtr = constVRTable[indexL].value;
}
if((isConstL && size == OpndSize_32) || (isConstL && isConstH)) {
if(updateRefCount) {
int indexOrig = searchCompileTable(type | LowOpndRegType_virtual, regNum);
if(indexOrig < 0) ALOGE("can't find VR in isVirtualRegConstant num %d type %d", regNum, type);
decreaseRefCount(indexOrig);
}
#ifdef DEBUG_CONST
ALOGI("VR %d %d is const case", regNum, type);
#endif
return 3;
}
if(size == OpndSize_32) return 0;
if(isConstL) return 1;
if(isConstH) return 2;
return 0;
}
//!update RegAccessType of virtual register vB given RegAccessType of vA
//!RegAccessType can be D, L, H
//!D means full definition, L means only lower-half is defined, H means only higher half is defined
//!we say a VR has no exposed usage in a basic block if the accessType is D or DU
//!we say a VR has exposed usage in a basic block if the accessType is not D nor DU
//!we say a VR has exposed usage in other basic blocks (hasOtherExposedUsage) if
//! there exists another basic block where VR has exposed usage in that basic block
//!A can be U, D, L, H, UD, UL, UH, DU, LU, HU (merged result)
//!B can be U, D, UD, DU (an entry for the current bytecode)
//!input isAPartiallyOverlapB can be any value between -1 to 6
//!if A is xmm: gp B lower half of A, (isAPartiallyOverlapB is 1)
//! gp B higher half of A, (isAPartiallyOverlapB is 2)
//! lower half of A covers the higher half of xmm B (isAPartiallyOverlapB is 4)
//! higher half of A covers the lower half of xmm B (isAPartiallyOverlapB is 3)
//!if A is gp: A covers the lower half of xmm B, (isAPartiallyOverlapB is 5)
//! A covers the higher half of xmm B (isAPartiallyOverlapB is 6)
RegAccessType updateAccess1(RegAccessType A, OverlapCase isAPartiallyOverlapB) {
if(A == REGACCESS_D || A == REGACCESS_DU || A == REGACCESS_UD) {
if(isAPartiallyOverlapB == OVERLAP_ALIGN) return REGACCESS_D;
if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A || isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A)
return REGACCESS_D;
if(isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
return REGACCESS_L;
return REGACCESS_H;
}
if(A == REGACCESS_L || A == REGACCESS_LU || A == REGACCESS_UL) {
if(isAPartiallyOverlapB == OVERLAP_ALIGN || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
return REGACCESS_L;
if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A) return REGACCESS_D;
if(isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A || isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B)
return REGACCESS_N;
if(isAPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B)
return REGACCESS_H;
}
if(A == REGACCESS_H || A == REGACCESS_HU || A == REGACCESS_UH) {
if(isAPartiallyOverlapB == OVERLAP_ALIGN || isAPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B)
return REGACCESS_H;
if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A || isAPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B)
return REGACCESS_N;
if(isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A) return REGACCESS_D;
if(isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
return REGACCESS_L;
}
return REGACCESS_N;
}
//! merge RegAccessType C1 with RegAccessType C2
//!C can be N,L,H,D
RegAccessType updateAccess2(RegAccessType C1, RegAccessType C2) {
if(C1 == REGACCESS_D || C2 == REGACCESS_D) return REGACCESS_D;
if(C1 == REGACCESS_N) return C2;
if(C2 == REGACCESS_N) return C1;
if(C1 == REGACCESS_L && C2 == REGACCESS_H) return REGACCESS_D;
if(C1 == REGACCESS_H && C2 == REGACCESS_L) return REGACCESS_D;
return C1;
}
//! merge RegAccessType C with RegAccessType B
//!C can be N,L,H,D
//!B can be U, D, UD, DU
RegAccessType updateAccess3(RegAccessType C, RegAccessType B) {
if(B == REGACCESS_D || B == REGACCESS_DU) return B; //no exposed usage
if(B == REGACCESS_U || B == REGACCESS_UD) {
if(C == REGACCESS_N) return B;
if(C == REGACCESS_L) return REGACCESS_LU;
if(C == REGACCESS_H) return REGACCESS_HU;
if(C == REGACCESS_D) return REGACCESS_DU;
}
return B;
}
//! merge RegAccessType A with RegAccessType B
//!argument isBPartiallyOverlapA can be any value between -1 and 2
//!0 means fully overlapping, 1 means B is the lower half, 2 means B is the higher half
RegAccessType mergeAccess2(RegAccessType A, RegAccessType B, OverlapCase isBPartiallyOverlapA) {
if(A == REGACCESS_UD || A == REGACCESS_UL || A == REGACCESS_UH ||
A == REGACCESS_DU || A == REGACCESS_LU || A == REGACCESS_HU) return A;
if(A == REGACCESS_D) {
if(B == REGACCESS_D) return REGACCESS_D;
if(B == REGACCESS_U) return REGACCESS_DU;
if(B == REGACCESS_UD) return REGACCESS_DU;
if(B == REGACCESS_DU) return B;
}
if(A == REGACCESS_U) {
if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
if(B == REGACCESS_U) return A;
if(B == REGACCESS_UD && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
if(B == REGACCESS_UD && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
if(B == REGACCESS_UD && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
}
if(A == REGACCESS_L) {
if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_L;
if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_D;
if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_D;
if(B == REGACCESS_U) return REGACCESS_LU;
if(B == REGACCESS_UD) return REGACCESS_LU;
if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_LU;
if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_DU;
if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_DU;
}
if(A == REGACCESS_H) {
if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_D;
if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_H;
if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_D;
if(B == REGACCESS_U) return REGACCESS_HU;
if(B == REGACCESS_UD) return REGACCESS_HU;
if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_DU;
if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_HU;
if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_DU;
}
return REGACCESS_N;
}
//!determines which part of a use is from a given definition
//!reachingDefLive tells us which part of the def is live at this point
//!isDefPartiallyOverlapUse can be any value between -1 and 2
RegAccessType setAccessTypeOfUse(OverlapCase isDefPartiallyOverlapUse, RegAccessType reachingDefLive) {
if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_A)
return reachingDefLive;
if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_LOW_OF_A) { //def covers the low half of use
return REGACCESS_L;
}
if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_HIGH_OF_A) {
return REGACCESS_H;
}
return REGACCESS_N;
}
//! search currentBB->defUseTable to find a def for regNum at offsetPC
//!
DefUsePair* searchDefUseTable(int offsetPC, int regNum, LowOpndRegType pType) {
DefUsePair* ptr = currentBB->defUseTable;
while(ptr != NULL) {
if(ptr->def.offsetPC == offsetPC &&
ptr->def.regNum == regNum &&
ptr->def.physicalType == pType) {
return ptr;
}
ptr = ptr->next;
}
return NULL;
}
void printDefUseTable() {
ALOGI("PRINT defUseTable --------");
DefUsePair* ptr = currentBB->defUseTable;
while(ptr != NULL) {
ALOGI(" def @ %x of VR %d %d has %d uses", ptr->def.offsetPC,
ptr->def.regNum, ptr->def.physicalType,
ptr->num_uses);
DefOrUseLink* ptr2 = ptr->uses;
while(ptr2 != NULL) {
ALOGI(" use @ %x of VR %d %d accessType %d", ptr2->offsetPC,
ptr2->regNum,
ptr2->physicalType,
ptr2->accessType);
ptr2 = ptr2->next;
}
ptr = ptr->next;
}
}
//! when a VR is used, check whether a transfer from memory to XMM is necessary
//!
int updateVRAtUse(int reg, LowOpndRegType pType, int regAll) {
int k;
for(k = 0; k < currentBB->num_xfer_points; k++) {
if(currentBB->xferPoints[k].offsetPC == offsetPC &&
currentBB->xferPoints[k].xtype == XFER_MEM_TO_XMM &&
currentBB->xferPoints[k].regNum == reg &&
currentBB->xferPoints[k].physicalType == pType) {
#ifdef DEBUG_XFER_POINTS
ALOGI("XFER from memory to xmm %d", reg);
#endif
move_mem_to_reg_noalloc(OpndSize_64,
4*currentBB->xferPoints[k].regNum, PhysicalReg_FP, true,
MemoryAccess_VR, currentBB->xferPoints[k].regNum,
regAll, true);
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// DEAD/USELESS STATEMENT ELMINATION
// bytecodes can be removed if a bytecode has no side effect and the defs are not used
// this optimization is guarded with DSE_OPT
// currently, this optimization is not on, since it does not provide observable performance improvement
// and it increases compilation time
/* we remove a maximal of 40 bytecodes within a single basic block */
#define MAX_NUM_DEAD_PC_IN_BB 40
int deadPCs[MAX_NUM_DEAD_PC_IN_BB];
int num_dead_pc = 0;
//! collect all PCs that can be removed
//! traverse each byte code in the current basic block and check whether it can be removed, if yes, update deadPCs
void getDeadStmts() {
BasicBlock_O1* bb = currentBB;
int k;
num_dead_pc = 0;
//traverse each bytecode in the basic block
//update offsetPC, rPC & inst
u2* rPC_start = (u2*)currentMethod->insns;
MIR* mir;
for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
offsetPC = mir->seqNum;
rPC = rPC_start + mir->offset;
if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) continue;
#ifdef DEBUG_DSE
ALOGI("DSE: offsetPC %x", offsetPC);
#endif
inst = FETCH(0);
bool isDeadStmt = true;
getVirtualRegInfo(infoByteCode);
u2 inst_op = INST_INST(inst);
//skip bytecodes with side effect
if(inst_op != OP_CONST_STRING && inst_op != OP_CONST_STRING_JUMBO &&
inst_op != OP_MOVE && inst_op != OP_MOVE_OBJECT &&
inst_op != OP_MOVE_FROM16 && inst_op != OP_MOVE_OBJECT_FROM16 &&
inst_op != OP_MOVE_16 && inst_op != OP_CONST_CLASS &&
inst_op != OP_MOVE_OBJECT_16 && inst_op != OP_MOVE_WIDE &&
inst_op != OP_MOVE_WIDE_FROM16 && inst_op != OP_MOVE_WIDE_16 &&
inst_op != OP_MOVE_RESULT && inst_op != OP_MOVE_RESULT_OBJECT) {
continue;
}
//some statements do not define any VR!!!
int num_defs = 0;
for(k = 0; k < num_regs_per_bytecode; k++) {
if(infoByteCode[k].accessType == REGACCESS_D ||
infoByteCode[k].accessType == REGACCESS_UD ||
infoByteCode[k].accessType == REGACCESS_DU) { //search defUseTable
num_defs++;
DefUsePair* indexT = searchDefUseTable(offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
if(indexT == NULL) {
ALOGE("def at %x of VR %d %d not in table",
offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
return;
}
if(indexT->num_uses > 0) {
isDeadStmt = false;
break;
} else {
#ifdef DEBUG_DSE
ALOGI("DSE: num_uses is %d for def at %d for VR %d %d", indexT->num_uses,
offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
#endif
}
}
} //for k
if(num_defs == 0) isDeadStmt = false;
if(isDeadStmt && num_dead_pc < MAX_NUM_DEAD_PC_IN_BB) {
#ifdef DEBUG_DSE
ALOGI("DSE: stmt at %x is dead", offsetPC);
#endif
deadPCs[num_dead_pc++] = offsetPC;
}
} //for offsetPC
#ifdef DEBUG_DSE
ALOGI("Dead Stmts: ");
for(k = 0; k < num_dead_pc; k++) ALOGI("%x ", deadPCs[k]);
ALOGI("");
#endif
}
//! entry point to remove dead statements
//! recursively call getDeadStmts and remove uses in defUseTable that are from a dead PC
//! until there is no change to number of dead PCs
void removeDeadDefs() {
int k;
int deadPCs_2[MAX_NUM_DEAD_PC_IN_BB];
int num_dead_pc_2 = 0;
getDeadStmts();
if(num_dead_pc == 0) return;
DefUsePair* ptr = NULL;
DefOrUseLink* ptrUse = NULL;
DefOrUseLink* ptrUse_prev = NULL;
while(true) {
//check all the uses in defUseTable and remove any use that is from a dead PC
ptr = currentBB->defUseTable;
while(ptr != NULL) {
int k3;
ptrUse = ptr->uses;
ptrUse_prev = NULL;
while(ptrUse != NULL) {
bool isIn = false;
for(k3 = 0; k3 < num_dead_pc; k3++) {
if(ptrUse->offsetPC == deadPCs[k3]) {
isIn = true;
break;
}
}//k3
if(!isIn) {
ptrUse_prev = ptrUse;
ptrUse = ptrUse->next; //next use
}
else {
//go to next use and remove ptrUse
#ifdef DEBUG_DSE
ALOGI("DSE: remove usage at offsetPC %d reached by def at %d", ptrUse->offsetPC,
ptr->def.offsetPC);
#endif
DefOrUseLink* nextP = ptrUse->next;
if(ptrUse == ptr->useTail) ptr->useTail = ptrUse_prev;
free(ptrUse);
if(ptrUse_prev == NULL) {
ptr->uses = nextP;
} else {
ptrUse_prev->next = nextP;
}
ptrUse = nextP; //do not update ptrUse_prev
ptr->num_uses--;
}
}//while ptrUse
ptr = ptr->next;
}//while ptr
//save deadPCs in deadPCs_2
num_dead_pc_2 = num_dead_pc;
for(k = 0; k < num_dead_pc_2; k++)
deadPCs_2[k] = deadPCs[k];
//update deadPCs
getDeadStmts();
//if no change to number of dead PCs, break out of the while loop
if(num_dead_pc_2 == num_dead_pc) break;
}//while
#ifdef DEBUG_DSE
ALOGI("DSE: DEAD STMTS: ");
for(k = 0; k < num_dead_pc; k++) {
ALOGI("%d ", deadPCs[k]);
}
ALOGI("");
#endif
}
/////////////////////////////////////////////////////////////
//!search memVRTable for a given virtual register
//!
int searchMemTable(int regNum) {
int k;
for(k = 0; k < num_memory_vr; k++) {
if(memVRTable[k].regNum == regNum) {
return k;
}
}
ALOGW("in searchMemTable can't find VR %d num_memory_vr %d", regNum, num_memory_vr);
return -1;
}
/////////////////////////////////////////////////////////////////////////
// A VR is already in memory && NULL CHECK
//!check whether the latest content of a VR is in memory
//!
bool isInMemory(int regNum, OpndSize size) {
int indexL = searchMemTable(regNum);
int indexH = -1;
if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
if(indexL < 0) return false;
if(size == OpndSize_64 && indexH < 0) return false;
if(!memVRTable[indexL].inMemory) return false;
if(size == OpndSize_64 && (!memVRTable[indexH].inMemory)) return false;
return true;
}
//!set field inMemory of memVRTable to true
//!
void setVRToMemory(int regNum, OpndSize size) {
int indexL = searchMemTable(regNum);
int indexH = -1;
if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
if(indexL < 0) {
ALOGE("VR %d not in memVRTable", regNum);
return;
}
memVRTable[indexL].inMemory = true;
if(size == OpndSize_64) {
if(indexH < 0) {
ALOGE("VR %d not in memVRTable", regNum+1);
return;
}
memVRTable[indexH].inMemory = true;
}
}
//! check whether null check for a VR is performed previously
//!
bool isVRNullCheck(int regNum, OpndSize size) {
if(size != OpndSize_32) {
ALOGE("isVRNullCheck size should be 32");
dvmAbort();
}
int indexL = searchMemTable(regNum);
if(indexL < 0) {
ALOGE("VR %d not in memVRTable", regNum);
return false;
}
return memVRTable[indexL].nullCheckDone;
}
bool isVRBoundCheck(int vr_array, int vr_index) {
int indexL = searchMemTable(vr_array);
if(indexL < 0) {
ALOGE("isVRBoundCheck: VR %d not in memVRTable", vr_array);
return false;
}
if(memVRTable[indexL].boundCheck.indexVR == vr_index)
return memVRTable[indexL].boundCheck.checkDone;
return false;
}
//! set nullCheckDone in memVRTable to true
//!
void setVRNullCheck(int regNum, OpndSize size) {
if(size != OpndSize_32) {
ALOGE("setVRNullCheck size should be 32");
dvmAbort();
}
int indexL = searchMemTable(regNum);
if(indexL < 0) {
ALOGE("VR %d not in memVRTable", regNum);
return;
}
memVRTable[indexL].nullCheckDone = true;
}
void setVRBoundCheck(int vr_array, int vr_index) {
int indexL = searchMemTable(vr_array);
if(indexL < 0) {
ALOGE("setVRBoundCheck: VR %d not in memVRTable", vr_array);
return;
}
memVRTable[indexL].boundCheck.indexVR = vr_index;
memVRTable[indexL].boundCheck.checkDone = true;
}
void clearVRBoundCheck(int regNum, OpndSize size) {
int k;
for(k = 0; k < num_memory_vr; k++) {
if(memVRTable[k].regNum == regNum ||
(size == OpndSize_64 && memVRTable[k].regNum == regNum+1)) {
memVRTable[k].boundCheck.checkDone = false;
}
if(memVRTable[k].boundCheck.indexVR == regNum ||
(size == OpndSize_64 && memVRTable[k].boundCheck.indexVR == regNum+1)) {
memVRTable[k].boundCheck.checkDone = false;
}
}
}
//! set inMemory of memVRTable to false
//!
void clearVRToMemory(int regNum, OpndSize size) {
int indexL = searchMemTable(regNum);
int indexH = -1;
if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
if(indexL >= 0) {
memVRTable[indexL].inMemory = false;
}
if(size == OpndSize_64 && indexH >= 0) {
memVRTable[indexH].inMemory = false;
}
}
//! set nullCheckDone of memVRTable to false
//!
void clearVRNullCheck(int regNum, OpndSize size) {
int indexL = searchMemTable(regNum);
int indexH = -1;
if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
if(indexL >= 0) {
memVRTable[indexL].nullCheckDone = false;
}
if(size == OpndSize_64 && indexH >= 0) {
memVRTable[indexH].nullCheckDone = false;
}
}
//! Extend Virtual Register life
//! Requests that the life of a specific virtual register be extended. This ensures
//! that its mapping to a physical register won't be canceled while the extension
//! request is valid. NOTE: This does not support 64-bit values (when two adjacent
//! VRs are used)
//! @see cancelVRFreeDelayRequest
//! @see getVRFreeDelayRequested
//! @see VRFreeDelayFlags
//! @param regNum is the VR number
//! @param reason explains why freeing must be delayed. A single or combination
//! of VRFreeDelayFlags should be used.
//! @return negative value if request failed
int requestVRFreeDelay(int regNum, u4 reason) {
//TODO Add 64-bit operand support when needed
int indexL = searchMemTable(regNum);
if(indexL >= 0) {
memVRTable[indexL].delayFreeFlags |= reason;
} else {
ALOGE("requestVRFreeDelay: VR %d not in memVRTable", regNum);
}
return indexL;
}
//! Cancel request for virtual register life extension
//! Cancels any outstanding requests to extended liveness of VR. Additionally,
//! this ensures that if the VR is no longer life after this point, it will
//! no longer be associated with a physical register which can then be reused.
//! NOTE: This does not support 64-bit values (when two adjacent VRs are used)
//! @see requestVRFreeDelay
//! @see getVRFreeDelayRequested
//! @see VRFreeDelayFlags
//! @param regNum is the VR number
//! @param reason explains what freeing delay request should be canceled. A single
//! or combination of VRFreeDelayFlags should be used.
void cancelVRFreeDelayRequest(int regNum, u4 reason) {
//TODO Add 64-bit operand support when needed
bool needCallToFreeReg = false;
int indexL = searchMemTable(regNum);
if(indexL >= 0) {
if((memVRTable[indexL].delayFreeFlags & reason) != VRDELAY_NONE) { // don't cancel delay if it wasn't requested
memVRTable[indexL].delayFreeFlags ^= reason; // only cancel this particular reason, not all others
if(memVRTable[indexL].delayFreeFlags == VRDELAY_NONE)
needCallToFreeReg = true; // freeReg might want to free this VR now if there is no longer a valid delay
}
}
if(needCallToFreeReg)
freeReg(true);
}
//! Gets status of virtual register free delay request
//! Finds out if there was a delay request for freeing this VR.
//! NOTE: This does not support 64-bit values (when two adjacent VRs are used)
//! @see requestVRFreeDelay
//! @see cancelVRFreeDelayRequest
//! @param regNum is the VR number
//! @return true if VR has an active delay request
bool getVRFreeDelayRequested(int regNum) {
//TODO Add 64-bit operand support when needed
int indexL = searchMemTable(regNum);
if(indexL >= 0) {
if(memVRTable[indexL].delayFreeFlags != VRDELAY_NONE)
return true;
return false;
}
return false;
}
//! find the basic block that a bytecode is in
//!
BasicBlock_O1* findForOffset(int offset) {
int k;
for(k = 0; k < num_bbs_for_method; k++) {
if(method_bbs_sorted[k]->pc_start <= offset && method_bbs_sorted[k]->pc_end > offset)
return method_bbs_sorted[k];
}
return NULL;
}
void dump_CFG(Method* method);
int current_bc_size = -1;
//! check whether a virtual register is used in a basic block
//!
bool isUsedInBB(int regNum, int type, BasicBlock_O1* bb) {
int k;
for(k = 0; k < bb->num_regs; k++) {
if(bb->infoBasicBlock[k].physicalType == (type&MASK_FOR_TYPE) && bb->infoBasicBlock[k].regNum == regNum)
return true;
}
return false;
}
//! return the index to infoBasicBlock for a given virtual register
//! return -1 if not found
int searchVirtualInfoOfBB(LowOpndRegType type, int regNum, BasicBlock_O1* bb) {
int k;
for(k = 0; k < bb->num_regs; k++) {
if(bb->infoBasicBlock[k].physicalType == type && bb->infoBasicBlock[k].regNum == regNum)
return k;
}
return -1;
}
//! return the index to compileTable for a given virtual register
//! return -1 if not found
int searchCompileTable(int type, int regNum) { //returns the index
int k;
for(k = 0; k < num_compile_entries; k++) {
if(compileTable[k].physicalType == type && compileTable[k].regNum == regNum)
return k;
}
return -1;
}
//!check whether a physical register for a variable with typeA will work for another variable with typeB
//!Type LowOpndRegType_ss is compatible with type LowOpndRegType_xmm
bool matchType(int typeA, int typeB) {
if((typeA & MASK_FOR_TYPE) == (typeB & MASK_FOR_TYPE)) return true;
if((typeA & MASK_FOR_TYPE) == LowOpndRegType_ss &&
(typeB & MASK_FOR_TYPE) == LowOpndRegType_xmm) return true;
if((typeA & MASK_FOR_TYPE) == LowOpndRegType_xmm &&
(typeB & MASK_FOR_TYPE) == LowOpndRegType_ss) return true;
return false;
}
//!check whether a virtual register is used in the current bytecode
//!
bool isUsedInByteCode(int regNum, int type) {
getVirtualRegInfo(infoByteCode);
int k;
for(k = 0; k < num_regs_per_bytecode; k++) {
if(infoByteCode[k].physicalType == (type&MASK_FOR_TYPE) && infoByteCode[k].regNum == regNum)
return true;
}
return false;
}
//! obsolete
bool defineFirst(int atype) {
if(atype == REGACCESS_D || atype == REGACCESS_L || atype == REGACCESS_H || atype == REGACCESS_DU)
return true;
return false;
}
//!check whether a virtual register is updated in a basic block
//!
bool notUpdated(RegAccessType atype) {
if(atype == REGACCESS_U) return true;
return false;
}
//!check whether a virtual register has exposed usage within a given basic block
//!
bool hasExposedUsage2(BasicBlock_O1* bb, int index) {
RegAccessType atype = bb->infoBasicBlock[index].accessType;
if(atype == REGACCESS_D || atype == REGACCESS_L || atype == REGACCESS_H || atype == REGACCESS_DU)
return false;
return true;
}
//! return the spill location that is not used
//!
int getSpillIndex(bool isGLUE, OpndSize size) {
if(isGLUE) return 0;
int k;
for(k = 1; k <= MAX_SPILL_JIT_IA-1; k++) {
if(size == OpndSize_64) {
if(k < MAX_SPILL_JIT_IA-1 && spillIndexUsed[k] == 0 && spillIndexUsed[k+1] == 0)
return k;
}
else if(spillIndexUsed[k] == 0) {
return k;
}
}
ALOGE("can't find spill position in spillLogicalReg");
return -1;
}
//!this is called before generating a native code, it sets entries in array canSpillReg to true
//!startNativeCode must be paired with endNativeCode
void startNativeCode(int vr_num, int vr_type) {
int k;
for(k = 0; k < PhysicalReg_Null; k++) {
canSpillReg[k] = true;
}
inGetVR_num = vr_num;
inGetVR_type = vr_type;
}
//! called right after generating a native code
//!It sets entries in array canSpillReg to true and reset inGetVR_num to -1
void endNativeCode() {
int k;
for(k = 0; k < PhysicalReg_Null; k++) {
canSpillReg[k] = true;
}
inGetVR_num = -1;
}
//! set canSpillReg[physicalReg] to false
//!
void donotSpillReg(int physicalReg) {
canSpillReg[physicalReg] = false;
}
//! set canSpillReg[physicalReg] to true
//!
void doSpillReg(int physicalReg) {
canSpillReg[physicalReg] = true;
}
//! touch hardcoded register %ecx and reduce its reference count
//!
int touchEcx() {
//registerAlloc will spill logical reg that is mapped to ecx
//registerAlloc will reduce refCount
registerAlloc(LowOpndRegType_gp, PhysicalReg_ECX, true, true);
return 0;
}
//! touch hardcoded register %eax and reduce its reference count
//!
int touchEax() {
registerAlloc(LowOpndRegType_gp, PhysicalReg_EAX, true, true);
return 0;
}
int touchEsi() {
registerAlloc(LowOpndRegType_gp, PhysicalReg_ESI, true, true);
return 0;
}
int touchXmm1() {
registerAlloc(LowOpndRegType_xmm, XMM_1, true, true);
return 0;
}
int touchEbx() {
registerAlloc(LowOpndRegType_gp, PhysicalReg_EBX, true, true);
return 0;
}
//! touch hardcoded register %edx and reduce its reference count
//!
int touchEdx() {
registerAlloc(LowOpndRegType_gp, PhysicalReg_EDX, true, true);
return 0;
}
#ifdef HACK_FOR_DEBUG
//for debugging purpose, instructions are added at a certain place
bool hacked = false;
void hackBug() {
if(!hacked && iget_obj_inst == 13) {
#if 0
move_reg_to_reg_noalloc(OpndSize_32, PhysicalReg_EBX, true, PhysicalReg_ECX, true);
//move from ebx to ecx & update compileTable for v3
int tIndex = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, 3);
if(tIndex < 0) ALOGE("hack can't find VR3");
compileTable[tIndex].physicalReg = PhysicalReg_ECX;
#else
move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBX, true, 12, PhysicalReg_FP, true);
#endif
}
}
void hackBug2() {
if(!hacked && iget_obj_inst == 13) {
dump_imm_mem_noalloc(Mnemonic_MOV, OpndSize_32, 0, 12, PhysicalReg_FP, true);
hacked = true;
}
}
#endif
//! this function is called before calling a helper function or a vm function
int beforeCall(const char* target) { //spill all live registers
if(currentBB == NULL) return -1;
/* special case for ncgGetEIP: this function only updates %edx */
if(!strcmp(target, "ncgGetEIP")) {
touchEdx();
return -1;
}
/* these functions use %eax for the return value */
if((!strcmp(target, "dvmInstanceofNonTrivial")) ||
(!strcmp(target, "dvmUnlockObject")) ||
(!strcmp(target, "dvmAllocObject")) ||
(!strcmp(target, "dvmAllocArrayByClass")) ||
(!strcmp(target, "dvmAllocPrimitiveArray")) ||
(!strcmp(target, "dvmInterpHandleFillArrayData")) ||
(!strcmp(target, "dvmFindInterfaceMethodInCache2")) ||
(!strcmp(target, "dvmNcgHandlePackedSwitch")) ||
(!strcmp(target, "dvmNcgHandleSparseSwitch")) ||
(!strcmp(target, "dvmCanPutArrayElement")) ||
(!strcmp(target, "moddi3")) || (!strcmp(target, "divdi3")) ||
(!strcmp(target, "execute_inline"))
|| (!strcmp(target, "dvmJitToPatchPredictedChain"))
|| (!strcmp(target, "dvmJitHandlePackedSwitch"))
|| (!strcmp(target, "dvmJitHandleSparseSwitch"))
) {
touchEax();
}
//these two functions also use %edx for the return value
if((!strcmp(target, "moddi3")) || (!strcmp(target, "divdi3"))) {
touchEdx();
}
if((!strcmp(target, ".new_instance_helper"))) {
touchEsi(); touchEax();
}
#if defined(ENABLE_TRACING)
if((!strcmp(target, "common_periodicChecks4"))) {
touchEdx();
}
#endif
if((!strcmp(target, ".const_string_helper"))) {
touchEcx(); touchEax();
}
if((!strcmp(target, ".check_cast_helper"))) {
touchEbx(); touchEsi();
}
if((!strcmp(target, ".instance_of_helper"))) {
touchEbx(); touchEsi(); touchEcx();
}
if((!strcmp(target, ".monitor_enter_helper"))) {
touchEbx();
}
if((!strcmp(target, ".monitor_exit_helper"))) {
touchEbx();
}
if((!strcmp(target, ".aget_wide_helper"))) {
touchEbx(); touchEcx(); touchXmm1();
}
if((!strcmp(target, ".aget_helper")) || (!strcmp(target, ".aget_char_helper")) ||
(!strcmp(target, ".aget_short_helper")) || (!strcmp(target, ".aget_bool_helper")) ||
(!strcmp(target, ".aget_byte_helper"))) {
touchEbx(); touchEcx(); touchEdx();
}
if((!strcmp(target, ".aput_helper")) || (!strcmp(target, ".aput_char_helper")) ||
(!strcmp(target, ".aput_short_helper")) || (!strcmp(target, ".aput_bool_helper")) ||
(!strcmp(target, ".aput_byte_helper")) || (!strcmp(target, ".aput_wide_helper"))) {
touchEbx(); touchEcx(); touchEdx();
}
if((!strcmp(target, ".sput_helper")) || (!strcmp(target, ".sput_wide_helper"))) {
touchEdx(); touchEax();
}
if((!strcmp(target, ".sget_helper"))) {
touchEdx(); touchEcx();
}
if((!strcmp(target, ".sget_wide_helper"))) {
touchEdx(); touchXmm1();
}
if((!strcmp(target, ".aput_obj_helper"))) {
touchEdx(); touchEcx(); touchEax();
}
if((!strcmp(target, ".iput_helper")) || (!strcmp(target, ".iput_wide_helper"))) {
touchEbx(); touchEcx(); touchEsi();
}
if((!strcmp(target, ".iget_helper"))) {
touchEbx(); touchEcx(); touchEdx();
}
if((!strcmp(target, ".iget_wide_helper"))) {
touchEbx(); touchEcx(); touchXmm1();
}
if((!strcmp(target, ".new_array_helper"))) {
touchEbx(); touchEdx(); touchEax();
}
if((!strcmp(target, ".invoke_virtual_helper"))) {
touchEbx(); touchEcx();
}
if((!strcmp(target, ".invoke_direct_helper"))) {
touchEsi(); touchEcx();
}
if((!strcmp(target, ".invoke_super_helper"))) {
touchEbx(); touchEcx();
}
if((!strcmp(target, ".invoke_interface_helper"))) {
touchEbx(); touchEcx();
}
if((!strcmp(target, ".invokeMethodNoRange_5_helper")) ||
(!strcmp(target, ".invokeMethodNoRange_4_helper"))) {
touchEbx(); touchEsi(); touchEax(); touchEdx();
}
if((!strcmp(target, ".invokeMethodNoRange_3_helper"))) {
touchEbx(); touchEsi(); touchEax();
}
if((!strcmp(target, ".invokeMethodNoRange_2_helper"))) {
touchEbx(); touchEsi();
}
if((!strcmp(target, ".invokeMethodNoRange_1_helper"))) {
touchEbx();
}
if((!strcmp(target, ".invokeMethodRange_helper"))) {
touchEdx(); touchEsi();
}
#ifdef DEBUG_REGALLOC
ALOGI("enter beforeCall");
#endif
if(!strncmp(target, ".invokeArgsDone", 15)) resetGlue(PhysicalReg_GLUE_DVMDEX);
freeReg(true); //to avoid spilling dead logical registers
int k;
for(k = 0; k < num_compile_entries; k++) {
/* before throwing an exception, if GLUE is spilled, load to %ebp
this should happen at last */
if(k == indexForGlue) continue;
if(compileTable[k].physicalReg != PhysicalReg_Null &&
(compileTable[k].physicalType & LowOpndRegType_hard) == 0) {
/* handles non hardcoded variables that are in physical registers */
if(!strcmp(target, "exception")) {
/* before throwing an exception
update contents of all VRs in Java stack */
if(!isVirtualReg(compileTable[k].physicalType)) continue;
/* to have correct GC, we should update contents for L VRs as well */
//if(compileTable[k].gType == GLOBALTYPE_L) continue;
}
if((!strcmp(target, ".const_string_resolve")) ||
(!strcmp(target, ".static_field_resolve")) ||
(!strcmp(target, ".inst_field_resolve")) ||
(!strcmp(target, ".class_resolve")) ||
(!strcmp(target, ".direct_method_resolve")) ||
(!strcmp(target, ".virtual_method_resolve")) ||
(!strcmp(target, ".static_method_resolve"))) {
/* physical register %ebx will keep its content
but to have correct GC, we should dump content of a VR
that is mapped to %ebx */
if(compileTable[k].physicalReg == PhysicalReg_EBX &&
(!isVirtualReg(compileTable[k].physicalType)))
continue;
}
if((!strncmp(target, "dvm", 3)) || (!strcmp(target, "moddi3")) ||
(!strcmp(target, "divdi3")) ||
(!strcmp(target, "fmod")) || (!strcmp(target, "fmodf"))) {
/* callee-saved registers (%ebx, %esi, %ebp, %edi) will keep the content
but to have correct GC, we should dump content of a VR
that is mapped to a callee-saved register */
if((compileTable[k].physicalReg == PhysicalReg_EBX ||
compileTable[k].physicalReg == PhysicalReg_ESI) &&
(!isVirtualReg(compileTable[k].physicalType)))
continue;
}
#ifdef DEBUG_REGALLOC
ALOGI("SPILL logical register %d %d in beforeCall",
compileTable[k].regNum, compileTable[k].physicalType);
#endif
spillLogicalReg(k, true);
}
}
if(indexForGlue >= 0 && !strcmp(target, "exception") &&
compileTable[indexForGlue].physicalReg == PhysicalReg_Null) {
unspillLogicalReg(indexForGlue, PhysicalReg_EBP); //load %ebp
}
#ifdef DEBUG_REGALLOC
ALOGI("exit beforeCall");
#endif
return 0;
}
int getFreeReg(int type, int reg, int indexToCompileTable);
//! after calling a helper function or a VM function
//!
int afterCall(const char* target) { //un-spill
if(currentBB == NULL) return -1;
if(!strcmp(target, "ncgGetEIP")) return -1;
return 0;
}
//! check whether a temporary is 8-bit
//!
bool isTemp8Bit(int type, int reg) {
if(currentBB == NULL) return false;
if(!isTemporary(type, reg)) return false;
int k;
for(k = 0; k < num_temp_regs_per_bytecode; k++) {
if(infoByteCodeTemp[k].physicalType == type &&
infoByteCodeTemp[k].regNum == reg) {
return infoByteCodeTemp[k].is8Bit;
}
}
ALOGE("isTemp8Bit %d %d", type, reg);
return false;
}
/* functions to access live ranges of a VR
Live range info is stored in memVRTable[].ranges, which is a linked list
*/
//! check whether a VR is live at the current bytecode
//!
bool isVRLive(int vA) {
int index = searchMemTable(vA);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", vA);
return false;
}
LiveRange* ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC >= ptr->start && offsetPC <= ptr->end) return true;
ptr = ptr->next;
}
return false;
}
//! check whether the current bytecode is the last access to a VR within a live range
//!for 64-bit VR, return true only when true for both low half and high half
bool isLastByteCodeOfLiveRange(int compileIndex) {
int k = compileIndex;
OpndSize tSize = getRegSize(compileTable[k].physicalType);
int index;
LiveRange* ptr = NULL;
if(tSize == OpndSize_32) {
/* check live ranges for the VR */
index = searchMemTable(compileTable[k].regNum);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
return false;
}
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC == ptr->end) return true;
ptr = ptr->next;
}
return false;
}
/* size of the VR is 64 */
/* check live ranges of the low half */
index = searchMemTable(compileTable[k].regNum);
bool tmpB = false;
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
return false;
}
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC == ptr->end) {
tmpB = true;
break;
}
ptr = ptr->next;
}
if(!tmpB) return false;
/* check live ranges of the high half */
index = searchMemTable(compileTable[k].regNum+1);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
return false;
}
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC == ptr->end) {
return true;
}
ptr = ptr->next;
}
return false;
}
//! check whether the current bytecode is in a live range that extends to end of a basic block
//!for 64 bit, return true if true for both low half and high half
bool reachEndOfBB(int compileIndex) {
int k = compileIndex;
OpndSize tSize = getRegSize(compileTable[k].physicalType);
int index;
bool retCode = false;
/* check live ranges of the low half */
index = searchMemTable(compileTable[k].regNum);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
return false;
}
LiveRange* ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC >= ptr->start &&
offsetPC <= ptr->end) {
if(ptr->end == currentBB->pc_end) {
retCode = true;
}
break;
}
ptr = ptr->next;
}
if(!retCode) return false;
if(tSize == OpndSize_32) return true;
/* check live ranges of the high half */
index = searchMemTable(compileTable[k].regNum+1);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
return false;
}
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC >= ptr->start &&
offsetPC <= ptr->end) {
if(ptr->end == currentBB->pc_end) return true;
return false;
}
ptr = ptr->next;
}
#ifdef PRINT_WARNING
ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum+1);
#endif
return false;
}
//!check whether the current bytecode is the next to last access to a VR within a live range
//!for 64 bit, return true if true for both low half and high half
bool isNextToLastAccess(int compileIndex) {
int k = compileIndex;
OpndSize tSize = getRegSize(compileTable[k].physicalType);
int index;
/* check live ranges for the low half */
bool retCode = false;
index = searchMemTable(compileTable[k].regNum);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
return false;
}
LiveRange* ptr = memVRTable[index].ranges;
while(ptr != NULL) {
int num_access = ptr->num_access;
if(num_access < 2) {
ptr = ptr->next;
continue;
}
if(offsetPC == ptr->accessPC[num_access-2]) {
retCode = true;
break;
}
ptr = ptr->next;
}
if(!retCode) return false;
if(tSize == OpndSize_32) return true;
/* check live ranges for the high half */
index = searchMemTable(compileTable[k].regNum+1);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
return false;
}
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
int num_access = ptr->num_access;
if(num_access < 2) {
ptr = ptr->next;
continue;
}
if(offsetPC == ptr->accessPC[num_access-2]) return true;
ptr = ptr->next;
}
return false;
}
/** return the start of the next live range
if there does not exist a next live range, return pc_end of the basic block
for 64 bits, return the larger one for low half and high half
Assume live ranges are sorted in order
*/
int getNextLiveRange(int compileIndex) {
int k = compileIndex;
OpndSize tSize = getRegSize(compileTable[k].physicalType);
/* check live ranges of the low half */
int index;
index = searchMemTable(compileTable[k].regNum);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
return offsetPC;
}
bool found = false;
int nextUse = offsetPC;
LiveRange* ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(ptr->start > offsetPC) {
nextUse = ptr->start;
found = true;
break;
}
ptr = ptr->next;
}
if(!found) return currentBB->pc_end;
if(tSize == OpndSize_32) return nextUse;
/* check live ranges of the high half */
found = false;
index = searchMemTable(compileTable[k].regNum+1);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
return offsetPC;
}
int nextUse2 = offsetPC;
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(ptr->start > offsetPC) {
nextUse2 = ptr->start;
found = true;
break;
}
ptr = ptr->next;
}
if(!found) return currentBB->pc_end;
/* return the larger one */
return (nextUse2 > nextUse ? nextUse2 : nextUse);
}
/** return the next access to a variable
If variable is 64-bit, get the next access to the lower half and the high half
return the eariler one
*/
int getNextAccess(int compileIndex) {
int k = compileIndex;
OpndSize tSize = getRegSize(compileTable[k].physicalType);
int index, k3;
/* check live ranges of the low half */
index = searchMemTable(compileTable[k].regNum);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
return offsetPC;
}
bool found = false;
int nextUse = offsetPC;
LiveRange* ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC >= ptr->start &&
offsetPC <= ptr->end) {
/* offsetPC belongs to this live range */
for(k3 = 0; k3 < ptr->num_access; k3++) {
if(ptr->accessPC[k3] > offsetPC) {
nextUse = ptr->accessPC[k3];
break;
}
}
found = true;
break;
}
ptr = ptr->next;
}
#ifdef PRINT_WARNING
if(!found)
ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum);
#endif
if(tSize == OpndSize_32) return nextUse;
/* check live ranges of the high half */
found = false;
index = searchMemTable(compileTable[k].regNum+1);
if(index < 0) {
ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
return offsetPC;
}
int nextUse2 = offsetPC;
ptr = memVRTable[index].ranges;
while(ptr != NULL) {
if(offsetPC >= ptr->start &&
offsetPC <= ptr->end) {
for(k3 = 0; k3 < ptr->num_access; k3++) {
if(ptr->accessPC[k3] > offsetPC) {
nextUse2 = ptr->accessPC[k3];
break;
}
}
found = true;
break;
}
ptr = ptr->next;
}
#ifdef PRINT_WARNING
if(!found) ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum+1);
#endif
/* return the earlier one */
if(nextUse2 < nextUse) return nextUse2;
return nextUse;
}
/** free variables that are no longer in use
free a temporary with reference count of zero
will dump content of a GL VR to memory if necessary
*/
int freeReg(bool spillGL) {
if(currentBB == NULL) return 0;
int k;
for(k = 0; k < num_compile_entries; k++) {
if(compileTable[k].refCount == 0 && compileTable[k].physicalReg != PhysicalReg_Null) {
/* check entries with reference count of zero and is mapped to a physical register */
bool typeA = !isVirtualReg(compileTable[k].physicalType);
bool freeCrit = true, delayFreeing = false;
bool typeC = false, typeB = false, reachEnd = false;
if(isVirtualReg(compileTable[k].physicalType)) {
/* VRs in the compile table */
/* Check if delay for freeing was requested for this VR */
delayFreeing = getVRFreeDelayRequested(compileTable[k].regNum);
freeCrit = isLastByteCodeOfLiveRange(k); /* last bytecode of a live range */
reachEnd = reachEndOfBB(k); /* in a live range that extends to end of a basic block */
#ifdef DEBUG_LIVE_RANGE
ALOGI("IN freeReg: VR %d offsetPC %x freecrit %d reachEnd %d nextToLast %d", compileTable[k].regNum, offsetPC, freeCrit, reachEnd, isNextToLastAccess(k));
#endif
/* Bug: spilling of VRs after edi(rFP) is updated in RETURN bytecode
will cause variables for callee to be spilled to the caller stack frame and
to overwrite varaibles for caller
*/
/* last bytecode of a live range reaching end of BB if not counting the fake usage at end */
bool boolB = reachEnd && isNextToLastAccess(k);
/* Bug: when a GG VR is checked at end of a basic block,
freeCrit will be true and physicalReg will be set to Null
Fix: change free condition from freeCrit to (freeCrit && offsetPC != currentBB->pc_end)
*/
/* conditions to free a GG VR:
last bytecode of a live range reaching end of BB if not counting the fake usage at end && endsWithReturn
or
last bytecode of a live range && offsetPC != currentBB->pc_end
-> last bytecode of a live range not reaching end
*/
typeC = ((freeCrit && offsetPC != currentBB->pc_end) ||
(currentBB->endsWithReturn && boolB)) &&
compileTable[k].gType == GLOBALTYPE_GG &&
!delayFreeing;
/* conditions to free a L|GL VR:
last bytecode of a live range
or
last bytecode of a live range reaching end of BB if not counting the fake usage at end
*/
typeB = (freeCrit || boolB) &&
(compileTable[k].gType != GLOBALTYPE_GG) &&
!delayFreeing;
}
if(typeA || typeB || typeC) {
#ifdef DEBUG_REGALLOC
if(typeA)
ALOGI("FREE TEMP %d with type %d allocated to %d",
compileTable[k].regNum, compileTable[k].physicalType,
compileTable[k].physicalReg);
else if(typeB)
ALOGI("FREE VR L|GL %d with type %d allocated to %d",
compileTable[k].regNum, compileTable[k].physicalType,
compileTable[k].physicalReg);
else if(typeC)
ALOGI("FREE VR GG %d with type %d allocated to %d",
compileTable[k].regNum, compileTable[k].physicalType,
compileTable[k].physicalReg);
#endif
bool dumpGL = false;
if(compileTable[k].gType == GLOBALTYPE_GL && !reachEnd) {
/* if the live range does not reach end of basic block
and there exists a try block from offsetPC to the next live range
dump VR to interpreted stack */
int tmpPC = getNextLiveRange(k);
if(existATryBlock(currentMethod, offsetPC, tmpPC)) dumpGL = true;
}
/* if the live range reach end of basic block, dump VR to interpreted stack */
if(compileTable[k].gType == GLOBALTYPE_GL && reachEnd) dumpGL = true;
if(dumpGL) {
if(spillGL) {
#ifdef DEBUG_REGALLOC
ALOGI("SPILL VR GL %d %d", compileTable[k].regNum, compileTable[k].physicalType);
#endif
spillLogicalReg(k, true); //will dump VR to memory & update physicalReg
}
}
else
compileTable[k].physicalReg = PhysicalReg_Null;
}
if(typeA) {
if(compileTable[k].spill_loc_index >= 0) {
/* update spill info for temporaries */
spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 0;
compileTable[k].spill_loc_index = -1;
ALOGE("free a temporary register with TRSTATE_SPILLED");
}
}
}
}
syncAllRegs(); //sync up allRegs (isUsed & freeTimeStamp) with compileTable
return 0;
}
//! reduce the reference count by 1
//! input: index to compileTable
void decreaseRefCount(int index) {
#ifdef DEBUG_REFCOUNT
ALOGI("REFCOUNT: %d in decreaseRefCount %d %d", compileTable[index].refCount,
compileTable[index].regNum, compileTable[index].physicalType);
#endif
compileTable[index].refCount--;
if(compileTable[index].refCount < 0) {
ALOGE("refCount is negative for REG %d %d", compileTable[index].regNum, compileTable[index].physicalType);
dvmAbort();
}
}
//! reduce the reference count of a VR by 1
//! input: reg & type
int updateRefCount(int reg, LowOpndRegType type) {
if(currentBB == NULL) return 0;
int index = searchCompileTable(LowOpndRegType_virtual | type, reg);
if(index < 0) {
ALOGE("virtual reg %d type %d not found in updateRefCount", reg, type);
return -1;
}
decreaseRefCount(index);
return 0;
}
//! reduce the reference count of a variable by 1
//! The variable is named with lowering module's naming mechanism
int updateRefCount2(int reg, int type, bool isPhysical) {
if(currentBB == NULL) return 0;
int newType = convertType(type, reg, isPhysical);
if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
int index = searchCompileTable(newType, reg);
if(index < 0) {
ALOGE("reg %d type %d not found in updateRefCount", reg, newType);
return -1;
}
decreaseRefCount(index);
return 0;
}
//! check whether a glue variable is in physical register or spilled
//!
bool isGlueHandled(int glue_reg) {
if(currentBB == NULL) return false;
int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
if(index < 0) {
ALOGE("glue reg %d not found in isGlueHandled", glue_reg);
return -1;
}
if(compileTable[index].spill_loc_index >= 0 ||
compileTable[index].physicalReg != PhysicalReg_Null) {
#ifdef DEBUG_GLUE
ALOGI("GLUE isGlueHandled for %d returns true", glue_reg);
#endif
return true;
}
#ifdef DEBUG_GLUE
ALOGI("GLUE isGlueHandled for %d returns false", glue_reg);
#endif
return false;
}
//! reset the state of a glue variable to not existant (not in physical register nor spilled)
//!
void resetGlue(int glue_reg) {
if(currentBB == NULL) return;
int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
if(index < 0) {
ALOGE("glue reg %d not found in resetGlue", glue_reg);
return;
}
#ifdef DEBUG_GLUE
ALOGI("GLUE reset for %d", glue_reg);
#endif
compileTable[index].physicalReg = PhysicalReg_Null;
if(compileTable[index].spill_loc_index >= 0)
spillIndexUsed[compileTable[index].spill_loc_index >> 2] = 0;
compileTable[index].spill_loc_index = -1;
}
//! set a glue variable in a physical register allocated for a variable
//! Variable is using lowering module's naming convention
void updateGlue(int reg, bool isPhysical, int glue_reg) {
if(currentBB == NULL) return;
int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
if(index < 0) {
ALOGE("glue reg %d not found in updateGlue", glue_reg);
return;
}
/* find the compileTable entry for variable <reg, isPhysical> */
int newType = convertType(LowOpndRegType_gp, reg, isPhysical);
if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
int index2 = searchCompileTable(newType, reg);
if(index2 < 0 || compileTable[index2].physicalReg == PhysicalReg_Null) {
ALOGE("updateGlue reg %d type %d", reg, newType);
return;
}
#ifdef DEBUG_GLUE
ALOGI("physical register for GLUE %d set to %d", glue_reg, compileTable[index2].physicalReg);
#endif
compileTable[index].physicalReg = compileTable[index2].physicalReg;
compileTable[index].spill_loc_index = -1;
}
//! check whether a virtual register is in a physical register
//! If updateRefCount is 0, do not update reference count;
//!If updateRefCount is 1, update reference count only when VR is in a physical register
//!If updateRefCount is 2, update reference count
int checkVirtualReg(int reg, LowOpndRegType type, int updateRefCount) {
if(currentBB == NULL) return PhysicalReg_Null;
int index = searchCompileTable(LowOpndRegType_virtual | type, reg);
if(index < 0) {
ALOGE("virtual reg %d type %d not found in checkVirtualReg", reg, type);
return PhysicalReg_Null;
}
//reduce reference count
if(compileTable[index].physicalReg != PhysicalReg_Null) {
if(updateRefCount != 0) decreaseRefCount(index);
return compileTable[index].physicalReg;
}
if(updateRefCount == 2) decreaseRefCount(index);
return PhysicalReg_Null;
}
//!check whether a temporary can share the same physical register with a VR
//!This is called in get_virtual_reg
//!If this function returns false, new register will be allocated for this temporary
bool checkTempReg2(int reg, int type, bool isPhysical, int physicalRegForVR) {
if(currentBB == NULL) return false;
if(isPhysical) return false;
int newType = convertType(type, reg, isPhysical);
if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
int k;
for(k = 0; k < num_temp_regs_per_bytecode; k++) {
if(infoByteCodeTemp[k].physicalType == newType &&
infoByteCodeTemp[k].regNum == reg) {
#ifdef DEBUG_MOVE_OPT
ALOGI("MOVE_OPT checkTempRegs for %d %d returns %d %d",
reg, newType, infoByteCodeTemp[k].shareWithVR, infoByteCodeTemp[k].is8Bit);
#endif
if(!infoByteCodeTemp[k].is8Bit) return infoByteCodeTemp[k].shareWithVR;
//is8Bit true for gp type only
if(!infoByteCodeTemp[k].shareWithVR) return false;
//both true
if(physicalRegForVR >= PhysicalReg_EAX && physicalRegForVR <= PhysicalReg_EDX) return true;
#ifdef DEBUG_MOVE_OPT
ALOGI("MOVE_OPT registerAllocMove not used for 8-bit register");
#endif
return false;
}
}
ALOGE("checkTempReg2 %d %d", reg, newType);
return false;
}
//!check whether a temporary can share the same physical register with a VR
//!This is called in set_virtual_reg
int checkTempReg(int reg, int type, bool isPhysical, int vrNum) {
if(currentBB == NULL) return PhysicalReg_Null;
int newType = convertType(type, reg, isPhysical);
if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
int index = searchCompileTable(newType, reg);
if(index < 0) {
ALOGE("temp reg %d type %d not found in checkTempReg", reg, newType);
return PhysicalReg_Null;
}
//a temporary register can share the same physical reg with a VR if registerAllocMove is called
//this will cause problem with move bytecode
//get_VR(v1, t1) t1 and v1 point to the same physical reg
//set_VR(t1, v2) t1 and v2 point to the same physical reg
//this will cause v1 and v2 point to the same physical reg
//FIX: if this temp reg shares a physical reg with another reg
if(compileTable[index].physicalReg != PhysicalReg_Null) {
int k;
for(k = 0; k < num_compile_entries; k++) {
if(k == index) continue;
if(compileTable[k].physicalReg == compileTable[index].physicalReg) {
return PhysicalReg_Null; //will allocate a register for VR
}
}
decreaseRefCount(index);
return compileTable[index].physicalReg;
}
if(compileTable[index].spill_loc_index >= 0) {
//registerAlloc will call unspillLogicalReg (load from memory)
#ifdef DEBUG_REGALLOC
ALOGW("in checkTempReg, the temporary register %d %d was spilled", reg, type);
#endif
int regAll = registerAlloc(type, reg, isPhysical, true/* updateRefCount */);
return regAll;
}
return PhysicalReg_Null;
}
//!check whether a variable has exposed usage in a basic block
//!It calls hasExposedUsage2
bool hasExposedUsage(LowOpndRegType type, int regNum, BasicBlock_O1* bb) {
int index = searchVirtualInfoOfBB(type, regNum, bb);
if(index >= 0 && hasExposedUsage2(bb, index)) {
return true;
}
return false;
}
//!check whether a variable has exposed usage in other basic blocks
//!
bool hasOtherExposedUsage(OpndSize size, int regNum, BasicBlock_O1* bb) {
return true; //assume the worst case
}
//! handles constant VRs at end of a basic block
//!If a VR is constant at end of a basic block and (it has exposed usage in other basic blocks or reaches a GG VR), dump immediate to memory
void constVREndOfBB() {
BasicBlock_O1* bb = currentBB;
int k, k2;
//go through GG VRs, update a bool array
int constUsedByGG[MAX_CONST_REG];
for(k = 0; k < num_const_vr; k++)
constUsedByGG[k] = 0;
for(k = 0; k < num_compile_entries; k++) {
if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType == GLOBALTYPE_GG) {
OpndSize size = getRegSize(compileTable[k].physicalType);
int regNum = compileTable[k].regNum;
int indexL = -1;
int indexH = -1;
for(k2 = 0; k2 < num_const_vr; k2++) {
if(constVRTable[k2].regNum == regNum) {
indexL = k2;
continue;
}
if(constVRTable[k2].regNum == regNum + 1 && size == OpndSize_64) {
indexH = k2;
continue;
}
}
if(indexL >= 0) constUsedByGG[indexL] = 1;
if(indexH >= 0) constUsedByGG[indexH] = 1;
} //GG VR
}
for(k = 0; k < num_const_vr; k++) {
if(!constVRTable[k].isConst) continue;
bool hasExp = false;
if(constUsedByGG[k] == 0)
hasExp = hasOtherExposedUsage(OpndSize_32, constVRTable[k].regNum, bb);
if(constUsedByGG[k] != 0 || hasExp) {
dumpImmToMem(constVRTable[k].regNum, OpndSize_32, constVRTable[k].value);
setVRToMemory(constVRTable[k].regNum, OpndSize_32);
#ifdef DEBUG_ENDOFBB
ALOGI("ENDOFBB: exposed VR %d is const %d (%x)",
constVRTable[k].regNum, constVRTable[k].value, constVRTable[k].value);
#endif
} else {
#ifdef DEBUG_ENDOFBB
ALOGI("ENDOFBB: unexposed VR %d is const %d (%x)",
constVRTable[k].regNum, constVRTable[k].value, constVRTable[k].value);
#endif
}
}
}
//!handles GG VRs at end of a basic block
//!make sure all GG VRs are in pre-defined physical registers
void globalVREndOfBB(const Method* method) {
//fix: freeReg first to write LL VR back to memory to avoid it gets overwritten by GG VRs
freeReg(true);
int k;
//spill GG VR first if it is not mapped to the specific reg
//release GLUE regs
for(k = 0; k < num_compile_entries; k++) {
if(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
compileTable[k].regNum != PhysicalReg_GLUE) {
compileTable[k].physicalReg = PhysicalReg_Null;
compileTable[k].spill_loc_index = -1;
}
//if part of a GG VR is const, the physical reg is set to null
if(isVirtualReg(compileTable[k].physicalType) &&
compileTable[k].gType == GLOBALTYPE_GG && compileTable[k].physicalReg != PhysicalReg_Null &&
compileTable[k].physicalReg != compileTable[k].physicalReg_prev) {
#ifdef DEBUG_ENDOFBB
ALOGW("end of BB GG VR is not mapped to the specific reg: %d %d %d",
compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg);
ALOGW("ENDOFBB SPILL VR %d %d", compileTable[k].regNum, compileTable[k].physicalType);
#endif
spillLogicalReg(k, true); //the next section will load VR from memory to the specific reg
}
}
syncAllRegs();
for(k = 0; k < num_compile_entries; k++) {
if(isVirtualReg(compileTable[k].physicalType)) {
if(compileTable[k].gType == GLOBALTYPE_GG &&
compileTable[k].physicalReg == PhysicalReg_Null && (!currentBB->endsWithReturn)) {
#ifdef DEBUG_ENDOFBB
ALOGI("ENDOFBB GET GG VR %d %d to physical register %d", compileTable[k].regNum,
compileTable[k].physicalType, compileTable[k].physicalReg_prev);
#endif
compileTable[k].physicalReg = compileTable[k].physicalReg_prev;
if(allRegs[compileTable[k].physicalReg_prev].isUsed) {
ALOGE("physical register for GG VR is still used");
}
get_virtual_reg_noalloc(compileTable[k].regNum,
getRegSize(compileTable[k].physicalType),
compileTable[k].physicalReg_prev,
true);
}
}//not const
}
if(indexForGlue >= 0 &&
compileTable[indexForGlue].physicalReg == PhysicalReg_Null) {
unspillLogicalReg(indexForGlue, PhysicalReg_EBP); //load %ebp
}
}
//! get ready for the next version of a hard-coded register
//!set its physicalReg to Null and update its reference count
int nextVersionOfHardReg(PhysicalReg pReg, int refCount) {
int indexT = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_hard, pReg);
if(indexT < 0)
return -1;
compileTable[indexT].physicalReg = PhysicalReg_Null;
#ifdef DEBUG_REFCOUNT
ALOGI("REFCOUNT: to %d in nextVersionOfHardReg %d", refCount, pReg);
#endif
compileTable[indexT].refCount = refCount;
return 0;
}
/** update compileTable with bb->infoBasicBlock[k]
*/
void insertFromVirtualInfo(BasicBlock_O1* bb, int k) {
int index = searchCompileTable(LowOpndRegType_virtual | bb->infoBasicBlock[k].physicalType, bb->infoBasicBlock[k].regNum);
if(index < 0) {
/* the virtual register is not in compileTable, insert it */
index = num_compile_entries;
compileTable[num_compile_entries].physicalType = (LowOpndRegType_virtual | bb->infoBasicBlock[k].physicalType);
compileTable[num_compile_entries].regNum = bb->infoBasicBlock[k].regNum;
compileTable[num_compile_entries].physicalReg = PhysicalReg_Null;
compileTable[num_compile_entries].bb = bb;
compileTable[num_compile_entries].indexToInfoBB = k;
compileTable[num_compile_entries].spill_loc_index = -1;
compileTable[num_compile_entries].gType = bb->infoBasicBlock[k].gType;
num_compile_entries++;
if(num_compile_entries >= COMPILE_TABLE_SIZE) {
ALOGE("compileTable overflow");
dvmAbort();
}
}
/* re-set reference count of all VRs */
compileTable[index].refCount = bb->infoBasicBlock[k].refCount;
compileTable[index].accessType = bb->infoBasicBlock[k].accessType;
if(compileTable[index].gType == GLOBALTYPE_GG)
compileTable[index].physicalReg_prev = bb->infoBasicBlock[k].physicalReg_GG;
}
/** update compileTable with infoByteCodeTemp[k]
*/
void insertFromTempInfo(int k) {
int index = searchCompileTable(infoByteCodeTemp[k].physicalType, infoByteCodeTemp[k].regNum);
if(index < 0) {
/* the temporary is not in compileTable, insert it */
index = num_compile_entries;
compileTable[num_compile_entries].physicalType = infoByteCodeTemp[k].physicalType;
compileTable[num_compile_entries].regNum = infoByteCodeTemp[k].regNum;
num_compile_entries++;
if(num_compile_entries >= COMPILE_TABLE_SIZE) {
ALOGE("compileTable overflow");
dvmAbort();
}
}
compileTable[index].physicalReg = PhysicalReg_Null;
compileTable[index].refCount = infoByteCodeTemp[k].refCount;
compileTable[index].linkageToVR = infoByteCodeTemp[k].linkageToVR;
compileTable[index].gType = GLOBALTYPE_L;
compileTable[index].spill_loc_index = -1;
}
/* insert a glue-related register GLUE_DVMDEX to compileTable */
void insertGlueReg() {
compileTable[num_compile_entries].physicalType = LowOpndRegType_gp;
compileTable[num_compile_entries].regNum = PhysicalReg_GLUE_DVMDEX;
compileTable[num_compile_entries].refCount = 2;
compileTable[num_compile_entries].physicalReg = PhysicalReg_Null;
compileTable[num_compile_entries].bb = NULL;
compileTable[num_compile_entries].spill_loc_index = -1;
compileTable[num_compile_entries].accessType = REGACCESS_N;
compileTable[num_compile_entries].linkageToVR = -1;
compileTable[num_compile_entries].gType = GLOBALTYPE_L;
num_compile_entries++;
if(num_compile_entries >= COMPILE_TABLE_SIZE) {
ALOGE("compileTable overflow");
dvmAbort();
}
}
/** print infoBasicBlock of the given basic block
*/
void dumpVirtualInfoOfBasicBlock(BasicBlock_O1* bb) {
int jj;
ALOGI("Virtual Info for BB%d --------", bb->bb_index);
for(jj = 0; jj < bb->num_regs; jj++) {
ALOGI("regNum %d physicalType %d accessType %d refCount %d def ",
bb->infoBasicBlock[jj].regNum, bb->infoBasicBlock[jj].physicalType,
bb->infoBasicBlock[jj].accessType, bb->infoBasicBlock[jj].refCount);
int k;
for(k = 0; k < bb->infoBasicBlock[jj].num_reaching_defs; k++)
ALOGI("[%x %d %d %d] ", bb->infoBasicBlock[jj].reachingDefs[k].offsetPC,
bb->infoBasicBlock[jj].reachingDefs[k].regNum,
bb->infoBasicBlock[jj].reachingDefs[k].physicalType,
bb->infoBasicBlock[jj].reachingDefs[k].accessType);
ALOGI("");
}
}
/** print compileTable
*/
void dumpCompileTable() {
int jj;
ALOGI("Compile Table for method ----------");
for(jj = 0; jj < num_compile_entries; jj++) {
ALOGI("regNum %d physicalType %d refCount %d isConst %d physicalReg %d type %d",
compileTable[jj].regNum, compileTable[jj].physicalType,
compileTable[jj].refCount, compileTable[jj].isConst, compileTable[jj].physicalReg, compileTable[jj].gType);
}
}
//!check whether a basic block is the start of an exception handler
//!
bool isFirstOfHandler(BasicBlock_O1* bb) {
int i;
for(i = 0; i < num_exception_handlers; i++) {
if(bb->pc_start == exceptionHandlers[i]) return true;
}
return false;
}
//! create a basic block that starts at src_pc and ends at end_pc
//!
BasicBlock_O1* createBasicBlock(int src_pc, int end_pc) {
BasicBlock_O1* bb = (BasicBlock_O1*)malloc(sizeof(BasicBlock_O1));
if(bb == NULL) {
ALOGE("out of memory");
return NULL;
}
bb->pc_start = src_pc;
bb->bb_index = num_bbs_for_method;
if(bb_entry == NULL) bb_entry = bb;
/* insert the basic block to method_bbs_sorted in ascending order of pc_start */
int k;
int index = -1;
for(k = 0; k < num_bbs_for_method; k++)
if(method_bbs_sorted[k]->pc_start > src_pc) {
index = k;
break;
}
if(index == -1)
method_bbs_sorted[num_bbs_for_method] = bb;
else {
/* push the elements from index by 1 */
for(k = num_bbs_for_method-1; k >= index; k--)
method_bbs_sorted[k+1] = method_bbs_sorted[k];
method_bbs_sorted[index] = bb;
}
num_bbs_for_method++;
if(num_bbs_for_method >= MAX_NUM_BBS_PER_METHOD) {
ALOGE("too many basic blocks");
dvmAbort();
}
return bb;
}
/* BEGIN code to handle state transfers */
//! save the current state of register allocator to a state table
//!
void rememberState(int stateNum) {
#ifdef DEBUG_STATE
ALOGI("STATE: remember state %d", stateNum);
#endif
int k;
for(k = 0; k < num_compile_entries; k++) {
if(stateNum == 1) {
stateTable1_1[k].physicalReg = compileTable[k].physicalReg;
stateTable1_1[k].spill_loc_index = compileTable[k].spill_loc_index;
}
else if(stateNum == 2) {
stateTable1_2[k].physicalReg = compileTable[k].physicalReg;
stateTable1_2[k].spill_loc_index = compileTable[k].spill_loc_index;
}
else if(stateNum == 3) {
stateTable1_3[k].physicalReg = compileTable[k].physicalReg;
stateTable1_3[k].spill_loc_index = compileTable[k].spill_loc_index;
}
else if(stateNum == 4) {
stateTable1_4[k].physicalReg = compileTable[k].physicalReg;
stateTable1_4[k].spill_loc_index = compileTable[k].spill_loc_index;
}
else ALOGE("state table overflow");
#ifdef DEBUG_STATE
ALOGI("logical reg %d %d mapped to physical reg %d with spill index %d refCount %d",
compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg,
compileTable[k].spill_loc_index, compileTable[k].refCount);
#endif
}
for(k = 0; k < num_memory_vr; k++) {
if(stateNum == 1) {
stateTable2_1[k].regNum = memVRTable[k].regNum;
stateTable2_1[k].inMemory = memVRTable[k].inMemory;
}
else if(stateNum == 2) {
stateTable2_2[k].regNum = memVRTable[k].regNum;
stateTable2_2[k].inMemory = memVRTable[k].inMemory;
}
else if(stateNum == 3) {
stateTable2_3[k].regNum = memVRTable[k].regNum;
stateTable2_3[k].inMemory = memVRTable[k].inMemory;
}
else if(stateNum == 4) {
stateTable2_4[k].regNum = memVRTable[k].regNum;
stateTable2_4[k].inMemory = memVRTable[k].inMemory;
}
else ALOGE("state table overflow");
#ifdef DEBUG_STATE
ALOGI("virtual reg %d in memory %d", memVRTable[k].regNum, memVRTable[k].inMemory);
#endif
}
}
//!update current state of register allocator with a state table
//!
void goToState(int stateNum) {
int k;
#ifdef DEBUG_STATE
ALOGI("STATE: go to state %d", stateNum);
#endif
for(k = 0; k < num_compile_entries; k++) {
if(stateNum == 1) {
compileTable[k].physicalReg = stateTable1_1[k].physicalReg;
compileTable[k].spill_loc_index = stateTable1_1[k].spill_loc_index;
}
else if(stateNum == 2) {
compileTable[k].physicalReg = stateTable1_2[k].physicalReg;
compileTable[k].spill_loc_index = stateTable1_2[k].spill_loc_index;
}
else if(stateNum == 3) {
compileTable[k].physicalReg = stateTable1_3[k].physicalReg;
compileTable[k].spill_loc_index = stateTable1_3[k].spill_loc_index;
}
else if(stateNum == 4) {
compileTable[k].physicalReg = stateTable1_4[k].physicalReg;
compileTable[k].spill_loc_index = stateTable1_4[k].spill_loc_index;
}
else ALOGE("state table overflow");
}
updateSpillIndexUsed();
syncAllRegs(); //to sync up allRegs CAN'T call freeReg here
//since it will change the state!!!
for(k = 0; k < num_memory_vr; k++) {
if(stateNum == 1) {
memVRTable[k].regNum = stateTable2_1[k].regNum;
memVRTable[k].inMemory = stateTable2_1[k].inMemory;
}
else if(stateNum == 2) {
memVRTable[k].regNum = stateTable2_2[k].regNum;
memVRTable[k].inMemory = stateTable2_2[k].inMemory;
}
else if(stateNum == 3) {
memVRTable[k].regNum = stateTable2_3[k].regNum;
memVRTable[k].inMemory = stateTable2_3[k].inMemory;
}
else if(stateNum == 4) {
memVRTable[k].regNum = stateTable2_4[k].regNum;
memVRTable[k].inMemory = stateTable2_4[k].inMemory;
}
else ALOGE("state table overflow");
}
}
typedef struct TransferOrder {
int targetReg;
int targetSpill;
int compileIndex;
} TransferOrder;
#define MAX_NUM_DEST 20
//! a source register is used as a source in transfer
//! it can have a maximum of MAX_NUM_DEST destinations
typedef struct SourceReg {
int physicalReg;
int num_dests; //check bound
TransferOrder dsts[MAX_NUM_DEST];
} SourceReg;
int num_src_regs = 0; //check bound
//! physical registers that are used as a source in transfer
//! we allow a maximum of MAX_NUM_DEST sources in a transfer
SourceReg srcRegs[MAX_NUM_DEST];
//! tell us whether a source register is handled already
bool handledSrc[MAX_NUM_DEST];
//! in what order should the source registers be handled
int handledOrder[MAX_NUM_DEST];
//! insert a source register with a single destination
//!
void insertSrcReg(int srcPhysical, int targetReg, int targetSpill, int index) {
int k = 0;
for(k = 0; k < num_src_regs; k++) {
if(srcRegs[k].physicalReg == srcPhysical) { //increase num_dests
if(srcRegs[k].num_dests >= MAX_NUM_DEST) {
ALOGE("exceed number dst regs for a source reg");
dvmAbort();
}
srcRegs[k].dsts[srcRegs[k].num_dests].targetReg = targetReg;
srcRegs[k].dsts[srcRegs[k].num_dests].targetSpill = targetSpill;
srcRegs[k].dsts[srcRegs[k].num_dests].compileIndex = index;
srcRegs[k].num_dests++;
return;
}
}
if(num_src_regs >= MAX_NUM_DEST) {
ALOGE("exceed number of source regs");
dvmAbort();
}
srcRegs[num_src_regs].physicalReg = srcPhysical;
srcRegs[num_src_regs].num_dests = 1;
srcRegs[num_src_regs].dsts[0].targetReg = targetReg;
srcRegs[num_src_regs].dsts[0].targetSpill = targetSpill;
srcRegs[num_src_regs].dsts[0].compileIndex = index;
num_src_regs++;
}
//! check whether a register is a source and the source is not yet handled
//!
bool dstStillInUse(int dstReg) {
if(dstReg == PhysicalReg_Null) return false;
int k;
int index = -1;
for(k = 0; k < num_src_regs; k++) {
if(dstReg == srcRegs[k].physicalReg) {
index = k;
break;
}
}
if(index < 0) return false; //not in use
if(handledSrc[index]) return false; //not in use
return true;
}
//! reset the state of glue variables in a state table
//!
void resetStateOfGlue(int stateNum, int k) {
#ifdef DEBUG_STATE
ALOGI("resetStateOfGlue state %d regNum %d", stateNum, compileTable[k].regNum);
#endif
if(stateNum == 1) {
stateTable1_1[k].physicalReg = PhysicalReg_Null;
stateTable1_1[k].spill_loc_index = -1;
}
else if(stateNum == 2) {
stateTable1_2[k].physicalReg = PhysicalReg_Null;
stateTable1_2[k].spill_loc_index = -1;
}
else if(stateNum == 3) {
stateTable1_3[k].physicalReg = PhysicalReg_Null;
stateTable1_3[k].spill_loc_index = -1;
}
else if(stateNum == 4) {
stateTable1_4[k].physicalReg = PhysicalReg_Null;
stateTable1_4[k].spill_loc_index = -1;
}
}
//! construct a legal order of the source registers in this transfer
//!
void constructSrcRegs(int stateNum) {
int k;
num_src_regs = 0;
#ifdef DEBUG_STATE
ALOGI("IN constructSrcRegs");
#endif
for(k = 0; k < num_compile_entries; k++) {
#ifdef DEBUG_STATE
ALOGI("logical reg %d %d mapped to physical reg %d with spill index %d refCount %d",
compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg,
compileTable[k].spill_loc_index, compileTable[k].refCount);
#endif
int pType = compileTable[k].physicalType;
//ignore hardcoded logical registers
if((pType & LowOpndRegType_hard) != 0) continue;
//ignore type _fs
if((pType & MASK_FOR_TYPE) == LowOpndRegType_fs) continue;
if((pType & MASK_FOR_TYPE) == LowOpndRegType_fs_s) continue;
//GL VR refCount is zero, can't ignore
//L VR refCount is zero, ignore
//GG VR refCount is zero, can't ignore
//temporary refCount is zero, ignore
//for GLUE variables, if they do not exist, reset the entries in state table
if(compileTable[k].physicalReg == PhysicalReg_Null &&
compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
compileTable[k].regNum != PhysicalReg_GLUE &&
compileTable[k].spill_loc_index < 0) {
resetStateOfGlue(stateNum, k);
}
/* get the target state */
int targetReg = PhysicalReg_Null;
int targetSpill = -1;
if(stateNum == 1) {
targetReg = stateTable1_1[k].physicalReg;
targetSpill = stateTable1_1[k].spill_loc_index;
}
else if(stateNum == 2) {
targetReg = stateTable1_2[k].physicalReg;
targetSpill = stateTable1_2[k].spill_loc_index;
}
else if(stateNum == 3) {
targetReg = stateTable1_3[k].physicalReg;
targetSpill = stateTable1_3[k].spill_loc_index;
}
else if(stateNum == 4) {
targetReg = stateTable1_4[k].physicalReg;
targetSpill = stateTable1_4[k].spill_loc_index;
}
/* there exists an ordering problem
for example:
for a VR, move from memory to a physical reg esi
for a temporary regsiter, from esi to ecx
if we handle VR first, content of the temporary reg. will be overwritten
there are 4 cases:
I: a variable is currently in memory and its target is in physical reg
II: a variable is currently in a register and its target is in memory
III: a variable is currently in a different register
IV: a variable is currently in a different memory location (for non-VRs)
for GLUE, since it can only be allocated to %ebp, we don't have case III
For now, case IV is not handled since it didn't show
*/
if(compileTable[k].physicalReg != targetReg &&
isVirtualReg(compileTable[k].physicalType)) {
/* handles VR for case I to III */
if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
/* handles VR for case I:
insert a xfer order from PhysicalReg_Null to targetReg */
insertSrcReg(PhysicalReg_Null, targetReg, targetSpill, k);
#ifdef DEBUG_STATE
ALOGI("insert for VR Null %d %d %d", targetReg, targetSpill, k);
#endif
}
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
/* handles VR for case III
insert a xfer order from srcReg to targetReg */
insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
}
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
/* handles VR for case II
insert a xfer order from srcReg to memory */
insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
}
}
if(compileTable[k].physicalReg != targetReg &&
!isVirtualReg(compileTable[k].physicalType)) {
/* handles non-VR for case I to III */
if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
/* handles non-VR for case I */
if(compileTable[k].spill_loc_index < 0) {
/* this variable is freed, no need to transfer */
#ifdef DEBUG_STATE
ALOGW("in transferToState spill_loc_index is negative for temporary %d", compileTable[k].regNum);
#endif
} else {
/* insert a xfer order from memory to targetReg */
#ifdef DEBUG_STATE
ALOGI("insert Null %d %d %d", targetReg, targetSpill, k);
#endif
insertSrcReg(PhysicalReg_Null, targetReg, targetSpill, k);
}
}
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
/* handles non-VR for case III
insert a xfer order from srcReg to targetReg */
insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
}
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
/* handles non-VR for case II */
if(targetSpill < 0) {
/* this variable is freed, no need to transfer */
#ifdef DEBUG_STATE
ALOGW("in transferToState spill_loc_index is negative for temporary %d", compileTable[k].regNum);
#endif
} else {
/* insert a xfer order from srcReg to memory */
insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
}
}
}
}//for compile entries
int k2;
#ifdef DEBUG_STATE
for(k = 0; k < num_src_regs; k++) {
ALOGI("SRCREG %d: ", srcRegs[k].physicalReg);
for(k2 = 0; k2 < srcRegs[k].num_dests; k2++) {
int index = srcRegs[k].dsts[k2].compileIndex;
ALOGI("[%d %d %d: %d %d %d] ", srcRegs[k].dsts[k2].targetReg,
srcRegs[k].dsts[k2].targetSpill, srcRegs[k].dsts[k2].compileIndex,
compileTable[index].regNum, compileTable[index].physicalType,
compileTable[index].spill_loc_index);
}
ALOGI("");
}
#endif
/* construct an order: xfers from srcReg first, then xfers from memory */
int num_handled = 0;
int num_in_order = 0;
for(k = 0; k < num_src_regs; k++) {
if(srcRegs[k].physicalReg == PhysicalReg_Null) {
handledSrc[k] = true;
num_handled++;
} else {
handledSrc[k] = false;
}
}
while(num_handled < num_src_regs) {
int prev_handled = num_handled;
for(k = 0; k < num_src_regs; k++) {
if(handledSrc[k]) continue;
bool canHandleNow = true;
for(k2 = 0; k2 < srcRegs[k].num_dests; k2++) {
if(dstStillInUse(srcRegs[k].dsts[k2].targetReg)) {
canHandleNow = false;
break;
}
}
if(canHandleNow) {
handledSrc[k] = true;
num_handled++;
handledOrder[num_in_order] = k;
num_in_order++;
}
} //for k
if(num_handled == prev_handled) {
ALOGE("no progress in selecting order");
dvmAbort();
}
} //while
for(k = 0; k < num_src_regs; k++) {
if(srcRegs[k].physicalReg == PhysicalReg_Null) {
handledOrder[num_in_order] = k;
num_in_order++;
}
}
if(num_in_order != num_src_regs) {
ALOGE("num_in_order != num_src_regs");
dvmAbort();
}
#ifdef DEBUG_STATE
ALOGI("ORDER: ");
for(k = 0; k < num_src_regs; k++) {
ALOGI("%d ", handledOrder[k]);
}
ALOGI("");
#endif
}
//! transfer the state of register allocator to a state specified in a state table
//!
void transferToState(int stateNum) {
freeReg(false); //do not spill GL
int k;
#ifdef DEBUG_STATE
ALOGI("STATE: transfer to state %d", stateNum);
#endif
if(stateNum > 4 || stateNum < 1) ALOGE("state table overflow");
constructSrcRegs(stateNum);
int k4, k3;
for(k4 = 0; k4 < num_src_regs; k4++) {
int k2 = handledOrder[k4]; //index to srcRegs
for(k3 = 0; k3 < srcRegs[k2].num_dests; k3++) {
k = srcRegs[k2].dsts[k3].compileIndex;
int targetReg = srcRegs[k2].dsts[k3].targetReg;
int targetSpill = srcRegs[k2].dsts[k3].targetSpill;
if(compileTable[k].physicalReg != targetReg && isVirtualReg(compileTable[k].physicalType)) {
OpndSize oSize = getRegSize(compileTable[k].physicalType);
bool isSS = ((compileTable[k].physicalType & MASK_FOR_TYPE) == LowOpndRegType_ss);
if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
if(isSS)
move_ss_mem_to_reg_noalloc(4*compileTable[k].regNum,
PhysicalReg_FP, true,
MemoryAccess_VR, compileTable[k].regNum,
targetReg, true);
else
move_mem_to_reg_noalloc(oSize, 4*compileTable[k].regNum,
PhysicalReg_FP, true,
MemoryAccess_VR, compileTable[k].regNum,
targetReg, true);
}
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
move_reg_to_reg_noalloc((isSS ? OpndSize_64 : oSize),
compileTable[k].physicalReg, true,
targetReg, true);
}
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
dumpToMem(compileTable[k].regNum, (LowOpndRegType)(compileTable[k].physicalType & MASK_FOR_TYPE),
compileTable[k].physicalReg);
}
} //VR
if(compileTable[k].physicalReg != targetReg && !isVirtualReg(compileTable[k].physicalType)) {
OpndSize oSize = getRegSize(compileTable[k].physicalType);
if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
loadFromSpillRegion(oSize, targetReg,
compileTable[k].spill_loc_index);
}
//both are not null, move from one to the other
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
move_reg_to_reg_noalloc(oSize, compileTable[k].physicalReg, true,
targetReg, true);
}
//current is not null, target is null (move from reg to memory)
if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
saveToSpillRegion(oSize, compileTable[k].physicalReg, targetSpill);
}
} //temporary
}//for
}//for
for(k = 0; k < num_memory_vr; k++) {
bool targetBool = false;
int targetReg = -1;
if(stateNum == 1) {
targetReg = stateTable2_1[k].regNum;
targetBool = stateTable2_1[k].inMemory;
}
else if(stateNum == 2) {
targetReg = stateTable2_2[k].regNum;
targetBool = stateTable2_2[k].inMemory;
}
else if(stateNum == 3) {
targetReg = stateTable2_3[k].regNum;
targetBool = stateTable2_3[k].inMemory;
}
else if(stateNum == 4) {
targetReg = stateTable2_4[k].regNum;
targetBool = stateTable2_4[k].inMemory;
}
if(targetReg != memVRTable[k].regNum)
ALOGE("regNum mismatch in transferToState");
if(targetBool && (!memVRTable[k].inMemory)) {
//dump to memory, check entries in compileTable: vA gp vA xmm vA ss
#ifdef DEBUG_STATE
ALOGW("inMemory mismatch for VR %d in transferToState", targetReg);
#endif
bool doneXfer = false;
int index = searchCompileTable(LowOpndRegType_xmm | LowOpndRegType_virtual, targetReg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
dumpToMem(targetReg, LowOpndRegType_xmm, compileTable[index].physicalReg);
doneXfer = true;
}
if(!doneXfer) { //vA-1, xmm
index = searchCompileTable(LowOpndRegType_xmm | LowOpndRegType_virtual, targetReg-1);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
dumpToMem(targetReg-1, LowOpndRegType_xmm, compileTable[index].physicalReg);
doneXfer = true;
}
}
if(!doneXfer) { //vA gp
index = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_virtual, targetReg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
dumpToMem(targetReg, LowOpndRegType_gp, compileTable[index].physicalReg);
doneXfer = true;
}
}
if(!doneXfer) { //vA, ss
index = searchCompileTable(LowOpndRegType_ss | LowOpndRegType_virtual, targetReg);
if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
dumpToMem(targetReg, LowOpndRegType_ss, compileTable[index].physicalReg);
doneXfer = true;
}
}
if(!doneXfer) ALOGW("can't match inMemory of VR %d in transferToState", targetReg);
}
if((!targetBool) && memVRTable[k].inMemory) {
//do nothing
}
}
#ifdef DEBUG_STATE
ALOGI("END transferToState %d", stateNum);
#endif
goToState(stateNum);
}
/* END code to handle state transfers */