Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees
to prevent dependency creep. They can now be individually built
without rebuilding the rest of the art projects. dalvikvm and jdwpspy
were already this way. Builds in the art directory should behave as
before, building everything including tests.
Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
new file mode 100644
index 0000000..9dd7daf
--- /dev/null
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_ARM_ARMLIR_H_
+#define ART_SRC_COMPILER_DEX_QUICK_ARM_ARMLIR_H_
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+/*
+ * Runtime register usage conventions.
+ *
+ * r0-r3: Argument registers in both Dalvik and C/C++ conventions.
+ * However, for Dalvik->Dalvik calls we'll pass the target's Method*
+ * pointer in r0 as a hidden arg0. Otherwise used as codegen scratch
+ * registers.
+ * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit
+ * r4 : (rARM_SUSPEND) is reserved (suspend check/debugger assist)
+ * r5 : Callee save (promotion target)
+ * r6 : Callee save (promotion target)
+ * r7 : Callee save (promotion target)
+ * r8 : Callee save (promotion target)
+ * r9 : (rARM_SELF) is reserved (pointer to thread-local storage)
+ * r10 : Callee save (promotion target)
+ * r11 : Callee save (promotion target)
+ * r12 : Scratch, may be trashed by linkage stubs
+ * r13 : (sp) is reserved
+ * r14 : (lr) is reserved
+ * r15 : (pc) is reserved
+ *
+ * 5 core temps that codegen can use (r0, r1, r2, r3, r12)
+ * 7 core registers that can be used for promotion
+ *
+ * Floating pointer registers
+ * s0-s31
+ * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ *
+ * s16-s31 (d8-d15) preserved across C calls
+ * s0-s15 (d0-d7) trashed across C calls
+ *
+ * s0-s15/d0-d7 used as codegen temp/scratch
+ * s16-s31/d8-d31 can be used for promotion.
+ *
+ * Calling convention
+ * o On a call to a Dalvik method, pass target's Method* in r0
+ * o r1-r3 will be used for up to the first 3 words of arguments
+ * o Arguments past the first 3 words will be placed in appropriate
+ * out slots by the caller.
+ * o If a 64-bit argument would span the register/memory argument
+ * boundary, it will instead be fully passed in the frame.
+ * o Maintain a 16-byte stack alignment
+ *
+ * Stack frame diagram (stack grows down, higher addresses at top):
+ *
+ * +------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's Method* |
+ * +========================+ {Note: start of callee's frame}
+ * | spill region | {variable sized - will include lr if non-leaf.}
+ * +------------------------+
+ * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
+ * +------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . |
+ * | V[1] |
+ * | V[0] |
+ * +------------------------+
+ * | 0 to 3 words padding |
+ * +------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | cur_method* | <<== sp w/ 16-byte alignment
+ * +========================+
+ */
+
+// Offset to distingish FP regs.
+#define ARM_FP_REG_OFFSET 32
+// Offset to distinguish DP FP regs.
+#define ARM_FP_DOUBLE 64
+// First FP callee save.
+#define ARM_FP_CALLEE_SAVE_BASE 16
+// Reg types.
+#define ARM_REGTYPE(x) (x & (ARM_FP_REG_OFFSET | ARM_FP_DOUBLE))
+#define ARM_FPREG(x) ((x & ARM_FP_REG_OFFSET) == ARM_FP_REG_OFFSET)
+#define ARM_LOWREG(x) ((x & 0x7) == x)
+#define ARM_DOUBLEREG(x) ((x & ARM_FP_DOUBLE) == ARM_FP_DOUBLE)
+#define ARM_SINGLEREG(x) (ARM_FPREG(x) && !ARM_DOUBLEREG(x))
+
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area. Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define ARM_S2D(x,y) ((x) | ARM_FP_DOUBLE)
+// Mask to strip off fp flags.
+#define ARM_FP_REG_MASK (ARM_FP_REG_OFFSET-1)
+
+// RegisterLocation templates return values (r0, or r0/r1).
+#define ARM_LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r0, INVALID_REG,\
+ INVALID_SREG, INVALID_SREG}
+#define ARM_LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1, \
+ INVALID_SREG, INVALID_SREG}
+#define ARM_LOC_C_RETURN_FLOAT ARM_LOC_C_RETURN
+#define ARM_LOC_C_RETURN_DOUBLE ARM_LOC_C_RETURN_WIDE
+
+enum ArmResourceEncodingPos {
+ kArmGPReg0 = 0,
+ kArmRegSP = 13,
+ kArmRegLR = 14,
+ kArmRegPC = 15,
+ kArmFPReg0 = 16,
+ kArmFPReg16 = 32,
+ kArmRegEnd = 48,
+};
+
+#define ENCODE_ARM_REG_LIST(N) (static_cast<uint64_t>(N))
+#define ENCODE_ARM_REG_SP (1ULL << kArmRegSP)
+#define ENCODE_ARM_REG_LR (1ULL << kArmRegLR)
+#define ENCODE_ARM_REG_PC (1ULL << kArmRegPC)
+#define ENCODE_ARM_REG_FPCS_LIST(N) (static_cast<uint64_t>(N) << kArmFPReg16)
+
+enum ArmNativeRegisterPool {
+ r0 = 0,
+ r1 = 1,
+ r2 = 2,
+ r3 = 3,
+ rARM_SUSPEND = 4,
+ r5 = 5,
+ r6 = 6,
+ r7 = 7,
+ r8 = 8,
+ rARM_SELF = 9,
+ r10 = 10,
+ r11 = 11,
+ r12 = 12,
+ r13sp = 13,
+ rARM_SP = 13,
+ r14lr = 14,
+ rARM_LR = 14,
+ r15pc = 15,
+ rARM_PC = 15,
+ fr0 = 0 + ARM_FP_REG_OFFSET,
+ fr1 = 1 + ARM_FP_REG_OFFSET,
+ fr2 = 2 + ARM_FP_REG_OFFSET,
+ fr3 = 3 + ARM_FP_REG_OFFSET,
+ fr4 = 4 + ARM_FP_REG_OFFSET,
+ fr5 = 5 + ARM_FP_REG_OFFSET,
+ fr6 = 6 + ARM_FP_REG_OFFSET,
+ fr7 = 7 + ARM_FP_REG_OFFSET,
+ fr8 = 8 + ARM_FP_REG_OFFSET,
+ fr9 = 9 + ARM_FP_REG_OFFSET,
+ fr10 = 10 + ARM_FP_REG_OFFSET,
+ fr11 = 11 + ARM_FP_REG_OFFSET,
+ fr12 = 12 + ARM_FP_REG_OFFSET,
+ fr13 = 13 + ARM_FP_REG_OFFSET,
+ fr14 = 14 + ARM_FP_REG_OFFSET,
+ fr15 = 15 + ARM_FP_REG_OFFSET,
+ fr16 = 16 + ARM_FP_REG_OFFSET,
+ fr17 = 17 + ARM_FP_REG_OFFSET,
+ fr18 = 18 + ARM_FP_REG_OFFSET,
+ fr19 = 19 + ARM_FP_REG_OFFSET,
+ fr20 = 20 + ARM_FP_REG_OFFSET,
+ fr21 = 21 + ARM_FP_REG_OFFSET,
+ fr22 = 22 + ARM_FP_REG_OFFSET,
+ fr23 = 23 + ARM_FP_REG_OFFSET,
+ fr24 = 24 + ARM_FP_REG_OFFSET,
+ fr25 = 25 + ARM_FP_REG_OFFSET,
+ fr26 = 26 + ARM_FP_REG_OFFSET,
+ fr27 = 27 + ARM_FP_REG_OFFSET,
+ fr28 = 28 + ARM_FP_REG_OFFSET,
+ fr29 = 29 + ARM_FP_REG_OFFSET,
+ fr30 = 30 + ARM_FP_REG_OFFSET,
+ fr31 = 31 + ARM_FP_REG_OFFSET,
+ dr0 = fr0 + ARM_FP_DOUBLE,
+ dr1 = fr2 + ARM_FP_DOUBLE,
+ dr2 = fr4 + ARM_FP_DOUBLE,
+ dr3 = fr6 + ARM_FP_DOUBLE,
+ dr4 = fr8 + ARM_FP_DOUBLE,
+ dr5 = fr10 + ARM_FP_DOUBLE,
+ dr6 = fr12 + ARM_FP_DOUBLE,
+ dr7 = fr14 + ARM_FP_DOUBLE,
+ dr8 = fr16 + ARM_FP_DOUBLE,
+ dr9 = fr18 + ARM_FP_DOUBLE,
+ dr10 = fr20 + ARM_FP_DOUBLE,
+ dr11 = fr22 + ARM_FP_DOUBLE,
+ dr12 = fr24 + ARM_FP_DOUBLE,
+ dr13 = fr26 + ARM_FP_DOUBLE,
+ dr14 = fr28 + ARM_FP_DOUBLE,
+ dr15 = fr30 + ARM_FP_DOUBLE,
+};
+
+// Target-independent aliases.
+#define rARM_ARG0 r0
+#define rARM_ARG1 r1
+#define rARM_ARG2 r2
+#define rARM_ARG3 r3
+#define rARM_FARG0 r0
+#define rARM_FARG1 r1
+#define rARM_FARG2 r2
+#define rARM_FARG3 r3
+#define rARM_RET0 r0
+#define rARM_RET1 r1
+#define rARM_INVOKE_TGT rARM_LR
+#define rARM_COUNT INVALID_REG
+
+enum ArmShiftEncodings {
+ kArmLsl = 0x0,
+ kArmLsr = 0x1,
+ kArmAsr = 0x2,
+ kArmRor = 0x3
+};
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding EncodingMap positions will be defined in
+ * Assemble.cc.
+ */
+enum ArmOpcode {
+ kArmFirst = 0,
+ kArm16BitData = kArmFirst, // DATA [0] rd[15..0].
+ kThumbAdcRR, // adc [0100000101] rm[5..3] rd[2..0].
+ kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/
+ kThumbAddRI8, // add(2) [00110] rd[10..8] imm_8[7..0].
+ kThumbAddRRR, // add(3) [0001100] rm[8..6] rn[5..3] rd[2..0].
+ kThumbAddRRLH, // add(4) [01000100] H12[01] rm[5..3] rd[2..0].
+ kThumbAddRRHL, // add(4) [01001000] H12[10] rm[5..3] rd[2..0].
+ kThumbAddRRHH, // add(4) [01001100] H12[11] rm[5..3] rd[2..0].
+ kThumbAddPcRel, // add(5) [10100] rd[10..8] imm_8[7..0].
+ kThumbAddSpRel, // add(6) [10101] rd[10..8] imm_8[7..0].
+ kThumbAddSpI7, // add(7) [101100000] imm_7[6..0].
+ kThumbAndRR, // and [0100000000] rm[5..3] rd[2..0].
+ kThumbAsrRRI5, // asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0].
+ kThumbAsrRR, // asr(2) [0100000100] rs[5..3] rd[2..0].
+ kThumbBCond, // b(1) [1101] cond[11..8] offset_8[7..0].
+ kThumbBUncond, // b(2) [11100] offset_11[10..0].
+ kThumbBicRR, // bic [0100001110] rm[5..3] rd[2..0].
+ kThumbBkpt, // bkpt [10111110] imm_8[7..0].
+ kThumbBlx1, // blx(1) [111] H[10] offset_11[10..0].
+ kThumbBlx2, // blx(1) [111] H[01] offset_11[10..0].
+ kThumbBl1, // blx(1) [111] H[10] offset_11[10..0].
+ kThumbBl2, // blx(1) [111] H[11] offset_11[10..0].
+ kThumbBlxR, // blx(2) [010001111] rm[6..3] [000].
+ kThumbBx, // bx [010001110] H2[6..6] rm[5..3] SBZ[000].
+ kThumbCmnRR, // cmn [0100001011] rm[5..3] rd[2..0].
+ kThumbCmpRI8, // cmp(1) [00101] rn[10..8] imm_8[7..0].
+ kThumbCmpRR, // cmp(2) [0100001010] rm[5..3] rd[2..0].
+ kThumbCmpLH, // cmp(3) [01000101] H12[01] rm[5..3] rd[2..0].
+ kThumbCmpHL, // cmp(3) [01000110] H12[10] rm[5..3] rd[2..0].
+ kThumbCmpHH, // cmp(3) [01000111] H12[11] rm[5..3] rd[2..0].
+ kThumbEorRR, // eor [0100000001] rm[5..3] rd[2..0].
+ kThumbLdmia, // ldmia [11001] rn[10..8] reglist [7..0].
+ kThumbLdrRRI5, // ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbLdrRRR, // ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrPcRel, // ldr(3) [01001] rd[10..8] imm_8[7..0].
+ kThumbLdrSpRel, // ldr(4) [10011] rd[10..8] imm_8[7..0].
+ kThumbLdrbRRI5, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbLdrbRRR, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrhRRI5, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbLdrhRRR, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrsbRRR, // ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrshRRR, // ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLslRRI5, // lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0].
+ kThumbLslRR, // lsl(2) [0100000010] rs[5..3] rd[2..0].
+ kThumbLsrRRI5, // lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0].
+ kThumbLsrRR, // lsr(2) [0100000011] rs[5..3] rd[2..0].
+ kThumbMovImm, // mov(1) [00100] rd[10..8] imm_8[7..0].
+ kThumbMovRR, // mov(2) [0001110000] rn[5..3] rd[2..0].
+ kThumbMovRR_H2H, // mov(3) [01000111] H12[11] rm[5..3] rd[2..0].
+ kThumbMovRR_H2L, // mov(3) [01000110] H12[01] rm[5..3] rd[2..0].
+ kThumbMovRR_L2H, // mov(3) [01000101] H12[10] rm[5..3] rd[2..0].
+ kThumbMul, // mul [0100001101] rm[5..3] rd[2..0].
+ kThumbMvn, // mvn [0100001111] rm[5..3] rd[2..0].
+ kThumbNeg, // neg [0100001001] rm[5..3] rd[2..0].
+ kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0].
+ kThumbPop, // pop [1011110] r[8..8] rl[7..0].
+ kThumbPush, // push [1011010] r[8..8] rl[7..0].
+ kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0].
+ kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0].
+ kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0].
+ kThumbStrRRI5, // str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbStrRRR, // str(2) [0101000] rm[8..6] rn[5..3] rd[2..0].
+ kThumbStrSpRel, // str(3) [10010] rd[10..8] imm_8[7..0].
+ kThumbStrbRRI5, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbStrbRRR, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0].
+ kThumbStrhRRI5, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbStrhRRR, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0].
+ kThumbSubRRI3, // sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/
+ kThumbSubRI8, // sub(2) [00111] rd[10..8] imm_8[7..0].
+ kThumbSubRRR, // sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0].
+ kThumbSubSpI7, // sub(4) [101100001] imm_7[6..0].
+ kThumbSwi, // swi [11011111] imm_8[7..0].
+ kThumbTst, // tst [0100001000] rm[5..3] rn[2..0].
+ kThumb2Vldrs, // vldr low sx [111011011001] rn[19..16] rd[15-12] [1010] imm_8[7..0].
+ kThumb2Vldrd, // vldr low dx [111011011001] rn[19..16] rd[15-12] [1011] imm_8[7..0].
+ kThumb2Vmuls, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10100000] rm[3..0].
+ kThumb2Vmuld, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10110000] rm[3..0].
+ kThumb2Vstrs, // vstr low sx [111011011000] rn[19..16] rd[15-12] [1010] imm_8[7..0].
+ kThumb2Vstrd, // vstr low dx [111011011000] rn[19..16] rd[15-12] [1011] imm_8[7..0].
+ kThumb2Vsubs, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100040] rm[3..0].
+ kThumb2Vsubd, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110040] rm[3..0].
+ kThumb2Vadds, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100000] rm[3..0].
+ kThumb2Vaddd, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110000] rm[3..0].
+ kThumb2Vdivs, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10100000] rm[3..0].
+ kThumb2Vdivd, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10110000] rm[3..0].
+ kThumb2VcvtIF, // vcvt.F32 vd, vm [1110111010111000] vd[15..12] [10101100] vm[3..0].
+ kThumb2VcvtID, // vcvt.F64 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0].
+ kThumb2VcvtFI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10101100] vm[3..0].
+ kThumb2VcvtDI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10111100] vm[3..0].
+ kThumb2VcvtFd, // vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] [10101100] vm[3..0].
+ kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0].
+ kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0].
+ kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0].
+ kThumb2MovImmShift,// mov(T2) rd, #<const> [11110] i [00001001111] imm3 rd[11..8] imm8.
+ kThumb2MovImm16, // mov(T3) rd, #<const> [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8.
+ kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0].
+ kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0].
+ kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]*/
+ kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]*/
+ kThumb2Cbnz, // cbnz rd,<label> [101110] i [1] imm5[7..3] rn[2..0].
+ kThumb2Cbz, // cbn rd,<label> [101100] i [1] imm5[7..3] rn[2..0].
+ kThumb2AddRRI12, // add rd, rn, #imm12 [11110] i [100000] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
+ kThumb2MovRR, // mov rd, rm [11101010010011110000] rd[11..8] [0000] rm[3..0].
+ kThumb2Vmovs, // vmov.f32 vd, vm [111011101] D [110000] vd[15..12] 101001] M [0] vm[3..0].
+ kThumb2Vmovd, // vmov.f64 vd, vm [111011101] D [110000] vd[15..12] 101101] M [0] vm[3..0].
+ kThumb2Ldmia, // ldmia [111010001001[ rn[19..16] mask[15..0].
+ kThumb2Stmia, // stmia [111010001000[ rn[19..16] mask[15..0].
+ kThumb2AddRRR, // add [111010110000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2SubRRR, // sub [111010111010] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2SbcRRR, // sbc [111010110110] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2CmpRR, // cmp [111010111011] rn[19..16] [0000] [1111] [0000] rm[3..0].
+ kThumb2SubRRI12, // sub rd, rn, #imm12 [11110] i [01010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
+ kThumb2MvnImm12, // mov(T2) rd, #<const> [11110] i [00011011110] imm3 rd[11..8] imm8.
+ kThumb2Sel, // sel rd, rn, rm [111110101010] rn[19-16] rd[11-8] rm[3-0].
+ kThumb2Ubfx, // ubfx rd,rn,#lsb,#width [111100111100] rn[19..16] [0] imm3[14-12] rd[11-8] w[4-0].
+ kThumb2Sbfx, // ubfx rd,rn,#lsb,#width [111100110100] rn[19..16] [0] imm3[14-12] rd[11-8] w[4-0].
+ kThumb2LdrRRR, // ldr rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrhRRR, // ldrh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrshRRR, // ldrsh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrbRRR, // ldrb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrsbRRR, // ldrsb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2StrRRR, // str rt,[rn,rm,LSL #imm] [111110000100] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2StrhRRR, // str rt,[rn,rm,LSL #imm] [111110000010] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2StrbRRR, // str rt,[rn,rm,LSL #imm] [111110000000] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrhRRI12, // ldrh rt,[rn,#imm12] [111110001011] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2LdrshRRI12, // ldrsh rt,[rn,#imm12] [111110011011] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2LdrbRRI12, // ldrb rt,[rn,#imm12] [111110001001] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2LdrsbRRI12, // ldrsb rt,[rn,#imm12] [111110011001] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2StrhRRI12, // strh rt,[rn,#imm12] [111110001010] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2StrbRRI12, // strb rt,[rn,#imm12] [111110001000] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2Pop, // pop [1110100010111101] list[15-0]*/
+ kThumb2Push, // push [1110100100101101] list[15-0]*/
+ kThumb2CmpRI12, // cmp rn, #<const> [11110] i [011011] rn[19-16] [0] imm3 [1111] imm8[7..0].
+ kThumb2AdcRRR, // adc [111010110101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2AndRRR, // and [111010100000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2BicRRR, // bic [111010100010] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2CmnRR, // cmn [111010110001] rn[19..16] [0000] [1111] [0000] rm[3..0].
+ kThumb2EorRRR, // eor [111010101000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2MulRRR, // mul [111110110000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2MnvRR, // mvn [11101010011011110] rd[11-8] [0000] rm[3..0].
+ kThumb2RsubRRI8, // rsub [111100011100] rn[19..16] [0000] rd[11..8] imm8[7..0].
+ kThumb2NegRR, // actually rsub rd, rn, #0.
+ kThumb2OrrRRR, // orr [111010100100] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2TstRR, // tst [111010100001] rn[19..16] [0000] [1111] [0000] rm[3..0].
+ kThumb2LslRRR, // lsl [111110100000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2LsrRRR, // lsr [111110100010] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2AsrRRR, // asr [111110100100] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2RorRRR, // ror [111110100110] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2LslRRI5, // lsl [11101010010011110] imm[14.12] rd[11..8] [00] rm[3..0].
+ kThumb2LsrRRI5, // lsr [11101010010011110] imm[14.12] rd[11..8] [01] rm[3..0].
+ kThumb2AsrRRI5, // asr [11101010010011110] imm[14.12] rd[11..8] [10] rm[3..0].
+ kThumb2RorRRI5, // ror [11101010010011110] imm[14.12] rd[11..8] [11] rm[3..0].
+ kThumb2BicRRI8, // bic [111100000010] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2AndRRI8, // bic [111100000000] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2OrrRRI8, // orr [111100000100] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2EorRRI8, // eor [111100001000] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2AddRRI8, // add [111100001000] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2AdcRRI8, // adc [111100010101] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2SubRRI8, // sub [111100011011] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2SbcRRI8, // sbc [111100010111] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2It, // it [10111111] firstcond[7-4] mask[3-0].
+ kThumb2Fmstat, // fmstat [11101110111100011111101000010000].
+ kThumb2Vcmpd, // vcmp [111011101] D [11011] rd[15-12] [1011] E [1] M [0] rm[3-0].
+ kThumb2Vcmps, // vcmp [111011101] D [11010] rd[15-12] [1011] E [1] M [0] rm[3-0].
+ kThumb2LdrPcRel12, // ldr rd,[pc,#imm12] [1111100011011111] rt[15-12] imm12[11-0].
+ kThumb2BCond, // b<c> [1110] S cond[25-22] imm6[21-16] [10] J1 [0] J2 imm11[10..0].
+ kThumb2Vmovd_RR, // vmov [111011101] D [110000] vd[15-12 [101101] M [0] vm[3-0].
+ kThumb2Vmovs_RR, // vmov [111011101] D [110000] vd[15-12 [101001] M [0] vm[3-0].
+ kThumb2Fmrs, // vmov [111011100000] vn[19-16] rt[15-12] [1010] N [0010000].
+ kThumb2Fmsr, // vmov [111011100001] vn[19-16] rt[15-12] [1010] N [0010000].
+ kThumb2Fmrrd, // vmov [111011000100] rt2[19-16] rt[15-12] [101100] M [1] vm[3-0].
+ kThumb2Fmdrr, // vmov [111011000101] rt2[19-16] rt[15-12] [101100] M [1] vm[3-0].
+ kThumb2Vabsd, // vabs.f64 [111011101] D [110000] rd[15-12] [1011110] M [0] vm[3-0].
+ kThumb2Vabss, // vabs.f32 [111011101] D [110000] rd[15-12] [1010110] M [0] vm[3-0].
+ kThumb2Vnegd, // vneg.f64 [111011101] D [110000] rd[15-12] [1011110] M [0] vm[3-0].
+ kThumb2Vnegs, // vneg.f32 [111011101] D [110000] rd[15-12] [1010110] M [0] vm[3-0].
+ kThumb2Vmovs_IMM8, // vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12] [10100000] imm4l[3-0].
+ kThumb2Vmovd_IMM8, // vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12] [10110000] imm4l[3-0].
+ kThumb2Mla, // mla [111110110000] rn[19-16] ra[15-12] rd[7-4] [0000] rm[3-0].
+ kThumb2Umull, // umull [111110111010] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0].
+ kThumb2Ldrex, // ldrex [111010000101] rn[19-16] rt[11-8] [1111] imm8[7-0].
+ kThumb2Strex, // strex [111010000100] rn[19-16] rt[11-8] rd[11-8] imm8[7-0].
+ kThumb2Clrex, // clrex [111100111011111110000111100101111].
+ kThumb2Bfi, // bfi [111100110110] rn[19-16] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0].
+ kThumb2Bfc, // bfc [11110011011011110] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0].
+ kThumb2Dmb, // dmb [1111001110111111100011110101] option[3-0].
+ kThumb2LdrPcReln12,// ldr rd,[pc,-#imm12] [1111100011011111] rt[15-12] imm12[11-0].
+ kThumb2Stm, // stm <list> [111010010000] rn[19-16] 000 rl[12-0].
+ kThumbUndefined, // undefined [11011110xxxxxxxx].
+ kThumb2VPopCS, // vpop <list of callee save fp singles (s16+).
+ kThumb2VPushCS, // vpush <list callee save fp singles (s16+).
+ kThumb2Vldms, // vldms rd, <list>.
+ kThumb2Vstms, // vstms rd, <list>.
+ kThumb2BUncond, // b <label>.
+ kThumb2MovImm16H, // similar to kThumb2MovImm16, but target high hw.
+ kThumb2AddPCR, // Thumb2 2-operand add with hard-coded PC target.
+ kThumb2Adr, // Special purpose encoding of ADR for switch tables.
+ kThumb2MovImm16LST,// Special purpose version for switch table use.
+ kThumb2MovImm16HST,// Special purpose version for switch table use.
+ kThumb2LdmiaWB, // ldmia [111010011001[ rn[19..16] mask[15..0].
+ kThumb2SubsRRI12, // setflags encoding.
+ kThumb2OrrRRRs, // orrx [111010100101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2Push1, // t3 encoding of push.
+ kThumb2Pop1, // t3 encoding of pop.
+ kThumb2RsubRRR, // rsb [111010111101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2Smull, // smull [111110111000] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0].
+ kThumb2LdrdPcRel8, // ldrd rt, rt2, pc +-/1024.
+ kThumb2LdrdI8, // ldrd rt, rt2, [rn +-/1024].
+ kThumb2StrdI8, // strd rt, rt2, [rn +-/1024].
+ kArmLast,
+};
+
+enum ArmOpDmbOptions {
+ kSY = 0xf,
+ kST = 0xe,
+ kISH = 0xb,
+ kISHST = 0xa,
+ kNSH = 0x7,
+ kNSHST = 0x6
+};
+
+// Instruction assembly field_loc kind.
+enum ArmEncodingKind {
+ kFmtUnused,
+ kFmtBitBlt, // Bit string using end/start.
+ kFmtDfp, // Double FP reg.
+ kFmtSfp, // Single FP reg.
+ kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
+ kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
+ kFmtImm6, // Encoded branch target using [9,7..3]0.
+ kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
+ kFmtShift, // Shift descriptor, [14..12,7..4].
+ kFmtLsb, // least significant bit using [14..12][7..6].
+ kFmtBWidth, // bit-field width, encoded as width-1.
+ kFmtShift5, // Shift count, [14..12,7..6].
+ kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
+ kFmtFPImm, // Encoded floating point immediate.
+ kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
+};
+
+// Struct used to define the snippet positions for each Thumb opcode.
+struct ArmEncodingMap {
+ uint32_t skeleton;
+ struct {
+ ArmEncodingKind kind;
+ int end; // end for kFmtBitBlt, 1-bit slice end for FP regs.
+ int start; // start for kFmtBitBlt, 4-bit slice end for FP regs.
+ } field_loc[4];
+ ArmOpcode opcode;
+ uint64_t flags;
+ const char* name;
+ const char* fmt;
+ int size; // Note: size is in bytes.
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_ARM_ARMLIR_H_
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
new file mode 100644
index 0000000..e804215
--- /dev/null
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -0,0 +1,1397 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+/*
+ * opcode: ArmOpcode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+ k3, k3s, k3e, flags, name, fmt, size) \
+ {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+ {k3, k3s, k3e}}, opcode, flags, 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)
+ * 3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ * h -> 4-digit hex
+ * d -> decimal
+ * 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
+ * s -> single precision floating point register
+ * S -> double precision floating point register
+ * m -> Thumb2 modified immediate
+ * n -> complimented Thumb2 modified immediate
+ * M -> Thumb2 16-bit zero-extended immediate
+ * b -> 4-digit binary
+ * B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
+ * H -> operand shift
+ * C -> core register name
+ * P -> fp cs register list (base of s16)
+ * Q -> fp cs register list (base of s0)
+ *
+ * [!] escape. To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum ArmOpcode from LIR.h */
+const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = {
+ ENCODING_MAP(kArm16BitData, 0x0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2),
+ ENCODING_MAP(kThumbAdcRR, 0x4140,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
+ "adcs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddRRI3, 0x1c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "adds", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbAddRI8, 0x3000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+ "adds", "!0C, !0C, #!1d", 2),
+ ENCODING_MAP(kThumbAddRRR, 0x1800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adds", "!0C, !1C, !2C", 2),
+ ENCODING_MAP(kThumbAddRRLH, 0x4440,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddRRHL, 0x4480,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddRRHH, 0x44c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddPcRel, 0xa000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH | NEEDS_FIXUP,
+ "add", "!0C, pc, #!1E", 2),
+ ENCODING_MAP(kThumbAddSpRel, 0xa800,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
+ "add", "!0C, sp, #!2E", 2),
+ ENCODING_MAP(kThumbAddSpI7, 0xb000,
+ kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+ "add", "sp, #!0d*4", 2),
+ ENCODING_MAP(kThumbAndRR, 0x4000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "ands", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAsrRRI5, 0x1000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "asrs", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbAsrRR, 0x4100,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "asrs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbBCond, 0xd000,
+ kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES |
+ NEEDS_FIXUP, "b!1c", "!0t", 2),
+ ENCODING_MAP(kThumbBUncond, 0xe000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP,
+ "b", "!0t", 2),
+ ENCODING_MAP(kThumbBicRR, 0x4380,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "bics", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbBkpt, 0xbe00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "bkpt", "!0d", 2),
+ ENCODING_MAP(kThumbBlx1, 0xf000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
+ NEEDS_FIXUP, "blx_1", "!0u", 2),
+ ENCODING_MAP(kThumbBlx2, 0xe800,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
+ NEEDS_FIXUP, "blx_2", "!0v", 2),
+ ENCODING_MAP(kThumbBl1, 0xf000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "bl_1", "!0u", 2),
+ ENCODING_MAP(kThumbBl2, 0xf800,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "bl_2", "!0v", 2),
+ ENCODING_MAP(kThumbBlxR, 0x4780,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
+ "blx", "!0C", 2),
+ ENCODING_MAP(kThumbBx, 0x4700,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "bx", "!0C", 2),
+ ENCODING_MAP(kThumbCmnRR, 0x42c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmn", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpRI8, 0x2800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+ "cmp", "!0C, #!1d", 2),
+ ENCODING_MAP(kThumbCmpRR, 0x4280,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpLH, 0x4540,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpHL, 0x4580,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpHH, 0x45c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbEorRR, 0x4040,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "eors", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbLdmia, 0xc800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "!0C!!, <!1R>", 2),
+ ENCODING_MAP(kThumbLdrRRI5, 0x6800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "!0C, [!1C, #!2E]", 2),
+ ENCODING_MAP(kThumbLdrRRR, 0x5800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldr", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrPcRel, 0x4800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
+ | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2),
+ ENCODING_MAP(kThumbLdrSpRel, 0x9800,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
+ | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2),
+ ENCODING_MAP(kThumbLdrbRRI5, 0x7800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrb", "!0C, [!1C, #2d]", 2),
+ ENCODING_MAP(kThumbLdrbRRR, 0x5c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrb", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrhRRI5, 0x8800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrh", "!0C, [!1C, #!2F]", 2),
+ ENCODING_MAP(kThumbLdrhRRR, 0x5a00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrh", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrsbRRR, 0x5600,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsb", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrshRRR, 0x5e00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsh", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLslRRI5, 0x0000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "lsls", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbLslRR, 0x4080,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "lsls", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbLsrRRI5, 0x0800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "lsrs", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbLsrRR, 0x40c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "lsrs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovImm, 0x2000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
+ "movs", "!0C, #!1d", 2),
+ ENCODING_MAP(kThumbMovRR, 0x1c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "movs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovRR_H2H, 0x46c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovRR_H2L, 0x4640,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovRR_L2H, 0x4680,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMul, 0x4340,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "muls", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMvn, 0x43c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "mvns", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbNeg, 0x4240,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "negs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbOrr, 0x4300,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "orrs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbPop, 0xbc00,
+ kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+ | IS_LOAD, "pop", "<!0R>", 2),
+ ENCODING_MAP(kThumbPush, 0xb400,
+ kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+ | IS_STORE, "push", "<!0R>", 2),
+ ENCODING_MAP(kThumbRorRR, 0x41c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "rors", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbSbc, 0x4180,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
+ "sbcs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbStmia, 0xc000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stmia", "!0C!!, <!1R>", 2),
+ ENCODING_MAP(kThumbStrRRI5, 0x6000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "!0C, [!1C, #!2E]", 2),
+ ENCODING_MAP(kThumbStrRRR, 0x5000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "str", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbStrSpRel, 0x9000,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
+ | IS_STORE, "str", "!0C, [sp, #!2E]", 2),
+ ENCODING_MAP(kThumbStrbRRI5, 0x7000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strb", "!0C, [!1C, #!2d]", 2),
+ ENCODING_MAP(kThumbStrbRRR, 0x5400,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "strb", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbStrhRRI5, 0x8000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strh", "!0C, [!1C, #!2F]", 2),
+ ENCODING_MAP(kThumbStrhRRR, 0x5200,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "strh", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbSubRRI3, 0x1e00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbSubRI8, 0x3800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+ "subs", "!0C, #!1d", 2),
+ ENCODING_MAP(kThumbSubRRR, 0x1a00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "subs", "!0C, !1C, !2C", 2),
+ ENCODING_MAP(kThumbSubSpI7, 0xb080,
+ kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+ "sub", "sp, #!0d*4", 2),
+ ENCODING_MAP(kThumbSwi, 0xdf00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "swi", "!0d", 2),
+ ENCODING_MAP(kThumbTst, 0x4200,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
+ "tst", "!0C, !1C", 2),
+ ENCODING_MAP(kThumb2Vldrs, 0xed900a00,
+ kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vldrd, 0xed900b00,
+ kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vmuls, 0xee200a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vmuls", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vmuld, 0xee200b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vmuld", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2Vstrs, 0xed800a00,
+ kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "vstr", "!0s, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vstrd, 0xed800b00,
+ kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "vstr", "!0S, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vsubs, 0xee300a40,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vsub", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vsubd, 0xee300b40,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vsub", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2Vadds, 0xee300a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vadd", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vaddd, 0xee300b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vadd", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2Vdivs, 0xee800a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vdivs", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vdivd, 0xee800b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vdivd", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2VcvtIF, 0xeeb80ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtID, 0xeeb80bc0,
+ kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f64", "!0S, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtFI, 0xeebd0ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.s32.f32 ", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtDI, 0xeebd0bc0,
+ kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.s32.f64 ", "!0s, !1S", 4),
+ ENCODING_MAP(kThumb2VcvtFd, 0xeeb70ac0,
+ kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f64.f32 ", "!0S, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtDF, 0xeeb70bc0,
+ kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f32.f64 ", "!0s, !1S", 4),
+ ENCODING_MAP(kThumb2Vsqrts, 0xeeb10ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vsqrt.f32 ", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vsqrtd, 0xeeb10bc0,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vsqrt.f64 ", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mov", "!0C, #!1m", 4),
+ ENCODING_MAP(kThumb2MovImm16, 0xf2400000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mov", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2StrRRI12, 0xf8c00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrRRI12, 0xf8d00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2StrRRI8Predec, 0xf8400c00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "!0C, [!1C, #-!2d]", 4),
+ ENCODING_MAP(kThumb2LdrRRI8Predec, 0xf8500c00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "!0C, [!1C, #-!2d]", 4),
+ ENCODING_MAP(kThumb2Cbnz, 0xb900, /* Note: does not affect flags */
+ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
+ NEEDS_FIXUP, "cbnz", "!0C,!1t", 2),
+ ENCODING_MAP(kThumb2Cbz, 0xb100, /* Note: does not affect flags */
+ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
+ NEEDS_FIXUP, "cbz", "!0C,!1t", 2),
+ ENCODING_MAP(kThumb2AddRRI12, 0xf2000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+ "add", "!0C,!1C,#!2d", 4),
+ ENCODING_MAP(kThumb2MovRR, 0xea4f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 4),
+ ENCODING_MAP(kThumb2Vmovs, 0xeeb00a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f32 ", " !0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vmovd, 0xeeb00b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f64 ", " !0S, !1S", 4),
+ ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "!0C!!, <!1R>", 4),
+ ENCODING_MAP(kThumb2Stmia, 0xe8800000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stmia", "!0C!!, <!1R>", 4),
+ ENCODING_MAP(kThumb2AddRRR, 0xeb100000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adds", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2SubRRR, 0xebb00000, /* setflags enconding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "subs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2SbcRRR, 0xeb700000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES,
+ "sbcs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2CmpRR, 0xebb00f00,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 4),
+ ENCODING_MAP(kThumb2SubRRI12, 0xf2a00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+ "sub", "!0C,!1C,#!2d", 4),
+ ENCODING_MAP(kThumb2MvnImm12, 0xf06f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mvn", "!0C, #!1n", 4),
+ ENCODING_MAP(kThumb2Sel, 0xfaa0f080,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES,
+ "sel", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2Ubfx, 0xf3c00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+ kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "ubfx", "!0C, !1C, #!2d, #!3d", 4),
+ ENCODING_MAP(kThumb2Sbfx, 0xf3400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+ kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "sbfx", "!0C, !1C, #!2d, #!3d", 4),
+ ENCODING_MAP(kThumb2LdrRRR, 0xf8500000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrhRRR, 0xf8300000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrshRRR, 0xf9300000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrbRRR, 0xf8100000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrsbRRR, 0xf9100000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2StrRRR, 0xf8400000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "str", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2StrhRRR, 0xf8200000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "strh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2StrbRRR, 0xf8000000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "strb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrhRRI12, 0xf8b00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrh", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrshRRI12, 0xf9b00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrsh", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrbRRI12, 0xf8900000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrb", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrsbRRI12, 0xf9900000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrsb", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2StrhRRI12, 0xf8a00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strh", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2StrbRRI12, 0xf8800000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strb", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2Pop, 0xe8bd0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+ | IS_LOAD | NEEDS_FIXUP, "pop", "<!0R>", 4),
+ ENCODING_MAP(kThumb2Push, 0xe92d0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+ | IS_STORE | NEEDS_FIXUP, "push", "<!0R>", 4),
+ ENCODING_MAP(kThumb2CmpRI12, 0xf1b00f00,
+ kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+ "cmp", "!0C, #!1m", 4),
+ ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adcs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2AndRRR, 0xea000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "and", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2BicRRR, 0xea200000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "bic", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2CmnRR, 0xeb000000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "cmn", "!0C, !1C, shift !2d", 4),
+ ENCODING_MAP(kThumb2EorRRR, 0xea800000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "eor", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2MulRRR, 0xfb00f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "mul", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2MnvRR, 0xea6f0000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "mvn", "!0C, !1C, shift !2d", 4),
+ ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "rsb", "!0C,!1C,#!2m", 4),
+ ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "neg", "!0C,!1C", 4),
+ ENCODING_MAP(kThumb2OrrRRR, 0xea400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "orr", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2TstRR, 0xea100f00,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+ "tst", "!0C, !1C, shift !2d", 4),
+ ENCODING_MAP(kThumb2LslRRR, 0xfa00f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "lsl", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2LsrRRR, 0xfa20f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "lsr", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2AsrRRR, 0xfa40f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "asr", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2RorRRR, 0xfa60f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "ror", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2LslRRI5, 0xea4f0000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lsl", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2LsrRRI5, 0xea4f0010,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lsr", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2AsrRRI5, 0xea4f0020,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "asr", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2RorRRI5, 0xea4f0030,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "ror", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2BicRRI8, 0xf0200000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "bic", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2AndRRI8, 0xf0000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "and", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "orr", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2EorRRI8, 0xf0800000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "eor", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2AddRRI8, 0xf1100000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "adds", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+ "adcs", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+ "sbcs", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2It, 0xbf00,
+ kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
+ "it:!1b", "!0c", 2),
+ ENCODING_MAP(kThumb2Fmstat, 0xeef1fa10,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
+ "fmstat", "", 4),
+ ENCODING_MAP(kThumb2Vcmpd, 0xeeb40b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+ "vcmp.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vcmps, 0xeeb40a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+ "vcmp.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2LdrPcRel12, 0xf8df0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
+ "ldr", "!0C, [r15pc, #!1d]", 4),
+ ENCODING_MAP(kThumb2BCond, 0xf0008000,
+ kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | IS_BRANCH | USES_CCODES | NEEDS_FIXUP,
+ "b!1c", "!0t", 4),
+ ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Fmrs, 0xee100a10,
+ kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "fmrs", "!0C, !1s", 4),
+ ENCODING_MAP(kThumb2Fmsr, 0xee000a10,
+ kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "fmsr", "!0s, !1C", 4),
+ ENCODING_MAP(kThumb2Fmrrd, 0xec500b10,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
+ "fmrrd", "!0C, !1C, !2S", 4),
+ ENCODING_MAP(kThumb2Fmdrr, 0xec400b10,
+ kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "fmdrr", "!0S, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2Vabsd, 0xeeb00bc0,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vabs.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vabss, 0xeeb00ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vabs.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vnegd, 0xeeb10b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vneg.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vnegs, 0xeeb10a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vneg.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vmovs_IMM8, 0xeeb00a00,
+ kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "vmov.f32", "!0s, #0x!1h", 4),
+ ENCODING_MAP(kThumb2Vmovd_IMM8, 0xeeb00b00,
+ kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "vmov.f64", "!0S, #0x!1h", 4),
+ ENCODING_MAP(kThumb2Mla, 0xfb000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 15, 12,
+ IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
+ "mla", "!0C, !1C, !2C, !3C", 4),
+ ENCODING_MAP(kThumb2Umull, 0xfba00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 3, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
+ "umull", "!0C, !1C, !2C, !3C", 4),
+ ENCODING_MAP(kThumb2Ldrex, 0xe8500f00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrex", "!0C, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Strex, 0xe8400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE,
+ "strex", "!0C,!1C, [!2C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "clrex", "", 4),
+ ENCODING_MAP(kThumb2Bfi, 0xf3600000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1,
+ kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "bfi", "!0C,!1C,#!2d,#!3d", 4),
+ ENCODING_MAP(kThumb2Bfc, 0xf36f0000,
+ kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
+ "bfc", "!0C,#!1d,#!2d", 4),
+ ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50,
+ kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP,
+ "dmb","#!0B",4),
+ ENCODING_MAP(kThumb2LdrPcReln12, 0xf85f0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+ "ldr", "!0C, [r15pc, -#!1d]", 4),
+ ENCODING_MAP(kThumb2Stm, 0xe9000000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stm", "!0C, <!1R>", 4),
+ ENCODING_MAP(kThumbUndefined, 0xde00,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "undefined", "", 2),
+ // NOTE: vpop, vpush hard-encoded for s16+ reg list
+ ENCODING_MAP(kThumb2VPopCS, 0xecbd8a00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_FPCS_LIST0
+ | IS_LOAD, "vpop", "<!0P>", 4),
+ ENCODING_MAP(kThumb2VPushCS, 0xed2d8a00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_FPCS_LIST0
+ | IS_STORE, "vpush", "<!0P>", 4),
+ ENCODING_MAP(kThumb2Vldms, 0xec900a00,
+ kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE0 | REG_DEF_FPCS_LIST2
+ | IS_LOAD, "vldms", "!0C, <!2Q>", 4),
+ ENCODING_MAP(kThumb2Vstms, 0xec800a00,
+ kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE0 | REG_USE_FPCS_LIST2
+ | IS_STORE, "vstms", "!0C, <!2Q>", 4),
+ ENCODING_MAP(kThumb2BUncond, 0xf0009000,
+ kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+ "b", "!0t", 4),
+ ENCODING_MAP(kThumb2MovImm16H, 0xf2c00000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0,
+ "movt", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2AddPCR, 0x4487,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH,
+ "add", "rPC, !0C", 2),
+ ENCODING_MAP(kThumb2Adr, 0xf20f0000,
+ kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ /* Note: doesn't affect flags */
+ IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP,
+ "adr", "!0C,#!1d", 4),
+ ENCODING_MAP(kThumb2MovImm16LST, 0xf2400000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP,
+ "mov", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2MovImm16HST, 0xf2c00000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
+ "movt", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "!0C!!, <!1R>", 4),
+ ENCODING_MAP(kThumb2SubsRRI12, 0xf1b00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "!0C,!1C,#!2d", 4),
+ ENCODING_MAP(kThumb2OrrRRRs, 0xea500000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "orrs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2Push1, 0xf84d0d04,
+ kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE0
+ | IS_STORE, "push1", "!0C", 4),
+ ENCODING_MAP(kThumb2Pop1, 0xf85d0b04,
+ kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF0
+ | IS_LOAD, "pop1", "!0C", 4),
+ ENCODING_MAP(kThumb2RsubRRR, 0xebd00000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "rsbs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2Smull, 0xfb800000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 3, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
+ "smull", "!0C, !1C, !2C, !3C", 4),
+ ENCODING_MAP(kThumb2LdrdPcRel8, 0xe9df0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0 | REG_DEF1 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
+ "ldrd", "!0C, !1C, [pc, #!2E]", 4),
+ ENCODING_MAP(kThumb2LdrdI8, 0xe9d00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | IS_LOAD,
+ "ldrd", "!0C, !1C, [!2C, #!3E]", 4),
+ ENCODING_MAP(kThumb2StrdI8, 0xe9c00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0,
+ IS_QUAD_OP | REG_USE0 | REG_USE1 | REG_USE2 | IS_STORE,
+ "strd", "!0C, !1C, [!2C, #!3E]", 4),
+};
+
+/*
+ * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
+ * not ready. Since r5FP is not updated often, it is less likely to
+ * generate unnecessary stall cycles.
+ * TUNING: No longer true - find new NOP pattern.
+ */
+#define PADDING_MOV_R5_R5 0x1C2D
+
+/*
+ * Assemble the LIR into binary instruction format. Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction.
+ */
+AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr)
+{
+ LIR* lir;
+ AssemblerStatus res = kSuccess; // Assume success
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+
+ if (lir->opcode < 0) {
+ /* 1 means padding is needed */
+ if ((lir->opcode == kPseudoPseudoAlign4) && (lir->operands[0] == 1)) {
+ code_buffer_.push_back(PADDING_MOV_R5_R5 & 0xFF);
+ code_buffer_.push_back((PADDING_MOV_R5_R5 >> 8) & 0xFF);
+ }
+ continue;
+ }
+
+ if (lir->flags.is_nop) {
+ continue;
+ }
+
+ /*
+ * For PC-relative displacements we won't know if the
+ * selected instruction will work until late (i.e. - now).
+ * If something doesn't fit, we must replace the short-form
+ * operation with a longer-form one. Note, though, that this
+ * can change code we've already processed, so we'll need to
+ * re-calculate offsets and restart. To limit the number of
+ * restarts, the entire list will be scanned and patched.
+ * Of course, the patching itself may cause new overflows so this
+ * is an iterative process.
+ */
+ if (lir->flags.pcRelFixup) {
+ if (lir->opcode == kThumbLdrPcRel ||
+ lir->opcode == kThumb2LdrPcRel12 ||
+ lir->opcode == kThumbAddPcRel ||
+ lir->opcode == kThumb2LdrdPcRel8 ||
+ ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) ||
+ ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) {
+ /*
+ * PC-relative loads are mostly used to load immediates
+ * that are too large to materialize directly in one shot.
+ * However, if the load displacement exceeds the limit,
+ * we revert to a multiple-instruction materialization sequence.
+ */
+ LIR *lir_target = lir->target;
+ uintptr_t pc = (lir->offset + 4) & ~3;
+ uintptr_t target = lir_target->offset;
+ int delta = target - pc;
+ if (delta & 0x3) {
+ LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
+ }
+ // First, a sanity check for cases we shouldn't see now
+ if (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) ||
+ ((lir->opcode == kThumbLdrPcRel) && (delta > 1020))) {
+ // Shouldn't happen in current codegen.
+ LOG(FATAL) << "Unexpected pc-rel offset " << delta;
+ }
+ // Now, check for the difficult cases
+ if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) ||
+ ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) {
+ /*
+ * Note: because rARM_LR may be used to fix up out-of-range
+ * vldrs/vldrd we include REG_DEF_LR in the resource
+ * masks for these instructions.
+ */
+ int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) || (lir->opcode == kThumb2LdrPcRel12))
+ ? lir->operands[0] : rARM_LR;
+
+ // Add new Adr to generate the address.
+ LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr,
+ base_reg, 0, 0, 0, 0, lir->target);
+ InsertLIRBefore(lir, new_adr);
+
+ // Convert to normal load.
+ if (lir->opcode == kThumb2LdrPcRel12) {
+ lir->opcode = kThumb2LdrRRI12;
+ } else if (lir->opcode == kThumb2LdrdPcRel8) {
+ lir->opcode = kThumb2LdrdI8;
+ }
+ // Change the load to be relative to the new Adr base.
+ if (lir->opcode == kThumb2LdrdI8) {
+ lir->operands[3] = 0;
+ lir->operands[2] = base_reg;
+ } else {
+ lir->operands[2] = 0;
+ lir->operands[1] = base_reg;
+ }
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ } else {
+ if ((lir->opcode == kThumb2Vldrs) ||
+ (lir->opcode == kThumb2Vldrd) ||
+ (lir->opcode == kThumb2LdrdPcRel8)) {
+ lir->operands[2] = delta >> 2;
+ } else {
+ lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta :
+ delta >> 2;
+ }
+ }
+ } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta > 126 || delta < 0) {
+ /*
+ * Convert to cmp rx,#0 / b[eq/ne] tgt pair
+ * Make new branch instruction and insert after
+ */
+ LIR* new_inst =
+ RawLIR(lir->dalvik_offset, kThumbBCond, 0,
+ (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe,
+ 0, 0, 0, lir->target);
+ InsertLIRAfter(lir, new_inst);
+ /* Convert the cb[n]z to a cmp rx, #0 ] */
+ lir->opcode = kThumbCmpRI8;
+ /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+ lir->operands[1] = 0;
+ lir->target = 0;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ } else {
+ lir->operands[1] = delta >> 1;
+ }
+ } else if (lir->opcode == kThumb2Push || lir->opcode == kThumb2Pop) {
+ if (__builtin_popcount(lir->operands[0]) == 1) {
+ /*
+ * The standard push/pop multiple instruction
+ * requires at least two registers in the list.
+ * If we've got just one, switch to the single-reg
+ * encoding.
+ */
+ lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 :
+ kThumb2Pop1;
+ int reg = 0;
+ while (lir->operands[0]) {
+ if (lir->operands[0] & 0x1) {
+ break;
+ } else {
+ reg++;
+ lir->operands[0] >>= 1;
+ }
+ }
+ lir->operands[0] = reg;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kThumbBCond || lir->opcode == kThumb2BCond) {
+ LIR *target_lir = lir->target;
+ int delta = 0;
+ DCHECK(target_lir);
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ delta = target - pc;
+ if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+ lir->opcode = kThumb2BCond;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ lir->operands[0] = delta >> 1;
+ } else if (lir->opcode == kThumb2BUncond) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
+ lir->operands[0] == 0) { // Useless branch
+ lir->flags.is_nop = true;
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kThumbBUncond) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta > 2046 || delta < -2048) {
+ // Convert to Thumb2BCond w/ kArmCondAl
+ lir->opcode = kThumb2BUncond;
+ lir->operands[0] = 0;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ } else {
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
+ lir->operands[0] == -1) { // Useless branch
+ lir->flags.is_nop = true;
+ res = kRetryAll;
+ }
+ }
+ } else if (lir->opcode == kThumbBlx1) {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2);
+ /* cur_pc is Thumb */
+ uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
+ uintptr_t target = lir->operands[1];
+
+ /* Match bit[1] in target with base */
+ if (cur_pc & 0x2) {
+ target |= 0x2;
+ }
+ int delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ } else if (lir->opcode == kThumbBl1) {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2);
+ /* Both cur_pc and target are Thumb */
+ uintptr_t cur_pc = start_addr + lir->offset + 4;
+ uintptr_t target = lir->operands[1];
+
+ int delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ } else if (lir->opcode == kThumb2Adr) {
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[2]);
+ LIR* target = lir->target;
+ int target_disp = tab_rec ? tab_rec->offset
+ : target->offset;
+ int disp = target_disp - ((lir->offset + 4) & ~3);
+ if (disp < 4096) {
+ lir->operands[1] = disp;
+ } else {
+ // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0]
+ // TUNING: if this case fires often, it can be improved. Not expected to be common.
+ LIR *new_mov16L =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16LST,
+ lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
+ reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
+ InsertLIRBefore(lir, new_mov16L);
+ LIR *new_mov16H =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16HST,
+ lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
+ reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
+ InsertLIRBefore(lir, new_mov16H);
+ if (ARM_LOWREG(lir->operands[0])) {
+ lir->opcode = kThumbAddRRLH;
+ } else {
+ lir->opcode = kThumbAddRRHH;
+ }
+ lir->operands[1] = rARM_PC;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kThumb2MovImm16LST) {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff;
+ } else if (lir->opcode == kThumb2MovImm16HST) {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] =
+ ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff;
+ }
+ }
+ /*
+ * If one of the pc-relative instructions expanded we'll have
+ * to make another pass. Don't bother to fully assemble the
+ * instruction.
+ */
+ if (res != kSuccess) {
+ continue;
+ }
+ const ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
+ uint32_t bits = encoder->skeleton;
+ int i;
+ for (i = 0; i < 4; i++) {
+ uint32_t operand;
+ uint32_t value;
+ operand = lir->operands[i];
+ switch (encoder->field_loc[i].kind) {
+ case kFmtUnused:
+ break;
+ case kFmtFPImm:
+ value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end;
+ value |= (operand & 0x0F) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtBrOffset:
+ value = ((operand & 0x80000) >> 19) << 26;
+ value |= ((operand & 0x40000) >> 18) << 11;
+ value |= ((operand & 0x20000) >> 17) << 13;
+ value |= ((operand & 0x1f800) >> 11) << 16;
+ value |= (operand & 0x007ff);
+ bits |= value;
+ break;
+ case kFmtShift5:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtShift:
+ value = ((operand & 0x70) >> 4) << 12;
+ value |= (operand & 0x0f) << 4;
+ bits |= value;
+ break;
+ case kFmtBWidth:
+ value = operand - 1;
+ bits |= value;
+ break;
+ case kFmtLsb:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtImm6:
+ value = ((operand & 0x20) >> 5) << 9;
+ value |= (operand & 0x1f) << 3;
+ bits |= value;
+ break;
+ case kFmtBitBlt:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ break;
+ case kFmtDfp: {
+ DCHECK(ARM_DOUBLEREG(operand));
+ DCHECK_EQ((operand & 0x1), 0U);
+ int reg_name = (operand & ARM_FP_REG_MASK) >> 1;
+ /* Snag the 1-bit slice and position it */
+ value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= (reg_name & 0x0f) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ }
+ case kFmtSfp:
+ DCHECK(ARM_SINGLEREG(operand));
+ /* Snag the 1-bit slice and position it */
+ value = (operand & 0x1) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtImm12:
+ case kFmtModImm:
+ value = ((operand & 0x800) >> 11) << 26;
+ value |= ((operand & 0x700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtImm16:
+ value = ((operand & 0x0800) >> 11) << 26;
+ value |= ((operand & 0xf000) >> 12) << 16;
+ value |= ((operand & 0x0700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtOff24: {
+ uint32_t signbit = (operand >> 31) & 0x1;
+ uint32_t i1 = (operand >> 22) & 0x1;
+ uint32_t i2 = (operand >> 21) & 0x1;
+ uint32_t imm10 = (operand >> 11) & 0x03ff;
+ uint32_t imm11 = operand & 0x07ff;
+ uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
+ uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
+ value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
+ imm11;
+ bits |= value;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind;
+ }
+ }
+ if (encoder->size == 4) {
+ code_buffer_.push_back((bits >> 16) & 0xff);
+ code_buffer_.push_back((bits >> 24) & 0xff);
+ }
+ code_buffer_.push_back(bits & 0xff);
+ code_buffer_.push_back((bits >> 8) & 0xff);
+ }
+ return res;
+}
+
+int ArmMir2Lir::GetInsnSize(LIR* lir)
+{
+ return EncodingMap[lir->opcode].size;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
new file mode 100644
index 0000000..a6720ce
--- /dev/null
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file contains codegen for the Thumb2 ISA. */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+
+/* Return the position of an ssa name within the argument list */
+int ArmMir2Lir::InPosition(int s_reg)
+{
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ return v_reg - cu_->num_regs;
+}
+
+/*
+ * Describe an argument. If it's already in an arg register, just leave it
+ * there. NOTE: all live arg registers must be locked prior to this call
+ * to avoid having them allocated as a temp by downstream utilities.
+ */
+RegLocation ArmMir2Lir::ArgLoc(RegLocation loc)
+{
+ int arg_num = InPosition(loc.s_reg_low);
+ if (loc.wide) {
+ if (arg_num == 2) {
+ // Bad case - half in register, half in frame. Just punt
+ loc.location = kLocInvalid;
+ } else if (arg_num < 2) {
+ loc.low_reg = rARM_ARG1 + arg_num;
+ loc.high_reg = loc.low_reg + 1;
+ loc.location = kLocPhysReg;
+ } else {
+ loc.location = kLocDalvikFrame;
+ }
+ } else {
+ if (arg_num < 3) {
+ loc.low_reg = rARM_ARG1 + arg_num;
+ loc.location = kLocPhysReg;
+ } else {
+ loc.location = kLocDalvikFrame;
+ }
+ }
+ return loc;
+}
+
+/*
+ * Load an argument. If already in a register, just return. If in
+ * the frame, we can't use the normal LoadValue() because it assumed
+ * a proper frame - and we're frameless.
+ */
+RegLocation ArmMir2Lir::LoadArg(RegLocation loc)
+{
+ if (loc.location == kLocDalvikFrame) {
+ int start = (InPosition(loc.s_reg_low) + 1) * sizeof(uint32_t);
+ loc.low_reg = AllocTemp();
+ LoadWordDisp(rARM_SP, start, loc.low_reg);
+ if (loc.wide) {
+ loc.high_reg = AllocTemp();
+ LoadWordDisp(rARM_SP, start + sizeof(uint32_t), loc.high_reg);
+ }
+ loc.location = kLocPhysReg;
+ }
+ return loc;
+}
+
+/* Lock any referenced arguments that arrive in registers */
+void ArmMir2Lir::LockLiveArgs(MIR* mir)
+{
+ int first_in = cu_->num_regs;
+ const int num_arg_regs = 3; // TODO: generalize & move to RegUtil.cc
+ for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
+ int v_reg = mir_graph_->SRegToVReg(mir->ssa_rep->uses[i]);
+ int InPosition = v_reg - first_in;
+ if (InPosition < num_arg_regs) {
+ LockTemp(rARM_ARG1 + InPosition);
+ }
+ }
+}
+
+/* Find the next MIR, which may be in a following basic block */
+// TODO: should this be a utility in mir_graph?
+MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir)
+{
+ BasicBlock* bb = *p_bb;
+ MIR* orig_mir = mir;
+ while (bb != NULL) {
+ if (mir != NULL) {
+ mir = mir->next;
+ }
+ if (mir != NULL) {
+ return mir;
+ } else {
+ bb = bb->fall_through;
+ *p_bb = bb;
+ if (bb) {
+ mir = bb->first_mir_insn;
+ if (mir != NULL) {
+ return mir;
+ }
+ }
+ }
+ }
+ return orig_mir;
+}
+
+/* Used for the "verbose" listing */
+//TODO: move to common code
+void ArmMir2Lir::GenPrintLabel(MIR* mir)
+{
+ /* Mark the beginning of a Dalvik instruction for line tracking */
+ char* inst_str = cu_->verbose ?
+ mir_graph_->GetDalvikDisassembly(mir) : NULL;
+ MarkBoundary(mir->offset, inst_str);
+}
+
+MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir,
+ OpSize size, bool long_or_double, bool is_object)
+{
+ int field_offset;
+ bool is_volatile;
+ uint32_t field_idx = mir->dalvikInsn.vC;
+ bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
+ return NULL;
+ }
+ RegLocation rl_obj = mir_graph_->GetSrc(mir, 0);
+ LockLiveArgs(mir);
+ rl_obj = ArmMir2Lir::ArgLoc(rl_obj);
+ RegLocation rl_dest;
+ if (long_or_double) {
+ rl_dest = GetReturnWide(false);
+ } else {
+ rl_dest = GetReturn(false);
+ }
+ // Point of no return - no aborts after this
+ ArmMir2Lir::GenPrintLabel(mir);
+ rl_obj = LoadArg(rl_obj);
+ GenIGet(field_idx, mir->optimization_flags, size, rl_dest, rl_obj, long_or_double, is_object);
+ return GetNextMir(bb, mir);
+}
+
+MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir,
+ OpSize size, bool long_or_double, bool is_object)
+{
+ int field_offset;
+ bool is_volatile;
+ uint32_t field_idx = mir->dalvikInsn.vC;
+ bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
+ return NULL;
+ }
+ RegLocation rl_src;
+ RegLocation rl_obj;
+ LockLiveArgs(mir);
+ if (long_or_double) {
+ rl_src = mir_graph_->GetSrcWide(mir, 0);
+ rl_obj = mir_graph_->GetSrc(mir, 2);
+ } else {
+ rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_obj = mir_graph_->GetSrc(mir, 1);
+ }
+ rl_src = ArmMir2Lir::ArgLoc(rl_src);
+ rl_obj = ArmMir2Lir::ArgLoc(rl_obj);
+ // Reject if source is split across registers & frame
+ if (rl_obj.location == kLocInvalid) {
+ ResetRegPool();
+ return NULL;
+ }
+ // Point of no return - no aborts after this
+ ArmMir2Lir::GenPrintLabel(mir);
+ rl_obj = LoadArg(rl_obj);
+ rl_src = LoadArg(rl_src);
+ GenIPut(field_idx, mir->optimization_flags, size, rl_src, rl_obj, long_or_double, is_object);
+ return GetNextMir(bb, mir);
+}
+
+MIR* ArmMir2Lir::SpecialIdentity(MIR* mir)
+{
+ RegLocation rl_src;
+ RegLocation rl_dest;
+ bool wide = (mir->ssa_rep->num_uses == 2);
+ if (wide) {
+ rl_src = mir_graph_->GetSrcWide(mir, 0);
+ rl_dest = GetReturnWide(false);
+ } else {
+ rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_dest = GetReturn(false);
+ }
+ LockLiveArgs(mir);
+ rl_src = ArmMir2Lir::ArgLoc(rl_src);
+ if (rl_src.location == kLocInvalid) {
+ ResetRegPool();
+ return NULL;
+ }
+ // Point of no return - no aborts after this
+ ArmMir2Lir::GenPrintLabel(mir);
+ rl_src = LoadArg(rl_src);
+ if (wide) {
+ StoreValueWide(rl_dest, rl_src);
+ } else {
+ StoreValue(rl_dest, rl_src);
+ }
+ return mir;
+}
+
+/*
+ * Special-case code genration for simple non-throwing leaf methods.
+ */
+void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
+ SpecialCaseHandler special_case)
+{
+ current_dalvik_offset_ = mir->offset;
+ MIR* next_mir = NULL;
+ switch (special_case) {
+ case kNullMethod:
+ DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID);
+ next_mir = mir;
+ break;
+ case kConstFunction:
+ ArmMir2Lir::GenPrintLabel(mir);
+ LoadConstant(rARM_RET0, mir->dalvikInsn.vB);
+ next_mir = GetNextMir(&bb, mir);
+ break;
+ case kIGet:
+ next_mir = SpecialIGet(&bb, mir, kWord, false, false);
+ break;
+ case kIGetBoolean:
+ case kIGetByte:
+ next_mir = SpecialIGet(&bb, mir, kUnsignedByte, false, false);
+ break;
+ case kIGetObject:
+ next_mir = SpecialIGet(&bb, mir, kWord, false, true);
+ break;
+ case kIGetChar:
+ next_mir = SpecialIGet(&bb, mir, kUnsignedHalf, false, false);
+ break;
+ case kIGetShort:
+ next_mir = SpecialIGet(&bb, mir, kSignedHalf, false, false);
+ break;
+ case kIGetWide:
+ next_mir = SpecialIGet(&bb, mir, kLong, true, false);
+ break;
+ case kIPut:
+ next_mir = SpecialIPut(&bb, mir, kWord, false, false);
+ break;
+ case kIPutBoolean:
+ case kIPutByte:
+ next_mir = SpecialIPut(&bb, mir, kUnsignedByte, false, false);
+ break;
+ case kIPutObject:
+ next_mir = SpecialIPut(&bb, mir, kWord, false, true);
+ break;
+ case kIPutChar:
+ next_mir = SpecialIPut(&bb, mir, kUnsignedHalf, false, false);
+ break;
+ case kIPutShort:
+ next_mir = SpecialIPut(&bb, mir, kSignedHalf, false, false);
+ break;
+ case kIPutWide:
+ next_mir = SpecialIPut(&bb, mir, kLong, true, false);
+ break;
+ case kIdentity:
+ next_mir = SpecialIdentity(mir);
+ break;
+ default:
+ return;
+ }
+ if (next_mir != NULL) {
+ current_dalvik_offset_ = next_mir->offset;
+ if (special_case != kIdentity) {
+ ArmMir2Lir::GenPrintLabel(next_mir);
+ }
+ NewLIR1(kThumbBx, rARM_LR);
+ core_spill_mask_ = 0;
+ num_core_spills_ = 0;
+ fp_spill_mask_ = 0;
+ num_fp_spills_ = 0;
+ frame_size_ = 0;
+ core_vmap_table_.clear();
+ fp_vmap_table_.clear();
+ }
+}
+
+/*
+ * The sparse table in the literal pool is an array of <key,displacement>
+ * pairs. For each set, we'll load them as a pair using ldmia.
+ * This means that the register number of the temp we use for the key
+ * must be lower than the reg for the displacement.
+ *
+ * The test loop will look something like:
+ *
+ * adr rBase, <table>
+ * ldr r_val, [rARM_SP, v_reg_off]
+ * mov r_idx, #table_size
+ * lp:
+ * ldmia rBase!, {r_key, r_disp}
+ * sub r_idx, #1
+ * cmp r_val, r_key
+ * ifeq
+ * add rARM_PC, r_disp ; This is the branch from which we compute displacement
+ * cbnz r_idx, lp
+ */
+void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpSparseSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int size = table[1];
+ tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
+ ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // Get the switch value
+ rl_src = LoadValue(rl_src, kCoreReg);
+ int rBase = AllocTemp();
+ /* Allocate key and disp temps */
+ int r_key = AllocTemp();
+ int r_disp = AllocTemp();
+ // Make sure r_key's register number is less than r_disp's number for ldmia
+ if (r_key > r_disp) {
+ int tmp = r_disp;
+ r_disp = r_key;
+ r_key = tmp;
+ }
+ // Materialize a pointer to the switch table
+ NewLIR3(kThumb2Adr, rBase, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ // Set up r_idx
+ int r_idx = AllocTemp();
+ LoadConstant(r_idx, size);
+ // Establish loop branch target
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ // Load next key/disp
+ NewLIR2(kThumb2LdmiaWB, rBase, (1 << r_key) | (1 << r_disp));
+ OpRegReg(kOpCmp, r_key, rl_src.low_reg);
+ // Go if match. NOTE: No instruction set switch here - must stay Thumb2
+ OpIT(kCondEq, "");
+ LIR* switch_branch = NewLIR1(kThumb2AddPCR, r_disp);
+ tab_rec->anchor = switch_branch;
+ // Needs to use setflags encoding here
+ NewLIR3(kThumb2SubsRRI12, r_idx, r_idx, 1);
+ OpCondBranch(kCondNe, target);
+}
+
+
+void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpPackedSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int size = table[1];
+ tab_rec->targets =
+ static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true, ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // Get the switch value
+ rl_src = LoadValue(rl_src, kCoreReg);
+ int table_base = AllocTemp();
+ // Materialize a pointer to the switch table
+ NewLIR3(kThumb2Adr, table_base, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ int low_key = s4FromSwitchData(&table[2]);
+ int keyReg;
+ // Remove the bias, if necessary
+ if (low_key == 0) {
+ keyReg = rl_src.low_reg;
+ } else {
+ keyReg = AllocTemp();
+ OpRegRegImm(kOpSub, keyReg, rl_src.low_reg, low_key);
+ }
+ // Bounds check - if < 0 or >= size continue following switch
+ OpRegImm(kOpCmp, keyReg, size-1);
+ LIR* branch_over = OpCondBranch(kCondHi, NULL);
+
+ // Load the displacement from the switch table
+ int disp_reg = AllocTemp();
+ LoadBaseIndexed(table_base, keyReg, disp_reg, 2, kWord);
+
+ // ..and go! NOTE: No instruction set switch here - must stay Thumb2
+ LIR* switch_branch = NewLIR1(kThumb2AddPCR, disp_reg);
+ tab_rec->anchor = switch_branch;
+
+ /* branch_over target here */
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+}
+
+/*
+ * Array data table format:
+ * ushort ident = 0x0300 magic value
+ * ushort width width of each element in the table
+ * uint size number of elements in the table
+ * ubyte data[size*width] table of data values (may contain a single-byte
+ * padding at the end)
+ *
+ * Total size is 4+(width * size + 1)/2 16-bit code units.
+ */
+void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ // Add the table to the list - we'll process it later
+ FillArrayData *tab_rec =
+ static_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ uint16_t width = tab_rec->table[1];
+ uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
+ tab_rec->size = (size * width) + 8;
+
+ fill_array_data_.Insert(tab_rec);
+
+ // Making a call - use explicit registers
+ FlushAllRegs(); /* Everything to home location */
+ LoadValueDirectFixed(rl_src, r0);
+ LoadWordDisp(rARM_SELF, ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode),
+ rARM_LR);
+ // Materialize a pointer to the fill data image
+ NewLIR3(kThumb2Adr, r1, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+}
+
+/*
+ * Handle simple case (thin lock) inline. If it's complicated, bail
+ * out to the heavyweight lock/unlock routines. We'll use dedicated
+ * registers here in order to be in the right position in case we
+ * to bail to oat[Lock/Unlock]Object(self, object)
+ *
+ * r0 -> self pointer [arg0 for oat[Lock/Unlock]Object
+ * r1 -> object [arg1 for oat[Lock/Unlock]Object
+ * r2 -> intial contents of object->lock, later result of strex
+ * r3 -> self->thread_id
+ * r12 -> allow to be used by utilities as general temp
+ *
+ * The result of the strex is 0 if we acquire the lock.
+ *
+ * See comments in monitor.cc for the layout of the lock word.
+ * Of particular interest to this code is the test for the
+ * simple case - which we handle inline. For monitor enter, the
+ * simple case is thin lock, held by no-one. For monitor exit,
+ * the simple case is thin lock, held by the unlocking thread with
+ * a recurse count of 0.
+ *
+ * A minor complication is that there is a field in the lock word
+ * unrelated to locking: the hash state. This field must be ignored, but
+ * preserved.
+ *
+ */
+void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src)
+{
+ FlushAllRegs();
+ DCHECK_EQ(LW_SHAPE_THIN, 0);
+ LoadValueDirectFixed(rl_src, r0); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ NewLIR3(kThumb2Ldrex, r1, r0,
+ mirror::Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
+ // Align owner
+ OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
+ // Is lock unheld on lock or held by us (==thread_id) on unlock?
+ NewLIR4(kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
+ NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondEq, "");
+ NewLIR4(kThumb2Strex, r1, r2, r0,
+ mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondNe, "T");
+ // Go expensive route - artLockObjectFromCode(self, obj);
+ LoadWordDisp(rARM_SELF, ENTRYPOINT_OFFSET(pLockObjectFromCode), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kLoadLoad);
+}
+
+/*
+ * For monitor unlock, we don't have to use ldrex/strex. Once
+ * we've determined that the lock is thin and that we own it with
+ * a zero recursion count, it's safe to punch it back to the
+ * initial, unlock thin state with a store word.
+ */
+void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src)
+{
+ DCHECK_EQ(LW_SHAPE_THIN, 0);
+ FlushAllRegs();
+ LoadValueDirectFixed(rl_src, r0); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ // Is lock unheld on lock or held by us (==thread_id) on unlock?
+ OpRegRegImm(kOpAnd, r3, r1,
+ (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+ // Align owner
+ OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
+ NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+ OpRegReg(kOpSub, r1, r2);
+ OpIT(kCondEq, "EE");
+ StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
+ // Go expensive route - UnlockObjectFromCode(obj);
+ LoadWordDisp(rARM_SELF, ENTRYPOINT_OFFSET(pUnlockObjectFromCode), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kStoreLoad);
+}
+
+void ArmMir2Lir::GenMoveException(RegLocation rl_dest)
+{
+ int ex_offset = Thread::ExceptionOffset().Int32Value();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int reset_reg = AllocTemp();
+ LoadWordDisp(rARM_SELF, ex_offset, rl_result.low_reg);
+ LoadConstant(reset_reg, 0);
+ StoreWordDisp(rARM_SELF, ex_offset, reset_reg);
+ FreeTemp(reset_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+void ArmMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg)
+{
+ int reg_card_base = AllocTemp();
+ int reg_card_no = AllocTemp();
+ LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
+ LoadWordDisp(rARM_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base);
+ OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
+ StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0,
+ kUnsignedByte);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+ FreeTemp(reg_card_base);
+ FreeTemp(reg_card_no);
+}
+
+void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
+{
+ int spill_count = num_core_spills_ + num_fp_spills_;
+ /*
+ * On entry, r0, r1, r2 & r3 are live. Let the register allocation
+ * mechanism know so it doesn't try to use any of them when
+ * expanding the frame or flushing. This leaves the utility
+ * code with a single temp: r12. This should be enough.
+ */
+ LockTemp(r0);
+ LockTemp(r1);
+ LockTemp(r2);
+ LockTemp(r3);
+
+ /*
+ * We can safely skip the stack overflow check if we're
+ * a leaf *and* our frame size < fudge factor.
+ */
+ bool skip_overflow_check = (mir_graph_->MethodIsLeaf() &&
+ (static_cast<size_t>(frame_size_) <
+ Thread::kStackOverflowReservedBytes));
+ NewLIR0(kPseudoMethodEntry);
+ if (!skip_overflow_check) {
+ /* Load stack limit */
+ LoadWordDisp(rARM_SELF, Thread::StackEndOffset().Int32Value(), r12);
+ }
+ /* Spill core callee saves */
+ NewLIR1(kThumb2Push, core_spill_mask_);
+ /* Need to spill any FP regs? */
+ if (num_fp_spills_) {
+ /*
+ * NOTE: fp spills are a little different from core spills in that
+ * they are pushed as a contiguous block. When promoting from
+ * the fp set, we must allocate all singles from s16..highest-promoted
+ */
+ NewLIR1(kThumb2VPushCS, num_fp_spills_);
+ }
+ if (!skip_overflow_check) {
+ OpRegRegImm(kOpSub, rARM_LR, rARM_SP, frame_size_ - (spill_count * 4));
+ GenRegRegCheck(kCondCc, rARM_LR, r12, kThrowStackOverflow);
+ OpRegCopy(rARM_SP, rARM_LR); // Establish stack
+ } else {
+ OpRegImm(kOpSub, rARM_SP, frame_size_ - (spill_count * 4));
+ }
+
+ FlushIns(ArgLocs, rl_method);
+
+ FreeTemp(r0);
+ FreeTemp(r1);
+ FreeTemp(r2);
+ FreeTemp(r3);
+}
+
+void ArmMir2Lir::GenExitSequence()
+{
+ int spill_count = num_core_spills_ + num_fp_spills_;
+ /*
+ * In the exit path, r0/r1 are live - make sure they aren't
+ * allocated by the register utilities as temps.
+ */
+ LockTemp(r0);
+ LockTemp(r1);
+
+ NewLIR0(kPseudoMethodExit);
+ OpRegImm(kOpAdd, rARM_SP, frame_size_ - (spill_count * 4));
+ /* Need to restore any FP callee saves? */
+ if (num_fp_spills_) {
+ NewLIR1(kThumb2VPopCS, num_fp_spills_);
+ }
+ if (core_spill_mask_ & (1 << rARM_LR)) {
+ /* Unspill rARM_LR to rARM_PC */
+ core_spill_mask_ &= ~(1 << rARM_LR);
+ core_spill_mask_ |= (1 << rARM_PC);
+ }
+ NewLIR1(kThumb2Pop, core_spill_mask_);
+ if (!(core_spill_mask_ & (1 << rARM_PC))) {
+ /* We didn't pop to rARM_PC, so must do a bv rARM_LR */
+ NewLIR1(kThumbBx, rARM_LR);
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
new file mode 100644
index 0000000..a9199df
--- /dev/null
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_ARM_CODEGENARM_H_
+#define ART_SRC_COMPILER_DEX_QUICK_ARM_CODEGENARM_H_
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+class ArmMir2Lir : public Mir2Lir {
+ public:
+ ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
+
+ // Required for target - codegen helpers.
+ bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+ RegLocation rl_dest, int lit);
+ int LoadHelper(int offset);
+ LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
+ LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi,
+ int s_reg);
+ LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size);
+ LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_dest, int r_dest_hi, OpSize size, int s_reg);
+ LIR* LoadConstantNoClobber(int r_dest, int value);
+ LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value);
+ LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size);
+ LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi);
+ LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size);
+ LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_src, int r_src_hi, OpSize size, int s_reg);
+ void MarkGCCard(int val_reg, int tgt_addr_reg);
+
+ // Required for target - register utilities.
+ bool IsFpReg(int reg);
+ bool SameRegType(int reg1, int reg2);
+ int AllocTypedTemp(bool fp_hint, int reg_class);
+ int AllocTypedTempPair(bool fp_hint, int reg_class);
+ int S2d(int low_reg, int high_reg);
+ int TargetReg(SpecialTargetRegister reg);
+ RegisterInfo* GetRegInfo(int reg);
+ RegLocation GetReturnAlt();
+ RegLocation GetReturnWideAlt();
+ RegLocation LocCReturn();
+ RegLocation LocCReturnDouble();
+ RegLocation LocCReturnFloat();
+ RegLocation LocCReturnWide();
+ uint32_t FpRegMask();
+ uint64_t GetRegMaskCommon(int reg);
+ void AdjustSpillMask();
+ void ClobberCalleeSave();
+ void FlushReg(int reg);
+ void FlushRegWide(int reg1, int reg2);
+ void FreeCallTemps();
+ void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free);
+ void LockCallTemps();
+ void MarkPreservedSingle(int v_reg, int reg);
+ void CompilerInitializeRegAlloc();
+
+ // Required for target - miscellaneous.
+ AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
+ void SetupTargetResourceMasks(LIR* lir);
+ const char* GetTargetInstFmt(int opcode);
+ const char* GetTargetInstName(int opcode);
+ std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
+ uint64_t GetPCUseDefEncoding();
+ uint64_t GetTargetInstFlags(int opcode);
+ int GetInsnSize(LIR* lir);
+ bool IsUnconditionalBranch(LIR* lir);
+
+ // Required for target - Dalvik-level generators.
+ void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src, int scale);
+ void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale);
+ void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift);
+ void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2);
+ void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
+ bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
+ bool GenInlinedSqrt(CallInfo* info);
+ void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
+ void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
+ ThrowKind kind);
+ RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
+ RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
+ void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenDivZeroCheck(int reg_lo, int reg_hi);
+ void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
+ void GenExitSequence();
+ void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
+ void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
+ void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenMemBarrier(MemBarrierKind barrier_kind);
+ void GenMonitorEnter(int opt_flags, RegLocation rl_src);
+ void GenMonitorExit(int opt_flags, RegLocation rl_src);
+ void GenMoveException(RegLocation rl_dest);
+ void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
+ int first_bit, int second_bit);
+ void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
+ void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+
+ // Required for target - single operation generators.
+ LIR* OpUnconditionalBranch(LIR* target);
+ LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target);
+ LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target);
+ LIR* OpCondBranch(ConditionCode cc, LIR* target);
+ LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target);
+ LIR* OpFpRegCopy(int r_dest, int r_src);
+ LIR* OpIT(ConditionCode cond, const char* guide);
+ LIR* OpMem(OpKind op, int rBase, int disp);
+ LIR* OpPcRelLoad(int reg, LIR* target);
+ LIR* OpReg(OpKind op, int r_dest_src);
+ LIR* OpRegCopy(int r_dest, int r_src);
+ LIR* OpRegCopyNoInsert(int r_dest, int r_src);
+ LIR* OpRegImm(OpKind op, int r_dest_src1, int value);
+ LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset);
+ LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2);
+ LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value);
+ LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2);
+ LIR* OpTestSuspend(LIR* target);
+ LIR* OpThreadMem(OpKind op, int thread_offset);
+ LIR* OpVldm(int rBase, int count);
+ LIR* OpVstm(int rBase, int count);
+ void OpLea(int rBase, int reg1, int reg2, int scale, int offset);
+ void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi);
+ void OpTlsCmp(int offset, int val);
+
+ RegLocation ArgLoc(RegLocation loc);
+ LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size,
+ int s_reg);
+ LIR* StoreBaseDispBody(int rBase, int displacement, int r_src, int r_src_hi, OpSize size);
+ void GenPrintLabel(MIR* mir);
+ LIR* OpRegRegRegShift(OpKind op, int r_dest, int r_src1, int r_src2, int shift);
+ LIR* OpRegRegShift(OpKind op, int r_dest_src1, int r_src2, int shift);
+ static const ArmEncodingMap EncodingMap[kArmLast];
+ int EncodeShift(int code, int amount);
+ int ModifiedImmediate(uint32_t value);
+ ArmConditionCode ArmConditionEncoding(ConditionCode code);
+ bool InexpensiveConstantInt(int32_t value);
+ bool InexpensiveConstantFloat(int32_t value);
+ bool InexpensiveConstantLong(int64_t value);
+ bool InexpensiveConstantDouble(int64_t value);
+
+ private:
+ void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, int64_t val,
+ ConditionCode ccode);
+ int InPosition(int s_reg);
+ RegLocation LoadArg(RegLocation loc);
+ void LockLiveArgs(MIR* mir);
+ MIR* GetNextMir(BasicBlock** p_bb, MIR* mir);
+ MIR* SpecialIGet(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object);
+ MIR* SpecialIPut(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object);
+ MIR* SpecialIdentity(MIR* mir);
+ LIR* LoadFPConstantValue(int r_dest, int value);
+ bool BadOverlap(RegLocation rl_src, RegLocation rl_dest);
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_ARM_CODEGENARM_H_
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
new file mode 100644
index 0000000..53a5e1a
--- /dev/null
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+void ArmMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ int op = kThumbBkpt;
+ RegLocation rl_result;
+
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (opcode) {
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::ADD_FLOAT:
+ op = kThumb2Vadds;
+ break;
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT:
+ op = kThumb2Vsubs;
+ break;
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT:
+ op = kThumb2Vdivs;
+ break;
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT:
+ op = kThumb2Vmuls;
+ break;
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2, false);
+ rl_result = GetReturn(true);
+ StoreValue(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_FLOAT:
+ GenNegFloat(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR3(op, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenArithOpDouble(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ int op = kThumbBkpt;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::ADD_DOUBLE:
+ op = kThumb2Vaddd;
+ break;
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE:
+ op = kThumb2Vsubd;
+ break;
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE:
+ op = kThumb2Vdivd;
+ break;
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE:
+ op = kThumb2Vmuld;
+ break;
+ case Instruction::REM_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2, false);
+ rl_result = GetReturnWide(true);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_DOUBLE:
+ GenNegDouble(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ DCHECK(rl_src1.wide);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ DCHECK(rl_src2.wide);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ DCHECK(rl_dest.wide);
+ DCHECK(rl_result.wide);
+ NewLIR3(op, S2d(rl_result.low_reg, rl_result.high_reg), S2d(rl_src1.low_reg, rl_src1.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenConversion(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src)
+{
+ int op = kThumbBkpt;
+ int src_reg;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::INT_TO_FLOAT:
+ op = kThumb2VcvtIF;
+ break;
+ case Instruction::FLOAT_TO_INT:
+ op = kThumb2VcvtFI;
+ break;
+ case Instruction::DOUBLE_TO_FLOAT:
+ op = kThumb2VcvtDF;
+ break;
+ case Instruction::FLOAT_TO_DOUBLE:
+ op = kThumb2VcvtFd;
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ op = kThumb2VcvtID;
+ break;
+ case Instruction::DOUBLE_TO_INT:
+ op = kThumb2VcvtDI;
+ break;
+ case Instruction::LONG_TO_DOUBLE:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2d), rl_dest, rl_src);
+ return;
+ case Instruction::FLOAT_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src);
+ return;
+ case Instruction::LONG_TO_FLOAT:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2f), rl_dest, rl_src);
+ return;
+ case Instruction::DOUBLE_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ if (rl_src.wide) {
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ src_reg = S2d(rl_src.low_reg, rl_src.high_reg);
+ } else {
+ rl_src = LoadValue(rl_src, kFPReg);
+ src_reg = rl_src.low_reg;
+ }
+ if (rl_dest.wide) {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, S2d(rl_result.low_reg, rl_result.high_reg), src_reg);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, rl_result.low_reg, src_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
+ bool is_double)
+{
+ LIR* target = &block_label_list_[bb->taken->id];
+ RegLocation rl_src1;
+ RegLocation rl_src2;
+ if (is_double) {
+ rl_src1 = mir_graph_->GetSrcWide(mir, 0);
+ rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ NewLIR2(kThumb2Vcmpd, S2d(rl_src1.low_reg, rl_src2.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ } else {
+ rl_src1 = mir_graph_->GetSrc(mir, 0);
+ rl_src2 = mir_graph_->GetSrc(mir, 1);
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ NewLIR2(kThumb2Vcmps, rl_src1.low_reg, rl_src2.low_reg);
+ }
+ NewLIR0(kThumb2Fmstat);
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+ switch(ccode) {
+ case kCondEq:
+ case kCondNe:
+ break;
+ case kCondLt:
+ if (gt_bias) {
+ ccode = kCondMi;
+ }
+ break;
+ case kCondLe:
+ if (gt_bias) {
+ ccode = kCondLs;
+ }
+ break;
+ case kCondGt:
+ if (gt_bias) {
+ ccode = kCondHi;
+ }
+ break;
+ case kCondGe:
+ if (gt_bias) {
+ ccode = kCondCs;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCondBranch(ccode, target);
+}
+
+
+void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ bool is_double = false;
+ int default_result = -1;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::CMPL_FLOAT:
+ is_double = false;
+ default_result = -1;
+ break;
+ case Instruction::CMPG_FLOAT:
+ is_double = false;
+ default_result = 1;
+ break;
+ case Instruction::CMPL_DOUBLE:
+ is_double = true;
+ default_result = -1;
+ break;
+ case Instruction::CMPG_DOUBLE:
+ is_double = true;
+ default_result = 1;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ if (is_double) {
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadConstant(rl_result.low_reg, default_result);
+ NewLIR2(kThumb2Vcmpd, S2d(rl_src1.low_reg, rl_src2.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ } else {
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadConstant(rl_result.low_reg, default_result);
+ NewLIR2(kThumb2Vcmps, rl_src1.low_reg, rl_src2.low_reg);
+ }
+ DCHECK(!ARM_FPREG(rl_result.low_reg));
+ NewLIR0(kThumb2Fmstat);
+
+ OpIT((default_result == -1) ? kCondGt : kCondMi, "");
+ NewLIR2(kThumb2MovImmShift, rl_result.low_reg,
+ ModifiedImmediate(-default_result)); // Must not alter ccodes
+ GenBarrier();
+
+ OpIT(kCondEq, "");
+ LoadConstant(rl_result.low_reg, 0);
+ GenBarrier();
+
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValue(rl_src, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kThumb2Vnegs, rl_result.low_reg, rl_src.low_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kThumb2Vnegd, S2d(rl_result.low_reg, rl_result.high_reg),
+ S2d(rl_src.low_reg, rl_src.high_reg));
+ StoreValueWide(rl_dest, rl_result);
+}
+
+bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+ LIR *branch;
+ RegLocation rl_src = info->args[0];
+ RegLocation rl_dest = InlineTargetWide(info); // double place for result
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kThumb2Vsqrtd, S2d(rl_result.low_reg, rl_result.high_reg),
+ S2d(rl_src.low_reg, rl_src.high_reg));
+ NewLIR2(kThumb2Vcmpd, S2d(rl_result.low_reg, rl_result.high_reg),
+ S2d(rl_result.low_reg, rl_result.high_reg));
+ NewLIR0(kThumb2Fmstat);
+ branch = NewLIR2(kThumbBCond, 0, kArmCondEq);
+ ClobberCalleeSave();
+ LockCallTemps(); // Using fixed registers
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pSqrt));
+ NewLIR3(kThumb2Fmrrd, r0, r1, S2d(rl_src.low_reg, rl_src.high_reg));
+ NewLIR1(kThumbBlxR, r_tgt);
+ NewLIR3(kThumb2Fmdrr, S2d(rl_result.low_reg, rl_result.high_reg), r0, r1);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+}
+
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
new file mode 100644
index 0000000..feea896
--- /dev/null
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file contains codegen for the Thumb2 ISA. */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mirror/array.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1,
+ int src2, LIR* target)
+{
+ OpRegReg(kOpCmp, src1, src2);
+ return OpCondBranch(cond, target);
+}
+
+/*
+ * Generate a Thumb2 IT instruction, which can nullify up to
+ * four subsequent instructions based on a condition and its
+ * inverse. The condition applies to the first instruction, which
+ * is executed if the condition is met. The string "guide" consists
+ * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
+ * A "T" means the instruction is executed if the condition is
+ * met, and an "E" means the instruction is executed if the condition
+ * is not met.
+ */
+LIR* ArmMir2Lir::OpIT(ConditionCode ccode, const char* guide)
+{
+ int mask;
+ int mask3 = 0;
+ int mask2 = 0;
+ int mask1 = 0;
+ ArmConditionCode code = ArmConditionEncoding(ccode);
+ int cond_bit = code & 1;
+ int alt_bit = cond_bit ^ 1;
+
+ //Note: case fallthroughs intentional
+ switch (strlen(guide)) {
+ case 3:
+ mask1 = (guide[2] == 'T') ? cond_bit : alt_bit;
+ case 2:
+ mask2 = (guide[1] == 'T') ? cond_bit : alt_bit;
+ case 1:
+ mask3 = (guide[0] == 'T') ? cond_bit : alt_bit;
+ break;
+ case 0:
+ break;
+ default:
+ LOG(FATAL) << "OAT: bad case in OpIT";
+ }
+ mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
+ (1 << (3 - strlen(guide)));
+ return NewLIR2(kThumb2It, code, mask);
+}
+
+/*
+ * 64-bit 3way compare function.
+ * mov rX, #-1
+ * cmp op1hi, op2hi
+ * blt done
+ * bgt flip
+ * sub rX, op1lo, op2lo (treat as unsigned)
+ * beq done
+ * ite hi
+ * mov(hi) rX, #-1
+ * mov(!hi) rX, #1
+ * flip:
+ * neg rX
+ * done:
+ */
+void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LIR* target1;
+ LIR* target2;
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ int t_reg = AllocTemp();
+ LoadConstant(t_reg, -1);
+ OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
+ LIR* branch1 = OpCondBranch(kCondLt, NULL);
+ LIR* branch2 = OpCondBranch(kCondGt, NULL);
+ OpRegRegReg(kOpSub, t_reg, rl_src1.low_reg, rl_src2.low_reg);
+ LIR* branch3 = OpCondBranch(kCondEq, NULL);
+
+ OpIT(kCondHi, "E");
+ NewLIR2(kThumb2MovImmShift, t_reg, ModifiedImmediate(-1));
+ LoadConstant(t_reg, 1);
+ GenBarrier();
+
+ target2 = NewLIR0(kPseudoTargetLabel);
+ OpRegReg(kOpNeg, t_reg, t_reg);
+
+ target1 = NewLIR0(kPseudoTargetLabel);
+
+ RegLocation rl_temp = LocCReturn(); // Just using as template, will change
+ rl_temp.low_reg = t_reg;
+ StoreValue(rl_dest, rl_temp);
+ FreeTemp(t_reg);
+
+ branch1->target = target1;
+ branch2->target = target2;
+ branch3->target = branch1->target;
+}
+
+void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
+ int64_t val, ConditionCode ccode)
+{
+ int32_t val_lo = Low32Bits(val);
+ int32_t val_hi = High32Bits(val);
+ DCHECK(ModifiedImmediate(val_lo) >= 0);
+ DCHECK(ModifiedImmediate(val_hi) >= 0);
+ LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ int32_t low_reg = rl_src1.low_reg;
+ int32_t high_reg = rl_src1.high_reg;
+
+ switch(ccode) {
+ case kCondEq:
+ case kCondNe:
+ LIR* target;
+ ConditionCode condition;
+ if (ccode == kCondEq) {
+ target = not_taken;
+ condition = kCondEq;
+ } else {
+ target = taken;
+ condition = kCondNe;
+ }
+ if (val == 0) {
+ int t_reg = AllocTemp();
+ NewLIR4(kThumb2OrrRRRs, t_reg, low_reg, high_reg, 0);
+ FreeTemp(t_reg);
+ OpCondBranch(condition, taken);
+ return;
+ }
+ OpCmpImmBranch(kCondNe, high_reg, val_hi, target);
+ break;
+ case kCondLt:
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken);
+ ccode = kCondCc;
+ break;
+ case kCondLe:
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken);
+ ccode = kCondLs;
+ break;
+ case kCondGt:
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken);
+ ccode = kCondHi;
+ break;
+ case kCondGe:
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken);
+ ccode = kCondCs;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCmpImmBranch(ccode, low_reg, val_lo, taken);
+}
+
+void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir)
+{
+ RegLocation rl_result;
+ RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
+ // Temporary debugging code
+ int dest_sreg = mir->ssa_rep->defs[0];
+ if ((dest_sreg < 0) || (dest_sreg >= mir_graph_->GetNumSSARegs())) {
+ LOG(INFO) << "Bad target sreg: " << dest_sreg << ", in "
+ << PrettyMethod(cu_->method_idx,*cu_->dex_file);
+ LOG(INFO) << "at dex offset 0x" << std::hex << mir->offset;
+ LOG(INFO) << "vreg = " << mir_graph_->SRegToVReg(dest_sreg);
+ LOG(INFO) << "num uses = " << mir->ssa_rep->num_uses;
+ if (mir->ssa_rep->num_uses == 1) {
+ LOG(INFO) << "CONST case, vals = " << mir->dalvikInsn.vB << ", " << mir->dalvikInsn.vC;
+ } else {
+ LOG(INFO) << "MOVE case, operands = " << mir->ssa_rep->uses[1] << ", "
+ << mir->ssa_rep->uses[2];
+ }
+ CHECK(false) << "Invalid target sreg on Select.";
+ }
+ // End temporary debugging code
+ RegLocation rl_dest = mir_graph_->GetDest(mir);
+ rl_src = LoadValue(rl_src, kCoreReg);
+ if (mir->ssa_rep->num_uses == 1) {
+ // CONST case
+ int true_val = mir->dalvikInsn.vB;
+ int false_val = mir->dalvikInsn.vC;
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if ((true_val == 1) && (false_val == 0)) {
+ OpRegRegImm(kOpRsub, rl_result.low_reg, rl_src.low_reg, 1);
+ OpIT(kCondCc, "");
+ LoadConstant(rl_result.low_reg, 0);
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ } else if (InexpensiveConstantInt(true_val) && InexpensiveConstantInt(false_val)) {
+ OpRegImm(kOpCmp, rl_src.low_reg, 0);
+ OpIT(kCondEq, "E");
+ LoadConstant(rl_result.low_reg, true_val);
+ LoadConstant(rl_result.low_reg, false_val);
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ } else {
+ // Unlikely case - could be tuned.
+ int t_reg1 = AllocTemp();
+ int t_reg2 = AllocTemp();
+ LoadConstant(t_reg1, true_val);
+ LoadConstant(t_reg2, false_val);
+ OpRegImm(kOpCmp, rl_src.low_reg, 0);
+ OpIT(kCondEq, "E");
+ OpRegCopy(rl_result.low_reg, t_reg1);
+ OpRegCopy(rl_result.low_reg, t_reg2);
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ }
+ } else {
+ // MOVE case
+ RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
+ RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
+ rl_true = LoadValue(rl_true, kCoreReg);
+ rl_false = LoadValue(rl_false, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegImm(kOpCmp, rl_src.low_reg, 0);
+ OpIT(kCondEq, "E");
+ LIR* l1 = OpRegCopy(rl_result.low_reg, rl_true.low_reg);
+ l1->flags.is_nop = false; // Make sure this instruction isn't optimized away
+ LIR* l2 = OpRegCopy(rl_result.low_reg, rl_false.low_reg);
+ l2->flags.is_nop = false; // Make sure this instruction isn't optimized away
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ }
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir)
+{
+ RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
+ RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ // Normalize such that if either operand is constant, src2 will be constant.
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+ if (rl_src1.is_const) {
+ RegLocation rl_temp = rl_src1;
+ rl_src1 = rl_src2;
+ rl_src2 = rl_temp;
+ ccode = FlipComparisonOrder(ccode);
+ }
+ if (rl_src2.is_const) {
+ RegLocation rl_temp = UpdateLocWide(rl_src2);
+ // Do special compare/branch against simple const operand if not already in registers.
+ int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+ if ((rl_temp.location != kLocPhysReg) &&
+ ((ModifiedImmediate(Low32Bits(val)) >= 0) && (ModifiedImmediate(High32Bits(val)) >= 0))) {
+ GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
+ return;
+ }
+ }
+ LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
+ switch(ccode) {
+ case kCondEq:
+ OpCondBranch(kCondNe, not_taken);
+ break;
+ case kCondNe:
+ OpCondBranch(kCondNe, taken);
+ break;
+ case kCondLt:
+ OpCondBranch(kCondLt, taken);
+ OpCondBranch(kCondGt, not_taken);
+ ccode = kCondCc;
+ break;
+ case kCondLe:
+ OpCondBranch(kCondLt, taken);
+ OpCondBranch(kCondGt, not_taken);
+ ccode = kCondLs;
+ break;
+ case kCondGt:
+ OpCondBranch(kCondGt, taken);
+ OpCondBranch(kCondLt, not_taken);
+ ccode = kCondHi;
+ break;
+ case kCondGe:
+ OpCondBranch(kCondGt, taken);
+ OpCondBranch(kCondLt, not_taken);
+ ccode = kCondCs;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
+ OpCondBranch(ccode, taken);
+}
+
+/*
+ * Generate a register comparison to an immediate and branch. Caller
+ * is responsible for setting branch target field.
+ */
+LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
+ LIR* target)
+{
+ LIR* branch;
+ int mod_imm;
+ ArmConditionCode arm_cond = ArmConditionEncoding(cond);
+ if ((ARM_LOWREG(reg)) && (check_value == 0) &&
+ ((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) {
+ branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
+ reg, 0);
+ } else {
+ mod_imm = ModifiedImmediate(check_value);
+ if (ARM_LOWREG(reg) && ((check_value & 0xff) == check_value)) {
+ NewLIR2(kThumbCmpRI8, reg, check_value);
+ } else if (mod_imm >= 0) {
+ NewLIR2(kThumb2CmpRI12, reg, mod_imm);
+ } else {
+ int t_reg = AllocTemp();
+ LoadConstant(t_reg, check_value);
+ OpRegReg(kOpCmp, reg, t_reg);
+ }
+ branch = NewLIR2(kThumbBCond, 0, arm_cond);
+ }
+ branch->target = target;
+ return branch;
+}
+
+LIR* ArmMir2Lir::OpRegCopyNoInsert(int r_dest, int r_src)
+{
+ LIR* res;
+ int opcode;
+ if (ARM_FPREG(r_dest) || ARM_FPREG(r_src))
+ return OpFpRegCopy(r_dest, r_src);
+ if (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src))
+ opcode = kThumbMovRR;
+ else if (!ARM_LOWREG(r_dest) && !ARM_LOWREG(r_src))
+ opcode = kThumbMovRR_H2H;
+ else if (ARM_LOWREG(r_dest))
+ opcode = kThumbMovRR_H2L;
+ else
+ opcode = kThumbMovRR_L2H;
+ res = RawLIR(current_dalvik_offset_, opcode, r_dest, r_src);
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+LIR* ArmMir2Lir::OpRegCopy(int r_dest, int r_src)
+{
+ LIR* res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ return res;
+}
+
+void ArmMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
+ int src_hi)
+{
+ bool dest_fp = ARM_FPREG(dest_lo) && ARM_FPREG(dest_hi);
+ bool src_fp = ARM_FPREG(src_lo) && ARM_FPREG(src_hi);
+ DCHECK_EQ(ARM_FPREG(src_lo), ARM_FPREG(src_hi));
+ DCHECK_EQ(ARM_FPREG(dest_lo), ARM_FPREG(dest_hi));
+ if (dest_fp) {
+ if (src_fp) {
+ OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
+ } else {
+ NewLIR3(kThumb2Fmdrr, S2d(dest_lo, dest_hi), src_lo, src_hi);
+ }
+ } else {
+ if (src_fp) {
+ NewLIR3(kThumb2Fmrrd, dest_lo, dest_hi, S2d(src_lo, src_hi));
+ } else {
+ // Handle overlap
+ if (src_hi == dest_lo) {
+ OpRegCopy(dest_hi, src_hi);
+ OpRegCopy(dest_lo, src_lo);
+ } else {
+ OpRegCopy(dest_lo, src_lo);
+ OpRegCopy(dest_hi, src_hi);
+ }
+ }
+ }
+}
+
+// Table of magic divisors
+struct MagicTable {
+ uint32_t magic;
+ uint32_t shift;
+ DividePattern pattern;
+};
+
+static const MagicTable magic_table[] = {
+ {0, 0, DivideNone}, // 0
+ {0, 0, DivideNone}, // 1
+ {0, 0, DivideNone}, // 2
+ {0x55555556, 0, Divide3}, // 3
+ {0, 0, DivideNone}, // 4
+ {0x66666667, 1, Divide5}, // 5
+ {0x2AAAAAAB, 0, Divide3}, // 6
+ {0x92492493, 2, Divide7}, // 7
+ {0, 0, DivideNone}, // 8
+ {0x38E38E39, 1, Divide5}, // 9
+ {0x66666667, 2, Divide5}, // 10
+ {0x2E8BA2E9, 1, Divide5}, // 11
+ {0x2AAAAAAB, 1, Divide5}, // 12
+ {0x4EC4EC4F, 2, Divide5}, // 13
+ {0x92492493, 3, Divide7}, // 14
+ {0x88888889, 3, Divide7}, // 15
+};
+
+// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
+bool ArmMir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit)
+{
+ if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) {
+ return false;
+ }
+ DividePattern pattern = magic_table[lit].pattern;
+ if (pattern == DivideNone) {
+ return false;
+ }
+ // Tuning: add rem patterns
+ if (dalvik_opcode != Instruction::DIV_INT_LIT8) {
+ return false;
+ }
+
+ int r_magic = AllocTemp();
+ LoadConstant(r_magic, magic_table[lit].magic);
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int r_hi = AllocTemp();
+ int r_lo = AllocTemp();
+ NewLIR4(kThumb2Smull, r_lo, r_hi, r_magic, rl_src.low_reg);
+ switch(pattern) {
+ case Divide3:
+ OpRegRegRegShift(kOpSub, rl_result.low_reg, r_hi,
+ rl_src.low_reg, EncodeShift(kArmAsr, 31));
+ break;
+ case Divide5:
+ OpRegRegImm(kOpAsr, r_lo, rl_src.low_reg, 31);
+ OpRegRegRegShift(kOpRsub, rl_result.low_reg, r_lo, r_hi,
+ EncodeShift(kArmAsr, magic_table[lit].shift));
+ break;
+ case Divide7:
+ OpRegReg(kOpAdd, r_hi, rl_src.low_reg);
+ OpRegRegImm(kOpAsr, r_lo, rl_src.low_reg, 31);
+ OpRegRegRegShift(kOpRsub, rl_result.low_reg, r_lo, r_hi,
+ EncodeShift(kArmAsr, magic_table[lit].shift));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected pattern: " << pattern;
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code,
+ int reg1, int base, int offset, ThrowKind kind)
+{
+ LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
+ return NULL;
+}
+
+RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit,
+ bool is_div)
+{
+ LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
+ return rl_dest;
+}
+
+RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2,
+ bool is_div)
+{
+ LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
+ return rl_dest;
+}
+
+bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min)
+{
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+ RegLocation rl_src1 = info->args[0];
+ RegLocation rl_src2 = info->args[1];
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
+ OpIT((is_min) ? kCondGt : kCondLt, "E");
+ OpRegReg(kOpMov, rl_result.low_reg, rl_src2.low_reg);
+ OpRegReg(kOpMov, rl_result.low_reg, rl_src1.low_reg);
+ GenBarrier();
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+void ArmMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset)
+{
+ LOG(FATAL) << "Unexpected use of OpLea for Arm";
+}
+
+void ArmMir2Lir::OpTlsCmp(int offset, int val)
+{
+ LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm";
+}
+
+bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) {
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+ // Unused - RegLocation rl_src_unsafe = info->args[0];
+ RegLocation rl_src_obj= info->args[1]; // Object - known non-null
+ RegLocation rl_src_offset= info->args[2]; // long low
+ rl_src_offset.wide = 0; // ignore high half in info->args[3]
+ RegLocation rl_src_expected= info->args[4]; // int or Object
+ RegLocation rl_src_new_value= info->args[5]; // int or Object
+ RegLocation rl_dest = InlineTarget(info); // boolean place for result
+
+
+ // Release store semantics, get the barrier out of the way. TODO: revisit
+ GenMemBarrier(kStoreLoad);
+
+ RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
+ RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg);
+
+ if (need_write_barrier && !mir_graph_->IsConstantNullRef(rl_new_value)) {
+ // Mark card for object assuming new value is stored.
+ MarkGCCard(rl_new_value.low_reg, rl_object.low_reg);
+ }
+
+ RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
+
+ int r_ptr = AllocTemp();
+ OpRegRegReg(kOpAdd, r_ptr, rl_object.low_reg, rl_offset.low_reg);
+
+ // Free now unneeded rl_object and rl_offset to give more temps.
+ ClobberSReg(rl_object.s_reg_low);
+ FreeTemp(rl_object.low_reg);
+ ClobberSReg(rl_offset.s_reg_low);
+ FreeTemp(rl_offset.low_reg);
+
+ int r_old_value = AllocTemp();
+ NewLIR3(kThumb2Ldrex, r_old_value, r_ptr, 0); // r_old_value := [r_ptr]
+
+ RegLocation rl_expected = LoadValue(rl_src_expected, kCoreReg);
+
+ // if (r_old_value == rExpected) {
+ // [r_ptr] <- r_new_value && r_result := success ? 0 : 1
+ // r_result ^= 1
+ // } else {
+ // r_result := 0
+ // }
+ OpRegReg(kOpCmp, r_old_value, rl_expected.low_reg);
+ FreeTemp(r_old_value); // Now unneeded.
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpIT(kCondEq, "TE");
+ NewLIR4(kThumb2Strex, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0);
+ FreeTemp(r_ptr); // Now unneeded.
+ OpRegImm(kOpXor, rl_result.low_reg, 1);
+ OpRegReg(kOpXor, rl_result.low_reg, rl_result.low_reg);
+
+ StoreValue(rl_dest, rl_result);
+
+ return true;
+}
+
+LIR* ArmMir2Lir::OpPcRelLoad(int reg, LIR* target)
+{
+ return RawLIR(current_dalvik_offset_, kThumb2LdrPcRel12, reg, 0, 0, 0, 0, target);
+}
+
+LIR* ArmMir2Lir::OpVldm(int rBase, int count)
+{
+ return NewLIR3(kThumb2Vldms, rBase, fr0, count);
+}
+
+LIR* ArmMir2Lir::OpVstm(int rBase, int count)
+{
+ return NewLIR3(kThumb2Vstms, rBase, fr0, count);
+}
+
+void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
+ RegLocation rl_result, int lit,
+ int first_bit, int second_bit)
+{
+ OpRegRegRegShift(kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg,
+ EncodeShift(kArmLsl, second_bit - first_bit));
+ if (first_bit != 0) {
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
+ }
+}
+
+void ArmMir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi)
+{
+ int t_reg = AllocTemp();
+ NewLIR4(kThumb2OrrRRRs, t_reg, reg_lo, reg_hi, 0);
+ FreeTemp(t_reg);
+ GenCheck(kCondEq, kThrowDivZero);
+}
+
+// Test suspend flag, return target of taken suspend branch
+LIR* ArmMir2Lir::OpTestSuspend(LIR* target)
+{
+ NewLIR2(kThumbSubRI8, rARM_SUSPEND, 1);
+ return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
+}
+
+// Decrement register and branch on condition
+LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target)
+{
+ // Combine sub & test using sub setflags encoding here
+ NewLIR3(kThumb2SubsRRI12, reg, reg, 1);
+ return OpCondBranch(c_code, target);
+}
+
+void ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind)
+{
+#if ANDROID_SMP != 0
+ int dmb_flavor;
+ // TODO: revisit Arm barrier kinds
+ switch (barrier_kind) {
+ case kLoadStore: dmb_flavor = kSY; break;
+ case kLoadLoad: dmb_flavor = kSY; break;
+ case kStoreStore: dmb_flavor = kST; break;
+ case kStoreLoad: dmb_flavor = kSY; break;
+ default:
+ LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
+ dmb_flavor = kSY; // quiet gcc.
+ break;
+ }
+ LIR* dmb = NewLIR1(kThumb2Dmb, dmb_flavor);
+ dmb->def_mask = ENCODE_ALL;
+#endif
+}
+
+void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src)
+{
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int z_reg = AllocTemp();
+ LoadConstantNoClobber(z_reg, 0);
+ // Check for destructive overlap
+ if (rl_result.low_reg == rl_src.high_reg) {
+ int t_reg = AllocTemp();
+ OpRegRegReg(kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
+ OpRegRegReg(kOpSbc, rl_result.high_reg, z_reg, t_reg);
+ FreeTemp(t_reg);
+ } else {
+ OpRegRegReg(kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
+ OpRegRegReg(kOpSbc, rl_result.high_reg, z_reg, rl_src.high_reg);
+ }
+ FreeTemp(z_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+
+ /*
+ * Check to see if a result pair has a misaligned overlap with an operand pair. This
+ * is not usual for dx to generate, but it is legal (for now). In a future rev of
+ * dex, we'll want to make this case illegal.
+ */
+bool ArmMir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest)
+{
+ DCHECK(rl_src.wide);
+ DCHECK(rl_dest.wide);
+ return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) == 1);
+}
+
+void ArmMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ /*
+ * To pull off inline multiply, we have a worst-case requirement of 8 temporary
+ * registers. Normally for Arm, we get 5. We can get to 6 by including
+ * lr in the temp set. The only problematic case is all operands and result are
+ * distinct, and none have been promoted. In that case, we can succeed by aggressively
+ * freeing operand temp registers after they are no longer needed. All other cases
+ * can proceed normally. We'll just punt on the case of the result having a misaligned
+ * overlap with either operand and send that case to a runtime handler.
+ */
+ RegLocation rl_result;
+ if (BadOverlap(rl_src1, rl_dest) || (BadOverlap(rl_src2, rl_dest))) {
+ int func_offset = ENTRYPOINT_OFFSET(pLmul);
+ FlushAllRegs();
+ CallRuntimeHelperRegLocationRegLocation(func_offset, rl_src1, rl_src2, false);
+ rl_result = GetReturnWide(false);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ }
+ // Temporarily add LR to the temp pool, and assign it to tmp1
+ MarkTemp(rARM_LR);
+ FreeTemp(rARM_LR);
+ int tmp1 = rARM_LR;
+ LockTemp(rARM_LR);
+
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+
+ bool special_case = true;
+ // If operands are the same, or any pair has been promoted we're not the special case.
+ if ((rl_src1.s_reg_low == rl_src2.s_reg_low) ||
+ (!IsTemp(rl_src1.low_reg) && !IsTemp(rl_src1.high_reg)) ||
+ (!IsTemp(rl_src2.low_reg) && !IsTemp(rl_src2.high_reg))) {
+ special_case = false;
+ }
+ // Tuning: if rl_dest has been promoted and is *not* either operand, could use directly.
+ int res_lo = AllocTemp();
+ int res_hi;
+ if (rl_src1.low_reg == rl_src2.low_reg) {
+ res_hi = AllocTemp();
+ NewLIR3(kThumb2MulRRR, tmp1, rl_src1.low_reg, rl_src1.high_reg);
+ NewLIR4(kThumb2Umull, res_lo, res_hi, rl_src1.low_reg, rl_src1.low_reg);
+ OpRegRegRegShift(kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
+ } else {
+ // In the special case, all temps are now allocated
+ NewLIR3(kThumb2MulRRR, tmp1, rl_src2.low_reg, rl_src1.high_reg);
+ if (special_case) {
+ DCHECK_NE(rl_src1.low_reg, rl_src2.low_reg);
+ DCHECK_NE(rl_src1.high_reg, rl_src2.high_reg);
+ FreeTemp(rl_src1.high_reg);
+ }
+ res_hi = AllocTemp();
+
+ NewLIR4(kThumb2Umull, res_lo, res_hi, rl_src2.low_reg, rl_src1.low_reg);
+ NewLIR4(kThumb2Mla, tmp1, rl_src1.low_reg, rl_src2.high_reg, tmp1);
+ NewLIR4(kThumb2AddRRR, res_hi, tmp1, res_hi, 0);
+ if (special_case) {
+ FreeTemp(rl_src1.low_reg);
+ Clobber(rl_src1.low_reg);
+ Clobber(rl_src1.high_reg);
+ }
+ }
+ FreeTemp(tmp1);
+ rl_result = GetReturnWide(false); // Just using as a template.
+ rl_result.low_reg = res_lo;
+ rl_result.high_reg = res_hi;
+ StoreValueWide(rl_dest, rl_result);
+ // Now, restore lr to its non-temp status.
+ Clobber(rARM_LR);
+ UnmarkTemp(rARM_LR);
+}
+
+void ArmMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenAddLong for Arm";
+}
+
+void ArmMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenSubLong for Arm";
+}
+
+void ArmMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenAndLong for Arm";
+}
+
+void ArmMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenOrLong for Arm";
+}
+
+void ArmMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of genXoLong for Arm";
+}
+
+/*
+ * Generate array load
+ */
+void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+ RegLocation rl_result;
+ bool constant_index = rl_index.is_const;
+ rl_array = LoadValue(rl_array, kCoreReg);
+ if (!constant_index) {
+ rl_index = LoadValue(rl_index, kCoreReg);
+ }
+
+ if (rl_dest.wide) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ // If index is constant, just fold it into the data offset
+ if (constant_index) {
+ data_offset += mir_graph_->ConstantValue(rl_index) << scale;
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = AllocTemp();
+ /* Get len */
+ LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
+ }
+ if (rl_dest.wide || rl_dest.fp || constant_index) {
+ int reg_ptr;
+ if (constant_index) {
+ reg_ptr = rl_array.low_reg; // NOTE: must not alter reg_ptr in constant case.
+ } else {
+ // No special indexed operation, lea + load w/ displacement
+ reg_ptr = AllocTemp();
+ OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
+ EncodeShift(kArmLsl, scale));
+ FreeTemp(rl_index.low_reg);
+ }
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ if (needs_range_check) {
+ if (constant_index) {
+ GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+ } else {
+ GenRegRegCheck(kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
+ }
+ FreeTemp(reg_len);
+ }
+ if (rl_dest.wide) {
+ LoadBaseDispWide(reg_ptr, data_offset, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+ if (!constant_index) {
+ FreeTemp(reg_ptr);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ LoadBaseDisp(reg_ptr, data_offset, rl_result.low_reg, size, INVALID_SREG);
+ if (!constant_index) {
+ FreeTemp(reg_ptr);
+ }
+ StoreValue(rl_dest, rl_result);
+ }
+ } else {
+ // Offset base, then use indexed load
+ int reg_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
+ FreeTemp(rl_array.low_reg);
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ if (needs_range_check) {
+ // TODO: change kCondCS to a more meaningful name, is the sense of
+ // carry-set/clear flipped?
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size);
+ FreeTemp(reg_ptr);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+ bool constant_index = rl_index.is_const;
+
+ if (rl_src.wide) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ // If index is constant, just fold it into the data offset.
+ if (constant_index) {
+ data_offset += mir_graph_->ConstantValue(rl_index) << scale;
+ }
+
+ rl_array = LoadValue(rl_array, kCoreReg);
+ if (!constant_index) {
+ rl_index = LoadValue(rl_index, kCoreReg);
+ }
+
+ int reg_ptr;
+ if (constant_index) {
+ reg_ptr = rl_array.low_reg;
+ } else if (IsTemp(rl_array.low_reg)) {
+ Clobber(rl_array.low_reg);
+ reg_ptr = rl_array.low_reg;
+ } else {
+ reg_ptr = AllocTemp();
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = AllocTemp();
+ //NOTE: max live temps(4) here.
+ /* Get len */
+ LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
+ }
+ /* at this point, reg_ptr points to array, 2 live temps */
+ if (rl_src.wide || rl_src.fp || constant_index) {
+ if (rl_src.wide) {
+ rl_src = LoadValueWide(rl_src, reg_class);
+ } else {
+ rl_src = LoadValue(rl_src, reg_class);
+ }
+ if (!constant_index) {
+ OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
+ EncodeShift(kArmLsl, scale));
+ }
+ if (needs_range_check) {
+ if (constant_index) {
+ GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+ } else {
+ GenRegRegCheck(kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
+ }
+ FreeTemp(reg_len);
+ }
+
+ if (rl_src.wide) {
+ StoreBaseDispWide(reg_ptr, data_offset, rl_src.low_reg, rl_src.high_reg);
+ } else {
+ StoreBaseDisp(reg_ptr, data_offset, rl_src.low_reg, size);
+ }
+ } else {
+ /* reg_ptr -> array data */
+ OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
+ rl_src = LoadValue(rl_src, reg_class);
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
+ scale, size);
+ }
+ if (!constant_index) {
+ FreeTemp(reg_ptr);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void ArmMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
+
+ FlushAllRegs(); // Use explicit registers
+ LockCallTemps();
+
+ int r_value = TargetReg(kArg0); // Register holding value
+ int r_array_class = TargetReg(kArg1); // Register holding array's Class
+ int r_array = TargetReg(kArg2); // Register holding array
+ int r_index = TargetReg(kArg3); // Register holding index into array
+
+ LoadValueDirectFixed(rl_array, r_array); // Grab array
+ LoadValueDirectFixed(rl_src, r_value); // Grab value
+ LoadValueDirectFixed(rl_index, r_index); // Grab index
+
+ GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
+
+ // Store of null?
+ LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
+
+ // Get the array's class.
+ LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value,
+ r_array_class, true);
+ // Redo LoadValues in case they didn't survive the call.
+ LoadValueDirectFixed(rl_array, r_array); // Reload array
+ LoadValueDirectFixed(rl_index, r_index); // Reload index
+ LoadValueDirectFixed(rl_src, r_value); // Reload value
+ r_array_class = INVALID_REG;
+
+ // Branch here if value to be stored == null
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ null_value_check->target = target;
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = TargetReg(kArg1);
+ LoadWordDisp(r_array, len_offset, reg_len); // Get len
+ }
+ /* r_ptr -> array data */
+ int r_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
+ }
+ StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
+ FreeTemp(r_ptr);
+ FreeTemp(r_index);
+ if (!mir_graph_->IsConstantNullRef(rl_src)) {
+ MarkGCCard(r_value, r_array);
+ }
+}
+
+void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift)
+{
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ // Per spec, we only care about low 6 bits of shift amount.
+ int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
+ if (shift_amount == 0) {
+ StoreValueWide(rl_dest, rl_src);
+ return;
+ }
+ if (BadOverlap(rl_src, rl_dest)) {
+ GenShiftOpLong(opcode, rl_dest, rl_src, rl_shift);
+ return;
+ }
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ switch(opcode) {
+ case Instruction::SHL_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ if (shift_amount == 1) {
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg);
+ OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, rl_src.high_reg);
+ } else if (shift_amount == 32) {
+ OpRegCopy(rl_result.high_reg, rl_src.low_reg);
+ LoadConstant(rl_result.low_reg, 0);
+ } else if (shift_amount > 31) {
+ OpRegRegImm(kOpLsl, rl_result.high_reg, rl_src.low_reg, shift_amount - 32);
+ LoadConstant(rl_result.low_reg, 0);
+ } else {
+ OpRegRegImm(kOpLsl, rl_result.high_reg, rl_src.high_reg, shift_amount);
+ OpRegRegRegShift(kOpOr, rl_result.high_reg, rl_result.high_reg, rl_src.low_reg,
+ EncodeShift(kArmLsr, 32 - shift_amount));
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_src.low_reg, shift_amount);
+ }
+ break;
+ case Instruction::SHR_LONG:
+ case Instruction::SHR_LONG_2ADDR:
+ if (shift_amount == 32) {
+ OpRegCopy(rl_result.low_reg, rl_src.high_reg);
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
+ } else if (shift_amount > 31) {
+ OpRegRegImm(kOpAsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
+ } else {
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, shift_amount);
+ OpRegRegRegShift(kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
+ EncodeShift(kArmLsl, 32 - shift_amount));
+ FreeTemp(t_reg);
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
+ }
+ break;
+ case Instruction::USHR_LONG:
+ case Instruction::USHR_LONG_2ADDR:
+ if (shift_amount == 32) {
+ OpRegCopy(rl_result.low_reg, rl_src.high_reg);
+ LoadConstant(rl_result.high_reg, 0);
+ } else if (shift_amount > 31) {
+ OpRegRegImm(kOpLsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
+ LoadConstant(rl_result.high_reg, 0);
+ } else {
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, shift_amount);
+ OpRegRegRegShift(kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
+ EncodeShift(kArmLsl, 32 - shift_amount));
+ FreeTemp(t_reg);
+ OpRegRegImm(kOpLsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected case";
+ }
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ if ((opcode == Instruction::SUB_LONG_2ADDR) || (opcode == Instruction::SUB_LONG)) {
+ if (!rl_src2.is_const) {
+ // Don't bother with special handling for subtract from immediate.
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ } else {
+ // Normalize
+ if (!rl_src2.is_const) {
+ DCHECK(rl_src1.is_const);
+ RegLocation rl_temp = rl_src1;
+ rl_src1 = rl_src2;
+ rl_src2 = rl_temp;
+ }
+ }
+ if (BadOverlap(rl_src1, rl_dest)) {
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ DCHECK(rl_src2.is_const);
+ int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+ uint32_t val_lo = Low32Bits(val);
+ uint32_t val_hi = High32Bits(val);
+ int32_t mod_imm_lo = ModifiedImmediate(val_lo);
+ int32_t mod_imm_hi = ModifiedImmediate(val_hi);
+
+ // Only a subset of add/sub immediate instructions set carry - so bail if we don't fit
+ switch(opcode) {
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_LONG_2ADDR:
+ if ((mod_imm_lo < 0) || (mod_imm_hi < 0)) {
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ // NOTE: once we've done the EvalLoc on dest, we can no longer bail.
+ switch (opcode) {
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ NewLIR3(kThumb2AddRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
+ NewLIR3(kThumb2AdcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
+ break;
+ case Instruction::OR_LONG:
+ case Instruction::OR_LONG_2ADDR:
+ if ((val_lo != 0) || (rl_result.low_reg != rl_src1.low_reg)) {
+ OpRegRegImm(kOpOr, rl_result.low_reg, rl_src1.low_reg, val_lo);
+ }
+ if ((val_hi != 0) || (rl_result.high_reg != rl_src1.high_reg)) {
+ OpRegRegImm(kOpOr, rl_result.high_reg, rl_src1.high_reg, val_hi);
+ }
+ break;
+ case Instruction::XOR_LONG:
+ case Instruction::XOR_LONG_2ADDR:
+ OpRegRegImm(kOpXor, rl_result.low_reg, rl_src1.low_reg, val_lo);
+ OpRegRegImm(kOpXor, rl_result.high_reg, rl_src1.high_reg, val_hi);
+ break;
+ case Instruction::AND_LONG:
+ case Instruction::AND_LONG_2ADDR:
+ if ((val_lo != 0xffffffff) || (rl_result.low_reg != rl_src1.low_reg)) {
+ OpRegRegImm(kOpAnd, rl_result.low_reg, rl_src1.low_reg, val_lo);
+ }
+ if ((val_hi != 0xffffffff) || (rl_result.high_reg != rl_src1.high_reg)) {
+ OpRegRegImm(kOpAnd, rl_result.high_reg, rl_src1.high_reg, val_hi);
+ }
+ break;
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::SUB_LONG:
+ NewLIR3(kThumb2SubRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
+ NewLIR3(kThumb2SbcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode " << opcode;
+ }
+ StoreValueWide(rl_dest, rl_result);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
new file mode 100644
index 0000000..4bece13
--- /dev/null
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+static int core_regs[] = {r0, r1, r2, r3, rARM_SUSPEND, r5, r6, r7, r8, rARM_SELF, r10,
+ r11, r12, rARM_SP, rARM_LR, rARM_PC};
+static int ReservedRegs[] = {rARM_SUSPEND, rARM_SELF, rARM_SP, rARM_LR, rARM_PC};
+static int FpRegs[] = {fr0, fr1, fr2, fr3, fr4, fr5, fr6, fr7,
+ fr8, fr9, fr10, fr11, fr12, fr13, fr14, fr15,
+ fr16, fr17, fr18, fr19, fr20, fr21, fr22, fr23,
+ fr24, fr25, fr26, fr27, fr28, fr29, fr30, fr31};
+static int core_temps[] = {r0, r1, r2, r3, r12};
+static int fp_temps[] = {fr0, fr1, fr2, fr3, fr4, fr5, fr6, fr7,
+ fr8, fr9, fr10, fr11, fr12, fr13, fr14, fr15};
+
+RegLocation ArmMir2Lir::LocCReturn()
+{
+ RegLocation res = ARM_LOC_C_RETURN;
+ return res;
+}
+
+RegLocation ArmMir2Lir::LocCReturnWide()
+{
+ RegLocation res = ARM_LOC_C_RETURN_WIDE;
+ return res;
+}
+
+RegLocation ArmMir2Lir::LocCReturnFloat()
+{
+ RegLocation res = ARM_LOC_C_RETURN_FLOAT;
+ return res;
+}
+
+RegLocation ArmMir2Lir::LocCReturnDouble()
+{
+ RegLocation res = ARM_LOC_C_RETURN_DOUBLE;
+ return res;
+}
+
+// Return a target-dependent special register.
+int ArmMir2Lir::TargetReg(SpecialTargetRegister reg) {
+ int res = INVALID_REG;
+ switch (reg) {
+ case kSelf: res = rARM_SELF; break;
+ case kSuspend: res = rARM_SUSPEND; break;
+ case kLr: res = rARM_LR; break;
+ case kPc: res = rARM_PC; break;
+ case kSp: res = rARM_SP; break;
+ case kArg0: res = rARM_ARG0; break;
+ case kArg1: res = rARM_ARG1; break;
+ case kArg2: res = rARM_ARG2; break;
+ case kArg3: res = rARM_ARG3; break;
+ case kFArg0: res = rARM_FARG0; break;
+ case kFArg1: res = rARM_FARG1; break;
+ case kFArg2: res = rARM_FARG2; break;
+ case kFArg3: res = rARM_FARG3; break;
+ case kRet0: res = rARM_RET0; break;
+ case kRet1: res = rARM_RET1; break;
+ case kInvokeTgt: res = rARM_INVOKE_TGT; break;
+ case kCount: res = rARM_COUNT; break;
+ }
+ return res;
+}
+
+
+// Create a double from a pair of singles.
+int ArmMir2Lir::S2d(int low_reg, int high_reg)
+{
+ return ARM_S2D(low_reg, high_reg);
+}
+
+// Return mask to strip off fp reg flags and bias.
+uint32_t ArmMir2Lir::FpRegMask()
+{
+ return ARM_FP_REG_MASK;
+}
+
+// True if both regs single, both core or both double.
+bool ArmMir2Lir::SameRegType(int reg1, int reg2)
+{
+ return (ARM_REGTYPE(reg1) == ARM_REGTYPE(reg2));
+}
+
+/*
+ * Decode the register id.
+ */
+uint64_t ArmMir2Lir::GetRegMaskCommon(int reg)
+{
+ uint64_t seed;
+ int shift;
+ int reg_id;
+
+
+ reg_id = reg & 0x1f;
+ /* Each double register is equal to a pair of single-precision FP registers */
+ seed = ARM_DOUBLEREG(reg) ? 3 : 1;
+ /* FP register starts at bit position 16 */
+ shift = ARM_FPREG(reg) ? kArmFPReg0 : 0;
+ /* Expand the double register id into single offset */
+ shift += reg_id;
+ return (seed << shift);
+}
+
+uint64_t ArmMir2Lir::GetPCUseDefEncoding()
+{
+ return ENCODE_ARM_REG_PC;
+}
+
+void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir)
+{
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+
+ // Thumb2 specific setup
+ uint64_t flags = ArmMir2Lir::EncodingMap[lir->opcode].flags;
+ int opcode = lir->opcode;
+
+ if (flags & REG_DEF_SP) {
+ lir->def_mask |= ENCODE_ARM_REG_SP;
+ }
+
+ if (flags & REG_USE_SP) {
+ lir->use_mask |= ENCODE_ARM_REG_SP;
+ }
+
+ if (flags & REG_DEF_LIST0) {
+ lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_DEF_LIST1) {
+ lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & REG_DEF_FPCS_LIST0) {
+ lir->def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_DEF_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->def_mask, lir->operands[1] + i);
+ }
+ }
+
+ if (flags & REG_USE_PC) {
+ lir->use_mask |= ENCODE_ARM_REG_PC;
+ }
+
+ /* Conservatively treat the IT block */
+ if (flags & IS_IT) {
+ lir->def_mask = ENCODE_ALL;
+ }
+
+ if (flags & REG_USE_LIST0) {
+ lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_LIST1) {
+ lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & REG_USE_FPCS_LIST0) {
+ lir->use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->use_mask, lir->operands[1] + i);
+ }
+ }
+ /* Fixup for kThumbPush/lr and kThumbPop/pc */
+ if (opcode == kThumbPush || opcode == kThumbPop) {
+ uint64_t r8Mask = GetRegMaskCommon(r8);
+ if ((opcode == kThumbPush) && (lir->use_mask & r8Mask)) {
+ lir->use_mask &= ~r8Mask;
+ lir->use_mask |= ENCODE_ARM_REG_LR;
+ } else if ((opcode == kThumbPop) && (lir->def_mask & r8Mask)) {
+ lir->def_mask &= ~r8Mask;
+ lir->def_mask |= ENCODE_ARM_REG_PC;
+ }
+ }
+ if (flags & REG_DEF_LR) {
+ lir->def_mask |= ENCODE_ARM_REG_LR;
+ }
+}
+
+ArmConditionCode ArmMir2Lir::ArmConditionEncoding(ConditionCode ccode)
+{
+ ArmConditionCode res;
+ switch (ccode) {
+ case kCondEq: res = kArmCondEq; break;
+ case kCondNe: res = kArmCondNe; break;
+ case kCondCs: res = kArmCondCs; break;
+ case kCondCc: res = kArmCondCc; break;
+ case kCondMi: res = kArmCondMi; break;
+ case kCondPl: res = kArmCondPl; break;
+ case kCondVs: res = kArmCondVs; break;
+ case kCondVc: res = kArmCondVc; break;
+ case kCondHi: res = kArmCondHi; break;
+ case kCondLs: res = kArmCondLs; break;
+ case kCondGe: res = kArmCondGe; break;
+ case kCondLt: res = kArmCondLt; break;
+ case kCondGt: res = kArmCondGt; break;
+ case kCondLe: res = kArmCondLe; break;
+ case kCondAl: res = kArmCondAl; break;
+ case kCondNv: res = kArmCondNv; break;
+ default:
+ LOG(FATAL) << "Bad condition code " << ccode;
+ res = static_cast<ArmConditionCode>(0); // Quiet gcc
+ }
+ return res;
+}
+
+static const char* core_reg_names[16] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "rSELF",
+ "r10",
+ "r11",
+ "r12",
+ "sp",
+ "lr",
+ "pc",
+};
+
+
+static const char* shift_names[4] = {
+ "lsl",
+ "lsr",
+ "asr",
+ "ror"};
+
+/* Decode and print a ARM register name */
+static char* DecodeRegList(int opcode, int vector, char* buf)
+{
+ int i;
+ bool printed = false;
+ buf[0] = 0;
+ for (i = 0; i < 16; i++, vector >>= 1) {
+ if (vector & 0x1) {
+ int reg_id = i;
+ if (opcode == kThumbPush && i == 8) {
+ reg_id = r14lr;
+ } else if (opcode == kThumbPop && i == 8) {
+ reg_id = r15pc;
+ }
+ if (printed) {
+ sprintf(buf + strlen(buf), ", r%d", reg_id);
+ } else {
+ printed = true;
+ sprintf(buf, "r%d", reg_id);
+ }
+ }
+ }
+ return buf;
+}
+
+static char* DecodeFPCSRegList(int count, int base, char* buf)
+{
+ sprintf(buf, "s%d", base);
+ for (int i = 1; i < count; i++) {
+ sprintf(buf + strlen(buf), ", s%d",base + i);
+ }
+ return buf;
+}
+
+static int ExpandImmediate(int value)
+{
+ int mode = (value & 0xf00) >> 8;
+ uint32_t bits = value & 0xff;
+ switch (mode) {
+ case 0:
+ return bits;
+ case 1:
+ return (bits << 16) | bits;
+ case 2:
+ return (bits << 24) | (bits << 8);
+ case 3:
+ return (bits << 24) | (bits << 16) | (bits << 8) | bits;
+ default:
+ break;
+ }
+ bits = (bits | 0x80) << 24;
+ return bits >> (((value & 0xf80) >> 7) - 8);
+}
+
+const char* cc_names[] = {"eq","ne","cs","cc","mi","pl","vs","vc",
+ "hi","ls","ge","lt","gt","le","al","nv"};
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr)
+{
+ std::string buf;
+ int i;
+ const char* fmt_end = &fmt[strlen(fmt)];
+ char tbuf[256];
+ const char* name;
+ char nc;
+ while (fmt < fmt_end) {
+ int operand;
+ if (*fmt == '!') {
+ fmt++;
+ DCHECK_LT(fmt, fmt_end);
+ nc = *fmt++;
+ if (nc=='!') {
+ strcpy(tbuf, "!");
+ } else {
+ DCHECK_LT(fmt, fmt_end);
+ DCHECK_LT(static_cast<unsigned>(nc-'0'), 4U);
+ operand = lir->operands[nc-'0'];
+ switch (*fmt++) {
+ case 'H':
+ if (operand != 0) {
+ sprintf(tbuf, ", %s %d",shift_names[operand & 0x3], operand >> 2);
+ } else {
+ strcpy(tbuf,"");
+ }
+ break;
+ case 'B':
+ switch (operand) {
+ case kSY:
+ name = "sy";
+ break;
+ case kST:
+ name = "st";
+ break;
+ case kISH:
+ name = "ish";
+ break;
+ case kISHST:
+ name = "ishst";
+ break;
+ case kNSH:
+ name = "nsh";
+ break;
+ case kNSHST:
+ name = "shst";
+ break;
+ default:
+ name = "DecodeError2";
+ break;
+ }
+ strcpy(tbuf, name);
+ break;
+ case 'b':
+ strcpy(tbuf,"0000");
+ for (i=3; i>= 0; i--) {
+ tbuf[i] += operand & 1;
+ operand >>= 1;
+ }
+ break;
+ case 'n':
+ operand = ~ExpandImmediate(operand);
+ sprintf(tbuf,"%d [%#x]", operand, operand);
+ break;
+ case 'm':
+ operand = ExpandImmediate(operand);
+ sprintf(tbuf,"%d [%#x]", operand, operand);
+ break;
+ case 's':
+ sprintf(tbuf,"s%d",operand & ARM_FP_REG_MASK);
+ break;
+ case 'S':
+ sprintf(tbuf,"d%d",(operand & ARM_FP_REG_MASK) >> 1);
+ break;
+ case 'h':
+ sprintf(tbuf,"%04x", operand);
+ break;
+ case 'M':
+ case 'd':
+ sprintf(tbuf,"%d", operand);
+ break;
+ case 'C':
+ DCHECK_LT(operand, static_cast<int>(
+ sizeof(core_reg_names)/sizeof(core_reg_names[0])));
+ sprintf(tbuf,"%s",core_reg_names[operand]);
+ break;
+ case 'E':
+ sprintf(tbuf,"%d", operand*4);
+ break;
+ case 'F':
+ sprintf(tbuf,"%d", operand*2);
+ break;
+ case 'c':
+ strcpy(tbuf, cc_names[operand]);
+ break;
+ case 't':
+ sprintf(tbuf,"0x%08x (L%p)",
+ reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 +
+ (operand << 1),
+ lir->target);
+ break;
+ case 'u': {
+ int offset_1 = lir->operands[0];
+ int offset_2 = NEXT_LIR(lir)->operands[0];
+ uintptr_t target =
+ (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) &
+ ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+ 0xfffffffc;
+ sprintf(tbuf, "%p", reinterpret_cast<void *>(target));
+ break;
+ }
+
+ /* Nothing to print for BLX_2 */
+ case 'v':
+ strcpy(tbuf, "see above");
+ break;
+ case 'R':
+ DecodeRegList(lir->opcode, operand, tbuf);
+ break;
+ case 'P':
+ DecodeFPCSRegList(operand, 16, tbuf);
+ break;
+ case 'Q':
+ DecodeFPCSRegList(operand, 0, tbuf);
+ break;
+ default:
+ strcpy(tbuf,"DecodeError1");
+ break;
+ }
+ buf += tbuf;
+ }
+ } else {
+ buf += *fmt++;
+ }
+ }
+ return buf;
+}
+
+void ArmMir2Lir::DumpResourceMask(LIR* arm_lir, uint64_t mask, const char* prefix)
+{
+ char buf[256];
+ buf[0] = 0;
+
+ if (mask == ENCODE_ALL) {
+ strcpy(buf, "all");
+ } else {
+ char num[8];
+ int i;
+
+ for (i = 0; i < kArmRegEnd; i++) {
+ if (mask & (1ULL << i)) {
+ sprintf(num, "%d ", i);
+ strcat(buf, num);
+ }
+ }
+
+ if (mask & ENCODE_CCODE) {
+ strcat(buf, "cc ");
+ }
+ if (mask & ENCODE_FP_STATUS) {
+ strcat(buf, "fpcc ");
+ }
+
+ /* Memory bits */
+ if (arm_lir && (mask & ENCODE_DALVIK_REG)) {
+ sprintf(buf + strlen(buf), "dr%d%s", arm_lir->alias_info & 0xffff,
+ (arm_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ }
+ if (mask & ENCODE_LITERAL) {
+ strcat(buf, "lit ");
+ }
+
+ if (mask & ENCODE_HEAP_REF) {
+ strcat(buf, "heap ");
+ }
+ if (mask & ENCODE_MUST_NOT_ALIAS) {
+ strcat(buf, "noalias ");
+ }
+ }
+ if (buf[0]) {
+ LOG(INFO) << prefix << ": " << buf;
+ }
+}
+
+bool ArmMir2Lir::IsUnconditionalBranch(LIR* lir)
+{
+ return ((lir->opcode == kThumbBUncond) || (lir->opcode == kThumb2BUncond));
+}
+
+ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
+ : Mir2Lir(cu, mir_graph, arena) {
+ // Sanity check - make sure encoding map lines up.
+ for (int i = 0; i < kArmLast; i++) {
+ if (ArmMir2Lir::EncodingMap[i].opcode != i) {
+ LOG(FATAL) << "Encoding order for " << ArmMir2Lir::EncodingMap[i].name
+ << " is wrong: expecting " << i << ", seeing "
+ << static_cast<int>(ArmMir2Lir::EncodingMap[i].opcode);
+ }
+ }
+}
+
+Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena) {
+ return new ArmMir2Lir(cu, mir_graph, arena);
+}
+
+/*
+ * Alloc a pair of core registers, or a double. Low reg in low byte,
+ * high reg in next byte.
+ */
+int ArmMir2Lir::AllocTypedTempPair(bool fp_hint, int reg_class)
+{
+ int high_reg;
+ int low_reg;
+ int res = 0;
+
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
+ low_reg = AllocTempDouble();
+ high_reg = low_reg + 1;
+ } else {
+ low_reg = AllocTemp();
+ high_reg = AllocTemp();
+ }
+ res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
+ return res;
+}
+
+int ArmMir2Lir::AllocTypedTemp(bool fp_hint, int reg_class)
+{
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg))
+ return AllocTempFloat();
+ return AllocTemp();
+}
+
+void ArmMir2Lir::CompilerInitializeRegAlloc()
+{
+ int num_regs = sizeof(core_regs)/sizeof(*core_regs);
+ int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs);
+ int num_temps = sizeof(core_temps)/sizeof(*core_temps);
+ int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
+ int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
+ reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_core_regs = num_regs;
+ reg_pool_->core_regs = reinterpret_cast<RegisterInfo*>
+ (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_fp_regs = num_fp_regs;
+ reg_pool_->FPRegs = static_cast<RegisterInfo*>
+ (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
+ CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
+ // Keep special registers from being allocated
+ for (int i = 0; i < num_reserved; i++) {
+ if (NO_SUSPEND && (ReservedRegs[i] == rARM_SUSPEND)) {
+ //To measure cost of suspend check
+ continue;
+ }
+ MarkInUse(ReservedRegs[i]);
+ }
+ // Mark temp regs - all others not in use can be used for promotion
+ for (int i = 0; i < num_temps; i++) {
+ MarkTemp(core_temps[i]);
+ }
+ for (int i = 0; i < num_fp_temps; i++) {
+ MarkTemp(fp_temps[i]);
+ }
+
+ // Start allocation at r2 in an attempt to avoid clobbering return values
+ reg_pool_->next_core_reg = r2;
+}
+
+void ArmMir2Lir::FreeRegLocTemps(RegLocation rl_keep,
+ RegLocation rl_free)
+{
+ if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) &&
+ (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) {
+ // No overlap, free both
+ FreeTemp(rl_free.low_reg);
+ FreeTemp(rl_free.high_reg);
+ }
+}
+/*
+ * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some
+ * instructions might call out to C/assembly helper functions. Until
+ * machinery is in place, always spill lr.
+ */
+
+void ArmMir2Lir::AdjustSpillMask()
+{
+ core_spill_mask_ |= (1 << rARM_LR);
+ num_core_spills_++;
+}
+
+/*
+ * Mark a callee-save fp register as promoted. Note that
+ * vpush/vpop uses contiguous register lists so we must
+ * include any holes in the mask. Associate holes with
+ * Dalvik register INVALID_VREG (0xFFFFU).
+ */
+void ArmMir2Lir::MarkPreservedSingle(int v_reg, int reg)
+{
+ DCHECK_GE(reg, ARM_FP_REG_MASK + ARM_FP_CALLEE_SAVE_BASE);
+ reg = (reg & ARM_FP_REG_MASK) - ARM_FP_CALLEE_SAVE_BASE;
+ // Ensure fp_vmap_table is large enough
+ int table_size = fp_vmap_table_.size();
+ for (int i = table_size; i < (reg + 1); i++) {
+ fp_vmap_table_.push_back(INVALID_VREG);
+ }
+ // Add the current mapping
+ fp_vmap_table_[reg] = v_reg;
+ // Size of fp_vmap_table is high-water mark, use to set mask
+ num_fp_spills_ = fp_vmap_table_.size();
+ fp_spill_mask_ = ((1 << num_fp_spills_) - 1) << ARM_FP_CALLEE_SAVE_BASE;
+}
+
+void ArmMir2Lir::FlushRegWide(int reg1, int reg2)
+{
+ RegisterInfo* info1 = GetRegInfo(reg1);
+ RegisterInfo* info2 = GetRegInfo(reg2);
+ DCHECK(info1 && info2 && info1->pair && info2->pair &&
+ (info1->partner == info2->reg) &&
+ (info2->partner == info1->reg));
+ if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+ if (!(info1->is_temp && info2->is_temp)) {
+ /* Should not happen. If it does, there's a problem in eval_loc */
+ LOG(FATAL) << "Long half-temp, half-promoted";
+ }
+
+ info1->dirty = false;
+ info2->dirty = false;
+ if (mir_graph_->SRegToVReg(info2->s_reg) <
+ mir_graph_->SRegToVReg(info1->s_reg))
+ info1 = info2;
+ int v_reg = mir_graph_->SRegToVReg(info1->s_reg);
+ StoreBaseDispWide(rARM_SP, VRegOffset(v_reg), info1->reg, info1->partner);
+ }
+}
+
+void ArmMir2Lir::FlushReg(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ if (info->live && info->dirty) {
+ info->dirty = false;
+ int v_reg = mir_graph_->SRegToVReg(info->s_reg);
+ StoreBaseDisp(rARM_SP, VRegOffset(v_reg), reg, kWord);
+ }
+}
+
+/* Give access to the target-dependent FP register encoding to common code */
+bool ArmMir2Lir::IsFpReg(int reg) {
+ return ARM_FPREG(reg);
+}
+
+/* Clobber all regs that might be used by an external C call */
+void ArmMir2Lir::ClobberCalleeSave()
+{
+ Clobber(r0);
+ Clobber(r1);
+ Clobber(r2);
+ Clobber(r3);
+ Clobber(r12);
+ Clobber(r14lr);
+ Clobber(fr0);
+ Clobber(fr1);
+ Clobber(fr2);
+ Clobber(fr3);
+ Clobber(fr4);
+ Clobber(fr5);
+ Clobber(fr6);
+ Clobber(fr7);
+ Clobber(fr8);
+ Clobber(fr9);
+ Clobber(fr10);
+ Clobber(fr11);
+ Clobber(fr12);
+ Clobber(fr13);
+ Clobber(fr14);
+ Clobber(fr15);
+}
+
+RegLocation ArmMir2Lir::GetReturnWideAlt()
+{
+ RegLocation res = LocCReturnWide();
+ res.low_reg = r2;
+ res.high_reg = r3;
+ Clobber(r2);
+ Clobber(r3);
+ MarkInUse(r2);
+ MarkInUse(r3);
+ MarkPair(res.low_reg, res.high_reg);
+ return res;
+}
+
+RegLocation ArmMir2Lir::GetReturnAlt()
+{
+ RegLocation res = LocCReturn();
+ res.low_reg = r1;
+ Clobber(r1);
+ MarkInUse(r1);
+ return res;
+}
+
+ArmMir2Lir::RegisterInfo* ArmMir2Lir::GetRegInfo(int reg)
+{
+ return ARM_FPREG(reg) ? ®_pool_->FPRegs[reg & ARM_FP_REG_MASK]
+ : ®_pool_->core_regs[reg];
+}
+
+/* To be used when explicitly managing register use */
+void ArmMir2Lir::LockCallTemps()
+{
+ LockTemp(r0);
+ LockTemp(r1);
+ LockTemp(r2);
+ LockTemp(r3);
+}
+
+/* To be used when explicitly managing register use */
+void ArmMir2Lir::FreeCallTemps()
+{
+ FreeTemp(r0);
+ FreeTemp(r1);
+ FreeTemp(r2);
+ FreeTemp(r3);
+}
+
+int ArmMir2Lir::LoadHelper(int offset)
+{
+ LoadWordDisp(rARM_SELF, offset, rARM_LR);
+ return rARM_LR;
+}
+
+uint64_t ArmMir2Lir::GetTargetInstFlags(int opcode)
+{
+ return ArmMir2Lir::EncodingMap[opcode].flags;
+}
+
+const char* ArmMir2Lir::GetTargetInstName(int opcode)
+{
+ return ArmMir2Lir::EncodingMap[opcode].name;
+}
+
+const char* ArmMir2Lir::GetTargetInstFmt(int opcode)
+{
+ return ArmMir2Lir::EncodingMap[opcode].fmt;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
new file mode 100644
index 0000000..abf921f
--- /dev/null
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+/* This file contains codegen for the Thumb ISA. */
+
+static int EncodeImmSingle(int value)
+{
+ int res;
+ int bit_a = (value & 0x80000000) >> 31;
+ int not_bit_b = (value & 0x40000000) >> 30;
+ int bit_b = (value & 0x20000000) >> 29;
+ int b_smear = (value & 0x3e000000) >> 25;
+ int slice = (value & 0x01f80000) >> 19;
+ int zeroes = (value & 0x0007ffff);
+ if (zeroes != 0)
+ return -1;
+ if (bit_b) {
+ if ((not_bit_b != 0) || (b_smear != 0x1f))
+ return -1;
+ } else {
+ if ((not_bit_b != 1) || (b_smear != 0x0))
+ return -1;
+ }
+ res = (bit_a << 7) | (bit_b << 6) | slice;
+ return res;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 floating point
+ * immediate. If not, return -1. If so return encoded 8-bit value.
+ */
+static int EncodeImmDouble(int64_t value)
+{
+ int res;
+ int bit_a = (value & 0x8000000000000000ll) >> 63;
+ int not_bit_b = (value & 0x4000000000000000ll) >> 62;
+ int bit_b = (value & 0x2000000000000000ll) >> 61;
+ int b_smear = (value & 0x3fc0000000000000ll) >> 54;
+ int slice = (value & 0x003f000000000000ll) >> 48;
+ uint64_t zeroes = (value & 0x0000ffffffffffffll);
+ if (zeroes != 0)
+ return -1;
+ if (bit_b) {
+ if ((not_bit_b != 0) || (b_smear != 0xff))
+ return -1;
+ } else {
+ if ((not_bit_b != 1) || (b_smear != 0x0))
+ return -1;
+ }
+ res = (bit_a << 7) | (bit_b << 6) | slice;
+ return res;
+}
+
+LIR* ArmMir2Lir::LoadFPConstantValue(int r_dest, int value)
+{
+ DCHECK(ARM_SINGLEREG(r_dest));
+ if (value == 0) {
+ // TODO: we need better info about the target CPU. a vector exclusive or
+ // would probably be better here if we could rely on its existance.
+ // Load an immediate +2.0 (which encodes to 0)
+ NewLIR2(kThumb2Vmovs_IMM8, r_dest, 0);
+ // +0.0 = +2.0 - +2.0
+ return NewLIR3(kThumb2Vsubs, r_dest, r_dest, r_dest);
+ } else {
+ int encoded_imm = EncodeImmSingle(value);
+ if (encoded_imm >= 0) {
+ return NewLIR2(kThumb2Vmovs_IMM8, r_dest, encoded_imm);
+ }
+ }
+ LIR* data_target = ScanLiteralPool(literal_list_, value, 0);
+ if (data_target == NULL) {
+ data_target = AddWordData(&literal_list_, value);
+ }
+ LIR* load_pc_rel = RawLIR(current_dalvik_offset_, kThumb2Vldrs,
+ r_dest, r15pc, 0, 0, 0, data_target);
+ SetMemRefType(load_pc_rel, true, kLiteral);
+ load_pc_rel->alias_info = reinterpret_cast<uintptr_t>(data_target);
+ AppendLIR(load_pc_rel);
+ return load_pc_rel;
+}
+
+static int LeadingZeros(uint32_t val)
+{
+ uint32_t alt;
+ int n;
+ int count;
+
+ count = 16;
+ n = 32;
+ do {
+ alt = val >> count;
+ if (alt != 0) {
+ n = n - count;
+ val = alt;
+ }
+ count >>= 1;
+ } while (count);
+ return n - val;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 modified
+ * immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
+ */
+int ArmMir2Lir::ModifiedImmediate(uint32_t value)
+{
+ int z_leading;
+ int z_trailing;
+ uint32_t b0 = value & 0xff;
+
+ /* Note: case of value==0 must use 0:000:0:0000000 encoding */
+ if (value <= 0xFF)
+ return b0; // 0:000:a:bcdefgh
+ if (value == ((b0 << 16) | b0))
+ return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
+ if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
+ return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
+ b0 = (value >> 8) & 0xff;
+ if (value == ((b0 << 24) | (b0 << 8)))
+ return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
+ /* Can we do it with rotation? */
+ z_leading = LeadingZeros(value);
+ z_trailing = 32 - LeadingZeros(~value & (value - 1));
+ /* A run of eight or fewer active bits? */
+ if ((z_leading + z_trailing) < 24)
+ return -1; /* No - bail */
+ /* left-justify the constant, discarding msb (known to be 1) */
+ value <<= z_leading + 1;
+ /* Create bcdefgh */
+ value >>= 25;
+ /* Put it all together */
+ return value | ((0x8 + z_leading) << 7); /* [01000..11111]:bcdefgh */
+}
+
+bool ArmMir2Lir::InexpensiveConstantInt(int32_t value)
+{
+ return (ModifiedImmediate(value) >= 0) || (ModifiedImmediate(~value) >= 0);
+}
+
+bool ArmMir2Lir::InexpensiveConstantFloat(int32_t value)
+{
+ return EncodeImmSingle(value) >= 0;
+}
+
+bool ArmMir2Lir::InexpensiveConstantLong(int64_t value)
+{
+ return InexpensiveConstantInt(High32Bits(value)) && InexpensiveConstantInt(Low32Bits(value));
+}
+
+bool ArmMir2Lir::InexpensiveConstantDouble(int64_t value)
+{
+ return EncodeImmDouble(value) >= 0;
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) r_dest is freshly returned from AllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+LIR* ArmMir2Lir::LoadConstantNoClobber(int r_dest, int value)
+{
+ LIR* res;
+ int mod_imm;
+
+ if (ARM_FPREG(r_dest)) {
+ return LoadFPConstantValue(r_dest, value);
+ }
+
+ /* See if the value can be constructed cheaply */
+ if (ARM_LOWREG(r_dest) && (value >= 0) && (value <= 255)) {
+ return NewLIR2(kThumbMovImm, r_dest, value);
+ }
+ /* Check Modified immediate special cases */
+ mod_imm = ModifiedImmediate(value);
+ if (mod_imm >= 0) {
+ res = NewLIR2(kThumb2MovImmShift, r_dest, mod_imm);
+ return res;
+ }
+ mod_imm = ModifiedImmediate(~value);
+ if (mod_imm >= 0) {
+ res = NewLIR2(kThumb2MvnImm12, r_dest, mod_imm);
+ return res;
+ }
+ /* 16-bit immediate? */
+ if ((value & 0xffff) == value) {
+ res = NewLIR2(kThumb2MovImm16, r_dest, value);
+ return res;
+ }
+ /* Do a low/high pair */
+ res = NewLIR2(kThumb2MovImm16, r_dest, Low16Bits(value));
+ NewLIR2(kThumb2MovImm16H, r_dest, High16Bits(value));
+ return res;
+}
+
+LIR* ArmMir2Lir::OpUnconditionalBranch(LIR* target)
+{
+ LIR* res = NewLIR1(kThumbBUncond, 0 /* offset to be patched during assembly*/);
+ res->target = target;
+ return res;
+}
+
+LIR* ArmMir2Lir::OpCondBranch(ConditionCode cc, LIR* target)
+{
+ LIR* branch = NewLIR2(kThumb2BCond, 0 /* offset to be patched */,
+ ArmConditionEncoding(cc));
+ branch->target = target;
+ return branch;
+}
+
+LIR* ArmMir2Lir::OpReg(OpKind op, int r_dest_src)
+{
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpBlx:
+ opcode = kThumbBlxR;
+ break;
+ default:
+ LOG(FATAL) << "Bad opcode " << op;
+ }
+ return NewLIR1(opcode, r_dest_src);
+}
+
+LIR* ArmMir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2,
+ int shift)
+{
+ bool thumb_form = ((shift == 0) && ARM_LOWREG(r_dest_src1) && ARM_LOWREG(r_src2));
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpAdc:
+ opcode = (thumb_form) ? kThumbAdcRR : kThumb2AdcRRR;
+ break;
+ case kOpAnd:
+ opcode = (thumb_form) ? kThumbAndRR : kThumb2AndRRR;
+ break;
+ case kOpBic:
+ opcode = (thumb_form) ? kThumbBicRR : kThumb2BicRRR;
+ break;
+ case kOpCmn:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbCmnRR : kThumb2CmnRR;
+ break;
+ case kOpCmp:
+ if (thumb_form)
+ opcode = kThumbCmpRR;
+ else if ((shift == 0) && !ARM_LOWREG(r_dest_src1) && !ARM_LOWREG(r_src2))
+ opcode = kThumbCmpHH;
+ else if ((shift == 0) && ARM_LOWREG(r_dest_src1))
+ opcode = kThumbCmpLH;
+ else if (shift == 0)
+ opcode = kThumbCmpHL;
+ else
+ opcode = kThumb2CmpRR;
+ break;
+ case kOpXor:
+ opcode = (thumb_form) ? kThumbEorRR : kThumb2EorRRR;
+ break;
+ case kOpMov:
+ DCHECK_EQ(shift, 0);
+ if (ARM_LOWREG(r_dest_src1) && ARM_LOWREG(r_src2))
+ opcode = kThumbMovRR;
+ else if (!ARM_LOWREG(r_dest_src1) && !ARM_LOWREG(r_src2))
+ opcode = kThumbMovRR_H2H;
+ else if (ARM_LOWREG(r_dest_src1))
+ opcode = kThumbMovRR_H2L;
+ else
+ opcode = kThumbMovRR_L2H;
+ break;
+ case kOpMul:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbMul : kThumb2MulRRR;
+ break;
+ case kOpMvn:
+ opcode = (thumb_form) ? kThumbMvn : kThumb2MnvRR;
+ break;
+ case kOpNeg:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbNeg : kThumb2NegRR;
+ break;
+ case kOpOr:
+ opcode = (thumb_form) ? kThumbOrr : kThumb2OrrRRR;
+ break;
+ case kOpSbc:
+ opcode = (thumb_form) ? kThumbSbc : kThumb2SbcRRR;
+ break;
+ case kOpTst:
+ opcode = (thumb_form) ? kThumbTst : kThumb2TstRR;
+ break;
+ case kOpLsl:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbLslRR : kThumb2LslRRR;
+ break;
+ case kOpLsr:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbLsrRR : kThumb2LsrRRR;
+ break;
+ case kOpAsr:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbAsrRR : kThumb2AsrRRR;
+ break;
+ case kOpRor:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbRorRR : kThumb2RorRRR;
+ break;
+ case kOpAdd:
+ opcode = (thumb_form) ? kThumbAddRRR : kThumb2AddRRR;
+ break;
+ case kOpSub:
+ opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR;
+ break;
+ case kOp2Byte:
+ DCHECK_EQ(shift, 0);
+ return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 8);
+ case kOp2Short:
+ DCHECK_EQ(shift, 0);
+ return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 16);
+ case kOp2Char:
+ DCHECK_EQ(shift, 0);
+ return NewLIR4(kThumb2Ubfx, r_dest_src1, r_src2, 0, 16);
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ DCHECK_GE(static_cast<int>(opcode), 0);
+ if (EncodingMap[opcode].flags & IS_BINARY_OP)
+ return NewLIR2(opcode, r_dest_src1, r_src2);
+ else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
+ if (EncodingMap[opcode].field_loc[2].kind == kFmtShift)
+ return NewLIR3(opcode, r_dest_src1, r_src2, shift);
+ else
+ return NewLIR3(opcode, r_dest_src1, r_dest_src1, r_src2);
+ } else if (EncodingMap[opcode].flags & IS_QUAD_OP)
+ return NewLIR4(opcode, r_dest_src1, r_dest_src1, r_src2, shift);
+ else {
+ LOG(FATAL) << "Unexpected encoding operand count";
+ return NULL;
+ }
+}
+
+LIR* ArmMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2)
+{
+ return OpRegRegShift(op, r_dest_src1, r_src2, 0);
+}
+
+LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1,
+ int r_src2, int shift)
+{
+ ArmOpcode opcode = kThumbBkpt;
+ bool thumb_form = (shift == 0) && ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1) &&
+ ARM_LOWREG(r_src2);
+ switch (op) {
+ case kOpAdd:
+ opcode = (thumb_form) ? kThumbAddRRR : kThumb2AddRRR;
+ break;
+ case kOpSub:
+ opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR;
+ break;
+ case kOpRsub:
+ opcode = kThumb2RsubRRR;
+ break;
+ case kOpAdc:
+ opcode = kThumb2AdcRRR;
+ break;
+ case kOpAnd:
+ opcode = kThumb2AndRRR;
+ break;
+ case kOpBic:
+ opcode = kThumb2BicRRR;
+ break;
+ case kOpXor:
+ opcode = kThumb2EorRRR;
+ break;
+ case kOpMul:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2MulRRR;
+ break;
+ case kOpOr:
+ opcode = kThumb2OrrRRR;
+ break;
+ case kOpSbc:
+ opcode = kThumb2SbcRRR;
+ break;
+ case kOpLsl:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2LslRRR;
+ break;
+ case kOpLsr:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2LsrRRR;
+ break;
+ case kOpAsr:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2AsrRRR;
+ break;
+ case kOpRor:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2RorRRR;
+ break;
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ DCHECK_GE(static_cast<int>(opcode), 0);
+ if (EncodingMap[opcode].flags & IS_QUAD_OP)
+ return NewLIR4(opcode, r_dest, r_src1, r_src2, shift);
+ else {
+ DCHECK(EncodingMap[opcode].flags & IS_TERTIARY_OP);
+ return NewLIR3(opcode, r_dest, r_src1, r_src2);
+ }
+}
+
+LIR* ArmMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2)
+{
+ return OpRegRegRegShift(op, r_dest, r_src1, r_src2, 0);
+}
+
+LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value)
+{
+ LIR* res;
+ bool neg = (value < 0);
+ int abs_value = (neg) ? -value : value;
+ ArmOpcode opcode = kThumbBkpt;
+ ArmOpcode alt_opcode = kThumbBkpt;
+ bool all_low_regs = (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1));
+ int mod_imm = ModifiedImmediate(value);
+ int mod_imm_neg = ModifiedImmediate(-value);
+
+ switch (op) {
+ case kOpLsl:
+ if (all_low_regs)
+ return NewLIR3(kThumbLslRRI5, r_dest, r_src1, value);
+ else
+ return NewLIR3(kThumb2LslRRI5, r_dest, r_src1, value);
+ case kOpLsr:
+ if (all_low_regs)
+ return NewLIR3(kThumbLsrRRI5, r_dest, r_src1, value);
+ else
+ return NewLIR3(kThumb2LsrRRI5, r_dest, r_src1, value);
+ case kOpAsr:
+ if (all_low_regs)
+ return NewLIR3(kThumbAsrRRI5, r_dest, r_src1, value);
+ else
+ return NewLIR3(kThumb2AsrRRI5, r_dest, r_src1, value);
+ case kOpRor:
+ return NewLIR3(kThumb2RorRRI5, r_dest, r_src1, value);
+ case kOpAdd:
+ if (ARM_LOWREG(r_dest) && (r_src1 == r13sp) &&
+ (value <= 1020) && ((value & 0x3)==0)) {
+ return NewLIR3(kThumbAddSpRel, r_dest, r_src1, value >> 2);
+ } else if (ARM_LOWREG(r_dest) && (r_src1 == r15pc) &&
+ (value <= 1020) && ((value & 0x3)==0)) {
+ return NewLIR3(kThumbAddPcRel, r_dest, r_src1, value >> 2);
+ }
+ // Note: intentional fallthrough
+ case kOpSub:
+ if (all_low_regs && ((abs_value & 0x7) == abs_value)) {
+ if (op == kOpAdd)
+ opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+ else
+ opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+ return NewLIR3(opcode, r_dest, r_src1, abs_value);
+ } else if ((abs_value & 0xff) == abs_value) {
+ if (op == kOpAdd)
+ opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12;
+ else
+ opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12;
+ return NewLIR3(opcode, r_dest, r_src1, abs_value);
+ }
+ if (mod_imm_neg >= 0) {
+ op = (op == kOpAdd) ? kOpSub : kOpAdd;
+ mod_imm = mod_imm_neg;
+ }
+ if (op == kOpSub) {
+ opcode = kThumb2SubRRI8;
+ alt_opcode = kThumb2SubRRR;
+ } else {
+ opcode = kThumb2AddRRI8;
+ alt_opcode = kThumb2AddRRR;
+ }
+ break;
+ case kOpRsub:
+ opcode = kThumb2RsubRRI8;
+ alt_opcode = kThumb2RsubRRR;
+ break;
+ case kOpAdc:
+ opcode = kThumb2AdcRRI8;
+ alt_opcode = kThumb2AdcRRR;
+ break;
+ case kOpSbc:
+ opcode = kThumb2SbcRRI8;
+ alt_opcode = kThumb2SbcRRR;
+ break;
+ case kOpOr:
+ opcode = kThumb2OrrRRI8;
+ alt_opcode = kThumb2OrrRRR;
+ break;
+ case kOpAnd:
+ opcode = kThumb2AndRRI8;
+ alt_opcode = kThumb2AndRRR;
+ break;
+ case kOpXor:
+ opcode = kThumb2EorRRI8;
+ alt_opcode = kThumb2EorRRR;
+ break;
+ case kOpMul:
+ //TUNING: power of 2, shift & add
+ mod_imm = -1;
+ alt_opcode = kThumb2MulRRR;
+ break;
+ case kOpCmp: {
+ int mod_imm = ModifiedImmediate(value);
+ LIR* res;
+ if (mod_imm >= 0) {
+ res = NewLIR2(kThumb2CmpRI12, r_src1, mod_imm);
+ } else {
+ int r_tmp = AllocTemp();
+ res = LoadConstant(r_tmp, value);
+ OpRegReg(kOpCmp, r_src1, r_tmp);
+ FreeTemp(r_tmp);
+ }
+ return res;
+ }
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ }
+
+ if (mod_imm >= 0) {
+ return NewLIR3(opcode, r_dest, r_src1, mod_imm);
+ } else {
+ int r_scratch = AllocTemp();
+ LoadConstant(r_scratch, value);
+ if (EncodingMap[alt_opcode].flags & IS_QUAD_OP)
+ res = NewLIR4(alt_opcode, r_dest, r_src1, r_scratch, 0);
+ else
+ res = NewLIR3(alt_opcode, r_dest, r_src1, r_scratch);
+ FreeTemp(r_scratch);
+ return res;
+ }
+}
+
+/* Handle Thumb-only variants here - otherwise punt to OpRegRegImm */
+LIR* ArmMir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value)
+{
+ bool neg = (value < 0);
+ int abs_value = (neg) ? -value : value;
+ bool short_form = (((abs_value & 0xff) == abs_value) && ARM_LOWREG(r_dest_src1));
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpAdd:
+ if ( !neg && (r_dest_src1 == r13sp) && (value <= 508)) { /* sp */
+ DCHECK_EQ((value & 0x3), 0);
+ return NewLIR1(kThumbAddSpI7, value >> 2);
+ } else if (short_form) {
+ opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+ }
+ break;
+ case kOpSub:
+ if (!neg && (r_dest_src1 == r13sp) && (value <= 508)) { /* sp */
+ DCHECK_EQ((value & 0x3), 0);
+ return NewLIR1(kThumbSubSpI7, value >> 2);
+ } else if (short_form) {
+ opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+ }
+ break;
+ case kOpCmp:
+ if (ARM_LOWREG(r_dest_src1) && short_form)
+ opcode = (short_form) ? kThumbCmpRI8 : kThumbCmpRR;
+ else if (ARM_LOWREG(r_dest_src1))
+ opcode = kThumbCmpRR;
+ else {
+ short_form = false;
+ opcode = kThumbCmpHL;
+ }
+ break;
+ default:
+ /* Punt to OpRegRegImm - if bad case catch it there */
+ short_form = false;
+ break;
+ }
+ if (short_form)
+ return NewLIR2(opcode, r_dest_src1, abs_value);
+ else {
+ return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
+ }
+}
+
+LIR* ArmMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value)
+{
+ LIR* res = NULL;
+ int32_t val_lo = Low32Bits(value);
+ int32_t val_hi = High32Bits(value);
+ int target_reg = S2d(r_dest_lo, r_dest_hi);
+ if (ARM_FPREG(r_dest_lo)) {
+ if ((val_lo == 0) && (val_hi == 0)) {
+ // TODO: we need better info about the target CPU. a vector exclusive or
+ // would probably be better here if we could rely on its existance.
+ // Load an immediate +2.0 (which encodes to 0)
+ NewLIR2(kThumb2Vmovd_IMM8, target_reg, 0);
+ // +0.0 = +2.0 - +2.0
+ res = NewLIR3(kThumb2Vsubd, target_reg, target_reg, target_reg);
+ } else {
+ int encoded_imm = EncodeImmDouble(value);
+ if (encoded_imm >= 0) {
+ res = NewLIR2(kThumb2Vmovd_IMM8, target_reg, encoded_imm);
+ }
+ }
+ } else {
+ if ((InexpensiveConstantInt(val_lo) && (InexpensiveConstantInt(val_hi)))) {
+ res = LoadConstantNoClobber(r_dest_lo, val_lo);
+ LoadConstantNoClobber(r_dest_hi, val_hi);
+ }
+ }
+ if (res == NULL) {
+ // No short form - load from the literal pool.
+ LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
+ if (data_target == NULL) {
+ data_target = AddWideData(&literal_list_, val_lo, val_hi);
+ }
+ if (ARM_FPREG(r_dest_lo)) {
+ res = RawLIR(current_dalvik_offset_, kThumb2Vldrd,
+ target_reg, r15pc, 0, 0, 0, data_target);
+ } else {
+ res = RawLIR(current_dalvik_offset_, kThumb2LdrdPcRel8,
+ r_dest_lo, r_dest_hi, r15pc, 0, 0, data_target);
+ }
+ SetMemRefType(res, true, kLiteral);
+ res->alias_info = reinterpret_cast<uintptr_t>(data_target);
+ AppendLIR(res);
+ }
+ return res;
+}
+
+int ArmMir2Lir::EncodeShift(int code, int amount) {
+ return ((amount & 0x1f) << 2) | code;
+}
+
+LIR* ArmMir2Lir::LoadBaseIndexed(int rBase, int r_index, int r_dest,
+ int scale, OpSize size)
+{
+ bool all_low_regs = ARM_LOWREG(rBase) && ARM_LOWREG(r_index) && ARM_LOWREG(r_dest);
+ LIR* load;
+ ArmOpcode opcode = kThumbBkpt;
+ bool thumb_form = (all_low_regs && (scale == 0));
+ int reg_ptr;
+
+ if (ARM_FPREG(r_dest)) {
+ if (ARM_SINGLEREG(r_dest)) {
+ DCHECK((size == kWord) || (size == kSingle));
+ opcode = kThumb2Vldrs;
+ size = kSingle;
+ } else {
+ DCHECK(ARM_DOUBLEREG(r_dest));
+ DCHECK((size == kLong) || (size == kDouble));
+ DCHECK_EQ((r_dest & 0x1), 0);
+ opcode = kThumb2Vldrd;
+ size = kDouble;
+ }
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ switch (size) {
+ case kDouble: // fall-through
+ case kSingle:
+ reg_ptr = AllocTemp();
+ if (scale) {
+ NewLIR4(kThumb2AddRRR, reg_ptr, rBase, r_index,
+ EncodeShift(kArmLsl, scale));
+ } else {
+ OpRegRegReg(kOpAdd, reg_ptr, rBase, r_index);
+ }
+ load = NewLIR3(opcode, r_dest, reg_ptr, 0);
+ FreeTemp(reg_ptr);
+ return load;
+ case kWord:
+ opcode = (thumb_form) ? kThumbLdrRRR : kThumb2LdrRRR;
+ break;
+ case kUnsignedHalf:
+ opcode = (thumb_form) ? kThumbLdrhRRR : kThumb2LdrhRRR;
+ break;
+ case kSignedHalf:
+ opcode = (thumb_form) ? kThumbLdrshRRR : kThumb2LdrshRRR;
+ break;
+ case kUnsignedByte:
+ opcode = (thumb_form) ? kThumbLdrbRRR : kThumb2LdrbRRR;
+ break;
+ case kSignedByte:
+ opcode = (thumb_form) ? kThumbLdrsbRRR : kThumb2LdrsbRRR;
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+ if (thumb_form)
+ load = NewLIR3(opcode, r_dest, rBase, r_index);
+ else
+ load = NewLIR4(opcode, r_dest, rBase, r_index, scale);
+
+ return load;
+}
+
+LIR* ArmMir2Lir::StoreBaseIndexed(int rBase, int r_index, int r_src,
+ int scale, OpSize size)
+{
+ bool all_low_regs = ARM_LOWREG(rBase) && ARM_LOWREG(r_index) && ARM_LOWREG(r_src);
+ LIR* store = NULL;
+ ArmOpcode opcode = kThumbBkpt;
+ bool thumb_form = (all_low_regs && (scale == 0));
+ int reg_ptr;
+
+ if (ARM_FPREG(r_src)) {
+ if (ARM_SINGLEREG(r_src)) {
+ DCHECK((size == kWord) || (size == kSingle));
+ opcode = kThumb2Vstrs;
+ size = kSingle;
+ } else {
+ DCHECK(ARM_DOUBLEREG(r_src));
+ DCHECK((size == kLong) || (size == kDouble));
+ DCHECK_EQ((r_src & 0x1), 0);
+ opcode = kThumb2Vstrd;
+ size = kDouble;
+ }
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ switch (size) {
+ case kDouble: // fall-through
+ case kSingle:
+ reg_ptr = AllocTemp();
+ if (scale) {
+ NewLIR4(kThumb2AddRRR, reg_ptr, rBase, r_index,
+ EncodeShift(kArmLsl, scale));
+ } else {
+ OpRegRegReg(kOpAdd, reg_ptr, rBase, r_index);
+ }
+ store = NewLIR3(opcode, r_src, reg_ptr, 0);
+ FreeTemp(reg_ptr);
+ return store;
+ case kWord:
+ opcode = (thumb_form) ? kThumbStrRRR : kThumb2StrRRR;
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opcode = (thumb_form) ? kThumbStrhRRR : kThumb2StrhRRR;
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opcode = (thumb_form) ? kThumbStrbRRR : kThumb2StrbRRR;
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+ if (thumb_form)
+ store = NewLIR3(opcode, r_src, rBase, r_index);
+ else
+ store = NewLIR4(opcode, r_src, rBase, r_index, scale);
+
+ return store;
+}
+
+/*
+ * Load value from base + displacement. Optionally perform null check
+ * on base (which must have an associated s_reg and MIR). If not
+ * performing null check, incoming MIR can be null.
+ */
+LIR* ArmMir2Lir::LoadBaseDispBody(int rBase, int displacement, int r_dest,
+ int r_dest_hi, OpSize size, int s_reg)
+{
+ LIR* load = NULL;
+ ArmOpcode opcode = kThumbBkpt;
+ bool short_form = false;
+ bool thumb2Form = (displacement < 4092 && displacement >= 0);
+ bool all_low_regs = (ARM_LOWREG(rBase) && ARM_LOWREG(r_dest));
+ int encoded_disp = displacement;
+ bool is64bit = false;
+ bool already_generated = false;
+ switch (size) {
+ case kDouble:
+ case kLong:
+ is64bit = true;
+ if (ARM_FPREG(r_dest)) {
+ if (ARM_SINGLEREG(r_dest)) {
+ DCHECK(ARM_FPREG(r_dest_hi));
+ r_dest = S2d(r_dest, r_dest_hi);
+ }
+ opcode = kThumb2Vldrd;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ break;
+ } else {
+ if (displacement <= 1020) {
+ load = NewLIR4(kThumb2LdrdI8, r_dest, r_dest_hi, rBase, displacement >> 2);
+ } else {
+ load = LoadBaseDispBody(rBase, displacement, r_dest,
+ -1, kWord, s_reg);
+ LoadBaseDispBody(rBase, displacement + 4, r_dest_hi,
+ -1, kWord, INVALID_SREG);
+ }
+ already_generated = true;
+ }
+ case kSingle:
+ case kWord:
+ if (ARM_FPREG(r_dest)) {
+ opcode = kThumb2Vldrs;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ break;
+ }
+ if (ARM_LOWREG(r_dest) && (rBase == r15pc) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbLdrPcRel;
+ } else if (ARM_LOWREG(r_dest) && (rBase == r13sp) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbLdrSpRel;
+ } else if (all_low_regs && displacement < 128 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x3), 0);
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbLdrRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrRRI12;
+ }
+ break;
+ case kUnsignedHalf:
+ if (all_low_regs && displacement < 64 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x1), 0);
+ short_form = true;
+ encoded_disp >>= 1;
+ opcode = kThumbLdrhRRI5;
+ } else if (displacement < 4092 && displacement >= 0) {
+ short_form = true;
+ opcode = kThumb2LdrhRRI12;
+ }
+ break;
+ case kSignedHalf:
+ if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrshRRI12;
+ }
+ break;
+ case kUnsignedByte:
+ if (all_low_regs && displacement < 32 && displacement >= 0) {
+ short_form = true;
+ opcode = kThumbLdrbRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrbRRI12;
+ }
+ break;
+ case kSignedByte:
+ if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrsbRRI12;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+
+ if (!already_generated) {
+ if (short_form) {
+ load = NewLIR3(opcode, r_dest, rBase, encoded_disp);
+ } else {
+ int reg_offset = AllocTemp();
+ LoadConstant(reg_offset, encoded_disp);
+ load = LoadBaseIndexed(rBase, reg_offset, r_dest, 0, size);
+ FreeTemp(reg_offset);
+ }
+ }
+
+ // TODO: in future may need to differentiate Dalvik accesses w/ spills
+ if (rBase == rARM_SP) {
+ AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, is64bit);
+ }
+ return load;
+}
+
+LIR* ArmMir2Lir::LoadBaseDisp(int rBase, int displacement, int r_dest,
+ OpSize size, int s_reg)
+{
+ return LoadBaseDispBody(rBase, displacement, r_dest, -1, size, s_reg);
+}
+
+LIR* ArmMir2Lir::LoadBaseDispWide(int rBase, int displacement, int r_dest_lo,
+ int r_dest_hi, int s_reg)
+{
+ return LoadBaseDispBody(rBase, displacement, r_dest_lo, r_dest_hi, kLong, s_reg);
+}
+
+
+LIR* ArmMir2Lir::StoreBaseDispBody(int rBase, int displacement,
+ int r_src, int r_src_hi, OpSize size) {
+ LIR* store = NULL;
+ ArmOpcode opcode = kThumbBkpt;
+ bool short_form = false;
+ bool thumb2Form = (displacement < 4092 && displacement >= 0);
+ bool all_low_regs = (ARM_LOWREG(rBase) && ARM_LOWREG(r_src));
+ int encoded_disp = displacement;
+ bool is64bit = false;
+ bool already_generated = false;
+ switch (size) {
+ case kLong:
+ case kDouble:
+ is64bit = true;
+ if (!ARM_FPREG(r_src)) {
+ if (displacement <= 1020) {
+ store = NewLIR4(kThumb2StrdI8, r_src, r_src_hi, rBase, displacement >> 2);
+ } else {
+ store = StoreBaseDispBody(rBase, displacement, r_src, -1, kWord);
+ StoreBaseDispBody(rBase, displacement + 4, r_src_hi, -1, kWord);
+ }
+ already_generated = true;
+ } else {
+ if (ARM_SINGLEREG(r_src)) {
+ DCHECK(ARM_FPREG(r_src_hi));
+ r_src = S2d(r_src, r_src_hi);
+ }
+ opcode = kThumb2Vstrd;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ }
+ break;
+ case kSingle:
+ case kWord:
+ if (ARM_FPREG(r_src)) {
+ DCHECK(ARM_SINGLEREG(r_src));
+ opcode = kThumb2Vstrs;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ break;
+ }
+ if (ARM_LOWREG(r_src) && (rBase == r13sp) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbStrSpRel;
+ } else if (all_low_regs && displacement < 128 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x3), 0);
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbStrRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2StrRRI12;
+ }
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ if (all_low_regs && displacement < 64 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x1), 0);
+ short_form = true;
+ encoded_disp >>= 1;
+ opcode = kThumbStrhRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2StrhRRI12;
+ }
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ if (all_low_regs && displacement < 32 && displacement >= 0) {
+ short_form = true;
+ opcode = kThumbStrbRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2StrbRRI12;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+ if (!already_generated) {
+ if (short_form) {
+ store = NewLIR3(opcode, r_src, rBase, encoded_disp);
+ } else {
+ int r_scratch = AllocTemp();
+ LoadConstant(r_scratch, encoded_disp);
+ store = StoreBaseIndexed(rBase, r_scratch, r_src, 0, size);
+ FreeTemp(r_scratch);
+ }
+ }
+
+ // TODO: In future, may need to differentiate Dalvik & spill accesses
+ if (rBase == rARM_SP) {
+ AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, is64bit);
+ }
+ return store;
+}
+
+LIR* ArmMir2Lir::StoreBaseDisp(int rBase, int displacement, int r_src,
+ OpSize size)
+{
+ return StoreBaseDispBody(rBase, displacement, r_src, -1, size);
+}
+
+LIR* ArmMir2Lir::StoreBaseDispWide(int rBase, int displacement,
+ int r_src_lo, int r_src_hi)
+{
+ return StoreBaseDispBody(rBase, displacement, r_src_lo, r_src_hi, kLong);
+}
+
+LIR* ArmMir2Lir::OpFpRegCopy(int r_dest, int r_src)
+{
+ int opcode;
+ DCHECK_EQ(ARM_DOUBLEREG(r_dest), ARM_DOUBLEREG(r_src));
+ if (ARM_DOUBLEREG(r_dest)) {
+ opcode = kThumb2Vmovd;
+ } else {
+ if (ARM_SINGLEREG(r_dest)) {
+ opcode = ARM_SINGLEREG(r_src) ? kThumb2Vmovs : kThumb2Fmsr;
+ } else {
+ DCHECK(ARM_SINGLEREG(r_src));
+ opcode = kThumb2Fmrs;
+ }
+ }
+ LIR* res = RawLIR(current_dalvik_offset_, opcode, r_dest, r_src);
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+LIR* ArmMir2Lir::OpThreadMem(OpKind op, int thread_offset)
+{
+ LOG(FATAL) << "Unexpected use of OpThreadMem for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::OpMem(OpKind op, int rBase, int disp)
+{
+ LOG(FATAL) << "Unexpected use of OpMem for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::StoreBaseIndexedDisp(int rBase, int r_index, int scale,
+ int displacement, int r_src, int r_src_hi, OpSize size,
+ int s_reg)
+{
+ LOG(FATAL) << "Unexpected use of StoreBaseIndexedDisp for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::OpRegMem(OpKind op, int r_dest, int rBase, int offset)
+{
+ LOG(FATAL) << "Unexpected use of OpRegMem for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale,
+ int displacement, int r_dest, int r_dest_hi, OpSize size,
+ int s_reg)
+{
+ LOG(FATAL) << "Unexpected use of LoadBaseIndexedDisp for Arm";
+ return NULL;
+}
+
+} // namespace art