Representative interview topic

Rust 1.97.1 LLVM Miscompilation: How Do You Respond to a Suspected Compiler Regression?

GeneralHard
Offer.cc Editorial TeamPublished Updated

Question

After a production service upgrades to Rust 1.97.0, a small set of inputs produces wrong results while debug builds pass and release builds fail. You suspect an LLVM optimization miscompilation. Explain how you would distinguish an application bug, undefined behavior, and a compiler regression; contain and roll back the release; and verify that Rust 1.97.1 actually covers your code.

Prompt and scope

This is a release-engineering and incident-judgment question. The Rust project says 1.97.1 fixes a miscompilation caused by an LLVM optimization and disables the underlying Rust 1.97.0 change that increased its likelihood; the underlying issue may have existed since at least 1.87. You do not need to guess the LLVM pass. You need an evidence chain that identifies a regression, protects users, chooses a version policy, and proves the repaired binary behaves correctly.

What the interviewer evaluates

  • Whether you split wrong output by input, build configuration, optimization level, platform, and dependency version before blaming the compiler.
  • Whether you design a minimal reproducer, differential builds, and binary-level comparisons.
  • Whether you contain risk when evidence is incomplete and define rollback, optimization-disable, or release-stop gates.
  • Whether you validate with properties, known answers, and a version matrix rather than one passing run.
  • Whether you communicate impact, fixed versions, supply-chain records, and prevention work.

Clarifying questions to ask

  • Does the failure appear only with Rust 1.97.0, a particular rustc target, or an optimization level?
  • Are -C opt-level, LTO, CPU features, and the linker identical between debug and release builds?
  • Can you preserve the failing input, expected output, and reproducible build command?
  • Did dependencies, macros, unsafe code, or FFI change during the upgrade?
  • Can the service quickly return to the previous binary, and do writes need compensation?

A 30-second answer

“I would freeze the suspect binary and build metadata, preserve the failing input and expected output, then compare Rust 1.97.0, 1.97.1, and the last known-good release with identical source, locked dependencies, target, and optimization settings. In parallel I would inspect unsafe code, FFI, and undefined behavior so an application defect is not miscalled a compiler regression. I would roll back first and, if necessary, lower optimization as temporary containment. Once a minimal reproducer triggers only in the affected compiler and disappears in 1.97.1, I would validate with property tests, differential execution, and a multi-platform matrix before a staged rollout.”

Step-by-step deep dive

Step 1: Freeze evidence and contain harm

Save the failing request, wrong output, binary hash, rustc -Vv, Cargo.lock, compiler flags, target platform, and dependency cache. Stop expanding the release and return to the last known-good binary. If rollback is not immediate, temporarily lower optimization or disable the triggering path while recording the performance cost. If writes may already be wrong, isolate them and prepare compensation instead of letting a successful deployment hide business damage.

Step 2: Build a minimal differential case

Use fixed inputs and a deterministic build, then remove business code, dependencies, and macros until only a minimal program remains. Compare debug and release, different opt-level values, LTO, target CPU, and linker. Differentially execute binaries produced by several compiler versions from the same source. A failure isolated to one version and optimization combination is stronger regression evidence than a single random incident.

Step 3: Rule out undefined behavior

Inspect unsafe code, pointer aliasing, bounds, data races, FFI ABI, and uninitialized memory. Use Miri, sanitizers, extra assertions, and modeled inputs to help rule out application defects, while stating their coverage limits. Rust's safe types do not prove every unsafe block or external library correct; if undefined behavior is present, a compiler upgrade may only change the symptom.

Step 4: Verify version and fix boundaries

The Rust 1.97.1 announcement says it fixes an LLVM optimization miscompilation and disables the underlying Rust 1.97.0 change that increased the likelihood. Turn the minimal reproducer into a one-shot verification command, build it with 1.97.0, 1.97.1, and the last known-good release, and check output, relevant assembly invariants, and runtime properties. If 1.97.1 still fails, do not claim coverage; keep reducing the case and follow official guidance or move to a safe version.

Step 5: Validate the production binary in layers

Cover normal and boundary inputs with golden fixtures, property tests, replayed random seeds, and differential execution. Use shadow traffic or a small canary for critical services and observe error rate, result consistency, crashes, latency, and resource changes. The matrix must include the real target, linker, LTO, CPU instruction set, and release container; passing on a local debug build is not production proof.

Step 6: Communicate and prevent recurrence

Record affected versions, platforms, optimization combinations, input traits, bad-data volume, rollback time, and repair evidence. Tell on-call, release, and affected teams the action thresholds, and avoid publishing certainty that the evidence does not support. Make compiler upgrades part of a version matrix, reproducible builds, golden-output tests, and staged release; retain a signed previous artifact for rollback.

High-quality sample answer

“I would freeze build metadata and failing samples, stop expanding the Rust 1.97.0 rollout, and return to the last known-good binary. Then I would lock source, dependencies, target, linker, and optimization flags, reduce the failure to a minimal reproducer, and compare debug, release, LTO, and multiple rustc versions. I would also inspect unsafe code, FFI, and undefined behavior so an application defect is not mislabeled as a compiler regression. Rust 1.97.1 documents an LLVM optimization miscompilation fix, so I would build the same reproducer with 1.97.0, 1.97.1, and the last stable release, then use golden tests, properties, differential execution, and a real-target canary. I would resume staged release only when the affected combination reproduces, 1.97.1 removes it, and production signals stay stable; otherwise I would keep the rollback and preserve evidence.”

Common mistakes

  • Calling it LLVM as soon as a version-related error appears without freezing inputs, target, and build flags.
  • Comparing only debug and release while ignoring unsafe code, FFI, or undefined behavior.
  • Upgrading to 1.97.1 and running one unit test before claiming the issue is fixed.
  • Disabling optimization and continuing a full rollout without stating performance and correctness risk.
  • Losing the failing binary, Cargo.lock, or supply-chain metadata needed for reproduction.
  • Expanding the official fix statement into a claim that every platform and codebase is safe.

Follow-up questions and answers

What if the minimal case triggers only with a CPU feature?

Treat the CPU target, code-generation flags, and linker as part of the reproducer. Restrict or roll back that target, then run a matrix with 1.97.1 and the last known-good release on affected and unaffected architectures. One developer machine cannot represent every production target.

When can lower optimization become a long-term solution?

Only after an explicit performance budget and risk review accept it while the root cause remains unresolved. It can change throughput, latency, and code layout, so it needs benchmarks, monitoring, and an exit condition. Prefer the official fixed release.

How do you prove historical data was not corrupted?

Replay golden and sampled data by time, version, target, and input traits; compare checksums, business invariants, and downstream differences. Build compensation or recomputation for confirmed affected writes and record the audit scope. A normal error rate alone does not prove zero damage.

Public sources

Related questions