Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5fd7a9c
add typed SSA and classfile utilities
IntegralPilot Sep 6, 2026
6a1f1e4
add SSA JVM codegen
IntegralPilot Sep 6, 2026
0fbadda
refactor java linker
IntegralPilot Sep 6, 2026
b45b75a
rework OOMIR construction and outlining
IntegralPilot Sep 6, 2026
6d9bbca
split type lowering into modules
IntegralPilot Sep 6, 2026
b28839b
split rvalue lowering into modules
IntegralPilot Sep 6, 2026
3325811
split call and control flow lowering
IntegralPilot Sep 6, 2026
4670fbc
refactor lowering context and debug info
IntegralPilot Sep 6, 2026
c3dfdab
switch compiler pipeline to SSA
IntegralPilot Sep 6, 2026
69af92e
remove old JVM translator
IntegralPilot Sep 6, 2026
4638339
remove old optimisers and stack maps
IntegralPilot Sep 6, 2026
01f4c5e
fix pointer identities and add SSA tests
IntegralPilot Sep 6, 2026
211027a
speed up codegen and linking
IntegralPilot Sep 6, 2026
cdc599a
stream class file encoding
IntegralPilot Sep 6, 2026
baaf62c
reuse lowering names and common SSA types
IntegralPilot Sep 6, 2026
aff9919
parallelize MIR lowering
IntegralPilot Sep 6, 2026
07ceebf
reuse upstream generic bodies
IntegralPilot Sep 6, 2026
3f8f11f
consume method bodies and forward SSA bindings
IntegralPilot Sep 6, 2026
6da051b
collect generated call dependencies during lowering
IntegralPilot Sep 6, 2026
f8d00b0
reduce SSA and helper generation
IntegralPilot Sep 6, 2026
6acbd25
avoid redundant lowering work
IntegralPilot Sep 6, 2026
51531a0
fix Windows compiler tests
IntegralPilot Sep 6, 2026
7345d44
fix union pointer views
IntegralPilot Sep 22, 2026
6e7abce
reduce pointer view overhead
IntegralPilot Sep 25, 2026
637ad53
fix raw field access and reborrows
IntegralPilot Sep 25, 2026
ad4392f
promote pointer cells and fields
IntegralPilot Sep 25, 2026
8f4306b
fix nested constant lowering
IntegralPilot Sep 25, 2026
a00eb5c
normalize trait object pointees
IntegralPilot Sep 25, 2026
682214b
support latest nightly
IntegralPilot Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,21 @@ jobs:

- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
with:
# Older caches contain source overlays that the action treats as build artifacts.
prefix-key: self-tests-artifacts-v1

- name: Build backend and tools
run: python build.py ci

- name: Run compiler unit tests
id: compiler_unit_tests
continue-on-error: true
run: cargo test
shell: bash
run: |
cargo test
cargo test --manifest-path compiler-core/Cargo.toml
cargo test --manifest-path java-linker/Cargo.toml

- name: Check JVM binding macros
id: jvm_macro_tests
Expand All @@ -93,6 +100,10 @@ jobs:
continue-on-error: true
run: python Tester.py --release

- name: Remove generated sources before caching build artifacts
if: always()
run: python -c "from pathlib import Path; import shutil; p = Path('target/stdlib-overlay'); p.exists() and shutil.rmtree(p)"

- name: Require 100% self-test pass rate
if: >-
always() &&
Expand Down
47 changes: 10 additions & 37 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ version = "0.1.0"
edition = "2024"

