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