Evaluating Hardware/ Software Tradeoffs - Hardware extensions and the compilers that make them real


< Prev Contents  

Java

Java has become a major factor in computing. Java is interesting in that it's a software, not hardware, extension. Java defines a Java "Virtual Machine" as a software CPU that executes Java instructions. This machine is platform independent and can run on many different CPUs. Java defines its own software ISA.

Java's advantages were not in speed. It is defined as a byte-coded interpretive language. Java instructions are individually decoded and interpreted, which can make for slower executions on the order of 20 to 40 times slower than C or C++ code.

Java does have its advantages though. For one thing, it's a newer language, one designed for multimedia and GUI operations. For another, it's a subset/superset of C/C++. Among other things, Java does not support pointers. Pointers were the glory of C, enabling programmers great flexibility. They were also its bane; problems with pointers trouble many developers. Java takes a much more disciplined approach to pointers: they are called "references" and are either null or point to an actual object; no dangling pointers, no de-referencing pointers (to cause an exception). Java also fixes a number C/C++ problems: array indexing is checked (out-of-bounds index equaling an exception) and fields have defined initial values.

In many ways, Java is a smaller, simpler and better C and C++. Unlike C and C++, it defines a runtime or "Virtual Machine" that contains many of the functions routinely compiled into C programs. With Java, they are part of the virtual machine. Additionally, Java forgoes traditional memory management: it supports automatic memory management and garbage collection.

The traditional Java implementation model is to compile Java source into .class files containing machine-independent bye-code instructions. These .class files are downloaded and interpreted by a browser or a Java "Virtual Machine." Figure 8 shows the typical JAVA implementation.

Figure 8:  Typical Java implementation

Interpreting Java byte-codes makes Java code many times slower than comparable C or C++ programs. One approach to improving this situation is "Just-In-Time" (JIT) compilers. These dynamically translate byte-codes into machine code just before a method is first executed.

This is a major advance over slower interpretation, but has the drawback that the JIT compilation must be done each time the application executes. Moreover, since the JIT compiler must make use of run-time resources to compile, it cannot devote much time to serious optimizations. Compilers can because they use host development resources, not runtime resources. Additionally, JIT compilers need memory resources to run; this can be very wasteful for embedded systems that try to minimize memory costs. No JIT compiler today fits in less than 512 KB of local memory. That's wasteful for many memory critical embedded applications.

Compiled Java
Compiling Java eliminates many common Java problems, especially for speed of execution. Compiled Java will be platform specific; however, many programmers are more interested in Java as a modern language, extending clean C/C++ program output. Although Java is, still, without platform independent execution, especially with its large interpreter performance penalty, a compiled Java still defines a Java virtual machine. However, code developed for that machine is compiled on the host, converting Java source code to platform-specific runtime code. The Java runtime, which provides features such as garbage collection, and exception handling, is still needed. However, for many embedded applications, it is possible to dispense with the need for a full blown Java VM or JIT compiler.

Cygnus Solutions is working on a Java programming environment that uses a conventional compiler, linker and debugger. These are enhanced versions of the existing GNUPro C/C++ development tools that have been ported to most development systems for just about every microprocessor and micro-controller in use. With the GNUPro Java compiler, Java byte-codes (or source) will be portable to a wide range of development platforms and targets. Such an approach to implementing Java is also expected to deliver very high performance. See Figure 9 for an example of compiled Java.

The core of Cygnus Solutions' compiled Java is the jcl compiler, a new GCC front-end. This is similar to other GNU front-ends such as cc1plus for C++, and shares most of the front-end code. This new compiler, jcl, is unique in that it can read-in and compile either Java source files or Java byte-codes. Future versions will use an integrated parser that handles both source code and byte-codes. Even with source code operation, it's important to be able to handle byte-codes to support Java code from other tools and to use Java byte-code libraries.

The basic strategy for translating Java stack-oriented byte-code operations is to convert Java's stack-oriented operations into operations on variables. jcl creates a dummy local variable for each Java stack slot or local variable. These are mapped into GCC "virtual registers" and, then, into standard GCC register allocation assigns each "virtual register" to a hard register or to a C/C++ stack location.

The jcl program creates standard assembler files that are processed by the standard unmodified GNU assembler. The resulting object files can go into a dynamic or static library, or they can be linked together with the runtime into an executable. The standard linker needs to be able to support static initializers, but it already does that for C++.

A compiled Java program still needs a Java run-time environment, just as C++ or Ada requires its own run-time. Java, however, requires more run-time support than these mainstream languages. It needs support for threads, garbage collection, type reflection and all the primitive Java methods, as well as being able to dynamically load new Java byte-coded classes. This last feature may not be a requirement in some embedded applications that use fixed code. Cygnus Solutions is integrating Kaffe, a version of the Java run-time or Java Virtual Machine, with its Java compiler. Kaffe was developed by Tim Wilkinson who made it publicly available on the Internet.

A major factor in Java's success in becoming a mainstream programming language is having mainstream implementation techniques (specifically, an optimizing, ahead-of-time compiler that allows much better optimization along with much faster application start-up times than with JIT translators). Cygnus Solutions is writing a Java front-end for the GNU compiler, GCC, in order to translate Java bytecodes to machine code. This will take advantage of a widely used, proven technology in order to solve many future needs. See Figure 9 to see how Java works.

Figure 9:  Compiled Java


< Prev Contents