OSDN Git Service

am 8d6291cc: Merge "[JIT] Fix volatile test"
[android-x86/dalvik.git] / vm / IndirectRefTable.h
1 /*
2  * Copyright (C) 2009 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 #ifndef _DALVIK_INDIRECTREFTABLE
18 #define _DALVIK_INDIRECTREFTABLE
19
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23
24 /*
25  * Maintain a table of indirect references.  Used for local/global JNI
26  * references.
27  *
28  * The table contains object references that are part of the GC root set.
29  * When an object is added we return an IndirectRef that is not a valid
30  * pointer but can be used to find the original value in O(1) time.
31  * Conversions to and from indirect refs are performed on JNI method calls
32  * in and out of the VM, so they need to be very fast.
33  *
34  * To be efficient for JNI local variable storage, we need to provide
35  * operations that allow us to operate on segments of the table, where
36  * segments are pushed and popped as if on a stack.  For example, deletion
37  * of an entry should only succeed if it appears in the current segment,
38  * and we want to be able to strip off the current segment quickly when
39  * a method returns.  Additions to the table must be made in the current
40  * segment even if space is available in an earlier area.
41  *
42  * A new segment is created when we call into native code from interpreted
43  * code, or when we handle the JNI PushLocalFrame function.
44  *
45  * The GC must be able to scan the entire table quickly.
46  *
47  * In summary, these must be very fast:
48  *  - adding or removing a segment
49  *  - adding references to a new segment
50  *  - converting an indirect reference back to an Object
51  * These can be a little slower, but must still be pretty quick:
52  *  - adding references to a "mature" segment
53  *  - removing individual references
54  *  - scanning the entire table straight through
55  *
56  * If there's more than one segment, we don't guarantee that the table
57  * will fill completely before we fail due to lack of space.  We do ensure
58  * that the current segment will pack tightly, which should satisfy JNI
59  * requirements (e.g. EnsureLocalCapacity).
60  *
61  * To make everything fit nicely in 32-bit integers, the maximum size of
62  * the table is capped at 64K.
63  *
64  * None of the table functions are synchronized.
65  */
66
67 /*
68  * Indirect reference definition.  This must be interchangeable with JNI's
69  * jobject, and it's convenient to let null be null, so we use void*.
70  *
71  * We need a 16-bit table index and a 2-bit reference type (global, local,
72  * weak global).  Real object pointers will have zeroes in the low 2 or 3
73  * bits (4- or 8-byte alignment), so it's useful to put the ref type
74  * in the low bits and reserve zero as an invalid value.
75  *
76  * The remaining 14 bits can be used to detect stale indirect references.
77  * For example, if objects don't move, we can use a hash of the original
78  * Object* to make sure the entry hasn't been re-used.  (If the Object*
79  * we find there doesn't match because of heap movement, we could do a
80  * secondary check on the preserved hash value; this implies that creating
81  * a global/local ref queries the hash value and forces it to be saved.)
82  * This is only done when CheckJNI is enabled.
83  *
84  * A more rigorous approach would be to put a serial number in the extra
85  * bits, and keep a copy of the serial number in a parallel table.  This is
86  * easier when objects can move, but requires 2x the memory and additional
87  * memory accesses on add/get.  It will catch additional problems, e.g.:
88  * create iref1 for obj, delete iref1, create iref2 for same obj, lookup
89  * iref1.  A pattern based on object bits will miss this.
90  */
91 typedef void* IndirectRef;
92
93 /*
94  * Indirect reference kind, used as the two low bits of IndirectRef.
95  *
96  * For convenience these match up with enum jobjectRefType from jni.h.
97  */
98 typedef enum IndirectRefKind {
99     kIndirectKindInvalid    = 0,
100     kIndirectKindLocal      = 1,
101     kIndirectKindGlobal     = 2,
102     kIndirectKindWeakGlobal = 3
103 } IndirectRefKind;
104
105 /*
106  * Extended debugging structure.  We keep a parallel array of these, one
107  * per slot in the table.
108  */
109 #define kIRTPrevCount   4
110 typedef struct IndirectRefSlot {
111     u4          serial;         /* slot serial */
112     Object*     previous[kIRTPrevCount];
113 } IndirectRefSlot;
114
115 /*
116  * Table definition.
117  *
118  * For the global reference table, the expected common operations are
119  * adding a new entry and removing a recently-added entry (usually the
120  * most-recently-added entry).  For JNI local references, the common
121  * operations are adding a new entry and removing an entire table segment.
122  *
123  * If "allocEntries" is not equal to "maxEntries", the table may expand
124  * when entries are added, which means the memory may move.  If you want
125  * to keep pointers into "table" rather than offsets, you must use a
126  * fixed-size table.
127  *
128  * If we delete entries from the middle of the list, we will be left with
129  * "holes".  We track the number of holes so that, when adding new elements,
130  * we can quickly decide to do a trivial append or go slot-hunting.
131  *
132  * When the top-most entry is removed, any holes immediately below it are
133  * also removed.  Thus, deletion of an entry may reduce "topIndex" by more
134  * than one.
135  *
136  * To get the desired behavior for JNI locals, we need to know the bottom
137  * and top of the current "segment".  The top is managed internally, and
138  * the bottom is passed in as a function argument (the VM keeps it in a
139  * slot in the interpreted stack frame).  When we call a native method or
140  * push a local frame, the current top index gets pushed on, and serves
141  * as the new bottom.  When we pop a frame off, the value from the stack
142  * becomes the new top index, and the value stored in the previous frame
143  * becomes the new bottom.
144  *
145  * To avoid having to re-scan the table after a pop, we want to push the
146  * number of holes in the table onto the stack.  Because of our 64K-entry
147  * cap, we can combine the two into a single unsigned 32-bit value.
148  * Instead of a "bottom" argument we take a "cookie", which includes the
149  * bottom index and the count of holes below the bottom.
150  *
151  * We need to minimize method call/return overhead.  If we store the
152  * "cookie" externally, on the interpreted call stack, the VM can handle
153  * pushes and pops with a single 4-byte load and store.  (We could also
154  * store it internally in a public structure, but the local JNI refs are
155  * logically tied to interpreted stack frames anyway.)
156  *
157  * Common alternative implementation: make IndirectRef a pointer to the
158  * actual reference slot.  Instead of getting a table and doing a lookup,
159  * the lookup can be done instantly.  Operations like determining the
160  * type and deleting the reference are more expensive because the table
161  * must be hunted for (i.e. you have to do a pointer comparison to see
162  * which table it's in), you can't move the table when expanding it (so
163  * realloc() is out), and tricks like serial number checking to detect
164  * stale references aren't possible (though we may be able to get similar
165  * benefits with other approaches).
166  *
167  * TODO: consider a "lastDeleteIndex" for quick hole-filling when an
168  * add immediately follows a delete; must invalidate after segment pop
169  * (which could increase the cost/complexity of method call/return).
170  * Might be worth only using it for JNI globals.
171  *
172  * TODO: may want completely different add/remove algorithms for global
173  * and local refs to improve performance.  A large circular buffer might
174  * reduce the amortized cost of adding global references.
175  *
176  * TODO: if we can guarantee that the underlying storage doesn't move,
177  * e.g. by using oversized mmap regions to handle expanding tables, we may
178  * be able to avoid having to synchronize lookups.  Might make sense to
179  * add a "synchronized lookup" call that takes the mutex as an argument,
180  * and either locks or doesn't lock based on internal details.
181  */
182 typedef union IRTSegmentState {
183     u4          all;
184     struct {
185         u4      topIndex:16;            /* index of first unused entry */
186         u4      numHoles:16;            /* #of holes in entire table */
187     } parts;
188 } IRTSegmentState;
189 typedef struct IndirectRefTable {
190     /* semi-public - read/write by interpreter in native call handler */
191     IRTSegmentState segmentState;
192
193     /* semi-public - read-only during GC scan; pointer must not be kept */
194     Object**        table;              /* bottom of the stack */
195
196     /* private */
197     IndirectRefSlot* slotData;          /* extended debugging info */
198     int             allocEntries;       /* #of entries we have space for */
199     int             maxEntries;         /* max #of entries allowed */
200     IndirectRefKind kind;               /* bit mask, ORed into all irefs */
201
202     // TODO: want hole-filling stats (#of holes filled, total entries scanned)
203     //       for performance evaluation.
204 } IndirectRefTable;
205
206 /* use as initial value for "cookie", and when table has only one segment */
207 #define IRT_FIRST_SEGMENT   0
208
209 /*
210  * (This is PRIVATE, but we want it inside other inlines in this header.)
211  *
212  * Indirectify the object.
213  *
214  * The object pointer itself is subject to relocation in some GC
215  * implementations, so we shouldn't really be using it here.
216  */
217 INLINE IndirectRef dvmObjectToIndirectRef(IndirectRefTable* pRef,
218     Object* obj, u4 tableIndex, IndirectRefKind kind)
219 {
220     assert(tableIndex < 65536);
221     //u4 objChunk = (((u4) obj >> 3) ^ ((u4) obj >> 19)) & 0x3fff;
222     //u4 uref = objChunk << 18 | (tableIndex << 2) | kind;
223     u4 serialChunk = pRef->slotData[tableIndex].serial;
224     u4 uref = serialChunk << 20 | (tableIndex << 2) | kind;
225     return (IndirectRef) uref;
226 }
227
228 /*
229  * (This is PRIVATE, but we want it inside other inlines in this header.)
230  *
231  * Extract the table index from an indirect reference.
232  */
233 INLINE u4 dvmIndirectRefToIndex(IndirectRef iref)
234 {
235     u4 uref = (u4) iref;
236     return (uref >> 2) & 0xffff;
237 }
238
239 /*
240  * Determine what kind of indirect reference this is.
241  */
242 INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref)
243 {
244     return (IndirectRefKind)((u4) iref & 0x03);
245 }
246
247 /*
248  * Return a string constant describing the indirect ref type.
249  */
250 const char* dvmIndirectRefTypeName(IndirectRef iref);
251
252 /*
253  * Initialize an IndirectRefTable.
254  *
255  * If "initialCount" != "maxCount", the table will expand as required.
256  *
257  * "kind" should be Local or Global.  The Global table may also hold
258  * WeakGlobal refs.
259  *
260  * Returns "false" if table allocation fails.
261  */
262 bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
263     int maxCount, IndirectRefKind kind);
264
265 /*
266  * Clear out the contents, freeing allocated storage.  Does not free "pRef".
267  *
268  * You must call dvmInitReferenceTable() before you can re-use this table.
269  */
270 void dvmClearIndirectRefTable(IndirectRefTable* pRef);
271
272 /*
273  * Start a new segment at the top of the table.
274  *
275  * Returns an opaque 32-bit value that must be provided when the segment
276  * is to be removed.
277  *
278  * IMPORTANT: this is implemented as a single instruction in mterp, rather
279  * than a call here.  You can add debugging aids for the C-language
280  * interpreters, but the basic implementation may not change.
281  */
282 INLINE u4 dvmPushIndirectRefTableSegment(IndirectRefTable* pRef)
283 {
284     return pRef->segmentState.all;
285 }
286
287 /* extra debugging checks */
288 bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie);
289
290 /*
291  * Remove one or more segments from the top.  The table entry identified
292  * by "cookie" becomes the new top-most entry.
293  *
294  * IMPORTANT: this is implemented as a single instruction in mterp, rather
295  * than a call here.  You can add debugging aids for the C-language
296  * interpreters, but the basic implementation must not change.
297  */
298 INLINE void dvmPopIndirectRefTableSegment(IndirectRefTable* pRef, u4 cookie)
299 {
300     dvmPopIndirectRefTableSegmentCheck(pRef, cookie);
301     pRef->segmentState.all = cookie;
302 }
303
304 /*
305  * Return the #of entries in the entire table.  This includes holes, and
306  * so may be larger than the actual number of "live" entries.
307  */
308 INLINE size_t dvmIndirectRefTableEntries(const IndirectRefTable* pRef)
309 {
310     return pRef->segmentState.parts.topIndex;
311 }
312
313 /*
314  * Returns "true" if the table is full.  The table is considered full if
315  * we would need to expand it to add another entry to the current segment.
316  */
317 INLINE size_t dvmIsIndirectRefTableFull(const IndirectRefTable* pRef)
318 {
319     return dvmIndirectRefTableEntries(pRef) == (size_t)pRef->allocEntries;
320 }
321
322 /*
323  * Add a new entry.  "obj" must be a valid non-NULL object reference
324  * (though it's okay if it's not fully-formed, e.g. the result from
325  * dvmMalloc doesn't have obj->clazz set).
326  *
327  * Returns NULL if the table is full (max entries reached, or alloc
328  * failed during expansion).
329  */
330 IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
331     Object* obj);
332
333 /*
334  * Add a new entry at the end.  Similar to Add but does not usually attempt
335  * to fill in holes.  This is only appropriate to use right after a new
336  * segment has been pushed.
337  *
338  * (This is intended for use when calling into a native JNI method, so
339  * performance is critical.)
340  */
341 INLINE IndirectRef dvmAppendToIndirectRefTable(IndirectRefTable* pRef,
342     u4 cookie, Object* obj)
343 {
344     int topIndex = pRef->segmentState.parts.topIndex;
345     if (topIndex == pRef->allocEntries) {
346         /* up against alloc or max limit, call the fancy version */
347         return dvmAddToIndirectRefTable(pRef, cookie, obj);
348     } else {
349         IndirectRef result = dvmObjectToIndirectRef(pRef, obj, topIndex,
350             pRef->kind);
351         pRef->table[topIndex++] = obj;
352         pRef->segmentState.parts.topIndex = topIndex;
353         return result;
354     }
355 }
356
357 /* extra debugging checks */
358 bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref);
359
360 /* magic failure value; must not pass dvmIsValidObject() */
361 #define kInvalidIndirectRefObject ((Object*)0xdead4321)
362
363 /*
364  * Given an IndirectRef in the table, return the Object it refers to.
365  *
366  * Returns kInvalidIndirectRefObject if iref is invalid.
367  */
368 INLINE Object* dvmGetFromIndirectRefTable(IndirectRefTable* pRef,
369     IndirectRef iref)
370 {
371     if (!dvmGetFromIndirectRefTableCheck(pRef, iref))
372         return kInvalidIndirectRefObject;
373
374     int idx = dvmIndirectRefToIndex(iref);
375     return pRef->table[idx];
376 }
377
378 /*
379  * Remove an existing entry.
380  *
381  * If the entry is not between the current top index and the bottom index
382  * specified by the cookie, we don't remove anything.  This is the behavior
383  * required by JNI's DeleteLocalRef function.
384  *
385  * Returns "false" if nothing was removed.
386  */
387 bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
388     IndirectRef iref);
389
390 /*
391  * Dump the contents of a reference table to the log file.
392  *
393  * The caller should lock any external sync before calling.
394  */
395 void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr);
396
397 #ifdef __cplusplus
398 }
399 #endif
400
401 #endif /*_DALVIK_INDIRECTREFTABLE*/