1 /*
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
19 * includes by:and support common to all supported
20 *
21 * Codegen-$(TARGET_ARCH_VARIANT).c
22 *
23 */
24
25 #include "Codegen.h"
26
27 /* Routines which must be supplied here */
28 static void loadConstant(CompilationUnit *cUnit, int rDest, int value);
29 static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr);
30 static void genConditionalBranch(CompilationUnit *cUnit,
31 ArmConditionCode cond,
32 ArmLIR *target);
33 static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target);
34 static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
35 int rDestHi);
36 static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
37 int vDest, int rScratch);
38 static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int vDest);
39 static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest);
40 static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
41 int rDest);
42 static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
43 int rScratch);
44 static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
45 ArmConditionCode cond, int reg,
46 int checkValue, int dOffset,
47 ArmLIR *pcrLabel);
48 ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
49
50 /*****************************************************************************/
51
52 /*
53 * Support for register allocation
54 */
55
56 /* non-existent register */
57 #define vNone (-1)
58
59 /* get the next register in r0..r3 in a round-robin fashion */
60 #define NEXT_REG(reg) ((reg + 1) & 3)
61 /*
62 * The following are utility routines to help maintain the RegisterScoreboard
63 * state to facilitate register renaming.
64 */
65
66 /* Reset the tracker to unknown state */
resetRegisterScoreboard(CompilationUnit * cUnit)67 static inline void resetRegisterScoreboard(CompilationUnit *cUnit)
68 {
69 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
70
71 dvmClearAllBits(registerScoreboard->nullCheckedRegs);
72 registerScoreboard->liveDalvikReg = vNone;
73 registerScoreboard->nativeReg = vNone;
74 registerScoreboard->nativeRegHi = vNone;
75 }
76
77 /* Kill the corresponding bit in the null-checked register list */
killNullCheckedRegister(CompilationUnit * cUnit,int vReg)78 static inline void killNullCheckedRegister(CompilationUnit *cUnit, int vReg)
79 {
80 dvmClearBit(cUnit->registerScoreboard.nullCheckedRegs, vReg);
81 }
82
83 /* The Dalvik register pair held in native registers have changed */
updateLiveRegisterPair(CompilationUnit * cUnit,int vReg,int mRegLo,int mRegHi)84 static inline void updateLiveRegisterPair(CompilationUnit *cUnit,
85 int vReg, int mRegLo, int mRegHi)
86 {
87 cUnit->registerScoreboard.liveDalvikReg = vReg;
88 cUnit->registerScoreboard.nativeReg = mRegLo;
89 cUnit->registerScoreboard.nativeRegHi = mRegHi;
90 cUnit->registerScoreboard.isWide = true;
91 }
92
93 /* The Dalvik register held in a native register has changed */
updateLiveRegister(CompilationUnit * cUnit,int vReg,int mReg)94 static inline void updateLiveRegister(CompilationUnit *cUnit,
95 int vReg, int mReg)
96 {
97 cUnit->registerScoreboard.liveDalvikReg = vReg;
98 cUnit->registerScoreboard.nativeReg = mReg;
99 cUnit->registerScoreboard.isWide = false;
100 }
101
102 /*
103 * Given a Dalvik register id vSrc, use a very simple algorithm to increase
104 * the lifetime of cached Dalvik value in a native register.
105 */
selectFirstRegister(CompilationUnit * cUnit,int vSrc,bool isWide)106 static inline int selectFirstRegister(CompilationUnit *cUnit, int vSrc,
107 bool isWide)
108 {
109 RegisterScoreboard *registerScoreboard = &cUnit->registerScoreboard;
110
111 /* No live value - suggest to use r0 */
112 if (registerScoreboard->liveDalvikReg == vNone)
113 return r0;
114
115 /* Reuse the previously used native reg */
116 if (registerScoreboard->liveDalvikReg == vSrc) {
117 if (isWide != true) {
118 return registerScoreboard->nativeReg;
119 } else {
120 /* Return either r0 or r2 */
121 return (registerScoreboard->nativeReg + 1) & 2;
122 }
123 }
124
125 /* No reuse - choose the next one among r0..r3 in the round-robin fashion */
126 if (isWide) {
127 return (registerScoreboard->nativeReg + 2) & 2;
128 } else {
129 return (registerScoreboard->nativeReg + 1) & 3;
130 }
131
132 }
133
134 /*****************************************************************************/
135
dvmCompilerRegCopy(CompilationUnit * cUnit,int rDest,int rSrc)136 ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
137 {
138 ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
139 res->operands[0] = rDest;
140 res->operands[1] = rSrc;
141 if (rDest == rSrc) {
142 res->isNop = true;
143 } else {
144 if (LOWREG(rDest) && LOWREG(rSrc)) {
145 res->opCode = THUMB_MOV_RR;
146 } else if (FPREG(rDest) && FPREG(rSrc)) {
147 if (DOUBLEREG(rDest)) {
148 assert(DOUBLEREG(rSrc));
149 res->opCode = THUMB2_VMOVD;
150 } else {
151 assert(SINGLEREG(rSrc));
152 res->opCode = THUMB2_VMOVS;
153 }
154 } else {
155 // TODO: support copy between FP and gen regs.
156 assert(!FPREG(rDest));
157 assert(!FPREG(rSrc));
158 res->opCode = THUMB2_MOV_RR;
159 }
160 }
161 return res;
162 }
163
leadingZeros(u4 val)164 static int leadingZeros(u4 val)
165 {
166 u4 alt;
167 int n;
168 int count;
169
170 count = 16;
171 n = 32;
172 do {
173 alt = val >> count;
174 if (alt != 0) {
175 n = n - count;
176 val = alt;
177 }
178 count >>= 1;
179 } while (count);
180 return n - val;
181 }
182
183 /*
184 * Determine whether value can be encoded as a Thumb modified
185 * immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
186 */
modifiedImmediate(u4 value)187 static int modifiedImmediate(u4 value)
188 {
189 int zLeading;
190 int zTrailing;
191 u4 b0 = value & 0xff;
192
193 /* Note: case of value==0 must use 0:000:0:0000000 encoding */
194 if (value <= 0xFF)
195 return b0; // 0:000:a:bcdefgh
196 if (value == ((b0 << 16) | b0))
197 return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
198 if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
199 return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
200 b0 = (value >> 8) & 0xff;
201 if (value == ((b0 << 24) | (b0 << 8)))
202 return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
203 /* Can we do it with rotation? */
204 zLeading = leadingZeros(value);
205 zTrailing = 32 - leadingZeros(~value & (value - 1));
206 /* A run of eight or fewer active bits? */
207 if ((zLeading + zTrailing) < 24)
208 return -1; /* No - bail */
209 /* left-justify the constant, discarding msb (known to be 1) */
210 value <<= zLeading + 1;
211 /* Create bcdefgh */
212 value >>= 25;
213 /* Put it all together */
214 return value | ((0x8 + zLeading) << 7); /* [01000..11111]:bcdefgh */
215 }
216
217 /*
218 * Load a immediate using a shortcut if possible; otherwise
219 * grab from the per-translation literal pool
220 */
loadConstant(CompilationUnit * cUnit,int rDest,int value)221 static void loadConstant(CompilationUnit *cUnit, int rDest, int value)
222 {
223 int modImm;
224 /* See if the value can be constructed cheaply */
225 if ((value & 0xff) == value) {
226 newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
227 return;
228 } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
229 newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
230 newLIR2(cUnit, THUMB_MVN, rDest, rDest);
231 return;
232 }
233 /* Check Modified immediate special cases */
234 modImm = modifiedImmediate(value);
235 if (modImm >= 0) {
236 newLIR2(cUnit, THUMB2_MOV_IMM_SHIFT, rDest, modImm);
237 return;
238 }
239 /* 16-bit immediate? */
240 if ((value & 0xffff) == value) {
241 newLIR2(cUnit, THUMB2_MOV_IMM16, rDest, value);
242 return;
243 }
244 /* No shortcut - go ahead and use literal pool */
245 ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
246 if (dataTarget == NULL) {
247 dataTarget = addWordData(cUnit, value, false);
248 }
249 ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
250 loadPcRel->opCode = THUMB_LDR_PC_REL;
251 loadPcRel->generic.target = (LIR *) dataTarget;
252 loadPcRel->operands[0] = rDest;
253 dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
254
255 /*
256 * To save space in the constant pool, we use the ADD_RRI8 instruction to
257 * add up to 255 to an existing constant value.
258 */
259 if (dataTarget->operands[0] != value) {
260 newLIR2(cUnit, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
261 }
262 }
263
264 /* Export the Dalvik PC assicated with an instruction to the StackSave area */
genExportPC(CompilationUnit * cUnit,MIR * mir,int rDPC,int rAddr)265 static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr)
266 {
267 int offset = offsetof(StackSaveArea, xtra.currentPc);
268 loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
269 newLIR3(cUnit, THUMB2_STR_RRI8_PREDEC, rDPC, rFP,
270 sizeof(StackSaveArea) - offset);
271 }
272
273 /* Generate conditional branch instructions */
genConditionalBranch(CompilationUnit * cUnit,ArmConditionCode cond,ArmLIR * target)274 static void genConditionalBranch(CompilationUnit *cUnit,
275 ArmConditionCode cond,
276 ArmLIR *target)
277 {
278 ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
279 branch->generic.target = (LIR *) target;
280 }
281
282 /* Generate unconditional branch instructions */
genUnconditionalBranch(CompilationUnit * cUnit,ArmLIR * target)283 static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target)
284 {
285 ArmLIR *branch = newLIR0(cUnit, THUMB_B_UNCOND);
286 branch->generic.target = (LIR *) target;
287 return branch;
288 }
289
290 /*
291 * Load a pair of values of rFP[src..src+1] and store them into rDestLo and
292 * rDestHi
293 */
loadValuePair(CompilationUnit * cUnit,int vSrc,int rDestLo,int rDestHi)294 static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
295 int rDestHi)
296 {
297 bool allLowRegs = (LOWREG(rDestLo) && LOWREG(rDestHi));
298
299 /* Use reg + imm5*4 to load the values if possible */
300 if (allLowRegs && vSrc <= 30) {
301 newLIR3(cUnit, THUMB_LDR_RRI5, rDestLo, rFP, vSrc);
302 newLIR3(cUnit, THUMB_LDR_RRI5, rDestHi, rFP, vSrc+1);
303 } else {
304 assert(rDestLo < rDestHi);
305 loadValueAddress(cUnit, vSrc, rDestLo);
306 if (allLowRegs) {
307 newLIR2(cUnit, THUMB_LDMIA, rDestLo, (1<<rDestLo) | (1<<(rDestHi)));
308 } else {
309 assert(0); // Unimp - need Thumb2 ldmia
310 }
311 }
312 }
313
314 /*
315 * Store a pair of values of rSrc and rSrc+1 and store them into vDest and
316 * vDest+1
317 */
storeValuePair(CompilationUnit * cUnit,int rSrcLo,int rSrcHi,int vDest,int rScratch)318 static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
319 int vDest, int rScratch)
320 {
321 bool allLowRegs = (LOWREG(rSrcLo) && LOWREG(rSrcHi));
322 killNullCheckedRegister(cUnit, vDest);
323 killNullCheckedRegister(cUnit, vDest+1);
324 updateLiveRegisterPair(cUnit, vDest, rSrcLo, rSrcHi);
325
326 /* Use reg + imm5*4 to store the values if possible */
327 if (allLowRegs && vDest <= 30) {
328 newLIR3(cUnit, THUMB_STR_RRI5, rSrcLo, rFP, vDest);
329 newLIR3(cUnit, THUMB_STR_RRI5, rSrcHi, rFP, vDest+1);
330 } else {
331 assert(rSrcLo < rSrcHi);
332 loadValueAddress(cUnit, vDest, rScratch);
333 if (allLowRegs) {
334 newLIR2(cUnit, THUMB_STMIA, rScratch,
335 (1<<rSrcLo) | (1 << (rSrcHi)));
336 } else {
337 assert(0); // Unimp - need Thumb2 stmia
338 }
339 }
340 }
341
addRegisterRegister(CompilationUnit * cUnit,int rDest,int rSrc1,int rSrc2)342 static void addRegisterRegister(CompilationUnit *cUnit, int rDest,
343 int rSrc1, int rSrc2)
344 {
345 if (!LOWREG(rDest) || !LOWREG(rSrc1) || !LOWREG(rSrc2)) {
346 assert(0); // Unimp
347 //newLIR3(cUnit, THUMB2_ADD_RRR, rDest, rFP, rDest);
348 } else {
349 newLIR3(cUnit, THUMB_ADD_RRR, rDest, rFP, rDest);
350 }
351 }
352
353 /* Add in immediate to a register. */
addRegisterImmediate(CompilationUnit * cUnit,int rDest,int rSrc,int value)354 static void addRegisterImmediate(CompilationUnit *cUnit, int rDest, int rSrc,
355 int value)
356 {
357 // TODO: check for modified immediate form
358 if (LOWREG(rDest) && LOWREG(rSrc) && (value <= 7)) {
359 newLIR3(cUnit, THUMB_ADD_RRI3, rDest, rSrc, value);
360 } else if (LOWREG(rDest) && (rDest == rSrc) && ((value & 0xff) == 0xff)) {
361 newLIR2(cUnit, THUMB_ADD_RI8, rDest, value);
362 } else if (value <= 4095) {
363 newLIR3(cUnit, THUMB2_ADD_RRI12, rDest, rSrc, value);
364 } else {
365 loadConstant(cUnit, rDest, value);
366 addRegisterRegister(cUnit, rDest, rDest, rFP);
367 }
368 }
369
370 /* Load the address of a Dalvik register on the frame */
loadValueAddress(CompilationUnit * cUnit,int vSrc,int rDest)371 static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest)
372 {
373 addRegisterImmediate(cUnit, rDest, rFP, vSrc*4);
374 }
375
376 /*
377 * FIXME: We need a general register temp for all of these coprocessor
378 * operations in case we can't reach in 1 shot. Might just want to
379 * designate a hot temp that all codegen routines could use in their
380 * scope. Alternately, callers will need to allocate a temp and
381 * pass it in to each of these.
382 */
383
384 /* Load a float from a Dalvik register */
loadFloat(CompilationUnit * cUnit,int vSrc,int rDest)385 static void loadFloat(CompilationUnit *cUnit, int vSrc, int rDest)
386 {
387 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
388 assert(SINGLEREG(rDest));
389 newLIR3(cUnit, THUMB2_VLDRS, rDest, rFP, vSrc);
390 }
391
392 /* Store a float to a Dalvik register */
storeFloat(CompilationUnit * cUnit,int rSrc,int vDest,int rScratch)393 static void storeFloat(CompilationUnit *cUnit, int rSrc, int vDest,
394 int rScratch)
395 {
396 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
397 assert(SINGLEREG(rSrc));
398 newLIR3(cUnit, THUMB2_VSTRS, rSrc, rFP, vDest);
399 }
400
401 /* Load a double from a Dalvik register */
loadDouble(CompilationUnit * cUnit,int vSrc,int rDest)402 static void loadDouble(CompilationUnit *cUnit, int vSrc, int rDest)
403 {
404 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
405 assert(DOUBLEREG(rDest));
406 newLIR3(cUnit, THUMB2_VLDRD, rDest, rFP, vSrc);
407 }
408
409 /* Store a double to a Dalvik register */
storeDouble(CompilationUnit * cUnit,int rSrc,int vDest,int rScratch)410 static void storeDouble(CompilationUnit *cUnit, int rSrc, int vDest,
411 int rScratch)
412 {
413 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
414 assert(DOUBLEREG(rSrc));
415 newLIR3(cUnit, THUMB2_VSTRD, rSrc, rFP, vDest);
416 }
417
418
419 /* Load a single value from rFP[src] and store them into rDest */
loadValue(CompilationUnit * cUnit,int vSrc,int rDest)420 static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest)
421 {
422 loadWordDisp(cUnit, rFP, vSrc * 4, rDest);
423 }
424
425 /* Load a word at base + displacement. Displacement must be word multiple */
loadWordDisp(CompilationUnit * cUnit,int rBase,int displacement,int rDest)426 static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
427 int rDest)
428 {
429 bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
430 assert((displacement & 0x3) == 0);
431 /* Can it fit in a RRI5? */
432 if (allLowRegs && displacement < 128) {
433 newLIR3(cUnit, THUMB_LDR_RRI5, rDest, rBase, displacement >> 2);
434 } else if (displacement < 4092) {
435 newLIR3(cUnit, THUMB2_LDR_RRI12, rDest, rFP, displacement);
436 } else {
437 loadConstant(cUnit, rDest, displacement);
438 if (allLowRegs) {
439 newLIR3(cUnit, THUMB_LDR_RRR, rDest, rBase, rDest);
440 } else {
441 assert(0); // Unimp - need Thumb2 ldr_rrr
442 }
443 }
444 }
445
446 /* Store a value from rSrc to vDest */
storeValue(CompilationUnit * cUnit,int rSrc,int vDest,int rScratch)447 static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
448 int rScratch)
449 {
450 killNullCheckedRegister(cUnit, vDest);
451 updateLiveRegister(cUnit, vDest, rSrc);
452
453 /* Use reg + imm5*4 to store the value if possible */
454 if (LOWREG(rSrc) && vDest <= 31) {
455 newLIR3(cUnit, THUMB_STR_RRI5, rSrc, rFP, vDest);
456 } else if (vDest <= 1023) {
457 newLIR3(cUnit, THUMB2_STR_RRI12, rSrc, rFP, vDest*4);
458 } else {
459 loadConstant(cUnit, rScratch, vDest*4);
460 if (LOWREG(rSrc)) {
461 newLIR3(cUnit, THUMB_STR_RRR, rSrc, rFP, rScratch);
462 } else {
463 assert(0); // Unimp: Need generic str_rrr routine
464 }
465 }
466 }
467
468 /*
469 * Perform a "reg cmp imm" operation and jump to the PCR region if condition
470 * satisfies.
471 */
genRegImmCheck(CompilationUnit * cUnit,ArmConditionCode cond,int reg,int checkValue,int dOffset,ArmLIR * pcrLabel)472 static ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
473 ArmConditionCode cond, int reg,
474 int checkValue, int dOffset,
475 ArmLIR *pcrLabel)
476 {
477 ArmLIR *branch;
478 if ((LOWREG(reg)) && (checkValue == 0) &&
479 ((cond == ARM_COND_EQ) || (cond == ARM_COND_NE))) {
480 branch = newLIR2(cUnit,
481 (cond == ARM_COND_EQ) ? THUMB2_CBZ : THUMB2_CBNZ,
482 reg, 0);
483 } else {
484 newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
485 branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
486 }
487 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
488 }
489