1 /*
2 * Copyright (C) 2013 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 #include "dex_file_method_inliner.h"
18
19 #include <algorithm>
20
21 #include "base/macros.h"
22 #include "base/mutex.h"
23 #include "base/mutex-inl.h"
24 #include "dex/frontend.h"
25 #include "thread.h"
26 #include "thread-inl.h"
27 #include "dex/mir_graph.h"
28 #include "dex_instruction.h"
29 #include "dex_instruction-inl.h"
30 #include "verifier/method_verifier.h"
31 #include "verifier/method_verifier-inl.h"
32
33 namespace art {
34
35 namespace { // anonymous namespace
36
37 static constexpr bool kIntrinsicIsStatic[] = {
38 true, // kIntrinsicDoubleCvt
39 true, // kIntrinsicFloatCvt
40 true, // kIntrinsicReverseBits
41 true, // kIntrinsicReverseBytes
42 true, // kIntrinsicAbsInt
43 true, // kIntrinsicAbsLong
44 true, // kIntrinsicAbsFloat
45 true, // kIntrinsicAbsDouble
46 true, // kIntrinsicMinMaxInt
47 true, // kIntrinsicMinMaxLong
48 true, // kIntrinsicMinMaxFloat
49 true, // kIntrinsicMinMaxDouble
50 true, // kIntrinsicSqrt
51 true, // kIntrinsicCeil
52 true, // kIntrinsicFloor
53 true, // kIntrinsicRint
54 true, // kIntrinsicRoundFloat
55 true, // kIntrinsicRoundDouble
56 false, // kIntrinsicReferenceGetReferent
57 false, // kIntrinsicCharAt
58 false, // kIntrinsicCompareTo
59 false, // kIntrinsicIsEmptyOrLength
60 false, // kIntrinsicIndexOf
61 true, // kIntrinsicCurrentThread
62 true, // kIntrinsicPeek
63 true, // kIntrinsicPoke
64 false, // kIntrinsicCas
65 false, // kIntrinsicUnsafeGet
66 false, // kIntrinsicUnsafePut
67 true, // kIntrinsicSystemArrayCopyCharArray
68 };
69 COMPILE_ASSERT(arraysize(kIntrinsicIsStatic) == kInlineOpNop, check_arraysize_kIntrinsicIsStatic);
70 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicDoubleCvt], DoubleCvt_must_be_static);
71 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloatCvt], FloatCvt_must_be_static);
72 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicReverseBits], ReverseBits_must_be_static);
73 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicReverseBytes], ReverseBytes_must_be_static);
74 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsInt], AbsInt_must_be_static);
75 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsLong], AbsLong_must_be_static);
76 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsFloat], AbsFloat_must_be_static);
77 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicAbsDouble], AbsDouble_must_be_static);
78 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxInt], MinMaxInt_must_be_static);
79 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxLong], MinMaxLong_must_be_static);
80 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], MinMaxFloat_must_be_static);
81 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], MinMaxDouble_must_be_static);
82 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSqrt], Sqrt_must_be_static);
83 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicCeil], Ceil_must_be_static);
84 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloor], Floor_must_be_static);
85 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRint], Rint_must_be_static);
86 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundFloat], RoundFloat_must_be_static);
87 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundDouble], RoundDouble_must_be_static);
88 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], Get_must_not_be_static);
89 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCharAt], CharAt_must_not_be_static);
90 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCompareTo], CompareTo_must_not_be_static);
91 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], IsEmptyOrLength_must_not_be_static);
92 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIndexOf], IndexOf_must_not_be_static);
93 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicCurrentThread], CurrentThread_must_be_static);
94 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicPeek], Peek_must_be_static);
95 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicPoke], Poke_must_be_static);
96 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCas], Cas_must_not_be_static);
97 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], UnsafeGet_must_not_be_static);
98 COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicUnsafePut], UnsafePut_must_not_be_static);
99 COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
100 SystemArrayCopyCharArray_must_be_static);
101
AllocReplacementMIR(MIRGraph * mir_graph,MIR * invoke,MIR * move_return)102 MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) {
103 MIR* insn = mir_graph->NewMIR();
104 insn->offset = invoke->offset;
105 insn->optimization_flags = MIR_CALLEE;
106 return insn;
107 }
108
GetInvokeReg(MIR * invoke,uint32_t arg)109 uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) {
110 DCHECK_LT(arg, invoke->dalvikInsn.vA);
111 DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode));
112 if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) {
113 return invoke->dalvikInsn.vC + arg; // Non-range invoke.
114 } else {
115 DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c);
116 return invoke->dalvikInsn.arg[arg]; // Range invoke.
117 }
118 }
119
WideArgIsInConsecutiveDalvikRegs(MIR * invoke,uint32_t arg)120 bool WideArgIsInConsecutiveDalvikRegs(MIR* invoke, uint32_t arg) {
121 DCHECK_LT(arg + 1, invoke->dalvikInsn.vA);
122 DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode));
123 return Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc ||
124 invoke->dalvikInsn.arg[arg + 1u] == invoke->dalvikInsn.arg[arg] + 1u;
125 }
126
127 } // anonymous namespace
128
129 const uint32_t DexFileMethodInliner::kIndexUnresolved;
130 const char* const DexFileMethodInliner::kClassCacheNames[] = {
131 "Z", // kClassCacheBoolean
132 "B", // kClassCacheByte
133 "C", // kClassCacheChar
134 "S", // kClassCacheShort
135 "I", // kClassCacheInt
136 "J", // kClassCacheLong
137 "F", // kClassCacheFloat
138 "D", // kClassCacheDouble
139 "V", // kClassCacheVoid
140 "Ljava/lang/Object;", // kClassCacheJavaLangObject
141 "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference
142 "Ljava/lang/String;", // kClassCacheJavaLangString
143 "Ljava/lang/Double;", // kClassCacheJavaLangDouble
144 "Ljava/lang/Float;", // kClassCacheJavaLangFloat
145 "Ljava/lang/Integer;", // kClassCacheJavaLangInteger
146 "Ljava/lang/Long;", // kClassCacheJavaLangLong
147 "Ljava/lang/Short;", // kClassCacheJavaLangShort
148 "Ljava/lang/Math;", // kClassCacheJavaLangMath
149 "Ljava/lang/StrictMath;", // kClassCacheJavaLangStrictMath
150 "Ljava/lang/Thread;", // kClassCacheJavaLangThread
151 "Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory
152 "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe
153 "Ljava/lang/System;", // kClassCacheJavaLangSystem
154 "[C" // kClassCacheJavaLangCharArray
155 };
156
157 const char* const DexFileMethodInliner::kNameCacheNames[] = {
158 "reverse", // kNameCacheReverse
159 "reverseBytes", // kNameCacheReverseBytes
160 "doubleToRawLongBits", // kNameCacheDoubleToRawLongBits
161 "longBitsToDouble", // kNameCacheLongBitsToDouble
162 "floatToRawIntBits", // kNameCacheFloatToRawIntBits
163 "intBitsToFloat", // kNameCacheIntBitsToFloat
164 "abs", // kNameCacheAbs
165 "max", // kNameCacheMax
166 "min", // kNameCacheMin
167 "sqrt", // kNameCacheSqrt
168 "ceil", // kNameCacheCeil
169 "floor", // kNameCacheFloor
170 "rint", // kNameCacheRint
171 "round", // kNameCacheRound
172 "getReferent", // kNameCacheReferenceGet
173 "charAt", // kNameCacheCharAt
174 "compareTo", // kNameCacheCompareTo
175 "isEmpty", // kNameCacheIsEmpty
176 "indexOf", // kNameCacheIndexOf
177 "length", // kNameCacheLength
178 "currentThread", // kNameCacheCurrentThread
179 "peekByte", // kNameCachePeekByte
180 "peekIntNative", // kNameCachePeekIntNative
181 "peekLongNative", // kNameCachePeekLongNative
182 "peekShortNative", // kNameCachePeekShortNative
183 "pokeByte", // kNameCachePokeByte
184 "pokeIntNative", // kNameCachePokeIntNative
185 "pokeLongNative", // kNameCachePokeLongNative
186 "pokeShortNative", // kNameCachePokeShortNative
187 "compareAndSwapInt", // kNameCacheCompareAndSwapInt
188 "compareAndSwapLong", // kNameCacheCompareAndSwapLong
189 "compareAndSwapObject", // kNameCacheCompareAndSwapObject
190 "getInt", // kNameCacheGetInt
191 "getIntVolatile", // kNameCacheGetIntVolatile
192 "putInt", // kNameCachePutInt
193 "putIntVolatile", // kNameCachePutIntVolatile
194 "putOrderedInt", // kNameCachePutOrderedInt
195 "getLong", // kNameCacheGetLong
196 "getLongVolatile", // kNameCacheGetLongVolatile
197 "putLong", // kNameCachePutLong
198 "putLongVolatile", // kNameCachePutLongVolatile
199 "putOrderedLong", // kNameCachePutOrderedLong
200 "getObject", // kNameCacheGetObject
201 "getObjectVolatile", // kNameCacheGetObjectVolatile
202 "putObject", // kNameCachePutObject
203 "putObjectVolatile", // kNameCachePutObjectVolatile
204 "putOrderedObject", // kNameCachePutOrderedObject
205 "arraycopy", // kNameCacheArrayCopy
206 };
207
208 const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
209 // kProtoCacheI_I
210 { kClassCacheInt, 1, { kClassCacheInt } },
211 // kProtoCacheJ_J
212 { kClassCacheLong, 1, { kClassCacheLong } },
213 // kProtoCacheS_S
214 { kClassCacheShort, 1, { kClassCacheShort } },
215 // kProtoCacheD_D
216 { kClassCacheDouble, 1, { kClassCacheDouble } },
217 // kProtoCacheDD_D
218 { kClassCacheDouble, 2, { kClassCacheDouble, kClassCacheDouble } },
219 // kProtoCacheF_F
220 { kClassCacheFloat, 1, { kClassCacheFloat } },
221 // kProtoCacheFF_F
222 { kClassCacheFloat, 2, { kClassCacheFloat, kClassCacheFloat } },
223 // kProtoCacheD_J
224 { kClassCacheLong, 1, { kClassCacheDouble } },
225 // kProtoCacheJ_D
226 { kClassCacheDouble, 1, { kClassCacheLong } },
227 // kProtoCacheF_I
228 { kClassCacheInt, 1, { kClassCacheFloat } },
229 // kProtoCacheI_F
230 { kClassCacheFloat, 1, { kClassCacheInt } },
231 // kProtoCacheII_I
232 { kClassCacheInt, 2, { kClassCacheInt, kClassCacheInt } },
233 // kProtoCacheI_C
234 { kClassCacheChar, 1, { kClassCacheInt } },
235 // kProtoCacheString_I
236 { kClassCacheInt, 1, { kClassCacheJavaLangString } },
237 // kProtoCache_Z
238 { kClassCacheBoolean, 0, { } },
239 // kProtoCache_I
240 { kClassCacheInt, 0, { } },
241 // kProtoCache_Object
242 { kClassCacheJavaLangObject, 0, { } },
243 // kProtoCache_Thread
244 { kClassCacheJavaLangThread, 0, { } },
245 // kProtoCacheJ_B
246 { kClassCacheByte, 1, { kClassCacheLong } },
247 // kProtoCacheJ_I
248 { kClassCacheInt, 1, { kClassCacheLong } },
249 // kProtoCacheJ_S
250 { kClassCacheShort, 1, { kClassCacheLong } },
251 // kProtoCacheJB_V
252 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheByte } },
253 // kProtoCacheJI_V
254 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } },
255 // kProtoCacheJJ_J
256 { kClassCacheLong, 2, { kClassCacheLong, kClassCacheLong } },
257 // kProtoCacheJJ_V
258 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } },
259 // kProtoCacheJS_V
260 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } },
261 // kProtoCacheObjectJII_Z
262 { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
263 kClassCacheInt, kClassCacheInt } },
264 // kProtoCacheObjectJJJ_Z
265 { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
266 kClassCacheLong, kClassCacheLong } },
267 // kProtoCacheObjectJObjectObject_Z
268 { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
269 kClassCacheJavaLangObject, kClassCacheJavaLangObject } },
270 // kProtoCacheObjectJ_I
271 { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
272 // kProtoCacheObjectJI_V
273 { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
274 // kProtoCacheObjectJ_J
275 { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
276 // kProtoCacheObjectJJ_V
277 { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
278 // kProtoCacheObjectJ_Object
279 { kClassCacheJavaLangObject, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
280 // kProtoCacheObjectJObject_V
281 { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong,
282 kClassCacheJavaLangObject } },
283 // kProtoCacheCharArrayICharArrayII_V
284 { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
285 kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt}}
286 };
287
288 const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
289 #define INTRINSIC(c, n, p, o, d) \
290 { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } }
291
292 INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
293 INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0),
294 INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
295 INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0),
296
297 INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32),
298 INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64),
299 INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf),
300 INTRINSIC(JavaLangInteger, Reverse, I_I, kIntrinsicReverseBits, k32),
301 INTRINSIC(JavaLangLong, Reverse, J_J, kIntrinsicReverseBits, k64),
302
303 INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0),
304 INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
305 INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0),
306 INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0),
307 INTRINSIC(JavaLangMath, Abs, F_F, kIntrinsicAbsFloat, 0),
308 INTRINSIC(JavaLangStrictMath, Abs, F_F, kIntrinsicAbsFloat, 0),
309 INTRINSIC(JavaLangMath, Abs, D_D, kIntrinsicAbsDouble, 0),
310 INTRINSIC(JavaLangStrictMath, Abs, D_D, kIntrinsicAbsDouble, 0),
311 INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin),
312 INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin),
313 INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax),
314 INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax),
315 INTRINSIC(JavaLangMath, Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin),
316 INTRINSIC(JavaLangStrictMath, Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin),
317 INTRINSIC(JavaLangMath, Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax),
318 INTRINSIC(JavaLangStrictMath, Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax),
319 INTRINSIC(JavaLangMath, Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin),
320 INTRINSIC(JavaLangStrictMath, Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin),
321 INTRINSIC(JavaLangMath, Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax),
322 INTRINSIC(JavaLangStrictMath, Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax),
323 INTRINSIC(JavaLangMath, Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin),
324 INTRINSIC(JavaLangStrictMath, Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin),
325 INTRINSIC(JavaLangMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
326 INTRINSIC(JavaLangStrictMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
327
328 INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0),
329 INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0),
330
331 INTRINSIC(JavaLangMath, Ceil, D_D, kIntrinsicCeil, 0),
332 INTRINSIC(JavaLangStrictMath, Ceil, D_D, kIntrinsicCeil, 0),
333 INTRINSIC(JavaLangMath, Floor, D_D, kIntrinsicFloor, 0),
334 INTRINSIC(JavaLangStrictMath, Floor, D_D, kIntrinsicFloor, 0),
335 INTRINSIC(JavaLangMath, Rint, D_D, kIntrinsicRint, 0),
336 INTRINSIC(JavaLangStrictMath, Rint, D_D, kIntrinsicRint, 0),
337 INTRINSIC(JavaLangMath, Round, F_I, kIntrinsicRoundFloat, 0),
338 INTRINSIC(JavaLangStrictMath, Round, F_I, kIntrinsicRoundFloat, 0),
339 INTRINSIC(JavaLangMath, Round, D_J, kIntrinsicRoundDouble, 0),
340 INTRINSIC(JavaLangStrictMath, Round, D_J, kIntrinsicRoundDouble, 0),
341
342 INTRINSIC(JavaLangRefReference, ReferenceGetReferent, _Object, kIntrinsicReferenceGetReferent, 0),
343
344 INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0),
345 INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0),
346 INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty),
347 INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone),
348 INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0),
349 INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength),
350
351 INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0),
352
353 INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte),
354 INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, k32),
355 INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, k64),
356 INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf),
357 INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte),
358 INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, k32),
359 INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, k64),
360 INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf),
361
362 INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas,
363 kIntrinsicFlagNone),
364 INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas,
365 kIntrinsicFlagIsLong),
366 INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas,
367 kIntrinsicFlagIsObject),
368
369 #define UNSAFE_GET_PUT(type, code, type_flags) \
370 INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \
371 type_flags & ~kIntrinsicFlagIsObject), \
372 INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \
373 (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \
374 INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
375 type_flags), \
376 INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
377 type_flags | kIntrinsicFlagIsVolatile), \
378 INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
379 type_flags | kIntrinsicFlagIsOrdered)
380
381 UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone),
382 UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong),
383 UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject),
384 #undef UNSAFE_GET_PUT
385
386 INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
387 0),
388
389
390 #undef INTRINSIC
391 };
392
DexFileMethodInliner()393 DexFileMethodInliner::DexFileMethodInliner()
394 : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock),
395 dex_file_(NULL) {
396 COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0);
397 COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames);
398 COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0);
399 COMPILE_ASSERT(arraysize(kNameCacheNames) == kNameCacheLast, bad_arraysize_kNameCacheNames);
400 COMPILE_ASSERT(kProtoCacheFirst == 0, kProtoCacheFirst_not_0);
401 COMPILE_ASSERT(arraysize(kProtoCacheDefs) == kProtoCacheLast, bad_arraysize_kProtoCacheNames);
402 }
403
~DexFileMethodInliner()404 DexFileMethodInliner::~DexFileMethodInliner() {
405 }
406
AnalyseMethodCode(verifier::MethodVerifier * verifier)407 bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier) {
408 InlineMethod method;
409 bool success = InlineMethodAnalyser::AnalyseMethodCode(verifier, &method);
410 return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method);
411 }
412
IsIntrinsic(uint32_t method_index,InlineMethod * intrinsic)413 bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) {
414 ReaderMutexLock mu(Thread::Current(), lock_);
415 auto it = inline_methods_.find(method_index);
416 bool res = (it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0);
417 if (res && intrinsic != nullptr) {
418 *intrinsic = it->second;
419 }
420 return res;
421 }
422
GenIntrinsic(Mir2Lir * backend,CallInfo * info)423 bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) {
424 InlineMethod intrinsic;
425 {
426 ReaderMutexLock mu(Thread::Current(), lock_);
427 auto it = inline_methods_.find(info->index);
428 if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) {
429 return false;
430 }
431 intrinsic = it->second;
432 }
433 if (kIntrinsicIsStatic[intrinsic.opcode] != (info->type == kStatic)) {
434 // Invoke type mismatch.
435 return false;
436 }
437 switch (intrinsic.opcode) {
438 case kIntrinsicDoubleCvt:
439 return backend->GenInlinedDoubleCvt(info);
440 case kIntrinsicFloatCvt:
441 return backend->GenInlinedFloatCvt(info);
442 case kIntrinsicReverseBytes:
443 return backend->GenInlinedReverseBytes(info, static_cast<OpSize>(intrinsic.d.data));
444 case kIntrinsicReverseBits:
445 return backend->GenInlinedReverseBits(info, static_cast<OpSize>(intrinsic.d.data));
446 case kIntrinsicAbsInt:
447 return backend->GenInlinedAbsInt(info);
448 case kIntrinsicAbsLong:
449 return backend->GenInlinedAbsLong(info);
450 case kIntrinsicAbsFloat:
451 return backend->GenInlinedAbsFloat(info);
452 case kIntrinsicAbsDouble:
453 return backend->GenInlinedAbsDouble(info);
454 case kIntrinsicMinMaxInt:
455 return backend->GenInlinedMinMax(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_long */);
456 case kIntrinsicMinMaxLong:
457 return backend->GenInlinedMinMax(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_long */);
458 case kIntrinsicMinMaxFloat:
459 return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_double */);
460 case kIntrinsicMinMaxDouble:
461 return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_double */);
462 case kIntrinsicSqrt:
463 return backend->GenInlinedSqrt(info);
464 case kIntrinsicCeil:
465 return backend->GenInlinedCeil(info);
466 case kIntrinsicFloor:
467 return backend->GenInlinedFloor(info);
468 case kIntrinsicRint:
469 return backend->GenInlinedRint(info);
470 case kIntrinsicRoundFloat:
471 return backend->GenInlinedRound(info, false /* is_double */);
472 case kIntrinsicRoundDouble:
473 return backend->GenInlinedRound(info, true /* is_double */);
474 case kIntrinsicReferenceGetReferent:
475 return backend->GenInlinedReferenceGetReferent(info);
476 case kIntrinsicCharAt:
477 return backend->GenInlinedCharAt(info);
478 case kIntrinsicCompareTo:
479 return backend->GenInlinedStringCompareTo(info);
480 case kIntrinsicIsEmptyOrLength:
481 return backend->GenInlinedStringIsEmptyOrLength(
482 info, intrinsic.d.data & kIntrinsicFlagIsEmpty);
483 case kIntrinsicIndexOf:
484 return backend->GenInlinedIndexOf(info, intrinsic.d.data & kIntrinsicFlagBase0);
485 case kIntrinsicCurrentThread:
486 return backend->GenInlinedCurrentThread(info);
487 case kIntrinsicPeek:
488 return backend->GenInlinedPeek(info, static_cast<OpSize>(intrinsic.d.data));
489 case kIntrinsicPoke:
490 return backend->GenInlinedPoke(info, static_cast<OpSize>(intrinsic.d.data));
491 case kIntrinsicCas:
492 return backend->GenInlinedCas(info, intrinsic.d.data & kIntrinsicFlagIsLong,
493 intrinsic.d.data & kIntrinsicFlagIsObject);
494 case kIntrinsicUnsafeGet:
495 return backend->GenInlinedUnsafeGet(info, intrinsic.d.data & kIntrinsicFlagIsLong,
496 intrinsic.d.data & kIntrinsicFlagIsVolatile);
497 case kIntrinsicUnsafePut:
498 return backend->GenInlinedUnsafePut(info, intrinsic.d.data & kIntrinsicFlagIsLong,
499 intrinsic.d.data & kIntrinsicFlagIsObject,
500 intrinsic.d.data & kIntrinsicFlagIsVolatile,
501 intrinsic.d.data & kIntrinsicFlagIsOrdered);
502 case kIntrinsicSystemArrayCopyCharArray:
503 return backend->GenInlinedArrayCopyCharArray(info);
504 default:
505 LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
506 return false; // avoid warning "control reaches end of non-void function"
507 }
508 }
509
IsSpecial(uint32_t method_index)510 bool DexFileMethodInliner::IsSpecial(uint32_t method_index) {
511 ReaderMutexLock mu(Thread::Current(), lock_);
512 auto it = inline_methods_.find(method_index);
513 return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0;
514 }
515
GenSpecial(Mir2Lir * backend,uint32_t method_idx)516 bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) {
517 InlineMethod special;
518 {
519 ReaderMutexLock mu(Thread::Current(), lock_);
520 auto it = inline_methods_.find(method_idx);
521 if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
522 return false;
523 }
524 special = it->second;
525 }
526 return backend->SpecialMIR2LIR(special);
527 }
528
GenInline(MIRGraph * mir_graph,BasicBlock * bb,MIR * invoke,uint32_t method_idx)529 bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
530 uint32_t method_idx) {
531 InlineMethod method;
532 {
533 ReaderMutexLock mu(Thread::Current(), lock_);
534 auto it = inline_methods_.find(method_idx);
535 if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
536 return false;
537 }
538 method = it->second;
539 }
540
541 MIR* move_result = nullptr;
542 bool result = true;
543 switch (method.opcode) {
544 case kInlineOpNop:
545 break;
546 case kInlineOpNonWideConst:
547 move_result = mir_graph->FindMoveResult(bb, invoke);
548 result = GenInlineConst(mir_graph, bb, invoke, move_result, method);
549 break;
550 case kInlineOpReturnArg:
551 move_result = mir_graph->FindMoveResult(bb, invoke);
552 result = GenInlineReturnArg(mir_graph, bb, invoke, move_result, method);
553 break;
554 case kInlineOpIGet:
555 move_result = mir_graph->FindMoveResult(bb, invoke);
556 result = GenInlineIGet(mir_graph, bb, invoke, move_result, method, method_idx);
557 break;
558 case kInlineOpIPut:
559 move_result = mir_graph->FindMoveResult(bb, invoke);
560 result = GenInlineIPut(mir_graph, bb, invoke, move_result, method, method_idx);
561 break;
562 default:
563 LOG(FATAL) << "Unexpected inline op: " << method.opcode;
564 }
565 if (result) {
566 invoke->optimization_flags |= MIR_INLINED;
567 if (move_result != nullptr) {
568 move_result->optimization_flags |= MIR_INLINED;
569 move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
570 }
571 }
572 return result;
573 }
574
FindClassIndex(const DexFile * dex_file,IndexCache * cache,ClassCacheIndex index)575 uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache,
576 ClassCacheIndex index) {
577 uint32_t* class_index = &cache->class_indexes[index];
578 if (*class_index != kIndexUnresolved) {
579 return *class_index;
580 }
581
582 const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]);
583 if (string_id == nullptr) {
584 *class_index = kIndexNotFound;
585 return *class_index;
586 }
587 uint32_t string_index = dex_file->GetIndexForStringId(*string_id);
588
589 const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index);
590 if (type_id == nullptr) {
591 *class_index = kIndexNotFound;
592 return *class_index;
593 }
594 *class_index = dex_file->GetIndexForTypeId(*type_id);
595 return *class_index;
596 }
597
FindNameIndex(const DexFile * dex_file,IndexCache * cache,NameCacheIndex index)598 uint32_t DexFileMethodInliner::FindNameIndex(const DexFile* dex_file, IndexCache* cache,
599 NameCacheIndex index) {
600 uint32_t* name_index = &cache->name_indexes[index];
601 if (*name_index != kIndexUnresolved) {
602 return *name_index;
603 }
604
605 const DexFile::StringId* string_id = dex_file->FindStringId(kNameCacheNames[index]);
606 if (string_id == nullptr) {
607 *name_index = kIndexNotFound;
608 return *name_index;
609 }
610 *name_index = dex_file->GetIndexForStringId(*string_id);
611 return *name_index;
612 }
613
FindProtoIndex(const DexFile * dex_file,IndexCache * cache,ProtoCacheIndex index)614 uint32_t DexFileMethodInliner::FindProtoIndex(const DexFile* dex_file, IndexCache* cache,
615 ProtoCacheIndex index) {
616 uint32_t* proto_index = &cache->proto_indexes[index];
617 if (*proto_index != kIndexUnresolved) {
618 return *proto_index;
619 }
620
621 const ProtoDef& proto_def = kProtoCacheDefs[index];
622 uint32_t return_index = FindClassIndex(dex_file, cache, proto_def.return_type);
623 if (return_index == kIndexNotFound) {
624 *proto_index = kIndexNotFound;
625 return *proto_index;
626 }
627 uint16_t return_type = static_cast<uint16_t>(return_index);
628 DCHECK_EQ(static_cast<uint32_t>(return_type), return_index);
629
630 uint32_t signature_length = proto_def.param_count;
631 uint16_t signature_type_idxs[kProtoMaxParams];
632 for (uint32_t i = 0; i != signature_length; ++i) {
633 uint32_t param_index = FindClassIndex(dex_file, cache, proto_def.params[i]);
634 if (param_index == kIndexNotFound) {
635 *proto_index = kIndexNotFound;
636 return *proto_index;
637 }
638 signature_type_idxs[i] = static_cast<uint16_t>(param_index);
639 DCHECK_EQ(static_cast<uint32_t>(signature_type_idxs[i]), param_index);
640 }
641
642 const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type, signature_type_idxs,
643 signature_length);
644 if (proto_id == nullptr) {
645 *proto_index = kIndexNotFound;
646 return *proto_index;
647 }
648 *proto_index = dex_file->GetIndexForProtoId(*proto_id);
649 return *proto_index;
650 }
651
FindMethodIndex(const DexFile * dex_file,IndexCache * cache,const MethodDef & method_def)652 uint32_t DexFileMethodInliner::FindMethodIndex(const DexFile* dex_file, IndexCache* cache,
653 const MethodDef& method_def) {
654 uint32_t declaring_class_index = FindClassIndex(dex_file, cache, method_def.declaring_class);
655 if (declaring_class_index == kIndexNotFound) {
656 return kIndexNotFound;
657 }
658 uint32_t name_index = FindNameIndex(dex_file, cache, method_def.name);
659 if (name_index == kIndexNotFound) {
660 return kIndexNotFound;
661 }
662 uint32_t proto_index = FindProtoIndex(dex_file, cache, method_def.proto);
663 if (proto_index == kIndexNotFound) {
664 return kIndexNotFound;
665 }
666 const DexFile::MethodId* method_id =
667 dex_file->FindMethodId(dex_file->GetTypeId(declaring_class_index),
668 dex_file->GetStringId(name_index),
669 dex_file->GetProtoId(proto_index));
670 if (method_id == nullptr) {
671 return kIndexNotFound;
672 }
673 return dex_file->GetIndexForMethodId(*method_id);
674 }
675
IndexCache()676 DexFileMethodInliner::IndexCache::IndexCache() {
677 std::fill_n(class_indexes, arraysize(class_indexes), kIndexUnresolved);
678 std::fill_n(name_indexes, arraysize(name_indexes), kIndexUnresolved);
679 std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved);
680 }
681
FindIntrinsics(const DexFile * dex_file)682 void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) {
683 DCHECK(dex_file != nullptr);
684 DCHECK(dex_file_ == nullptr);
685 IndexCache cache;
686 for (const IntrinsicDef& def : kIntrinsicMethods) {
687 uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def);
688 if (method_idx != kIndexNotFound) {
689 DCHECK(inline_methods_.find(method_idx) == inline_methods_.end());
690 inline_methods_.Put(method_idx, def.intrinsic);
691 }
692 }
693 dex_file_ = dex_file;
694 }
695
AddInlineMethod(int32_t method_idx,const InlineMethod & method)696 bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, const InlineMethod& method) {
697 WriterMutexLock mu(Thread::Current(), lock_);
698 if (LIKELY(inline_methods_.find(method_idx) == inline_methods_.end())) {
699 inline_methods_.Put(method_idx, method);
700 return true;
701 } else {
702 if (PrettyMethod(method_idx, *dex_file_) == "int java.lang.String.length()") {
703 // TODO: String.length is both kIntrinsicIsEmptyOrLength and kInlineOpIGet.
704 } else {
705 LOG(ERROR) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline";
706 }
707 return false;
708 }
709 }
710
GenInlineConst(MIRGraph * mir_graph,BasicBlock * bb,MIR * invoke,MIR * move_result,const InlineMethod & method)711 bool DexFileMethodInliner::GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
712 MIR* move_result, const InlineMethod& method) {
713 if (move_result == nullptr) {
714 // Result is unused.
715 return true;
716 }
717
718 // Check the opcode and for MOVE_RESULT_OBJECT check also that the constant is null.
719 DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT ||
720 (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT &&
721 method.d.data == 0u));
722
723 // Insert the CONST instruction.
724 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
725 insn->dalvikInsn.opcode = Instruction::CONST;
726 insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
727 insn->dalvikInsn.vB = method.d.data;
728 bb->InsertMIRAfter(move_result, insn);
729 return true;
730 }
731
GenInlineReturnArg(MIRGraph * mir_graph,BasicBlock * bb,MIR * invoke,MIR * move_result,const InlineMethod & method)732 bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
733 MIR* move_result, const InlineMethod& method) {
734 if (move_result == nullptr) {
735 // Result is unused.
736 return true;
737 }
738
739 // Select opcode and argument.
740 const InlineReturnArgData& data = method.d.return_data;
741 Instruction::Code opcode = Instruction::MOVE_FROM16;
742 uint32_t arg = GetInvokeReg(invoke, data.arg);
743 if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
744 DCHECK_EQ(data.is_object, 1u);
745 DCHECK_EQ(data.is_wide, 0u);
746 opcode = Instruction::MOVE_OBJECT_FROM16;
747 } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE) {
748 DCHECK_EQ(data.is_wide, 1u);
749 DCHECK_EQ(data.is_object, 0u);
750 opcode = Instruction::MOVE_WIDE_FROM16;
751 if (!WideArgIsInConsecutiveDalvikRegs(invoke, data.arg)) {
752 // The two halfs of the source value are not in consecutive dalvik registers in INVOKE.
753 return false;
754 }
755 } else {
756 DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT);
757 DCHECK_EQ(data.is_wide, 0u);
758 DCHECK_EQ(data.is_object, 0u);
759 }
760
761 // Insert the move instruction
762 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
763 insn->dalvikInsn.opcode = opcode;
764 insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
765 insn->dalvikInsn.vB = arg;
766 bb->InsertMIRAfter(move_result, insn);
767 return true;
768 }
769
GenInlineIGet(MIRGraph * mir_graph,BasicBlock * bb,MIR * invoke,MIR * move_result,const InlineMethod & method,uint32_t method_idx)770 bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
771 MIR* move_result, const InlineMethod& method,
772 uint32_t method_idx) {
773 CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
774 if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
775 return false;
776 }
777
778 const InlineIGetIPutData& data = method.d.ifield_data;
779 Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant);
780 DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant);
781 uint32_t object_reg = GetInvokeReg(invoke, data.object_arg);
782
783 if (move_result == nullptr) {
784 // Result is unused. If volatile, we still need to emit the IGET but we have no destination.
785 return !data.is_volatile;
786 }
787
788 DCHECK_EQ(data.method_is_static != 0u,
789 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
790 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE);
791 bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u);
792 if (!object_is_this) {
793 // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
794 // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
795 if (!InlineMethodAnalyser::IsSyntheticAccessor(
796 mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) {
797 return false;
798 }
799 }
800
801 if (object_is_this) {
802 // Mark invoke as NOP, null-check is done on IGET. No aborts after this.
803 invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
804 }
805
806 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
807 insn->offset = invoke->offset;
808 insn->dalvikInsn.opcode = opcode;
809 insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
810 insn->dalvikInsn.vB = object_reg;
811 mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn);
812
813 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved());
814 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastGet());
815 DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value());
816 DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u);
817
818 bb->InsertMIRAfter(move_result, insn);
819 return true;
820 }
821
GenInlineIPut(MIRGraph * mir_graph,BasicBlock * bb,MIR * invoke,MIR * move_result,const InlineMethod & method,uint32_t method_idx)822 bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
823 MIR* move_result, const InlineMethod& method,
824 uint32_t method_idx) {
825 CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
826 if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
827 return false;
828 }
829
830 const InlineIGetIPutData& data = method.d.ifield_data;
831 Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant);
832 DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant);
833 uint32_t object_reg = GetInvokeReg(invoke, data.object_arg);
834 uint32_t src_reg = GetInvokeReg(invoke, data.src_arg);
835 uint32_t return_reg =
836 data.return_arg_plus1 != 0u ? GetInvokeReg(invoke, data.return_arg_plus1 - 1u) : 0u;
837
838 if (opcode == Instruction::IPUT_WIDE && !WideArgIsInConsecutiveDalvikRegs(invoke, data.src_arg)) {
839 // The two halfs of the source value are not in consecutive dalvik registers in INVOKE.
840 return false;
841 }
842
843 DCHECK(move_result == nullptr || data.return_arg_plus1 != 0u);
844 if (move_result != nullptr && move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE &&
845 !WideArgIsInConsecutiveDalvikRegs(invoke, data.return_arg_plus1 - 1u)) {
846 // The two halfs of the return value are not in consecutive dalvik registers in INVOKE.
847 return false;
848 }
849
850 DCHECK_EQ(data.method_is_static != 0u,
851 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
852 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE);
853 bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u);
854 if (!object_is_this) {
855 // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
856 // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
857 if (!InlineMethodAnalyser::IsSyntheticAccessor(
858 mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) {
859 return false;
860 }
861 }
862
863 if (object_is_this) {
864 // Mark invoke as NOP, null-check is done on IPUT. No aborts after this.
865 invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
866 }
867
868 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
869 insn->dalvikInsn.opcode = opcode;
870 insn->dalvikInsn.vA = src_reg;
871 insn->dalvikInsn.vB = object_reg;
872 mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn);
873
874 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved());
875 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastPut());
876 DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value());
877 DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u);
878
879 bb->InsertMIRAfter(invoke, insn);
880
881 if (move_result != nullptr) {
882 MIR* move = AllocReplacementMIR(mir_graph, invoke, move_result);
883 move->offset = move_result->offset;
884 if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT) {
885 move->dalvikInsn.opcode = Instruction::MOVE_FROM16;
886 } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
887 move->dalvikInsn.opcode = Instruction::MOVE_OBJECT_FROM16;
888 } else {
889 DCHECK_EQ(move_result->dalvikInsn.opcode, Instruction::MOVE_RESULT_WIDE);
890 move->dalvikInsn.opcode = Instruction::MOVE_WIDE_FROM16;
891 }
892 move->dalvikInsn.vA = move_result->dalvikInsn.vA;
893 move->dalvikInsn.vB = return_reg;
894 bb->InsertMIRAfter(insn, move);
895 }
896 return true;
897 }
898
899 } // namespace art
900