blob: 155675c700acc2a1568584a0944a5d119b330ad7 [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
buzbee82488f52012-03-02 08:20:26 -080088 opIT(cUnit, kArmCondEq, "");
buzbee5de34942012-03-01 14:51:57 -080089 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);
buzbee82488f52012-03-02 08:20:26 -080093 LIR* branch = opCondBranch(cUnit, kCondNe, target);
buzbeee3acd072012-02-25 17:03:10 -080094#endif
95}
96
buzbee5de34942012-03-01 14:51:57 -080097
98void genPackedSwitch(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -080099{
buzbee5de34942012-03-01 14:51:57 -0800100 UNIMPLEMENTED(FATAL) << "Need Mips packed switch";
buzbeee3acd072012-02-25 17:03:10 -0800101#if 0
102 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
103 if (cUnit->printMe) {
104 dumpPackedSwitchTable(table);
105 }
106 // Add the table to the list - we'll process it later
107 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
108 true, kAllocData);
109 tabRec->table = table;
110 tabRec->vaddr = mir->offset;
111 int size = table[1];
buzbee5de34942012-03-01 14:51:57 -0800112 tabRec->targets = (LIR* *)oatNew(cUnit, size * sizeof(LIR*), true,
buzbeee3acd072012-02-25 17:03:10 -0800113 kAllocLIR);
114 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
115
116 // Get the switch value
117 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
118 int tableBase = oatAllocTemp(cUnit);
119 // Materialize a pointer to the switch table
120 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
121 int lowKey = s4FromSwitchData(&table[2]);
122 int keyReg;
123 // Remove the bias, if necessary
124 if (lowKey == 0) {
125 keyReg = rlSrc.lowReg;
126 } else {
127 keyReg = oatAllocTemp(cUnit);
128 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
129 }
130 // Bounds check - if < 0 or >= size continue following switch
131 opRegImm(cUnit, kOpCmp, keyReg, size-1);
buzbee82488f52012-03-02 08:20:26 -0800132 LIR* branchOver = opCondBranch(cUnit, kCondHi, NULL);
buzbeee3acd072012-02-25 17:03:10 -0800133
134 // Load the displacement from the switch table
135 int dispReg = oatAllocTemp(cUnit);
136 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
137
138 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
buzbee5de34942012-03-01 14:51:57 -0800139 LIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
buzbeee3acd072012-02-25 17:03:10 -0800140 tabRec->bxInst = switchBranch;
141
142 /* branchOver target here */
buzbee5de34942012-03-01 14:51:57 -0800143 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
buzbeee3acd072012-02-25 17:03:10 -0800144 target->defMask = ENCODE_ALL;
buzbee5de34942012-03-01 14:51:57 -0800145 branchOver->target = (LIR*)target;
buzbeee3acd072012-02-25 17:03:10 -0800146#endif
147}
148
149/*
150 * Array data table format:
151 * ushort ident = 0x0300 magic value
152 * ushort width width of each element in the table
153 * uint size number of elements in the table
154 * ubyte data[size*width] table of data values (may contain a single-byte
155 * padding at the end)
156 *
157 * Total size is 4+(width * size + 1)/2 16-bit code units.
158 */
buzbee5de34942012-03-01 14:51:57 -0800159void genFillArrayData(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800160{
buzbee5de34942012-03-01 14:51:57 -0800161 UNIMPLEMENTED(FATAL) << "Needs Mips FillArrayData";
buzbeee3acd072012-02-25 17:03:10 -0800162#if 0
163 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
164 // Add the table to the list - we'll process it later
165 FillArrayData *tabRec = (FillArrayData *)
166 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
167 tabRec->table = table;
168 tabRec->vaddr = mir->offset;
169 u2 width = tabRec->table[1];
170 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
171 tabRec->size = (size * width) + 8;
172
173 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
174
175 // Making a call - use explicit registers
176 oatFlushAllRegs(cUnit); /* Everything to home location */
buzbee5de34942012-03-01 14:51:57 -0800177 loadValueDirectFixed(cUnit, rlSrc, rARG0);
buzbeee3acd072012-02-25 17:03:10 -0800178 loadWordDisp(cUnit, rSELF,
179 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
180 // Materialize a pointer to the fill data image
181 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
182 callRuntimeHelper(cUnit, rLR);
183#endif
184}
185
buzbee71ac9942012-03-01 17:23:10 -0800186void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest, RegLocation rlSrc)
187{
188 RegLocation rlResult;
189 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
190 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
191 opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
192 rlSrc.lowReg, 0x80000000);
193 storeValue(cUnit, rlDest, rlResult);
194}
195
196void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest, RegLocation rlSrc)
197{
198 RegLocation rlResult;
199 rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
200 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
201 opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
202 0x80000000);
buzbee82488f52012-03-02 08:20:26 -0800203 opRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
buzbee71ac9942012-03-01 17:23:10 -0800204 storeValueWide(cUnit, rlDest, rlResult);
205}
206
buzbee5de34942012-03-01 14:51:57 -0800207/*
208 * TODO: implement fast path to short-circuit thin-lock case
209 */
210void genMonitorEnter(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800211{
buzbee5de34942012-03-01 14:51:57 -0800212 oatFlushAllRegs(cUnit);
213 loadValueDirectFixed(cUnit, rlSrc, rARG0); // Get obj
214 oatLockCallTemps(cUnit); // Prepare for explicit register usage
215 genNullCheck(cUnit, rlSrc.sRegLow, rARG0, mir);
216 // Go expensive route - artLockObjectFromCode(self, obj);
217 int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pLockObjectFromCode));
218 callRuntimeHelper(cUnit, rTgt);
buzbeee3acd072012-02-25 17:03:10 -0800219}
220
221/*
buzbee5de34942012-03-01 14:51:57 -0800222 * TODO: implement fast path to short-circuit thin-lock case
buzbeee3acd072012-02-25 17:03:10 -0800223 */
buzbee5de34942012-03-01 14:51:57 -0800224void genMonitorExit(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbeee3acd072012-02-25 17:03:10 -0800225{
buzbee5de34942012-03-01 14:51:57 -0800226 oatFlushAllRegs(cUnit);
227 loadValueDirectFixed(cUnit, rlSrc, rARG0); // Get obj
228 oatLockCallTemps(cUnit); // Prepare for explicit register usage
229 genNullCheck(cUnit, rlSrc.sRegLow, rARG0, mir);
230 // Go expensive route - UnlockObjectFromCode(obj);
231 int rTgt = loadHelper(cUnit, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode));
232 callRuntimeHelper(cUnit, rTgt);
233}
234
235/*
236 * Compare two 64-bit values
237 * x = y return 0
238 * x < y return -1
239 * x > y return 1
240 *
241 * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0
242 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0
243 * subu res, t0, t1 # res = -1:1:0 for [ < > = ]
244 * bnez res, finish
245 * sltu t0, x.lo, y.lo
246 * sgtu r1, x.lo, y.lo
247 * subu res, t0, t1
248 * finish:
249 *
250 */
251void genCmpLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
252 RegLocation rlSrc1, RegLocation rlSrc2)
253{
254 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
255 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
256 int t0 = oatAllocTemp(cUnit);
257 int t1 = oatAllocTemp(cUnit);
258 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
259 newLIR3(cUnit, kMipsSlt, t0, rlSrc1.highReg, rlSrc2.highReg);
260 newLIR3(cUnit, kMipsSlt, t1, rlSrc2.highReg, rlSrc1.highReg);
261 newLIR3(cUnit, kMipsSubu, rlResult.lowReg, t1, t0);
buzbee82488f52012-03-02 08:20:26 -0800262 LIR* branch = opCmpImmBranch(cUnit, kCondNe, rlResult.lowReg, 0, NULL);
buzbee5de34942012-03-01 14:51:57 -0800263 newLIR3(cUnit, kMipsSltu, t0, rlSrc1.lowReg, rlSrc2.lowReg);
264 newLIR3(cUnit, kMipsSltu, t1, rlSrc2.lowReg, rlSrc1.lowReg);
265 newLIR3(cUnit, kMipsSubu, rlResult.lowReg, t1, t0);
266 oatFreeTemp(cUnit, t0);
267 oatFreeTemp(cUnit, t1);
268 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
269 target->defMask = ENCODE_ALL;
270 branch->target = (LIR*)target;
buzbeee3acd072012-02-25 17:03:10 -0800271 storeValue(cUnit, rlDest, rlResult);
272}
273
buzbee82488f52012-03-02 08:20:26 -0800274LIR* opCmpBranch(CompilationUnit* cUnit, ConditionCode cond, int src1,
275 int src2, LIR* target)
buzbeee3acd072012-02-25 17:03:10 -0800276{
buzbee0398c422012-03-02 15:22:47 -0800277 LIR* branch;
278 MipsOpCode sltOp;
279 MipsOpCode brOp;
280 bool cmpZero = false;
281 bool swapped = false;
282 switch(cond) {
283 case kCondEq:
284 brOp = kMipsBeq;
285 cmpZero = true;
286 break;
287 case kCondNe:
288 brOp = kMipsBne;
289 cmpZero = true;
290 break;
291 case kCondCc:
292 sltOp = kMipsSltu;
293 brOp = kMipsBnez;
294 break;
295 case kCondCs:
296 sltOp = kMipsSltu;
297 brOp = kMipsBeqz;
298 break;
299 case kCondGe:
300 sltOp = kMipsSlt;
301 brOp = kMipsBeqz;
302 break;
303 case kCondGt:
304 sltOp = kMipsSlt;
305 brOp = kMipsBnez;
306 swapped = true;
307 break;
308 case kCondLe:
309 sltOp = kMipsSlt;
310 brOp = kMipsBeqz;
311 swapped = true;
312 break;
313 case kCondLt:
314 sltOp = kMipsSlt;
315 brOp = kMipsBnez;
316 break;
317 default:
318 UNIMPLEMENTED(FATAL) << "No support for ConditionCode: "
319 << (int) cond;
320 return NULL;
321 }
322 if (cmpZero) {
323 branch = newLIR2(cUnit, brOp, src1, src2);
buzbee82488f52012-03-02 08:20:26 -0800324 } else {
buzbee82488f52012-03-02 08:20:26 -0800325 int tReg = oatAllocTemp(cUnit);
326 if (swapped) {
327 newLIR3(cUnit, sltOp, tReg, src2, src1);
328 } else {
329 newLIR3(cUnit, sltOp, tReg, src1, src2);
330 }
331 branch = newLIR1(cUnit, brOp, tReg);
buzbee0398c422012-03-02 15:22:47 -0800332 oatFreeTemp(cUnit, tReg);
buzbee5de34942012-03-01 14:51:57 -0800333 }
buzbee0398c422012-03-02 15:22:47 -0800334 branch->target = target;
buzbee82488f52012-03-02 08:20:26 -0800335 return branch;
buzbeee3acd072012-02-25 17:03:10 -0800336}
337
buzbee82488f52012-03-02 08:20:26 -0800338LIR* opCmpImmBranch(CompilationUnit* cUnit, ConditionCode cond, int reg,
339 int checkValue, LIR* target)
buzbeee3acd072012-02-25 17:03:10 -0800340{
buzbee82488f52012-03-02 08:20:26 -0800341 LIR* branch;
buzbee5de34942012-03-01 14:51:57 -0800342 if (checkValue != 0) {
343 // TUNING: handle s16 & kCondLt/Mi case using slti
344 int tReg = oatAllocTemp(cUnit);
345 loadConstant(cUnit, tReg, checkValue);
buzbee82488f52012-03-02 08:20:26 -0800346 branch = opCmpBranch(cUnit, cond, reg, tReg, target);
347 oatFreeTemp(cUnit, tReg);
348 return branch;
buzbee5de34942012-03-01 14:51:57 -0800349 }
350 MipsOpCode opc;
351 switch(cond) {
352 case kCondEq: opc = kMipsBeqz; break;
353 case kCondGe: opc = kMipsBgez; break;
354 case kCondGt: opc = kMipsBgtz; break;
355 case kCondLe: opc = kMipsBlez; break;
356 //case KCondMi:
357 case kCondLt: opc = kMipsBltz; break;
358 case kCondNe: opc = kMipsBnez; break;
359 default:
buzbee82488f52012-03-02 08:20:26 -0800360 // Tuning: use slti when applicable
buzbeee3acd072012-02-25 17:03:10 -0800361 int tReg = oatAllocTemp(cUnit);
buzbee5de34942012-03-01 14:51:57 -0800362 loadConstant(cUnit, tReg, checkValue);
buzbee82488f52012-03-02 08:20:26 -0800363 branch = opCmpBranch(cUnit, cond, reg, tReg, target);
364 oatFreeTemp(cUnit, tReg);
365 return branch;
buzbeee3acd072012-02-25 17:03:10 -0800366 }
buzbee82488f52012-03-02 08:20:26 -0800367 branch = newLIR1(cUnit, opc, reg);
368 branch->target = target;
369 return branch;
buzbeee3acd072012-02-25 17:03:10 -0800370}
371
buzbee82488f52012-03-02 08:20:26 -0800372LIR* opRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
buzbeee3acd072012-02-25 17:03:10 -0800373{
buzbee5de34942012-03-01 14:51:57 -0800374 LIR* res;
375 MipsOpCode opcode;
buzbeee3acd072012-02-25 17:03:10 -0800376#ifdef __mips_hard_float
buzbee5de34942012-03-01 14:51:57 -0800377 if (FPREG(rDest) || FPREG(rSrc))
378 return fpRegCopy(cUnit, rDest, rSrc);
379#endif
380 res = (LIR *) oatNew(cUnit, sizeof(LIR), true, kAllocLIR);
381 opcode = kMipsMove;
382 assert(LOWREG(rDest) && LOWREG(rSrc));
383 res->operands[0] = rDest;
384 res->operands[1] = rSrc;
385 res->opcode = opcode;
386 setupResourceMasks(res);
387 if (rDest == rSrc) {
388 res->flags.isNop = true;
389 }
390 return res;
391}
392
buzbee82488f52012-03-02 08:20:26 -0800393LIR* opRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
buzbee5de34942012-03-01 14:51:57 -0800394{
buzbee82488f52012-03-02 08:20:26 -0800395 LIR *res = opRegCopyNoInsert(cUnit, rDest, rSrc);
buzbee5de34942012-03-01 14:51:57 -0800396 oatAppendLIR(cUnit, (LIR*)res);
397 return res;
398}
399
buzbee82488f52012-03-02 08:20:26 -0800400void opRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
buzbee5de34942012-03-01 14:51:57 -0800401 int srcLo, int srcHi)
402{
403#ifdef __mips_hard_float
404 bool destFP = FPREG(destLo) && FPREG(destHi);
405 bool srcFP = FPREG(srcLo) && FPREG(srcHi);
406 assert(FPREG(srcLo) == FPREG(srcHi));
407 assert(FPREG(destLo) == FPREG(destHi));
408 if (destFP) {
409 if (srcFP) {
buzbee82488f52012-03-02 08:20:26 -0800410 opRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
buzbee5de34942012-03-01 14:51:57 -0800411 } else {
412 /* note the operands are swapped for the mtc1 instr */
413 newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
414 newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
415 }
416 } else {
417 if (srcFP) {
418 newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
419 newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
420 } else {
421 // Handle overlap
422 if (srcHi == destLo) {
buzbee82488f52012-03-02 08:20:26 -0800423 opRegCopy(cUnit, destHi, srcHi);
424 opRegCopy(cUnit, destLo, srcLo);
buzbee5de34942012-03-01 14:51:57 -0800425 } else {
buzbee82488f52012-03-02 08:20:26 -0800426 opRegCopy(cUnit, destLo, srcLo);
427 opRegCopy(cUnit, destHi, srcHi);
buzbee5de34942012-03-01 14:51:57 -0800428 }
429 }
430 }
buzbeee3acd072012-02-25 17:03:10 -0800431#else
buzbee5de34942012-03-01 14:51:57 -0800432 // Handle overlap
433 if (srcHi == destLo) {
buzbee82488f52012-03-02 08:20:26 -0800434 opRegCopy(cUnit, destHi, srcHi);
435 opRegCopy(cUnit, destLo, srcLo);
buzbee5de34942012-03-01 14:51:57 -0800436 } else {
buzbee82488f52012-03-02 08:20:26 -0800437 opRegCopy(cUnit, destLo, srcLo);
438 opRegCopy(cUnit, destHi, srcHi);
buzbee5de34942012-03-01 14:51:57 -0800439 }
buzbeee3acd072012-02-25 17:03:10 -0800440#endif
buzbeee3acd072012-02-25 17:03:10 -0800441}
442
443} // namespace art