Skip to content

Commit 9b411a1

Browse files
committed
OOMIR optimiser! (took a while, but finally ready) :)
1 parent d77dc55 commit 9b411a1

18 files changed

+2739
-255
lines changed

Cargo.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "0.1.0"
44
edition = "2024"
55

66
[dependencies]
7+
bigdecimal = "0.4.8"
78
half = "2.6.0"
89
num-bigint = "0.4.6"
910
num-traits = "0.2.19"

Readme.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ All examples live in `tests/binary` and are compiled to JVM bytecode & run/teste
3939
## ✨ Features
4040

4141
- **Minimal `no_std` & `no_core`** programs via `jvm-unknown-unknown`
42+
- Optimisations including constant folding and propogation, dead code elimination, and more to generate efficient JVM bytecode
4243
- Basic `core` support on host target for JVM output
4344
- Arithmetic (integers + floats, incl. checked ops)
4445
- Comparisons, bitwise & logical ops
@@ -60,12 +61,19 @@ All examples live in `tests/binary` and are compiled to JVM bytecode & run/teste
6061
2. **MIR → OOMIR**
6162
Custom “Object‑Oriented MIR” simplifies MIR into OOP‑style constructs.
6263
_(see `src/lower1.rs`)_
63-
3. **OOMIR → JVM Classfile**
64+
3. **OOMIR optimiser**
65+
Optimises OOMIR using constant folding, dead code elimination, and more.
66+
_(see `src/optimise1.rs`)_
67+
- **Constant Folding**: Evaluates constant expressions at compile time.
68+
- **Constant Propagation**: Replaces variables with their constant values.
69+
- **Dead Code Elimination**: Removes unused code paths.
70+
- **Algebraic Simplification**: Simplifies expressions using algebraic identities.
71+
4. **OOMIR → JVM Classfile**
6472
Translate to `.class` files using `ristretto_classfile`.
6573
_(see `src/lower2.rs`)_
66-
4. **Post‑Process Stack Map Frames**
74+
5. **Post‑Process Stack Map Frames**
6775
Kotlin `asm-processor` (ASM library) adds verification frames.
68-
5. **Link & Package**
76+
6. **Link & Package**
6977
`java-linker` bundles `.class` files into a runnable `.jar` with `META-INF/MANIFEST.MF`.
7078

7179
---

Tester.py

+6
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def main():
109109
parser = argparse.ArgumentParser(description="Tester for Rustc's JVM Codegen Backend")
110110
parser.add_argument("--release", action="store_true", help="Run cargo in release mode")
111111
parser.add_argument("--only-run", type=str, help="Comma-separated list of specific test names to run")
112+
parser.add_argument("--dont-run", type=str, help="Comma-separated list of specific test names to exclude")
112113
args = parser.parse_args()
113114

114115
print("🧪 Tester for Rustc's JVM Codegen Backend started!")
@@ -131,6 +132,11 @@ def main():
131132
requested_tests = set([name.strip() for name in args.only_run.split(",")])
132133
binary_tests = [t for t in binary_tests if os.path.basename(t) in requested_tests]
133134

135+
# Exclude tests based on --dont-run
136+
if args.dont_run:
137+
excluded_tests = set([name.strip() for name in args.dont_run.split(",")])
138+
binary_tests = [t for t in binary_tests if os.path.basename(t) not in excluded_tests]
139+
134140
print(f"|- 📦 Running {len(binary_tests)} binary build test(s)...")
135141
for test_dir in binary_tests:
136142
if not process_test(test_dir, args.release):

src/lib.rs

+15
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use std::{any::Any, io::Write, path::Path};
3737
mod lower1;
3838
mod lower2;
3939
mod oomir;
40+
mod optimise1;
4041

