Skip to content

Commit bb007f3

Browse files
committed
Centralise checked arithmetic into intrinsics for faster compilation and maintainability.
1 parent c27c4d9 commit bb007f3

File tree

15 files changed

+610
-361
lines changed

15 files changed

+610
-361
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ libc = "0.2.172"
1010
md5 = "0.7.0"
1111
num-bigint = "0.4.6"
1212
num-traits = "0.2.19"
13+
once_cell = "1.20.2"
1314
regex = "1.11.1"
1415
ristretto_classfile = "0.18.1"
1516
serde = { version = "1.0.219", features = ["derive"] }

CompareSize.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ def stash_based(tests, binary_dir):
114114
"""Compares working dir vs. stashed state, measures JAR sizes and per-test build times."""
115115
# Phase 1A: build main project WITH changes
116116
print("|--- 🛠️ Building main project WITH unstaged changes…")
117-
run_command(["make", "clean"], check=True)
118-
run_command(["make", "all"], check=True)
117+
run_command(["./build.py", "clean"], check=True)
118+
run_command(["./build.py", "all"], check=True)
119119

120120
# Phase 1B: build each test and time it
121121
times_with = {}
@@ -125,7 +125,7 @@ def stash_based(tests, binary_dir):
125125
print(f"|---- Building test: {name}")
126126
t0 = time.time()
127127
# Note: The original script didn't specify --release or --target here.
128-
# The actual JAR location might depend on how 'make all' configures things.
128+
# The actual JAR location might depend on how './build.py all' configures things.
129129
run_command(["cargo", "clean"], cwd=t, check=True)
130130
run_command(["cargo", "build"], cwd=t, check=True)
131131
elapsed = time.time() - t0
@@ -155,8 +155,8 @@ def stash_based(tests, binary_dir):
155155

156156
# Phase 2A: build main project at HEAD
157157
print("\n|--- 🛠️ Building main project at HEAD (no changes)…")
158-
run_command(["make", "clean"], check=True)
159-
run_command(["make", "all"], check=True)
158+
run_command(["./build.py", "clean"], check=True)
159+
run_command(["./build.py", "all"], check=True)
160160

161161
# Phase 2B: build each test at HEAD and time it
162162
times_without = {}
@@ -250,8 +250,8 @@ def commit_based(tests, binary_dir, n):
250250

251251
# Phase 1A: build main project at HEAD
252252
print(f"\n|--- 🛠️ Building main project at HEAD ({orig_head[:10]})…")
253-
run_command(["make", "clean"], check=True)
254-
run_command(["make", "all"], check=True)
253+
run_command(["./build.py", "clean"], check=True)
254+
run_command(["./build.py", "all"], check=True)
255255

256256
# Phase 1B: build & time each test at HEAD
257257
times_head = {}
@@ -282,8 +282,8 @@ def commit_based(tests, binary_dir, n):
282282

283283
# Phase 2A: build main project at HEAD~N
284284
print(f"\n|--- 🛠️ Building main project at HEAD~{n}…")
285-
run_command(["make", "clean"], check=True)
286-
run_command(["make", "all"], check=True)
285+
run_command(["./build.py", "clean"], check=True)
286+
run_command(["./build.py", "all"], check=True)
287287

288288
# Phase 2B: build & time each test at HEAD~N
289289
times_old = {}

Tester.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,59 @@ def build_rust_code(test_dir: str, release_mode: bool) -> tuple[bool, str]:
3737
build_cmd.extend(["--target", "../../../jvm-unknown-unknown.json"])
3838

3939
proc = run_command(build_cmd, cwd=test_dir)
40+
target_dir = "release" if release_mode else "debug"
41+
4042
if proc.returncode != 0:
4143
fail_path = os.path.join(test_dir, "cargo-build-fail.generated")
4244
output = f"STDOUT:\n{proc.stdout}\n\nSTDERR:\n{proc.stderr}"
4345
write_to_file(fail_path, output)
4446
print(f"|---- ❌ cargo build exited with code {proc.returncode}")
47+
48+
# Check if this is an R8 linking error (indicates invalid JVM bytecode)
49+
error_output = proc.stdout + proc.stderr
50+
if "R8" in error_output:
51+
print("|---- 🔍 Detected R8 error - attempting to run with -noverify for better diagnostics...")
52+
test_name = os.path.basename(test_dir)
53+
54+
# Try to run the main class with -noverify to get actual JVM error
55+
deps_dir = os.path.join(test_dir, "target", target_dir, "deps")
56+
if os.path.exists(deps_dir):
57+
# Check if the main class file exists
58+
main_class = os.path.join(deps_dir, f"{test_name}.class")
59+
if os.path.exists(main_class):
60+
# Build classpath: runtime libraries + deps directory
61+
java_cp = f"{RUNTIME_CLASSPATH_BASE}{os.pathsep}{deps_dir}"
62+
verify_proc = run_command(
63+
["java", "-noverify", "-cp", java_cp, test_name]
64+
)
65+
66+
# Always write output if we got any
67+
verify_output = f"\n\n--- JVM OUTPUT WITH -noverify ---\n"
68+
verify_output += f"Command: java -noverify -cp {java_cp} {test_name}\n"
69+
verify_output += f"STDOUT:\n{verify_proc.stdout}\n\nSTDERR:\n{verify_proc.stderr}\n"
70+
verify_output += f"Return code: {verify_proc.returncode}\n"
71+
72+
# Append to the fail file
73+
full_output = output + verify_output
74+
write_to_file(fail_path, full_output)
75+
print("|---- 📝 JVM diagnostics written to cargo-build-fail.generated")
76+
77+
# Show relevant error info
78+
if verify_proc.stderr:
79+
# Filter out the deprecation warning about -noverify
80+
error_lines = [line for line in verify_proc.stderr.split('\n')
81+
if '-noverify' not in line and '-Xverify:none' not in line and line.strip()]
82+
if error_lines:
83+
print(f"|---- JVM error: {error_lines[0][:200]}")
84+
elif verify_proc.returncode != 0:
85+
print(f"|---- JVM exited with code {verify_proc.returncode}")
86+
else:
87+
print(f"|---- ⚠️ Main class file not found: {main_class}")
88+
else:
89+
print(f"|---- ⚠️ Deps directory not found: {deps_dir}")
90+
4591
return False, ""
4692

