From ba4fc8bfc1bccae048403bd1cea3b869dca61dd7 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Mon, 1 Jun 2009 13:00:29 -0700 Subject: Initial port of the Dalvik JIT enging to the internal repository. Fixed files with trailing spaces. Addressed review comments from Dan. Addressed review comments from fadden. Addressed review comments from Dan x 2. Addressed review comments from Dan x 3. --- vm/compiler/codegen/armv5te/Assemble.c | 499 +++++++++++++++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 vm/compiler/codegen/armv5te/Assemble.c (limited to 'vm/compiler/codegen/armv5te/Assemble.c') diff --git a/vm/compiler/codegen/armv5te/Assemble.c b/vm/compiler/codegen/armv5te/Assemble.c new file mode 100644 index 000000000..14355cb28 --- /dev/null +++ b/vm/compiler/codegen/armv5te/Assemble.c @@ -0,0 +1,499 @@ +/* + * 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 /* 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!!, "), + 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", ""), + ENCODING_MAP(ARMV5TE_PUSH, 0xb400, 8, 0, -1, -1, -1, -1, + 1, "push", ""), + 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!!, "), + 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; +} -- cgit v1.2.3