#include "types.h" #include "trace.h" #include "constants.h" #include "specialsignatures.h" #include "specialclasses.h" #include "memory.h" #include "threads.h" #include "classes.h" #include "language.h" #include "configure.h" #include "interpreter.h" #include "exceptions.h" #include "stdlib.h" #ifdef VERIFY static boolean memoryInitialized = false; #endif #define NULL_OFFSET 0xFFFF // Size of stack frame in 2-byte words #define NORM_SF_SIZE ((sizeof(StackFrame) + 1) / 2) byte typeSize[] = { 4, // 0 == T_REFERENCE SF_SIZE, // 1 == T_STACKFRAME 0, // 2 0, // 3 1, // 4 == T_BOOLEAN 2, // 5 == T_CHAR 4, // 6 == T_FLOAT 8, // 7 == T_DOUBLE 1, // 8 == T_BYTE 2, // 9 == T_SHORT 4, // 10 == T_INT 8 // 11 == T_LONG }; typedef struct MemoryRegion_S { #if SEGMENTED_HEAP struct MemoryRegion_S *next; /* pointer to next region */ #endif TWOBYTES *end; /* pointer to end of region */ TWOBYTES contents; /* start of contents, even length */ } MemoryRegion; /** * Beginning of heap. */ #if SEGMENTED_HEAP static MemoryRegion *memory_regions; /* list of regions */ #else static MemoryRegion *region; /* list of regions */ #endif static TWOBYTES memory_size; /* total number of words in heap */ static TWOBYTES memory_free; /* total number of free words in heap */ extern void deallocate (TWOBYTES *ptr, TWOBYTES size); extern TWOBYTES *allocate (TWOBYTES size); /** * @param numWords Number of 2-byte words used in allocating the object. */ #define initialize_state(OBJ_,NWORDS_) zero_mem(((TWOBYTES *) (OBJ_)) + NORM_OBJ_SIZE, (NWORDS_) - NORM_OBJ_SIZE) #define get_object_size(OBJ_) (get_class_record(get_na_class_index(OBJ_))->classSize) /** * Zeroes out memory. * @param ptr The starting address. * @param numWords Number of two-byte words to clear. */ void zero_mem (register TWOBYTES *ptr, register TWOBYTES numWords) { while (numWords--) *ptr++ = 0; } static inline void set_array (Object *obj, const byte elemType, const TWOBYTES length) { #ifdef VERIFY assert (elemType <= (ELEM_TYPE_MASK >> ELEM_TYPE_SHIFT), MEMORY0); assert (length <= (ARRAY_LENGTH_MASK >> ARRAY_LENGTH_SHIFT), MEMORY1); #endif obj->flags.all = IS_ALLOCATED_MASK | IS_ARRAY_MASK | ((TWOBYTES) elemType << ELEM_TYPE_SHIFT) | length; #ifdef VERIFY assert (is_array(obj), MEMORY3); #endif } Object *memcheck_allocate (const TWOBYTES size) { Object *ref; ref = (Object *) allocate (size); if (ref == JNULL) { #ifdef VERIFY assert (outOfMemoryError != null, MEMORY5); #endif throw_exception (outOfMemoryError); return JNULL; } ref->monitorCount = 0; ref->threadId = 0; #if SAFE ref->flags.all = 0; #endif return ref; } /** * Checks if the class needs to be initialized. * If so, the static initializer is dispatched. * Otherwise, an instance of the class is allocated. * * @param btAddr Back-track PC address, in case * a static initializer needs to be invoked. * @return Object reference or null iff * NullPointerException had to be thrown or * static initializer had to be invoked. */ Object *new_object_checked (const byte classIndex, byte *btAddr) { #if 0 trace (-1, classIndex, 0); #endif if (dispatch_static_initializer (get_class_record(classIndex), btAddr)) { #if DEBUG_MEMORY printf("New object checked returning null\n"); #endif return JNULL; } return new_object_for_class (classIndex); } /** * Allocates and initializes the state of * an object. It does not dispatch static * initializers. */ Object *new_object_for_class (const byte classIndex) { Object *ref; TWOBYTES instanceSize; #if DEBUG_MEMORY printf("New object for class %d\n", classIndex); #endif instanceSize = get_class_record(classIndex)->classSize; ref = memcheck_allocate (instanceSize); if (ref == null) { #if DEBUG_MEMORY printf("New object for class returning null\n"); #endif return JNULL; } // Initialize default values ref->flags.all = IS_ALLOCATED_MASK | classIndex; initialize_state (ref, instanceSize); #if DEBUG_OBJECTS || DEBUG_MEMORY printf ("new_object_for_class: returning %d\n", (int) ref); #endif return ref; } /** * Return the size in words of an array of the given type */ TWOBYTES comp_array_size (const TWOBYTES length, const byte elemType) { return NORM_OBJ_SIZE + (((TWOBYTES) length * typeSize[elemType]) + 1) / 2; } /** * Allocates an array. The size of the array is NORM_OBJ_SIZE * plus the size necessary to contain length elements * of the given type. */ Object *new_primitive_array (const byte primitiveType, STACKWORD length) { Object *ref; TWOBYTES allocSize; // Check if length is too large to be representable if (length > (ARRAY_LENGTH_MASK >> ARRAY_LENGTH_SHIFT)) { throw_exception (outOfMemoryError); return JNULL; } allocSize = comp_array_size (length, primitiveType); #if DEBUG_MEMORY printf("New array of type %d, length %ld\n", primitiveType, length); #endif ref = memcheck_allocate (allocSize); #if DEBUG_MEMORY printf("Array ptr=%d\n", (int)ref); #endif if (ref == null) return JNULL; set_array (ref, primitiveType, length); initialize_state (ref, allocSize); return ref; } TWOBYTES get_array_size (Object *obj) { return comp_array_size (get_array_length (obj), get_element_type (obj)); } void free_array (Object *objectRef) { #ifdef VERIFY assert (is_array(objectRef), MEMORY7); #endif // VERIFY deallocate ((TWOBYTES *) objectRef, get_array_size (objectRef)); } #if !FIXED_STACK_SIZE Object *reallocate_array(Object *obj, STACKWORD newlen) { byte elemType = get_element_type(obj); Object *newArray = new_primitive_array(elemType, newlen); // If can't allocate new array, give in! if (newArray != JNULL) { // Copy old array to new memcpy(((byte *) newArray + HEADER_SIZE), ((byte *) obj + HEADER_SIZE), get_array_length(obj) * typeSize[elemType]); // Free old array free_array(obj); } return newArray; } #endif /** * @param elemType Type of primitive element of multi-dimensional array. * @param totalDimensions Same as number of brackets in array class descriptor. * @param reqDimensions Number of requested dimensions for allocation. * @param numElemPtr Pointer to first dimension. Next dimension at numElemPtr+1. */ Object *new_multi_array (byte elemType, byte totalDimensions, byte reqDimensions, STACKWORD *numElemPtr) { Object *ref; #ifdef WIMPY_MATH Object *aux; TWOBYTES ne; #endif #ifdef VERIFY assert (totalDimensions >= 1, MEMORY6); assert (reqDimensions <= totalDimensions, MEMORY8); #endif #if 0 printf ("new_multi_array (%d, %d, %d)\n", (int) elemType, (int) totalDimensions, (int) reqDimensions); #endif if (reqDimensions == 0) return JNULL; #if 0 printf ("num elements: %d\n", (int) *numElemPtr); #endif if (totalDimensions == 1) return new_primitive_array (elemType, *numElemPtr); ref = new_primitive_array (T_REFERENCE, *numElemPtr); if (ref == JNULL) return JNULL; while ((*numElemPtr)--) { #ifdef WIMPY_MATH aux = new_multi_array (elemType, totalDimensions - 1, reqDimensions - 1, numElemPtr + 1); ne = *numElemPtr; ref_array(ref)[ne] = ptr2word (aux); #else ref_array(ref)[*numElemPtr] = ptr2word (new_multi_array (elemType, totalDimensions - 1, reqDimensions - 1, numElemPtr + 1)); #endif // WIMPY_MATH } return ref; } #ifdef WIMPY_MATH void store_word (byte *ptr, byte aSize, STACKWORD aWord) { byte *wptr; byte ctr; wptr = &aWord; ctr = aSize; while (ctr--) { #if LITTLE_ENDIAN ptr[ctr] = wptr[aSize-ctr-1]; #else ptr[ctr] = wptr[ctr]; #endif // LITTLE_ENDIAN } } #else /** * Problem here is bigendian v. littleendian. Java has its * words stored bigendian, intel is littleendian. */ STACKWORD get_word(byte *ptr, byte aSize) { STACKWORD aWord = 0; while (aSize--) { aWord = (aWord << 8) | (STACKWORD)(*ptr++); } return aWord; } void store_word (byte *ptr, byte aSize, STACKWORD aWord) { ptr += aSize-1; while (aSize--) { *ptr-- = (byte) aWord; aWord = aWord >> 8; } } #endif // WIMPY_MATH typedef union { struct { byte byte0; byte byte1; byte byte2; byte byte3; } st; STACKWORD word; } AuxStackUnion; void make_word (byte *ptr, byte aSize, STACKWORD *aWordPtr) { // This switch statement is // a workaround for a h8300-gcc bug. switch (aSize) { case 1: *aWordPtr = (JINT) (JBYTE) (ptr[0]); return; case 2: *aWordPtr = (JINT) (JSHORT) (((TWOBYTES) ptr[0] << 8) | (ptr[1])); return; #ifdef VERIFY default: assert (aSize == 4, MEMORY9); #endif // VERIFY } #if LITTLE_ENDIAN ((AuxStackUnion *) aWordPtr)->st.byte3 = *ptr++; ((AuxStackUnion *) aWordPtr)->st.byte2 = *ptr++; ((AuxStackUnion *) aWordPtr)->st.byte1 = *ptr++; ((AuxStackUnion *) aWordPtr)->st.byte0 = *ptr; #else ((AuxStackUnion *) aWordPtr)->st.byte0 = *ptr++; ((AuxStackUnion *) aWordPtr)->st.byte1 = *ptr++; ((AuxStackUnion *) aWordPtr)->st.byte2 = *ptr++; ((AuxStackUnion *) aWordPtr)->st.byte3 = *ptr; #endif } #if DEBUG_RCX_MEMORY void scan_memory (TWOBYTES *numNodes, TWOBYTES *biggest, TWOBYTES *freeMem) { } #endif // DEBUG_RCX_MEMORY void memory_init () { #ifdef VERIFY memoryInitialized = true; #endif #if SEGMENTED_HEAP memory_regions = null; #endif memory_size = 0; memory_free = 0; } /** * @param region Beginning of region. * @param size Size of region in bytes. */ void memory_add_region (byte *start, byte *end) { #if SEGMENTED_HEAP MemoryRegion *region; #endif TWOBYTES contents_size; /* word align upwards */ region = (MemoryRegion *) (((unsigned int)start+1) & ~1); #if SEGMENTED_HEAP /* initialize region header */ region->next = memory_regions; /* add to list */ memory_regions = region; #endif region->end = (TWOBYTES *) ((unsigned int)end & ~1); /* word align downwards */ /* create free block in region */ contents_size = region->end - &(region->contents); ((Object*)&(region->contents))->flags.all = contents_size; /* memory accounting */ memory_size += contents_size; memory_free += contents_size; #if SEGMENTED_HEAP #if DEBUG_MEMORY printf ("Added memory region\n"); printf (" start: %5d\n", (int)start); printf (" end: %5d\n", (int)end); printf (" region: %5d\n", (int)region); printf (" region->next: %5d\n", (int)region->next); printf (" region->end: %5d\n", (int)region->end); printf (" memory_regions: %5d\n", (int)memory_regions); printf (" contents_size: %5d\n", (int)contents_size); #endif #endif } /** * @param size Size of block including header in 2-byte words. */ TWOBYTES *allocate (TWOBYTES size) { #if SEGMENTED_HEAP MemoryRegion *region; #endif #if DEBUG_MEMORY printf("Allocate %d - free %d\n", size, memory_free-size); #endif #if SEGMENTED_HEAP for (region = memory_regions; region != null; region = region->next) #endif { TWOBYTES *ptr = &(region->contents); TWOBYTES *regionTop = region->end; while (ptr < regionTop) { TWOBYTES blockHeader = *ptr; if (blockHeader & IS_ALLOCATED_MASK) { /* jump over allocated block */ TWOBYTES s = (blockHeader & IS_ARRAY_MASK) ? get_array_size ((Object *) ptr) : get_object_size ((Object *) ptr); ptr += s; } else { if (size <= blockHeader) { /* allocate from this block */ #if COALESCE { TWOBYTES nextBlockHeader; /* NOTE: Theoretically there could be adjacent blocks that are too small, so we never arrive here and fail. However, in practice this should suffice as it keeps the big block at the beginning in one piece. Putting it here saves code space as we only have to search through the heap once, and deallocat remains simple. */ while (true) { TWOBYTES *next_ptr = ptr + blockHeader; nextBlockHeader = *next_ptr; if (next_ptr >= regionTop || (nextBlockHeader & IS_ALLOCATED_MASK) != 0) break; blockHeader += nextBlockHeader; *ptr = blockHeader; } } #endif if (size < blockHeader) { /* cut into two blocks */ blockHeader -= size; /* first block gets remaining free space */ *ptr = blockHeader; ptr += blockHeader; /* second block gets allocated */ #if SAFE *ptr = size; #endif /* NOTE: allocating from the top downwards avoids most searching through already allocated blocks */ } memory_free -= size; return ptr; } else { /* continue searching */ ptr += blockHeader; } } } } /* couldn't allocate block */ return JNULL; } /** * @param size Must be exactly same size used in allocation. */ void deallocate (TWOBYTES *p, TWOBYTES size) { #ifdef VERIFY assert (size <= (FREE_BLOCK_SIZE_MASK >> FREE_BLOCK_SIZE_SHIFT), MEMORY3); #endif memory_free += size; #if DEBUG_MEMORY printf("Deallocate %d at %d - free %d\n", size, (int)p, memory_free); #endif ((Object*)p)->flags.all = size; } int getHeapSize() { return ((int)memory_size) << 1; } int getHeapFree() { return ((int)memory_free) << 1; } int getRegionAddress() { #if SEGMENTED_HEAP return 0xf002; #else return (int)region; #endif }