blob: 1db4eee53bbd77177102a85fbdfde265728ae1ca [file] [log] [blame]
buzbeeefc63692012-11-14 16:31:52 -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/* This file contains codegen for the Mips ISA */
18
19#include "oat/runtime/oat_support_entrypoints.h"
20
21namespace art {
22
23void genSpecialCase(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir,
24 SpecialCaseHandler specialCase)
25{
26 // TODO
27}
28
29/*
30 * The lack of pc-relative loads on Mips presents somewhat of a challenge
31 * for our PIC switch table strategy. To materialize the current location
32 * we'll do a dummy JAL and reference our tables using r_RA as the
33 * base register. Note that r_RA will be used both as the base to
34 * locate the switch table data and as the reference base for the switch
35 * target offsets stored in the table. We'll use a special pseudo-instruction
36 * to represent the jal and trigger the construction of the
37 * switch table offsets (which will happen after final assembly and all
38 * labels are fixed).
39 *
40 * The test loop will look something like:
41 *
42 * ori rEnd, r_ZERO, #tableSize ; size in bytes
43 * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA
44 * nop ; opportunistically fill
45 * BaseLabel:
46 * addiu rBase, r_RA, <table> - <BaseLabel> ; table relative to BaseLabel
47 addu rEnd, rEnd, rBase ; end of table
48 * lw rVal, [rSP, vRegOff] ; Test Value
49 * loop:
50 * beq rBase, rEnd, done
51 * lw rKey, 0(rBase)
52 * addu rBase, 8
53 * bne rVal, rKey, loop
54 * lw rDisp, -4(rBase)
55 * addu r_RA, rDisp
56 * jr r_RA
57 * done:
58 *
59 */
60void genSparseSwitch(CompilationUnit* cUnit, uint32_t tableOffset,
61 RegLocation rlSrc)
62{
buzbeeeaf09bc2012-11-15 14:51:41 -080063 const uint16_t* table = cUnit->insns + cUnit->currentDalvikOffset + tableOffset;
buzbeeefc63692012-11-14 16:31:52 -080064 if (cUnit->printMe) {
65 dumpSparseSwitchTable(table);
66 }
67 // Add the table to the list - we'll process it later
buzbeecbd6d442012-11-17 14:11:25 -080068 SwitchTable *tabRec =
69 static_cast<SwitchTable*>(oatNew(cUnit, sizeof(SwitchTable), true, kAllocData));
buzbeeefc63692012-11-14 16:31:52 -080070 tabRec->table = table;
71 tabRec->vaddr = cUnit->currentDalvikOffset;
72 int elements = table[1];
buzbeecbd6d442012-11-17 14:11:25 -080073 tabRec->targets =
74 static_cast<LIR**>(oatNew(cUnit, elements * sizeof(LIR*), true, kAllocLIR));
75 oatInsertGrowableList(cUnit, &cUnit->switchTables, reinterpret_cast<uintptr_t>(tabRec));
buzbeeefc63692012-11-14 16:31:52 -080076
77 // The table is composed of 8-byte key/disp pairs
78 int byteSize = elements * 8;
79
80 int sizeHi = byteSize >> 16;
81 int sizeLo = byteSize & 0xffff;
82
83 int rEnd = oatAllocTemp(cUnit);
84 if (sizeHi) {
85 newLIR2(cUnit, kMipsLui, rEnd, sizeHi);
86 }
87 // Must prevent code motion for the curr pc pair
88 genBarrier(cUnit); // Scheduling barrier
89 newLIR0(cUnit, kMipsCurrPC); // Really a jal to .+8
90 // Now, fill the branch delay slot
91 if (sizeHi) {
92 newLIR3(cUnit, kMipsOri, rEnd, rEnd, sizeLo);
93 } else {
94 newLIR3(cUnit, kMipsOri, rEnd, r_ZERO, sizeLo);
95 }
96 genBarrier(cUnit); // Scheduling barrier
97
98 // Construct BaseLabel and set up table base register
99 LIR* baseLabel = newLIR0(cUnit, kPseudoTargetLabel);
100 // Remember base label so offsets can be computed later
101 tabRec->anchor = baseLabel;
102 int rBase = oatAllocTemp(cUnit);
buzbeecbd6d442012-11-17 14:11:25 -0800103 newLIR4(cUnit, kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(baseLabel),
104 reinterpret_cast<uintptr_t>(tabRec));
buzbeeefc63692012-11-14 16:31:52 -0800105 opRegRegReg(cUnit, kOpAdd, rEnd, rEnd, rBase);
106
107 // Grab switch test value
108 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
109
110 // Test loop
111 int rKey = oatAllocTemp(cUnit);
112 LIR* loopLabel = newLIR0(cUnit, kPseudoTargetLabel);
113 LIR* exitBranch = opCmpBranch(cUnit , kCondEq, rBase, rEnd, NULL);
114 loadWordDisp(cUnit, rBase, 0, rKey);
115 opRegImm(cUnit, kOpAdd, rBase, 8);
116 opCmpBranch(cUnit, kCondNe, rlSrc.lowReg, rKey, loopLabel);
117 int rDisp = oatAllocTemp(cUnit);
118 loadWordDisp(cUnit, rBase, -4, rDisp);
119 opRegRegReg(cUnit, kOpAdd, r_RA, r_RA, rDisp);
120 opReg(cUnit, kOpBx, r_RA);
121
122 // Loop exit
123 LIR* exitLabel = newLIR0(cUnit, kPseudoTargetLabel);
124 exitBranch->target = exitLabel;
125}
126
127/*
128 * Code pattern will look something like:
129 *
130 * lw rVal
131 * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA
132 * nop ; opportunistically fill
133 * [subiu rVal, bias] ; Remove bias if lowVal != 0
134 * bound check -> done
135 * lw rDisp, [r_RA, rVal]
136 * addu r_RA, rDisp
137 * jr r_RA
138 * done:
139 */
140void genPackedSwitch(CompilationUnit* cUnit, uint32_t tableOffset,
141 RegLocation rlSrc)
142{
buzbeeeaf09bc2012-11-15 14:51:41 -0800143 const uint16_t* table = cUnit->insns + cUnit->currentDalvikOffset + tableOffset;
buzbeeefc63692012-11-14 16:31:52 -0800144 if (cUnit->printMe) {
145 dumpPackedSwitchTable(table);
146 }
147 // Add the table to the list - we'll process it later
buzbeecbd6d442012-11-17 14:11:25 -0800148 SwitchTable *tabRec =
149 static_cast<SwitchTable*>(oatNew(cUnit, sizeof(SwitchTable), true, kAllocData));
buzbeeefc63692012-11-14 16:31:52 -0800150 tabRec->table = table;
151 tabRec->vaddr = cUnit->currentDalvikOffset;
152 int size = table[1];
buzbeecbd6d442012-11-17 14:11:25 -0800153 tabRec->targets = static_cast<LIR**>(oatNew(cUnit, size * sizeof(LIR*), true, kAllocLIR));
154 oatInsertGrowableList(cUnit, &cUnit->switchTables, reinterpret_cast<uintptr_t>(tabRec));
buzbeeefc63692012-11-14 16:31:52 -0800155
156 // Get the switch value
157 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
158
159 // Prepare the bias. If too big, handle 1st stage here
160 int lowKey = s4FromSwitchData(&table[2]);
161 bool largeBias = false;
162 int rKey;
163 if (lowKey == 0) {
164 rKey = rlSrc.lowReg;
165 } else if ((lowKey & 0xffff) != lowKey) {
166 rKey = oatAllocTemp(cUnit);
167 loadConstant(cUnit, rKey, lowKey);
168 largeBias = true;
169 } else {
170 rKey = oatAllocTemp(cUnit);
171 }
172
173 // Must prevent code motion for the curr pc pair
174 genBarrier(cUnit);
175 newLIR0(cUnit, kMipsCurrPC); // Really a jal to .+8
176 // Now, fill the branch delay slot with bias strip
177 if (lowKey == 0) {
178 newLIR0(cUnit, kMipsNop);
179 } else {
180 if (largeBias) {
181 opRegRegReg(cUnit, kOpSub, rKey, rlSrc.lowReg, rKey);
182 } else {
183 opRegRegImm(cUnit, kOpSub, rKey, rlSrc.lowReg, lowKey);
184 }
185 }
186 genBarrier(cUnit); // Scheduling barrier
187
188 // Construct BaseLabel and set up table base register
189 LIR* baseLabel = newLIR0(cUnit, kPseudoTargetLabel);
190 // Remember base label so offsets can be computed later
191 tabRec->anchor = baseLabel;
192
193 // Bounds check - if < 0 or >= size continue following switch
194 LIR* branchOver = opCmpImmBranch(cUnit, kCondHi, rKey, size-1, NULL);
195
196 // Materialize the table base pointer
197 int rBase = oatAllocTemp(cUnit);
buzbeecbd6d442012-11-17 14:11:25 -0800198 newLIR4(cUnit, kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(baseLabel),
199 reinterpret_cast<uintptr_t>(tabRec));
buzbeeefc63692012-11-14 16:31:52 -0800200
201 // Load the displacement from the switch table
202 int rDisp = oatAllocTemp(cUnit);
203 loadBaseIndexed(cUnit, rBase, rKey, rDisp, 2, kWord);
204
205 // Add to r_AP and go
206 opRegRegReg(cUnit, kOpAdd, r_RA, r_RA, rDisp);
207 opReg(cUnit, kOpBx, r_RA);
208
209 /* branchOver target here */
210 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
buzbeecbd6d442012-11-17 14:11:25 -0800211 branchOver->target = target;
buzbeeefc63692012-11-14 16:31:52 -0800212}
213
214/*
215 * Array data table format:
216 * ushort ident = 0x0300 magic value
217 * ushort width width of each element in the table
218 * uint size number of elements in the table
219 * ubyte data[size*width] table of data values (may contain a single-byte
220 * padding at the end)
221 *
222 * Total size is 4+(width * size + 1)/2 16-bit code units.
223 */
224void genFillArrayData(CompilationUnit* cUnit, uint32_t tableOffset,
225 RegLocation rlSrc)
226{
buzbeeeaf09bc2012-11-15 14:51:41 -0800227 const uint16_t* table = cUnit->insns + cUnit->currentDalvikOffset + tableOffset;
buzbeeefc63692012-11-14 16:31:52 -0800228 // Add the table to the list - we'll process it later
buzbeecbd6d442012-11-17 14:11:25 -0800229 FillArrayData *tabRec =
230 reinterpret_cast<FillArrayData*>(oatNew(cUnit, sizeof(FillArrayData), true, kAllocData));
buzbeeefc63692012-11-14 16:31:52 -0800231 tabRec->table = table;
232 tabRec->vaddr = cUnit->currentDalvikOffset;
buzbeeeaf09bc2012-11-15 14:51:41 -0800233 uint16_t width = tabRec->table[1];
234 uint32_t size = tabRec->table[2] | ((static_cast<uint32_t>(tabRec->table[3])) << 16);
buzbeeefc63692012-11-14 16:31:52 -0800235 tabRec->size = (size * width) + 8;
236
buzbeecbd6d442012-11-17 14:11:25 -0800237 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, reinterpret_cast<uintptr_t>(tabRec));
buzbeeefc63692012-11-14 16:31:52 -0800238
239 // Making a call - use explicit registers
240 oatFlushAllRegs(cUnit); /* Everything to home location */
241 oatLockCallTemps(cUnit);
242 loadValueDirectFixed(cUnit, rlSrc, rMIPS_ARG0);
243
244 // Must prevent code motion for the curr pc pair
245 genBarrier(cUnit);
246 newLIR0(cUnit, kMipsCurrPC); // Really a jal to .+8
247 // Now, fill the branch delay slot with the helper load
248 int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode));
249 genBarrier(cUnit); // Scheduling barrier
250
251 // Construct BaseLabel and set up table base register
252 LIR* baseLabel = newLIR0(cUnit, kPseudoTargetLabel);
253
254 // Materialize a pointer to the fill data image
buzbeecbd6d442012-11-17 14:11:25 -0800255 newLIR4(cUnit, kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(baseLabel),
256 reinterpret_cast<uintptr_t>(tabRec));
buzbeeefc63692012-11-14 16:31:52 -0800257
258 // And go...
259 oatClobberCalleeSave(cUnit);
260 LIR* callInst = opReg(cUnit, kOpBlx, rTgt); // ( array*, fill_data* )
261 markSafepointPC(cUnit, callInst);
262}
263
264/*
265 * TODO: implement fast path to short-circuit thin-lock case
266 */
267void genMonitorEnter(CompilationUnit* cUnit, int optFlags, RegLocation rlSrc)
268{
269 oatFlushAllRegs(cUnit);
270 loadValueDirectFixed(cUnit, rlSrc, rMIPS_ARG0); // Get obj
271 oatLockCallTemps(cUnit); // Prepare for explicit register usage
272 genNullCheck(cUnit, rlSrc.sRegLow, rMIPS_ARG0, optFlags);
273 // Go expensive route - artLockObjectFromCode(self, obj);
274 int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pLockObjectFromCode));
275 oatClobberCalleeSave(cUnit);
276 LIR* callInst = opReg(cUnit, kOpBlx, rTgt);
277 markSafepointPC(cUnit, callInst);
278}
279
280/*
281 * TODO: implement fast path to short-circuit thin-lock case
282 */
283void genMonitorExit(CompilationUnit* cUnit, int optFlags, RegLocation rlSrc)
284{
285 oatFlushAllRegs(cUnit);
286 loadValueDirectFixed(cUnit, rlSrc, rMIPS_ARG0); // Get obj
287 oatLockCallTemps(cUnit); // Prepare for explicit register usage
288 genNullCheck(cUnit, rlSrc.sRegLow, rMIPS_ARG0, optFlags);
289 // Go expensive route - UnlockObjectFromCode(obj);
290 int rTgt = loadHelper(cUnit, ENTRYPOINT_OFFSET(pUnlockObjectFromCode));
291 oatClobberCalleeSave(cUnit);
292 LIR* callInst = opReg(cUnit, kOpBlx, rTgt);
293 markSafepointPC(cUnit, callInst);
294}
295
296/*
297 * Mark garbage collection card. Skip if the value we're storing is null.
298 */
299void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
300{
301 int regCardBase = oatAllocTemp(cUnit);
302 int regCardNo = oatAllocTemp(cUnit);
303 LIR* branchOver = opCmpImmBranch(cUnit, kCondEq, valReg, 0, NULL);
304 loadWordDisp(cUnit, rMIPS_SELF, Thread::CardTableOffset().Int32Value(), regCardBase);
305 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, CardTable::kCardShift);
306 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
307 kUnsignedByte);
308 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
buzbeecbd6d442012-11-17 14:11:25 -0800309 branchOver->target = target;
buzbeeefc63692012-11-14 16:31:52 -0800310 oatFreeTemp(cUnit, regCardBase);
311 oatFreeTemp(cUnit, regCardNo);
312}
313void genEntrySequence(CompilationUnit* cUnit, RegLocation* argLocs,
314 RegLocation rlMethod)
315{
316 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
317 /*
318 * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live. Let the register
319 * allocation mechanism know so it doesn't try to use any of them when
320 * expanding the frame or flushing. This leaves the utility
321 * code with a single temp: r12. This should be enough.
322 */
323 oatLockTemp(cUnit, rMIPS_ARG0);
324 oatLockTemp(cUnit, rMIPS_ARG1);
325 oatLockTemp(cUnit, rMIPS_ARG2);
326 oatLockTemp(cUnit, rMIPS_ARG3);
327
328 /*
329 * We can safely skip the stack overflow check if we're
330 * a leaf *and* our frame size < fudge factor.
331 */
332 bool skipOverflowCheck = ((cUnit->attrs & METHOD_IS_LEAF) &&
buzbeecbd6d442012-11-17 14:11:25 -0800333 (static_cast<size_t>(cUnit->frameSize) < Thread::kStackOverflowReservedBytes));
buzbeeefc63692012-11-14 16:31:52 -0800334 newLIR0(cUnit, kPseudoMethodEntry);
335 int checkReg = oatAllocTemp(cUnit);
336 int newSP = oatAllocTemp(cUnit);
337 if (!skipOverflowCheck) {
338 /* Load stack limit */
339 loadWordDisp(cUnit, rMIPS_SELF, Thread::StackEndOffset().Int32Value(), checkReg);
340 }
341 /* Spill core callee saves */
342 spillCoreRegs(cUnit);
343 /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
344 DCHECK_EQ(cUnit->numFPSpills, 0);
345 if (!skipOverflowCheck) {
346 opRegRegImm(cUnit, kOpSub, newSP, rMIPS_SP, cUnit->frameSize - (spillCount * 4));
347 genRegRegCheck(cUnit, kCondCc, newSP, checkReg, kThrowStackOverflow);
348 opRegCopy(cUnit, rMIPS_SP, newSP); // Establish stack
349 } else {
350 opRegImm(cUnit, kOpSub, rMIPS_SP, cUnit->frameSize - (spillCount * 4));
351 }
352
353 flushIns(cUnit, argLocs, rlMethod);
354
355 oatFreeTemp(cUnit, rMIPS_ARG0);
356 oatFreeTemp(cUnit, rMIPS_ARG1);
357 oatFreeTemp(cUnit, rMIPS_ARG2);
358 oatFreeTemp(cUnit, rMIPS_ARG3);
359}
360
361void genExitSequence(CompilationUnit* cUnit)
362{
363 /*
364 * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't
365 * allocated by the register utilities as temps.
366 */
367 oatLockTemp(cUnit, rMIPS_RET0);
368 oatLockTemp(cUnit, rMIPS_RET1);
369
370 newLIR0(cUnit, kPseudoMethodExit);
371 unSpillCoreRegs(cUnit);
372 opReg(cUnit, kOpBx, r_RA);
373}
374
375} // namespace art