47-
target_dir = "release" if release_mode else "debug"
4893
return True, target_dir
4994

5095
def find_and_prepare_jar(test_dir: str, test_name: str, target_dir: str) -> tuple[bool, str]:

src/lib.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ impl CodegenBackend for MyBackend {
682682
entry: "bb0".into(),
683683
basic_blocks: bbs,
684684
},
685+
is_static: false,
685686
},
686687
);
687688

@@ -867,6 +868,7 @@ impl CodegenBackend for MyBackend {
867868
entry: "bb0".into(),
868869
basic_blocks: bbs,
869870
},
871+
is_static: false,
870872
};
871873

872874
new_functions.insert(format!("dyn_{}_{}", ident, name), function);
@@ -932,6 +934,24 @@ impl CodegenBackend for MyBackend {
932934
format!("OOMIR module: {:?}", oomir_module)
933935
);
934936

937+
// Emit checked arithmetic intrinsics for all needed operations
938+
breadcrumbs::log!(
939+
breadcrumbs::LogLevel::Info,
940+
"intrinsics",
941+
"Emitting checked arithmetic intrinsics..."
942+
);
943+
let needed_intrinsics = lower1::control_flow::take_needed_intrinsics();
944+
if !needed_intrinsics.is_empty() {
945+
breadcrumbs::log!(
946+
breadcrumbs::LogLevel::Info,
947+
"intrinsics",
948+
format!("Emitting {} intrinsics: {:?}", needed_intrinsics.len(), needed_intrinsics)
949+
);
950+
let intrinsic_class =
951+
lower1::control_flow::checked_intrinsics::emit_all_needed_intrinsics(&needed_intrinsics);
952+
oomir_module.data_types.insert("RustcCodegenJVMIntrinsics".to_string(), intrinsic_class);
953+
}
954+
935955
breadcrumbs::log!(
936956
breadcrumbs::LogLevel::Info,
937957
"optimisation",
@@ -1074,7 +1094,7 @@ impl CodegenBackend for MyBackend {
10741094

10751095
struct RustcCodegenJvmLogListener;
10761096

1077-
const LISTENING_CHANNELS: &[&str] = &[];
1097+
const LISTENING_CHANNELS: &[&str] = &["backend"];
10781098

10791099
impl breadcrumbs::LogListener for RustcCodegenJvmLogListener {
10801100
fn on_log(&mut self, log: breadcrumbs::Log) {

src/lower1.rs

Lines changed: 2 additions & 1 deletion
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 closures;
20-
mod control_flow;
20+
pub mod control_flow;
2121
pub mod naming;
2222
pub mod operand;
2323
pub mod place;
@@ -204,6 +204,7 @@ pub fn mir_to_oomir<'tcx>(
204204
name: fn_name,
205205
signature,
206206
body: codeblock,
207+
is_static: false,
207208
},
208209
data_types.clone(),
209210
)

src/lower1/control_flow.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ use rustc_middle::{
1818
use std::collections::HashMap;
1919

2020
mod checked_ops;
21+
mod checked_intrinsic_registry;
22+
pub mod checked_intrinsics;
2123
mod rvalue;
2224

25+
pub use checked_intrinsic_registry::take_needed_intrinsics;
26+
2327
/// Convert a single MIR basic block into an OOMIR basic block.
2428
pub fn convert_basic_block<'tcx>(
2529
bb: BasicBlock,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// checked_intrinsic_registry.rs
2+
// Global registry to track which checked arithmetic intrinsics are needed
3+
4+
use std::collections::HashSet;
5+
use std::sync::Mutex;
6+
use once_cell::sync::Lazy;
7+
8+
/// Global registry of needed checked arithmetic intrinsics
9+
/// Format: (operation, type) e.g., ("add", "i32")
10+
static NEEDED_INTRINSICS: Lazy<Mutex<HashSet<(String, String)>>> = Lazy::new(|| {
11+
Mutex::new(HashSet::new())
12+
});
13+
14+
/// Register that a checked arithmetic intrinsic is needed
15+
pub fn register_intrinsic(operation: &str, ty: &str) {
16+
let mut registry = NEEDED_INTRINSICS.lock().unwrap();
17+
registry.insert((operation.to_string(), ty.to_string()));
18+
}
19+
20+
/// Get all registered intrinsics and clear the registry
21+
pub fn take_needed_intrinsics() -> Vec<(String, String)> {
22+
let mut registry = NEEDED_INTRINSICS.lock().unwrap();
23+
let intrinsics: Vec<_> = registry.iter().cloned().collect();
24+
registry.clear();
25+
intrinsics
26+
}
27+
28+
/// Check if an intrinsic has been registered
29+
pub fn is_registered(operation: &str, ty: &str) -> bool {
30+
let registry = NEEDED_INTRINSICS.lock().unwrap();
31+
registry.contains(&(operation.to_string(), ty.to_string()))
32+
}

0 commit comments

Comments
 (0)