-
Notifications
You must be signed in to change notification settings - Fork 0
Unboxed Types and VM Magic
Unboxed types (
Address,Word,Offset,Extent) represent raw machine values that the JIT compiler translates into native instructions, bypassing Java object allocation.
JNode's VM needs to manipulate raw memory addresses and perform low-level operations that normal Java cannot express. The org.vmmagic.unboxed package provides four value types that appear as Java classes but are compiled directly into machine registers by the JIT compiler.
The key insight from the source code (every unboxed class has this comment):
"This class contains some 'magic' methods that are interpreted by the VM itself, instead of being executed as normal java methods. The actual method bodies are never used."
All four types share a common structure:
-
final long v— the underlying machine value - Private constructor (only
BootImageBuildercan create instances) - Static factory methods (
fromInt,zero,max) - Arithmetic and comparison operations
-
toWord()/toOffset()/toExtent()conversion methods
File: core/src/vmmagic/org/vmmagic/unboxed/Address.java
Represents a raw memory pointer. This is the primary type for:
- Memory-mapped I/O operations
- DMA buffer addressing
- Direct memory access
Address ptr = Address.fromInt(0x1000);
Address ptr2 = ptr.add(16); // pointer arithmetic
int value = ptr.loadInt(); // read 4 bytes from memory
ptr.store(value + 1); // write 4 bytes to memoryKey operations:
-
add(Offset),sub(Offset)— pointer arithmetic -
loadInt(),loadLong(),loadAddress()— memory reads -
store(int),store(Address)— memory writes -
prepareWord(),attempt(old, new)— atomic compare-and-swap -
diff(Address)— returns Offset between two pointers
File: core/src/vmmagic/org/vmmagic/unboxed/Word.java
An unsigned machine-word integer. Used for:
- Low-level bitwise operations
- Bit masking and shifting
- Atomic operations
Word bits = Word.zero();
bits = bits.or(new Word(0xFF));
int shifted = bits.lsh(4).toInt();Key operations:
-
and(Word),or(Word),xor(Word)— bitwise -
lsh(int),rshl(int),rsha(int)— shifts (logical/arithmetic) -
not()— bitwise negation -
toAddress(),toOffset()— type conversions
File: core/src/vmmagic/org/vmmagic/unboxed/Offset.java
A signed offset value, used for pointer arithmetic:
- Field offset calculations in object layout
- Array index calculations
- Memory region sizing
Offset fieldOffset = Offset.fromInt(12);
ObjectReference obj = ...;
Address fieldAddr = obj.toAddress().add(fieldOffset);
int fieldValue = fieldAddr.loadInt();Key operations:
-
add(int),sub(int)— arithmetic -
sLT(),sLE(),sGT(),sGE()— signed comparisons -
EQ(),NE()— equality checks
File: core/src/vmmagic/org/vmmagic/unboxed/Extent.java
An unsigned size/length value. Used for:
- Memory allocation sizes
- Buffer lengths
- Data structure sizes
Extent size = Extent.fromInt(1024);
Extent remaining = size.sub(Extent.fromInt(256));Key operations:
-
add(Extent),sub(Extent)— arithmetic -
LT(),LE(),GT(),GE()— unsigned comparisons
The JIT compiler recognizes these four types specially:
-
No object allocation — When the compiler sees
new Address(value), it allocates the value in a register instead of on the heap. -
Inline machine instructions — Method calls like
address.loadInt()are intercepted byMagicHelperand replaced withMOVinstructions:address.loadInt() → MOV eax, [address]
-
Register-based arithmetic — Operations like
ptr.add(16)becomeADD ptr, 16directly in a register. -
No null checks — Unlike normal object references, unboxed types don't trigger null pointer exceptions until the actual memory access occurs.
All unboxed types implement UnboxedObject (marker interface with no methods):
public interface UnboxedObject {}This allows the compiler to quickly identify unboxed types via instanceof checks.
File: core/src/vmmagic/org/vmmagic/unboxed/ObjectReference.java
A raw pointer to a Java object header. Unlike Address, this type is:
- Used exclusively for GC operations
- Guaranteed to point to a valid object
- Convertible to/from Java objects
ObjectReference ref = ObjectReference.fromObject(myObject);
Address objAddress = ref.toAddress();
// Now we can use objAddress for memory scanningThe JIT compilers (L1, L1b, L2) contain MagicHelper classes that handle unboxed operations:
-
MagicHelper.processIntrinsic()— recognizes unboxed type operations - Emits x86 instructions directly (MOV, ADD, SUB, CMP, etc.)
- Handles the signed/unsigned distinction for comparisons
-
Signed vs unsigned confusion —
Offsetuses signed comparisons (sLT), whileExtentuses unsigned (LT). Mixing them up causes subtle bugs. -
No object header — Unboxed types have no identity, can't be stored in
Objectvariables, and can't be used withinstanceofagainst normal classes. -
Private constructors — Normal code cannot create unboxed types directly. Use factory methods (
fromInt,zero,max). -
Method bodies are stubs — The actual Java implementations in these classes are never executed. All real work happens in the JIT's
MagicHelper. -
Address != ObjectReference —
Addressis for raw memory;ObjectReferenceis specifically for pointing at object headers in the GC heap.
- VM-Magic — The broader magic framework including annotations
- Code-Conventions — Guidelines on using unboxed types
-
Object-Layout — How the GC uses
ObjectReferencefor heap scanning - JIT-Compilers — How MagicHelper integrates into the compiler pipeline