Skip to content

Commit 203223e

Browse files
committed
fix RawPtr handelling, new shims, compadibility with latest nightly, make investigation script, remove redundent just_main_func test
1 parent ec8cc99 commit 203223e

File tree

14 files changed

+287
-52
lines changed

14 files changed

+287
-52
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
**/*.generated
1111
**/*.diff
1212

13+
# Files generated by Investigate.py
14+
investigation/
15+
1316
# ICE files generated by Rustc
1417
**/*.txt
1518

Investigate.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import subprocess
5+
import sys
6+
import argparse
7+
import shutil
8+
9+
# --- Helper Functions ---
10+
11+
def run_command(cmd: list, cwd=None):
12+
"""Runs a command and captures its output, printing the command first."""
13+
print(f"|-----> Running: {' '.join(cmd)}")
14+
proc = subprocess.run(
15+
cmd,
16+
cwd=cwd,
17+
stdout=subprocess.PIPE,
18+
stderr=subprocess.PIPE,
19+
text=True,
20+
encoding='utf-8',
21+
errors='replace'
22+
)
23+
return proc
24+
25+
def write_to_file(path: str, content: str):
26+
"""Writes content to a file, creating parent directories if they don't exist."""
27+
os.makedirs(os.path.dirname(path), exist_ok=True)
28+
with open(path, "w", encoding='utf-8') as f:
29+
f.write(content)
30+
31+
# --- Main Investigation Logic ---
32+
33+
def investigate_test(test_name: str, release_mode: bool):
34+
"""Runs a single test and gathers detailed investigation artifacts."""
35+
mode = "release" if release_mode else "debug"
36+
print(f"🔬 Starting investigation for test '{test_name}' in '{mode}' mode.")
37+
38+
# 1. Set up paths
39+
test_dir = os.path.join("tests", "binary", test_name)
40+
investigation_dir = os.path.join("investigation", f"{test_name}_{mode}")
41+
javap_dir = os.path.join(investigation_dir, "javap_output")
42+
extracted_jar_dir = os.path.join(investigation_dir, "extracted_jar")
43+
44+
if not os.path.isdir(test_dir):
45+
print(f"❌ Error: Test directory not found at '{test_dir}'")
46+
sys.exit(1)
47+
48+
# 2. Create a clean investigation directory
49+
print(f"|-- 📁 Setting up investigation directory: '{investigation_dir}'")
50+
if os.path.exists(investigation_dir):
51+
shutil.rmtree(investigation_dir)
52+
os.makedirs(javap_dir)
53+
os.makedirs(extracted_jar_dir)
54+
55+
# 3. Clean the project
56+
print("|-- 🧼 Cleaning test folder...")
57+
run_command(["cargo", "clean"], cwd=test_dir)
58+
59+
# 4. Build with Cargo and capture output
60+
print("|-- ⚒️ Building with Cargo and capturing logs...")
61+
build_cmd = ["cargo", "build", "--release"] if release_mode else ["cargo", "build"]
62+
no_jvm_target = os.path.join(test_dir, "no_jvm_target.flag")
63+
if not os.path.exists(no_jvm_target):
64+
build_cmd.extend(["--target", "../../../jvm-unknown-unknown.json"])
65+
66+
proc = run_command(build_cmd, cwd=test_dir)
67+
build_log_content = f"--- COMMAND ---\n{' '.join(build_cmd)}\n\n--- RETURN CODE: {proc.returncode} ---\n\n--- STDOUT ---\n{proc.stdout}\n\n--- STDERR ---\n{proc.stderr}"
68+
write_to_file(os.path.join(investigation_dir, "cargo_build.log"), build_log_content)
69+
70+
if proc.returncode != 0:
71+
print(f"|---- ❌ cargo build failed with code {proc.returncode}. See cargo_build.log for details.")
72+
else:
73+
print("|---- ✅ cargo build succeeded.")
74+
75+
# 5. Locate the JAR file, handling special cases
76+
print("|-- 🔎 Locating generated JAR file...")
77+
target_dir = "release" if release_mode else "debug"
78+
79+
# Handle the case where no custom target is used ('no_jvm_target.flag' exists)
80+
if os.path.exists(no_jvm_target):
81+
print("|---- Found 'no_jvm_target.flag', searching for JAR in standard deps folder...")
82+
deps_dir = os.path.join(test_dir, "target", target_dir, "deps")
83+
jar_file_name = None
84+
if os.path.isdir(deps_dir):
85+
for file in os.listdir(deps_dir):
86+
if file.startswith(test_name) and file.endswith(".jar"):
87+
jar_file_name = file
88+
break
89+
90+
if jar_file_name is None:
91+
print(f"|---- ❌ No JAR file found in {deps_dir}. Cannot continue.")
92+
return
93+
94+
# Move the jar to the location expected by the rest of the script for consistency
95+
src_path = os.path.join(deps_dir, jar_file_name)
96+
dest_dir = os.path.join(test_dir, "target", "jvm-unknown-unknown", target_dir)
97+
os.makedirs(dest_dir, exist_ok=True)
98+
dest_path = os.path.join(dest_dir, f"{test_name}.jar")
99+
shutil.move(src_path, dest_path)
100+
print(f"|---- Moved JAR to {dest_path}")
101+
102+
jar_path = os.path.join(test_dir, "target", "jvm-unknown-unknown", target_dir, f"{test_name}.jar")
103+
104+
if not os.path.exists(jar_path):
105+
print(f"|---- ❌ JAR file not found at expected path: {jar_path}")
106+
print("|---- Investigation cannot proceed without a JAR file. Exiting.")
107+
return
108+
109+
print(f"|---- ✅ Found JAR: {jar_path}")
110+
111+
# 6. Analyze the JAR file
112+
# 6a. `jar tf` - List JAR contents
113+
print("|-- 🔍 Listing JAR contents (jar tf)...")
114+
proc = run_command(["jar", "tf", jar_path])
115+
write_to_file(os.path.join(investigation_dir, "jar_contents.txt"), proc.stdout)
116+
117+
# 6b. `jar xf` - Extract JAR
118+
print("|-- 📦 Extracting JAR contents for analysis...")
119+
run_command(["jar", "xf", os.path.abspath(jar_path)], cwd=extracted_jar_dir)
120+
121+
# 6c. `javap -v -p` on all .class files
122+
print("|-- 뜯 Decompiling .class files (javap -v -p)...")
123+
class_count = 0
124+
for root, _, files in os.walk(extracted_jar_dir):
125+
for file in files:
126+
if file.endswith(".class"):
127+
class_count += 1
128+
class_file_path = os.path.join(root, file)
129+
relative_path = os.path.relpath(class_file_path, extracted_jar_dir)
130+
131+
# Create a mirrored directory structure for the output
132+
output_path_dir = os.path.join(javap_dir, os.path.dirname(relative_path))
133+
output_file_path = os.path.join(output_path_dir, os.path.basename(relative_path).replace(".class", ".javap.txt"))
134+
135+
# Run javap on the .class file
136+
proc = run_command(["javap", "-v", "-p", class_file_path])
137+
output_content = f"--- COMMAND ---\njavap -v -p {class_file_path}\n\n--- STDOUT ---\n{proc.stdout}\n\n--- STDERR ---\n{proc.stderr}"
138+
write_to_file(output_file_path, output_content)
139+
140+
print(f"|---- ✅ Decompiled {class_count} class file(s).")
141+
142+
# 7. Run with Java and capture output
143+
print("|-- 🤖 Running with Java and capturing logs...")
144+
# This classpath is copied from the original script. It assumes the script is run from the project root.
145+
classpath = f"library/build/distributions/library-0.1.0/lib/library-0.1.0.jar:library/build/distributions/library-0.1.0/lib/kotlin-stdlib-2.1.20.jar:{jar_path}"
146+
java_cmd = ["java", "-cp", classpath, test_name]
147+
148+
proc = run_command(java_cmd)
149+
150+
run_log_content = f"--- COMMAND ---\n{' '.join(java_cmd)}\n\n--- RETURN CODE: {proc.returncode} ---\n\n--- STDOUT ---\n{proc.stdout}\n\n--- STDERR ---\n{proc.stderr}"
151+
write_to_file(os.path.join(investigation_dir, "java_run.log"), run_log_content)
152+
153+
if proc.returncode != 0:
154+
print(f"|---- ❌ Java process exited with non-zero code: {proc.returncode}. See java_run.log for details.")
155+
else:
156+
print("|---- ✅ Java process exited successfully (code 0).")
157+
158+
print(f"\n✨ Investigation complete! All artifacts are in '{investigation_dir}'")
159+
160+
def main():
161+
parser = argparse.ArgumentParser(
162+
description="Run a single Rustc JVM backend test and gather detailed artifacts for investigation.",
163+
formatter_class=argparse.RawTextHelpFormatter
164+
)
165+
parser.add_argument("test_name", help="The name of the test directory to investigate (e.g., 'hello_world').")
166+
parser.add_argument("--release", action="store_true", help="Run the test in release mode (cargo build --release).")
167+
args = parser.parse_args()
168+
169+
investigate_test(args.test_name, args.release)
170+
171+
if __name__ == "__main__":
172+
main()

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ rust-components:
5252
ifeq ($(IS_CI),1)
5353
rust: $(SHIM_METADATA_GEN_DIR)/core.json
5454
@echo "$(CYAN)📦 Building Rust root project...$(RESET)"
55-
RUSTFLAGS="-Awarnings" cargo build
55+
RUSTFLAGS="-Awarnings" cargo build --release
5656
else
5757
rust: $(SHIM_METADATA_GEN_DIR)/core.json rust-components
5858
@echo "$(CYAN)📦 Building Rust root project...$(RESET)"
59-
RUSTFLAGS="-Awarnings" cargo build
59+
RUSTFLAGS="-Awarnings" cargo build --release
6060
endif
6161

6262
clean-rust:
@@ -66,7 +66,7 @@ clean-rust:
6666
# === Java Linker Subproject ===
6767
java-linker:
6868
@echo "$(CYAN)📦 Building Java Linker...$(RESET)"
69-
cd $(JAVA_LINKER_DIR) && RUSTFLAGS="-Awarnings" cargo build
69+
cd $(JAVA_LINKER_DIR) && RUSTFLAGS="-Awarnings" cargo build --release
7070

7171
clean-java-linker:
7272
@echo "$(CYAN)🧹 Cleaning Java Linker...$(RESET)"

config.toml.template

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[build]
22
rustflags = [
3-
"-Z", "codegen-backend=../../../target/debug/librustc_codegen_jvm.dylib",
4-
"-C", "linker=../../../java-linker/target/debug/java-linker",
3+
"-Z", "codegen-backend=../../../target/release/librustc_codegen_jvm.dylib",
4+
"-C", "linker=../../../java-linker/target/release/java-linker",
55
"-C", "link-args=../../../library/build/distributions/library-0.1.0/lib/library-0.1.0.jar ../../../library/build/distributions/library-0.1.0/lib/kotlin-stdlib-2.1.20.jar ../../../library/build/distributions/library-0.1.0/lib/annotations-13.0.jar --r8-jar ../../../vendor/r8.jar --proguard-config ../../../proguard/default.pro"
66
]
77

library/src/main/kotlin/org/rustlang/core/Core.kt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,69 @@ public fun core_slice_u8_starts_with(value: Any, prefix: Any): Boolean {
564564

565565
return arrayOf(dst)
566566
}
567+
568+
@JvmStatic
569+
public fun std_intrinsics_size_of_val_u8(value: Any?): Long {
570+
if (value == null) {
571+
// size_of_val in Rust operates on a reference, which cannot be null.
572+
// Receiving a null here indicates a potential codegen issue.
573+
throw NullPointerException("Argument to std_intrinsics_size_of_val_u8 cannot be null.")
574+
}
575+
576+
// The name implies a slice of u8, which is represented as a ByteArray.
577+
// We can make this more general to handle other potential slice types as well.
578+
return when (value) {
579+
// For a byte array, the size in bytes is simply its length.
580+
is ByteArray -> value.size.toLong()
581+
582+
// Handle other primitive array types for completeness, calculating size in bytes.
583+
is ShortArray -> value.size.toLong() * 2
584+
is CharArray -> value.size.toLong() * 2
585+
is IntArray -> value.size.toLong() * 4
586+
is LongArray -> value.size.toLong() * 8
587+
is FloatArray -> value.size.toLong() * 4
588+
is DoubleArray -> value.size.toLong() * 8
589+
is BooleanArray -> value.size.toLong() // JVM representation is 1 byte per boolean in an array.
590+
591+
else -> {
592+
// If it's not an array, size_of_val is not applicable in this context for DSTs.
593+
// This indicates an unexpected type was passed.
594+
throw IllegalArgumentException("Unsupported type for std_intrinsics_size_of_val: ${value::class.java.name}")
595+
}
596+
}
597+
}
598+
599+
@JvmStatic
600+
public fun compare_bytes(ptr1: Any?, ptr2: Any?, len: Long): Int {
601+
if (ptr1 === ptr2) return 0
602+
// The intrinsic doesn't expect nulls, this indicates a codegen issue.
603+
if (ptr1 == null || ptr2 == null) {
604+
throw NullPointerException("compare_bytes received a null pointer")
605+
}
606+
607+
// The representation of &[u8] from a string is ShortArray in this backend
608+
if (ptr1 !is ShortArray || ptr2 !is ShortArray) {
609+
throw IllegalArgumentException("compare_bytes expects ShortArray arguments, got ${ptr1::class.simpleName} and ${ptr2::class.simpleName}")
610+
}
611+
612+
val arr1 = ptr1 as ShortArray
613+
val arr2 = ptr2 as ShortArray
614+
val n = len.toInt() // Assuming len fits in Int
615+
616+
// The 'len' parameter is the number of elements to check
617+
val limit = Math.min(Math.min(arr1.size, arr2.size), n)
618+
619+
for (i in 0 until limit) {
620+
// Compare elements directly. Use compareTo for standard signed comparison.
621+
val cmp = arr1[i].compareTo(arr2[i])
622+
if (cmp != 0) {
623+
return cmp
624+
}
625+
}
626+
627+
// If we've checked `n` elements and all are equal, the regions are considered equal
628+
// for the purpose of this intrinsic. The intrinsic does not compare overall length
629+
// if the compared prefix is identical.
630+
return 0
631+
}
567632
}

shim-metadata-gen/core.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"descriptor": "([Ljava/lang/String;)Ljava/lang/String;",
44
"is_static": true
55
},
6+
"compare_bytes": {
7+
"descriptor": "(Ljava/lang/Object;Ljava/lang/Object;J)I",
8+
"is_static": true
9+
},
610
"core_assert_failed": {
711
"descriptor": "(Ljava/lang/String;)V",
812
"is_static": true
@@ -115,6 +119,10 @@
115119
"descriptor": "(Ljava/lang/Object;Ljava/lang/Object;)Z",
116120
"is_static": true
117121
},
122+
"std_intrinsics_size_of_val_u8": {
123+
"descriptor": "(Ljava/lang/Object;)J",
124+
"is_static": true
125+
},
118126
"str_eq": {
119127
"descriptor": "(Ljava/lang/String;Ljava/lang/String;)Z",
120128
"is_static": true

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ impl CodegenBackend for MyBackend {
5454
""
5555
}
5656

57+
fn name(&self) -> &'static str {
58+
"rustc_codegen_jvm"
59+
}
60+
5761
fn codegen_crate<'a>(&self, tcx: TyCtxt<'_>) -> Box<dyn Any> {
5862
let crate_name = tcx
5963
.crate_name(rustc_hir::def_id::CRATE_DEF_ID.to_def_id().krate)
@@ -639,6 +643,7 @@ impl CodegenBackend for MyBackend {
639643
codegen_results,
640644
metadata,
641645
outputs,
646+
"jvm",
642647
);
643648
}
644649
}

src/lower1/control_flow.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,6 @@ pub fn convert_basic_block<'tcx>(
127127
);
128128
// TODO: Need logic similar to emit_instructions_to_set_value but for discriminants
129129
}
130-
StatementKind::Deinit(place) => {
131-
println!("Warning: StatementKind::Deinit NYI. Place: {:?}", place);
132-
// Often a no-op in GC'd languages unless specific resource cleanup needed
133-
}
134130
// Handle other StatementKind variants if necessary
135131
_ => {
136132
println!("Warning: Unhandled StatementKind: {:?}", stmt.kind);

0 commit comments

Comments
 (0)