blob: db34ce35bbe139f7850a6d74ed6146faf19bdc0a [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{
buzbee82488f52012-03-02 08:20:26 -0800277 LIR* branch;
buzbee5de34942012-03-01 14:51:57 -0800278 if (cond == kCondEq) {
buzbee82488f52012-03-02 08:20:26 -0800279 branch = newLIR2(cUnit, kMipsBeq, src1, src2);
buzbee5de34942012-03-01 14:51:57 -0800280 } else if (cond == kCondNe) {
buzbee82488f52012-03-02 08:20:26 -0800281 branch = newLIR2(cUnit, kMipsBne, src1, src2);
282 } else {
283 MipsOpCode sltOp;
284 MipsOpCode brOp;
285 bool swapped = false;
286 switch(cond) {
287 case kCondEq: return newLIR2(cUnit, kMipsBeq, src1, src2);
288 case kCondNe: return newLIR2(cUnit, kMipsBne, src1, src2);
289 case kCondCc:
290 sltOp = kMipsSltu;
291 brOp = kMipsBnez;
292 break;
293 case kCondGe:
294 sltOp = kMipsSlt;
295 brOp = kMipsBeqz;
296 break;
297 case kCondGt:
298 sltOp = kMipsSlt;
299 brOp = kMipsBnez;
300 swapped = true;
301 break;
302 case kCondLe:
303 sltOp = kMipsSlt;
304 brOp = kMipsBeqz;
305 swapped = true;
306 break;
307 case kCondLt:
308 sltOp = kMipsSlt;
309 brOp = kMipsBnez;
310 break;
311 default:
312 UNIMPLEMENTED(FATAL) << "No support for ConditionCode: "
313 << (int) cond;
314 return NULL;
315 }
316 int tReg = oatAllocTemp(cUnit);
317 if (swapped) {
318 newLIR3(cUnit, sltOp, tReg, src2, src1);
319 } else {
320 newLIR3(cUnit, sltOp, tReg, src1, src2);
321 }
322 branch = newLIR1(cUnit, brOp, tReg);
323 branch->target = target;
buzbee5de34942012-03-01 14:51:57 -0800324 }
buzbee82488f52012-03-02 08:20:26 -0800325 return branch;
buzbeee3acd072012-02-25 17:03:10 -0800326}
327
buzbee82488f52012-03-02 08:20:26 -0800328LIR* opCmpImmBranch(CompilationUnit* cUnit, ConditionCode cond, int reg,
329 int checkValue, LIR* target)
buzbeee3acd072012-02-25 17:03:10 -0800330{
buzbee82488f52012-03-02 08:20:26 -0800331 LIR* branch;
buzbee5de34942012-03-01 14:51:57 -0800332 if (checkValue != 0) {
333 // TUNING: handle s16 & kCondLt/Mi case using slti
334 int tReg = oatAllocTemp(cUnit);
335 loadConstant(cUnit, tReg, checkValue);
buzbee82488f52012-03-02 08:20:26 -0800336 branch = opCmpBranch(cUnit, cond, reg, tReg, target);
337 oatFreeTemp(cUnit, tReg);
338 return branch;
buzbee5de34942012-03-01 14:51:57 -0800339 }
340 MipsOpCode opc;
341 switch(cond) {
342 case kCondEq: opc = kMipsBeqz; break;
343 case kCondGe: opc = kMipsBgez; break;
344 case kCondGt: opc = kMipsBgtz; break;
345 case kCondLe: opc = kMipsBlez; break;
346 //case KCondMi:
347 case kCondLt: opc = kMipsBltz; break;
348 case kCondNe: opc = kMipsBnez; break;
349 default:
buzbee82488f52012-03-02 08:20:26 -0800350 // Tuning: use slti when applicable
buzbeee3acd072012-02-25 17:03:10 -0800351 int tReg = oatAllocTemp(cUnit);
buzbee5de34942012-03-01 14:51:57 -0800352 loadConstant(cUnit, tReg, checkValue);
buzbee82488f52012-03-02 08:20:26 -0800353 branch = opCmpBranch(cUnit, cond, reg, tReg, target);
354 oatFreeTemp(cUnit, tReg);
355 return branch;
buzbeee3acd072012-02-25 17:03:10 -0800356 }
buzbee82488f52012-03-02 08:20:26 -0800357 branch = newLIR1(cUnit, opc, reg);
358 branch->target = target;
359 return branch;
buzbeee3acd072012-02-25 17:03:10 -0800360}
361
buzbee82488f52012-03-02 08:20:26 -0800362LIR* opRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
buzbeee3acd072012-02-25 17:03:10 -0800363{
buzbee5de34942012-03-01 14:51:57 -0800364 LIR* res;
365 MipsOpCode opcode;
buzbeee3acd072012-02-25 17:03:10 -0800366#ifdef __mips_hard_float
buzbee5de34942012-03-01 14:51:57 -0800367 if (FPREG(rDest) || FPREG(rSrc))
368 return fpRegCopy(cUnit, rDest, rSrc);
369#endif
370 res = (LIR *) oatNew(cUnit, sizeof(LIR), true, kAllocLIR);
371 opcode = kMipsMove;
372 assert(LOWREG(rDest) && LOWREG(rSrc));
373 res->operands[0] = rDest;
374 res->operands[1] = rSrc;
375 res->opcode = opcode;
376 setupResourceMasks(res);
377 if (rDest == rSrc) {
378 res->flags.isNop = true;
379 }
380 return res;
381}
382
buzbee82488f52012-03-02 08:20:26 -0800383LIR* opRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
buzbee5de34942012-03-01 14:51:57 -0800384{
buzbee82488f52012-03-02 08:20:26 -0800385 LIR *res = opRegCopyNoInsert(cUnit, rDest, rSrc);
buzbee5de34942012-03-01 14:51:57 -0800386 oatAppendLIR(cUnit, (LIR*)res);
387 return res;
388}
389
buzbee82488f52012-03-02 08:20:26 -0800390void opRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
buzbee5de34942012-03-01 14:51:57 -0800391 int srcLo, int srcHi)
392{
393#ifdef __mips_hard_float
394 bool destFP = FPREG(destLo) && FPREG(destHi);
395 bool srcFP = FPREG(srcLo) && FPREG(srcHi);
396 assert(FPREG(srcLo) == FPREG(srcHi));
397 assert(FPREG(destLo) == FPREG(destHi));
398 if (destFP) {
399 if (srcFP) {
buzbee82488f52012-03-02 08:20:26 -0800400 opRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
buzbee5de34942012-03-01 14:51:57 -0800401 } else {
402 /* note the operands are swapped for the mtc1 instr */
403 newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
404 newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
405 }
406 } else {
407 if (srcFP) {
408 newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
409 newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
410 } else {
411 // Handle overlap
412 if (srcHi == destLo) {
buzbee82488f52012-03-02 08:20:26 -0800413 opRegCopy(cUnit, destHi, srcHi);
414 opRegCopy(cUnit, destLo, srcLo);
buzbee5de34942012-03-01 14:51:57 -0800415 } else {
buzbee82488f52012-03-02 08:20:26 -0800416 opRegCopy(cUnit, destLo, srcLo);
417 opRegCopy(cUnit, destHi, srcHi);
buzbee5de34942012-03-01 14:51:57 -0800418 }
419 }
420 }
buzbeee3acd072012-02-25 17:03:10 -0800421#else
buzbee5de34942012-03-01 14:51:57 -0800422 // Handle overlap
423 if (srcHi == destLo) {
buzbee82488f52012-03-02 08:20:26 -0800424 opRegCopy(cUnit, destHi, srcHi);
425 opRegCopy(cUnit, destLo, srcLo);
buzbee5de34942012-03-01 14:51:57 -0800426 } else {
buzbee82488f52012-03-02 08:20:26 -0800427 opRegCopy(cUnit, destLo, srcLo);
428 opRegCopy(cUnit, destHi, srcHi);
buzbee5de34942012-03-01 14:51:57 -0800429 }
buzbeee3acd072012-02-25 17:03:10 -0800430#endif
buzbeee3acd072012-02-25 17:03:10 -0800431}
432
433} // namespace art