Skip to content

llvm-inprocess RS4GC runs before text/native constant-folding converges, changing code and GC maps #8065

Description

@proggeramlug

Failure

Fresh local audit of exact origin/main fe4cd09db0f1e260fc0ff63ec2e5c0079fceac3d (PR #8062 atop #8063), using the substantive llvm-inprocess workflow commands and an isolated target, still fails the spike object-byte oracle:

PERRY_LLVM_INPROCESS=diff ... spike.ts
perry: in-process LLVM backend active (LLVM 22.1.4)
perry: [ir-diff] MISMATCH — object bytes differ (text 164063 vs native 164671)

The text and native executables both exit 0 and emit identical 92-byte output (SHA-256 ccfecd9c51f0cd29a7fa349f3047d527e96b2b60f2078a269f91d0e9b6a4bf9c). This is not a behavior-only failure and not metadata noise: the emitted code and the GC map differ.

This is a distinct follow-on to #8052/#8062. #8062 correctly made native construction apply precise-root lowering; doing so exposes a pass-order difference that its small one-root emitted-object fixture does not exercise.

Exact local setup

main: fe4cd09db0f1e260fc0ff63ec2e5c0079fceac3d
LLVM: Homebrew 22.1.4
host: arm64 macOS
build: cargo build --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static --features perry/llvm-inprocess
env: PERRY_RUNTIME_DIR=<isolated-target>/perry-dev PERRY_NO_AUTO_OPTIMIZE=1 PERRY_NO_CACHE=1

The focused perry-codegen --lib gate is otherwise green: 990/990, including all three corpus liveness lines and rs4gc_schedules_in_process.

First semantic delta

With PERRY_LLVM_DIFF_DIR set, the two dumped pre-opt modules show that LLVM's C-API builder folds constant instructions during native construction, while the whole-module text parser retains the equivalent instruction graph until optimization. Examples include a constant bitcast + inttoptr root becoming one constant expression and constant select/write-barrier predicates disappearing.

Production currently schedules:

function(mem2reg),rewrite-statepoints-for-gc

RS4GC therefore observes the two non-canonicalized SSA graphs before the normal O3 pipeline can converge them. The first assembly delta is inside _main: native has an additional live zero/root register and a different spill assignment. The final artifacts demonstrate that this reaches semantic sections:

                         text       native
Mach-O object bytes      23056      23104
__text size              0x1dd0     0x1df4
__perry_gcmap size        0x059e     0x05a9

Relocations and compact-map root offsets shift with that code/spill difference; this is not safe to normalize away.

Causal probe

Running both exact dumped modules through:

function(mem2reg,instcombine),rewrite-statepoints-for-gc,default<O3>

and then the same LLVM target emission produces byte-identical full assembly:

text sha256   9df871b21a1ae73034ac213233cd30454070a70364bdb2cd13a123771d65e2bb
native sha256 9df871b21a1ae73034ac213233cd30454070a70364bdb2cd13a123771d65e2bb
cmp exit      0

That makes the narrow causal fix adding canonicalization before RS4GC, while preserving the exact byte oracle.

Acceptance

  • Add the minimum pre-RS4GC canonicalization needed to make construction-time folding order-independent.
  • Add an emitted-object regression with constant-foldable rooted values plus live dynamic roots; the current single-root fix(codegen): lower precise roots in native LLVM construction #8062 fixture is too small and already passes.
  • The focused spike PERRY_LLVM_INPROCESS=diff reports ir-diff. OK.
  • Do not weaken or normalize the emitted-byte oracle.

Refs #7971, #7966, #8052, #8062.

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