Re: [Sablevm-developer] types of stack frames
Brought to you by:
egagnon
From: Etienne G. <gag...@uq...> - 2004-04-02 08:12:21
|
Chris Pickett wrote: > Can you please write about the types of stack frames / methods that > might be on the stack in SableVM, and when they are pushed and popped? > For one, I would like to know about pushing and popping native methods. SableVM has 3 types of "method frames" on a thread stack: - Normal Java method frame - Native method frame - Internal method frame Both the normal Java method and native method frames are already explained in stack_layout.txt. Internal method frame ===================== When JNI_CreateJavaVM is invoked (and for each new thread), an initial stack is created. (a) It needs a bottom "method frame", yet, we are not yet executing any specific Java method. So, the frame type needs to be some non-Java frame type. We do need a frame as JNI calls need to work, and particularly local native reference must be allocatable. [local native references are stored on the stack]. (b) But, we are not executing any specific "native" method, so, the method frame cannot be of type "native method". 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, in fact, 3 "subtypes" of internal method frames: - stack_bottom_method: The initial frame of the stack of a thread. - vm_initiated_call_method: Used by the VM when it calls (of its own) a Java method. (See below for motivation.) - internal_call_method: Used by the VM when executing JNI Call* instructions. stack_bottom_method ------------------- mostly self explanatory. vm_initiated_call_method ------------------------ SableVM often calls Java methods of its own. 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 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: | | current_frame - +--------------------+ method->start_offset |local 0 | * | 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_off ---> | | 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 | @ | | @ | | @ | | @ | | @ +--------------------+ @ But, we DON'T want normal execution to continue! We want _svmf_interpreter() to "return" so that we can resume the execution of the GETSTATIC instruction. Of course, one try to make a more complex implementation of the RETURN bytecode that would be smarter and somehow detect that the method was the bottom one of the "current" invocation of _svmf_interpreter(), and that would thus simply "return". Instead, SableVM uses a simpler strategy; it inserts a "fake" method frame (e.g. internal method frame), which has a frame->pc pointing to a INTERNAL_CALL_END (sablevm-specific) bytecode instruction. e.g. | | +--------------------+ |local 0 | * | 1 | * | 2 | * | ... | * |local n | * +--------------------+ * | stack_frame_struct | * current frame | | * | | * +--------------------+ * | padding to align | * +--------------------+ * | expression stack | * | | * | | * | | * | | * +--------------------+ * | stack_frame_struct | |(pc = INT._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 bytecode, it simply continues execution at the previous frame's frame->pc bytecode, which happens, in this case, to be INTERNAL_CALL_END. As you can guess, INTERNAL_CALL_END's body first removes the "internal frame", and executes a C "return" statement that terminates the _svmf_interpreter() function call. internal_call_method -------------------- It's the exact same idea as vm_initiated_call_method, but it is used when executing a JNI Call* function. We use a "different" internal frame to distinguish between "application call stack" (which includes Java methods and JNI functions), and "vm initiated" call stack. This is necessary for all sort of things like implementing SecurityManager.currentClassLoader() calls reliably. >... I'll > take whatever you write and incorporate it into David's > "stack_layout.txt" document. Thanks! Etienne -- Etienne M. Gagnon, Ph.D. http://www.info.uqam.ca/~egagnon/ SableVM: http://www.sablevm.org/ SableCC: http://www.sablecc.org/ |