blob: c20151e5330693894bcc65f84935f3559188602b [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This file contains arm-specific codegen factory support.
* It is included by
*
* Codegen-$(TARGET_ARCH_VARIANT).c
*
*/
#include "oat/runtime/oat_support_entrypoints.h"
namespace art {
void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset);
bool genNegLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
RegLocation rlSrc)
{
rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
int zReg = oatAllocTemp(cUnit);
loadConstantNoClobber(cUnit, zReg, 0);
// Check for destructive overlap
if (rlResult.lowReg == rlSrc.highReg) {
int tReg = oatAllocTemp(cUnit);
opRegRegReg(cUnit, kOpSub, rlResult.lowReg, zReg, rlSrc.lowReg);
opRegRegReg(cUnit, kOpSbc, rlResult.highReg, zReg, tReg);
oatFreeTemp(cUnit, tReg);
} else {
opRegRegReg(cUnit, kOpSub, rlResult.lowReg, zReg, rlSrc.lowReg);
opRegRegReg(cUnit, kOpSbc, rlResult.highReg, zReg, rlSrc.highReg);
}
oatFreeTemp(cUnit, zReg);
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
int loadHelper(CompilationUnit* cUnit, int offset)
{
loadWordDisp(cUnit, rSELF, offset, rLR);
return rLR;
}
void genEntrySequence(CompilationUnit* cUnit, BasicBlock* bb)
{
int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
/*
* On entry, r0, r1, r2 & r3 are live. Let the register allocation
* mechanism know so it doesn't try to use any of them when
* expanding the frame or flushing. This leaves the utility
* code with a single temp: r12. This should be enough.
*/
oatLockTemp(cUnit, r0);
oatLockTemp(cUnit, r1);
oatLockTemp(cUnit, r2);
oatLockTemp(cUnit, r3);
/*
* We can safely skip the stack overflow check if we're
* a leaf *and* our frame size < fudge factor.
*/
bool skipOverflowCheck = ((cUnit->attrs & METHOD_IS_LEAF) &&
((size_t)cUnit->frameSize <
Thread::kStackOverflowReservedBytes));
newLIR0(cUnit, kPseudoMethodEntry);
if (!skipOverflowCheck) {
/* Load stack limit */
loadWordDisp(cUnit, rSELF, Thread::StackEndOffset().Int32Value(), r12);
}
/* Spill core callee saves */
newLIR1(cUnit, kThumb2Push, cUnit->coreSpillMask);
/* Need to spill any FP regs? */
if (cUnit->numFPSpills) {
/*
* NOTE: fp spills are a little different from core spills in that
* they are pushed as a contiguous block. When promoting from
* the fp set, we must allocate all singles from s16..highest-promoted
*/
newLIR1(cUnit, kThumb2VPushCS, cUnit->numFPSpills);
}
if (!skipOverflowCheck) {
opRegRegImm(cUnit, kOpSub, rLR, rSP, cUnit->frameSize - (spillCount * 4));
genRegRegCheck(cUnit, kCondCc, rLR, r12, NULL, kThrowStackOverflow);
opRegCopy(cUnit, rSP, rLR); // Establish stack
} else {
opRegImm(cUnit, kOpSub, rSP, cUnit->frameSize - (spillCount * 4));
}
flushIns(cUnit);
if (cUnit->genDebugger) {
// Refresh update debugger callout
loadWordDisp(cUnit, rSELF,
ENTRYPOINT_OFFSET(pUpdateDebuggerFromCode), rSUSPEND);
genDebuggerUpdate(cUnit, DEBUGGER_METHOD_ENTRY);
}
oatFreeTemp(cUnit, r0);
oatFreeTemp(cUnit, r1);
oatFreeTemp(cUnit, r2);
oatFreeTemp(cUnit, r3);
}
void genExitSequence(CompilationUnit* cUnit, BasicBlock* bb)
{
int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
/*
* In the exit path, r0/r1 are live - make sure they aren't
* allocated by the register utilities as temps.
*/
oatLockTemp(cUnit, r0);
oatLockTemp(cUnit, r1);
newLIR0(cUnit, kPseudoMethodExit);
/* If we're compiling for the debugger, generate an update callout */
if (cUnit->genDebugger) {
genDebuggerUpdate(cUnit, DEBUGGER_METHOD_EXIT);
}
opRegImm(cUnit, kOpAdd, rSP, cUnit->frameSize - (spillCount * 4));
/* Need to restore any FP callee saves? */
if (cUnit->numFPSpills) {
newLIR1(cUnit, kThumb2VPopCS, cUnit->numFPSpills);
}
if (cUnit->coreSpillMask & (1 << rLR)) {
/* Unspill rLR to rPC */
cUnit->coreSpillMask &= ~(1 << rLR);
cUnit->coreSpillMask |= (1 << rPC);
}
newLIR1(cUnit, kThumb2Pop, cUnit->coreSpillMask);
if (!(cUnit->coreSpillMask & (1 << rPC))) {
/* We didn't pop to rPC, so must do a bv rLR */
newLIR1(cUnit, kThumbBx, rLR);
}
}
/*
* Nop any unconditional branches that go to the next instruction.
* Note: new redundant branches may be inserted later, and we'll
* use a check in final instruction assembly to nop those out.
*/
void removeRedundantBranches(CompilationUnit* cUnit)
{
LIR* thisLIR;
for (thisLIR = (LIR*) cUnit->firstLIRInsn;
thisLIR != (LIR*) cUnit->lastLIRInsn;
thisLIR = NEXT_LIR(thisLIR)) {
/* Branch to the next instruction */
if ((thisLIR->opcode == kThumbBUncond) ||
(thisLIR->opcode == kThumb2BUncond)) {
LIR* nextLIR = thisLIR;
while (true) {
nextLIR = NEXT_LIR(nextLIR);
/*
* Is the branch target the next instruction?
*/
if (nextLIR == (LIR*) thisLIR->target) {
thisLIR->flags.isNop = true;
break;
}
/*
* Found real useful stuff between the branch and the target.
* Need to explicitly check the lastLIRInsn here because it
* might be the last real instruction.
*/
if (!isPseudoOpcode(nextLIR->opcode) ||
(nextLIR = (LIR*) cUnit->lastLIRInsn))
break;
}
}
}
}
/* Common initialization routine for an architecture family */
bool oatArchInit()
{
int i;
for (i = 0; i < kArmLast; i++) {
if (EncodingMap[i].opcode != i) {
LOG(FATAL) << "Encoding order for " << EncodingMap[i].name
<< " is wrong: expecting " << i << ", seeing "
<< (int)EncodingMap[i].opcode;
}
}
return oatArchVariantInit();
}
} // namespace art