blob: 001a93d19987221fcf8acdb186d416936e734d80 [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 x86-specific codegen factory support.
* It is included by
*
* Codegen-$(TARGET_ARCH_VARIANT).c
*
*/
namespace art {
bool genAddLong(CompilationUnit* cUnit, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Prepare for explicit register usage
loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
// Compute (r1:r0) = (r1:r0) + (r2:r3)
opRegReg(cUnit, kOpAdd, r0, r2); // r0 = r0 + r2
opRegReg(cUnit, kOpAdc, r1, r3); // r1 = r1 + r3 + CF
RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
INVALID_SREG, INVALID_SREG};
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genSubLong(CompilationUnit* cUnit, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Prepare for explicit register usage
loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
// Compute (r1:r0) = (r1:r0) + (r2:r3)
opRegReg(cUnit, kOpSub, r0, r2); // r0 = r0 - r2
opRegReg(cUnit, kOpSbc, r1, r3); // r1 = r1 - r3 - CF
RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
INVALID_SREG, INVALID_SREG};
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genAndLong(CompilationUnit* cUnit, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Prepare for explicit register usage
loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
// Compute (r1:r0) = (r1:r0) + (r2:r3)
opRegReg(cUnit, kOpAnd, r0, r2); // r0 = r0 - r2
opRegReg(cUnit, kOpAnd, r1, r3); // r1 = r1 - r3 - CF
RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
INVALID_SREG, INVALID_SREG};
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genOrLong(CompilationUnit* cUnit, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Prepare for explicit register usage
loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
// Compute (r1:r0) = (r1:r0) + (r2:r3)
opRegReg(cUnit, kOpOr, r0, r2); // r0 = r0 - r2
opRegReg(cUnit, kOpOr, r1, r3); // r1 = r1 - r3 - CF
RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
INVALID_SREG, INVALID_SREG};
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genXorLong(CompilationUnit* cUnit, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Prepare for explicit register usage
loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
// Compute (r1:r0) = (r1:r0) + (r2:r3)
opRegReg(cUnit, kOpXor, r0, r2); // r0 = r0 - r2
opRegReg(cUnit, kOpXor, r1, r3); // r1 = r1 - r3 - CF
RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
INVALID_SREG, INVALID_SREG};
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genNegLong(CompilationUnit* cUnit, RegLocation rlDest,
RegLocation rlSrc)
{
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Prepare for explicit register usage
loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
// Compute (r1:r0) = -(r1:r0)
opRegReg(cUnit, kOpNeg, r0, r0); // r0 = -r0
opRegImm(cUnit, kOpAdc, r1, 0); // r1 = r1 + CF
opRegReg(cUnit, kOpNeg, r1, r1); // r1 = -r1
RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
INVALID_SREG, INVALID_SREG};
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset);
void spillCoreRegs(CompilationUnit* cUnit) {
if (cUnit->numCoreSpills == 0) {
return;
}
// Spill mask not including fake return address register
uint32_t mask = cUnit->coreSpillMask & ~(1 << rRET);
int offset = cUnit->frameSize - (4 * cUnit->numCoreSpills);
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
storeWordDisp(cUnit, rSP, offset, reg);
offset += 4;
}
}
}
void unSpillCoreRegs(CompilationUnit* cUnit) {
if (cUnit->numCoreSpills == 0) {
return;
}
// Spill mask not including fake return address register
uint32_t mask = cUnit->coreSpillMask & ~(1 << rRET);
int offset = cUnit->frameSize - (4 * cUnit->numCoreSpills);
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
loadWordDisp(cUnit, rSP, offset, reg);
offset += 4;
}
}
}
void opRegThreadMem(CompilationUnit* cUnit, OpKind op, int rDest, int threadOffset) {
X86OpCode opcode = kX86Bkpt;
switch (op) {
case kOpCmp: opcode = kX86Cmp32RT; break;
default:
LOG(FATAL) << "Bad opcode: " << op;
break;
}
newLIR2(cUnit, opcode, rDest, threadOffset);
}
void genEntrySequence(CompilationUnit* cUnit, RegLocation* argLocs,
RegLocation rlMethod)
{
/*
* On entry, rARG0, rARG1, rARG2 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 no spare temps.
*/
oatLockTemp(cUnit, rARG0);
oatLockTemp(cUnit, rARG1);
oatLockTemp(cUnit, rARG2);
/* Build frame, return address already on stack */
opRegImm(cUnit, kOpSub, rSP, cUnit->frameSize - 4);
/*
* 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);
/* Spill core callee saves */
spillCoreRegs(cUnit);
/* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
DCHECK_EQ(cUnit->numFPSpills, 0);
if (!skipOverflowCheck) {
// cmp rSP, fs:[stack_end_]; jcc throw_launchpad
LIR* tgt = rawLIR(cUnit, 0, kPseudoThrowTarget, kThrowStackOverflow, 0, 0, 0, 0);
opRegThreadMem(cUnit, kOpCmp, rSP, Thread::StackEndOffset().Int32Value());
opCondBranch(cUnit, kCondUlt, tgt);
// Remember branch target - will process later
oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt);
}
flushIns(cUnit, argLocs, rlMethod);
if (cUnit->genDebugger) {
// Refresh update debugger callout
UNIMPLEMENTED(WARNING) << "genDebugger";
#if 0
loadWordDisp(cUnit, rSELF,
OFFSETOF_MEMBER(Thread, pUpdateDebuggerFromCode), rSUSPEND);
genDebuggerUpdate(cUnit, DEBUGGER_METHOD_ENTRY);
#endif
}
oatFreeTemp(cUnit, rARG0);
oatFreeTemp(cUnit, rARG1);
oatFreeTemp(cUnit, rARG2);
}
void genExitSequence(CompilationUnit* cUnit) {
/*
* In the exit path, rRET0/rRET1 are live - make sure they aren't
* allocated by the register utilities as temps.
*/
oatLockTemp(cUnit, rRET0);
oatLockTemp(cUnit, rRET1);
newLIR0(cUnit, kPseudoMethodExit);
/* If we're compiling for the debugger, generate an update callout */
if (cUnit->genDebugger) {
genDebuggerUpdate(cUnit, DEBUGGER_METHOD_EXIT);
}
unSpillCoreRegs(cUnit);
/* Remove frame except for return address */
opRegImm(cUnit, kOpAdd, rSP, cUnit->frameSize - 4);
newLIR0(cUnit, kX86Ret);
}
/*
* 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 == kX86Jmp8 || thisLIR->opcode == kX86Jmp32) {
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 < kX86Last; 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