blob: 14355cb287fc315604142dbcc8f63c07b035e3de [file] [log] [blame]
/*
* Copyright (C) 2009 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.
*/
#include "Dalvik.h"
#include "libdex/OpCode.h"
#include "dexdump/OpCodeNames.h"
#include "../../CompilerInternals.h"
#include "Armv5teLIR.h"
#include <unistd.h> /* for cacheflush */
/*
* opcode: Armv5teOpCode enum
* skeleton: pre-designated bit-pattern for this opcode
* ds: dest start bit position
* de: dest end bit position
* s1s: src1 start bit position
* s1e: src1 end bit position
* s2s: src2 start bit position
* s2e: src2 end bit position
* operands: number of operands (for sanity check purposes)
* name: mnemonic name
* fmt: for pretty-prining
*/
#define ENCODING_MAP(opcode, skeleton, ds, de, s1s, s1e, s2s, s2e, operands, \
name, fmt) \
{skeleton, {{ds, de}, {s1s, s1e}, {s2s, s2e}}, opcode, operands, name, \
fmt}
/* Instruction dump string format keys: !pf, where "!" is the start
* of the key, "p" is which numeric operand to use and "f" is the
* print format.
*
* [p]ositions:
* 0 -> operands[0] (dest)
* 1 -> operands[1] (src1)
* 2 -> operands[2] (src2)
*
* [f]ormats:
* h -> 4-digit hex
* d -> decimal
* D -> decimal+8 (used to convert 3-bit regnum field to high reg)
* E -> decimal*4
* F -> decimal*2
* c -> branch condition (beq, bne, etc.)
* t -> pc-relative target
* u -> 1st half of bl[x] target
* v -> 2nd half ob bl[x] target
* R -> register list
*
* [!] escape. To insert "!", use "!!"
*/
/* NOTE: must be kept in sync with enum Armv5teOpcode from Armv5teLIR.h */
Armv5teEncodingMap EncodingMap[ARMV5TE_LAST] = {
ENCODING_MAP(ARMV5TE_16BIT_DATA, 0x0000, 15, 0, -1, -1, -1, -1,
1, "data", "0x!0h(!0d)"),
ENCODING_MAP(ARMV5TE_ADC, 0x4140, 2, 0, 5, 3, -1, -1,
2, "adc", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_ADD_RRI3, 0x1c00, 2, 0, 5, 3, 8, 6,
3, "add", "r!0d, r!1d, #!2d"),
ENCODING_MAP(ARMV5TE_ADD_RI8, 0x3000, 10, 8, 7, 0, -1, -1,
2, "add", "r!0d, r!0d, #!1d"),
ENCODING_MAP(ARMV5TE_ADD_RRR, 0x1800, 2, 0, 5, 3, 8, 6,
3, "add", "r!0d, r!1d, r!2d"),
ENCODING_MAP(ARMV5TE_ADD_RR_LH, 0x4440, 2, 0, 5, 3, -1, -1,
2, "add", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_ADD_RR_HL, 0x4480, 2, 0, 5, 3, -1, -1,
2, "add", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_ADD_RR_HH, 0x44c0, 2, 0, 5, 3, -1, -1,
2, "add", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_ADD_PC_REL, 0xa000, 10, 8, 7, 0, -1, -1,
2, "add", "r!0d, pc, #!1E"),
ENCODING_MAP(ARMV5TE_ADD_SP_REL, 0xa800, 10, 8, 7, 0, -1, -1,
2, "add", "r!0d, sp, #!1E"),
ENCODING_MAP(ARMV5TE_ADD_SPI7, 0xb000, 6, 0, -1, -1, -1, -1,
1, "add", "sp, #!0d*4"),
ENCODING_MAP(ARMV5TE_AND_RR, 0x4000, 2, 0, 5, 3, -1, -1,
2, "and", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_ASR, 0x1000, 2, 0, 5, 3, 10, 6,
3, "asr", "r!0d, r!1d, #!2d"),
ENCODING_MAP(ARMV5TE_ASRV, 0x4100, 2, 0, 5, 3, -1, -1,
2, "asr", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_B_COND, 0xd000, 7, 0, 11, 8, -1, -1,
2, "!1c", "!0t"),
ENCODING_MAP(ARMV5TE_B_UNCOND, 0xe000, 10, 0, -1, -1, -1, -1,
0, "b", "!0t"),
ENCODING_MAP(ARMV5TE_BIC, 0x4380, 2, 0, 5, 3, -1, -1,
2, "bic", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_BKPT, 0xbe00, 7, 0, -1, -1, -1, -1,
1, "bkpt", "!0d"),
ENCODING_MAP(ARMV5TE_BLX_1, 0xf000, 10, 0, -1, -1, -1, -1,
2, "blx_1", "!0u"),
ENCODING_MAP(ARMV5TE_BLX_2, 0xe800, 10, 0, -1, -1, -1, -1,
2, "blx_2", "!0v"),
ENCODING_MAP(ARMV5TE_BL_1, 0xf000, 10, 0, -1, -1, -1, -1,
1, "bl_1", "!0u"),
ENCODING_MAP(ARMV5TE_BL_2, 0xf800, 10, 0, -1, -1, -1, -1,
1, "bl_2", "!0v"),
ENCODING_MAP(ARMV5TE_BLX_R, 0x4780, 6, 3, -1, -1, -1, -1,
1, "blx", "r!0d"),
ENCODING_MAP(ARMV5TE_BX, 0x4700, 6, 3, -1, -1, -1, -1,
1, "bx", "r!0d"),
ENCODING_MAP(ARMV5TE_CMN, 0x42c0, 2, 0, 5, 3, -1, -1,
2, "cmn", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_CMP_RI8, 0x2800, 10, 8, 7, 0, -1, -1,
2, "cmp", "r!0d, #!1d"),
ENCODING_MAP(ARMV5TE_CMP_RR, 0x4280, 2, 0, 5, 3, -1, -1,
2, "cmp", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_CMP_LH, 0x4540, 2, 0, 5, 3, -1, -1,
2, "cmp", "r!0d, r!1D"),
ENCODING_MAP(ARMV5TE_CMP_HL, 0x4580, 2, 0, 5, 3, -1, -1,
2, "cmp", "r!0D, r!1d"),
ENCODING_MAP(ARMV5TE_CMP_HH, 0x45c0, 2, 0, 5, 3, -1, -1,
2, "cmp", "r!0D, r!1D"),
ENCODING_MAP(ARMV5TE_EOR, 0x4040, 2, 0, 5, 3, -1, -1,
2, "eor", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_LDMIA, 0xc800, 10, 8, 7, 0, -1, -1,
2, "ldmia", "r!0d!!, <!1R>"),
ENCODING_MAP(ARMV5TE_LDR_RRI5, 0x6800, 2, 0, 5, 3, 10, 6,
3, "ldr", "r!0d, [r!1d, #!2E]"),
ENCODING_MAP(ARMV5TE_LDR_RRR, 0x5800, 2, 0, 5, 3, 8, 6,
3, "ldr", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_LDR_PC_REL, 0x4800, 10, 8, 7, 0, -1, -1,
2, "ldr", "r!0d, [pc, #!1E]"),
ENCODING_MAP(ARMV5TE_LDR_SP_REL, 0x9800, 10, 8, 7, 0, -1, -1,
2, "ldr", "r!0d, [sp, #!1E]"),
ENCODING_MAP(ARMV5TE_LDRB_RRI5, 0x7800, 2, 0, 5, 3, 10, 6,
3, "ldrb", "r!0d, [r!1d, #2d]"),
ENCODING_MAP(ARMV5TE_LDRB_RRR, 0x5c00, 2, 0, 5, 3, 8, 6,
3, "ldrb", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_LDRH_RRI5, 0x8800, 2, 0, 5, 3, 10, 6,
3, "ldrh", "r!0d, [r!1d, #!2F]"),
ENCODING_MAP(ARMV5TE_LDRH_RRR, 0x5a00, 2, 0, 5, 3, 8, 6,
3, "ldrh", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_LDRSB_RRR, 0x5600, 2, 0, 5, 3, 8, 6,
3, "ldrsb", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_LDRSH_RRR, 0x5e00, 2, 0, 5, 3, 8, 6,
3, "ldrsh", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_LSL, 0x0000, 2, 0, 5, 3, 10, 6,
3, "lsl", "r!0d, r!1d, #!2d"),
ENCODING_MAP(ARMV5TE_LSLV, 0x4080, 2, 0, 5, 3, -1, -1,
2, "lsl", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_LSR, 0x0800, 2, 0, 5, 3, 10, 6,
3, "lsr", "r!0d, r!1d, #!2d"),
ENCODING_MAP(ARMV5TE_LSRV, 0x40c0, 2, 0, 5, 3, -1, -1,
2, "lsr", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_MOV_IMM, 0x2000, 10, 8, 7, 0, -1, -1,
2, "mov", "r!0d, #!1d"),
ENCODING_MAP(ARMV5TE_MOV_RR, 0x1c00, 2, 0, 5, 3, -1, -1,
2, "mov", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_MOV_RR_LH, 0x4640, 2, 0, 5, 3, -1, -1,
2, "mov", "r!0D, r!1d"),
ENCODING_MAP(ARMV5TE_MOV_RR_HL, 0x4680, 2, 0, 5, 3, -1, -1,
2, "mov", "r!0d, r!1D"),
ENCODING_MAP(ARMV5TE_MOV_RR_HH, 0x46c0, 2, 0, 5, 3, -1, -1,
2, "mov", "r!0D, r!1D"),
ENCODING_MAP(ARMV5TE_MUL, 0x4340, 2, 0, 5, 3, -1, -1,
2, "mul", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_MVN, 0x43c0, 2, 0, 5, 3, -1, -1,
2, "mvn", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_NEG, 0x4240, 2, 0, 5, 3, -1, -1,
2, "neg", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_ORR, 0x4300, 2, 0, 5, 3, -1, -1,
2, "orr", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_POP, 0xbc00, 8, 0, -1, -1, -1, -1,
1, "pop", "<!0R>"),
ENCODING_MAP(ARMV5TE_PUSH, 0xb400, 8, 0, -1, -1, -1, -1,
1, "push", "<!0R>"),
ENCODING_MAP(ARMV5TE_ROR, 0x41c0, 2, 0, 5, 3, -1, -1,
2, "ror", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_SBC, 0x4180, 2, 0, 5, 3, -1, -1,
2, "sbc", "r!0d, r!1d"),
ENCODING_MAP(ARMV5TE_STMIA, 0xc000, 10, 8, 7, 0, -1, -1,
2, "stmia", "r!0d!!, <!1R>"),
ENCODING_MAP(ARMV5TE_STR_RRI5, 0x6000, 2, 0, 5, 3, 10, 6,
3, "str", "r!0d, [r!1d, #!2E]"),
ENCODING_MAP(ARMV5TE_STR_RRR, 0x5000, 2, 0, 5, 3, 8, 6,
3, "str", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_STR_SP_REL, 0x9000, 10, 8, 7, 0, -1, -1,
2, "str", "r!0d, [sp, #!1E]"),
ENCODING_MAP(ARMV5TE_STRB_RRI5, 0x7000, 2, 0, 5, 3, 10, 6,
3, "strb", "r!0d, [r!1d, #!2d]"),
ENCODING_MAP(ARMV5TE_STRB_RRR, 0x5400, 2, 0, 5, 3, 8, 6,
3, "strb", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_STRH_RRI5, 0x8000, 2, 0, 5, 3, 10, 6,
3, "strh", "r!0d, [r!1d, #!2F]"),
ENCODING_MAP(ARMV5TE_STRH_RRR, 0x5200, 2, 0, 5, 3, 8, 6,
3, "strh", "r!0d, [r!1d, r!2d]"),
ENCODING_MAP(ARMV5TE_SUB_RRI3, 0x1e00, 2, 0, 5, 3, 8, 6,
3, "sub", "r!0d, r!1d, #!2d]"),
ENCODING_MAP(ARMV5TE_SUB_RI8, 0x3800, 10, 8, 7, 0, -1, -1,
2, "sub", "r!0d, #!1d"),
ENCODING_MAP(ARMV5TE_SUB_RRR, 0x1a00, 2, 0, 5, 3, 8, 6,
3, "sub", "r!0d, r!1d, r!2d"),
ENCODING_MAP(ARMV5TE_SUB_SPI7, 0xb080, 6, 0, -1, -1, -1, -1,
1, "sub", "sp, #!0d"),
ENCODING_MAP(ARMV5TE_SWI, 0xdf00, 7, 0, -1, -1, -1, -1,
1, "swi", "!0d"),
ENCODING_MAP(ARMV5TE_TST, 0x4200, 2, 0, 5, 3, -1, -1,
1, "tst", "r!0d, r!1d"),
};
#define PADDING_MOV_R0_R0 0x1C00
/* Write the numbers in the literal pool to the codegen stream */
static void writeDataContent(CompilationUnit *cUnit)
{
int *dataPtr = (int *) (cUnit->codeBuffer + cUnit->dataOffset);
Armv5teLIR *dataLIR = (Armv5teLIR *) cUnit->wordList;
while (dataLIR) {
*dataPtr++ = dataLIR->operands[0];
dataLIR = NEXT_LIR(dataLIR);
}
}
/* Return TRUE if error happens */
static bool assembleInstructions(CompilationUnit *cUnit, intptr_t startAddr)
{
short *bufferAddr = (short *) cUnit->codeBuffer;
Armv5teLIR *lir;
bool retry = false;
for (lir = (Armv5teLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
if (lir->opCode < 0) {
if ((lir->opCode == ARMV5TE_PSEUDO_ALIGN4) &&
(lir->operands[0] == 1) &&
!retry) {
*bufferAddr++ = PADDING_MOV_R0_R0;
}
continue;
}
if (lir->opCode == ARMV5TE_LDR_PC_REL ||
lir->opCode == ARMV5TE_ADD_PC_REL) {
Armv5teLIR *lirTarget = (Armv5teLIR *) lir->generic.target;
intptr_t pc = (lir->generic.offset + 4) & ~3;
intptr_t target = lirTarget->generic.offset;
int delta = target - pc;
if (delta & 0x3) {
LOGE("PC-rel distance is not multiples of 4: %d\n", delta);
dvmAbort();
}
lir->operands[1] = delta >> 2;
} else if (lir->opCode == ARMV5TE_B_COND) {
Armv5teLIR *targetLIR = (Armv5teLIR *) lir->generic.target;
intptr_t pc = lir->generic.offset + 4;
intptr_t target = targetLIR->generic.offset;
int delta = target - pc;
if (delta > 254 || delta < -256) {
/* Pull in the PC reconstruction code inline */
if (targetLIR->opCode == ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL){
/*
* The original code is:
*
* bxx targetLIR
* origNextLir
* :
* :
* targetLIR (a PC reconstruction cell)
* :
* lastLIR (should be a unconditional branch)
*
* The distance from bxx to targetLIR is too far, so we want
* to rearrange the code to be:
*
* bxx targetLIR
* branchoverLIR to origNextLir
* targetLIR (a PC reconstruction cell)
* :
* lastLIR (should be a unconditional branch)
* origNextLir
*
* Although doing so adds a unconditional branchover
* instruction, it can be predicted for free by ARM so
* the penalty should be minimal.
*/
Armv5teLIR *pcrLIR = targetLIR;
Armv5teLIR *lastLIR = pcrLIR;
Armv5teLIR *origNextLIR = NEXT_LIR(lir);
/*
* Find out the last instruction in the PC reconstruction
* cell
*/
while (lastLIR->opCode != ARMV5TE_B_UNCOND) {
lastLIR = NEXT_LIR(lastLIR);
}
/* Yank out the PCR code */
PREV_LIR_LVALUE(NEXT_LIR(lastLIR)) =
(LIR *) PREV_LIR(targetLIR);
NEXT_LIR_LVALUE(PREV_LIR(targetLIR)) =
(LIR *) NEXT_LIR(lastLIR);
/* Create the branch over instruction */
Armv5teLIR *branchoverLIR =
dvmCompilerNew(sizeof(Armv5teLIR), true);
branchoverLIR->opCode = ARMV5TE_B_UNCOND;
branchoverLIR->generic.target = (LIR *) origNextLIR;
/* Reconnect the instructions */
NEXT_LIR_LVALUE(lir) = (LIR *) branchoverLIR;
PREV_LIR_LVALUE(branchoverLIR) = (LIR *) lir;
NEXT_LIR_LVALUE(branchoverLIR) = (LIR *) targetLIR;
PREV_LIR_LVALUE(targetLIR) = (LIR *) branchoverLIR;
NEXT_LIR_LVALUE(lastLIR) = (LIR *) origNextLIR;
PREV_LIR_LVALUE(origNextLIR) = (LIR *) lastLIR;
retry = true;
continue;
} else {
LOGE("Conditional branch distance out of range: %d\n",
delta);
dvmAbort();
}
}
lir->operands[0] = delta >> 1;
} else if (lir->opCode == ARMV5TE_B_UNCOND) {
Armv5teLIR *targetLIR = (Armv5teLIR *) lir->generic.target;
intptr_t pc = lir->generic.offset + 4;
intptr_t target = targetLIR->generic.offset;
int delta = target - pc;
if (delta > 2046 || delta < -2048) {
LOGE("Unconditional branch distance out of range: %d\n", delta);
dvmAbort();
}
lir->operands[0] = delta >> 1;
} else if (lir->opCode == ARMV5TE_BLX_1) {
assert(NEXT_LIR(lir)->opCode == ARMV5TE_BLX_2);
/* curPC is Thumb */
intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3;
intptr_t target = lir->operands[1];
/* Match bit[1] in target with base */
if (curPC & 0x2) {
target |= 0x2;
}
int delta = target - curPC;
assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
lir->operands[0] = (delta >> 12) & 0x7ff;
NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
}
/*
* The code offset will be recalculated, just continue to check if
* there are other places where code will be rescheduled and do not
* write to the output buffer
*/
if (retry) {
continue;
}
Armv5teEncodingMap *encoder = &EncodingMap[lir->opCode];
short bits = encoder->skeleton;
int i;
for (i = 0; i < 3; i++) {
short value;
if (encoder->fieldLoc[i].end != -1) {
value = (lir->operands[i] << encoder->fieldLoc[i].start) &
((1 << (encoder->fieldLoc[i].end + 1)) - 1);
bits |= value;
}
}
*bufferAddr++ = bits;
}
return retry;
}
/*
* Go over each instruction in the list and calculate the offset from the top
* before sending them off to the assembler. If out-of-range branch distance is
* seen rearrange the instructions a bit to correct it.
*/
void dvmCompilerAssembleLIR(CompilationUnit *cUnit)
{
LIR *lir;
Armv5teLIR *armLIR;
int offset;
int i;
retry:
for (armLIR = (Armv5teLIR *) cUnit->firstLIRInsn, offset = 0;
armLIR;
armLIR = NEXT_LIR(armLIR)) {
armLIR->generic.offset = offset;
if (armLIR->opCode >= 0) {
offset += 2;
} else if (armLIR->opCode == ARMV5TE_PSEUDO_ALIGN4) {
if (offset & 0x2) {
offset += 2;
armLIR->operands[0] = 1;
} else {
armLIR->operands[0] = 0;
}
}
/* Pseudo opcodes don't consume space */
}
/* Const values have to be word aligned */
offset = ((offset + 3) >> 2) << 2;
cUnit->dataOffset = offset;
for (lir = cUnit->wordList; lir; lir = lir->next) {
lir->offset = offset;
offset += 4;
}
cUnit->totalSize = offset;
if (gDvmJit.codeCacheByteUsed + offset > CODE_CACHE_SIZE) {
gDvmJit.codeCacheFull = true;
cUnit->baseAddr = NULL;
return;
}
cUnit->codeBuffer = dvmCompilerNew(offset, true);
if (cUnit->codeBuffer == NULL) {
LOGE("Code buffer allocation failure\n");
cUnit->baseAddr = NULL;
return;
}
bool needRetry = assembleInstructions(
cUnit, (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
if (needRetry)
goto retry;
writeDataContent(cUnit);
cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
gDvmJit.codeCacheByteUsed += offset;
/* Install the compilation */
memcpy(cUnit->baseAddr, cUnit->codeBuffer, offset);
gDvmJit.numCompilations++;
/* Flush dcache and invalidate the icache to maintain coherence */
cacheflush((intptr_t) cUnit->baseAddr,
(intptr_t) (cUnit->baseAddr + offset), 0);
}
/*
* Perform translation chain operation.
* For ARM, we'll use a pair of thumb instructions to generate
* an unconditional chaining branch of up to 4MB in distance.
* Use a BL, though we don't really need the link. The format is
* 111HHooooooooooo
* Where HH is 10 for the 1st inst, and 11 for the second and
* the "o" field is each instruction's 11-bit contribution to the
* 22-bit branch offset.
* TUNING: use a single-instruction variant if it reaches.
*/
void* dvmJitChain(void* tgtAddr, u4* branchAddr)
{
int baseAddr = (u4) branchAddr + 4;
int branchOffset = (int) tgtAddr - baseAddr;
u4 thumb1;
u4 thumb2;
u4 newInst;
assert((branchOffset >= -(1<<22)) && (branchOffset <= ((1<<22)-2)));
gDvmJit.translationChains++;
COMPILER_TRACE_CHAINING(
LOGD("Jit Runtime: chaining 0x%x to 0x%x\n",
(int) branchAddr, (int) tgtAddr & -2));
if ((branchOffset < -2048) | (branchOffset > 2046)) {
thumb1 = (0xf000 | ((branchOffset>>12) & 0x7ff));
thumb2 = (0xf800 | ((branchOffset>> 1) & 0x7ff));
} else {
thumb1 = (0xe000 | ((branchOffset>> 1) & 0x7ff));
thumb2 = 0x4300; /* nop -> or r0, r0 */
}
newInst = thumb2<<16 | thumb1;
*branchAddr = newInst;
cacheflush((intptr_t) branchAddr, (intptr_t) branchAddr + 4, 0);
return tgtAddr;
}