blob: f05a867e5738e40902b66f836bdeec1b7c31397e [file] [log] [blame]
Bill Buzbee9bc3df32009-07-30 10:52:29 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * This file contains codegen for the Thumb ISA and is intended to be
Bill Buzbee270c1d62009-08-13 16:58:07 -070019 * includes by:
Bill Buzbee9bc3df32009-07-30 10:52:29 -070020 *
21 * Codegen-$(TARGET_ARCH_VARIANT).c
22 *
23 */
24
25#include "Codegen.h"
Bill Buzbee270c1d62009-08-13 16:58:07 -070026/* Forward decls */
27static ArmLIR *genNullCheck(CompilationUnit *cUnit, int vReg, int mReg,
28 int dOffset, ArmLIR *pcrLabel);
29static ArmLIR *loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest);
30static ArmLIR *loadValue(CompilationUnit *cUnit, int vSrc, int rDest);
31static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase,
32 int displacement, int rDest);
33static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
34 int displacement, int rSrc, int rScratch);
35static ArmLIR *storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
36 int rScratch);
37static ArmLIR *genConditionalBranch(CompilationUnit *cUnit,
38 ArmConditionCode cond,
39 ArmLIR *target);
40static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target);
41static ArmLIR *loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
42 int rDestHi);
43static ArmLIR *storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
44 int vDest, int rScratch);
45static ArmLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
46 int rBound, int dOffset, ArmLIR *pcrLabel);
47static ArmLIR *genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
48
Bill Buzbee9bc3df32009-07-30 10:52:29 -070049
50/* Routines which must be supplied here */
Bill Buzbee270c1d62009-08-13 16:58:07 -070051static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
52static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
53 int rAddr);
54static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
55 int displacement, int rDest, OpSize size,
56 bool nullCheck, int vReg);
57static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
58 int displacement, int rSrc, OpSize size,
59 int rScratch);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070060static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
Bill Buzbee270c1d62009-08-13 16:58:07 -070061 ArmConditionCode cond, int reg,
62 int checkValue, int dOffset,
63 ArmLIR *pcrLabel);
Bill Buzbee7ea0f642009-08-10 17:06:51 -070064ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
Bill Buzbee270c1d62009-08-13 16:58:07 -070065static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
66static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070067
Bill Buzbee270c1d62009-08-13 16:58:07 -070068static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op);
69static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value);
70static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
71 int value2);
72static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
73static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
74 int rSrc2);
75static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
76 int value, int rScratch);
77static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
78 int rSrc1, int value, int rScratch);
79static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
80 int rSrc1, int rSrc2);
81static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
82 int rIndex, int rDest, int scale, OpSize size);
83
84static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir);
85static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir);
86static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir);
87static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir);
88static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir);
89static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin);
90static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070091
92/*
93 * Support for register allocation
94 */
95
Bill Buzbee9bc3df32009-07-30 10:52:29 -070096/* get the next register in r0..r3 in a round-robin fashion */
97#define NEXT_REG(reg) ((reg + 1) & 3)
98/*
99 * The following are utility routines to help maintain the RegisterScoreboard
100 * state to facilitate register renaming.
101 */
102
103/* Reset the tracker to unknown state */
104static inline void resetRegisterScoreboard(CompilationUnit *cUnit)
105{
106 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
107
108 dvmClearAllBits(registerScoreboard->nullCheckedRegs);
109 registerScoreboard->liveDalvikReg = vNone;
110 registerScoreboard->nativeReg = vNone;
111 registerScoreboard->nativeRegHi = vNone;
112}
113
114/* Kill the corresponding bit in the null-checked register list */
115static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg)
116{
117 dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg);
118}
119
120/* The Dalvik register pair held in native registers have changed */
121static inline void updateLiveRegisterPair(CompilationUnit *cUnit,
122 int vReg, int mRegLo, int mRegHi)
123{
124 cUnit->registerScoreboard.liveDalvikReg = vReg;
125 cUnit->registerScoreboard.nativeReg = mRegLo;
126 cUnit->registerScoreboard.nativeRegHi = mRegHi;
127 cUnit->registerScoreboard.isWide = true;
128}
129
130/* The Dalvik register held in a native register has changed */
131static inline void updateLiveRegister(CompilationUnit *cUnit,
132 int vReg, int mReg)
133{
134 cUnit->registerScoreboard.liveDalvikReg = vReg;
135 cUnit->registerScoreboard.nativeReg = mReg;
136 cUnit->registerScoreboard.isWide = false;
137}
138
139/*
140 * Given a Dalvik register id vSrc, use a very simple algorithm to increase
141 * the lifetime of cached Dalvik value in a native register.
142 */
143static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc,
144 bool isWide)
145{
146 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
147
148 /* No live value - suggest to use r0 */
149 if (registerScoreboard->liveDalvikReg == vNone)
150 return r0;
151
152 /* Reuse the previously used native reg */
153 if (registerScoreboard->liveDalvikReg == vSrc) {
154 if (isWide != true) {
155 return registerScoreboard->nativeReg;
156 } else {
157 /* Return either r0 or r2 */
158 return (registerScoreboard->nativeReg + 1) & 2;
159 }
160 }
161
162 /* No reuse - choose the next one among r0..r3 in the round-robin fashion */
163 if (isWide) {
164 return (registerScoreboard->nativeReg + 2) & 2;
165 } else {
166 return (registerScoreboard->nativeReg + 1) & 3;
167 }
168
169}
170
Bill Buzbee270c1d62009-08-13 16:58:07 -0700171static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700172{
173 ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
174 res->operands[0] = rDest;
175 res->operands[1] = rSrc;
176 if (rDest == rSrc) {
177 res->isNop = true;
178 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700179 // TODO: support copy between FP and gen regs
180 if (DOUBLEREG(rDest)) {
181 assert(DOUBLEREG(rSrc));
182 res->opCode = THUMB2_VMOVD;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700183 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700184 assert(SINGLEREG(rSrc));
185 res->opCode = THUMB2_VMOVS;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700186 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700187 res->operands[0] = rDest;
188 res->operands[1] = rSrc;
189 }
190 return res;
191}
192
193ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
194{
195 ArmLIR* res;
196 ArmOpCode opCode;
197 if (FPREG(rDest) || FPREG(rSrc))
198 return fpRegCopy(cUnit, rDest, rSrc);
199 res = dvmCompilerNew(sizeof(ArmLIR), true);
200 if (LOWREG(rDest) && LOWREG(rSrc))
201 opCode = THUMB_MOV_RR;
202 else if (!LOWREG(rDest) && !LOWREG(rSrc))
203 opCode = THUMB_MOV_RR_H2H;
204 else if (LOWREG(rDest))
205 opCode = THUMB_MOV_RR_H2L;
206 else
207 opCode = THUMB_MOV_RR_L2H;
208
209 res->operands[0] = rDest & THUMB_REG_MASK;
210 res->operands[1] = rSrc & THUMB_REG_MASK;
211 res->opCode = opCode;
212 if (rDest == rSrc) {
213 res->isNop = true;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700214 }
215 return res;
216}
217
218static int leadingZeros(u4 val)
219{
220 u4 alt;
221 int n;
222 int count;
223
224 count = 16;
225 n = 32;
226 do {
227 alt = val >> count;
228 if (alt != 0) {
229 n = n - count;
230 val = alt;
231 }
232 count >>= 1;
233 } while (count);
234 return n - val;
235}
236
237/*
Bill Buzbee270c1d62009-08-13 16:58:07 -0700238 * Determine whether value can be encoded as a Thumb2 modified
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700239 * immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
240 */
241static int modifiedImmediate(u4 value)
242{
243 int zLeading;
244 int zTrailing;
245 u4 b0 = value & 0xff;
246
247 /* Note: case of value==0 must use 0:000:0:0000000 encoding */
248 if (value <= 0xFF)
249 return b0; // 0:000:a:bcdefgh
250 if (value == ((b0 << 16) | b0))
251 return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
252 if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
253 return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
254 b0 = (value >> 8) & 0xff;
255 if (value == ((b0 << 24) | (b0 << 8)))
256 return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
257 /* Can we do it with rotation? */
258 zLeading = leadingZeros(value);
259 zTrailing = 32 - leadingZeros(~value & (value - 1));
260 /* A run of eight or fewer active bits? */
261 if ((zLeading + zTrailing) < 24)
262 return -1; /* No - bail */
263 /* left-justify the constant, discarding msb (known to be 1) */
264 value <<= zLeading + 1;
265 /* Create bcdefgh */
266 value >>= 25;
267 /* Put it all together */
268 return value | ((0x8 + zLeading) << 7); /* [01000..11111]:bcdefgh */
269}
270
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700271/*
272 * Load a immediate using a shortcut if possible; otherwise
273 * grab from the per-translation literal pool
274 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700275static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700276{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700277 ArmLIR *res;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700278 int modImm;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700279 /* See if the value can be constructed cheaply */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700280 if ((value >= 0) && (value <= 255)) {
281 return newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700282 } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700283 res = newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700284 newLIR2(cUnit, THUMB_MVN, rDest, rDest);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700285 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700286 }
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700287 /* Check Modified immediate special cases */
288 modImm = modifiedImmediate(value);
289 if (modImm >= 0) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700290 res = newLIR2(cUnit, THUMB2_MOV_IMM_SHIFT, rDest, modImm);
291 return res;
292 }
293 modImm = modifiedImmediate(~value);
294 if (modImm >= 0) {
295 res = newLIR2(cUnit, THUMB2_MVN_IMM_SHIFT, rDest, modImm);
296 return res;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700297 }
298 /* 16-bit immediate? */
299 if ((value & 0xffff) == value) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700300 res = newLIR2(cUnit, THUMB2_MOV_IMM16, rDest, value);
301 return res;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700302 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700303 /* No shortcut - go ahead and use literal pool */
304 ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
305 if (dataTarget == NULL) {
306 dataTarget = addWordData(cUnit, value, false);
307 }
308 ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
309 loadPcRel->opCode = THUMB_LDR_PC_REL;
310 loadPcRel->generic.target = (LIR *) dataTarget;
311 loadPcRel->operands[0] = rDest;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700312 res = loadPcRel;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700313 dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
314
315 /*
316 * To save space in the constant pool, we use the ADD_RRI8 instruction to
317 * add up to 255 to an existing constant value.
318 */
319 if (dataTarget->operands[0] != value) {
320 newLIR2(cUnit, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
321 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700322 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700323}
324
325/* Export the Dalvik PC assicated with an instruction to the StackSave area */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700326static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
327 int rAddr)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700328{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700329 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700330 int offset = offsetof(StackSaveArea, xtra.currentPc);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700331 res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700332 newLIR3(cUnit, THUMB2_STR_RRI8_PREDEC, rDPC, rFP,
333 sizeof(StackSaveArea) - offset);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700334 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700335}
336
Bill Buzbee270c1d62009-08-13 16:58:07 -0700337/* Load value from base + scaled index. Note: index reg killed */
338static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
339 int rIndex, int rDest, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700340{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700341 bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rDest);
342 ArmOpCode opCode = THUMB_BKPT;
343 bool thumbForm = (allLowRegs && (scale == 0));
344 switch (size) {
345 case WORD:
346 opCode = (thumbForm) ? THUMB_LDR_RRR : THUMB2_LDR_RRR;
347 break;
348 case UNSIGNED_HALF:
349 opCode = (thumbForm) ? THUMB_LDRH_RRR : THUMB2_LDRH_RRR;
350 break;
351 case SIGNED_HALF:
352 opCode = (thumbForm) ? THUMB_LDRSH_RRR : THUMB2_LDRSH_RRR;
353 break;
354 case UNSIGNED_BYTE:
355 opCode = (thumbForm) ? THUMB_LDRB_RRR : THUMB2_LDRB_RRR;
356 break;
357 case SIGNED_BYTE:
358 opCode = (thumbForm) ? THUMB_LDRSB_RRR : THUMB2_LDRSB_RRR;
359 break;
360 default:
361 assert(0);
362 }
363 if (thumbForm)
364 return newLIR3(cUnit, opCode, rDest, rBase, rIndex);
365 else
366 return newLIR4(cUnit, opCode, rDest, rBase, rIndex, scale);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700367}
368
Bill Buzbee270c1d62009-08-13 16:58:07 -0700369/* store value base base + scaled index. Note: index reg killed */
370static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
371 int rIndex, int rSrc, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700372{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700373 bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rSrc);
374 ArmOpCode opCode = THUMB_BKPT;
375 bool thumbForm = (allLowRegs && (scale == 0));
376 switch (size) {
377 case WORD:
378 opCode = (thumbForm) ? THUMB_STR_RRR : THUMB2_STR_RRR;
379 break;
380 case UNSIGNED_HALF:
381 case SIGNED_HALF:
382 opCode = (thumbForm) ? THUMB_STRH_RRR : THUMB2_STRH_RRR;
383 break;
384 case UNSIGNED_BYTE:
385 case SIGNED_BYTE:
386 opCode = (thumbForm) ? THUMB_STRB_RRR : THUMB2_STRB_RRR;
387 break;
388 default:
389 assert(0);
390 }
391 if (thumbForm)
392 return newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
393 else
394 return newLIR4(cUnit, opCode, rSrc, rBase, rIndex, scale);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700395}
396
397/*
Bill Buzbee270c1d62009-08-13 16:58:07 -0700398 * Load a float from a Dalvik register. Note: using fixed r7 here
399 * when operation is out of range. Revisit this when registor allocation
400 * strategy changes.
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700401 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700402static ArmLIR *fpVarAccess(CompilationUnit *cUnit, int vSrcDest,
403 int rSrcDest, ArmOpCode opCode)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700404{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700405 ArmLIR *res;
406 if (vSrcDest > 255) {
407 res = opRegRegImm(cUnit, OP_ADD, r7, rFP, vSrcDest * 4, rNone);
408 newLIR3(cUnit, opCode, rSrcDest, r7, 0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700409 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700410 res = newLIR3(cUnit, opCode, rSrcDest, rFP, vSrcDest);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700411 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700412 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700413}
Bill Buzbee270c1d62009-08-13 16:58:07 -0700414static ArmLIR *loadFloat(CompilationUnit *cUnit, int vSrc, int rDest)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700415{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700416 assert(SINGLEREG(rDest));
Bill Buzbee270c1d62009-08-13 16:58:07 -0700417 return fpVarAccess(cUnit, vSrc, rDest, THUMB2_VLDRS);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700418}
419
420/* Store a float to a Dalvik register */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700421static ArmLIR *storeFloat(CompilationUnit *cUnit, int rSrc, int vDest,
422 int rScratch)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700423{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700424 assert(SINGLEREG(rSrc));
Bill Buzbee270c1d62009-08-13 16:58:07 -0700425 return fpVarAccess(cUnit, vDest, rSrc, THUMB2_VSTRS);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700426}
427
428/* Load a double from a Dalvik register */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700429static ArmLIR *loadDouble(CompilationUnit *cUnit, int vSrc, int rDest)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700430{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700431 assert(DOUBLEREG(rDest));
Bill Buzbee270c1d62009-08-13 16:58:07 -0700432 return fpVarAccess(cUnit, vSrc, rDest, THUMB2_VLDRD);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700433}
434
435/* Store a double to a Dalvik register */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700436static ArmLIR *storeDouble(CompilationUnit *cUnit, int rSrc, int vDest,
437 int rScratch)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700438{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700439 assert(DOUBLEREG(rSrc));
Bill Buzbee270c1d62009-08-13 16:58:07 -0700440 return fpVarAccess(cUnit, vDest, rSrc, THUMB2_VSTRD);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700441}
442
443
Bill Buzbee270c1d62009-08-13 16:58:07 -0700444/*
445 * Load value from base + displacement. Optionally perform null check
446 * on base (which must have an associated vReg and MIR). If not
447 * performing null check, incoming MIR can be null. Note: base and
448 * dest must not be the same if there is any chance that the long
449 * form must be used.
450 * TODO: revisit, perhaps use hot temp reg in (base == dest) case.
451 */
452static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
453 int displacement, int rDest, OpSize size,
454 bool nullCheck, int vReg)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700455{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700456 ArmLIR *first = NULL;
457 ArmLIR *res;
458 ArmOpCode opCode = THUMB_BKPT;
459 bool shortForm = false;
460 bool thumb2Form = (displacement < 4092 && displacement >= 0);
461 int shortMax = 128;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700462 bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
Bill Buzbee270c1d62009-08-13 16:58:07 -0700463 switch (size) {
464 case WORD:
465 if (LOWREG(rDest) && (rBase == rpc) &&
466 (displacement <= 1020) && (displacement >= 0)) {
467 shortForm = true;
468 displacement >>= 2;
469 opCode = THUMB_LDR_PC_REL;
470 } else if (LOWREG(rDest) && (rBase == r13) &&
471 (displacement <= 1020) && (displacement >= 0)) {
472 shortForm = true;
473 displacement >>= 2;
474 opCode = THUMB_LDR_SP_REL;
475 } else if (allLowRegs && displacement < 128 && displacement >= 0) {
476 assert((displacement & 0x3) == 0);
477 shortForm = true;
478 displacement >>= 2;
479 opCode = THUMB_LDR_RRI5;
480 } else if (thumb2Form) {
481 shortForm = true;
482 opCode = THUMB2_LDR_RRI12;
483 }
484 break;
485 case UNSIGNED_HALF:
486 if (allLowRegs && displacement < 64 && displacement >= 0) {
487 assert((displacement & 0x1) == 0);
488 shortForm = true;
489 displacement >>= 1;
490 opCode = THUMB_LDRH_RRI5;
491 } else if (displacement < 4092 && displacement >= 0) {
492 shortForm = true;
493 opCode = THUMB2_LDRH_RRI12;
494 }
495 break;
496 case SIGNED_HALF:
497 if (thumb2Form) {
498 shortForm = true;
499 opCode = THUMB2_LDRSH_RRI12;
500 }
501 break;
502 case UNSIGNED_BYTE:
503 if (allLowRegs && displacement < 32 && displacement >= 0) {
504 shortForm = true;
505 opCode = THUMB_LDRB_RRI5;
506 } else if (thumb2Form) {
507 shortForm = true;
508 opCode = THUMB2_LDRB_RRI12;
509 }
510 break;
511 case SIGNED_BYTE:
512 if (thumb2Form) {
513 shortForm = true;
514 opCode = THUMB2_LDRSB_RRI12;
515 }
516 break;
517 default:
518 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700519 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700520 if (nullCheck)
521 first = genNullCheck(cUnit, vReg, rBase, mir->offset, NULL);
522 if (shortForm) {
523 res = newLIR3(cUnit, opCode, rDest, rBase, displacement);
524 } else {
525 assert(rBase != rDest);
526 res = loadConstant(cUnit, rDest, displacement);
527 loadBaseIndexed(cUnit, rBase, rDest, rDest, 0, size);
528 }
529 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700530}
531
Bill Buzbee270c1d62009-08-13 16:58:07 -0700532static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
533 int displacement, int rSrc, OpSize size,
534 int rScratch)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700535{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700536 ArmLIR *res;
537 ArmOpCode opCode = THUMB_BKPT;
538 bool shortForm = false;
539 bool thumb2Form = (displacement < 4092 && displacement >= 0);
540 int shortMax = 128;
541 bool allLowRegs = (LOWREG(rBase) && LOWREG(rSrc));
542 if (rScratch != -1)
543 allLowRegs &= LOWREG(rScratch);
544 switch (size) {
545 case WORD:
546 if (allLowRegs && displacement < 128 && displacement >= 0) {
547 assert((displacement & 0x3) == 0);
548 shortForm = true;
549 displacement >>= 2;
550 opCode = THUMB_STR_RRI5;
551 } else if (thumb2Form) {
552 shortForm = true;
553 opCode = THUMB2_STR_RRI12;
554 }
555 break;
556 case UNSIGNED_HALF:
557 case SIGNED_HALF:
558 if (displacement < 64 && displacement >= 0) {
559 assert((displacement & 0x1) == 0);
560 shortForm = true;
561 displacement >>= 1;
562 opCode = THUMB_STRH_RRI5;
563 } else if (thumb2Form) {
564 shortForm = true;
565 opCode = THUMB2_STRH_RRI12;
566 }
567 break;
568 case UNSIGNED_BYTE:
569 case SIGNED_BYTE:
570 if (displacement < 32 && displacement >= 0) {
571 shortForm = true;
572 opCode = THUMB_STRB_RRI5;
573 } else if (thumb2Form) {
574 shortForm = true;
575 opCode = THUMB2_STRH_RRI12;
576 }
577 break;
578 default:
579 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700580 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700581 if (shortForm) {
582 res = newLIR3(cUnit, opCode, rSrc, rBase, displacement);
583 } else {
584 assert(rScratch != -1);
585 res = loadConstant(cUnit, rScratch, displacement);
586 storeBaseIndexed(cUnit, rBase, rScratch, rSrc, 0, size);
587 }
588 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700589}
590
591/*
592 * Perform a "reg cmp imm" operation and jump to the PCR region if condition
593 * satisfies.
594 */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700595static ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700596 ArmConditionCode cond, int reg,
597 int checkValue, int dOffset,
598 ArmLIR *pcrLabel)
599{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700600 ArmLIR *branch;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700601 int modImm;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700602 if ((LOWREG(reg)) && (checkValue == 0) &&
603 ((cond == ARM_COND_EQ) || (cond == ARM_COND_NE))) {
604 branch = newLIR2(cUnit,
605 (cond == ARM_COND_EQ) ? THUMB2_CBZ : THUMB2_CBNZ,
606 reg, 0);
607 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700608 modImm = modifiedImmediate(checkValue);
609 if ((checkValue & 0xff) == checkValue) {
610 newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
611 } else if (modImm >= 0) {
612 newLIR2(cUnit, THUMB2_CMP_RI8, reg, modImm);
613 } else {
614 /* Note: direct use of hot temp r7 here. Revisit. */
615 loadConstant(cUnit, r7, checkValue);
616 newLIR2(cUnit, THUMB_CMP_RR, reg, r7);
617 }
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700618 branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
619 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700620 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
621}
Bill Buzbee270c1d62009-08-13 16:58:07 -0700622
623static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
624{
625 ArmLIR *res;
626 if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
627 res = newLIR2(cUnit, THUMB_LDMIA, rBase, rMask);
628 } else {
629 res = newLIR2(cUnit, THUMB2_LDMIA, rBase, rMask);
630 }
631 return res;
632}
633
634static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
635{
636 ArmLIR *res;
637 if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
638 res = newLIR2(cUnit, THUMB_STMIA, rBase, rMask);
639 } else {
640 res = newLIR2(cUnit, THUMB2_STMIA, rBase, rMask);
641 }
642 return res;
643}
644
645static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
646{
647 ArmOpCode opCode = THUMB_BKPT;
648 switch (op) {
649 case OP_UNCOND_BR:
650 opCode = THUMB_B_UNCOND;
651 break;
652 default:
653 assert(0);
654 }
655 return newLIR0(cUnit, opCode);
656}
657
658static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
659 int value2)
660{
661 ArmOpCode opCode = THUMB_BKPT;
662 switch (op) {
663 case OP_COND_BR:
664 opCode = THUMB_B_COND;
665 break;
666 default:
667 assert(0);
668 }
669 return newLIR2(cUnit, opCode, value1, value2);
670}
671
672static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
673{
674 ArmOpCode opCode = THUMB_BKPT;
675 switch (op) {
676 case OP_PUSH:
677 opCode = ((value & 0xff00) != 0) ? THUMB2_PUSH : THUMB_PUSH;
678 break;
679 case OP_POP:
680 opCode = ((value & 0xff00) != 0) ? THUMB2_POP : THUMB_POP;
681 break;
682 default:
683 assert(0);
684 }
685 return newLIR1(cUnit, opCode, value);
686}
687
688static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
689{
690 ArmOpCode opCode = THUMB_BKPT;
691 switch (op) {
692 case OP_BLX:
693 opCode = THUMB_BLX_R;
694 break;
695 default:
696 assert(0);
697 }
698 return newLIR1(cUnit, opCode, rDestSrc);
699}
700
701static ArmLIR *opRegRegShift(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
702 int rSrc2, int shift)
703{
704 bool thumbForm = ((shift == 0) && LOWREG(rDestSrc1) && LOWREG(rSrc2));
705 ArmOpCode opCode = THUMB_BKPT;
706 switch (op) {
707 case OP_ADC:
708 opCode = (thumbForm) ? THUMB_ADC : THUMB2_ADC_RRR;
709 break;
710 case OP_AND:
711 opCode = (thumbForm) ? THUMB_AND_RR : THUMB2_AND_RRR;
712 break;
713 case OP_BIC:
714 opCode = (thumbForm) ? THUMB_BIC : THUMB2_BIC_RRR;
715 break;
716 case OP_CMN:
717 assert(shift == 0);
718 opCode = (thumbForm) ? THUMB_CMN : THUMB2_CMN_RR;
719 break;
720 case OP_CMP:
721 if (thumbForm)
722 opCode = THUMB_CMP_RR;
723 else if ((shift == 0) && !LOWREG(rDestSrc1) && !LOWREG(rSrc2))
724 opCode = THUMB_CMP_HH;
725 else if ((shift == 0) && LOWREG(rDestSrc1))
726 opCode = THUMB_CMP_LH;
727 else if (shift == 0)
728 opCode = THUMB_CMP_HL;
729 if (shift == 0) {
730 rDestSrc1 &= THUMB_REG_MASK;
731 rSrc2 &= THUMB_REG_MASK;
732 } else {
733 opCode = THUMB2_CMP_RR;
734 }
735 break;
736 case OP_XOR:
737 opCode = (thumbForm) ? THUMB_EOR : THUMB2_EOR_RRR;
738 break;
739 case OP_MOV:
740 assert(shift == 0);
741 if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
742 opCode = THUMB_MOV_RR;
743 else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
744 opCode = THUMB_MOV_RR_H2H;
745 else if (LOWREG(rDestSrc1))
746 opCode = THUMB_MOV_RR_H2L;
747 else
748 opCode = THUMB_MOV_RR_L2H;
749 rDestSrc1 &= THUMB_REG_MASK;
750 rSrc2 &= THUMB_REG_MASK;
751 break;
752 case OP_MUL:
753 assert(shift == 0);
754 opCode = (thumbForm) ? THUMB_MUL : THUMB2_MUL_RRR;
755 break;
756 case OP_MVN:
757 opCode = (thumbForm) ? THUMB_MVN : THUMB2_MVN_RR;
758 break;
759 case OP_NEG:
760 assert(shift == 0);
761 opCode = (thumbForm) ? THUMB_NEG : THUMB2_NEG_RR;
762 break;
763 case OP_OR:
764 opCode = (thumbForm) ? THUMB_ORR : THUMB2_ORR_RRR;
765 break;
766 case OP_SBC:
767 opCode = (thumbForm) ? THUMB_SBC : THUMB2_SBC_RRR;
768 break;
769 case OP_TST:
770 opCode = (thumbForm) ? THUMB_TST : THUMB2_TST_RR;
771 break;
772 case OP_LSL:
773 assert(shift == 0);
774 opCode = (thumbForm) ? THUMB_LSLV : THUMB2_LSLV_RRR;
775 break;
776 case OP_LSR:
777 assert(shift == 0);
778 opCode = (thumbForm) ? THUMB_LSRV : THUMB2_LSRV_RRR;
779 break;
780 case OP_ASR:
781 assert(shift == 0);
782 opCode = (thumbForm) ? THUMB_ASRV : THUMB2_ASRV_RRR;
783 break;
784 case OP_ROR:
785 assert(shift == 0);
786 opCode = (thumbForm) ? THUMB_RORV : THUMB2_RORV_RRR;
787 break;
788 case OP_ADD:
789 opCode = (thumbForm) ? THUMB_ADD_RRR : THUMB2_ADD_RRR;
790 break;
791 case OP_SUB:
792 opCode = (thumbForm) ? THUMB_SUB_RRR : THUMB2_SUB_RRR;
793 break;
794 case OP_2BYTE:
795 assert(shift == 0);
796 return newLIR4(cUnit, THUMB2_SBFX, rDestSrc1, rSrc2, 0, 8);
797 case OP_2SHORT:
798 assert(shift == 0);
799 return newLIR4(cUnit, THUMB2_SBFX, rDestSrc1, rSrc2, 0, 16);
800 case OP_2CHAR:
801 assert(shift == 0);
802 return newLIR4(cUnit, THUMB2_UBFX, rDestSrc1, rSrc2, 0, 16);
803 default:
804 assert(0);
805 break;
806 }
807 assert(opCode >= 0);
808 if (EncodingMap[opCode].flags & IS_BINARY_OP)
809 return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
810 else if (EncodingMap[opCode].flags & IS_TERTIARY_OP) {
811 if (EncodingMap[opCode].fieldLoc[2].kind == SHIFT)
812 return newLIR3(cUnit, opCode, rDestSrc1, rSrc2, shift);
813 else
814 return newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rSrc2);
815 } else if (EncodingMap[opCode].flags & IS_QUAD_OP)
816 return newLIR4(cUnit, opCode, rDestSrc1, rDestSrc1, rSrc2, shift);
817 else {
818 assert(0);
819 return NULL;
820 }
821}
822
823static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
824 int rSrc2)
825{
826 return opRegRegShift(cUnit, op, rDestSrc1, rSrc2, 0);
827}
828
829/* Handle Thumb-only variants here - otherwise punt to opRegRegImm */
830static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
831 int value, int rScratch)
832{
833 ArmLIR *res;
834 bool neg = (value < 0);
835 int absValue = (neg) ? -value : value;
836 bool shortForm = (((absValue & 0xff) == absValue) && LOWREG(rDestSrc1));
837 ArmOpCode opCode = THUMB_BKPT;
838 switch (op) {
839 case OP_ADD:
840 if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
841 assert((value & 0x3) == 0);
842 return newLIR1(cUnit, THUMB_ADD_SPI7, value >> 2);
843 } else if (shortForm) {
844 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
845 }
846 break;
847 case OP_SUB:
848 if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
849 assert((value & 0x3) == 0);
850 return newLIR1(cUnit, THUMB_SUB_SPI7, value >> 2);
851 } else if (shortForm) {
852 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
853 }
854 break;
855 case OP_CMP:
856 if (LOWREG(rDestSrc1) && shortForm)
857 opCode = (shortForm) ? THUMB_CMP_RI8 : THUMB_CMP_RR;
858 else if (LOWREG(rDestSrc1))
859 opCode = THUMB_CMP_RR;
860 else {
861 shortForm = false;
862 opCode = THUMB_CMP_HL;
863 }
864 break;
865 default:
866 /* Punt to opRegRegImm - if bad case catch it there */
867 shortForm = false;
868 break;
869 }
870 if (shortForm)
871 return newLIR2(cUnit, opCode, rDestSrc1, absValue);
872 else
873 return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value, rScratch);
874}
875
876static ArmLIR *opRegRegRegShift(CompilationUnit *cUnit, OpKind op,
877 int rDest, int rSrc1, int rSrc2, int shift)
878{
879 ArmOpCode opCode = THUMB_BKPT;
880 bool thumbForm = (shift == 0) && LOWREG(rDest) && LOWREG(rSrc1) &&
881 LOWREG(rSrc2);
882 switch (op) {
883 case OP_ADD:
884 opCode = (thumbForm) ? THUMB_ADD_RRR : THUMB2_ADD_RRR;
885 break;
886 case OP_SUB:
887 opCode = (thumbForm) ? THUMB_SUB_RRR : THUMB2_SUB_RRR;
888 break;
889 case OP_ADC:
890 opCode = THUMB2_ADC_RRR;
891 break;
892 case OP_AND:
893 opCode = THUMB2_AND_RRR;
894 break;
895 case OP_BIC:
896 opCode = THUMB2_BIC_RRR;
897 break;
898 case OP_XOR:
899 opCode = THUMB2_EOR_RRR;
900 break;
901 case OP_MUL:
902 assert(shift == 0);
903 opCode = THUMB2_MUL_RRR;
904 break;
905 case OP_OR:
906 opCode = THUMB2_ORR_RRR;
907 break;
908 case OP_SBC:
909 opCode = THUMB2_SBC_RRR;
910 break;
911 case OP_LSL:
912 assert(shift == 0);
913 opCode = THUMB2_LSLV_RRR;
914 break;
915 case OP_LSR:
916 assert(shift == 0);
917 opCode = THUMB2_LSRV_RRR;
918 break;
919 case OP_ASR:
920 assert(shift == 0);
921 opCode = THUMB2_ASRV_RRR;
922 break;
923 case OP_ROR:
924 assert(shift == 0);
925 opCode = THUMB2_RORV_RRR;
926 break;
927 default:
928 assert(0);
929 break;
930 }
931 assert(opCode >= 0);
932 if (EncodingMap[opCode].flags & IS_QUAD_OP)
933 return newLIR4(cUnit, opCode, rDest, rSrc1, rSrc2, shift);
934 else {
935 assert(EncodingMap[opCode].flags & IS_TERTIARY_OP);
936 return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
937 }
938}
939
940static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
941 int rSrc1, int rSrc2)
942{
943 return opRegRegRegShift(cUnit, op, rDest, rSrc1, rSrc2, 0);
944}
945
946static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
947 int rSrc1, int value, int rScratch)
948{
949 ArmLIR *res;
950 bool neg = (value < 0);
951 int absValue = (neg) ? -value : value;
952 ArmOpCode opCode = THUMB_BKPT;
953 ArmOpCode altOpCode = THUMB_BKPT;
954 bool allLowRegs = (LOWREG(rDest) && LOWREG(rSrc1));
955 int modImm = modifiedImmediate(value);
956 int modImmNeg = modifiedImmediate(-value);
957
958 switch(op) {
959 case OP_LSL:
960 if (allLowRegs)
961 return newLIR3(cUnit, THUMB_LSL, rDest, rSrc1, value);
962 else
963 return newLIR3(cUnit, THUMB2_LSL_RRI5, rDest, rSrc1, value);
964 case OP_LSR:
965 if (allLowRegs)
966 return newLIR3(cUnit, THUMB_LSR, rDest, rSrc1, value);
967 else
968 return newLIR3(cUnit, THUMB2_LSR_RRI5, rDest, rSrc1, value);
969 case OP_ASR:
970 if (allLowRegs)
971 return newLIR3(cUnit, THUMB_ASR, rDest, rSrc1, value);
972 else
973 return newLIR3(cUnit, THUMB2_ASR_RRI5, rDest, rSrc1, value);
974 case OP_ROR:
975 return newLIR3(cUnit, THUMB2_ROR_RRI5, rDest, rSrc1, value);
976 case OP_ADD:
977 if (LOWREG(rDest) && (rSrc1 == 13) && (value <= 1020)) { /* sp */
978 assert((value & 0x3) == 0);
979 return newLIR3(cUnit, THUMB_ADD_SP_REL, rDest, rSrc1,
980 value >> 2);
981 } else if (LOWREG(rDest) && (rSrc1 == rpc) && (value <= 1020)) {
982 assert((value & 0x3) == 0);
983 return newLIR3(cUnit, THUMB_ADD_PC_REL, rDest, rSrc1,
984 value >> 2);
985 }
986 opCode = THUMB2_ADD_RRI8;
987 altOpCode = THUMB2_ADD_RRR;
988 // Note: intentional fallthrough
989 case OP_SUB:
990 if (allLowRegs && ((absValue & 0x7) == absValue)) {
991 if (op == OP_ADD)
992 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
993 else
994 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
995 return newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
996 } else if ((absValue & 0xff) == absValue) {
997 if (op == OP_ADD)
998 opCode = (neg) ? THUMB2_SUB_RRI12 : THUMB2_ADD_RRI12;
999 else
1000 opCode = (neg) ? THUMB2_ADD_RRI12 : THUMB2_SUB_RRI12;
1001 return newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
1002 }
1003 if (modImmNeg >= 0) {
1004 op = (op == OP_ADD) ? OP_SUB : OP_ADD;
1005 modImm = modImmNeg;
1006 }
1007 if (op == OP_SUB) {
1008 opCode = THUMB2_SUB_RRI8;
1009 altOpCode = THUMB2_SUB_RRR;
1010 }
1011 break;
1012 case OP_ADC:
1013 opCode = THUMB2_ADC_RRI8;
1014 altOpCode = THUMB2_ADC_RRR;
1015 break;
1016 case OP_SBC:
1017 opCode = THUMB2_SBC_RRI8;
1018 altOpCode = THUMB2_SBC_RRR;
1019 break;
1020 case OP_OR:
1021 opCode = THUMB2_ORR_RRI8;
1022 altOpCode = THUMB2_ORR_RRR;
1023 break;
1024 case OP_AND:
1025 opCode = THUMB2_AND_RRI8;
1026 altOpCode = THUMB2_AND_RRR;
1027 break;
1028 case OP_XOR:
1029 opCode = THUMB2_EOR_RRI8;
1030 altOpCode = THUMB2_EOR_RRR;
1031 break;
1032 case OP_MUL:
1033 //TUNING: power of 2, shift & add
1034 modImm = -1;
1035 altOpCode = THUMB2_MUL_RRR;
1036 break;
1037 default:
1038 assert(0);
1039 }
1040
1041 if (modImm >= 0) {
1042 return newLIR3(cUnit, opCode, rDest, rSrc1, modImm);
1043 } else {
1044 loadConstant(cUnit, rScratch, value);
1045 if (EncodingMap[opCode].flags & IS_QUAD_OP)
1046 return newLIR4(cUnit, altOpCode, rDest, rSrc1, rScratch, 0);
1047 else
1048 return newLIR3(cUnit, altOpCode, rDest, rSrc1, rScratch);
1049 }
1050}
1051
1052//TODO: specialize the inlined routines for Thumb2
1053static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
1054{
1055 DecodedInstruction *dInsn = &mir->dalvikInsn;
1056 int offset = offsetof(InterpState, retval);
1057 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
1058 int reg1 = NEXT_REG(regObj);
1059 loadValue(cUnit, dInsn->arg[0], regObj);
1060 genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL);
1061 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1);
1062 storeWordDisp(cUnit, rGLUE, offset, reg1, regObj);
1063 return false;
1064}
1065
1066static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
1067{
1068 DecodedInstruction *dInsn = &mir->dalvikInsn;
1069 int offset = offsetof(InterpState, retval);
1070 int contents = offsetof(ArrayObject, contents);
1071 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
1072 int regIdx = NEXT_REG(regObj);
1073 int regMax = NEXT_REG(regIdx);
1074 int regOff = NEXT_REG(regMax);
1075 loadValue(cUnit, dInsn->arg[0], regObj);
1076 loadValue(cUnit, dInsn->arg[1], regIdx);
1077 ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj,
1078 mir->offset, NULL);
1079 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax);
1080 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff);
1081 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj);
1082 genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel);
1083
1084 newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents);
1085 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff);
1086 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx);
1087 newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx);
1088 storeWordDisp(cUnit, rGLUE, offset, regMax, regObj);
1089 return false;
1090}
1091
1092static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
1093{
1094 int offset = offsetof(InterpState, retval);
1095 DecodedInstruction *dInsn = &mir->dalvikInsn;
1096 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
1097 int sign = NEXT_REG(reg0);
1098 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
1099 loadValue(cUnit, dInsn->arg[0], reg0);
1100 newLIR3(cUnit, THUMB_ASR, sign, reg0, 31);
1101 newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign);
1102 newLIR2(cUnit, THUMB_EOR, reg0, sign);
1103 storeWordDisp(cUnit, rGLUE, offset, reg0, sign);
1104 return false;
1105}
1106
1107static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
1108{
1109 int offset = offsetof(InterpState, retval);
1110 DecodedInstruction *dInsn = &mir->dalvikInsn;
1111 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
1112 int signMask = NEXT_REG(reg0);
1113 loadValue(cUnit, dInsn->arg[0], reg0);
1114 loadConstant(cUnit, signMask, 0x7fffffff);
1115 newLIR2(cUnit, THUMB_AND_RR, reg0, signMask);
1116 storeWordDisp(cUnit, rGLUE, offset, reg0, signMask);
1117 return false;
1118}
1119
1120static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
1121{
1122 int offset = offsetof(InterpState, retval);
1123 DecodedInstruction *dInsn = &mir->dalvikInsn;
1124 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
1125 int ophi = NEXT_REG(oplo);
1126 int signMask = NEXT_REG(ophi);
1127 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
1128 loadConstant(cUnit, signMask, 0x7fffffff);
1129 storeWordDisp(cUnit, rGLUE, offset, oplo, ophi);
1130 newLIR2(cUnit, THUMB_AND_RR, ophi, signMask);
1131 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, oplo);
1132 return false;
1133}
1134
1135 /* No select in thumb, so we need to branch. Thumb2 will do better */
1136static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
1137{
1138 int offset = offsetof(InterpState, retval);
1139 DecodedInstruction *dInsn = &mir->dalvikInsn;
1140 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
1141 int reg1 = NEXT_REG(reg0);
1142 loadValue(cUnit, dInsn->arg[0], reg0);
1143 loadValue(cUnit, dInsn->arg[1], reg1);
1144 newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1);
1145 ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2,
1146 isMin ? ARM_COND_LT : ARM_COND_GT);
1147 newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1);
1148 ArmLIR *target =
1149 newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2);
1150 branch1->generic.target = (LIR *)target;
1151 return false;
1152}
1153
1154static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
1155{
1156 int offset = offsetof(InterpState, retval);
1157 DecodedInstruction *dInsn = &mir->dalvikInsn;
1158 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
1159 int ophi = NEXT_REG(oplo);
1160 int sign = NEXT_REG(ophi);
1161 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
1162 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
1163 newLIR3(cUnit, THUMB_ASR, sign, ophi, 31);
1164 newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign);
1165 newLIR2(cUnit, THUMB_ADC, ophi, sign);
1166 newLIR2(cUnit, THUMB_EOR, oplo, sign);
1167 newLIR2(cUnit, THUMB_EOR, ophi, sign);
1168 storeWordDisp(cUnit, rGLUE, offset, oplo, sign);
1169 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, sign);
1170 return false;
1171}