-
-
Notifications
You must be signed in to change notification settings - Fork 155
ci(gc): let the statepoint arm pass, and put ELF back under test #7344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -111,6 +111,35 @@ jobs: | |
| set -euo pipefail | ||
| export PERRY_RUNTIME_DIR="$PWD/target/perry-dev" | ||
| export PERRY_NO_AUTO_OPTIMIZE=1 | ||
|
|
||
| # Pin an LLVM that registers the `statepoint-example` GC strategy. | ||
| # Without this the job cannot pass at all: the macos-14 image ships | ||
| # Apple clang 15, which does not register it, so every run died with | ||
| # fatal error: error in backend: unsupported GC: statepoint-example | ||
| # before a single probe executed. That is CLAUDE.md's failure mode | ||
| # inverted — a gate that cannot PASS is as useless as one that cannot | ||
| # fail, and it stayed red across five consecutive main runs while | ||
| # looking like a real regression. | ||
| # | ||
| # Newer Apple clang does register it (21 does, locally), so this is a | ||
| # property of the runner image rather than of Apple clang forever. | ||
| # Pin explicitly rather than depend on the image. The RS4GC job below | ||
| # already does this for its own reasons. | ||
| brew list llvm >/dev/null 2>&1 || brew install llvm | ||
| llvm_bin="$(brew --prefix llvm)/bin" | ||
| if [ ! -x "$llvm_bin/clang" ]; then | ||
| echo "::error::no clang under $llvm_bin — cannot run the statepoint arm, and skipping it silently is the gate that cannot fail" | ||
| exit 1 | ||
| fi | ||
| export PERRY_LLVM_CLANG="$llvm_bin/clang" | ||
| "$PERRY_LLVM_CLANG" --version | head -2 | ||
| # Assert the pinned toolchain actually supports the strategy, so a | ||
| # future image change fails here with a clear message instead of | ||
| # deep inside a probe compile. | ||
| printf 'define void @f() gc "statepoint-example" {\n ret void\n}\n' > /tmp/gcstrategy.ll | ||
| "$PERRY_LLVM_CLANG" -c /tmp/gcstrategy.ll -o /dev/null \ | ||
| || { echo "::error::pinned clang does not support gc \"statepoint-example\""; exit 1; } | ||
|
|
||
| pass=0 | ||
| total=0 | ||
| errs="" | ||
|
|
@@ -470,6 +499,80 @@ jobs: | |
| # REFUSAL (never a silently rootless binary), and goes red the day x86-64 | ||
| # starts working, which is the prompt to widen the aarch64 matrix above (#7321). | ||
| # Deliberately cheap: one probe, no runtime, no oracle. | ||
| # ELF coverage. The two GC arms above run on macos-14, so without this job | ||
| # nothing exercises the object format the whole compact-map design had to be | ||
| # rewritten for. Every bug that reached main in that area was ELF-only and | ||
| # invisible on Mach-O: | ||
| # * the section needed SHF_GNU_RETAIN or `--gc-sections` dropped it, since | ||
| # nothing references it (Mach-O has `.no_dead_strip`); | ||
| # * it needed SHF_WRITE too, or the relocated function addresses forced a | ||
| # DT_TEXTREL in a PIE; | ||
| # * `eh_walker`'s asm defined `_perry_eh_capture_context` with the Mach-O | ||
| # underscore convention, so aarch64-Linux could not even link. | ||
| # | ||
| # Runs on ARM64 because the backend is aarch64-only (#7324); on x86-64 it | ||
| # would exercise only the refusal, which `statepoints-refuse-x86` covers. | ||
| # Stock Ubuntu clang registers `statepoint-example` (verified on 18.1.3), so | ||
| # unlike macOS this needs no toolchain install. | ||
| native-roots-elf-aarch64: | ||
| runs-on: ubuntu-24.04-arm | ||
| timeout-minutes: 45 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version-file: .node-version | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| key: gc-native-roots-elf | ||
| - name: Build compiler and static runtime (perry-dev profile) | ||
| run: | | ||
| export RUSTFLAGS="-Cforce-frame-pointers=yes" | ||
| cargo build --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static | ||
| - name: Assert the toolchain registers the GC strategy | ||
| run: | | ||
| set -euo pipefail | ||
| printf 'define void @f() gc "statepoint-example" {\n ret void\n}\n' > /tmp/gcstrategy.ll | ||
| clang -c /tmp/gcstrategy.ll -o /dev/null \ | ||
| || { echo "::error::this clang does not support gc \"statepoint-example\" — pin one that does"; exit 1; } | ||
| - name: Probe matrix, statepoint mode, forced evacuation | ||
| if: ${{ !cancelled() }} | ||
| run: | | ||
| set -euo pipefail | ||
| export PERRY_RUNTIME_DIR="$PWD/target/perry-dev" | ||
| export PERRY_NO_AUTO_OPTIMIZE=1 | ||
| pass=0 | ||
| total=0 | ||
| errs="" | ||
| for probe in benchmarks/gc_ratchet/probes/*.ts; do | ||
| # Same exclusion as the macOS bridge arm: the explicit bridge | ||
| # refuses a try-carrying probe rather than emit a frame with no | ||
| # roots (#7330/#7335). RS4GC covers 09. | ||
| [ "$(basename "$probe")" = "09_try_catch_roots.ts" ] && continue | ||
| total=$((total+1)) | ||
| name=$(basename "$probe" .ts) | ||
| node --expose-gc --experimental-strip-types "$probe" > "/tmp/$name.oracle" | ||
| PERRY_STATEPOINTS=1 ./target/perry-dev/perry "$probe" -o "/tmp/$name" | ||
| # Liveness: the compact section must be present AND LLVM's gone, | ||
| # so a run where the rewrite silently stopped cannot read as green. | ||
| readelf -S "/tmp/$name" | grep -q "\.perry_gcmap" \ | ||
| || { echo "::error::$name has no .perry_gcmap — the statepoint arm was not live"; exit 1; } | ||
| readelf -S "/tmp/$name" | grep -q "\.llvm_stackmaps" \ | ||
| && { echo "::error::$name still carries .llvm_stackmaps — the compact rewrite did not run"; exit 1; } | ||
| PERRY_STATEPOINTS=1 PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1 \ | ||
| "/tmp/$name" > "/tmp/$name.out" 2> "/tmp/$name.err" | ||
| diff "/tmp/$name.oracle" "/tmp/$name.out" \ | ||
| || { echo "::error::$name output diverged from the pinned oracle"; exit 1; } | ||
| errs="$errs /tmp/$name.err" | ||
| pass=$((pass+1)) | ||
| done | ||
| echo "ELF statepoint matrix: $pass/$total" | ||
| [ "$total" -gt 0 ] || { echo "::error::no probes matched — the matrix ran on nothing"; exit 1; } | ||
| [ "$pass" -eq "$total" ] | ||
| grep -l "#gcmetric" $errs >/dev/null \ | ||
| || { echo "::error::no probe emitted gc metrics — the collector never ran"; exit 1; } | ||
|
|
||
| statepoints-refuse-x86: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 45 | ||
|
|
@@ -512,7 +615,13 @@ jobs: | |
| # branch protection to require, so adding an arm never needs a protection edit | ||
| # and a red arm cannot hide behind a green sibling. | ||
| gc-native-roots-complete: | ||
| needs: [native-roots-aarch64, native-roots-rs4gc-aarch64, statepoints-refuse-x86] | ||
| needs: | ||
| [ | ||
| native-roots-aarch64, | ||
| native-roots-rs4gc-aarch64, | ||
| native-roots-elf-aarch64, | ||
| statepoints-refuse-x86, | ||
| ] | ||
|
Comment on lines
+618
to
+624
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Check the ELF arm result in the fan-in gate.
Add the new result to the loop. Proposed fix for arm in \
"native-roots-aarch64=${{ needs.native-roots-aarch64.result }}" \
"native-roots-rs4gc-aarch64=${{ needs.native-roots-rs4gc-aarch64.result }}" \
+ "native-roots-elf-aarch64=${{ needs.native-roots-elf-aarch64.result }}" \
"statepoints-refuse-x86=${{ needs.statepoints-refuse-x86.result }}"; doAs per coding guidelines, a CI gate must be included in required branch-protection contexts. 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| if: always() | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| ### `gc-native-roots`: make the statepoint arm able to pass, and restore ELF coverage | ||
|
|
||
| The gate has been red on `main` since it landed — five consecutive runs — and | ||
| neither cause was a code regression. | ||
|
|
||
| **It could not pass.** `native-roots-aarch64` runs on `macos-14`, whose image | ||
| ships **Apple clang 15**, which does not register the `statepoint-example` GC | ||
| strategy. Every run died with `fatal error: error in backend: unsupported GC: | ||
| statepoint-example` before a single probe executed. That is CLAUDE.md's | ||
| failure mode inverted: a gate that cannot *pass* is as useless as one that | ||
| cannot fail, and it looked like a real regression for a day. The job now pins | ||
| Homebrew LLVM, as the RS4GC job beside it already did, and asserts the pinned | ||
| toolchain registers the strategy so a future image change fails with a clear | ||
| message rather than inside a probe compile. Newer Apple clang does register it | ||
| (21 does), so this is a property of the runner image, not of Apple clang. | ||
|
|
||
| **It stopped testing ELF.** Both GC arms run on macOS, so nothing exercised the | ||
| object format the compact map had to be reworked for. Every bug that reached | ||
| `main` in that area was ELF-only and invisible on Mach-O: the section needed | ||
| `SHF_GNU_RETAIN` or `--gc-sections` discarded it; it needed `SHF_WRITE` too, or | ||
| the relocated addresses forced a `DT_TEXTREL` in a PIE; and `eh_walker`'s asm | ||
| used the Mach-O underscore convention, so aarch64-Linux could not link at all. | ||
| A `native-roots-elf-aarch64` arm runs the same matrix on `ubuntu-24.04-arm` | ||
| with both liveness asserts (`.perry_gcmap` present **and** `.llvm_stackmaps` | ||
| absent). It needs no toolchain install — stock Ubuntu clang registers the | ||
| strategy, verified on 18.1.3. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Drive and prove the precise-root evacuation path.
The ELF matrix sets
PERRY_GC_FORCE_EVACUATE=1, but it omits the minor-path configuration used by the macOS arm. The probes can therefore collect through the manual conservative path, where forced evacuation is inert.#gcmetriconly proves that a metric was emitted. It does not prove object movement or precise-root use.Add the minor-path settings and run
gc_evacuation_liveness_assert.pyfor each probe.Proposed fix
As per coding guidelines, a CI gate must assert that the behavior it measures actually executed.
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Coding guidelines