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