2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * This file contains codegen for the Thumb ISA and is intended to be
21 * Codegen-$(TARGET_ARCH_VARIANT).c
25 static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7};
26 static int corePreserved[] = {};
28 /* FIXME - circular dependency */
29 static void storePair(CompilationUnit *cUnit, int base, int lowReg,
31 static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
32 static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
34 static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
35 int displacement, int rSrc);
36 static ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
37 ArmConditionCode cond,
38 int reg1, int reg2, int dOffset,
43 * Load a immediate using a shortcut if possible; otherwise
44 * grab from the per-translation literal pool. If target is
45 * a high register, build constant into a low register and copy.
47 static ArmLIR *loadConstantValue(CompilationUnit *cUnit, int rDest, int value)
50 int tDest = LOWREG(rDest) ? rDest : allocTemp(cUnit);
51 /* See if the value can be constructed cheaply */
52 if ((value >= 0) && (value <= 255)) {
53 res = newLIR2(cUnit, kThumbMovImm, tDest, value);
55 opRegReg(cUnit, kOpMov, rDest, tDest);
56 freeTemp(cUnit, tDest);
59 } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
60 res = newLIR2(cUnit, kThumbMovImm, tDest, ~value);
61 newLIR2(cUnit, kThumbMvn, tDest, tDest);
63 opRegReg(cUnit, kOpMov, rDest, tDest);
64 freeTemp(cUnit, tDest);
68 /* No shortcut - go ahead and use literal pool */
69 ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
70 if (dataTarget == NULL) {
71 dataTarget = addWordData(cUnit, value, false);
73 ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
74 loadPcRel->opCode = kThumbLdrPcRel;
75 loadPcRel->generic.target = (LIR *) dataTarget;
76 loadPcRel->operands[0] = tDest;
77 setupResourceMasks(loadPcRel);
79 dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
82 * To save space in the constant pool, we use the ADD_RRI8 instruction to
83 * add up to 255 to an existing constant value.
85 if (dataTarget->operands[0] != value) {
86 newLIR2(cUnit, kThumbAddRI8, tDest, value - dataTarget->operands[0]);
89 opRegReg(cUnit, kOpMov, rDest, tDest);
90 freeTemp(cUnit, tDest);
96 * Load an immediate value into a fixed or temp register. Target
97 * register is clobbered, and marked inUse.
99 static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
101 if (isTemp(cUnit, rDest)) {
102 clobberReg(cUnit, rDest);
103 markRegInUse(cUnit, rDest);
105 return loadConstantValue(cUnit, rDest, value);
108 static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
110 ArmOpCode opCode = kThumbBkpt;
113 opCode = kThumbBUncond;
118 return newLIR0(cUnit, opCode);
121 static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
123 return newLIR2(cUnit, kThumbBCond, 0 /* offset to be patched */, cc);
126 static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
128 ArmOpCode opCode = kThumbBkpt;
139 return newLIR1(cUnit, opCode, value);
142 static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
144 ArmOpCode opCode = kThumbBkpt;
152 return newLIR1(cUnit, opCode, rDestSrc);
155 static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
159 bool neg = (value < 0);
160 int absValue = (neg) ? -value : value;
161 bool shortForm = (absValue & 0xff) == absValue;
162 ArmOpCode opCode = kThumbBkpt;
165 if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
166 assert((value & 0x3) == 0);
167 return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
168 } else if (shortForm) {
169 opCode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
171 opCode = kThumbAddRRR;
174 if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
175 assert((value & 0x3) == 0);
176 return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
177 } else if (shortForm) {
178 opCode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
180 opCode = kThumbSubRRR;
185 if (LOWREG(rDestSrc1) && shortForm) {
186 opCode = kThumbCmpRI8;
187 } else if (LOWREG(rDestSrc1)) {
188 opCode = kThumbCmpRR;
191 opCode = kThumbCmpHL;
199 res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
201 int rScratch = allocTemp(cUnit);
202 res = loadConstant(cUnit, rScratch, value);
204 newLIR2(cUnit, opCode, rDestSrc1, rScratch);
206 newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
211 static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
212 int rSrc1, int rSrc2)
214 ArmOpCode opCode = kThumbBkpt;
217 opCode = kThumbAddRRR;
220 opCode = kThumbSubRRR;
223 if (rDest == rSrc1) {
224 return opRegReg(cUnit, op, rDest, rSrc2);
225 } else if (rDest == rSrc2) {
226 assert(isTemp(cUnit, rSrc1));
227 clobberReg(cUnit, rSrc1);
228 opRegReg(cUnit, op, rSrc1, rSrc2);
229 return opRegReg(cUnit, kOpMov, rDest, rSrc1);
231 opRegReg(cUnit, kOpMov, rDest, rSrc1);
232 return opRegReg(cUnit, op, rDest, rSrc2);
236 return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
239 static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
240 int rSrc1, int value)
243 bool neg = (value < 0);
244 int absValue = (neg) ? -value : value;
245 ArmOpCode opCode = kThumbBkpt;
246 bool shortForm = (absValue & 0x7) == absValue;
250 return opRegImm(cUnit, op, rDest, value);
251 if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
252 assert((value & 0x3) == 0);
254 opCode = kThumbAddSpRel;
256 } else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
257 assert((value & 0x3) == 0);
259 opCode = kThumbAddPcRel;
261 } else if (shortForm) {
262 opCode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
263 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
264 /* Two shots - 1st handle the 7 */
265 opCode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
266 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
267 opCode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
268 newLIR2(cUnit, opCode, rDest, absValue - 7);
271 opCode = kThumbAddRRR;
276 return opRegImm(cUnit, op, rDest, value);
278 opCode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
279 } else if ((absValue > 0) && (absValue <= (255 + 7))) {
280 /* Two shots - 1st handle the 7 */
281 opCode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
282 res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
283 opCode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
284 newLIR2(cUnit, opCode, rDest, absValue - 7);
287 opCode = kThumbSubRRR;
290 shortForm = (!neg && value <= 31);
291 opCode = kThumbLslRRI5;
294 shortForm = (!neg && value <= 31);
295 opCode = kThumbLsrRRI5;
298 shortForm = (!neg && value <= 31);
299 opCode = kThumbAsrRRI5;
305 if (rDest == rSrc1) {
306 int rScratch = allocTemp(cUnit);
307 res = loadConstant(cUnit, rScratch, value);
308 opRegReg(cUnit, op, rDest, rScratch);
310 res = loadConstant(cUnit, rDest, value);
311 opRegReg(cUnit, op, rDest, rSrc1);
319 res = newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
321 if (rDest != rSrc1) {
322 res = loadConstant(cUnit, rDest, value);
323 newLIR3(cUnit, opCode, rDest, rSrc1, rDest);
325 int rScratch = allocTemp(cUnit);
326 res = loadConstant(cUnit, rScratch, value);
327 newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
333 static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
337 ArmOpCode opCode = kThumbBkpt;
340 opCode = kThumbAdcRR;
343 opCode = kThumbAndRR;
346 opCode = kThumbBicRR;
349 opCode = kThumbCmnRR;
352 opCode = kThumbCmpRR;
355 opCode = kThumbEorRR;
358 if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
359 opCode = kThumbMovRR;
360 else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
361 opCode = kThumbMovRR_H2H;
362 else if (LOWREG(rDestSrc1))
363 opCode = kThumbMovRR_H2L;
365 opCode = kThumbMovRR_L2H;
386 opCode = kThumbLslRR;
389 opCode = kThumbLsrRR;
392 opCode = kThumbAsrRR;
395 opCode = kThumbRorRR;
398 return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
400 res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
401 opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
404 res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
405 opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
408 res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
409 opRegRegImm(cUnit, kOpLsr, rDestSrc1, rDestSrc1, 16);
415 return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
418 static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
419 int rDestHi, int valLo, int valHi)
422 res = loadConstantValue(cUnit, rDestLo, valLo);
423 loadConstantValue(cUnit, rDestHi, valHi);
427 /* Load value from base + scaled index. */
428 static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
429 int rIndex, int rDest, int scale, OpSize size)
431 ArmLIR *first = NULL;
433 ArmOpCode opCode = kThumbBkpt;
434 int rNewIndex = rIndex;
436 // Scale the index, but can't trash the original.
437 rNewIndex = allocTemp(cUnit);
438 first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
442 opCode = kThumbLdrRRR;
445 opCode = kThumbLdrhRRR;
448 opCode = kThumbLdrshRRR;
451 opCode = kThumbLdrbRRR;
454 opCode = kThumbLdrsbRRR;
459 res = newLIR3(cUnit, opCode, rDest, rBase, rNewIndex);
461 freeTemp(cUnit, rNewIndex);
462 return (first) ? first : res;
465 /* store value base base + scaled index. */
466 static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
467 int rIndex, int rSrc, int scale, OpSize size)
469 ArmLIR *first = NULL;
471 ArmOpCode opCode = kThumbBkpt;
472 int rNewIndex = rIndex;
474 rNewIndex = allocTemp(cUnit);
475 first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
479 opCode = kThumbStrRRR;
483 opCode = kThumbStrhRRR;
487 opCode = kThumbStrbRRR;
492 res = newLIR3(cUnit, opCode, rSrc, rBase, rNewIndex);
494 freeTemp(cUnit, rNewIndex);
495 return (first) ? first : res;
498 static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
502 res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
507 static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
511 res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
516 static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
517 int displacement, int rDest, int rDestHi,
518 OpSize size, int sReg)
520 * Load value from base + displacement. Optionally perform null check
521 * on base (which must have an associated sReg and MIR). If not
522 * performing null check, incoming MIR can be null. IMPORTANT: this
523 * code must not allocate any new temps. If a new register is needed
524 * and base and dest are the same, spill some other register to
525 * rlp and then restore.
530 ArmLIR *load2 = NULL;
531 ArmOpCode opCode = kThumbBkpt;
532 bool shortForm = false;
534 int encodedDisp = displacement;
541 if ((displacement < 124) && (displacement >= 0)) {
542 assert((displacement & 0x3) == 0);
545 opCode = kThumbLdrRRI5;
547 opCode = kThumbLdrRRR;
551 if (LOWREG(rDest) && (rBase == rpc) &&
552 (displacement <= 1020) && (displacement >= 0)) {
555 opCode = kThumbLdrPcRel;
556 } else if (LOWREG(rDest) && (rBase == r13) &&
557 (displacement <= 1020) && (displacement >= 0)) {
560 opCode = kThumbLdrSpRel;
561 } else if (displacement < 128 && displacement >= 0) {
562 assert((displacement & 0x3) == 0);
565 opCode = kThumbLdrRRI5;
567 opCode = kThumbLdrRRR;
571 if (displacement < 64 && displacement >= 0) {
572 assert((displacement & 0x1) == 0);
575 opCode = kThumbLdrhRRI5;
577 opCode = kThumbLdrhRRR;
581 opCode = kThumbLdrshRRR;
584 if (displacement < 32 && displacement >= 0) {
586 opCode = kThumbLdrbRRI5;
588 opCode = kThumbLdrbRRR;
592 opCode = kThumbLdrsbRRR;
598 load = res = newLIR3(cUnit, opCode, rDest, rBase, encodedDisp);
600 load2 = newLIR3(cUnit, opCode, rDestHi, rBase, encodedDisp+1);
604 int rTmp = allocFreeTemp(cUnit);
606 //UNIMP: need to spill if no temps.
609 res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
610 //TUNING: how to mark loadPair if Dalvik access?
611 loadPair(cUnit, rTmp, rDest, rDestHi);
612 freeTemp(cUnit, rTmp);
614 int rTmp = (rBase == rDest) ? allocFreeTemp(cUnit) : rDest;
616 //UNIMP: need to spill if no temps.
619 res = loadConstant(cUnit, rTmp, displacement);
620 load = newLIR3(cUnit, opCode, rDest, rBase, rTmp);
622 annotateDalvikRegAccess(load, displacement >> 2,
625 freeTemp(cUnit, rTmp);
632 static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
633 int displacement, int rDest, OpSize size,
636 return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
640 static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
641 int displacement, int rDestLo, int rDestHi,
644 return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
648 static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
649 int displacement, int rSrc, int rSrcHi,
653 ArmLIR *store = NULL;
654 ArmLIR *store2 = NULL;
655 ArmOpCode opCode = kThumbBkpt;
656 bool shortForm = false;
658 int encodedDisp = displacement;
665 if ((displacement < 124) && (displacement >= 0)) {
666 assert((displacement & 0x3) == 0);
670 opCode = kThumbStrRRI5;
672 opCode = kThumbStrRRR;
676 if (displacement < 128 && displacement >= 0) {
677 assert((displacement & 0x3) == 0);
680 opCode = kThumbStrRRI5;
682 opCode = kThumbStrRRR;
687 if (displacement < 64 && displacement >= 0) {
688 assert((displacement & 0x1) == 0);
691 opCode = kThumbStrhRRI5;
693 opCode = kThumbStrhRRR;
698 if (displacement < 32 && displacement >= 0) {
700 opCode = kThumbStrbRRI5;
702 opCode = kThumbStrbRRR;
709 store = res = newLIR3(cUnit, opCode, rSrc, rBase, encodedDisp);
711 store2 = newLIR3(cUnit, opCode, rSrcHi, rBase, encodedDisp + 1);
714 int rScratch = allocTemp(cUnit);
716 //TUNING: how to mark storePair as Dalvik access if it is?
717 res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
718 storePair(cUnit, rScratch, rSrc, rSrcHi);
720 res = loadConstant(cUnit, rScratch, displacement);
721 store = newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
723 annotateDalvikRegAccess(store, displacement >> 2,
727 freeTemp(cUnit, rScratch);
732 static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
733 int displacement, int rSrc, OpSize size)
735 return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
738 static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
739 int displacement, int rSrcLo, int rSrcHi)
741 return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
744 static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
746 if (lowReg < highReg) {
747 storeMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
749 storeWordDisp(cUnit, base, 0, lowReg);
750 storeWordDisp(cUnit, base, 4, highReg);
754 static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
756 if (lowReg < highReg) {
757 loadMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
759 loadWordDisp(cUnit, base, 0 , lowReg);
760 loadWordDisp(cUnit, base, 4 , highReg);
764 static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
768 res = dvmCompilerNew(sizeof(ArmLIR), true);
769 if (LOWREG(rDest) && LOWREG(rSrc))
770 opCode = kThumbMovRR;
771 else if (!LOWREG(rDest) && !LOWREG(rSrc))
772 opCode = kThumbMovRR_H2H;
773 else if (LOWREG(rDest))
774 opCode = kThumbMovRR_H2L;
776 opCode = kThumbMovRR_L2H;
778 res->operands[0] = rDest;
779 res->operands[1] = rSrc;
780 res->opCode = opCode;
781 setupResourceMasks(res);
788 static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
790 ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
791 dvmCompilerAppendLIR(cUnit, (LIR*)res);
795 static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
796 int srcLo, int srcHi)
799 if (srcHi == destLo) {
800 genRegCopy(cUnit, destHi, srcHi);
801 genRegCopy(cUnit, destLo, srcLo);
803 genRegCopy(cUnit, destLo, srcLo);
804 genRegCopy(cUnit, destHi, srcHi);
808 static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
809 ArmConditionCode cond, int reg,
810 int checkValue, int dOffset,
815 if ((checkValue & 0xff) != checkValue) {
816 tReg = allocTemp(cUnit);
817 loadConstant(cUnit, tReg, checkValue);
818 res = genRegRegCheck(cUnit, cond, reg, tReg, dOffset, pcrLabel);
819 freeTemp(cUnit, tReg);
822 newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
823 ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond);
824 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);