X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=doc%2Fnestedvm.ivme04.tex;h=aa823a70b417c06829d4dacbfce475a197820994;hp=87be5a30f146cc5636eb1227c9fc4fc8e7bff7a5;hb=04c36ab522bc8f0c3c8dd7f8bde0859abaa4c480;hpb=e8cd1c6c6f65ee7d573de0cc927944f2c79265f5 diff --git a/doc/nestedvm.ivme04.tex b/doc/nestedvm.ivme04.tex index 87be5a3..aa823a7 100644 --- a/doc/nestedvm.ivme04.tex +++ b/doc/nestedvm.ivme04.tex @@ -189,7 +189,7 @@ running C/C++ code within a Java VM is shown in Figure~\ref{lattice}. A number of commercial products and research projects attempt to translate C++ code to Java code, preserving the mapping of C++ classes -to Java classes. Unfortunately this is problematic since there is no +to Java classes. Unfortunately, this is problematic since there is no way to do pointer arithmetic except within arrays, and even in that case, arithmetic cannot be done in terms of fractional objects. @@ -383,11 +383,11 @@ public void trampoline() { \caption{\label{code1} Trampoline transformation necessitated by Java's 64kb method size limit} \end{figure*} -Unfortunately Java imposes a 64kb limit on the size of the bytecode +Unfortunately, Java imposes a 64kb limit on the size of the bytecode for a single method. This presents a problem for NestedVM, and necessitates a {\it trampoline transformation}, as shown in Figure~\ref{code1}. With this trampoline in place somewhat large -binaries can be handled without much difficulty -- fortunately there +binaries can be handled without much difficulty -- fortunately, there is no corresponding limit on the size of a classfile as a whole. Another interesting problem that was discovered while creating the @@ -409,7 +409,11 @@ switch(pc&0xffffff00) { \begin{figure} {\footnotesize\begin{verbatim} -Brian, we're missing the code here... can you put it in? +switch(pc>>>8) { + case 0x1: run_100(); + case 0x2: run_200(); + case 0x3: run_300(); +} \end{verbatim}} \caption{\label{tableswitch} Code which {\it is} optimized into a tableswitch} \end{figure} @@ -483,10 +487,10 @@ identified. The sources for possible jump targets come from 3 places. Eliminating unnecessary case statements provided a 10-25\% speed increase. -Despite all the above optimizations and workaround an impossible to +Despite all the above optimizations and workarounds an impossible to workaround hard classfile limit was eventually hit, the constant pool. The constant pool in classfiles is limited to 65536 -entries. Every Integer with a magnitude greater than 32767 requires an +entries. Every integer with a magnitude greater than 32767 requires an entry in the constant pool. Every time the compiler emits a jump or branch instruction the PC field is set to the branch target. This means nearly every branch instruction requires an entry in the @@ -551,7 +555,7 @@ if(condition) { pc = TARGET; continue; } This requires a branch in the JVM regardless of whether the MIPS branch is actually taken. If condition is false the JVM has to jump over the code to set the PC and go back to the switch block. If -condition is true the JVM as to jump to the switch block. By +condition is true the JVM has to jump to the switch block. By generating bytecode directly we can make the target of the JVM branch statement the actual bytecode of the final destination. In the case where the branch isn't taken the JVM doesn't need to branch at all. @@ -611,7 +615,7 @@ registers are not visible to the branch bytecode. This allows delay slots to be used with no performance penalty or size penalty. One final advantage that generating bytecode directly allows is -smaller more compact bytecode. All the optimization above lead to +smaller more compact bytecode. All the optimizations above lead to smaller bytecode as a side effect. There are also a few other areas where the generated bytecode can be optimized for size with more knowledge of the program as a whole. @@ -633,39 +637,101 @@ prepare for a invoke special call. By simple moving this outside the switch statement each case arm was reduced in size by one instruction. Similar optimizations were also done in other parts of the compiler. - \section{Interfacing with Java Code} -Java source code can create a copy of the translated binary by -instantiating the corresponding class, which extends {\tt Runtime}. -Invoking the {\tt main()} method on this class is equivalent to -calling the {\tt main()} function within the binary; the {\tt String} -arguments to this function are copied into the binary's memory space -and made available as {\tt **argv} and {\tt argc}. +NestedVM has two primary ways of executing code, the interpreter, and the binary translators. Both the interpreter and the output from the binary translators sit on top of a Runtime class. This class provides the public interface to both the interpreter and the translated binaries. -The translated binary communicates with the rest of the VM by -executing MIPS {\tt SYSCALL} instructions, which are translated into -invocations of the {\tt syscall()} method. This calls back to the -native Java world, which can manipulate the binary's environment by -reading and writing to its memory space, checking its exit status, -pausing the VM, and restarting the VM. +\subsection{The Runtime Class} +The Runtime class does the work that the operating system usually does. Conceptually the Runtime class can be thought of as the operating system and itÕs subclasses (translated binaries and the interpreter) the CPU. The Runtime fulfills 5 primary goals: -\subsection{Virtualization} +\begin{itemize} -The {\tt Runtime} class implements the majority of the standard {\tt -libc} syscalls, providing a complete interface to the filesystem, -network socket library, time of day, (Brian: what else goes here?). +\item Provides a consistent external interface - The method of actually executing the code (currently only translated binaries and the interpreter) can be changed without any code changes to the caller because only Runtime exposes a public interface. -\begin{itemize} +\item Provide an easy to use interface - The interpreter and the output from the binary translators only know how to execute code. The Runtime class provides an easy to use interface to the code. It contains methods to pass arguments to the main() function, read and write from memory, and call individual functions in the binary. -\item ability to provide the same interface to CNI code and - NestedVMified code - -\item security advantages (chroot the {\tt fork()}ed process) +\item Manage the processÕs memory - The Runtime class contains large int[] arrays that represent the process`s entire memory space. Subclasses read and write to these arrays as required by the instructions they are executing. Subclasses can expend their memory space using the sbrk syscall. + +\item Provide access to the file system and streams - Subclasses access the file system through standard UNIX syscalls (read, write, open, etc). The Runtime manages the file descriptor table that maps UNIX file descriptors to Java RandomAccessFiles, InputStreams, OutputStreams, and sockets. + +\item Miscellaneous other syscalls - In additions to those mentioned above the Runtime class implements a variety of other syscalls (sleep, gettimeofday, getpagesize, sysconf, fcntl, etc). \end{itemize} +\subsection{Interacting with the Binary} + +Java source code can create a copy of the translated binary by instantiating the class generated by the binary translator or instantiating the interpreter. It can then interact with the process through the many facilities provided by the Runtime interface. Invoking the run() method of the Runtime interface will load the given arguments into the processÕs memory as invoke the binaries entry point (typically \_start() in crt0.o). This will pass control on to the main() function which will have the arguments passed to run() loaded into argv and argc. + +As the binary executes it often passes control back to the Runtime class through the MIPS {\tt SYSCALL} instruction. The interpreter and translated binaries invoke the {\tt syscall()} method of the Runtime class when the {\tt SYSCALL} instruction is executed. The Runtime class then can manipulate the processÕs environment (read and write to memory, modify the file descriptor table, etc) and interact with the rest of the JVM on behalf of the process (read and write to a file or stream, etc). There is even a syscall to pause the VM and temporarily return control to the caller. + +In addition to the interfaces provided by NestedVM, users can create their own interfaces between the MIPS and Java world. The Runtime provides a method called call() that will call a function by name in the MIPS binary. The call() method looks up the function name in the binaryÕs ELF symbol table and manipulating the stack and registers accordingly to execute the given function. This allows Java code to seamlessly invoke functions in the binary. + +{\footnotesize\begin{verbatim} +// Java +private Runtime rt = new MyBinary(); +public void foo(int n) { + for(int i=0;i<10;i++) { + int result = rt.call("do_work",i); + System.err.println("do_work(i) = " + result); + } +} +// C +void do_work(int n) { + int i; + int ret=0; + for(i=0;i