1 /* src/vm/builtin.c - functions for unsupported operations
3 Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
4 C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5 E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6 J. Wenninger, Institut f. Computersprachen - TU Wien
8 This file is part of CACAO.
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2, or (at
13 your option) any later version.
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 Contains C functions for JavaVM Instructions that cannot be
26 translated to machine language directly. Consequently, the
27 generated machine code for these instructions contains function
28 calls instead of machine instructions, using the C calling
47 #include "fdlibm/fdlibm.h"
48 #if defined(__CYGWIN__) && defined(Bias)
52 #include "mm/gc-common.h"
53 #include "mm/memory.h"
55 #include "native/jni.h"
56 #include "native/llni.h"
58 #include "threads/lock-common.h"
59 #include "threads/threads-common.h"
61 #include "toolbox/logging.h"
62 #include "toolbox/util.h"
65 #include "vm/builtin.h"
66 #include "vm/cycles-stats.h"
67 #include "vm/exceptions.h"
68 #include "vm/global.h"
69 #include "vm/initialize.h"
70 #include "vm/primitive.h"
71 #include "vm/stringlocal.h"
73 #include "vm/jit/asmpart.h"
74 #include "vm/jit/trace.h"
76 #include "vmcore/class.h"
77 #include "vmcore/linker.h"
78 #include "vmcore/loader.h"
79 #include "vmcore/options.h"
80 #include "vmcore/rt-timing.h"
82 #if defined(ENABLE_VMLOG)
83 #include <vmlog_cacao.h>
87 /* include builtin tables *****************************************************/
89 #include "vm/builtintable.inc"
92 CYCLES_STATS_DECLARE(builtin_new ,100,5)
93 CYCLES_STATS_DECLARE(builtin_overhead , 80,1)
96 /*============================================================================*/
97 /* BUILTIN TABLE MANAGEMENT FUNCTIONS */
98 /*============================================================================*/
100 /* builtintable_init ***********************************************************
102 Parse the descriptors of builtin functions and create the parsed
105 *******************************************************************************/
107 static bool builtintable_init(void)
109 descriptor_pool *descpool;
111 builtintable_entry *bte;
114 /* mark start of dump memory area */
116 dumpsize = dump_size();
118 /* create a new descriptor pool */
120 descpool = descriptor_pool_new(class_java_lang_Object);
122 /* add some entries we need */
124 if (!descriptor_pool_add_class(descpool, utf_java_lang_Object))
127 if (!descriptor_pool_add_class(descpool, utf_java_lang_Class))
130 /* first add all descriptors to the pool */
132 for (bte = builtintable_internal; bte->fp != NULL; bte++) {
133 bte->name = utf_new_char(bte->cname);
134 bte->descriptor = utf_new_char(bte->cdescriptor);
136 if (!descriptor_pool_add(descpool, bte->descriptor, NULL)) {
137 /* release dump area */
139 dump_release(dumpsize);
145 for (bte = builtintable_automatic; bte->fp != NULL; bte++) {
146 bte->descriptor = utf_new_char(bte->cdescriptor);
148 if (!descriptor_pool_add(descpool, bte->descriptor, NULL)) {
149 dump_release(dumpsize);
154 for (bte = builtintable_function; bte->fp != NULL; bte++) {
155 bte->classname = utf_new_char(bte->cclassname);
156 bte->name = utf_new_char(bte->cname);
157 bte->descriptor = utf_new_char(bte->cdescriptor);
159 if (!descriptor_pool_add(descpool, bte->descriptor, NULL)) {
160 dump_release(dumpsize);
165 /* create the class reference table */
167 (void) descriptor_pool_create_classrefs(descpool, NULL);
169 /* allocate space for the parsed descriptors */
171 descriptor_pool_alloc_parsed_descriptors(descpool);
173 /* Now parse all descriptors. NOTE: builtin-functions are treated
174 like static methods (no `this' pointer). */
176 for (bte = builtintable_internal; bte->fp != NULL; bte++) {
178 descriptor_pool_parse_method_descriptor(descpool,
180 ACC_STATIC | ACC_METHOD_BUILTIN,
183 /* generate a builtin stub if we need one */
185 if (bte->flags & BUILTINTABLE_FLAG_STUB) {
186 m = method_new_builtin(bte);
187 codegen_generate_stub_builtin(m, bte);
191 for (bte = builtintable_automatic; bte->fp != NULL; bte++) {
193 descriptor_pool_parse_method_descriptor(descpool,
195 ACC_STATIC | ACC_METHOD_BUILTIN,
198 /* no stubs should be needed for this table */
200 assert(!bte->flags & BUILTINTABLE_FLAG_STUB);
203 for (bte = builtintable_function; bte->fp != NULL; bte++) {
205 descriptor_pool_parse_method_descriptor(descpool,
207 ACC_STATIC | ACC_METHOD_BUILTIN,
210 /* generate a builtin stub if we need one */
212 if (bte->flags & BUILTINTABLE_FLAG_STUB) {
213 m = method_new_builtin(bte);
214 codegen_generate_stub_builtin(m, bte);
218 /* release dump area */
220 dump_release(dumpsize);
226 /* builtintable_comparator *****************************************************
228 qsort comparator for the automatic builtin table.
230 *******************************************************************************/
232 static int builtintable_comparator(const void *a, const void *b)
234 builtintable_entry *bte1;
235 builtintable_entry *bte2;
237 bte1 = (builtintable_entry *) a;
238 bte2 = (builtintable_entry *) b;
240 return (bte1->opcode < bte2->opcode) ? -1 : (bte1->opcode > bte2->opcode);
244 /* builtintable_sort_automatic *************************************************
246 Sorts the automatic builtin table.
248 *******************************************************************************/
250 static void builtintable_sort_automatic(void)
254 /* calculate table size statically (`- 1' comment see builtintable.inc) */
256 entries = sizeof(builtintable_automatic) / sizeof(builtintable_entry) - 1;
258 qsort(builtintable_automatic, entries, sizeof(builtintable_entry),
259 builtintable_comparator);
263 /* builtin_init ****************************************************************
265 Initialize the global table of builtin functions.
267 *******************************************************************************/
269 bool builtin_init(void)
271 /* initialize the builtin tables */
273 if (!builtintable_init())
276 /* sort builtin tables */
278 builtintable_sort_automatic();
284 /* builtintable_get_internal ***************************************************
286 Finds an entry in the builtintable for internal functions and
287 returns the a pointer to the structure.
289 *******************************************************************************/
291 builtintable_entry *builtintable_get_internal(functionptr fp)
293 builtintable_entry *bte;
295 for (bte = builtintable_internal; bte->fp != NULL; bte++) {
304 /* builtintable_get_automatic **************************************************
306 Finds an entry in the builtintable for functions which are replaced
307 automatically and returns the a pointer to the structure.
309 *******************************************************************************/
311 builtintable_entry *builtintable_get_automatic(s4 opcode)
313 builtintable_entry *first;
314 builtintable_entry *last;
315 builtintable_entry *middle;
319 /* calculate table size statically (`- 1' comment see builtintable.inc) */
321 entries = sizeof(builtintable_automatic) / sizeof(builtintable_entry) - 1;
323 first = builtintable_automatic;
324 last = builtintable_automatic + entries;
326 while (entries > 0) {
328 middle = first + half;
330 if (middle->opcode < opcode) {
338 return (first != last ? first : NULL);
342 /* builtintable_replace_function ***********************************************
346 *******************************************************************************/
348 #if defined(ENABLE_JIT)
349 bool builtintable_replace_function(void *iptr_)
352 builtintable_entry *bte;
355 iptr = (instruction *) iptr_; /* twisti will kill me ;) */
357 /* get name and descriptor of the function */
360 case ICMD_INVOKESTATIC:
361 /* The instruction MUST be resolved, otherwise we run into
362 lazy loading troubles. Anyway, we should/can only replace
363 very VM-close functions. */
365 if (INSTRUCTION_IS_UNRESOLVED(iptr))
368 mr = iptr->sx.s23.s3.fmiref;
375 /* search the function table */
377 for (bte = builtintable_function; bte->fp != NULL; bte++) {
378 if ((METHODREF_CLASSNAME(mr) == bte->classname) &&
379 (mr->name == bte->name) &&
380 (mr->descriptor == bte->descriptor)) {
382 /* set the values in the instruction */
384 iptr->opc = bte->opcode;
385 iptr->sx.s23.s3.bte = bte;
387 if (bte->flags & BUILTINTABLE_FLAG_EXCEPTION)
388 iptr->flags.bits |= INS_FLAG_CHECK;
390 iptr->flags.bits &= ~INS_FLAG_CHECK;
398 #endif /* defined(ENABLE_JIT) */
401 /*============================================================================*/
402 /* INTERNAL BUILTIN FUNCTIONS */
403 /*============================================================================*/
405 /* builtin_instanceof **********************************************************
407 Checks if an object is an instance of some given class (or subclass
408 of that class). If class is an interface, checks if the interface
412 1......o is an instance of class or implements the interface
413 0......otherwise or if o == NULL
415 NOTE: This builtin can be called from NATIVE code only.
417 *******************************************************************************/
419 s4 builtin_instanceof(java_handle_t *o, classinfo *class)
426 LLNI_class_get(o, c);
428 return class_isanysubclass(c, class);
433 /* builtin_checkcast ***********************************************************
435 The same as builtin_instanceof but with the exception
436 that 1 is returned when (o == NULL).
438 NOTE: This builtin can be called from NATIVE code only.
440 *******************************************************************************/
442 s4 builtin_checkcast(java_handle_t *o, classinfo *class)
449 LLNI_class_get(o, c);
451 if (class_isanysubclass(c, class))
458 /* builtin_descriptorscompatible ***********************************************
460 Checks if two array type descriptors are assignment compatible.
463 1......target = desc is possible
466 *******************************************************************************/
468 static s4 builtin_descriptorscompatible(arraydescriptor *desc,
469 arraydescriptor *target)
474 if (desc->arraytype != target->arraytype)
477 if (desc->arraytype != ARRAYTYPE_OBJECT)
480 /* {both arrays are arrays of references} */
482 if (desc->dimension == target->dimension) {
483 /* an array which contains elements of interface types is
484 allowed to be casted to Object (JOWENN)*/
486 if ((desc->elementvftbl->baseval < 0) &&
487 (target->elementvftbl->baseval == 1))
490 return class_isanysubclass(desc->elementvftbl->class,
491 target->elementvftbl->class);
494 if (desc->dimension < target->dimension)
497 /* {desc has higher dimension than target} */
499 return class_isanysubclass(pseudo_class_Arraystub,
500 target->elementvftbl->class);
504 /* builtin_arraycheckcast ******************************************************
506 Checks if an object is really a subtype of the requested array
507 type. The object has to be an array to begin with. For simple
508 arrays (int, short, double, etc.) the types have to match exactly.
509 For arrays of objects, the type of elements in the array has to be
510 a subtype (or the same type) of the requested element type. For
511 arrays of arrays (which in turn can again be arrays of arrays), the
512 types at the lowest level have to satisfy the corresponding sub
515 NOTE: This is a FAST builtin and can be called from JIT code only.
517 *******************************************************************************/
519 s4 builtin_fast_arraycheckcast(java_object_t *o, classinfo *targetclass)
521 arraydescriptor *desc;
526 desc = o->vftbl->arraydesc;
531 return builtin_descriptorscompatible(desc, targetclass->vftbl->arraydesc);
535 /* builtin_fast_arrayinstanceof ************************************************
537 NOTE: This is a FAST builtin and can be called from JIT code only.
539 *******************************************************************************/
541 s4 builtin_fast_arrayinstanceof(java_object_t *o, classinfo *targetclass)
546 return builtin_fast_arraycheckcast(o, targetclass);
550 /* builtin_arrayinstanceof *****************************************************
552 NOTE: This builtin can be called from NATIVE code only.
554 *******************************************************************************/
556 s4 builtin_arrayinstanceof(java_handle_t *h, classinfo *targetclass)
562 result = builtin_fast_arrayinstanceof(LLNI_UNWRAP(h), targetclass);
570 /* builtin_throw_exception *****************************************************
572 Sets the exception pointer with the thrown exception and prints some
573 debugging information.
575 NOTE: This is a FAST builtin and can be called from JIT code,
576 or from asm_vm_call_method.
578 *******************************************************************************/
580 void *builtin_throw_exception(java_object_t *xptr)
583 /* print exception trace */
585 if (opt_TraceExceptions)
586 trace_exception_builtin(xptr);
587 #endif /* !defined(NDEBUG) */
589 /* actually set the exception */
591 exceptions_set_exception(LLNI_QUICKWRAP(xptr));
593 /* Return a NULL pointer. This is required for vm_call_method to
594 check for an exception. This is for convenience. */
600 /* builtin_canstore ************************************************************
602 Checks, if an object can be stored in an array.
606 0......otherwise (throws an ArrayStoreException)
608 NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code.
610 *******************************************************************************/
612 s4 builtin_canstore(java_handle_objectarray_t *oa, java_handle_t *o)
618 result = builtin_fast_canstore(LLNI_DIRECT(oa), LLNI_UNWRAP(o));
622 /* if not possible, throw an exception */
625 exceptions_throw_arraystoreexception();
631 /* builtin_fast_canstore *******************************************************
633 Checks, if an object can be stored in an array.
637 0......otherwise (no exception thrown!)
639 NOTE: This is a FAST builtin and can be called from JIT code only.
641 *******************************************************************************/
643 s4 builtin_fast_canstore(java_objectarray_t *oa, java_object_t *o)
645 arraydescriptor *desc;
646 arraydescriptor *valuedesc;
647 vftbl_t *componentvftbl;
656 /* The following is guaranteed (by verifier checks):
658 * *) oa->...vftbl->arraydesc != NULL
659 * *) oa->...vftbl->arraydesc->componentvftbl != NULL
660 * *) o->vftbl is not an interface vftbl
663 desc = oa->header.objheader.vftbl->arraydesc;
664 componentvftbl = desc->componentvftbl;
665 valuevftbl = o->vftbl;
666 valuedesc = valuevftbl->arraydesc;
668 if ((desc->dimension - 1) == 0) {
669 /* {oa is a one-dimensional array} */
670 /* {oa is an array of references} */
672 if (valuevftbl == componentvftbl)
675 LOCK_MONITOR_ENTER(linker_classrenumber_lock);
677 baseval = componentvftbl->baseval;
680 /* an array of interface references */
682 result = ((valuevftbl->interfacetablelength > -baseval) &&
683 (valuevftbl->interfacetable[baseval] != NULL));
686 diffval = valuevftbl->baseval - componentvftbl->baseval;
687 result = diffval <= (uint32_t) componentvftbl->diffval;
690 LOCK_MONITOR_EXIT(linker_classrenumber_lock);
692 else if (valuedesc == NULL) {
693 /* {oa has dimension > 1} */
694 /* {componentvftbl->arraydesc != NULL} */
696 /* check if o is an array */
701 /* {o is an array} */
703 result = builtin_descriptorscompatible(valuedesc, componentvftbl->arraydesc);
712 /* This is an optimized version where a is guaranteed to be one-dimensional */
713 s4 builtin_fast_canstore_onedim(java_objectarray_t *a, java_object_t *o)
715 arraydescriptor *desc;
716 vftbl_t *elementvftbl;
725 /* The following is guaranteed (by verifier checks):
727 * *) a->...vftbl->arraydesc != NULL
728 * *) a->...vftbl->arraydesc->elementvftbl != NULL
729 * *) a->...vftbl->arraydesc->dimension == 1
730 * *) o->vftbl is not an interface vftbl
733 desc = a->header.objheader.vftbl->arraydesc;
734 elementvftbl = desc->elementvftbl;
735 valuevftbl = o->vftbl;
737 /* {a is a one-dimensional array} */
739 if (valuevftbl == elementvftbl)
742 LOCK_MONITOR_ENTER(linker_classrenumber_lock);
744 baseval = elementvftbl->baseval;
747 /* an array of interface references */
748 result = ((valuevftbl->interfacetablelength > -baseval) &&
749 (valuevftbl->interfacetable[baseval] != NULL));
752 diffval = valuevftbl->baseval - elementvftbl->baseval;
753 result = diffval <= (uint32_t) elementvftbl->diffval;
756 LOCK_MONITOR_EXIT(linker_classrenumber_lock);
762 /* This is an optimized version where a is guaranteed to be a
763 * one-dimensional array of a class type */
764 s4 builtin_fast_canstore_onedim_class(java_objectarray_t *a, java_object_t *o)
766 vftbl_t *elementvftbl;
774 /* The following is guaranteed (by verifier checks):
776 * *) a->...vftbl->arraydesc != NULL
777 * *) a->...vftbl->arraydesc->elementvftbl != NULL
778 * *) a->...vftbl->arraydesc->elementvftbl is not an interface vftbl
779 * *) a->...vftbl->arraydesc->dimension == 1
780 * *) o->vftbl is not an interface vftbl
783 elementvftbl = a->header.objheader.vftbl->arraydesc->elementvftbl;
784 valuevftbl = o->vftbl;
786 /* {a is a one-dimensional array} */
788 if (valuevftbl == elementvftbl)
791 LOCK_MONITOR_ENTER(linker_classrenumber_lock);
793 diffval = valuevftbl->baseval - elementvftbl->baseval;
794 result = diffval <= (uint32_t) elementvftbl->diffval;
796 LOCK_MONITOR_EXIT(linker_classrenumber_lock);
802 /* builtin_new *****************************************************************
804 Creates a new instance of class c on the heap.
807 pointer to the object, or NULL if no memory is available
809 NOTE: This builtin can be called from NATIVE code only.
811 *******************************************************************************/
813 java_handle_t *builtin_new(classinfo *c)
816 #if defined(ENABLE_RT_TIMING)
817 struct timespec time_start, time_end;
819 #if defined(ENABLE_CYCLES_STATS)
820 u8 cycles_start, cycles_end;
823 RT_TIMING_GET_TIME(time_start);
824 CYCLES_STATS_GET(cycles_start);
826 /* is the class loaded */
828 assert(c->state & CLASS_LOADED);
830 /* check if we can instantiate this class */
832 if (c->flags & ACC_ABSTRACT) {
833 exceptions_throw_instantiationerror(c);
837 /* is the class linked */
839 if (!(c->state & CLASS_LINKED))
843 if (!(c->state & CLASS_INITIALIZED)) {
846 log_message_class("Initialize class (from builtin_new): ", c);
849 if (!initialize_class(c))
853 o = heap_alloc(c->instancesize, c->flags & ACC_CLASS_HAS_POINTERS,
859 #if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
860 /* XXX this is only a dirty hack to make Boehm work with handles */
862 o = LLNI_WRAP((java_object_t *) o);
865 LLNI_vftbl_direct(o) = c->vftbl;
867 #if defined(ENABLE_THREADS)
868 lock_init_object_lock(LLNI_DIRECT(o));
871 CYCLES_STATS_GET(cycles_end);
872 RT_TIMING_GET_TIME(time_end);
874 CYCLES_STATS_COUNT(builtin_new,cycles_end - cycles_start);
875 RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_OBJECT);
881 /* builtin_java_new ************************************************************
883 NOTE: This is a SLOW builtin and can be called from JIT code only.
885 *******************************************************************************/
887 java_handle_t *builtin_java_new(java_handle_t *clazz)
889 return builtin_new(LLNI_classinfo_unwrap(clazz));
893 /* builtin_fast_new ************************************************************
895 Creates a new instance of class c on the heap.
898 pointer to the object, or NULL if no fast return
899 is possible for any reason.
901 NOTE: This is a FAST builtin and can be called from JIT code only.
903 *******************************************************************************/
905 java_object_t *builtin_fast_new(classinfo *c)
908 #if defined(ENABLE_RT_TIMING)
909 struct timespec time_start, time_end;
911 #if defined(ENABLE_CYCLES_STATS)
912 u8 cycles_start, cycles_end;
915 RT_TIMING_GET_TIME(time_start);
916 CYCLES_STATS_GET(cycles_start);
918 /* is the class loaded */
920 assert(c->state & CLASS_LOADED);
922 /* check if we can instantiate this class */
924 if (c->flags & ACC_ABSTRACT)
927 /* is the class linked */
929 if (!(c->state & CLASS_LINKED))
932 if (!(c->state & CLASS_INITIALIZED))
935 o = heap_alloc(c->instancesize, c->flags & ACC_CLASS_HAS_POINTERS,
936 c->finalizer, false);
943 #if defined(ENABLE_THREADS)
944 lock_init_object_lock(o);
947 CYCLES_STATS_GET(cycles_end);
948 RT_TIMING_GET_TIME(time_end);
950 CYCLES_STATS_COUNT(builtin_new,cycles_end - cycles_start);
951 RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_OBJECT);
957 /* builtin_newarray ************************************************************
959 Creates an array with the given vftbl on the heap. This function
960 takes as class argument an array class.
963 pointer to the array or NULL if no memory is available
965 NOTE: This builtin can be called from NATIVE code only.
967 *******************************************************************************/
969 java_handle_t *builtin_newarray(s4 size, classinfo *arrayclass)
971 arraydescriptor *desc;
976 #if defined(ENABLE_RT_TIMING)
977 struct timespec time_start, time_end;
980 RT_TIMING_GET_TIME(time_start);
982 desc = arrayclass->vftbl->arraydesc;
983 dataoffset = desc->dataoffset;
984 componentsize = desc->componentsize;
987 exceptions_throw_negativearraysizeexception();
991 actualsize = dataoffset + size * componentsize;
993 /* check for overflow */
995 if (((u4) actualsize) < ((u4) size)) {
996 exceptions_throw_outofmemoryerror();
1000 a = heap_alloc(actualsize, (desc->arraytype == ARRAYTYPE_OBJECT), NULL, true);
1005 #if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
1006 /* XXX this is only a dirty hack to make Boehm work with handles */
1008 a = LLNI_WRAP((java_object_t *) a);
1011 LLNI_vftbl_direct(a) = arrayclass->vftbl;
1013 #if defined(ENABLE_THREADS)
1014 lock_init_object_lock(LLNI_DIRECT(a));
1017 LLNI_array_size(a) = size;
1019 RT_TIMING_GET_TIME(time_end);
1020 RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_ARRAY);
1026 /* builtin_java_newarray *******************************************************
1028 NOTE: This is a SLOW builtin and can be called from JIT code only.
1030 *******************************************************************************/
1032 java_handle_t *builtin_java_newarray(s4 size, java_handle_t *arrayclazz)
1034 return builtin_newarray(size, LLNI_classinfo_unwrap(arrayclazz));
1038 /* builtin_anewarray ***********************************************************
1040 Creates an array of references to the given class type on the heap.
1043 pointer to the array or NULL if no memory is
1046 NOTE: This builtin can be called from NATIVE code only.
1048 *******************************************************************************/
1050 java_handle_objectarray_t *builtin_anewarray(s4 size, classinfo *componentclass)
1052 classinfo *arrayclass;
1054 /* is class loaded */
1056 assert(componentclass->state & CLASS_LOADED);
1058 /* is class linked */
1060 if (!(componentclass->state & CLASS_LINKED))
1061 if (!link_class(componentclass))
1064 arrayclass = class_array_of(componentclass, true);
1069 return (java_handle_objectarray_t *) builtin_newarray(size, arrayclass);
1073 /* builtin_newarray_type ****************************************************
1075 Creates an array of [type]s on the heap.
1078 pointer to the array or NULL if no memory is available
1080 NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code.
1082 *******************************************************************************/
1084 #define BUILTIN_NEWARRAY_TYPE(type, arraytype) \
1085 java_handle_##type##array_t *builtin_newarray_##type(s4 size) \
1087 return (java_handle_##type##array_t *) \
1088 builtin_newarray(size, primitivetype_table[arraytype].arrayclass); \
1091 BUILTIN_NEWARRAY_TYPE(boolean, ARRAYTYPE_BOOLEAN)
1092 BUILTIN_NEWARRAY_TYPE(byte, ARRAYTYPE_BYTE)
1093 BUILTIN_NEWARRAY_TYPE(char, ARRAYTYPE_CHAR)
1094 BUILTIN_NEWARRAY_TYPE(short, ARRAYTYPE_SHORT)
1095 BUILTIN_NEWARRAY_TYPE(int, ARRAYTYPE_INT)
1096 BUILTIN_NEWARRAY_TYPE(long, ARRAYTYPE_LONG)
1097 BUILTIN_NEWARRAY_TYPE(float, ARRAYTYPE_FLOAT)
1098 BUILTIN_NEWARRAY_TYPE(double, ARRAYTYPE_DOUBLE)
1101 /* builtin_multianewarray_intern ***********************************************
1103 Creates a multi-dimensional array on the heap. The dimensions are
1104 passed in an array of longs.
1107 n.............number of dimensions to create
1108 arrayclass....the array class
1109 dims..........array containing the size of each dimension to create
1112 pointer to the array or NULL if no memory is available
1114 ******************************************************************************/
1116 static java_handle_t *builtin_multianewarray_intern(int n,
1117 classinfo *arrayclass,
1122 classinfo *componentclass;
1125 /* create this dimension */
1127 size = (s4) dims[0];
1128 a = builtin_newarray(size, arrayclass);
1133 /* if this is the last dimension return */
1138 /* get the class of the components to create */
1140 componentclass = arrayclass->vftbl->arraydesc->componentvftbl->class;
1142 /* The verifier guarantees that the dimension count is in the range. */
1144 /* create the component arrays */
1146 for (i = 0; i < size; i++) {
1148 #if defined(__MIPS__) && (SIZEOF_VOID_P == 4)
1149 /* we save an s4 to a s8 slot, 8-byte aligned */
1151 builtin_multianewarray_intern(n, componentclass, dims + 2);
1153 builtin_multianewarray_intern(n, componentclass, dims + 1);
1159 array_objectarray_element_set((java_handle_objectarray_t *) a, i, ea);
1166 /* builtin_multianewarray ******************************************************
1168 Wrapper for builtin_multianewarray_intern which checks all
1169 dimensions before we start allocating.
1171 NOTE: This is a SLOW builtin and can be called from JIT code only.
1173 ******************************************************************************/
1175 java_handle_objectarray_t *builtin_multianewarray(int n,
1176 java_handle_t *arrayclazz,
1183 /* check all dimensions before doing anything */
1185 for (i = 0; i < n; i++) {
1186 #if defined(__MIPS__) && (SIZEOF_VOID_P == 4)
1187 /* we save an s4 to a s8 slot, 8-byte aligned */
1188 size = (s4) dims[i * 2];
1190 size = (s4) dims[i];
1194 exceptions_throw_negativearraysizeexception();
1199 c = LLNI_classinfo_unwrap(arrayclazz);
1201 /* now call the real function */
1203 return (java_handle_objectarray_t *)
1204 builtin_multianewarray_intern(n, c, dims);
1208 /* builtin_verbosecall_enter ***************************************************
1210 Print method call with arguments for -verbose:call.
1212 XXX: Remove mew once all archs use the new tracer!
1214 *******************************************************************************/
1216 #if !defined(NDEBUG)
1217 #ifdef TRACE_ARGS_NUM
1218 void builtin_verbosecall_enter(s8 a0, s8 a1,
1219 # if TRACE_ARGS_NUM >= 4
1222 # if TRACE_ARGS_NUM >= 6
1225 # if TRACE_ARGS_NUM == 8
1230 log_text("builtin_verbosecall_enter: Do not call me anymore!");
1233 #endif /* !defined(NDEBUG) */
1236 /* builtin_verbosecall_exit ****************************************************
1238 Print method exit for -verbose:call.
1240 XXX: Remove mew once all archs use the new tracer!
1242 *******************************************************************************/
1244 #if !defined(NDEBUG)
1245 void builtin_verbosecall_exit(s8 l, double d, float f, methodinfo *m)
1247 log_text("builtin_verbosecall_exit: Do not call me anymore!");
1249 #endif /* !defined(NDEBUG) */
1252 /*============================================================================*/
1253 /* MISCELLANEOUS MATHEMATICAL HELPER FUNCTIONS */
1254 /*============================================================================*/
1256 /*********** Functions for integer divisions *****************************
1258 On some systems (eg. DEC ALPHA), integer division is not supported by the
1259 CPU. These helper functions implement the missing functionality.
1261 ******************************************************************************/
1263 #if !SUPPORT_DIVISION || defined(DISABLE_GC)
1264 s4 builtin_idiv(s4 a, s4 b)
1273 s4 builtin_irem(s4 a, s4 b)
1281 #endif /* !SUPPORT_DIVISION || defined(DISABLE_GC) */
1284 /* functions for long arithmetics **********************************************
1286 On systems where 64 bit Integers are not supported by the CPU,
1287 these functions are needed.
1289 ******************************************************************************/
1291 #if !(SUPPORT_LONG && SUPPORT_LONG_ADD)
1292 s8 builtin_ladd(s8 a, s8 b)
1305 s8 builtin_lsub(s8 a, s8 b)
1318 s8 builtin_lneg(s8 a)
1330 #endif /* !(SUPPORT_LONG && SUPPORT_LONG_ADD) */
1333 #if !(SUPPORT_LONG && SUPPORT_LONG_MUL)
1334 s8 builtin_lmul(s8 a, s8 b)
1346 #endif /* !(SUPPORT_LONG && SUPPORT_LONG_MUL) */
1349 #if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) || defined (DISABLE_GC)
1350 s8 builtin_ldiv(s8 a, s8 b)
1363 s8 builtin_lrem(s8 a, s8 b)
1375 #endif /* !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) */
1378 #if !(SUPPORT_LONG && SUPPORT_LONG_SHIFT)
1379 s8 builtin_lshl(s8 a, s4 b)
1392 s8 builtin_lshr(s8 a, s4 b)
1405 s8 builtin_lushr(s8 a, s4 b)
1410 c = ((u8) a) >> (b & 63);
1417 #endif /* !(SUPPORT_LONG && SUPPORT_LONG_SHIFT) */
1420 #if !(SUPPORT_LONG && SUPPORT_LONG_LOGICAL)
1421 s8 builtin_land(s8 a, s8 b)
1434 s8 builtin_lor(s8 a, s8 b)
1447 s8 builtin_lxor(s8 a, s8 b)
1459 #endif /* !(SUPPORT_LONG && SUPPORT_LONG_LOGICAL) */
1462 #if !(SUPPORT_LONG && SUPPORT_LONG_CMP)
1463 s4 builtin_lcmp(s8 a, s8 b)
1477 #endif /* !(SUPPORT_LONG && SUPPORT_LONG_CMP) */
1480 /* functions for unsupported floating instructions ****************************/
1482 /* used to convert FLT_xxx defines into float values */
1484 static inline float intBitsToFloat(s4 i)
1493 /* used to convert DBL_xxx defines into double values */
1495 static inline float longBitsToDouble(s8 l)
1505 float builtin_fadd(float a, float b)
1507 if (isnanf(a)) return intBitsToFloat(FLT_NAN);
1508 if (isnanf(b)) return intBitsToFloat(FLT_NAN);
1519 if (copysignf(1.0, a) == copysignf(1.0, b))
1522 return intBitsToFloat(FLT_NAN);
1528 float builtin_fsub(float a, float b)
1530 return builtin_fadd(a, builtin_fneg(b));
1534 float builtin_fmul(float a, float b)
1536 if (isnanf(a)) return intBitsToFloat(FLT_NAN);
1537 if (isnanf(b)) return intBitsToFloat(FLT_NAN);
1539 if (finitef(b)) return a * b;
1541 if (a == 0) return intBitsToFloat(FLT_NAN);
1542 else return copysignf(b, copysignf(1.0, b)*a);
1547 if (b == 0) return intBitsToFloat(FLT_NAN);
1548 else return copysignf(a, copysignf(1.0, a)*b);
1551 return copysignf(a, copysignf(1.0, a)*copysignf(1.0, b));
1557 /* builtin_ddiv ****************************************************************
1559 Implementation as described in VM Spec.
1561 *******************************************************************************/
1563 float builtin_fdiv(float a, float b)
1567 /* If neither value1' nor value2' is NaN, the sign of the result */
1568 /* is positive if both values have the same sign, negative if the */
1569 /* values have different signs. */
1575 /* If either value1' or value2' is NaN, the result is NaN. */
1577 return intBitsToFloat(FLT_NAN);
1580 /* Division of a finite value by an infinity results in a */
1581 /* signed zero, with the sign-producing rule just given. */
1583 /* is sign equal? */
1585 if (copysignf(1.0, a) == copysignf(1.0, b))
1594 /* If either value1' or value2' is NaN, the result is NaN. */
1596 return intBitsToFloat(FLT_NAN);
1598 } else if (finitef(b)) {
1599 /* Division of an infinity by a finite value results in a signed */
1600 /* infinity, with the sign-producing rule just given. */
1602 /* is sign equal? */
1604 if (copysignf(1.0, a) == copysignf(1.0, b))
1605 return intBitsToFloat(FLT_POSINF);
1607 return intBitsToFloat(FLT_NEGINF);
1610 /* Division of an infinity by an infinity results in NaN. */
1612 return intBitsToFloat(FLT_NAN);
1618 float builtin_fneg(float a)
1620 if (isnanf(a)) return a;
1622 if (finitef(a)) return -a;
1623 else return copysignf(a, -copysignf(1.0, a));
1626 #endif /* !SUPPORT_FLOAT */
1629 #if !SUPPORT_FLOAT || !SUPPORT_FLOAT_CMP || defined(ENABLE_INTRP)
1630 s4 builtin_fcmpl(float a, float b)
1638 if (!finitef(a) || !finitef(b)) {
1639 a = finitef(a) ? 0 : copysignf(1.0, a);
1640 b = finitef(b) ? 0 : copysignf(1.0, b);
1653 s4 builtin_fcmpg(float a, float b)
1655 if (isnanf(a)) return 1;
1656 if (isnanf(b)) return 1;
1657 if (!finitef(a) || !finitef(b)) {
1658 a = finitef(a) ? 0 : copysignf(1.0, a);
1659 b = finitef(b) ? 0 : copysignf(1.0, b);
1661 if (a > b) return 1;
1662 if (a == b) return 0;
1665 #endif /* !SUPPORT_FLOAT || !SUPPORT_FLOAT_CMP || defined(ENABLE_INTRP) */
1668 float builtin_frem(float a, float b)
1674 /* functions for unsupported double instructions ******************************/
1677 double builtin_dadd(double a, double b)
1679 if (isnan(a)) return longBitsToDouble(DBL_NAN);
1680 if (isnan(b)) return longBitsToDouble(DBL_NAN);
1682 if (finite(b)) return a + b;
1686 if (finite(b)) return a;
1688 if (copysign(1.0, a)==copysign(1.0, b)) return a;
1689 else return longBitsToDouble(DBL_NAN);
1695 double builtin_dsub(double a, double b)
1697 return builtin_dadd(a, builtin_dneg(b));
1701 double builtin_dmul(double a, double b)
1703 if (isnan(a)) return longBitsToDouble(DBL_NAN);
1704 if (isnan(b)) return longBitsToDouble(DBL_NAN);
1706 if (finite(b)) return a * b;
1708 if (a == 0) return longBitsToDouble(DBL_NAN);
1709 else return copysign(b, copysign(1.0, b) * a);
1714 if (b == 0) return longBitsToDouble(DBL_NAN);
1715 else return copysign(a, copysign(1.0, a) * b);
1718 return copysign(a, copysign(1.0, a) * copysign(1.0, b));
1724 /* builtin_ddiv ****************************************************************
1726 Implementation as described in VM Spec.
1728 *******************************************************************************/
1730 double builtin_ddiv(double a, double b)
1734 /* If neither value1' nor value2' is NaN, the sign of the result */
1735 /* is positive if both values have the same sign, negative if the */
1736 /* values have different signs. */
1742 /* If either value1' or value2' is NaN, the result is NaN. */
1744 return longBitsToDouble(DBL_NAN);
1747 /* Division of a finite value by an infinity results in a */
1748 /* signed zero, with the sign-producing rule just given. */
1750 /* is sign equal? */
1752 if (copysign(1.0, a) == copysign(1.0, b))
1761 /* If either value1' or value2' is NaN, the result is NaN. */
1763 return longBitsToDouble(DBL_NAN);
1765 } else if (finite(b)) {
1766 /* Division of an infinity by a finite value results in a signed */
1767 /* infinity, with the sign-producing rule just given. */
1769 /* is sign equal? */
1771 if (copysign(1.0, a) == copysign(1.0, b))
1772 return longBitsToDouble(DBL_POSINF);
1774 return longBitsToDouble(DBL_NEGINF);
1777 /* Division of an infinity by an infinity results in NaN. */
1779 return longBitsToDouble(DBL_NAN);
1785 /* builtin_dneg ****************************************************************
1787 Implemented as described in VM Spec.
1789 *******************************************************************************/
1791 double builtin_dneg(double a)
1794 /* If the operand is NaN, the result is NaN (recall that NaN has no */
1801 /* If the operand is a zero, the result is the zero of opposite */
1807 /* If the operand is an infinity, the result is the infinity of */
1808 /* opposite sign. */
1810 return copysign(a, -copysign(1.0, a));
1814 #endif /* !SUPPORT_DOUBLE */
1817 #if !SUPPORT_DOUBLE || !SUPPORT_DOUBLE_CMP || defined(ENABLE_INTRP)
1818 s4 builtin_dcmpl(double a, double b)
1826 if (!finite(a) || !finite(b)) {
1827 a = finite(a) ? 0 : copysign(1.0, a);
1828 b = finite(b) ? 0 : copysign(1.0, b);
1841 s4 builtin_dcmpg(double a, double b)
1849 if (!finite(a) || !finite(b)) {
1850 a = finite(a) ? 0 : copysign(1.0, a);
1851 b = finite(b) ? 0 : copysign(1.0, b);
1862 #endif /* !SUPPORT_DOUBLE || !SUPPORT_DOUBLE_CMP || defined(ENABLE_INTRP) */
1865 double builtin_drem(double a, double b)
1871 /* conversion operations ******************************************************/
1874 s8 builtin_i2l(s4 i)
1886 s4 builtin_l2i(s8 l)
1897 #if !(SUPPORT_FLOAT && SUPPORT_I2F)
1898 float builtin_i2f(s4 a)
1900 float f = (float) a;
1903 #endif /* !(SUPPORT_FLOAT && SUPPORT_I2F) */
1906 #if !(SUPPORT_DOUBLE && SUPPORT_I2D)
1907 double builtin_i2d(s4 a)
1909 double d = (double) a;
1912 #endif /* !(SUPPORT_DOUBLE && SUPPORT_I2D) */
1915 #if !(SUPPORT_LONG && SUPPORT_FLOAT && SUPPORT_L2F)
1916 float builtin_l2f(s8 a)
1919 float f = (float) a;
1925 #endif /* !(SUPPORT_LONG && SUPPORT_FLOAT && SUPPORT_L2F) */
1928 #if !(SUPPORT_LONG && SUPPORT_DOUBLE && SUPPORT_L2D)
1929 double builtin_l2d(s8 a)
1932 double d = (double) a;
1938 #endif /* !(SUPPORT_LONG && SUPPORT_DOUBLE && SUPPORT_L2D) */
1941 #if !(SUPPORT_FLOAT && SUPPORT_F2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC)
1942 s4 builtin_f2i(float a)
1946 i = builtin_d2i((double) a);
1957 if (a < (-2147483648))
1958 return (-2147483648);
1961 f = copysignf((float) 1.0, a);
1964 return (-2147483648); */
1966 #endif /* !(SUPPORT_FLOAT && SUPPORT_F2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC) */
1969 #if !(SUPPORT_FLOAT && SUPPORT_LONG && SUPPORT_F2L) || defined(DISABLE_GC)
1970 s8 builtin_f2l(float a)
1974 l = builtin_d2l((double) a);
1981 if (a > 9223372036854775807L)
1982 return 9223372036854775807L;
1983 if (a < (-9223372036854775808L))
1984 return (-9223372036854775808L);
1989 f = copysignf((float) 1.0, a);
1991 return 9223372036854775807L;
1992 return (-9223372036854775808L); */
1994 #endif /* !(SUPPORT_FLOAT && SUPPORT_LONG && SUPPORT_F2L) */
1997 #if !(SUPPORT_DOUBLE && SUPPORT_D2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC)
1998 s4 builtin_d2i(double a)
2003 if (a >= 2147483647)
2005 if (a <= (-2147483647-1))
2006 return (-2147483647-1);
2011 d = copysign(1.0, a);
2014 return (-2147483647-1);
2016 #endif /* !(SUPPORT_DOUBLE && SUPPORT_D2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC) */
2019 #if !(SUPPORT_DOUBLE && SUPPORT_LONG && SUPPORT_D2L) || defined(DISABLE_GC)
2020 s8 builtin_d2l(double a)
2025 if (a >= 9223372036854775807LL)
2026 return 9223372036854775807LL;
2027 if (a <= (-9223372036854775807LL-1))
2028 return (-9223372036854775807LL-1);
2033 d = copysign(1.0, a);
2035 return 9223372036854775807LL;
2036 return (-9223372036854775807LL-1);
2038 #endif /* !(SUPPORT_DOUBLE && SUPPORT_LONG && SUPPORT_D2L) */
2041 #if !(SUPPORT_FLOAT && SUPPORT_DOUBLE)
2042 double builtin_f2d(float a)
2044 if (finitef(a)) return (double) a;
2047 return longBitsToDouble(DBL_NAN);
2049 return copysign(longBitsToDouble(DBL_POSINF), (double) copysignf(1.0, a) );
2053 float builtin_d2f(double a)
2059 return intBitsToFloat(FLT_NAN);
2061 return copysignf(intBitsToFloat(FLT_POSINF), (float) copysign(1.0, a));
2064 #endif /* !(SUPPORT_FLOAT && SUPPORT_DOUBLE) */
2067 /*============================================================================*/
2068 /* AUTOMATICALLY REPLACED FUNCTIONS */
2069 /*============================================================================*/
2071 /* builtin_arraycopy ***********************************************************
2073 Builtin for java.lang.System.arraycopy.
2075 NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code.
2077 *******************************************************************************/
2079 void builtin_arraycopy(java_handle_t *src, s4 srcStart,
2080 java_handle_t *dest, s4 destStart, s4 len)
2082 arraydescriptor *sdesc;
2083 arraydescriptor *ddesc;
2086 if ((src == NULL) || (dest == NULL)) {
2087 exceptions_throw_nullpointerexception();
2091 sdesc = LLNI_vftbl_direct(src)->arraydesc;
2092 ddesc = LLNI_vftbl_direct(dest)->arraydesc;
2094 if (!sdesc || !ddesc || (sdesc->arraytype != ddesc->arraytype)) {
2095 exceptions_throw_arraystoreexception();
2099 /* we try to throw exception with the same message as SUN does */
2101 if ((len < 0) || (srcStart < 0) || (destStart < 0) ||
2102 (srcStart + len < 0) || (srcStart + len > LLNI_array_size(src)) ||
2103 (destStart + len < 0) || (destStart + len > LLNI_array_size(dest))) {
2104 exceptions_throw_arrayindexoutofboundsexception();
2108 if (sdesc->componentvftbl == ddesc->componentvftbl) {
2109 /* We copy primitive values or references of exactly the same type */
2111 s4 dataoffset = sdesc->dataoffset;
2112 s4 componentsize = sdesc->componentsize;
2114 LLNI_CRITICAL_START;
2116 MMOVE(((u1 *) LLNI_DIRECT(dest)) + dataoffset + componentsize * destStart,
2117 ((u1 *) LLNI_DIRECT(src)) + dataoffset + componentsize * srcStart,
2118 u1, (size_t) len * componentsize);
2123 /* We copy references of different type */
2125 java_handle_objectarray_t *oas = (java_handle_objectarray_t *) src;
2126 java_handle_objectarray_t *oad = (java_handle_objectarray_t *) dest;
2128 if (destStart <= srcStart) {
2129 for (i = 0; i < len; i++) {
2132 o = array_objectarray_element_get(oas, srcStart + i);
2134 if (!builtin_canstore(oad, o))
2137 array_objectarray_element_set(oad, destStart + i, o);
2141 /* XXX this does not completely obey the specification!
2142 If an exception is thrown only the elements above the
2143 current index have been copied. The specification
2144 requires that only the elements *below* the current
2145 index have been copied before the throw. */
2147 for (i = len - 1; i >= 0; i--) {
2150 o = array_objectarray_element_get(oas, srcStart + i);
2152 if (!builtin_canstore(oad, o))
2155 array_objectarray_element_set(oad, destStart + i, o);
2162 /* builtin_nanotime ************************************************************
2164 Return the current time in nanoseconds.
2166 *******************************************************************************/
2168 s8 builtin_nanotime(void)
2173 if (gettimeofday(&tv, NULL) == -1)
2174 vm_abort("gettimeofday failed: %s", strerror(errno));
2176 usecs = (s8) tv.tv_sec * (1000 * 1000) + (s8) tv.tv_usec;
2178 return usecs * 1000;
2182 /* builtin_currenttimemillis ***************************************************
2184 Return the current time in milliseconds.
2186 *******************************************************************************/
2188 s8 builtin_currenttimemillis(void)
2192 msecs = builtin_nanotime() / 1000 / 1000;
2198 /* builtin_clone ***************************************************************
2200 Function for cloning objects or arrays.
2202 NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code.
2204 *******************************************************************************/
2206 java_handle_t *builtin_clone(void *env, java_handle_t *o)
2208 arraydescriptor *ad;
2211 java_handle_t *co; /* cloned object header */
2213 /* get the array descriptor */
2215 ad = LLNI_vftbl_direct(o)->arraydesc;
2217 /* we are cloning an array */
2220 size = ad->dataoffset + ad->componentsize * LLNI_array_size(o);
2222 co = heap_alloc(size, (ad->arraytype == ARRAYTYPE_OBJECT), NULL, true);
2227 #if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
2228 /* XXX this is only a dirty hack to make Boehm work with handles */
2230 co = LLNI_WRAP((java_object_t *) co);
2233 LLNI_CRITICAL_START;
2235 MCOPY(LLNI_DIRECT(co), LLNI_DIRECT(o), u1, size);
2237 #if defined(ENABLE_GC_CACAO)
2238 heap_init_objectheader(LLNI_DIRECT(co), size);
2241 #if defined(ENABLE_THREADS)
2242 lock_init_object_lock(LLNI_DIRECT(co));
2250 /* we are cloning a non-array */
2252 if (!builtin_instanceof(o, class_java_lang_Cloneable)) {
2253 exceptions_throw_clonenotsupportedexception();
2257 /* get the class of the object */
2259 LLNI_class_get(o, c);
2261 /* create new object */
2263 co = builtin_new(c);
2268 LLNI_CRITICAL_START;
2270 MCOPY(LLNI_DIRECT(co), LLNI_DIRECT(o), u1, c->instancesize);
2272 #if defined(ENABLE_GC_CACAO)
2273 heap_init_objectheader(LLNI_DIRECT(co), c->instancesize);
2276 #if defined(ENABLE_THREADS)
2277 lock_init_object_lock(LLNI_DIRECT(co));
2286 #if defined(ENABLE_CYCLES_STATS)
2287 void builtin_print_cycles_stats(FILE *file)
2289 fprintf(file,"builtin cylce count statistics:\n");
2291 CYCLES_STATS_PRINT_OVERHEAD(builtin_overhead,file);
2292 CYCLES_STATS_PRINT(builtin_new ,file);
2296 #endif /* defined(ENABLE_CYCLES_STATS) */
2299 #if defined(ENABLE_VMLOG)
2301 #include <vmlog_cacao.c>
2306 * These are local overrides for various environment variables in Emacs.
2307 * Please do not remove this and leave it at the end of the file, where
2308 * Emacs will automagically detect them.
2309 * ---------------------------------------------------------------------
2312 * indent-tabs-mode: t
2316 * vim:noexpandtab:sw=4:ts=4: