blob: 0b2b15ac454f573c0d49c0ce154af70a855e9dab [file] [log] [blame]
buzbeee3acd072012-02-25 17:03:10 -08001/*
2 * Copyright (C) 2012 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 Mips ISA and is intended to be
19 * includes by:
20 *
21 * Codegen-$(TARGET_ARCH_VARIANT).c
22 *
23 */
24
25namespace art {
26
buzbeee3acd072012-02-25 17:03:10 -080027/*
28 * The sparse table in the literal pool is an array of <key,displacement>
29 * pairs. For each set, we'll load them as a pair using ldmia.
30 * This means that the register number of the temp we use for the key
31 * must be lower than the reg for the displacement.
32 *
33 * The test loop will look something like:
34 *
35 * adr rBase, <table>
36 * ldr rVal, [rSP, vRegOff]
37 * mov rIdx, #tableSize
38 * lp:
39 * ldmia rBase!, {rKey, rDisp}
40 * sub rIdx, #1
41 * cmp rVal, rKey
42 * ifeq
43 * add rPC, rDisp ; This is the branch from which we compute displacement
44 * cbnz rIdx, lp
45 */
buzbee5de34942012-03-01 14:51:57 -080046void genSparseSwitch(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -080047{
buzbee5de34942012-03-01 14:51:57 -080048 UNIMPLEMENTED(FATAL) << "Needs Mips sparse switch";
buzbeee3acd072012-02-25 17:03:10 -080049#if 0
50 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
51 if (cUnit->printMe) {
52 dumpSparseSwitchTable(table);
53 }
54 // Add the table to the list - we'll process it later
55 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
56 true, kAllocData);
57 tabRec->table = table;
58 tabRec->vaddr = mir->offset;
59 int size = table[1];
buzbee5de34942012-03-01 14:51:57 -080060 tabRec->targets = (LIR* *)oatNew(cUnit, size * sizeof(LIR*), true,
61 kAllocLIR);
buzbeee3acd072012-02-25 17:03:10 -080062 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
63
64 // Get the switch value
65 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
66 int rBase = oatAllocTemp(cUnit);
67 /* Allocate key and disp temps */
68 int rKey = oatAllocTemp(cUnit);
69 int rDisp = oatAllocTemp(cUnit);
70 // Make sure rKey's register number is less than rDisp's number for ldmia
71 if (rKey > rDisp) {
72 int tmp = rDisp;
73 rDisp = rKey;
74 rKey = tmp;
75 }
76 // Materialize a pointer to the switch table
77 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
78 // Set up rIdx
79 int rIdx = oatAllocTemp(cUnit);
80 loadConstant(cUnit, rIdx, size);
81 // Establish loop branch target
buzbee5de34942012-03-01 14:51:57 -080082 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
buzbeee3acd072012-02-25 17:03:10 -080083 target->defMask = ENCODE_ALL;
84 // Load next key/disp
85 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
86 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
87 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
buzbee5de34942012-03-01 14:51:57 -080088 genIT(cUnit, kArmCondEq, "");
89 LIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
buzbeee3acd072012-02-25 17:03:10 -080090 tabRec->bxInst = switchBranch;
91 // Needs to use setflags encoding here
92 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
buzbee5de34942012-03-01 14:51:57 -080093 LIR* branch = opCondBranch(cUnit, kCondNe);
94 branch->target = (LIR*)target;
buzbeee3acd072012-02-25 17:03:10 -080095#endif
96}
97
buzbee5de34942012-03-01 14:51:57 -080098
99void genPackedSwitch(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800100{
buzbee5de34942012-03-01 14:51:57 -0800101 UNIMPLEMENTED(FATAL) << "Need Mips packed switch";
buzbeee3acd072012-02-25 17:03:10 -0800102#if 0
103 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
104 if (cUnit->printMe) {
105 dumpPackedSwitchTable(table);
106 }
107 // Add the table to the list - we'll process it later
108 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
109 true, kAllocData);
110 tabRec->table = table;
111 tabRec->vaddr = mir->offset;
112 int size = table[1];
buzbee5de34942012-03-01 14:51:57 -0800113 tabRec->targets = (LIR* *)oatNew(cUnit, size * sizeof(LIR*), true,
buzbeee3acd072012-02-25 17:03:10 -0800114 kAllocLIR);
115 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
116
117 // Get the switch value
118 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
119 int tableBase = oatAllocTemp(cUnit);
120 // Materialize a pointer to the switch table
121 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
122 int lowKey = s4FromSwitchData(&table[2]);
123 int keyReg;
124 // Remove the bias, if necessary
125 if (lowKey == 0) {
126 keyReg = rlSrc.lowReg;
127 } else {
128 keyReg = oatAllocTemp(cUnit);
129 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
130 }
131 // Bounds check - if < 0 or >= size continue following switch
132 opRegImm(cUnit, kOpCmp, keyReg, size-1);
buzbee5de34942012-03-01 14:51:57 -0800133 LIR* branchOver = opCondBranch(cUnit, kCondHi);
buzbeee3acd072012-02-25 17:03:10 -0800134
135 // Load the displacement from the switch table
136 int dispReg = oatAllocTemp(cUnit);
137 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
138
139 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
buzbee5de34942012-03-01 14:51:57 -0800140 LIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
buzbeee3acd072012-02-25 17:03:10 -0800141 tabRec->bxInst = switchBranch;
142
143 /* branchOver target here */
buzbee5de34942012-03-01 14:51:57 -0800144 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
buzbeee3acd072012-02-25 17:03:10 -0800145 target->defMask = ENCODE_ALL;
buzbee5de34942012-03-01 14:51:57 -0800146 branchOver->target = (LIR*)target;
buzbeee3acd072012-02-25 17:03:10 -0800147#endif
148}
149
150/*
151 * Array data table format:
152 * ushort ident = 0x0300 magic value
153 * ushort width width of each element in the table
154 * uint size number of elements in the table
155 * ubyte data[size*width] table of data values (may contain a single-byte
156 * padding at the end)
157 *
158 * Total size is 4+(width * size + 1)/2 16-bit code units.
159 */
buzbee5de34942012-03-01 14:51:57 -0800160void genFillArrayData(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800161{
buzbee5de34942012-03-01 14:51:57 -0800162 UNIMPLEMENTED(FATAL) << "Needs Mips FillArrayData";
buzbeee3acd072012-02-25 17:03:10 -0800163#if 0
164 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
165 // Add the table to the list - we'll process it later
166 FillArrayData *tabRec = (FillArrayData *)
167 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
168 tabRec->table = table;
169 tabRec->vaddr = mir->offset;
170 u2 width = tabRec->table[1];
171 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
172 tabRec->size = (size * width) + 8;
173
174 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
175
176 // Making a call - use explicit registers
177 oatFlushAllRegs(cUnit); /* Everything to home location */
buzbee5de34942012-03-01 14:51:57 -0800178 loadValueDirectFixed(cUnit, rlSrc, rARG0);
buzbeee3acd072012-02-25 17:03:10 -0800179 loadWordDisp(cUnit, rSELF,
180 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
181 // Materialize a pointer to the fill data image
182 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
183 callRuntimeHelper(cUnit, rLR);
184#endif
185}
186
buzbee5de34942012-03-01 14:51:57 -0800187/*
188 * TODO: implement fast path to short-circuit thin-lock case
189 */
190void genMonitorEnter(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800191{
buzbee5de34942012-03-01 14:51:57 -0800192 oatFlushAllRegs(cUnit);
193 loadValueDirectFixed(cUnit, rlSrc, rARG0); // Get obj
194 oatLockCallTemps(cUnit); // Prepare for explicit register usage
195 genNullCheck(cUnit, rlSrc.sRegLow, rARG0, mir);
196 // Go expensive route - artLockObjectFromCode(self, obj);
197 int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pLockObjectFromCode));
198 callRuntimeHelper(cUnit, rTgt);
buzbeee3acd072012-02-25 17:03:10 -0800199}
200
201/*
buzbee5de34942012-03-01 14:51:57 -0800202 * TODO: implement fast path to short-circuit thin-lock case
buzbeee3acd072012-02-25 17:03:10 -0800203 */
buzbee5de34942012-03-01 14:51:57 -0800204void genMonitorExit(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800205{
buzbee5de34942012-03-01 14:51:57 -0800206 oatFlushAllRegs(cUnit);
207 loadValueDirectFixed(cUnit, rlSrc, rARG0); // Get obj
208 oatLockCallTemps(cUnit); // Prepare for explicit register usage
209 genNullCheck(cUnit, rlSrc.sRegLow, rARG0, mir);
210 // Go expensive route - UnlockObjectFromCode(obj);
211 int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode));
212 callRuntimeHelper(cUnit, rTgt);
213}
214
215/*
216 * Compare two 64-bit values
217 * x = y return 0
218 * x < y return -1
219 * x > y return 1
220 *
221 * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0
222 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0
223 * subu res, t0, t1 # res = -1:1:0 for [ < > = ]
224 * bnez res, finish
225 * sltu t0, x.lo, y.lo
226 * sgtu r1, x.lo, y.lo
227 * subu res, t0, t1
228 * finish:
229 *
230 */
231void genCmpLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
232 RegLocation rlSrc1, RegLocation rlSrc2)
233{
234 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
235 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
236 int t0 = oatAllocTemp(cUnit);
237 int t1 = oatAllocTemp(cUnit);
238 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
239 newLIR3(cUnit, kMipsSlt, t0, rlSrc1.highReg, rlSrc2.highReg);
240 newLIR3(cUnit, kMipsSlt, t1, rlSrc2.highReg, rlSrc1.highReg);
241 newLIR3(cUnit, kMipsSubu, rlResult.lowReg, t1, t0);
242 LIR* branch = genCmpImmBranch(cUnit, kCondNe, rlResult.lowReg, 0);
243 newLIR3(cUnit, kMipsSltu, t0, rlSrc1.lowReg, rlSrc2.lowReg);
244 newLIR3(cUnit, kMipsSltu, t1, rlSrc2.lowReg, rlSrc1.lowReg);
245 newLIR3(cUnit, kMipsSubu, rlResult.lowReg, t1, t0);
246 oatFreeTemp(cUnit, t0);
247 oatFreeTemp(cUnit, t1);
248 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
249 target->defMask = ENCODE_ALL;
250 branch->target = (LIR*)target;
buzbeee3acd072012-02-25 17:03:10 -0800251 storeValue(cUnit, rlDest, rlResult);
252}
253
buzbee5de34942012-03-01 14:51:57 -0800254LIR* genCompareBranch(CompilationUnit* cUnit, ConditionCode cond, int src1,
255 int src2)
buzbeee3acd072012-02-25 17:03:10 -0800256{
buzbee5de34942012-03-01 14:51:57 -0800257 if (cond == kCondEq) {
258 return newLIR2(cUnit, kMipsBeq, src1, src2);
259 } else if (cond == kCondNe) {
260 return newLIR2(cUnit, kMipsBne, src1, src2);
261 }
262 //int rRes = oatAllocTemp(cUnit);
263 switch(cond) {
264 case kCondEq: return newLIR2(cUnit, kMipsBeq, src1, src2);
265 case kCondNe: return newLIR2(cUnit, kMipsBne, src1, src2);
266 default:
267 UNIMPLEMENTED(FATAL) << "Need to flesh out genCompareBranch";
268 return NULL;
269 }
buzbeee3acd072012-02-25 17:03:10 -0800270}
271
buzbee5de34942012-03-01 14:51:57 -0800272LIR* genCmpImmBranch(CompilationUnit* cUnit, ConditionCode cond, int reg,
273 int checkValue)
buzbeee3acd072012-02-25 17:03:10 -0800274{
buzbee5de34942012-03-01 14:51:57 -0800275 if (checkValue != 0) {
276 // TUNING: handle s16 & kCondLt/Mi case using slti
277 int tReg = oatAllocTemp(cUnit);
278 loadConstant(cUnit, tReg, checkValue);
279 return genCompareBranch(cUnit, cond, reg, tReg);
280 }
281 MipsOpCode opc;
282 switch(cond) {
283 case kCondEq: opc = kMipsBeqz; break;
284 case kCondGe: opc = kMipsBgez; break;
285 case kCondGt: opc = kMipsBgtz; break;
286 case kCondLe: opc = kMipsBlez; break;
287 //case KCondMi:
288 case kCondLt: opc = kMipsBltz; break;
289 case kCondNe: opc = kMipsBnez; break;
290 default:
buzbeee3acd072012-02-25 17:03:10 -0800291 int tReg = oatAllocTemp(cUnit);
buzbee5de34942012-03-01 14:51:57 -0800292 loadConstant(cUnit, tReg, checkValue);
293 return genCompareBranch(cUnit, cond, reg, tReg);
buzbeee3acd072012-02-25 17:03:10 -0800294 }
buzbee5de34942012-03-01 14:51:57 -0800295 return newLIR1(cUnit, opc, reg);
buzbeee3acd072012-02-25 17:03:10 -0800296}
297
buzbee5de34942012-03-01 14:51:57 -0800298LIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
buzbeee3acd072012-02-25 17:03:10 -0800299{
buzbee5de34942012-03-01 14:51:57 -0800300 LIR* res;
301 MipsOpCode opcode;
buzbeee3acd072012-02-25 17:03:10 -0800302#ifdef __mips_hard_float
buzbee5de34942012-03-01 14:51:57 -0800303 if (FPREG(rDest) || FPREG(rSrc))
304 return fpRegCopy(cUnit, rDest, rSrc);
305#endif
306 res = (LIR *) oatNew(cUnit, sizeof(LIR), true, kAllocLIR);
307 opcode = kMipsMove;
308 assert(LOWREG(rDest) && LOWREG(rSrc));
309 res->operands[0] = rDest;
310 res->operands[1] = rSrc;
311 res->opcode = opcode;
312 setupResourceMasks(res);
313 if (rDest == rSrc) {
314 res->flags.isNop = true;
315 }
316 return res;
317}
318
319LIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
320{
321 LIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
322 oatAppendLIR(cUnit, (LIR*)res);
323 return res;
324}
325
326void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
327 int srcLo, int srcHi)
328{
329#ifdef __mips_hard_float
330 bool destFP = FPREG(destLo) && FPREG(destHi);
331 bool srcFP = FPREG(srcLo) && FPREG(srcHi);
332 assert(FPREG(srcLo) == FPREG(srcHi));
333 assert(FPREG(destLo) == FPREG(destHi));
334 if (destFP) {
335 if (srcFP) {
336 genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
337 } else {
338 /* note the operands are swapped for the mtc1 instr */
339 newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
340 newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
341 }
342 } else {
343 if (srcFP) {
344 newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
345 newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
346 } else {
347 // Handle overlap
348 if (srcHi == destLo) {
349 genRegCopy(cUnit, destHi, srcHi);
350 genRegCopy(cUnit, destLo, srcLo);
351 } else {
352 genRegCopy(cUnit, destLo, srcLo);
353 genRegCopy(cUnit, destHi, srcHi);
354 }
355 }
356 }
buzbeee3acd072012-02-25 17:03:10 -0800357#else
buzbee5de34942012-03-01 14:51:57 -0800358 // Handle overlap
359 if (srcHi == destLo) {
360 genRegCopy(cUnit, destHi, srcHi);
361 genRegCopy(cUnit, destLo, srcLo);
362 } else {
363 genRegCopy(cUnit, destLo, srcLo);
364 genRegCopy(cUnit, destHi, srcHi);
365 }
buzbeee3acd072012-02-25 17:03:10 -0800366#endif
buzbeee3acd072012-02-25 17:03:10 -0800367}
368
369} // namespace art