blob: 406586574416152a7a3fd8cc7303ee984888bb6d [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);
Bill Buzbeea4a7f072009-08-27 13:58:09 -070048static int inlinedTarget(MIR *mir);
Bill Buzbee270c1d62009-08-13 16:58:07 -070049
Bill Buzbee9bc3df32009-07-30 10:52:29 -070050
51/* Routines which must be supplied here */
Bill Buzbee270c1d62009-08-13 16:58:07 -070052static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
53static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
54 int rAddr);
55static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
56 int displacement, int rDest, OpSize size,
57 bool nullCheck, int vReg);
58static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
59 int displacement, int rSrc, OpSize size,
60 int rScratch);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070061static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
Bill Buzbee270c1d62009-08-13 16:58:07 -070062 ArmConditionCode cond, int reg,
63 int checkValue, int dOffset,
64 ArmLIR *pcrLabel);
Ben Cheng0fd31e42009-09-03 14:40:16 -070065static inline ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
66 ArmConditionCode cond,
67 int reg1, int reg2, int dOffset,
68 ArmLIR *pcrLabel);
Bill Buzbee7ea0f642009-08-10 17:06:51 -070069ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
Bill Buzbee270c1d62009-08-13 16:58:07 -070070static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
71static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070072
Bill Buzbee270c1d62009-08-13 16:58:07 -070073static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op);
74static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value);
Ben Cheng4f489172009-09-27 17:08:35 -070075static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc);
Bill Buzbee270c1d62009-08-13 16:58:07 -070076static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
77static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
78 int rSrc2);
79static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
80 int value, int rScratch);
81static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
82 int rSrc1, int value, int rScratch);
83static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
84 int rSrc1, int rSrc2);
85static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
86 int rIndex, int rDest, int scale, OpSize size);
Bill Buzbeea4a7f072009-08-27 13:58:09 -070087static void genCmpLong(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1,
88 int vSrc2);
Bill Buzbee270c1d62009-08-13 16:58:07 -070089
90static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir);
91static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir);
92static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir);
93static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir);
94static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir);
95static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin);
96static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070097
98/*
99 * Support for register allocation
100 */
101
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700102/* get the next register in r0..r3 in a round-robin fashion */
103#define NEXT_REG(reg) ((reg + 1) & 3)
104/*
105 * The following are utility routines to help maintain the RegisterScoreboard
106 * state to facilitate register renaming.
107 */
108
109/* Reset the tracker to unknown state */
110static inline void resetRegisterScoreboard(CompilationUnit *cUnit)
111{
112 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
113
114 dvmClearAllBits(registerScoreboard->nullCheckedRegs);
115 registerScoreboard->liveDalvikReg = vNone;
116 registerScoreboard->nativeReg = vNone;
117 registerScoreboard->nativeRegHi = vNone;
118}
119
120/* Kill the corresponding bit in the null-checked register list */
121static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg)
122{
123 dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg);
124}
125
126/* The Dalvik register pair held in native registers have changed */
127static inline void updateLiveRegisterPair(CompilationUnit *cUnit,
128 int vReg, int mRegLo, int mRegHi)
129{
130 cUnit->registerScoreboard.liveDalvikReg = vReg;
131 cUnit->registerScoreboard.nativeReg = mRegLo;
132 cUnit->registerScoreboard.nativeRegHi = mRegHi;
133 cUnit->registerScoreboard.isWide = true;
134}
135
136/* The Dalvik register held in a native register has changed */
137static inline void updateLiveRegister(CompilationUnit *cUnit,
138 int vReg, int mReg)
139{
140 cUnit->registerScoreboard.liveDalvikReg = vReg;
141 cUnit->registerScoreboard.nativeReg = mReg;
142 cUnit->registerScoreboard.isWide = false;
143}
144
145/*
146 * Given a Dalvik register id vSrc, use a very simple algorithm to increase
147 * the lifetime of cached Dalvik value in a native register.
148 */
149static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc,
150 bool isWide)
151{
152 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
153
154 /* No live value - suggest to use r0 */
155 if (registerScoreboard->liveDalvikReg == vNone)
156 return r0;
157
158 /* Reuse the previously used native reg */
159 if (registerScoreboard->liveDalvikReg == vSrc) {
160 if (isWide != true) {
161 return registerScoreboard->nativeReg;
162 } else {
163 /* Return either r0 or r2 */
164 return (registerScoreboard->nativeReg + 1) & 2;
165 }
166 }
167
168 /* No reuse - choose the next one among r0..r3 in the round-robin fashion */
169 if (isWide) {
170 return (registerScoreboard->nativeReg + 2) & 2;
171 } else {
172 return (registerScoreboard->nativeReg + 1) & 3;
173 }
174
175}
176
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700177ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
178{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700179 ArmLIR* res;
180 ArmOpCode opCode;
181 res = dvmCompilerNew(sizeof(ArmLIR), true);
182 if (LOWREG(rDest) && LOWREG(rSrc))
183 opCode = THUMB_MOV_RR;
184 else if (!LOWREG(rDest) && !LOWREG(rSrc))
185 opCode = THUMB_MOV_RR_H2H;
186 else if (LOWREG(rDest))
187 opCode = THUMB_MOV_RR_H2L;
188 else
189 opCode = THUMB_MOV_RR_L2H;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700190
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700191 res->operands[0] = rDest;
192 res->operands[1] = rSrc;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700193 res->opCode = opCode;
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700194 setupResourceMasks(res);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700195 if (rDest == rSrc) {
196 res->isNop = true;
197 }
198 return res;
199}
200
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700201/*
202 * Load a immediate using a shortcut if possible; otherwise
203 * grab from the per-translation literal pool
204 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700205static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700206{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700207 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700208 /* See if the value can be constructed cheaply */
209 if ((value >= 0) && (value <= 255)) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700210 return newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700211 } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700212 res = newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700213 newLIR2(cUnit, THUMB_MVN, rDest, rDest);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700214 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700215 }
216 /* No shortcut - go ahead and use literal pool */
217 ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
218 if (dataTarget == NULL) {
219 dataTarget = addWordData(cUnit, value, false);
220 }
221 ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
222 loadPcRel->opCode = THUMB_LDR_PC_REL;
223 loadPcRel->generic.target = (LIR *) dataTarget;
224 loadPcRel->operands[0] = rDest;
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700225 setupResourceMasks(loadPcRel);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700226 res = loadPcRel;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700227 dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
228
229 /*
230 * To save space in the constant pool, we use the ADD_RRI8 instruction to
231 * add up to 255 to an existing constant value.
232 */
233 if (dataTarget->operands[0] != value) {
234 newLIR2(cUnit, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
235 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700236 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700237}
238
239/* Export the Dalvik PC assicated with an instruction to the StackSave area */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700240static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
241 int rAddr)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700242{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700243 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700244 int offset = offsetof(StackSaveArea, xtra.currentPc);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700245 res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700246 newLIR2(cUnit, THUMB_MOV_RR, rAddr, rFP);
247 newLIR2(cUnit, THUMB_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700248 storeWordDisp( cUnit, rAddr, 0, rDPC, -1);
249 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700250}
251
Bill Buzbee270c1d62009-08-13 16:58:07 -0700252/* Load value from base + scaled index. Note: index reg killed */
253static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
254 int rIndex, int rDest, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700255{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700256 ArmLIR *first = NULL;
257 ArmLIR *res;
258 ArmOpCode opCode = THUMB_BKPT;
259 if (scale)
260 first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
261 switch (size) {
262 case WORD:
263 opCode = THUMB_LDR_RRR;
264 break;
265 case UNSIGNED_HALF:
266 opCode = THUMB_LDRH_RRR;
267 break;
268 case SIGNED_HALF:
269 opCode = THUMB_LDRSH_RRR;
270 break;
271 case UNSIGNED_BYTE:
272 opCode = THUMB_LDRB_RRR;
273 break;
274 case SIGNED_BYTE:
275 opCode = THUMB_LDRSB_RRR;
276 break;
277 default:
278 assert(0);
279 }
280 res = newLIR3(cUnit, opCode, rDest, rBase, rIndex);
281 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700282}
283
Bill Buzbee270c1d62009-08-13 16:58:07 -0700284/* store value base base + scaled index. Note: index reg killed */
285static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
286 int rIndex, int rSrc, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700287{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700288 ArmLIR *first = NULL;
289 ArmLIR *res;
290 ArmOpCode opCode = THUMB_BKPT;
291 if (scale)
292 first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
293 switch (size) {
294 case WORD:
295 opCode = THUMB_STR_RRR;
296 break;
297 case UNSIGNED_HALF:
298 case SIGNED_HALF:
299 opCode = THUMB_STRH_RRR;
300 break;
301 case UNSIGNED_BYTE:
302 case SIGNED_BYTE:
303 opCode = THUMB_STRB_RRR;
304 break;
305 default:
306 assert(0);
307 }
308 res = newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
309 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700310}
311
312/*
Bill Buzbee270c1d62009-08-13 16:58:07 -0700313 * Load value from base + displacement. Optionally perform null check
314 * on base (which must have an associated vReg and MIR). If not
315 * performing null check, incoming MIR can be null. Note: base and
316 * dest must not be the same if there is any chance that the long
317 * form must be used.
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700318 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700319static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
320 int displacement, int rDest, OpSize size,
321 bool nullCheck, int vReg)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700322{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700323 ArmLIR *first = NULL;
Ben Chengd7d426a2009-09-22 11:23:36 -0700324 ArmLIR *res, *load;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700325 ArmOpCode opCode = THUMB_BKPT;
326 bool shortForm = false;
327 int shortMax = 128;
Ben Chengd7d426a2009-09-22 11:23:36 -0700328 int encodedDisp = displacement;
329
Bill Buzbee270c1d62009-08-13 16:58:07 -0700330 switch (size) {
331 case WORD:
332 if (LOWREG(rDest) && (rBase == rpc) &&
333 (displacement <= 1020) && (displacement >= 0)) {
334 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700335 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700336 opCode = THUMB_LDR_PC_REL;
337 } else if (LOWREG(rDest) && (rBase == r13) &&
338 (displacement <= 1020) && (displacement >= 0)) {
339 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700340 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700341 opCode = THUMB_LDR_SP_REL;
342 } else if (displacement < 128 && displacement >= 0) {
343 assert((displacement & 0x3) == 0);
344 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700345 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700346 opCode = THUMB_LDR_RRI5;
347 } else {
348 opCode = THUMB_LDR_RRR;
349 }
350 break;
351 case UNSIGNED_HALF:
352 if (displacement < 64 && displacement >= 0) {
353 assert((displacement & 0x1) == 0);
354 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700355 encodedDisp >>= 1;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700356 opCode = THUMB_LDRH_RRI5;
357 } else {
358 opCode = THUMB_LDRH_RRR;
359 }
360 break;
361 case SIGNED_HALF:
362 opCode = THUMB_LDRSH_RRR;
363 break;
364 case UNSIGNED_BYTE:
365 if (displacement < 32 && displacement >= 0) {
366 shortForm = true;
367 opCode = THUMB_LDRB_RRI5;
368 } else {
369 opCode = THUMB_LDRB_RRR;
370 }
371 break;
372 case SIGNED_BYTE:
373 opCode = THUMB_LDRSB_RRR;
374 break;
375 default:
376 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700377 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700378 if (nullCheck)
379 first = genNullCheck(cUnit, vReg, rBase, mir->offset, NULL);
380 if (shortForm) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700381 load = res = newLIR3(cUnit, opCode, rDest, rBase, encodedDisp);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700382 } else {
383 assert(rBase != rDest);
Ben Chengd7d426a2009-09-22 11:23:36 -0700384 res = loadConstant(cUnit, rDest, encodedDisp);
385 load = newLIR3(cUnit, opCode, rDest, rBase, rDest);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700386 }
Ben Chengd7d426a2009-09-22 11:23:36 -0700387
388 if (rBase == rFP) {
389 annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
390 }
391
Bill Buzbee270c1d62009-08-13 16:58:07 -0700392 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700393}
394
Bill Buzbee270c1d62009-08-13 16:58:07 -0700395static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
396 int displacement, int rSrc, OpSize size,
397 int rScratch)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700398{
Ben Chengd7d426a2009-09-22 11:23:36 -0700399 ArmLIR *res, *store;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700400 ArmOpCode opCode = THUMB_BKPT;
401 bool shortForm = false;
402 int shortMax = 128;
Ben Chengd7d426a2009-09-22 11:23:36 -0700403 int encodedDisp = displacement;
404
Bill Buzbee270c1d62009-08-13 16:58:07 -0700405 switch (size) {
406 case WORD:
407 if (displacement < 128 && displacement >= 0) {
408 assert((displacement & 0x3) == 0);
409 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700410 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700411 opCode = THUMB_STR_RRI5;
412 } else {
413 opCode = THUMB_STR_RRR;
414 }
415 break;
416 case UNSIGNED_HALF:
417 case SIGNED_HALF:
418 if (displacement < 64 && displacement >= 0) {
419 assert((displacement & 0x1) == 0);
420 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700421 encodedDisp >>= 1;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700422 opCode = THUMB_STRH_RRI5;
423 } else {
424 opCode = THUMB_STRH_RRR;
425 }
426 break;
427 case UNSIGNED_BYTE:
428 case SIGNED_BYTE:
429 if (displacement < 32 && displacement >= 0) {
430 shortForm = true;
431 opCode = THUMB_STRB_RRI5;
432 } else {
433 opCode = THUMB_STRB_RRR;
434 }
435 break;
436 default:
437 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700438 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700439 if (shortForm) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700440 store = res = newLIR3(cUnit, opCode, rSrc, rBase, encodedDisp);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700441 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700442 assert(rScratch != -1);
Ben Chengd7d426a2009-09-22 11:23:36 -0700443 res = loadConstant(cUnit, rScratch, encodedDisp);
444 store = newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
445 }
446
447 if (rBase == rFP) {
448 annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700449 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700450 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700451}
452
453/*
454 * Perform a "reg cmp imm" operation and jump to the PCR region if condition
455 * satisfies.
456 */
457static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
458 ArmConditionCode cond, int reg,
459 int checkValue, int dOffset,
460 ArmLIR *pcrLabel)
461{
Ben Cheng0fd31e42009-09-03 14:40:16 -0700462 if ((checkValue & 0xff) != checkValue) {
463 /* NOTE: direct use of hot temp r7 here. Revisit. */
464 loadConstant(cUnit, r7, checkValue);
465 return genRegRegCheck(cUnit, cond, reg, r7, dOffset, pcrLabel);
466 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700467 newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
468 ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
469 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
470}
Bill Buzbee270c1d62009-08-13 16:58:07 -0700471
472static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
473{
474 return newLIR2(cUnit, THUMB_LDMIA, rBase, rMask);
475}
476
477static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
478{
479 return newLIR2(cUnit, THUMB_STMIA, rBase, rMask);
480}
481
482static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
483{
484 ArmOpCode opCode = THUMB_BKPT;
485 switch (op) {
486 case OP_UNCOND_BR:
487 opCode = THUMB_B_UNCOND;
488 break;
489 default:
490 assert(0);
491 }
492 return newLIR0(cUnit, opCode);
493}
494
Ben Cheng4f489172009-09-27 17:08:35 -0700495static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
Bill Buzbee270c1d62009-08-13 16:58:07 -0700496{
Ben Cheng4f489172009-09-27 17:08:35 -0700497 return newLIR2(cUnit, THUMB_B_COND, 0 /* offset to be patched */, cc);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700498}
499
500static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
501{
502 ArmOpCode opCode = THUMB_BKPT;
503 switch (op) {
504 case OP_PUSH:
505 opCode = THUMB_PUSH;
506 break;
507 case OP_POP:
508 opCode = THUMB_POP;
509 break;
510 default:
511 assert(0);
512 }
513 return newLIR1(cUnit, opCode, value);
514}
515
516static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
517{
518 ArmOpCode opCode = THUMB_BKPT;
519 switch (op) {
520 case OP_BLX:
521 opCode = THUMB_BLX_R;
522 break;
523 default:
524 assert(0);
525 }
526 return newLIR1(cUnit, opCode, rDestSrc);
527}
528
529static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
530 int rSrc2)
531{
532 ArmLIR *res;
533 ArmOpCode opCode = THUMB_BKPT;
534 switch (op) {
535 case OP_ADC:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700536 opCode = THUMB_ADC_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700537 break;
538 case OP_AND:
539 opCode = THUMB_AND_RR;
540 break;
541 case OP_BIC:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700542 opCode = THUMB_BIC_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700543 break;
544 case OP_CMN:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700545 opCode = THUMB_CMN_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700546 break;
547 case OP_CMP:
548 opCode = THUMB_CMP_RR;
549 break;
550 case OP_XOR:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700551 opCode = THUMB_EOR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700552 break;
553 case OP_MOV:
554 if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
555 opCode = THUMB_MOV_RR;
556 else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
557 opCode = THUMB_MOV_RR_H2H;
558 else if (LOWREG(rDestSrc1))
559 opCode = THUMB_MOV_RR_H2L;
560 else
561 opCode = THUMB_MOV_RR_L2H;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700562 break;
563 case OP_MUL:
564 opCode = THUMB_MUL;
565 break;
566 case OP_MVN:
567 opCode = THUMB_MVN;
568 break;
569 case OP_NEG:
570 opCode = THUMB_NEG;
571 break;
572 case OP_OR:
573 opCode = THUMB_ORR;
574 break;
575 case OP_SBC:
576 opCode = THUMB_SBC;
577 break;
578 case OP_TST:
579 opCode = THUMB_TST;
580 break;
581 case OP_LSL:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700582 opCode = THUMB_LSL_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700583 break;
584 case OP_LSR:
Ben Cheng2275f9c2009-09-11 15:34:19 -0700585 opCode = THUMB_LSR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700586 break;
587 case OP_ASR:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700588 opCode = THUMB_ASR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700589 break;
590 case OP_ROR:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700591 opCode = THUMB_ROR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700592 case OP_ADD:
593 case OP_SUB:
594 return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
595 case OP_2BYTE:
596 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 24, rNone);
597 opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 24, rNone);
598 return res;
599 case OP_2SHORT:
600 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
601 opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 16, rNone);
602 return res;
603 case OP_2CHAR:
604 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
605 opRegRegImm(cUnit, OP_LSR, rDestSrc1, rDestSrc1, 16, rNone);
606 return res;
607 default:
608 assert(0);
609 break;
610 }
611 return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
612}
613
614static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
615 int value, int rScratch)
616{
617 ArmLIR *res;
618 bool neg = (value < 0);
619 int absValue = (neg) ? -value : value;
620 bool shortForm = (absValue & 0xff) == absValue;
621 ArmOpCode opCode = THUMB_BKPT;
622 switch (op) {
623 case OP_ADD:
624 if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
625 assert((value & 0x3) == 0);
626 return newLIR1(cUnit, THUMB_ADD_SPI7, value >> 2);
627 } else if (shortForm) {
628 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
629 } else
630 opCode = THUMB_ADD_RRR;
631 break;
632 case OP_SUB:
633 if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
634 assert((value & 0x3) == 0);
635 return newLIR1(cUnit, THUMB_SUB_SPI7, value >> 2);
636 } else if (shortForm) {
637 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
638 } else
639 opCode = THUMB_SUB_RRR;
640 break;
641 case OP_CMP:
642 if (LOWREG(rDestSrc1) && shortForm)
643 opCode = (shortForm) ? THUMB_CMP_RI8 : THUMB_CMP_RR;
644 else if (LOWREG(rDestSrc1))
645 opCode = THUMB_CMP_RR;
646 else {
647 shortForm = false;
648 opCode = THUMB_CMP_HL;
649 }
650 break;
651 default:
652 assert(0);
653 break;
654 }
655 if (shortForm)
656 res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
657 else {
658 assert(rScratch != rNone);
659 res = loadConstant(cUnit, rScratch, value);
660 newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
661 }
662 return res;
663}
664
665static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
666 int rSrc1, int rSrc2)
667{
668 ArmOpCode opCode = THUMB_BKPT;
669 switch (op) {
670 case OP_ADD:
671 opCode = THUMB_ADD_RRR;
672 break;
673 case OP_SUB:
674 opCode = THUMB_SUB_RRR;
675 break;
676 default:
677 assert(0);
678 break;
679 }
680 return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
681}
682
683static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
684 int rSrc1, int value, int rScratch)
685{
686 ArmLIR *res;
687 bool neg = (value < 0);
688 int absValue = (neg) ? -value : value;
689 ArmOpCode opCode = THUMB_BKPT;
690 bool shortForm = (absValue & 0x7) == absValue;
691 switch(op) {
692 case OP_ADD:
693 if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
694 assert((value & 0x3) == 0);
695 shortForm = true;
696 opCode = THUMB_ADD_SP_REL;
697 value >>= 2;
698 } else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
699 assert((value & 0x3) == 0);
700 shortForm = true;
701 opCode = THUMB_ADD_PC_REL;
702 value >>= 2;
703 } else if (shortForm) {
704 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
705 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
706 /* Two shots - 1st handle the 7 */
707 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
708 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
709 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
710 newLIR2(cUnit, opCode, rDest, absValue - 7);
711 return res;
712 } else
713 opCode = THUMB_ADD_RRR;
714 break;
715
716 case OP_SUB:
717 if (shortForm) {
718 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
719 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
720 /* Two shots - 1st handle the 7 */
721 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
722 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
723 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
724 newLIR2(cUnit, opCode, rDest, absValue - 7);
725 return res;
726 } else
727 opCode = THUMB_SUB_RRR;
728 break;
729 case OP_LSL:
730 shortForm = (!neg && value <= 31);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700731 opCode = THUMB_LSL_RRI5;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700732 break;
733 case OP_LSR:
734 shortForm = (!neg && value <= 31);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700735 opCode = THUMB_LSR_RRI5;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700736 break;
737 case OP_ASR:
738 shortForm = (!neg && value <= 31);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700739 opCode = THUMB_ASR_RRI5;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700740 break;
741 case OP_MUL:
742 case OP_AND:
743 case OP_OR:
744 case OP_XOR:
745 if (rDest == rSrc1) {
746 res = loadConstant(cUnit, rScratch, value);
747 opRegReg(cUnit, op, rDest, rScratch);
748 } else {
749 res = loadConstant(cUnit, rDest, value);
750 opRegReg(cUnit, op, rDest, rSrc1);
751 }
752 return res;
753 default:
754 assert(0);
755 break;
756 }
757 if (shortForm)
758 res = newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
759 else {
760 if (rDest != rSrc1) {
761 res = loadConstant(cUnit, rDest, value);
762 newLIR3(cUnit, opCode, rDest, rSrc1, rDest);
763 } else {
764 assert(rScratch != rNone);
765 res = loadConstant(cUnit, rScratch, value);
766 newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
767 }
768 }
769 return res;
770}
771
Bill Buzbeea4a7f072009-08-27 13:58:09 -0700772static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
773 int vDest, int vSrc1, int vSrc2)
774{
775 loadValuePair(cUnit, vSrc1, r0, r1);
776 loadValuePair(cUnit, vSrc2, r2, r3);
777 genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
778 storeValue(cUnit, r0, vDest, r1);
779}
780
Bill Buzbee270c1d62009-08-13 16:58:07 -0700781static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
782{
783 DecodedInstruction *dInsn = &mir->dalvikInsn;
784 int offset = offsetof(InterpState, retval);
785 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
786 int reg1 = NEXT_REG(regObj);
787 loadValue(cUnit, dInsn->arg[0], regObj);
788 genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL);
789 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1);
790 storeWordDisp(cUnit, rGLUE, offset, reg1, regObj);
791 return false;
792}
793
794static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
795{
796 DecodedInstruction *dInsn = &mir->dalvikInsn;
797 int offset = offsetof(InterpState, retval);
798 int contents = offsetof(ArrayObject, contents);
799 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
800 int regIdx = NEXT_REG(regObj);
801 int regMax = NEXT_REG(regIdx);
802 int regOff = NEXT_REG(regMax);
803 loadValue(cUnit, dInsn->arg[0], regObj);
804 loadValue(cUnit, dInsn->arg[1], regIdx);
805 ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj,
806 mir->offset, NULL);
807 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax);
808 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff);
809 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj);
810 genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel);
811
812 newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents);
813 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff);
814 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx);
815 newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx);
816 storeWordDisp(cUnit, rGLUE, offset, regMax, regObj);
817 return false;
818}
819
820static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
821{
822 int offset = offsetof(InterpState, retval);
823 DecodedInstruction *dInsn = &mir->dalvikInsn;
824 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
825 int sign = NEXT_REG(reg0);
826 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
827 loadValue(cUnit, dInsn->arg[0], reg0);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700828 newLIR3(cUnit, THUMB_ASR_RRI5, sign, reg0, 31);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700829 newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700830 newLIR2(cUnit, THUMB_EOR_RR, reg0, sign);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700831 storeWordDisp(cUnit, rGLUE, offset, reg0, sign);
832 return false;
833}
834
835static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
836{
837 int offset = offsetof(InterpState, retval);
838 DecodedInstruction *dInsn = &mir->dalvikInsn;
839 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
840 int signMask = NEXT_REG(reg0);
841 loadValue(cUnit, dInsn->arg[0], reg0);
842 loadConstant(cUnit, signMask, 0x7fffffff);
843 newLIR2(cUnit, THUMB_AND_RR, reg0, signMask);
844 storeWordDisp(cUnit, rGLUE, offset, reg0, signMask);
845 return false;
846}
847
848static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
849{
850 int offset = offsetof(InterpState, retval);
851 DecodedInstruction *dInsn = &mir->dalvikInsn;
852 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
853 int ophi = NEXT_REG(oplo);
854 int signMask = NEXT_REG(ophi);
855 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
856 loadConstant(cUnit, signMask, 0x7fffffff);
857 storeWordDisp(cUnit, rGLUE, offset, oplo, ophi);
858 newLIR2(cUnit, THUMB_AND_RR, ophi, signMask);
859 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, oplo);
860 return false;
861}
862
863 /* No select in thumb, so we need to branch. Thumb2 will do better */
864static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
865{
866 int offset = offsetof(InterpState, retval);
867 DecodedInstruction *dInsn = &mir->dalvikInsn;
868 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
869 int reg1 = NEXT_REG(reg0);
870 loadValue(cUnit, dInsn->arg[0], reg0);
871 loadValue(cUnit, dInsn->arg[1], reg1);
872 newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1);
873 ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2,
874 isMin ? ARM_COND_LT : ARM_COND_GT);
875 newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1);
876 ArmLIR *target =
877 newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2);
878 branch1->generic.target = (LIR *)target;
879 return false;
880}
881
882static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
883{
884 int offset = offsetof(InterpState, retval);
885 DecodedInstruction *dInsn = &mir->dalvikInsn;
886 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
887 int ophi = NEXT_REG(oplo);
888 int sign = NEXT_REG(ophi);
889 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
890 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700891 newLIR3(cUnit, THUMB_ASR_RRI5, sign, ophi, 31);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700892 newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700893 newLIR2(cUnit, THUMB_ADC_RR, ophi, sign);
894 newLIR2(cUnit, THUMB_EOR_RR, oplo, sign);
895 newLIR2(cUnit, THUMB_EOR_RR, ophi, sign);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700896 storeWordDisp(cUnit, rGLUE, offset, oplo, sign);
897 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, sign);
898 return false;
899}