diff options
author | Bill Buzbee <buzbee@google.com> | 2009-07-28 11:22:22 -0700 |
---|---|---|
committer | Bill Buzbee <buzbee@google.com> | 2009-07-28 11:22:22 -0700 |
commit | 89efc3d632adfa076bd622369b1ad8e4b49cf20e (patch) | |
tree | 8fef4780be23cea9ae084e060ea0e92478c64311 /vm/compiler/codegen/arm | |
parent | e2557513420f6be2d70c19a4d826731174c828d1 (diff) | |
download | android_dalvik-89efc3d632adfa076bd622369b1ad8e4b49cf20e.tar.gz android_dalvik-89efc3d632adfa076bd622369b1ad8e4b49cf20e.tar.bz2 android_dalvik-89efc3d632adfa076bd622369b1ad8e4b49cf20e.zip |
Stage 2 of structural changes for support of THUMB2. No logic changes.
Diffstat (limited to 'vm/compiler/codegen/arm')
-rw-r--r-- | vm/compiler/codegen/arm/ArchUtility.c | 230 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/ArmLIR.h | 233 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/Assemble.c | 941 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/Codegen-armv5te-vfp.c | 28 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/Codegen-armv5te.c | 28 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/Codegen-armv7-a.c | 28 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/Codegen.c | 3614 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/GlobalOptimizations.c | 62 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/LocalOptimizations.c | 139 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c | 291 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h | 34 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/armv5te/ArchVariant.c | 183 | ||||
-rw-r--r-- | vm/compiler/codegen/arm/armv5te/ArchVariant.h | 34 |
13 files changed, 5845 insertions, 0 deletions
diff --git a/vm/compiler/codegen/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c new file mode 100644 index 000000000..6d1a261bb --- /dev/null +++ b/vm/compiler/codegen/arm/ArchUtility.c @@ -0,0 +1,230 @@ +/* + * 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 "ArmLIR.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, ArmLIR *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) +{ + ArmLIR *lir = (ArmLIR *) 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 ARM_PSEUDO_TARGET_LABEL: + break; + case ARM_PSEUDO_CHAINING_CELL_NORMAL: + LOGD("-------- chaining cell (normal): 0x%04x\n", dest); + break; + case ARM_PSEUDO_CHAINING_CELL_HOT: + LOGD("-------- chaining cell (hot): 0x%04x\n", dest); + break; + case ARM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED: + LOGD("-------- chaining cell (predicted)\n"); + break; + case ARM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON: + LOGD("-------- chaining cell (invoke singleton): %s/%p\n", + ((Method *)dest)->name, + ((Method *)dest)->insns); + break; + case ARM_PSEUDO_DALVIK_BYTECODE_BOUNDARY: + LOGD("-------- dalvik offset: 0x%04x @ %s\n", dest, + getOpcodeName(lir->operands[1])); + break; + case ARM_PSEUDO_ALIGN4: + LOGD("%p (%04x): .align4\n", baseAddr + offset, offset); + break; + case ARM_PSEUDO_PC_RECONSTRUCTION_CELL: + LOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x\n", dest, + lir->operands[1]); + break; + case ARM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL: + /* Do nothing */ + break; + case ARM_PSEUDO_EH_BLOCK_LABEL: + LOGD("Exception_Handling:\n"); + break; + case ARM_PSEUDO_NORMAL_BLOCK_LABEL: + LOGD("L%#06x:\n", dest); + break; + default: + if (lir->isNop) { + break; + } + 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; + ArmLIR *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 = (ArmLIR *) lirInsn; + LOGD("%p (%04x): .word (0x%x)\n", + (char*)cUnit->baseAddr + armLIR->generic.offset, armLIR->generic.offset, + armLIR->operands[0]); + } +} diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h new file mode 100644 index 000000000..81e7346db --- /dev/null +++ b/vm/compiler/codegen/arm/ArmLIR.h @@ -0,0 +1,233 @@ +/* + * 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_ARM_ARMLIR_H +#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_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, + r8 = 8, + r9 = 9, + r10 = 10, + r11 = 11, + r12 = 12, + r13 = 13, + rlr = 14, + rpc = 15 +} NativeRegisterPool; + +/* Mask to convert high reg to low for Thumb */ +#define THUMB_REG_MASK 0x7 + +/* Thumb condition encodings */ +typedef enum ArmConditionCode { + 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 */ +} ArmConditionCode; + +#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 ArmOpCode { + ARM_PSEUDO_TARGET_LABEL = -11, + ARM_PSEUDO_CHAINING_CELL_HOT = -10, + ARM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED = -9, + ARM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON = -8, + ARM_PSEUDO_CHAINING_CELL_NORMAL = -7, + ARM_PSEUDO_DALVIK_BYTECODE_BOUNDARY = -6, + ARM_PSEUDO_ALIGN4 = -5, + ARM_PSEUDO_PC_RECONSTRUCTION_CELL = -4, + ARM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL = -3, + ARM_PSEUDO_EH_BLOCK_LABEL = -2, + ARM_PSEUDO_NORMAL_BLOCK_LABEL = -1, + /************************************************************************/ + ARM_16BIT_DATA, /* DATA [0] rd[15..0] */ + THUMB_ADC, /* adc [0100000101] rm[5..3] rd[2..0] */ + THUMB_ADD_RRI3, /* add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/ + THUMB_ADD_RI8, /* add(2) [00110] rd[10..8] imm_8[7..0] */ + THUMB_ADD_RRR, /* add(3) [0001100] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_ADD_RR_LH, /* add(4) [01000100] H12[01] rm[5..3] rd[2..0] */ + THUMB_ADD_RR_HL, /* add(4) [01001000] H12[10] rm[5..3] rd[2..0] */ + THUMB_ADD_RR_HH, /* add(4) [01001100] H12[11] rm[5..3] rd[2..0] */ + THUMB_ADD_PC_REL, /* add(5) [10100] rd[10..8] imm_8[7..0] */ + THUMB_ADD_SP_REL, /* add(6) [10101] rd[10..8] imm_8[7..0] */ + THUMB_ADD_SPI7, /* add(7) [101100000] imm_7[6..0] */ + THUMB_AND_RR, /* and [0100000000] rm[5..3] rd[2..0] */ + THUMB_ASR, /* asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0] */ + THUMB_ASRV, /* asr(2) [0100000100] rs[5..3] rd[2..0] */ + THUMB_B_COND, /* b(1) [1101] cond[11..8] offset_8[7..0] */ + THUMB_B_UNCOND, /* b(2) [11100] offset_11[10..0] */ + THUMB_BIC, /* bic [0100001110] rm[5..3] rd[2..0] */ + THUMB_BKPT, /* bkpt [10111110] imm_8[7..0] */ + THUMB_BLX_1, /* blx(1) [111] H[10] offset_11[10..0] */ + THUMB_BLX_2, /* blx(1) [111] H[01] offset_11[10..0] */ + THUMB_BL_1, /* blx(1) [111] H[10] offset_11[10..0] */ + THUMB_BL_2, /* blx(1) [111] H[11] offset_11[10..0] */ + THUMB_BLX_R, /* blx(2) [010001111] H2[6..6] rm[5..3] SBZ[000] */ + THUMB_BX, /* bx [010001110] H2[6..6] rm[5..3] SBZ[000] */ + THUMB_CMN, /* cmn [0100001011] rm[5..3] rd[2..0] */ + THUMB_CMP_RI8, /* cmp(1) [00101] rn[10..8] imm_8[7..0] */ + THUMB_CMP_RR, /* cmp(2) [0100001010] rm[5..3] rd[2..0] */ + THUMB_CMP_LH, /* cmp(3) [01000101] H12[01] rm[5..3] rd[2..0] */ + THUMB_CMP_HL, /* cmp(3) [01000110] H12[10] rm[5..3] rd[2..0] */ + THUMB_CMP_HH, /* cmp(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + THUMB_EOR, /* eor [0100000001] rm[5..3] rd[2..0] */ + THUMB_LDMIA, /* ldmia [11001] rn[10..8] reglist [7..0] */ + THUMB_LDR_RRI5, /* ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_LDR_RRR, /* ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDR_PC_REL, /* ldr(3) [01001] rd[10..8] imm_8[7..0] */ + THUMB_LDR_SP_REL, /* ldr(4) [10011] rd[10..8] imm_8[7..0] */ + THUMB_LDRB_RRI5, /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_LDRB_RRR, /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDRH_RRI5, /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_LDRH_RRR, /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDRSB_RRR, /* ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDRSH_RRR, /* ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LSL, /* lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0] */ + THUMB_LSLV, /* lsl(2) [0100000010] rs[5..3] rd[2..0] */ + THUMB_LSR, /* lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0] */ + THUMB_LSRV, /* lsr(2) [0100000011] rs[5..3] rd[2..0] */ + THUMB_MOV_IMM, /* mov(1) [00100] rd[10..8] imm_8[7..0] */ + THUMB_MOV_RR, /* mov(2) [0001110000] rn[5..3] rd[2..0] */ + THUMB_MOV_RR_H2H, /* mov(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + THUMB_MOV_RR_H2L, /* mov(3) [01000110] H12[01] rm[5..3] rd[2..0] */ + THUMB_MOV_RR_L2H, /* mov(3) [01000101] H12[10] rm[5..3] rd[2..0] */ + THUMB_MUL, /* mul [0100001101] rm[5..3] rd[2..0] */ + THUMB_MVN, /* mvn [0100001111] rm[5..3] rd[2..0] */ + THUMB_NEG, /* neg [0100001001] rm[5..3] rd[2..0] */ + THUMB_ORR, /* orr [0100001100] rm[5..3] rd[2..0] */ + THUMB_POP, /* pop [1011110] r[8..8] rl[7..0] */ + THUMB_PUSH, /* push [1011010] r[8..8] rl[7..0] */ + THUMB_ROR, /* ror [0100000111] rs[5..3] rd[2..0] */ + THUMB_SBC, /* sbc [0100000110] rm[5..3] rd[2..0] */ + THUMB_STMIA, /* stmia [11000] rn[10..8] reglist [7.. 0] */ + THUMB_STR_RRI5, /* str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_STR_RRR, /* str(2) [0101000] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_STR_SP_REL, /* str(3) [10010] rd[10..8] imm_8[7..0] */ + THUMB_STRB_RRI5, /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_STRB_RRR, /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_STRH_RRI5, /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_STRH_RRR, /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_SUB_RRI3, /* sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ + THUMB_SUB_RI8, /* sub(2) [00111] rd[10..8] imm_8[7..0] */ + THUMB_SUB_RRR, /* sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_SUB_SPI7, /* sub(4) [101100001] imm_7[6..0] */ + THUMB_SWI, /* swi [11011111] imm_8[7..0] */ + THUMB_TST, /* tst [0100001000] rm[5..3] rn[2..0] */ + ARM_LAST, +} ArmOpCode; + +/* Bit flags describing the behavior of each native opcode */ +typedef enum ArmOpFeatureFlags { + IS_BRANCH = 1 << 1, + CLOBBER_DEST = 1 << 2, + CLOBBER_SRC1 = 1 << 3, + NO_OPERAND = 1 << 4, + IS_UNARY_OP = 1 << 5, + IS_BINARY_OP = 1 << 6, + IS_TERTIARY_OP = 1 << 7, +} ArmOpFeatureFlags; + +/* Struct used to define the snippet positions for each Thumb opcode */ +typedef struct ArmEncodingMap { + short skeleton; + struct { + int end; + int start; + } fieldLoc[3]; + ArmOpCode opCode; + int flags; + char *name; + char* fmt; + int size; +} ArmEncodingMap; + +extern ArmEncodingMap EncodingMap[ARM_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 ArmLIR { + LIR generic; + ArmOpCode opCode; + int operands[3]; // [0..2] = [dest, src1, src2] + bool isNop; // LIR is optimized away + int age; // default is 0, set lazily by the optimizer + int size; // 16-bit unit size (1 for thumb, 1 or 2 for thumb2) +} ArmLIR; + +/* Chain cell for predicted method invocation */ +typedef struct PredictedChainingCell { + u4 branch; /* Branch to chained destination */ + const ClassObject *clazz; /* key #1 for prediction */ + const Method *method; /* key #2 to lookup native PC from dalvik PC */ + u4 counter; /* counter to patch the chaining cell */ +} PredictedChainingCell; + +/* Init values when a predicted chain is initially assembled */ +#define PREDICTED_CHAIN_BX_PAIR_INIT 0 +#define PREDICTED_CHAIN_CLAZZ_INIT 0 +#define PREDICTED_CHAIN_METHOD_INIT 0 +#define PREDICTED_CHAIN_COUNTER_INIT 0 + +/* Used when the callee is not compiled yet */ +#define PREDICTED_CHAIN_COUNTER_DELAY 16 + +/* Rechain after this many mis-predictions have happened */ +#define PREDICTED_CHAIN_COUNTER_RECHAIN 1024 + +/* Used if the resolved callee is a native method */ +#define PREDICTED_CHAIN_COUNTER_AVOID 0x7fffffff + +/* Utility macros to traverse the LIR/ArmLIR list */ +#define NEXT_LIR(lir) ((ArmLIR *) lir->generic.next) +#define PREV_LIR(lir) ((ArmLIR *) lir->generic.prev) + +#define NEXT_LIR_LVALUE(lir) (lir)->generic.next +#define PREV_LIR_LVALUE(lir) (lir)->generic.prev + +#define CHAIN_CELL_OFFSET_TAG 0xcdab + +#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H */ diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c new file mode 100644 index 000000000..f9961cef5 --- /dev/null +++ b/vm/compiler/codegen/arm/Assemble.c @@ -0,0 +1,941 @@ +/* + * 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 "ArmLIR.h" +#include <unistd.h> /* for cacheflush */ + +/* + * opcode: ArmOpCode 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, size) \ + {skeleton, {{ds, de}, {s1s, s1e}, {s2s, s2e}}, opcode, operands, name, \ + fmt, size} + +/* 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 ArmOpcode from ArmLIR.h */ +ArmEncodingMap EncodingMap[ARM_LAST] = { + ENCODING_MAP(ARM_16BIT_DATA, 0x0000, 15, 0, -1, -1, -1, -1, + IS_UNARY_OP, + "data", "0x!0h(!0d)", 1), + ENCODING_MAP(THUMB_ADC, 0x4140, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "adc", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_ADD_RRI3, 0x1c00, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "add", "r!0d, r!1d, #!2d", 1), + ENCODING_MAP(THUMB_ADD_RI8, 0x3000, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "add", "r!0d, r!0d, #!1d", 1), + ENCODING_MAP(THUMB_ADD_RRR, 0x1800, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "add", "r!0d, r!1d, r!2d", 1), + ENCODING_MAP(THUMB_ADD_RR_LH, 0x4440, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "add", + "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_ADD_RR_HL, 0x4480, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "add", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_ADD_RR_HH, 0x44c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "add", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_ADD_PC_REL, 0xa000, 10, 8, 7, 0, -1, -1, + IS_TERTIARY_OP | CLOBBER_DEST, + "add", "r!0d, pc, #!1E", 1), + ENCODING_MAP(THUMB_ADD_SP_REL, 0xa800, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "add", "r!0d, sp, #!1E", 1), + ENCODING_MAP(THUMB_ADD_SPI7, 0xb000, 6, 0, -1, -1, -1, -1, + IS_UNARY_OP | CLOBBER_DEST, + "add", "sp, #!0d*4", 1), + ENCODING_MAP(THUMB_AND_RR, 0x4000, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "and", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_ASR, 0x1000, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "asr", "r!0d, r!1d, #!2d", 1), + ENCODING_MAP(THUMB_ASRV, 0x4100, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "asr", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_B_COND, 0xd000, 7, 0, 11, 8, -1, -1, + IS_BINARY_OP | IS_BRANCH, + "!1c", "!0t", 1), + ENCODING_MAP(THUMB_B_UNCOND, 0xe000, 10, 0, -1, -1, -1, -1, + NO_OPERAND | IS_BRANCH, + "b", "!0t", 1), + ENCODING_MAP(THUMB_BIC, 0x4380, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "bic", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_BKPT, 0xbe00, 7, 0, -1, -1, -1, -1, + IS_UNARY_OP | IS_BRANCH, + "bkpt", "!0d", 1), + ENCODING_MAP(THUMB_BLX_1, 0xf000, 10, 0, -1, -1, -1, -1, + IS_BINARY_OP | IS_BRANCH, + "blx_1", "!0u", 1), + ENCODING_MAP(THUMB_BLX_2, 0xe800, 10, 0, -1, -1, -1, -1, + IS_BINARY_OP | IS_BRANCH, + "blx_2", "!0v", 1), + ENCODING_MAP(THUMB_BL_1, 0xf000, 10, 0, -1, -1, -1, -1, + IS_UNARY_OP | IS_BRANCH, + "bl_1", "!0u", 1), + ENCODING_MAP(THUMB_BL_2, 0xf800, 10, 0, -1, -1, -1, -1, + IS_UNARY_OP | IS_BRANCH, + "bl_2", "!0v", 1), + ENCODING_MAP(THUMB_BLX_R, 0x4780, 6, 3, -1, -1, -1, -1, + IS_UNARY_OP | IS_BRANCH, + "blx", "r!0d", 1), + ENCODING_MAP(THUMB_BX, 0x4700, 6, 3, -1, -1, -1, -1, + IS_UNARY_OP | IS_BRANCH, + "bx", "r!0d", 1), + ENCODING_MAP(THUMB_CMN, 0x42c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP, + "cmn", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_CMP_RI8, 0x2800, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP, + "cmp", "r!0d, #!1d", 1), + ENCODING_MAP(THUMB_CMP_RR, 0x4280, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP, + "cmp", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_CMP_LH, 0x4540, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP, + "cmp", "r!0d, r!1D", 1), + ENCODING_MAP(THUMB_CMP_HL, 0x4580, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP, + "cmp", "r!0D, r!1d", 1), + ENCODING_MAP(THUMB_CMP_HH, 0x45c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP, + "cmp", "r!0D, r!1D", 1), + ENCODING_MAP(THUMB_EOR, 0x4040, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "eor", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_LDMIA, 0xc800, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_DEST | CLOBBER_SRC1, + "ldmia", "r!0d!!, <!1R>", 1), + ENCODING_MAP(THUMB_LDR_RRI5, 0x6800, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldr", "r!0d, [r!1d, #!2E]", 1), + ENCODING_MAP(THUMB_LDR_RRR, 0x5800, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldr", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_LDR_PC_REL, 0x4800, 10, 8, 7, 0, -1, -1, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldr", "r!0d, [pc, #!1E]", 1), + ENCODING_MAP(THUMB_LDR_SP_REL, 0x9800, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "ldr", "r!0d, [sp, #!1E]", 1), + ENCODING_MAP(THUMB_LDRB_RRI5, 0x7800, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldrb", "r!0d, [r!1d, #2d]", 1), + ENCODING_MAP(THUMB_LDRB_RRR, 0x5c00, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldrb", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_LDRH_RRI5, 0x8800, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldrh", "r!0d, [r!1d, #!2F]", 1), + ENCODING_MAP(THUMB_LDRH_RRR, 0x5a00, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldrh", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_LDRSB_RRR, 0x5600, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldrsb", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_LDRSH_RRR, 0x5e00, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "ldrsh", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_LSL, 0x0000, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "lsl", "r!0d, r!1d, #!2d", 1), + ENCODING_MAP(THUMB_LSLV, 0x4080, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "lsl", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_LSR, 0x0800, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "lsr", "r!0d, r!1d, #!2d", 1), + ENCODING_MAP(THUMB_LSRV, 0x40c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "lsr", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_MOV_IMM, 0x2000, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mov", "r!0d, #!1d", 1), + ENCODING_MAP(THUMB_MOV_RR, 0x1c00, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mov", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_MOV_RR_H2H, 0x46c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mov", "r!0D, r!1D", 1), + ENCODING_MAP(THUMB_MOV_RR_H2L, 0x4640, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mov", "r!0d, r!1D", 1), + ENCODING_MAP(THUMB_MOV_RR_L2H, 0x4680, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mov", "r!0D, r!1d", 1), + ENCODING_MAP(THUMB_MUL, 0x4340, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mul", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_MVN, 0x43c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "mvn", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_NEG, 0x4240, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "neg", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_ORR, 0x4300, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "orr", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_POP, 0xbc00, 8, 0, -1, -1, -1, -1, + IS_UNARY_OP, + "pop", "<!0R>", 1), + ENCODING_MAP(THUMB_PUSH, 0xb400, 8, 0, -1, -1, -1, -1, + IS_UNARY_OP, + "push", "<!0R>", 1), + ENCODING_MAP(THUMB_ROR, 0x41c0, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "ror", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_SBC, 0x4180, 2, 0, 5, 3, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "sbc", "r!0d, r!1d", 1), + ENCODING_MAP(THUMB_STMIA, 0xc000, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_SRC1, + "stmia", "r!0d!!, <!1R>", 1), + ENCODING_MAP(THUMB_STR_RRI5, 0x6000, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP, + "str", "r!0d, [r!1d, #!2E]", 1), + ENCODING_MAP(THUMB_STR_RRR, 0x5000, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP, + "str", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_STR_SP_REL, 0x9000, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP, + "str", "r!0d, [sp, #!1E]", 1), + ENCODING_MAP(THUMB_STRB_RRI5, 0x7000, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP, + "strb", "r!0d, [r!1d, #!2d]", 1), + ENCODING_MAP(THUMB_STRB_RRR, 0x5400, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP, + "strb", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_STRH_RRI5, 0x8000, 2, 0, 5, 3, 10, 6, + IS_TERTIARY_OP, + "strh", "r!0d, [r!1d, #!2F]", 1), + ENCODING_MAP(THUMB_STRH_RRR, 0x5200, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP, + "strh", "r!0d, [r!1d, r!2d]", 1), + ENCODING_MAP(THUMB_SUB_RRI3, 0x1e00, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "sub", "r!0d, r!1d, #!2d]", 1), + ENCODING_MAP(THUMB_SUB_RI8, 0x3800, 10, 8, 7, 0, -1, -1, + IS_BINARY_OP | CLOBBER_DEST, + "sub", "r!0d, #!1d", 1), + ENCODING_MAP(THUMB_SUB_RRR, 0x1a00, 2, 0, 5, 3, 8, 6, + IS_TERTIARY_OP | CLOBBER_DEST, + "sub", "r!0d, r!1d, r!2d", 1), + ENCODING_MAP(THUMB_SUB_SPI7, 0xb080, 6, 0, -1, -1, -1, -1, + IS_UNARY_OP | CLOBBER_DEST, + "sub", "sp, #!0d", 1), + ENCODING_MAP(THUMB_SWI, 0xdf00, 7, 0, -1, -1, -1, -1, + IS_UNARY_OP | IS_BRANCH, + "swi", "!0d", 1), + ENCODING_MAP(THUMB_TST, 0x4200, 2, 0, 5, 3, -1, -1, + IS_UNARY_OP, + "tst", "r!0d, r!1d", 1), +}; + +#define PADDING_MOV_R0_R0 0x1C00 + +/* Write the numbers in the literal pool to the codegen stream */ +static void installDataContent(CompilationUnit *cUnit) +{ + int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset); + ArmLIR *dataLIR = (ArmLIR *) cUnit->wordList; + while (dataLIR) { + *dataPtr++ = dataLIR->operands[0]; + dataLIR = NEXT_LIR(dataLIR); + } +} + +/* Returns the size of a Jit trace description */ +static int jitTraceDescriptionSize(const JitTraceDescription *desc) +{ + int runCount; + for (runCount = 0; ; runCount++) { + if (desc->trace[runCount].frag.runEnd) + break; + } + return sizeof(JitCodeDesc) + ((runCount+1) * sizeof(JitTraceRun)); +} + +/* Return TRUE if error happens */ +static bool assembleInstructions(CompilationUnit *cUnit, intptr_t startAddr) +{ + short *bufferAddr = (short *) cUnit->codeBuffer; + ArmLIR *lir; + + for (lir = (ArmLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) { + if (lir->opCode < 0) { + if ((lir->opCode == ARM_PSEUDO_ALIGN4) && + /* 1 means padding is needed */ + (lir->operands[0] == 1)) { + *bufferAddr++ = PADDING_MOV_R0_R0; + } + continue; + } + + if (lir->isNop) { + continue; + } + + if (lir->opCode == THUMB_LDR_PC_REL || + lir->opCode == THUMB_ADD_PC_REL) { + ArmLIR *lirTarget = (ArmLIR *) lir->generic.target; + intptr_t pc = (lir->generic.offset + 4) & ~3; + /* + * Allow an offset (stored in operands[2] to be added to the + * PC-relative target. Useful to get to a fixed field inside a + * chaining cell. + */ + intptr_t target = lirTarget->generic.offset + lir->operands[2]; + int delta = target - pc; + if (delta & 0x3) { + LOGE("PC-rel distance is not multiples of 4: %d\n", delta); + dvmAbort(); + } + if (delta > 1023) { + return true; + } + lir->operands[1] = delta >> 2; + } else if (lir->opCode == THUMB_B_COND) { + ArmLIR *targetLIR = (ArmLIR *) 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) { + return true; + } + lir->operands[0] = delta >> 1; + } else if (lir->opCode == THUMB_B_UNCOND) { + ArmLIR *targetLIR = (ArmLIR *) 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 == THUMB_BLX_1) { + assert(NEXT_LIR(lir)->opCode == THUMB_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; + } + + ArmEncodingMap *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 false; +} + +/* + * Translation layout in the code cache. Note that the codeAddress pointer + * in JitTable will point directly to the code body (field codeAddress). The + * chain cell offset codeAddress - 2, and (if present) executionCount is at + * codeAddress - 6. + * + * +----------------------------+ + * | Execution count | -> [Optional] 4 bytes + * +----------------------------+ + * +--| Offset to chain cell counts| -> 2 bytes + * | +----------------------------+ + * | | Code body | -> Start address for translation + * | | | variable in 2-byte chunks + * | . . (JitTable's codeAddress points here) + * | . . + * | | | + * | +----------------------------+ + * | | Chaining Cells | -> 8 bytes each, must be 4 byte aligned + * | . . + * | . . + * | | | + * | +----------------------------+ + * +->| Chaining cell counts | -> 4 bytes, chain cell counts by type + * +----------------------------+ + * | Trace description | -> variable sized + * . . + * | | + * +----------------------------+ + * | Literal pool | -> 4-byte aligned, variable size + * . . + * . . + * | | + * +----------------------------+ + * + * 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, JitTranslationInfo *info) +{ + LIR *lir; + ArmLIR *armLIR; + int offset = 0; + int i; + ChainCellCounts chainCellCounts; + int descSize = jitTraceDescriptionSize(cUnit->traceDesc); + + info->codeAddress = NULL; + info->instructionSet = cUnit->instructionSet; + + /* Beginning offset needs to allow space for chain cell offset */ + for (armLIR = (ArmLIR *) cUnit->firstLIRInsn; + armLIR; + armLIR = NEXT_LIR(armLIR)) { + armLIR->generic.offset = offset; + if (armLIR->opCode >= 0 && !armLIR->isNop) { + offset += 2; + } else if (armLIR->opCode == ARM_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) & ~3; + + /* Add space for chain cell counts & trace description */ + u4 chainCellOffset = offset; + ArmLIR *chainCellOffsetLIR = (ArmLIR *) cUnit->chainCellOffsetLIR; + assert(chainCellOffsetLIR); + assert(chainCellOffset < 0x10000); + assert(chainCellOffsetLIR->opCode == ARM_16BIT_DATA && + chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG); + + /* + * Replace the CHAIN_CELL_OFFSET_TAG with the real value. If trace + * profiling is enabled, subtract 4 (occupied by the counter word) from + * the absolute offset as the value stored in chainCellOffsetLIR is the + * delta from &chainCellOffsetLIR to &ChainCellCounts. + */ + chainCellOffsetLIR->operands[0] = + gDvmJit.profile ? (chainCellOffset - 4) : chainCellOffset; + + offset += sizeof(chainCellCounts) + descSize; + + assert((offset & 0x3) == 0); /* Should still be word aligned */ + + /* Set up offsets for literals */ + cUnit->dataOffset = offset; + + for (lir = cUnit->wordList; lir; lir = lir->next) { + lir->offset = offset; + offset += 4; + } + + cUnit->totalSize = offset; + + if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > CODE_CACHE_SIZE) { + gDvmJit.codeCacheFull = true; + cUnit->baseAddr = NULL; + return; + } + + /* Allocate enough space for the code block */ + cUnit->codeBuffer = dvmCompilerNew(chainCellOffset, true); + if (cUnit->codeBuffer == NULL) { + LOGE("Code buffer allocation failure\n"); + cUnit->baseAddr = NULL; + return; + } + + bool assemblerFailure = assembleInstructions( + cUnit, (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed); + + /* + * Currently the only reason that can cause the assembler to fail is due to + * trace length - cut it in half and retry. + */ + if (assemblerFailure) { + cUnit->halveInstCount = true; + return; + } + + + cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed; + gDvmJit.codeCacheByteUsed += offset; + + /* Install the code block */ + memcpy((char*)cUnit->baseAddr, cUnit->codeBuffer, chainCellOffset); + gDvmJit.numCompilations++; + + /* Install the chaining cell counts */ + for (i=0; i< CHAINING_CELL_LAST; i++) { + chainCellCounts.u.count[i] = cUnit->numChainingCells[i]; + } + memcpy((char*)cUnit->baseAddr + chainCellOffset, &chainCellCounts, + sizeof(chainCellCounts)); + + /* Install the trace description */ + memcpy((char*)cUnit->baseAddr + chainCellOffset + sizeof(chainCellCounts), + cUnit->traceDesc, descSize); + + /* Write the literals directly into the code cache */ + installDataContent(cUnit); + + /* Flush dcache and invalidate the icache to maintain coherence */ + cacheflush((long)cUnit->baseAddr, + (long)((char *) cUnit->baseAddr + offset), 0); + + /* Record code entry point and instruction set */ + info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize; + info->instructionSet = cUnit->instructionSet; + /* If applicable, mark low bit to denote thumb */ + if (info->instructionSet != DALVIK_JIT_ARM) + info->codeAddress = (char*)info->codeAddress + 1; +} + +static u4 assembleBXPair(int branchOffset) +{ + u4 thumb1, thumb2; + + 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 */ + } + + return thumb2<<16 | thumb1; +} + +/* + * 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. + * If the target is nearby, use a single-instruction bl. + * If one or more threads is suspended, don't chain. + */ +void* dvmJitChain(void* tgtAddr, u4* branchAddr) +{ + int baseAddr = (u4) branchAddr + 4; + int branchOffset = (int) tgtAddr - baseAddr; + u4 newInst; + + if (gDvm.sumThreadSuspendCount == 0) { + 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)); + + newInst = assembleBXPair(branchOffset); + + *branchAddr = newInst; + cacheflush((long)branchAddr, (long)branchAddr + 4, 0); + } + + return tgtAddr; +} + +/* + * This method is called from the invoke templates for virtual and interface + * methods to speculatively setup a chain to the callee. The templates are + * written in assembly and have setup method, cell, and clazz at r0, r2, and + * r3 respectively, so there is a unused argument in the list. Upon return one + * of the following three results may happen: + * 1) Chain is not setup because the callee is native. Reset the rechain + * count to a big number so that it will take a long time before the next + * rechain attempt to happen. + * 2) Chain is not setup because the callee has not been created yet. Reset + * the rechain count to a small number and retry in the near future. + * 3) Ask all other threads to stop before patching this chaining cell. + * This is required because another thread may have passed the class check + * but hasn't reached the chaining cell yet to follow the chain. If we + * patch the content before halting the other thread, there could be a + * small window for race conditions to happen that it may follow the new + * but wrong chain to invoke a different method. + */ +const Method *dvmJitToPatchPredictedChain(const Method *method, + void *unused, + PredictedChainingCell *cell, + const ClassObject *clazz) +{ + /* Don't come back here for a long time if the method is native */ + if (dvmIsNativeMethod(method)) { + cell->counter = PREDICTED_CHAIN_COUNTER_AVOID; + cacheflush((long) cell, (long) (cell+1), 0); + COMPILER_TRACE_CHAINING( + LOGD("Jit Runtime: predicted chain %p to native method %s ignored", + cell, method->name)); + goto done; + } + int tgtAddr = (int) dvmJitGetCodeAddr(method->insns); + + /* + * Compilation not made yet for the callee. Reset the counter to a small + * value and come back to check soon. + */ + if (tgtAddr == 0) { + /* + * Wait for a few invocations (currently set to be 16) before trying + * to setup the chain again. + */ + cell->counter = PREDICTED_CHAIN_COUNTER_DELAY; + cacheflush((long) cell, (long) (cell+1), 0); + COMPILER_TRACE_CHAINING( + LOGD("Jit Runtime: predicted chain %p to method %s delayed", + cell, method->name)); + goto done; + } + + /* Stop the world */ + dvmSuspendAllThreads(SUSPEND_FOR_JIT); + + int baseAddr = (int) cell + 4; // PC is cur_addr + 4 + int branchOffset = tgtAddr - baseAddr; + + COMPILER_TRACE_CHAINING( + LOGD("Jit Runtime: predicted chain %p from %s to %s (%s) patched", + cell, cell->clazz ? cell->clazz->descriptor : "NULL", + clazz->descriptor, + method->name)); + + cell->branch = assembleBXPair(branchOffset); + cell->clazz = clazz; + cell->method = method; + cell->counter = PREDICTED_CHAIN_COUNTER_RECHAIN; + + cacheflush((long) cell, (long) (cell+1), 0); + + /* All done - resume all other threads */ + dvmResumeAllThreads(SUSPEND_FOR_JIT); + +done: + return method; +} + +/* + * Unchain a trace given the starting address of the translation + * in the code cache. Refer to the diagram in dvmCompilerAssembleLIR. + * Returns the address following the last cell unchained. Note that + * the incoming codeAddr is a thumb code address, and therefore has + * the low bit set. + */ +u4* dvmJitUnchain(void* codeAddr) +{ + u2* pChainCellOffset = (u2*)((char*)codeAddr - 3); + u2 chainCellOffset = *pChainCellOffset; + ChainCellCounts *pChainCellCounts = + (ChainCellCounts*)((char*)codeAddr + chainCellOffset - 3); + int cellSize; + u4* pChainCells; + u4* pStart; + u4 thumb1; + u4 thumb2; + u4 newInst; + int i,j; + PredictedChainingCell *predChainCell; + + /* Get total count of chain cells */ + for (i = 0, cellSize = 0; i < CHAINING_CELL_LAST; i++) { + if (i != CHAINING_CELL_INVOKE_PREDICTED) { + cellSize += pChainCellCounts->u.count[i] * 2; + } else { + cellSize += pChainCellCounts->u.count[i] * 4; + } + } + + /* Locate the beginning of the chain cell region */ + pStart = pChainCells = ((u4 *) pChainCellCounts) - cellSize; + + /* The cells are sorted in order - walk through them and reset */ + for (i = 0; i < CHAINING_CELL_LAST; i++) { + int elemSize = 2; /* Most chaining cell has two words */ + if (i == CHAINING_CELL_INVOKE_PREDICTED) { + elemSize = 4; + } + + for (j = 0; j < pChainCellCounts->u.count[i]; j++) { + int targetOffset; + switch(i) { + case CHAINING_CELL_NORMAL: + targetOffset = offsetof(InterpState, + jitToInterpEntries.dvmJitToInterpNormal); + break; + case CHAINING_CELL_HOT: + case CHAINING_CELL_INVOKE_SINGLETON: + targetOffset = offsetof(InterpState, + jitToInterpEntries.dvmJitToTraceSelect); + break; + case CHAINING_CELL_INVOKE_PREDICTED: + targetOffset = 0; + predChainCell = (PredictedChainingCell *) pChainCells; + /* Reset the cell to the init state */ + predChainCell->branch = PREDICTED_CHAIN_BX_PAIR_INIT; + predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT; + predChainCell->method = PREDICTED_CHAIN_METHOD_INIT; + predChainCell->counter = PREDICTED_CHAIN_COUNTER_INIT; + break; + default: + dvmAbort(); + } + COMPILER_TRACE_CHAINING( + LOGD("Jit Runtime: unchaining 0x%x", (int)pChainCells)); + /* + * Thumb code sequence for a chaining cell is: + * ldr r0, rGLUE, #<word offset> + * blx r0 + */ + if (i != CHAINING_CELL_INVOKE_PREDICTED) { + targetOffset = targetOffset >> 2; /* convert to word offset */ + thumb1 = 0x6800 | (targetOffset << 6) | + (rGLUE << 3) | (r0 << 0); + thumb2 = 0x4780 | (r0 << 3); + newInst = thumb2<<16 | thumb1; + *pChainCells = newInst; + } + pChainCells += elemSize; /* Advance by a fixed number of words */ + } + } + return pChainCells; +} + +/* Unchain all translation in the cache. */ +void dvmJitUnchainAll() +{ + u4* lowAddress = NULL; + u4* highAddress = NULL; + unsigned int i; + if (gDvmJit.pJitEntryTable != NULL) { + COMPILER_TRACE_CHAINING(LOGD("Jit Runtime: unchaining all")); + dvmLockMutex(&gDvmJit.tableLock); + for (i = 0; i < gDvmJit.jitTableSize; i++) { + if (gDvmJit.pJitEntryTable[i].dPC && + gDvmJit.pJitEntryTable[i].codeAddress) { + u4* lastAddress; + lastAddress = + dvmJitUnchain(gDvmJit.pJitEntryTable[i].codeAddress); + if (lowAddress == NULL || + (u4*)gDvmJit.pJitEntryTable[i].codeAddress < lowAddress) + lowAddress = lastAddress; + if (lastAddress > highAddress) + highAddress = lastAddress; + } + } + cacheflush((long)lowAddress, (long)highAddress, 0); + dvmUnlockMutex(&gDvmJit.tableLock); + } +} + +typedef struct jitProfileAddrToLine { + u4 lineNum; + u4 bytecodeOffset; +} jitProfileAddrToLine; + + +/* Callback function to track the bytecode offset/line number relationiship */ +static int addrToLineCb (void *cnxt, u4 bytecodeOffset, u4 lineNum) +{ + jitProfileAddrToLine *addrToLine = (jitProfileAddrToLine *) cnxt; + + /* Best match so far for this offset */ + if (addrToLine->bytecodeOffset >= bytecodeOffset) { + addrToLine->lineNum = lineNum; + } + return 0; +} + +char *getTraceBase(const JitEntry *p) +{ + return (char*)p->codeAddress - + (6 + (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1)); +} + +/* Dumps profile info for a single trace */ +static int dumpTraceProfile(JitEntry *p) +{ + ChainCellCounts* pCellCounts; + char* traceBase; + u4* pExecutionCount; + u2* pCellOffset; + JitTraceDescription *desc; + const Method* method; + + traceBase = getTraceBase(p); + + if (p->codeAddress == NULL) { + LOGD("TRACEPROFILE 0x%08x 0 NULL 0 0", (int)traceBase); + return 0; + } + + pExecutionCount = (u4*) (traceBase); + pCellOffset = (u2*) (traceBase + 4); + pCellCounts = (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset); + desc = (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts)); + method = desc->method; + char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype); + jitProfileAddrToLine addrToLine = {0, desc->trace[0].frag.startOffset}; + + /* + * We may end up decoding the debug information for the same method + * multiple times, but the tradeoff is we don't need to allocate extra + * space to store the addr/line mapping. Since this is a debugging feature + * and done infrequently so the slower but simpler mechanism should work + * just fine. + */ + dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, + dvmGetMethodCode(method), + method->clazz->descriptor, + method->prototype.protoIdx, + method->accessFlags, + addrToLineCb, NULL, &addrToLine); + + LOGD("TRACEPROFILE 0x%08x % 10d [%#x(+%d), %d] %s%s;%s", + (int)traceBase, + *pExecutionCount, + desc->trace[0].frag.startOffset, + desc->trace[0].frag.numInsts, + addrToLine.lineNum, + method->clazz->descriptor, method->name, methodDesc); + free(methodDesc); + + return *pExecutionCount; +} + +/* Handy function to retrieve the profile count */ +static inline int getProfileCount(const JitEntry *entry) +{ + if (entry->dPC == 0 || entry->codeAddress == 0) + return 0; + u4 *pExecutionCount = (u4 *) getTraceBase(entry); + + return *pExecutionCount; +} + + +/* qsort callback function */ +static int sortTraceProfileCount(const void *entry1, const void *entry2) +{ + const JitEntry *jitEntry1 = entry1; + const JitEntry *jitEntry2 = entry2; + + int count1 = getProfileCount(jitEntry1); + int count2 = getProfileCount(jitEntry2); + return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1); +} + +/* Sort the trace profile counts and dump them */ +void dvmCompilerSortAndPrintTraceProfiles() +{ + JitEntry *sortedEntries; + int numTraces = 0; + unsigned long counts = 0; + unsigned int i; + + /* Make sure that the table is not changing */ + dvmLockMutex(&gDvmJit.tableLock); + + /* Sort the entries by descending order */ + sortedEntries = malloc(sizeof(JitEntry) * gDvmJit.jitTableSize); + if (sortedEntries == NULL) + goto done; + memcpy(sortedEntries, gDvmJit.pJitEntryTable, + sizeof(JitEntry) * gDvmJit.jitTableSize); + qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry), + sortTraceProfileCount); + + /* Dump the sorted entries */ + for (i=0; i < gDvmJit.jitTableSize; i++) { + if (sortedEntries[i].dPC != 0) { + counts += dumpTraceProfile(&sortedEntries[i]); + numTraces++; + } + } + if (numTraces == 0) + numTraces = 1; + LOGD("JIT: Average execution count -> %d",(int)(counts / numTraces)); + + free(sortedEntries); +done: + dvmUnlockMutex(&gDvmJit.tableLock); + return; +} diff --git a/vm/compiler/codegen/arm/Codegen-armv5te-vfp.c b/vm/compiler/codegen/arm/Codegen-armv5te-vfp.c new file mode 100644 index 000000000..bbe7541e1 --- /dev/null +++ b/vm/compiler/codegen/arm/Codegen-armv5te-vfp.c @@ -0,0 +1,28 @@ +/* + * 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 "ArmLIR.h" +#include "vm/mterp/common/FindInterface.h" + +#include "armv5te-vfp/ArchVariant.h" + +#include "Codegen.c" +#include "armv5te-vfp/ArchVariant.c" diff --git a/vm/compiler/codegen/arm/Codegen-armv5te.c b/vm/compiler/codegen/arm/Codegen-armv5te.c new file mode 100644 index 000000000..ba94d239d --- /dev/null +++ b/vm/compiler/codegen/arm/Codegen-armv5te.c @@ -0,0 +1,28 @@ +/* + * 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 "ArmLIR.h" +#include "vm/mterp/common/FindInterface.h" + +#include "armv5te/ArchVariant.h" + +#include "Codegen.c" +#include "armv5te/ArchVariant.c" diff --git a/vm/compiler/codegen/arm/Codegen-armv7-a.c b/vm/compiler/codegen/arm/Codegen-armv7-a.c new file mode 100644 index 000000000..bbe7541e1 --- /dev/null +++ b/vm/compiler/codegen/arm/Codegen-armv7-a.c @@ -0,0 +1,28 @@ +/* + * 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 "ArmLIR.h" +#include "vm/mterp/common/FindInterface.h" + +#include "armv5te-vfp/ArchVariant.h" + +#include "Codegen.c" +#include "armv5te-vfp/ArchVariant.c" diff --git a/vm/compiler/codegen/arm/Codegen.c b/vm/compiler/codegen/arm/Codegen.c new file mode 100644 index 000000000..16779d9dc --- /dev/null +++ b/vm/compiler/codegen/arm/Codegen.c @@ -0,0 +1,3614 @@ +/* + * 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. + */ + +/* + * This file contains codegen and support common to all supported + * ARM variants. It is included by: + * + * Codegen-$(TARGET_ARCH_VARIANT).c + * + * which combines this common code with specific support found in the + * applicable directory below this one. + */ + +/* Routines which must be supplied by the variant-specific code */ +static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode); +bool dvmCompilerArchInit(void); +static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir); +static bool genInlineCos(CompilationUnit *cUnit, MIR *mir); +static bool genInlineSin(CompilationUnit *cUnit, MIR *mir); +static bool genConversion(CompilationUnit *cUnit, MIR *mir); +static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2); +static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2); +static bool genCmpX(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1, + int vSrc2); + +/* 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]; + +/* non-existent register */ +#define vNone (-1) + +/* get the next register in r0..r3 in a round-robin fashion */ +#define NEXT_REG(reg) ((reg + 1) & 3) + +/*****************************************************************************/ + +/* + * The following are building blocks to construct low-level IRs with 0 - 3 + * operands. + */ +static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpCode opCode) +{ + ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpCode(opCode) || (EncodingMap[opCode].flags & NO_OPERAND)); + insn->opCode = opCode; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR1(CompilationUnit *cUnit, ArmOpCode opCode, + int dest) +{ + ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpCode(opCode) || (EncodingMap[opCode].flags & IS_UNARY_OP)); + insn->opCode = opCode; + insn->operands[0] = dest; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR2(CompilationUnit *cUnit, ArmOpCode opCode, + int dest, int src1) +{ + ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpCode(opCode) || + (EncodingMap[opCode].flags & IS_BINARY_OP)); + insn->opCode = opCode; + insn->operands[0] = dest; + insn->operands[1] = src1; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR3(CompilationUnit *cUnit, ArmOpCode opCode, + int dest, int src1, int src2) +{ + ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpCode(opCode) || + (EncodingMap[opCode].flags & IS_TERTIARY_OP)); + insn->opCode = opCode; + insn->operands[0] = dest; + insn->operands[1] = src1; + insn->operands[2] = src2; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR23(CompilationUnit *cUnit, ArmOpCode opCode, + int srcdest, int src2) +{ + assert(!isPseudoOpCode(opCode)); + if (EncodingMap[opCode].flags & IS_BINARY_OP) + return newLIR2(cUnit, opCode, srcdest, src2); + else + return newLIR3(cUnit, opCode, srcdest, srcdest, src2); +} + +/*****************************************************************************/ + +/* + * The following are utility routines to help maintain the RegisterScoreboard + * state to facilitate register renaming. + */ + +/* Reset the tracker to unknown state */ +static inline void resetRegisterScoreboard(CompilationUnit *cUnit) +{ + RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard; + + dvmClearAllBits(registerScoreboard->nullCheckedRegs); + registerScoreboard->liveDalvikReg = vNone; + registerScoreboard->nativeReg = vNone; + registerScoreboard->nativeRegHi = vNone; +} + +/* Kill the corresponding bit in the null-checked register list */ +static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg) +{ + dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg); +} + +/* The Dalvik register pair held in native registers have changed */ +static inline void updateLiveRegisterPair(CompilationUnit *cUnit, + int vReg, int mRegLo, int mRegHi) +{ + cUnit->registerScoreboard.liveDalvikReg = vReg; + cUnit->registerScoreboard.nativeReg = mRegLo; + cUnit->registerScoreboard.nativeRegHi = mRegHi; + cUnit->registerScoreboard.isWide = true; +} + +/* The Dalvik register held in a native register has changed */ +static inline void updateLiveRegister(CompilationUnit *cUnit, + int vReg, int mReg) +{ + cUnit->registerScoreboard.liveDalvikReg = vReg; + cUnit->registerScoreboard.nativeReg = mReg; + cUnit->registerScoreboard.isWide = false; +} + +/* + * Given a Dalvik register id vSrc, use a very simple algorithm to increase + * the lifetime of cached Dalvik value in a native register. + */ +static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc, + bool isWide) +{ + RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard; + + /* No live value - suggest to use r0 */ + if (registerScoreboard->liveDalvikReg == vNone) + return r0; + + /* Reuse the previously used native reg */ + if (registerScoreboard->liveDalvikReg == vSrc) { + if (isWide != true) { + return registerScoreboard->nativeReg; + } else { + /* Return either r0 or r2 */ + return (registerScoreboard->nativeReg + 1) & 2; + } + } + + /* No reuse - choose the next one among r0..r3 in the round-robin fashion */ + if (isWide) { + return (registerScoreboard->nativeReg + 2) & 2; + } else { + return (registerScoreboard->nativeReg + 1) & 3; + } + +} +/*****************************************************************************/ + +/* + * 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 ArmLIR *addWordData(CompilationUnit *cUnit, int value, bool inPlace) +{ + /* Add the constant to the literal pool */ + if (!inPlace) { + ArmLIR *newValue = dvmCompilerNew(sizeof(ArmLIR), 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, ARM_16BIT_DATA, (value & 0xffff)); + newLIR1(cUnit, ARM_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 ArmLIR *scanLiteralPool(CompilationUnit *cUnit, int value, + unsigned int delta) +{ + LIR *dataTarget = cUnit->wordList; + while (dataTarget) { + if (((unsigned) (value - ((ArmLIR *) dataTarget)->operands[0])) <= + delta) + return (ArmLIR *) 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, THUMB_MOV_IMM, rDest, value); + return; + } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) { + newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value); + newLIR2(cUnit, THUMB_MVN, rDest, rDest); + return; + } + /* No shortcut - go ahead and use literal pool */ + ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255); + if (dataTarget == NULL) { + dataTarget = addWordData(cUnit, value, false); + } + ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true); + loadPcRel->opCode = THUMB_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, THUMB_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, THUMB_MOV_RR, rAddr, rFP); + newLIR2(cUnit, THUMB_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset); + newLIR3(cUnit, THUMB_STR_RRI5, rDPC, rAddr, 0); +} + +/* Generate conditional branch instructions */ +static void genConditionalBranch(CompilationUnit *cUnit, + ArmConditionCode cond, + ArmLIR *target) +{ + ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond); + branch->generic.target = (LIR *) target; +} + +/* Generate unconditional branch instructions */ +static void genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target) +{ + ArmLIR *branch = newLIR0(cUnit, THUMB_B_UNCOND); + branch->generic.target = (LIR *) target; +} + +/* Perform the actual operation for OP_RETURN_* */ +static void genReturnCommon(CompilationUnit *cUnit, MIR *mir) +{ + genDispatchToHandler(cUnit, TEMPLATE_RETURN); +#if defined(INVOKE_STATS) + gDvmJit.returnOp++; +#endif + int dPC = (int) (cUnit->method->insns + mir->offset); + ArmLIR *branch = newLIR0(cUnit, THUMB_B_UNCOND); + /* Set up the place holder to reconstruct this Dalvik PC */ + ArmLIR *pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); + pcrLabel->opCode = ARM_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, THUMB_LDR_RRI5, rDestLo, rFP, vSrc); + newLIR3(cUnit, THUMB_LDR_RRI5, rDestHi, rFP, vSrc+1); + } else { + if (vSrc <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, THUMB_ADD_RRI3, rDestLo, rFP, 4); + newLIR2(cUnit, THUMB_ADD_RI8, rDestLo, (vSrc-1)*4); + } else { + /* Offset too far from rFP */ + loadConstant(cUnit, rDestLo, vSrc*4); + newLIR3(cUnit, THUMB_ADD_RRR, rDestLo, rFP, rDestLo); + } + assert(rDestLo < rDestHi); + newLIR2(cUnit, THUMB_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) +{ + killNullCheckedRegister(cUnit, vDest); + killNullCheckedRegister(cUnit, vDest+1); + updateLiveRegisterPair(cUnit, vDest, rSrcLo, rSrcHi); + + /* Use reg + imm5*4 to store the values if possible */ + if (vDest <= 30) { + newLIR3(cUnit, THUMB_STR_RRI5, rSrcLo, rFP, vDest); + newLIR3(cUnit, THUMB_STR_RRI5, rSrcHi, rFP, vDest+1); + } else { + if (vDest <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, THUMB_ADD_RRI3, rScratch, rFP, 4); + newLIR2(cUnit, THUMB_ADD_RI8, rScratch, (vDest-1)*4); + } else { + /* Offset too far from rFP */ + loadConstant(cUnit, rScratch, vDest*4); + newLIR3(cUnit, THUMB_ADD_RRR, rScratch, rFP, rScratch); + } + assert(rSrcLo < rSrcHi); + newLIR2(cUnit, THUMB_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, THUMB_ADD_RRI3, rDest, rFP, vSrc*4); + } else if (vSrc <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, THUMB_ADD_RRI3, rDest, rFP, 4); + newLIR2(cUnit, THUMB_ADD_RI8, rDest, (vSrc-1)*4); + } else { + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, THUMB_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, THUMB_LDR_RRI5, rDest, rFP, vSrc); + } else { + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, THUMB_LDR_RRR, rDest, rFP, rDest); + } +} + +/* Load a word at base + displacement. Displacement must be word multiple */ +static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement, + int rDest) +{ + assert((displacement & 0x3) == 0); + /* Can it fit in a RRI5? */ + if (displacement < 128) { + newLIR3(cUnit, THUMB_LDR_RRI5, rDest, rBase, displacement >> 2); + } else { + loadConstant(cUnit, rDest, displacement); + newLIR3(cUnit, THUMB_LDR_RRR, rDest, rBase, rDest); + } +} + +/* Store a value from rSrc to vDest */ +static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest, + int rScratch) +{ + killNullCheckedRegister(cUnit, vDest); + updateLiveRegister(cUnit, vDest, rSrc); + + /* Use reg + imm5*4 to store the value if possible */ + if (vDest <= 31) { + newLIR3(cUnit, THUMB_STR_RRI5, rSrc, rFP, vDest); + } else { + loadConstant(cUnit, rScratch, vDest*4); + newLIR3(cUnit, THUMB_STR_RRR, rSrc, rFP, rScratch); + } +} + +/* + * Perform a binary operation on 64-bit operands and leave the results in the + * r0/r1 pair. + */ +static void genBinaryOpWide(CompilationUnit *cUnit, int vDest, + ArmOpCode preinst, ArmOpCode inst, + int reg0, int reg2) +{ + int reg1 = NEXT_REG(reg0); + int reg3 = NEXT_REG(reg2); + newLIR23(cUnit, preinst, reg0, reg2); + newLIR23(cUnit, inst, reg1, reg3); + storeValuePair(cUnit, reg0, reg1, vDest, reg2); +} + +/* Perform a binary operation on 32-bit operands and leave the results in r0. */ +static void genBinaryOp(CompilationUnit *cUnit, int vDest, ArmOpCode inst, + int reg0, int reg1, int regDest) +{ + if (EncodingMap[inst].flags & IS_BINARY_OP) { + newLIR2(cUnit, inst, reg0, reg1); + storeValue(cUnit, reg0, vDest, reg1); + } else { + newLIR3(cUnit, inst, regDest, reg0, reg1); + storeValue(cUnit, regDest, vDest, reg1); + } +} + +/* Create the PC reconstruction slot if not already done */ +static inline ArmLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset, + ArmLIR *branch, + ArmLIR *pcrLabel) +{ + /* Set up the place holder to reconstruct this Dalvik PC */ + if (pcrLabel == NULL) { + int dPC = (int) (cUnit->method->insns + dOffset); + pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); + pcrLabel->opCode = ARM_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 ArmLIR *genRegImmCheck(CompilationUnit *cUnit, + ArmConditionCode cond, int reg, + int checkValue, int dOffset, + ArmLIR *pcrLabel) +{ + newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue); + ArmLIR *branch = newLIR2(cUnit, THUMB_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 ArmLIR *inertRegRegCheck(CompilationUnit *cUnit, + ArmConditionCode cond, + int reg1, int reg2, int dOffset, + ArmLIR *pcrLabel) +{ + newLIR2(cUnit, THUMB_CMP_RR, reg1, reg2); + ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* + * Perform null-check on a register. vReg is the Dalvik register being checked, + * and mReg is the machine register holding the actual value. If internal state + * indicates that vReg has been checked before the check request is ignored. + */ +static ArmLIR *genNullCheck(CompilationUnit *cUnit, int vReg, int mReg, + int dOffset, ArmLIR *pcrLabel) +{ + /* This particular Dalvik register has been null-checked */ + if (dvmIsBitSet(cUnit->registerScoreboard.nullCheckedRegs, vReg)) { + return pcrLabel; + } + dvmSetBit(cUnit->registerScoreboard.nullCheckedRegs, vReg); + return genRegImmCheck(cUnit, ARM_COND_EQ, mReg, 0, dOffset, pcrLabel); +} + +/* + * Perform zero-check on a register. Similar to genNullCheck but the value being + * checked does not have a corresponding Dalvik register. + */ +static ArmLIR *genZeroCheck(CompilationUnit *cUnit, int mReg, + int dOffset, ArmLIR *pcrLabel) +{ + return genRegImmCheck(cUnit, ARM_COND_EQ, mReg, 0, dOffset, pcrLabel); +} + +/* Perform bound check on two registers */ +static ArmLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex, + int rBound, int dOffset, ArmLIR *pcrLabel) +{ + return inertRegRegCheck(cUnit, ARM_COND_CS, rIndex, rBound, dOffset, + pcrLabel); +} + +/* Generate a unconditional branch to go to the interpreter */ +static inline ArmLIR *genTrap(CompilationUnit *cUnit, int dOffset, + ArmLIR *pcrLabel) +{ + ArmLIR *branch = newLIR0(cUnit, THUMB_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; + int reg0, reg1, reg2, reg3; + + /* Allocate reg0..reg3 into physical registers r0..r3 */ + + /* See if vB is in a native register. If so, reuse it. */ + reg2 = selectFirstRegister(cUnit, dInsn->vB, false); + /* Ping reg3 to the other register of the same pair containing reg2 */ + reg3 = reg2 ^ 0x1; + /* + * Ping reg0 to the first register of the alternate register pair + */ + reg0 = (reg2 + 2) & 0x2; + reg1 = NEXT_REG(reg0); + + loadValue(cUnit, dInsn->vB, reg2); + loadConstant(cUnit, reg3, fieldOffset); + genNullCheck(cUnit, dInsn->vB, reg2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, THUMB_ADD_RRR, reg2, reg2, reg3); + newLIR2(cUnit, THUMB_LDMIA, reg2, (1<<reg0 | 1<<reg1)); + storeValuePair(cUnit, reg0, reg1, dInsn->vA, reg3); +} + +/* Store a wide field to an object instance */ +static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + int reg0, reg1, reg2, reg3; + + /* Allocate reg0..reg3 into physical registers r0..r3 */ + + /* See if vB is in a native register. If so, reuse it. */ + reg2 = selectFirstRegister(cUnit, dInsn->vB, false); + /* Ping reg3 to the other register of the same pair containing reg2 */ + reg3 = reg2 ^ 0x1; + /* + * Ping reg0 to the first register of the alternate register pair + */ + reg0 = (reg2 + 2) & 0x2; + reg1 = NEXT_REG(reg0); + + + loadValue(cUnit, dInsn->vB, reg2); + loadValuePair(cUnit, dInsn->vA, reg0, reg1); + updateLiveRegisterPair(cUnit, dInsn->vA, reg0, reg1); + loadConstant(cUnit, reg3, fieldOffset); + genNullCheck(cUnit, dInsn->vB, reg2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, THUMB_ADD_RRR, reg2, reg2, reg3); + newLIR2(cUnit, THUMB_STMIA, reg2, (1<<reg0 | 1<<reg1)); +} + +/* + * Load a field from an object instance + * + * Inst should be one of: + * THUMB_LDR_RRR + * THUMB_LDRB_RRR + * THUMB_LDRH_RRR + * THUMB_LDRSB_RRR + * THUMB_LDRSH_RRR + */ +static void genIGet(CompilationUnit *cUnit, MIR *mir, ArmOpCode inst, + int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + int reg0, reg1; + + reg0 = selectFirstRegister(cUnit, dInsn->vB, false); + reg1 = NEXT_REG(reg0); + /* TUNING: write a utility routine to load via base + constant offset */ + loadValue(cUnit, dInsn->vB, reg0); + loadConstant(cUnit, reg1, fieldOffset); + genNullCheck(cUnit, dInsn->vB, reg0, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, inst, reg0, reg0, reg1); + storeValue(cUnit, reg0, dInsn->vA, reg1); +} + +/* + * Store a field to an object instance + * + * Inst should be one of: + * THUMB_STR_RRR + * THUMB_STRB_RRR + * THUMB_STRH_RRR + */ +static void genIPut(CompilationUnit *cUnit, MIR *mir, ArmOpCode inst, + int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + int reg0, reg1, reg2; + + reg0 = selectFirstRegister(cUnit, dInsn->vB, false); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + + /* TUNING: write a utility routine to load via base + constant offset */ + loadValue(cUnit, dInsn->vB, reg0); + loadConstant(cUnit, reg1, fieldOffset); + loadValue(cUnit, dInsn->vA, reg2); + updateLiveRegister(cUnit, dInsn->vA, reg2); + genNullCheck(cUnit, dInsn->vB, reg0, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, inst, reg2, reg0, reg1); +} + + +/* TODO: This should probably be done as an out-of-line instruction handler. */ + +/* + * Generate array load + * + * Inst should be one of: + * THUMB_LDR_RRR + * THUMB_LDRB_RRR + * THUMB_LDRH_RRR + * THUMB_LDRSB_RRR + * THUMB_LDRSH_RRR + */ +static void genArrayGet(CompilationUnit *cUnit, MIR *mir, ArmOpCode inst, + int vArray, int vIndex, int vDest, int scale) +{ + int lenOffset = offsetof(ArrayObject, length); + int dataOffset = offsetof(ArrayObject, contents); + int reg0, reg1, reg2, reg3; + + reg0 = selectFirstRegister(cUnit, vArray, false); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + reg3 = NEXT_REG(reg2); + + loadValue(cUnit, vArray, reg2); + loadValue(cUnit, vIndex, reg3); + + /* null object? */ + ArmLIR * pcrLabel = genNullCheck(cUnit, vArray, reg2, mir->offset, + NULL); + newLIR3(cUnit, THUMB_LDR_RRI5, reg0, reg2, lenOffset >> 2); /* Get len */ + newLIR2(cUnit, THUMB_ADD_RI8, reg2, dataOffset); /* reg2 -> array data */ + genBoundsCheck(cUnit, reg3, reg0, mir->offset, pcrLabel); + if (scale) { + newLIR3(cUnit, THUMB_LSL, reg3, reg3, scale); + } + if (scale==3) { + newLIR3(cUnit, inst, reg0, reg2, reg3); + newLIR2(cUnit, THUMB_ADD_RI8, reg2, 4); + newLIR3(cUnit, inst, reg1, reg2, reg3); + storeValuePair(cUnit, reg0, reg1, vDest, reg3); + } else { + newLIR3(cUnit, inst, reg0, reg2, reg3); + storeValue(cUnit, reg0, vDest, reg3); + } +} + +/* TODO: This should probably be done as an out-of-line instruction handler. */ + +/* + * Generate array store + * + * Inst should be one of: + * THUMB_STR_RRR + * THUMB_STRB_RRR + * THUMB_STRH_RRR + */ +static void genArrayPut(CompilationUnit *cUnit, MIR *mir, ArmOpCode inst, + int vArray, int vIndex, int vSrc, int scale) +{ + int lenOffset = offsetof(ArrayObject, length); + int dataOffset = offsetof(ArrayObject, contents); + int reg0, reg1, reg2, reg3; + + reg0 = selectFirstRegister(cUnit, vArray, false); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + reg3 = NEXT_REG(reg2); + + loadValue(cUnit, vArray, reg2); + loadValue(cUnit, vIndex, reg3); + + /* null object? */ + ArmLIR * pcrLabel = genNullCheck(cUnit, vArray, reg2, mir->offset, + NULL); + newLIR3(cUnit, THUMB_LDR_RRI5, reg0, reg2, lenOffset >> 2); /* Get len */ + newLIR2(cUnit, THUMB_ADD_RI8, reg2, dataOffset); /* reg2 -> array data */ + genBoundsCheck(cUnit, reg3, reg0, mir->offset, pcrLabel); + /* at this point, reg2 points to array, reg3 is unscaled index */ + if (scale==3) { + loadValuePair(cUnit, vSrc, reg0, reg1); + updateLiveRegisterPair(cUnit, vSrc, reg0, reg1); + } else { + loadValue(cUnit, vSrc, reg0); + updateLiveRegister(cUnit, vSrc, reg0); + } + if (scale) { + newLIR3(cUnit, THUMB_LSL, reg3, reg3, scale); + } + /* + * at this point, reg2 points to array, reg3 is scaled index, and + * reg0[reg1] is data + */ + if (scale==3) { + newLIR3(cUnit, inst, reg0, reg2, reg3); + newLIR2(cUnit, THUMB_ADD_RI8, reg2, 4); + newLIR3(cUnit, inst, reg1, reg2, reg3); + } else { + newLIR3(cUnit, inst, reg0, reg2, reg3); + } +} + +static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vShift) +{ + /* + * Don't mess with the regsiters here as there is a particular calling + * convention to the out-of-line handler. + */ + loadValue(cUnit, vShift, r2); + loadValuePair(cUnit, vSrc1, r0, r1); + 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; +} +bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir, + int vDest, int vSrc1, int vSrc2) +{ + /* + * Don't optimize the regsiter usage here as they are governed by the EABI + * calling convention. + */ + void* funct; + int reg0, reg1; + + /* 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); + + reg0 = selectFirstRegister(cUnit, vSrc2, false); + reg1 = NEXT_REG(reg0); + + 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, reg0); + loadConstant(cUnit, reg1, 0x80000000); + newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, reg1); + storeValue(cUnit, reg0, vDest, reg1); + return false; + } + default: + return true; + } + loadConstant(cUnit, r2, (int)funct); + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + newLIR1(cUnit, THUMB_BLX_R, r2); + storeValue(cUnit, r0, vDest, r1); + return false; +} + +bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir, + int vDest, int vSrc1, int vSrc2) +{ + void* funct; + int reg0, reg1, reg2; + + /* 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); + + reg0 = selectFirstRegister(cUnit, vSrc2, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + + 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, reg0, reg1); + loadConstant(cUnit, reg2, 0x80000000); + newLIR3(cUnit, THUMB_ADD_RRR, reg1, reg1, reg2); + storeValuePair(cUnit, reg0, reg1, vDest, reg2); + return false; + } + default: + return true; + } + /* + * Don't optimize the regsiter usage here as they are governed by the EABI + * calling convention. + */ + loadConstant(cUnit, r4PC, (int)funct); + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + newLIR1(cUnit, THUMB_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 = THUMB_BKPT; + int secondOp = THUMB_BKPT; + bool callOut = false; + void *callTgt; + int retReg = r0; + int reg0, reg1, reg2, reg3; + /* 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 = THUMB_MVN; + secondOp = THUMB_MVN; + break; + case OP_ADD_LONG: + case OP_ADD_LONG_2ADDR: + firstOp = THUMB_ADD_RRR; + secondOp = THUMB_ADC; + break; + case OP_SUB_LONG: + case OP_SUB_LONG_2ADDR: + firstOp = THUMB_SUB_RRR; + secondOp = THUMB_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 = THUMB_AND_RR; + secondOp = THUMB_AND_RR; + break; + case OP_OR_LONG: + case OP_OR_LONG_2ADDR: + firstOp = THUMB_ORR; + secondOp = THUMB_ORR; + break; + case OP_XOR_LONG: + case OP_XOR_LONG_2ADDR: + firstOp = THUMB_EOR; + secondOp = THUMB_EOR; + break; + case OP_NEG_LONG: { + reg0 = selectFirstRegister(cUnit, vSrc2, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + reg3 = NEXT_REG(reg2); + + loadValuePair(cUnit, vSrc2, reg0, reg1); + loadConstant(cUnit, reg3, 0); + newLIR3(cUnit, THUMB_SUB_RRR, reg2, reg3, reg0); + newLIR2(cUnit, THUMB_SBC, reg3, reg1); + storeValuePair(cUnit, reg2, reg3, vDest, reg0); + return false; + } + default: + LOGE("Invalid long arith op"); + dvmAbort(); + } + if (!callOut) { + reg0 = selectFirstRegister(cUnit, vSrc1, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + reg3 = NEXT_REG(reg2); + + loadValuePair(cUnit, vSrc1, reg0, reg1); + loadValuePair(cUnit, vSrc2, reg2, reg3); + genBinaryOpWide(cUnit, vDest, firstOp, secondOp, reg0, reg2); + /* + * Don't optimize the regsiter usage here as they are governed by the EABI + * calling convention. + */ + } else { + loadValuePair(cUnit, vSrc2, r2, r3); + loadConstant(cUnit, r4PC, (int) callTgt); + loadValuePair(cUnit, vSrc1, r0, r1); + newLIR1(cUnit, THUMB_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 = THUMB_BKPT; + bool callOut = false; + bool checkZero = false; + int retReg = r0; + void *callTgt; + int reg0, reg1, regDest; + + /* 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 = THUMB_NEG; + break; + case OP_NOT_INT: + armOp = THUMB_MVN; + break; + case OP_ADD_INT: + case OP_ADD_INT_2ADDR: + armOp = THUMB_ADD_RRR; + break; + case OP_SUB_INT: + case OP_SUB_INT_2ADDR: + armOp = THUMB_SUB_RRR; + break; + case OP_MUL_INT: + case OP_MUL_INT_2ADDR: + armOp = THUMB_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 = THUMB_AND_RR; + break; + case OP_OR_INT: + case OP_OR_INT_2ADDR: + armOp = THUMB_ORR; + break; + case OP_XOR_INT: + case OP_XOR_INT_2ADDR: + armOp = THUMB_EOR; + break; + case OP_SHL_INT: + case OP_SHL_INT_2ADDR: + armOp = THUMB_LSLV; + break; + case OP_SHR_INT: + case OP_SHR_INT_2ADDR: + armOp = THUMB_ASRV; + break; + case OP_USHR_INT: + case OP_USHR_INT_2ADDR: + armOp = THUMB_LSRV; + break; + default: + LOGE("Invalid word arith op: 0x%x(%d)", + mir->dalvikInsn.opCode, mir->dalvikInsn.opCode); + dvmAbort(); + } + if (!callOut) { + /* Try to allocate reg0 to the currently cached source operand */ + if (cUnit->registerScoreboard.liveDalvikReg == vSrc1) { + reg0 = selectFirstRegister(cUnit, vSrc1, false); + reg1 = NEXT_REG(reg0); + regDest = NEXT_REG(reg1); + + loadValue(cUnit, vSrc1, reg0); /* Should be optimized away */ + loadValue(cUnit, vSrc2, reg1); + genBinaryOp(cUnit, vDest, armOp, reg0, reg1, regDest); + } else { + reg0 = selectFirstRegister(cUnit, vSrc2, false); + reg1 = NEXT_REG(reg0); + regDest = NEXT_REG(reg1); + + loadValue(cUnit, vSrc1, reg1); /* Load this value first */ + loadValue(cUnit, vSrc2, reg0); /* May be optimized away */ + genBinaryOp(cUnit, vDest, armOp, reg1, reg0, regDest); + } + } else { + /* + * Load the callout target first since it will never be eliminated + * and its value will be used first. + */ + loadConstant(cUnit, r2, (int) callTgt); + /* + * Load vSrc2 first if it is not cached in a native register or it + * is in r0 which will be clobbered if vSrc1 is loaded first. + */ + if (cUnit->registerScoreboard.liveDalvikReg != vSrc2 || + cUnit->registerScoreboard.nativeReg == r0) { + /* Cannot be optimized and won't clobber r0 */ + loadValue(cUnit, vSrc2, r1); + /* May be optimized if vSrc1 is cached */ + loadValue(cUnit, vSrc1, r0); + } else { + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + } + if (checkZero) { + genNullCheck(cUnit, vSrc2, r1, mir->offset, NULL); + } + newLIR1(cUnit, THUMB_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 genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct, + int srcSize, int tgtSize) +{ + /* + * Don't optimize the register usage since it calls out to template + * functions + */ + loadConstant(cUnit, r2, (int)funct); + if (srcSize == 1) { + loadValue(cUnit, mir->dalvikInsn.vB, r0); + } else { + loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1); + } + newLIR1(cUnit, THUMB_BLX_R, r2); + if (tgtSize == 1) { + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + } else { + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + } + return false; +} + +static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + int offset = offsetof(InterpState, retval); + int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false); + int reg1 = NEXT_REG(regObj); + loadValue(cUnit, dInsn->arg[0], regObj); + genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL); + loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1); + newLIR3(cUnit, THUMB_STR_RRI5, reg1, rGLUE, offset >> 2); + return false; +} + +/* + * NOTE: The amount of code for this body suggests it ought to + * be handled in a template (and could also be coded quite a bit + * more efficiently in ARM). However, the code is dependent on the + * internal structure layout of string objects which are most safely + * known at run time. + * TUNING: One possibility (which could also be used for StringCompareTo + * and StringEquals) is to generate string access helper subroutines on + * Jit startup, and then call them from the translated inline-executes. + */ +static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + int offset = offsetof(InterpState, retval); + int contents = offsetof(ArrayObject, contents); + int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false); + int regIdx = NEXT_REG(regObj); + int regMax = NEXT_REG(regIdx); + int regOff = NEXT_REG(regMax); + loadValue(cUnit, dInsn->arg[0], regObj); + loadValue(cUnit, dInsn->arg[1], regIdx); + ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj, + mir->offset, NULL); + loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax); + loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff); + loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj); + genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel); + + newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents); + newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff); + newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx); + newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx); + newLIR3(cUnit, THUMB_STR_RRI5, regMax, rGLUE, offset >> 2); + return false; +} + +static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir) +{ + int offset = offsetof(InterpState, retval); + DecodedInstruction *dInsn = &mir->dalvikInsn; + int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false); + int sign = NEXT_REG(reg0); + /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */ + loadValue(cUnit, dInsn->arg[0], reg0); + newLIR3(cUnit, THUMB_ASR, sign, reg0, 31); + newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign); + newLIR2(cUnit, THUMB_EOR, reg0, sign); + newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2); + return false; +} + +static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir) +{ + int offset = offsetof(InterpState, retval); + DecodedInstruction *dInsn = &mir->dalvikInsn; + int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false); + int signMask = NEXT_REG(reg0); + loadValue(cUnit, dInsn->arg[0], reg0); + loadConstant(cUnit, signMask, 0x7fffffff); + newLIR2(cUnit, THUMB_AND_RR, reg0, signMask); + newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2); + return false; +} + +static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir) +{ + int offset = offsetof(InterpState, retval); + DecodedInstruction *dInsn = &mir->dalvikInsn; + int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true); + int ophi = NEXT_REG(oplo); + int signMask = NEXT_REG(ophi); + loadValuePair(cUnit, dInsn->arg[0], oplo, ophi); + loadConstant(cUnit, signMask, 0x7fffffff); + newLIR3(cUnit, THUMB_STR_RRI5, oplo, rGLUE, offset >> 2); + newLIR2(cUnit, THUMB_AND_RR, ophi, signMask); + newLIR3(cUnit, THUMB_STR_RRI5, ophi, rGLUE, (offset >> 2)+1); + return false; +} + + /* No select in thumb, so we need to branch. Thumb2 will do better */ +static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin) +{ + int offset = offsetof(InterpState, retval); + DecodedInstruction *dInsn = &mir->dalvikInsn; + int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false); + int reg1 = NEXT_REG(reg0); + loadValue(cUnit, dInsn->arg[0], reg0); + loadValue(cUnit, dInsn->arg[1], reg1); + newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1); + ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2, + isMin ? ARM_COND_LT : ARM_COND_GT); + newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1); + ArmLIR *target = + newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2); + branch1->generic.target = (LIR *)target; + return false; +} + +static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir) +{ + int offset = offsetof(InterpState, retval); + DecodedInstruction *dInsn = &mir->dalvikInsn; + int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true); + int ophi = NEXT_REG(oplo); + int sign = NEXT_REG(ophi); + /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */ + loadValuePair(cUnit, dInsn->arg[0], oplo, ophi); + newLIR3(cUnit, THUMB_ASR, sign, ophi, 31); + newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign); + newLIR2(cUnit, THUMB_ADC, ophi, sign); + newLIR2(cUnit, THUMB_EOR, oplo, sign); + newLIR2(cUnit, THUMB_EOR, ophi, sign); + newLIR3(cUnit, THUMB_STR_RRI5, oplo, rGLUE, offset >> 2); + newLIR3(cUnit, THUMB_STR_RRI5, ophi, rGLUE, (offset >> 2)+1); + return false; +} + +static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir, + DecodedInstruction *dInsn, + ArmLIR **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, THUMB_MOV_RR, r7, rFP); + newLIR2(cUnit, THUMB_SUB_RI8, r7, + sizeof(StackSaveArea) + (dInsn->vA << 2)); + /* generate null check */ + if (pcrLabel) { + *pcrLabel = genNullCheck(cUnit, dInsn->arg[0], r0, mir->offset, + NULL); + } + newLIR2(cUnit, THUMB_STMIA, r7, regMask); + } +} + +static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir, + DecodedInstruction *dInsn, + ArmLIR **pcrLabel) +{ + int srcOffset = dInsn->vC << 2; + int numArgs = dInsn->vA; + int regMask; + /* + * r4PC : &rFP[vC] + * r7: &newFP[0] + */ + if (srcOffset < 8) { + newLIR3(cUnit, THUMB_ADD_RRI3, r4PC, rFP, srcOffset); + } else { + loadConstant(cUnit, r4PC, srcOffset); + newLIR3(cUnit, THUMB_ADD_RRR, r4PC, rFP, r4PC); + } + /* load [r0 .. min(numArgs,4)] */ + regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1; + newLIR2(cUnit, THUMB_LDMIA, r4PC, regMask); + + if (sizeof(StackSaveArea) + (numArgs << 2) < 256) { + newLIR2(cUnit, THUMB_MOV_RR, r7, rFP); + newLIR2(cUnit, THUMB_SUB_RI8, r7, + sizeof(StackSaveArea) + (numArgs << 2)); + } else { + loadConstant(cUnit, r7, sizeof(StackSaveArea) + (numArgs << 2)); + newLIR3(cUnit, THUMB_SUB_RRR, r7, rFP, r7); + } + + /* generate null check */ + if (pcrLabel) { + *pcrLabel = genNullCheck(cUnit, dInsn->vC, r0, mir->offset, NULL); + } + + /* + * Handle remaining 4n arguments: + * store previously loaded 4 values and load the next 4 values + */ + if (numArgs >= 8) { + ArmLIR *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, THUMB_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, ARM_PSEUDO_TARGET_LABEL); + } + newLIR2(cUnit, THUMB_STMIA, r7, regMask); + newLIR2(cUnit, THUMB_LDMIA, r4PC, regMask); + /* No need to generate the loop structure if numArgs <= 11 */ + if (numArgs > 11) { + newLIR2(cUnit, THUMB_SUB_RI8, 5, 4); + genConditionalBranch(cUnit, ARM_COND_NE, loopLabel); + } + } + + /* Save the last batch of loaded values */ + newLIR2(cUnit, THUMB_STMIA, r7, regMask); + + /* Generate the loop epilogue - don't use r0 */ + if ((numArgs > 4) && (numArgs % 4)) { + regMask = ((1 << (numArgs & 0x3)) - 1) << 1; + newLIR2(cUnit, THUMB_LDMIA, r4PC, regMask); + } + if (numArgs >= 8) + newLIR1(cUnit, THUMB_POP, 1 << r0 | 1 << 5); + + /* Save the modulo 4 arguments */ + if ((numArgs > 4) && (numArgs % 4)) { + newLIR2(cUnit, THUMB_STMIA, r7, regMask); + } +} + +/* + * Generate code to setup the call stack then jump to the chaining cell if it + * is not a native method. + */ +static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir, + BasicBlock *bb, ArmLIR *labelList, + ArmLIR *pcrLabel, + const Method *calleeMethod) +{ + ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; + + /* r1 = &retChainingCell */ + ArmLIR *addrRetChain = newLIR3(cUnit, THUMB_ADD_PC_REL, + r1, 0, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + addrRetChain->generic.target = (LIR *) retChainingCell; + /* + * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon) + * r1 = &ChainingCell + * r4PC = callsiteDPC + */ + if (dvmIsNativeMethod(calleeMethod)) { + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NATIVE); +#if defined(INVOKE_STATS) + gDvmJit.invokeNative++; +#endif + } else { + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_CHAIN); +#if defined(INVOKE_STATS) + gDvmJit.invokeChain++; +#endif + /* Branch to the chaining cell */ + genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); + } + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); +} + +/* + * Generate code to check the validity of a predicted chain and take actions + * based on the result. + * + * 0x426a99aa : ldr r4, [pc, #72] --> r4 <- dalvikPC of this invoke + * 0x426a99ac : add r1, pc, #32 --> r1 <- &retChainingCell + * 0x426a99ae : add r2, pc, #40 --> r2 <- &predictedChainingCell + * 0x426a99b0 : blx_1 0x426a918c --+ TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN + * 0x426a99b2 : blx_2 see above --+ + * 0x426a99b4 : b 0x426a99d8 --> off to the predicted chain + * 0x426a99b6 : b 0x426a99c8 --> punt to the interpreter + * 0x426a99b8 : ldr r0, [r7, #44] --> r0 <- this->class->vtable[methodIdx] + * 0x426a99ba : cmp r1, #0 --> compare r1 (rechain count) against 0 + * 0x426a99bc : bgt 0x426a99c2 --> >=0? don't rechain + * 0x426a99be : ldr r7, [r6, #96] --+ dvmJitToPatchPredictedChain + * 0x426a99c0 : blx r7 --+ + * 0x426a99c2 : add r1, pc, #12 --> r1 <- &retChainingCell + * 0x426a99c4 : blx_1 0x426a9098 --+ TEMPLATE_INVOKE_METHOD_NO_OPT + * 0x426a99c6 : blx_2 see above --+ + */ +static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir, + int methodIndex, + ArmLIR *retChainingCell, + ArmLIR *predChainingCell, + ArmLIR *pcrLabel) +{ + /* "this" is already left in r0 by genProcessArgs* */ + + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r1 = &retChainingCell */ + ArmLIR *addrRetChain = newLIR2(cUnit, THUMB_ADD_PC_REL, + r1, 0); + addrRetChain->generic.target = (LIR *) retChainingCell; + + /* r2 = &predictedChainingCell */ + ArmLIR *predictedChainingCell = + newLIR2(cUnit, THUMB_ADD_PC_REL, r2, 0); + predictedChainingCell->generic.target = (LIR *) predChainingCell; + + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN); + + /* return through lr - jump to the chaining cell */ + genUnconditionalBranch(cUnit, predChainingCell); + + /* + * null-check on "this" may have been eliminated, but we still need a PC- + * reconstruction label for stack overflow bailout. + */ + if (pcrLabel == NULL) { + int dPC = (int) (cUnit->method->insns + mir->offset); + pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); + pcrLabel->opCode = ARM_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); + } + + /* return through lr+2 - punt to the interpreter */ + genUnconditionalBranch(cUnit, pcrLabel); + + /* + * return through lr+4 - fully resolve the callee method. + * r1 <- count + * r2 <- &predictedChainCell + * r3 <- this->class + * r4 <- dPC + * r7 <- this->class->vtable + */ + + /* r0 <- calleeMethod */ + if (methodIndex < 32) { + newLIR3(cUnit, THUMB_LDR_RRI5, r0, r7, methodIndex); + } else { + loadConstant(cUnit, r0, methodIndex<<2); + newLIR3(cUnit, THUMB_LDR_RRR, r0, r7, r0); + } + + /* Check if rechain limit is reached */ + newLIR2(cUnit, THUMB_CMP_RI8, r1, 0); + + ArmLIR *bypassRechaining = + newLIR2(cUnit, THUMB_B_COND, 0, ARM_COND_GT); + + newLIR3(cUnit, THUMB_LDR_RRI5, r7, rGLUE, + offsetof(InterpState, + jitToInterpEntries.dvmJitToPatchPredictedChain) + >> 2); + + /* + * r0 = calleeMethod + * r2 = &predictedChainingCell + * r3 = class + * + * &returnChainingCell has been loaded into r1 but is not needed + * when patching the chaining cell and will be clobbered upon + * returning so it will be reconstructed again. + */ + newLIR1(cUnit, THUMB_BLX_R, r7); + + /* r1 = &retChainingCell */ + addrRetChain = newLIR3(cUnit, THUMB_ADD_PC_REL, r1, 0, 0); + addrRetChain->generic.target = (LIR *) retChainingCell; + + bypassRechaining->generic.target = (LIR *) addrRetChain; + /* + * r0 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokePredictedChain++; +#endif + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); +} + +/* + * Up calling this function, "this" is stored in r0. The actual class will be + * chased down off r0 and the predicted one will be retrieved through + * predictedChainingCell then a comparison is performed to see whether the + * previously established chaining is still valid. + * + * The return LIR is a branch based on the comparison result. The actual branch + * target will be setup in the caller. + */ +static ArmLIR *genCheckPredictedChain(CompilationUnit *cUnit, + ArmLIR *predChainingCell, + ArmLIR *retChainingCell, + MIR *mir) +{ + /* r3 now contains this->clazz */ + newLIR3(cUnit, THUMB_LDR_RRI5, r3, r0, + offsetof(Object, clazz) >> 2); + + /* + * r2 now contains predicted class. The starting offset of the + * cached value is 4 bytes into the chaining cell. + */ + ArmLIR *getPredictedClass = + newLIR3(cUnit, THUMB_LDR_PC_REL, r2, 0, + offsetof(PredictedChainingCell, clazz)); + getPredictedClass->generic.target = (LIR *) predChainingCell; + + /* + * r0 now contains predicted method. The starting offset of the + * cached value is 8 bytes into the chaining cell. + */ + ArmLIR *getPredictedMethod = + newLIR3(cUnit, THUMB_LDR_PC_REL, r0, 0, + offsetof(PredictedChainingCell, method)); + getPredictedMethod->generic.target = (LIR *) predChainingCell; + + /* Load the stats counter to see if it is time to unchain and refresh */ + ArmLIR *getRechainingRequestCount = + newLIR3(cUnit, THUMB_LDR_PC_REL, r7, 0, + offsetof(PredictedChainingCell, counter)); + getRechainingRequestCount->generic.target = + (LIR *) predChainingCell; + + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r1 = &retChainingCell */ + ArmLIR *addrRetChain = newLIR3(cUnit, THUMB_ADD_PC_REL, + r1, 0, 0); + addrRetChain->generic.target = (LIR *) retChainingCell; + + /* Check if r2 (predicted class) == r3 (actual class) */ + newLIR2(cUnit, THUMB_CMP_RR, r2, r3); + + return newLIR2(cUnit, THUMB_B_COND, 0, ARM_COND_EQ); +} + +/* 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, THUMB_LDR_RRI5, r1, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpPunt) >> 2); + newLIR1(cUnit, THUMB_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, THUMB_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, THUMB_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, ArmLIR *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) +{ + int reg0, reg1, reg2; + + switch (mir->dalvikInsn.opCode) { + case OP_CONST: + case OP_CONST_4: { + /* Avoid using the previously used register */ + reg0 = selectFirstRegister(cUnit, vNone, false); + reg1 = NEXT_REG(reg0); + loadConstant(cUnit, reg0, mir->dalvikInsn.vB); + storeValue(cUnit, reg0, mir->dalvikInsn.vA, reg1); + break; + } + case OP_CONST_WIDE_32: { + /* Avoid using the previously used register */ + reg0 = selectFirstRegister(cUnit, vNone, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + loadConstant(cUnit, reg0, mir->dalvikInsn.vB); + newLIR3(cUnit, THUMB_ASR, reg1, reg0, 31); + storeValuePair(cUnit, reg0, reg1, mir->dalvikInsn.vA, reg2); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir) +{ + int reg0, reg1, reg2; + + /* Avoid using the previously used register */ + switch (mir->dalvikInsn.opCode) { + case OP_CONST_HIGH16: { + reg0 = selectFirstRegister(cUnit, vNone, false); + reg1 = NEXT_REG(reg0); + loadConstant(cUnit, reg0, mir->dalvikInsn.vB << 16); + storeValue(cUnit, reg0, mir->dalvikInsn.vA, reg1); + break; + } + case OP_CONST_WIDE_HIGH16: { + reg0 = selectFirstRegister(cUnit, vNone, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + loadConstant(cUnit, reg1, mir->dalvikInsn.vB << 16); + loadConstant(cUnit, reg0, 0); + storeValuePair(cUnit, reg0, reg1, mir->dalvikInsn.vA, reg2); + 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) +{ + /* Native register to use if the interested value is vA */ + int regvA = selectFirstRegister(cUnit, mir->dalvikInsn.vA, false); + /* Native register to use if source is not from Dalvik registers */ + int regvNone = selectFirstRegister(cUnit, vNone, false); + /* Similar to regvA but for 64-bit values */ + int regvAWide = selectFirstRegister(cUnit, mir->dalvikInsn.vA, true); + /* Similar to regvNone but for 64-bit values */ + int regvNoneWide = selectFirstRegister(cUnit, vNone, true); + + 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, regvNone, (int) strPtr ); + storeValue(cUnit, regvNone, mir->dalvikInsn.vA, NEXT_REG(regvNone)); + 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, regvNone, (int) classPtr ); + storeValue(cUnit, regvNone, mir->dalvikInsn.vA, NEXT_REG(regvNone)); + 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 = offsetof(StaticField, value); + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadConstant(cUnit, regvNone, (int) fieldPtr + valOffset); + newLIR3(cUnit, THUMB_LDR_RRI5, regvNone, regvNone, 0); + storeValue(cUnit, regvNone, mir->dalvikInsn.vA, NEXT_REG(regvNone)); + break; + } + case OP_SGET_WIDE: { + int valOffset = offsetof(StaticField, value); + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + int reg0, reg1, reg2; + + assert(fieldPtr != NULL); + reg0 = regvNoneWide; + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + loadConstant(cUnit, reg2, (int) fieldPtr + valOffset); + newLIR2(cUnit, THUMB_LDMIA, reg2, (1<<reg0 | 1<<reg1)); + storeValuePair(cUnit, reg0, reg1, mir->dalvikInsn.vA, reg2); + 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 = offsetof(StaticField, value); + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + + assert(fieldPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vA, regvA); + updateLiveRegister(cUnit, mir->dalvikInsn.vA, regvA); + loadConstant(cUnit, NEXT_REG(regvA), (int) fieldPtr + valOffset); + newLIR3(cUnit, THUMB_STR_RRI5, regvA, NEXT_REG(regvA), 0); + break; + } + case OP_SPUT_WIDE: { + int reg0, reg1, reg2; + int valOffset = offsetof(StaticField, value); + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + + assert(fieldPtr != NULL); + reg0 = regvAWide; + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + loadValuePair(cUnit, mir->dalvikInsn.vA, reg0, reg1); + updateLiveRegisterPair(cUnit, mir->dalvikInsn.vA, reg0, reg1); + loadConstant(cUnit, reg2, (int) fieldPtr + valOffset); + newLIR2(cUnit, THUMB_STMIA, reg2, (1<<reg0 | 1<<reg1)); + break; + } + case OP_NEW_INSTANCE: { + /* + * Obey the calling convention and don't mess with the register + * usage. + */ + 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, r4PC, (int)dvmAllocObject); + loadConstant(cUnit, r0, (int) classPtr); + genExportPC(cUnit, mir, r2, r3 ); + loadConstant(cUnit, r1, ALLOC_DONT_TRACK); + newLIR1(cUnit, THUMB_BLX_R, r4PC); + /* + * TODO: As coded, we'll bail and reinterpret on alloc failure. + * Need a general mechanism to bail to thrown exception code. + */ + genZeroCheck(cUnit, r0, mir->offset, NULL); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_CHECK_CAST: { + /* + * Obey the calling convention and don't mess with the register + * usage. + */ + 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. + */ + ArmLIR * pcrLabel = genZeroCheck(cUnit, r1, mir->offset, NULL); + newLIR2(cUnit, THUMB_CMP_RI8, r0, 0); /* Null? */ + ArmLIR *branch1 = + newLIR2(cUnit, THUMB_B_COND, 4, ARM_COND_EQ); + /* r0 now contains object->clazz */ + newLIR3(cUnit, THUMB_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial); + newLIR2(cUnit, THUMB_CMP_RR, r0, r1); + ArmLIR *branch2 = + newLIR2(cUnit, THUMB_B_COND, 2, ARM_COND_EQ); + newLIR1(cUnit, THUMB_BLX_R, r4PC); + /* check cast failed - punt to the interpreter */ + genZeroCheck(cUnit, r0, mir->offset, pcrLabel); + /* check cast passed - branch target here */ + ArmLIR *target = newLIR0(cUnit, ARM_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, THUMB_LDR_RRI5, r1, rGLUE, offset >> 2); + newLIR3(cUnit, THUMB_LDR_RRI5, r0, r1, exOffset >> 2); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_MOVE_RESULT: + case OP_MOVE_RESULT_OBJECT: { + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, THUMB_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, THUMB_LDR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, THUMB_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, THUMB_STR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, THUMB_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, THUMB_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, THUMB_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, mir->dalvikInsn.vA, r1, mir->offset, NULL); + /* Do the call */ + newLIR1(cUnit, THUMB_BLX_R, r2); + break; + } + case OP_THROW: { + genInterpSingleStep(cUnit, mir); + break; + } + default: + return true; + } + return false; +} + +static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + + 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 ); + float __aeabi_l2f( long op1 ); + double __aeabi_l2d( long op1 ); + + switch (opCode) { + case OP_INT_TO_FLOAT: + return genConversionCall(cUnit, mir, (void*)__aeabi_i2f, 1, 1); + case OP_FLOAT_TO_INT: + return genConversionCall(cUnit, mir, (void*)__aeabi_f2iz, 1, 1); + case OP_DOUBLE_TO_FLOAT: + return genConversionCall(cUnit, mir, (void*)__aeabi_d2f, 2, 1); + case OP_FLOAT_TO_DOUBLE: + return genConversionCall(cUnit, mir, (void*)__aeabi_f2d, 1, 2); + case OP_INT_TO_DOUBLE: + return genConversionCall(cUnit, mir, (void*)__aeabi_i2d, 1, 2); + case OP_DOUBLE_TO_INT: + return genConversionCall(cUnit, mir, (void*)__aeabi_d2iz, 2, 1); + case OP_FLOAT_TO_LONG: + return genConversionCall(cUnit, mir, (void*)dvmJitf2l, 1, 2); + case OP_LONG_TO_FLOAT: + return genConversionCall(cUnit, mir, (void*)__aeabi_l2f, 2, 1); + case OP_DOUBLE_TO_LONG: + return genConversionCall(cUnit, mir, (void*)dvmJitd2l, 2, 2); + case OP_LONG_TO_DOUBLE: + return genConversionCall(cUnit, mir, (void*)__aeabi_l2d, 2, 2); + 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; + int reg0, reg1, reg2; + + /* TODO - find the proper include file to declare these */ + + if ( (opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { + return genArithOp( cUnit, mir ); + } + + /* + * If data type is 64-bit, re-calculate the register numbers in the + * corresponding cases. + */ + reg0 = selectFirstRegister(cUnit, vSrc2, false); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + + switch (opCode) { + case OP_INT_TO_FLOAT: + case OP_FLOAT_TO_INT: + case OP_DOUBLE_TO_FLOAT: + case OP_FLOAT_TO_DOUBLE: + case OP_INT_TO_DOUBLE: + case OP_DOUBLE_TO_INT: + case OP_FLOAT_TO_LONG: + case OP_LONG_TO_FLOAT: + case OP_DOUBLE_TO_LONG: + case OP_LONG_TO_DOUBLE: + return genConversion(cUnit, mir); + 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: { + reg0 = selectFirstRegister(cUnit, vSrc2, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + + loadValuePair(cUnit, vSrc2, reg0, reg1); + storeValuePair(cUnit, reg0, reg1, vSrc1Dest, reg2); + break; + } + case OP_INT_TO_LONG: { + reg0 = selectFirstRegister(cUnit, vSrc2, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + + loadValue(cUnit, vSrc2, reg0); + newLIR3(cUnit, THUMB_ASR, reg1, reg0, 31); + storeValuePair(cUnit, reg0, reg1, vSrc1Dest, reg2); + break; + } + case OP_MOVE: + case OP_MOVE_OBJECT: + case OP_LONG_TO_INT: + loadValue(cUnit, vSrc2, reg0); + storeValue(cUnit, reg0, vSrc1Dest, reg1); + break; + case OP_INT_TO_BYTE: + loadValue(cUnit, vSrc2, reg0); + newLIR3(cUnit, THUMB_LSL, reg0, reg0, 24); + newLIR3(cUnit, THUMB_ASR, reg0, reg0, 24); + storeValue(cUnit, reg0, vSrc1Dest, reg1); + break; + case OP_INT_TO_SHORT: + loadValue(cUnit, vSrc2, reg0); + newLIR3(cUnit, THUMB_LSL, reg0, reg0, 16); + newLIR3(cUnit, THUMB_ASR, reg0, reg0, 16); + storeValue(cUnit, reg0, vSrc1Dest, reg1); + break; + case OP_INT_TO_CHAR: + loadValue(cUnit, vSrc2, reg0); + newLIR3(cUnit, THUMB_LSL, reg0, reg0, 16); + newLIR3(cUnit, THUMB_LSR, reg0, reg0, 16); + storeValue(cUnit, reg0, vSrc1Dest, reg1); + break; + case OP_ARRAY_LENGTH: { + int lenOffset = offsetof(ArrayObject, length); + loadValue(cUnit, vSrc2, reg0); + genNullCheck(cUnit, vSrc2, reg0, mir->offset, NULL); + newLIR3(cUnit, THUMB_LDR_RRI5, reg0, reg0, lenOffset >> 2); + storeValue(cUnit, reg0, vSrc1Dest, reg1); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int reg0, reg1, reg2; + + /* It takes few instructions to handle OP_CONST_WIDE_16 inline */ + if (dalvikOpCode == OP_CONST_WIDE_16) { + int vDest = mir->dalvikInsn.vA; + int BBBB = mir->dalvikInsn.vB; + + reg0 = selectFirstRegister(cUnit, vNone, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + + loadConstant(cUnit, reg0, BBBB); + newLIR3(cUnit, THUMB_ASR, reg1, reg0, 31); + + /* Save the long values to the specified Dalvik register pair */ + storeValuePair(cUnit, reg0, reg1, vDest, reg2); + } else if (dalvikOpCode == OP_CONST_16) { + int vDest = mir->dalvikInsn.vA; + int BBBB = mir->dalvikInsn.vB; + + reg0 = selectFirstRegister(cUnit, vNone, false); + reg1 = NEXT_REG(reg0); + + loadConstant(cUnit, reg0, BBBB); + storeValue(cUnit, reg0, vDest, reg1); + } else { + return true; + } + return false; +} + +/* Compare agaist zero */ +static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + ArmLIR *labelList) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + ArmConditionCode cond; + int reg0 = selectFirstRegister(cUnit, mir->dalvikInsn.vA, false); + + loadValue(cUnit, mir->dalvikInsn.vA, reg0); + newLIR2(cUnit, THUMB_CMP_RI8, reg0, 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; + int reg0, reg1, regDest; + + reg0 = selectFirstRegister(cUnit, vSrc, false); + reg1 = NEXT_REG(reg0); + regDest = NEXT_REG(reg1); + + /* 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, reg0); + if (lit <= 7 && lit >= 0) { + newLIR3(cUnit, THUMB_ADD_RRI3, regDest, reg0, lit); + storeValue(cUnit, regDest, vDest, reg1); + } else if (lit <= 255 && lit >= 0) { + newLIR2(cUnit, THUMB_ADD_RI8, reg0, lit); + storeValue(cUnit, reg0, vDest, reg1); + } else if (lit >= -7 && lit <= 0) { + /* Convert to a small constant subtraction */ + newLIR3(cUnit, THUMB_SUB_RRI3, regDest, reg0, -lit); + storeValue(cUnit, regDest, vDest, reg1); + } else if (lit >= -255 && lit <= 0) { + /* Convert to a small constant subtraction */ + newLIR2(cUnit, THUMB_SUB_RI8, reg0, -lit); + storeValue(cUnit, reg0, vDest, reg1); + } else { + loadConstant(cUnit, reg1, lit); + genBinaryOp(cUnit, vDest, THUMB_ADD_RRR, reg0, reg1, regDest); + } + break; + + case OP_RSUB_INT_LIT8: + case OP_RSUB_INT: + loadValue(cUnit, vSrc, reg1); + loadConstant(cUnit, reg0, lit); + genBinaryOp(cUnit, vDest, THUMB_SUB_RRR, reg0, reg1, regDest); + 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, reg0); + loadConstant(cUnit, reg1, lit); + switch (dalvikOpCode) { + case OP_MUL_INT_LIT8: + case OP_MUL_INT_LIT16: + armOp = THUMB_MUL; + break; + case OP_AND_INT_LIT8: + case OP_AND_INT_LIT16: + armOp = THUMB_AND_RR; + break; + case OP_OR_INT_LIT8: + case OP_OR_INT_LIT16: + armOp = THUMB_ORR; + break; + case OP_XOR_INT_LIT8: + case OP_XOR_INT_LIT16: + armOp = THUMB_EOR; + break; + default: + dvmAbort(); + } + genBinaryOp(cUnit, vDest, armOp, reg0, reg1, regDest); + break; + + case OP_SHL_INT_LIT8: + case OP_SHR_INT_LIT8: + case OP_USHR_INT_LIT8: + loadValue(cUnit, vSrc, reg0); + switch (dalvikOpCode) { + case OP_SHL_INT_LIT8: + armOp = THUMB_LSL; + break; + case OP_SHR_INT_LIT8: + armOp = THUMB_ASR; + break; + case OP_USHR_INT_LIT8: + armOp = THUMB_LSR; + break; + default: dvmAbort(); + } + newLIR3(cUnit, armOp, reg0, reg0, lit); + storeValue(cUnit, reg0, vDest, reg1); + break; + + case OP_DIV_INT_LIT8: + case OP_DIV_INT_LIT16: + /* Register usage based on the calling convention */ + 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, THUMB_BLX_R, r2); + storeValue(cUnit, r0, vDest, r2); + break; + + case OP_REM_INT_LIT8: + case OP_REM_INT_LIT16: + /* Register usage based on the calling convention */ + 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, THUMB_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); + ArmLIR *pcrLabel = + genRegImmCheck(cUnit, ARM_COND_MI, r1, 0, mir->offset, NULL); + genExportPC(cUnit, mir, r2, r3 ); + newLIR2(cUnit, THUMB_MOV_IMM,r2,ALLOC_DONT_TRACK); + newLIR1(cUnit, THUMB_BLX_R, r4PC); + /* + * TODO: As coded, we'll bail and reinterpret on alloc failure. + * Need a general mechanism to bail to thrown exception code. + */ + genZeroCheck(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, r0); /* Ref */ + loadConstant(cUnit, r2, (int) classPtr ); + newLIR2(cUnit, THUMB_CMP_RI8, r0, 0); /* Null? */ + /* When taken r0 has NULL which can be used for store directly */ + ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 4, + ARM_COND_EQ); + /* r1 now contains object->clazz */ + newLIR3(cUnit, THUMB_LDR_RRI5, r1, r0, + offsetof(Object, clazz) >> 2); + loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial); + loadConstant(cUnit, r0, 1); /* Assume true */ + newLIR2(cUnit, THUMB_CMP_RR, r1, r2); + ArmLIR *branch2 = newLIR2(cUnit, THUMB_B_COND, 2, + ARM_COND_EQ); + newLIR2(cUnit, THUMB_MOV_RR, r0, r1); + newLIR2(cUnit, THUMB_MOV_RR, r1, r2); + newLIR1(cUnit, THUMB_BLX_R, r4PC); + /* branch target here */ + ArmLIR *target = newLIR0(cUnit, ARM_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, THUMB_LDR_RRR, fieldOffset); + break; + case OP_IGET_BOOLEAN: + genIGet(cUnit, mir, THUMB_LDRB_RRR, fieldOffset); + break; + case OP_IGET_BYTE: + genIGet(cUnit, mir, THUMB_LDRSB_RRR, fieldOffset); + break; + case OP_IGET_CHAR: + genIGet(cUnit, mir, THUMB_LDRH_RRR, fieldOffset); + break; + case OP_IGET_SHORT: + genIGet(cUnit, mir, THUMB_LDRSH_RRR, fieldOffset); + break; + case OP_IPUT_WIDE: + genIPutWide(cUnit, mir, fieldOffset); + break; + case OP_IPUT: + case OP_IPUT_OBJECT: + genIPut(cUnit, mir, THUMB_STR_RRR, fieldOffset); + break; + case OP_IPUT_SHORT: + case OP_IPUT_CHAR: + genIPut(cUnit, mir, THUMB_STRH_RRR, fieldOffset); + break; + case OP_IPUT_BYTE: + case OP_IPUT_BOOLEAN: + genIPut(cUnit, mir, THUMB_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, THUMB_LDR_RRR, fieldOffset); + break; + case OP_IPUT_QUICK: + case OP_IPUT_OBJECT_QUICK: + genIPut(cUnit, mir, THUMB_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, + ArmLIR *labelList) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + ArmConditionCode cond; + int reg0, reg1; + + if (cUnit->registerScoreboard.liveDalvikReg == (int) mir->dalvikInsn.vA) { + reg0 = selectFirstRegister(cUnit, mir->dalvikInsn.vA, false); + reg1 = NEXT_REG(reg0); + /* Load vB first since vA can be fetched via a move */ + loadValue(cUnit, mir->dalvikInsn.vB, reg1); + loadValue(cUnit, mir->dalvikInsn.vA, reg0); + } else { + reg0 = selectFirstRegister(cUnit, mir->dalvikInsn.vB, false); + reg1 = NEXT_REG(reg0); + /* Load vA first since vB can be fetched via a move */ + loadValue(cUnit, mir->dalvikInsn.vA, reg0); + loadValue(cUnit, mir->dalvikInsn.vB, reg1); + } + newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1); + + 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; + int reg0, reg1, reg2; + + switch (opCode) { + case OP_MOVE_16: + case OP_MOVE_OBJECT_16: + case OP_MOVE_FROM16: + case OP_MOVE_OBJECT_FROM16: { + reg0 = selectFirstRegister(cUnit, vSrc2, false); + reg1 = NEXT_REG(reg0); + loadValue(cUnit, vSrc2, reg0); + storeValue(cUnit, reg0, vSrc1Dest, reg1); + break; + } + case OP_MOVE_WIDE_16: + case OP_MOVE_WIDE_FROM16: { + reg0 = selectFirstRegister(cUnit, vSrc2, true); + reg1 = NEXT_REG(reg0); + reg2 = NEXT_REG(reg1); + loadValuePair(cUnit, vSrc2, reg0, reg1); + storeValuePair(cUnit, reg0, reg1, vSrc1Dest, reg2); + 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; + + /* Don't optimize for register usage since out-of-line handlers are used */ + if ( (opCode >= OP_ADD_INT) && (opCode <= OP_REM_DOUBLE)) { + return genArithOp( cUnit, mir ); + } + + switch (opCode) { + case OP_CMPL_FLOAT: + case OP_CMPG_FLOAT: + case OP_CMPL_DOUBLE: + case OP_CMPG_DOUBLE: + return genCmpX(cUnit, mir, vA, vB, vC); + 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_AGET_WIDE: + genArrayGet(cUnit, mir, THUMB_LDR_RRR, vB, vC, vA, 3); + break; + case OP_AGET: + case OP_AGET_OBJECT: + genArrayGet(cUnit, mir, THUMB_LDR_RRR, vB, vC, vA, 2); + break; + case OP_AGET_BOOLEAN: + genArrayGet(cUnit, mir, THUMB_LDRB_RRR, vB, vC, vA, 0); + break; + case OP_AGET_BYTE: + genArrayGet(cUnit, mir, THUMB_LDRSB_RRR, vB, vC, vA, 0); + break; + case OP_AGET_CHAR: + genArrayGet(cUnit, mir, THUMB_LDRH_RRR, vB, vC, vA, 1); + break; + case OP_AGET_SHORT: + genArrayGet(cUnit, mir, THUMB_LDRSH_RRR, vB, vC, vA, 1); + break; + case OP_APUT_WIDE: + genArrayPut(cUnit, mir, THUMB_STR_RRR, vB, vC, vA, 3); + break; + case OP_APUT: + case OP_APUT_OBJECT: + genArrayPut(cUnit, mir, THUMB_STR_RRR, vB, vC, vA, 2); + break; + case OP_APUT_SHORT: + case OP_APUT_CHAR: + genArrayPut(cUnit, mir, THUMB_STRH_RRR, vB, vC, vA, 1); + break; + case OP_APUT_BYTE: + case OP_APUT_BOOLEAN: + genArrayPut(cUnit, mir, THUMB_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, THUMB_BLX_R, r4PC); + genZeroCheck(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, THUMB_BLX_R, r4PC); + loadConstant(cUnit, r1, (int)(cUnit->method->insns + mir->offset)); + newLIR3(cUnit, THUMB_LDR_RRI5, r2, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNoChain) + >> 2); + newLIR3(cUnit, THUMB_ADD_RRR, r0, r0, r0); + newLIR3(cUnit, THUMB_ADD_RRR, r4PC, r0, r1); + newLIR1(cUnit, THUMB_BLX_R, r2); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + ArmLIR *labelList) +{ + ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; + ArmLIR *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: { + ArmLIR *predChainingCell = &labelList[bb->taken->id]; + 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); + + genInvokeVirtualCommon(cUnit, mir, methodIndex, + retChainingCell, + predChainingCell, + 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); + + genInvokeSingletonCommon(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); + + genInvokeSingletonCommon(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); + + genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* + * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz, + * BBBB, method, method->clazz->pDvmDex) + * + * Given "invoke-interface {v0}", the following is the generated code: + * + * 0x426a9abe : ldr r0, [r5, #0] --+ + * 0x426a9ac0 : mov r7, r5 | + * 0x426a9ac2 : sub r7, #24 | + * 0x426a9ac4 : cmp r0, #0 | genProcessArgsNoRange + * 0x426a9ac6 : beq 0x426a9afe | + * 0x426a9ac8 : stmia r7, <r0> --+ + * 0x426a9aca : ldr r4, [pc, #104] --> r4 <- dalvikPC of this invoke + * 0x426a9acc : add r1, pc, #52 --> r1 <- &retChainingCell + * 0x426a9ace : add r2, pc, #60 --> r2 <- &predictedChainingCell + * 0x426a9ad0 : blx_1 0x426a918c --+ TEMPLATE_INVOKE_METHOD_ + * 0x426a9ad2 : blx_2 see above --+ PREDICTED_CHAIN + * 0x426a9ad4 : b 0x426a9b0c --> off to the predicted chain + * 0x426a9ad6 : b 0x426a9afe --> punt to the interpreter + * 0x426a9ad8 : mov r9, r1 --+ + * 0x426a9ada : mov r10, r2 | + * 0x426a9adc : mov r12, r3 | + * 0x426a9ade : mov r0, r3 | + * 0x426a9ae0 : mov r1, #74 | dvmFindInterfaceMethodInCache + * 0x426a9ae2 : ldr r2, [pc, #76] | + * 0x426a9ae4 : ldr r3, [pc, #68] | + * 0x426a9ae6 : ldr r7, [pc, #64] | + * 0x426a9ae8 : blx r7 --+ + * 0x426a9aea : mov r1, r9 --> r1 <- rechain count + * 0x426a9aec : cmp r1, #0 --> compare against 0 + * 0x426a9aee : bgt 0x426a9af8 --> >=0? don't rechain + * 0x426a9af0 : ldr r7, [r6, #96] --+ + * 0x426a9af2 : mov r2, r10 | dvmJitToPatchPredictedChain + * 0x426a9af4 : mov r3, r12 | + * 0x426a9af6 : blx r7 --+ + * 0x426a9af8 : add r1, pc, #8 --> r1 <- &retChainingCell + * 0x426a9afa : blx_1 0x426a9098 --+ TEMPLATE_INVOKE_METHOD_NO_OPT + * 0x426a9afc : blx_2 see above --+ + * -------- reconstruct dalvik PC : 0x428b786c @ +0x001e + * 0x426a9afe (0042): ldr r0, [pc, #52] + * Exception_Handling: + * 0x426a9b00 (0044): ldr r1, [r6, #84] + * 0x426a9b02 (0046): blx r1 + * 0x426a9b04 (0048): .align4 + * -------- chaining cell (hot): 0x0021 + * 0x426a9b04 (0048): ldr r0, [r6, #92] + * 0x426a9b06 (004a): blx r0 + * 0x426a9b08 (004c): data 0x7872(30834) + * 0x426a9b0a (004e): data 0x428b(17035) + * 0x426a9b0c (0050): .align4 + * -------- chaining cell (predicted) + * 0x426a9b0c (0050): data 0x0000(0) --> will be patched into bx + * 0x426a9b0e (0052): data 0x0000(0) + * 0x426a9b10 (0054): data 0x0000(0) --> class + * 0x426a9b12 (0056): data 0x0000(0) + * 0x426a9b14 (0058): data 0x0000(0) --> method + * 0x426a9b16 (005a): data 0x0000(0) + * 0x426a9b18 (005c): data 0x0000(0) --> reset count + * 0x426a9b1a (005e): data 0x0000(0) + * 0x426a9b28 (006c): .word (0xad0392a5) + * 0x426a9b2c (0070): .word (0x6e750) + * 0x426a9b30 (0074): .word (0x4109a618) + * 0x426a9b34 (0078): .word (0x428b786c) + */ + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: { + ArmLIR *predChainingCell = &labelList[bb->taken->id]; + int methodIndex = dInsn->vB; + + if (mir->dalvikInsn.opCode == OP_INVOKE_INTERFACE) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* "this" is already left in r0 by genProcessArgs* */ + + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r1 = &retChainingCell */ + ArmLIR *addrRetChain = newLIR2(cUnit, THUMB_ADD_PC_REL, + r1, 0); + addrRetChain->generic.target = (LIR *) retChainingCell; + + /* r2 = &predictedChainingCell */ + ArmLIR *predictedChainingCell = + newLIR2(cUnit, THUMB_ADD_PC_REL, r2, 0); + predictedChainingCell->generic.target = (LIR *) predChainingCell; + + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN); + + /* return through lr - jump to the chaining cell */ + genUnconditionalBranch(cUnit, predChainingCell); + + /* + * null-check on "this" may have been eliminated, but we still need + * a PC-reconstruction label for stack overflow bailout. + */ + if (pcrLabel == NULL) { + int dPC = (int) (cUnit->method->insns + mir->offset); + pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); + pcrLabel->opCode = ARM_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); + } + + /* return through lr+2 - punt to the interpreter */ + genUnconditionalBranch(cUnit, pcrLabel); + + /* + * return through lr+4 - fully resolve the callee method. + * r1 <- count + * r2 <- &predictedChainCell + * r3 <- this->class + * r4 <- dPC + * r7 <- this->class->vtable + */ + + /* Save count, &predictedChainCell, and class to high regs first */ + newLIR2(cUnit, THUMB_MOV_RR_L2H, r9 & THUMB_REG_MASK, r1); + newLIR2(cUnit, THUMB_MOV_RR_L2H, r10 & THUMB_REG_MASK, r2); + newLIR2(cUnit, THUMB_MOV_RR_L2H, r12 & THUMB_REG_MASK, r3); + + /* r0 now contains this->clazz */ + newLIR2(cUnit, THUMB_MOV_RR, r0, r3); + + /* 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, THUMB_BLX_R, r7); + + /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */ + + newLIR2(cUnit, THUMB_MOV_RR_H2L, r1, r9 & THUMB_REG_MASK); + + /* Check if rechain limit is reached */ + newLIR2(cUnit, THUMB_CMP_RI8, r1, 0); + + ArmLIR *bypassRechaining = + newLIR2(cUnit, THUMB_B_COND, 0, ARM_COND_GT); + + newLIR3(cUnit, THUMB_LDR_RRI5, r7, rGLUE, + offsetof(InterpState, + jitToInterpEntries.dvmJitToPatchPredictedChain) + >> 2); + + newLIR2(cUnit, THUMB_MOV_RR_H2L, r2, r10 & THUMB_REG_MASK); + newLIR2(cUnit, THUMB_MOV_RR_H2L, r3, r12 & THUMB_REG_MASK); + + /* + * r0 = calleeMethod + * r2 = &predictedChainingCell + * r3 = class + * + * &returnChainingCell has been loaded into r1 but is not needed + * when patching the chaining cell and will be clobbered upon + * returning so it will be reconstructed again. + */ + newLIR1(cUnit, THUMB_BLX_R, r7); + + /* r1 = &retChainingCell */ + addrRetChain = newLIR3(cUnit, THUMB_ADD_PC_REL, + r1, 0, 0); + addrRetChain->generic.target = (LIR *) retChainingCell; + + bypassRechaining->generic.target = (LIR *) addrRetChain; + + /* + * r0 = this, r1 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokePredictedChain++; +#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, ArmLIR *labelList) +{ + ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; + ArmLIR *predChainingCell = &labelList[bb->taken->id]; + ArmLIR *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); + + genInvokeVirtualCommon(cUnit, mir, methodIndex, + retChainingCell, + predChainingCell, + pcrLabel); + 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); + + genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + break; + } + default: + return true; + } + 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 = offsetof(InterpState, retval); + int operation = dInsn->vB; + + switch (operation) { + case INLINE_EMPTYINLINEMETHOD: + return false; /* Nop */ + case INLINE_STRING_LENGTH: + return genInlinedStringLength(cUnit, mir); + case INLINE_MATH_ABS_INT: + return genInlinedAbsInt(cUnit, mir); + case INLINE_MATH_ABS_LONG: + return genInlinedAbsLong(cUnit, mir); + case INLINE_MATH_MIN_INT: + return genInlinedMinMaxInt(cUnit, mir, true); + case INLINE_MATH_MAX_INT: + return genInlinedMinMaxInt(cUnit, mir, false); + case INLINE_STRING_CHARAT: + return genInlinedStringCharAt(cUnit, mir); + case INLINE_MATH_SQRT: + if (genInlineSqrt(cUnit, mir)) + return true; + else + break; /* Handle with C routine */ + case INLINE_MATH_COS: + if (genInlineCos(cUnit, mir)) + return true; + else + break; /* Handle with C routine */ + case INLINE_MATH_SIN: + if (genInlineSin(cUnit, mir)) + return true; + else + break; /* Handle with C routine */ + case INLINE_MATH_ABS_FLOAT: + return genInlinedAbsFloat(cUnit, mir); + case INLINE_MATH_ABS_DOUBLE: + return genInlinedAbsDouble(cUnit, mir); + case INLINE_STRING_COMPARETO: + case INLINE_STRING_EQUALS: + break; + default: + dvmAbort(); + } + + /* Materialize pointer to retval & push */ + newLIR2(cUnit, THUMB_MOV_RR, r4PC, rGLUE); + newLIR2(cUnit, THUMB_ADD_RI8, r4PC, offset); + /* Push r4 and (just to take up space) r5) */ + newLIR1(cUnit, THUMB_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, THUMB_BLX_R, r4PC); + + /* Strip frame */ + newLIR1(cUnit, THUMB_ADD_SPI7, 2); + + /* Did we throw? If so, redo under interpreter*/ + genZeroCheck(cUnit, r0, mir->offset, NULL); + + resetRegisterScoreboard(cUnit); + 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 code that may need warmup. */ +static void handleNormalChainingCell(CompilationUnit *cUnit, + unsigned int offset) +{ + newLIR3(cUnit, THUMB_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2); + newLIR1(cUnit, THUMB_BLX_R, r0); + addWordData(cUnit, (int) (cUnit->method->insns + offset), true); +} + +/* + * Chaining cell for instructions that immediately following already translated + * code. + */ +static void handleHotChainingCell(CompilationUnit *cUnit, + unsigned int offset) +{ + newLIR3(cUnit, THUMB_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2); + newLIR1(cUnit, THUMB_BLX_R, r0); + addWordData(cUnit, (int) (cUnit->method->insns + offset), true); +} + +/* Chaining cell for monomorphic method invocations. */ +static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit, + const Method *callee) +{ + newLIR3(cUnit, THUMB_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2); + newLIR1(cUnit, THUMB_BLX_R, r0); + addWordData(cUnit, (int) (callee->insns), true); +} + +/* Chaining cell for monomorphic method invocations. */ +static void handleInvokePredictedChainingCell(CompilationUnit *cUnit) +{ + + /* Should not be executed in the initial state */ + addWordData(cUnit, PREDICTED_CHAIN_BX_PAIR_INIT, true); + /* To be filled: class */ + addWordData(cUnit, PREDICTED_CHAIN_CLAZZ_INIT, true); + /* To be filled: method */ + addWordData(cUnit, PREDICTED_CHAIN_METHOD_INIT, true); + /* + * Rechain count. The initial value of 0 here will trigger chaining upon + * the first invocation of this callsite. + */ + addWordData(cUnit, PREDICTED_CHAIN_COUNTER_INIT, true); +} + +/* Load the Dalvik PC into r0 and jump to the specified target */ +static void handlePCReconstruction(CompilationUnit *cUnit, + ArmLIR *targetLabel) +{ + ArmLIR **pcrLabel = + (ArmLIR **) 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 */ + ArmLIR *labelList = + dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true); + GrowableList chainingListByType[CHAINING_CELL_LAST]; + int i; + + /* + * Initialize various types chaining lists. + */ + for (i = 0; i < CHAINING_CELL_LAST; i++) { + dvmInitGrowableList(&chainingListByType[i], 2); + } + + BasicBlock **blockList = cUnit->blockList; + + if (cUnit->executionCount) { + /* + * Reserve 6 bytes at the beginning of the trace + * +----------------------------+ + * | execution count (4 bytes) | + * +----------------------------+ + * | chain cell offset (2 bytes)| + * +----------------------------+ + * ...and then code to increment the execution + * count: + * mov r0, pc @ move adr of "mov r0,pc" + 4 to r0 + * sub r0, #10 @ back up to addr of executionCount + * ldr r1, [r0] + * add r1, #1 + * str r1, [r0] + */ + newLIR1(cUnit, ARM_16BIT_DATA, 0); + newLIR1(cUnit, ARM_16BIT_DATA, 0); + cUnit->chainCellOffsetLIR = + (LIR *) newLIR1(cUnit, ARM_16BIT_DATA, CHAIN_CELL_OFFSET_TAG); + cUnit->headerSize = 6; + newLIR2(cUnit, THUMB_MOV_RR_H2L, r0, rpc & THUMB_REG_MASK); + newLIR2(cUnit, THUMB_SUB_RI8, r0, 10); + newLIR3(cUnit, THUMB_LDR_RRI5, r1, r0, 0); + newLIR2(cUnit, THUMB_ADD_RI8, r1, 1); + newLIR3(cUnit, THUMB_STR_RRI5, r1, r0, 0); + } else { + /* Just reserve 2 bytes for the chain cell offset */ + cUnit->chainCellOffsetLIR = + (LIR *) newLIR1(cUnit, ARM_16BIT_DATA, CHAIN_CELL_OFFSET_TAG); + cUnit->headerSize = 2; + } + + /* 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 = ARM_PSEUDO_NORMAL_BLOCK_LABEL; + /* Reset the register state */ + resetRegisterScoreboard(cUnit); + } else { + switch (blockList[i]->blockType) { + case CHAINING_CELL_NORMAL: + labelList[i].opCode = ARM_PSEUDO_CHAINING_CELL_NORMAL; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_NORMAL], (void *) i); + break; + case CHAINING_CELL_INVOKE_SINGLETON: + labelList[i].opCode = + ARM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON; + labelList[i].operands[0] = + (int) blockList[i]->containingMethod; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_INVOKE_SINGLETON], + (void *) i); + break; + case CHAINING_CELL_INVOKE_PREDICTED: + labelList[i].opCode = + ARM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_INVOKE_PREDICTED], + (void *) i); + break; + case CHAINING_CELL_HOT: + labelList[i].opCode = + ARM_PSEUDO_CHAINING_CELL_HOT; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_HOT], + (void *) i); + break; + case PC_RECONSTRUCTION: + /* Make sure exception handling block is next */ + labelList[i].opCode = + ARM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL; + assert (i == cUnit->numBlocks - 2); + handlePCReconstruction(cUnit, &labelList[i+1]); + break; + case EXCEPTION_HANDLING: + labelList[i].opCode = ARM_PSEUDO_EH_BLOCK_LABEL; + if (cUnit->pcReconstructionList.numUsed) { + newLIR3(cUnit, THUMB_LDR_RRI5, r1, rGLUE, + offsetof(InterpState, + jitToInterpEntries.dvmJitToInterpPunt) + >> 2); + newLIR1(cUnit, THUMB_BLX_R, r1); + } + break; + default: + break; + } + continue; + } + + ArmLIR *headLIR = NULL; + + for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) { + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + InstructionFormat dalvikFormat = + dexGetInstrFormat(gDvm.instrFormat, dalvikOpCode); + ArmLIR *boundaryLIR = + newLIR2(cUnit, ARM_PSEUDO_DALVIK_BYTECODE_BOUNDARY, + mir->offset,dalvikOpCode); + /* Remember the first LIR for this block */ + if (headLIR == NULL) { + headLIR = boundaryLIR; + } + 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; + } + } + /* Eliminate redundant loads/stores and delay stores into later slots */ + dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR, + cUnit->lastLIRInsn); + /* + * Check if the block is terminated due to trace length constraint - + * insert an unconditional branch to the chaining cell. + */ + if (blockList[i]->needFallThroughBranch) { + genUnconditionalBranch(cUnit, + &labelList[blockList[i]->fallThrough->id]); + } + + } + + /* Handle the chaining cells 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, ARM_PSEUDO_ALIGN4); + + /* Insert the pseudo chaining instruction */ + dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]); + + + switch (blockList[blockId]->blockType) { + case CHAINING_CELL_NORMAL: + handleNormalChainingCell(cUnit, + blockList[blockId]->startOffset); + break; + case CHAINING_CELL_INVOKE_SINGLETON: + handleInvokeSingletonChainingCell(cUnit, + blockList[blockId]->containingMethod); + break; + case CHAINING_CELL_INVOKE_PREDICTED: + handleInvokePredictedChainingCell(cUnit); + break; + case CHAINING_CELL_HOT: + handleHotChainingCell(cUnit, + blockList[blockId]->startOffset); + break; + default: + dvmAbort(); + break; + } + } + } + + dvmCompilerApplyGlobalOptimizations(cUnit); +} + +/* Accept the work and start compiling */ +bool dvmCompilerDoWork(CompilerWorkOrder *work) +{ + bool res; + + if (gDvmJit.codeCacheFull) { + return false; + } + + switch (work->kind) { + case kWorkOrderMethod: + res = dvmCompileMethod(work->info, &work->result); + break; + case kWorkOrderTrace: + /* Start compilation with maximally allowed trace length */ + res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result); + break; + default: + res = false; + dvmAbort(); + } + return res; +} + +/* 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.jit.op = %s", buf); + } +} diff --git a/vm/compiler/codegen/arm/GlobalOptimizations.c b/vm/compiler/codegen/arm/GlobalOptimizations.c new file mode 100644 index 000000000..40e1f0786 --- /dev/null +++ b/vm/compiler/codegen/arm/GlobalOptimizations.c @@ -0,0 +1,62 @@ +/* + * 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 "vm/compiler/CompilerInternals.h" +#include "ArmLIR.h" + +/* + * Identify unconditional branches that jump to the immediate successor of the + * branch itself. + */ +static void applyRedundantBranchElimination(CompilationUnit *cUnit) +{ + ArmLIR *thisLIR; + + for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn; + thisLIR != (ArmLIR *) cUnit->lastLIRInsn; + thisLIR = NEXT_LIR(thisLIR)) { + + /* Branch to the next instruction */ + if (thisLIR->opCode == THUMB_B_UNCOND) { + ArmLIR *nextLIR = thisLIR; + + while (true) { + nextLIR = NEXT_LIR(nextLIR); + + /* + * Is the branch target the next instruction? + */ + if (nextLIR == (ArmLIR *) thisLIR->generic.target) { + thisLIR->isNop = true; + break; + } + + /* + * Found real useful stuff between the branch and the target + */ + if (!isPseudoOpCode(nextLIR->opCode) || + nextLIR->opCode == ARM_PSEUDO_ALIGN4) + break; + } + } + } +} + +void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit) +{ + applyRedundantBranchElimination(cUnit); +} diff --git a/vm/compiler/codegen/arm/LocalOptimizations.c b/vm/compiler/codegen/arm/LocalOptimizations.c new file mode 100644 index 000000000..30b9d8610 --- /dev/null +++ b/vm/compiler/codegen/arm/LocalOptimizations.c @@ -0,0 +1,139 @@ +/* + * 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 "vm/compiler/CompilerInternals.h" +#include "ArmLIR.h" + +/* + * Perform a pass of top-down walk to + * 1) Eliminate redundant loads and stores + * 2) Sink stores to latest possible slot + */ +static void applyLoadStoreElimination(CompilationUnit *cUnit, + ArmLIR *headLIR, + ArmLIR *tailLIR) +{ + ArmLIR *thisLIR; + + cUnit->optRound++; + for (thisLIR = headLIR; + thisLIR != tailLIR; + thisLIR = NEXT_LIR(thisLIR)) { + /* Skip newly added instructions */ + if (thisLIR->age >= cUnit->optRound) { + continue; + } + if (thisLIR->opCode == THUMB_STR_RRI5 && + thisLIR->operands[1] == rFP) { + int dRegId = thisLIR->operands[2]; + int nativeRegId = thisLIR->operands[0]; + ArmLIR *checkLIR; + int sinkDistance = 0; + + for (checkLIR = NEXT_LIR(thisLIR); + checkLIR != tailLIR; + checkLIR = NEXT_LIR(checkLIR)) { + + /* Check if a Dalvik register load is redundant */ + if (checkLIR->opCode == THUMB_LDR_RRI5 && + checkLIR->operands[1] == rFP && + checkLIR->operands[2] == dRegId) { + /* Insert a move to replace the load */ + if (checkLIR->operands[0] != nativeRegId) { + ArmLIR *moveLIR = + dvmCompilerNew(sizeof(ArmLIR), true); + moveLIR->opCode = THUMB_MOV_RR; + moveLIR->operands[0] = checkLIR->operands[0]; + moveLIR->operands[1] = nativeRegId; + /* + * Insertion is guaranteed to succeed since checkLIR + * is never the first LIR on the list + */ + dvmCompilerInsertLIRBefore((LIR *) checkLIR, + (LIR *) moveLIR); + } + checkLIR->isNop = true; + continue; + + /* Found a true output dependency - nuke the previous store */ + } else if (checkLIR->opCode == THUMB_STR_RRI5 && + checkLIR->operands[1] == rFP && + checkLIR->operands[2] == dRegId) { + thisLIR->isNop = true; + break; + /* Find out the latest slot that the store can be sunk into */ + } else { + bool stopHere = false; + + /* Last instruction reached */ + stopHere |= checkLIR->generic.next == NULL; + + /* Store data is clobbered */ + stopHere |= (EncodingMap[checkLIR->opCode].flags & + CLOBBER_DEST) != 0 && + checkLIR->operands[0] == nativeRegId; + /* + * Conservatively assume there is a memory dependency + * for st/ld multiples and reg+reg address mode + */ + stopHere |= checkLIR->opCode == THUMB_STMIA || + checkLIR->opCode == THUMB_LDMIA || + checkLIR->opCode == THUMB_STR_RRR || + checkLIR->opCode == THUMB_LDR_RRR; + + stopHere |= (EncodingMap[checkLIR->opCode].flags & + IS_BRANCH) != 0; + + /* Found a new place to put the store - move it here */ + if (stopHere == true) { + + /* The store can be sunk for at least one cycle */ + if (sinkDistance != 0) { + ArmLIR *newStoreLIR = + dvmCompilerNew(sizeof(ArmLIR), true); + *newStoreLIR = *thisLIR; + newStoreLIR->age = cUnit->optRound; + /* + * Insertion is guaranteed to succeed since checkLIR + * is never the first LIR on the list + */ + dvmCompilerInsertLIRBefore((LIR *) checkLIR, + (LIR *) newStoreLIR); + thisLIR->isNop = true; + } + break; + } + + /* + * Saw a real instruction that the store can be sunk after + */ + if (!isPseudoOpCode(checkLIR->opCode)) { + sinkDistance++; + } + } + } + } + } +} + +void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR, + LIR *tailLIR) +{ + applyLoadStoreElimination(cUnit, + (ArmLIR *) headLIR, + (ArmLIR *) tailLIR); +} diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c new file mode 100644 index 000000000..6c5b0103e --- /dev/null +++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c @@ -0,0 +1,291 @@ +/* + * 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. + */ + +/* + * This file is included by Codegen-armv5te-vfp.c, and implements architecture + * variant-specific code. + */ + +#define USE_IN_CACHE_HANDLER 1 + +/* + * Determine the initial instruction set to be used for this trace. + * Later components may decide to change this. + */ +JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit) +{ + return DALVIK_JIT_THUMB; +} + +/* + * 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, THUMB_BLX_1, + (int) gDvmJit.codeCache + templateEntryOffsets[opCode], + (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); + newLIR2(cUnit, THUMB_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-vfp/TemplateOpList.h" +#undef JIT_TEMPLATE + switch (opCode) { +#define JIT_TEMPLATE(X) \ + case TEMPLATE_##X: { templatePtr = dvmCompiler_TEMPLATE_##X; break; } +#include "../../../template/armv5te-vfp/TemplateOpList.h" +#undef JIT_TEMPLATE + default: templatePtr = NULL; + } + loadConstant(cUnit, r7, (int) templatePtr); + newLIR1(cUnit, THUMB_BLX_R, r7); +#endif +} + +/* 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-vfp/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-vfp/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; +} + +static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir) +{ + int offset = offsetof(InterpState, retval); + OpCode opCode = mir->dalvikInsn.opCode; + int vSrc = mir->dalvikInsn.vA; + loadValueAddress(cUnit, vSrc, r2); + genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP); + newLIR3(cUnit, THUMB_STR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, THUMB_STR_RRI5, r1, rGLUE, (offset >> 2) + 1); + return false; +} + +static bool genInlineCos(CompilationUnit *cUnit, MIR *mir) +{ + return false; +} + +static bool genInlineSin(CompilationUnit *cUnit, MIR *mir) +{ + return false; +} + +static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + TemplateOpCode opCode; + + /* + * Don't attempt to optimize register usage since these opcodes call out to + * the handlers. + */ + switch (mir->dalvikInsn.opCode) { + case OP_ADD_FLOAT_2ADDR: + case OP_ADD_FLOAT: + opCode = TEMPLATE_ADD_FLOAT_VFP; + break; + case OP_SUB_FLOAT_2ADDR: + case OP_SUB_FLOAT: + opCode = TEMPLATE_SUB_FLOAT_VFP; + break; + case OP_DIV_FLOAT_2ADDR: + case OP_DIV_FLOAT: + opCode = TEMPLATE_DIV_FLOAT_VFP; + break; + case OP_MUL_FLOAT_2ADDR: + case OP_MUL_FLOAT: + opCode = TEMPLATE_MUL_FLOAT_VFP; + break; + case OP_REM_FLOAT_2ADDR: + case OP_REM_FLOAT: + case OP_NEG_FLOAT: { + return genArithOpFloatPortable(cUnit, mir, vDest, + vSrc1, vSrc2); + } + default: + return true; + } + loadValueAddress(cUnit, vDest, r0); + loadValueAddress(cUnit, vSrc1, r1); + loadValueAddress(cUnit, vSrc2, r2); + genDispatchToHandler(cUnit, opCode); + return false; +} + +static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + TemplateOpCode opCode; + + /* + * Don't attempt to optimize register usage since these opcodes call out to + * the handlers. + */ + switch (mir->dalvikInsn.opCode) { + case OP_ADD_DOUBLE_2ADDR: + case OP_ADD_DOUBLE: + opCode = TEMPLATE_ADD_DOUBLE_VFP; + break; + case OP_SUB_DOUBLE_2ADDR: + case OP_SUB_DOUBLE: + opCode = TEMPLATE_SUB_DOUBLE_VFP; + break; + case OP_DIV_DOUBLE_2ADDR: + case OP_DIV_DOUBLE: + opCode = TEMPLATE_DIV_DOUBLE_VFP; + break; + case OP_MUL_DOUBLE_2ADDR: + case OP_MUL_DOUBLE: + opCode = TEMPLATE_MUL_DOUBLE_VFP; + break; + case OP_REM_DOUBLE_2ADDR: + case OP_REM_DOUBLE: + case OP_NEG_DOUBLE: { + return genArithOpDoublePortable(cUnit, mir, vDest, + vSrc1, vSrc2); + } + default: + return true; + } + loadValueAddress(cUnit, vDest, r0); + loadValueAddress(cUnit, vSrc1, r1); + loadValueAddress(cUnit, vSrc2, r2); + genDispatchToHandler(cUnit, opCode); + return false; +} + +static bool genConversion(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vSrc1Dest = mir->dalvikInsn.vA; + int vSrc2 = mir->dalvikInsn.vB; + TemplateOpCode template; + + switch (opCode) { + case OP_INT_TO_FLOAT: + template = TEMPLATE_INT_TO_FLOAT_VFP; + break; + case OP_FLOAT_TO_INT: + template = TEMPLATE_FLOAT_TO_INT_VFP; + break; + case OP_DOUBLE_TO_FLOAT: + template = TEMPLATE_DOUBLE_TO_FLOAT_VFP; + break; + case OP_FLOAT_TO_DOUBLE: + template = TEMPLATE_FLOAT_TO_DOUBLE_VFP; + break; + case OP_INT_TO_DOUBLE: + template = TEMPLATE_INT_TO_DOUBLE_VFP; + break; + case OP_DOUBLE_TO_INT: + template = TEMPLATE_DOUBLE_TO_INT_VFP; + break; + case OP_FLOAT_TO_LONG: + case OP_LONG_TO_FLOAT: + case OP_DOUBLE_TO_LONG: + case OP_LONG_TO_DOUBLE: + return genConversionPortable(cUnit, mir); + default: + return true; + } + loadValueAddress(cUnit, vSrc1Dest, r0); + loadValueAddress(cUnit, vSrc2, r1); + genDispatchToHandler(cUnit, template); + return false; +} + +static bool genCmpX(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1, + int vSrc2) +{ + TemplateOpCode template; + + /* + * Don't attempt to optimize register usage since these opcodes call out to + * the handlers. + */ + switch(mir->dalvikInsn.opCode) { + case OP_CMPL_FLOAT: + template = TEMPLATE_CMPL_FLOAT_VFP; + break; + case OP_CMPG_FLOAT: + template = TEMPLATE_CMPG_FLOAT_VFP; + break; + case OP_CMPL_DOUBLE: + template = TEMPLATE_CMPL_DOUBLE_VFP; + break; + case OP_CMPG_DOUBLE: + template = TEMPLATE_CMPG_DOUBLE_VFP; + break; + default: + return true; + } + loadValueAddress(cUnit, vSrc1, r0); + loadValueAddress(cUnit, vSrc2, r1); + genDispatchToHandler(cUnit, template); + storeValue(cUnit, r0, vDest, r1); + return false; +} diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h new file mode 100644 index 000000000..9f862e840 --- /dev/null +++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H +#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H + +/* Create the TemplateOpcode enum */ +#define JIT_TEMPLATE(X) TEMPLATE_##X, +typedef enum { +#include "../../../template/armv5te-vfp/TemplateOpList.h" +/* + * For example, + * TEMPLATE_CMP_LONG, + * TEMPLATE_RETURN, + * ... + */ + TEMPLATE_LAST_MARK, +} TemplateOpCode; +#undef JIT_TEMPLATE + +#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H */ diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.c b/vm/compiler/codegen/arm/armv5te/ArchVariant.c new file mode 100644 index 000000000..a1f2b006c --- /dev/null +++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.c @@ -0,0 +1,183 @@ +/* + * 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. + */ + +/* + * This file is included by Codegen-armv5te.c, and implements architecture + * variant-specific code. + */ + +#define USE_IN_CACHE_HANDLER 1 + +/* + * Determine the initial instruction set to be used for this trace. + * Later components may decide to change this. + */ +JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit) +{ + return DALVIK_JIT_THUMB; +} + +/* + * 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, THUMB_BLX_1, + (int) gDvmJit.codeCache + templateEntryOffsets[opCode], + (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); + newLIR2(cUnit, THUMB_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, THUMB_BLX_R, r7); +#endif +} + +/* 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; +} + +static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir) +{ + return false; /* punt to C handler */ +} + +static bool genInlineCos(CompilationUnit *cUnit, MIR *mir) +{ + return false; /* punt to C handler */ +} + +static bool genInlineSin(CompilationUnit *cUnit, MIR *mir) +{ + return false; /* punt to C handler */ +} + +static bool genConversion(CompilationUnit *cUnit, MIR *mir) +{ + return genConversionPortable(cUnit, mir); +} + +static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + return genArithOpFloatPortable(cUnit, mir, vDest, vSrc1, vSrc2); +} + +static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + return genArithOpDoublePortable(cUnit, mir, vDest, vSrc1, vSrc2); +} + +static bool genCmpX(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1, + int vSrc2) +{ + /* + * Don't attempt to optimize register usage since these opcodes call out to + * the handlers. + */ + switch (mir->dalvikInsn.opCode) { + case OP_CMPL_FLOAT: + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT); + storeValue(cUnit, r0, vDest, r1); + break; + case OP_CMPG_FLOAT: + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT); + storeValue(cUnit, r0, vDest, r1); + break; + case OP_CMPL_DOUBLE: + loadValueAddress(cUnit, vSrc1, r0); + loadValueAddress(cUnit, vSrc2, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE); + storeValue(cUnit, r0, vDest, r1); + break; + case OP_CMPG_DOUBLE: + loadValueAddress(cUnit, vSrc1, r0); + loadValueAddress(cUnit, vSrc2, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE); + storeValue(cUnit, r0, vDest, r1); + break; + default: + return true; + } + return false; +} diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.h b/vm/compiler/codegen/arm/armv5te/ArchVariant.h new file mode 100644 index 000000000..6420df719 --- /dev/null +++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H +#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_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 + +#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H */ |