blob: 92b067c51ab92a68e2681604d81d911d11ebd4e8 [file] [log] [blame]
buzbeeefc63692012-11-14 16:31:52 -08001/*
2 * Copyright (C) 2011 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 Thumb2 ISA. */
18
19#include "oat_compilation_unit.h"
20#include "oat/runtime/oat_support_entrypoints.h"
21
22namespace art {
23
24
25/* Return the position of an ssa name within the argument list */
26int inPosition(CompilationUnit* cUnit, int sReg)
27{
28 int vReg = SRegToVReg(cUnit, sReg);
29 return vReg - cUnit->numRegs;
30}
31
32/*
33 * Describe an argument. If it's already in an arg register, just leave it
34 * there. NOTE: all live arg registers must be locked prior to this call
35 * to avoid having them allocated as a temp by downstream utilities.
36 */
37RegLocation argLoc(CompilationUnit* cUnit, RegLocation loc)
38{
39 int argNum = inPosition(cUnit, loc.sRegLow);
40 if (loc.wide) {
41 if (argNum == 2) {
42 // Bad case - half in register, half in frame. Just punt
43 loc.location = kLocInvalid;
44 } else if (argNum < 2) {
45 loc.lowReg = rARM_ARG1 + argNum;
46 loc.highReg = loc.lowReg + 1;
47 loc.location = kLocPhysReg;
48 } else {
49 loc.location = kLocDalvikFrame;
50 }
51 } else {
52 if (argNum < 3) {
53 loc.lowReg = rARM_ARG1 + argNum;
54 loc.location = kLocPhysReg;
55 } else {
56 loc.location = kLocDalvikFrame;
57 }
58 }
59 return loc;
60}
61
62/*
63 * Load an argument. If already in a register, just return. If in
64 * the frame, we can't use the normal loadValue() because it assumed
65 * a proper frame - and we're frameless.
66 */
67RegLocation loadArg(CompilationUnit* cUnit, RegLocation loc)
68{
69 if (loc.location == kLocDalvikFrame) {
70 int start = (inPosition(cUnit, loc.sRegLow) + 1) * sizeof(uint32_t);
71 loc.lowReg = oatAllocTemp(cUnit);
72 loadWordDisp(cUnit, rARM_SP, start, loc.lowReg);
73 if (loc.wide) {
74 loc.highReg = oatAllocTemp(cUnit);
75 loadWordDisp(cUnit, rARM_SP, start + sizeof(uint32_t), loc.highReg);
76 }
77 loc.location = kLocPhysReg;
78 }
79 return loc;
80}
81
82/* Lock any referenced arguments that arrive in registers */
83void lockLiveArgs(CompilationUnit* cUnit, MIR* mir)
84{
85 int firstIn = cUnit->numRegs;
86 const int numArgRegs = 3; // TODO: generalize & move to RegUtil.cc
87 for (int i = 0; i < mir->ssaRep->numUses; i++) {
88 int vReg = SRegToVReg(cUnit, mir->ssaRep->uses[i]);
89 int inPosition = vReg - firstIn;
90 if (inPosition < numArgRegs) {
91 oatLockTemp(cUnit, rARM_ARG1 + inPosition);
92 }
93 }
94}
95
96/* Find the next MIR, which may be in a following basic block */
97MIR* getNextMir(CompilationUnit* cUnit, BasicBlock** pBb, MIR* mir)
98{
99 BasicBlock* bb = *pBb;
100 MIR* origMir = mir;
101 while (bb != NULL) {
102 if (mir != NULL) {
103 mir = mir->next;
104 }
105 if (mir != NULL) {
106 return mir;
107 } else {
108 bb = bb->fallThrough;
109 *pBb = bb;
110 if (bb) {
111 mir = bb->firstMIRInsn;
112 if (mir != NULL) {
113 return mir;
114 }
115 }
116 }
117 }
118 return origMir;
119}
120
121/* Used for the "printMe" listing */
122void genPrintLabel(CompilationUnit *cUnit, MIR* mir)
123{
124 /* Mark the beginning of a Dalvik instruction for line tracking */
125 char* instStr = cUnit->printMe ?
126 oatGetDalvikDisassembly(cUnit, mir->dalvikInsn, "") : NULL;
127 markBoundary(cUnit, mir->offset, instStr);
128 /* Don't generate the SSA annotation unless verbose mode is on */
129 if (cUnit->printMe && mir->ssaRep) {
130 char* ssaString = oatGetSSAString(cUnit, mir->ssaRep);
131 newLIR1(cUnit, kPseudoSSARep, (int) ssaString);
132 }
133}
134
135MIR* specialIGet(CompilationUnit* cUnit, BasicBlock** bb, MIR* mir,
136 OpSize size, bool longOrDouble, bool isObject)
137{
138 int fieldOffset;
139 bool isVolatile;
140 uint32_t fieldIdx = mir->dalvikInsn.vC;
141 bool fastPath = fastInstance(cUnit, fieldIdx, fieldOffset, isVolatile, false);
142 if (!fastPath || !(mir->optimizationFlags & MIR_IGNORE_NULL_CHECK)) {
143 return NULL;
144 }
145 RegLocation rlObj = oatGetSrc(cUnit, mir, 0);
146 lockLiveArgs(cUnit, mir);
147 rlObj = argLoc(cUnit, rlObj);
148 RegLocation rlDest;
149 if (longOrDouble) {
150 rlDest = oatGetReturnWide(cUnit, false);
151 } else {
152 rlDest = oatGetReturn(cUnit, false);
153 }
154 // Point of no return - no aborts after this
155 genPrintLabel(cUnit, mir);
156 rlObj = loadArg(cUnit, rlObj);
157 genIGet(cUnit, fieldIdx, mir->optimizationFlags, size, rlDest, rlObj,
158 longOrDouble, isObject);
159 return getNextMir(cUnit, bb, mir);
160}
161
162MIR* specialIPut(CompilationUnit* cUnit, BasicBlock** bb, MIR* mir,
163 OpSize size, bool longOrDouble, bool isObject)
164{
165 int fieldOffset;
166 bool isVolatile;
167 uint32_t fieldIdx = mir->dalvikInsn.vC;
168 bool fastPath = fastInstance(cUnit, fieldIdx, fieldOffset, isVolatile, false);
169 if (!fastPath || !(mir->optimizationFlags & MIR_IGNORE_NULL_CHECK)) {
170 return NULL;
171 }
172 RegLocation rlSrc;
173 RegLocation rlObj;
174 lockLiveArgs(cUnit, mir);
175 if (longOrDouble) {
176 rlSrc = oatGetSrcWide(cUnit, mir, 0);
177 rlObj = oatGetSrc(cUnit, mir, 2);
178 } else {
179 rlSrc = oatGetSrc(cUnit, mir, 0);
180 rlObj = oatGetSrc(cUnit, mir, 1);
181 }
182 rlSrc = argLoc(cUnit, rlSrc);
183 rlObj = argLoc(cUnit, rlObj);
184 // Reject if source is split across registers & frame
185 if (rlObj.location == kLocInvalid) {
186 oatResetRegPool(cUnit);
187 return NULL;
188 }
189 // Point of no return - no aborts after this
190 genPrintLabel(cUnit, mir);
191 rlObj = loadArg(cUnit, rlObj);
192 rlSrc = loadArg(cUnit, rlSrc);
193 genIPut(cUnit, fieldIdx, mir->optimizationFlags, size, rlSrc, rlObj,
194 longOrDouble, isObject);
195 return getNextMir(cUnit, bb, mir);
196}
197
198MIR* specialIdentity(CompilationUnit* cUnit, MIR* mir)
199{
200 RegLocation rlSrc;
201 RegLocation rlDest;
202 bool wide = (mir->ssaRep->numUses == 2);
203 if (wide) {
204 rlSrc = oatGetSrcWide(cUnit, mir, 0);
205 rlDest = oatGetReturnWide(cUnit, false);
206 } else {
207 rlSrc = oatGetSrc(cUnit, mir, 0);
208 rlDest = oatGetReturn(cUnit, false);
209 }
210 lockLiveArgs(cUnit, mir);
211 rlSrc = argLoc(cUnit, rlSrc);
212 if (rlSrc.location == kLocInvalid) {
213 oatResetRegPool(cUnit);
214 return NULL;
215 }
216 // Point of no return - no aborts after this
217 genPrintLabel(cUnit, mir);
218 rlSrc = loadArg(cUnit, rlSrc);
219 if (wide) {
220 storeValueWide(cUnit, rlDest, rlSrc);
221 } else {
222 storeValue(cUnit, rlDest, rlSrc);
223 }
224 return mir;
225}
226
227/*
228 * Special-case code genration for simple non-throwing leaf methods.
229 */
230void genSpecialCase(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir,
231 SpecialCaseHandler specialCase)
232{
233 cUnit->currentDalvikOffset = mir->offset;
234 MIR* nextMir = NULL;
235 switch (specialCase) {
236 case kNullMethod:
237 DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID);
238 nextMir = mir;
239 break;
240 case kConstFunction:
241 genPrintLabel(cUnit, mir);
242 loadConstant(cUnit, rARM_RET0, mir->dalvikInsn.vB);
243 nextMir = getNextMir(cUnit, &bb, mir);
244 break;
245 case kIGet:
246 nextMir = specialIGet(cUnit, &bb, mir, kWord, false, false);
247 break;
248 case kIGetBoolean:
249 case kIGetByte:
250 nextMir = specialIGet(cUnit, &bb, mir, kUnsignedByte, false, false);
251 break;
252 case kIGetObject:
253 nextMir = specialIGet(cUnit, &bb, mir, kWord, false, true);
254 break;
255 case kIGetChar:
256 nextMir = specialIGet(cUnit, &bb, mir, kUnsignedHalf, false, false);
257 break;
258 case kIGetShort:
259 nextMir = specialIGet(cUnit, &bb, mir, kSignedHalf, false, false);
260 break;
261 case kIGetWide:
262 nextMir = specialIGet(cUnit, &bb, mir, kLong, true, false);
263 break;
264 case kIPut:
265 nextMir = specialIPut(cUnit, &bb, mir, kWord, false, false);
266 break;
267 case kIPutBoolean:
268 case kIPutByte:
269 nextMir = specialIPut(cUnit, &bb, mir, kUnsignedByte, false, false);
270 break;
271 case kIPutObject:
272 nextMir = specialIPut(cUnit, &bb, mir, kWord, false, true);
273 break;
274 case kIPutChar:
275 nextMir = specialIPut(cUnit, &bb, mir, kUnsignedHalf, false, false);
276 break;
277 case kIPutShort:
278 nextMir = specialIPut(cUnit, &bb, mir, kSignedHalf, false, false);
279 break;
280 case kIPutWide:
281 nextMir = specialIPut(cUnit, &bb, mir, kLong, true, false);
282 break;
283 case kIdentity:
284 nextMir = specialIdentity(cUnit, mir);
285 break;
286 default:
287 return;
288 }
289 if (nextMir != NULL) {
290 cUnit->currentDalvikOffset = nextMir->offset;
291 if (specialCase != kIdentity) {
292 genPrintLabel(cUnit, nextMir);
293 }
294 newLIR1(cUnit, kThumbBx, rARM_LR);
295 cUnit->coreSpillMask = 0;
296 cUnit->numCoreSpills = 0;
297 cUnit->fpSpillMask = 0;
298 cUnit->numFPSpills = 0;
299 cUnit->frameSize = 0;
300 cUnit->coreVmapTable.clear();
301 cUnit->fpVmapTable.clear();
302 }
303}
304
305/*
306 * The sparse table in the literal pool is an array of <key,displacement>
307 * pairs. For each set, we'll load them as a pair using ldmia.
308 * This means that the register number of the temp we use for the key
309 * must be lower than the reg for the displacement.
310 *
311 * The test loop will look something like:
312 *
313 * adr rBase, <table>
314 * ldr rVal, [rARM_SP, vRegOff]
315 * mov rIdx, #tableSize
316 * lp:
317 * ldmia rBase!, {rKey, rDisp}
318 * sub rIdx, #1
319 * cmp rVal, rKey
320 * ifeq
321 * add rARM_PC, rDisp ; This is the branch from which we compute displacement
322 * cbnz rIdx, lp
323 */
324void genSparseSwitch(CompilationUnit* cUnit, uint32_t tableOffset,
325 RegLocation rlSrc)
326{
buzbeeeaf09bc2012-11-15 14:51:41 -0800327 const uint16_t* table = cUnit->insns + cUnit->currentDalvikOffset + tableOffset;
buzbeeefc63692012-11-14 16:31:52 -0800328 if (cUnit->printMe) {
329 dumpSparseSwitchTable(table);
330 }
331 // Add the table to the list - we'll process it later
332 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
333 true, kAllocData);
334 tabRec->table = table;
335 tabRec->vaddr = cUnit->currentDalvikOffset;
336 int size = table[1];
337 tabRec->targets = (LIR* *)oatNew(cUnit, size * sizeof(LIR*), true, kAllocLIR);
338 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
339
340 // Get the switch value
341 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
342 int rBase = oatAllocTemp(cUnit);
343 /* Allocate key and disp temps */
344 int rKey = oatAllocTemp(cUnit);
345 int rDisp = oatAllocTemp(cUnit);
346 // Make sure rKey's register number is less than rDisp's number for ldmia
347 if (rKey > rDisp) {
348 int tmp = rDisp;
349 rDisp = rKey;
350 rKey = tmp;
351 }
352 // Materialize a pointer to the switch table
353 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
354 // Set up rIdx
355 int rIdx = oatAllocTemp(cUnit);
356 loadConstant(cUnit, rIdx, size);
357 // Establish loop branch target
358 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
359 // Load next key/disp
360 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
361 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
362 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
363 opIT(cUnit, kArmCondEq, "");
364 LIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
365 tabRec->anchor = switchBranch;
366 // Needs to use setflags encoding here
367 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
368 opCondBranch(cUnit, kCondNe, target);
369}
370
371
372void genPackedSwitch(CompilationUnit* cUnit, uint32_t tableOffset,
373 RegLocation rlSrc)
374{
buzbeeeaf09bc2012-11-15 14:51:41 -0800375 const uint16_t* table = cUnit->insns + cUnit->currentDalvikOffset + tableOffset;
buzbeeefc63692012-11-14 16:31:52 -0800376 if (cUnit->printMe) {
377 dumpPackedSwitchTable(table);
378 }
379 // Add the table to the list - we'll process it later
380 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
381 true, kAllocData);
382 tabRec->table = table;
383 tabRec->vaddr = cUnit->currentDalvikOffset;
384 int size = table[1];
385 tabRec->targets = (LIR* *)oatNew(cUnit, size * sizeof(LIR*), true, kAllocLIR);
386 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
387
388 // Get the switch value
389 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
390 int tableBase = oatAllocTemp(cUnit);
391 // Materialize a pointer to the switch table
392 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
393 int lowKey = s4FromSwitchData(&table[2]);
394 int keyReg;
395 // Remove the bias, if necessary
396 if (lowKey == 0) {
397 keyReg = rlSrc.lowReg;
398 } else {
399 keyReg = oatAllocTemp(cUnit);
400 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
401 }
402 // Bounds check - if < 0 or >= size continue following switch
403 opRegImm(cUnit, kOpCmp, keyReg, size-1);
404 LIR* branchOver = opCondBranch(cUnit, kCondHi, NULL);
405
406 // Load the displacement from the switch table
407 int dispReg = oatAllocTemp(cUnit);
408 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
409
410 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
411 LIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
412 tabRec->anchor = switchBranch;
413
414 /* branchOver target here */
415 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
416 branchOver->target = (LIR*)target;
417}
418
419/*
420 * Array data table format:
421 * ushort ident = 0x0300 magic value
422 * ushort width width of each element in the table
423 * uint size number of elements in the table
424 * ubyte data[size*width] table of data values (may contain a single-byte
425 * padding at the end)
426 *
427 * Total size is 4+(width * size + 1)/2 16-bit code units.
428 */
429void genFillArrayData(CompilationUnit* cUnit, uint32_t tableOffset, RegLocation rlSrc)
430{
buzbeeeaf09bc2012-11-15 14:51:41 -0800431 const uint16_t* table = cUnit->insns + cUnit->currentDalvikOffset + tableOffset;
buzbeeefc63692012-11-14 16:31:52 -0800432 // Add the table to the list - we'll process it later
433 FillArrayData *tabRec = (FillArrayData *)
434 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
435 tabRec->table = table;
436 tabRec->vaddr = cUnit->currentDalvikOffset;
buzbeeeaf09bc2012-11-15 14:51:41 -0800437 uint16_t width = tabRec->table[1];
438 uint32_t size = tabRec->table[2] | ((static_cast<uint32_t>(tabRec->table[3])) << 16);
buzbeeefc63692012-11-14 16:31:52 -0800439 tabRec->size = (size * width) + 8;
440
441 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
442
443 // Making a call - use explicit registers
444 oatFlushAllRegs(cUnit); /* Everything to home location */
445 loadValueDirectFixed(cUnit, rlSrc, r0);
446 loadWordDisp(cUnit, rARM_SELF, ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode),
447 rARM_LR);
448 // Materialize a pointer to the fill data image
449 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
450 oatClobberCalleeSave(cUnit);
451 LIR* callInst = opReg(cUnit, kOpBlx, rARM_LR);
452 markSafepointPC(cUnit, callInst);
453}
454
455/*
456 * Handle simple case (thin lock) inline. If it's complicated, bail
457 * out to the heavyweight lock/unlock routines. We'll use dedicated
458 * registers here in order to be in the right position in case we
buzbeeeaf09bc2012-11-15 14:51:41 -0800459 * to bail to oat[Lock/Unlock]Object(self, object)
buzbeeefc63692012-11-14 16:31:52 -0800460 *
buzbeeeaf09bc2012-11-15 14:51:41 -0800461 * r0 -> self pointer [arg0 for oat[Lock/Unlock]Object
462 * r1 -> object [arg1 for oat[Lock/Unlock]Object
buzbeeefc63692012-11-14 16:31:52 -0800463 * r2 -> intial contents of object->lock, later result of strex
464 * r3 -> self->threadId
465 * r12 -> allow to be used by utilities as general temp
466 *
467 * The result of the strex is 0 if we acquire the lock.
468 *
469 * See comments in Sync.c for the layout of the lock word.
470 * Of particular interest to this code is the test for the
471 * simple case - which we handle inline. For monitor enter, the
472 * simple case is thin lock, held by no-one. For monitor exit,
473 * the simple case is thin lock, held by the unlocking thread with
474 * a recurse count of 0.
475 *
476 * A minor complication is that there is a field in the lock word
477 * unrelated to locking: the hash state. This field must be ignored, but
478 * preserved.
479 *
480 */
481void genMonitorEnter(CompilationUnit* cUnit, int optFlags, RegLocation rlSrc)
482{
483 oatFlushAllRegs(cUnit);
484 DCHECK_EQ(LW_SHAPE_THIN, 0);
485 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
486 oatLockCallTemps(cUnit); // Prepare for explicit register usage
487 genNullCheck(cUnit, rlSrc.sRegLow, r0, optFlags);
488 loadWordDisp(cUnit, rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
489 newLIR3(cUnit, kThumb2Ldrex, r1, r0,
490 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
491 // Align owner
492 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
493 // Is lock unheld on lock or held by us (==threadId) on unlock?
494 newLIR4(cUnit, kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
495 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
496 opRegImm(cUnit, kOpCmp, r1, 0);
497 opIT(cUnit, kArmCondEq, "");
498 newLIR4(cUnit, kThumb2Strex, r1, r2, r0,
499 Object::MonitorOffset().Int32Value() >> 2);
500 opRegImm(cUnit, kOpCmp, r1, 0);
501 opIT(cUnit, kArmCondNe, "T");
502 // Go expensive route - artLockObjectFromCode(self, obj);
503 loadWordDisp(cUnit, rARM_SELF, ENTRYPOINT_OFFSET(pLockObjectFromCode), rARM_LR);
504 oatClobberCalleeSave(cUnit);
505 LIR* callInst = opReg(cUnit, kOpBlx, rARM_LR);
506 markSafepointPC(cUnit, callInst);
507 oatGenMemBarrier(cUnit, kSY);
508}
509
510/*
511 * For monitor unlock, we don't have to use ldrex/strex. Once
512 * we've determined that the lock is thin and that we own it with
513 * a zero recursion count, it's safe to punch it back to the
514 * initial, unlock thin state with a store word.
515 */
516void genMonitorExit(CompilationUnit* cUnit, int optFlags, RegLocation rlSrc)
517{
518 DCHECK_EQ(LW_SHAPE_THIN, 0);
519 oatFlushAllRegs(cUnit);
520 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
521 oatLockCallTemps(cUnit); // Prepare for explicit register usage
522 genNullCheck(cUnit, rlSrc.sRegLow, r0, optFlags);
523 loadWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r1); // Get lock
524 loadWordDisp(cUnit, rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
525 // Is lock unheld on lock or held by us (==threadId) on unlock?
526 opRegRegImm(cUnit, kOpAnd, r3, r1,
527 (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
528 // Align owner
529 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
530 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
531 opRegReg(cUnit, kOpSub, r1, r2);
532 opIT(cUnit, kArmCondEq, "EE");
533 storeWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r3);
534 // Go expensive route - UnlockObjectFromCode(obj);
535 loadWordDisp(cUnit, rARM_SELF, ENTRYPOINT_OFFSET(pUnlockObjectFromCode), rARM_LR);
536 oatClobberCalleeSave(cUnit);
537 LIR* callInst = opReg(cUnit, kOpBlx, rARM_LR);
538 markSafepointPC(cUnit, callInst);
539 oatGenMemBarrier(cUnit, kSY);
540}
541
542/*
543 * Mark garbage collection card. Skip if the value we're storing is null.
544 */
545void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
546{
547 int regCardBase = oatAllocTemp(cUnit);
548 int regCardNo = oatAllocTemp(cUnit);
549 LIR* branchOver = opCmpImmBranch(cUnit, kCondEq, valReg, 0, NULL);
550 loadWordDisp(cUnit, rARM_SELF, Thread::CardTableOffset().Int32Value(), regCardBase);
551 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, CardTable::kCardShift);
552 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
553 kUnsignedByte);
554 LIR* target = newLIR0(cUnit, kPseudoTargetLabel);
555 branchOver->target = (LIR*)target;
556 oatFreeTemp(cUnit, regCardBase);
557 oatFreeTemp(cUnit, regCardNo);
558}
559
560void genEntrySequence(CompilationUnit* cUnit, RegLocation* argLocs,
561 RegLocation rlMethod)
562{
563 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
564 /*
565 * On entry, r0, r1, r2 & r3 are live. Let the register allocation
566 * mechanism know so it doesn't try to use any of them when
567 * expanding the frame or flushing. This leaves the utility
568 * code with a single temp: r12. This should be enough.
569 */
570 oatLockTemp(cUnit, r0);
571 oatLockTemp(cUnit, r1);
572 oatLockTemp(cUnit, r2);
573 oatLockTemp(cUnit, r3);
574
575 /*
576 * We can safely skip the stack overflow check if we're
577 * a leaf *and* our frame size < fudge factor.
578 */
579 bool skipOverflowCheck = ((cUnit->attrs & METHOD_IS_LEAF) &&
580 ((size_t)cUnit->frameSize <
581 Thread::kStackOverflowReservedBytes));
582 newLIR0(cUnit, kPseudoMethodEntry);
583 if (!skipOverflowCheck) {
584 /* Load stack limit */
585 loadWordDisp(cUnit, rARM_SELF, Thread::StackEndOffset().Int32Value(), r12);
586 }
587 /* Spill core callee saves */
588 newLIR1(cUnit, kThumb2Push, cUnit->coreSpillMask);
589 /* Need to spill any FP regs? */
590 if (cUnit->numFPSpills) {
591 /*
592 * NOTE: fp spills are a little different from core spills in that
593 * they are pushed as a contiguous block. When promoting from
594 * the fp set, we must allocate all singles from s16..highest-promoted
595 */
596 newLIR1(cUnit, kThumb2VPushCS, cUnit->numFPSpills);
597 }
598 if (!skipOverflowCheck) {
599 opRegRegImm(cUnit, kOpSub, rARM_LR, rARM_SP, cUnit->frameSize - (spillCount * 4));
600 genRegRegCheck(cUnit, kCondCc, rARM_LR, r12, kThrowStackOverflow);
601 opRegCopy(cUnit, rARM_SP, rARM_LR); // Establish stack
602 } else {
603 opRegImm(cUnit, kOpSub, rARM_SP, cUnit->frameSize - (spillCount * 4));
604 }
605
606 flushIns(cUnit, argLocs, rlMethod);
607
608 oatFreeTemp(cUnit, r0);
609 oatFreeTemp(cUnit, r1);
610 oatFreeTemp(cUnit, r2);
611 oatFreeTemp(cUnit, r3);
612}
613
614void genExitSequence(CompilationUnit* cUnit)
615{
616 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
617 /*
618 * In the exit path, r0/r1 are live - make sure they aren't
619 * allocated by the register utilities as temps.
620 */
621 oatLockTemp(cUnit, r0);
622 oatLockTemp(cUnit, r1);
623
624 newLIR0(cUnit, kPseudoMethodExit);
625 opRegImm(cUnit, kOpAdd, rARM_SP, cUnit->frameSize - (spillCount * 4));
626 /* Need to restore any FP callee saves? */
627 if (cUnit->numFPSpills) {
628 newLIR1(cUnit, kThumb2VPopCS, cUnit->numFPSpills);
629 }
630 if (cUnit->coreSpillMask & (1 << rARM_LR)) {
631 /* Unspill rARM_LR to rARM_PC */
632 cUnit->coreSpillMask &= ~(1 << rARM_LR);
633 cUnit->coreSpillMask |= (1 << rARM_PC);
634 }
635 newLIR1(cUnit, kThumb2Pop, cUnit->coreSpillMask);
636 if (!(cUnit->coreSpillMask & (1 << rARM_PC))) {
637 /* We didn't pop to rARM_PC, so must do a bv rARM_LR */
638 newLIR1(cUnit, kThumbBx, rARM_LR);
639 }
640}
641
642} // namespace art