* src/native/localref.cpp: Using free list instead of array scanning.
[cacao.git] / src / native / localref.cpp
1 /* src/native/localref.cpp - Management of local reference tables
2
3    Copyright (C) 1996-2005, 2006, 2007, 2008, 2010
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <assert.h>
29 #include <stdint.h>
30
31 #include "mm/memory.hpp"
32
33 #include "native/localref.hpp"
34
35 #include "threads/thread.hpp"
36
37 #include "toolbox/logging.hpp"
38
39 #include "vm/options.h"
40 #include "vm/vm.hpp"
41
42 #include "vm/jit/argument.hpp"
43
44
45 /* debug **********************************************************************/
46
47 #if !defined(NDEBUG)
48 # define DEBUGLOCALREF(message, index) \
49         do { \
50                 if (opt_DebugLocalReferences) { \
51                         localref_table *dlrt = LOCALREFTABLE; \
52                         log_start(); \
53                         log_print("[local reference %-12s: lrt=%016p frame=%d capacity=%d used=%d hwm=%d", message, dlrt, dlrt->localframes, dlrt->capacity, dlrt->used, dlrt->hwm); \
54                         if (index >= 0) \
55                                 log_print(" localref=%p object=%p", &(dlrt->refs[index]), dlrt->refs[index]); \
56                         log_print("]"); \
57                         log_finish(); \
58                 } \
59         } while (0)
60 #else
61 # define DEBUGLOCALREF(message, index)
62 #endif
63
64
65 /* global variables ***********************************************************/
66
67 #if !defined(ENABLE_THREADS)
68 localref_table *_no_threads_localref_table;
69 #endif
70
71
72 /* some forward declarations **************************************************/
73
74 #if !defined(NDEBUG)
75 static bool localref_check_uncleared();
76 #endif
77
78
79 /* localref_table_init *********************************************************
80
81    Initializes the local references table of the current thread.
82
83 *******************************************************************************/
84
85 bool localref_table_init(void)
86 {
87         localref_table *lrt;
88
89         TRACESUBSYSTEMINITIALIZATION("localref_table_init");
90
91         assert(LOCALREFTABLE == NULL);
92
93 #if !defined(ENABLE_GC_BOEHM)
94         /* this is freed by localref_table_destroy */
95         lrt = (localref_table*) NEW(localref_table);
96 #else
97         /* this does not need to be freed again */
98         lrt = (localref_table*) GCNEW(localref_table);
99 #endif
100
101         if (lrt == NULL)
102                 return false;
103
104         localref_table_add(lrt);
105
106         DEBUGLOCALREF("table init", -1);
107
108         return true;
109 }
110
111
112 /* localref_table_destroy ******************************************************
113
114    Destroys the complete local references table of the current thread.
115
116 *******************************************************************************/
117
118 bool localref_table_destroy(void)
119 {
120         localref_table *lrt;
121
122         lrt = LOCALREFTABLE;
123         assert(lrt != NULL);
124         assert(lrt->prev == NULL);
125
126         DEBUGLOCALREF("table destroy", -1);
127
128 #if !defined(ENABLE_GC_BOEHM)
129         FREE(lrt, localref_table);
130 #endif
131
132         LOCALREFTABLE = NULL;
133
134         return true;
135 }
136
137
138 /* localref_table_add **********************************************************
139
140    Adds a new local references table to the current thread.
141
142 *******************************************************************************/
143
144 void localref_table_add(localref_table *lrt)
145 {
146         /* initialize the local reference table */
147
148         lrt->capacity    = LOCALREFTABLE_CAPACITY;
149         lrt->used        = 0;
150         lrt->localframes = 1;
151         lrt->prev        = LOCALREFTABLE;
152
153         lrt->hwm = 0;
154         lrt->firstfree = -1;
155
156         /* add given local references table to this thread */
157
158         LOCALREFTABLE = lrt;
159
160         /*DEBUGLOCALREF("table add", -1);*/
161 }
162
163
164 /* localref_table_remove *******************************************************
165
166    Removes the topmost local references table from the current thread.
167
168 *******************************************************************************/
169
170 void localref_table_remove()
171 {
172         localref_table *lrt;
173
174 #if !defined(NDEBUG)
175         /* check for uncleared local references */
176
177         localref_check_uncleared();
178 #endif
179
180         /* get current local reference table from thread */
181
182         lrt = LOCALREFTABLE;
183         assert(lrt != NULL);
184         assert(lrt->localframes == 1);
185
186         /*DEBUGLOCALREF("table remove", -1);*/
187
188         lrt = lrt->prev;
189
190         LOCALREFTABLE = lrt;
191 }
192
193
194 /* localref_frame_push *********************************************************
195
196    Creates a new local reference frame, in which at least a given
197    number of local references can be created.
198
199 *******************************************************************************/
200
201 bool localref_frame_push(int32_t capacity)
202 {
203         localref_table *lrt;
204         localref_table *nlrt;
205         int32_t         additionalrefs;
206
207         /* get current local reference table from thread */
208
209         lrt = LOCALREFTABLE;
210         assert(lrt != NULL);
211         assert(capacity > 0);
212
213         /* Allocate new local reference table on Java heap.  Calculate the
214            additional memory we have to allocate. */
215
216         if (capacity > LOCALREFTABLE_CAPACITY)
217                 additionalrefs = capacity - LOCALREFTABLE_CAPACITY;
218         else
219                 additionalrefs = 0;
220
221 #if !defined(ENABLE_GC_BOEHM)
222         nlrt = (localref_table *)
223                         MNEW(u1, sizeof(localref_table) + additionalrefs * SIZEOF_VOID_P);
224 #else
225         nlrt = (localref_table *)
226                         GCMNEW(u1, sizeof(localref_table) + additionalrefs * SIZEOF_VOID_P);
227 #endif
228
229         if (nlrt == NULL)
230                 return false;
231
232         /* Set up the new local reference table and add it to the local
233            frames chain. */
234
235         nlrt->capacity    = capacity;
236         nlrt->used        = 0;
237         nlrt->localframes = lrt->localframes + 1;
238         nlrt->prev        = lrt;
239
240         /* store new local reference table in thread */
241
242         LOCALREFTABLE = nlrt;
243
244         DEBUGLOCALREF("frame push", -1);
245
246         return true;
247 }
248
249
250 /* localref_frame_pop_all ******************************************************
251
252    Pops off all the local reference frames of the current table.
253
254 *******************************************************************************/
255
256 void localref_frame_pop_all(void)
257 {
258         localref_table *lrt;
259         localref_table *plrt;
260         int32_t         localframes;
261 #if !defined(ENABLE_GC_BOEHM)
262         int32_t         additionalrefs;
263 #endif
264
265         /* get current local reference table from thread */
266
267         lrt = LOCALREFTABLE;
268         assert(lrt != NULL);
269
270         localframes = lrt->localframes;
271
272         /* Don't delete the top local frame, as this one is allocated in
273            the native stub on the stack and is freed automagically on
274            return. */
275
276         if (localframes == 1)
277                 return;
278
279         /* release all current local frames */
280
281         for (; localframes > 1; localframes--) {
282                 /* get previous frame */
283
284                 plrt = lrt->prev;
285
286                 DEBUGLOCALREF("frame pop", -1);
287
288                 lrt->prev = NULL;
289
290 #if !defined(ENABLE_GC_BOEHM)
291                 /* for the exact GC local reference tables are not on the heap,
292                    so we need to free them explicitly here. */
293
294                 if (lrt->capacity > LOCALREFTABLE_CAPACITY)
295                         additionalrefs = lrt->capacity - LOCALREFTABLE_CAPACITY;
296                 else
297                         additionalrefs = 0;
298
299                 MFREE(lrt, u1, sizeof(localref_table) + additionalrefs * SIZEOF_VOID_P);
300 #endif
301
302                 /* set new local references table */
303
304                 lrt = plrt;
305         }
306
307         /* store new local reference table in thread */
308
309         LOCALREFTABLE = lrt;
310 }
311
312
313 /* localref_add ****************************************************************
314
315    Adds a new entry into the local reference table and returns the
316    new local reference.
317
318 *******************************************************************************/
319
320 java_handle_t *localref_add(java_object_t *o)
321 {
322         localref_table *lrt;
323         java_handle_t  *h;
324         int32_t         i;
325
326         /* get current local reference table from thread */
327
328         lrt = LOCALREFTABLE;
329         assert(lrt != NULL);
330         assert(o != NULL);
331         /* XXX: assert that we are in a GC critical section! */
332
333         /* Check if we have space for the requested reference?  No,
334            allocate a new frame.  This is actually not what the spec says,
335            but for compatibility reasons... */
336
337     if (lrt->used == lrt->capacity) {
338                 if (!localref_frame_push(64))
339                         assert(0);
340
341                 /* get the new local reference table */ 
342
343                 lrt = LOCALREFTABLE;
344         }
345
346         /* insert the reference into the local reference table */
347
348         i = lrt->hwm;
349         if (i == lrt->capacity) {
350                 if (lrt->firstfree >= 0) {
351                         i = lrt->firstfree;
352                         lrt->firstfree = lrt->refs[i].nextfree;
353                 }
354                 else {
355                         /* this should not happen */
356
357                         log_println("localref_add: WARNING: unable to add localref for %p", o);
358
359                         return NULL;
360                 }
361         } else
362                 lrt->hwm++;
363
364         lrt->refs[i].ptr = o;
365         lrt->used++;
366
367 #if defined(ENABLE_HANDLES)
368         h = (java_handle_t *) &(lrt->refs[i].ptr);
369 #else
370         h = (java_handle_t *) o;
371 #endif
372
373         /*DEBUGLOCALREF("entry add", i);*/
374
375         return h;
376 }
377
378
379 /* localref_del ****************************************************************
380
381    Deletes an entry from the local reference table.
382
383 *******************************************************************************/
384
385 void localref_del(java_handle_t *localref)
386 {
387         localref_table *lrt;
388         java_handle_t  *h;
389         int32_t         localframes;
390         int32_t         i;
391
392         /* get local reference table from thread */
393
394         lrt = LOCALREFTABLE;
395         assert(lrt != NULL);
396         assert(localref != NULL);
397
398         localframes = lrt->localframes;
399
400         /* go through all local frames of the current table */
401         /* XXX: this is propably not what the spec wants! */
402
403         for (; localframes > 0; localframes--) {
404
405                 /* and try to remove the reference */
406     
407                 for (i = 0; i < lrt->hwm; i++) {
408 #if defined(ENABLE_HANDLES)
409                         h = (java_handle_t *) &(lrt->refs[i].ptr);
410 #else
411                         h = (java_handle_t *) lrt->refs[i].ptr;
412 #endif
413
414                         if (h == localref) {
415                                 DEBUGLOCALREF("entry delete", i);
416
417                                 lrt->refs[i].nextfree = lrt->firstfree;
418                                 lrt->firstfree = i;
419                                 lrt->used--;
420
421                                 return;
422                         }
423                 }
424
425                 lrt = lrt->prev;
426         }
427
428         /* this should not happen */
429
430         log_println("localref_del: WARNING: unable to find localref %p", localref);
431 }
432
433
434 /* localref_native_enter *******************************************************
435
436    Insert arguments to a native method into the local reference table.
437    This is done by the native stub through codegen_start_native_call.
438
439 *******************************************************************************/
440
441 void localref_native_enter(methodinfo *m, uint64_t *argument_regs, uint64_t *argument_stack)
442 {
443         localref_table *lrt;
444         methoddesc     *md;
445         imm_union       arg;
446         java_handle_t  *h;
447         int i;
448
449         /* get local reference table from thread */
450
451         lrt = LOCALREFTABLE;
452         assert(lrt != NULL);
453         assert(m != NULL);
454
455         md = m->parseddesc;
456
457         /* walk through all parameters to the method */
458
459         for (i = 0; i < md->paramcount; ++i) {
460                 /* load TYPE_ADR parameters ... */
461
462                 if (md->paramtypes[i].type == TYPE_ADR) {
463                         arg = argument_jitarray_load(md, i, argument_regs, argument_stack);
464
465                         if (arg.a == NULL)
466                                 continue;
467
468                         /* ... and insert them into the table */
469
470                         h = localref_add((java_object_t *) arg.a);
471
472 #if defined(ENABLE_HANDLES)
473                         /* update the modified parameter if necesarry */
474
475                         arg.a = (void *) h;
476                         argument_jitarray_store(md, i, argument_regs, argument_stack, arg);
477 #endif
478                 }
479         }
480 }
481
482
483 /* localref_native_exit ********************************************************
484
485    Undo the wrapping of the return value of a native method. This is
486    done by the native stub through codegen_finish_native_call.
487
488    NOTE: This function is only useful if handles are enabled.
489
490 *******************************************************************************/
491
492 #if defined(ENABLE_HANDLES)
493 void localref_native_exit(methodinfo *m, uint64_t *return_regs)
494 {
495         localref_table *lrt;
496         methoddesc     *md;
497         imm_union       ret;
498         java_handle_t  *h;
499
500         /* get local reference table from thread */
501
502         lrt = LOCALREFTABLE;
503         assert(lrt != NULL);
504         assert(m != NULL);
505
506         md = m->parseddesc;
507
508         /* load TYPE_ADR return values ... */
509
510         if (md->returntype.type == TYPE_ADR) {
511                 ret = argument_jitreturn_load(md, return_regs);
512
513                 if (ret.a == NULL)
514                         return;
515
516                 h = (java_handle_t *) ret.a;
517
518                 /* update the modified return valie */
519
520                 ret.a = (void *) h->heap_object;
521                 argument_jitreturn_store(md, return_regs, ret);
522
523 #if !defined(NDEBUG) && 0
524                 /* removing the entry from the local reference table is not really
525                    necesarry, but gives us warnings if the entry does not exist. */
526
527                 localref_del(h);
528 #endif
529         }
530 }
531 #endif /* defined(ENABLE_HANDLES) */
532
533
534 /* localref_dump ***************************************************************
535
536    Dumps all local reference tables, including all frames.
537
538 *******************************************************************************/
539
540 #if !defined(NDEBUG)
541 # define LOCALREF_DUMP_REFS_PER_LINE 4
542 void localref_dump()
543 {
544         localref_table *lrt, dlrt;
545         int i, j;
546
547         /* get current local reference table from thread */
548
549         lrt = LOCALREFTABLE;
550
551         log_println("--------- Local Reference Tables Dump ---------");
552
553         while (lrt != NULL) {
554                 log_println("Frame #%d, Used=%d, Capacity=%d, Hwm=%d, Addr=%p:", lrt->localframes, lrt->used, lrt->capacity, lrt->hwm, (void *) lrt);
555
556                         if (lrt->used != 0) {
557
558                                 dlrt = *lrt;                    // copy it for dumping
559                                 for (i = dlrt.firstfree; i >= 0; i = j) {
560                                         j = dlrt.refs[i].nextfree;
561                                         dlrt.refs[i].ptr = NULL;
562                                 }
563
564                                 log_start();
565
566                                 j = 0;
567                                 for (i = 0; i < dlrt.hwm; i++) {
568                                         if (dlrt.refs[i].ptr != NULL) {
569                                                 if (j != 0 && j % LOCALREF_DUMP_REFS_PER_LINE == 0) {
570                                                         log_finish();
571                                                         log_start();
572                                                 }
573                                                 j++;
574                                                 log_print("\t0x%016lx ", (intptr_t) dlrt.refs[i].ptr);
575                                         }
576                                 }
577
578                                 log_finish();
579                         }
580
581                 lrt = lrt->prev;
582         }
583 }
584 #endif /* !defined(NDEBUG) */
585
586
587 /* localref_check_uncleared ****************************************************
588
589    Checks the topmost local reference table for uncleared references.
590
591 *******************************************************************************/
592
593 #if !defined(NDEBUG)
594 static bool localref_check_uncleared()
595 {
596         localref_table *lrt;
597         int32_t         localframes;
598         int32_t         lrt_uncleared;
599         int32_t         lrt_used;
600         int i;
601
602         /* get current local reference table from thread */
603
604         lrt = LOCALREFTABLE;
605         assert(lrt != NULL);
606         assert(lrt->localframes > 0);
607
608         localframes   = lrt->localframes;
609         lrt_uncleared = 0;
610         lrt_used      = 0;
611
612         for (; localframes > 0; localframes--) {
613                 lrt_used += lrt->used;
614
615                 lrt_uncleared += lrt->hwm;
616                 for (i = lrt->firstfree; i >= 0; i = lrt->refs[i].nextfree)
617                         lrt_uncleared--;
618
619                 lrt = lrt->prev;
620         }
621
622         if (lrt_uncleared != lrt_used) {
623                 localref_dump();
624                 vm_abort("localref_check_uncleared: (uncleared=%d) != (used=%d)", lrt_uncleared, lrt_used);
625         }
626
627         if (lrt_uncleared <= 1)
628                 return true;
629         else {
630                 /*log_println("localref_check_uncleared: %d uncleared local references", lrt_uncleared);*/
631                 return false;
632         }
633 }
634 #endif
635
636
637 /*
638  * These are local overrides for various environment variables in Emacs.
639  * Please do not remove this and leave it at the end of the file, where
640  * Emacs will automagically detect them.
641  * ---------------------------------------------------------------------
642  * Local variables:
643  * mode: c++
644  * indent-tabs-mode: t
645  * c-basic-offset: 4
646  * tab-width: 4
647  * End:
648  * vim:noexpandtab:sw=4:ts=4:
649  */