blob: 79a62ad30b84d5b5b1520e6080ef483e2e7d417b [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;
Ian Rogers19846512012-02-24 11:42:47 -0800493 case 1: // Get method->dex_cache_resolved_methods_
buzbeee3acd072012-02-25 17:03:10 -0800494 loadWordDisp(cUnit, r0,
Ian Rogers19846512012-02-24 11:42:47 -0800495 Method::DexCacheResolvedMethodsOffset().Int32Value(),
buzbeee3acd072012-02-25 17:03:10 -0800496 r0);
497 break;
Ian Rogers19846512012-02-24 11:42:47 -0800498 case 2: // Grab target method*
buzbeee3acd072012-02-25 17:03:10 -0800499 loadWordDisp(cUnit, r0,
Ian Rogers19846512012-02-24 11:42:47 -0800500 Array::DataOffset().Int32Value() + dexIdx * 4, r0);
501 break;
502 case 3: // Grab the code from the method*
503 loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
buzbeee3acd072012-02-25 17:03:10 -0800504 break;
505 default:
506 return -1;
507 }
508 return state + 1;
509}
510
511/*
512 * Bit of a hack here - in leiu of a real scheduling pass,
513 * emit the next instruction in a virtual invoke sequence.
514 * We can use rLR as a temp prior to target address loading
515 * Note also that we'll load the first argument ("this") into
516 * r1 here rather than the standard loadArgRegs.
517 */
518STATIC int nextVCallInsn(CompilationUnit* cUnit, MIR* mir,
519 int state, uint32_t dexIdx, uint32_t methodIdx)
520{
521 RegLocation rlArg;
522 /*
523 * This is the fast path in which the target virtual method is
524 * fully resolved at compile time.
525 */
526 switch(state) {
527 case 0: // Get "this" [set r1]
528 rlArg = oatGetSrc(cUnit, mir, 0);
529 loadValueDirectFixed(cUnit, rlArg, r1);
530 break;
531 case 1: // Is "this" null? [use r1]
532 genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
533 // get this->klass_ [use r1, set rLR]
534 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
535 break;
536 case 2: // Get this->klass_->vtable [usr rLR, set rLR]
537 loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
538 break;
539 case 3: // Get target method [use rLR, set r0]
540 loadWordDisp(cUnit, rLR, (methodIdx * 4) +
541 Array::DataOffset().Int32Value(), r0);
542 break;
543 case 4: // Get the target compiled code address [uses r0, sets rLR]
544 loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
545 break;
546 default:
547 return -1;
548 }
549 return state + 1;
550}
551
552/*
553 * Interleave launch code for INVOKE_SUPER. See comments
554 * for nextVCallIns.
555 */
556STATIC int nextSuperCallInsn(CompilationUnit* cUnit, MIR* mir,
557 int state, uint32_t dexIdx, uint32_t methodIdx)
558{
559 /*
560 * This is the fast path in which the target virtual method is
561 * fully resolved at compile time. Note also that this path assumes
562 * that the check to verify that the target method index falls
563 * within the size of the super's vtable has been done at compile-time.
564 */
565 RegLocation rlArg;
566 switch(state) {
567 case 0: // Get current Method* [set r0]
568 loadCurrMethodDirect(cUnit, r0);
569 // Load "this" [set r1]
570 rlArg = oatGetSrc(cUnit, mir, 0);
571 loadValueDirectFixed(cUnit, rlArg, r1);
572 // Get method->declaring_class_ [use r0, set rLR]
573 loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
574 rLR);
575 // Is "this" null? [use r1]
576 genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
577 break;
578 case 1: // Get method->declaring_class_->super_class [usr rLR, set rLR]
579 loadWordDisp(cUnit, rLR, Class::SuperClassOffset().Int32Value(),
580 rLR);
581 break;
582 case 2: // Get ...->super_class_->vtable [u/s rLR]
583 loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR);
584 break;
585 case 3: // Get target method [use rLR, set r0]
586 loadWordDisp(cUnit, rLR, (methodIdx * 4) +
587 Array::DataOffset().Int32Value(), r0);
588 break;
589 case 4: // Get the target compiled code address [uses r0, sets rLR]
590 loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR);
591 break;
592 default:
593 return -1;
594 }
595 return state + 1;
596}
597
598STATIC int nextInvokeInsnSP(CompilationUnit* cUnit, MIR* mir, int trampoline,
599 int state, uint32_t dexIdx, uint32_t methodIdx)
600{
601 /*
602 * This handles the case in which the base method is not fully
603 * resolved at compile time, we bail to a runtime helper.
604 */
605 if (state == 0) {
606 // Load trampoline target
607 loadWordDisp(cUnit, rSELF, trampoline, rLR);
608 // Load r0 with method index
609 loadConstant(cUnit, r0, dexIdx);
610 return 1;
611 }
612 return -1;
613}
614
615STATIC int nextStaticCallInsnSP(CompilationUnit* cUnit, MIR* mir,
616 int state, uint32_t dexIdx, uint32_t methodIdx)
617{
618 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeStaticTrampolineWithAccessCheck);
619 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
620}
621
622STATIC int nextDirectCallInsnSP(CompilationUnit* cUnit, MIR* mir,
623 int state, uint32_t dexIdx, uint32_t methodIdx)
624{
625 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeDirectTrampolineWithAccessCheck);
626 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
627}
628
629STATIC int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir,
630 int state, uint32_t dexIdx, uint32_t methodIdx)
631{
632 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeSuperTrampolineWithAccessCheck);
633 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
634}
635
636STATIC int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir,
637 int state, uint32_t dexIdx, uint32_t methodIdx)
638{
639 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeVirtualTrampolineWithAccessCheck);
640 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
641}
642
643/*
644 * All invoke-interface calls bounce off of art_invoke_interface_trampoline,
645 * which will locate the target and continue on via a tail call.
646 */
647STATIC int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir,
648 int state, uint32_t dexIdx, uint32_t unused)
649{
650 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline);
651 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
652}
653
654STATIC int nextInterfaceCallInsnWithAccessCheck(CompilationUnit* cUnit,
655 MIR* mir, int state,
656 uint32_t dexIdx,
657 uint32_t unused)
658{
659 int trampoline = OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampolineWithAccessCheck);
660 return nextInvokeInsnSP(cUnit, mir, trampoline, state, dexIdx, 0);
661}
662
663STATIC int loadArgRegs(CompilationUnit* cUnit, MIR* mir,
664 DecodedInstruction* dInsn, int callState,
665 NextCallInsn nextCallInsn, uint32_t dexIdx,
666 uint32_t methodIdx, bool skipThis)
667{
668 int nextReg = r1;
669 int nextArg = 0;
670 if (skipThis) {
671 nextReg++;
672 nextArg++;
673 }
674 for (; (nextReg <= r3) && (nextArg < mir->ssaRep->numUses); nextReg++) {
675 RegLocation rlArg = oatGetRawSrc(cUnit, mir, nextArg++);
676 rlArg = oatUpdateRawLoc(cUnit, rlArg);
677 if (rlArg.wide && (nextReg <= r2)) {
678 loadValueDirectWideFixed(cUnit, rlArg, nextReg, nextReg + 1);
679 nextReg++;
680 nextArg++;
681 } else {
682 rlArg.wide = false;
683 loadValueDirectFixed(cUnit, rlArg, nextReg);
684 }
685 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
686 }
687 return callState;
688}
689
690/*
691 * Load up to 5 arguments, the first three of which will be in
692 * r1 .. r3. On entry r0 contains the current method pointer,
693 * and as part of the load sequence, it must be replaced with
694 * the target method pointer. Note, this may also be called
695 * for "range" variants if the number of arguments is 5 or fewer.
696 */
697STATIC int genDalvikArgsNoRange(CompilationUnit* cUnit, MIR* mir,
698 DecodedInstruction* dInsn, int callState,
699 ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
700 uint32_t dexIdx, uint32_t methodIdx,
701 bool skipThis)
702{
703 RegLocation rlArg;
704
705 /* If no arguments, just return */
706 if (dInsn->vA == 0)
707 return callState;
708
709 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
710
711 DCHECK_LE(dInsn->vA, 5U);
712 if (dInsn->vA > 3) {
713 uint32_t nextUse = 3;
714 //Detect special case of wide arg spanning arg3/arg4
715 RegLocation rlUse0 = oatGetRawSrc(cUnit, mir, 0);
716 RegLocation rlUse1 = oatGetRawSrc(cUnit, mir, 1);
717 RegLocation rlUse2 = oatGetRawSrc(cUnit, mir, 2);
718 if (((!rlUse0.wide && !rlUse1.wide) || rlUse0.wide) &&
719 rlUse2.wide) {
720 int reg;
721 // Wide spans, we need the 2nd half of uses[2].
722 rlArg = oatUpdateLocWide(cUnit, rlUse2);
723 if (rlArg.location == kLocPhysReg) {
724 reg = rlArg.highReg;
725 } else {
726 // r2 & r3 can safely be used here
727 reg = r3;
728 loadWordDisp(cUnit, rSP,
729 oatSRegOffset(cUnit, rlArg.sRegLow) + 4, reg);
730 callState = nextCallInsn(cUnit, mir, callState, dexIdx,
731 methodIdx);
732 }
733 storeBaseDisp(cUnit, rSP, (nextUse + 1) * 4, reg, kWord);
734 storeBaseDisp(cUnit, rSP, 16 /* (3+1)*4 */, reg, kWord);
735 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
736 nextUse++;
737 }
738 // Loop through the rest
739 while (nextUse < dInsn->vA) {
740 int lowReg;
741 int highReg;
742 rlArg = oatGetRawSrc(cUnit, mir, nextUse);
743 rlArg = oatUpdateRawLoc(cUnit, rlArg);
744 if (rlArg.location == kLocPhysReg) {
745 lowReg = rlArg.lowReg;
746 highReg = rlArg.highReg;
747 } else {
748 lowReg = r2;
749 highReg = r3;
750 if (rlArg.wide) {
751 loadValueDirectWideFixed(cUnit, rlArg, lowReg, highReg);
752 } else {
753 loadValueDirectFixed(cUnit, rlArg, lowReg);
754 }
755 callState = nextCallInsn(cUnit, mir, callState, dexIdx,
756 methodIdx);
757 }
758 int outsOffset = (nextUse + 1) * 4;
759 if (rlArg.wide) {
760 storeBaseDispWide(cUnit, rSP, outsOffset, lowReg, highReg);
761 nextUse += 2;
762 } else {
763 storeWordDisp(cUnit, rSP, outsOffset, lowReg);
764 nextUse++;
765 }
766 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
767 }
768 }
769
770 callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
771 dexIdx, methodIdx, skipThis);
772
773 if (pcrLabel) {
774 *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
775 }
776 return callState;
777}
778
779/*
780 * May have 0+ arguments (also used for jumbo). Note that
781 * source virtual registers may be in physical registers, so may
782 * need to be flushed to home location before copying. This
783 * applies to arg3 and above (see below).
784 *
785 * Two general strategies:
786 * If < 20 arguments
787 * Pass args 3-18 using vldm/vstm block copy
788 * Pass arg0, arg1 & arg2 in r1-r3
789 * If 20+ arguments
790 * Pass args arg19+ using memcpy block copy
791 * Pass arg0, arg1 & arg2 in r1-r3
792 *
793 */
794STATIC int genDalvikArgsRange(CompilationUnit* cUnit, MIR* mir,
795 DecodedInstruction* dInsn, int callState,
796 ArmLIR** pcrLabel, NextCallInsn nextCallInsn,
797 uint32_t dexIdx, uint32_t methodIdx,
798 bool skipThis)
799{
800 int firstArg = dInsn->vC;
801 int numArgs = dInsn->vA;
802
803 // If we can treat it as non-range (Jumbo ops will use range form)
804 if (numArgs <= 5)
805 return genDalvikArgsNoRange(cUnit, mir, dInsn, callState, pcrLabel,
806 nextCallInsn, dexIdx, methodIdx,
807 skipThis);
808 /*
809 * Make sure range list doesn't span the break between in normal
810 * Dalvik vRegs and the ins.
811 */
812 int highestArg = oatGetSrc(cUnit, mir, numArgs-1).sRegLow;
813 int boundaryReg = cUnit->numDalvikRegisters - cUnit->numIns;
814 if ((firstArg < boundaryReg) && (highestArg >= boundaryReg)) {
815 LOG(FATAL) << "Argument list spanned locals & args";
816 }
817
818 /*
819 * First load the non-register arguments. Both forms expect all
820 * of the source arguments to be in their home frame location, so
821 * scan the sReg names and flush any that have been promoted to
822 * frame backing storage.
823 */
824 // Scan the rest of the args - if in physReg flush to memory
825 for (int nextArg = 0; nextArg < numArgs;) {
826 RegLocation loc = oatGetRawSrc(cUnit, mir, nextArg);
827 if (loc.wide) {
828 loc = oatUpdateLocWide(cUnit, loc);
829 if ((nextArg >= 2) && (loc.location == kLocPhysReg)) {
830 storeBaseDispWide(cUnit, rSP,
831 oatSRegOffset(cUnit, loc.sRegLow),
832 loc.lowReg, loc.highReg);
833 }
834 nextArg += 2;
835 } else {
836 loc = oatUpdateLoc(cUnit, loc);
837 if ((nextArg >= 3) && (loc.location == kLocPhysReg)) {
838 storeBaseDisp(cUnit, rSP, oatSRegOffset(cUnit, loc.sRegLow),
839 loc.lowReg, kWord);
840 }
841 nextArg++;
842 }
843 }
844
845 int startOffset = oatSRegOffset(cUnit,
846 cUnit->regLocation[mir->ssaRep->uses[3]].sRegLow);
847 int outsOffset = 4 /* Method* */ + (3 * 4);
848 if (numArgs >= 20) {
849 // Generate memcpy
850 opRegRegImm(cUnit, kOpAdd, r0, rSP, outsOffset);
851 opRegRegImm(cUnit, kOpAdd, r1, rSP, startOffset);
852 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pMemcpy), rLR);
853 loadConstant(cUnit, r2, (numArgs - 3) * 4);
854 callRuntimeHelper(cUnit, rLR);
855 // Restore Method*
856 loadCurrMethodDirect(cUnit, r0);
857 } else {
858 // Use vldm/vstm pair using r3 as a temp
859 int regsLeft = std::min(numArgs - 3, 16);
860 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
861 opRegRegImm(cUnit, kOpAdd, r3, rSP, startOffset);
862 ArmLIR* ld = newLIR3(cUnit, kThumb2Vldms, r3, fr0, regsLeft);
863 //TUNING: loosen barrier
864 ld->defMask = ENCODE_ALL;
865 setMemRefType(ld, true /* isLoad */, kDalvikReg);
866 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
867 opRegRegImm(cUnit, kOpAdd, r3, rSP, 4 /* Method* */ + (3 * 4));
868 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
869 ArmLIR* st = newLIR3(cUnit, kThumb2Vstms, r3, fr0, regsLeft);
870 setMemRefType(st, false /* isLoad */, kDalvikReg);
871 st->defMask = ENCODE_ALL;
872 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
873 }
874
875 callState = loadArgRegs(cUnit, mir, dInsn, callState, nextCallInsn,
876 dexIdx, methodIdx, skipThis);
877
878 callState = nextCallInsn(cUnit, mir, callState, dexIdx, methodIdx);
879 if (pcrLabel) {
880 *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
881 }
882 return callState;
883}
884
885// Debugging routine - if null target, branch to DebugMe
886STATIC void genShowTarget(CompilationUnit* cUnit)
887{
888 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
889 loadWordDisp(cUnit, rSELF,
890 OFFSETOF_MEMBER(Thread, pDebugMe), rLR);
891 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
892 target->defMask = -1;
893 branchOver->generic.target = (LIR*)target;
894}
895
896STATIC void genThrowVerificationError(CompilationUnit* cUnit, MIR* mir)
897{
898 loadWordDisp(cUnit, rSELF,
899 OFFSETOF_MEMBER(Thread, pThrowVerificationErrorFromCode), rLR);
900 loadConstant(cUnit, r0, mir->dalvikInsn.vA);
901 loadConstant(cUnit, r1, mir->dalvikInsn.vB);
902 callRuntimeHelper(cUnit, rLR);
903}
904
905STATIC void genCompareAndBranch(CompilationUnit* cUnit, BasicBlock* bb,
906 MIR* mir, RegLocation rlSrc1,
907 RegLocation rlSrc2, ArmLIR* labelList)
908{
909 ArmConditionCode cond;
910 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
911 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
912 opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
913 Opcode opcode = mir->dalvikInsn.opcode;
914 switch(opcode) {
915 case OP_IF_EQ:
916 cond = kArmCondEq;
917 break;
918 case OP_IF_NE:
919 cond = kArmCondNe;
920 break;
921 case OP_IF_LT:
922 cond = kArmCondLt;
923 break;
924 case OP_IF_GE:
925 cond = kArmCondGe;
926 break;
927 case OP_IF_GT:
928 cond = kArmCondGt;
929 break;
930 case OP_IF_LE:
931 cond = kArmCondLe;
932 break;
933 default:
934 cond = (ArmConditionCode)0;
935 LOG(FATAL) << "Unexpected opcode " << (int)opcode;
936 }
937 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
938 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
939}
940
941STATIC void genCompareZeroAndBranch(CompilationUnit* cUnit, BasicBlock* bb,
942 MIR* mir, RegLocation rlSrc,
943 ArmLIR* labelList)
944{
945 ArmConditionCode cond;
946 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
947 opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0);
948 Opcode opcode = mir->dalvikInsn.opcode;
949 switch(opcode) {
950 case OP_IF_EQZ:
951 cond = kArmCondEq;
952 break;
953 case OP_IF_NEZ:
954 cond = kArmCondNe;
955 break;
956 case OP_IF_LTZ:
957 cond = kArmCondLt;
958 break;
959 case OP_IF_GEZ:
960 cond = kArmCondGe;
961 break;
962 case OP_IF_GTZ:
963 cond = kArmCondGt;
964 break;
965 case OP_IF_LEZ:
966 cond = kArmCondLe;
967 break;
968 default:
969 cond = (ArmConditionCode)0;
970 LOG(FATAL) << "Unexpected opcode " << (int)opcode;
971 }
972 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
973 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
974}
975
976STATIC void genIntToLong(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
977 RegLocation rlSrc)
978{
979 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
980 if (rlSrc.location == kLocPhysReg) {
981 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
982 } else {
983 loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
984 }
985 opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
986 rlResult.lowReg, 31);
987 storeValueWide(cUnit, rlDest, rlResult);
988}
989
990STATIC void genIntNarrowing(CompilationUnit* cUnit, MIR* mir,
991 RegLocation rlDest, RegLocation rlSrc)
992{
993 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
994 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
995 OpKind op = kOpInvalid;
996 switch(mir->dalvikInsn.opcode) {
997 case OP_INT_TO_BYTE:
998 op = kOp2Byte;
999 break;
1000 case OP_INT_TO_SHORT:
1001 op = kOp2Short;
1002 break;
1003 case OP_INT_TO_CHAR:
1004 op = kOp2Char;
1005 break;
1006 default:
1007 LOG(ERROR) << "Bad int conversion type";
1008 }
1009 opRegReg(cUnit, op, rlResult.lowReg, rlSrc.lowReg);
1010 storeValue(cUnit, rlDest, rlResult);
1011}
1012
1013/*
1014 * If there are any ins passed in registers that have not been promoted
1015 * to a callee-save register, flush them to the frame. Perform intial
1016 * assignment of promoted arguments.
1017 */
1018STATIC void flushIns(CompilationUnit* cUnit)
1019{
1020 if (cUnit->numIns == 0)
1021 return;
1022 int firstArgReg = r1;
1023 int lastArgReg = r3;
1024 int startVReg = cUnit->numDalvikRegisters - cUnit->numIns;
1025 /*
1026 * Arguments passed in registers should be flushed
1027 * to their backing locations in the frame for now.
1028 * Also, we need to do initial assignment for promoted
1029 * arguments. NOTE: an older version of dx had an issue
1030 * in which it would reuse static method argument registers.
1031 * This could result in the same Dalvik virtual register
1032 * being promoted to both core and fp regs. In those
1033 * cases, copy argument to both. This will be uncommon
1034 * enough that it isn't worth attempting to optimize.
1035 */
1036 for (int i = 0; i < cUnit->numIns; i++) {
1037 PromotionMap vMap = cUnit->promotionMap[startVReg + i];
1038 if (i <= (lastArgReg - firstArgReg)) {
1039 // If arriving in register
1040 if (vMap.coreLocation == kLocPhysReg) {
1041 genRegCopy(cUnit, vMap.coreReg, firstArgReg + i);
1042 }
1043 if (vMap.fpLocation == kLocPhysReg) {
1044 genRegCopy(cUnit, vMap.fpReg, firstArgReg + i);
1045 }
1046 // Also put a copy in memory in case we're partially promoted
1047 storeBaseDisp(cUnit, rSP, oatSRegOffset(cUnit, startVReg + i),
1048 firstArgReg + i, kWord);
1049 } else {
1050 // If arriving in frame & promoted
1051 if (vMap.coreLocation == kLocPhysReg) {
1052 loadWordDisp(cUnit, rSP, oatSRegOffset(cUnit, startVReg + i),
1053 vMap.coreReg);
1054 }
1055 if (vMap.fpLocation == kLocPhysReg) {
1056 loadWordDisp(cUnit, rSP, oatSRegOffset(cUnit, startVReg + i),
1057 vMap.fpReg);
1058 }
1059 }
1060 }
1061}
1062
1063STATIC void genEntrySequence(CompilationUnit* cUnit, BasicBlock* bb)
1064{
1065 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
1066 /*
1067 * On entry, r0, r1, r2 & r3 are live. Let the register allocation
1068 * mechanism know so it doesn't try to use any of them when
1069 * expanding the frame or flushing. This leaves the utility
1070 * code with a single temp: r12. This should be enough.
1071 */
1072 oatLockTemp(cUnit, r0);
1073 oatLockTemp(cUnit, r1);
1074 oatLockTemp(cUnit, r2);
1075 oatLockTemp(cUnit, r3);
1076
1077 /*
1078 * We can safely skip the stack overflow check if we're
1079 * a leaf *and* our frame size < fudge factor.
1080 */
1081 bool skipOverflowCheck = ((cUnit->attrs & METHOD_IS_LEAF) &&
1082 ((size_t)cUnit->frameSize <
1083 Thread::kStackOverflowReservedBytes));
1084 newLIR0(cUnit, kArmPseudoMethodEntry);
1085 if (!skipOverflowCheck) {
1086 /* Load stack limit */
1087 loadWordDisp(cUnit, rSELF,
1088 Thread::StackEndOffset().Int32Value(), r12);
1089 }
1090 /* Spill core callee saves */
1091 newLIR1(cUnit, kThumb2Push, cUnit->coreSpillMask);
1092 /* Need to spill any FP regs? */
1093 if (cUnit->numFPSpills) {
1094 /*
1095 * NOTE: fp spills are a little different from core spills in that
1096 * they are pushed as a contiguous block. When promoting from
1097 * the fp set, we must allocate all singles from s16..highest-promoted
1098 */
1099 newLIR1(cUnit, kThumb2VPushCS, cUnit->numFPSpills);
1100 }
1101 if (!skipOverflowCheck) {
1102 opRegRegImm(cUnit, kOpSub, rLR, rSP,
1103 cUnit->frameSize - (spillCount * 4));
1104 genRegRegCheck(cUnit, kArmCondCc, rLR, r12, NULL,
1105 kArmThrowStackOverflow);
1106 genRegCopy(cUnit, rSP, rLR); // Establish stack
1107 } else {
1108 opRegImm(cUnit, kOpSub, rSP,
1109 cUnit->frameSize - (spillCount * 4));
1110 }
1111 storeBaseDisp(cUnit, rSP, 0, r0, kWord);
1112 flushIns(cUnit);
1113
1114 if (cUnit->genDebugger) {
1115 // Refresh update debugger callout
1116 loadWordDisp(cUnit, rSELF,
1117 OFFSETOF_MEMBER(Thread, pUpdateDebuggerFromCode), rSUSPEND);
1118 genDebuggerUpdate(cUnit, DEBUGGER_METHOD_ENTRY);
1119 }
1120
1121 oatFreeTemp(cUnit, r0);
1122 oatFreeTemp(cUnit, r1);
1123 oatFreeTemp(cUnit, r2);
1124 oatFreeTemp(cUnit, r3);
1125}
1126
1127STATIC void genExitSequence(CompilationUnit* cUnit, BasicBlock* bb)
1128{
1129 int spillCount = cUnit->numCoreSpills + cUnit->numFPSpills;
1130 /*
1131 * In the exit path, r0/r1 are live - make sure they aren't
1132 * allocated by the register utilities as temps.
1133 */
1134 oatLockTemp(cUnit, r0);
1135 oatLockTemp(cUnit, r1);
1136
1137 newLIR0(cUnit, kArmPseudoMethodExit);
1138 /* If we're compiling for the debugger, generate an update callout */
1139 if (cUnit->genDebugger) {
1140 genDebuggerUpdate(cUnit, DEBUGGER_METHOD_EXIT);
1141 }
1142 opRegImm(cUnit, kOpAdd, rSP, cUnit->frameSize - (spillCount * 4));
1143 /* Need to restore any FP callee saves? */
1144 if (cUnit->numFPSpills) {
1145 newLIR1(cUnit, kThumb2VPopCS, cUnit->numFPSpills);
1146 }
1147 if (cUnit->coreSpillMask & (1 << rLR)) {
1148 /* Unspill rLR to rPC */
1149 cUnit->coreSpillMask &= ~(1 << rLR);
1150 cUnit->coreSpillMask |= (1 << rPC);
1151 }
1152 newLIR1(cUnit, kThumb2Pop, cUnit->coreSpillMask);
1153 if (!(cUnit->coreSpillMask & (1 << rPC))) {
1154 /* We didn't pop to rPC, so must do a bv rLR */
1155 newLIR1(cUnit, kThumbBx, rLR);
1156 }
1157}
1158
1159/*
1160 * Nop any unconditional branches that go to the next instruction.
1161 * Note: new redundant branches may be inserted later, and we'll
1162 * use a check in final instruction assembly to nop those out.
1163 */
1164void removeRedundantBranches(CompilationUnit* cUnit)
1165{
1166 ArmLIR* thisLIR;
1167
1168 for (thisLIR = (ArmLIR*) cUnit->firstLIRInsn;
1169 thisLIR != (ArmLIR*) cUnit->lastLIRInsn;
1170 thisLIR = NEXT_LIR(thisLIR)) {
1171
1172 /* Branch to the next instruction */
1173 if ((thisLIR->opcode == kThumbBUncond) ||
1174 (thisLIR->opcode == kThumb2BUncond)) {
1175 ArmLIR* nextLIR = thisLIR;
1176
1177 while (true) {
1178 nextLIR = NEXT_LIR(nextLIR);
1179
1180 /*
1181 * Is the branch target the next instruction?
1182 */
1183 if (nextLIR == (ArmLIR*) thisLIR->generic.target) {
1184 thisLIR->flags.isNop = true;
1185 break;
1186 }
1187
1188 /*
1189 * Found real useful stuff between the branch and the target.
1190 * Need to explicitly check the lastLIRInsn here because it
1191 * might be the last real instruction.
1192 */
1193 if (!isPseudoOpcode(nextLIR->opcode) ||
1194 (nextLIR = (ArmLIR*) cUnit->lastLIRInsn))
1195 break;
1196 }
1197 }
1198 }
1199}
1200
1201STATIC void handleSuspendLaunchpads(CompilationUnit *cUnit)
1202{
1203 ArmLIR** suspendLabel =
1204 (ArmLIR **) cUnit->suspendLaunchpads.elemList;
1205 int numElems = cUnit->suspendLaunchpads.numUsed;
1206
1207 for (int i = 0; i < numElems; i++) {
1208 /* TUNING: move suspend count load into helper */
1209 ArmLIR* lab = suspendLabel[i];
1210 ArmLIR* resumeLab = (ArmLIR*)lab->operands[0];
1211 cUnit->currentDalvikOffset = lab->operands[1];
1212 oatAppendLIR(cUnit, (LIR *)lab);
1213 loadWordDisp(cUnit, rSELF,
1214 OFFSETOF_MEMBER(Thread, pTestSuspendFromCode), rLR);
1215 if (!cUnit->genDebugger) {
1216 // use rSUSPEND for suspend count
1217 loadWordDisp(cUnit, rSELF,
1218 Thread::SuspendCountOffset().Int32Value(), rSUSPEND);
1219 }
1220 opReg(cUnit, kOpBlx, rLR);
1221 if ( cUnit->genDebugger) {
1222 // use rSUSPEND for update debugger
1223 loadWordDisp(cUnit, rSELF,
1224 OFFSETOF_MEMBER(Thread, pUpdateDebuggerFromCode), rSUSPEND);
1225 }
1226 genUnconditionalBranch(cUnit, resumeLab);
1227 }
1228}
1229
1230STATIC void handleThrowLaunchpads(CompilationUnit *cUnit)
1231{
1232 ArmLIR** throwLabel =
1233 (ArmLIR **) cUnit->throwLaunchpads.elemList;
1234 int numElems = cUnit->throwLaunchpads.numUsed;
1235 int i;
1236
1237 for (i = 0; i < numElems; i++) {
1238 ArmLIR* lab = throwLabel[i];
1239 cUnit->currentDalvikOffset = lab->operands[1];
1240 oatAppendLIR(cUnit, (LIR *)lab);
1241 int funcOffset = 0;
1242 int v1 = lab->operands[2];
1243 int v2 = lab->operands[3];
1244 switch(lab->operands[0]) {
1245 case kArmThrowNullPointer:
1246 funcOffset = OFFSETOF_MEMBER(Thread, pThrowNullPointerFromCode);
1247 break;
1248 case kArmThrowArrayBounds:
1249 if (v2 != r0) {
1250 genRegCopy(cUnit, r0, v1);
1251 genRegCopy(cUnit, r1, v2);
1252 } else {
1253 if (v1 == r1) {
1254 genRegCopy(cUnit, r12, v1);
1255 genRegCopy(cUnit, r1, v2);
1256 genRegCopy(cUnit, r0, r12);
1257 } else {
1258 genRegCopy(cUnit, r1, v2);
1259 genRegCopy(cUnit, r0, v1);
1260 }
1261 }
1262 funcOffset = OFFSETOF_MEMBER(Thread, pThrowArrayBoundsFromCode);
1263 break;
1264 case kArmThrowDivZero:
1265 funcOffset = OFFSETOF_MEMBER(Thread, pThrowDivZeroFromCode);
1266 break;
1267 case kArmThrowVerificationError:
1268 loadConstant(cUnit, r0, v1);
1269 loadConstant(cUnit, r1, v2);
1270 funcOffset =
1271 OFFSETOF_MEMBER(Thread, pThrowVerificationErrorFromCode);
1272 break;
1273 case kArmThrowNegArraySize:
1274 genRegCopy(cUnit, r0, v1);
1275 funcOffset =
1276 OFFSETOF_MEMBER(Thread, pThrowNegArraySizeFromCode);
1277 break;
1278 case kArmThrowNoSuchMethod:
1279 genRegCopy(cUnit, r0, v1);
1280 funcOffset =
1281 OFFSETOF_MEMBER(Thread, pThrowNoSuchMethodFromCode);
1282 break;
1283 case kArmThrowStackOverflow:
1284 funcOffset =
1285 OFFSETOF_MEMBER(Thread, pThrowStackOverflowFromCode);
1286 // Restore stack alignment
1287 opRegImm(cUnit, kOpAdd, rSP,
1288 (cUnit->numCoreSpills + cUnit->numFPSpills) * 4);
1289 break;
1290 default:
1291 LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0];
1292 }
1293 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1294 callRuntimeHelper(cUnit, rLR);
1295 }
1296}
1297
1298/* Common initialization routine for an architecture family */
1299bool oatArchInit()
1300{
1301 int i;
1302
1303 for (i = 0; i < kArmLast; i++) {
1304 if (EncodingMap[i].opcode != i) {
1305 LOG(FATAL) << "Encoding order for " << EncodingMap[i].name <<
1306 " is wrong: expecting " << i << ", seeing " <<
1307 (int)EncodingMap[i].opcode;
1308 }
1309 }
1310
1311 return oatArchVariantInit();
1312}
1313
1314/* Needed by the Assembler */
1315void oatSetupResourceMasks(ArmLIR* lir)
1316{
1317 setupResourceMasks(lir);
1318}
1319
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08001320} // namespace art