blob: fb25a569fd77d45d56c20b0509d9f1e837e7c851 [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);
Bill Buzbee7ea0f642009-08-10 17:06:51 -070065ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
Bill Buzbee270c1d62009-08-13 16:58:07 -070066static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
67static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070068
Bill Buzbee270c1d62009-08-13 16:58:07 -070069static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op);
70static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value);
71static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
72 int value2);
73static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
74static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
75 int rSrc2);
76static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
77 int value, int rScratch);
78static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
79 int rSrc1, int value, int rScratch);
80static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
81 int rSrc1, int rSrc2);
82static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
83 int rIndex, int rDest, int scale, OpSize size);
Bill Buzbeea4a7f072009-08-27 13:58:09 -070084static void genCmpLong(CompilationUnit *cUnit, MIR *mir, int vDest, int vSrc1,
85 int vSrc2);
Bill Buzbee270c1d62009-08-13 16:58:07 -070086
87static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir);
88static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir);
89static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir);
90static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir);
91static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir);
92static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin);
93static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070094
95/*
96 * Support for register allocation
97 */
98
Bill Buzbee9bc3df32009-07-30 10:52:29 -070099/* get the next register in r0..r3 in a round-robin fashion */
100#define NEXT_REG(reg) ((reg + 1) & 3)
101/*
102 * The following are utility routines to help maintain the RegisterScoreboard
103 * state to facilitate register renaming.
104 */
105
106/* Reset the tracker to unknown state */
107static inline void resetRegisterScoreboard(CompilationUnit *cUnit)
108{
109 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
110
111 dvmClearAllBits(registerScoreboard->nullCheckedRegs);
112 registerScoreboard->liveDalvikReg = vNone;
113 registerScoreboard->nativeReg = vNone;
114 registerScoreboard->nativeRegHi = vNone;
115}
116
117/* Kill the corresponding bit in the null-checked register list */
118static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg)
119{
120 dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg);
121}
122
123/* The Dalvik register pair held in native registers have changed */
124static inline void updateLiveRegisterPair(CompilationUnit *cUnit,
125 int vReg, int mRegLo, int mRegHi)
126{
127 cUnit->registerScoreboard.liveDalvikReg = vReg;
128 cUnit->registerScoreboard.nativeReg = mRegLo;
129 cUnit->registerScoreboard.nativeRegHi = mRegHi;
130 cUnit->registerScoreboard.isWide = true;
131}
132
133/* The Dalvik register held in a native register has changed */
134static inline void updateLiveRegister(CompilationUnit *cUnit,
135 int vReg, int mReg)
136{
137 cUnit->registerScoreboard.liveDalvikReg = vReg;
138 cUnit->registerScoreboard.nativeReg = mReg;
139 cUnit->registerScoreboard.isWide = false;
140}
141
142/*
143 * Given a Dalvik register id vSrc, use a very simple algorithm to increase
144 * the lifetime of cached Dalvik value in a native register.
145 */
146static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc,
147 bool isWide)
148{
149 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
150
151 /* No live value - suggest to use r0 */
152 if (registerScoreboard->liveDalvikReg == vNone)
153 return r0;
154
155 /* Reuse the previously used native reg */
156 if (registerScoreboard->liveDalvikReg == vSrc) {
157 if (isWide != true) {
158 return registerScoreboard->nativeReg;
159 } else {
160 /* Return either r0 or r2 */
161 return (registerScoreboard->nativeReg + 1) & 2;
162 }
163 }
164
165 /* No reuse - choose the next one among r0..r3 in the round-robin fashion */
166 if (isWide) {
167 return (registerScoreboard->nativeReg + 2) & 2;
168 } else {
169 return (registerScoreboard->nativeReg + 1) & 3;
170 }
171
172}
173
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700174ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
175{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700176 ArmLIR* res;
177 ArmOpCode opCode;
178 res = dvmCompilerNew(sizeof(ArmLIR), true);
179 if (LOWREG(rDest) && LOWREG(rSrc))
180 opCode = THUMB_MOV_RR;
181 else if (!LOWREG(rDest) && !LOWREG(rSrc))
182 opCode = THUMB_MOV_RR_H2H;
183 else if (LOWREG(rDest))
184 opCode = THUMB_MOV_RR_H2L;
185 else
186 opCode = THUMB_MOV_RR_L2H;
187 rDest &= THUMB_REG_MASK;
188 rSrc &= THUMB_REG_MASK;
189
190 res->operands[0] = rDest & THUMB_REG_MASK;
191 res->operands[1] = rSrc & THUMB_REG_MASK;
192 res->opCode = opCode;
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700193 if (rDest == rSrc) {
194 res->isNop = true;
195 }
196 return res;
197}
198
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700199/*
200 * Load a immediate using a shortcut if possible; otherwise
201 * grab from the per-translation literal pool
202 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700203static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700204{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700205 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700206 /* See if the value can be constructed cheaply */
207 if ((value >= 0) && (value <= 255)) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700208 return newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700209 } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700210 res = newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700211 newLIR2(cUnit, THUMB_MVN, rDest, rDest);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700212 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700213 }
214 /* No shortcut - go ahead and use literal pool */
215 ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
216 if (dataTarget == NULL) {
217 dataTarget = addWordData(cUnit, value, false);
218 }
219 ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
220 loadPcRel->opCode = THUMB_LDR_PC_REL;
221 loadPcRel->generic.target = (LIR *) dataTarget;
222 loadPcRel->operands[0] = rDest;
Bill Buzbee270c1d62009-08-13 16:58:07 -0700223 res = loadPcRel;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700224 dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
225
226 /*
227 * To save space in the constant pool, we use the ADD_RRI8 instruction to
228 * add up to 255 to an existing constant value.
229 */
230 if (dataTarget->operands[0] != value) {
231 newLIR2(cUnit, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
232 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700233 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700234}
235
236/* Export the Dalvik PC assicated with an instruction to the StackSave area */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700237static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
238 int rAddr)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700239{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700240 ArmLIR *res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700241 int offset = offsetof(StackSaveArea, xtra.currentPc);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700242 res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700243 newLIR2(cUnit, THUMB_MOV_RR, rAddr, rFP);
244 newLIR2(cUnit, THUMB_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset);
Bill Buzbee270c1d62009-08-13 16:58:07 -0700245 storeWordDisp( cUnit, rAddr, 0, rDPC, -1);
246 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700247}
248
Bill Buzbee270c1d62009-08-13 16:58:07 -0700249/* Load value from base + scaled index. Note: index reg killed */
250static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
251 int rIndex, int rDest, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700252{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700253 ArmLIR *first = NULL;
254 ArmLIR *res;
255 ArmOpCode opCode = THUMB_BKPT;
256 if (scale)
257 first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
258 switch (size) {
259 case WORD:
260 opCode = THUMB_LDR_RRR;
261 break;
262 case UNSIGNED_HALF:
263 opCode = THUMB_LDRH_RRR;
264 break;
265 case SIGNED_HALF:
266 opCode = THUMB_LDRSH_RRR;
267 break;
268 case UNSIGNED_BYTE:
269 opCode = THUMB_LDRB_RRR;
270 break;
271 case SIGNED_BYTE:
272 opCode = THUMB_LDRSB_RRR;
273 break;
274 default:
275 assert(0);
276 }
277 res = newLIR3(cUnit, opCode, rDest, rBase, rIndex);
278 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700279}
280
Bill Buzbee270c1d62009-08-13 16:58:07 -0700281/* store value base base + scaled index. Note: index reg killed */
282static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
283 int rIndex, int rSrc, int scale, OpSize size)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700284{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700285 ArmLIR *first = NULL;
286 ArmLIR *res;
287 ArmOpCode opCode = THUMB_BKPT;
288 if (scale)
289 first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
290 switch (size) {
291 case WORD:
292 opCode = THUMB_STR_RRR;
293 break;
294 case UNSIGNED_HALF:
295 case SIGNED_HALF:
296 opCode = THUMB_STRH_RRR;
297 break;
298 case UNSIGNED_BYTE:
299 case SIGNED_BYTE:
300 opCode = THUMB_STRB_RRR;
301 break;
302 default:
303 assert(0);
304 }
305 res = newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
306 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700307}
308
309/*
Bill Buzbee270c1d62009-08-13 16:58:07 -0700310 * Load value from base + displacement. Optionally perform null check
311 * on base (which must have an associated vReg and MIR). If not
312 * performing null check, incoming MIR can be null. Note: base and
313 * dest must not be the same if there is any chance that the long
314 * form must be used.
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700315 */
Bill Buzbee270c1d62009-08-13 16:58:07 -0700316static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
317 int displacement, int rDest, OpSize size,
318 bool nullCheck, int vReg)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700319{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700320 ArmLIR *first = NULL;
321 ArmLIR *res;
322 ArmOpCode opCode = THUMB_BKPT;
323 bool shortForm = false;
324 int shortMax = 128;
325 switch (size) {
326 case WORD:
327 if (LOWREG(rDest) && (rBase == rpc) &&
328 (displacement <= 1020) && (displacement >= 0)) {
329 shortForm = true;
330 displacement >>= 2;
331 opCode = THUMB_LDR_PC_REL;
332 } else if (LOWREG(rDest) && (rBase == r13) &&
333 (displacement <= 1020) && (displacement >= 0)) {
334 shortForm = true;
335 displacement >>= 2;
336 opCode = THUMB_LDR_SP_REL;
337 } else if (displacement < 128 && displacement >= 0) {
338 assert((displacement & 0x3) == 0);
339 shortForm = true;
340 displacement >>= 2;
341 opCode = THUMB_LDR_RRI5;
342 } else {
343 opCode = THUMB_LDR_RRR;
344 }
345 break;
346 case UNSIGNED_HALF:
347 if (displacement < 64 && displacement >= 0) {
348 assert((displacement & 0x1) == 0);
349 shortForm = true;
350 displacement >>= 1;
351 opCode = THUMB_LDRH_RRI5;
352 } else {
353 opCode = THUMB_LDRH_RRR;
354 }
355 break;
356 case SIGNED_HALF:
357 opCode = THUMB_LDRSH_RRR;
358 break;
359 case UNSIGNED_BYTE:
360 if (displacement < 32 && displacement >= 0) {
361 shortForm = true;
362 opCode = THUMB_LDRB_RRI5;
363 } else {
364 opCode = THUMB_LDRB_RRR;
365 }
366 break;
367 case SIGNED_BYTE:
368 opCode = THUMB_LDRSB_RRR;
369 break;
370 default:
371 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700372 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700373 if (nullCheck)
374 first = genNullCheck(cUnit, vReg, rBase, mir->offset, NULL);
375 if (shortForm) {
376 res = newLIR3(cUnit, opCode, rDest, rBase, displacement);
377 } else {
378 assert(rBase != rDest);
379 res = loadConstant(cUnit, rDest, displacement);
380 newLIR3(cUnit, opCode, rDest, rBase, rDest);
381 }
382 return (first) ? first : res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700383}
384
Bill Buzbee270c1d62009-08-13 16:58:07 -0700385static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
386 int displacement, int rSrc, OpSize size,
387 int rScratch)
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700388{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700389 ArmLIR *res;
390 ArmOpCode opCode = THUMB_BKPT;
391 bool shortForm = false;
392 int shortMax = 128;
393 switch (size) {
394 case WORD:
395 if (displacement < 128 && displacement >= 0) {
396 assert((displacement & 0x3) == 0);
397 shortForm = true;
398 displacement >>= 2;
399 opCode = THUMB_STR_RRI5;
400 } else {
401 opCode = THUMB_STR_RRR;
402 }
403 break;
404 case UNSIGNED_HALF:
405 case SIGNED_HALF:
406 if (displacement < 64 && displacement >= 0) {
407 assert((displacement & 0x1) == 0);
408 shortForm = true;
409 displacement >>= 1;
410 opCode = THUMB_STRH_RRI5;
411 } else {
412 opCode = THUMB_STRH_RRR;
413 }
414 break;
415 case UNSIGNED_BYTE:
416 case SIGNED_BYTE:
417 if (displacement < 32 && displacement >= 0) {
418 shortForm = true;
419 opCode = THUMB_STRB_RRI5;
420 } else {
421 opCode = THUMB_STRB_RRR;
422 }
423 break;
424 default:
425 assert(0);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700426 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700427 if (shortForm) {
428 res = newLIR3(cUnit, opCode, rSrc, rBase, displacement);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700429 } else {
Bill Buzbee270c1d62009-08-13 16:58:07 -0700430 assert(rScratch != -1);
431 res = loadConstant(cUnit, rScratch, displacement);
432 newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700433 }
Bill Buzbee270c1d62009-08-13 16:58:07 -0700434 return res;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700435}
436
437/*
438 * Perform a "reg cmp imm" operation and jump to the PCR region if condition
439 * satisfies.
440 */
441static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
442 ArmConditionCode cond, int reg,
443 int checkValue, int dOffset,
444 ArmLIR *pcrLabel)
445{
Bill Buzbee270c1d62009-08-13 16:58:07 -0700446 assert((checkValue & 0xff) == checkValue);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700447 newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
448 ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
449 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
450}
Bill Buzbee270c1d62009-08-13 16:58:07 -0700451
452static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
453{
454 return newLIR2(cUnit, THUMB_LDMIA, rBase, rMask);
455}
456
457static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
458{
459 return newLIR2(cUnit, THUMB_STMIA, rBase, rMask);
460}
461
462static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
463{
464 ArmOpCode opCode = THUMB_BKPT;
465 switch (op) {
466 case OP_UNCOND_BR:
467 opCode = THUMB_B_UNCOND;
468 break;
469 default:
470 assert(0);
471 }
472 return newLIR0(cUnit, opCode);
473}
474
475static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
476 int value2)
477{
478 ArmOpCode opCode = THUMB_BKPT;
479 switch (op) {
480 case OP_COND_BR:
481 opCode = THUMB_B_COND;
482 break;
483 default:
484 assert(0);
485 }
486 return newLIR2(cUnit, opCode, value1, value2);
487}
488
489static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
490{
491 ArmOpCode opCode = THUMB_BKPT;
492 switch (op) {
493 case OP_PUSH:
494 opCode = THUMB_PUSH;
495 break;
496 case OP_POP:
497 opCode = THUMB_POP;
498 break;
499 default:
500 assert(0);
501 }
502 return newLIR1(cUnit, opCode, value);
503}
504
505static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
506{
507 ArmOpCode opCode = THUMB_BKPT;
508 switch (op) {
509 case OP_BLX:
510 opCode = THUMB_BLX_R;
511 break;
512 default:
513 assert(0);
514 }
515 return newLIR1(cUnit, opCode, rDestSrc);
516}
517
518static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
519 int rSrc2)
520{
521 ArmLIR *res;
522 ArmOpCode opCode = THUMB_BKPT;
523 switch (op) {
524 case OP_ADC:
525 opCode = THUMB_ADC;
526 break;
527 case OP_AND:
528 opCode = THUMB_AND_RR;
529 break;
530 case OP_BIC:
531 opCode = THUMB_BIC;
532 break;
533 case OP_CMN:
534 opCode = THUMB_CMN;
535 break;
536 case OP_CMP:
537 opCode = THUMB_CMP_RR;
538 break;
539 case OP_XOR:
540 opCode = THUMB_EOR;
541 break;
542 case OP_MOV:
543 if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
544 opCode = THUMB_MOV_RR;
545 else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
546 opCode = THUMB_MOV_RR_H2H;
547 else if (LOWREG(rDestSrc1))
548 opCode = THUMB_MOV_RR_H2L;
549 else
550 opCode = THUMB_MOV_RR_L2H;
551 rDestSrc1 &= THUMB_REG_MASK;
552 rSrc2 &= THUMB_REG_MASK;
553 break;
554 case OP_MUL:
555 opCode = THUMB_MUL;
556 break;
557 case OP_MVN:
558 opCode = THUMB_MVN;
559 break;
560 case OP_NEG:
561 opCode = THUMB_NEG;
562 break;
563 case OP_OR:
564 opCode = THUMB_ORR;
565 break;
566 case OP_SBC:
567 opCode = THUMB_SBC;
568 break;
569 case OP_TST:
570 opCode = THUMB_TST;
571 break;
572 case OP_LSL:
573 opCode = THUMB_LSLV;
574 break;
575 case OP_LSR:
576 opCode = THUMB_LSRV;
577 break;
578 case OP_ASR:
579 opCode = THUMB_ASRV;
580 break;
581 case OP_ROR:
582 opCode = THUMB_RORV;
583 case OP_ADD:
584 case OP_SUB:
585 return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
586 case OP_2BYTE:
587 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 24, rNone);
588 opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 24, rNone);
589 return res;
590 case OP_2SHORT:
591 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
592 opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 16, rNone);
593 return res;
594 case OP_2CHAR:
595 res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
596 opRegRegImm(cUnit, OP_LSR, rDestSrc1, rDestSrc1, 16, rNone);
597 return res;
598 default:
599 assert(0);
600 break;
601 }
602 return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
603}
604
605static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
606 int value, int rScratch)
607{
608 ArmLIR *res;
609 bool neg = (value < 0);
610 int absValue = (neg) ? -value : value;
611 bool shortForm = (absValue & 0xff) == absValue;
612 ArmOpCode opCode = THUMB_BKPT;
613 switch (op) {
614 case OP_ADD:
615 if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
616 assert((value & 0x3) == 0);
617 return newLIR1(cUnit, THUMB_ADD_SPI7, value >> 2);
618 } else if (shortForm) {
619 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
620 } else
621 opCode = THUMB_ADD_RRR;
622 break;
623 case OP_SUB:
624 if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
625 assert((value & 0x3) == 0);
626 return newLIR1(cUnit, THUMB_SUB_SPI7, value >> 2);
627 } else if (shortForm) {
628 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
629 } else
630 opCode = THUMB_SUB_RRR;
631 break;
632 case OP_CMP:
633 if (LOWREG(rDestSrc1) && shortForm)
634 opCode = (shortForm) ? THUMB_CMP_RI8 : THUMB_CMP_RR;
635 else if (LOWREG(rDestSrc1))
636 opCode = THUMB_CMP_RR;
637 else {
638 shortForm = false;
639 opCode = THUMB_CMP_HL;
640 }
641 break;
642 default:
643 assert(0);
644 break;
645 }
646 if (shortForm)
647 res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
648 else {
649 assert(rScratch != rNone);
650 res = loadConstant(cUnit, rScratch, value);
651 newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
652 }
653 return res;
654}
655
656static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
657 int rSrc1, int rSrc2)
658{
659 ArmOpCode opCode = THUMB_BKPT;
660 switch (op) {
661 case OP_ADD:
662 opCode = THUMB_ADD_RRR;
663 break;
664 case OP_SUB:
665 opCode = THUMB_SUB_RRR;
666 break;
667 default:
668 assert(0);
669 break;
670 }
671 return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
672}
673
674static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
675 int rSrc1, int value, int rScratch)
676{
677 ArmLIR *res;
678 bool neg = (value < 0);
679 int absValue = (neg) ? -value : value;
680 ArmOpCode opCode = THUMB_BKPT;
681 bool shortForm = (absValue & 0x7) == absValue;
682 switch(op) {
683 case OP_ADD:
684 if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
685 assert((value & 0x3) == 0);
686 shortForm = true;
687 opCode = THUMB_ADD_SP_REL;
688 value >>= 2;
689 } else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
690 assert((value & 0x3) == 0);
691 shortForm = true;
692 opCode = THUMB_ADD_PC_REL;
693 value >>= 2;
694 } else if (shortForm) {
695 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
696 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
697 /* Two shots - 1st handle the 7 */
698 opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
699 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
700 opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
701 newLIR2(cUnit, opCode, rDest, absValue - 7);
702 return res;
703 } else
704 opCode = THUMB_ADD_RRR;
705 break;
706
707 case OP_SUB:
708 if (shortForm) {
709 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
710 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
711 /* Two shots - 1st handle the 7 */
712 opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
713 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
714 opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
715 newLIR2(cUnit, opCode, rDest, absValue - 7);
716 return res;
717 } else
718 opCode = THUMB_SUB_RRR;
719 break;
720 case OP_LSL:
721 shortForm = (!neg && value <= 31);
722 opCode = THUMB_LSL;
723 break;
724 case OP_LSR:
725 shortForm = (!neg && value <= 31);
726 opCode = THUMB_LSR;
727 break;
728 case OP_ASR:
729 shortForm = (!neg && value <= 31);
730 opCode = THUMB_ASR;
731 break;
732 case OP_MUL:
733 case OP_AND:
734 case OP_OR:
735 case OP_XOR:
736 if (rDest == rSrc1) {
737 res = loadConstant(cUnit, rScratch, value);
738 opRegReg(cUnit, op, rDest, rScratch);
739 } else {
740 res = loadConstant(cUnit, rDest, value);
741 opRegReg(cUnit, op, rDest, rSrc1);
742 }
743 return res;
744 default:
745 assert(0);
746 break;
747 }
748 if (shortForm)
749 res = newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
750 else {
751 if (rDest != rSrc1) {
752 res = loadConstant(cUnit, rDest, value);
753 newLIR3(cUnit, opCode, rDest, rSrc1, rDest);
754 } else {
755 assert(rScratch != rNone);
756 res = loadConstant(cUnit, rScratch, value);
757 newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
758 }
759 }
760 return res;
761}
762
Bill Buzbeea4a7f072009-08-27 13:58:09 -0700763static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
764 int vDest, int vSrc1, int vSrc2)
765{
766 loadValuePair(cUnit, vSrc1, r0, r1);
767 loadValuePair(cUnit, vSrc2, r2, r3);
768 genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
769 storeValue(cUnit, r0, vDest, r1);
770}
771
Bill Buzbee270c1d62009-08-13 16:58:07 -0700772static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
773{
774 DecodedInstruction *dInsn = &mir->dalvikInsn;
775 int offset = offsetof(InterpState, retval);
776 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
777 int reg1 = NEXT_REG(regObj);
778 loadValue(cUnit, dInsn->arg[0], regObj);
779 genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL);
780 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1);
781 storeWordDisp(cUnit, rGLUE, offset, reg1, regObj);
782 return false;
783}
784
785static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
786{
787 DecodedInstruction *dInsn = &mir->dalvikInsn;
788 int offset = offsetof(InterpState, retval);
789 int contents = offsetof(ArrayObject, contents);
790 int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
791 int regIdx = NEXT_REG(regObj);
792 int regMax = NEXT_REG(regIdx);
793 int regOff = NEXT_REG(regMax);
794 loadValue(cUnit, dInsn->arg[0], regObj);
795 loadValue(cUnit, dInsn->arg[1], regIdx);
796 ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj,
797 mir->offset, NULL);
798 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax);
799 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff);
800 loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj);
801 genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel);
802
803 newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents);
804 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff);
805 newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx);
806 newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx);
807 storeWordDisp(cUnit, rGLUE, offset, regMax, regObj);
808 return false;
809}
810
811static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
812{
813 int offset = offsetof(InterpState, retval);
814 DecodedInstruction *dInsn = &mir->dalvikInsn;
815 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
816 int sign = NEXT_REG(reg0);
817 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
818 loadValue(cUnit, dInsn->arg[0], reg0);
819 newLIR3(cUnit, THUMB_ASR, sign, reg0, 31);
820 newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign);
821 newLIR2(cUnit, THUMB_EOR, reg0, sign);
822 storeWordDisp(cUnit, rGLUE, offset, reg0, sign);
823 return false;
824}
825
826static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
827{
828 int offset = offsetof(InterpState, retval);
829 DecodedInstruction *dInsn = &mir->dalvikInsn;
830 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
831 int signMask = NEXT_REG(reg0);
832 loadValue(cUnit, dInsn->arg[0], reg0);
833 loadConstant(cUnit, signMask, 0x7fffffff);
834 newLIR2(cUnit, THUMB_AND_RR, reg0, signMask);
835 storeWordDisp(cUnit, rGLUE, offset, reg0, signMask);
836 return false;
837}
838
839static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
840{
841 int offset = offsetof(InterpState, retval);
842 DecodedInstruction *dInsn = &mir->dalvikInsn;
843 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
844 int ophi = NEXT_REG(oplo);
845 int signMask = NEXT_REG(ophi);
846 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
847 loadConstant(cUnit, signMask, 0x7fffffff);
848 storeWordDisp(cUnit, rGLUE, offset, oplo, ophi);
849 newLIR2(cUnit, THUMB_AND_RR, ophi, signMask);
850 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, oplo);
851 return false;
852}
853
854 /* No select in thumb, so we need to branch. Thumb2 will do better */
855static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
856{
857 int offset = offsetof(InterpState, retval);
858 DecodedInstruction *dInsn = &mir->dalvikInsn;
859 int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
860 int reg1 = NEXT_REG(reg0);
861 loadValue(cUnit, dInsn->arg[0], reg0);
862 loadValue(cUnit, dInsn->arg[1], reg1);
863 newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1);
864 ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2,
865 isMin ? ARM_COND_LT : ARM_COND_GT);
866 newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1);
867 ArmLIR *target =
868 newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2);
869 branch1->generic.target = (LIR *)target;
870 return false;
871}
872
873static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
874{
875 int offset = offsetof(InterpState, retval);
876 DecodedInstruction *dInsn = &mir->dalvikInsn;
877 int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
878 int ophi = NEXT_REG(oplo);
879 int sign = NEXT_REG(ophi);
880 /* abs(x) = y<=x>>31, (x+y)^y. Shorter in ARM/THUMB2, no skip in THUMB */
881 loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
882 newLIR3(cUnit, THUMB_ASR, sign, ophi, 31);
883 newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign);
884 newLIR2(cUnit, THUMB_ADC, ophi, sign);
885 newLIR2(cUnit, THUMB_EOR, oplo, sign);
886 newLIR2(cUnit, THUMB_EOR, ophi, sign);
887 storeWordDisp(cUnit, rGLUE, offset, oplo, sign);
888 storeWordDisp(cUnit, rGLUE, offset + 4, ophi, sign);
889 return false;
890}