[dependencies]
num-bigint = "0.4.6"
num-traits = "0.2.19"
once_cell = "1.20.2"
jvm-compiler-core = { path = "compiler-core", features = ["serde"] }
ristretto_classfile = "0.31.0"
rustc-hash = "2.1.1"
serde = { version = "1.0.219", features = ["derive"] }
Expand Down
107 changes: 25 additions & 82 deletions Metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,43 +102,18 @@ def summarize(records: list[dict[str, Any]]) -> dict[str, Any]:
)
oomir_before: dict[str, int] = defaultdict(int)
oomir_after: dict[str, int] = defaultdict(int)
optimise2: dict[str, int] = defaultdict(int)
liveness: dict[str, int] = defaultdict(int)
selection: dict[str, int] = defaultdict(int)
type_cache: dict[str, int] = defaultdict(int)
classfiles: dict[str, int] = defaultdict(int)
passes: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))
repeated_types: dict[str, int] = defaultdict(int)
amplified_classes: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))
top_methods: list[dict[str, Any]] = []
largest_shards: list[dict[str, Any]] = []

for record in compilers:
add_fields(oomir_before, record.get("oomir_before_optimise1", {}), oomir_fields)
add_fields(oomir_after, record.get("oomir_after_optimise1", {}), oomir_fields)
compiler_optimise2 = record.get("optimise2", {})
add_fields(
optimise2,
compiler_optimise2,
(
"methods",
"input_instructions",
"output_instructions",
"input_max_locals",
"output_max_locals",
),
)
add_fields(
liveness,
compiler_optimise2.get("liveness", {}),
(
"analyses",
"instructions",
"locals",
"matrix_words",
"successor_edges",
"worklist_pops",
),
)
add_fields(oomir_before, record.get("oomir_construction", {}), oomir_fields)
add_fields(oomir_after, record.get("oomir_sealed", {}), oomir_fields)
add_fields(selection, record.get("selection", {}), ("methods", "ssa_instructions", "jvm_instructions", "locals"))
add_fields(type_cache, record.get("type_lowering_cache", {}), ("hits", "misses"))
for origin in record.get("classfiles_by_origin", []):
add_fields(
Expand All @@ -154,20 +129,7 @@ def summarize(records: list[dict[str, Any]]) -> dict[str, Any]:
"name_collisions",
),
)
for item in compiler_optimise2.get("passes", []):
add_fields(
passes[item.get("pass", "<unknown>")],
item,
(
"invocations",
"input_instructions",
"output_instructions",
"instructions_removed",
"instructions_added",
"length_changing_invocations",
),
)
for item in compiler_optimise2.get("top_methods_by_structural_work", []):
for item in record.get("top_methods", []):
top_methods.append({"crate": record.get("crate_name", "<unknown>"), **item})
for item in record.get("largest_shards", []):
largest_shards.append({"crate": record.get("crate_name", "<unknown>"), **item})
Expand Down Expand Up @@ -210,13 +172,11 @@ def summarize(records: list[dict[str, Any]]) -> dict[str, Any]:
"compiler_processes": len(compilers),
"linker_processes": len(linkers),
"parse_errors": parse_errors,
"oomir_before_optimise1": dict(oomir_before),
"oomir_after_optimise1": dict(oomir_after),
"oomir_construction": dict(oomir_before),
"oomir_sealed": dict(oomir_after),
"type_lowering_cache": dict(type_cache),
"optimise2": dict(optimise2),
"liveness": dict(liveness),
"selection": dict(selection),
"classfiles": dict(classfiles),
"passes": {name: dict(values) for name, values in passes.items()},
"repeated_data_types": sorted(
repeated_types.items(), key=lambda item: item[1], reverse=True
)[:20],
Expand All @@ -229,13 +189,13 @@ def summarize(records: list[dict[str, Any]]) -> dict[str, Any]:
reverse=True,
)[:20],
"top_methods": sorted(
top_methods, key=lambda item: item.get("work_units", 0), reverse=True
top_methods, key=lambda item: item.get("ssa_instructions", 0), reverse=True
)[:20],
"largest_shards": sorted(
largest_shards,
key=lambda item: (
item.get("before_optimise1", {}).get("instructions", 0),
item.get("before_optimise1", {}).get("data_types", 0),
item.get("construction", {}).get("instructions", 0),
item.get("construction", {}).get("data_types", 0),
),
reverse=True,
)[:20],
Expand All @@ -259,26 +219,20 @@ def format_result(result: Result, top: int) -> str:
f" records: {summary.get('compiler_processes', 0)} compiler, "
f"{summary.get('linker_processes', 0)} linker",
]
before = summary.get("oomir_before_optimise1", {})
after = summary.get("oomir_after_optimise1", {})
before = summary.get("oomir_construction", {})
after = summary.get("oomir_sealed", {})
lines.append(
" OOMIR: "
" Construction -> SSA: "
f"{before.get('instructions', 0):,} -> {after.get('instructions', 0):,} instructions; "
f"{before.get('data_types', 0):,} shard-local data-type definitions"
)
optimise2 = summary.get("optimise2", {})
selection = summary.get("selection", {})
lines.append(
" optimise2: "
f"{optimise2.get('methods', 0):,} methods, "
f"{optimise2.get('input_instructions', 0):,} -> "
f"{optimise2.get('output_instructions', 0):,} bytecode instructions"
)
liveness = summary.get("liveness", {})
lines.append(
" liveness: "
f"{liveness.get('analyses', 0):,} analyses, "
f"{liveness.get('matrix_words', 0):,} matrix words allocated, "
f"{liveness.get('worklist_pops', 0):,} worklist pops"
" JVM selection: "
f"{selection.get('methods', 0):,} methods, "
f"{selection.get('ssa_instructions', 0):,} SSA instructions -> "
f"{selection.get('jvm_instructions', 0):,} bytecode instructions, "
f"{selection.get('locals', 0):,} total local slots"
)
cache = summary.get("type_lowering_cache", {})
hits = cache.get("hits", 0)
Expand All @@ -293,17 +247,6 @@ def format_result(result: Result, top: int) -> str:
f"{classes.get('exact_duplicates', 0):,} exact duplicates discarded "
f"({classes.get('exact_duplicate_bytes', 0):,} generated bytes)"
)
hottest_passes = sorted(
summary.get("passes", {}).items(),
key=lambda item: item[1].get("input_instructions", 0),
reverse=True,
)[:top]
lines.append(" largest optimise2 pass inputs:")
for name, values in hottest_passes:
lines.append(
f" {name}: received {values.get('input_instructions', 0):,}, "
f"removed {values.get('instructions_removed', 0):,}"
)
repeated = summary.get("repeated_data_types", [])[:top]
if repeated:
lines.append(" most repeated shard-local data types:")
Expand All @@ -318,20 +261,20 @@ def format_result(result: Result, top: int) -> str:
)
methods = summary.get("top_methods", [])[:top]
if methods:
lines.append(" highest optimise2 structural work:")
lines.append(" largest SSA bodies:")
lines.extend(
f" {item.get('crate', '<unknown>')}::{item.get('item', '<unknown>')}: "
f"{item.get('work_units', 0):,} work units, "
f"{item.get('input_instructions', 0):,} input instructions"
f"{item.get('ssa_instructions', 0):,} SSA instructions, "
f"{item.get('jvm_instructions', 0):,} JVM instructions"
for item in methods
)
shards = summary.get("largest_shards", [])[:top]
if shards:
lines.append(" largest OOMIR shards:")
lines.extend(
f" {item.get('crate', '<unknown>')}::{item.get('shard', '<unknown>')}: "
f"{item.get('before_optimise1', {}).get('instructions', 0):,} instructions, "
f"{item.get('before_optimise1', {}).get('data_types', 0):,} data types"
f"{item.get('construction', {}).get('instructions', 0):,} instructions, "
f"{item.get('construction', {}).get('data_types', 0):,} data types"
for item in shards
)
linker = summary.get("linker", {})
Expand Down
30 changes: 27 additions & 3 deletions Tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,9 @@ def javap_debug_info(output: str, included_methods: set[str] | None = None) -> s

