* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This source file is part of SableVM. * * * * See the file "LICENSE" for the copyright information and for * * the terms and conditions for copying, distribution and * * modification of this source file. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *** SableVM Java Stack Layout *** Introduction ------------ This document describes the stack layout used by SableVM. This is still work in progress and is still incomplete. Comments are welcome. To do: ------ - java stack frame with native refs - internal stack frame - initial (bottom) stack frame Document History ---------------- 2003-03-19 - Initial draft. General Info ------------ Note: In this document, when we simply use the word 'stack' we are refering to the java execution stack. - each thread has its own stack - info about a thread's stack can be obtained from the env pointer (env->stack) - the stack is contiguous - the stack may move in memory - the stack grows to from low address to high address. High-Level View --------------- env->stack The thread stack data structure. env->stack is of type _svmt_stack (defined in types.h): struct _svmt_stack_struct { /* each thread has a single contiguous "Java stack", that can grow (thus it can move around) */ void *start; void *end; _svmt_stack_frame *current_frame; }; start: pointer to beginning of memory allocated for stack end: pointer to the end current_frame: points to the beginning of a _svmt_stack_frame data structure on the stack. We will come back to this later. width is sizeof(_svmt_stack_value) (4 on Linux/x86, 8 on Linux/ppc for example) +--------------------+ env->stack.start -->| | | | | | | | | | | | | | | | | | | | | | +--------------------+ env->stack.end --> Note that stack.start points to the first element of the memory block allocated and stack.end points to the memory right after the stack block. Stack Initialization -------------------- Stack is created and initialized by jint _svmf_stack_init (_svmt_JNIEnv *env) defined in thread.c. Stack size is always a multiple of vm->stack_allocation_increment. Initial size will be picked such that it is >= vm->stack_min_size, big enough to fit the initial frame and "aligned" on the increment size. If the size needed for the initial frame is >= vm->stack_max_size, it will result in an out of memory error. TO DO: Describe initial frame layout. Allocating a Stack Frame ------------------------ Before allocating a new frame on the stack, a call to: static jint _svmf_ensure_stack_capacity (_svmt_JNIEnv *env, size_t frame_size) is required. This funtion is defined in thread.c. If not enough space is left on the stack, the stack memory will be reallocated. The new size will be a multiple of the stack incremented. Note that if the new size required is > stack_max_size, we get a out of memory error. The start, end and current_frame pointers may be set to new values when this function returns. In other words, the stack may have moved in memory. The Frame Struct ---------------- Each stack frame has a data structure _svmt_stack_frame. Note that it is not necessary located neither at the beginning nor at the end of a stack frame. struct _svmt_stack_frame_struct { size_t previous_offset; size_t end_offset; _svmt_method_info *method; _svmt_object_instance *stack_trace_element; /* cached element */ jint lock_count; /* structured locking */ _svmt_object_instance *this; /* for static methods, this points to the class instance */ _svmt_code *pc; jint stack_size; }; previous_offset: offset to get to the previous stack_frame_struct end_offset: offset to get to the end of the frame. Note: These are not absolute addresses (pointers) as the stack may move in memory due to reallocation. Note: All offsets are expressed as positive integers and are in number of bytes. Case 1: No Native References ---------------------------- | | * current_frame +--------------------+ * - previous_offset --> |stack_frame_struct | * | | * +--------------------+ * | | * | | * | | * previous frame | | * current_frame - +--------------------+ * method->start_offset |local 0 | * * | 1 | * * | 2 | * *__ last param | ... | * |local n | * env->stack. +--------------------+ * current_frame ----> | stack_frame_struct | * current frame | | * | | * +--------------------+ * | padding to align | * current_frame + +--------------------+ * _svmv_stack_offset -> | expression stack | * | | * | | * | | * | | * current_frame + +--------------------+ * end_off ---> | | Note how the previous frame and current frame overlaps such as the method's arguments pushed correspond to the first few locals of the current frame. Note that env->stack.current_frame points to the _svmt_stack_frame data structure and it is usually not at the beginning of the stack frame. Values on the stack: locals and values on the expression stack are of type _svmt_stack_value (defined in types.h): union _svmt_stack_value_union { jint jint; jfloat jfloat; _svmt_object_instance *reference; _svmt_code *addr; void *ptr; char alignment[SVM_ALIGNMENT]; }; The expression stack starts at an offset of _svmv_stack_offset (constant defined in thread.c): /* stack_offset */ static const size_t _svmv_stack_offset = (sizeof (_svmt_stack_frame) + (SVM_ALIGNMENT - 1)) & ~((size_t) (SVM_ALIGNMENT - 1)); Struct _svmt_method_frame_info ------------------------------ Contains information about the method as well as some info needed for stack frames. It is obtained from a method data structure: method->frame_info start_offset: Offset from current_frame to get the first local. end_offset: Used to set the _svmt_stack_frame end_offset. java_invoke_frame_size: java frame size for that method struct _svmt_method_frame_info_struct { /* The actual sablevm internal code array */ _svmt_code *code; /* number of non-parameter reference local variables */ jint non_parameter_ref_locals_count; size_t start_offset; size_t end_offset; size_t java_invoke_frame_size; size_t internal_invoke_frame_size; }; Summary: /* to get the current frame */ _svmt_stack_frame *frame = env->stack.current_frame; /* to get the current method */ _svmt_method_info *method = frame->method; /* to get a pointer to the beginning of locals */ locals = (_svmt_stack_value *) (((char *) frame) - method->frame_info->start_offset); /* to get a pointer to the beginning (bottom) of the expression stack */ stack = (_svmt_stack_value *) (((char *) frame) + _svmv_stack_offset); Case 2: With Native References ------------------------------ +----------- \ params --> | | | +- java_args_and_ret_count | | space reserved for Java args and return value | / |------------ ptrs --> |&env |&lrefs[0] | | |ptrs[i] = ¶ms[param] - if java arg is primitive |ptrs[i] = &lrefs[ref] - if java arg is ref | (or &nullvar-pointer to var set to NULL if ref is NULL) | | |-------------| lrefs --> |jobject 0 -------->+----------+ | |native ref| | +----------+ | | |jobject lrefs_count - 1 |lrefs_count |lrefs_size +----- <---- frame + frame_info->end_offset lrefs_count = # ref args + SVM_FRAME_NATIVE_REFS_MIN lrefs_size = total space (in bytes) occupied by native refs and including lrefs_count and lrefs_size. array of _svmt_stack_native_reference_union union _svmt_stack_native_reference_union { jint jint; /* used for lrefs_count */ size_t size_t; /* used for lrefs_size */ jobject jobject; /* used for jobject */ }; struct _svmt_native_ref_struct { _svmt_object_instance *ref; _svmt_native_ref *previous; _svmt_native_ref *next; }; typedef struct _svmt_object_instance_struct **jobject; Note: The jobjects on the stack are pointers to the ref in the native_ref struct. Initialization: 1. a new native local is obtained[1] for each local native on the stack in the lrefs array. etc. Result will go in params[0]. Note: For double/long may occupy both params[0]/params[1] slot. Footnotes: 1. Free lists are maintained, not always allocating memory ****