Skip to content

codegen: computed_store_rooting tier assertions fail intermittently when runtime+codegen are tested together #8658

Description

@proggeramlug

Three expr::computed_store_rooting_tests cases fail intermittently when perry-runtime and perry-codegen are tested in one cargo invocation under RUST_TEST_THREADS=1:

expr::computed_store_rooting_tests::collecting_masked_window_index_declines_the_hoisted_pointer_tier
expr::computed_store_rooting_tests::collecting_rhs_between_masked_reads_declines_the_hoisted_pointer_tier
expr::computed_store_rooting_tests::collecting_rhs_declines_the_straight_line_masked_region

Measured rates

Command, identical in every run:

RUST_TEST_THREADS=1 cargo test --profile perry-dev -p perry-runtime -p perry-codegen --lib
tree failures
main @ c2da03439 0 / 12
the #8657 stack (pre-merge) 3 / 12
each of #8647, #8652, #8650 alone 0 / 4 each

Machine load was 52-68 across both sets, so load alone does not separate them.

It is a combination trigger, not the tests themselves

  • -p perry-codegen --lib alone, with RUST_TEST_THREADS=1: passes on both trees.
  • -p perry-codegen --lib alone, unfiltered, no flag: passes, repeatedly.
  • Filtering to -- computed_store_rooting_tests always passes — the filter changes the experiment, so a filtered re-run is not a valid reproduction attempt.

Only "both crates in one invocation + RUST_TEST_THREADS=1" reproduces it.

Why this is worth a look beyond test hygiene

These assertions are about which optimization tier codegen selected — e.g. inert.contains("masked_region.ta_i32.preheader"), !collecting.contains(...). They compile a snippet and inspect the emitted IR; no runtime code executes. A runtime-only change cannot affect them semantically.

So a tier choice that flips depending on what else is running on the machine implies codegen is not fully deterministic under load. That is the part worth understanding: a compiler whose optimization tier depends on ambient load will produce different binaries on a busy CI runner than on a quiet one, and any A/B measurement across such a boundary is unreliable.

Two possibilities worth separating:

  1. a compile-time budget or timing-sensitive heuristic in the tier decision (would explain load sensitivity directly); or
  2. shared mutable state between the two test binaries — though they are separate processes, so this needs a concrete mechanism.

Note

Reported at merge time of #8657 rather than silently: main was 0/12 and the stack 3/12, so I could not honestly call it pre-existing, and each constituent PR was clean alone. If it turns out to be (1), it predates all of them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions