 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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] = &params[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


****


