Executing ARM Jazelle (JVM Bytecode) on the Wii's Starlet
github.com
github.com
I never understood why ARM hid jazzelle docs away. It made no sense. It is probably why Jazzelle died.
There's that one ARM instruction specifically for JavaScript float-to-i32 casts, but at least that's relatively narrowly scoped.
To be clear, the behavior in question is Intel behavior that snuck its way into the JavaScript spec; ARM just doesn't wanna use the word "Intel" in public documentation. For similar reasons there's a few other places where there's an "alternate" execution mode that exists solely to emulate x86.
Is this something different from TSO?
It was designed for a market with feature phones running J2ME apps in about 4-8MB of RAM. The CPU was probably fast enough to do decent JIT compilation, but a JIT compiler would take a few hundred KB of code, and then suck up a large chunk of RAM for the code cache.
As far as I'm aware, it's is not as fast as a JIT would be on the same hardware (due to the stack machine), but Jazelle has the advantage of being significantly faster than an interpreter, while using about the same RAM (if not less).
By the time you could start doing full desktop OSes and full Java VMs on small ARM chips, Jazelle (DBX) had been stubbed out completely for years, handling only the most simple of opcodes or even none at all.
Because Jazelle converted Java bytecodes into ARM instructions in sequence, there is no opportunity for any instruction scheduling. So a bytecode sequence like:
// public static int get_x(int x, T a, T b) { return a.x+b.x; }
aload_1
getfield #N
aload_2
getfield #N
iadd
would go down the pipeline as something like: LDR r1, [r0, #4] // a_load1
* LDR r1, [r1] // getfield
LDR r2, [r0, #8] // aload_2
* LDR r2, [r2] // getfield
* ADD r1, r1, r2 // iadd
There would be a pipeline stall before each instruction marked with a *.On the first ARM 9 CPUs with Jazelle, the pipeline is fairly similar to the standard 5 stage RISC pipeline (Fetch-Decode-Execute-MemoryAccess-Writeback) so this stall would be 1 cycle. That wasn't too bad - you could just accept that loads took usally 2 cycles, and it would still be pretty fast.
However, on later CPUs with a longer pipeline the load-use delay was increased. By ARM11, it was 2 cycles - so now the CPU is spending more time waiting for pipeline stalls that it spends actually executing instructions.
In contrast, even a basic JIT can implement instruction scheduling and find some independent instructions to do between a load and the use of the result, which makes the JIT much more performant than Jazelle could be.
Of course it might just also have been a cooperation with a specific JVM vendor (that could then promote their JVM as the fastest on a given set of CPUs).
.byte 0x05 @ iconst_2
.byte 0x06 @ iconst_3
.byte 0x60 @ iadd
.byte 0xAC @ ireturnStill, this repo links to some other stuff I found interesting. The Starlet exploit which is linked is funny for how basic it is, and it also seems to be part of a much bigger and more ambitious (but mothballed?) project.
I would have thought any encoding in the unused part of the instruction space would generate a SIGILL, needing the "we don't do that here" logic.
The Wii sold well, thus, if anything, it is a particularly good target for playing with Jazelle.
EDIT: Following one of the reference links, apparently you can enter Jazelle mode on the Nintendo 3DS's application cores. That's another suitable target. https://github.com/SonoSooS/libjz
In my view, it's people having fun with it and maybe learning a new thing or two, so I'd say this is as useful as it gets.