blob: 942dbc51cd43833c6945c81154bd24eb3d2b8f77 [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
buzbee71ac9942012-03-01 17:23:10 -0800187void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest, RegLocation rlSrc)
188{
189 RegLocation rlResult;
190 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
191 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
192 opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
193 rlSrc.lowReg, 0x80000000);
194 storeValue(cUnit, rlDest, rlResult);
195}
196
197void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest, RegLocation rlSrc)
198{
199 RegLocation rlResult;
200 rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
201 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
202 opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
203 0x80000000);
204 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
205 storeValueWide(cUnit, rlDest, rlResult);
206}
207
buzbee5de34942012-03-01 14:51:57 -0800208/*
209 * TODO: implement fast path to short-circuit thin-lock case
210 */
211void genMonitorEnter(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800212{
buzbee5de34942012-03-01 14:51:57 -0800213 oatFlushAllRegs(cUnit);
214 loadValueDirectFixed(cUnit, rlSrc, rARG0); // Get obj
215 oatLockCallTemps(cUnit); // Prepare for explicit register usage
216 genNullCheck(cUnit, rlSrc.sRegLow, rARG0, mir);
217 // Go expensive route - artLockObjectFromCode(self, obj);
218 int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pLockObjectFromCode));
219 callRuntimeHelper(cUnit, rTgt);
buzbeee3acd072012-02-25 17:03:10 -0800220}
221
222/*
buzbee5de34942012-03-01 14:51:57 -0800223 * TODO: implement fast path to short-circuit thin-lock case
buzbeee3acd072012-02-25 17:03:10 -0800224 */
buzbee5de34942012-03-01 14:51:57 -0800225void genMonitorExit(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800226{
buzbee5de34942012-03-01 14:51:57 -0800227 oatFlushAllRegs(cUnit);
228 loadValueDirectFixed(cUnit, rlSrc, rARG0); // Get obj
229 oatLockCallTemps(cUnit); // Prepare for explicit register usage
230 genNullCheck(cUnit, rlSrc.sRegLow, rARG0, mir);
231 // Go expensive route - UnlockObjectFromCode(obj);
232 int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode));
233 callRuntimeHelper(cUnit, rTgt);
234}
235
236/*
237 * Compare two 64-bit values
238 * x = y return 0
239 * x < y return -1
240 * x > y return 1
241 *
242 * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0
243 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0
244 * subu res, t0, t1 # res = -1:1:0 for [ < > = ]
245 * bnez res, finish
246 * sltu t0, x.lo, y.lo
247 * sgtu r1, x.lo, y.lo
248 * subu res, t0, t1
249 * finish:
250 *
251 */
252void genCmpLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
253 RegLocation rlSrc1, RegLocation rlSrc2)
254{
255 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
256 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
257 int t0 = oatAllocTemp(cUnit);
258 int t1 = oatAllocTemp(cUnit);
259 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
260 newLIR3(cUnit, kMipsSlt, t0, rlSrc1.highReg, rlSrc2.highReg);
261 newLIR3(cUnit, kMipsSlt, t1, rlSrc2.highReg, rlSrc1.highReg);
262 newLIR3(cUnit, kMipsSubu, rlResult.lowReg, t1, t0);
263 LIR* branch = genCmpImmBranch(cUnit, kCondNe, rlResult.lowReg, 0);
264 newLIR3(cUnit, kMipsSltu, t0, rlSrc1.lowReg, rlSrc2.lowReg);
265 newLIR3(cUnit, kMipsSltu, t1, rlSrc2.lowReg, rlSrc1.lowReg);
266 newLIR3(cUnit, kMipsSubu, rlResult.lowReg, t1, t0);
267 oatFreeTemp(cUnit, t0);
268 oatFreeTemp(cUnit, t1);
269 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
270 target->defMask = ENCODE_ALL;
271 branch->target = (LIR*)target;
buzbeee3acd072012-02-25 17:03:10 -0800272 storeValue(cUnit, rlDest, rlResult);
273}
274
buzbee5de34942012-03-01 14:51:57 -0800275LIR* genCompareBranch(CompilationUnit* cUnit, ConditionCode cond, int src1,
276 int src2)
buzbeee3acd072012-02-25 17:03:10 -0800277{
buzbee5de34942012-03-01 14:51:57 -0800278 if (cond == kCondEq) {
279 return newLIR2(cUnit, kMipsBeq, src1, src2);
280 } else if (cond == kCondNe) {
281 return newLIR2(cUnit, kMipsBne, src1, src2);
282 }
283 //int rRes = oatAllocTemp(cUnit);
284 switch(cond) {
285 case kCondEq: return newLIR2(cUnit, kMipsBeq, src1, src2);
286 case kCondNe: return newLIR2(cUnit, kMipsBne, src1, src2);
287 default:
288 UNIMPLEMENTED(FATAL) << "Need to flesh out genCompareBranch";
289 return NULL;
290 }
buzbeee3acd072012-02-25 17:03:10 -0800291}
292
buzbee5de34942012-03-01 14:51:57 -0800293LIR* genCmpImmBranch(CompilationUnit* cUnit, ConditionCode cond, int reg,
294 int checkValue)
buzbeee3acd072012-02-25 17:03:10 -0800295{
buzbee5de34942012-03-01 14:51:57 -0800296 if (checkValue != 0) {
297 // TUNING: handle s16 & kCondLt/Mi case using slti
298 int tReg = oatAllocTemp(cUnit);
299 loadConstant(cUnit, tReg, checkValue);
300 return genCompareBranch(cUnit, cond, reg, tReg);
301 }
302 MipsOpCode opc;
303 switch(cond) {
304 case kCondEq: opc = kMipsBeqz; break;
305 case kCondGe: opc = kMipsBgez; break;
306 case kCondGt: opc = kMipsBgtz; break;
307 case kCondLe: opc = kMipsBlez; break;
308 //case KCondMi:
309 case kCondLt: opc = kMipsBltz; break;
310 case kCondNe: opc = kMipsBnez; break;
311 default:
buzbeee3acd072012-02-25 17:03:10 -0800312 int tReg = oatAllocTemp(cUnit);
buzbee5de34942012-03-01 14:51:57 -0800313 loadConstant(cUnit, tReg, checkValue);
314 return genCompareBranch(cUnit, cond, reg, tReg);
buzbeee3acd072012-02-25 17:03:10 -0800315 }
buzbee5de34942012-03-01 14:51:57 -0800316 return newLIR1(cUnit, opc, reg);
buzbeee3acd072012-02-25 17:03:10 -0800317}
318
buzbee5de34942012-03-01 14:51:57 -0800319LIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
buzbeee3acd072012-02-25 17:03:10 -0800320{
buzbee5de34942012-03-01 14:51:57 -0800321 LIR* res;
322 MipsOpCode opcode;
buzbeee3acd072012-02-25 17:03:10 -0800323#ifdef __mips_hard_float
buzbee5de34942012-03-01 14:51:57 -0800324 if (FPREG(rDest) || FPREG(rSrc))
325 return fpRegCopy(cUnit, rDest, rSrc);
326#endif
327 res = (LIR *) oatNew(cUnit, sizeof(LIR), true, kAllocLIR);
328 opcode = kMipsMove;
329 assert(LOWREG(rDest) && LOWREG(rSrc));
330 res->operands[0] = rDest;
331 res->operands[1] = rSrc;
332 res->opcode = opcode;
333 setupResourceMasks(res);
334 if (rDest == rSrc) {
335 res->flags.isNop = true;
336 }
337 return res;
338}
339
340LIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
341{
342 LIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
343 oatAppendLIR(cUnit, (LIR*)res);
344 return res;
345}
346
347void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
348 int srcLo, int srcHi)
349{
350#ifdef __mips_hard_float
351 bool destFP = FPREG(destLo) && FPREG(destHi);
352 bool srcFP = FPREG(srcLo) && FPREG(srcHi);
353 assert(FPREG(srcLo) == FPREG(srcHi));
354 assert(FPREG(destLo) == FPREG(destHi));
355 if (destFP) {
356 if (srcFP) {
357 genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
358 } else {
359 /* note the operands are swapped for the mtc1 instr */
360 newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
361 newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
362 }
363 } else {
364 if (srcFP) {
365 newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
366 newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
367 } else {
368 // Handle overlap
369 if (srcHi == destLo) {
370 genRegCopy(cUnit, destHi, srcHi);
371 genRegCopy(cUnit, destLo, srcLo);
372 } else {
373 genRegCopy(cUnit, destLo, srcLo);
374 genRegCopy(cUnit, destHi, srcHi);
375 }
376 }
377 }
buzbeee3acd072012-02-25 17:03:10 -0800378#else
buzbee5de34942012-03-01 14:51:57 -0800379 // Handle overlap
380 if (srcHi == destLo) {
381 genRegCopy(cUnit, destHi, srcHi);
382 genRegCopy(cUnit, destLo, srcLo);
383 } else {
384 genRegCopy(cUnit, destLo, srcLo);
385 genRegCopy(cUnit, destHi, srcHi);
386 }
buzbeee3acd072012-02-25 17:03:10 -0800387#endif
buzbeee3acd072012-02-25 17:03:10 -0800388}
389
390} // namespace art