blob: 3a9f1de1e92f0251ee891d76d7488fa2ab215fc7 [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
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 */
28static void loadConstant(CompilationUnit *cUnit, int rDest, int value);
29static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr);
30static void genConditionalBranch(CompilationUnit *cUnit,
31 ArmConditionCode cond,
32 ArmLIR *target);
33static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target);
34static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
35 int rDestHi);
36static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
37 int vDest, int rScratch);
38static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int vDest);
39static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest);
40static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
41 int rDest);
42static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
43 int rScratch);
44static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
45 ArmConditionCode cond, int reg,
46 int checkValue, int dOffset,
47 ArmLIR *pcrLabel);
Bill Buzbee7ea0f642009-08-10 17:06:51 -070048ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
Bill Buzbee9bc3df32009-07-30 10:52:29 -070049
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 */
67static 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 */
78static 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 */
84static 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 */
94static 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 */
106static 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
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700136ArmLIR* 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
164static 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 */
187static 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
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700217/*
218 * Load a immediate using a shortcut if possible; otherwise
219 * grab from the per-translation literal pool
220 */
221static void loadConstant(CompilationUnit *cUnit, int rDest, int value)
222{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700223 int modImm;
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700224 /* See if the value can be constructed cheaply */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700225 if ((value & 0xff) == value) {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700226 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 }
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700233 /* 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 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700244 /* 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 */
265static 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));
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700269 newLIR3(cUnit, THUMB2_STR_RRI8_PREDEC, rDPC, rFP,
270 sizeof(StackSaveArea) - offset);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700271}
272
273/* Generate conditional branch instructions */
274static 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 */
283static 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 */
294static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
295 int rDestHi)
296{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700297 bool allLowRegs = (LOWREG(rDestLo) && LOWREG(rDestHi));
298
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700299 /* Use reg + imm5*4 to load the values if possible */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700300 if (allLowRegs && vSrc <= 30) {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700301 newLIR3(cUnit, THUMB_LDR_RRI5, rDestLo, rFP, vSrc);
302 newLIR3(cUnit, THUMB_LDR_RRI5, rDestHi, rFP, vSrc+1);
303 } else {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700304 assert(rDestLo < rDestHi);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700305 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 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700311 }
312}
313
314/*
315 * Store a pair of values of rSrc and rSrc+1 and store them into vDest and
316 * vDest+1
317 */
318static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
319 int vDest, int rScratch)
320{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700321 bool allLowRegs = (LOWREG(rSrcLo) && LOWREG(rSrcHi));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700322 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 */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700327 if (allLowRegs && vDest <= 30) {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700328 newLIR3(cUnit, THUMB_STR_RRI5, rSrcLo, rFP, vDest);
329 newLIR3(cUnit, THUMB_STR_RRI5, rSrcHi, rFP, vDest+1);
330 } else {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700331 assert(rSrcLo < rSrcHi);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700332 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
342static 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. */
354static 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);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700367 }
368}
369
370/* Load the address of a Dalvik register on the frame */
371static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest)
372{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700373 addRegisterImmediate(cUnit, rDest, rFP, vSrc*4);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700374}
375
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700376/*
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
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700384/* Load a float from a Dalvik register */
385static void loadFloat(CompilationUnit *cUnit, int vSrc, int rDest)
386{
387 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700388 assert(SINGLEREG(rDest));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700389 newLIR3(cUnit, THUMB2_VLDRS, rDest, rFP, vSrc);
390}
391
392/* Store a float to a Dalvik register */
393static void storeFloat(CompilationUnit *cUnit, int rSrc, int vDest,
394 int rScratch)
395{
396 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700397 assert(SINGLEREG(rSrc));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700398 newLIR3(cUnit, THUMB2_VSTRS, rSrc, rFP, vDest);
399}
400
401/* Load a double from a Dalvik register */
402static void loadDouble(CompilationUnit *cUnit, int vSrc, int rDest)
403{
404 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700405 assert(DOUBLEREG(rDest));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700406 newLIR3(cUnit, THUMB2_VLDRD, rDest, rFP, vSrc);
407}
408
409/* Store a double to a Dalvik register */
410static void storeDouble(CompilationUnit *cUnit, int rSrc, int vDest,
411 int rScratch)
412{
413 assert(vSrc <= 255); // FIXME - temp limit to 1st 256
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700414 assert(DOUBLEREG(rSrc));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700415 newLIR3(cUnit, THUMB2_VSTRD, rSrc, rFP, vDest);
416}
417
418
419/* Load a single value from rFP[src] and store them into rDest */
420static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest)
421{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700422 loadWordDisp(cUnit, rFP, vSrc * 4, rDest);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700423}
424
425/* Load a word at base + displacement. Displacement must be word multiple */
426static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
427 int rDest)
428{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700429 bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700430 assert((displacement & 0x3) == 0);
431 /* Can it fit in a RRI5? */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700432 if (allLowRegs && displacement < 128) {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700433 newLIR3(cUnit, THUMB_LDR_RRI5, rDest, rBase, displacement >> 2);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700434 } else if (displacement < 4092) {
435 newLIR3(cUnit, THUMB2_LDR_RRI12, rDest, rFP, displacement);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700436 } else {
437 loadConstant(cUnit, rDest, displacement);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700438 if (allLowRegs) {
439 newLIR3(cUnit, THUMB_LDR_RRR, rDest, rBase, rDest);
440 } else {
441 assert(0); // Unimp - need Thumb2 ldr_rrr
442 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700443 }
444}
445
446/* Store a value from rSrc to vDest */
447static 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 */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700454 if (LOWREG(rSrc) && vDest <= 31) {
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700455 newLIR3(cUnit, THUMB_STR_RRI5, rSrc, rFP, vDest);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700456 } else if (vDest <= 1023) {
457 newLIR3(cUnit, THUMB2_STR_RRI12, rSrc, rFP, vDest*4);
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700458 } else {
459 loadConstant(cUnit, rScratch, vDest*4);
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700460 if (LOWREG(rSrc)) {
461 newLIR3(cUnit, THUMB_STR_RRR, rSrc, rFP, rScratch);
462 } else {
463 assert(0); // Unimp: Need generic str_rrr routine
464 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700465 }
466}
467
468/*
469 * Perform a "reg cmp imm" operation and jump to the PCR region if condition
470 * satisfies.
471 */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700472static ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700473 ArmConditionCode cond, int reg,
474 int checkValue, int dOffset,
475 ArmLIR *pcrLabel)
476{
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700477 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 }
Bill Buzbee9bc3df32009-07-30 10:52:29 -0700487 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
488}