diff --git a/.github/workflows/llvm-inprocess.yml b/.github/workflows/llvm-inprocess.yml new file mode 100644 index 0000000000..4e76b87185 --- /dev/null +++ b/.github/workflows/llvm-inprocess.yml @@ -0,0 +1,111 @@ +# CI arm for the in-process LLVM backend (#7241, merged in #7301). +# +# The GC knob kill-policy (CLAUDE.md) demands every shipped mode be exercised +# by CI or deleted; this job is the exercise for `PERRY_LLVM_INPROCESS`. +# Each step asserts its subject was LIVE (liveness line, diff verdict) rather +# than merely that nothing threw — see "Four ways a gate can be unable to +# fail". NON-REQUIRED until it has run green once; promote afterwards +# (a new gate has never been green, so promoting first blocks every PR). +name: llvm-inprocess + +# No trigger-level `paths:` — deliberately. A required check whose workflow +# is path-filtered at the trigger never CREATES a check run for PRs outside +# those paths, so the required context sits "waiting" forever and blocks the +# merge (the promotion trap CodeRabbit flagged on #7304). Filtering lives in +# the `changes` job instead: a job skipped by `if:` still reports a check +# run (conclusion: skipped), which branch protection accepts. +on: + pull_request: + push: + branches: [main] + +concurrency: + group: llvm-inprocess-${{ github.ref }} + # Gate-trap 3: cancel superseded PR runs, but NEVER cancel main runs — a + # busy merge day would otherwise starve the gate to zero executions. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + # Cheap relevance filter via the PR files API (no checkout, no third-party + # action). Pushes to main always run — main executions are the gate's + # anchor and the promotion prerequisite. + changes: + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read + outputs: + relevant: ${{ steps.filter.outputs.relevant }} + steps: + - id: filter + env: + GH_TOKEN: ${{ github.token }} + run: | + if [ "${{ github.event_name }}" = "push" ]; then + echo "relevant=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + files=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename') + if echo "$files" | grep -qE '^(crates/perry-codegen/|crates/perry/src/commands/compile/|experiments/llvm-inprocess-spike/|benchmarks/app-patterns/kernels/batch\.ts$|\.github/workflows/llvm-inprocess\.yml$)'; then + echo "relevant=true" >> "$GITHUB_OUTPUT" + else + echo "relevant=false" >> "$GITHUB_OUTPUT" + fi + + native-backend: + needs: changes + if: needs.changes.outputs.relevant == 'true' + runs-on: macos-15 + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + - name: Install LLVM 22 (pinned major — fail loudly on drift) + run: | + set -euo pipefail + brew install llvm@22 2>/dev/null || brew install llvm + PREFIX="$(brew --prefix llvm@22 2>/dev/null || brew --prefix llvm)" + # llvm-sys 221 requires major 22. If the runner's formula moves on, + # this must go red, not quietly build something else. + "$PREFIX/bin/llvm-config" --version | grep -q '^22\.' + echo "LLVM_SYS_221_PREFIX=$PREFIX" >> "$GITHUB_ENV" + + - name: Build with the llvm-inprocess feature + run: | + cargo build --profile perry-dev -p perry -p perry-runtime-static \ + -p perry-stdlib-static --features perry/llvm-inprocess + + - name: Unit gates (528 incl. corpus construction + RS4GC pin) + run: | + set -euo pipefail + out=$(cargo test --profile perry-dev -p perry-codegen \ + --features llvm-inprocess --lib 2>&1) || { echo "$out"; exit 1; } + # The corpus gates must have RUN, not skipped: a checkout missing + # the tracked .ll corpora would otherwise green vacuously. + echo "$out" | grep -q "dialect::tests::corpus_spike ... ok" + echo "$out" | grep -q "dialect::tests::corpus_batch_kernel ... ok" + echo "$out" | grep -q "inprocess::tests::rs4gc_schedules_in_process ... ok" + + - name: Native-mode smoke — liveness, behavior parity, object-byte verdicts + run: | + set -euo pipefail + export PERRY_RUNTIME_DIR="$PWD/target/perry-dev" + export PERRY_NO_AUTO_OPTIMIZE=1 + BIN=target/perry-dev/perry + SRC=experiments/llvm-inprocess-spike/spike.ts + + "$BIN" "$SRC" -o /tmp/spike_text + /tmp/spike_text > /tmp/text.out + + PERRY_LLVM_INPROCESS=native "$BIN" "$SRC" -o /tmp/spike_native 2> /tmp/native.err + grep -q "in-process LLVM backend active" /tmp/native.err + /tmp/spike_native > /tmp/native.out + cmp /tmp/text.out /tmp/native.out + + PERRY_LLVM_INPROCESS=diff "$BIN" "$SRC" -o /tmp/spike_diff 2> /tmp/diff.err + grep -q "ir-diff. OK" /tmp/diff.err + + PERRY_LLVM_INPROCESS=diff PERRY_CODEGEN_UNITS=3 "$BIN" \ + benchmarks/app-patterns/kernels/batch.ts -o /tmp/batch_diff 2> /tmp/diffu.err + grep -q "ir-diff. OK.*3 units" /tmp/diffu.err diff --git a/changelog.d/7304-llvm-inprocess-ci-arm.md b/changelog.d/7304-llvm-inprocess-ci-arm.md new file mode 100644 index 0000000000..617f513692 --- /dev/null +++ b/changelog.d/7304-llvm-inprocess-ci-arm.md @@ -0,0 +1,7 @@ +CI arm for the in-process LLVM backend (#7301 follow-up, kill-policy): a +path-filtered macOS job builds the `llvm-inprocess` feature against a +loudly-pinned LLVM 22, runs the perry-codegen suite asserting the corpus +and RS4GC gates actually ran, and smokes `PERRY_LLVM_INPROCESS=native` +end-to-end — liveness line, behavior parity with the text arm, and +object-byte `=diff` verdicts for a single module and a forced 3-unit +split. Lands non-required; promote after first green.