Quick Links:

Releases | Mailing Lists | Source Control | Issue Tracker | Regression Tests

15 Magic

Chapter 15
Magic

Most Java runtimes rely upon the foreign language APIs of the underlying platform operating system to implement runtime behaviour which involves interaction with the underlying platform. Runtimes also occasionally employ small segments of machine code to provide access to platform hardware state. Note that this is expedient rather than mandatory. With a suitably smart Java bytecode compiler it would be quite possible to implement a full Java-in-Java runtime i.e. one comprising only compiled Java code (the JNode project is an attempt to implement a runtime along these lines; the Xerox, MIT, Lambda and TI Explorer Lisp machine implementations and the Xerox Smalltalk implementation were highly successful attemtps at fully compiled language runtimes).

This section provides information on magic which is an escape hatch that JikesTM RVM provides to implement functionality that is not possible using the pure JavaTM programming language. For example, the Jikes RVM garbage collectors and runtime system must, on occasion, access memory or perform unsafe casts. The compiler will also translate a call to Magic.threadSwitch() into a sequence of machine code that swaps out old thread registers and swaps in new ones, switching execution to the new thread’s stack resumed at its saved PC

There are three mechanisms via which the Jikes RVM magic is implemented:

The mechanisms are used to implement the following functionality:

15.1 Compiler Intrinsics

A compiler intrinsic will usually generate a specific code sequence. The code sequence will usually be inlined and optimized as part of compilation phase of the optimizing compiler.

Magic

All the methods in Magic are compiler intrinsics. Because these methods access raw memory or other machine state, perform unsafe casts, or are operating system calls, they cannot be implemented in Java code.

A JikesTM RVM implementor must be extremely careful when writing code that uses Magic to circumvent the Java type system. The use of Magic.objectAsAddress to perform various forms of pointer arithmetic is especially hazardous, since it can result in pointers being ”lost” during garbage collection. All such uses of magic must either occur in uninterruptible methods or be guarded by calls to VM.disableGC and VM.enableGC. The optimizing compiler performs aggressive inlining and code motion, so not explicitly marking such dangerous regions in one of these two manners will lead to disaster.

Since magic is inexpressible in the Java programming language , it is unsurprising that the bodies of Magic methods are undefined. Instead, for each of these methods, the Java instructions to generate the code is stored in GenerateMagic and GenerateMachineSpecificMagic (to generate HIR) and the baseline compilers (to generate assembly code) (Note: The optimizing compiler always uses the set of instructions that generate HIR; the instructions that generate assembly code are only invoked by the baseline compiler.). Whenever the compiler encounters a call to one of these magic methods, it inlines appropriate code for the magic method into the caller method.

sun.misc.Unsafe

The methods of sun.misc.Unsafe are not treated specially by the compilers. The Jikes RVM ships a custom sun.misc.Unsafe implementation that implements the operations with Jikes RVM magics and internal helper routines.

15.2 Unboxed Types

If a type is boxed then it means that values of that type are represented by a pointer to a heap object. An unboxed type is represented by the value itself such as int, double, float, byte etc.

In the Jikes RVM terminology, an unboxed type is a custom unboxed type. Normal Java primitives such as int are never referred to as unboxed types.

The Jikes RVM also defines a number of unboxed types. Due to a limitation of the way the compiler generates code the Jikes RVM must define an unboxed array type for each unboxed type. The unboxed types are:

Values of unboxed types appear only in the virtual machine’s stack, registers, or as fields/elements of class/array instances.

Unboxed types may inherit from Object but they are not objects. As such there are some restrictions on the use of unboxed types:

15.3 Raw Memory Access

The type org.vmmagic.Address is used to represent a machine-dependent address type. org.vmmagic.Address is an unboxed type. In the past, the base type int was used to represent addresses but this approach had several shortcomings. First, the lack of abstraction makes porting nightmarish. Equally important is that Java type int is signed whereas addresses are more appropriately considered unsigned. The difference is problematic since an unsigned comparison on int is inexpressible in the Java programming language.

To overcome these problems, instances of org.vmmagic.Address are used to represent addresses. The class supports the expected well-typed methods like adding an integer offset to an address to obtain another address, computing the difference of two addresses, and comparing addresses. Other operations that make sense on int but not on addresses are excluded like multiplication of addresses. Two methods deserve special attention: converting an address into an integer and the inverse. These methods should be avoided where possible.

Without special intervention, using a Java object to represent an address would be at best abysmally inefficient. Instead, when the Jikes RVM compiler encounters creation of an address object, it will return the primitive value that represents an address for that platform. Currently, the address type maps to either a 32-bit or 64-bit unsigned integer. Since an address is an unboxed type it must obey the rules outlined in Unboxed Types.

15.4 Uninterruptible Code

Declaring a method uninterruptible enables a Jikes RVM developer to prevent the Jikes RVM compilers from inserting ”hidden” thread switch points in the compiled code for the method. As a result, the code can be written assuming that it cannot involuntarily ”lose control” while executing due to a timer-driven thread switch. In particular, neither yield points nor stack overflow checks will be generated for uninterruptible methods. When writing uninterruptible code, the programmer is restricted to a subset of the Java language. The following are the restrictions on uninterruptible code.

We have augmented the baseline compiler to print a warning message when one of these restrictions is violated. The optimizing compiler currently does not check for uninterruptibility violations. Consequently, it is a good idea to compile a boot image with the baseline compiler (e.g. using prototype-opt) after modifying uninterruptible code.

If uninterruptible code were to raise a runtime exception such as NullPointerException, ArrayIndexOutOfBoundsException, or ClassCastException, then it could be interrupted. We assume that such conditions are a programming error (or VM bug) and do not flag bytecodes that might result in one of these exceptions being raised as a violation of uninterruptibility.

In a few cases it is necessary to modify the conditions of checking for uninterruptibility to avoid spurious warning messages. This should be done with extreme care. The checking conditions for a particular method can be modified by using one of the following annotations:

Do not use the annotation org.vmmagic.pragma.LogicallyUninterruptible. Its usage is being phased out.

The following rules determine whether or not a method is uninterruptible.

Whether to annotate a class or a method with org.vmmagic.pragma.Uninterruptible is a matter of taste and mainly depends on the ratio of interruptible to uninterruptible methods in a class. If most methods of the class should be uninterruptible, then annotating the class is preferred.