blob: eec1cbd375d5a169c1bef09eaa5c42042ec8042a [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, MIR* mir, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
/*
* [v1 v0] = [a1 a0] + [a3 a2];
* add v0,a2,a0
* adc v1,a3,a1
*/
opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc2.lowReg, rlSrc1.lowReg);
opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc2.highReg, rlSrc1.highReg);
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genSubLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
/*
* [v1 v0] = [a1 a0] - [a3 a2];
* sub v0,a0,a2
* sbb v1,a1,a3
*/
opRegRegReg(cUnit, kOpSub, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
opRegRegReg(cUnit, kOpSbc, rlResult.highReg, rlSrc1.highReg, rlSrc2.highReg);
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
bool genNegLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
RegLocation rlSrc)
{
UNIMPLEMENTED(WARNING) << "genNegLong";
#if 0
rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
/*
* [v1 v0] = -[a1 a0]
* negu v0,a0
* negu v1,a1
* sltu t1,r_zero
* subu v1,v1,t1
*/
opRegReg(cUnit, kOpNeg, rlResult.lowReg, rlSrc.lowReg);
opRegReg(cUnit, kOpNeg, rlResult.highReg, rlSrc.highReg);
int tReg = oatAllocTemp(cUnit);
newLIR3(cUnit, kX86Sltu, tReg, r_ZERO, rlResult.lowReg);
opRegRegReg(cUnit, kOpSub, rlResult.highReg, rlResult.highReg, tReg);
oatFreeTemp(cUnit, tReg);
storeValueWide(cUnit, rlDest, rlResult);
#endif
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;
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
offset -= 4;
storeWordDisp(cUnit, rSP, offset, reg);
}
}
}
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;
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
offset -= 4;
loadWordDisp(cUnit, rSP, offset, reg);
}
}
}
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, BasicBlock* bb)
{
/*
* 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);
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, BasicBlock* bb) {
/*
* 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