blob: 62e4b3eb29e8126c84bfb60a045bba51ee52813c [file] [log] [blame]
buzbee67bf8852011-08-17 17:51:35 -07001/*
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/*
18 * This file contains arm-specific codegen factory support.
19 * It is included by
20 *
21 * Codegen-$(TARGET_ARCH_VARIANT).c
22 *
23 */
24
buzbeee3acd072012-02-25 17:03:10 -080025#define SLOW_FIELD_PATH (cUnit->enableDebug & (1 << kDebugSlowFieldPath))
26#define SLOW_INVOKE_PATH (cUnit->enableDebug & (1 << kDebugSlowInvokePath))
27#define SLOW_STRING_PATH (cUnit->enableDebug & (1 << kDebugSlowStringPath))
28#define SLOW_TYPE_PATH (cUnit->enableDebug & (1 << kDebugSlowTypePath))
29#define EXERCISE_SLOWEST_FIELD_PATH (cUnit->enableDebug & \
30 (1 << kDebugSlowestFieldPath))
31#define EXERCISE_SLOWEST_STRING_PATH (cUnit->enableDebug & \
32 (1 << kDebugSlowestStringPath))
33#define EXERCISE_RESOLVE_METHOD (cUnit->enableDebug & \
34 (1 << kDebugExerciseResolveMethod))
35
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080036namespace art {
37
buzbeee3acd072012-02-25 17:03:10 -080038STATIC void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset);
39
40/* Generate conditional branch instructions */
41STATIC ArmLIR* genConditionalBranch(CompilationUnit* cUnit,
42 ArmConditionCode cond,
43 ArmLIR* target)
44{
45 ArmLIR* branch = opCondBranch(cUnit, cond);
46 branch->generic.target = (LIR*) target;
47 return branch;
48}
49
50/* Generate unconditional branch instructions */
51STATIC ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target)
52{
53 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
54 branch->generic.target = (LIR*) target;
55 return branch;
56}
57
58STATIC ArmLIR* callRuntimeHelper(CompilationUnit* cUnit, int reg)
59{
60 oatClobberCalleeSave(cUnit);
61 return opReg(cUnit, kOpBlx, reg);
62}
63
64/*
65 * Mark garbage collection card. Skip if the value we're storing is null.
66 */
67STATIC void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
68{
69 int regCardBase = oatAllocTemp(cUnit);
70 int regCardNo = oatAllocTemp(cUnit);
71 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
72 loadWordDisp(cUnit, rSELF, Thread::CardTableOffset().Int32Value(),
73 regCardBase);
74 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
75 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
76 kUnsignedByte);
77 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
78 target->defMask = ENCODE_ALL;
79 branchOver->generic.target = (LIR*)target;
80 oatFreeTemp(cUnit, regCardBase);
81 oatFreeTemp(cUnit, regCardNo);
82}
buzbee5ade1d22011-09-09 14:44:52 -070083
buzbee67bf8852011-08-17 17:51:35 -070084/*
buzbeedfd3d702011-08-28 12:56:51 -070085 * Utiltiy to load the current Method*. Broken out
86 * to allow easy change between placing the current Method* in a
87 * dedicated register or its home location in the frame.
88 */
buzbeeed3e9302011-09-23 17:34:19 -070089STATIC void loadCurrMethodDirect(CompilationUnit *cUnit, int rTgt)
buzbeedfd3d702011-08-28 12:56:51 -070090{
91#if defined(METHOD_IN_REG)
92 genRegCopy(cUnit, rTgt, rMETHOD);
93#else
94 loadWordDisp(cUnit, rSP, 0, rTgt);
95#endif
96}
97
buzbeeed3e9302011-09-23 17:34:19 -070098STATIC int loadCurrMethod(CompilationUnit *cUnit)
buzbee1b4c8592011-08-31 10:43:51 -070099{
100#if defined(METHOD_IN_REG)
101 return rMETHOD;
102#else
103 int mReg = oatAllocTemp(cUnit);
104 loadCurrMethodDirect(cUnit, mReg);
105 return mReg;
106#endif
107}
108
buzbee58f92742011-10-01 11:22:17 -0700109STATIC ArmLIR* genCheck(CompilationUnit* cUnit, ArmConditionCode cCode,
110 MIR* mir, ArmThrowKind kind)
111{
buzbeeba938cb2012-02-03 14:47:55 -0800112 ArmLIR* tgt = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee58f92742011-10-01 11:22:17 -0700113 tgt->opcode = kArmPseudoThrowTarget;
114 tgt->operands[0] = kind;
115 tgt->operands[1] = mir ? mir->offset : 0;
116 ArmLIR* branch = genConditionalBranch(cUnit, cCode, tgt);
117 // Remember branch target - will process later
buzbeeba938cb2012-02-03 14:47:55 -0800118 oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt);
buzbee58f92742011-10-01 11:22:17 -0700119 return branch;
120}
121
buzbeeed3e9302011-09-23 17:34:19 -0700122STATIC ArmLIR* genImmedCheck(CompilationUnit* cUnit, ArmConditionCode cCode,
buzbee5ade1d22011-09-09 14:44:52 -0700123 int reg, int immVal, MIR* mir, ArmThrowKind kind)
buzbee67bf8852011-08-17 17:51:35 -0700124{
buzbeeba938cb2012-02-03 14:47:55 -0800125 ArmLIR* tgt = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee5ade1d22011-09-09 14:44:52 -0700126 tgt->opcode = kArmPseudoThrowTarget;
127 tgt->operands[0] = kind;
128 tgt->operands[1] = mir->offset;
129 ArmLIR* branch;
130 if (cCode == kArmCondAl) {
131 branch = genUnconditionalBranch(cUnit, tgt);
buzbee67bf8852011-08-17 17:51:35 -0700132 } else {
buzbeeec5adf32011-09-11 15:25:43 -0700133 branch = genCmpImmBranch(cUnit, cCode, reg, immVal);
buzbee5ade1d22011-09-09 14:44:52 -0700134 branch->generic.target = (LIR*)tgt;
buzbee67bf8852011-08-17 17:51:35 -0700135 }
buzbee5ade1d22011-09-09 14:44:52 -0700136 // Remember branch target - will process later
buzbeeba938cb2012-02-03 14:47:55 -0800137 oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt);
buzbee5ade1d22011-09-09 14:44:52 -0700138 return branch;
buzbee67bf8852011-08-17 17:51:35 -0700139}
140
buzbee43a36422011-09-14 14:00:13 -0700141/* Perform null-check on a register. */
buzbeeed3e9302011-09-23 17:34:19 -0700142STATIC ArmLIR* genNullCheck(CompilationUnit* cUnit, int sReg, int mReg,
buzbee5ade1d22011-09-09 14:44:52 -0700143 MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -0700144{
buzbee43a36422011-09-14 14:00:13 -0700145 if (!(cUnit->disableOpt & (1 << kNullCheckElimination)) &&
146 mir->optimizationFlags & MIR_IGNORE_NULL_CHECK) {
buzbee5ade1d22011-09-09 14:44:52 -0700147 return NULL;
buzbee67bf8852011-08-17 17:51:35 -0700148 }
buzbee5ade1d22011-09-09 14:44:52 -0700149 return genImmedCheck(cUnit, kArmCondEq, mReg, 0, mir, kArmThrowNullPointer);
buzbee67bf8852011-08-17 17:51:35 -0700150}
151
buzbeeec5adf32011-09-11 15:25:43 -0700152/* Perform check on two registers */
buzbeeed3e9302011-09-23 17:34:19 -0700153STATIC TGT_LIR* genRegRegCheck(CompilationUnit* cUnit, ArmConditionCode cCode,
buzbeeec5adf32011-09-11 15:25:43 -0700154 int reg1, int reg2, MIR* mir, ArmThrowKind kind)
buzbee67bf8852011-08-17 17:51:35 -0700155{
buzbeeba938cb2012-02-03 14:47:55 -0800156 ArmLIR* tgt = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee5ade1d22011-09-09 14:44:52 -0700157 tgt->opcode = kArmPseudoThrowTarget;
158 tgt->operands[0] = kind;
buzbeeec5adf32011-09-11 15:25:43 -0700159 tgt->operands[1] = mir ? mir->offset : 0;
160 tgt->operands[2] = reg1;
161 tgt->operands[3] = reg2;
162 opRegReg(cUnit, kOpCmp, reg1, reg2);
163 ArmLIR* branch = genConditionalBranch(cUnit, cCode, tgt);
buzbee5ade1d22011-09-09 14:44:52 -0700164 // Remember branch target - will process later
buzbeeba938cb2012-02-03 14:47:55 -0800165 oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, (intptr_t)tgt);
buzbee5ade1d22011-09-09 14:44:52 -0700166 return branch;
buzbee67bf8852011-08-17 17:51:35 -0700167}
Elliott Hughes11d1b0c2012-01-23 16:57:47 -0800168
buzbeee3acd072012-02-25 17:03:10 -0800169/*
170 * Let helper function take care of everything. Will call
171 * Array::AllocFromCode(type_idx, method, count);
172 * Note: AllocFromCode will handle checks for errNegativeArraySize.
173 */
174STATIC void genNewArray(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
175 RegLocation rlSrc)
176{
177 oatFlushAllRegs(cUnit); /* Everything to home location */
178 uint32_t type_idx = mir->dalvikInsn.vC;
179 if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
180 cUnit->dex_cache,
181 *cUnit->dex_file,
182 type_idx)) {
183 loadWordDisp(cUnit, rSELF,
184 OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR);
185 } else {
186 loadWordDisp(cUnit, rSELF,
187 OFFSETOF_MEMBER(Thread, pAllocArrayFromCodeWithAccessCheck), rLR);
188 }
189 loadCurrMethodDirect(cUnit, r1); // arg1 <- Method*
190 loadConstant(cUnit, r0, type_idx); // arg0 <- type_id
191 loadValueDirectFixed(cUnit, rlSrc, r2); // arg2 <- count
192 callRuntimeHelper(cUnit, rLR);
193 RegLocation rlResult = oatGetReturn(cUnit);
194 storeValue(cUnit, rlDest, rlResult);
195}
196
197/*
198 * Similar to genNewArray, but with post-allocation initialization.
199 * Verifier guarantees we're dealing with an array class. Current
200 * code throws runtime exception "bad Filled array req" for 'D' and 'J'.
201 * Current code also throws internal unimp if not 'L', '[' or 'I'.
202 */
203STATIC void genFilledNewArray(CompilationUnit* cUnit, MIR* mir, bool isRange)
204{
205 DecodedInstruction* dInsn = &mir->dalvikInsn;
206 int elems = dInsn->vA;
207 int typeId = dInsn->vB;
208 oatFlushAllRegs(cUnit); /* Everything to home location */
209 if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
210 cUnit->dex_cache,
211 *cUnit->dex_file,
212 typeId)) {
213 loadWordDisp(cUnit, rSELF,
214 OFFSETOF_MEMBER(Thread, pCheckAndAllocArrayFromCode), rLR);
215 } else {
216 loadWordDisp(cUnit, rSELF,
217 OFFSETOF_MEMBER(Thread, pCheckAndAllocArrayFromCodeWithAccessCheck), rLR);
218 }
219 loadCurrMethodDirect(cUnit, r1); // arg1 <- Method*
220 loadConstant(cUnit, r0, typeId); // arg0 <- type_id
221 loadConstant(cUnit, r2, elems); // arg2 <- count
222 callRuntimeHelper(cUnit, rLR);
223 /*
224 * NOTE: the implicit target for OP_FILLED_NEW_ARRAY is the
225 * return region. Because AllocFromCode placed the new array
226 * in r0, we'll just lock it into place. When debugger support is
227 * added, it may be necessary to additionally copy all return
228 * values to a home location in thread-local storage
229 */
230 oatLockTemp(cUnit, r0);
231
232 // Having a range of 0 is legal
233 if (isRange && (dInsn->vA > 0)) {
234 /*
235 * Bit of ugliness here. We're going generate a mem copy loop
236 * on the register range, but it is possible that some regs
237 * in the range have been promoted. This is unlikely, but
238 * before generating the copy, we'll just force a flush
239 * of any regs in the source range that have been promoted to
240 * home location.
241 */
242 for (unsigned int i = 0; i < dInsn->vA; i++) {
243 RegLocation loc = oatUpdateLoc(cUnit,
244 oatGetSrc(cUnit, mir, i));
245 if (loc.location == kLocPhysReg) {
246 storeBaseDisp(cUnit, rSP, oatSRegOffset(cUnit, loc.sRegLow),
247 loc.lowReg, kWord);
248 }
249 }
250 /*
251 * TUNING note: generated code here could be much improved, but
252 * this is an uncommon operation and isn't especially performance
253 * critical.
254 */
255 int rSrc = oatAllocTemp(cUnit);
256 int rDst = oatAllocTemp(cUnit);
257 int rIdx = oatAllocTemp(cUnit);
258 int rVal = rLR; // Using a lot of temps, rLR is known free here
259 // Set up source pointer
260 RegLocation rlFirst = oatGetSrc(cUnit, mir, 0);
261 opRegRegImm(cUnit, kOpAdd, rSrc, rSP,
262 oatSRegOffset(cUnit, rlFirst.sRegLow));
263 // Set up the target pointer
264 opRegRegImm(cUnit, kOpAdd, rDst, r0,
265 Array::DataOffset().Int32Value());
266 // Set up the loop counter (known to be > 0)
267 loadConstant(cUnit, rIdx, dInsn->vA - 1);
268 // Generate the copy loop. Going backwards for convenience
269 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
270 target->defMask = ENCODE_ALL;
271 // Copy next element
272 loadBaseIndexed(cUnit, rSrc, rIdx, rVal, 2, kWord);
273 storeBaseIndexed(cUnit, rDst, rIdx, rVal, 2, kWord);
274 // Use setflags encoding here
275 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
276 ArmLIR* branch = opCondBranch(cUnit, kArmCondGe);
277 branch->generic.target = (LIR*)target;
278 } else if (!isRange) {
279 // TUNING: interleave
280 for (unsigned int i = 0; i < dInsn->vA; i++) {
281 RegLocation rlArg = loadValue(cUnit,
282 oatGetSrc(cUnit, mir, i), kCoreReg);
283 storeBaseDisp(cUnit, r0,
284 Array::DataOffset().Int32Value() +
285 i * 4, rlArg.lowReg, kWord);
286 // If the loadValue caused a temp to be allocated, free it
287 if (oatIsTemp(cUnit, rlArg.lowReg)) {
288 oatFreeTemp(cUnit, rlArg.lowReg);
289 }
290 }
291 }
292}
293
294STATIC void genSput(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
295 bool isLongOrDouble, bool isObject)
296{
297 int fieldOffset;
298 int ssbIndex;
299 bool isVolatile;
300 bool isReferrersClass;
301 uint32_t fieldIdx = mir->dalvikInsn.vB;
302 bool fastPath =
303 cUnit->compiler->ComputeStaticFieldInfo(fieldIdx, cUnit,
304 fieldOffset, ssbIndex,
305 isReferrersClass, isVolatile, true);
306 if (fastPath && !SLOW_FIELD_PATH) {
307 DCHECK_GE(fieldOffset, 0);
308 int rBase;
309 int rMethod;
310 if (isReferrersClass) {
311 // Fast path, static storage base is this method's class
312 rMethod = loadCurrMethod(cUnit);
313 rBase = oatAllocTemp(cUnit);
314 loadWordDisp(cUnit, rMethod,
315 Method::DeclaringClassOffset().Int32Value(), rBase);
316 } else {
317 // Medium path, static storage base in a different class which
318 // requires checks that the other class is initialized.
319 DCHECK_GE(ssbIndex, 0);
320 // May do runtime call so everything to home locations.
321 oatFlushAllRegs(cUnit);
322 // Using fixed register to sync with possible call to runtime
323 // support.
324 rMethod = r1;
325 oatLockTemp(cUnit, rMethod);
326 loadCurrMethodDirect(cUnit, rMethod);
327 rBase = r0;
328 oatLockTemp(cUnit, rBase);
329 loadWordDisp(cUnit, rMethod,
330 Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
331 rBase);
332 loadWordDisp(cUnit, rBase,
333 Array::DataOffset().Int32Value() + sizeof(int32_t*) *
334 ssbIndex, rBase);
335 // rBase now points at appropriate static storage base (Class*)
336 // or NULL if not initialized. Check for NULL and call helper if NULL.
337 // TUNING: fast path should fall through
338 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
339 loadWordDisp(cUnit, rSELF,
340 OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
341 loadConstant(cUnit, r0, ssbIndex);
342 callRuntimeHelper(cUnit, rLR);
343 ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
344 skipTarget->defMask = ENCODE_ALL;
345 branchOver->generic.target = (LIR*)skipTarget;
346 }
347 // rBase now holds static storage base
348 oatFreeTemp(cUnit, rMethod);
349 if (isLongOrDouble) {
350 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
351 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
352 } else {
353 rlSrc = oatGetSrc(cUnit, mir, 0);
354 rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
355 }
356 if (isVolatile) {
357 oatGenMemBarrier(cUnit, kST);
358 }
359 if (isLongOrDouble) {
360 storeBaseDispWide(cUnit, rBase, fieldOffset, rlSrc.lowReg,
361 rlSrc.highReg);
362 } else {
363 storeWordDisp(cUnit, rBase, fieldOffset, rlSrc.lowReg);
364 }
365 if (isVolatile) {
366 oatGenMemBarrier(cUnit, kSY);
367 }
368 if (isObject) {
369 markGCCard(cUnit, rlSrc.lowReg, rBase);
370 }
371 oatFreeTemp(cUnit, rBase);
372 } else {
373 oatFlushAllRegs(cUnit); // Everything to home locations
374 int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Static) :
375 (isObject ? OFFSETOF_MEMBER(Thread, pSetObjStatic)
376 : OFFSETOF_MEMBER(Thread, pSet32Static));
377 loadWordDisp(cUnit, rSELF, setterOffset, rLR);
378 loadConstant(cUnit, r0, fieldIdx);
379 if (isLongOrDouble) {
380 loadValueDirectWideFixed(cUnit, rlSrc, r2, r3);
381 } else {
382 loadValueDirect(cUnit, rlSrc, r1);
383 }
384 callRuntimeHelper(cUnit, rLR);
385 }
386}
387
388STATIC void genSget(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
389 bool isLongOrDouble, bool isObject)
390{
391 int fieldOffset;
392 int ssbIndex;
393 bool isVolatile;
394 bool isReferrersClass;
395 uint32_t fieldIdx = mir->dalvikInsn.vB;
396 bool fastPath =
397 cUnit->compiler->ComputeStaticFieldInfo(fieldIdx, cUnit,
398 fieldOffset, ssbIndex,
399 isReferrersClass, isVolatile, false);
400 if (fastPath && !SLOW_FIELD_PATH) {
401 DCHECK_GE(fieldOffset, 0);
402 int rBase;
403 int rMethod;
404 if (isReferrersClass) {
405 // Fast path, static storage base is this method's class
406 rMethod = loadCurrMethod(cUnit);
407 rBase = oatAllocTemp(cUnit);
408 loadWordDisp(cUnit, rMethod,
409 Method::DeclaringClassOffset().Int32Value(), rBase);
410 } else {
411 // Medium path, static storage base in a different class which
412 // requires checks that the other class is initialized
413 DCHECK_GE(ssbIndex, 0);
414 // May do runtime call so everything to home locations.
415 oatFlushAllRegs(cUnit);
416 // Using fixed register to sync with possible call to runtime
417 // support
418 rMethod = r1;
419 oatLockTemp(cUnit, rMethod);
420 loadCurrMethodDirect(cUnit, rMethod);
421 rBase = r0;
422 oatLockTemp(cUnit, rBase);
423 loadWordDisp(cUnit, rMethod,
424 Method::DexCacheInitializedStaticStorageOffset().Int32Value(),
425 rBase);
426 loadWordDisp(cUnit, rBase,
427 Array::DataOffset().Int32Value() + sizeof(int32_t*) * ssbIndex,
428 rBase);
429 // rBase now points at appropriate static storage base (Class*)
430 // or NULL if not initialized. Check for NULL and call helper if NULL.
431 // TUNING: fast path should fall through
432 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rBase, 0);
433 loadWordDisp(cUnit, rSELF,
434 OFFSETOF_MEMBER(Thread, pInitializeStaticStorage), rLR);
435 loadConstant(cUnit, r0, ssbIndex);
436 callRuntimeHelper(cUnit, rLR);
437 ArmLIR* skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
438 skipTarget->defMask = ENCODE_ALL;
439 branchOver->generic.target = (LIR*)skipTarget;
440 }
441 // rBase now holds static storage base
442 oatFreeTemp(cUnit, rMethod);
443 rlDest = isLongOrDouble ? oatGetDestWide(cUnit, mir, 0, 1)
444 : oatGetDest(cUnit, mir, 0);
445 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
446 if (isVolatile) {
447 oatGenMemBarrier(cUnit, kSY);
448 }
449 if (isLongOrDouble) {
450 loadBaseDispWide(cUnit, NULL, rBase, fieldOffset, rlResult.lowReg,
451 rlResult.highReg, INVALID_SREG);
452 } else {
453 loadWordDisp(cUnit, rBase, fieldOffset, rlResult.lowReg);
454 }
455 oatFreeTemp(cUnit, rBase);
456 if (isLongOrDouble) {
457 storeValueWide(cUnit, rlDest, rlResult);
458 } else {
459 storeValue(cUnit, rlDest, rlResult);
460 }
461 } else {
462 oatFlushAllRegs(cUnit); // Everything to home locations
463 int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Static) :
464 (isObject ? OFFSETOF_MEMBER(Thread, pGetObjStatic)
465 : OFFSETOF_MEMBER(Thread, pGet32Static));
466 loadWordDisp(cUnit, rSELF, getterOffset, rLR);
467 loadConstant(cUnit, r0, fieldIdx);
468 callRuntimeHelper(cUnit, rLR);
469 if (isLongOrDouble) {
470 RegLocation rlResult = oatGetReturnWide(cUnit);
471 storeValueWide(cUnit, rlDest, rlResult);
472 } else {
473 RegLocation rlResult = oatGetReturn(cUnit);
474 storeValue(cUnit, rlDest, rlResult);
475 }
476 }
477}
478
479typedef int (*NextCallInsn)(CompilationUnit*, MIR*, int, uint32_t dexIdx,
480 uint32_t methodIdx);
481
482/*
483 * Bit of a hack here - in leiu of a real scheduling pass,
484 * emit the next instruction in static & direct invoke sequences.
485 */
486STATIC int nextSDCallInsn(CompilationUnit* cUnit, MIR* mir,
487 int state, uint32_t dexIdx, uint32_t unused)
488{
489 switch(state) {
490 case 0: // Get the current Method* [sets r0]
491 loadCurrMethodDirect(cUnit, r0);
492 break;
493 case 1: // Get method->code_and_direct_methods_
494 loadWordDisp(cUnit, r0,
495 Method::GetDexCacheCodeAndDirectMethodsOffset().Int32Value(),
496 r0);
497 break;
498 case 2: // Grab target method* and target code_
499 loadWordDisp(cUnit, r0,
500 CodeAndDirectMethods::CodeOffsetInBytes(dexIdx), rLR);
501 loadWordDisp(cUnit, r0,
502 CodeAndDirectMethods::MethodOffsetInBytes(dexIdx), r0);
503 break;
504 default:
505 return -1;
506 }
507 return state + 1;
508}
509
510/*
511 * Bit of a hack here - in leiu of a real scheduling pass,
512 * emit the next instruction in a virtual invoke sequence.
513 * We can use rLR as a temp prior to target address loading
514 * Note also that we'll load the first argument ("this") into
515 * r1 here rather than the standard loadArgRegs.
516 */
517STATIC int nextVCallInsn(CompilationUnit* cUnit, MIR* mir,
518 int state, uint32_t dexIdx, uint32_t methodIdx)
519{
520 RegLocation rlArg;
521 /*
522 * This is the fast path in which the target virtual method is
523 * fully resolved at compile time.
524 */
525 switch(state) {
526 case 0: // Get "this" [set r1]
527 rlArg = oatGetSrc(cUnit, mir, 0);
528 loadValueDirectFixed(cUnit, rlArg, r1);
529 break;
530 case 1: // Is "this" null? [use r1]
531 genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
532 // get this->klass_ [use r1, set rLR]
533 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
534 break;
535 case 2: // Get this->klass_->vtable [usr rLR, set rLR]
536 loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
537 break;
538 case 3: // Get target method [use rLR, set r0]
539 loadWordDisp(cUnit, rLR, (methodIdx * 4) +
540 Array::DataOffset().Int32Value(), r0);
541 break;
542 case 4: // Get the target compiled code address [uses r0, sets rLR]
543 loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
544 break;
545 default:
546 return -1;
547 }
548 return state + 1;
549}
550
551/*
552 * Interleave launch code for INVOKE_SUPER. See comments
553 * for nextVCallIns.
554 */
555STATIC int nextSuperCallInsn(CompilationUnit* cUnit, MIR* mir,
556 int state, uint32_t dexIdx, uint32_t methodIdx)
557{
558 /*
559 * This is the fast path in which the target virtual method is
560 * fully resolved at compile time. Note also that this path assumes
561 * that the check to verify that the target method index falls
562 * within the size of the super's vtable has been done at compile-time.
563 */
564 RegLocation rlArg;
565 switch(state) {
566 case 0: // Get current Method* [set r0]
567 loadCurrMethodDirect(cUnit, r0);
568 // Load "this" [set r1]
569 rlArg = oatGetSrc(cUnit, mir, 0);
570 loadValueDirectFixed(cUnit, rlArg, r1);
571 // Get method->declaring_class_ [use r0, set rLR]
572 loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
573 rLR);
574 // Is "this" null? [use r1]
575 genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
576 break;
577 case 1: // Get method->declaring_class_->super_class [usr rLR, set rLR]
578 loadWordDisp(cUnit, rLR, Class::SuperClassOffset().Int32Value(),
579 rLR);
580 break;
581 case 2: // Get ...->super_class_->vtable [u/s rLR]
582 loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
583 break;
584 case 3: // Get target method [use rLR, set r0]
585 loadWordDisp(cUnit, rLR, (methodIdx * 4) +
586 Array::DataOffset().Int32Value(), r0);
587 break;
588 case 4: // Get the target compiled code address [uses r0, sets rLR]
589 loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
590 break;
591 default:
592 return -1;
593 }
594 return state + 1;
595}
596
597STATIC int nextInvokeInsnSP(CompilationUnit* cUnit, MIR* mir, int trampoline,
598 int state, uint32_t dexIdx, uint32_t methodIdx)
599{
600 /*
601 * This handles the case in which the base method is not fully
602 * resolved at compile time, we bail to a runtime helper.
603 */
604 if (state == 0) {
605 // Load trampoline target
606 loadWordDisp(cUnit, rSELF, trampoline, rLR);
607 // Load r0 with method index
608 loadConstant(cUnit, r0, dexIdx);
609 return 1;
610 }
611 return -1;
612}
613
614STATIC int nextStaticCallInsnSP(CompilationUnit* cUnit, MIR* mir,
615 int state, uint32_t dexIdx, uint32_t methodIdx)
616{
617 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeStaticTrampolineWithAccessCheck);
618 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
619}
620
621STATIC int nextDirectCallInsnSP(CompilationUnit* cUnit, MIR* mir,
622 int state, uint32_t dexIdx, uint32_t methodIdx)
623{
624 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeDirectTrampolineWithAccessCheck);
625 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
626}
627
628STATIC int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
629 int state, uint32_t dexIdx, uint32_t methodIdx)
630{
631 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeSuperTrampolineWithAccessCheck);
632 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
633}
634
635STATIC int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
636 int state, uint32_t dexIdx, uint32_t methodIdx)
637{
638 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeVirtualTrampolineWithAccessCheck);
639 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
640}
641
642/*
643 * All invoke-interface calls bounce off of art_invoke_interface_trampoline,
644 * which will locate the target and continue on via a tail call.
645 */
646STATIC int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
647 int state, uint32_t dexIdx, uint32_t unused)
648{
649 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline);
650 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
651}
652
653STATIC int nextInterfaceCallInsnWithAccessCheck(CompilationUnit* cUnit,
654 MIR* mir, int state,
655 uint32_t dexIdx,
656 uint32_t unused)
657{
658 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampolineWithAccessCheck);
659 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
660}
661
662STATIC int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
663 DecodedInstruction* dInsn, int callState,
664 NextCallInsn nextCallInsn, uint32_t dexIdx,
665 uint32_t methodIdx, bool skipThis)
666{
667 int nextReg = r1;
668 int nextArg = 0;
669 if (skipThis) {
670 nextReg++;
671 nextArg++;
672 }
673 for (; (nextReg <= r3) && (nextArg < mir->ssaRep->numUses); nextReg++) {
674 RegLocation rlArg = oatGetRawSrc(cUnit, mir, nextArg++);
675 rlArg = oatUpdateRawLoc(cUnit, rlArg);
676 if (rlArg.wide && (nextReg <= r2)) {
677 loadValueDirectWideFixed(cUnit, rlArg, nextReg, nextReg + 1);
678 nextReg++;
679 nextArg++;
680 } else {
681 rlArg.wide = false;
682 loadValueDirectFixed(cUnit, rlArg, nextReg);
683 }
684 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
685 }
686 return callState;
687}
688
689/*
690 * Load up to 5 arguments, the first three of which will be in
691 * r1 .. r3. On entry r0 contains the current method pointer,
692 * and as part of the load sequence, it must be replaced with
693 * the target method pointer. Note, this may also be called
694 * for "range" variants if the number of arguments is 5 or fewer.
695 */
696STATIC int genDalvikArgsNoRange(CompilationUnit* cUnit, MIR* mir,
697 DecodedInstruction* dInsn, int callState,
698 ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
699 uint32_t dexIdx, uint32_t methodIdx,
700 bool skipThis)
701{
702 RegLocation rlArg;
703
704 /* If no arguments, just return */
705 if (dInsn->vA == 0)
706 return callState;
707
708 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
709
710 DCHECK_LE(dInsn->vA, 5U);
711 if (dInsn->vA > 3) {
712 uint32_t nextUse = 3;
713 //Detect special case of wide arg spanning arg3/arg4
714 RegLocation rlUse0 = oatGetRawSrc(cUnit, mir, 0);
715 RegLocation rlUse1 = oatGetRawSrc(cUnit, mir, 1);
716 RegLocation rlUse2 = oatGetRawSrc(cUnit, mir, 2);
717 if (((!rlUse0.wide && !rlUse1.wide) || rlUse0.wide) &&
718 rlUse2.wide) {
719 int reg;
720 // Wide spans, we need the 2nd half of uses[2].
721 rlArg = oatUpdateLocWide(cUnit, rlUse2);
722 if (rlArg.location == kLocPhysReg) {
723 reg = rlArg.highReg;
724 } else {
725 // r2 & r3 can safely be used here
726 reg = r3;
727 loadWordDisp(cUnit, rSP,
728 oatSRegOffset(cUnit, rlArg.sRegLow) + 4, reg);
729 callState = nextCallInsn(cUnit, mir, callState, dexIdx,
730 methodIdx);
731 }
732 storeBaseDisp(cUnit, rSP, (nextUse + 1) * 4, reg, kWord);
733 storeBaseDisp(cUnit, rSP, 16 /* (3+1)*4 */, reg, kWord);
734 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
735 nextUse++;
736 }
737 // Loop through the rest
738 while (nextUse < dInsn->vA) {
739 int lowReg;
740 int highReg;
741 rlArg = oatGetRawSrc(cUnit, mir, nextUse);
742 rlArg = oatUpdateRawLoc(cUnit, rlArg);
743 if (rlArg.location == kLocPhysReg) {
744 lowReg = rlArg.lowReg;
745 highReg = rlArg.highReg;
746 } else {
747 lowReg = r2;
748 highReg = r3;
749 if (rlArg.wide) {
750 loadValueDirectWideFixed(cUnit, rlArg, lowReg, highReg);
751 } else {
752 loadValueDirectFixed(cUnit, rlArg, lowReg);
753 }
754 callState = nextCallInsn(cUnit, mir, callState, dexIdx,
755 methodIdx);
756 }
757 int outsOffset = (nextUse + 1) * 4;
758 if (rlArg.wide) {
759 storeBaseDispWide(cUnit, rSP, outsOffset, lowReg, highReg);
760 nextUse += 2;
761 } else {
762 storeWordDisp(cUnit, rSP, outsOffset, lowReg);
763 nextUse++;
764 }
765 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
766 }
767 }
768
769 callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
770 dexIdx, methodIdx, skipThis);
771
772 if (pcrLabel) {
773 *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
774 }
775 return callState;
776}
777
778/*
779 * May have 0+ arguments (also used for jumbo). Note that
780 * source virtual registers may be in physical registers, so may
781 * need to be flushed to home location before copying. This
782 * applies to arg3 and above (see below).
783 *
784 * Two general strategies:
785 * If < 20 arguments
786 * Pass args 3-18 using vldm/vstm block copy
787 * Pass arg0, arg1 & arg2 in r1-r3
788 * If 20+ arguments
789 * Pass args arg19+ using memcpy block copy
790 * Pass arg0, arg1 & arg2 in r1-r3
791 *
792 */
793STATIC int genDalvikArgsRange(CompilationUnit* cUnit, MIR* mir,
794 DecodedInstruction* dInsn, int callState,
795 ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
796 uint32_t dexIdx, uint32_t methodIdx,
797 bool skipThis)
798{
799 int firstArg = dInsn->vC;
800 int numArgs = dInsn->vA;
801
802 // If we can treat it as non-range (Jumbo ops will use range form)
803 if (numArgs <= 5)
804 return genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pcrLabel,
805 nextCallInsn, dexIdx, methodIdx,
806 skipThis);
807 /*
808 * Make sure range list doesn't span the break between in normal
809 * Dalvik vRegs and the ins.
810 */
811 int highestArg = oatGetSrc(cUnit, mir, numArgs-1).sRegLow;
812 int boundaryReg = cUnit->numDalvikRegisters - cUnit->numIns;
813 if ((firstArg < boundaryReg) && (highestArg >= boundaryReg)) {
814 LOG(FATAL) << "Argument list spanned locals & args";
815 }
816
817 /*
818 * First load the non-register arguments. Both forms expect all
819 * of the source arguments to be in their home frame location, so
820 * scan the sReg names and flush any that have been promoted to
821 * frame backing storage.
822 */
823 // Scan the rest of the args - if in physReg flush to memory
824 for (int nextArg = 0; nextArg < numArgs;) {
825 RegLocation loc = oatGetRawSrc(cUnit, mir, nextArg);
826 if (loc.wide) {
827 loc = oatUpdateLocWide(cUnit, loc);
828 if ((nextArg >= 2) && (loc.location == kLocPhysReg)) {
829 storeBaseDispWide(cUnit, rSP,
830 oatSRegOffset(cUnit, loc.sRegLow),
831 loc.lowReg, loc.highReg);
832 }
833 nextArg += 2;
834 } else {
835 loc = oatUpdateLoc(cUnit, loc);
836 if ((nextArg >= 3) && (loc.location == kLocPhysReg)) {
837 storeBaseDisp(cUnit, rSP, oatSRegOffset(cUnit, loc.sRegLow),
838 loc.lowReg, kWord);
839 }
840 nextArg++;
841 }
842 }
843
844 int startOffset = oatSRegOffset(cUnit,
845 cUnit->regLocation[mir->ssaRep->uses[3]].sRegLow);
846 int outsOffset = 4 /* Method* */ + (3 * 4);
847 if (numArgs >= 20) {
848 // Generate memcpy
849 opRegRegImm(cUnit, kOpAdd, r0, rSP, outsOffset);
850 opRegRegImm(cUnit, kOpAdd, r1, rSP, startOffset);
851 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pMemcpy), rLR);
852 loadConstant(cUnit, r2, (numArgs - 3) * 4);
853 callRuntimeHelper(cUnit, rLR);
854 // Restore Method*
855 loadCurrMethodDirect(cUnit, r0);
856 } else {
857 // Use vldm/vstm pair using r3 as a temp
858 int regsLeft = std::min(numArgs - 3, 16);
859 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
860 opRegRegImm(cUnit, kOpAdd, r3, rSP, startOffset);
861 ArmLIR* ld = newLIR3(cUnit, kThumb2Vldms, r3, fr0, regsLeft);
862 //TUNING: loosen barrier
863 ld->defMask = ENCODE_ALL;
864 setMemRefType(ld, true /* isLoad */, kDalvikReg);
865 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
866 opRegRegImm(cUnit, kOpAdd, r3, rSP, 4 /* Method* */ + (3 * 4));
867 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
868 ArmLIR* st = newLIR3(cUnit, kThumb2Vstms, r3, fr0, regsLeft);
869 setMemRefType(st, false /* isLoad */, kDalvikReg);
870 st->defMask = ENCODE_ALL;
871 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
872 }
873
874 callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
875 dexIdx, methodIdx, skipThis);
876
877 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
878 if (pcrLabel) {
879 *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
880 }
881 return callState;
882}
883
884// Debugging routine - if null target, branch to DebugMe
885STATIC void genShowTarget(CompilationUnit* cUnit)
886{
887 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
888 loadWordDisp(cUnit, rSELF,
889 OFFSETOF_MEMBER(Thread, pDebugMe), rLR);
890 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
891 target->defMask = -1;
892 branchOver->generic.target = (LIR*)target;
893}
894
895STATIC void genThrowVerificationError(CompilationUnit* cUnit, MIR* mir)
896{
897 loadWordDisp(cUnit, rSELF,
898 OFFSETOF_MEMBER(Thread, pThrowVerificationErrorFromCode), rLR);
899 loadConstant(cUnit, r0, mir->dalvikInsn.vA);
900 loadConstant(cUnit, r1, mir->dalvikInsn.vB);
901 callRuntimeHelper(cUnit, rLR);
902}
903
904STATIC void genCompareAndBranch(CompilationUnit* cUnit, BasicBlock* bb,
905 MIR* mir, RegLocation rlSrc1,
906 RegLocation rlSrc2, ArmLIR* labelList)
907{
908 ArmConditionCode cond;
909 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
910 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
911 opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
912 Opcode opcode = mir->dalvikInsn.opcode;
913 switch(opcode) {
914 case OP_IF_EQ:
915 cond = kArmCondEq;
916 break;
917 case OP_IF_NE:
918 cond = kArmCondNe;
919 break;
920 case OP_IF_LT:
921 cond = kArmCondLt;
922 break;
923 case OP_IF_GE:
924 cond = kArmCondGe;
925 break;
926 case OP_IF_GT:
927 cond = kArmCondGt;
928 break;
929 case OP_IF_LE:
930 cond = kArmCondLe;
931 break;
932 default:
933 cond = (ArmConditionCode)0;
934 LOG(FATAL) << "Unexpected opcode " << (int)opcode;
935 }
936 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
937 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
938}
939
940STATIC void genCompareZeroAndBranch(CompilationUnit* cUnit, BasicBlock* bb,
941 MIR* mir, RegLocation rlSrc,
942 ArmLIR* labelList)
943{
944 ArmConditionCode cond;
945 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
946 opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0);
947 Opcode opcode = mir->dalvikInsn.opcode;
948 switch(opcode) {
949 case OP_IF_EQZ:
950 cond = kArmCondEq;
951 break;
952 case OP_IF_NEZ:
953 cond = kArmCondNe;
954 break;
955 case OP_IF_LTZ:
956 cond = kArmCondLt;
957 break;
958 case OP_IF_GEZ:
959 cond = kArmCondGe;
960 break;
961 case OP_IF_GTZ:
962 cond = kArmCondGt;
963 break;
964 case OP_IF_LEZ:
965 cond = kArmCondLe;
966 break;
967 default:
968 cond = (ArmConditionCode)0;
969 LOG(FATAL) << "Unexpected opcode " << (int)opcode;
970 }
971 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
972 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
973}
974
975STATIC void genIntToLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
976 RegLocation rlSrc)
977{
978 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
979 if (rlSrc.location == kLocPhysReg) {
980 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
981 } else {
982 loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
983 }
984 opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
985 rlResult.lowReg, 31);
986 storeValueWide(cUnit, rlDest, rlResult);
987}
988
989STATIC void genIntNarrowing(CompilationUnit* cUnit, MIR* mir,
990 RegLocation rlDest, RegLocation rlSrc)
991{
992 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
993 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
994 OpKind op = kOpInvalid;
995 switch(mir->dalvikInsn.opcode) {
996 case OP_INT_TO_BYTE:
997 op = kOp2Byte;
998 break;
999 case OP_INT_TO_SHORT:
1000 op = kOp2Short;
1001 break;
1002 case OP_INT_TO_CHAR:
1003 op = kOp2Char;
1004 break;
1005 default:
1006 LOG(ERROR) << "Bad int conversion type";
1007 }
1008 opRegReg(cUnit, op, rlResult.lowReg, rlSrc.lowReg);
1009 storeValue(cUnit, rlDest, rlResult);
1010}
1011
1012/*
1013 * If there are any ins passed in registers that have not been promoted
1014 * to a callee-save register, flush them to the frame. Perform intial
1015 * assignment of promoted arguments.
1016 */
1017STATIC void flushIns(CompilationUnit* cUnit)
1018{
1019 if (cUnit->numIns == 0)
1020 return;
1021 int firstArgReg = r1;
1022 int lastArgReg = r3;
1023 int startVReg = cUnit->numDalvikRegisters - cUnit->numIns;
1024 /*
1025 * Arguments passed in registers should be flushed
1026 * to their backing locations in the frame for now.
1027 * Also, we need to do initial assignment for promoted
1028 * arguments. NOTE: an older version of dx had an issue
1029 * in which it would reuse static method argument registers.
1030 * This could result in the same Dalvik virtual register
1031 * being promoted to both core and fp regs. In those
1032 * cases, copy argument to both. This will be uncommon
1033 * enough that it isn't worth attempting to optimize.
1034 */
1035 for (int i = 0; i < cUnit->numIns; i++) {
1036 PromotionMap vMap = cUnit->promotionMap[startVReg + i];
1037 if (i <= (lastArgReg - firstArgReg)) {
1038 // If arriving in register
1039 if (vMap.coreLocation == kLocPhysReg) {
1040 genRegCopy(cUnit, vMap.coreReg, firstArgReg + i);
1041 }
1042 if (vMap.fpLocation == kLocPhysReg) {
1043 genRegCopy(cUnit, vMap.fpReg, firstArgReg + i);
1044 }
1045 // Also put a copy in memory in case we're partially promoted
1046 storeBaseDisp(cUnit, rSP, oatSRegOffset(cUnit, startVReg + i),
1047 firstArgReg + i, kWord);
1048 } else {
1049 // If arriving in frame & promoted
1050 if (vMap.coreLocation == kLocPhysReg) {
1051 loadWordDisp(cUnit, rSP, oatSRegOffset(cUnit, startVReg + i),
1052 vMap.coreReg);
1053 }
1054 if (vMap.fpLocation == kLocPhysReg) {
1055 loadWordDisp(cUnit, rSP, oatSRegOffset(cUnit, startVReg + i),
1056 vMap.fpReg);
1057 }
1058 }
1059 }
1060}
1061
1062STATIC void genEntrySequence(CompilationUnit* cUnit, BasicBlock* bb)
1063{
1064 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
1065 /*
1066 * On entry, r0, r1, r2 & r3 are live. Let the register allocation
1067 * mechanism know so it doesn't try to use any of them when
1068 * expanding the frame or flushing. This leaves the utility
1069 * code with a single temp: r12. This should be enough.
1070 */
1071 oatLockTemp(cUnit, r0);
1072 oatLockTemp(cUnit, r1);
1073 oatLockTemp(cUnit, r2);
1074 oatLockTemp(cUnit, r3);
1075
1076 /*
1077 * We can safely skip the stack overflow check if we're
1078 * a leaf *and* our frame size < fudge factor.
1079 */
1080 bool skipOverflowCheck = ((cUnit->attrs & METHOD_IS_LEAF) &&
1081 ((size_t)cUnit->frameSize <
1082 Thread::kStackOverflowReservedBytes));
1083 newLIR0(cUnit, kArmPseudoMethodEntry);
1084 if (!skipOverflowCheck) {
1085 /* Load stack limit */
1086 loadWordDisp(cUnit, rSELF,
1087 Thread::StackEndOffset().Int32Value(), r12);
1088 }
1089 /* Spill core callee saves */
1090 newLIR1(cUnit, kThumb2Push, cUnit->coreSpillMask);
1091 /* Need to spill any FP regs? */
1092 if (cUnit->numFPSpills) {
1093 /*
1094 * NOTE: fp spills are a little different from core spills in that
1095 * they are pushed as a contiguous block. When promoting from
1096 * the fp set, we must allocate all singles from s16..highest-promoted
1097 */
1098 newLIR1(cUnit, kThumb2VPushCS, cUnit->numFPSpills);
1099 }
1100 if (!skipOverflowCheck) {
1101 opRegRegImm(cUnit, kOpSub, rLR, rSP,
1102 cUnit->frameSize - (spillCount * 4));
1103 genRegRegCheck(cUnit, kArmCondCc, rLR, r12, NULL,
1104 kArmThrowStackOverflow);
1105 genRegCopy(cUnit, rSP, rLR); // Establish stack
1106 } else {
1107 opRegImm(cUnit, kOpSub, rSP,
1108 cUnit->frameSize - (spillCount * 4));
1109 }
1110 storeBaseDisp(cUnit, rSP, 0, r0, kWord);
1111 flushIns(cUnit);
1112
1113 if (cUnit->genDebugger) {
1114 // Refresh update debugger callout
1115 loadWordDisp(cUnit, rSELF,
1116 OFFSETOF_MEMBER(Thread, pUpdateDebuggerFromCode), rSUSPEND);
1117 genDebuggerUpdate(cUnit, DEBUGGER_METHOD_ENTRY);
1118 }
1119
1120 oatFreeTemp(cUnit, r0);
1121 oatFreeTemp(cUnit, r1);
1122 oatFreeTemp(cUnit, r2);
1123 oatFreeTemp(cUnit, r3);
1124}
1125
1126STATIC void genExitSequence(CompilationUnit* cUnit, BasicBlock* bb)
1127{
1128 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
1129 /*
1130 * In the exit path, r0/r1 are live - make sure they aren't
1131 * allocated by the register utilities as temps.
1132 */
1133 oatLockTemp(cUnit, r0);
1134 oatLockTemp(cUnit, r1);
1135
1136 newLIR0(cUnit, kArmPseudoMethodExit);
1137 /* If we're compiling for the debugger, generate an update callout */
1138 if (cUnit->genDebugger) {
1139 genDebuggerUpdate(cUnit, DEBUGGER_METHOD_EXIT);
1140 }
1141 opRegImm(cUnit, kOpAdd, rSP, cUnit->frameSize - (spillCount * 4));
1142 /* Need to restore any FP callee saves? */
1143 if (cUnit->numFPSpills) {
1144 newLIR1(cUnit, kThumb2VPopCS, cUnit->numFPSpills);
1145 }
1146 if (cUnit->coreSpillMask & (1 << rLR)) {
1147 /* Unspill rLR to rPC */
1148 cUnit->coreSpillMask &= ~(1 << rLR);
1149 cUnit->coreSpillMask |= (1 << rPC);
1150 }
1151 newLIR1(cUnit, kThumb2Pop, cUnit->coreSpillMask);
1152 if (!(cUnit->coreSpillMask & (1 << rPC))) {
1153 /* We didn't pop to rPC, so must do a bv rLR */
1154 newLIR1(cUnit, kThumbBx, rLR);
1155 }
1156}
1157
1158/*
1159 * Nop any unconditional branches that go to the next instruction.
1160 * Note: new redundant branches may be inserted later, and we'll
1161 * use a check in final instruction assembly to nop those out.
1162 */
1163void removeRedundantBranches(CompilationUnit* cUnit)
1164{
1165 ArmLIR* thisLIR;
1166
1167 for (thisLIR = (ArmLIR*) cUnit->firstLIRInsn;
1168 thisLIR != (ArmLIR*) cUnit->lastLIRInsn;
1169 thisLIR = NEXT_LIR(thisLIR)) {
1170
1171 /* Branch to the next instruction */
1172 if ((thisLIR->opcode == kThumbBUncond) ||
1173 (thisLIR->opcode == kThumb2BUncond)) {
1174 ArmLIR* nextLIR = thisLIR;
1175
1176 while (true) {
1177 nextLIR = NEXT_LIR(nextLIR);
1178
1179 /*
1180 * Is the branch target the next instruction?
1181 */
1182 if (nextLIR == (ArmLIR*) thisLIR->generic.target) {
1183 thisLIR->flags.isNop = true;
1184 break;
1185 }
1186
1187 /*
1188 * Found real useful stuff between the branch and the target.
1189 * Need to explicitly check the lastLIRInsn here because it
1190 * might be the last real instruction.
1191 */
1192 if (!isPseudoOpcode(nextLIR->opcode) ||
1193 (nextLIR = (ArmLIR*) cUnit->lastLIRInsn))
1194 break;
1195 }
1196 }
1197 }
1198}
1199
1200STATIC void handleSuspendLaunchpads(CompilationUnit *cUnit)
1201{
1202 ArmLIR** suspendLabel =
1203 (ArmLIR **) cUnit->suspendLaunchpads.elemList;
1204 int numElems = cUnit->suspendLaunchpads.numUsed;
1205
1206 for (int i = 0; i < numElems; i++) {
1207 /* TUNING: move suspend count load into helper */
1208 ArmLIR* lab = suspendLabel[i];
1209 ArmLIR* resumeLab = (ArmLIR*)lab->operands[0];
1210 cUnit->currentDalvikOffset = lab->operands[1];
1211 oatAppendLIR(cUnit, (LIR *)lab);
1212 loadWordDisp(cUnit, rSELF,
1213 OFFSETOF_MEMBER(Thread, pTestSuspendFromCode), rLR);
1214 if (!cUnit->genDebugger) {
1215 // use rSUSPEND for suspend count
1216 loadWordDisp(cUnit, rSELF,
1217 Thread::SuspendCountOffset().Int32Value(), rSUSPEND);
1218 }
1219 opReg(cUnit, kOpBlx, rLR);
1220 if ( cUnit->genDebugger) {
1221 // use rSUSPEND for update debugger
1222 loadWordDisp(cUnit, rSELF,
1223 OFFSETOF_MEMBER(Thread, pUpdateDebuggerFromCode), rSUSPEND);
1224 }
1225 genUnconditionalBranch(cUnit, resumeLab);
1226 }
1227}
1228
1229STATIC void handleThrowLaunchpads(CompilationUnit *cUnit)
1230{
1231 ArmLIR** throwLabel =
1232 (ArmLIR **) cUnit->throwLaunchpads.elemList;
1233 int numElems = cUnit->throwLaunchpads.numUsed;
1234 int i;
1235
1236 for (i = 0; i < numElems; i++) {
1237 ArmLIR* lab = throwLabel[i];
1238 cUnit->currentDalvikOffset = lab->operands[1];
1239 oatAppendLIR(cUnit, (LIR *)lab);
1240 int funcOffset = 0;
1241 int v1 = lab->operands[2];
1242 int v2 = lab->operands[3];
1243 switch(lab->operands[0]) {
1244 case kArmThrowNullPointer:
1245 funcOffset = OFFSETOF_MEMBER(Thread, pThrowNullPointerFromCode);
1246 break;
1247 case kArmThrowArrayBounds:
1248 if (v2 != r0) {
1249 genRegCopy(cUnit, r0, v1);
1250 genRegCopy(cUnit, r1, v2);
1251 } else {
1252 if (v1 == r1) {
1253 genRegCopy(cUnit, r12, v1);
1254 genRegCopy(cUnit, r1, v2);
1255 genRegCopy(cUnit, r0, r12);
1256 } else {
1257 genRegCopy(cUnit, r1, v2);
1258 genRegCopy(cUnit, r0, v1);
1259 }
1260 }
1261 funcOffset = OFFSETOF_MEMBER(Thread, pThrowArrayBoundsFromCode);
1262 break;
1263 case kArmThrowDivZero:
1264 funcOffset = OFFSETOF_MEMBER(Thread, pThrowDivZeroFromCode);
1265 break;
1266 case kArmThrowVerificationError:
1267 loadConstant(cUnit, r0, v1);
1268 loadConstant(cUnit, r1, v2);
1269 funcOffset =
1270 OFFSETOF_MEMBER(Thread, pThrowVerificationErrorFromCode);
1271 break;
1272 case kArmThrowNegArraySize:
1273 genRegCopy(cUnit, r0, v1);
1274 funcOffset =
1275 OFFSETOF_MEMBER(Thread, pThrowNegArraySizeFromCode);
1276 break;
1277 case kArmThrowNoSuchMethod:
1278 genRegCopy(cUnit, r0, v1);
1279 funcOffset =
1280 OFFSETOF_MEMBER(Thread, pThrowNoSuchMethodFromCode);
1281 break;
1282 case kArmThrowStackOverflow:
1283 funcOffset =
1284 OFFSETOF_MEMBER(Thread, pThrowStackOverflowFromCode);
1285 // Restore stack alignment
1286 opRegImm(cUnit, kOpAdd, rSP,
1287 (cUnit->numCoreSpills + cUnit->numFPSpills) * 4);
1288 break;
1289 default:
1290 LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0];
1291 }
1292 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1293 callRuntimeHelper(cUnit, rLR);
1294 }
1295}
1296
1297/* Common initialization routine for an architecture family */
1298bool oatArchInit()
1299{
1300 int i;
1301
1302 for (i = 0; i < kArmLast; i++) {
1303 if (EncodingMap[i].opcode != i) {
1304 LOG(FATAL) << "Encoding order for " << EncodingMap[i].name <<
1305 " is wrong: expecting " << i << ", seeing " <<
1306 (int)EncodingMap[i].opcode;
1307 }
1308 }
1309
1310 return oatArchVariantInit();
1311}
1312
1313/* Needed by the Assembler */
1314void oatSetupResourceMasks(ArmLIR* lir)
1315{
1316 setupResourceMasks(lir);
1317}
1318
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08001319} // namespace art