Bytecode offsets and local-variable live ranges legitimately vary with
constant-pool layout, the host toolchain, and optimization details. The
source-line sequence and each variable's slot/name/signature are the stable
metadata this test is intended to protect.
source-line sequence and each variable's slot/name/signature protect the
selected lowering path. SSA may have its own expectation because removing
source copies and reordering branches changes the executable line sequence.
"""
source: str | None = None
methods: dict[str, list[str]] = {}
Expand Down Expand Up @@ -405,6 +406,29 @@ def javap_debug_info(output: str, included_methods: set[str] | None = None) -> s
return "\n".join(result).strip()


def debug_semantics(text: str) -> tuple:
"""Source coverage and bindings survive register allocation and block layout."""
source = None
methods = {}
current = None
for line in text.splitlines():
line = line.strip()
if line.startswith('Compiled from '):
source = line
elif '(' in line and line.endswith(';'):
current = (set(), [])
methods[line] = current
elif current is not None:
if match := re.fullmatch(r'line (\d+)', line):
current[0].add(int(match[1]))
elif match := re.fullmatch(r'\d+ (\S+ \S+)', line):
# Keep repeated names: shadowed bindings are distinct even if
# their source name and descriptor happen to be identical.
current[1].append(match[1])
return source, tuple((name, tuple(sorted(lines)), tuple(sorted(locals)))
for name, (lines, locals) in sorted(methods.items()))


def check_javap_debug_info(
test: TestCase, jar: Path, release: bool, logs: list[str]
) -> bool:
Expand All @@ -431,7 +455,7 @@ def check_javap_debug_info(
if "(" in line and line.strip().endswith(";")
}
actual = javap_debug_info(proc.stdout, expected_methods)
if actual == expected:
if debug_semantics(actual) == debug_semantics(expected):
logs.append("|--- ✅ JVM debug metadata matches expected output!")
return True

Expand Down
6 changes: 3 additions & 3 deletions alloctests/alloctests-patches/0001-restricted-std.patch
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ diff --git a/lib.rs b/lib.rs
@@ -1,3 +1,4 @@
#![cfg(test)]
+#![feature(restricted_std)]
#![allow(unused_attributes)]
#![unstable(feature = "alloctests", issue = "none")]
#![no_std]
diff --git a/tests/lib.rs b/tests/lib.rs
--- a/tests/lib.rs
+++ b/tests/lib.rs
Expand All @@ -16,8 +16,8 @@ diff --git a/tests/lib.rs b/tests/lib.rs
#![deny(implicit_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]
#![feature(alloc_io)]
#![feature(allocator_api)]
@@ -59,5 +60,6 @@
#![feature(allocator_ext)]
@@ -61,5 +62,6 @@
#![feature(vec_deque_retain_range)]
#![feature(vec_peek_mut)]
#![feature(vec_try_remove)]
Expand Down
7 changes: 5 additions & 2 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@ class Config:
# Source File Lists (used for dependency tracking)
# Using glob patterns for automatic discovery
RUNTIME_SOURCES = sorted(RUNTIME_DIR.glob("src/**/*.java"))
COMPILER_CORE_SOURCES = [ROOT_DIR / "compiler-core/Cargo.toml"] + list(
ROOT_DIR.glob("compiler-core/src/**/*.rs")
)
BACKEND_RUST_SOURCES = [ROOT_DIR / "Cargo.toml", ROOT_DIR / "Cargo.lock"] + list(
ROOT_DIR.glob("src/**/*.rs")
)
) + COMPILER_CORE_SOURCES
LINKER_RUST_SOURCES = [
JAVA_LINKER_DIR / "Cargo.toml",
JAVA_LINKER_DIR / "Cargo.lock",
] + list(JAVA_LINKER_DIR.glob("src/**/*.rs"))
] + list(JAVA_LINKER_DIR.glob("src/**/*.rs")) + COMPILER_CORE_SOURCES
CARGO_JVM_RUST_SOURCES = [
CARGO_JVM_DIR / "Cargo.toml",
CARGO_JVM_DIR / "Cargo.lock",
Expand Down
Loading
Loading