├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── perf ├── flamegraph-fetch-improv-nostdout.svg ├── flamegraph-fetch-improv.svg └── flamegraph.svg ├── rustfmt.toml ├── src ├── arm64.rs ├── bytecode.rs ├── jit.rs ├── jvm.rs ├── lib.rs ├── main.rs ├── profiler.rs ├── program.rs ├── runtime.rs ├── trace.rs └── x86.rs └── support ├── integration ├── AntiTautologi.class ├── AntiTautologi.java ├── ChineseRemainder.class ├── ChineseRemainder.java ├── DoubleFibonacci.class ├── DoubleFibonacci.java ├── Empty.class ├── Empty.java ├── EvenMoreLoops.class ├── EvenMoreLoops.java ├── Factorial.class ├── Factorial.java ├── Fibonacci.class ├── Fibonacci.java ├── FloatFibonacci.class ├── FloatFibonacci.java ├── FunctionInsideTrace.class ├── FunctionInsideTrace.java ├── IsPrime.class ├── IsPrime.java ├── LongFibonacci.class ├── LongFibonacci.java ├── LoopWithFuncCall.class ├── LoopWithFuncCall.java ├── MEDouble.class ├── MEDouble.java ├── ManyVariables.class ├── ManyVariables.java ├── ManyVariablesMulTrace.class ├── ManyVariablesMulTrace.java ├── MixedArg.class ├── MixedArg.java ├── MixedExecution.class ├── MixedExecution.java ├── MixedExecutionTwoVars.class ├── MixedExecutionTwoVars.java ├── MixedTypes.class ├── MixedTypes.java ├── MoreLoops.class ├── MoreLoops.java ├── RecursiveArguments.class ├── RecursiveArguments.java ├── ReportExample.class ├── ReportExample.java ├── SingleFuncCall.class ├── SingleFuncCall.java ├── SingleLoop.class ├── SingleLoop.java ├── SingleLoopWithHotSideExit.class ├── SingleLoopWithHotSideExit.java ├── SingleLoopWithIfStatement.class ├── SingleLoopWithIfStatement.java ├── TernaryAssign.class ├── TernaryAssign.java ├── Test.class ├── Test.java ├── TwoHotSideExits.class ├── TwoHotSideExits.java ├── WhileLoopAtStart.class └── WhileLoopAtStart.java ├── jit ├── Loop10.class ├── Loop10.java ├── Loop100.class └── Loop100.java └── tests ├── AllAtOnce.class ├── AllAtOnce.java ├── ChineseRemainder.class ├── ChineseRemainder.java ├── Compare.class ├── Compare.java ├── CompareEq.class ├── CompareEq.java ├── CompareGe.class ├── CompareGe.java ├── CompareGt.class ├── CompareGt.java ├── CompareLe.class ├── CompareLe.java ├── CompareLt.class ├── CompareLt.java ├── CompareNe.class ├── CompareNe.java ├── DoubleFibonacci.class ├── DoubleFibonacci.java ├── Factorial.class ├── Factorial.java ├── Fibonacci.class ├── Fibonacci.java ├── FuncCall.class ├── FuncCall.java ├── HotLoop.class ├── HotLoop.java ├── Loop.class ├── Loop.java ├── MoreLoops.class ├── MoreLoops.java ├── MultiFuncCall.class ├── MultiFuncCall.java ├── NakedMain.class ├── NakedMain.java ├── Rem.class ├── Rem.java ├── SingleFuncCall.class └── SingleFuncCall.java /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | tryouts/ 4 | TODO.md 5 | *.bin 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coldbrew" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | byteorder = "1.4.3" 10 | dynasmrt = "2.0.0" 11 | regex = "1.8.4" 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 @jmpnz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # coldbrew 2 | 3 | `coldbrew` is a toy interpreter and tracing JIT compiler for the JVM 4 | it mainly serves as a demo project for how JIT compilers work in genenral. 5 | 6 | Currently `coldbrew` is able to successfully interpret, record, compile and 7 | execute native code on x86-64 for some very simple demo programs e.g `support/jit`. 8 | 9 | `coldbrew` is inspired primarly by TigerShrimp[^1] and some ideas from Higgs[^2] 10 | the TigerShrimp C++ implementation[^3] is very readable and was of huge help. 11 | 12 | Other implementations I've found useful is LuaJIT 2.0 and Mike Pall's email 13 | about the LuaJIT internals which you can in the mailing list[^4]. 14 | 15 | While I tried to remain as close as the TigerShrimp implementation as possible, 16 | there are some changes such as (trace recording logic is different, we don't 17 | support trace stitching and we want to maybe add support for inlining calls). 18 | 19 | It's possible support for ARM64 will be added in the future. 20 | 21 | I was originally planning to use the C++ implementation as a baseline to test 22 | against but I didn't have much success building it. 23 | 24 | ## How it works 25 | 26 | `coldbrew` bundles a traditional bytecode interpreter with a runtime for the JVM 27 | as per the Java SE7 specification described in the link below[^6], during the 28 | execution the bytecode is profiled and execution traces are recorded. 29 | 30 | The recorded traces are self-contained with backwards branches and inner branches 31 | only. Ideally we want to make this even more self-contained by recording just 32 | basic blocks. 33 | 34 | The trace contains all the information needed to compile the bytecode to native 35 | such as the entry, exit codes and the bytecode of the core loop. 36 | 37 | Once a trace is ready we pipeline it to the JIT cache for compilation and when we 38 | reach that code path again (loop entry) execution leaves the interpreter and executes 39 | the compiled native trace. 40 | 41 | When a compiled trace finishes executing we overwrite the interpreter current stack 42 | frame to record all mutations that happened in native code then execution is returned 43 | to the interpreter again. 44 | 45 | ### Trace recording and execution 46 | 47 | To identify hotpaths we use heuristics that target *loop headers* which we can 48 | identify when we encounter a *backwards branch* instruction. Once such branch 49 | is identified we record the *program counter* where we branch as the start of 50 | the loop. 51 | 52 | But it's not sufficient to track *backwards branches* we need to calculate 53 | their execution frequency to identify if they are *hot*, the invocation frequency 54 | threshold (currently set to 1) triggewrs the start of recording. 55 | 56 | An example of a trace would be a sequence of bytecode like the one below, the 57 | format is `Inst(opcode, operands) @ PC`: 58 | 59 | ```asm 60 | 61 | Inst(iload, Some([Int(2)])) @ Instruction Index 6 @ Method Index: 11 62 | Inst(bipush, Some([Int(10)])) @ Instruction Index 7 @ Method Index: 11 63 | Inst(if_icmpgt, Some([Int(13)])) @ Instruction Index 9 @ Method Index: 11 64 | Inst(iload, Some([Int(1)])) @ Instruction Index 12 @ Method Index: 11 65 | Inst(iload, Some([Int(2)])) @ Instruction Index 13 @ Method Index: 11 66 | Inst(iadd, None) @ Instruction Index 14 @ Method Index: 11 67 | Inst(istore, Some([Int(1)])) @ Instruction Index 15 @ Method Index: 11 68 | Inst(iinc, Some([Int(2), Int(1)])) @ Instruction Index 16 @ Method Index: 11 69 | Inst(goto, Some([Int(-13)])) @ Instruction Index 19 @ Method Index: 11 70 | 71 | ``` 72 | 73 | Ideally in a tracing JIT you might want to replace the comparison instruction by 74 | speculatively executing under the assumption that the condition is true. This is 75 | done in many production tracing JITs were guard clauses are introduced to assert 76 | the condition. 77 | 78 | In our case we don't do any of that we simply compile the code as is, the above 79 | bytecode results in the following assembly (comments added for clarification). 80 | 81 | 82 | ```asm 83 | 84 | ; epilogue 85 | 00000000 55 push rbp 86 | 00000001 4889E5 mov rbp,rsp 87 | ; this is not needed since we don't clobber rdi or rsi 88 | 00000004 48897DE8 mov [rbp-0x18],rdi 89 | 00000008 488975E0 mov [rbp-0x20],rsi 90 | ; loop entry 91 | 0000000C 488B842710000000 mov rax,[rdi+0x10] 92 | 00000014 4881F80A000000 cmp rax,0xa 93 | ; loop condition i <= 10 94 | 0000001B 0F8F29000000 jg near 0x4a 95 | ; loop code. 96 | 00000021 488B8C2708000000 mov rcx,[rdi+0x8] 97 | 00000029 4C8B842710000000 mov r8,[rdi+0x10] 98 | 00000031 4C01C1 add rcx,r8 99 | 00000034 48898C2708000000 mov [rdi+0x8],rcx 100 | 0000003C 4080842710000000 add byte [rdi+0x10],0x1 101 | -01 102 | ; go back to the loop entry (equivalent to goto) 103 | 00000045 E9C2FFFFFF jmp 0xc 104 | ; 0xd is the program counter of the target instruction of if_icmpge above 105 | ; this is preloaded and known at compile time and we don't need to inject 106 | ; it. 107 | 0000004A 48C7C00D000000 mov rax,0xd 108 | 00000051 5D pop rbp 109 | 00000052 C3 ret 110 | 111 | ``` 112 | 113 | The above is the relocation free version, emitted by [dynasm-rs](https://github.com/CensoredUsername/dynasm-rs). 114 | 115 | *Note*: Special thanks to `dynasm-rs` author for an exellent and pleasent to use dynamic 116 | assembler. 117 | 118 | When it comes to executing the trace we assemble the native trace using `dynasm` 119 | and record it as a pointer to a function with the following signature. 120 | 121 | ## Going Further 122 | 123 | I might possibly keep working on this but if you would like a challenge 124 | here are some ideas : 125 | 126 | - Handle nested loops. 127 | - Inline invoked functions (currently we abort traces that do function calls 128 | but under certain heuristics we can pretty much compile simple functions) 129 | - Add an IR then compile and optimize the IR before compiling to assembly 130 | this offers you the opportunity for DCE, Algebraic Simplification, Constant 131 | Folding, Loop Unrolling (the list goes on really). 132 | - Rewrite the tracer to build tracelets instead (basic blocks) then do trace 133 | splatting with branch flipping to really speed up things. 134 | - Add support for trace stitching 135 | - Add ARM64 support 136 | 137 | ## Acknowledgments 138 | 139 | I would like to thank the authors of the TigerShrimp work and for providing 140 | their implementation. The thesis is an exellent introduction to Tracing JITs 141 | and is a must read to anyone who wishes to understand the overall architecture 142 | and details of tracing JIT interpreters. 143 | 144 | 145 | [^1]: [TigerShrimp: An Understandable Tracing JIT 146 | Compiler](https://odr.chalmers.se/server/api/core/bitstreams/87898837-623a-46f0-bcdc-06d2bf10805d/content) 147 | 148 | [^2]: [Higgs: A New Tracing JIT for 149 | JavaScript](https://pointersgonewild.com/2012/12/08/higgs-my-new-tracing-jit-for-javascript/) 150 | 151 | [^3]: [Github/TigerShrimp](https://github.com/TigerShrimp/TracingJITCompiler) 152 | 153 | [^4]: [Archive: On LuaJIT 2.0](https://gist.github.com/jmpnz/fb8a1f2c9c0e70b4d2b0cc6cb5ddec25) 154 | 155 | [^5]: [It's called arm64](https://lore.kernel.org/lkml/CA+55aFxL6uEre-c=JrhPfts=7BGmhb2Js1c2ZGkTH8F=+rEWDg@mail.gmail.com/) 156 | 157 | [^6]: [Java SE7 Spec](https://docs.oracle.com/javase/specs/jvms/se7/html/) 158 | 159 | [^7]: [HotpathVM: An Effective JIT Compiler for Resource-constrained Devices](https://www.usenix.org/legacy/events/vee06/full_papers/p144-gal.pdf) 160 | 161 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | reorder_imports = true 3 | -------------------------------------------------------------------------------- /src/arm64.rs: -------------------------------------------------------------------------------- 1 | //! Minimal ARM64 assembly module useful for doing ARM64 codegen. 2 | 3 | /// ARM64 (aarch64) registers, mainly used to keep track of available 4 | /// and used registers during compilation. 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 6 | #[cfg(target_arch = "aarch64")] 7 | enum Register { 8 | // Arguments and return values. 9 | X0 = 0x0, 10 | X1 = 0x1, 11 | X2 = 0x2, 12 | X3 = 0x3, 13 | X4 = 0x4, 14 | X5 = 0x5, 15 | X6 = 0x6, 16 | X7 = 0x7, 17 | // Indirect result. 18 | X8 = 0x8, 19 | // Temporary. 20 | X9 = 0x9, 21 | X10 = 0xA, 22 | X11 = 0xB, 23 | X12 = 0xC, 24 | X13 = 0xD, 25 | X14 = 0xE, 26 | X15 = 0xF, 27 | // Intra-procedure call temporaries. 28 | X16 = 0x10, 29 | X17 = 0x11, 30 | // Platform defined usage. 31 | X18 = 0x12, 32 | // Temporary (must be preserved). 33 | X19 = 0x13, 34 | X20 = 0x14, 35 | X21 = 0x15, 36 | X22 = 0x16, 37 | X23 = 0x17, 38 | X24 = 0x18, 39 | X25 = 0x19, 40 | X26 = 0x1A, 41 | X27 = 0x1B, 42 | X28 = 0x1C, 43 | // Stack/Frame pointer (must be preserved). 44 | X29 = 0x1D, 45 | // Link Register/Return address. 46 | X30 = 0x1E, 47 | // Zero register. 48 | X31 = 0x1F, 49 | } 50 | 51 | /// Create a mask to extract n-bits of a given value from start. 52 | #[cfg(target_arch = "aarch64")] 53 | pub fn mask(len: u64, start: u64) -> u64 { 54 | ((1 << len) - 1) << start 55 | } 56 | 57 | /// Split a u64 into two chunks of high and low bits. 58 | #[cfg(target_arch = "aarch64")] 59 | pub fn split(x: u64) -> (u32, u32) { 60 | return ((x >> 16) as u32, (x & mask(16, 0)) as u32); 61 | } 62 | 63 | #[cfg(test)] 64 | #[cfg(target_arch = "aarch64")] 65 | mod tests { 66 | use super::*; 67 | #[test] 68 | fn immediate_from_i32() { 69 | // Given the following immediate break it to separate bits to fit 70 | // an ARM64 instruction. 71 | // This is useful when loading immediates that don't fit the fixed 72 | // size instruction width of 32-bits. 73 | let x = 0x48f0d0i32; 74 | // To read x in x0 we can use a movz movk pair 75 | // movz x0, #0xf0d0 76 | // movk x0, #0x48, lsl #16 77 | // Which is equivalent to the following. 78 | let lo = x as u64 & mask(16, 0); 79 | let hi = x as u64 >> 16; 80 | assert_eq!((hi << 16 | lo) as i32, x); 81 | // Another example with an even bigger integer 82 | let v = 0x1122334455667788u64; 83 | let lo_1 = v & mask(16, 0); 84 | let lo_2 = v & mask(16, 16); 85 | let lo_3 = v & mask(16, 32); 86 | let lo_4 = v & mask(16, 48); 87 | assert_eq!(lo_4 | lo_3 | lo_2 | lo_1, v); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/bytecode.rs: -------------------------------------------------------------------------------- 1 | //! JVM bytecode definitions. 2 | use std::fmt; 3 | 4 | /// OPCodes supported by the JVM as documented in the spec document. 5 | /// ref: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-7.html 6 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 7 | pub enum OPCode { 8 | /// Nop designates a no operation, it's similar to a NOP (0x90). 9 | Nop, 10 | /// Push `null` into the stack. 11 | AConstNull, 12 | IconstM1, 13 | Iconst0, 14 | Iconst1, 15 | Iconst2, 16 | Iconst3, 17 | Iconst4, 18 | Iconst5, 19 | Lconst0, 20 | Lconst1, 21 | Fconst0, 22 | Fconst1, 23 | Fconst2, 24 | Dconst0, 25 | Dconst1, 26 | /// Push a single byte operand into the stack. 27 | BiPush, 28 | /// Push a two byte operand (short) into the stack. 29 | SiPush, 30 | /// Push an `int` or `float` value from the runtime constant pool at the 31 | /// given index (byte long) into the stack. 32 | Ldc, 33 | /// Push an `int` or `float` value from the runtime constant pool at the 34 | /// given index (two byte long) into the stack. 35 | LdcW, 36 | /// Push a `long` or `double` value from the runtime constant pool at the 37 | /// given index into the stack. 38 | Ldc2W, 39 | /// Load an `int` from the local variables array of the current frame 40 | /// and push it into the stack, the index is given as an operand. 41 | ILoad, 42 | /// Load a `long` from the local variables array of the current frame 43 | /// and push it into the stack, the index is given as an operand. 44 | LLoad, 45 | /// Load a `float` from the local variables array of the current frame 46 | /// and push it into the stack, the index is given as an operand. 47 | FLoad, 48 | /// Load a `double` from the local variables array of the current frame 49 | /// and push it into the stack, the index is given as an operand. 50 | DLoad, 51 | /// Load a `reference` from a local variable. 52 | ALoad, 53 | /// Load `int` at index 0 from the local variables array of the current 54 | /// frame and push it into the stack. 55 | ILoad0, 56 | /// Load `int` at index 1 from the local variables array of the current 57 | /// frame and push it into the stack. 58 | ILoad1, 59 | /// Load `int` at index 2 from the local variables array of the current 60 | /// frame and push it into the stack. 61 | ILoad2, 62 | /// Load `int` at index 3 from the local variables array of the current 63 | /// frame and push it into the stack. 64 | ILoad3, 65 | /// Load `long` at index 0 from the local variables array of the current 66 | /// frame and push it into the stack. 67 | LLoad0, 68 | /// Load `long` at index 1 from the local variables array of the current 69 | /// frame and push it into the stack. 70 | LLoad1, 71 | /// Load `long` at index 2 from the local variables array of the current 72 | /// frame and push it into the stack. 73 | LLoad2, 74 | /// Load `long` at index 3 from the local variables array of the current 75 | /// frame and push it into the stack. 76 | LLoad3, 77 | /// Load `float` at index 0 from the local variables array of the current 78 | /// frame and push it into the stack. 79 | FLoad0, 80 | /// Load `float` at index 1 from the local variables array of the current 81 | /// frame and push it into the stack. 82 | FLoad1, 83 | /// Load `float` at index 2 from the local variables array of the current 84 | /// frame and push it into the stack. 85 | FLoad2, 86 | /// Load `float` at index 3 from the local variables array of the current 87 | /// frame and push it into the stack. 88 | FLoad3, 89 | /// Load `double` at index 0 from the local variables array of the current 90 | /// frame and push it into the stack. 91 | DLoad0, 92 | /// Load `double` at index 1 from the local variables array of the current 93 | /// frame and push it into the stack. 94 | DLoad1, 95 | /// Load `double` at index 2 from the local variables array of the current 96 | /// frame and push it into the stack. 97 | DLoad2, 98 | /// Load `double` at index 3 from the local variables array of the current 99 | /// frame and push it into the stack. 100 | DLoad3, 101 | /// Load the value at index 0 in the local variable array of the current 102 | /// frame into the stack. 103 | ALoad0, 104 | /// Load the value at index 1 in the local variable array of the current 105 | /// frame into the stack. 106 | ALoad1, 107 | /// Load the value at index 2 in the local variable array of the current 108 | /// frame into the stack. 109 | ALoad2, 110 | /// Load the value at index 3 in the local variable array of the current 111 | /// frame into the stack. 112 | ALoad3, 113 | IALoad, 114 | LALoad, 115 | FALoad, 116 | DALoad, 117 | /// Load `reference` from an array, the top tweo values on the stack are 118 | /// the `index` and `reference`. The loaded value is pushed back into the 119 | /// stack. 120 | AALoad, 121 | BALoad, 122 | CALoad, 123 | SALoad, 124 | /// Store `int` from the local variables array of the current frame 125 | /// and push it into the stack, the index is given as operand. 126 | IStore, 127 | /// Store `long` from the local variables array of the current frame 128 | /// and push it into the stack, the index is given as operand. 129 | LStore, 130 | /// Store `float` from the local variables array of the current frame 131 | /// and push it into the stack, the index is given as operand. 132 | FStore, 133 | /// Store `double` from the local variables array of the current frame 134 | /// and push it into the stack, the index is given as operand. 135 | DStore, 136 | /// Store `reference` into a local variable. 137 | AStore, 138 | /// Store `int` at index 0 in the local variables array of the current 139 | /// frame into the stack. 140 | IStore0, 141 | /// Store `int` at index 1 in the local variables array of the current 142 | /// frame into the stack. 143 | IStore1, 144 | /// Store `int` at index 2 in the local variables array of the current 145 | /// frame into the stack. 146 | IStore2, 147 | /// Store `int` at index 3 in the local variables array of the current 148 | /// frame into the stack. 149 | IStore3, 150 | /// Store `long` at index 0 in the local variables array of the current 151 | /// frame into the stack. 152 | LStore0, 153 | /// Store `long` at index 1 in the local variables array of the current 154 | /// frame into the stack. 155 | LStore1, 156 | /// Store `long` at index 2 in the local variables array of the current 157 | /// frame into the stack. 158 | LStore2, 159 | /// Store `long` at index 3 in the local variables array of the current 160 | /// frame into the stack. 161 | LStore3, 162 | /// Store `float` at index 0 in the local variables array of the current 163 | /// frame into the stack. 164 | FStore0, 165 | /// Store `float` at index 1 in the local variables array of the current 166 | /// frame into the stack. 167 | FStore1, 168 | /// Store `float` at index 2 in the local variables array of the current 169 | /// frame into the stack. 170 | FStore2, 171 | /// Store `float` at index 3 in the local variables array of the current 172 | /// frame into the stack. 173 | FStore3, 174 | /// Store `double` at index 0 in the local variables array of the current 175 | /// frame into the stack. 176 | DStore0, 177 | /// Store `double` at index 1 in the local variables array of the current 178 | /// frame into the stack. 179 | DStore1, 180 | /// Store `double` at index 2 in the local variables array of the current 181 | /// frame into the stack. 182 | DStore2, 183 | /// Store `double` at index 3 in the local variables array of the current 184 | /// frame into the stack. 185 | DStore3, 186 | AStore0, 187 | AStore1, 188 | AStore2, 189 | AStore3, 190 | IAStore, 191 | LAStore, 192 | FAStore, 193 | DAStore, 194 | /// Store into a `reference` array, the top three values on the stack are 195 | /// the value, index and reference to the array. 196 | AAStore, 197 | BAStore, 198 | CAStore, 199 | SAStore, 200 | Pop, 201 | Pop2, 202 | Dup, 203 | DupX1, 204 | DupX2, 205 | Dup2, 206 | Dup2X1, 207 | Dup2X2, 208 | Swap, 209 | /// Pop the top two value from the stack (they must be of type `int`) then 210 | /// push their sum into the stack. 211 | IAdd, 212 | /// Pop the top two value from the stack (they must be of type `long`) then 213 | /// push their sum into the stack. 214 | LAdd, 215 | /// Pop the top two value from the stack (they must be of type `float`) then 216 | /// push their sum into the stack. 217 | FAdd, 218 | /// Pop the top two value from the stack (they must be of type `double`) then 219 | /// push their sum into the stack. 220 | DAdd, 221 | /// Pop the top two value from the stack (they must be of type `int`) then 222 | /// push their difference into the stack. The result is `value1` - `value2` 223 | /// and the values are laid as [`value1`, `value2`]. 224 | ISub, 225 | /// Pop the top two value from the stack (they must be of type `long`) then 226 | /// push their difference into the stack. The result is `value1` - `value2` 227 | /// and the values are laid as [`value1`, `value2`]. 228 | LSub, 229 | /// Pop the top two value from the stack (they must be of type `float`) then 230 | /// push their difference into the stack. The result is `value1` - `value2` 231 | /// and the values are laid as [`value1`, `value2`]. 232 | FSub, 233 | /// Pop the top two value from the stack (they must be of type `double`) then 234 | /// push their difference into the stack. The result is `value1` - `value2` 235 | /// and the values are laid as [`value1`, `value2`]. 236 | DSub, 237 | /// Pop the top two value from the stack (they must be of type `int`) then 238 | /// push their product into the stack. The result is `value1` * `value2` 239 | /// and the values are laid as [`value1`, `value2`]. 240 | IMul, 241 | /// Pop the top two value from the stack (they must be of type `long`) then 242 | /// push their product into the stack. The result is `value1` * `value2` 243 | /// and the values are laid as [`value1`, `value2`]. 244 | LMul, 245 | /// Pop the top two value from the stack (they must be of type `float`) then 246 | /// push their product into the stack. The result is `value1` * `value2` 247 | /// and the values are laid as [`value1`, `value2`]. 248 | FMul, 249 | /// Pop the top two value from the stack (they must be of type `double`) then 250 | /// push their product into the stack. The result is `value1` * `value2` 251 | /// and the values are laid as [`value1`, `value2`]. 252 | DMul, 253 | /// Pop the top two value from the stack (they must be of type `int`) then 254 | /// push their division into the stack. The result is `value1` / `value2` 255 | /// and the values are laid as [`value1`, `value2`]. 256 | IDiv, 257 | /// Pop the top two value from the stack (they must be of type `long`) then 258 | /// push their division into the stack. The result is `value1` / `value2` 259 | /// and the values are laid as [`value1`, `value2`]. 260 | LDiv, 261 | /// Pop the top two value from the stack (they must be of type `float`) then 262 | /// push their division into the stack. The result is `value1` / `value2` 263 | /// and the values are laid as [`value1`, `value2`]. 264 | FDiv, 265 | /// Pop the top two value from the stack (they must be of type `double`) then 266 | /// push their division into the stack. The result is `value1` / `value2` 267 | /// and the values are laid as [`value1`, `value2`]. 268 | DDiv, 269 | /// Pop the top two value from the stack (they must be of type `int`) then 270 | /// push their modulo into the stack. The result is `value1` / `value2` 271 | /// and the values are laid as [`value1`, `value2`]. 272 | IRem, 273 | /// Pop the top two value from the stack (they must be of type `long`) then 274 | /// push their modulo into the stack. The result is `value1` / `value2` 275 | /// and the values are laid as [`value1`, `value2`]. 276 | LRem, 277 | /// Pop the top two value from the stack (they must be of type `float`) then 278 | /// push their modulo into the stack. The result is `value1` / `value2` 279 | /// and the values are laid as [`value1`, `value2`]. 280 | FRem, 281 | /// Pop the top two value from the stack (they must be of type `double`) then 282 | /// push their modulo into the stack. The result is `value1` / `value2` 283 | /// and the values are laid as [`value1`, `value2`]. 284 | DRem, 285 | INeg, 286 | LNeg, 287 | FNeg, 288 | DNeg, 289 | IShl, 290 | LShl, 291 | IShr, 292 | LShr, 293 | IUShr, 294 | LUShr, 295 | Iand, 296 | Land, 297 | IOr, 298 | LOr, 299 | IXor, 300 | LXor, 301 | /// Increment the value in the local variables array stored at `index` given 302 | /// as an operand by the constant `const` given as an operand. 303 | IInc, 304 | I2L, 305 | I2F, 306 | I2D, 307 | L2I, 308 | L2F, 309 | L2D, 310 | F2I, 311 | F2L, 312 | F2D, 313 | D2I, 314 | D2L, 315 | D2F, 316 | I2B, 317 | I2C, 318 | I2S, 319 | LCmp, 320 | FCmpL, 321 | FCmpG, 322 | DCmpL, 323 | DCmpG, 324 | /// Branch to the target offset (given as operand) if the comparison is 325 | /// true, the compared values are the top value on the stack and 0. 326 | /// 327 | /// [value1] ---> 328 | /// 329 | /// The value must be an `int` and the comparison is signed. 330 | /// 331 | /// Branch if `value` is equal to zero. 332 | IfEq, 333 | /// Branch if `value` is not equal to zero. 334 | IfNe, 335 | /// Branch if `value` is less than zero. 336 | IfLt, 337 | /// Branch if `value` is greater than or equal to zero. 338 | IfGe, 339 | /// Branch if `value` is greater than zero. 340 | IfGt, 341 | /// Branch if `value` is less than or equal to zero. 342 | IfLe, 343 | /// Branch to the target offset (given as operand) if the comparison is 344 | /// true, the compared values are the top two values in the stack laid 345 | /// out as (the values are interpreted as `int`). : 346 | /// 347 | /// [value1, value2] ---> 348 | /// 349 | /// All comparisons are signed. 350 | /// 351 | /// Branch if the two top values on the stack are equal. 352 | IfICmpEq, 353 | /// Branch if the two top values on the stack are not equal. 354 | IfICmpNe, 355 | /// Branch if the `value1` is less than `value2`. 356 | IfICmpLt, 357 | /// Branch if `value1` is greater or equal than `value2`. 358 | IfICmpGe, 359 | /// Branch if `value1` is greater than `value2`. 360 | IfICmpGt, 361 | /// Branch if `value1` is less then or equal `value2`. 362 | IfICmpLe, 363 | IfACmpEq, 364 | IfACmpNe, 365 | /// Branch to the relative offset given as two 1 byte operands, execution 366 | /// continues at the relative offset from the address of the opcode of the 367 | /// goto instruction. The target address must be that of an opcode of an 368 | /// instruction within the method that contains this `goto` instruction. 369 | Goto, 370 | Jsr, 371 | Ret, 372 | TableSwitch, 373 | LookupSwitch, 374 | IReturn, 375 | LReturn, 376 | FReturn, 377 | DReturn, 378 | // REeturn `reference` from method. 379 | AReturn, 380 | Return, 381 | GetStatic, 382 | PutStatic, 383 | GetField, 384 | PutField, 385 | InvokeVirtual, 386 | InvokeSpecial, 387 | InvokeStatic, 388 | InvokeInterface, 389 | InvokeDynamic, 390 | New, 391 | NewArray, 392 | ANewArray, 393 | /// Pops the `reference` to the array from the stack and push its length 394 | /// into the stack. 395 | ArrayLength, 396 | AThrow, 397 | CheckCast, 398 | InstanceOf, 399 | MonitorEnter, 400 | MonitorExit, 401 | Wide, 402 | MultiANewArray, 403 | IfNull, 404 | IfNonNull, 405 | /// Similar to `goto` but the offset is given as a 4 byte value constructed 406 | /// from 4 1-byte operands. The constructed target address must be that of 407 | /// an opcode of an instruction within the method that contains the current 408 | /// `goto_w` instruction. 409 | GotoW, 410 | JsrW, 411 | Breakpoint, 412 | // Proxy value to signal unknown opcode values. 413 | Unspecified, 414 | } 415 | 416 | impl fmt::Display for OPCode { 417 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 418 | match self { 419 | Self::Nop => write!(f, "nop"), 420 | Self::AConstNull => write!(f, "aconst_null"), 421 | Self::IconstM1 => write!(f, "iconst_m1"), 422 | Self::Iconst0 => write!(f, "iconst_0"), 423 | Self::Iconst1 => write!(f, "iconst_1"), 424 | Self::Iconst2 => write!(f, "iconst_2"), 425 | Self::Iconst3 => write!(f, "iconst_3"), 426 | Self::Iconst4 => write!(f, "iconst_4"), 427 | Self::Iconst5 => write!(f, "iconst_5"), 428 | Self::Lconst0 => write!(f, "lconst_0"), 429 | Self::Lconst1 => write!(f, "lconst_1"), 430 | Self::Fconst0 => write!(f, "fconst_0"), 431 | Self::Fconst1 => write!(f, "fconst_1"), 432 | Self::Fconst2 => write!(f, "fconst_2"), 433 | Self::Dconst0 => write!(f, "dconst_0"), 434 | Self::Dconst1 => write!(f, "dconst_1"), 435 | Self::BiPush => write!(f, "bipush"), 436 | Self::SiPush => write!(f, "sipush"), 437 | Self::Ldc => write!(f, "ldc"), 438 | Self::LdcW => write!(f, "ldc_w"), 439 | Self::Ldc2W => write!(f, "ldc2_w"), 440 | Self::ILoad => write!(f, "iload"), 441 | Self::LLoad => write!(f, "lload"), 442 | Self::FLoad => write!(f, "fload"), 443 | Self::DLoad => write!(f, "dload"), 444 | Self::ALoad => write!(f, "aload"), 445 | Self::ILoad0 => write!(f, "iload_0"), 446 | Self::ILoad1 => write!(f, "iload_1"), 447 | Self::ILoad2 => write!(f, "iload_2"), 448 | Self::ILoad3 => write!(f, "iload_3"), 449 | Self::LLoad0 => write!(f, "lload_0"), 450 | Self::LLoad1 => write!(f, "lload_1"), 451 | Self::LLoad2 => write!(f, "lload_2"), 452 | Self::LLoad3 => write!(f, "lload_3"), 453 | Self::FLoad0 => write!(f, "fload_0"), 454 | Self::FLoad1 => write!(f, "fload_1"), 455 | Self::FLoad2 => write!(f, "fload_2"), 456 | Self::FLoad3 => write!(f, "fload_3"), 457 | Self::DLoad0 => write!(f, "dload_0"), 458 | Self::DLoad1 => write!(f, "dload_1"), 459 | Self::DLoad2 => write!(f, "dload_2"), 460 | Self::DLoad3 => write!(f, "dload_3"), 461 | Self::ALoad0 => write!(f, "aload_0"), 462 | Self::ALoad1 => write!(f, "aload_1"), 463 | Self::ALoad2 => write!(f, "aload_2"), 464 | Self::ALoad3 => write!(f, "aload_3"), 465 | Self::IALoad => write!(f, "iaload"), 466 | Self::LALoad => write!(f, "laload"), 467 | Self::FALoad => write!(f, "faload"), 468 | Self::DALoad => write!(f, "daload"), 469 | Self::AALoad => write!(f, "aaload"), 470 | Self::BALoad => write!(f, "baload"), 471 | Self::CALoad => write!(f, "caload"), 472 | Self::SALoad => write!(f, "saload"), 473 | Self::IStore => write!(f, "istore"), 474 | Self::LStore => write!(f, "lstore"), 475 | Self::FStore => write!(f, "fstore"), 476 | Self::DStore => write!(f, "dstore"), 477 | Self::AStore => write!(f, "astore"), 478 | Self::IStore0 => write!(f, "istore_0"), 479 | Self::IStore1 => write!(f, "istore_1"), 480 | Self::IStore2 => write!(f, "istore_2"), 481 | Self::IStore3 => write!(f, "istore_3"), 482 | Self::LStore0 => write!(f, "lstore_0"), 483 | Self::LStore1 => write!(f, "lstore_1"), 484 | Self::LStore2 => write!(f, "lstore_2"), 485 | Self::LStore3 => write!(f, "lstore_3"), 486 | Self::FStore0 => write!(f, "fstore_0"), 487 | Self::FStore1 => write!(f, "fstore_1"), 488 | Self::FStore2 => write!(f, "fstore_2"), 489 | Self::FStore3 => write!(f, "fstore_3"), 490 | Self::DStore0 => write!(f, "dstore_0"), 491 | Self::DStore1 => write!(f, "dstore_1"), 492 | Self::DStore2 => write!(f, "dstore_2"), 493 | Self::DStore3 => write!(f, "dstore_3"), 494 | Self::AStore0 => write!(f, "astore_0"), 495 | Self::AStore1 => write!(f, "astore_1"), 496 | Self::AStore2 => write!(f, "astore_2"), 497 | Self::AStore3 => write!(f, "astore_3"), 498 | Self::IAStore => write!(f, "iastore"), 499 | Self::LAStore => write!(f, "lastore"), 500 | Self::FAStore => write!(f, "fastore"), 501 | Self::DAStore => write!(f, "dastore"), 502 | Self::AAStore => write!(f, "aastore"), 503 | Self::BAStore => write!(f, "bastore"), 504 | Self::CAStore => write!(f, "castore"), 505 | Self::SAStore => write!(f, "sastore"), 506 | Self::Pop => write!(f, "pop"), 507 | Self::Pop2 => write!(f, "pop_2"), 508 | Self::Dup => write!(f, "dup"), 509 | Self::DupX1 => write!(f, "dup_x1"), 510 | Self::DupX2 => write!(f, "dup_x2"), 511 | Self::Dup2 => write!(f, "dup2"), 512 | Self::Dup2X1 => write!(f, "dup2_x1"), 513 | Self::Dup2X2 => write!(f, "dup2_x2"), 514 | Self::Swap => write!(f, "swap"), 515 | Self::IAdd => write!(f, "iadd"), 516 | Self::LAdd => write!(f, "ladd"), 517 | Self::FAdd => write!(f, "fadd"), 518 | Self::DAdd => write!(f, "dadd"), 519 | Self::ISub => write!(f, "isub"), 520 | Self::LSub => write!(f, "lsub"), 521 | Self::FSub => write!(f, "fsub"), 522 | Self::DSub => write!(f, "dsub"), 523 | Self::IMul => write!(f, "imul"), 524 | Self::LMul => write!(f, "lmul"), 525 | Self::FMul => write!(f, "fmul"), 526 | Self::DMul => write!(f, "dmul"), 527 | Self::IDiv => write!(f, "idiv"), 528 | Self::LDiv => write!(f, "ldiv"), 529 | Self::FDiv => write!(f, "fdiv"), 530 | Self::DDiv => write!(f, "ddiv"), 531 | Self::IRem => write!(f, "irem"), 532 | Self::LRem => write!(f, "lrem"), 533 | Self::FRem => write!(f, "frem"), 534 | Self::DRem => write!(f, "drem"), 535 | Self::INeg => write!(f, "ineg"), 536 | Self::LNeg => write!(f, "lneg"), 537 | Self::FNeg => write!(f, "fneg"), 538 | Self::DNeg => write!(f, "dneg"), 539 | Self::IShl => write!(f, "ishl"), 540 | Self::LShl => write!(f, "lshl"), 541 | Self::IShr => write!(f, "ishr"), 542 | Self::LShr => write!(f, "lshr"), 543 | Self::IUShr => write!(f, "iushr"), 544 | Self::LUShr => write!(f, "lushr"), 545 | Self::Iand => write!(f, "iand"), 546 | Self::Land => write!(f, "land"), 547 | Self::IOr => write!(f, "ior"), 548 | Self::LOr => write!(f, "lor"), 549 | Self::IXor => write!(f, "ixor"), 550 | Self::LXor => write!(f, "lxor"), 551 | Self::IInc => write!(f, "iinc"), 552 | Self::I2L => write!(f, "i2l"), 553 | Self::I2F => write!(f, "i2f"), 554 | Self::I2D => write!(f, "i2d"), 555 | Self::L2I => write!(f, "l2i"), 556 | Self::L2F => write!(f, "l2f"), 557 | Self::L2D => write!(f, "l2d"), 558 | Self::F2I => write!(f, "f2i"), 559 | Self::F2L => write!(f, "f2l"), 560 | Self::F2D => write!(f, "f2d"), 561 | Self::D2I => write!(f, "d2i"), 562 | Self::D2L => write!(f, "d2l"), 563 | Self::D2F => write!(f, "d2f"), 564 | Self::I2B => write!(f, "i2b"), 565 | Self::I2C => write!(f, "i2c"), 566 | Self::I2S => write!(f, "i2s"), 567 | Self::LCmp => write!(f, "lcmp"), 568 | Self::FCmpL => write!(f, "fcmpl"), 569 | Self::FCmpG => write!(f, "fcmpg"), 570 | Self::DCmpL => write!(f, "dcmpl"), 571 | Self::DCmpG => write!(f, "dcmpg"), 572 | Self::IfEq => write!(f, "ifeq"), 573 | Self::IfNe => write!(f, "ifne"), 574 | Self::IfLt => write!(f, "iflt"), 575 | Self::IfGe => write!(f, "ifge"), 576 | Self::IfGt => write!(f, "ifgt"), 577 | Self::IfLe => write!(f, "ifle"), 578 | Self::IfICmpEq => write!(f, "if_icmpeq"), 579 | Self::IfICmpNe => write!(f, "if_icmpne"), 580 | Self::IfICmpLt => write!(f, "if_icmplt"), 581 | Self::IfICmpGe => write!(f, "if_icmpge"), 582 | Self::IfICmpGt => write!(f, "if_icmpgt"), 583 | Self::IfICmpLe => write!(f, "if_icmple"), 584 | Self::IfACmpEq => write!(f, "if_acmpeq"), 585 | Self::IfACmpNe => write!(f, "if_acmpne"), 586 | Self::Goto => write!(f, "goto"), 587 | Self::Jsr => write!(f, "jsr"), 588 | Self::Ret => write!(f, "ret"), 589 | Self::TableSwitch => write!(f, "tableswitch"), 590 | Self::LookupSwitch => write!(f, "lookupswitch"), 591 | Self::IReturn => write!(f, "ireturn"), 592 | Self::LReturn => write!(f, "lreturn"), 593 | Self::FReturn => write!(f, "freturn"), 594 | Self::DReturn => write!(f, "dreturn"), 595 | Self::AReturn => write!(f, "areturn"), 596 | Self::Return => write!(f, "return"), 597 | Self::GetStatic => write!(f, "getstatic"), 598 | Self::PutStatic => write!(f, "putstatic"), 599 | Self::GetField => write!(f, "getfield"), 600 | Self::PutField => write!(f, "putfield"), 601 | Self::InvokeVirtual => write!(f, "invokevirtual"), 602 | Self::InvokeSpecial => write!(f, "invokespecial"), 603 | Self::InvokeStatic => write!(f, "invokestatic"), 604 | Self::InvokeInterface => write!(f, "invokeinterface"), 605 | Self::InvokeDynamic => write!(f, "invokedynamic"), 606 | Self::New => write!(f, "new"), 607 | Self::NewArray => write!(f, "newarray"), 608 | Self::ANewArray => write!(f, "anewarray"), 609 | Self::ArrayLength => write!(f, "arraylength"), 610 | Self::AThrow => write!(f, "athrow"), 611 | Self::CheckCast => write!(f, "checkcast"), 612 | Self::InstanceOf => write!(f, "instanceof"), 613 | Self::MonitorEnter => write!(f, "monitorenter"), 614 | Self::MonitorExit => write!(f, "monitorexit"), 615 | Self::Wide => write!(f, "wide"), 616 | Self::MultiANewArray => write!(f, "multianewarray"), 617 | Self::IfNull => write!(f, "ifnull"), 618 | Self::IfNonNull => write!(f, "ifnonnull"), 619 | Self::GotoW => write!(f, "goto_w"), 620 | Self::JsrW => write!(f, "jsr_w"), 621 | Self::Breakpoint => write!(f, "breakpoint"), 622 | _ => write!(f, "unspecified"), 623 | } 624 | } 625 | } 626 | 627 | // Since bytecode is initially loaded as `Vec` we need a way to convert it 628 | // to `OPCode` enum, this might be done better with a macro but copy paste and 629 | // move on for now. 630 | impl From for OPCode { 631 | fn from(byte: u8) -> Self { 632 | match byte { 633 | 0 => Self::Nop, 634 | 1 => Self::AConstNull, 635 | 2 => Self::IconstM1, 636 | 3 => Self::Iconst0, 637 | 4 => Self::Iconst1, 638 | 5 => Self::Iconst2, 639 | 6 => Self::Iconst3, 640 | 7 => Self::Iconst4, 641 | 8 => Self::Iconst5, 642 | 9 => Self::Lconst0, 643 | 10 => Self::Lconst1, 644 | 11 => Self::Fconst0, 645 | 12 => Self::Fconst1, 646 | 13 => Self::Fconst2, 647 | 14 => Self::Dconst0, 648 | 15 => Self::Dconst1, 649 | 16 => Self::BiPush, 650 | 17 => Self::SiPush, 651 | 18 => Self::Ldc, 652 | 19 => Self::LdcW, 653 | 20 => Self::Ldc2W, 654 | 21 => Self::ILoad, 655 | 22 => Self::LLoad, 656 | 23 => Self::FLoad, 657 | 24 => Self::DLoad, 658 | 25 => Self::ALoad, 659 | 26 => Self::ILoad0, 660 | 27 => Self::ILoad1, 661 | 28 => Self::ILoad2, 662 | 29 => Self::ILoad3, 663 | 30 => Self::LLoad0, 664 | 31 => Self::LLoad1, 665 | 32 => Self::LLoad2, 666 | 33 => Self::LLoad3, 667 | 34 => Self::FLoad0, 668 | 35 => Self::FLoad1, 669 | 36 => Self::FLoad2, 670 | 37 => Self::FLoad3, 671 | 38 => Self::DLoad0, 672 | 39 => Self::DLoad1, 673 | 40 => Self::DLoad2, 674 | 41 => Self::DLoad3, 675 | 42 => Self::ALoad0, 676 | 43 => Self::ALoad1, 677 | 44 => Self::ALoad2, 678 | 45 => Self::ALoad3, 679 | 46 => Self::IALoad, 680 | 47 => Self::LALoad, 681 | 48 => Self::FALoad, 682 | 49 => Self::DALoad, 683 | 50 => Self::AALoad, 684 | 51 => Self::BALoad, 685 | 52 => Self::CALoad, 686 | 53 => Self::SALoad, 687 | 54 => Self::IStore, 688 | 55 => Self::LStore, 689 | 56 => Self::FStore, 690 | 57 => Self::DStore, 691 | 58 => Self::AStore, 692 | 59 => Self::IStore0, 693 | 60 => Self::IStore1, 694 | 61 => Self::IStore2, 695 | 62 => Self::IStore3, 696 | 63 => Self::LStore0, 697 | 64 => Self::LStore1, 698 | 65 => Self::LStore2, 699 | 66 => Self::LStore3, 700 | 67 => Self::FStore0, 701 | 68 => Self::FStore1, 702 | 69 => Self::FStore2, 703 | 70 => Self::FStore3, 704 | 71 => Self::DStore0, 705 | 72 => Self::DStore1, 706 | 73 => Self::DStore2, 707 | 74 => Self::DStore3, 708 | 75 => Self::AStore0, 709 | 76 => Self::AStore1, 710 | 77 => Self::AStore2, 711 | 78 => Self::AStore3, 712 | 79 => Self::IAStore, 713 | 80 => Self::LAStore, 714 | 81 => Self::FAStore, 715 | 82 => Self::DAStore, 716 | 83 => Self::AAStore, 717 | 84 => Self::BAStore, 718 | 85 => Self::CAStore, 719 | 86 => Self::SAStore, 720 | 87 => Self::Pop, 721 | 88 => Self::Pop2, 722 | 89 => Self::Dup, 723 | 90 => Self::DupX1, 724 | 91 => Self::DupX2, 725 | 92 => Self::Dup2, 726 | 93 => Self::Dup2X1, 727 | 94 => Self::Dup2X2, 728 | 95 => Self::Swap, 729 | 96 => Self::IAdd, 730 | 97 => Self::LAdd, 731 | 98 => Self::FAdd, 732 | 99 => Self::DAdd, 733 | 100 => Self::ISub, 734 | 101 => Self::LSub, 735 | 102 => Self::FSub, 736 | 103 => Self::DSub, 737 | 104 => Self::IMul, 738 | 105 => Self::LMul, 739 | 106 => Self::FMul, 740 | 107 => Self::DMul, 741 | 108 => Self::IDiv, 742 | 109 => Self::LDiv, 743 | 110 => Self::FDiv, 744 | 111 => Self::DDiv, 745 | 112 => Self::IRem, 746 | 113 => Self::LRem, 747 | 114 => Self::FRem, 748 | 115 => Self::DRem, 749 | 116 => Self::INeg, 750 | 117 => Self::LNeg, 751 | 118 => Self::FNeg, 752 | 119 => Self::DNeg, 753 | 120 => Self::IShl, 754 | 121 => Self::LShl, 755 | 122 => Self::IShr, 756 | 123 => Self::LShr, 757 | 124 => Self::IUShr, 758 | 125 => Self::LUShr, 759 | 126 => Self::Iand, 760 | 127 => Self::Land, 761 | 128 => Self::IOr, 762 | 129 => Self::LOr, 763 | 130 => Self::IXor, 764 | 131 => Self::LXor, 765 | 132 => Self::IInc, 766 | 133 => Self::I2L, 767 | 134 => Self::I2F, 768 | 135 => Self::I2D, 769 | 136 => Self::L2I, 770 | 137 => Self::L2F, 771 | 138 => Self::L2D, 772 | 139 => Self::F2I, 773 | 140 => Self::F2L, 774 | 141 => Self::F2D, 775 | 142 => Self::D2I, 776 | 143 => Self::D2L, 777 | 144 => Self::D2F, 778 | 145 => Self::I2B, 779 | 146 => Self::I2C, 780 | 147 => Self::I2S, 781 | 148 => Self::LCmp, 782 | 149 => Self::FCmpL, 783 | 150 => Self::FCmpG, 784 | 151 => Self::DCmpL, 785 | 152 => Self::DCmpG, 786 | 153 => Self::IfEq, 787 | 154 => Self::IfNe, 788 | 155 => Self::IfLt, 789 | 156 => Self::IfGe, 790 | 157 => Self::IfGt, 791 | 158 => Self::IfLe, 792 | 159 => Self::IfICmpEq, 793 | 160 => Self::IfICmpNe, 794 | 161 => Self::IfICmpLt, 795 | 162 => Self::IfICmpGe, 796 | 163 => Self::IfICmpGt, 797 | 164 => Self::IfICmpLe, 798 | 165 => Self::IfACmpEq, 799 | 166 => Self::IfACmpNe, 800 | 167 => Self::Goto, 801 | 168 => Self::Jsr, 802 | 169 => Self::Ret, 803 | 170 => Self::TableSwitch, 804 | 171 => Self::LookupSwitch, 805 | 172 => Self::IReturn, 806 | 173 => Self::LReturn, 807 | 174 => Self::FReturn, 808 | 175 => Self::DReturn, 809 | 176 => Self::AReturn, 810 | 177 => Self::Return, 811 | 178 => Self::GetStatic, 812 | 179 => Self::PutStatic, 813 | 180 => Self::GetField, 814 | 181 => Self::PutField, 815 | 182 => Self::InvokeVirtual, 816 | 183 => Self::InvokeSpecial, 817 | 184 => Self::InvokeStatic, 818 | 185 => Self::InvokeInterface, 819 | 186 => Self::InvokeDynamic, 820 | 187 => Self::New, 821 | 188 => Self::NewArray, 822 | 189 => Self::ANewArray, 823 | 190 => Self::ArrayLength, 824 | 191 => Self::AThrow, 825 | 192 => Self::CheckCast, 826 | 193 => Self::InstanceOf, 827 | 194 => Self::MonitorEnter, 828 | 195 => Self::MonitorExit, 829 | 196 => Self::Wide, 830 | 197 => Self::MultiANewArray, 831 | 198 => Self::IfNull, 832 | 199 => Self::IfNonNull, 833 | 200 => Self::GotoW, 834 | 201 => Self::JsrW, 835 | 202 => Self::Breakpoint, 836 | 203..=u8::MAX => Self::Unspecified, 837 | } 838 | } 839 | } 840 | -------------------------------------------------------------------------------- /src/jit.rs: -------------------------------------------------------------------------------- 1 | //! JIT compiler for coldrew targeting x86_64. 2 | use std::collections::{HashMap, VecDeque}; 3 | 4 | use crate::bytecode::OPCode; 5 | use crate::runtime::{Frame, ProgramCounter, Value}; 6 | use crate::trace::Trace; 7 | 8 | use dynasmrt::x64::Assembler; 9 | use dynasmrt::{ 10 | dynasm, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, 11 | ExecutableBuffer, 12 | }; 13 | 14 | /// Intel x86-64 registers, ordered by their syntactic order in the Intel 15 | /// manuals. The usage of the registers follows the System ADM64 ABI. 16 | /// 17 | /// Arguments 1 to 6 go into Rdi, Rsi, Rdx, Rcx, R8 and R9. 18 | /// Excess arguments are pushed to the stack, but since the Jit calling 19 | /// convention restrics the `execute` function to two arguments we want be 20 | /// using any registers besides Rdi and Rsi. 21 | /// 22 | /// Registers Rbx, Rsp, Rbp and R12 to R15 must be callee preserved if they 23 | /// are to be used, the other registers can be clobbered and caller must 24 | /// preserve them. 25 | #[allow(dead_code)] 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 27 | enum Register { 28 | Rax, 29 | Rcx, 30 | Rdx, 31 | Rbx, 32 | Rsp, 33 | Rbp, 34 | Rsi, 35 | Rdi, 36 | R8, 37 | R9, 38 | R10, 39 | R11, 40 | R12, 41 | R13, 42 | R14, 43 | R15, 44 | } 45 | 46 | /// Intel x86-64 shorthand for instructions. 47 | #[allow(dead_code)] 48 | #[derive(Debug, Clone, Copy)] 49 | enum Inst { 50 | Add, 51 | Sub, 52 | IMul, 53 | IDiv, 54 | IRem, 55 | Jge, 56 | Jg, 57 | Jle, 58 | } 59 | 60 | /// Generic representation of assembly operands that allows for supporting 61 | /// both x86 and ARM64. 62 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 63 | enum Operand { 64 | // Register operands. 65 | Register(Register), 66 | // Immediate operands. 67 | Immediate(i32), 68 | // Memory operands represent memory addresses as a pair of base register 69 | // and immediate offset often seen as `[bp, offset]`. 70 | Memory(Register, i32), 71 | } 72 | 73 | /// x86_64 function prologue, allocates `max_locals` space on the stack even 74 | /// though they might not be all used. 75 | macro_rules! prologue { 76 | ($ops:ident) => {{ 77 | #[cfg(target_arch = "x86_64")] 78 | { 79 | let start = $ops.offset(); 80 | dynasm!($ops 81 | ; push rbp 82 | ; mov rbp, rsp 83 | ; mov QWORD [rbp-24], rdi 84 | ; mov QWORD [rbp-32], rsi 85 | ); 86 | start 87 | } 88 | #[cfg(target_arch = "aarch64")] 89 | { 90 | let start = $ops.offset(); 91 | dynasm!($ops 92 | ; sub sp, sp, #32 93 | ; str x0, [sp, 8] 94 | ; str x1, [sp] 95 | ); 96 | start 97 | } 98 | }}; 99 | } 100 | 101 | /// aarch64 function epilogue. 102 | macro_rules! epilogue { 103 | ($ops:ident) => {{ 104 | let epilogue = $ops.offset(); 105 | #[cfg(target_arch = "x86_64")] 106 | dynasm!($ops 107 | ; pop rbp 108 | ; ret 109 | ); 110 | #[cfg(target_arch = "aarch64")] 111 | dynasm!($ops 112 | // Increment stack pointer to go back to where we were 113 | // before the function call. 114 | ; add sp, sp, #32 115 | ; ret 116 | ); 117 | epilogue 118 | }}; 119 | } 120 | 121 | /// `NativeTrace` is a pair of `usize` and `Assembler` that represents an entry 122 | /// point in the `Assembler` buffer. 123 | #[derive(Debug)] 124 | pub struct NativeTrace(AssemblyOffset, ExecutableBuffer); 125 | 126 | /// `JitCache` is responsible for compiling, caching and executing the native 127 | /// traces. 128 | /// 129 | /// The calling convention for our Jit is the following : 130 | /// 131 | /// - Rdi & Rsi are used to pass input arguments which are the local variables 132 | /// in the current frame and a guard program counter which is the entry point 133 | /// of our native trace. 134 | /// 135 | /// - Rax, Rbx, Rcx and R9-R15 are used for intermediate operations. 136 | /// 137 | /// Since every trace is self contained all register allocation is local and 138 | /// done with a simple queue based scheme. 139 | pub struct JitCache { 140 | // Internal cache of available registers. 141 | registers: VecDeque, 142 | // Operand stack. 143 | operands: Vec, 144 | // Cache of native traces. 145 | traces: HashMap, 146 | // Cache of `pc` entries to labels. 147 | labels: HashMap, 148 | } 149 | 150 | impl Default for JitCache { 151 | fn default() -> Self { 152 | Self::new() 153 | } 154 | } 155 | 156 | impl JitCache { 157 | /// Create a new JIT cache. 158 | pub fn new() -> Self { 159 | let registers = vec![ 160 | Register::Rax, 161 | Register::Rcx, 162 | Register::R8, 163 | Register::R9, 164 | Register::R10, 165 | Register::R11, 166 | Register::Rbx, 167 | Register::R12, 168 | Register::R13, 169 | Register::R14, 170 | Register::R15, 171 | ]; 172 | JitCache { 173 | registers: VecDeque::from(registers), 174 | traces: HashMap::new(), 175 | operands: Vec::new(), 176 | labels: HashMap::new(), 177 | } 178 | } 179 | 180 | /// Execute the trace at `pc` and return the mutated locals for the frame 181 | /// and the program counter where the runtime should continue execution. 182 | /// 183 | /// Ideally we can just return the updated `locals` and exit but for now 184 | /// let's take in the entire execution frame of VM and update it. 185 | /// 186 | /// Following the x86-64 convention the locals are passed in `rdi`, exit 187 | /// information is passed in `rsi`. 188 | pub fn execute(&mut self, pc: ProgramCounter, frame: &mut Frame) -> usize { 189 | if self.traces.contains_key(&pc) { 190 | // execute the assembled trace. 191 | let trace = self 192 | .traces 193 | .get_mut(&pc) 194 | .expect("Expected a native trace @ {pc}"); 195 | 196 | // Flatten the locals `HashMap` into a `i32` slice. 197 | let mut locals = vec![0i32; frame.max_locals as usize * 8]; 198 | // Exit information, for now is empty. 199 | let exits = [0i32; 0]; 200 | 201 | for (key, val) in frame.locals.iter() { 202 | locals[*key] = match val { 203 | Value::Int(x) => *x, 204 | Value::Long(x) => *x as i32, 205 | Value::Float(x) => *x as i32, 206 | Value::Double(x) => *x as i32, 207 | }; 208 | } 209 | 210 | let entry = trace.0; 211 | let buf = &trace.1; 212 | let execute: fn(*mut i32, *const i32) -> i32 = 213 | unsafe { std::mem::transmute(buf.ptr(entry)) }; 214 | 215 | let exit_pc = execute(locals.as_mut_ptr(), exits.as_ptr()) as usize; 216 | frame.locals.clear(); 217 | for (index, value) in locals.iter().enumerate() { 218 | frame.locals.insert(index, Value::Int(*value)); 219 | } 220 | 221 | frame.pc.instruction_index = exit_pc as usize; 222 | exit_pc 223 | } else { 224 | pc.get_instruction_index() 225 | } 226 | } 227 | 228 | /// Checks if a native trace exists at this `pc`. 229 | pub fn has_native_trace(&self, pc: ProgramCounter) -> bool { 230 | self.traces.contains_key(&pc) 231 | } 232 | 233 | /// Compile the trace given as argument and prepare a native trace 234 | /// for execution. 235 | /// 236 | /// Compile works as follows : 237 | /// 1. Build a dynasmrt Assembler object. 238 | /// 2. Emits a static prologue for the jitted code. 239 | /// 3. For each recorded instruction generate its equivalent x86 or arm64 240 | /// instruction and create a label for it. 241 | /// 3.1 If the instruction is a jump i.e `Goto` check if we have a label 242 | /// for it, since all recorded traces are straight lines with backward 243 | /// jumps we must have one, then emit the equivalent jump with the label 244 | /// as the target. 245 | /// 4. Emits a static epilogue for the jitted code. 246 | /// 5. When a trace recording is looked, run the jitted code. 247 | /// 248 | /// When we run the trace we need to return PC at which the interpreter 249 | /// will continue execution (`reentry_pc`) 250 | /// 251 | /// How jumps are handled (in more details) : 252 | /// 1. At each trace.instruction() 253 | /// 1.1 Create a DynasmLabel `inst_label_{pc}` 254 | /// 1.2 Append the new label to the `global_jump_table` 255 | /// 2. If the trace.instruction() is a branch: 256 | /// 1.1 Check if we have an existing entry in the `global_jump_table`. 257 | /// 1.2 If an entry exists it means we've compiled a trace for this block. 258 | /// 1.2.1 Fetch the label and mark the native trace with this label 259 | /// the trace will either be stitched if the jump is outside this trace 260 | /// or it will be local if it is inside this trace. 261 | /// 1.3 If an entry doesn't exists it means we're exiting the JIT so we 262 | /// preserve the target `pc` in `rax` and return, when calling `execute` 263 | /// the assumption is that we will always exit back to the interpreter 264 | /// since we currently don't support trace stitching. 265 | pub fn compile(&mut self, recording: &Trace) { 266 | // Reset Jit state. 267 | let pc = recording.start; 268 | let mut ops = dynasmrt::x64::Assembler::new().unwrap(); 269 | // Prologue for dynamically compiled code. 270 | let offset = prologue!(ops); 271 | let mut exit_pc = 0i32; 272 | // Trace compilation : 273 | // For now we compile only the prologue and epilogue and ensure that 274 | // entering the Jit executing the assembled code and leaving the Jit 275 | // works correct. 276 | for entry in &recording.trace { 277 | // Record the instruction program counter to a new label. 278 | let inst_label = ops.new_dynamic_label(); 279 | let _ = self.labels.insert(entry.pc(), inst_label); 280 | match entry.instruction().get_mnemonic() { 281 | // Load operation loads a constant from the locals array at 282 | // the position given by the opcode's operand. 283 | // 284 | // Since the locals array is the first argument to our JIT 285 | // `execute` function the value can be fetched from memory 286 | // using base addressing. 287 | // We assume (for now) locals are 8 bytes long. 288 | OPCode::ILoad 289 | | OPCode::ILoad0 290 | | OPCode::ILoad1 291 | | OPCode::ILoad2 292 | | OPCode::ILoad3 => { 293 | let value = match entry.instruction().nth(0) { 294 | Some(Value::Int(x)) => x, 295 | _ => unreachable!("Operand to iload (index in locals) must be int in current implementation") 296 | }; 297 | let dst = self.first_available_register(); 298 | 299 | #[cfg(target_arch = "x86_64")] 300 | dynasm!(ops 301 | ; =>inst_label 302 | ); 303 | Self::emit_mov( 304 | &mut ops, 305 | &dst, 306 | &Operand::Memory(Register::Rdi, 4 * value), 307 | ); 308 | self.operands.push(dst); 309 | } 310 | OPCode::IStore 311 | | OPCode::IStore0 312 | | OPCode::IStore1 313 | | OPCode::IStore2 314 | | OPCode::IStore3 => { 315 | let value = match entry.instruction().nth(0) { 316 | Some(Value::Int(x)) => x, 317 | _ => unreachable!("Operand to istore (index in locals) must be int in current implementation") 318 | }; 319 | if let Some(src) = self.free_register() { 320 | dynasm!(ops 321 | ; =>inst_label 322 | ); 323 | Self::emit_mov( 324 | &mut ops, 325 | &Operand::Memory(Register::Rdi, 4 * value), 326 | &src, 327 | ); 328 | } 329 | } 330 | OPCode::BiPush | OPCode::SiPush | OPCode::Ldc => { 331 | let imm = match entry.instruction().nth(0) { 332 | Some(Value::Int(imm)) => imm, 333 | _ => unreachable!("Operand to {} must be an int in current implementation", entry.instruction().get_mnemonic()) 334 | }; 335 | self.operands.push(Operand::Immediate(imm)); 336 | } 337 | OPCode::IAdd => { 338 | #[cfg(target_arch = "x86_64")] 339 | dynasm!(ops 340 | ; =>inst_label 341 | ); 342 | self.emit_arithmetic(&mut ops, Inst::Add); 343 | } 344 | OPCode::ISub => { 345 | #[cfg(target_arch = "x86_64")] 346 | dynasm!(ops 347 | ; =>inst_label 348 | ); 349 | self.emit_arithmetic(&mut ops, Inst::Sub); 350 | } 351 | OPCode::IMul => { 352 | #[cfg(target_arch = "x86_64")] 353 | dynasm!(ops 354 | ; =>inst_label 355 | ); 356 | self.emit_arithmetic(&mut ops, Inst::IMul); 357 | } 358 | OPCode::IDiv => { 359 | #[cfg(target_arch = "x86_64")] 360 | dynasm!(ops 361 | ; =>inst_label 362 | ); 363 | self.emit_div(&mut ops, Inst::IDiv); 364 | } 365 | OPCode::IRem => { 366 | #[cfg(target_arch = "x86_64")] 367 | dynasm!(ops 368 | ; =>inst_label 369 | ); 370 | self.emit_div(&mut ops, Inst::IRem); 371 | } 372 | OPCode::IInc => { 373 | let index = match entry.instruction().nth(0) { 374 | Some(Value::Int(x)) => x, 375 | _ => unreachable!("First operand to iinc (index in locals) must be int") 376 | }; 377 | let constant = match entry.instruction().nth(1) { 378 | Some(Value::Int(x)) => x, 379 | _ => unreachable!("Second operand to iinc (constant for increment) must be int in current implementation") 380 | }; 381 | #[cfg(target_arch = "x86_64")] 382 | dynasm!(ops 383 | ; =>inst_label 384 | ); 385 | dynasm!(ops 386 | ; add [Rq(Register::Rdi as u8) + 4* index], constant as _ 387 | ); 388 | } 389 | OPCode::Goto => { 390 | // let target = match ... 391 | // if let Some(pc) = trace.contains(target) { 392 | // the target jump is inside the trace 393 | // is it before or after ? 394 | // if pc < entry.pc { 395 | // The target PC is before the current instruction 396 | // do we have a label for it ? 397 | // self.labels.get(pc) 398 | // emit a jmp .label 399 | // } else if pc > entry.pc { 400 | // The target PC is forward (think a break statement) so emit a jump 401 | // instruction to a new label and add this to labels map. 402 | // } 403 | // If the Goto target is outside then abondon this trace. 404 | // 405 | let target = match entry.instruction().nth(0) { 406 | Some(Value::Int(x)) => x, 407 | _ => unreachable!("First operand to goto (relative offset) must be int") 408 | }; 409 | if let Some(label) = self.labels.get(&ProgramCounter::new( 410 | entry.pc().get_method_index(), 411 | (entry.pc().get_instruction_index() as isize 412 | + target as isize) as usize, 413 | )) { 414 | #[cfg(target_arch = "x86_64")] 415 | dynasm!(ops 416 | ; jmp =>*label 417 | ); 418 | } 419 | } 420 | // if_icmp{cond} compares the top two values on the stack 421 | // and branches to the target offset given as an operand 422 | // if the comparison is not true. 423 | // Since our traces are self contained to the loop code 424 | // the target offset will be the exit pc value at which 425 | // the interpreter should continue execution. 426 | OPCode::IfICmpGe 427 | | OPCode::IfICmpGt 428 | | OPCode::IfICmpLe 429 | | OPCode::IfICmpEq => { 430 | let target = match entry.instruction().nth(0) { 431 | Some(Value::Int(x)) => x, 432 | _ => unreachable!("First operand to if_icmpge (relative offset) must be int") 433 | }; 434 | let mnemonic = entry.instruction().get_mnemonic(); 435 | exit_pc = (entry.pc().get_instruction_index() as isize 436 | + target as isize) as i32; 437 | 438 | self.emit_cond_branch(&mut ops, mnemonic); 439 | } 440 | OPCode::IfEq => { 441 | let operand = self.free_register(); 442 | match operand { 443 | Some(Operand::Register(reg)) => { 444 | #[cfg(target_arch = "x86_64")] 445 | dynasm!(ops 446 | ; cmp Rq(reg as u8), 0 447 | ; je ->abort_guard 448 | ); 449 | } 450 | Some(Operand::Memory(base, offset)) => { 451 | #[cfg(target_arch = "x86_64")] 452 | dynasm!(ops 453 | ; cmp [Rq(base as u8) + offset], 0 454 | ; je ->abort_guard 455 | ); 456 | } 457 | _ => unreachable!("expected operand for if_eq to be either `Operand::Memory` or `Operand::Register`"), 458 | } 459 | } 460 | OPCode::IfNe => { 461 | let operand = self.free_register(); 462 | match operand { 463 | Some(Operand::Register(reg)) => { 464 | #[cfg(target_arch = "x86_64")] 465 | dynasm!(ops 466 | ; cmp Rq(reg as u8), 0 467 | ; jz ->abort_guard 468 | ); 469 | } 470 | Some(Operand::Memory(base, offset)) => { 471 | #[cfg(target_arch = "x86_64")] 472 | dynasm!(ops 473 | ; cmp [Rq(base as u8) + offset], 0 474 | ; jz ->abort_guard 475 | ); 476 | } 477 | _ => unreachable!("expected operand for if_eq to be either `Operand::Memory` or `Operand::Register`"), 478 | } 479 | } 480 | _ => (), 481 | } 482 | } 483 | #[cfg(target_arch = "x86_64")] 484 | dynasm!(ops 485 | ; ->abort_guard: 486 | ; mov rax, exit_pc as _ 487 | ); 488 | // Epilogue for dynamically compiled code. 489 | epilogue!(ops); 490 | 491 | let buf = ops.finalize().unwrap(); 492 | 493 | let native_trace = NativeTrace(offset, buf); 494 | self.traces.insert(pc, native_trace); 495 | } 496 | 497 | /// Emit a move operation, this includes all data movement operations 498 | /// register to register and immediate to register. 499 | fn emit_mov(ops: &mut Assembler, dst: &Operand, src: &Operand) { 500 | match (dst, src) { 501 | (Operand::Register(dst), Operand::Register(src)) => { 502 | #[cfg(target_arch = "x86_64")] 503 | dynasm!(ops 504 | ;mov Rq(*dst as u8), Rq(*src as u8) 505 | ); 506 | } 507 | (Operand::Register(dst), Operand::Immediate(imm)) => { 508 | #[cfg(target_arch = "x86_64")] 509 | dynasm!(ops 510 | ;mov Rq(*dst as u8), *imm 511 | ); 512 | } 513 | (Operand::Register(dst), Operand::Memory(base, offset)) => { 514 | #[cfg(target_arch = "x86_64")] 515 | dynasm!(ops 516 | ;mov Rq(*dst as u8), [Rq(*base as u8) + *offset] 517 | ); 518 | } 519 | (Operand::Memory(base, offset), Operand::Register(src)) => { 520 | #[cfg(target_arch = "x86_64")] 521 | dynasm!(ops 522 | ; mov [Rq(*base as u8) + *offset], Rq(*src as u8) 523 | ); 524 | } 525 | (Operand::Memory(base, offset), Operand::Immediate(imm)) => { 526 | #[cfg(target_arch = "x86_64")] 527 | dynasm!(ops 528 | ; mov DWORD [Rq(*base as u8) + *offset], *imm as _ 529 | ); 530 | } 531 | _ => unreachable!( 532 | "Unexpected operands for `mov` `dst`={:?}, `src`={:?})", 533 | dst, src 534 | ), 535 | } 536 | } 537 | 538 | /// Emit an arithmetic operation, covers only simple instructions such as 539 | /// `add`, `mul` and `sub`. 540 | fn emit_arithmetic(&mut self, ops: &mut Assembler, op: Inst) { 541 | let rhs = match self.operands.pop() { 542 | Some(rhs) => rhs, 543 | None => panic!("expected operand found None"), 544 | }; 545 | let lhs = match self.operands.pop() { 546 | Some(lhs) => lhs, 547 | None => panic!("expected operand found None"), 548 | }; 549 | 550 | let dst = match &lhs { 551 | &Operand::Register(reg) => Operand::Register(reg), 552 | // TODO: need to mov lhs operand to the first free register. 553 | _ => { 554 | let dst = self.first_available_register(); 555 | JitCache::emit_mov(ops, &dst, &lhs); 556 | dst 557 | } 558 | }; 559 | if let Operand::Register(reg) = &rhs { 560 | self.registers.push_back(*reg) 561 | } 562 | 563 | self.operands.push(dst); 564 | 565 | match op { 566 | Inst::Add => { 567 | let Operand::Register(dst) = dst else { 568 | unreachable!("Unexpected enum variant for `Operand` expected `Register` got {:?}", dst) 569 | }; 570 | 571 | match rhs { 572 | Operand::Register(src) => { 573 | #[cfg(target_arch = "x86_64")] 574 | dynasm!(ops 575 | ; add Rq(dst as u8), Rq(src as u8) 576 | ); 577 | }, 578 | Operand::Immediate(val) => { 579 | #[cfg(target_arch = "x86_64")] 580 | dynasm!(ops 581 | ; add Rq(dst as u8), val as _ 582 | ); 583 | }, 584 | Operand::Memory(base, offset) => { 585 | #[cfg(target_arch = "x86_64")] 586 | dynasm!(ops 587 | ; add Rq(dst as u8), [Rq(base as u8) + offset] 588 | ); 589 | }, 590 | } 591 | } 592 | Inst::Sub => { 593 | let Operand::Register(dst) = dst else { 594 | unreachable!("Unexpected enum variant for `Operand` expected `Register` got {:?}", dst) 595 | }; 596 | 597 | match rhs { 598 | Operand::Register(src) => { 599 | #[cfg(target_arch = "x86_64")] 600 | dynasm!(ops 601 | ; sub Rq(dst as u8), Rq(src as u8) 602 | ); 603 | }, 604 | Operand::Immediate(val) => { 605 | #[cfg(target_arch = "x86_64")] 606 | dynasm!(ops 607 | ; sub Rq(dst as u8), val as _ 608 | ); 609 | }, 610 | Operand::Memory(base, offset) => { 611 | #[cfg(target_arch = "x86_64")] 612 | dynasm!(ops 613 | ; sub Rq(dst as u8), [Rq(base as u8) + offset] 614 | ); 615 | }, 616 | } 617 | } 618 | Inst::IMul => { 619 | let Operand::Register(dst) = dst else { 620 | unreachable!("Unexpected enum variant for `Operand` expected `Register` got {:?}", dst) 621 | }; 622 | match rhs { 623 | Operand::Register(src) => { 624 | #[cfg(target_arch = "x86_64")] 625 | dynasm!(ops 626 | ; imul Rq(dst as u8), Rq(src as u8) 627 | ); 628 | }, 629 | Operand::Immediate(val) => { 630 | #[cfg(target_arch = "x86_64")] 631 | dynasm!(ops 632 | ; imul Rq(dst as u8), Rq(dst as u8), val as _ 633 | ); 634 | }, 635 | Operand::Memory(base, offset) => { 636 | #[cfg(target_arch = "x86_64")] 637 | dynasm!(ops 638 | ; imul Rq(dst as u8), [Rq(base as u8) + offset] 639 | ); 640 | }, 641 | } 642 | } 643 | _ => unreachable!("emit_arithmetic only supports simple x86-64 arithmetic (add, sub and mul).)"), 644 | } 645 | } 646 | 647 | /// Emit division operation. 648 | fn emit_div(&mut self, ops: &mut Assembler, op: Inst) { 649 | let rdx = Register::Rdx; 650 | let rax = Register::Rax; 651 | 652 | let denom = match self.operands.pop() { 653 | Some(operand) => operand, 654 | _ => { 655 | unreachable!("Expected operand for `idiv` and `irem` got None") 656 | } 657 | }; 658 | 659 | if let Some(nom) = self.free_register() { 660 | JitCache::emit_mov(ops, &Operand::Register(Register::Rax), &nom); 661 | } 662 | let dst = match denom { 663 | Operand::Register(reg) => Operand::Register(reg), 664 | _ => { 665 | let reg = self.first_available_register(); 666 | JitCache::emit_mov(ops, ®, &denom); 667 | reg 668 | } 669 | }; 670 | 671 | let src = match op { 672 | // x86 division rax holds divident rdx holds modulo. 673 | Inst::IDiv => rax, 674 | Inst::IRem => rdx, 675 | _ => unreachable!("emit_div expected op to be idiv or irem"), 676 | }; 677 | 678 | #[cfg(target_arch = "x86_64")] 679 | let Operand::Register(dst_reg) = dst 680 | else { 681 | unreachable!("Unexpected enum variant for `Operand` expected `Register` got {:?}", dst) 682 | }; 683 | dynasm!(ops 684 | ; mov Rq(rdx as u8), 0 685 | ; div Rq(dst_reg as u8) 686 | ); 687 | JitCache::emit_mov(ops, &dst, &Operand::Register(src)); 688 | self.operands.push(dst); 689 | } 690 | 691 | /// Emit conditional branch for the given instruction. 692 | fn emit_cond_branch(&mut self, ops: &mut Assembler, cond: OPCode) { 693 | let rhs = match self.free_register() { 694 | Some(operand) => operand, 695 | None => panic!("expected operand found None"), 696 | }; 697 | let lhs = match self.free_register() { 698 | Some(operand) => operand, 699 | None => todo!("Expected register in operand stack found None"), 700 | }; 701 | 702 | match (lhs, rhs) { 703 | (Operand::Register(lhs), Operand::Register(rhs)) => { 704 | dynasm!(ops 705 | ; cmp Rq(lhs as u8), Rq(rhs as u8) 706 | ); 707 | } 708 | (Operand::Register(lhs), Operand::Memory(base, offset)) => { 709 | dynasm!(ops 710 | ; cmp Rq(lhs as u8), [Rq(base as u8) + offset] 711 | ); 712 | } 713 | (Operand::Register(lhs), Operand::Immediate(imm)) => { 714 | dynasm!(ops 715 | ; cmp Rq(lhs as u8), imm as _ 716 | ); 717 | } 718 | (Operand::Memory(base, offset), Operand::Register(rhs)) => { 719 | dynasm!(ops 720 | ; cmp [Rq(base as u8) + offset], Rq(rhs as u8) 721 | ); 722 | } 723 | (Operand::Memory(base, offset), Operand::Immediate(imm)) => { 724 | dynasm!(ops 725 | ; cmp [Rq(base as u8) + offset], imm as _ 726 | ); 727 | } 728 | _ => unreachable!( 729 | "unsupported comparison between operands {:?} and {:?}", 730 | lhs, rhs 731 | ), 732 | } 733 | 734 | match cond { 735 | OPCode::IfICmpGt => { 736 | dynasm!(ops 737 | ; jg ->abort_guard 738 | ); 739 | } 740 | OPCode::IfICmpGe => { 741 | dynasm!(ops 742 | ; jge ->abort_guard 743 | ); 744 | } 745 | OPCode::IfICmpLe => { 746 | dynasm!(ops 747 | ; jle -> abort_guard 748 | ); 749 | } 750 | OPCode::IfICmpEq => { 751 | dynasm!(ops 752 | ; je -> abort_guard 753 | ); 754 | } 755 | _ => unreachable!("Expected instruction for conditional branch to be a if_icmp {:?}", cond) 756 | } 757 | } 758 | 759 | /// Returns the first available register. 760 | fn first_available_register(&mut self) -> Operand { 761 | if !self.registers.is_empty() { 762 | let reg = self.registers.pop_front().unwrap(); 763 | Operand::Register(reg) 764 | } else { 765 | panic!("no available registers") 766 | } 767 | } 768 | 769 | /// Free the top most register in the operand stack. 770 | fn free_register(&mut self) -> Option { 771 | let op = self.operands.pop(); 772 | if let Some(Operand::Register(reg)) = op { 773 | self.registers.push_back(reg) 774 | } 775 | op 776 | } 777 | } 778 | 779 | #[cfg(test)] 780 | mod tests { 781 | use std::env; 782 | use std::path::Path; 783 | 784 | use crate::jvm::read_class_file; 785 | use crate::jvm::JVMParser; 786 | use crate::program::Program; 787 | use crate::runtime::{Runtime, Value}; 788 | 789 | macro_rules! run_jit_test_case { 790 | ($name: ident, $test_file:expr, $expected:expr) => { 791 | #[test] 792 | fn $name() { 793 | let env_var = env::var("CARGO_MANIFEST_DIR").unwrap(); 794 | let path = Path::new(&env_var).join($test_file); 795 | let class_file_bytes = 796 | read_class_file(&path).unwrap_or_else(|_| { 797 | panic!("Failed to parse file : {:?}", path.as_os_str()) 798 | }); 799 | let class_file = JVMParser::parse(&class_file_bytes); 800 | assert!(class_file.is_ok()); 801 | let program = Program::new(&class_file.unwrap()); 802 | let mut runtime = Runtime::new(program); 803 | assert!(runtime.run(true).is_ok()); 804 | assert_eq!(runtime.top_return_value(), $expected); 805 | } 806 | }; 807 | } 808 | 809 | run_jit_test_case!( 810 | loops, 811 | "support/tests/HotLoop.class", 812 | Some(Value::Int(55)) 813 | ); 814 | } 815 | -------------------------------------------------------------------------------- /src/jvm.rs: -------------------------------------------------------------------------------- 1 | //! Lightweight binary parser for Java class files. 2 | use byteorder::{BigEndian, ReadBytesExt}; 3 | 4 | use std::collections::HashMap; 5 | use std::io; 6 | use std::io::{Cursor, Read, Seek}; 7 | use std::path::Path; 8 | 9 | /// Values of magic bytes of a JVM class file. 10 | const JVM_CLASS_FILE_MAGIC: u32 = 0xCAFE_BABE; 11 | 12 | /// `CPInfo` represents constant pool entries, 13 | #[derive(Debug, Clone, PartialEq, Eq)] 14 | pub enum CPInfo { 15 | ConstantClass { 16 | name_index: u16, 17 | }, 18 | ConstantFieldRef { 19 | class_index: u16, 20 | name_and_type_index: u16, 21 | }, 22 | ConstantMethodRef { 23 | class_index: u16, 24 | name_and_type_index: u16, 25 | }, 26 | ConstantInterfaceMethodRef { 27 | class_index: u16, 28 | name_and_type_index: u16, 29 | }, 30 | ConstantString { 31 | string_index: u16, 32 | }, 33 | ConstantInteger { 34 | bytes: u32, 35 | }, 36 | ConstantFloat { 37 | bytes: u32, 38 | }, 39 | ConstantLong { 40 | hi_bytes: u32, 41 | lo_bytes: u32, 42 | }, 43 | ConstantDouble { 44 | hi_bytes: u32, 45 | lo_bytes: u32, 46 | }, 47 | ConstantNameAndType { 48 | name_index: u16, 49 | descriptor_index: u16, 50 | }, 51 | ConstantUtf8 { 52 | bytes: String, 53 | }, 54 | ConstantMethodHandle { 55 | reference_kind: u8, 56 | reference_index: u16, 57 | }, 58 | ConstantMethodType { 59 | descriptor_index: u16, 60 | }, 61 | ConstantInvokeDynamic { 62 | bootstrap_method_attr_index: u16, 63 | name_and_type_index: u16, 64 | }, 65 | // Proxy value used mostly to populate the gaps in the constant pool. 66 | Unspecified, 67 | } 68 | 69 | /// `ConstantKind` encodes the kind of a constant in the constants pool. 70 | #[repr(u8)] 71 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 72 | enum ConstantKind { 73 | Class = 7, 74 | FieldRef = 9, 75 | MethodRef = 10, 76 | InterfaceMethodRef = 11, 77 | String = 8, 78 | Integer = 3, 79 | Float = 4, 80 | Long = 5, 81 | Double = 6, 82 | NameAndType = 12, 83 | Utf8 = 1, 84 | MethodHandle = 15, 85 | MethodType = 16, 86 | Dynamic = 17, 87 | InvokeDynamic = 18, 88 | // Unspecified or unsupported constant kinds. 89 | // Module = 19, 90 | // Package = 20, 91 | Unspecified, 92 | } 93 | 94 | impl From for ConstantKind { 95 | fn from(v: u8) -> Self { 96 | match v { 97 | 1 => Self::Utf8, 98 | 3 => Self::Integer, 99 | 4 => Self::Float, 100 | 5 => Self::Long, 101 | 6 => Self::Double, 102 | 7 => Self::Class, 103 | 8 => Self::String, 104 | 9 => Self::FieldRef, 105 | 10 => Self::MethodRef, 106 | 11 => Self::InterfaceMethodRef, 107 | 12 => Self::NameAndType, 108 | 15 => Self::MethodHandle, 109 | 16 => Self::MethodType, 110 | 17 => Self::Dynamic, 111 | 18 => Self::InvokeDynamic, 112 | _ => Self::Unspecified, 113 | } 114 | } 115 | } 116 | 117 | /// Verification type specifies the type of a single variable location or 118 | /// a single operand stack entry. 119 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 120 | enum VerificationType { 121 | TopVerification = 0, 122 | IntegerVerification = 1, 123 | FloatVerification = 2, 124 | LongVerification = 4, 125 | DoubleVerification = 3, 126 | NullVerification = 5, 127 | UninitializedThisVerification = 6, 128 | ObjectVerification = 7, 129 | UninitializedVerification = 8, 130 | Unspecified, 131 | } 132 | 133 | impl From for VerificationType { 134 | fn from(v: u8) -> Self { 135 | match v { 136 | 0 => Self::TopVerification, 137 | 1 => Self::IntegerVerification, 138 | 2 => Self::FloatVerification, 139 | 3 => Self::DoubleVerification, 140 | 4 => Self::LongVerification, 141 | 5 => Self::NullVerification, 142 | 6 => Self::UninitializedThisVerification, 143 | 7 => Self::ObjectVerification, 144 | 8 => Self::UninitializedVerification, 145 | _ => Self::Unspecified, 146 | } 147 | } 148 | } 149 | 150 | /// Verification info struct. 151 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 152 | struct VerificationInfo { 153 | tag: VerificationType, 154 | cpool_index_or_offset: u16, 155 | } 156 | 157 | /// Stack map frame type. 158 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 159 | enum StackMapFrameType { 160 | Same, 161 | SameLocals, 162 | SameLocalsExtended, 163 | Chop, 164 | SameExtended, 165 | Append, 166 | Full, 167 | } 168 | 169 | /// Stack map frame. 170 | #[derive(Debug, Clone, PartialEq, Eq)] 171 | pub struct StackMapFrame { 172 | t: StackMapFrameType, 173 | offset_delta: u16, 174 | locals: Vec, 175 | stack: Vec, 176 | } 177 | 178 | /// Bootstrap method. 179 | #[derive(Debug, Clone, PartialEq, Eq)] 180 | pub struct BootstrapMethod { 181 | method_ref: u16, 182 | arguments: Vec, 183 | } 184 | 185 | /// Exception table. 186 | #[derive(Debug, Clone, PartialEq, Eq)] 187 | pub struct ExceptionEntry { 188 | start_pc: u16, 189 | end_pc: u16, 190 | handler_pc: u16, 191 | catch_type: u16, 192 | } 193 | 194 | #[derive(Debug, Clone, PartialEq, Eq)] 195 | pub enum AttributeInfo { 196 | ConstantValueAttribute { 197 | constant_value_index: u16, 198 | attribute_name: String, 199 | }, 200 | CodeAttribute { 201 | max_stack: u16, 202 | max_locals: u16, 203 | code: Vec, 204 | exception_table: Vec, 205 | attributes: HashMap, 206 | attribute_name: String, 207 | }, 208 | StackMapTableAttribute { 209 | entries: Vec, 210 | attribute_name: String, 211 | }, 212 | SourceFileAttribute { 213 | source_file_index: u16, 214 | attribute_name: String, 215 | }, 216 | BootstrapMethodsAttribute { 217 | bootstrap_methods: Vec, 218 | attribute_name: String, 219 | }, 220 | NestHostAttribute { 221 | host_class_index: u16, 222 | attribute_name: String, 223 | }, 224 | NestMembersAttribute { 225 | classes: Vec, 226 | attribute_name: String, 227 | }, 228 | } 229 | 230 | #[derive(Debug, Clone, PartialEq, Eq)] 231 | pub struct FieldInfo { 232 | access_flag: u16, 233 | name_index: u16, 234 | descriptor_index: u16, 235 | attributes: HashMap, 236 | } 237 | 238 | #[derive(Debug, Clone, PartialEq, Eq)] 239 | pub struct MethodInfo { 240 | access_flag: u16, 241 | name_index: u16, 242 | descriptor_index: u16, 243 | attributes: HashMap, 244 | } 245 | 246 | impl MethodInfo { 247 | /// Returns method info descriptor index. 248 | #[must_use] 249 | pub const fn descriptor_index(&self) -> u16 { 250 | self.descriptor_index 251 | } 252 | 253 | /// Returns method info name index. 254 | #[must_use] 255 | pub const fn name_index(&self) -> u16 { 256 | self.name_index 257 | } 258 | 259 | /// Returns a copy of the method info attributes. 260 | #[must_use] 261 | pub fn attributes(&self) -> HashMap { 262 | self.attributes.clone() 263 | } 264 | } 265 | 266 | /// `JVMClassFile` represents a Java class file. 267 | #[derive(Debug, Clone)] 268 | pub struct JVMClassFile { 269 | _magic: u32, 270 | _minor_version: u16, 271 | _major_version: u16, 272 | _constant_pool_count: u16, 273 | constant_pool: Vec, 274 | _access_flags: u16, 275 | _this_class: u16, 276 | _super_class: u16, 277 | _interfaces_count: u16, 278 | _interfaces: Vec, 279 | _fields_count: u16, 280 | _fields: Vec, 281 | _methods_count: u16, 282 | methods: Vec, 283 | _attributes_count: u16, 284 | _attributes: HashMap, 285 | } 286 | 287 | impl JVMClassFile { 288 | /// Returns a copy of the underlying constant pool. 289 | #[must_use] 290 | pub fn constant_pool(&self) -> Vec { 291 | self.constant_pool.clone() 292 | } 293 | 294 | /// Returns a copy of the underlying methods vector. 295 | #[must_use] 296 | pub fn methods(&self) -> Vec { 297 | self.methods.clone() 298 | } 299 | } 300 | 301 | /// `JVMParser` namespaces functions that handle parsing of Java class files. 302 | #[derive(Debug)] 303 | pub struct JVMParser; 304 | 305 | impl JVMParser { 306 | /// Parse a Java class file. 307 | /// # Errors 308 | /// Returns `io::Error` in case a `std::io::Read` fails. 309 | /// # Panics 310 | /// Can panic if file isn't valid, since we don't handle some 311 | /// `std::io::Read` failures. 312 | pub fn parse(class_file_bytes: &[u8]) -> io::Result { 313 | // Create a new cursor on the class file bytes. 314 | let mut buffer = Cursor::new(class_file_bytes); 315 | // Read magic header.. 316 | let magic = buffer.read_u32::()?; 317 | // Read the class file version numbers. 318 | let minor_version = buffer.read_u16::()?; 319 | let major_version = buffer.read_u16::()?; 320 | // Read the number of constants in the pool. 321 | let cp_size = buffer.read_u16::()?; 322 | // Parse the constant pool. 323 | let constant_pool = parse_constant_pool(&mut buffer, cp_size as usize); 324 | // Extra class file metdata. 325 | let access_flags = buffer.read_u16::()?; 326 | let this_class = buffer.read_u16::()?; 327 | let super_class = buffer.read_u16::()?; 328 | // Interface definitions 329 | let interfaces_count = buffer.read_u16::()?; 330 | let mut interfaces = Vec::new(); 331 | 332 | for _ in 0..interfaces_count { 333 | let interface = buffer.read_u16::()?; 334 | interfaces.push(interface); 335 | } 336 | // Field information. 337 | let (fields_count, fields) = 338 | parse_field_information(&mut buffer, &constant_pool); 339 | // Methods. 340 | let (methods_count, methods) = 341 | parse_method_information(&mut buffer, &constant_pool); 342 | // Attributes. 343 | let (attributes_count, attributes) = 344 | parse_attribute_info(&mut buffer, &constant_pool); 345 | 346 | Ok(JVMClassFile { 347 | _magic: magic, 348 | _minor_version: minor_version, 349 | _major_version: major_version, 350 | _constant_pool_count: cp_size, 351 | constant_pool, 352 | _access_flags: access_flags, 353 | _this_class: this_class, 354 | _super_class: super_class, 355 | _interfaces_count: interfaces_count, 356 | _interfaces: interfaces, 357 | _fields_count: fields_count, 358 | _fields: fields, 359 | _methods_count: methods_count, 360 | methods, 361 | _attributes_count: attributes_count, 362 | _attributes: attributes, 363 | }) 364 | } 365 | } 366 | 367 | /// Parse constants pool. 368 | fn parse_constant_pool( 369 | reader: &mut (impl Read + Seek), 370 | pool_size: usize, 371 | ) -> Vec { 372 | // We preallocate because indexing is shifted and we know the pool size. 373 | let mut constant_pool = vec![CPInfo::Unspecified; pool_size]; 374 | // The first entry in the pool is at index 1 according to JVM 375 | // spec. 376 | #[allow(unused_assignments)] 377 | (1..pool_size).for_each(|mut ii| { 378 | let tag = reader.read_u8().unwrap(); 379 | match ConstantKind::from(tag) { 380 | ConstantKind::Class => { 381 | constant_pool[ii] = CPInfo::ConstantClass { 382 | name_index: reader.read_u16::().unwrap(), 383 | }; 384 | } 385 | ConstantKind::FieldRef => { 386 | constant_pool[ii] = CPInfo::ConstantFieldRef { 387 | class_index: reader.read_u16::().unwrap(), 388 | name_and_type_index: reader 389 | .read_u16::() 390 | .unwrap(), 391 | }; 392 | } 393 | ConstantKind::MethodRef => { 394 | constant_pool[ii] = CPInfo::ConstantMethodRef { 395 | class_index: reader.read_u16::().unwrap(), 396 | name_and_type_index: reader 397 | .read_u16::() 398 | .unwrap(), 399 | }; 400 | } 401 | ConstantKind::InterfaceMethodRef => { 402 | constant_pool[ii] = CPInfo::ConstantInterfaceMethodRef { 403 | class_index: reader.read_u16::().unwrap(), 404 | name_and_type_index: reader 405 | .read_u16::() 406 | .unwrap(), 407 | }; 408 | } 409 | ConstantKind::String => { 410 | constant_pool[ii] = CPInfo::ConstantString { 411 | string_index: reader.read_u16::().unwrap(), 412 | }; 413 | } 414 | ConstantKind::Integer => { 415 | constant_pool[ii] = CPInfo::ConstantInteger { 416 | bytes: reader.read_u32::().unwrap(), 417 | }; 418 | } 419 | ConstantKind::Float => { 420 | constant_pool[ii] = CPInfo::ConstantFloat { 421 | bytes: reader.read_u32::().unwrap(), 422 | }; 423 | } 424 | ConstantKind::Long => { 425 | constant_pool[ii] = CPInfo::ConstantLong { 426 | hi_bytes: reader.read_u32::().unwrap(), 427 | lo_bytes: reader.read_u32::().unwrap(), 428 | }; 429 | ii += 1; 430 | } 431 | ConstantKind::Double => { 432 | constant_pool[ii] = CPInfo::ConstantDouble { 433 | hi_bytes: reader.read_u32::().unwrap(), 434 | lo_bytes: reader.read_u32::().unwrap(), 435 | }; 436 | ii += 1; 437 | } 438 | ConstantKind::NameAndType => { 439 | constant_pool[ii] = CPInfo::ConstantNameAndType { 440 | name_index: reader.read_u16::().unwrap(), 441 | descriptor_index: reader.read_u16::().unwrap(), 442 | }; 443 | } 444 | ConstantKind::Utf8 => { 445 | let length = reader.read_u16::().unwrap(); 446 | let mut buf = vec![0u8; length as usize]; 447 | reader.read_exact(&mut buf).unwrap(); 448 | constant_pool[ii] = CPInfo::ConstantUtf8 { 449 | bytes: String::from_utf8(buf).unwrap(), 450 | }; 451 | } 452 | ConstantKind::MethodHandle => { 453 | let ref_kind = reader.read_u8().unwrap(); 454 | let ref_index = reader.read_u16::().unwrap(); 455 | constant_pool[ii] = CPInfo::ConstantMethodHandle { 456 | reference_kind: ref_kind, 457 | reference_index: ref_index, 458 | }; 459 | } 460 | ConstantKind::MethodType => { 461 | let desc_index = reader.read_u16::().unwrap(); 462 | constant_pool[ii] = CPInfo::ConstantMethodType { 463 | descriptor_index: desc_index, 464 | }; 465 | } 466 | ConstantKind::InvokeDynamic => { 467 | let bootstrap_method_attr_index = 468 | reader.read_u16::().unwrap(); 469 | let name_and_type_index = 470 | reader.read_u16::().unwrap(); 471 | constant_pool[ii] = CPInfo::ConstantInvokeDynamic { 472 | bootstrap_method_attr_index, 473 | name_and_type_index, 474 | }; 475 | } 476 | _ => panic!( 477 | "Unexpected constant kind {:?} with tag {}", 478 | ConstantKind::from(tag), 479 | tag 480 | ), 481 | } 482 | }); 483 | constant_pool 484 | } 485 | 486 | /// Parse field information. 487 | fn parse_field_information( 488 | reader: &mut (impl Read + Seek), 489 | constant_pool: &[CPInfo], 490 | ) -> (u16, Vec) { 491 | let fields_count = reader.read_u16::().unwrap(); 492 | let mut fields: Vec = Vec::new(); 493 | 494 | for _ in 0..fields_count { 495 | let access_flag = reader.read_u16::().unwrap(); 496 | let name_index = reader.read_u16::().unwrap(); 497 | let descriptor_index = reader.read_u16::().unwrap(); 498 | let (_, attributes) = parse_attribute_info(reader, constant_pool); 499 | fields.push(FieldInfo { 500 | access_flag, 501 | name_index, 502 | descriptor_index, 503 | attributes, 504 | }); 505 | } 506 | 507 | (fields_count, fields) 508 | } 509 | 510 | /// Parse method infromation. 511 | fn parse_method_information( 512 | reader: &mut (impl Read + Seek), 513 | constant_pool: &[CPInfo], 514 | ) -> (u16, Vec) { 515 | let methods_count = reader.read_u16::().unwrap(); 516 | let mut methods: Vec = Vec::new(); 517 | 518 | for _ in 0..methods_count { 519 | let access_flag = reader.read_u16::().unwrap(); 520 | let name_index = reader.read_u16::().unwrap(); 521 | let descriptor_index = reader.read_u16::().unwrap(); 522 | let (_, attributes) = parse_attribute_info(reader, constant_pool); 523 | methods.push(MethodInfo { 524 | access_flag, 525 | name_index, 526 | descriptor_index, 527 | attributes, 528 | }); 529 | } 530 | 531 | (methods_count, methods) 532 | } 533 | 534 | /// Parse code attribute 535 | fn parse_code_attribute( 536 | reader: &mut (impl Read + Seek), 537 | constant_pool: &[CPInfo], 538 | ) -> AttributeInfo { 539 | let max_stack = reader.read_u16::().unwrap(); 540 | let max_locals = reader.read_u16::().unwrap(); 541 | let code_length = reader.read_u32::().unwrap(); 542 | let mut buf = vec![0u8; code_length as usize]; 543 | reader.read_exact(&mut buf).unwrap(); 544 | let exception_table_length = reader.read_u16::().unwrap(); 545 | let mut exception_table_entries: Vec = Vec::new(); 546 | for _ in 0..exception_table_length { 547 | let start_pc = reader.read_u16::().unwrap(); 548 | let end_pc = reader.read_u16::().unwrap(); 549 | let handler_pc = reader.read_u16::().unwrap(); 550 | let catch_type = reader.read_u16::().unwrap(); 551 | 552 | exception_table_entries.push(ExceptionEntry { 553 | start_pc, 554 | end_pc, 555 | handler_pc, 556 | catch_type, 557 | }); 558 | } 559 | let (_, attributes) = parse_attribute_info(reader, constant_pool); 560 | AttributeInfo::CodeAttribute { 561 | max_stack, 562 | max_locals, 563 | code: buf, 564 | exception_table: exception_table_entries, 565 | attributes, 566 | attribute_name: "Code".to_string(), 567 | } 568 | } 569 | 570 | /// Parse attributes. 571 | fn parse_attribute_info( 572 | reader: &mut (impl Read + Seek), 573 | constant_pool: &[CPInfo], 574 | ) -> (u16, HashMap) { 575 | let attribute_count = reader.read_u16::().unwrap(); 576 | let mut attributes: HashMap = HashMap::new(); 577 | for _ in 0..attribute_count { 578 | let attribute_name_index = reader.read_u16::().unwrap(); 579 | let attr_name = &constant_pool[attribute_name_index as usize]; 580 | let attribute_name = match attr_name { 581 | CPInfo::ConstantUtf8 { bytes } => bytes.clone(), 582 | _ => panic!( 583 | "Expected attribute name to be CPInfo::ConstantUtf8 got {attr_name:?}", 584 | ), 585 | }; 586 | let attribute_length = reader.read_u32::().unwrap(); 587 | let attribute_info = match attribute_name.as_str() { 588 | "ConstantValue" => Some(AttributeInfo::ConstantValueAttribute { 589 | constant_value_index: reader.read_u16::().unwrap(), 590 | attribute_name: attribute_name.clone(), 591 | }), 592 | "Code" => Some(parse_code_attribute(reader, constant_pool)), 593 | "StackMapTable" => { 594 | let number_of_entries = reader.read_u16::().unwrap(); 595 | let mut stack_map_entries: Vec = Vec::new(); 596 | for _ in 0..number_of_entries { 597 | let tag = reader.read_u8().unwrap(); 598 | let frame = parse_stack_frame_entry(reader, tag); 599 | stack_map_entries.push(frame); 600 | } 601 | Some(AttributeInfo::StackMapTableAttribute { 602 | entries: stack_map_entries, 603 | attribute_name: "StackMapTable".to_string(), 604 | }) 605 | } 606 | "SourceFile" => Some(AttributeInfo::SourceFileAttribute { 607 | source_file_index: reader.read_u16::().unwrap(), 608 | attribute_name: "SourceFile".to_string(), 609 | }), 610 | "BootstrapMethods" => { 611 | let num_bootstrap_methods = 612 | reader.read_u16::().unwrap(); 613 | let mut bootstrap_method_table: Vec = 614 | Vec::new(); 615 | 616 | for _ in 0..num_bootstrap_methods { 617 | let method_ref = reader.read_u16::().unwrap(); 618 | let argument_count = 619 | reader.read_u16::().unwrap(); 620 | let mut arguments = Vec::new(); 621 | for _ in 0..argument_count { 622 | let arg = reader.read_u16::().unwrap(); 623 | arguments.push(arg); 624 | } 625 | bootstrap_method_table.push(BootstrapMethod { 626 | method_ref, 627 | arguments, 628 | }); 629 | } 630 | 631 | Some(AttributeInfo::BootstrapMethodsAttribute { 632 | bootstrap_methods: bootstrap_method_table, 633 | attribute_name: "BootstrapMethods".to_string(), 634 | }) 635 | } 636 | "NestHost" => Some(AttributeInfo::NestHostAttribute { 637 | host_class_index: reader.read_u16::().unwrap(), 638 | attribute_name: "NestHost".to_string(), 639 | }), 640 | "NestMembers" => { 641 | let num_classes = reader.read_u16::().unwrap(); 642 | let mut classes = Vec::new(); 643 | for _ in 0..num_classes { 644 | let class_index = reader.read_u16::().unwrap(); 645 | classes.push(class_index); 646 | } 647 | Some(AttributeInfo::NestMembersAttribute { 648 | classes, 649 | attribute_name: "NestMembers".to_string(), 650 | }) 651 | } 652 | _ => { 653 | reader 654 | .seek(std::io::SeekFrom::Current(i64::from( 655 | attribute_length, 656 | ))) 657 | .unwrap(); 658 | None 659 | } 660 | }; 661 | let _ = attribute_info.map_or((), |attr| { 662 | attributes.insert(attribute_name.clone(), attr); 663 | }); 664 | } 665 | (attribute_count, attributes) 666 | } 667 | 668 | /// Helper function to parse the `StackMapFrameTable` entry give a tag. 669 | fn parse_stack_frame_entry(reader: &mut impl Read, tag: u8) -> StackMapFrame { 670 | match tag { 671 | 0..=63 => StackMapFrame { 672 | t: StackMapFrameType::Same, 673 | offset_delta: 0, 674 | locals: vec![], 675 | stack: vec![], 676 | }, 677 | 64..=127 => StackMapFrame { 678 | t: StackMapFrameType::SameLocals, 679 | offset_delta: 0, 680 | locals: vec![], 681 | stack: parse_verification_info(reader, 1), 682 | }, 683 | 247 => StackMapFrame { 684 | t: StackMapFrameType::SameLocalsExtended, 685 | offset_delta: 0, 686 | locals: vec![], 687 | stack: parse_verification_info(reader, 1), 688 | }, 689 | 248..=250 => StackMapFrame { 690 | t: StackMapFrameType::Chop, 691 | offset_delta: reader.read_u16::().unwrap(), 692 | locals: vec![], 693 | stack: vec![], 694 | }, 695 | 251 => StackMapFrame { 696 | t: StackMapFrameType::SameExtended, 697 | offset_delta: reader.read_u16::().unwrap(), 698 | locals: vec![], 699 | stack: vec![], 700 | }, 701 | 252..=254 => StackMapFrame { 702 | t: StackMapFrameType::Append, 703 | offset_delta: reader.read_u16::().unwrap(), 704 | locals: parse_verification_info(reader, (tag - 251).into()), 705 | stack: vec![], 706 | }, 707 | 255 => { 708 | let offset_delta = reader.read_u16::().unwrap(); 709 | let n_locals_entries = reader.read_u16::().unwrap(); 710 | let locals = parse_verification_info(reader, n_locals_entries); 711 | let n_stack_entries = reader.read_u16::().unwrap(); 712 | let stack = parse_verification_info(reader, n_stack_entries); 713 | StackMapFrame { 714 | t: StackMapFrameType::Full, 715 | offset_delta, 716 | locals, 717 | stack, 718 | } 719 | } 720 | _ => unreachable!("Unexpected tag entry {tag}"), 721 | } 722 | } 723 | 724 | /// Helper function parse verification info. 725 | fn parse_verification_info( 726 | reader: &mut impl Read, 727 | num_entries: u16, 728 | ) -> Vec { 729 | let mut verifications: Vec = Vec::new(); 730 | for _ in 0..num_entries { 731 | let tag = VerificationType::from(reader.read_u8().unwrap()); 732 | let cpool_index_or_offset = if tag 733 | == VerificationType::ObjectVerification 734 | || tag == VerificationType::UninitializedVerification 735 | { 736 | reader.read_u16::().unwrap() 737 | } else { 738 | 0 739 | }; 740 | verifications.push(VerificationInfo { 741 | tag, 742 | cpool_index_or_offset, 743 | }); 744 | } 745 | verifications 746 | } 747 | 748 | /// Helper function to read file into a buffer. 749 | /// # Panics 750 | /// Function panics on any `File::open` error. 751 | pub fn read_class_file(fp: &Path) -> io::Result> { 752 | use std::fs::File; 753 | use std::io::prelude::*; 754 | 755 | let mut f = File::open(fp).unwrap(); 756 | let mut buffer = Vec::new(); 757 | 758 | match f.read_to_end(&mut buffer) { 759 | Ok(bytes_read) => { 760 | assert!(bytes_read == f.metadata()?.len() as usize); 761 | assert!( 762 | u32::from_be_bytes( 763 | buffer[0..4].try_into().expect("slice with wrong length") 764 | ) == JVM_CLASS_FILE_MAGIC 765 | ); 766 | Ok(buffer) 767 | } 768 | Err(err) => Err(err), 769 | } 770 | } 771 | 772 | #[cfg(test)] 773 | mod tests { 774 | use super::*; 775 | use std::env; 776 | use std::path::Path; 777 | 778 | #[test] 779 | fn can_you_read_class_file() { 780 | let env_var = env::var("CARGO_MANIFEST_DIR").unwrap(); 781 | let path = 782 | Path::new(&env_var).join("support/tests//SingleFuncCall.class"); 783 | let class_file_bytes = read_class_file(&path).unwrap_or_else(|_| { 784 | panic!("Failed to parse file : {:?}", path.as_os_str()) 785 | }); 786 | let result = JVMParser::parse(&class_file_bytes); 787 | assert!(result.is_ok()); 788 | let class_file = result.unwrap(); 789 | assert_eq!(JVM_CLASS_FILE_MAGIC, class_file._magic); 790 | assert!( 791 | class_file._minor_version == 0 792 | || class_file._minor_version == 0xFFFF 793 | ); 794 | assert!(class_file._major_version > 61); 795 | } 796 | 797 | #[test] 798 | fn can_parse_class_file_header() { 799 | let env_var = env::var("CARGO_MANIFEST_DIR").unwrap(); 800 | let path = 801 | Path::new(&env_var).join("support/tests/SingleFuncCall.class"); 802 | let class_file_bytes = read_class_file(&path).unwrap_or_else(|_| { 803 | panic!("Failed to parse file : {:?}", path.as_os_str()) 804 | }); 805 | let result = JVMParser::parse(&class_file_bytes); 806 | assert!(result.is_ok()); 807 | let class_file = result.unwrap(); 808 | let expected_class_file = JVMClassFile { 809 | _magic: 3405691582, 810 | _minor_version: 0, 811 | _major_version: 64, 812 | _constant_pool_count: 31, 813 | constant_pool: vec![ 814 | CPInfo::Unspecified, 815 | CPInfo::ConstantMethodRef { 816 | class_index: 2, 817 | name_and_type_index: 3, 818 | }, 819 | CPInfo::ConstantClass { name_index: 4 }, 820 | CPInfo::ConstantNameAndType { 821 | name_index: 5, 822 | descriptor_index: 6, 823 | }, 824 | CPInfo::ConstantUtf8 { 825 | bytes: "java/lang/Object".to_string(), 826 | }, 827 | CPInfo::ConstantUtf8 { 828 | bytes: "".to_string(), 829 | }, 830 | CPInfo::ConstantUtf8 { 831 | bytes: "()V".to_string(), 832 | }, 833 | CPInfo::ConstantMethodRef { 834 | class_index: 8, 835 | name_and_type_index: 9, 836 | }, 837 | CPInfo::ConstantClass { name_index: 10 }, 838 | CPInfo::ConstantNameAndType { 839 | name_index: 11, 840 | descriptor_index: 12, 841 | }, 842 | CPInfo::ConstantUtf8 { 843 | bytes: "SingleFuncCall".to_string(), 844 | }, 845 | CPInfo::ConstantUtf8 { 846 | bytes: "add".to_string(), 847 | }, 848 | CPInfo::ConstantUtf8 { 849 | bytes: "(II)I".to_string(), 850 | }, 851 | CPInfo::ConstantFieldRef { 852 | class_index: 14, 853 | name_and_type_index: 15, 854 | }, 855 | CPInfo::ConstantClass { name_index: 16 }, 856 | CPInfo::ConstantNameAndType { 857 | name_index: 17, 858 | descriptor_index: 18, 859 | }, 860 | CPInfo::ConstantUtf8 { 861 | bytes: "java/lang/System".to_string(), 862 | }, 863 | CPInfo::ConstantUtf8 { 864 | bytes: "out".to_string(), 865 | }, 866 | CPInfo::ConstantUtf8 { 867 | bytes: "Ljava/io/PrintStream;".to_string(), 868 | }, 869 | CPInfo::ConstantMethodRef { 870 | class_index: 20, 871 | name_and_type_index: 21, 872 | }, 873 | CPInfo::ConstantClass { name_index: 22 }, 874 | CPInfo::ConstantNameAndType { 875 | name_index: 23, 876 | descriptor_index: 24, 877 | }, 878 | CPInfo::ConstantUtf8 { 879 | bytes: "java/io/PrintStream".to_string(), 880 | }, 881 | CPInfo::ConstantUtf8 { 882 | bytes: "println".to_string(), 883 | }, 884 | CPInfo::ConstantUtf8 { 885 | bytes: "(I)V".to_string(), 886 | }, 887 | CPInfo::ConstantUtf8 { 888 | bytes: "Code".to_string(), 889 | }, 890 | CPInfo::ConstantUtf8 { 891 | bytes: "LineNumberTable".to_string(), 892 | }, 893 | CPInfo::ConstantUtf8 { 894 | bytes: "main".to_string(), 895 | }, 896 | CPInfo::ConstantUtf8 { 897 | bytes: "([Ljava/lang/String;)V".to_string(), 898 | }, 899 | CPInfo::ConstantUtf8 { 900 | bytes: "SourceFile".to_string(), 901 | }, 902 | CPInfo::ConstantUtf8 { 903 | bytes: "SingleFuncCall.java".to_string(), 904 | }, 905 | ], 906 | _access_flags: 33, 907 | _this_class: 8, 908 | _super_class: 2, 909 | _interfaces_count: 0, 910 | _interfaces: vec![], 911 | _fields_count: 0, 912 | _fields: vec![], 913 | _methods_count: 3, 914 | methods: vec![ 915 | MethodInfo { 916 | access_flag: 1, 917 | name_index: 5, 918 | descriptor_index: 6, 919 | attributes: HashMap::from([( 920 | "Code".to_string(), 921 | AttributeInfo::CodeAttribute { 922 | max_stack: 1, 923 | max_locals: 1, 924 | code: vec![42, 183, 0, 1, 177], 925 | exception_table: vec![], 926 | attributes: HashMap::new(), 927 | attribute_name: "Code".to_string(), 928 | }, 929 | )]), 930 | }, 931 | MethodInfo { 932 | access_flag: 9, 933 | name_index: 27, 934 | descriptor_index: 28, 935 | attributes: HashMap::from([( 936 | "Code".to_string(), 937 | AttributeInfo::CodeAttribute { 938 | max_stack: 2, 939 | max_locals: 2, 940 | code: vec![ 941 | 6, 5, 184, 0, 7, 60, 178, 0, 13, 27, 182, 0, 942 | 19, 177, 943 | ], 944 | exception_table: vec![], 945 | attributes: HashMap::new(), 946 | attribute_name: "Code".to_string(), 947 | }, 948 | )]), 949 | }, 950 | MethodInfo { 951 | access_flag: 8, 952 | name_index: 11, 953 | descriptor_index: 12, 954 | attributes: HashMap::from([( 955 | "Code".to_string(), 956 | AttributeInfo::CodeAttribute { 957 | max_stack: 2, 958 | max_locals: 2, 959 | code: vec![26, 27, 96, 172], 960 | exception_table: vec![], 961 | attributes: HashMap::new(), 962 | attribute_name: "Code".to_string(), 963 | }, 964 | )]), 965 | }, 966 | ], 967 | _attributes_count: 1, 968 | _attributes: HashMap::from([( 969 | "SourceFile".to_string(), 970 | AttributeInfo::SourceFileAttribute { 971 | source_file_index: 30, 972 | attribute_name: "SourceFile".to_string(), 973 | }, 974 | )]), 975 | }; 976 | 977 | assert_eq!(class_file._magic, expected_class_file._magic); 978 | assert_eq!( 979 | class_file._minor_version, 980 | expected_class_file._minor_version 981 | ); 982 | assert_eq!( 983 | class_file._major_version, 984 | expected_class_file._major_version 985 | ); 986 | assert_eq!( 987 | class_file._constant_pool_count, 988 | expected_class_file._constant_pool_count 989 | ); 990 | assert_eq!(class_file.constant_pool, expected_class_file.constant_pool); 991 | assert_eq!(class_file._access_flags, expected_class_file._access_flags); 992 | assert_eq!(class_file._this_class, expected_class_file._this_class); 993 | assert_eq!(class_file._super_class, expected_class_file._super_class); 994 | assert_eq!( 995 | class_file._interfaces_count, 996 | expected_class_file._interfaces_count 997 | ); 998 | assert_eq!(class_file._interfaces, expected_class_file._interfaces); 999 | assert_eq!(class_file._fields_count, expected_class_file._fields_count); 1000 | assert_eq!(class_file._fields, expected_class_file._fields); 1001 | assert_eq!( 1002 | class_file._methods_count, 1003 | expected_class_file._methods_count 1004 | ); 1005 | assert_eq!(class_file.methods, expected_class_file.methods); 1006 | assert_eq!( 1007 | class_file._attributes_count, 1008 | expected_class_file._attributes_count 1009 | ); 1010 | } 1011 | } 1012 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod arm64; 2 | pub mod bytecode; 3 | pub mod jit; 4 | pub mod jvm; 5 | pub mod profiler; 6 | pub mod program; 7 | pub mod runtime; 8 | pub mod trace; 9 | pub mod x86; 10 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process::exit; 3 | 4 | use coldbrew::jvm::{read_class_file, JVMParser}; 5 | use coldbrew::program::Program; 6 | use coldbrew::runtime::Runtime; 7 | 8 | const USAGE_CMD: &str = " 9 | Coldbrew Tracing JIT usage guide : 10 | 11 | Run `coldbrew unit` to run small test programs (interpreter only). 12 | Run `coldbrew integration` to run end to end CPU intensive test programs (interpreter only). 13 | Run `coldbrew jit` to run small test programs with hot loops (interpreter + tracing jit). 14 | Run `coldbrew help` to see this message. 15 | "; 16 | 17 | fn main() { 18 | // Decide which test files to run. 19 | let args: Vec = env::args().collect(); 20 | let jit_mode = args[1].as_str() == "jit"; 21 | assert!( 22 | (args.len() >= 2), 23 | "Unexpected argument use `coldbrew help` to see usage guide." 24 | ); 25 | let folder = match args[1].as_str() { 26 | "unit" => "./support/tests/", 27 | "integration" => "./support/integration/", 28 | "jit" => "./support/jit/", 29 | "help" => { 30 | println!("{USAGE_CMD}"); 31 | exit(0); 32 | } 33 | _ => { 34 | println!( 35 | "Unexpected argument use `coldbrew help` to see usage guide." 36 | ); 37 | exit(64); 38 | } 39 | }; 40 | 41 | let mut paths: Vec = Vec::new(); 42 | let to_skip: Vec<&str> = vec![ 43 | "DoubleFibonacci.class", 44 | "MixedTypes.class", 45 | "MixedArg.class", 46 | "MEDouble.class", 47 | "FloatFibonacci.class", 48 | "LongFibonacci.class", 49 | ]; 50 | for path in std::path::Path::new(folder).read_dir().unwrap() { 51 | let path = match path { 52 | Ok(entry) => entry.path(), 53 | Err(err) => { 54 | println!("Error occured when reading file paths : {err}"); 55 | exit(1); 56 | } 57 | }; 58 | if let Some(extension) = path.extension() { 59 | if to_skip.contains(&path.file_name().unwrap().to_str().unwrap()) { 60 | continue; 61 | } 62 | if extension == "class" { 63 | paths.push(path); 64 | } 65 | } 66 | } 67 | for path in &paths { 68 | let class_file_bytes = read_class_file(path).unwrap_or_else(|_| { 69 | panic!("Failed to read class file : {:?}", path.as_os_str()) 70 | }); 71 | let class_file = 72 | JVMParser::parse(&class_file_bytes).unwrap_or_else(|_| { 73 | panic!("Failed to parse class file {:?}", path.as_os_str()) 74 | }); 75 | 76 | let program = Program::new(&class_file); 77 | let mut runtime = Runtime::new(program); 78 | match runtime.run(jit_mode) { 79 | Ok(()) => { 80 | println!( 81 | "[+] Program {:?} finished running successfully !", 82 | path.file_name().unwrap() 83 | ); 84 | } 85 | Err(err) => println!("Error : {err}"), 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/profiler.rs: -------------------------------------------------------------------------------- 1 | //! Code profiler for the interpreter works by keeping track of loop 2 | //! entries and exits. When a given loop entry has exceeded the threshold 3 | //! it's considered hot and a trace will be compiled for it. 4 | use std::collections::HashMap; 5 | 6 | use crate::runtime::ProgramCounter; 7 | 8 | #[derive(Debug)] 9 | pub struct Profiler { 10 | // Threshold before a loop entry is considered hot. 11 | threshold: usize, 12 | // Last accessed program counter. 13 | last_pc: ProgramCounter, 14 | // Record of loop entries and their access counts. 15 | records: HashMap, 16 | } 17 | 18 | impl Profiler { 19 | pub fn new() -> Profiler { 20 | Profiler { 21 | threshold: 1, 22 | last_pc: ProgramCounter::default(), 23 | records: HashMap::new(), 24 | } 25 | } 26 | 27 | // Count an entry to a loop header, since JVM bytecode is organized by 28 | // two indexes, the first `method_index` points to the method we are 29 | // currently executing and the second `instruction_index` actually points 30 | // to the bytecode offset within that method. 31 | // 32 | // For a `pc` entry to be considered a valid loop header it needs to 33 | // verify two conditions : 34 | // 35 | // - The loop header exists in the same method of the last accessed 36 | // method index. 37 | // - The instruction index within the method is before the last accessed 38 | // program counter's instruction index. 39 | pub fn count_entry(&mut self, pc: &ProgramCounter) { 40 | if pc.get_method_index() == self.last_pc.get_method_index() 41 | && pc.get_instruction_index() < self.last_pc.get_instruction_index() 42 | { 43 | match self.records.get_mut(pc) { 44 | Some(record) => *record += 1, 45 | None => { 46 | self.records.insert(*pc, 1); 47 | } 48 | } 49 | } 50 | self.last_pc = *pc; 51 | } 52 | 53 | // Count an exit from the JIT back to the interpreter, these "side-exits" 54 | // mark the non presence of a native trace which causes the exit back 55 | // to interpretation. Since we ideally want to spend as much time executing 56 | // native code we count these exists to trigger them for recording so we 57 | // can have a native trace next time we hit this `pc`. 58 | pub fn count_exit(&mut self, pc: &ProgramCounter) { 59 | match self.records.get_mut(pc) { 60 | Some(record) => *record += 1, 61 | None => { 62 | self.records.insert(*pc, 1); 63 | } 64 | } 65 | self.last_pc = *pc 66 | } 67 | 68 | // Returns whether a given `pc` is considered "hot" which just signals 69 | // to the recorder to start recording a trace. 70 | pub fn is_hot(&self, pc: &ProgramCounter) -> bool { 71 | if let Some(record) = self.records.get(pc) { 72 | return record > &self.threshold; 73 | } 74 | false 75 | } 76 | } 77 | 78 | impl Default for Profiler { 79 | fn default() -> Self { 80 | Self::new() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/program.rs: -------------------------------------------------------------------------------- 1 | //! Abstract representation of a Java program. 2 | use crate::jvm::{AttributeInfo, CPInfo, JVMClassFile, StackMapFrame}; 3 | 4 | use regex::Regex; 5 | 6 | /// Primitive types supported by the JVM. 7 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 8 | pub enum BaseTypeKind { 9 | Int, 10 | Long, 11 | Float, 12 | Double, 13 | Void, 14 | String, 15 | List, 16 | } 17 | 18 | /// JVM value type. 19 | #[derive(Debug, Clone)] 20 | pub struct Type { 21 | t: BaseTypeKind, 22 | sub_t: Option>, 23 | } 24 | 25 | impl Type { 26 | /// Empty constructor, we could use `Default` but hey. 27 | pub fn new() -> Self { 28 | Self { 29 | t: BaseTypeKind::Int, 30 | sub_t: None, 31 | } 32 | } 33 | /// Returns the size in WORD (4 bytes) of a given type. 34 | pub fn size(&self) -> usize { 35 | match self.t { 36 | BaseTypeKind::Int | BaseTypeKind::Float => 1, 37 | BaseTypeKind::Long | BaseTypeKind::Double => 2, 38 | _ => 0, 39 | } 40 | } 41 | } 42 | 43 | /// Representation of Java programs that we want to run. 44 | #[derive(Debug, Clone)] 45 | pub struct Program { 46 | // Constant pool. 47 | pub constant_pool: Vec, 48 | // Methods. 49 | // pub methods: HashMap, 50 | pub methods: Vec, 51 | } 52 | 53 | /// Java class method representation for the interpreter. 54 | #[derive(Debug, Clone)] 55 | pub struct Method { 56 | _name_index: u16, 57 | _return_type: Type, 58 | pub arg_types: Vec, 59 | _max_stack: u16, 60 | pub max_locals: u16, 61 | pub code: Vec, 62 | _constant: Option, 63 | _stack_map_table: Option>, 64 | } 65 | 66 | impl Default for Method { 67 | fn default() -> Self { 68 | Self { 69 | _name_index: 0, 70 | _return_type: Type::new(), 71 | arg_types: Vec::new(), 72 | _max_stack: 0, 73 | max_locals: 0, 74 | code: Vec::new(), 75 | _constant: None, 76 | _stack_map_table: None, 77 | } 78 | } 79 | } 80 | 81 | impl Program { 82 | /// Build a new program from a parsed class file. 83 | /// # Panics 84 | /// Can panic if class file is missing Code attribute. 85 | #[must_use] 86 | pub fn new(class_file: &JVMClassFile) -> Self { 87 | let constants = class_file.constant_pool(); 88 | // let mut methods: HashMap = HashMap::new(); 89 | let mut methods: Vec = vec![Method::default(); 256]; 90 | for method_info in &class_file.methods() { 91 | let mut arg_types: Vec = Vec::new(); 92 | let mut return_type: Type = Type { 93 | t: BaseTypeKind::Void, 94 | sub_t: None, 95 | }; 96 | let descriptor = 97 | &constants[method_info.descriptor_index() as usize]; 98 | let _method_name = &constants[method_info.name_index() as usize]; 99 | 100 | if let CPInfo::ConstantUtf8 { bytes } = descriptor { 101 | (arg_types, return_type) = Self::parse_method_types(bytes); 102 | } 103 | let attr = method_info.attributes(); 104 | 105 | let (max_stack, max_locals, code) = 106 | if let Some(AttributeInfo::CodeAttribute { 107 | max_stack, 108 | max_locals, 109 | code, 110 | .. 111 | }) = attr.get("Code") 112 | { 113 | (*max_stack, *max_locals, code.clone()) 114 | } else { 115 | panic!("Expected at least one code attribute") 116 | }; 117 | 118 | let constant = 119 | if let Some(AttributeInfo::ConstantValueAttribute { 120 | constant_value_index, 121 | .. 122 | }) = attr.get("ConstantValue") 123 | { 124 | Some(*constant_value_index) 125 | } else { 126 | None 127 | }; 128 | 129 | let stack_map_table = 130 | if let Some(AttributeInfo::StackMapTableAttribute { 131 | entries, 132 | .. 133 | }) = attr.get("StackMapTable") 134 | { 135 | Some(entries.clone()) 136 | } else { 137 | None 138 | }; 139 | 140 | let method = Method { 141 | _name_index: method_info.name_index(), 142 | _return_type: return_type, 143 | arg_types, 144 | _max_stack: max_stack, 145 | max_locals, 146 | code, 147 | _constant: constant, 148 | _stack_map_table: stack_map_table, 149 | }; 150 | // methods.insert(method_info.name_index() as usize, method); 151 | methods[method_info.name_index() as usize] = method; 152 | } 153 | 154 | Self { 155 | // Get a copy of the constant pool. 156 | constant_pool: class_file.constant_pool(), 157 | // Get a copy of the program methods. 158 | methods, 159 | } 160 | } 161 | 162 | // Find method name index in the constant pool by reference. 163 | pub fn find_method(&self, method_ref: usize) -> i32 { 164 | match self.constant_pool[method_ref] { 165 | CPInfo::ConstantMethodRef { 166 | name_and_type_index, 167 | .. 168 | } => { 169 | if let CPInfo::ConstantNameAndType { name_index, .. } = 170 | self.constant_pool[name_and_type_index as usize] 171 | { 172 | return name_index.into(); 173 | } 174 | 0 175 | } 176 | _ => panic!("Expected ConstantMethodRef"), 177 | } 178 | } 179 | 180 | // Returns program entry point, in this case the index of the method 181 | // main. 182 | pub fn entry_point(&self) -> usize { 183 | for (index, _) in self.methods.iter().enumerate() { 184 | match self.constant_pool.get(index) { 185 | Some(constant) => { 186 | if let CPInfo::ConstantUtf8 { bytes } = constant { 187 | if bytes == "main" { 188 | return index; 189 | } 190 | } 191 | } 192 | None => panic!("method \"main\" was not found"), 193 | } 194 | } 195 | // This might cause some issues but since the input to our runtime 196 | // is a class file that already passed the Java compiler we should 197 | // assume a main function already exists. 198 | 0 199 | } 200 | 201 | // Returns a slice containing code of method pointed at by `method_index`. 202 | pub fn code(&self, method_index: usize) -> &[u8] { 203 | &self.methods[method_index].code 204 | } 205 | 206 | // Return the declared max locals for a method. 207 | pub fn max_locals(&self, method_index: usize) -> u16 { 208 | self.methods[method_index].max_locals 209 | } 210 | 211 | // Parse constant method types, returns a tuple of argument types and 212 | // return types. 213 | fn parse_method_types(bytes: &str) -> (Vec, Type) { 214 | let re = Regex::new(r"\(([^\)]*)\)([^$]+)").unwrap(); 215 | let caps = re.captures(bytes).unwrap(); 216 | let arg_string = caps.get(1).map_or("", |m| return m.as_str()); 217 | let return_type_string = caps.get(2).map_or("", |m| return m.as_str()); 218 | let mut types: Vec = Vec::new(); 219 | let ret_type = Self::decode_type(return_type_string); 220 | 221 | let mut arg_string_slice = arg_string; 222 | while !arg_string_slice.is_empty() { 223 | let t = Self::decode_type(arg_string_slice); 224 | types.push(t.clone()); 225 | let length = Self::decode_type_string_length(&t); 226 | arg_string_slice = substr( 227 | arg_string_slice, 228 | length, 229 | arg_string_slice.len() - length, 230 | ); 231 | } 232 | (types, ret_type) 233 | } 234 | 235 | /// Returns the type's string representation length. 236 | /// # Panics 237 | /// Function panics if class file has invalid representation for a list 238 | /// type. 239 | #[must_use] 240 | pub fn decode_type_string_length(t: &Type) -> usize { 241 | match t.t { 242 | BaseTypeKind::String => 18, 243 | BaseTypeKind::List => { 244 | return 1 + Self::decode_type_string_length( 245 | t.sub_t.as_ref().unwrap(), 246 | ) 247 | } 248 | _ => 1, 249 | } 250 | } 251 | 252 | /// Returns the Java equivalent type from a type's string representation. 253 | #[must_use] 254 | pub fn decode_type(type_str: &str) -> Type { 255 | match &type_str[0..1] { 256 | "I" => Type { 257 | t: BaseTypeKind::Int, 258 | sub_t: None, 259 | }, 260 | "J" => Type { 261 | t: BaseTypeKind::Long, 262 | sub_t: None, 263 | }, 264 | "F" => Type { 265 | t: BaseTypeKind::Float, 266 | sub_t: None, 267 | }, 268 | "D" => Type { 269 | t: BaseTypeKind::Double, 270 | sub_t: None, 271 | }, 272 | "V" => Type { 273 | t: BaseTypeKind::Void, 274 | sub_t: None, 275 | }, 276 | "[" => { 277 | let st = Self::decode_type(&type_str[1..(type_str.len() - 1)]); 278 | let subtype = Type { 279 | t: st.t, 280 | sub_t: st.sub_t, 281 | }; 282 | Type { 283 | t: BaseTypeKind::List, 284 | sub_t: Some(Box::new(subtype)), 285 | } 286 | } 287 | // We can support byte, char... later 288 | _ => Type { 289 | t: BaseTypeKind::String, 290 | sub_t: None, 291 | }, 292 | } 293 | } 294 | } 295 | 296 | fn substr(s: &str, start: usize, length: usize) -> &str { 297 | let end = start + length; 298 | &s[start..end] 299 | } 300 | 301 | #[cfg(test)] 302 | mod tests { 303 | use super::*; 304 | 305 | use crate::jvm::{read_class_file, JVMParser}; 306 | use std::env; 307 | use std::path::Path; 308 | 309 | #[test] 310 | fn can_build_program() { 311 | let env_var = env::var("CARGO_MANIFEST_DIR").unwrap(); 312 | let path = Path::new(&env_var).join("support/tests/Factorial.class"); 313 | let class_file_bytes = read_class_file(&path).unwrap_or_else(|_| { 314 | panic!("Failed to parse file : {:?}", path.as_os_str()) 315 | }); 316 | let result = JVMParser::parse(&class_file_bytes); 317 | assert!(result.is_ok()); 318 | let class_file = result.unwrap(); 319 | let program = Program::new(&class_file); 320 | 321 | let methods = vec![ 322 | Method { 323 | _name_index: 27, 324 | _return_type: Type { 325 | t: BaseTypeKind::Void, 326 | sub_t: None, 327 | }, 328 | arg_types: vec![Type { 329 | t: BaseTypeKind::List, 330 | sub_t: Some(Box::new(Type { 331 | t: BaseTypeKind::String, 332 | sub_t: None, 333 | })), 334 | }], 335 | _max_stack: 2, 336 | max_locals: 2, 337 | code: vec![ 338 | 16, 12, 184, 0, 7, 60, 178, 0, 13, 27, 182, 0, 19, 177, 339 | ], 340 | _constant: None, 341 | _stack_map_table: None, 342 | }, 343 | Method { 344 | _name_index: 5, 345 | _return_type: Type { 346 | t: BaseTypeKind::Void, 347 | sub_t: None, 348 | }, 349 | arg_types: vec![], 350 | _max_stack: 1, 351 | max_locals: 1, 352 | code: vec![42, 183, 0, 1, 177], 353 | _constant: None, 354 | _stack_map_table: None, 355 | }, 356 | Method { 357 | _name_index: 11, 358 | _return_type: Type { 359 | t: BaseTypeKind::Int, 360 | sub_t: None, 361 | }, 362 | arg_types: vec![Type { 363 | t: BaseTypeKind::Int, 364 | sub_t: None, 365 | }], 366 | _max_stack: 2, 367 | max_locals: 3, 368 | code: vec![ 369 | 4, 60, 5, 61, 28, 26, 163, 0, 13, 27, 28, 104, 60, 132, 2, 370 | 1, 167, 255, 244, 27, 172, 371 | ], 372 | _constant: None, 373 | _stack_map_table: None, 374 | }, 375 | ]; 376 | 377 | for method in methods { 378 | let name_index = method._name_index; 379 | let program_method = &program.methods[name_index as usize]; 380 | assert_eq!(method.code, program_method.code); 381 | } 382 | assert_eq!(program.entry_point(), 27); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /src/trace.rs: -------------------------------------------------------------------------------- 1 | //! Runtime tracing module for coldbrew. 2 | use core::fmt; 3 | use std::collections::HashSet; 4 | 5 | use crate::bytecode::OPCode; 6 | use crate::runtime::{Instruction, ProgramCounter, Value}; 7 | 8 | /// Trace recording involves capturing an execution trace of the program in 9 | /// various places. Each record entry in the trace is a tuple of (pc, inst) 10 | /// where pc is the program counter (position of the entry in the bytecode) 11 | /// and inst is the instruction executed there. 12 | #[derive(Debug, Clone)] 13 | pub struct Record { 14 | pc: ProgramCounter, 15 | inst: Instruction, 16 | } 17 | 18 | impl Record { 19 | pub fn instruction(&self) -> Instruction { 20 | self.inst.clone() 21 | } 22 | 23 | pub fn pc(&self) -> ProgramCounter { 24 | self.pc 25 | } 26 | } 27 | 28 | impl fmt::Display for Record { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | write!(f, "{:} @ {:}", self.inst, self.pc) 31 | } 32 | } 33 | 34 | #[derive(Debug, Clone)] 35 | pub struct Trace { 36 | pub start: ProgramCounter, 37 | pub trace: Vec, 38 | } 39 | 40 | /// Recorder is the runtime component responsible for recording traces. 41 | pub struct Recorder { 42 | trace_start: ProgramCounter, 43 | loop_header: ProgramCounter, 44 | is_recording: bool, 45 | last_instruction_was_branch: bool, 46 | pub trace: Vec, 47 | inner_branch_targets: HashSet, 48 | outer_branch_targets: HashSet, 49 | } 50 | 51 | impl Default for Recorder { 52 | fn default() -> Self { 53 | Self::new() 54 | } 55 | } 56 | 57 | impl Recorder { 58 | pub fn new() -> Self { 59 | Self { 60 | trace_start: ProgramCounter::default(), 61 | loop_header: ProgramCounter::default(), 62 | is_recording: false, 63 | last_instruction_was_branch: false, 64 | trace: Vec::new(), 65 | inner_branch_targets: HashSet::new(), 66 | outer_branch_targets: HashSet::new(), 67 | } 68 | } 69 | 70 | /// Check if we are recording a trace already. 71 | pub fn is_recording(&self) -> bool { 72 | self.is_recording 73 | } 74 | 75 | /// Check if we finished recording a trace. 76 | pub fn is_done_recording(&mut self, pc: ProgramCounter) -> bool { 77 | if self.trace.is_empty() { 78 | return false; 79 | } 80 | match self.trace.last() { 81 | Some(entry) => match entry.inst.get_mnemonic() { 82 | OPCode::Return 83 | | OPCode::IReturn 84 | | OPCode::LReturn 85 | | OPCode::FReturn 86 | | OPCode::DReturn => { 87 | // If we found a recursive call we need to exit. 88 | if pc.get_method_index() == entry.pc.get_method_index() { 89 | self.is_recording = false; 90 | return false; 91 | } 92 | pc == self.loop_header 93 | } 94 | _ => pc == self.loop_header, 95 | }, 96 | None => false, 97 | } 98 | } 99 | 100 | /// Record the bytecode instruction at the given `pc` and `inst` 101 | /// the final recorded traces are linear, straight line code with 102 | /// no loops or function calls (ideally some calls could be inlined). 103 | /// 104 | /// During the recording phase if any aborting condition is met we stop 105 | /// recording and return. The aborting conditions are (1) jumps to outer 106 | /// branches, (2) function calls or (3) conditional branches. 107 | pub fn record(&mut self, pc: ProgramCounter, mut inst: Instruction) { 108 | match inst.get_mnemonic() { 109 | OPCode::Goto => { 110 | let offset = match inst.nth(0) { 111 | Some(Value::Int(v)) => v, 112 | _ => panic!( 113 | "Expected Goto to have at least one integer parameter" 114 | ), 115 | }; 116 | // Forward branch, aborting. 117 | if offset > 0 { 118 | return; 119 | } else { 120 | let mut branch_target = pc; 121 | branch_target.inc_instruction_index(offset); 122 | if self.trace_start == branch_target { 123 | self.inner_branch_targets.insert(branch_target); 124 | } else { 125 | self.outer_branch_targets.insert(branch_target); 126 | } 127 | } 128 | } 129 | OPCode::IfNe 130 | | OPCode::IfEq 131 | | OPCode::IfGt 132 | | OPCode::IfICmpGe 133 | | OPCode::IfICmpGt 134 | | OPCode::IfICmpLt 135 | | OPCode::IfICmpLe 136 | | OPCode::IfICmpNe 137 | | OPCode::IfICmpEq => { 138 | self.last_instruction_was_branch = true; 139 | } 140 | OPCode::InvokeStatic => { 141 | // Check for recursive function calls by comparing the invoked 142 | // method index with the one we are currently recording. 143 | let method_index = match inst.nth(0) { 144 | Some(Value::Int(v)) => v, 145 | _ => panic!( 146 | "Expected InvokeStatic to have at least one parameter" 147 | ), 148 | }; 149 | if self.trace_start.get_method_index() == method_index as usize 150 | { 151 | // Found a recursive call, aborting. 152 | self.is_recording = false; 153 | return; 154 | } 155 | } 156 | OPCode::Iconst0 157 | | OPCode::Iconst1 158 | | OPCode::Iconst2 159 | | OPCode::Iconst3 160 | | OPCode::Iconst4 161 | | OPCode::Iconst5 162 | | OPCode::IconstM1 163 | | OPCode::Lconst0 164 | | OPCode::Lconst1 165 | | OPCode::Fconst0 166 | | OPCode::Fconst1 167 | | OPCode::Fconst2 168 | | OPCode::Dconst0 169 | | OPCode::Dconst1 170 | | OPCode::ILoad0 171 | | OPCode::ILoad1 172 | | OPCode::ILoad2 173 | | OPCode::ILoad3 174 | | OPCode::DLoad0 175 | | OPCode::DLoad1 176 | | OPCode::DLoad2 177 | | OPCode::DLoad3 178 | | OPCode::FLoad0 179 | | OPCode::FLoad1 180 | | OPCode::FLoad2 181 | | OPCode::FLoad3 182 | | OPCode::LLoad0 183 | | OPCode::LLoad1 184 | | OPCode::LLoad2 185 | | OPCode::LLoad3 186 | | OPCode::IStore0 187 | | OPCode::IStore1 188 | | OPCode::IStore2 189 | | OPCode::IStore3 190 | | OPCode::FStore0 191 | | OPCode::FStore1 192 | | OPCode::FStore2 193 | | OPCode::FStore3 194 | | OPCode::DStore0 195 | | OPCode::DStore1 196 | | OPCode::DStore2 197 | | OPCode::DStore3 => { 198 | if let Some(value) = Self::get_params(inst.get_mnemonic()) { 199 | inst = Instruction::new( 200 | Self::get_mnemonic(inst.get_mnemonic()), 201 | Some(vec![value]), 202 | ); 203 | } 204 | } 205 | _ => (), 206 | } 207 | self.trace.push(Record { 208 | pc, 209 | inst: inst.clone(), 210 | }); 211 | } 212 | 213 | /// Returns an equivalent mnemonic from the given one. 214 | fn get_mnemonic(opcode: OPCode) -> OPCode { 215 | match opcode { 216 | OPCode::ILoad0 217 | | OPCode::ILoad1 218 | | OPCode::ILoad2 219 | | OPCode::ILoad3 => OPCode::ILoad, 220 | OPCode::FLoad0 221 | | OPCode::FLoad1 222 | | OPCode::FLoad2 223 | | OPCode::FLoad3 => OPCode::FLoad, 224 | OPCode::LLoad0 225 | | OPCode::LLoad1 226 | | OPCode::LLoad2 227 | | OPCode::LLoad3 => OPCode::LLoad, 228 | OPCode::DLoad0 229 | | OPCode::DLoad1 230 | | OPCode::DLoad2 231 | | OPCode::DLoad3 => OPCode::DLoad, 232 | OPCode::IStore0 233 | | OPCode::IStore1 234 | | OPCode::IStore2 235 | | OPCode::IStore3 => OPCode::IStore, 236 | OPCode::FStore0 237 | | OPCode::FStore1 238 | | OPCode::FStore2 239 | | OPCode::FStore3 => OPCode::FStore, 240 | OPCode::LStore0 241 | | OPCode::LStore1 242 | | OPCode::LStore2 243 | | OPCode::LStore3 => OPCode::LStore, 244 | OPCode::DStore0 245 | | OPCode::DStore1 246 | | OPCode::DStore2 247 | | OPCode::DStore3 => OPCode::DStore, 248 | OPCode::Iconst0 249 | | OPCode::Iconst1 250 | | OPCode::Iconst2 251 | | OPCode::Iconst3 252 | | OPCode::Iconst4 253 | | OPCode::Iconst5 254 | | OPCode::IconstM1 255 | | OPCode::Fconst0 256 | | OPCode::Fconst1 257 | | OPCode::Fconst2 => OPCode::Ldc, 258 | OPCode::Lconst0 259 | | OPCode::Lconst1 260 | | OPCode::Dconst0 261 | | OPCode::Dconst1 => OPCode::Ldc2W, 262 | _ => unreachable!( 263 | "expected only constant load or store got {opcode}" 264 | ), 265 | } 266 | } 267 | 268 | /// Returns the `jvm::Value` from a given mnemonic. 269 | const fn get_params(opcode: OPCode) -> Option { 270 | match opcode { 271 | OPCode::ILoad0 272 | | OPCode::FLoad0 273 | | OPCode::LLoad0 274 | | OPCode::DLoad0 275 | | OPCode::IStore0 276 | | OPCode::FStore0 277 | | OPCode::LStore0 278 | | OPCode::DStore0 279 | | OPCode::Iconst0 => Some(Value::Int(0)), 280 | OPCode::ILoad1 281 | | OPCode::FLoad1 282 | | OPCode::LLoad1 283 | | OPCode::DLoad1 284 | | OPCode::IStore1 285 | | OPCode::FStore1 286 | | OPCode::LStore1 287 | | OPCode::DStore1 288 | | OPCode::Iconst1 => Some(Value::Int(1)), 289 | OPCode::ILoad2 290 | | OPCode::FLoad2 291 | | OPCode::LLoad2 292 | | OPCode::DLoad2 293 | | OPCode::IStore2 294 | | OPCode::FStore2 295 | | OPCode::LStore2 296 | | OPCode::DStore2 297 | | OPCode::Iconst2 => Some(Value::Int(2)), 298 | OPCode::ILoad3 299 | | OPCode::FLoad3 300 | | OPCode::LLoad3 301 | | OPCode::DLoad3 302 | | OPCode::IStore3 303 | | OPCode::FStore3 304 | | OPCode::LStore3 305 | | OPCode::DStore3 306 | | OPCode::Iconst3 => Some(Value::Int(3)), 307 | OPCode::Iconst4 => Some(Value::Int(4)), 308 | OPCode::Iconst5 => Some(Value::Int(5)), 309 | OPCode::IconstM1 => Some(Value::Int(-1)), 310 | OPCode::Fconst0 => Some(Value::Float(0.)), 311 | OPCode::Fconst1 => Some(Value::Float(1.)), 312 | OPCode::Fconst2 => Some(Value::Float(2.)), 313 | OPCode::Lconst0 => Some(Value::Long(0)), 314 | OPCode::Lconst1 => Some(Value::Long(1)), 315 | OPCode::Dconst0 => Some(Value::Double(0.)), 316 | OPCode::Dconst1 => Some(Value::Double(1.)), 317 | _ => None, 318 | } 319 | } 320 | 321 | /// Init a trace recording. 322 | pub fn init(&mut self, loop_header: ProgramCounter, start: ProgramCounter) { 323 | if self.is_recording && self.trace_start == start { 324 | return; 325 | } 326 | self.is_recording = true; 327 | self.last_instruction_was_branch = false; 328 | self.trace_start = start; 329 | self.loop_header = loop_header; 330 | // Clear existing traces. 331 | self.trace.clear(); 332 | self.inner_branch_targets.clear(); 333 | self.outer_branch_targets.clear(); 334 | } 335 | 336 | /// Return the last recorded trace. 337 | pub fn recording(&mut self) -> Trace { 338 | self.is_recording = false; 339 | Trace { 340 | start: self.trace_start, 341 | trace: self.trace.clone(), 342 | } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /src/x86.rs: -------------------------------------------------------------------------------- 1 | //! Functions used for the x86_64 target. 2 | 3 | /// Reads the current value of the CPU timestamp counter. 4 | #[cfg(target = "x86_64")] 5 | pub fn rdtsc() -> u64 { 6 | unsafe { std::arch::x86_64::_rdtsc() } 7 | } 8 | -------------------------------------------------------------------------------- /support/integration/AntiTautologi.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/AntiTautologi.class -------------------------------------------------------------------------------- /support/integration/AntiTautologi.java: -------------------------------------------------------------------------------- 1 | public class AntiTautologi { 2 | public static void main(String[] args) { 3 | int i = 0; 4 | if (3 != 0) { 5 | i = 3; 6 | } else { 7 | i = 4; 8 | } 9 | System.out.println(i); 10 | } 11 | } -------------------------------------------------------------------------------- /support/integration/ChineseRemainder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/ChineseRemainder.class -------------------------------------------------------------------------------- /support/integration/ChineseRemainder.java: -------------------------------------------------------------------------------- 1 | public class ChineseRemainder { 2 | public static long mul_inv(int a, int b) { 3 | int b0 = b, t, q; 4 | int x0 = 0, x1 = 1; 5 | if (b == 1) 6 | return 1; 7 | while (a > 1) { 8 | q = a / b; 9 | t = b; 10 | b = a % b; 11 | a = t; 12 | t = x0; 13 | x0 = x1 - q * x0; 14 | x1 = t; 15 | } 16 | if (x1 < 0) 17 | x1 += b0; 18 | return x1; 19 | } 20 | public static int chinese_remainder(int n1, int n2, int n3, int a1, int a2, int a3) { 21 | int p, i, prod = 1, sum = 0; 22 | prod = n1 * n2 * n3; 23 | p = prod / n1; 24 | sum += a1 * mul_inv(p, n1) * p; 25 | p = prod / n2; 26 | sum += a2 * mul_inv(p, n2) * p; 27 | p = prod / n3; 28 | sum += a3 * mul_inv(p, n3) * p; 29 | return ((sum % prod) + prod) % prod; 30 | } 31 | 32 | public static void main(String[] args) { 33 | System.out.println(chinese_remainder(997, 991, 983, 123, 14, 66)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /support/integration/DoubleFibonacci.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/DoubleFibonacci.class -------------------------------------------------------------------------------- /support/integration/DoubleFibonacci.java: -------------------------------------------------------------------------------- 1 | public class DoubleFibonacci { 2 | public static void main(String[] args) { 3 | System.out.println(fibonacci(20.0)); 4 | } 5 | public static double fibonacci(double n) { 6 | if (n <= 2.0) 7 | return 1.0; 8 | else 9 | return fibonacci(n - 1) + fibonacci(n - 2); 10 | } 11 | } -------------------------------------------------------------------------------- /support/integration/Empty.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/Empty.class -------------------------------------------------------------------------------- /support/integration/Empty.java: -------------------------------------------------------------------------------- 1 | public class Empty { 2 | public static void main(String[] args) {} 3 | } -------------------------------------------------------------------------------- /support/integration/EvenMoreLoops.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/EvenMoreLoops.class -------------------------------------------------------------------------------- /support/integration/EvenMoreLoops.java: -------------------------------------------------------------------------------- 1 | public class EvenMoreLoops { 2 | public static void main(String[] args) { 3 | int a = 1; 4 | int i = 0, j = 0, k = 0, l = 0, m = 0, n = 0, o = 0, p = 0, q = 0, r = 0, s = 0, t = 0, u = 0, 5 | v = 0, w = 0; 6 | for (; i < 4; i++) { 7 | a += i; 8 | } 9 | for (; j < 4; j++) { 10 | a += i + j; 11 | } 12 | for (; k < 4; k++) { 13 | a += i + j + k; 14 | } 15 | for (; l < 4; l++) { 16 | a += i + j + k + l; 17 | } 18 | for (; m < 4; m++) { 19 | a += i + j + k + l + m; 20 | } 21 | for (; n < 4; n++) { 22 | a += i + j + k + l + m + n; 23 | } 24 | for (; o < 4; o++) { 25 | a += i + j + k + l + m + n + o; 26 | } 27 | for (; p < 4; p++) { 28 | a += i + j + k + l + m + n + o + p; 29 | } 30 | for (; q < 4; q++) { 31 | a += i + j + k + l + m + n + o + p + q; 32 | } 33 | for (; r < 4; r++) { 34 | a += i + j + k + l + m + n + o + p + q + r; 35 | } 36 | for (; s < 4; s++) { 37 | a += i + j + k + l + m + n + o + p + q + r + s; 38 | } 39 | for (; t < 4; t++) { 40 | a += i + j + k + l + m + n + o + p + q + r + s + t; 41 | } 42 | for (; u < 4; u++) { 43 | a += i + j + k + l + m + n + o + p + q + r + s + t + u; 44 | } 45 | for (; v < 4; v++) { 46 | a += i + j + k + l + m + n + o + p + q + r + s + t + u + v; 47 | } 48 | for (; w < 4; w++) { 49 | a += i + j + k + l + m + n + o + p + q + r + s + t + u + v + w; 50 | } 51 | System.out.println(a); 52 | } 53 | } -------------------------------------------------------------------------------- /support/integration/Factorial.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/Factorial.class -------------------------------------------------------------------------------- /support/integration/Factorial.java: -------------------------------------------------------------------------------- 1 | public class Factorial { 2 | public static void main(String[] args) { 3 | int f = factorial(12); 4 | System.out.println(f); 5 | } 6 | 7 | public static int factorial(int n) { 8 | int accumulator = 1; 9 | for (int i = 2; i <= n; i++) accumulator *= i; 10 | return accumulator; 11 | } 12 | } -------------------------------------------------------------------------------- /support/integration/Fibonacci.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/Fibonacci.class -------------------------------------------------------------------------------- /support/integration/Fibonacci.java: -------------------------------------------------------------------------------- 1 | public class Fibonacci { 2 | public static void main(String[] args) { 3 | int n = fibonacci(15); 4 | System.out.println(n); 5 | } 6 | public static int fibonacci(int n) { 7 | if (n <= 1) 8 | return 1; 9 | else 10 | return fibonacci(n - 1) + fibonacci(n - 2); 11 | } 12 | } -------------------------------------------------------------------------------- /support/integration/FloatFibonacci.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/FloatFibonacci.class -------------------------------------------------------------------------------- /support/integration/FloatFibonacci.java: -------------------------------------------------------------------------------- 1 | public class FloatFibonacci { 2 | public static void main(String[] args) { 3 | System.out.println(fibonacci(10f)); 4 | } 5 | public static float fibonacci(float n) { 6 | if (n <= 2f) 7 | return 1f; 8 | else 9 | return fibonacci(n - 1) + fibonacci(n - 2); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/integration/FunctionInsideTrace.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/FunctionInsideTrace.class -------------------------------------------------------------------------------- /support/integration/FunctionInsideTrace.java: -------------------------------------------------------------------------------- 1 | public class FunctionInsideTrace { 2 | public static void main(String[] args) { 3 | int j = 0; 4 | for (int i = 0; i < 10000; i++) { 5 | j = add(j, i); 6 | } 7 | System.out.println(j); 8 | } 9 | 10 | public static int add(int a, int b) { 11 | return a + b; 12 | } 13 | } -------------------------------------------------------------------------------- /support/integration/IsPrime.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/IsPrime.class -------------------------------------------------------------------------------- /support/integration/IsPrime.java: -------------------------------------------------------------------------------- 1 | public class IsPrime { 2 | public static void main(String[] args) { 3 | int potentialPrime = 104729; 4 | int largestCheck = integerRoot(potentialPrime); 5 | for (int i = 2; i < largestCheck; i++) { 6 | if (potentialPrime % i == 0) { 7 | System.out.println(0); 8 | return; 9 | } 10 | } 11 | System.out.println(1); 12 | } 13 | 14 | public static int integerRoot(int num) { 15 | for (int i = 1; i < num; i++) { 16 | if (num % i == i) { 17 | return i; 18 | } 19 | } 20 | return num; 21 | } 22 | } -------------------------------------------------------------------------------- /support/integration/LongFibonacci.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/LongFibonacci.class -------------------------------------------------------------------------------- /support/integration/LongFibonacci.java: -------------------------------------------------------------------------------- 1 | public class LongFibonacci { 2 | public static void main(String[] args) { 3 | System.out.println(fibonacci(20)); 4 | } 5 | public static long fibonacci(long n) { 6 | if (n <= 2) 7 | return 1; 8 | else 9 | return fibonacci(n - 1) + fibonacci(n - 2); 10 | } 11 | } -------------------------------------------------------------------------------- /support/integration/LoopWithFuncCall.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/LoopWithFuncCall.class -------------------------------------------------------------------------------- /support/integration/LoopWithFuncCall.java: -------------------------------------------------------------------------------- 1 | 2 | public class LoopWithFuncCall { 3 | public static void main(String[] args) { 4 | int i = 0, j = 0; 5 | for (int k = 0; k < 1000; k++) { 6 | i = threeArgs(i, j, k); 7 | j = threeArgs(j, k, i); 8 | } 9 | System.out.println(i); 10 | System.out.println(j); 11 | } 12 | 13 | public static int threeArgs(int a, int b, int c) { 14 | return a + b - c; 15 | } 16 | } -------------------------------------------------------------------------------- /support/integration/MEDouble.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/MEDouble.class -------------------------------------------------------------------------------- /support/integration/MEDouble.java: -------------------------------------------------------------------------------- 1 | public class MEDouble { 2 | public static void main(String[] args) { 3 | double d = 1.3; 4 | d += 2.4; 5 | d *= 3.7; 6 | d -= 2.5; 7 | System.out.println(d); 8 | } 9 | } -------------------------------------------------------------------------------- /support/integration/ManyVariables.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/ManyVariables.class -------------------------------------------------------------------------------- /support/integration/ManyVariables.java: -------------------------------------------------------------------------------- 1 | public class ManyVariables { 2 | public static void main(String[] args) { 3 | int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t; 4 | a = b = c = d = e = f = g = h = i = j = k = l = m = n = o = p = q = r = s = t = 0; 5 | for (int index = 0; index < 5000; index++) { 6 | a += index; 7 | b += index; 8 | c += index; 9 | d += index; 10 | e += index; 11 | f += index; 12 | g += index; 13 | h += index; 14 | i += index; 15 | j += index; 16 | k += index; 17 | l += index; 18 | m += index; 19 | n += index; 20 | o += index; 21 | p += index; 22 | q += index; 23 | r += index; 24 | s += index; 25 | t += index; 26 | } 27 | System.out.println(a + b + c + d + e + f + g); 28 | System.out.println(h + i + j + k + l + m + n); 29 | System.out.println(o + p + q + r + s + t); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /support/integration/ManyVariablesMulTrace.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/ManyVariablesMulTrace.class -------------------------------------------------------------------------------- /support/integration/ManyVariablesMulTrace.java: -------------------------------------------------------------------------------- 1 | public class ManyVariablesMulTrace { 2 | public static void main(String[] args) { 3 | int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t; 4 | a = b = c = d = e = f = g = h = i = j = k = l = m = n = o = p = q = r = s = t = 0; 5 | int offset; 6 | for (int index = 0; index < 5000; index++) { 7 | if (index % 2 == 0) { 8 | offset = 1; 9 | } else if (index % 3 == 0) { 10 | offset = 2; 11 | } else if (index % 5 == 0) { 12 | offset = 3; 13 | } else { 14 | offset = 4; 15 | } 16 | a += offset; 17 | b += offset; 18 | c += offset; 19 | d += offset; 20 | e += offset; 21 | f += offset; 22 | g += offset; 23 | h += offset; 24 | i += offset; 25 | j += offset; 26 | k += offset; 27 | l += offset; 28 | m += offset; 29 | n += offset; 30 | o += offset; 31 | p += offset; 32 | q += offset; 33 | r += offset; 34 | s += offset; 35 | t += offset; 36 | } 37 | System.out.println( 38 | a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t); 39 | } 40 | } -------------------------------------------------------------------------------- /support/integration/MixedArg.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/MixedArg.class -------------------------------------------------------------------------------- /support/integration/MixedArg.java: -------------------------------------------------------------------------------- 1 | public class MixedArg { 2 | public static void main(String[] args) { 3 | int i = 1; 4 | long l = 2; 5 | float f = 3; 6 | double d = 4; 7 | printSum(i, l, f, d); 8 | } 9 | 10 | public static void printSum(int i, long l, float f, double d) { 11 | double res = i + l + f + d; 12 | System.out.println(res); 13 | } 14 | } -------------------------------------------------------------------------------- /support/integration/MixedExecution.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/MixedExecution.class -------------------------------------------------------------------------------- /support/integration/MixedExecution.java: -------------------------------------------------------------------------------- 1 | public class MixedExecution { 2 | public static void main(String[] args) { 3 | int i = 0; 4 | // Trace starts here 5 | i += 6; 6 | i += 7; 7 | i *= 3; 8 | i -= 9; 9 | i++; 10 | // Trace stops here 11 | System.out.println(i); 12 | } 13 | } -------------------------------------------------------------------------------- /support/integration/MixedExecutionTwoVars.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/MixedExecutionTwoVars.class -------------------------------------------------------------------------------- /support/integration/MixedExecutionTwoVars.java: -------------------------------------------------------------------------------- 1 | public class MixedExecutionTwoVars { 2 | public static void main(String[] args) { 3 | int i = 0; 4 | int j = 7; 5 | i += j * 3; 6 | j *= 6; 7 | j -= 43; 8 | i *= 2; 9 | System.out.println(i + j); 10 | } 11 | } -------------------------------------------------------------------------------- /support/integration/MixedTypes.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/MixedTypes.class -------------------------------------------------------------------------------- /support/integration/MixedTypes.java: -------------------------------------------------------------------------------- 1 | public class MixedTypes { 2 | public static void main(String[] args) { 3 | double d = mixedMul(2, 2L, 2.0f, 2.0); 4 | System.out.println(d); 5 | } 6 | 7 | public static double mixedMul(int i, long l, float f, double d) { 8 | return i * l * f * d; 9 | } 10 | } -------------------------------------------------------------------------------- /support/integration/MoreLoops.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/MoreLoops.class -------------------------------------------------------------------------------- /support/integration/MoreLoops.java: -------------------------------------------------------------------------------- 1 | public class MoreLoops { 2 | public static void main(String[] args) { 3 | int i = 0, j = 0, k = 0; 4 | for (; i < 10; i++) { 5 | System.out.println(i); 6 | } 7 | for (; j < 10; j++) { 8 | System.out.println(j); 9 | } 10 | for (; k < 10; k++) { 11 | System.out.println(k); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /support/integration/RecursiveArguments.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/RecursiveArguments.class -------------------------------------------------------------------------------- /support/integration/RecursiveArguments.java: -------------------------------------------------------------------------------- 1 | public class RecursiveArguments { 2 | public static void main(String[] args) { 3 | int i = 10; 4 | int j = 5; 5 | int k = sub(i, sub(i, j)); 6 | System.out.println(k); 7 | } 8 | 9 | public static int sub(int i, int j) { 10 | return i - j; 11 | } 12 | } -------------------------------------------------------------------------------- /support/integration/ReportExample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/ReportExample.class -------------------------------------------------------------------------------- /support/integration/ReportExample.java: -------------------------------------------------------------------------------- 1 | public class ReportExample { 2 | public static void main(String[] args) { 3 | foo(); 4 | System.out.println(-1); 5 | } 6 | public static void foo() { 7 | int i = 0; 8 | for (int j = 0; j < 100000; j++) { 9 | if (j > 66666) { 10 | i += 1; 11 | } else if (j > 33333) { 12 | i += 2; 13 | } else { 14 | i += 3; 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /support/integration/SingleFuncCall.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/SingleFuncCall.class -------------------------------------------------------------------------------- /support/integration/SingleFuncCall.java: -------------------------------------------------------------------------------- 1 | public class SingleFuncCall { 2 | public static void main(String[] args) { 3 | int res = add(3, 2); 4 | System.out.println(res); 5 | } 6 | 7 | static int add(int a, int b) { 8 | return a + b; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/integration/SingleLoop.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/SingleLoop.class -------------------------------------------------------------------------------- /support/integration/SingleLoop.java: -------------------------------------------------------------------------------- 1 | public class SingleLoop { 2 | public static void main(String[] args) { 3 | int j = 1; 4 | for (int i = 0; i < 500000; i++) { 5 | int k = i + j; 6 | j = k; 7 | } 8 | System.out.println(j); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/integration/SingleLoopWithHotSideExit.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/SingleLoopWithHotSideExit.class -------------------------------------------------------------------------------- /support/integration/SingleLoopWithHotSideExit.java: -------------------------------------------------------------------------------- 1 | public class SingleLoopWithHotSideExit { 2 | public static void main(String[] args) { 3 | int i = 0; 4 | for (int j = 0; j < 10000000; j++) { 5 | if (j % 100 == 0) { 6 | i += 2; 7 | } else { 8 | i += 1; 9 | } 10 | } 11 | System.out.println(i); 12 | } 13 | } -------------------------------------------------------------------------------- /support/integration/SingleLoopWithIfStatement.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/SingleLoopWithIfStatement.class -------------------------------------------------------------------------------- /support/integration/SingleLoopWithIfStatement.java: -------------------------------------------------------------------------------- 1 | public class SingleLoopWithIfStatement { 2 | public static void main(String[] args) { 3 | int j = 0; 4 | for (int i = 0; i < 100000; i++) { 5 | if (j == 50000) { 6 | j -= 50000; 7 | } else { 8 | j++; 9 | } 10 | } 11 | System.out.println(j); 12 | } 13 | } -------------------------------------------------------------------------------- /support/integration/TernaryAssign.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/TernaryAssign.class -------------------------------------------------------------------------------- /support/integration/TernaryAssign.java: -------------------------------------------------------------------------------- 1 | public class TernaryAssign { 2 | public static void main(String[] args) { 3 | int i = 0; 4 | for (int j = 0; j < 10; j++) { 5 | i += j < 5 ? 1 : -1; 6 | } 7 | System.out.println(i); 8 | } 9 | } -------------------------------------------------------------------------------- /support/integration/Test.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/Test.class -------------------------------------------------------------------------------- /support/integration/Test.java: -------------------------------------------------------------------------------- 1 | public class Test { 2 | public static void main(String[] args) { 3 | System.out.println(3); 4 | } 5 | } -------------------------------------------------------------------------------- /support/integration/TwoHotSideExits.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/TwoHotSideExits.class -------------------------------------------------------------------------------- /support/integration/TwoHotSideExits.java: -------------------------------------------------------------------------------- 1 | public class TwoHotSideExits { 2 | public static void main(String[] args) { 3 | int i = 0; 4 | for (int j = 0; j < 300000; j++) { 5 | if (j < 100000) { 6 | i += 1; 7 | } else if (j < 200000) { 8 | i += 2; 9 | } else { 10 | i += 3; 11 | } 12 | } 13 | System.out.println(i); 14 | } 15 | } -------------------------------------------------------------------------------- /support/integration/WhileLoopAtStart.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/integration/WhileLoopAtStart.class -------------------------------------------------------------------------------- /support/integration/WhileLoopAtStart.java: -------------------------------------------------------------------------------- 1 | public class WhileLoopAtStart { 2 | public static void main(String[] args) { 3 | int j = 10; 4 | loopdiloop(j); 5 | System.out.println(j); 6 | } 7 | public static void loopdiloop(int j) { 8 | while (j < 15) { 9 | j++; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /support/jit/Loop10.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/jit/Loop10.class -------------------------------------------------------------------------------- /support/jit/Loop10.java: -------------------------------------------------------------------------------- 1 | public class Loop10 { 2 | public static void main(String[] args) { 3 | int sum = 0; 4 | int i =1; 5 | for (i=1;i <= 10;i++) { 6 | sum = sum + i; 7 | } 8 | System.out.println(sum); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/jit/Loop100.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/jit/Loop100.class -------------------------------------------------------------------------------- /support/jit/Loop100.java: -------------------------------------------------------------------------------- 1 | public class Loop100 { 2 | public static void main(String[] args) { 3 | int sum = 0; 4 | int i =1; 5 | for (i=1;i <= 100;i++) { 6 | sum = sum + i; 7 | } 8 | System.out.println(sum); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/tests/AllAtOnce.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/AllAtOnce.class -------------------------------------------------------------------------------- /support/tests/AllAtOnce.java: -------------------------------------------------------------------------------- 1 | // Example that uses many constructs to test the interpreter correctness 2 | public class AllAtOnce { 3 | public static int main(String[] args) { 4 | int result = 0; 5 | 6 | int i = 0; 7 | 8 | for (i = 0;i < 10;i++) { 9 | result += add2IfGreaterThan4(i); 10 | } 11 | 12 | return result; 13 | } 14 | 15 | public static int add2IfGreaterThan4(int a) { 16 | if (a > 4) { 17 | return a + 2; 18 | } 19 | return a; 20 | } 21 | 22 | public static int sub2IfGreaterThan4(int a) { 23 | if (a > 4) { 24 | return a - 2; 25 | } 26 | return a; 27 | } 28 | 29 | public static int mul2IfGreaterThan4Div2IfLess(int a) { 30 | if (a > 4) { 31 | return a * 2; 32 | } 33 | return a / 2; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /support/tests/ChineseRemainder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/ChineseRemainder.class -------------------------------------------------------------------------------- /support/tests/ChineseRemainder.java: -------------------------------------------------------------------------------- 1 | public class ChineseRemainder { 2 | public static long mul_inv(int a, int b) { 3 | int b0 = b, t, q; 4 | int x0 = 0, x1 = 1; 5 | if (b == 1) 6 | return 1; 7 | while (a > 1) { 8 | q = a / b; 9 | t = b; 10 | b = a % b; 11 | a = t; 12 | t = x0; 13 | x0 = x1 - q * x0; 14 | x1 = t; 15 | } 16 | if (x1 < 0) 17 | x1 += b0; 18 | return x1; 19 | } 20 | public static int chinese_remainder(int n1, int n2, int n3, int a1, int a2, int a3) { 21 | int p, i, prod = 1, sum = 0; 22 | prod = n1 * n2 * n3; 23 | p = prod / n1; 24 | sum += a1 * mul_inv(p, n1) * p; 25 | p = prod / n2; 26 | sum += a2 * mul_inv(p, n2) * p; 27 | p = prod / n3; 28 | sum += a3 * mul_inv(p, n3) * p; 29 | return ((sum % prod) + prod) % prod; 30 | } 31 | 32 | public static void main(String[] args) { 33 | System.out.println(chinese_remainder(997, 991, 983, 123, 14, 66)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /support/tests/Compare.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/Compare.class -------------------------------------------------------------------------------- /support/tests/Compare.java: -------------------------------------------------------------------------------- 1 | public class Compare { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 15; 5 | int b = 16; 6 | if (a > b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/CompareEq.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/CompareEq.class -------------------------------------------------------------------------------- /support/tests/CompareEq.java: -------------------------------------------------------------------------------- 1 | public class CompareEq { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 15; 5 | int b = 15; 6 | if (a == b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/CompareGe.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/CompareGe.class -------------------------------------------------------------------------------- /support/tests/CompareGe.java: -------------------------------------------------------------------------------- 1 | public class CompareGe { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 16; 5 | int b = 16; 6 | if (a >= b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/CompareGt.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/CompareGt.class -------------------------------------------------------------------------------- /support/tests/CompareGt.java: -------------------------------------------------------------------------------- 1 | public class CompareGt { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 18; 5 | int b = 16; 6 | if (a > b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/CompareLe.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/CompareLe.class -------------------------------------------------------------------------------- /support/tests/CompareLe.java: -------------------------------------------------------------------------------- 1 | public class CompareLe { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 15; 5 | int b = 16; 6 | if (a <= b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/CompareLt.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/CompareLt.class -------------------------------------------------------------------------------- /support/tests/CompareLt.java: -------------------------------------------------------------------------------- 1 | public class CompareLt { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 15; 5 | int b = 16; 6 | if (a < b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/CompareNe.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/CompareNe.class -------------------------------------------------------------------------------- /support/tests/CompareNe.java: -------------------------------------------------------------------------------- 1 | public class CompareNe { 2 | public static int main(String[] args) { 3 | // Testing control flow ops 4 | int a = 15; 5 | int b = 16; 6 | if (a != b) { 7 | return 1; 8 | } 9 | return -1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/DoubleFibonacci.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/DoubleFibonacci.class -------------------------------------------------------------------------------- /support/tests/DoubleFibonacci.java: -------------------------------------------------------------------------------- 1 | public class DoubleFibonacci { 2 | public static void main(String[] args) { 3 | System.out.println(fibonacci(5.0)); 4 | } 5 | public static double fibonacci(double n) { 6 | if (n <= 2.0) 7 | return 1.0; 8 | else 9 | return fibonacci(n - 1) + fibonacci(n - 2); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/Factorial.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/Factorial.class -------------------------------------------------------------------------------- /support/tests/Factorial.java: -------------------------------------------------------------------------------- 1 | public class Factorial { 2 | public static void main(String[] args) { 3 | int f = factorial(12); 4 | System.out.println(f); 5 | } 6 | 7 | public static int factorial(int n) { 8 | int accumulator = 1; 9 | for (int i = 2; i <= n; i++) accumulator *= i; 10 | return accumulator; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /support/tests/Fibonacci.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/Fibonacci.class -------------------------------------------------------------------------------- /support/tests/Fibonacci.java: -------------------------------------------------------------------------------- 1 | public class Fibonacci { 2 | public static void main(String[] args) { 3 | int n = fibonacci(15); 4 | System.out.println(n); 5 | } 6 | public static int fibonacci(int n) { 7 | if (n <= 1) 8 | return 1; 9 | else 10 | return fibonacci(n - 1) + fibonacci(n - 2); 11 | } 12 | } -------------------------------------------------------------------------------- /support/tests/FuncCall.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/FuncCall.class -------------------------------------------------------------------------------- /support/tests/FuncCall.java: -------------------------------------------------------------------------------- 1 | public class FuncCall { 2 | public static int main(String[] args) { 3 | int x = 200; 4 | int y = 300; 5 | return add(x,y); 6 | } 7 | 8 | public static int add(int a, int b) { 9 | return a + b; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /support/tests/HotLoop.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/HotLoop.class -------------------------------------------------------------------------------- /support/tests/HotLoop.java: -------------------------------------------------------------------------------- 1 | public class HotLoop { 2 | public static int main(String[] args) { 3 | int sum = 0; 4 | int i =1; 5 | for (i=1;i <= 10;i++) { 6 | sum = sum + i; 7 | } 8 | return sum; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/tests/Loop.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/Loop.class -------------------------------------------------------------------------------- /support/tests/Loop.java: -------------------------------------------------------------------------------- 1 | public class Loop { 2 | public static int main(String[] args) { 3 | int sum = 0; 4 | int i =0; 5 | for (i=0;i < 1000;i++) { 6 | sum += 1; 7 | } 8 | return sum; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/tests/MoreLoops.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/MoreLoops.class -------------------------------------------------------------------------------- /support/tests/MoreLoops.java: -------------------------------------------------------------------------------- 1 | public class MoreLoops { 2 | public static int main(String[] args) { 3 | int i =0; 4 | int sum = 0; 5 | for (i=0;i < 1000;i++) { 6 | sum += 1; 7 | } 8 | return sum; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /support/tests/MultiFuncCall.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/MultiFuncCall.class -------------------------------------------------------------------------------- /support/tests/MultiFuncCall.java: -------------------------------------------------------------------------------- 1 | public class MultiFuncCall { 2 | public static int main(String[] args) { 3 | int sum = 0; 4 | int a = 4; 5 | int b = 3; 6 | int c = 2; 7 | for (int i = 0;i < 10;i++) { 8 | sum += a + (b - c); 9 | } 10 | return sum; 11 | } 12 | 13 | public static int add(int a, int b) { 14 | return a + b; 15 | } 16 | 17 | public static int sub(int a, int b) { 18 | return a - b; 19 | } 20 | 21 | public static int threeArgs(int a, int b, int c) { 22 | return add(a, sub(b,c)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /support/tests/NakedMain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/NakedMain.class -------------------------------------------------------------------------------- /support/tests/NakedMain.java: -------------------------------------------------------------------------------- 1 | public class NakedMain { 2 | public static void main(String[] args) { 3 | int a = 0; 4 | int res = 3 + 2; 5 | if (res > 6) { 6 | a = 6; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /support/tests/Rem.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/Rem.class -------------------------------------------------------------------------------- /support/tests/Rem.java: -------------------------------------------------------------------------------- 1 | public class Rem { 2 | public static int main(String[] args) { 3 | // Testing arithmetic op (remainder). 4 | int a = 100; 5 | int b = 7; 6 | return a % b; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /support/tests/SingleFuncCall.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clflushopt/coldbrew/8c25161e64b334d1ca49758def8669e96b8d3009/support/tests/SingleFuncCall.class -------------------------------------------------------------------------------- /support/tests/SingleFuncCall.java: -------------------------------------------------------------------------------- 1 | public class SingleFuncCall { 2 | public static void main(String[] args) { 3 | int res = add(3, 2); 4 | System.out.println(res); 5 | } 6 | 7 | static int add(int a, int b) { 8 | return a + b; 9 | } 10 | } 11 | --------------------------------------------------------------------------------