diff options
author | Ben Cheng <bccheng@android.com> | 2009-06-01 13:00:29 -0700 |
---|---|---|
committer | Ben Cheng <bccheng@android.com> | 2009-06-04 12:46:11 -0700 |
commit | ba4fc8bfc1bccae048403bd1cea3b869dca61dd7 (patch) | |
tree | 3c05c1988501fd8a22ae73e02742ab291b2ff333 /vm/compiler/codegen/armv5te | |
parent | b38914129f30aa696726717db6484735ad7fe234 (diff) | |
download | android_dalvik-ba4fc8bfc1bccae048403bd1cea3b869dca61dd7.tar.gz android_dalvik-ba4fc8bfc1bccae048403bd1cea3b869dca61dd7.tar.bz2 android_dalvik-ba4fc8bfc1bccae048403bd1cea3b869dca61dd7.zip |
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.
Diffstat (limited to 'vm/compiler/codegen/armv5te')
-rw-r--r-- | vm/compiler/codegen/armv5te/ArchUtility.c | 223 | ||||
-rw-r--r-- | vm/compiler/codegen/armv5te/Armv5teLIR.h | 181 | ||||
-rw-r--r-- | vm/compiler/codegen/armv5te/Assemble.c | 499 | ||||
-rw-r--r-- | vm/compiler/codegen/armv5te/Codegen.c | 2892 |
4 files changed, 3795 insertions, 0 deletions
diff --git a/vm/compiler/codegen/armv5te/ArchUtility.c b/vm/compiler/codegen/armv5te/ArchUtility.c new file mode 100644 index 000000000..58b181b7e --- /dev/null +++ b/vm/compiler/codegen/armv5te/ArchUtility.c @@ -0,0 +1,223 @@ +/* + * 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 "../../CompilerInternals.h" +#include "dexdump/OpCodeNames.h" +#include "Armv5teLIR.h" + +/* Decode and print a ARM register name */ +static char * decodeRegList(int vector, char *buf) +{ + int i; + bool printed = false; + buf[0] = 0; + for (i = 0; i < 8; i++, vector >>= 1) { + if (vector & 0x1) { + if (printed) { + sprintf(buf + strlen(buf), ", r%d", i); + } else { + printed = true; + sprintf(buf, "r%d", i); + } + } + } + return buf; +} + +/* + * Interpret a format string and build a string no longer than size + * See format key in Assemble.c. + */ +static void buildInsnString(char *fmt, Armv5teLIR *lir, char* buf, + unsigned char *baseAddr, int size) +{ + int i; + char *bufEnd = &buf[size-1]; + char *fmtEnd = &fmt[strlen(fmt)]; + char tbuf[256]; + char nc; + while (fmt < fmtEnd) { + int operand; + if (*fmt == '!') { + fmt++; + assert(fmt < fmtEnd); + nc = *fmt++; + if (nc=='!') { + strcpy(tbuf, "!"); + } else { + assert(fmt < fmtEnd); + assert((unsigned)(nc-'0') < 3); + operand = lir->operands[nc-'0']; + switch(*fmt++) { + case 'h': + sprintf(tbuf,"%04x", operand); + break; + case 'd': + sprintf(tbuf,"%d", operand); + break; + case 'D': + sprintf(tbuf,"%d", operand+8); + break; + case 'E': + sprintf(tbuf,"%d", operand*4); + break; + case 'F': + sprintf(tbuf,"%d", operand*2); + break; + case 'c': + switch (operand) { + case ARM_COND_EQ: + strcpy(tbuf, "beq"); + break; + case ARM_COND_NE: + strcpy(tbuf, "bne"); + break; + case ARM_COND_LT: + strcpy(tbuf, "blt"); + break; + case ARM_COND_GE: + strcpy(tbuf, "bge"); + break; + case ARM_COND_GT: + strcpy(tbuf, "bgt"); + break; + case ARM_COND_LE: + strcpy(tbuf, "ble"); + break; + case ARM_COND_CS: + strcpy(tbuf, "bcs"); + break; + default: + strcpy(tbuf, ""); + break; + } + break; + case 't': + sprintf(tbuf,"0x%08x", + (int) baseAddr + lir->generic.offset + 4 + + (operand << 1)); + break; + case 'u': { + int offset_1 = lir->operands[0]; + int offset_2 = NEXT_LIR(lir)->operands[0]; + intptr_t target = + ((((intptr_t) baseAddr + lir->generic.offset + 4) & + ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) & + 0xfffffffc; + sprintf(tbuf, "%p", (void *) target); + break; + } + + /* Nothing to print for BLX_2 */ + case 'v': + strcpy(tbuf, "see above"); + break; + case 'R': + decodeRegList(operand, tbuf); + break; + default: + strcpy(tbuf,"DecodeError"); + break; + } + if (buf+strlen(tbuf) <= bufEnd) { + strcpy(buf, tbuf); + buf += strlen(tbuf); + } else { + break; + } + } + } else { + *buf++ = *fmt++; + } + if (buf == bufEnd) + break; + } + *buf = 0; +} + +/* Pretty-print a LIR instruction */ +static void dumpLIRInsn(LIR *arg, unsigned char *baseAddr) +{ + Armv5teLIR *lir = (Armv5teLIR *) arg; + char buf[256]; + char opName[256]; + int offset = lir->generic.offset; + int dest = lir->operands[0]; + u2 *cPtr = (u2*)baseAddr; + /* Handle pseudo-ops individually, and all regular insns as a group */ + switch(lir->opCode) { + case ARMV5TE_PSEUDO_TARGET_LABEL: + break; + case ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC: + LOGD("-------- chaining cell (generic): 0x%04x\n", dest); + break; + case ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE: + LOGD("-------- chaining cell (post-invoke): 0x%04x\n", dest); + break; + case ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE: + LOGD("-------- chaining cell (invoke): %s/%p\n", + ((Method *)dest)->name, + ((Method *)dest)->insns); + break; + case ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY: + LOGD("-------- dalvik offset: 0x%04x @ %s\n", dest, + getOpcodeName(lir->operands[1])); + break; + case ARMV5TE_PSEUDO_ALIGN4: + LOGD("%p (%04x): .align4\n", baseAddr + offset, offset); + break; + case ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL: + LOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x\n", dest, + lir->operands[1]); + break; + case ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL: + /* Do nothing */ + break; + case ARMV5TE_PSEUDO_EH_BLOCK_LABEL: + LOGD("Exception_Handling:\n"); + break; + case ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL: + LOGD("L%#06x:\n", dest); + break; + default: + buildInsnString(EncodingMap[lir->opCode].name, lir, opName, + baseAddr, 256); + buildInsnString(EncodingMap[lir->opCode].fmt, lir, buf, baseAddr, + 256); + LOGD("%p (%04x): %-8s%s\n", baseAddr + offset, offset, opName, buf); + break; + } +} + +/* Dump instructions and constant pool contents */ +void dvmCompilerCodegenDump(CompilationUnit *cUnit) +{ + LOGD("Dumping LIR insns\n"); + LIR *lirInsn; + Armv5teLIR *armLIR; + + LOGD("installed code is at %p\n", cUnit->baseAddr); + LOGD("total size is %d bytes\n", cUnit->totalSize); + for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) { + dumpLIRInsn(lirInsn, cUnit->baseAddr); + } + for (lirInsn = cUnit->wordList; lirInsn; lirInsn = lirInsn->next) { + armLIR = (Armv5teLIR *) lirInsn; + LOGD("%p (%04x): .word (0x%x)\n", + cUnit->baseAddr + armLIR->generic.offset, armLIR->generic.offset, + armLIR->operands[0]); + } +} diff --git a/vm/compiler/codegen/armv5te/Armv5teLIR.h b/vm/compiler/codegen/armv5te/Armv5teLIR.h new file mode 100644 index 000000000..208e6c0eb --- /dev/null +++ b/vm/compiler/codegen/armv5te/Armv5teLIR.h @@ -0,0 +1,181 @@ +/* + * 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 "compiler/CompilerInternals.h" + +#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARMV5TE_H +#define _DALVIK_VM_COMPILER_CODEGEN_ARMV5TE_H + +/* + * r0, r1, r2, r3, and r7 are always scratch + * r4PC is scratch if used solely in the compiled land. Otherwise it holds the + * Dalvik PC. + * rFP holds the current frame pointer + * rGLUE holds &InterpState + */ +typedef enum NativeRegisterPool { + r0 = 0, + r1 = 1, + r2 = 2, + r3 = 3, + r4PC = 4, + rFP = 5, + rGLUE = 6, + r7 = 7, +} NativeRegisterPool; + +/* Thumb condition encodings */ +typedef enum Armv5teConditionCode { + ARM_COND_EQ = 0x0, /* 0000 */ + ARM_COND_NE = 0x1, /* 0001 */ + ARM_COND_LT = 0xb, /* 1011 */ + ARM_COND_GE = 0xa, /* 1010 */ + ARM_COND_GT = 0xc, /* 1100 */ + ARM_COND_LE = 0xd, /* 1101 */ + ARM_COND_CS = 0x2, /* 0010 */ + ARM_COND_MI = 0x4, /* 0100 */ +} Armv5teConditionCode; + +#define isPseudoOpCode(opCode) ((int)(opCode) < 0) + +/* + * The following enum defines the list of supported Thumb instructions by the + * assembler. Their corresponding snippet positions will be defined in + * Assemble.c. + */ +typedef enum Armv5teOpCode { + ARMV5TE_PSEUDO_TARGET_LABEL = -10, + ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE = -9, + ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE = -8, + ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC = -7, + ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY = -6, + ARMV5TE_PSEUDO_ALIGN4 = -5, + ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL = -4, + ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL = -3, + ARMV5TE_PSEUDO_EH_BLOCK_LABEL = -2, + ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL = -1, + /************************************************************************/ + ARMV5TE_16BIT_DATA, /* DATA [0] rd[15..0] */ + ARMV5TE_ADC, /* adc [0100000101] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_RRI3, /* add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/ + ARMV5TE_ADD_RI8, /* add(2) [00110] rd[10..8] imm_8[7..0] */ + ARMV5TE_ADD_RRR, /* add(3) [0001100] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_ADD_RR_LH, /* add(4) [01000100] H12[01] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_RR_HL, /* add(4) [01001000] H12[10] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_RR_HH, /* add(4) [01001100] H12[11] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_PC_REL, /* add(5) [10100] rd[10..8] imm_8[7..0] */ + ARMV5TE_ADD_SP_REL, /* add(6) [10101] rd[10..8] imm_8[7..0] */ + ARMV5TE_ADD_SPI7, /* add(7) [101100000] imm_7[6..0] */ + ARMV5TE_AND_RR, /* and [0100000000] rm[5..3] rd[2..0] */ + ARMV5TE_ASR, /* asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0] */ + ARMV5TE_ASRV, /* asr(2) [0100000100] rs[5..3] rd[2..0] */ + ARMV5TE_B_COND, /* b(1) [1101] cond[11..8] offset_8[7..0] */ + ARMV5TE_B_UNCOND, /* b(2) [11100] offset_11[10..0] */ + ARMV5TE_BIC, /* bic [0100001110] rm[5..3] rd[2..0] */ + ARMV5TE_BKPT, /* bkpt [10111110] imm_8[7..0] */ + ARMV5TE_BLX_1, /* blx(1) [111] H[10] offset_11[10..0] */ + ARMV5TE_BLX_2, /* blx(1) [111] H[01] offset_11[10..0] */ + ARMV5TE_BL_1, /* blx(1) [111] H[10] offset_11[10..0] */ + ARMV5TE_BL_2, /* blx(1) [111] H[11] offset_11[10..0] */ + ARMV5TE_BLX_R, /* blx(2) [010001111] H2[6..6] rm[5..3] SBZ[000] */ + ARMV5TE_BX, /* bx [010001110] H2[6..6] rm[5..3] SBZ[000] */ + ARMV5TE_CMN, /* cmn [0100001011] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_RI8, /* cmp(1) [00101] rn[10..8] imm_8[7..0] */ + ARMV5TE_CMP_RR, /* cmp(2) [0100001010] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_LH, /* cmp(3) [01000101] H12[01] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_HL, /* cmp(3) [01000110] H12[10] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_HH, /* cmp(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + ARMV5TE_EOR, /* eor [0100000001] rm[5..3] rd[2..0] */ + ARMV5TE_LDMIA, /* ldmia [11001] rn[10..8] reglist [7..0] */ + ARMV5TE_LDR_RRI5, /* ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDR_RRR, /* ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDR_PC_REL, /* ldr(3) [01001] rd[10..8] imm_8[7..0] */ + ARMV5TE_LDR_SP_REL, /* ldr(4) [10011] rd[10..8] imm_8[7..0] */ + ARMV5TE_LDRB_RRI5, /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRB_RRR, /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRH_RRI5, /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRH_RRR, /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRSB_RRR, /* ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRSH_RRR, /* ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LSL, /* lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0] */ + ARMV5TE_LSLV, /* lsl(2) [0100000010] rs[5..3] rd[2..0] */ + ARMV5TE_LSR, /* lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0] */ + ARMV5TE_LSRV, /* lsr(2) [0100000011] rs[5..3] rd[2..0] */ + ARMV5TE_MOV_IMM, /* mov(1) [00100] rd[10..8] imm_8[7..0] */ + ARMV5TE_MOV_RR, /* mov(2) [0001110000] rn[5..3] rd[2..0] */ + ARMV5TE_MOV_RR_HL, /* mov(3) [01000110] H12[10] rm[5..3] rd[2..0] */ + ARMV5TE_MOV_RR_LH, /* mov(3) [01000101] H12[01] rm[5..3] rd[2..0] */ + ARMV5TE_MOV_RR_HH, /* mov(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + ARMV5TE_MUL, /* mul [0100001101] rm[5..3] rd[2..0] */ + ARMV5TE_MVN, /* mvn [0100001111] rm[5..3] rd[2..0] */ + ARMV5TE_NEG, /* neg [0100001001] rm[5..3] rd[2..0] */ + ARMV5TE_ORR, /* orr [0100001100] rm[5..3] rd[2..0] */ + ARMV5TE_POP, /* pop [1011110] r[8..8] rl[7..0] */ + ARMV5TE_PUSH, /* push [1011010] r[8..8] rl[7..0] */ + ARMV5TE_ROR, /* ror [0100000111] rs[5..3] rd[2..0] */ + ARMV5TE_SBC, /* sbc [0100000110] rm[5..3] rd[2..0] */ + ARMV5TE_STMIA, /* stmia [11000] rn[10..8] reglist [7.. 0] */ + ARMV5TE_STR_RRI5, /* str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_STR_RRR, /* str(2) [0101000] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_STR_SP_REL, /* str(3) [10010] rd[10..8] imm_8[7..0] */ + ARMV5TE_STRB_RRI5, /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_STRB_RRR, /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_STRH_RRI5, /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_STRH_RRR, /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_SUB_RRI3, /* sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ + ARMV5TE_SUB_RI8, /* sub(2) [00111] rd[10..8] imm_8[7..0] */ + ARMV5TE_SUB_RRR, /* sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_SUB_SPI7, /* sub(4) [101100001] imm_7[6..0] */ + ARMV5TE_SWI, /* swi [11011111] imm_8[7..0] */ + ARMV5TE_TST, /* tst [0100001000] rm[5..3] rn[2..0] */ + ARMV5TE_LAST, +} Armv5teOpCode; + +/* Struct used to define the snippet posotions for each Thumb opcode */ +typedef struct Armv5teEncodingMap { + short skeleton; + struct { + int end; + int start; + } fieldLoc[3]; + Armv5teOpCode opCode; + int operands; + char *name; + char* fmt; +} Armv5teEncodingMap; + +extern Armv5teEncodingMap EncodingMap[ARMV5TE_LAST]; + +/* + * Each instance of this struct holds a pseudo or real LIR instruction: + * - pesudo ones (eg labels and marks) and will be discarded by the assembler. + * - real ones will e assembled into Thumb instructions. + */ +typedef struct Armv5teLIR { + LIR generic; + Armv5teOpCode opCode; + int operands[3]; /* dest, src1, src2 */ +} Armv5teLIR; + +/* Utility macros to traverse the LIR/Armv5teLIR list */ +#define NEXT_LIR(lir) ((Armv5teLIR *) lir->generic.next) +#define PREV_LIR(lir) ((Armv5teLIR *) lir->generic.prev) + +#define NEXT_LIR_LVALUE(lir) (lir)->generic.next +#define PREV_LIR_LVALUE(lir) (lir)->generic.prev + +#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARMV5TE_H */ 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 <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; +} diff --git a/vm/compiler/codegen/armv5te/Codegen.c b/vm/compiler/codegen/armv5te/Codegen.c new file mode 100644 index 000000000..178e5368b --- /dev/null +++ b/vm/compiler/codegen/armv5te/Codegen.c @@ -0,0 +1,2892 @@ +/* + * 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 "interp/InterpDefs.h" +#include "libdex/OpCode.h" +#include "dexdump/OpCodeNames.h" +#include "vm/compiler/CompilerInternals.h" +#include "Armv5teLIR.h" +#include "vm/mterp/common/FindInterface.h" + +/* Create the TemplateOpcode enum */ +#define JIT_TEMPLATE(X) TEMPLATE_##X, +typedef enum { +#include "../../template/armv5te/TemplateOpList.h" +/* + * For example, + * TEMPLATE_CMP_LONG, + * TEMPLATE_RETURN, + * ... + */ + TEMPLATE_LAST_MARK, +} TemplateOpCode; +#undef JIT_TEMPLATE + +/* Array holding the entry offset of each template relative to the first one */ +static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK]; + +/* Track exercised opcodes */ +static int opcodeCoverage[256]; + +/*****************************************************************************/ + +/* + * The following are building blocks to construct low-level IRs with 0 - 3 + * operands. + */ +static Armv5teLIR *newLIR0(CompilationUnit *cUnit, Armv5teOpCode opCode) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 0); + insn->opCode = opCode; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR1(CompilationUnit *cUnit, Armv5teOpCode opCode, + int dest) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 1); + insn->opCode = opCode; + insn->operands[0] = dest; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR2(CompilationUnit *cUnit, Armv5teOpCode opCode, + int dest, int src1) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 2); + insn->opCode = opCode; + insn->operands[0] = dest; + insn->operands[1] = src1; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR3(CompilationUnit *cUnit, Armv5teOpCode opCode, + int dest, int src1, int src2) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 3); + insn->opCode = opCode; + insn->operands[0] = dest; + insn->operands[1] = src1; + insn->operands[2] = src2; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR23(CompilationUnit *cUnit, Armv5teOpCode opCode, + int srcdest, int src2) +{ + assert(!isPseudoOpCode(opCode)); + if (EncodingMap[opCode].operands==2) + return newLIR2(cUnit, opCode, srcdest, src2); + else + return newLIR3(cUnit, opCode, srcdest, srcdest, src2); +} + +/*****************************************************************************/ + +/* + * The following are building blocks to insert constants into the pool or + * instruction streams. + */ + +/* Add a 32-bit constant either in the constant pool or mixed with code */ +static Armv5teLIR *addWordData(CompilationUnit *cUnit, int value, bool inPlace) +{ + /* Add the constant to the literal pool */ + if (!inPlace) { + Armv5teLIR *newValue = dvmCompilerNew(sizeof(Armv5teLIR), true); + newValue->operands[0] = value; + newValue->generic.next = cUnit->wordList; + cUnit->wordList = (LIR *) newValue; + return newValue; + } else { + /* Add the constant in the middle of code stream */ + newLIR1(cUnit, ARMV5TE_16BIT_DATA, (value & 0xffff)); + newLIR1(cUnit, ARMV5TE_16BIT_DATA, (value >> 16)); + } + return NULL; +} + +/* + * Search the existing constants in the literal pool for an exact or close match + * within specified delta (greater or equal to 0). + */ +static Armv5teLIR *scanLiteralPool(CompilationUnit *cUnit, int value, + unsigned int delta) +{ + LIR *dataTarget = cUnit->wordList; + while (dataTarget) { + if (((unsigned) (value - ((Armv5teLIR *) dataTarget)->operands[0])) <= + delta) + return (Armv5teLIR *) dataTarget; + dataTarget = dataTarget->next; + } + return NULL; +} + +/* + * Load a immediate using a shortcut if possible; otherwise + * grab from the per-translation literal pool + */ +void loadConstant(CompilationUnit *cUnit, int rDest, int value) +{ + /* See if the value can be constructed cheaply */ + if ((value >= 0) && (value <= 255)) { + newLIR2(cUnit, ARMV5TE_MOV_IMM, rDest, value); + return; + } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) { + newLIR2(cUnit, ARMV5TE_MOV_IMM, rDest, ~value); + newLIR2(cUnit, ARMV5TE_MVN, rDest, rDest); + return; + } + /* No shortcut - go ahead and use literal pool */ + Armv5teLIR *dataTarget = scanLiteralPool(cUnit, value, 255); + if (dataTarget == NULL) { + dataTarget = addWordData(cUnit, value, false); + } + Armv5teLIR *loadPcRel = dvmCompilerNew(sizeof(Armv5teLIR), true); + loadPcRel->opCode = ARMV5TE_LDR_PC_REL; + loadPcRel->generic.target = (LIR *) dataTarget; + loadPcRel->operands[0] = rDest; + dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel); + + /* + * To save space in the constant pool, we use the ADD_RRI8 instruction to + * add up to 255 to an existing constant value. + */ + if (dataTarget->operands[0] != value) { + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, value - dataTarget->operands[0]); + } +} + +/* Export the Dalvik PC assicated with an instruction to the StackSave area */ +static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr) +{ + int offset = offsetof(StackSaveArea, xtra.currentPc); + loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset)); + newLIR2(cUnit, ARMV5TE_MOV_RR, rAddr, rFP); + newLIR2(cUnit, ARMV5TE_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset); + newLIR3(cUnit, ARMV5TE_STR_RRI5, rDPC, rAddr, 0); +} + +/* Generate conditional branch instructions */ +static void genConditionalBranch(CompilationUnit *cUnit, + Armv5teConditionCode cond, + Armv5teLIR *target) +{ + Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond); + branch->generic.target = (LIR *) target; +} + +/* Generate unconditional branch instructions */ +static void genUnconditionalBranch(CompilationUnit *cUnit, Armv5teLIR *target) +{ + Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND); + branch->generic.target = (LIR *) target; +} + +#define USE_IN_CACHE_HANDLER 1 + +/* + * Jump to the out-of-line handler in ARM mode to finish executing the + * remaining of more complex instructions. + */ +static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode) +{ +#if USE_IN_CACHE_HANDLER + /* + * NOTE - In practice BLX only needs one operand, but since the assembler + * may abort itself and retry due to other out-of-range conditions we + * cannot really use operand[0] to store the absolute target address since + * it may get clobbered by the final relative offset. Therefore, + * we fake BLX_1 is a two operand instruction and the absolute target + * address is stored in operand[1]. + */ + newLIR2(cUnit, ARMV5TE_BLX_1, + (int) gDvmJit.codeCache + templateEntryOffsets[opCode], + (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); + newLIR2(cUnit, ARMV5TE_BLX_2, + (int) gDvmJit.codeCache + templateEntryOffsets[opCode], + (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); +#else + /* + * In case we want to access the statically compiled handlers for + * debugging purposes, define USE_IN_CACHE_HANDLER to 0 + */ + void *templatePtr; + +#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X(); +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + switch (opCode) { +#define JIT_TEMPLATE(X) \ + case TEMPLATE_##X: { templatePtr = dvmCompiler_TEMPLATE_##X; break; } +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + default: templatePtr = NULL; + } + loadConstant(cUnit, r7, (int) templatePtr); + newLIR1(cUnit, ARMV5TE_BLX_R, r7); +#endif +} + +/* Perform the actual operation for OP_RETURN_* */ +static void genReturnCommon(CompilationUnit *cUnit, MIR *mir) +{ + genDispatchToHandler(cUnit, TEMPLATE_RETURN); +#if defined(INVOKE_STATS) + gDvmJit.jitReturn++; +#endif + int dPC = (int) (cUnit->method->insns + mir->offset); + Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND); + /* Set up the place holder to reconstruct this Dalvik PC */ + Armv5teLIR *pcrLabel = dvmCompilerNew(sizeof(Armv5teLIR), true); + pcrLabel->opCode = ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL; + pcrLabel->operands[0] = dPC; + pcrLabel->operands[1] = mir->offset; + /* Insert the place holder to the growable list */ + dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); + /* Branch to the PC reconstruction code */ + branch->generic.target = (LIR *) pcrLabel; +} + +/* + * Load a pair of values of rFP[src..src+1] and store them into rDestLo and + * rDestHi + */ +static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo, + int rDestHi) +{ + /* Use reg + imm5*4 to load the values if possible */ + if (vSrc <= 30) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDestLo, rFP, vSrc); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDestHi, rFP, vSrc+1); + } else { + if (vSrc <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDestLo, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDestHi, (vSrc-1)*4); + } else { + /* Offset too far from rFP */ + loadConstant(cUnit, rDestLo, vSrc*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rDestLo, rFP, rDestLo); + } + assert(rDestLo != rDestHi); + newLIR2(cUnit, ARMV5TE_LDMIA, rDestLo, (1<<rDestLo) | (1<<(rDestHi))); + } +} + +/* + * Store a pair of values of rSrc and rSrc+1 and store them into vDest and + * vDest+1 + */ +static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi, + int vDest, int rScratch) +{ + /* Use reg + imm5*4 to store the values if possible */ + if (vDest <= 30) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrcLo, rFP, vDest); + newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrcHi, rFP, vDest+1); + } else { + if (vDest <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rScratch, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rScratch, (vDest-1)*4); + } else { + /* Offset too far from rFP */ + loadConstant(cUnit, rScratch, vDest*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rScratch, rFP, rScratch); + } + assert(rSrcLo != rSrcHi); + newLIR2(cUnit, ARMV5TE_STMIA, rScratch, (1<<rSrcLo) | (1 << (rSrcHi))); + } +} + +/* Load the address of a Dalvik register on the frame */ +static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest) +{ + /* RRI3 can add up to 7 */ + if (vSrc <= 1) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, vSrc*4); + } else if (vSrc <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, (vSrc-1)*4); + } else { + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rDest, rFP, rDest); + } +} + + +/* Load a single value from rFP[src] and store them into rDest */ +static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest) +{ + /* Use reg + imm5*4 to load the value if possible */ + if (vSrc <= 31) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDest, rFP, vSrc); + } else { + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, ARMV5TE_LDR_RRR, rDest, rFP, rDest); + } +} + +/* Store a value from rSrc to vDest */ +static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest, + int rScratch) +{ + /* Use reg + imm5*4 to store the value if possible */ + if (vDest <= 31) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrc, rFP, vDest); + } else { + loadConstant(cUnit, rScratch, vDest*4); + newLIR3(cUnit, ARMV5TE_STR_RRR, rSrc, rFP, rScratch); + } +} + +/* Calculate the address of rFP+vSrc*4 */ +static void calculateValueAddress(CompilationUnit *cUnit, int vSrc, int rDest) +{ + /* Use add rd, rs, imm_3 */ + if (vSrc <= 1) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, vSrc*4); + } else if (vSrc <= 64) { + /* Use add rd, imm_8 */ + /* Sneak in 4 above rFP to cover one more register offset (ie v64) */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, (vSrc-1)*4); + } else { + /* Load offset from the constant pool */ + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rDest, rFP, rDest); + } +} + +/* + * Perform a binary operation on 64-bit operands and leave the results in the + * r0/r1 pair. + */ +static void genBinaryOpWide(CompilationUnit *cUnit, int vDest, + Armv5teOpCode preinst, Armv5teOpCode inst) +{ + newLIR23(cUnit, preinst, r0, r2); + newLIR23(cUnit, inst, r1, r3); + storeValuePair(cUnit, r0, r1, vDest, r2); +} + +/* Perform a binary operation on 32-bit operands and leave the results in r0. */ +static void genBinaryOp(CompilationUnit *cUnit, int vDest, Armv5teOpCode inst) +{ + newLIR23(cUnit, inst, r0, r1); + storeValue(cUnit, r0, vDest, r1); +} + +/* Create the PC reconstruction slot if not already done */ +static inline Armv5teLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset, + Armv5teLIR *branch, + Armv5teLIR *pcrLabel) +{ + /* Set up the place holder to reconstruct this Dalvik PC */ + if (pcrLabel == NULL) { + int dPC = (int) (cUnit->method->insns + dOffset); + pcrLabel = dvmCompilerNew(sizeof(Armv5teLIR), true); + pcrLabel->opCode = ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL; + pcrLabel->operands[0] = dPC; + pcrLabel->operands[1] = dOffset; + /* Insert the place holder to the growable list */ + dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); + } + /* Branch to the PC reconstruction code */ + branch->generic.target = (LIR *) pcrLabel; + return pcrLabel; +} + +/* + * Perform a "reg cmp imm" operation and jump to the PCR region if condition + * satisfies. + */ +static inline Armv5teLIR *genRegImmCheck(CompilationUnit *cUnit, + Armv5teConditionCode cond, int reg, + int checkValue, int dOffset, + Armv5teLIR *pcrLabel) +{ + newLIR2(cUnit, ARMV5TE_CMP_RI8, reg, checkValue); + Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* + * Perform a "reg cmp reg" operation and jump to the PCR region if condition + * satisfies. + */ +static inline Armv5teLIR *inertRegRegCheck(CompilationUnit *cUnit, + Armv5teConditionCode cond, + int reg1, int reg2, int dOffset, + Armv5teLIR *pcrLabel) +{ + newLIR2(cUnit, ARMV5TE_CMP_RR, reg1, reg2); + Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* Perform null-check on a register */ +static Armv5teLIR *genNullCheck(CompilationUnit *cUnit, int reg, int dOffset, + Armv5teLIR *pcrLabel) +{ + return genRegImmCheck(cUnit, ARM_COND_EQ, reg, 0, dOffset, pcrLabel); +} + +/* Perform bound check on two registers */ +static Armv5teLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex, + int rBound, int dOffset, Armv5teLIR *pcrLabel) +{ + return inertRegRegCheck(cUnit, ARM_COND_CS, rIndex, rBound, dOffset, + pcrLabel); +} + +/* Generate a unconditional branch to go to the interpreter */ +static inline Armv5teLIR *genTrap(CompilationUnit *cUnit, int dOffset, + Armv5teLIR *pcrLabel) +{ + Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* Load a wide field from an object instance */ +static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + loadValue(cUnit, dInsn->vB, r2); + loadConstant(cUnit, r3, fieldOffset); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, r2, r3); + newLIR2(cUnit, ARMV5TE_LDMIA, r2, (1<<r0 | 1<<r1)); + storeValuePair(cUnit, r0, r1, dInsn->vA, r3); +} + +/* Store a wide field to an object instance */ +static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + loadValue(cUnit, dInsn->vB, r2); + loadValuePair(cUnit, dInsn->vA, r0, r1); + loadConstant(cUnit, r3, fieldOffset); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, r2, r3); + newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1<<r1)); +} + +/* + * Load a field from an object instance + * + * Inst should be one of: + * ARMV5TE_LDR_RRR + * ARMV5TE_LDRB_RRR + * ARMV5TE_LDRH_RRR + * ARMV5TE_LDRSB_RRR + * ARMV5TE_LDRSH_RRR + */ +static void genIGet(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + /* TUNING: write a utility routine to load via base + constant offset */ + loadValue(cUnit, dInsn->vB, r0); + loadConstant(cUnit, r1, fieldOffset); + genNullCheck(cUnit, r0, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, inst, r0, r0, r1); + storeValue(cUnit, r0, dInsn->vA, r1); +} + +/* + * Store a field to an object instance + * + * Inst should be one of: + * ARMV5TE_STR_RRR + * ARMV5TE_STRB_RRR + * ARMV5TE_STRH_RRR + */ +static void genIPut(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + /* TUNING: write a utility routine to load via base + constant offset */ + loadValue(cUnit, dInsn->vB, r2); + loadConstant(cUnit, r1, fieldOffset); + loadValue(cUnit, dInsn->vA, r0); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, inst, r0, r2, r1); +} + + +/* TODO: This should probably be done as an out-of-line instruction handler. */ + +/* + * Generate array load + * + * Inst should be one of: + * ARMV5TE_LDR_RRR + * ARMV5TE_LDRB_RRR + * ARMV5TE_LDRH_RRR + * ARMV5TE_LDRSB_RRR + * ARMV5TE_LDRSH_RRR + */ +static void genArrayGet(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int vArray, int vIndex, int vDest, int scale) +{ + int lenOffset = offsetof(ArrayObject, length); + int dataOffset = offsetof(ArrayObject, contents); + + loadValue(cUnit, vArray, r2); + loadValue(cUnit, vIndex, r3); + + /* null object? */ + Armv5teLIR * pcrLabel = genNullCheck(cUnit, r2, mir->offset, NULL); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r2, lenOffset >> 2); /* Get len */ + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, dataOffset); /* r2 -> array data */ + genBoundsCheck(cUnit, r3, r0, mir->offset, pcrLabel); + if (scale) { + newLIR3(cUnit, ARMV5TE_LSL, r3, r3, scale); + } + if (scale==3) { + newLIR3(cUnit, inst, r0, r2, r3); + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, 4); + newLIR3(cUnit, inst, r1, r2, r3); + storeValuePair(cUnit, r0, r1, vDest, r3); + } else { + newLIR3(cUnit, inst, r0, r2, r3); + storeValue(cUnit, r0, vDest, r3); + } +} + +/* TODO: This should probably be done as an out-of-line instruction handler. */ + +/* + * Generate array store + * + * Inst should be one of: + * ARMV5TE_STR_RRR + * ARMV5TE_STRB_RRR + * ARMV5TE_STRH_RRR + */ +static void genArrayPut(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int vArray, int vIndex, int vSrc, int scale) +{ + int lenOffset = offsetof(ArrayObject, length); + int dataOffset = offsetof(ArrayObject, contents); + + loadValue(cUnit, vArray, r2); + loadValue(cUnit, vIndex, r3); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r2, lenOffset >> 2); /* Get len */ + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, dataOffset); /* r2 -> array data */ + genBoundsCheck(cUnit, r3, r0, mir->offset, NULL); + /* at this point, r2 points to array, r3 is unscaled index */ + if (scale==3) { + loadValuePair(cUnit, vSrc, r0, r1); + } else { + loadValue(cUnit, vSrc, r0); + } + if (scale) { + newLIR3(cUnit, ARMV5TE_LSL, r3, r3, scale); + } + /* + * at this point, r2 points to array, r3 is scaled index, and r0[r1] is + * data + */ + if (scale==3) { + newLIR3(cUnit, inst, r0, r2, r3); + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, 4); + newLIR3(cUnit, inst, r1, r2, r3); + } else { + newLIR3(cUnit, inst, r0, r2, r3); + } +} + +static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vShift) +{ + loadValuePair(cUnit, vSrc1, r0, r1); + loadValue(cUnit, vShift, r2); + switch( mir->dalvikInsn.opCode) { + case OP_SHL_LONG: + case OP_SHL_LONG_2ADDR: + genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG); + break; + case OP_SHR_LONG: + case OP_SHR_LONG_2ADDR: + genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG); + break; + case OP_USHR_LONG: + case OP_USHR_LONG_2ADDR: + genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG); + break; + default: + return true; + } + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; +} + +static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + void* funct; + /* TODO: use a proper include file to define these */ + float __aeabi_fadd(float a, float b); + float __aeabi_fsub(float a, float b); + float __aeabi_fdiv(float a, float b); + float __aeabi_fmul(float a, float b); + float fmodf(float a, float b); + + switch (mir->dalvikInsn.opCode) { + case OP_ADD_FLOAT_2ADDR: + case OP_ADD_FLOAT: + funct = (void*) __aeabi_fadd; + break; + case OP_SUB_FLOAT_2ADDR: + case OP_SUB_FLOAT: + funct = (void*) __aeabi_fsub; + break; + case OP_DIV_FLOAT_2ADDR: + case OP_DIV_FLOAT: + funct = (void*) __aeabi_fdiv; + break; + case OP_MUL_FLOAT_2ADDR: + case OP_MUL_FLOAT: + funct = (void*) __aeabi_fmul; + break; + case OP_REM_FLOAT_2ADDR: + case OP_REM_FLOAT: + funct = (void*) fmodf; + break; + case OP_NEG_FLOAT: { + loadValue(cUnit, vSrc2, r0); + loadConstant(cUnit, r1, 0x80000000); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r0, r0, r1); + storeValue(cUnit, r0, vDest, r1); + return false; + } + default: + return true; + } + loadConstant(cUnit, r2, (int)funct); + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, r0, vDest, r1); + return false; +} + +static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + void* funct; + /* TODO: use a proper include file to define these */ + double __aeabi_dadd(double a, double b); + double __aeabi_dsub(double a, double b); + double __aeabi_ddiv(double a, double b); + double __aeabi_dmul(double a, double b); + double fmod(double a, double b); + + switch (mir->dalvikInsn.opCode) { + case OP_ADD_DOUBLE_2ADDR: + case OP_ADD_DOUBLE: + funct = (void*) __aeabi_dadd; + break; + case OP_SUB_DOUBLE_2ADDR: + case OP_SUB_DOUBLE: + funct = (void*) __aeabi_dsub; + break; + case OP_DIV_DOUBLE_2ADDR: + case OP_DIV_DOUBLE: + funct = (void*) __aeabi_ddiv; + break; + case OP_MUL_DOUBLE_2ADDR: + case OP_MUL_DOUBLE: + funct = (void*) __aeabi_dmul; + break; + case OP_REM_DOUBLE_2ADDR: + case OP_REM_DOUBLE: + funct = (void*) fmod; + break; + case OP_NEG_DOUBLE: { + loadValuePair(cUnit, vSrc2, r0, r1); + loadConstant(cUnit, r2, 0x80000000); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r1, r1, r2); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; + } + default: + return true; + } + loadConstant(cUnit, r4PC, (int)funct); + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; +} + +static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + int firstOp = ARMV5TE_BKPT; + int secondOp = ARMV5TE_BKPT; + bool callOut = false; + void *callTgt; + int retReg = r0; + /* TODO - find proper .h file to declare these */ + long long __aeabi_ldivmod(long long op1, long long op2); + + switch (mir->dalvikInsn.opCode) { + case OP_NOT_LONG: + firstOp = ARMV5TE_MVN; + secondOp = ARMV5TE_MVN; + break; + case OP_ADD_LONG: + case OP_ADD_LONG_2ADDR: + firstOp = ARMV5TE_ADD_RRR; + secondOp = ARMV5TE_ADC; + break; + case OP_SUB_LONG: + case OP_SUB_LONG_2ADDR: + firstOp = ARMV5TE_SUB_RRR; + secondOp = ARMV5TE_SBC; + break; + case OP_MUL_LONG: + case OP_MUL_LONG_2ADDR: + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; + break; + case OP_DIV_LONG: + case OP_DIV_LONG_2ADDR: + callOut = true; + retReg = r0; + callTgt = (void*)__aeabi_ldivmod; + break; + /* NOTE - result is in r2/r3 instead of r0/r1 */ + case OP_REM_LONG: + case OP_REM_LONG_2ADDR: + callOut = true; + callTgt = (void*)__aeabi_ldivmod; + retReg = r2; + break; + case OP_AND_LONG: + case OP_AND_LONG_2ADDR: + firstOp = ARMV5TE_AND_RR; + secondOp = ARMV5TE_AND_RR; + break; + case OP_OR_LONG: + case OP_OR_LONG_2ADDR: + firstOp = ARMV5TE_ORR; + secondOp = ARMV5TE_ORR; + break; + case OP_XOR_LONG: + case OP_XOR_LONG_2ADDR: + firstOp = ARMV5TE_EOR; + secondOp = ARMV5TE_EOR; + break; + case OP_NEG_LONG: + loadValuePair(cUnit, vSrc2, r2, r3); + loadConstant(cUnit, r1, 0); + newLIR3(cUnit, ARMV5TE_SUB_RRR, r0, r1, r2); + newLIR2(cUnit, ARMV5TE_SBC, r1, r3); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; + default: + LOGE("Invalid long arith op"); + dvmAbort(); + } + if (!callOut) { + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + genBinaryOpWide(cUnit, vDest, firstOp, secondOp); + } else { + loadValuePair(cUnit, vSrc2, r2, r3); + loadConstant(cUnit, r4PC, (int) callTgt); + loadValuePair(cUnit, vSrc1, r0, r1); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + storeValuePair(cUnit, retReg, retReg+1, vDest, r4PC); + } + return false; +} + +static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + int armOp = ARMV5TE_BKPT; + bool callOut = false; + bool checkZero = false; + int retReg = r0; + void *callTgt; + + /* TODO - find proper .h file to declare these */ + int __aeabi_idivmod(int op1, int op2); + int __aeabi_idiv(int op1, int op2); + + switch (mir->dalvikInsn.opCode) { + case OP_NEG_INT: + armOp = ARMV5TE_NEG; + break; + case OP_NOT_INT: + armOp = ARMV5TE_MVN; + break; + case OP_ADD_INT: + case OP_ADD_INT_2ADDR: + armOp = ARMV5TE_ADD_RRR; + break; + case OP_SUB_INT: + case OP_SUB_INT_2ADDR: + armOp = ARMV5TE_SUB_RRR; + break; + case OP_MUL_INT: + case OP_MUL_INT_2ADDR: + armOp = ARMV5TE_MUL; + break; + case OP_DIV_INT: + case OP_DIV_INT_2ADDR: + callOut = true; + checkZero = true; + callTgt = __aeabi_idiv; + retReg = r0; + break; + /* NOTE: returns in r1 */ + case OP_REM_INT: + case OP_REM_INT_2ADDR: + callOut = true; + checkZero = true; + callTgt = __aeabi_idivmod; + retReg = r1; + break; + case OP_AND_INT: + case OP_AND_INT_2ADDR: + armOp = ARMV5TE_AND_RR; + break; + case OP_OR_INT: + case OP_OR_INT_2ADDR: + armOp = ARMV5TE_ORR; + break; + case OP_XOR_INT: + case OP_XOR_INT_2ADDR: + armOp = ARMV5TE_EOR; + break; + case OP_SHL_INT: + case OP_SHL_INT_2ADDR: + armOp = ARMV5TE_LSLV; + break; + case OP_SHR_INT: + case OP_SHR_INT_2ADDR: + armOp = ARMV5TE_ASRV; + break; + case OP_USHR_INT: + case OP_USHR_INT_2ADDR: + armOp = ARMV5TE_LSRV; + break; + default: + LOGE("Invalid word arith op: 0x%x(%d)", + mir->dalvikInsn.opCode, mir->dalvikInsn.opCode); + dvmAbort(); + } + if (!callOut) { + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + genBinaryOp(cUnit, vDest, armOp); + } else { + loadValue(cUnit, vSrc2, r1); + loadConstant(cUnit, r2, (int) callTgt); + loadValue(cUnit, vSrc1, r0); + if (checkZero) { + genNullCheck(cUnit, r1, mir->offset, NULL); + } + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, retReg, vDest, r2); + } + return false; +} + +static bool genArithOp(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vA = mir->dalvikInsn.vA; + int vB = mir->dalvikInsn.vB; + int vC = mir->dalvikInsn.vC; + + if ((opCode >= OP_ADD_LONG_2ADDR) && (opCode <= OP_XOR_LONG_2ADDR)) { + return genArithOpLong(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_LONG) && (opCode <= OP_XOR_LONG)) { + return genArithOpLong(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_SHL_LONG_2ADDR) && (opCode <= OP_USHR_LONG_2ADDR)) { + return genShiftOpLong(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_SHL_LONG) && (opCode <= OP_USHR_LONG)) { + return genShiftOpLong(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_USHR_INT_2ADDR)) { + return genArithOpInt(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_INT) && (opCode <= OP_USHR_INT)) { + return genArithOpInt(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_ADD_FLOAT_2ADDR) && (opCode <= OP_REM_FLOAT_2ADDR)) { + return genArithOpFloat(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_FLOAT) && (opCode <= OP_REM_FLOAT)) { + return genArithOpFloat(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_ADD_DOUBLE_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { + return genArithOpDouble(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_DOUBLE) && (opCode <= OP_REM_DOUBLE)) { + return genArithOpDouble(cUnit,mir, vA, vB, vC); + } + return true; +} + +static bool genConversion(CompilationUnit *cUnit, MIR *mir, void *funct, + int srcSize, int tgtSize) +{ + loadConstant(cUnit, r2, (int)funct); + if (srcSize == 1) { + loadValue(cUnit, mir->dalvikInsn.vB, r0); + } else { + loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1); + } + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + if (tgtSize == 1) { + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + } else { + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + } + return false; +} + +/* Experimental example of completely inlining a native replacement */ +static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir) +{ + int offset = (int) &((InterpState *) NULL)->retval; + DecodedInstruction *dInsn = &mir->dalvikInsn; + assert(dInsn->vA == 1); + loadValue(cUnit, dInsn->arg[0], r0); + loadConstant(cUnit, r1, gDvm.offJavaLangString_count); + genNullCheck(cUnit, r0, mir->offset, NULL); + newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r1); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2); + return false; +} + +static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir, + DecodedInstruction *dInsn, + Armv5teLIR **pcrLabel) +{ + unsigned int i; + unsigned int regMask = 0; + + /* Load arguments to r0..r4 */ + for (i = 0; i < dInsn->vA; i++) { + regMask |= 1 << i; + loadValue(cUnit, dInsn->arg[i], i); + } + if (regMask) { + /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ + newLIR2(cUnit, ARMV5TE_MOV_RR, r7, rFP); + newLIR2(cUnit, ARMV5TE_SUB_RI8, r7, + sizeof(StackSaveArea) + (dInsn->vA << 2)); + /* generate null check */ + if (pcrLabel) { + *pcrLabel = genNullCheck(cUnit, r0, mir->offset, NULL); + } + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + } +} + +static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir, + DecodedInstruction *dInsn, + Armv5teLIR **pcrLabel) +{ + int srcOffset = dInsn->vC << 2; + int numArgs = dInsn->vA; + int regMask; + /* + * r4PC : &rFP[vC] + * r7: &newFP[0] + */ + if (srcOffset < 8) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, r4PC, rFP, srcOffset); + } else { + loadConstant(cUnit, r4PC, srcOffset); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r4PC, rFP, r4PC); + } + /* load [r0 .. min(numArgs,4)] */ + regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1; + newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask); + + if (sizeof(StackSaveArea) + (numArgs << 2) < 256) { + newLIR2(cUnit, ARMV5TE_MOV_RR, r7, rFP); + newLIR2(cUnit, ARMV5TE_SUB_RI8, r7, + sizeof(StackSaveArea) + (numArgs << 2)); + } else { + loadConstant(cUnit, r7, sizeof(StackSaveArea) + (numArgs << 2)); + newLIR3(cUnit, ARMV5TE_SUB_RRR, r7, rFP, r7); + } + + /* generate null check */ + if (pcrLabel) { + *pcrLabel = genNullCheck(cUnit, r0, mir->offset, NULL); + } + + /* + * Handle remaining 4n arguments: + * store previously loaded 4 values and load the next 4 values + */ + if (numArgs >= 8) { + Armv5teLIR *loopLabel = NULL; + /* + * r0 contains "this" and it will be used later, so push it to the stack + * first. Pushing r5 is just for stack alignment purposes. + */ + newLIR1(cUnit, ARMV5TE_PUSH, 1 << r0 | 1 << 5); + /* No need to generate the loop structure if numArgs <= 11 */ + if (numArgs > 11) { + loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2); + loopLabel = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL); + } + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask); + /* No need to generate the loop structure if numArgs <= 11 */ + if (numArgs > 11) { + newLIR2(cUnit, ARMV5TE_SUB_RI8, 5, 4); + genConditionalBranch(cUnit, ARM_COND_NE, loopLabel); + } + } + + /* Save the last batch of loaded values */ + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + + /* Generate the loop epilogue - don't use r0 */ + if ((numArgs > 4) && (numArgs % 4)) { + regMask = ((1 << (numArgs & 0x3)) - 1) << 1; + newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask); + } + if (numArgs >= 8) + newLIR1(cUnit, ARMV5TE_POP, 1 << r0 | 1 << 5); + + /* Save the modulo 4 arguments */ + if ((numArgs > 4) && (numArgs % 4)) { + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + } +} + +static void genInvokeCommon(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList, Armv5teLIR *pcrLabel, + const Method *calleeMethod) +{ + Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id]; + + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + addrRetChain->generic.target = (LIR *) retChainingCell; + /* + * r0 = calleeMethod (loaded upon calling genInvokeCommon) + * r1 = &ChainingCell + * r4PC = callsiteDPC + */ + if (dvmIsNativeMethod(calleeMethod)) { + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + } else { + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_CHAIN); +#if defined(INVOKE_STATS) + gDvmJit.invokeChain++; +#endif + genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); + } + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); +} + +/* Geneate a branch to go back to the interpreter */ +static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset) +{ + /* r0 = dalvik pc */ + loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset)); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpPunt) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r1); +} + +/* + * Attempt to single step one instruction using the interpreter and return + * to the compiled code for the next Dalvik instruction + */ +static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir) +{ + int flags = dexGetInstrFlags(gDvm.instrFlags, mir->dalvikInsn.opCode); + int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn | + kInstrCanThrow; + if ((mir->next == NULL) || (flags & flagsToCheck)) { + genPuntToInterp(cUnit, mir->offset); + return; + } + int entryAddr = offsetof(InterpState, + jitToInterpEntries.dvmJitToInterpSingleStep); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r2, rGLUE, entryAddr >> 2); + /* r0 = dalvik pc */ + loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); + /* r1 = dalvik pc of following instruction */ + loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset)); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); +} + + +/*****************************************************************************/ +/* + * The following are the first-level codegen routines that analyze the format + * of each bytecode then either dispatch special purpose codegen routines + * or produce corresponding Thumb instructions directly. + */ + +static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir, + BasicBlock *bb, Armv5teLIR *labelList) +{ + /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */ + genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); + return false; +} + +static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + if (((dalvikOpCode >= OP_UNUSED_3E) && (dalvikOpCode <= OP_UNUSED_43)) || + ((dalvikOpCode >= OP_UNUSED_E3) && (dalvikOpCode <= OP_UNUSED_EC))) { + LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode); + return true; + } + switch (dalvikOpCode) { + case OP_RETURN_VOID: + genReturnCommon(cUnit,mir); + break; + case OP_UNUSED_73: + case OP_UNUSED_79: + case OP_UNUSED_7A: + LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode); + return true; + case OP_NOP: + break; + default: + return true; + } + return false; +} + +static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir) +{ + switch (mir->dalvikInsn.opCode) { + case OP_CONST: + case OP_CONST_4: + loadConstant(cUnit, r0, mir->dalvikInsn.vB); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + case OP_CONST_WIDE_32: + loadConstant(cUnit, r0, mir->dalvikInsn.vB); + newLIR3(cUnit, ARMV5TE_ASR, r1, r0, 31); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir) +{ + switch (mir->dalvikInsn.opCode) { + case OP_CONST_HIGH16: + loadConstant(cUnit, r0, mir->dalvikInsn.vB << 16); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + case OP_CONST_WIDE_HIGH16: + loadConstant(cUnit, r1, mir->dalvikInsn.vB << 16); + loadConstant(cUnit, r0, 0); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir) +{ + /* For OP_THROW_VERIFICATION_ERROR */ + genInterpSingleStep(cUnit, mir); + return false; +} + +static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) +{ + switch (mir->dalvikInsn.opCode) { + /* + * TODO: Verify that we can ignore the resolution check here because + * it will have already successfully been interpreted once + */ + case OP_CONST_STRING_JUMBO: + case OP_CONST_STRING: { + void *strPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]); + assert(strPtr != NULL); + loadConstant(cUnit, r0, (int) strPtr ); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + /* + * TODO: Verify that we can ignore the resolution check here because + * it will have already successfully been interpreted once + */ + case OP_CONST_CLASS: { + void *classPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); + assert(classPtr != NULL); + loadConstant(cUnit, r0, (int) classPtr ); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_SGET_OBJECT: + case OP_SGET_BOOLEAN: + case OP_SGET_CHAR: + case OP_SGET_BYTE: + case OP_SGET_SHORT: + case OP_SGET: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadConstant(cUnit, r0, (int) fieldPtr + valOffset); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, 0); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r2); + break; + } + case OP_SGET_WIDE: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadConstant(cUnit, r2, (int) fieldPtr + valOffset); + newLIR2(cUnit, ARMV5TE_LDMIA, r2, (1<<r0 | 1<<r1)); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + } + case OP_SPUT_OBJECT: + case OP_SPUT_BOOLEAN: + case OP_SPUT_CHAR: + case OP_SPUT_BYTE: + case OP_SPUT_SHORT: + case OP_SPUT: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vA, r0); + loadConstant(cUnit, r1, (int) fieldPtr + valOffset); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, r1, 0); + break; + } + case OP_SPUT_WIDE: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadValuePair(cUnit, mir->dalvikInsn.vA, r0, r1); + loadConstant(cUnit, r2, (int) fieldPtr + valOffset); + newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1<<r1)); + break; + } + case OP_NEW_INSTANCE: { + ClassObject *classPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); + assert(classPtr != NULL); + assert(classPtr->status & CLASS_INITIALIZED); + if ((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) != 0) { + /* It's going to throw, just let the interp. deal with it. */ + genInterpSingleStep(cUnit, mir); + return false; + } + loadConstant(cUnit, r0, (int) classPtr); + loadConstant(cUnit, r4PC, (int)dvmAllocObject); + genExportPC(cUnit, mir, r2, r3 ); + loadConstant(cUnit, r1, ALLOC_DONT_TRACK); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* + * TODO: As coded, we'll bail and reinterpret on alloc failure. + * Need a general mechanism to bail to thrown exception code. + */ + genNullCheck(cUnit, r0, mir->offset, NULL); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_CHECK_CAST: { + ClassObject *classPtr = + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); + loadConstant(cUnit, r1, (int) classPtr ); + loadValue(cUnit, mir->dalvikInsn.vA, r0); /* Ref */ + /* + * TODO - in theory classPtr should be resoved by the time this + * instruction made into a trace, but we are seeing NULL at runtime + * so this check is temporarily used as a workaround. + */ + Armv5teLIR * pcrLabel = genNullCheck(cUnit, r1, mir->offset, NULL); + newLIR2(cUnit, ARMV5TE_CMP_RI8, r0, 0); /* Null? */ + Armv5teLIR *branch1 = + newLIR2(cUnit, ARMV5TE_B_COND, 4, ARM_COND_EQ); + /* r0 now contains object->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial); + newLIR2(cUnit, ARMV5TE_CMP_RR, r0, r1); + Armv5teLIR *branch2 = + newLIR2(cUnit, ARMV5TE_B_COND, 2, ARM_COND_EQ); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* check cast failed - punt to the interpreter */ + genNullCheck(cUnit, r0, mir->offset, pcrLabel); + /* check cast passed - branch target here */ + Armv5teLIR *target = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL); + branch1->generic.target = (LIR *)target; + branch2->generic.target = (LIR *)target; + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + switch (dalvikOpCode) { + case OP_MOVE_EXCEPTION: { + int offset = offsetof(InterpState, self); + int exOffset = offsetof(Thread, exception); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, r0, exOffset >> 2); + storeValue(cUnit, r1, mir->dalvikInsn.vA, r0); + break; + } + case OP_MOVE_RESULT: + case OP_MOVE_RESULT_OBJECT: { + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_MOVE_RESULT_WIDE: { + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, (offset >> 2)+1); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + } + case OP_RETURN_WIDE: { + loadValuePair(cUnit, mir->dalvikInsn.vA, r0, r1); + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r1, rGLUE, (offset >> 2)+1); + genReturnCommon(cUnit,mir); + break; + } + case OP_RETURN: + case OP_RETURN_OBJECT: { + loadValue(cUnit, mir->dalvikInsn.vA, r0); + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2); + genReturnCommon(cUnit,mir); + break; + } + /* + * TODO-VERIFY: May be playing a bit fast and loose here. As coded, + * a failure on lock/unlock will cause us to revert to the interpeter + * to try again. This means we essentially ignore the first failure on + * the assumption that the interpreter will correctly handle the 2nd. + */ + case OP_MONITOR_ENTER: + case OP_MONITOR_EXIT: { + int offset = offsetof(InterpState, self); + loadValue(cUnit, mir->dalvikInsn.vA, r1); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + if (dalvikOpCode == OP_MONITOR_ENTER) { + loadConstant(cUnit, r2, (int)dvmLockObject); + } else { + loadConstant(cUnit, r2, (int)dvmUnlockObject); + } + /* + * TODO-VERIFY: Note that we're not doing an EXPORT_PC, as + * Lock/unlock won't throw, and this code does not support + * DEADLOCK_PREDICTION or MONITOR_TRACKING. Should it? + */ + genNullCheck(cUnit, r1, mir->offset, NULL); + /* Do the call */ + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + break; + } + case OP_THROW: { + genInterpSingleStep(cUnit, mir); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vSrc1Dest = mir->dalvikInsn.vA; + int vSrc2 = mir->dalvikInsn.vB; + + /* TODO - find the proper include file to declare these */ + float __aeabi_i2f( int op1 ); + int __aeabi_f2iz( float op1 ); + float __aeabi_d2f( double op1 ); + double __aeabi_f2d( float op1 ); + double __aeabi_i2d( int op1 ); + int __aeabi_d2iz( double op1 ); + long __aeabi_f2lz( float op1 ); + float __aeabi_l2f( long op1 ); + long __aeabi_d2lz( double op1 ); + double __aeabi_l2d( long op1 ); + + if ( (opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { + return genArithOp( cUnit, mir ); + } + + switch (opCode) { + case OP_INT_TO_FLOAT: + return genConversion(cUnit, mir, (void*)__aeabi_i2f, 1, 1); + case OP_FLOAT_TO_INT: + return genConversion(cUnit, mir, (void*)__aeabi_f2iz, 1, 1); + case OP_DOUBLE_TO_FLOAT: + return genConversion(cUnit, mir, (void*)__aeabi_d2f, 2, 1); + case OP_FLOAT_TO_DOUBLE: + return genConversion(cUnit, mir, (void*)__aeabi_f2d, 1, 2); + case OP_INT_TO_DOUBLE: + return genConversion(cUnit, mir, (void*)__aeabi_i2d, 1, 2); + case OP_DOUBLE_TO_INT: + return genConversion(cUnit, mir, (void*)__aeabi_d2iz, 2, 1); + case OP_FLOAT_TO_LONG: + return genConversion(cUnit, mir, (void*)__aeabi_f2lz, 1, 2); + case OP_LONG_TO_FLOAT: + return genConversion(cUnit, mir, (void*)__aeabi_l2f, 2, 1); + case OP_DOUBLE_TO_LONG: + return genConversion(cUnit, mir, (void*)__aeabi_d2lz, 2, 2); + case OP_LONG_TO_DOUBLE: + return genConversion(cUnit, mir, (void*)__aeabi_l2d, 2, 2); + case OP_NEG_INT: + case OP_NOT_INT: + return genArithOpInt(cUnit, mir, vSrc1Dest, vSrc1Dest, vSrc2); + case OP_NEG_LONG: + case OP_NOT_LONG: + return genArithOpLong(cUnit,mir, vSrc1Dest, vSrc1Dest, vSrc2); + case OP_NEG_FLOAT: + return genArithOpFloat(cUnit,mir,vSrc1Dest,vSrc1Dest,vSrc2); + case OP_NEG_DOUBLE: + return genArithOpDouble(cUnit,mir,vSrc1Dest,vSrc1Dest,vSrc2); + case OP_MOVE_WIDE: + loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + case OP_INT_TO_LONG: + loadValue(cUnit, mir->dalvikInsn.vB, r0); + newLIR3(cUnit, ARMV5TE_ASR, r1, r0, 31); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + case OP_MOVE: + case OP_MOVE_OBJECT: + case OP_LONG_TO_INT: + loadValue(cUnit, vSrc2, r0); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_INT_TO_BYTE: + loadValue(cUnit, vSrc2, r0); + newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 24); + newLIR3(cUnit, ARMV5TE_ASR, r0, r0, 24); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_INT_TO_SHORT: + loadValue(cUnit, vSrc2, r0); + newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 16); + newLIR3(cUnit, ARMV5TE_ASR, r0, r0, 16); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_INT_TO_CHAR: + loadValue(cUnit, vSrc2, r0); + newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 16); + newLIR3(cUnit, ARMV5TE_LSR, r0, r0, 16); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_ARRAY_LENGTH: { + int lenOffset = offsetof(ArrayObject, length); + loadValue(cUnit, vSrc2, r0); + genNullCheck(cUnit, r0, mir->offset, NULL); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, lenOffset >> 2); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + /* It takes few instructions to handle OP_CONST_WIDE_16 inline */ + if (dalvikOpCode == OP_CONST_WIDE_16) { + int rDest = mir->dalvikInsn.vA; + int BBBB = mir->dalvikInsn.vB; + int rLow = r0, rHigh = r1; + if (BBBB == 0) { + newLIR2(cUnit, ARMV5TE_MOV_IMM, rLow, 0); + rHigh = rLow; + } else if (BBBB > 0 && BBBB <= 255) { + /* rLow = ssssBBBB */ + newLIR2(cUnit, ARMV5TE_MOV_IMM, rLow, BBBB); + /* rHigh = 0 */ + newLIR2(cUnit, ARMV5TE_MOV_IMM, rHigh, 0); + } else { + loadConstant(cUnit, rLow, BBBB); + /* + * arithmetic-shift-right 32 bits to get the high half of long + * [63..32] + */ + newLIR3(cUnit, ARMV5TE_ASR, rHigh, rLow, 0); + } + + /* Save the long values to the specified Dalvik register pair */ + /* + * If rDest is no greater than 30, use two "str rd, [rFP + immed_5]" + * instructions to store the results. Effective address is + * rFP + immed_5 << 2. + */ + if (rDest < 31) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, rLow, rFP, rDest); + newLIR3(cUnit, ARMV5TE_STR_RRI5, rHigh, rFP, rDest+1); + } else { + /* + * Otherwise just load the frame offset from the constant pool and add + * it to rFP. Then use stmia to store the results to the specified + * register pair. + */ + /* Need to replicate the content in r0 to r1 */ + if (rLow == rHigh) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rLow+1, rLow, 0); + } + /* load the rFP offset into r2 */ + loadConstant(cUnit, r2, rDest*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, rFP, r2); + newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1 << r1)); + } + } else if (dalvikOpCode == OP_CONST_16) { + int rDest = mir->dalvikInsn.vA; + int BBBB = mir->dalvikInsn.vB; + if (BBBB >= 0 && BBBB <= 255) { + /* r0 = BBBB */ + newLIR2(cUnit, ARMV5TE_MOV_IMM, r0, BBBB); + } else { + loadConstant(cUnit, r0, BBBB); + } + + /* Save the constant to the specified Dalvik register */ + /* + * If rDest is no greater than 31, effective address is + * rFP + immed_5 << 2. + */ + if (rDest < 32) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rFP, rDest); + } else { + /* + * Otherwise just load the frame offset from the constant pool and add + * it to rFP. Then use stmia to store the results to the specified + * register pair. + */ + /* load the rFP offset into r2 */ + loadConstant(cUnit, r2, rDest*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, rFP, r2); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, r2, 0); + } + } else { + return true; + } + return false; +} + +/* Compare agaist zero */ +static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + Armv5teConditionCode cond; + + loadValue(cUnit, mir->dalvikInsn.vA, r0); + newLIR2(cUnit, ARMV5TE_CMP_RI8, r0, 0); + + switch (dalvikOpCode) { + case OP_IF_EQZ: + cond = ARM_COND_EQ; + break; + case OP_IF_NEZ: + cond = ARM_COND_NE; + break; + case OP_IF_LTZ: + cond = ARM_COND_LT; + break; + case OP_IF_GEZ: + cond = ARM_COND_GE; + break; + case OP_IF_GTZ: + cond = ARM_COND_GT; + break; + case OP_IF_LEZ: + cond = ARM_COND_LE; + break; + default: + cond = 0; + LOGE("Unexpected opcode (%d) for Fmt21t\n", dalvikOpCode); + dvmAbort(); + } + genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); + /* This mostly likely will be optimized away in a later phase */ + genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); + return false; +} + +static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int vSrc = mir->dalvikInsn.vB; + int vDest = mir->dalvikInsn.vA; + int lit = mir->dalvikInsn.vC; + int armOp; + + /* TODO: find the proper .h file to declare these */ + int __aeabi_idivmod(int op1, int op2); + int __aeabi_idiv(int op1, int op2); + + switch (dalvikOpCode) { + case OP_ADD_INT_LIT8: + case OP_ADD_INT_LIT16: + loadValue(cUnit, vSrc, r0); + if (lit <= 255 && lit >= 0) { + newLIR2(cUnit, ARMV5TE_ADD_RI8, r0, lit); + storeValue(cUnit, r0, vDest, r1); + } else if (lit >= -255 && lit <= 0) { + /* Convert to a small constant subtraction */ + newLIR2(cUnit, ARMV5TE_SUB_RI8, r0, -lit); + storeValue(cUnit, r0, vDest, r1); + } else { + loadConstant(cUnit, r1, lit); + genBinaryOp(cUnit, vDest, ARMV5TE_ADD_RRR); + } + break; + + case OP_RSUB_INT_LIT8: + case OP_RSUB_INT: + loadValue(cUnit, vSrc, r1); + loadConstant(cUnit, r0, lit); + genBinaryOp(cUnit, vDest, ARMV5TE_SUB_RRR); + break; + + case OP_MUL_INT_LIT8: + case OP_MUL_INT_LIT16: + case OP_AND_INT_LIT8: + case OP_AND_INT_LIT16: + case OP_OR_INT_LIT8: + case OP_OR_INT_LIT16: + case OP_XOR_INT_LIT8: + case OP_XOR_INT_LIT16: + loadValue(cUnit, vSrc, r0); + loadConstant(cUnit, r1, lit); + switch (dalvikOpCode) { + case OP_MUL_INT_LIT8: + case OP_MUL_INT_LIT16: + armOp = ARMV5TE_MUL; + break; + case OP_AND_INT_LIT8: + case OP_AND_INT_LIT16: + armOp = ARMV5TE_AND_RR; + break; + case OP_OR_INT_LIT8: + case OP_OR_INT_LIT16: + armOp = ARMV5TE_ORR; + break; + case OP_XOR_INT_LIT8: + case OP_XOR_INT_LIT16: + armOp = ARMV5TE_EOR; + break; + default: + dvmAbort(); + } + genBinaryOp(cUnit, vDest, armOp); + break; + + case OP_SHL_INT_LIT8: + case OP_SHR_INT_LIT8: + case OP_USHR_INT_LIT8: + loadValue(cUnit, vSrc, r0); + switch (dalvikOpCode) { + case OP_SHL_INT_LIT8: + armOp = ARMV5TE_LSL; + break; + case OP_SHR_INT_LIT8: + armOp = ARMV5TE_ASR; + break; + case OP_USHR_INT_LIT8: + armOp = ARMV5TE_LSR; + break; + default: dvmAbort(); + } + newLIR3(cUnit, armOp, r0, r0, lit); + storeValue(cUnit, r0, vDest, r1); + break; + + case OP_DIV_INT_LIT8: + case OP_DIV_INT_LIT16: + if (lit == 0) { + /* Let the interpreter deal with div by 0 */ + genInterpSingleStep(cUnit, mir); + return false; + } + loadConstant(cUnit, r2, (int)__aeabi_idiv); + loadConstant(cUnit, r1, lit); + loadValue(cUnit, vSrc, r0); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, r0, vDest, r2); + break; + + case OP_REM_INT_LIT8: + case OP_REM_INT_LIT16: + if (lit == 0) { + /* Let the interpreter deal with div by 0 */ + genInterpSingleStep(cUnit, mir); + return false; + } + loadConstant(cUnit, r2, (int)__aeabi_idivmod); + loadConstant(cUnit, r1, lit); + loadValue(cUnit, vSrc, r0); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, r1, vDest, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int fieldOffset; + + if (dalvikOpCode >= OP_IGET && dalvikOpCode <= OP_IPUT_SHORT) { + InstField *pInstField = (InstField *) + cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC]; + int fieldOffset; + + assert(pInstField != NULL); + fieldOffset = pInstField->byteOffset; + } else { + /* To make the compiler happy */ + fieldOffset = 0; + } + switch (dalvikOpCode) { + /* + * TODO: I may be assuming too much here. + * Verify what is known at JIT time. + */ + case OP_NEW_ARRAY: { + void *classPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); + assert(classPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vB, r1); /* Len */ + loadConstant(cUnit, r0, (int) classPtr ); + loadConstant(cUnit, r4PC, (int)dvmAllocArrayByClass); + Armv5teLIR *pcrLabel = + genRegImmCheck(cUnit, ARM_COND_MI, r1, 0, mir->offset, NULL); + genExportPC(cUnit, mir, r2, r3 ); + newLIR2(cUnit, ARMV5TE_MOV_IMM,r2,ALLOC_DONT_TRACK); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* + * TODO: As coded, we'll bail and reinterpret on alloc failure. + * Need a general mechanism to bail to thrown exception code. + */ + genNullCheck(cUnit, r0, mir->offset, pcrLabel); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + /* + * TODO: I may be assuming too much here. + * Verify what is known at JIT time. + */ + case OP_INSTANCE_OF: { + ClassObject *classPtr = + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); + assert(classPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vB, r1); /* Ref */ + loadConstant(cUnit, r2, (int) classPtr ); + loadConstant(cUnit, r0, 1); /* Assume true */ + newLIR2(cUnit, ARMV5TE_CMP_RI8, r1, 0); /* Null? */ + Armv5teLIR *branch1 = newLIR2(cUnit, ARMV5TE_B_COND, 4, + ARM_COND_EQ); + /* r1 now contains object->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, r1, + offsetof(Object, clazz) >> 2); + loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial); + newLIR2(cUnit, ARMV5TE_CMP_RR, r1, r2); + Armv5teLIR *branch2 = newLIR2(cUnit, ARMV5TE_B_COND, 2, + ARM_COND_EQ); + newLIR2(cUnit, ARMV5TE_MOV_RR, r0, r1); + newLIR2(cUnit, ARMV5TE_MOV_RR, r1, r2); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* branch target here */ + Armv5teLIR *target = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + branch1->generic.target = (LIR *)target; + branch2->generic.target = (LIR *)target; + break; + } + case OP_IGET_WIDE: + genIGetWide(cUnit, mir, fieldOffset); + break; + case OP_IGET: + case OP_IGET_OBJECT: + genIGet(cUnit, mir, ARMV5TE_LDR_RRR, fieldOffset); + break; + case OP_IGET_BOOLEAN: + genIGet(cUnit, mir, ARMV5TE_LDRB_RRR, fieldOffset); + break; + case OP_IGET_BYTE: + genIGet(cUnit, mir, ARMV5TE_LDRSB_RRR, fieldOffset); + break; + case OP_IGET_CHAR: + genIGet(cUnit, mir, ARMV5TE_LDRH_RRR, fieldOffset); + break; + case OP_IGET_SHORT: + genIGet(cUnit, mir, ARMV5TE_LDRSH_RRR, fieldOffset); + break; + case OP_IPUT_WIDE: + genIPutWide(cUnit, mir, fieldOffset); + break; + case OP_IPUT: + case OP_IPUT_OBJECT: + genIPut(cUnit, mir, ARMV5TE_STR_RRR, fieldOffset); + break; + case OP_IPUT_SHORT: + case OP_IPUT_CHAR: + genIPut(cUnit, mir, ARMV5TE_STRH_RRR, fieldOffset); + break; + case OP_IPUT_BYTE: + case OP_IPUT_BOOLEAN: + genIPut(cUnit, mir, ARMV5TE_STRB_RRR, fieldOffset); + break; + default: + return true; + } + return false; +} + +static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int fieldOffset = mir->dalvikInsn.vC; + switch (dalvikOpCode) { + case OP_IGET_QUICK: + case OP_IGET_OBJECT_QUICK: + genIGet(cUnit, mir, ARMV5TE_LDR_RRR, fieldOffset); + break; + case OP_IPUT_QUICK: + case OP_IPUT_OBJECT_QUICK: + genIPut(cUnit, mir, ARMV5TE_STR_RRR, fieldOffset); + break; + case OP_IGET_WIDE_QUICK: + genIGetWide(cUnit, mir, fieldOffset); + break; + case OP_IPUT_WIDE_QUICK: + genIPutWide(cUnit, mir, fieldOffset); + break; + default: + return true; + } + return false; + +} + +/* Compare agaist zero */ +static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + Armv5teConditionCode cond; + + loadValue(cUnit, mir->dalvikInsn.vA, r0); + loadValue(cUnit, mir->dalvikInsn.vB, r1); + newLIR2(cUnit, ARMV5TE_CMP_RR, r0, r1); + + switch (dalvikOpCode) { + case OP_IF_EQ: + cond = ARM_COND_EQ; + break; + case OP_IF_NE: + cond = ARM_COND_NE; + break; + case OP_IF_LT: + cond = ARM_COND_LT; + break; + case OP_IF_GE: + cond = ARM_COND_GE; + break; + case OP_IF_GT: + cond = ARM_COND_GT; + break; + case OP_IF_LE: + cond = ARM_COND_LE; + break; + default: + cond = 0; + LOGE("Unexpected opcode (%d) for Fmt22t\n", dalvikOpCode); + dvmAbort(); + } + genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); + /* This mostly likely will be optimized away in a later phase */ + genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); + return false; +} + +static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vSrc1Dest = mir->dalvikInsn.vA; + int vSrc2 = mir->dalvikInsn.vB; + + switch (opCode) { + case OP_MOVE_16: + case OP_MOVE_OBJECT_16: + case OP_MOVE_FROM16: + case OP_MOVE_OBJECT_FROM16: + loadValue(cUnit, vSrc2, r0); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_MOVE_WIDE_16: + case OP_MOVE_WIDE_FROM16: + loadValuePair(cUnit, vSrc2, r0, r1); + storeValuePair(cUnit, r0, r1, vSrc1Dest, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vA = mir->dalvikInsn.vA; + int vB = mir->dalvikInsn.vB; + int vC = mir->dalvikInsn.vC; + + if ( (opCode >= OP_ADD_INT) && (opCode <= OP_REM_DOUBLE)) { + return genArithOp( cUnit, mir ); + } + + switch (opCode) { + case OP_CMP_LONG: + loadValuePair(cUnit,vB, r0, r1); + loadValuePair(cUnit, vC, r2, r3); + genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPL_FLOAT: + loadValue(cUnit, vB, r0); + loadValue(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPG_FLOAT: + loadValue(cUnit, vB, r0); + loadValue(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPL_DOUBLE: + loadValueAddress(cUnit, vB, r0); + loadValueAddress(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPG_DOUBLE: + loadValueAddress(cUnit, vB, r0); + loadValueAddress(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE); + storeValue(cUnit, r0, vA, r1); + break; + case OP_AGET_WIDE: + genArrayGet(cUnit, mir, ARMV5TE_LDR_RRR, vB, vC, vA, 3); + break; + case OP_AGET: + case OP_AGET_OBJECT: + genArrayGet(cUnit, mir, ARMV5TE_LDR_RRR, vB, vC, vA, 2); + break; + case OP_AGET_BOOLEAN: + genArrayGet(cUnit, mir, ARMV5TE_LDRB_RRR, vB, vC, vA, 0); + break; + case OP_AGET_BYTE: + genArrayGet(cUnit, mir, ARMV5TE_LDRSB_RRR, vB, vC, vA, 0); + break; + case OP_AGET_CHAR: + genArrayGet(cUnit, mir, ARMV5TE_LDRH_RRR, vB, vC, vA, 1); + break; + case OP_AGET_SHORT: + genArrayGet(cUnit, mir, ARMV5TE_LDRSH_RRR, vB, vC, vA, 1); + break; + case OP_APUT_WIDE: + genArrayPut(cUnit, mir, ARMV5TE_STR_RRR, vB, vC, vA, 3); + break; + case OP_APUT: + case OP_APUT_OBJECT: + genArrayPut(cUnit, mir, ARMV5TE_STR_RRR, vB, vC, vA, 2); + break; + case OP_APUT_SHORT: + case OP_APUT_CHAR: + genArrayPut(cUnit, mir, ARMV5TE_STRH_RRR, vB, vC, vA, 1); + break; + case OP_APUT_BYTE: + case OP_APUT_BOOLEAN: + genArrayPut(cUnit, mir, ARMV5TE_STRB_RRR, vB, vC, vA, 0); + break; + default: + return true; + } + return false; +} + +static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + switch (dalvikOpCode) { + case OP_FILL_ARRAY_DATA: { + loadConstant(cUnit, r4PC, (int)dvmInterpHandleFillArrayData); + loadValue(cUnit, mir->dalvikInsn.vA, r0); + loadConstant(cUnit, r1, (mir->dalvikInsn.vB << 1) + + (int) (cUnit->method->insns + mir->offset)); + genExportPC(cUnit, mir, r2, r3 ); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + genNullCheck(cUnit, r0, mir->offset, NULL); + break; + } + /* + * TODO + * - Add a 1 to 3-entry per-location cache here to completely + * bypass the dvmInterpHandle[Packed/Sparse]Switch call w/ chaining + * - Use out-of-line handlers for both of these + */ + case OP_PACKED_SWITCH: + case OP_SPARSE_SWITCH: { + if (dalvikOpCode == OP_PACKED_SWITCH) { + loadConstant(cUnit, r4PC, (int)dvmInterpHandlePackedSwitch); + } else { + loadConstant(cUnit, r4PC, (int)dvmInterpHandleSparseSwitch); + } + loadValue(cUnit, mir->dalvikInsn.vA, r1); + loadConstant(cUnit, r0, (mir->dalvikInsn.vB << 1) + + (int) (cUnit->method->insns + mir->offset)); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + loadConstant(cUnit, r1, (int)(cUnit->method->insns + mir->offset)); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r2, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNoChain) + >> 2); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r0, r0, r0); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r4PC, r0, r1); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList) +{ + Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id]; + Armv5teLIR *pcrLabel = NULL; + + DecodedInstruction *dInsn = &mir->dalvikInsn; + switch (mir->dalvikInsn.opCode) { + /* + * calleeMethod = this->clazz->vtable[ + * method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex + * ] + */ + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: { + int methodIndex = + cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]-> + methodIndex; + + if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 now contains this->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r0 now contains this->clazz->vtable */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(ClassObject, vtable) >> 2); + addrRetChain->generic.target = (LIR *) retChainingCell; + + if (methodIndex < 32) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, methodIndex); + } else { + loadConstant(cUnit, r7, methodIndex<<2); + newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r7); + } + + /* + * r0 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + break; + } + /* + * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex + * ->pResMethods[BBBB]->methodIndex] + */ + /* TODO - not excersized in RunPerf.jar */ + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: { + int mIndex = cUnit->method->clazz->pDvmDex-> + pResMethods[dInsn->vB]->methodIndex; + const Method *calleeMethod = + cUnit->method->clazz->super->vtable[mIndex]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: { + const Method *calleeMethod = + cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_DIRECT) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: { + const Method *calleeMethod = + cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_STATIC) + genProcessArgsNoRange(cUnit, mir, dInsn, + NULL /* no null check */); + else + genProcessArgsRange(cUnit, mir, dInsn, + NULL /* no null check */); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* + * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz, + * BBBB, method, method->clazz->pDvmDex) + */ + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: { + int methodIndex = dInsn->vB; + + if (mir->dalvikInsn.opCode == OP_INVOKE_INTERFACE) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 now contains this->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + + /* r1 = BBBB */ + loadConstant(cUnit, r1, dInsn->vB); + + /* r2 = method (caller) */ + loadConstant(cUnit, r2, (int) cUnit->method); + + /* r3 = pDvmDex */ + loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex); + + loadConstant(cUnit, r7, + (intptr_t) dvmFindInterfaceMethodInCache); + newLIR1(cUnit, ARMV5TE_BLX_R, r7); + + /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */ + + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + addrRetChain->generic.target = (LIR *) retChainingCell; + /* + * r0 = this, r1 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + break; + } + /* NOP */ + case OP_INVOKE_DIRECT_EMPTY: { + return false; + } + case OP_FILLED_NEW_ARRAY: + case OP_FILLED_NEW_ARRAY_RANGE: { + /* Just let the interpreter deal with these */ + genInterpSingleStep(cUnit, mir); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir, + BasicBlock *bb, Armv5teLIR *labelList) +{ + Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id]; + Armv5teLIR *pcrLabel = NULL; + + DecodedInstruction *dInsn = &mir->dalvikInsn; + switch (mir->dalvikInsn.opCode) { + /* calleeMethod = this->clazz->vtable[BBBB] */ + case OP_INVOKE_VIRTUAL_QUICK_RANGE: + case OP_INVOKE_VIRTUAL_QUICK: { + int methodIndex = dInsn->vB; + if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL_QUICK) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 now contains this->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r0 now contains this->clazz->vtable */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(ClassObject, vtable) >> 2); + addrRetChain->generic.target = (LIR *) retChainingCell; + + if (methodIndex < 32) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, methodIndex); + } else { + loadConstant(cUnit, r7, methodIndex<<2); + newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r7); + } + + /* + * r0 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + break; + } + /* calleeMethod = method->clazz->super->vtable[BBBB] */ + case OP_INVOKE_SUPER_QUICK: + case OP_INVOKE_SUPER_QUICK_RANGE: { + const Method *calleeMethod = + cUnit->method->clazz->super->vtable[dInsn->vB]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER_QUICK) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* calleeMethod = method->clazz->super->vtable[BBBB] */ + default: + return true; + } + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + return false; +} + +/* + * NOTE: We assume here that the special native inline routines + * are side-effect free. By making this assumption, we can safely + * re-execute the routine from the interpreter if it decides it + * wants to throw an exception. We still need to EXPORT_PC(), though. + */ +static bool handleFmt3inline(CompilationUnit *cUnit, MIR *mir) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + switch( mir->dalvikInsn.opCode) { + case OP_EXECUTE_INLINE: { + unsigned int i; + const InlineOperation* inLineTable = dvmGetInlineOpsTable(); + int offset = (int) &((InterpState *) NULL)->retval; + int operation = dInsn->vB; + + if (!strcmp(inLineTable[operation].classDescriptor, + "Ljava/lang/String;") && + !strcmp(inLineTable[operation].methodName, + "length") && + !strcmp(inLineTable[operation].methodSignature, + "()I")) { + return genInlinedStringLength(cUnit,mir); + } + + /* Materialize pointer to retval & push */ + newLIR2(cUnit, ARMV5TE_MOV_RR, r4PC, rGLUE); + newLIR2(cUnit, ARMV5TE_ADD_RI8, r4PC, offset); + /* Push r4 and (just to take up space) r5) */ + newLIR1(cUnit, ARMV5TE_PUSH, (1<<r4PC | 1<<rFP)); + + /* Get code pointer to inline routine */ + loadConstant(cUnit, r4PC, (int)inLineTable[operation].func); + + /* Export PC */ + genExportPC(cUnit, mir, r0, r1 ); + + /* Load arguments to r0 through r3 as applicable */ + for (i=0; i < dInsn->vA; i++) { + loadValue(cUnit, dInsn->arg[i], i); + } + /* Call inline routine */ + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + + /* Strip frame */ + newLIR1(cUnit, ARMV5TE_ADD_SPI7, 2); + + /* Did we throw? If so, redo under interpreter*/ + genNullCheck(cUnit, r0, mir->offset, NULL); + + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir) +{ + loadConstant(cUnit, r0, mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL); + loadConstant(cUnit, r1, (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + return false; +} + +/*****************************************************************************/ +/* + * The following are special processing routines that handle transfer of + * controls between compiled code and the interpreter. Certain VM states like + * Dalvik PC and special-purpose registers are reconstructed here. + */ + +/* Chaining cell for normal-ending compiles (eg branches) */ +static void handleGenericChainingCell(CompilationUnit *cUnit, + unsigned int offset) +{ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r0); + addWordData(cUnit, (int) (cUnit->method->insns + offset), true); +} + +/* + * Chaining cell for instructions that immediately following a method + * invocation. + */ +static void handlePostInvokeChainingCell(CompilationUnit *cUnit, + unsigned int offset) +{ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r0); + addWordData(cUnit, (int) (cUnit->method->insns + offset), true); +} + +/* Chaining cell for monomorphic method invocations. */ +static void handleInvokeChainingCell(CompilationUnit *cUnit, + const Method *callee) +{ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r0); + addWordData(cUnit, (int) (callee->insns), true); +} + +/* Load the Dalvik PC into r0 and jump to the specified target */ +static void handlePCReconstruction(CompilationUnit *cUnit, + Armv5teLIR *targetLabel) +{ + Armv5teLIR **pcrLabel = + (Armv5teLIR **) cUnit->pcReconstructionList.elemList; + int numElems = cUnit->pcReconstructionList.numUsed; + int i; + for (i = 0; i < numElems; i++) { + dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]); + /* r0 = dalvik PC */ + loadConstant(cUnit, r0, pcrLabel[i]->operands[0]); + genUnconditionalBranch(cUnit, targetLabel); + } +} + +/* Entry function to invoke the backend of the JIT compiler */ +void dvmCompilerMIR2LIR(CompilationUnit *cUnit) +{ + /* Used to hold the labels of each block */ + Armv5teLIR *labelList = + dvmCompilerNew(sizeof(Armv5teLIR) * cUnit->numBlocks, true); + GrowableList chainingListByType[CHAINING_CELL_LAST]; + int i; + + /* + * Initialize the three chaining lists for generic, post-invoke, and invoke + * chains. + */ + for (i = 0; i < CHAINING_CELL_LAST; i++) { + dvmInitGrowableList(&chainingListByType[i], 2); + } + + BasicBlock **blockList = cUnit->blockList; + + /* Handle the content in each basic block */ + for (i = 0; i < cUnit->numBlocks; i++) { + blockList[i]->visited = true; + MIR *mir; + + labelList[i].operands[0] = blockList[i]->startOffset; + + if (blockList[i]->blockType >= CHAINING_CELL_LAST) { + /* + * Append the label pseudo LIR first. Chaining cells will be handled + * separately afterwards. + */ + dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]); + } + + if (blockList[i]->blockType == DALVIK_BYTECODE) { + labelList[i].opCode = ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL; + } else { + switch (blockList[i]->blockType) { + case CHAINING_CELL_GENERIC: + labelList[i].opCode = ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_GENERIC], (void *) i); + break; + case CHAINING_CELL_INVOKE: + labelList[i].opCode = ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE; + labelList[i].operands[0] = + (int) blockList[i]->containingMethod; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_INVOKE], (void *) i); + break; + case CHAINING_CELL_POST_INVOKE: + labelList[i].opCode = + ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_POST_INVOKE], + (void *) i); + break; + case PC_RECONSTRUCTION: + /* Make sure exception handling block is next */ + labelList[i].opCode = + ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL; + assert (i == cUnit->numBlocks - 2); + handlePCReconstruction(cUnit, &labelList[i+1]); + break; + case EXCEPTION_HANDLING: + labelList[i].opCode = ARMV5TE_PSEUDO_EH_BLOCK_LABEL; + if (cUnit->pcReconstructionList.numUsed) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, + offsetof(InterpState, + jitToInterpEntries.dvmJitToInterpPunt) + >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r1); + } + break; + default: + break; + } + continue; + } + for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) { + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + InstructionFormat dalvikFormat = + dexGetInstrFormat(gDvm.instrFormat, dalvikOpCode); + newLIR2(cUnit, ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY, + mir->offset,dalvikOpCode); + bool notHandled; + /* + * Debugging: screen the opcode first to see if it is in the + * do[-not]-compile list + */ + bool singleStepMe = + gDvmJit.includeSelectedOp != + ((gDvmJit.opList[dalvikOpCode >> 3] & + (1 << (dalvikOpCode & 0x7))) != + 0); + if (singleStepMe || cUnit->allSingleStep) { + notHandled = false; + genInterpSingleStep(cUnit, mir); + } else { + opcodeCoverage[dalvikOpCode]++; + switch (dalvikFormat) { + case kFmt10t: + case kFmt20t: + case kFmt30t: + notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit, + mir, blockList[i], labelList); + break; + case kFmt10x: + notHandled = handleFmt10x(cUnit, mir); + break; + case kFmt11n: + case kFmt31i: + notHandled = handleFmt11n_Fmt31i(cUnit, mir); + break; + case kFmt11x: + notHandled = handleFmt11x(cUnit, mir); + break; + case kFmt12x: + notHandled = handleFmt12x(cUnit, mir); + break; + case kFmt20bc: + notHandled = handleFmt20bc(cUnit, mir); + break; + case kFmt21c: + case kFmt31c: + notHandled = handleFmt21c_Fmt31c(cUnit, mir); + break; + case kFmt21h: + notHandled = handleFmt21h(cUnit, mir); + break; + case kFmt21s: + notHandled = handleFmt21s(cUnit, mir); + break; + case kFmt21t: + notHandled = handleFmt21t(cUnit, mir, blockList[i], + labelList); + break; + case kFmt22b: + case kFmt22s: + notHandled = handleFmt22b_Fmt22s(cUnit, mir); + break; + case kFmt22c: + notHandled = handleFmt22c(cUnit, mir); + break; + case kFmt22cs: + notHandled = handleFmt22cs(cUnit, mir); + break; + case kFmt22t: + notHandled = handleFmt22t(cUnit, mir, blockList[i], + labelList); + break; + case kFmt22x: + case kFmt32x: + notHandled = handleFmt22x_Fmt32x(cUnit, mir); + break; + case kFmt23x: + notHandled = handleFmt23x(cUnit, mir); + break; + case kFmt31t: + notHandled = handleFmt31t(cUnit, mir); + break; + case kFmt3rc: + case kFmt35c: + notHandled = handleFmt35c_3rc(cUnit, mir, blockList[i], + labelList); + break; + case kFmt3rms: + case kFmt35ms: + notHandled = handleFmt35ms_3rms(cUnit, mir,blockList[i], + labelList); + break; + case kFmt3inline: + notHandled = handleFmt3inline(cUnit, mir); + break; + case kFmt51l: + notHandled = handleFmt51l(cUnit, mir); + break; + default: + notHandled = true; + break; + } + } + if (notHandled) { + LOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled\n", + mir->offset, + dalvikOpCode, getOpcodeName(dalvikOpCode), + dalvikFormat); + dvmAbort(); + break; + } else { + gDvmJit.opHistogram[dalvikOpCode]++; + } + } + } + + /* Handle the codegen in predefined order */ + for (i = 0; i < CHAINING_CELL_LAST; i++) { + size_t j; + int *blockIdList = (int *) chainingListByType[i].elemList; + + cUnit->numChainingCells[i] = chainingListByType[i].numUsed; + + /* No chaining cells of this type */ + if (cUnit->numChainingCells[i] == 0) + continue; + + /* Record the first LIR for a new type of chaining cell */ + cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]]; + + for (j = 0; j < chainingListByType[i].numUsed; j++) { + int blockId = blockIdList[j]; + + /* Align this chaining cell first */ + newLIR0(cUnit, ARMV5TE_PSEUDO_ALIGN4); + + /* Insert the pseudo chaining instruction */ + dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]); + + + switch (blockList[blockId]->blockType) { + case CHAINING_CELL_GENERIC: + handleGenericChainingCell(cUnit, + blockList[blockId]->startOffset); + break; + case CHAINING_CELL_INVOKE: + handleInvokeChainingCell(cUnit, + blockList[blockId]->containingMethod); + break; + case CHAINING_CELL_POST_INVOKE: + handlePostInvokeChainingCell(cUnit, + blockList[blockId]->startOffset); + break; + default: + dvmAbort(); + break; + } + } + } +} + +/* Accept the work and start compiling */ +void *dvmCompilerDoWork(CompilerWorkOrder *work) +{ + void *res; + + if (gDvmJit.codeCacheFull) { + return NULL; + } + + switch (work->kind) { + case kWorkOrderMethod: + res = dvmCompileMethod(work->info); + break; + case kWorkOrderTrace: + res = dvmCompileTrace(work->info); + break; + default: + res = NULL; + dvmAbort(); + } + return res; +} + +/* Architecture-specific initializations and checks go here */ +bool dvmCompilerArchInit(void) +{ + /* First, declare dvmCompiler_TEMPLATE_XXX for each template */ +#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X(); +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + + int i = 0; + extern void dvmCompilerTemplateStart(void); + + /* + * Then, populate the templateEntryOffsets array with the offsets from the + * the dvmCompilerTemplateStart symbol for each template. + */ +#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \ + (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart; +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + + /* Codegen-specific assumptions */ + assert(offsetof(ClassObject, vtable) < 128 && + (offsetof(ClassObject, vtable) & 0x3) == 0); + assert(offsetof(ArrayObject, length) < 128 && + (offsetof(ArrayObject, length) & 0x3) == 0); + assert(offsetof(ArrayObject, contents) < 256); + + /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ + assert(sizeof(StackSaveArea) < 236); + + /* + * EA is calculated by doing "Rn + imm5 << 2", and there are 5 entry points + * that codegen may access, make sure that the offset from the top of the + * struct is less than 108. + */ + assert(offsetof(InterpState, jitToInterpEntries) < 108); + return true; +} + +/* Architectural-specific debugging helpers go here */ +void dvmCompilerArchDump(void) +{ + /* Print compiled opcode in this VM instance */ + int i, start, streak; + char buf[1024]; + + streak = i = 0; + buf[0] = 0; + while (opcodeCoverage[i] == 0 && i < 256) { + i++; + } + if (i == 256) { + return; + } + for (start = i++, streak = 1; i < 256; i++) { + if (opcodeCoverage[i]) { + streak++; + } else { + if (streak == 1) { + sprintf(buf+strlen(buf), "%x,", start); + } else { + sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1); + } + streak = 0; + while (opcodeCoverage[i] == 0 && i < 256) { + i++; + } + if (i < 256) { + streak = 1; + start = i; + } + } + } + if (streak) { + if (streak == 1) { + sprintf(buf+strlen(buf), "%x", start); + } else { + sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1); + } + } + if (strlen(buf)) { + LOGD("dalvik.vm.jitop = %s", buf); + } +} |