File: stack_layout.txt

package info (click to toggle)
sablevm 1.13-1.1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 10,084 kB
  • ctags: 9,231
  • sloc: ansic: 127,926; sh: 8,886; asm: 5,548; makefile: 693
file content (533 lines) | stat: -rw-r--r-- 19,919 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 a 
work in progress but is now mostly complete.  Comments are welcome. 
Written by DBP and EMG, with some edits by CJFP. 


To do:
------
- finish description of native method frame initialization 
- provide some high-level examples of what the stack looks like: 
    -- during vm startup 
    -- when calling into native methods 
    -- when calling from native methods into SableVM 

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
- a thread's stack is accessible through the env pointer (env->stack)
- the stack is contiguous
- the stack may move in memory
- the stack grows from a low address to a high address, but does not
  shrink in current or past versions (r2150).

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 an _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 
-------------------- 
The stack is created and initialized by 
jint _svmf_stack_init (_svmt_JNIEnv *env) 
defined in thread.c.

The stack size is always a multiple of vm->stack_allocation_increment.

The initial size is 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.

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 function 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 size
increment.  Note that if the new size required is > stack_max_size,
we get an 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.  It is not
necessarily located either at the beginning or 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 _svmt_stack_frame
     end_offset: offset to get to the end of the frame.

These are not absolute addresses (pointers) as the stack may move in
memory due to reallocation.  Rather, all offsets are expressed as
positive integers and are in number of bytes.

SableVM has 3 types of method frames on a thread stack:
- Normal Java method frame
- Native method frame
- Internal method frame

Case 1: Normal Java Method Frame
================================

                      |                    |   *
current_frame         +--------------------+   *
- previous_offset --> |stack_frame_struct  |   *
                      |                    |   *
                      +--------------------+   *
                      |                    |   *
                      |                    |   *
                      |                    |   * previous frame
                      |                    |   *
current_frame -       +--------------------+   *
method->frame_info->  |local 0             | * *
start_offset          |      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_offset       ---> |                    |


The previous frame and current frame overlap such that the method
arguments pushed by the previous frame correspond to the first few
locals of the current frame.

env->stack.current_frame points to the _svmt_stack_frame
data structure and it is usually not at the beginning of the stack
frame, although it will be if the current method is called by
an INVOKESTATIC that takes no arguments.  This is because only
INVOKESTATIC's do not require an objectref on the stack.

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 alignment[] array forces the stack value to have an
architecture-specific size.  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: Native Method Frame
===========================

            +-------------+ 
 params --> |             |    -- size of params is java_args_and_ret_count
            |             |    -- the space is reserved for Java args
            |             |       and the return value
            |             | 
            |-------------|
   ptrs --> |&env	  |
            |&lrefs[0]	  |
            |		  |  ptrs[i] = &params[param] 
            |             |    - if java arg is primitive
            |		  |  
	    |		  |  ptrs[i] = &lrefs[ref]
            |             |    - if java arg is ref and non-NULL
	    |             |  
	    |		  |  ptrs[i] = &nullvar (pointer to var set to NULL)
            |             |    - if java arg is ref and NULL
            |             |
            |-------------|
  lrefs --> |jobject 0  -------->+----------+
	    |             |      |native ref|
	    |             |      +----------+
            |		  |
	    |		  |
            |jobject lrefs_count - 1
	    |lrefs_count  |
            |lrefs_size   |
            +-------------+
                           <---- frame + frame_info->end_offset


lrefs_count = nubmer of ref args + SVM_FRAME_NATIVE_REFS_MIN
lrefs_size  = total space (in bytes) occupied by native refs and
              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     */
};

typedef struct _svmt_object_instance_struct **jobject;

The jobjects on the stack are pointers to the ref in the following
_svmt_native_ref struct.

struct _svmt_native_ref_struct
{
  _svmt_object_instance *ref;
  _svmt_native_ref *previous;
  _svmt_native_ref *next;
};


Initialization
--------------

1. A new native local is obtained for each local native on the stack in
   the lrefs array.  Free lists are maintained, and so it is not
   always necessary to allocate new memory.

TODO

The return value will go in params[0].  A double or long return value
may occupy both params[0]/params[1] slot.


Case 3: Internal Method Frame
=============================

When JNI_CreateJavaVM is invoked (and for each new thread), an
initial stack is created.  There are a couple of considerations:

(a) It needs a bottom "method frame".  Since we are not yet executing
any specific Java method, the frame needs to be of some non-Java frame
type.  We really do need a frame as JNI calls need to work, and in
particular local native reference must be allocatable.  In Case 2 we
saw that local native references are stored on the stack.

(b) However, we are not executing any specific "native" method, so the
method frame cannot be of type "native method" either.  But it should
be pretty similar, as we need native locals, etc.

So, SableVM adds a third type of frame: Internal method frame.
An internal method frame is identified by the SVM_ACC_INTERNAL
access flag.  There are 3 subtypes of internal method frames:

  (i) stack_bottom_method: The initial frame of the stack of a thread.

 (ii) vm_initiated_call_method: Used by the VM when it calls a Java method
      for itself (see below for motivation).