4142
/// An instance of our Java bytecode codegen backend.
4243
struct MyBackend;
@@ -100,6 +101,20 @@ impl CodegenBackend for MyBackend {
100101

101102
println!("OOMIR module: {:?}", oomir_module);
102103

104+
println!(
105+
"--- Starting OOMIR Optimisation for module: {} ---",
106+
crate_name
107+
);
108+
109+
let oomir_module = optimise1::optimise_module(oomir_module);
110+
111+
println!("Optimised OOMIR module: {:?}", oomir_module);
112+
113+
println!(
114+
"--- Finished OOMIR Optimisation for module: {} ---",
115+
crate_name
116+
);
117+
103118
println!(
104119
"--- Starting OOMIR to JVM Bytecode Lowering for module: {} ---",
105120
crate_name

src/lower1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::collections::HashMap;
1717
use types::ty_to_oomir_type;
1818

1919
mod control_flow;
20-
mod operand;
20+
pub mod operand;
2121
mod place;
2222
mod types;
2323

src/lower1/control_flow/rvalue.rs

+36-23
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub fn convert_rvalue_to_operand<'a>(
100100
for i in 0..array_size {
101101
let index_operand = oomir::Operand::Constant(oomir::Constant::I32(i as i32));
102102
instructions.push(oomir::Instruction::ArrayStore {
103-
array_var: temp_array_var.clone(), // Store into temp array
103+
array: temp_array_var.clone(),
104104
index: index_operand,
105105
value: oomir_elem_op.clone(),
106106
});
@@ -135,7 +135,14 @@ pub fn convert_rvalue_to_operand<'a>(
135135
let source_name = place_to_string(source_place, tcx);
136136
instructions.push(oomir::Instruction::Length {
137137
dest: temp_len_var.clone(),
138-
array_var: source_name,
138+
array: oomir::Operand::Variable {
139+
name: source_name,
140+
ty: ty_to_oomir_type(
141+
source_place.ty(&mir.local_decls, tcx).ty,
142+
tcx,
143+
data_types,
144+
),
145+
},
139146
});
140147
result_operand = oomir::Operand::Variable {
141148
name: temp_len_var,
@@ -307,7 +314,7 @@ pub fn convert_rvalue_to_operand<'a>(
307314
});
308315
// Set fields on the *temporary* tuple
309316
instructions.push(oomir::Instruction::SetField {
310-
object_var: temp_tuple_var.clone(),
317+
object: temp_tuple_var.clone(),
311318
field_name: "field0".to_string(),
312319
value: oomir::Operand::Variable {
313320
name: tmp_result_var,
@@ -317,7 +324,7 @@ pub fn convert_rvalue_to_operand<'a>(
317324
owner_class: tuple_class_name.clone(),
318325
});
319326
instructions.push(oomir::Instruction::SetField {
320-
object_var: temp_tuple_var.clone(),
327+
object: temp_tuple_var.clone(),
321328
field_name: "field1".to_string(),
322329
value: oomir::Operand::Variable {
323330
name: tmp_overflow_var,
@@ -374,7 +381,7 @@ pub fn convert_rvalue_to_operand<'a>(
374381
match oomir_src_operand {
375382
oomir::Operand::Variable {
376383
name: source_name,
377-
ty: _,
384+
ty: var_ty,
378385
} => {
379386
// Check if source is actually an array/slice/str type
380387
let operand_place = match operand {
@@ -400,7 +407,10 @@ pub fn convert_rvalue_to_operand<'a>(
400407
println!("Info: Detected Length op via PtrMetadata.");
401408
instructions.push(oomir::Instruction::Length {
402409
dest: temp_unop_var.clone(),
403-
array_var: source_name,
410+
array: oomir::Operand::Variable {
411+
name: source_name,
412+
ty: var_ty,
413+
},
404414
});
405415
} else {
406416
println!(
@@ -481,7 +491,7 @@ pub fn convert_rvalue_to_operand<'a>(
481491
let value_operand =
482492
convert_operand(mir_op, tcx, mir, data_types, &mut instructions);
483493
instructions.push(oomir::Instruction::SetField {
484-
object_var: temp_aggregate_var.clone(),
494+
object: temp_aggregate_var.clone(),
485495
field_name,
486496
value: value_operand,
487497
field_ty: element_oomir_type,
@@ -510,7 +520,7 @@ pub fn convert_rvalue_to_operand<'a>(
510520
let index_operand =
511521
oomir::Operand::Constant(oomir::Constant::I32(i as i32));
512522
instructions.push(oomir::Instruction::ArrayStore {
513-
array_var: temp_aggregate_var.clone(),
523+
array: temp_aggregate_var.clone(),
514524
index: index_operand,
515525
value: value_operand,
516526
});
@@ -544,7 +554,7 @@ pub fn convert_rvalue_to_operand<'a>(
544554
&mut instructions,
545555
);
546556
instructions.push(oomir::Instruction::SetField {
547-
object_var: temp_aggregate_var.clone(),
557+
object: temp_aggregate_var.clone(),
548558
field_name,
549559
value: value_operand,
550560
field_ty: field_oomir_type,
@@ -686,7 +696,7 @@ pub fn convert_rvalue_to_operand<'a>(
686696
&mut instructions,
687697
);
688698
instructions.push(oomir::Instruction::SetField {
689-
object_var: temp_aggregate_var.clone(),
699+
object: temp_aggregate_var.clone(),
690700
field_name,
691701
value: value_operand,
692702
field_ty: field_oomir_type,
@@ -768,34 +778,37 @@ pub fn convert_rvalue_to_operand<'a>(
768778
// 1. Generate instructions to get the actual value from the place
769779
let (actual_value_var_name, get_instructions, actual_value_oomir_type) =
770780
emit_instructions_to_get_on_own(place, tcx, mir, data_types);
771-
781+
772782
// Add the instructions needed to get the value (e.g., ArrayGet)
773783
instructions.extend(get_instructions);
774-
784+
775785
// 2. Now operate on the variable holding the actual value
776786
let temp_discriminant_var = generate_temp_var_name(&base_temp_name);
777-
787+
778788
let place_class_name = match actual_value_oomir_type.clone() {
779789
oomir::Type::Class(name) => name.clone(),
780790
// Handle potential references if get_on_own returns Ref(Class)
781791
oomir::Type::Reference(inner) => {
782-
if let oomir::Type::Class(name) = inner.as_ref() {
792+
if let oomir::Type::Class(name) = inner.as_ref() {
783793
name.clone()
784-
} else {
785-
panic!("Discriminant on Ref to non-class type: {:?}", inner)
786-
}
794+
} else {
795+
panic!("Discriminant on Ref to non-class type: {:?}", inner)
796+
}
787797
}
788-
_ => panic!("Discriminant on non-class type: {:?}", actual_value_oomir_type),
798+
_ => panic!(
799+
"Discriminant on non-class type: {:?}",
800+
actual_value_oomir_type
801+
),
789802
};
790-
803+
791804
let method_name = "getVariantIdx".to_string();
792805
let method_return_type = oomir::Type::I32;
793-
806+
794807
let method_ty = oomir::Signature {
795808
params: vec![],
796809
ret: Box::new(method_return_type.clone()),
797810
};
798-
811+
799812
// 3. Call InvokeVirtual on the CORRECT variable
800813
instructions.push(oomir::Instruction::InvokeVirtual {
801814
class_name: place_class_name.clone(),
@@ -808,13 +821,13 @@ pub fn convert_rvalue_to_operand<'a>(
808821
ty: actual_value_oomir_type,
809822
},
810823
});
811-
824+
812825
// 4. The result is the temporary variable holding the discriminant value
813826
result_operand = oomir::Operand::Variable {
814827
name: temp_discriminant_var,
815828
ty: method_return_type, // Should be I32
816829
};
817-
}
830+
}
818831
// Handle other Rvalue variants by generating a placeholder
819832
_ => {
820833
println!(

src/lower1/operand.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -426,20 +426,30 @@ pub fn handle_const_value<'tcx>(
426426
}
427427
} else {
428428
/* Array element type is not Ref */
429-
match read_constant_value_from_memory(tcx, allocation, pointer.into_parts().1, *inner_ty, data_types) {
429+
match read_constant_value_from_memory(
430+
tcx,
431+
allocation,
432+
pointer.into_parts().1,
433+
*inner_ty,
434+
data_types,
435+
) {
430436
Ok(oomir_const) => {
431437
println!(
432438
"Info: Successfully read constant value from memory: {:?}",
433439
oomir_const
434440
);
435-
return oomir::Operand::Constant(oomir_const);
441+
return oomir::Operand::Constant(
442+
oomir_const,
443+
);
436444
}
437445
Err(e) => {
438446
println!(
439447
"Warning: Failed to read constant value from memory for allocation {:?}. Error: {:?}",
440448
alloc_id, e
441449
);
442-
return oomir::Operand::Constant(oomir::Constant::I32(-1))
450+
return oomir::Operand::Constant(
451+
oomir::Constant::I32(-1),
452+
);
443453
}
444454
}
445455
}

src/lower1/operand/float.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use num_bigint::BigUint;
22
use num_traits::{One, ToPrimitive, Zero};
3-
use std::mem;
43

54
/// Convert an `f128` (binary128) to a decimal `String` with up to 34 significant digits,
65
/// rounding to nearest, ties-to-even.
@@ -35,7 +34,7 @@ pub fn f128_to_string(x: f128) -> String {
3534
const EXP_BIAS: i32 = 16383; // binary128 exponent bias
3635

3736
// Transmute to raw bits
38-
let bits: u128 = unsafe { mem::transmute(x) };
37+
let bits: u128 = x.to_bits();
3938
let sign_bit = (bits >> 127) != 0;
4039
let exp_bits = ((bits >> 112) & 0x7fff) as i32;
4140
let frac_bits = bits & ((1u128 << 112) - 1);

0 commit comments

Comments
 (0)