blob: 49e04b414cc8f2f413dc589b61b982d5063169f2 [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);
75static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
76 int value2);
77static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
78static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
79 int rSrc2);
80static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
81 int value, int rScratch);
82static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
83 int rSrc1, int value, int rScratch);
84static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
85 int rSrc1, int rSrc2);
86static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
87 int rIndex, int rDest, int scale, OpSize size);
Bill Buzbeea4a7f072009-08-27 13:58:09 -070088static void genCmpLong(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1,
89 int vSrc2);
Bill Buzbee270c1d62009-08-13 16:58:07 -070090
91static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir);
92static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir);
93static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir);
94static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir);
95static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir);
96static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin);
97static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070098
99/*
100 * Support for register allocation
101 */
102
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700103/* get the next register in r0..r3 in a round-robin fashion */
104#define NEXT_REG(reg) ((reg + 1) & 3)
105/*
106 * The following are utility routines to help maintain the RegisterScoreboard
107 * state to facilitate register renaming.
108 */
109
110/* Reset the tracker to unknown state */
111static inline void resetRegisterScoreboard(CompilationUnit *cUnit)
112{
113 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
114
115 dvmClearAllBits(registerScoreboard->nullCheckedRegs);
116 registerScoreboard->liveDalvikReg = vNone;
117 registerScoreboard->nativeReg = vNone;
118 registerScoreboard->nativeRegHi = vNone;
119}
120
121/* Kill the corresponding bit in the null-checked register list */
122static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg)
123{
124 dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg);
125}
126
127/* The Dalvik register pair held in native registers have changed */
128static inline void updateLiveRegisterPair(CompilationUnit *cUnit,
129 int vReg, int mRegLo, int mRegHi)
130{
131 cUnit->registerScoreboard.liveDalvikReg = vReg;
132 cUnit->registerScoreboard.nativeReg = mRegLo;
133 cUnit->registerScoreboard.nativeRegHi = mRegHi;
134 cUnit->registerScoreboard.isWide = true;
135}
136
137/* The Dalvik register held in a native register has changed */
138static inline void updateLiveRegister(CompilationUnit *cUnit,
139 int vReg, int mReg)
140{
141 cUnit->registerScoreboard.liveDalvikReg = vReg;
142 cUnit->registerScoreboard.nativeReg = mReg;
143 cUnit->registerScoreboard.isWide = false;
144}
145
146/*
147 * Given a Dalvik register id vSrc, use a very simple algorithm to increase
148 * the lifetime of cached Dalvik value in a native register.
149 */
150static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc,
151 bool isWide)
152{
153 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
154
155 /* No live value - suggest to use r0 */
156 if (registerScoreboard->liveDalvikReg == vNone)
157 return r0;
158
159 /* Reuse the previously used native reg */
160 if (registerScoreboard->liveDalvikReg == vSrc) {
161 if (isWide != true) {
162 return registerScoreboard->nativeReg;
163 } else {
164 /* Return either r0 or r2 */
165 return (registerScoreboard->nativeReg + 1) & 2;
166 }
167 }
168
169 /* No reuse - choose the next one among r0..r3 in the round-robin fashion */
170 if (isWide) {
171 return (registerScoreboard->nativeReg + 2) & 2;
172 } else {
173 return (registerScoreboard->nativeReg + 1) & 3;
174 }
175
176}
177
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700178ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
179{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700180 ArmLIR* res;
181 ArmOpCode opCode;
182 res = dvmCompilerNew(sizeof(ArmLIR), true);
183 if (LOWREG(rDest) && LOWREG(rSrc))
184 opCode = THUMB_MOV_RR;
185 else if (!LOWREG(rDest) && !LOWREG(rSrc))
186 opCode = THUMB_MOV_RR_H2H;
187 else if (LOWREG(rDest))
188 opCode = THUMB_MOV_RR_H2L;
189 else
190 opCode = THUMB_MOV_RR_L2H;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700191
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700192 res->operands[0] = rDest;
193 res->operands[1] = rSrc;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700194 res->opCode = opCode;
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700195 setupResourceMasks(res);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700196 if (rDest == rSrc) {
197 res->isNop = true;
198 }
199 return res;
200}
201
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700202/*
203 * Load a immediate using a shortcut if possible; otherwise
204 * grab from the per-translation literal pool
205 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700206static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700207{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700208 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700209 /* See if the value can be constructed cheaply */
210 if ((value >= 0) && (value <= 255)) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700211 return newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700212 } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700213 res = newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700214 newLIR2(cUnit, THUMB_MVN, rDest, rDest);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700215 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700216 }
217 /* No shortcut - go ahead and use literal pool */
218 ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
219 if (dataTarget == NULL) {
220 dataTarget = addWordData(cUnit, value, false);
221 }
222 ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
223 loadPcRel->opCode = THUMB_LDR_PC_REL;
224 loadPcRel->generic.target = (LIR *) dataTarget;
225 loadPcRel->operands[0] = rDest;
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700226 setupResourceMasks(loadPcRel);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700227 res = loadPcRel;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700228 dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
229
230 /*
231 * To save space in the constant pool, we use the ADD_RRI8 instruction to
232 * add up to 255 to an existing constant value.
233 */
234 if (dataTarget->operands[0] != value) {
235 newLIR2(cUnit, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
236 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700237 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700238}
239
240/* Export the Dalvik PC assicated with an instruction to the StackSave area */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700241static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
242 int rAddr)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700243{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700244 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700245 int offset = offsetof(StackSaveArea, xtra.currentPc);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700246 res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700247 newLIR2(cUnit, THUMB_MOV_RR, rAddr, rFP);
248 newLIR2(cUnit, THUMB_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700249 storeWordDisp( cUnit, rAddr, 0, rDPC, -1);
250 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700251}
252
Bill Buzbee270c1d62009-08-13 16:58:07 -0700253/* Load value from base + scaled index. Note: index reg killed */
254static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
255 int rIndex, int rDest, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700256{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700257 ArmLIR *first = NULL;
258 ArmLIR *res;
259 ArmOpCode opCode = THUMB_BKPT;
260 if (scale)
261 first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
262 switch (size) {
263 case WORD:
264 opCode = THUMB_LDR_RRR;
265 break;
266 case UNSIGNED_HALF:
267 opCode = THUMB_LDRH_RRR;
268 break;
269 case SIGNED_HALF:
270 opCode = THUMB_LDRSH_RRR;
271 break;
272 case UNSIGNED_BYTE:
273 opCode = THUMB_LDRB_RRR;
274 break;
275 case SIGNED_BYTE:
276 opCode = THUMB_LDRSB_RRR;
277 break;
278 default:
279 assert(0);
280 }
281 res = newLIR3(cUnit, opCode, rDest, rBase, rIndex);
282 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700283}
284
Bill Buzbee270c1d62009-08-13 16:58:07 -0700285/* store value base base + scaled index. Note: index reg killed */
286static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
287 int rIndex, int rSrc, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700288{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700289 ArmLIR *first = NULL;
290 ArmLIR *res;
291 ArmOpCode opCode = THUMB_BKPT;
292 if (scale)
293 first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
294 switch (size) {
295 case WORD:
296 opCode = THUMB_STR_RRR;
297 break;
298 case UNSIGNED_HALF:
299 case SIGNED_HALF:
300 opCode = THUMB_STRH_RRR;
301 break;
302 case UNSIGNED_BYTE:
303 case SIGNED_BYTE:
304 opCode = THUMB_STRB_RRR;
305 break;
306 default:
307 assert(0);
308 }
309 res = newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
310 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700311}
312
313/*
Bill Buzbee270c1d62009-08-13 16:58:07 -0700314 * Load value from base + displacement. Optionally perform null check
315 * on base (which must have an associated vReg and MIR). If not
316 * performing null check, incoming MIR can be null. Note: base and
317 * dest must not be the same if there is any chance that the long
318 * form must be used.
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700319 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700320static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
321 int displacement, int rDest, OpSize size,
322 bool nullCheck, int vReg)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700323{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700324 ArmLIR *first = NULL;
Ben Chengd7d426a2009-09-22 11:23:36 -0700325 ArmLIR *res, *load;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700326 ArmOpCode opCode = THUMB_BKPT;
327 bool shortForm = false;
328 int shortMax = 128;
Ben Chengd7d426a2009-09-22 11:23:36 -0700329 int encodedDisp = displacement;
330
Bill Buzbee270c1d62009-08-13 16:58:07 -0700331 switch (size) {
332 case WORD:
333 if (LOWREG(rDest) && (rBase == rpc) &&
334 (displacement <= 1020) && (displacement >= 0)) {
335 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700336 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700337 opCode = THUMB_LDR_PC_REL;
338 } else if (LOWREG(rDest) && (rBase == r13) &&
339 (displacement <= 1020) && (displacement >= 0)) {
340 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700341 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700342 opCode = THUMB_LDR_SP_REL;
343 } else if (displacement < 128 && displacement >= 0) {
344 assert((displacement & 0x3) == 0);
345 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700346 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700347 opCode = THUMB_LDR_RRI5;
348 } else {
349 opCode = THUMB_LDR_RRR;
350 }
351 break;
352 case UNSIGNED_HALF:
353 if (displacement < 64 && displacement >= 0) {
354 assert((displacement & 0x1) == 0);
355 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700356 encodedDisp >>= 1;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700357 opCode = THUMB_LDRH_RRI5;
358 } else {
359 opCode = THUMB_LDRH_RRR;
360 }
361 break;
362 case SIGNED_HALF:
363 opCode = THUMB_LDRSH_RRR;
364 break;
365 case UNSIGNED_BYTE:
366 if (displacement < 32 && displacement >= 0) {
367 shortForm = true;
368 opCode = THUMB_LDRB_RRI5;
369 } else {
370 opCode = THUMB_LDRB_RRR;
371 }
372 break;
373 case SIGNED_BYTE:
374 opCode = THUMB_LDRSB_RRR;
375 break;
376 default:
377 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700378 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700379 if (nullCheck)
380 first = genNullCheck(cUnit, vReg, rBase, mir->offset, NULL);
381 if (shortForm) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700382 load = res = newLIR3(cUnit, opCode, rDest, rBase, encodedDisp);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700383 } else {
384 assert(rBase != rDest);
Ben Chengd7d426a2009-09-22 11:23:36 -0700385 res = loadConstant(cUnit, rDest, encodedDisp);
386 load = newLIR3(cUnit, opCode, rDest, rBase, rDest);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700387 }
Ben Chengd7d426a2009-09-22 11:23:36 -0700388
389 if (rBase == rFP) {
390 annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
391 }
392
Bill Buzbee270c1d62009-08-13 16:58:07 -0700393 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700394}
395
Bill Buzbee270c1d62009-08-13 16:58:07 -0700396static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
397 int displacement, int rSrc, OpSize size,
398 int rScratch)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700399{
Ben Chengd7d426a2009-09-22 11:23:36 -0700400 ArmLIR *res, *store;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700401 ArmOpCode opCode = THUMB_BKPT;
402 bool shortForm = false;
403 int shortMax = 128;
Ben Chengd7d426a2009-09-22 11:23:36 -0700404 int encodedDisp = displacement;
405
Bill Buzbee270c1d62009-08-13 16:58:07 -0700406 switch (size) {
407 case WORD:
408 if (displacement < 128 && displacement >= 0) {
409 assert((displacement & 0x3) == 0);
410 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700411 encodedDisp >>= 2;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700412 opCode = THUMB_STR_RRI5;
413 } else {
414 opCode = THUMB_STR_RRR;
415 }
416 break;
417 case UNSIGNED_HALF:
418 case SIGNED_HALF:
419 if (displacement < 64 && displacement >= 0) {
420 assert((displacement & 0x1) == 0);
421 shortForm = true;
Ben Chengd7d426a2009-09-22 11:23:36 -0700422 encodedDisp >>= 1;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700423 opCode = THUMB_STRH_RRI5;
424 } else {
425 opCode = THUMB_STRH_RRR;
426 }
427 break;
428 case UNSIGNED_BYTE:
429 case SIGNED_BYTE:
430 if (displacement < 32 && displacement >= 0) {
431 shortForm = true;
432 opCode = THUMB_STRB_RRI5;
433 } else {
434 opCode = THUMB_STRB_RRR;
435 }
436 break;
437 default:
438 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700439 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700440 if (shortForm) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700441 store = res = newLIR3(cUnit, opCode, rSrc, rBase, encodedDisp);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700442 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700443 assert(rScratch != -1);
Ben Chengd7d426a2009-09-22 11:23:36 -0700444 res = loadConstant(cUnit, rScratch, encodedDisp);
445 store = newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
446 }
447
448 if (rBase == rFP) {
449 annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700450 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700451 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700452}
453
454/*
455 * Perform a "reg cmp imm" operation and jump to the PCR region if condition
456 * satisfies.
457 */
458static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
459 ArmConditionCode cond, int reg,
460 int checkValue, int dOffset,
461 ArmLIR *pcrLabel)
462{
Ben Cheng0fd31e42009-09-03 14:40:16 -0700463 if ((checkValue & 0xff) != checkValue) {
464 /* NOTE: direct use of hot temp r7 here. Revisit. */
465 loadConstant(cUnit, r7, checkValue);
466 return genRegRegCheck(cUnit, cond, reg, r7, dOffset, pcrLabel);
467 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700468 newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
469 ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
470 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
471}
Bill Buzbee270c1d62009-08-13 16:58:07 -0700472
473static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
474{
475 return newLIR2(cUnit, THUMB_LDMIA, rBase, rMask);
476}
477
478static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
479{
480 return newLIR2(cUnit, THUMB_STMIA, rBase, rMask);
481}
482
483static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
484{
485 ArmOpCode opCode = THUMB_BKPT;
486 switch (op) {
487 case OP_UNCOND_BR:
488 opCode = THUMB_B_UNCOND;
489 break;
490 default:
491 assert(0);
492 }
493 return newLIR0(cUnit, opCode);
494}
495
496static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
497 int value2)
498{
499 ArmOpCode opCode = THUMB_BKPT;
500 switch (op) {
501 case OP_COND_BR:
502 opCode = THUMB_B_COND;
503 break;
504 default:
505 assert(0);
506 }
507 return newLIR2(cUnit, opCode, value1, value2);
508}
509
510static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
511{
512 ArmOpCode opCode = THUMB_BKPT;
513 switch (op) {
514 case OP_PUSH:
515 opCode = THUMB_PUSH;
516 break;
517 case OP_POP:
518 opCode = THUMB_POP;
519 break;
520 default:
521 assert(0);
522 }
523 return newLIR1(cUnit, opCode, value);
524}
525
526static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
527{
528 ArmOpCode opCode = THUMB_BKPT;
529 switch (op) {
530 case OP_BLX:
531 opCode = THUMB_BLX_R;
532 break;
533 default:
534 assert(0);
535 }
536 return newLIR1(cUnit, opCode, rDestSrc);
537}
538
539static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
540 int rSrc2)
541{
542 ArmLIR *res;
543 ArmOpCode opCode = THUMB_BKPT;
544 switch (op) {
545 case OP_ADC:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700546 opCode = THUMB_ADC_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700547 break;
548 case OP_AND:
549 opCode = THUMB_AND_RR;
550 break;
551 case OP_BIC:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700552 opCode = THUMB_BIC_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700553 break;
554 case OP_CMN:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700555 opCode = THUMB_CMN_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700556 break;
557 case OP_CMP:
558 opCode = THUMB_CMP_RR;
559 break;
560 case OP_XOR:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700561 opCode = THUMB_EOR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700562 break;
563 case OP_MOV:
564 if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
565 opCode = THUMB_MOV_RR;
566 else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
567 opCode = THUMB_MOV_RR_H2H;
568 else if (LOWREG(rDestSrc1))
569 opCode = THUMB_MOV_RR_H2L;
570 else
571 opCode = THUMB_MOV_RR_L2H;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700572 break;
573 case OP_MUL:
574 opCode = THUMB_MUL;
575 break;
576 case OP_MVN:
577 opCode = THUMB_MVN;
578 break;
579 case OP_NEG:
580 opCode = THUMB_NEG;
581 break;
582 case OP_OR:
583 opCode = THUMB_ORR;
584 break;
585 case OP_SBC:
586 opCode = THUMB_SBC;
587 break;
588 case OP_TST:
589 opCode = THUMB_TST;
590 break;
591 case OP_LSL:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700592 opCode = THUMB_LSL_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700593 break;
594 case OP_LSR:
Ben Cheng2275f9c2009-09-11 15:34:19 -0700595 opCode = THUMB_LSR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700596 break;
597 case OP_ASR:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700598 opCode = THUMB_ASR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700599 break;
600 case OP_ROR:
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700601 opCode = THUMB_ROR_RR;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700602 case OP_ADD:
603 case OP_SUB:
604 return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
605 case OP_2BYTE:
606 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 24, rNone);
607 opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 24, rNone);
608 return res;
609 case OP_2SHORT:
610 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
611 opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 16, rNone);
612 return res;
613 case OP_2CHAR:
614 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
615 opRegRegImm(cUnit, OP_LSR, rDestSrc1, rDestSrc1, 16, rNone);
616 return res;
617 default:
618 assert(0);
619 break;
620 }
621 return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
622}
623
624static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
625 int value, int rScratch)
626{
627 ArmLIR *res;
628 bool neg = (value < 0);
629 int absValue = (neg) ? -value : value;
630 bool shortForm = (absValue & 0xff) == absValue;
631 ArmOpCode opCode = THUMB_BKPT;
632 switch (op) {
633 case OP_ADD:
634 if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
635 assert((value & 0x3) == 0);
636 return newLIR1(cUnit, THUMB_ADD_SPI7, value >> 2);
637 } else if (shortForm) {
638 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
639 } else
640 opCode = THUMB_ADD_RRR;
641 break;
642 case OP_SUB:
643 if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
644 assert((value & 0x3) == 0);
645 return newLIR1(cUnit, THUMB_SUB_SPI7, value >> 2);
646 } else if (shortForm) {
647 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
648 } else
649 opCode = THUMB_SUB_RRR;
650 break;
651 case OP_CMP:
652 if (LOWREG(rDestSrc1) && shortForm)
653 opCode = (shortForm) ? THUMB_CMP_RI8 : THUMB_CMP_RR;
654 else if (LOWREG(rDestSrc1))
655 opCode = THUMB_CMP_RR;
656 else {
657 shortForm = false;
658 opCode = THUMB_CMP_HL;
659 }
660 break;
661 default:
662 assert(0);
663 break;
664 }
665 if (shortForm)
666 res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
667 else {
668 assert(rScratch != rNone);
669 res = loadConstant(cUnit, rScratch, value);
670 newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
671 }
672 return res;
673}
674
675static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
676 int rSrc1, int rSrc2)
677{
678 ArmOpCode opCode = THUMB_BKPT;
679 switch (op) {
680 case OP_ADD:
681 opCode = THUMB_ADD_RRR;
682 break;
683 case OP_SUB:
684 opCode = THUMB_SUB_RRR;
685 break;
686 default:
687 assert(0);
688 break;
689 }
690 return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
691}
692
693static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
694 int rSrc1, int value, int rScratch)
695{
696 ArmLIR *res;
697 bool neg = (value < 0);
698 int absValue = (neg) ? -value : value;
699 ArmOpCode opCode = THUMB_BKPT;
700 bool shortForm = (absValue & 0x7) == absValue;
701 switch(op) {
702 case OP_ADD:
703 if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
704 assert((value & 0x3) == 0);
705 shortForm = true;
706 opCode = THUMB_ADD_SP_REL;
707 value >>= 2;
708 } else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
709 assert((value & 0x3) == 0);
710 shortForm = true;
711 opCode = THUMB_ADD_PC_REL;
712 value >>= 2;
713 } else if (shortForm) {
714 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
715 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
716 /* Two shots - 1st handle the 7 */
717 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
718 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
719 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
720 newLIR2(cUnit, opCode, rDest, absValue - 7);
721 return res;
722 } else
723 opCode = THUMB_ADD_RRR;
724 break;
725
726 case OP_SUB:
727 if (shortForm) {
728 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
729 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
730 /* Two shots - 1st handle the 7 */
731 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
732 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
733 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
734 newLIR2(cUnit, opCode, rDest, absValue - 7);
735 return res;
736 } else
737 opCode = THUMB_SUB_RRR;
738 break;
739 case OP_LSL:
740 shortForm = (!neg && value <= 31);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700741 opCode = THUMB_LSL_RRI5;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700742 break;
743 case OP_LSR:
744 shortForm = (!neg && value <= 31);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700745 opCode = THUMB_LSR_RRI5;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700746 break;
747 case OP_ASR:
748 shortForm = (!neg && value <= 31);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700749 opCode = THUMB_ASR_RRI5;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700750 break;
751 case OP_MUL:
752 case OP_AND:
753 case OP_OR:
754 case OP_XOR:
755 if (rDest == rSrc1) {
756 res = loadConstant(cUnit, rScratch, value);
757 opRegReg(cUnit, op, rDest, rScratch);
758 } else {
759 res = loadConstant(cUnit, rDest, value);
760 opRegReg(cUnit, op, rDest, rSrc1);
761 }
762 return res;
763 default:
764 assert(0);
765 break;
766 }
767 if (shortForm)
768 res = newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
769 else {
770 if (rDest != rSrc1) {
771 res = loadConstant(cUnit, rDest, value);
772 newLIR3(cUnit, opCode, rDest, rSrc1, rDest);
773 } else {
774 assert(rScratch != rNone);
775 res = loadConstant(cUnit, rScratch, value);
776 newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
777 }
778 }
779 return res;
780}
781
Bill Buzbeea4a7f072009-08-27 13:58:09 -0700782static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
783 int vDest, int vSrc1, int vSrc2)
784{
785 loadValuePair(cUnit, vSrc1, r0, r1);
786 loadValuePair(cUnit, vSrc2, r2, r3);
787 genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
788 storeValue(cUnit, r0, vDest, r1);
789}
790
Bill Buzbee270c1d62009-08-13 16:58:07 -0700791static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
792{
793 DecodedInstruction *dInsn = &mir->dalvikInsn;
794 int offset = offsetof(InterpState, retval);
795 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
796 int reg1 = NEXT_REG(regObj);
797 loadValue(cUnit, dInsn->arg[0], regObj);
798 genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL);
799 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1);
800 storeWordDisp(cUnit, rGLUE, offset, reg1, regObj);
801 return false;
802}
803
804static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
805{
806 DecodedInstruction *dInsn = &mir->dalvikInsn;
807 int offset = offsetof(InterpState, retval);
808 int contents = offsetof(ArrayObject, contents);
809 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
810 int regIdx = NEXT_REG(regObj);
811 int regMax = NEXT_REG(regIdx);
812 int regOff = NEXT_REG(regMax);
813 loadValue(cUnit, dInsn->arg[0], regObj);
814 loadValue(cUnit, dInsn->arg[1], regIdx);
815 ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj,
816 mir->offset, NULL);
817 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax);
818 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff);
819 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj);
820 genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel);
821
822 newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents);
823 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff);
824 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx);
825 newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx);
826 storeWordDisp(cUnit, rGLUE, offset, regMax, regObj);
827 return false;
828}
829
830static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
831{
832 int offset = offsetof(InterpState, retval);
833 DecodedInstruction *dInsn = &mir->dalvikInsn;
834 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
835 int sign = NEXT_REG(reg0);
836 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
837 loadValue(cUnit, dInsn->arg[0], reg0);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700838 newLIR3(cUnit, THUMB_ASR_RRI5, sign, reg0, 31);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700839 newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700840 newLIR2(cUnit, THUMB_EOR_RR, reg0, sign);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700841 storeWordDisp(cUnit, rGLUE, offset, reg0, sign);
842 return false;
843}
844
845static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
846{
847 int offset = offsetof(InterpState, retval);
848 DecodedInstruction *dInsn = &mir->dalvikInsn;
849 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
850 int signMask = NEXT_REG(reg0);
851 loadValue(cUnit, dInsn->arg[0], reg0);
852 loadConstant(cUnit, signMask, 0x7fffffff);
853 newLIR2(cUnit, THUMB_AND_RR, reg0, signMask);
854 storeWordDisp(cUnit, rGLUE, offset, reg0, signMask);
855 return false;
856}
857
858static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
859{
860 int offset = offsetof(InterpState, retval);
861 DecodedInstruction *dInsn = &mir->dalvikInsn;
862 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
863 int ophi = NEXT_REG(oplo);
864 int signMask = NEXT_REG(ophi);
865 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
866 loadConstant(cUnit, signMask, 0x7fffffff);
867 storeWordDisp(cUnit, rGLUE, offset, oplo, ophi);
868 newLIR2(cUnit, THUMB_AND_RR, ophi, signMask);
869 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, oplo);
870 return false;
871}
872
873 /* No select in thumb, so we need to branch. Thumb2 will do better */
874static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
875{
876 int offset = offsetof(InterpState, retval);
877 DecodedInstruction *dInsn = &mir->dalvikInsn;
878 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
879 int reg1 = NEXT_REG(reg0);
880 loadValue(cUnit, dInsn->arg[0], reg0);
881 loadValue(cUnit, dInsn->arg[1], reg1);
882 newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1);
883 ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2,
884 isMin ? ARM_COND_LT : ARM_COND_GT);
885 newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1);
886 ArmLIR *target =
887 newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2);
888 branch1->generic.target = (LIR *)target;
889 return false;
890}
891
892static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
893{
894 int offset = offsetof(InterpState, retval);
895 DecodedInstruction *dInsn = &mir->dalvikInsn;
896 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
897 int ophi = NEXT_REG(oplo);
898 int sign = NEXT_REG(ophi);
899 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
900 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700901 newLIR3(cUnit, THUMB_ASR_RRI5, sign, ophi, 31);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700902 newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700903 newLIR2(cUnit, THUMB_ADC_RR, ophi, sign);
904 newLIR2(cUnit, THUMB_EOR_RR, oplo, sign);
905 newLIR2(cUnit, THUMB_EOR_RR, ophi, sign);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700906 storeWordDisp(cUnit, rGLUE, offset, oplo, sign);
907 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, sign);
908 return false;
909}