(iii) internal_call_method: Used by the VM when executing JNI Call*
      instructions.


(i) stack_bottom_method
-----------------------

mostly self explanatory.  see (a) above.


(ii) vm_initiated_call_method
-----------------------------

SableVM often calls Java methods for its own use.  A typical example of
such a call is the invocation of static initializers.  A static
initializer is never invoked directly from Java code, nor is it called
from JNI library code.

So, what's the problem?  Imagine the situation:  SableVM is executing
some Java code and reaches a GETSTATIC instruction.  A potential side
effect of executing such an instruction is the invocation of a static
initializer ("<clinit>") before actually executing the real access
to the static field.  Yet the current stack looks like this:

                       |                    |
current_frame -        +--------------------+
method->frame_info->   |local 0             | *
start_offset           |      1             | *
                       |      2             | *
                       |      ...           | *
                       |local n             | *
env->stack.            +--------------------+ *
current_frame   ---->  | stack_frame_struct | *  current frame
                       |                    | *
                       |                    | *
                       +--------------------+ *
                       | padding to align   | *
current_frame +        +--------------------+ *
_svmv_stack_offset ->  | expression stack   | *
                       |                    | *
                       |                    | *
                       |                    | *
                       |                    | *
current_frame +        +--------------------+ *
end_offset       --->  |                    |


If we simply put the static initializer frame after this frame,
and then invoke _svmf_interpreter() to start executing "<clinit>"
the RETURN instruction of the static initializer will look for
its previous frame and find the "current" frame, and discover
that it simply is a normal Java method frame, so it will continue
bytecode interpretation at current_frame->pc.  In other words,
the called _svmf_interpreter() will continue executing code.

                       |                    |
                       +--------------------+
                       |local 0             | *
                       |      1             | *
                       |      2             | *
                       |      ...           | *
                       |local n             | *
                       +--------------------+ *
                       | stack_frame_struct | *  current frame
                       |                    | *
                       |                    | *
                       +--------------------+ *
                       | padding to align   | *
                       +--------------------+ *
                       | expression stack   | *
                       |                    | *
                       |                    | *
                       |                    | *
                       |                    | *
                       +--------------------+ *
                       |local 0             |   @
                       |      1             |   @
                       |      2             |   @
                       |      ...           |   @
                       |local n             |   @
                       +--------------------+   @
                       | stack_frame_struct |   @ static initializer frame
                       |                    |   @
                       |                    |   @
                       +--------------------+   @
                       | padding to align   |   @
                       +--------------------+   @
                       | expression stack   |   @
                       |                    |   @
                       |                    |   @
                       |                    |   @
                       |                    |   @
                       +--------------------+   @


However, we don't want normal execution to continue!  We want the
actual call to _svmf_interpreter() to return so that we can resume
execution of the GETSTATIC instruction.  One might try to make a more
complex implementation of the RETURN bytecode that could somehow
detect that the current method was at the bottom of the current
invocation of _svmf_interpreter(), and would return to the previous
invocation of _svmf_interpreter() instead of returning erroneously to
the previous Java stack frame.

Instead, SableVM uses a simpler strategy.  It inserts a "fake" method
frame (i.e. internal method frame), which has a frame->pc pointing to
a SableVM-specific INTERNAL_CALL_END instruction.

  e.g.:

                       |                    |
                       +--------------------+
                       |local 0             | *
                       |      1             | *
                       |      2             | *
                       |      ...           | *
                       |local n             | *
                       +--------------------+ *
                       | stack_frame_struct | *  current frame
                       |                    | *
                       |                    | *
                       +--------------------+ *
                       | padding to align   | *
                       +--------------------+ *
                       | expression stack   | *
                       |                    | *
                       |                    | *
                       |                    | *
                       |                    | *
                       +--------------------+ *
                       | stack_frame_struct |
                       |(pc =               |
                       | INTERNAL_CALL_END) |
                       |                    |
                       |                    |
                       +--------------------+
                       |local 0             |   @
                       |      1             |   @
                       |      2             |   @
                       |      ...           |   @
                       |local n             |   @
                       +--------------------+   @
                       | stack_frame_struct |   @ static initializer frame
                       |                    |   @
                       |                    |   @
                       +--------------------+   @
                       | padding to align   |   @
                       +--------------------+   @
                       | expression stack   |   @
                       |                    |   @
                       |                    |   @
                       |                    |   @
                       |                    |   @
                       +--------------------+   @


This way, when _svmf_interpreter() executes the static initializer's
RETURN instruction, it simply continues execution at the previous
frame's frame->pc instruction, which happens in this case to be an
INTERNAL_CALL_END instruction.  As you can guess, INTERNAL_CALL_END's
body first removes the internal frame, and then executes a C return
statement that terminates the _svmf_interpreter() function call.

(iii) internal_call_method
--------------------------

We use a different internal frame to distinguish between the
"application call stack" (which includes Java methods and JNI
functions), and the "vm-initiated" call stack.  It's the exact same
idea as vm_initiated_call_method, but it is used when executing a JNI
Call* function.  This is necessary for all sort of things like
implementing SecurityManager.currentClassLoader() calls reliably.