From: Stefan Ring Date: Wed, 17 Sep 2008 11:39:41 +0000 (+0200) Subject: Merged trunk and subtype. X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=cacao.git;a=commitdiff_plain;h=219e4a46e3d127d3c0883ee2e8635b4fe3c94d60 Merged trunk and subtype. --HG-- branch : subtype-trunk rename : src/vm/builtin.c => src/vm/jit/builtin.cpp rename : src/vm/jit/emit-common.c => src/vm/jit/emit-common.cpp rename : src/vm/jit/emit-common.h => src/vm/jit/emit-common.hpp rename : src/vmcore/linker.c => src/vm/linker.c rename : src/vmcore/linker.h => src/vm/linker.h --- 219e4a46e3d127d3c0883ee2e8635b4fe3c94d60 diff --cc src/vm/jit/builtin.cpp index 5dbd12a75,000000000..cd53c03dd mode 100644,000000..100644 --- a/src/vm/jit/builtin.cpp +++ b/src/vm/jit/builtin.cpp @@@ -1,2422 -1,0 +1,2418 @@@ +/* src/vm/jit/builtin.cpp - functions for unsupported operations + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contains C functions for JavaVM Instructions that cannot be + translated to machine language directly. Consequently, the + generated machine code for these instructions contains function + calls instead of machine instructions, using the C calling + convention. + +*/ + + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "vm/types.h" + +#include "arch.h" +#include "md-abi.h" + +#include "fdlibm/fdlibm.h" +#if defined(__CYGWIN__) && defined(Bias) +# undef Bias +#endif + +#include "mm/gc.hpp" +#include "mm/memory.h" + +#include "native/llni.h" + +#include "threads/lock-common.h" +#include "threads/mutex.hpp" +#include "threads/thread.hpp" + +#include "toolbox/logging.h" +#include "toolbox/util.h" + +#include "vm/array.h" +#include "vm/jit/builtin.hpp" +#include "vm/class.h" +#include "vm/cycles-stats.h" +#include "vm/exceptions.hpp" +#include "vm/global.h" +#include "vm/globals.hpp" +#include "vm/initialize.h" +#include "vm/linker.h" +#include "vm/loader.hpp" +#include "vm/options.h" +#include "vm/primitive.hpp" +#include "vm/rt-timing.h" +#include "vm/string.hpp" + +#include "vm/jit/asmpart.h" +#include "vm/jit/stubs.hpp" +#include "vm/jit/trace.hpp" + +#if defined(ENABLE_VMLOG) +#include +#endif + + +/* include builtin tables *****************************************************/ + +#include "vm/jit/builtintable.inc" + + +CYCLES_STATS_DECLARE(builtin_new ,100,5) +CYCLES_STATS_DECLARE(builtin_overhead , 80,1) + + +/*============================================================================*/ +/* BUILTIN TABLE MANAGEMENT FUNCTIONS */ +/*============================================================================*/ + +/* builtintable_init *********************************************************** + + Parse the descriptors of builtin functions and create the parsed + descriptors. + +*******************************************************************************/ + +static bool builtintable_init(void) +{ + descriptor_pool *descpool; + builtintable_entry *bte; + methodinfo *m; + + // Create new dump memory area. + DumpMemoryArea dma; + + /* create a new descriptor pool */ + + descpool = descriptor_pool_new(class_java_lang_Object); + + /* add some entries we need */ + + if (!descriptor_pool_add_class(descpool, utf_java_lang_Object)) + return false; + + if (!descriptor_pool_add_class(descpool, utf_java_lang_Class)) + return false; + + /* first add all descriptors to the pool */ + + for (bte = builtintable_internal; bte->fp != NULL; bte++) { + bte->name = utf_new_char(bte->cname); + bte->descriptor = utf_new_char(bte->cdescriptor); + + if (!descriptor_pool_add(descpool, bte->descriptor, NULL)) + return false; + } + + for (bte = builtintable_automatic; bte->fp != NULL; bte++) { + bte->descriptor = utf_new_char(bte->cdescriptor); + + if (!descriptor_pool_add(descpool, bte->descriptor, NULL)) + return false; + } + + for (bte = builtintable_function; bte->fp != NULL; bte++) { + bte->classname = utf_new_char(bte->cclassname); + bte->name = utf_new_char(bte->cname); + bte->descriptor = utf_new_char(bte->cdescriptor); + + if (!descriptor_pool_add(descpool, bte->descriptor, NULL)) + return false; + } + + /* create the class reference table */ + + (void) descriptor_pool_create_classrefs(descpool, NULL); + + /* allocate space for the parsed descriptors */ + + descriptor_pool_alloc_parsed_descriptors(descpool); + + /* Now parse all descriptors. NOTE: builtin-functions are treated + like static methods (no `this' pointer). */ + + for (bte = builtintable_internal; bte->fp != NULL; bte++) { + bte->md = + descriptor_pool_parse_method_descriptor(descpool, + bte->descriptor, + ACC_STATIC | ACC_METHOD_BUILTIN, + NULL); + + /* generate a builtin stub if we need one */ + + if (bte->flags & BUILTINTABLE_FLAG_STUB) { + m = method_new_builtin(bte); + BuiltinStub::generate(m, bte); + } + } + + for (bte = builtintable_automatic; bte->fp != NULL; bte++) { + bte->md = + descriptor_pool_parse_method_descriptor(descpool, + bte->descriptor, + ACC_STATIC | ACC_METHOD_BUILTIN, + NULL); + + /* no stubs should be needed for this table */ + + assert(!bte->flags & BUILTINTABLE_FLAG_STUB); + } + + for (bte = builtintable_function; bte->fp != NULL; bte++) { + bte->md = + descriptor_pool_parse_method_descriptor(descpool, + bte->descriptor, + ACC_STATIC | ACC_METHOD_BUILTIN, + NULL); + + /* generate a builtin stub if we need one */ + + if (bte->flags & BUILTINTABLE_FLAG_STUB) { + m = method_new_builtin(bte); + BuiltinStub::generate(m, bte); + } + } + + return true; +} + + +/* builtintable_comparator ***************************************************** + + qsort comparator for the automatic builtin table. + +*******************************************************************************/ + +static int builtintable_comparator(const void *a, const void *b) +{ + builtintable_entry *bte1; + builtintable_entry *bte2; + + bte1 = (builtintable_entry *) a; + bte2 = (builtintable_entry *) b; + + return (bte1->opcode < bte2->opcode) ? -1 : (bte1->opcode > bte2->opcode); +} + + +/* builtintable_sort_automatic ************************************************* + + Sorts the automatic builtin table. + +*******************************************************************************/ + +static void builtintable_sort_automatic(void) +{ + s4 entries; + + /* calculate table size statically (`- 1' comment see builtintable.inc) */ + + entries = sizeof(builtintable_automatic) / sizeof(builtintable_entry) - 1; + + qsort(builtintable_automatic, entries, sizeof(builtintable_entry), + builtintable_comparator); +} + + +/* builtin_init **************************************************************** + + Initialize the global table of builtin functions. + +*******************************************************************************/ + +bool builtin_init(void) +{ + TRACESUBSYSTEMINITIALIZATION("builtin_init"); + + /* initialize the builtin tables */ + + if (!builtintable_init()) + return false; + + /* sort builtin tables */ + + builtintable_sort_automatic(); + + return true; +} + + +/* builtintable_get_internal *************************************************** + + Finds an entry in the builtintable for internal functions and + returns the a pointer to the structure. + +*******************************************************************************/ + +builtintable_entry *builtintable_get_internal(functionptr fp) +{ + builtintable_entry *bte; + + for (bte = builtintable_internal; bte->fp != NULL; bte++) { + if (bte->fp == fp) + return bte; + } + + return NULL; +} + + +/* builtintable_get_automatic ************************************************** + + Finds an entry in the builtintable for functions which are replaced + automatically and returns the a pointer to the structure. + +*******************************************************************************/ + +builtintable_entry *builtintable_get_automatic(s4 opcode) +{ + builtintable_entry *first; + builtintable_entry *last; + builtintable_entry *middle; + s4 half; + s4 entries; + + /* calculate table size statically (`- 1' comment see builtintable.inc) */ + + entries = sizeof(builtintable_automatic) / sizeof(builtintable_entry) - 1; + + first = builtintable_automatic; + last = builtintable_automatic + entries; + + while (entries > 0) { + half = entries / 2; + middle = first + half; + + if (middle->opcode < opcode) { + first = middle + 1; + entries -= half + 1; + } + else + entries = half; + } + + return (first != last ? first : NULL); +} + + +/* builtintable_replace_function *********************************************** + + XXX + +*******************************************************************************/ + +#if defined(ENABLE_JIT) +bool builtintable_replace_function(void *iptr_) +{ + constant_FMIref *mr; + builtintable_entry *bte; + instruction *iptr; + + iptr = (instruction *) iptr_; /* twisti will kill me ;) */ + + /* get name and descriptor of the function */ + + switch (iptr->opc) { + case ICMD_INVOKESTATIC: + /* The instruction MUST be resolved, otherwise we run into + lazy loading troubles. Anyway, we should/can only replace + very VM-close functions. */ + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) + return false; + + mr = iptr->sx.s23.s3.fmiref; + break; + + default: + return false; + } + + /* search the function table */ + + for (bte = builtintable_function; bte->fp != NULL; bte++) { + if ((METHODREF_CLASSNAME(mr) == bte->classname) && + (mr->name == bte->name) && + (mr->descriptor == bte->descriptor)) { + + /* set the values in the instruction */ + + iptr->opc = bte->opcode; + iptr->sx.s23.s3.bte = bte; + + if (bte->flags & BUILTINTABLE_FLAG_EXCEPTION) + iptr->flags.bits |= INS_FLAG_CHECK; + else + iptr->flags.bits &= ~INS_FLAG_CHECK; + + return true; + } + } + + return false; +} +#endif /* defined(ENABLE_JIT) */ + + +/*============================================================================*/ +/* INTERNAL BUILTIN FUNCTIONS */ +/*============================================================================*/ + +/* builtin_instanceof ********************************************************** + + Checks if an object is an instance of some given class (or subclass + of that class). If class is an interface, checks if the interface + is implemented. + + RETURN VALUE: + 1......o is an instance of class or implements the interface + 0......otherwise or if o == NULL + + NOTE: This builtin can be called from NATIVE code only. + +*******************************************************************************/ + +bool builtin_instanceof(java_handle_t *o, classinfo *c) +{ + classinfo *oc; + + if (o == NULL) + return 0; + + LLNI_class_get(o, oc); + + return class_isanysubclass(oc, c); +} + + + +/* builtin_checkcast *********************************************************** + + The same as builtin_instanceof but with the exception + that 1 is returned when (o == NULL). + + NOTE: This builtin can be called from NATIVE code only. + +*******************************************************************************/ + +bool builtin_checkcast(java_handle_t *o, classinfo *c) +{ + classinfo *oc; + + if (o == NULL) + return 1; + + LLNI_class_get(o, oc); + + if (class_isanysubclass(oc, c)) + return 1; + + return 0; +} + + +/* builtin_descriptorscompatible *********************************************** + + Checks if two array type descriptors are assignment compatible. + + RETURN VALUE: + 1......target = desc is possible + 0......otherwise + +*******************************************************************************/ + +static bool builtin_descriptorscompatible(arraydescriptor *desc, arraydescriptor *target) +{ + if (desc == target) + return 1; + + if (desc->arraytype != target->arraytype) + return 0; + + if (desc->arraytype != ARRAYTYPE_OBJECT) + return 1; + + /* {both arrays are arrays of references} */ + + if (desc->dimension == target->dimension) { + if (!desc->elementvftbl) + return 0; + /* an array which contains elements of interface types is + allowed to be casted to Object (JOWENN)*/ + + if ((desc->elementvftbl->baseval < 0) && + (target->elementvftbl->baseval == 1)) + return 1; + + return class_isanysubclass(desc->elementvftbl->clazz, + target->elementvftbl->clazz); + } + + if (desc->dimension < target->dimension) + return 0; + + /* {desc has higher dimension than target} */ + + return class_isanysubclass(pseudo_class_Arraystub, + target->elementvftbl->clazz); +} + + +/* builtin_arraycheckcast ****************************************************** + + Checks if an object is really a subtype of the requested array + type. The object has to be an array to begin with. For simple + arrays (int, short, double, etc.) the types have to match exactly. + For arrays of objects, the type of elements in the array has to be + a subtype (or the same type) of the requested element type. For + arrays of arrays (which in turn can again be arrays of arrays), the + types at the lowest level have to satisfy the corresponding sub + class relation. + + NOTE: This is a FAST builtin and can be called from JIT code only. + +*******************************************************************************/ + +bool builtin_fast_arraycheckcast(java_object_t *o, classinfo *targetclass) +{ + arraydescriptor *desc; + + if (o == NULL) + return 1; + + desc = o->vftbl->arraydesc; + + if (desc == NULL) + return 0; + + return builtin_descriptorscompatible(desc, targetclass->vftbl->arraydesc); +} + + +/* builtin_fast_arrayinstanceof ************************************************ + + NOTE: This is a FAST builtin and can be called from JIT code only. + +*******************************************************************************/ + +bool builtin_fast_arrayinstanceof(java_object_t *o, classinfo *targetclass) +{ + if (o == NULL) + return 0; + + return builtin_fast_arraycheckcast(o, targetclass); +} + + +/* builtin_arrayinstanceof ***************************************************** + + NOTE: This builtin can be called from NATIVE code only. + +*******************************************************************************/ + +bool builtin_arrayinstanceof(java_handle_t *h, classinfo *targetclass) +{ + bool result; + + LLNI_CRITICAL_START; + + result = builtin_fast_arrayinstanceof(LLNI_UNWRAP(h), targetclass); + + LLNI_CRITICAL_END; + + return result; +} + + +/* builtin_throw_exception ***************************************************** + + Sets the exception pointer with the thrown exception and prints some + debugging information. + + NOTE: This is a FAST builtin and can be called from JIT code, + or from asm_vm_call_method. + +*******************************************************************************/ + +void *builtin_throw_exception(java_object_t *xptr) +{ +#if !defined(NDEBUG) + /* print exception trace */ + + if (opt_TraceExceptions) + trace_exception_builtin(xptr); +#endif /* !defined(NDEBUG) */ + + /* actually set the exception */ + + exceptions_set_exception(LLNI_QUICKWRAP(xptr)); + + /* Return a NULL pointer. This is required for vm_call_method to + check for an exception. This is for convenience. */ + + return NULL; +} + + +/* builtin_retrieve_exception ************************************************** + + Gets and clears the exception pointer of the current thread. + + RETURN VALUE: + the exception object, or NULL if no exception was thrown. + + NOTE: This is a FAST builtin and can be called from JIT code, + or from the signal handlers. + +*******************************************************************************/ + +java_object_t *builtin_retrieve_exception(void) +{ + java_handle_t *h; + java_object_t *o; + + /* actually get and clear the exception */ + + h = exceptions_get_and_clear_exception(); + o = LLNI_UNWRAP(h); + + return o; +} + + +/* builtin_canstore ************************************************************ + + Checks, if an object can be stored in an array. + + RETURN VALUE: + 1......possible + 0......otherwise (throws an ArrayStoreException) + + NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code. + +*******************************************************************************/ + +bool builtin_canstore(java_handle_objectarray_t *oa, java_handle_t *o) +{ + bool result; + + LLNI_CRITICAL_START; + + result = builtin_fast_canstore(LLNI_DIRECT(oa), LLNI_UNWRAP(o)); + + LLNI_CRITICAL_END; + + /* if not possible, throw an exception */ + + if (result == 0) + exceptions_throw_arraystoreexception(); + + return result; +} + + +/* builtin_fast_canstore ******************************************************* + + Checks, if an object can be stored in an array. + + RETURN VALUE: + 1......possible + 0......otherwise (no exception thrown!) + + NOTE: This is a FAST builtin and can be called from JIT code only. + +*******************************************************************************/ + +bool fast_subtype_check(struct _vftbl *s, struct _vftbl *t) +{ - int i; + if (s->subtype_display[t->subtype_depth] == t) + return true; + if (t->subtype_offset != OFFSET(vftbl_t, subtype_display[DISPLAY_SIZE])) + return false; - for (i=0; isubtype_overflow_length; i++) - if (s->subtype_overflow[i] == t) - return true; - return false; ++ return s->subtype_depth >= t->subtype_depth && s->subtype_overflow[t->subtype_depth - DISPLAY_SIZE] == t; +} + +bool builtin_fast_canstore(java_objectarray_t *oa, java_object_t *o) +{ + arraydescriptor *desc; + arraydescriptor *valuedesc; + vftbl_t *componentvftbl; + vftbl_t *valuevftbl; + int32_t baseval; + uint32_t diffval; + bool result; + + if (o == NULL) + return 1; + + /* The following is guaranteed (by verifier checks): + * + * *) oa->...vftbl->arraydesc != NULL + * *) oa->...vftbl->arraydesc->componentvftbl != NULL + * *) o->vftbl is not an interface vftbl + */ + + desc = oa->header.objheader.vftbl->arraydesc; + componentvftbl = desc->componentvftbl; + valuevftbl = o->vftbl; + valuedesc = valuevftbl->arraydesc; + + if ((desc->dimension - 1) == 0) { + /* {oa is a one-dimensional array} */ + /* {oa is an array of references} */ + + if (valuevftbl == componentvftbl) + return 1; + + baseval = componentvftbl->baseval; + + if (baseval <= 0) { + /* an array of interface references */ + + result = ((valuevftbl->interfacetablelength > -baseval) && + (valuevftbl->interfacetable[baseval] != NULL)); + } + else { + result = fast_subtype_check(valuevftbl, componentvftbl); + } + } + else if (valuedesc == NULL) { + /* {oa has dimension > 1} */ + /* {componentvftbl->arraydesc != NULL} */ + + /* check if o is an array */ + + return 0; + } + else { + /* {o is an array} */ + + result = builtin_descriptorscompatible(valuedesc, componentvftbl->arraydesc); + } + + /* return result */ + + return result; +} + + +/* This is an optimized version where a is guaranteed to be one-dimensional */ +bool builtin_fast_canstore_onedim(java_objectarray_t *a, java_object_t *o) +{ + arraydescriptor *desc; + vftbl_t *elementvftbl; + vftbl_t *valuevftbl; + int32_t baseval; + uint32_t diffval; + bool result; + + if (o == NULL) + return 1; + + /* The following is guaranteed (by verifier checks): + * + * *) a->...vftbl->arraydesc != NULL + * *) a->...vftbl->arraydesc->elementvftbl != NULL + * *) a->...vftbl->arraydesc->dimension == 1 + * *) o->vftbl is not an interface vftbl + */ + + desc = a->header.objheader.vftbl->arraydesc; + elementvftbl = desc->elementvftbl; + valuevftbl = o->vftbl; + + /* {a is a one-dimensional array} */ + + if (valuevftbl == elementvftbl) + return 1; + + baseval = elementvftbl->baseval; + + if (baseval <= 0) { + /* an array of interface references */ + result = ((valuevftbl->interfacetablelength > -baseval) && + (valuevftbl->interfacetable[baseval] != NULL)); + } + else { + result = fast_subtype_check(valuevftbl, elementvftbl); + } + + return result; +} + + +/* This is an optimized version where a is guaranteed to be a + * one-dimensional array of a class type */ +bool builtin_fast_canstore_onedim_class(java_objectarray_t *a, java_object_t *o) +{ + vftbl_t *elementvftbl; + vftbl_t *valuevftbl; + uint32_t diffval; + bool result; + + if (o == NULL) + return 1; + + /* The following is guaranteed (by verifier checks): + * + * *) a->...vftbl->arraydesc != NULL + * *) a->...vftbl->arraydesc->elementvftbl != NULL + * *) a->...vftbl->arraydesc->elementvftbl is not an interface vftbl + * *) a->...vftbl->arraydesc->dimension == 1 + * *) o->vftbl is not an interface vftbl + */ + + elementvftbl = a->header.objheader.vftbl->arraydesc->elementvftbl; + valuevftbl = o->vftbl; + + /* {a is a one-dimensional array} */ + + if (valuevftbl == elementvftbl) + return 1; + + result = fast_subtype_check(valuevftbl, elementvftbl); + + return result; +} + + +/* builtin_new ***************************************************************** + + Creates a new instance of class c on the heap. + + RETURN VALUE: + pointer to the object, or NULL if no memory is available + + NOTE: This builtin can be called from NATIVE code only. + +*******************************************************************************/ + +java_handle_t *builtin_new(classinfo *c) +{ + java_handle_t *o; +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_end; +#endif +#if defined(ENABLE_CYCLES_STATS) + u8 cycles_start, cycles_end; +#endif + + RT_TIMING_GET_TIME(time_start); + CYCLES_STATS_GET(cycles_start); + + /* is the class loaded */ + + assert(c->state & CLASS_LOADED); + + /* check if we can instantiate this class */ + + if (c->flags & ACC_ABSTRACT) { + exceptions_throw_instantiationerror(c); + return NULL; + } + + /* is the class linked */ + + if (!(c->state & CLASS_LINKED)) + if (!link_class(c)) + return NULL; + + if (!(c->state & CLASS_INITIALIZED)) { +#if !defined(NDEBUG) + if (initverbose) + log_message_class("Initialize class (from builtin_new): ", c); +#endif + + if (!initialize_class(c)) + return NULL; + } + + o = (java_handle_t*) heap_alloc(c->instancesize, c->flags & ACC_CLASS_HAS_POINTERS, + c->finalizer, true); + + if (!o) + return NULL; + +#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES) + /* XXX this is only a dirty hack to make Boehm work with handles */ + + o = LLNI_WRAP((java_object_t *) o); +#endif + + LLNI_vftbl_direct(o) = c->vftbl; + +#if defined(ENABLE_THREADS) + lock_init_object_lock(LLNI_DIRECT(o)); +#endif + + CYCLES_STATS_GET(cycles_end); + RT_TIMING_GET_TIME(time_end); + + CYCLES_STATS_COUNT(builtin_new,cycles_end - cycles_start); + RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_OBJECT); + + return o; +} + +#if defined(ENABLE_ESCAPE_REASON) +java_handle_t *builtin_escape_reason_new(classinfo *c) { + print_escape_reasons(); + return builtin_java_new(c); +} +#endif + +#if defined(ENABLE_TLH) +java_handle_t *builtin_tlh_new(classinfo *c) +{ + java_handle_t *o; +# if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_end; +# endif +# if defined(ENABLE_CYCLES_STATS) + u8 cycles_start, cycles_end; +# endif + + RT_TIMING_GET_TIME(time_start); + CYCLES_STATS_GET(cycles_start); + + /* is the class loaded */ + + assert(c->state & CLASS_LOADED); + + /* check if we can instantiate this class */ + + if (c->flags & ACC_ABSTRACT) { + exceptions_throw_instantiationerror(c); + return NULL; + } + + /* is the class linked */ + + if (!(c->state & CLASS_LINKED)) + if (!link_class(c)) + return NULL; + + if (!(c->state & CLASS_INITIALIZED)) { +# if !defined(NDEBUG) + if (initverbose) + log_message_class("Initialize class (from builtin_new): ", c); +# endif + + if (!initialize_class(c)) + return NULL; + } + + /* + o = tlh_alloc(&(THREADOBJECT->tlh), c->instancesize); + */ + o = NULL; + + if (o == NULL) { + o = (java_handle_t*) heap_alloc(c->instancesize, c->flags & ACC_CLASS_HAS_POINTERS, + c->finalizer, true); + } + + if (!o) + return NULL; + +# if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES) + /* XXX this is only a dirty hack to make Boehm work with handles */ + + o = LLNI_WRAP((java_object_t *) o); +# endif + + LLNI_vftbl_direct(o) = c->vftbl; + +# if defined(ENABLE_THREADS) + lock_init_object_lock(LLNI_DIRECT(o)); +# endif + + CYCLES_STATS_GET(cycles_end); + RT_TIMING_GET_TIME(time_end); + +/* + CYCLES_STATS_COUNT(builtin_new,cycles_end - cycles_start); + RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_OBJECT); +*/ + + return o; +} +#endif + + +/* builtin_java_new ************************************************************ + + NOTE: This is a SLOW builtin and can be called from JIT code only. + +*******************************************************************************/ + +java_handle_t *builtin_java_new(java_handle_t *clazz) +{ + return builtin_new(LLNI_classinfo_unwrap(clazz)); +} + + +/* builtin_fast_new ************************************************************ + + Creates a new instance of class c on the heap. + + RETURN VALUE: + pointer to the object, or NULL if no fast return + is possible for any reason. + + NOTE: This is a FAST builtin and can be called from JIT code only. + +*******************************************************************************/ + +java_object_t *builtin_fast_new(classinfo *c) +{ + java_object_t *o; +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_end; +#endif +#if defined(ENABLE_CYCLES_STATS) + u8 cycles_start, cycles_end; +#endif + + RT_TIMING_GET_TIME(time_start); + CYCLES_STATS_GET(cycles_start); + + /* is the class loaded */ + + assert(c->state & CLASS_LOADED); + + /* check if we can instantiate this class */ + + if (c->flags & ACC_ABSTRACT) + return NULL; + + /* is the class linked */ + + if (!(c->state & CLASS_LINKED)) + return NULL; + + if (!(c->state & CLASS_INITIALIZED)) + return NULL; + + o = (java_handle_t*) heap_alloc(c->instancesize, c->flags & ACC_CLASS_HAS_POINTERS, + c->finalizer, false); + + if (!o) + return NULL; + + o->vftbl = c->vftbl; + +#if defined(ENABLE_THREADS) + lock_init_object_lock(o); +#endif + + CYCLES_STATS_GET(cycles_end); + RT_TIMING_GET_TIME(time_end); + + CYCLES_STATS_COUNT(builtin_new,cycles_end - cycles_start); + RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_OBJECT); + + return o; +} + + +/* builtin_newarray ************************************************************ + + Creates an array with the given vftbl on the heap. This function + takes as class argument an array class. + + RETURN VALUE: + pointer to the array or NULL if no memory is available + + NOTE: This builtin can be called from NATIVE code only. + +*******************************************************************************/ + +java_handle_t *builtin_newarray(int32_t size, classinfo *arrayclass) +{ + arraydescriptor *desc; + s4 dataoffset; + s4 componentsize; + s4 actualsize; + java_handle_t *a; +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_end; +#endif + + RT_TIMING_GET_TIME(time_start); + + desc = arrayclass->vftbl->arraydesc; + dataoffset = desc->dataoffset; + componentsize = desc->componentsize; + + if (size < 0) { + exceptions_throw_negativearraysizeexception(); + return NULL; + } + + actualsize = dataoffset + size * componentsize; + + /* check for overflow */ + + if (((u4) actualsize) < ((u4) size)) { + exceptions_throw_outofmemoryerror(); + return NULL; + } + + a = (java_handle_t*) heap_alloc(actualsize, (desc->arraytype == ARRAYTYPE_OBJECT), NULL, true); + + if (a == NULL) + return NULL; + +#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES) + /* XXX this is only a dirty hack to make Boehm work with handles */ + + a = LLNI_WRAP((java_object_t *) a); +#endif + + LLNI_vftbl_direct(a) = arrayclass->vftbl; + +#if defined(ENABLE_THREADS) + lock_init_object_lock(LLNI_DIRECT(a)); +#endif + + LLNI_array_size(a) = size; + + RT_TIMING_GET_TIME(time_end); + RT_TIMING_TIME_DIFF(time_start, time_end, RT_TIMING_NEW_ARRAY); + + return a; +} + + +/* builtin_java_newarray ******************************************************* + + NOTE: This is a SLOW builtin and can be called from JIT code only. + +*******************************************************************************/ + +java_handle_t *builtin_java_newarray(int32_t size, java_handle_t *arrayclazz) +{ + return builtin_newarray(size, LLNI_classinfo_unwrap(arrayclazz)); +} + + +/* builtin_anewarray *********************************************************** + + Creates an array of references to the given class type on the heap. + + RETURN VALUE: + pointer to the array or NULL if no memory is + available + + NOTE: This builtin can be called from NATIVE code only. + +*******************************************************************************/ + +java_handle_objectarray_t *builtin_anewarray(int32_t size, classinfo *componentclass) +{ + classinfo *arrayclass; + + /* is class loaded */ + + assert(componentclass->state & CLASS_LOADED); + + /* is class linked */ + + if (!(componentclass->state & CLASS_LINKED)) + if (!link_class(componentclass)) + return NULL; + + arrayclass = class_array_of(componentclass, true); + + if (!arrayclass) + return NULL; + + return (java_handle_objectarray_t *) builtin_newarray(size, arrayclass); +} + + +/* builtin_newarray_type **************************************************** + + Creates an array of [type]s on the heap. + + RETURN VALUE: + pointer to the array or NULL if no memory is available + + NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code. + +*******************************************************************************/ + +#define BUILTIN_NEWARRAY_TYPE(type, arraytype) \ +java_handle_##type##array_t *builtin_newarray_##type(int32_t size) \ +{ \ + return (java_handle_##type##array_t *) \ + builtin_newarray(size, primitivetype_table[arraytype].arrayclass); \ +} + +BUILTIN_NEWARRAY_TYPE(boolean, ARRAYTYPE_BOOLEAN) +BUILTIN_NEWARRAY_TYPE(byte, ARRAYTYPE_BYTE) +BUILTIN_NEWARRAY_TYPE(char, ARRAYTYPE_CHAR) +BUILTIN_NEWARRAY_TYPE(short, ARRAYTYPE_SHORT) +BUILTIN_NEWARRAY_TYPE(int, ARRAYTYPE_INT) +BUILTIN_NEWARRAY_TYPE(long, ARRAYTYPE_LONG) +BUILTIN_NEWARRAY_TYPE(float, ARRAYTYPE_FLOAT) +BUILTIN_NEWARRAY_TYPE(double, ARRAYTYPE_DOUBLE) + + +/* builtin_multianewarray_intern *********************************************** + + Creates a multi-dimensional array on the heap. The dimensions are + passed in an array of longs. + + ARGUMENTS: + n.............number of dimensions to create + arrayclass....the array class + dims..........array containing the size of each dimension to create + + RETURN VALUE: + pointer to the array or NULL if no memory is available + +******************************************************************************/ + +static java_handle_t *builtin_multianewarray_intern(int n, + classinfo *arrayclass, + long *dims) +{ + s4 size; + java_handle_t *a; + classinfo *componentclass; + s4 i; + + /* create this dimension */ + + size = (s4) dims[0]; + a = builtin_newarray(size, arrayclass); + + if (!a) + return NULL; + + /* if this is the last dimension return */ + + if (!--n) + return a; + + /* get the class of the components to create */ + + componentclass = arrayclass->vftbl->arraydesc->componentvftbl->clazz; + + /* The verifier guarantees that the dimension count is in the range. */ + + /* create the component arrays */ + + for (i = 0; i < size; i++) { + java_handle_t *ea = +#if defined(__MIPS__) && (SIZEOF_VOID_P == 4) + /* we save an s4 to a s8 slot, 8-byte aligned */ + + builtin_multianewarray_intern(n, componentclass, dims + 2); +#else + builtin_multianewarray_intern(n, componentclass, dims + 1); +#endif + + if (!ea) + return NULL; + + array_objectarray_element_set((java_handle_objectarray_t *) a, i, ea); + } + + return a; +} + + +/* builtin_multianewarray ****************************************************** + + Wrapper for builtin_multianewarray_intern which checks all + dimensions before we start allocating. + + NOTE: This is a SLOW builtin and can be called from JIT code only. + +******************************************************************************/ + +java_handle_objectarray_t *builtin_multianewarray(int n, + java_handle_t *arrayclazz, + long *dims) +{ + classinfo *c; + s4 i; + s4 size; + + /* check all dimensions before doing anything */ + + for (i = 0; i < n; i++) { +#if defined(__MIPS__) && (SIZEOF_VOID_P == 4) + /* we save an s4 to a s8 slot, 8-byte aligned */ + size = (s4) dims[i * 2]; +#else + size = (s4) dims[i]; +#endif + + if (size < 0) { + exceptions_throw_negativearraysizeexception(); + return NULL; + } + } + + c = LLNI_classinfo_unwrap(arrayclazz); + + /* now call the real function */ + + return (java_handle_objectarray_t *) + builtin_multianewarray_intern(n, c, dims); +} + + +/* builtin_verbosecall_enter *************************************************** + + Print method call with arguments for -verbose:call. + + XXX: Remove mew once all archs use the new tracer! + +*******************************************************************************/ + +#if !defined(NDEBUG) +#ifdef TRACE_ARGS_NUM +void builtin_verbosecall_enter(s8 a0, s8 a1, +# if TRACE_ARGS_NUM >= 4 + s8 a2, s8 a3, +# endif +# if TRACE_ARGS_NUM >= 6 + s8 a4, s8 a5, +# endif +# if TRACE_ARGS_NUM == 8 + s8 a6, s8 a7, +# endif + methodinfo *m) +{ + log_text("builtin_verbosecall_enter: Do not call me anymore!"); +} +#endif +#endif /* !defined(NDEBUG) */ + + +/* builtin_verbosecall_exit **************************************************** + + Print method exit for -verbose:call. + + XXX: Remove mew once all archs use the new tracer! + +*******************************************************************************/ + +#if !defined(NDEBUG) +void builtin_verbosecall_exit(s8 l, double d, float f, methodinfo *m) +{ + log_text("builtin_verbosecall_exit: Do not call me anymore!"); +} +#endif /* !defined(NDEBUG) */ + + +/*============================================================================*/ +/* MISCELLANEOUS MATHEMATICAL HELPER FUNCTIONS */ +/*============================================================================*/ + +/*********** Functions for integer divisions ***************************** + + On some systems (eg. DEC ALPHA), integer division is not supported by the + CPU. These helper functions implement the missing functionality. + +******************************************************************************/ + +#if !SUPPORT_DIVISION || defined(DISABLE_GC) +s4 builtin_idiv(s4 a, s4 b) +{ + s4 c; + + c = a / b; + + return c; +} + +s4 builtin_irem(s4 a, s4 b) +{ + s4 c; + + c = a % b; + + return c; +} +#endif /* !SUPPORT_DIVISION || defined(DISABLE_GC) */ + + +/* functions for long arithmetics ********************************************** + + On systems where 64 bit Integers are not supported by the CPU, + these functions are needed. + +******************************************************************************/ + +#if !(SUPPORT_LONG && SUPPORT_LONG_ADD) +s8 builtin_ladd(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a + b; +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lsub(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a - b; +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lneg(s8 a) +{ + s8 c; + +#if U8_AVAILABLE + c = -a; +#else + c = builtin_i2l(0); +#endif + + return c; +} +#endif /* !(SUPPORT_LONG && SUPPORT_LONG_ADD) */ + + +#if !(SUPPORT_LONG && SUPPORT_LONG_MUL) +s8 builtin_lmul(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a * b; +#else + c = builtin_i2l(0); +#endif + + return c; +} +#endif /* !(SUPPORT_LONG && SUPPORT_LONG_MUL) */ + + +#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) || defined (DISABLE_GC) +s8 builtin_ldiv(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a / b; +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lrem(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a % b; +#else + c = builtin_i2l(0); +#endif + + return c; +} +#endif /* !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV) */ + + +#if !(SUPPORT_LONG && SUPPORT_LONG_SHIFT) +s8 builtin_lshl(s8 a, s4 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a << (b & 63); +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lshr(s8 a, s4 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a >> (b & 63); +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lushr(s8 a, s4 b) +{ + s8 c; + +#if U8_AVAILABLE + c = ((u8) a) >> (b & 63); +#else + c = builtin_i2l(0); +#endif + + return c; +} +#endif /* !(SUPPORT_LONG && SUPPORT_LONG_SHIFT) */ + + +#if !(SUPPORT_LONG && SUPPORT_LONG_LOGICAL) +s8 builtin_land(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a & b; +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lor(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a | b; +#else + c = builtin_i2l(0); +#endif + + return c; +} + +s8 builtin_lxor(s8 a, s8 b) +{ + s8 c; + +#if U8_AVAILABLE + c = a ^ b; +#else + c = builtin_i2l(0); +#endif + + return c; +} +#endif /* !(SUPPORT_LONG && SUPPORT_LONG_LOGICAL) */ + + +#if !(SUPPORT_LONG && SUPPORT_LONG_CMP) +s4 builtin_lcmp(s8 a, s8 b) +{ +#if U8_AVAILABLE + if (a < b) + return -1; + + if (a > b) + return 1; + + return 0; +#else + return 0; +#endif +} +#endif /* !(SUPPORT_LONG && SUPPORT_LONG_CMP) */ + + +/* functions for unsupported floating instructions ****************************/ + +/* used to convert FLT_xxx defines into float values */ + +static inline float intBitsToFloat(s4 i) +{ + imm_union imb; + + imb.i = i; + return imb.f; +} + + +/* used to convert DBL_xxx defines into double values */ + +static inline float longBitsToDouble(s8 l) +{ + imm_union imb; + + imb.l = l; + return imb.d; +} + + +#if !SUPPORT_FLOAT +float builtin_fadd(float a, float b) +{ + if (isnanf(a)) return intBitsToFloat(FLT_NAN); + if (isnanf(b)) return intBitsToFloat(FLT_NAN); + if (finitef(a)) { + if (finitef(b)) + return a + b; + else + return b; + } + else { + if (finitef(b)) + return a; + else { + if (copysignf(1.0, a) == copysignf(1.0, b)) + return a; + else + return intBitsToFloat(FLT_NAN); + } + } +} + + +float builtin_fsub(float a, float b) +{ + return builtin_fadd(a, builtin_fneg(b)); +} + + +float builtin_fmul(float a, float b) +{ + if (isnanf(a)) return intBitsToFloat(FLT_NAN); + if (isnanf(b)) return intBitsToFloat(FLT_NAN); + if (finitef(a)) { + if (finitef(b)) return a * b; + else { + if (a == 0) return intBitsToFloat(FLT_NAN); + else return copysignf(b, copysignf(1.0, b)*a); + } + } + else { + if (finitef(b)) { + if (b == 0) return intBitsToFloat(FLT_NAN); + else return copysignf(a, copysignf(1.0, a)*b); + } + else { + return copysignf(a, copysignf(1.0, a)*copysignf(1.0, b)); + } + } +} + + +/* builtin_ddiv **************************************************************** + + Implementation as described in VM Spec. + +*******************************************************************************/ + +float builtin_fdiv(float a, float b) +{ + if (finitef(a)) { + if (finitef(b)) { + /* If neither value1' nor value2' is NaN, the sign of the result */ + /* is positive if both values have the same sign, negative if the */ + /* values have different signs. */ + + return a / b; + + } else { + if (isnanf(b)) { + /* If either value1' or value2' is NaN, the result is NaN. */ + + return intBitsToFloat(FLT_NAN); + + } else { + /* Division of a finite value by an infinity results in a */ + /* signed zero, with the sign-producing rule just given. */ + + /* is sign equal? */ + + if (copysignf(1.0, a) == copysignf(1.0, b)) + return 0.0; + else + return -0.0; + } + } + + } else { + if (isnanf(a)) { + /* If either value1' or value2' is NaN, the result is NaN. */ + + return intBitsToFloat(FLT_NAN); + + } else if (finitef(b)) { + /* Division of an infinity by a finite value results in a signed */ + /* infinity, with the sign-producing rule just given. */ + + /* is sign equal? */ + + if (copysignf(1.0, a) == copysignf(1.0, b)) + return intBitsToFloat(FLT_POSINF); + else + return intBitsToFloat(FLT_NEGINF); + + } else { + /* Division of an infinity by an infinity results in NaN. */ + + return intBitsToFloat(FLT_NAN); + } + } +} + + +float builtin_fneg(float a) +{ + if (isnanf(a)) return a; + else { + if (finitef(a)) return -a; + else return copysignf(a, -copysignf(1.0, a)); + } +} +#endif /* !SUPPORT_FLOAT */ + + +#if !SUPPORT_FLOAT || !SUPPORT_FLOAT_CMP || defined(ENABLE_INTRP) +s4 builtin_fcmpl(float a, float b) +{ + if (isnanf(a)) + return -1; + + if (isnanf(b)) + return -1; + + if (!finitef(a) || !finitef(b)) { + a = finitef(a) ? 0 : copysignf(1.0, a); + b = finitef(b) ? 0 : copysignf(1.0, b); + } + + if (a > b) + return 1; + + if (a == b) + return 0; + + return -1; +} + + +s4 builtin_fcmpg(float a, float b) +{ + if (isnanf(a)) return 1; + if (isnanf(b)) return 1; + if (!finitef(a) || !finitef(b)) { + a = finitef(a) ? 0 : copysignf(1.0, a); + b = finitef(b) ? 0 : copysignf(1.0, b); + } + if (a > b) return 1; + if (a == b) return 0; + return -1; +} +#endif /* !SUPPORT_FLOAT || !SUPPORT_FLOAT_CMP || defined(ENABLE_INTRP) */ + + +float builtin_frem(float a, float b) +{ + return fmodf(a, b); +} + + +/* functions for unsupported double instructions ******************************/ + +#if !SUPPORT_DOUBLE +double builtin_dadd(double a, double b) +{ + if (isnan(a)) return longBitsToDouble(DBL_NAN); + if (isnan(b)) return longBitsToDouble(DBL_NAN); + if (finite(a)) { + if (finite(b)) return a + b; + else return b; + } + else { + if (finite(b)) return a; + else { + if (copysign(1.0, a)==copysign(1.0, b)) return a; + else return longBitsToDouble(DBL_NAN); + } + } +} + + +double builtin_dsub(double a, double b) +{ + return builtin_dadd(a, builtin_dneg(b)); +} + + +double builtin_dmul(double a, double b) +{ + if (isnan(a)) return longBitsToDouble(DBL_NAN); + if (isnan(b)) return longBitsToDouble(DBL_NAN); + if (finite(a)) { + if (finite(b)) return a * b; + else { + if (a == 0) return longBitsToDouble(DBL_NAN); + else return copysign(b, copysign(1.0, b) * a); + } + } + else { + if (finite(b)) { + if (b == 0) return longBitsToDouble(DBL_NAN); + else return copysign(a, copysign(1.0, a) * b); + } + else { + return copysign(a, copysign(1.0, a) * copysign(1.0, b)); + } + } +} + + +/* builtin_ddiv **************************************************************** + + Implementation as described in VM Spec. + +*******************************************************************************/ + +double builtin_ddiv(double a, double b) +{ + if (finite(a)) { + if (finite(b)) { + /* If neither value1' nor value2' is NaN, the sign of the result */ + /* is positive if both values have the same sign, negative if the */ + /* values have different signs. */ + + return a / b; + + } else { + if (isnan(b)) { + /* If either value1' or value2' is NaN, the result is NaN. */ + + return longBitsToDouble(DBL_NAN); + + } else { + /* Division of a finite value by an infinity results in a */ + /* signed zero, with the sign-producing rule just given. */ + + /* is sign equal? */ + + if (copysign(1.0, a) == copysign(1.0, b)) + return 0.0; + else + return -0.0; + } + } + + } else { + if (isnan(a)) { + /* If either value1' or value2' is NaN, the result is NaN. */ + + return longBitsToDouble(DBL_NAN); + + } else if (finite(b)) { + /* Division of an infinity by a finite value results in a signed */ + /* infinity, with the sign-producing rule just given. */ + + /* is sign equal? */ + + if (copysign(1.0, a) == copysign(1.0, b)) + return longBitsToDouble(DBL_POSINF); + else + return longBitsToDouble(DBL_NEGINF); + + } else { + /* Division of an infinity by an infinity results in NaN. */ + + return longBitsToDouble(DBL_NAN); + } + } +} + + +/* builtin_dneg **************************************************************** + + Implemented as described in VM Spec. + +*******************************************************************************/ + +double builtin_dneg(double a) +{ + if (isnan(a)) { + /* If the operand is NaN, the result is NaN (recall that NaN has no */ + /* sign). */ + + return a; + + } else { + if (finite(a)) { + /* If the operand is a zero, the result is the zero of opposite */ + /* sign. */ + + return -a; + + } else { + /* If the operand is an infinity, the result is the infinity of */ + /* opposite sign. */ + + return copysign(a, -copysign(1.0, a)); + } + } +} +#endif /* !SUPPORT_DOUBLE */ + + +#if !SUPPORT_DOUBLE || !SUPPORT_DOUBLE_CMP || defined(ENABLE_INTRP) +s4 builtin_dcmpl(double a, double b) +{ + if (isnan(a)) + return -1; + + if (isnan(b)) + return -1; + + if (!finite(a) || !finite(b)) { + a = finite(a) ? 0 : copysign(1.0, a); + b = finite(b) ? 0 : copysign(1.0, b); + } + + if (a > b) + return 1; + + if (a == b) + return 0; + + return -1; +} + + +s4 builtin_dcmpg(double a, double b) +{ + if (isnan(a)) + return 1; + + if (isnan(b)) + return 1; + + if (!finite(a) || !finite(b)) { + a = finite(a) ? 0 : copysign(1.0, a); + b = finite(b) ? 0 : copysign(1.0, b); + } + + if (a > b) + return 1; + + if (a == b) + return 0; + + return -1; +} +#endif /* !SUPPORT_DOUBLE || !SUPPORT_DOUBLE_CMP || defined(ENABLE_INTRP) */ + + +double builtin_drem(double a, double b) +{ + return fmod(a, b); +} + + +/* conversion operations ******************************************************/ + +#if 0 +s8 builtin_i2l(s4 i) +{ +#if U8_AVAILABLE + return i; +#else + s8 v; + v.high = 0; + v.low = i; + return v; +#endif +} + +s4 builtin_l2i(s8 l) +{ +#if U8_AVAILABLE + return (s4) l; +#else + return l.low; +#endif +} +#endif + + +#if !(SUPPORT_FLOAT && SUPPORT_I2F) +float builtin_i2f(s4 a) +{ + float f = (float) a; + return f; +} +#endif /* !(SUPPORT_FLOAT && SUPPORT_I2F) */ + + +#if !(SUPPORT_DOUBLE && SUPPORT_I2D) +double builtin_i2d(s4 a) +{ + double d = (double) a; + return d; +} +#endif /* !(SUPPORT_DOUBLE && SUPPORT_I2D) */ + + +#if !(SUPPORT_LONG && SUPPORT_FLOAT && SUPPORT_L2F) +float builtin_l2f(s8 a) +{ +#if U8_AVAILABLE + float f = (float) a; + return f; +#else + return 0.0; +#endif +} +#endif /* !(SUPPORT_LONG && SUPPORT_FLOAT && SUPPORT_L2F) */ + + +#if !(SUPPORT_LONG && SUPPORT_DOUBLE && SUPPORT_L2D) +double builtin_l2d(s8 a) +{ +#if U8_AVAILABLE + double d = (double) a; + return d; +#else + return 0.0; +#endif +} +#endif /* !(SUPPORT_LONG && SUPPORT_DOUBLE && SUPPORT_L2D) */ + + +#if !(SUPPORT_FLOAT && SUPPORT_F2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC) +s4 builtin_f2i(float a) +{ + s4 i; + + i = builtin_d2i((double) a); + + return i; + + /* float f; + + if (isnanf(a)) + return 0; + if (finitef(a)) { + if (a > 2147483647) + return 2147483647; + if (a < (-2147483648)) + return (-2147483648); + return (s4) a; + } + f = copysignf((float) 1.0, a); + if (f > 0) + return 2147483647; + return (-2147483648); */ +} +#endif /* !(SUPPORT_FLOAT && SUPPORT_F2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC) */ + + +#if !(SUPPORT_FLOAT && SUPPORT_LONG && SUPPORT_F2L) || defined(DISABLE_GC) +s8 builtin_f2l(float a) +{ + s8 l; + + l = builtin_d2l((double) a); + + return l; + + /* float f; + + if (finitef(a)) { + if (a > 9223372036854775807L) + return 9223372036854775807L; + if (a < (-9223372036854775808L)) + return (-9223372036854775808L); + return (s8) a; + } + if (isnanf(a)) + return 0; + f = copysignf((float) 1.0, a); + if (f > 0) + return 9223372036854775807L; + return (-9223372036854775808L); */ +} +#endif /* !(SUPPORT_FLOAT && SUPPORT_LONG && SUPPORT_F2L) */ + + +#if !(SUPPORT_DOUBLE && SUPPORT_D2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC) +s4 builtin_d2i(double a) +{ + double d; + + if (finite(a)) { + if (a >= 2147483647) + return 2147483647; + if (a <= (-2147483647-1)) + return (-2147483647-1); + return (s4) a; + } + if (isnan(a)) + return 0; + d = copysign(1.0, a); + if (d > 0) + return 2147483647; + return (-2147483647-1); +} +#endif /* !(SUPPORT_DOUBLE && SUPPORT_D2I) || defined(ENABLE_INTRP) || defined(DISABLE_GC) */ + + +#if !(SUPPORT_DOUBLE && SUPPORT_LONG && SUPPORT_D2L) || defined(DISABLE_GC) +s8 builtin_d2l(double a) +{ + double d; + + if (finite(a)) { + if (a >= 9223372036854775807LL) + return 9223372036854775807LL; + if (a <= (-9223372036854775807LL-1)) + return (-9223372036854775807LL-1); + return (s8) a; + } + if (isnan(a)) + return 0; + d = copysign(1.0, a); + if (d > 0) + return 9223372036854775807LL; + return (-9223372036854775807LL-1); +} +#endif /* !(SUPPORT_DOUBLE && SUPPORT_LONG && SUPPORT_D2L) */ + + +#if !(SUPPORT_FLOAT && SUPPORT_DOUBLE) +double builtin_f2d(float a) +{ + if (finitef(a)) return (double) a; + else { + if (isnanf(a)) + return longBitsToDouble(DBL_NAN); + else + return copysign(longBitsToDouble(DBL_POSINF), (double) copysignf(1.0, a) ); + } +} + +float builtin_d2f(double a) +{ + if (finite(a)) + return (float) a; + else { + if (isnan(a)) + return intBitsToFloat(FLT_NAN); + else + return copysignf(intBitsToFloat(FLT_POSINF), (float) copysign(1.0, a)); + } +} +#endif /* !(SUPPORT_FLOAT && SUPPORT_DOUBLE) */ + + +/*============================================================================*/ +/* AUTOMATICALLY REPLACED FUNCTIONS */ +/*============================================================================*/ + +/* builtin_arraycopy *********************************************************** + + Builtin for java.lang.System.arraycopy. + + NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code. + +*******************************************************************************/ + +void builtin_arraycopy(java_handle_t *src, s4 srcStart, + java_handle_t *dest, s4 destStart, s4 len) +{ + arraydescriptor *sdesc; + arraydescriptor *ddesc; + s4 i; + + if ((src == NULL) || (dest == NULL)) { + exceptions_throw_nullpointerexception(); + return; + } + + sdesc = LLNI_vftbl_direct(src)->arraydesc; + ddesc = LLNI_vftbl_direct(dest)->arraydesc; + + if (!sdesc || !ddesc || (sdesc->arraytype != ddesc->arraytype)) { + exceptions_throw_arraystoreexception(); + return; + } + + // Check if offsets and length are positive. + if ((srcStart < 0) || (destStart < 0) || (len < 0)) { + exceptions_throw_arrayindexoutofboundsexception(); + return; + } + + // Check if ranges are valid. + if ((((uint32_t) srcStart + (uint32_t) len) > (uint32_t) LLNI_array_size(src)) || + (((uint32_t) destStart + (uint32_t) len) > (uint32_t) LLNI_array_size(dest))) { + exceptions_throw_arrayindexoutofboundsexception(); + return; + } + + // Special case. + if (len == 0) { + return; + } + + if (sdesc->componentvftbl == ddesc->componentvftbl) { + /* We copy primitive values or references of exactly the same type */ + + s4 dataoffset = sdesc->dataoffset; + s4 componentsize = sdesc->componentsize; + + LLNI_CRITICAL_START; + + MMOVE(((u1 *) LLNI_DIRECT(dest)) + dataoffset + componentsize * destStart, + ((u1 *) LLNI_DIRECT(src)) + dataoffset + componentsize * srcStart, + u1, (size_t) len * componentsize); + + LLNI_CRITICAL_END; + } + else { + /* We copy references of different type */ + + java_handle_objectarray_t *oas = (java_handle_objectarray_t *) src; + java_handle_objectarray_t *oad = (java_handle_objectarray_t *) dest; + + if (destStart <= srcStart) { + for (i = 0; i < len; i++) { + java_handle_t *o; + + o = array_objectarray_element_get(oas, srcStart + i); + + if (!builtin_canstore(oad, o)) + return; + + array_objectarray_element_set(oad, destStart + i, o); + } + } + else { + /* XXX this does not completely obey the specification! + If an exception is thrown only the elements above the + current index have been copied. The specification + requires that only the elements *below* the current + index have been copied before the throw. */ + + for (i = len - 1; i >= 0; i--) { + java_handle_t *o; + + o = array_objectarray_element_get(oas, srcStart + i); + + if (!builtin_canstore(oad, o)) + return; + + array_objectarray_element_set(oad, destStart + i, o); + } + } + } +} + + +/* builtin_nanotime ************************************************************ + + Return the current time in nanoseconds. + +*******************************************************************************/ + +s8 builtin_nanotime(void) +{ + struct timeval tv; + s8 usecs; + + if (gettimeofday(&tv, NULL) == -1) + vm_abort("gettimeofday failed: %s", strerror(errno)); + + usecs = (s8) tv.tv_sec * (1000 * 1000) + (s8) tv.tv_usec; + + return usecs * 1000; +} + + +/* builtin_currenttimemillis *************************************************** + + Return the current time in milliseconds. + +*******************************************************************************/ + +s8 builtin_currenttimemillis(void) +{ + s8 msecs; + + msecs = builtin_nanotime() / 1000 / 1000; + + return msecs; +} + + +/* builtin_clone *************************************************************** + + Function for cloning objects or arrays. + + NOTE: This is a SLOW builtin and can be called from JIT & NATIVE code. + +*******************************************************************************/ + +java_handle_t *builtin_clone(void *env, java_handle_t *o) +{ + arraydescriptor *ad; + u4 size; + classinfo *c; + java_handle_t *co; /* cloned object header */ + + /* get the array descriptor */ + + ad = LLNI_vftbl_direct(o)->arraydesc; + + /* we are cloning an array */ + + if (ad != NULL) { + size = ad->dataoffset + ad->componentsize * LLNI_array_size(o); + + co = (java_handle_t*) heap_alloc(size, (ad->arraytype == ARRAYTYPE_OBJECT), NULL, true); + + if (co == NULL) + return NULL; + +#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES) + /* XXX this is only a dirty hack to make Boehm work with handles */ + + co = LLNI_WRAP((java_object_t *) co); +#endif + + LLNI_CRITICAL_START; + + MCOPY(LLNI_DIRECT(co), LLNI_DIRECT(o), u1, size); + +#if defined(ENABLE_GC_CACAO) + heap_init_objectheader(LLNI_DIRECT(co), size); +#endif + +#if defined(ENABLE_THREADS) + lock_init_object_lock(LLNI_DIRECT(co)); +#endif + + LLNI_CRITICAL_END; + + return co; + } + + /* we are cloning a non-array */ + + if (!builtin_instanceof(o, class_java_lang_Cloneable)) { + exceptions_throw_clonenotsupportedexception(); + return NULL; + } + + /* get the class of the object */ + + LLNI_class_get(o, c); + + /* create new object */ + + co = builtin_new(c); + + if (co == NULL) + return NULL; + + LLNI_CRITICAL_START; + + MCOPY(LLNI_DIRECT(co), LLNI_DIRECT(o), u1, c->instancesize); + +#if defined(ENABLE_GC_CACAO) + heap_init_objectheader(LLNI_DIRECT(co), c->instancesize); +#endif + +#if defined(ENABLE_THREADS) + lock_init_object_lock(LLNI_DIRECT(co)); +#endif + + LLNI_CRITICAL_END; + + return co; +} + + +#if defined(ENABLE_CYCLES_STATS) +void builtin_print_cycles_stats(FILE *file) +{ + fprintf(file,"builtin cylce count statistics:\n"); + + CYCLES_STATS_PRINT_OVERHEAD(builtin_overhead,file); + CYCLES_STATS_PRINT(builtin_new ,file); + + fprintf(file,"\n"); +} +#endif /* defined(ENABLE_CYCLES_STATS) */ + + +#if defined(ENABLE_VMLOG) +#define NDEBUG +#include +#endif + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c++ + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --cc src/vm/jit/emit-common.cpp index a5fa481e3,000000000..f6cb5d5ac mode 100644,000000..100644 --- a/src/vm/jit/emit-common.cpp +++ b/src/vm/jit/emit-common.cpp @@@ -1,731 -1,0 +1,736 @@@ +/* src/vm/jit/emit-common.cpp - common code emitter functions + + Copyright (C) 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#include "config.h" + +#include +#include + +#include "vm/types.h" + +#include "arch.h" +#include "codegen.h" + +#include "vm/options.h" +#include "vm/statistics.h" + +#include "vm/jit/emit-common.hpp" +#include "vm/jit/jit.hpp" +#include "vm/jit/patcher-common.hpp" + + +/* emit_load_s1 **************************************************************** + + Emits a possible load of the first source operand. + +*******************************************************************************/ + +s4 emit_load_s1(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->s1); + + reg = emit_load(jd, iptr, src, tempreg); + + return reg; +} + + +/* emit_load_s2 **************************************************************** + + Emits a possible load of the second source operand. + +*******************************************************************************/ + +s4 emit_load_s2(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->sx.s23.s2); + + reg = emit_load(jd, iptr, src, tempreg); + + return reg; +} + + +/* emit_load_s3 **************************************************************** + + Emits a possible load of the third source operand. + +*******************************************************************************/ + +s4 emit_load_s3(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->sx.s23.s3); + + reg = emit_load(jd, iptr, src, tempreg); + + return reg; +} + + +/* emit_load_s1_low ************************************************************ + + Emits a possible load of the low 32-bits of the first long source + operand. + +*******************************************************************************/ + +#if SIZEOF_VOID_P == 4 +s4 emit_load_s1_low(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->s1); + + reg = emit_load_low(jd, iptr, src, tempreg); + + return reg; +} +#endif + + +/* emit_load_s2_low ************************************************************ + + Emits a possible load of the low 32-bits of the second long source + operand. + +*******************************************************************************/ + +#if SIZEOF_VOID_P == 4 +s4 emit_load_s2_low(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->sx.s23.s2); + + reg = emit_load_low(jd, iptr, src, tempreg); + + return reg; +} +#endif + + +/* emit_load_s3_low ************************************************************ + + Emits a possible load of the low 32-bits of the third long source + operand. + +*******************************************************************************/ + +#if SIZEOF_VOID_P == 4 +s4 emit_load_s3_low(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->sx.s23.s3); + + reg = emit_load_low(jd, iptr, src, tempreg); + + return reg; +} +#endif + + +/* emit_load_s1_high *********************************************************** + + Emits a possible load of the high 32-bits of the first long source + operand. + +*******************************************************************************/ + +#if SIZEOF_VOID_P == 4 +s4 emit_load_s1_high(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->s1); + + reg = emit_load_high(jd, iptr, src, tempreg); + + return reg; +} +#endif + + +/* emit_load_s2_high *********************************************************** + + Emits a possible load of the high 32-bits of the second long source + operand. + +*******************************************************************************/ + +#if SIZEOF_VOID_P == 4 +s4 emit_load_s2_high(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->sx.s23.s2); + + reg = emit_load_high(jd, iptr, src, tempreg); + + return reg; +} +#endif + + +/* emit_load_s3_high *********************************************************** + + Emits a possible load of the high 32-bits of the third long source + operand. + +*******************************************************************************/ + +#if SIZEOF_VOID_P == 4 +s4 emit_load_s3_high(jitdata *jd, instruction *iptr, s4 tempreg) +{ + varinfo *src; + s4 reg; + + src = VAROP(iptr->sx.s23.s3); + + reg = emit_load_high(jd, iptr, src, tempreg); + + return reg; +} +#endif + + +/* emit_store_dst ************************************************************** + + This function generates the code to store the result of an + operation back into a spilled pseudo-variable. If the + pseudo-variable has not been spilled in the first place, this + function will generate nothing. + +*******************************************************************************/ + +void emit_store_dst(jitdata *jd, instruction *iptr, s4 d) +{ + emit_store(jd, iptr, VAROP(iptr->dst), d); +} + + +/* emit_patcher_traps ********************************************************** + + Generates the code for the patcher traps. + +*******************************************************************************/ + +void emit_patcher_traps(jitdata *jd) +{ + codegendata *cd; + codeinfo *code; + patchref_t *pr; + u1 *savedmcodeptr; + u1 *tmpmcodeptr; + uint32_t mcode; + + /* get required compiler data */ + + cd = jd->cd; + code = jd->code; + + /* generate patcher traps code */ + + for (pr = (patchref_t*) list_first(code->patchers); pr != NULL; pr = (patchref_t*) list_next(code->patchers, pr)) { + + /* Calculate the patch position where the original machine + code is located and the trap should be placed. */ + + tmpmcodeptr = (u1 *) (cd->mcodebase + pr->mpc); + + /* Patch in the trap to call the signal handler (done at + compile time). */ + + savedmcodeptr = cd->mcodeptr; /* save current mcodeptr */ + cd->mcodeptr = tmpmcodeptr; /* set mcodeptr to patch position */ + + mcode = emit_trap(cd); + + cd->mcodeptr = savedmcodeptr; /* restore the current mcodeptr */ + + /* Remember the original machine code which is patched + back in later (done at runtime). */ + + pr->mcode = mcode; + } +} + + +/* emit_bccz ******************************************************************* + + Emit conditional and unconditional branch instructions on integer + regiseters. + +*******************************************************************************/ + +void emit_bccz(codegendata *cd, basicblock *target, s4 condition, s4 reg, u4 options) +{ + s4 branchmpc; + s4 disp; + + /* Target basic block already has an PC, so we can generate the + branch immediately. */ + + if ((target->mpc >= 0)) { + STATISTICS(count_branches_resolved++); + + /* calculate the mpc of the branch instruction */ + + branchmpc = cd->mcodeptr - cd->mcodebase; + disp = target->mpc - branchmpc; + +#if defined(ENABLE_STATISTICS) + count_emit_branch++; + if ((int8_t)disp == disp) count_emit_branch_8bit++; + else if ((int16_t)disp == disp) count_emit_branch_16bit++; + else if ((int32_t)disp == disp) count_emit_branch_32bit++; +# if SIZEOF_VOID_P == 8 + else if ((int64_t)disp == disp) count_emit_branch_64bit++; +# endif +#endif + + emit_branch(cd, disp, condition, reg, options); + } + else { + /* current mcodeptr is the correct position, + afterwards emit the NOPs */ + + codegen_add_branch_ref(cd, target, condition, reg, options); + + /* generate NOPs as placeholder for branch code */ + + BRANCH_NOPS; + } +} + + +/* emit_bcc ******************************************************************** + + Emit conditional and unconditional branch instructions on condition + codes. + +*******************************************************************************/ + +void emit_bcc(codegendata *cd, basicblock *target, s4 condition, u4 options) +{ + emit_bccz(cd, target, condition, -1, options); +} + + +/* emit_br ********************************************************************* + + Wrapper for unconditional branches. + +*******************************************************************************/ + +void emit_br(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_UNCONDITIONAL, BRANCH_OPT_NONE); +} + + +/* emit_bxxz ******************************************************************* + + Wrappers for branches on one integer register. + +*******************************************************************************/ + +#if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER + +void emit_beqz(codegendata *cd, basicblock *target, s4 reg) +{ + emit_bccz(cd, target, BRANCH_EQ, reg, BRANCH_OPT_NONE); +} + +void emit_bnez(codegendata *cd, basicblock *target, s4 reg) +{ + emit_bccz(cd, target, BRANCH_NE, reg, BRANCH_OPT_NONE); +} + +void emit_bltz(codegendata *cd, basicblock *target, s4 reg) +{ + emit_bccz(cd, target, BRANCH_LT, reg, BRANCH_OPT_NONE); +} + +void emit_bgez(codegendata *cd, basicblock *target, s4 reg) +{ + emit_bccz(cd, target, BRANCH_GE, reg, BRANCH_OPT_NONE); +} + +void emit_bgtz(codegendata *cd, basicblock *target, s4 reg) +{ + emit_bccz(cd, target, BRANCH_GT, reg, BRANCH_OPT_NONE); +} + +void emit_blez(codegendata *cd, basicblock *target, s4 reg) +{ + emit_bccz(cd, target, BRANCH_LE, reg, BRANCH_OPT_NONE); +} + +#endif /* SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER */ + + +/* emit_bxx ******************************************************************** + + Wrappers for branches on two integer registers. + + We use PACK_REGS here, so we don't have to change the branchref + data structure and the emit_bccz function. + +*******************************************************************************/ + +#if SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS + +void emit_beq(codegendata *cd, basicblock *target, s4 s1, s4 s2) +{ + emit_bccz(cd, target, BRANCH_EQ, PACK_REGS(s1, s2), BRANCH_OPT_NONE); +} + +void emit_bne(codegendata *cd, basicblock *target, s4 s1, s4 s2) +{ + emit_bccz(cd, target, BRANCH_NE, PACK_REGS(s1, s2), BRANCH_OPT_NONE); +} + +#endif /* SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS */ + + +/* emit_bxx ******************************************************************** + + Wrappers for branches on condition codes. + +*******************************************************************************/ + +#if SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER + +void emit_beq(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_EQ, BRANCH_OPT_NONE); +} + +void emit_bne(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_NE, BRANCH_OPT_NONE); +} + +void emit_blt(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_LT, BRANCH_OPT_NONE); +} + +void emit_bge(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_GE, BRANCH_OPT_NONE); +} + +void emit_bgt(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_GT, BRANCH_OPT_NONE); +} + +void emit_ble(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_LE, BRANCH_OPT_NONE); +} + +#if SUPPORT_BRANCH_CONDITIONAL_UNSIGNED_CONDITIONS +void emit_bult(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_ULT, BRANCH_OPT_NONE); +} + +void emit_bule(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_ULE, BRANCH_OPT_NONE); +} + +void emit_buge(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_UGE, BRANCH_OPT_NONE); +} + +void emit_bugt(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_UGT, BRANCH_OPT_NONE); +} +#endif + +#if defined(__POWERPC__) || defined(__POWERPC64__) +void emit_bnan(codegendata *cd, basicblock *target) +{ + emit_bcc(cd, target, BRANCH_NAN, BRANCH_OPT_NONE); +} +#endif + +#endif /* SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER */ + + +/* emit_label_bccz ************************************************************* + + Emit a branch to a label. Possibly emit the branch, if it is a + backward branch. + +*******************************************************************************/ + +void emit_label_bccz(codegendata *cd, s4 label, s4 condition, s4 reg, u4 options) +{ + list_t *list; + branch_label_ref_t *br; + s4 mpc; + s4 disp; + + /* get the label list */ + + list = cd->brancheslabel; + + /* search if the label is already in the list */ + + for (br = (branch_label_ref_t*) list_first(list); br != NULL; br = (branch_label_ref_t*) list_next(list, br)) { + /* is this entry the correct label? */ + + if (br->label == label) + break; + } + + if (br == NULL) { + /* current mcodeptr is the correct position, + afterwards emit the NOPs */ + + codegen_branch_label_add(cd, label, condition, reg, options); + + /* generate NOPs as placeholder for branch code */ + + BRANCH_NOPS; + return; + } + + /* Branch reference was found. */ + + /* calculate the mpc of the branch instruction */ + + mpc = cd->mcodeptr - cd->mcodebase; + disp = br->mpc - mpc; + +#if defined(ENABLE_STATISTICS) + count_emit_branch++; + if ((int8_t)disp == disp) count_emit_branch_8bit++; + else if ((int16_t)disp == disp) count_emit_branch_16bit++; + else if ((int32_t)disp == disp) count_emit_branch_32bit++; +# if SIZEOF_VOID_P == 8 + else if ((int64_t)disp == disp) count_emit_branch_64bit++; +# endif +#endif + + emit_branch(cd, disp, condition, reg, options); + + /* now remove the branch reference */ + + list_remove(list, br); +} + + +/* emit_label ****************************************************************** + + Emit a label for a branch. Possibly emit the branch, if it is a + forward branch. + +*******************************************************************************/ + +void emit_label(codegendata *cd, s4 label) +{ + list_t *list; + branch_label_ref_t *br; + s4 mpc; + s4 disp; + u1 *mcodeptr; + + /* get the label list */ + + list = cd->brancheslabel; + + /* search if the label is already in the list */ + + for (br = (branch_label_ref_t*) list_first(list); br != NULL; br = (branch_label_ref_t*) list_next(list, br)) { + /* is this entry the correct label? */ + + if (br->label == label) + break; + } + + if (br == NULL) { + /* No branch reference found, add the label to the list (use + invalid values for condition and register). */ + + codegen_branch_label_add(cd, label, -1, -1, BRANCH_OPT_NONE ); + return; + } + + /* Branch reference was found. */ + + /* calculate the mpc of the branch instruction */ + + mpc = cd->mcodeptr - cd->mcodebase; + disp = mpc - br->mpc; + + /* temporary set the mcodeptr */ + + mcodeptr = cd->mcodeptr; + cd->mcodeptr = cd->mcodebase + br->mpc; + +#if defined(ENABLE_STATISTICS) + count_emit_branch++; + if ((int8_t)disp == disp) count_emit_branch_8bit++; + else if ((int16_t)disp == disp) count_emit_branch_16bit++; + else if ((int32_t)disp == disp) count_emit_branch_32bit++; +# if SIZEOF_VOID_P == 8 + else if ((int64_t)disp == disp) count_emit_branch_64bit++; +# endif +#endif + + emit_branch(cd, disp, br->condition, br->reg, br->options); + + /* restore mcodeptr */ + + cd->mcodeptr = mcodeptr; + + /* now remove the branch reference */ + + list_remove(list, br); +} + + +/* emit_label_bcc ************************************************************** + + Emit conditional and unconditional label-branch instructions on + condition codes. + +*******************************************************************************/ + +void emit_label_bcc(codegendata *cd, s4 label, s4 condition, u4 options) +{ + emit_label_bccz(cd, label, condition, -1, options); +} + + +/* emit_label_br *************************************************************** + + Wrapper for unconditional label-branches. + +*******************************************************************************/ + +void emit_label_br(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_UNCONDITIONAL, BRANCH_OPT_NONE); +} + + +/* emit_label_bxxz ************************************************************* + + Wrappers for label-branches on one integer register. + +*******************************************************************************/ + +#if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER + +void emit_label_beqz(codegendata *cd, s4 label, s4 reg) +{ + emit_label_bccz(cd, label, BRANCH_EQ, reg, BRANCH_OPT_NONE); +} + ++void emit_label_bnez(codegendata *cd, s4 label, s4 reg) ++{ ++ emit_label_bccz(cd, label, BRANCH_NE, reg, BRANCH_OPT_NONE); ++} ++ +#endif /* SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER */ + + +/* emit_label_bxx ************************************************************** + + Wrappers for label-branches on condition codes. + +*******************************************************************************/ + +#if SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER + +void emit_label_beq(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_EQ, BRANCH_OPT_NONE); +} + +void emit_label_bne(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_NE, BRANCH_OPT_NONE); +} + +void emit_label_blt(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_LT, BRANCH_OPT_NONE); +} + +void emit_label_bge(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_GE, BRANCH_OPT_NONE); +} + +void emit_label_bgt(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_GT, BRANCH_OPT_NONE); +} + +void emit_label_ble(codegendata *cd, s4 label) +{ + emit_label_bcc(cd, label, BRANCH_LE, BRANCH_OPT_NONE); +} + +#endif /* SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c++ + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --cc src/vm/jit/emit-common.hpp index 02e90d6b7,000000000..b96bc08ea mode 100644,000000..100644 --- a/src/vm/jit/emit-common.hpp +++ b/src/vm/jit/emit-common.hpp @@@ -1,216 -1,0 +1,217 @@@ +/* src/vm/jit/emit-common.hpp - common code emitter functions + + Copyright (C) 2006, 2007 R. Grafl, A. Krall, C. Kruegel, C. Oates, + R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner, + C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger, + Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#ifndef _EMIT_COMMON_H +#define _EMIT_COMMON_H + +#include "config.h" +#include "vm/types.h" + +#include "arch.h" + +#include "vm/jit/codegen-common.hpp" +#include "vm/jit/jit.hpp" + + +/* branch labels **************************************************************/ + +#define BRANCH_LABEL_1 1 +#define BRANCH_LABEL_2 2 +#define BRANCH_LABEL_3 3 +#define BRANCH_LABEL_4 4 +#define BRANCH_LABEL_5 5 +#define BRANCH_LABEL_6 6 +#define BRANCH_LABEL_7 7 +#define BRANCH_LABEL_8 8 +#define BRANCH_LABEL_9 9 +#define BRANCH_LABEL_10 10 + + +/* constant range macros ******************************************************/ + +#if SIZEOF_VOID_P == 8 + +# define IS_IMM8(c) \ + (((s8) (c) >= -128) && ((s8) (c) <= 127)) + +# define IS_IMM32(c) \ + (((s8) (c) >= (-2147483647-1)) && ((s8) (c) <= 2147483647)) + +#else + +# define IS_IMM8(c) \ + (((s4) (c) >= -128) && ((s4) (c) <= 127)) + +# define IS_IMM16(c) \ + (((s4) (c) >= -32768) && ((s4) (c) <= 32767)) + +#endif + + +/* code generation functions **************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg); +s4 emit_load_s1(jitdata *jd, instruction *iptr, s4 tempreg); +s4 emit_load_s2(jitdata *jd, instruction *iptr, s4 tempreg); +s4 emit_load_s3(jitdata *jd, instruction *iptr, s4 tempreg); + +#if SIZEOF_VOID_P == 4 +s4 emit_load_low(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg); +s4 emit_load_s1_low(jitdata *jd, instruction *iptr, s4 tempreg); +s4 emit_load_s2_low(jitdata *jd, instruction *iptr, s4 tempreg); +s4 emit_load_s3_low(jitdata *jd, instruction *iptr, s4 tempreg); + +s4 emit_load_high(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg); +s4 emit_load_s1_high(jitdata *jd, instruction *iptr, s4 tempreg); +s4 emit_load_s2_high(jitdata *jd, instruction *iptr, s4 tempreg); +s4 emit_load_s3_high(jitdata *jd, instruction *iptr, s4 tempreg); +#endif + +void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d); +void emit_store_dst(jitdata *jd, instruction *iptr, s4 d); + +#if SIZEOF_VOID_P == 4 +void emit_store_low(jitdata *jd, instruction *iptr, varinfo *dst, s4 d); +void emit_store_high(jitdata *jd, instruction *iptr, varinfo *dst, s4 d); +#endif + +void emit_copy(jitdata *jd, instruction *iptr); + +void emit_iconst(codegendata *cd, s4 d, s4 value); +void emit_lconst(codegendata *cd, s4 d, s8 value); + +/* branch-emitting functions */ +void emit_bccz(codegendata *cd, basicblock *target, s4 condition, s4 reg, u4 options); +void emit_bcc(codegendata *cd, basicblock *target, s4 condition, u4 options); + +/* wrapper for unconditional branches */ +void emit_br(codegendata *cd, basicblock *target); + +/* wrappers for branches on one integer register */ + +#if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER +void emit_beqz(codegendata *cd, basicblock *target, s4 reg); +void emit_bnez(codegendata *cd, basicblock *target, s4 reg); +void emit_bltz(codegendata *cd, basicblock *target, s4 reg); +void emit_bgez(codegendata *cd, basicblock *target, s4 reg); +void emit_bgtz(codegendata *cd, basicblock *target, s4 reg); +void emit_blez(codegendata *cd, basicblock *target, s4 reg); +#endif + +/* wrappers for branches on two integer registers */ + +#if SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS +void emit_beq(codegendata *cd, basicblock *target, s4 s1, s4 s2); +void emit_bne(codegendata *cd, basicblock *target, s4 s1, s4 s2); +#endif + +/* wrappers for branches on condition codes */ + +#if SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER +void emit_beq(codegendata *cd, basicblock *target); +void emit_bne(codegendata *cd, basicblock *target); +void emit_blt(codegendata *cd, basicblock *target); +void emit_bge(codegendata *cd, basicblock *target); +void emit_bgt(codegendata *cd, basicblock *target); +void emit_ble(codegendata *cd, basicblock *target); +#endif + +#if SUPPORT_BRANCH_CONDITIONAL_UNSIGNED_CONDITIONS +void emit_bult(codegendata *cd, basicblock *target); +void emit_bule(codegendata *cd, basicblock *target); +void emit_buge(codegendata *cd, basicblock *target); +void emit_bugt(codegendata *cd, basicblock *target); +#endif + +#if defined(__POWERPC__) || defined(__POWERPC64__) +void emit_bnan(codegendata *cd, basicblock *target); +#endif + +/* label-branches */ +void emit_label_bccz(codegendata *cd, s4 label, s4 condition, s4 reg, u4 options); +void emit_label(codegendata *cd, s4 label); +void emit_label_bcc(codegendata *cd, s4 label, s4 condition, u4 options); + +void emit_label_br(codegendata *cd, s4 label); + +#if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER +void emit_label_beqz(codegendata *cd, s4 label, s4 reg); ++void emit_label_bnez(codegendata *cd, s4 label, s4 reg); +#endif + +#if SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER +void emit_label_beq(codegendata *cd, s4 label); +void emit_label_bne(codegendata *cd, s4 label); +void emit_label_blt(codegendata *cd, s4 label); +void emit_label_bge(codegendata *cd, s4 label); +void emit_label_bgt(codegendata *cd, s4 label); +void emit_label_ble(codegendata *cd, s4 label); +#endif + +/* machine dependent branch-emitting function */ +void emit_branch(codegendata *cd, s4 disp, s4 condition, s4 reg, u4 options); + +void emit_arithmetic_check(codegendata *cd, instruction *iptr, s4 reg); +void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1, s4 s2); +void emit_arraystore_check(codegendata *cd, instruction *iptr); +void emit_classcast_check(codegendata *cd, instruction *iptr, s4 condition, s4 reg, s4 s1); +void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg); +void emit_exception_check(codegendata *cd, instruction *iptr); + +void emit_trap_compiler(codegendata *cd); +void emit_trap_countdown(codegendata *cd, s4 *counter); +uint32_t emit_trap(codegendata *cd); + +void emit_patcher_traps(jitdata *jd); + +void emit_verbosecall_enter(jitdata *jd); +void emit_verbosecall_exit(jitdata *jd); + +#ifdef __cplusplus +} +#endif + +#endif /* _EMIT_COMMON_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --cc src/vm/linker.c index c1b00e40b,000000000..438c7eec6 mode 100644,000000..100644 --- a/src/vm/linker.c +++ b/src/vm/linker.c @@@ -1,1374 -1,0 +1,1372 @@@ +/* src/vm/linker.c - class linker functions + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#include "config.h" + +#include +#include + +#include "vm/types.h" + +#include "mm/memory.h" + +#include "native/native.h" + +#include "threads/lock-common.h" +#include "threads/mutex.hpp" + +#include "toolbox/logging.h" + +#include "vm/access.h" +#include "vm/array.h" +#include "vm/class.h" +#include "vm/classcache.h" +#include "vm/exceptions.hpp" +#include "vm/globals.hpp" +#include "vm/loader.hpp" +#include "vm/options.h" +#include "vm/primitive.hpp" +#include "vm/rt-timing.h" +#include "vm/string.hpp" +#include "vm/vm.hpp" + +#include "vm/jit/asmpart.h" +#include "vm/jit/stubs.hpp" + + +/* debugging macros ***********************************************************/ + +#if !defined(NDEBUG) +# define TRACELINKCLASS(c) \ + do { \ + if (opt_TraceLinkClass) { \ + log_start(); \ + log_print("[Linking "); \ + class_print((c)); \ + log_print("]"); \ + log_finish(); \ + } \ + } while (0) +#else +# define TRACELINKCLASS(c) +#endif + + +/* #include "vm/resolve.h" */ +/* copied prototype to avoid bootstrapping problem: */ +classinfo *resolve_classref_or_classinfo_eager(classref_or_classinfo cls, bool checkaccess); + +#if defined(ENABLE_STATISTICS) +# include "vm/statistics.h" +#endif + +#if !defined(NDEBUG) && defined(ENABLE_INLINING) +#define INLINELOG(code) do { if (opt_TraceInlining) { code } } while (0) +#else +#define INLINELOG(code) +#endif + + +/* global variables ***********************************************************/ + +static s4 interfaceindex; /* sequential numbering of interfaces */ +static s4 classvalue; + + +/* private functions **********************************************************/ + +static classinfo *link_class_intern(classinfo *c); +static arraydescriptor *link_array(classinfo *c); +static void linker_compute_class_values(classinfo *c); +static void linker_compute_subclasses(classinfo *c); +static bool linker_addinterface(classinfo *c, classinfo *ic); +static s4 class_highestinterface(classinfo *c); + + +/* linker_init ***************************************************************** + + Initializes the linker subsystem and links classes required for the + primitive table. + +*******************************************************************************/ + +void linker_preinit(void) +{ + TRACESUBSYSTEMINITIALIZATION("linker_preinit"); + + /* Reset interface index. */ + + interfaceindex = 0; + + /* Link the most basic classes. */ + + if (!link_class(class_java_lang_Object)) + vm_abort("linker_preinit: linking java/lang/Object failed"); + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_Cloneable)) + vm_abort("linker_preinit: linking java/lang/Cloneable failed"); + + if (!link_class(class_java_io_Serializable)) + vm_abort("linker_preinit: linking java/io/Serializable failed"); +#endif +} + + +/* linker_init ***************************************************************** + + Links all classes required in the VM. + +*******************************************************************************/ + +void linker_init(void) +{ + TRACESUBSYSTEMINITIALIZATION("linker_init"); + + /* Link java.lang.Class as first class of the system, because we + need it's vftbl for all other classes so we can use a class as + object. */ + + if (!link_class(class_java_lang_Class)) + vm_abort("linker_init: linking java/lang/Class failed"); + + /* Now set the header.vftbl of all classes which were created + before java.lang.Class was linked. */ + + class_postset_header_vftbl(); + + /* Link primitive-type wrapping classes. */ + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_Void)) + vm_abort("linker_init: linking failed"); +#endif + + if (!link_class(class_java_lang_Boolean)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Byte)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Character)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Short)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Integer)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Long)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Float)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Double)) + vm_abort("linker_init: linking failed"); + + /* Link important system classes. */ + + if (!link_class(class_java_lang_String)) + vm_abort("linker_init: linking java/lang/String failed"); + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_ClassLoader)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_SecurityManager)) + vm_abort("linker_init: linking failed"); +#endif + + if (!link_class(class_java_lang_System)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Thread)) + vm_abort("linker_init: linking failed"); + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_ThreadGroup)) + vm_abort("linker_init: linking failed"); +#endif + + if (!link_class(class_java_lang_Throwable)) + vm_abort("linker_init: linking failed"); + +#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) + if (!link_class(class_java_lang_VMSystem)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_VMThread)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_VMThrowable)) + vm_abort("linker_init: linking failed"); +#endif + + /* Important system exceptions. */ + + if (!link_class(class_java_lang_Exception)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_ClassNotFoundException)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_RuntimeException)) + vm_abort("linker_init: linking failed"); + + /* some classes which may be used more often */ + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_StackTraceElement)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_Constructor)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_Field)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_Method)) + vm_abort("linker_init: linking failed"); + +# if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) + if (!link_class(class_java_lang_reflect_VMConstructor)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_VMField)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_VMMethod)) + vm_abort("linker_init: linking failed"); +# endif + + if (!link_class(class_java_security_PrivilegedAction)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_util_Vector)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_util_HashMap)) + vm_abort("linker_init: linking failed"); + +# if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) + if (!link_class(class_sun_misc_Signal)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_sun_reflect_MagicAccessorImpl)) + vm_abort("linker_init: linking failed"); +# endif + + if (!link_class(arrayclass_java_lang_Object)) + vm_abort("linker_init: linking failed"); +#endif + + + /* create pseudo classes used by the typechecker */ + + /* pseudo class for Arraystubs (extends java.lang.Object) */ + + pseudo_class_Arraystub = + class_create_classinfo(utf_new_char("$ARRAYSTUB$")); + pseudo_class_Arraystub->state |= CLASS_LOADED; + pseudo_class_Arraystub->super = class_java_lang_Object; + +#if defined(ENABLE_JAVASE) + + pseudo_class_Arraystub->interfacescount = 2; + pseudo_class_Arraystub->interfaces = MNEW(classinfo*, 2); + pseudo_class_Arraystub->interfaces[0] = class_java_lang_Cloneable; + pseudo_class_Arraystub->interfaces[1] = class_java_io_Serializable; + +#elif defined(ENABLE_JAVAME_CLDC1_1) + + pseudo_class_Arraystub->interfacescount = 0; + pseudo_class_Arraystub->interfaces = NULL; + +#else +# error unknown Java configuration +#endif + + if (!classcache_store_unique(pseudo_class_Arraystub)) + vm_abort("linker_init: could not cache pseudo_class_Arraystub"); + + if (!link_class(pseudo_class_Arraystub)) + vm_abort("linker_init: linking pseudo_class_Arraystub failed"); + + /* pseudo class representing the null type */ + + pseudo_class_Null = class_create_classinfo(utf_new_char("$NULL$")); + pseudo_class_Null->state |= CLASS_LOADED; + pseudo_class_Null->super = class_java_lang_Object; + + if (!classcache_store_unique(pseudo_class_Null)) + vm_abort("linker_init: could not cache pseudo_class_Null"); + + if (!link_class(pseudo_class_Null)) + vm_abort("linker_init: linking failed"); + + /* pseudo class representing new uninitialized objects */ + + pseudo_class_New = class_create_classinfo(utf_new_char("$NEW$")); + pseudo_class_New->state |= CLASS_LOADED; + pseudo_class_New->state |= CLASS_LINKED; /* XXX is this allright? */ + pseudo_class_New->super = class_java_lang_Object; + + if (!classcache_store_unique(pseudo_class_New)) + vm_abort("linker_init: could not cache pseudo_class_New"); + + /* Correct vftbl-entries (retarded loading and linking of class + java/lang/String). */ + + stringtable_update(); +} + + +/* link_class ****************************************************************** + + Wrapper function for link_class_intern to ease monitor enter/exit + and exception handling. + +*******************************************************************************/ + +classinfo *link_class(classinfo *c) +{ + classinfo *r; +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_end; +#endif + + RT_TIMING_GET_TIME(time_start); + + if (c == NULL) { + exceptions_throw_nullpointerexception(); + return NULL; + } + + LOCK_MONITOR_ENTER(c); + + /* Maybe the class is currently linking or is already linked.*/ + + if ((c->state & CLASS_LINKING) || (c->state & CLASS_LINKED)) { + LOCK_MONITOR_EXIT(c); + + return c; + } + +#if defined(ENABLE_STATISTICS) + /* measure time */ + + if (opt_getcompilingtime) + compilingtime_stop(); + + if (opt_getloadingtime) + loadingtime_start(); +#endif + + /* call the internal function */ + + r = link_class_intern(c); + + /* If return value is NULL, we had a problem and the class is not + linked. */ + + if (r == NULL) + c->state &= ~CLASS_LINKING; + +#if defined(ENABLE_STATISTICS) + /* measure time */ + + if (opt_getloadingtime) + loadingtime_stop(); + + if (opt_getcompilingtime) + compilingtime_start(); +#endif + + LOCK_MONITOR_EXIT(c); + + RT_TIMING_GET_TIME(time_end); + + RT_TIMING_TIME_DIFF(time_start,time_end,RT_TIMING_LINK_TOTAL); + + return r; +} + + +/* linker_overwrite_method ***************************************************** + + Overwrite a method with another one, update method flags and check + assumptions. + + IN: + mg................the general method being overwritten + ms................the overwriting (more specialized) method + wl................worklist where to add invalidated methods + + RETURN VALUE: + true..............everything ok + false.............an exception has been thrown + +*******************************************************************************/ + +static bool linker_overwrite_method(methodinfo *mg, + methodinfo *ms, + method_worklist **wl) +{ + classinfo *cg; + classinfo *cs; + + cg = mg->clazz; + cs = ms->clazz; + + /* overriding a final method is illegal */ + + if (mg->flags & ACC_FINAL) { + exceptions_throw_verifyerror(mg, "Overriding final method"); + return false; + } + + /* method ms overwrites method mg */ + +#if defined(ENABLE_VERIFIER) + /* Add loading constraints (for the more general types of method mg). */ + /* Not for , as it is not invoked virtually. */ + + if ((ms->name != utf_init) + && !classcache_add_constraints_for_params( + cs->classloader, cg->classloader, mg)) + { + return false; + } +#endif + + /* inherit the vftbl index, and record the overwriting */ + + ms->vftblindex = mg->vftblindex; + ms->overwrites = mg; + + /* update flags and check assumptions */ + /* methods are a special case, as they are never dispatched dynamically */ + + if ((ms->flags & ACC_METHOD_IMPLEMENTED) && ms->name != utf_init) { + do { + +#if defined(ENABLE_TLH) + if (mg->flags & ACC_METHOD_MONOMORPHY_USED) { + printf("%s/%s is evil! the siner is %s/%s\n", mg->clazz->name->text, mg->name->text, + ms->clazz->name->text, ms->name->text); + ms->flags |= ACC_METHOD_PARENT_MONOMORPHY_USED; + } +#endif + + if (mg->flags & ACC_METHOD_IMPLEMENTED) { + /* this adds another implementation */ + + mg->flags &= ~ACC_METHOD_MONOMORPHIC; + + INLINELOG( printf("becomes polymorphic: "); method_println(mg); ); + + method_break_assumption_monomorphic(mg, wl); + } + else { + /* this is the first implementation */ + + mg->flags |= ACC_METHOD_IMPLEMENTED; + + INLINELOG( printf("becomes implemented: "); method_println(mg); ); + } + + ms = mg; + mg = mg->overwrites; + } while (mg != NULL); + } + + return true; +} + + +/* link_class_intern *********************************************************** + + Tries to link a class. The function calculates the length in bytes + that an instance of this class requires as well as the VTBL for + methods and interface methods. + +*******************************************************************************/ + +static int build_display_inner(classinfo *topc, classinfo *c, int i) +{ + int depth; + if (!c) + return 0; + do { + if (c->vftbl->arraydesc) + { + arraydescriptor *a = c->vftbl->arraydesc; + if (a->elementvftbl && a->elementvftbl->clazz->super) + { + classinfo *cls = a->elementvftbl->clazz->super; + int n; + for (n=0; ndimension; n++) + cls = class_array_of(cls, true); + depth = build_display_inner(topc, cls, i+1); + break; + } + if (a->componentvftbl && a->elementvftbl) + { + depth = build_display_inner(topc, a->componentvftbl->clazz, i+1); + break; + } + } + depth = build_display_inner(topc, c->super, i+1); + } while (false); + if (depth >= DISPLAY_SIZE) + { + if (depth == DISPLAY_SIZE) + { - topc->vftbl->subtype_overflow_length = i+1; + topc->vftbl->subtype_overflow = malloc(sizeof(void*) * (i+1)); +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += sizeof(void*) * (i+1); +#endif + } + topc->vftbl->subtype_overflow[depth - DISPLAY_SIZE] = c->vftbl; + return depth + 1; + } + topc->vftbl->subtype_display[depth] = c->vftbl; + return depth + 1; +} + +static void build_display(classinfo *c) +{ + int depth; + int i; + + depth = build_display_inner(c, c, 0) - 1; + c->vftbl->subtype_depth = depth; + if (depth >= DISPLAY_SIZE) + { + c->vftbl->subtype_offset = OFFSET(vftbl_t, subtype_display[DISPLAY_SIZE]); + } + else + { + c->vftbl->subtype_offset = OFFSET(vftbl_t, subtype_display[0]) + sizeof(void*) * depth; + for (i=depth+1; i<=DISPLAY_SIZE; i++) + c->vftbl->subtype_display[i] = NULL; - c->vftbl->subtype_overflow_length = 0; + } +} + +static classinfo *link_class_intern(classinfo *c) +{ + classinfo *super; /* super class */ + classinfo *tc; /* temporary class variable */ + s4 supervftbllength; /* vftbllegnth of super class */ + s4 vftbllength; /* vftbllength of current class */ + s4 interfacetablelength; /* interface table length */ + vftbl_t *v; /* vftbl of current class */ + s4 i; /* interface/method/field counter */ + arraydescriptor *arraydesc; /* descriptor for array classes */ + method_worklist *worklist; /* worklist for recompilation */ +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_resolving, time_compute_vftbl, + time_abstract, time_compute_iftbl, time_fill_vftbl, + time_offsets, time_fill_iftbl, time_finalizer, + time_subclasses; +#endif + + RT_TIMING_GET_TIME(time_start); + + TRACELINKCLASS(c); + + /* the class must be loaded */ + + /* XXX should this be a specific exception? */ + assert(c->state & CLASS_LOADED); + + /* This is check in link_class. */ + + assert(!(c->state & CLASS_LINKED)); + + /* cache the self-reference of this class */ + /* we do this for cases where the defining loader of the class */ + /* has not yet been recorded as an initiating loader for the class */ + /* this is needed so subsequent code can assume that self-refs */ + /* will always resolve lazily */ + /* No need to do it for the bootloader - it is always registered */ + /* as initiating loader for the classes it loads. */ + if (c->classloader) + classcache_store(c->classloader,c,false); + + /* this class is currently linking */ + + c->state |= CLASS_LINKING; + + arraydesc = NULL; + worklist = NULL; + + /* Link the super interfaces. */ + + for (i = 0; i < c->interfacescount; i++) { + tc = c->interfaces[i]; + + if (!(tc->state & CLASS_LINKED)) + if (!link_class(tc)) + return NULL; + } + + /* check super class */ + + super = NULL; + + /* Check for java/lang/Object. */ + + if (c->super == NULL) { + c->index = 0; + c->instancesize = sizeof(java_object_t); + + vftbllength = supervftbllength = 0; + + c->finalizer = NULL; + } + else { + /* Get super class. */ + + super = c->super; + + /* Link the super class if necessary. */ + + if (!(super->state & CLASS_LINKED)) + if (!link_class(super)) + return NULL; + + /* OR the ACC_CLASS_HAS_POINTERS and the ACC_CLASS_REFERENCE_* + flags. */ + + c->flags |= (super->flags & + (ACC_CLASS_HAS_POINTERS | ACC_CLASS_REFERENCE_MASK)); + + /* handle array classes */ + + if (c->name->text[0] == '[') + if (!(arraydesc = link_array(c))) + return NULL; + + if (c->flags & ACC_INTERFACE) + c->index = interfaceindex++; + else + c->index = super->index + 1; + + c->instancesize = super->instancesize; + + vftbllength = supervftbllength = super->vftbl->vftbllength; + + c->finalizer = super->finalizer; + } + RT_TIMING_GET_TIME(time_resolving); + + + /* compute vftbl length */ + + for (i = 0; i < c->methodscount; i++) { + methodinfo *m = &(c->methods[i]); + + if (!(m->flags & ACC_STATIC)) { /* is instance method */ + tc = super; + + while (tc) { + s4 j; + + for (j = 0; j < tc->methodscount; j++) { + if (method_canoverwrite(m, &(tc->methods[j]))) { + if (tc->methods[j].flags & ACC_PRIVATE) + goto notfoundvftblindex; + + /* package-private methods in other packages */ + /* must not be overridden */ + /* (see Java Language Specification 8.4.8.1) */ + if ( !(tc->methods[j].flags & (ACC_PUBLIC | ACC_PROTECTED)) + && !SAME_PACKAGE(c,tc) ) + { + goto notfoundvftblindex; + } + + if (!linker_overwrite_method(&(tc->methods[j]), m, &worklist)) + return NULL; + + goto foundvftblindex; + } + } + + tc = tc->super; + } + + notfoundvftblindex: + m->vftblindex = (vftbllength++); + foundvftblindex: + ; + } + } + RT_TIMING_GET_TIME(time_compute_vftbl); + + + /* Check all interfaces of an abstract class (maybe be an + interface too) for unimplemented methods. Such methods are + called miranda-methods and are marked with the ACC_MIRANDA + flag. VMClass.getDeclaredMethods does not return such + methods. */ + + if (c->flags & ACC_ABSTRACT) { + classinfo *ic; + methodinfo *im; + s4 abstractmethodscount; + s4 j; + s4 k; + + abstractmethodscount = 0; + + /* check all interfaces of the abstract class */ + + for (i = 0; i < c->interfacescount; i++) { + ic = c->interfaces[i]; + + for (j = 0; j < ic->methodscount; j++) { + im = &(ic->methods[j]); + + /* skip `' and `' */ + + if ((im->name == utf_clinit) || (im->name == utf_init)) + continue; + + for (tc = c; tc != NULL; tc = tc->super) { + for (k = 0; k < tc->methodscount; k++) { + if (method_canoverwrite(im, &(tc->methods[k]))) + goto noabstractmethod; + } + } + + abstractmethodscount++; + + noabstractmethod: + ; + } + } + + if (abstractmethodscount > 0) { + methodinfo *am; + + /* reallocate methods memory */ + + c->methods = MREALLOC(c->methods, methodinfo, c->methodscount, + c->methodscount + abstractmethodscount); + + for (i = 0; i < c->interfacescount; i++) { + ic = c->interfaces[i]; + + for (j = 0; j < ic->methodscount; j++) { + im = &(ic->methods[j]); + + /* skip `' and `' */ + + if ((im->name == utf_clinit) || (im->name == utf_init)) + continue; + + for (tc = c; tc != NULL; tc = tc->super) { + for (k = 0; k < tc->methodscount; k++) { + if (method_canoverwrite(im, &(tc->methods[k]))) + goto noabstractmethod2; + } + } + + /* Copy the method found into the new c->methods + array and tag it as miranda-method. */ + + am = &(c->methods[c->methodscount]); + c->methodscount++; + + MCOPY(am, im, methodinfo, 1); + + am->vftblindex = (vftbllength++); + am->clazz = c; + am->flags |= ACC_MIRANDA; + + noabstractmethod2: + ; + } + } + } + } + RT_TIMING_GET_TIME(time_abstract); + + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += + sizeof(vftbl_t) + (sizeof(methodptr) * (vftbllength - 1)); +#endif + + /* compute interfacetable length */ + + interfacetablelength = 0; + + for (tc = c; tc != NULL; tc = tc->super) { + for (i = 0; i < tc->interfacescount; i++) { + s4 h = class_highestinterface(tc->interfaces[i]) + 1; + + if (h > interfacetablelength) + interfacetablelength = h; + } + } + RT_TIMING_GET_TIME(time_compute_iftbl); + + /* allocate virtual function table */ + + v = (vftbl_t *) mem_alloc(sizeof(vftbl_t) + + sizeof(methodptr) * (vftbllength - 1) + + sizeof(methodptr*) * (interfacetablelength - (interfacetablelength > 0))); + v = (vftbl_t *) (((methodptr *) v) + + (interfacetablelength - 1) * (interfacetablelength > 1)); + + c->vftbl = v; + v->clazz = c; + v->vftbllength = vftbllength; + v->interfacetablelength = interfacetablelength; + v->arraydesc = arraydesc; + + /* store interface index in vftbl */ + + if (c->flags & ACC_INTERFACE) + v->baseval = -(c->index); + + /* copy virtual function table of super class */ + + for (i = 0; i < supervftbllength; i++) + v->table[i] = super->vftbl->table[i]; + + /* Fill the remaining vftbl slots with the AbstractMethodError + stub (all after the super class slots, because they are already + initialized). */ + + for (; i < vftbllength; i++) { +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (opt_intrp) + v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror; + else +# endif + v->table[i] = (methodptr) (ptrint) &asm_abstractmethoderror; +#else + v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror; +#endif + } + + /* add method stubs into virtual function table */ + + for (i = 0; i < c->methodscount; i++) { + methodinfo *m = &(c->methods[i]); + + assert(m->stubroutine == NULL); + + /* Don't create a compiler stub for abstract methods as they + throw an AbstractMethodError with the default stub in the + vftbl. This entry is simply copied by sub-classes. */ + + if (m->flags & ACC_ABSTRACT) + continue; + +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (opt_intrp) + m->stubroutine = intrp_createcompilerstub(m); + else +#endif + m->stubroutine = CompilerStub_generate(m); +#else + m->stubroutine = intrp_createcompilerstub(m); +#endif + + /* static methods are not in the vftbl */ + + if (m->flags & ACC_STATIC) + continue; + + /* insert the stubroutine into the vftbl */ + + v->table[m->vftblindex] = (methodptr) (ptrint) m->stubroutine; + } + RT_TIMING_GET_TIME(time_fill_vftbl); + + /* compute instance size and offset of each field */ + + for (i = 0; i < c->fieldscount; i++) { + s4 dsize; + fieldinfo *f = &(c->fields[i]); + + if (!(f->flags & ACC_STATIC)) { + dsize = descriptor_typesize(f->parseddesc); + c->instancesize = MEMORY_ALIGN(c->instancesize, dsize); + f->offset = c->instancesize; + c->instancesize += dsize; + } + } + RT_TIMING_GET_TIME(time_offsets); + + /* initialize interfacetable and interfacevftbllength */ + + v->interfacevftbllength = MNEW(s4, interfacetablelength); + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += (4 + sizeof(s4)) * v->interfacetablelength; +#endif + + for (i = 0; i < interfacetablelength; i++) { + v->interfacevftbllength[i] = 0; + v->interfacetable[-i] = NULL; + } + + /* add interfaces */ + + for (tc = c; tc != NULL; tc = tc->super) + for (i = 0; i < tc->interfacescount; i++) + if (!linker_addinterface(c, tc->interfaces[i])) + return NULL; + + RT_TIMING_GET_TIME(time_fill_iftbl); + + /* add finalizer method (not for java.lang.Object) */ + + if (super) { + methodinfo *fi; + + fi = class_findmethod(c, utf_finalize, utf_void__void); + + if (fi) + if (!(fi->flags & ACC_STATIC)) + c->finalizer = fi; + } + RT_TIMING_GET_TIME(time_finalizer); + + /* final tasks */ + + linker_compute_subclasses(c); + + /* FIXME: this is completely useless now */ + RT_TIMING_GET_TIME(time_subclasses); + + build_display(c); + + /* revert the linking state and class is linked */ + + c->state = (c->state & ~CLASS_LINKING) | CLASS_LINKED; + + /* check worklist */ + + /* XXX must this also be done in case of exception? */ + + while (worklist != NULL) { + method_worklist *wi = worklist; + + worklist = worklist->next; + + INLINELOG( printf("MUST BE RECOMPILED: "); method_println(wi->m); ); + jit_invalidate_code(wi->m); + + /* XXX put worklist into dump memory? */ + FREE(wi, method_worklist); + } + + RT_TIMING_TIME_DIFF(time_start ,time_resolving ,RT_TIMING_LINK_RESOLVE); + RT_TIMING_TIME_DIFF(time_resolving ,time_compute_vftbl,RT_TIMING_LINK_C_VFTBL); + RT_TIMING_TIME_DIFF(time_compute_vftbl,time_abstract ,RT_TIMING_LINK_ABSTRACT); + RT_TIMING_TIME_DIFF(time_abstract ,time_compute_iftbl,RT_TIMING_LINK_C_IFTBL); + RT_TIMING_TIME_DIFF(time_compute_iftbl,time_fill_vftbl ,RT_TIMING_LINK_F_VFTBL); + RT_TIMING_TIME_DIFF(time_fill_vftbl ,time_offsets ,RT_TIMING_LINK_OFFSETS); + RT_TIMING_TIME_DIFF(time_offsets ,time_fill_iftbl ,RT_TIMING_LINK_F_IFTBL); + RT_TIMING_TIME_DIFF(time_fill_iftbl ,time_finalizer ,RT_TIMING_LINK_FINALIZER); + RT_TIMING_TIME_DIFF(time_finalizer ,time_subclasses ,RT_TIMING_LINK_SUBCLASS); + + /* just return c to show that we didn't had a problem */ + + return c; +} + + +/* link_array ****************************************************************** + + This function is called by link_class to create the arraydescriptor + for an array class. + + This function returns NULL if the array cannot be linked because + the component type has not been linked yet. + +*******************************************************************************/ + +static arraydescriptor *link_array(classinfo *c) +{ + classinfo *comp; + s4 namelen; + arraydescriptor *desc; + vftbl_t *compvftbl; + utf *u; + + comp = NULL; + namelen = c->name->blength; + + /* Check the component type */ + + switch (c->name->text[1]) { + case '[': + /* c is an array of arrays. */ + u = utf_new(c->name->text + 1, namelen - 1); + if (!(comp = load_class_from_classloader(u, c->classloader))) + return NULL; + break; + + case 'L': + /* c is an array of objects. */ + u = utf_new(c->name->text + 2, namelen - 3); + if (!(comp = load_class_from_classloader(u, c->classloader))) + return NULL; + break; + } + + /* If the component type has not been linked, link it now */ + + assert(!comp || (comp->state & CLASS_LOADED)); + + if (comp && !(comp->state & CLASS_LINKED)) + if (!link_class(comp)) + return NULL; + + /* Allocate the arraydescriptor */ + + desc = NEW(arraydescriptor); + + if (comp) { + /* c is an array of references */ + desc->arraytype = ARRAYTYPE_OBJECT; + desc->componentsize = sizeof(void*); + desc->dataoffset = OFFSET(java_objectarray_t, data); + + compvftbl = comp->vftbl; + + if (!compvftbl) { + log_text("Component class has no vftbl"); + assert(0); + } + + desc->componentvftbl = compvftbl; + + if (compvftbl->arraydesc) { + desc->elementvftbl = compvftbl->arraydesc->elementvftbl; + + if (compvftbl->arraydesc->dimension >= 255) { + log_text("Creating array of dimension >255"); + assert(0); + } + + desc->dimension = compvftbl->arraydesc->dimension + 1; + desc->elementtype = compvftbl->arraydesc->elementtype; + + } else { + desc->elementvftbl = compvftbl; + desc->dimension = 1; + desc->elementtype = ARRAYTYPE_OBJECT; + } + + } else { + /* c is an array of a primitive type */ + switch (c->name->text[1]) { + case 'Z': + desc->arraytype = ARRAYTYPE_BOOLEAN; + desc->dataoffset = OFFSET(java_booleanarray_t,data); + desc->componentsize = sizeof(u1); + break; + + case 'B': + desc->arraytype = ARRAYTYPE_BYTE; + desc->dataoffset = OFFSET(java_bytearray_t,data); + desc->componentsize = sizeof(u1); + break; + + case 'C': + desc->arraytype = ARRAYTYPE_CHAR; + desc->dataoffset = OFFSET(java_chararray_t,data); + desc->componentsize = sizeof(u2); + break; + + case 'D': + desc->arraytype = ARRAYTYPE_DOUBLE; + desc->dataoffset = OFFSET(java_doublearray_t,data); + desc->componentsize = sizeof(double); + break; + + case 'F': + desc->arraytype = ARRAYTYPE_FLOAT; + desc->dataoffset = OFFSET(java_floatarray_t,data); + desc->componentsize = sizeof(float); + break; + + case 'I': + desc->arraytype = ARRAYTYPE_INT; + desc->dataoffset = OFFSET(java_intarray_t,data); + desc->componentsize = sizeof(s4); + break; + + case 'J': + desc->arraytype = ARRAYTYPE_LONG; + desc->dataoffset = OFFSET(java_longarray_t,data); + desc->componentsize = sizeof(s8); + break; + + case 'S': + desc->arraytype = ARRAYTYPE_SHORT; + desc->dataoffset = OFFSET(java_shortarray_t,data); + desc->componentsize = sizeof(s2); + break; + + default: + exceptions_throw_noclassdeffounderror(c->name); + return NULL; + } + + desc->componentvftbl = NULL; + desc->elementvftbl = NULL; + desc->dimension = 1; + desc->elementtype = desc->arraytype; + } + + return desc; +} + + +/* linker_compute_subclasses *************************************************** + + XXX + + ATTENTION: DO NOT REMOVE ANY OF THE LOCKING MECHANISMS BELOW: + This function needs to take the class renumber lock and stop the + world during class renumbering. The lock is used in C code which + is not that performance critical. Whereas JIT code uses critical + sections to atomically access the class values. + +*******************************************************************************/ + +static void linker_compute_subclasses(classinfo *c) +{ + + if (!(c->flags & ACC_INTERFACE)) { + c->nextsub = NULL; + c->sub = NULL; + c->vftbl->baseval = 1; /* so it does not look like an interface */ + } + + if (!(c->flags & ACC_INTERFACE) && (c->super != NULL)) { + c->nextsub = c->super->sub; + c->super->sub = c; + } + + classvalue = 0; + +} + + +/* linker_compute_class_values ************************************************* + + XXX + +*******************************************************************************/ + +static void linker_compute_class_values(classinfo *c) +{ + classinfo *subs; + + c->vftbl->baseval = ++classvalue; + + subs = c->sub; + + while (subs) { + linker_compute_class_values(subs); + + subs = subs->nextsub; + } + + c->vftbl->diffval = classvalue - c->vftbl->baseval; +} + + +/* linker_addinterface ********************************************************* + + Is needed by link_class for adding a VTBL to a class. All + interfaces implemented by ic are added as well. + + RETURN VALUE: + true.........everything ok + false........an exception has been thrown + +*******************************************************************************/ + +static bool linker_addinterface(classinfo *c, classinfo *ic) +{ + s4 j, k; + vftbl_t *v; + s4 i; + classinfo *sc; + methodinfo *m; + + v = c->vftbl; + i = ic->index; + + if (i >= v->interfacetablelength) + vm_abort("Internal error: interfacetable overflow"); + + /* if this interface has already been added, return immediately */ + + if (v->interfacetable[-i] != NULL) + return true; + + if (ic->methodscount == 0) { /* fake entry needed for subtype test */ + v->interfacevftbllength[i] = 1; + v->interfacetable[-i] = MNEW(methodptr, 1); + v->interfacetable[-i][0] = NULL; + } + else { + v->interfacevftbllength[i] = ic->methodscount; + v->interfacetable[-i] = MNEW(methodptr, ic->methodscount); + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += sizeof(methodptr) * + (ic->methodscount + (ic->methodscount == 0)); +#endif + + for (j = 0; j < ic->methodscount; j++) { + for (sc = c; sc != NULL; sc = sc->super) { + for (k = 0; k < sc->methodscount; k++) { + m = &(sc->methods[k]); + + if (method_canoverwrite(m, &(ic->methods[j]))) { + /* method m overwrites the (abstract) method */ +#if defined(ENABLE_VERIFIER) + /* Add loading constraints (for the more + general types of the method + ic->methods[j]). */ + if (!classcache_add_constraints_for_params( + c->classloader, ic->classloader, + &(ic->methods[j]))) + { + return false; + } +#endif + + /* XXX taken from gcj */ + /* check for ACC_STATIC: IncompatibleClassChangeError */ + + /* check for !ACC_PUBLIC: IllegalAccessError */ + + /* check for ACC_ABSTRACT: AbstracMethodError, + not sure about that one */ + + v->interfacetable[-i][j] = v->table[m->vftblindex]; + goto foundmethod; + } + } + } + + /* If no method was found, insert the AbstractMethodError + stub. */ + +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (opt_intrp) + v->interfacetable[-i][j] = + (methodptr) (ptrint) &intrp_asm_abstractmethoderror; + else +# endif + v->interfacetable[-i][j] = + (methodptr) (ptrint) &asm_abstractmethoderror; +#else + v->interfacetable[-i][j] = + (methodptr) (ptrint) &intrp_asm_abstractmethoderror; +#endif + + foundmethod: + ; + } + } + + /* add superinterfaces of this interface */ + + for (j = 0; j < ic->interfacescount; j++) + if (!linker_addinterface(c, ic->interfaces[j])) + return false; + + /* everything ok */ + + return true; +} + + +/* class_highestinterface ****************************************************** + + Used by the function link_class to determine the amount of memory + needed for the interface table. + +*******************************************************************************/ + +static s4 class_highestinterface(classinfo *c) +{ + s4 h; + s4 h2; + s4 i; + + /* check for ACC_INTERFACE bit already done in link_class_intern */ + + h = c->index; + + for (i = 0; i < c->interfacescount; i++) { + h2 = class_highestinterface(c->interfaces[i]); + + if (h2 > h) + h = h2; + } + + return h; +} + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --cc src/vm/linker.h index 124f8b731,000000000..b9dc1876f mode 100644,000000..100644 --- a/src/vm/linker.h +++ b/src/vm/linker.h @@@ -1,173 -1,0 +1,172 @@@ +/* src/vm/linker.h - class linker header + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#ifndef _LINKER_H +#define _LINKER_H + +/* forward typedefs ***********************************************************/ + +typedef struct _vftbl vftbl_t; +typedef struct arraydescriptor arraydescriptor; +typedef struct primitivetypeinfo primitivetypeinfo; + + +#include "config.h" +#include "vm/types.h" + +#include "threads/mutex.hpp" + +#include "vm/class.h" +#include "vm/references.h" + + +/* virtual function table ****************************************************** + + The vtbl has a bidirectional layout with open ends at both sides. + interfacetablelength gives the number of entries of the interface + table at the start of the vftbl. The vftbl pointer points to + &interfacetable[0]. vftbllength gives the number of entries of + table at the end of the vftbl. + + runtime type check (checkcast): + + Different methods are used for runtime type check depending on the + argument of checkcast/instanceof. + + A check against a class is implemented via relative numbering on + the class hierachy tree. The tree is numbered in a depth first + traversal setting the base field and the diff field. The diff field + gets the result of (high - base) so that a range check can be + implemented by an unsigned compare. A sub type test is done by + checking the inclusion of base of the sub class in the range of the + superclass. + + A check against an interface is implemented via the + interfacevftbl. If the interfacevftbl contains a nonnull value a + class is a subclass of this interface. + + interfacetable: + + Like standard virtual methods interface methods are called using + virtual function tables. All interfaces are numbered sequentially + (starting with zero). For each class there exist an interface table + of virtual function tables for each implemented interface. The + length of the interface table is determined by the highest number + of an implemented interface. + + The following example assumes a class which implements interface 0 and 3: + + interfacetablelength = 4 + + | ... | +----------+ + +-----------+ | method 2 |---> method z + | class | | method 1 |---> method y + +-----------+ | method 0 |---> method x + | ivftbl 0 |----------> +----------+ + vftblptr ---> +-----------+ + | ivftbl -1 |--> NULL +----------+ + | ivftbl -2 |--> NULL | method 1 |---> method x + | ivftbl -3 |-----+ | method 0 |---> method a + +-----------+ +----> +----------+ + + +---------------+ + | length 3 = 2 | + | length 2 = 0 | + | length 1 = 0 | + | length 0 = 3 | + interfacevftbllength ---> +---------------+ + +*******************************************************************************/ + +#define DISPLAY_SIZE 4 + +struct _vftbl { + methodptr *interfacetable[1]; /* interface table (access via macro) */ + classinfo *clazz; /* class, the vtbl belongs to */ + arraydescriptor *arraydesc; /* for array classes, otherwise NULL */ + s4 vftbllength; /* virtual function table length */ + s4 interfacetablelength; /* interface table length */ + s4 baseval; /* base for runtime type check */ + /* (-index for interfaces) */ + s4 diffval; /* high - base for runtime type check */ + + s4 subtype_depth; - ptrint subtype_offset; ++ s4 subtype_offset; + struct _vftbl *subtype_display[DISPLAY_SIZE+1]; /* the last one is cache */ - s4 subtype_overflow_length; + struct _vftbl **subtype_overflow; + + s4 *interfacevftbllength; /* length of interface vftbls */ + methodptr table[1]; /* class vftbl */ +}; + + +/* arraydescriptor ************************************************************* + + For every array class an arraydescriptor is allocated which + describes the array class. The arraydescriptor is referenced from + the vftbl of the array class. + +*******************************************************************************/ + +struct arraydescriptor { + vftbl_t *componentvftbl; /* vftbl of the component type, NULL for primit. */ + vftbl_t *elementvftbl; /* vftbl of the element type, NULL for primitive */ + s2 arraytype; /* ARRAYTYPE_* constant */ + s2 dimension; /* dimension of the array (always >= 1) */ + s4 dataoffset; /* offset of the array data from object pointer */ + s4 componentsize; /* size of a component in bytes */ + s2 elementtype; /* ARRAYTYPE_* constant */ +}; + + +/* function prototypes ********************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +void linker_preinit(void); +void linker_init(void); +classinfo *link_class(classinfo *c); + +#ifdef __cplusplus +} +#endif + +#endif /* _LINKER_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + */