Skill: V8 Regression Testing
Use this skill to design, write, minimize, and refine high-quality regression
tests in the V8 codebase (typically under test/mjsunit/ or its
component-specific subfolders like compiler/, turboshaft/, maglev/, etc.).
Core Mandates
- Mandatory Reproducer Rule: Unless explicitly specified otherwise by the
user, every single bug fix in V8 MUST be uploaded with a working reproducer
(regression test). Uploading just the fix alone is NEVER acceptable.
- Semantic Quality: V8 regression tests must be semantic
(root-cause-driven) rather than just syntactic (fuzzer-driven). A
high-quality test case is a clear, human-readable, and robust demonstration of
the specific logical bug or compiler invariant under test, rather than a
fragile, black-box crash script.
- CRITICAL REPRODUCER CONSTRAINT: NEVER provide a reproducer that does
not actually trigger the issue to the user without explicitly telling them
about it.
The "From-Scratch" Reproducer Philosophy
When constructing a reproducer for a bug, always prefer writing it from
scratch based on a deep conceptual understanding of the issue, rather than
starting from a fuzzer-generated crash script and trying to minimize it.
Why the Post-Hoc Fuzzer Minimization Trap Fails:
- The Black Box Dilemma: Starting with a fuzzer-generated block of code
means you are constantly guessing which strange construct is necessary. You
are highly likely to retain useless nesting (
__wrapTC), redundant
try-catches, and magic numbers because you are afraid to break the fragile
reproduction state.
- Fragility: Fuzzer scripts often rely on incidental timings, engine
heuristics, or side effects to trigger a crash. If optimization heuristics
change slightly in a future V8 release, the fuzzer reproducer will silently
break and stop testing the intended bug path.
The "From-Scratch" Synthesis Approach:
- Identify the Root Cause: Analyze the compiler phase graph, the crash
backtrace, and the failing
DCHECK or invariant to understand the exact
abstract state machine failure (e.g., "an incorrect type representation
selection, a specific Map transition mismatch, or a loop-carried
side-effect").
- Begin with a Blank File: Open an empty editor. Do not copy-paste the
fuzzer script.
- Target the State Directly: Write the absolute minimum JavaScript
necessary to construct that logical state:
- Use clean, structured object definitions and allocations to establish
target internal structures (such as distinct Maps or Shapes).
- Control the IC feedback vector cleanly by warming it up with specific
types.
- Explicitly trigger the compiler pass with intrinsic calls (e.g.,
%PrepareFunctionForOptimization, %OptimizeFunctionOnNextCall).
- Result: You produce a clean, robust, perfectly commented, 15-line
reproducer that explicitly tests the underlying logic. It is
self-documenting, easy to maintain, and impervious to unrelated engine
optimization tweaks.
Debugging a Failing Reproducer (The Scientific Loop)
When writing a reproducer from scratch, it is common for the first attempt to
not trigger the crash. When this happens, do not give up or fall back to
blindly copying the fuzzer script. Instead, treat the reproducer's failure as a
debugging task and enter a scientific feedback loop.
The Feedback Loop:
- Trace the Execution: Use V8's extensive set of tracing flags to inspect
the JIT/engine pipeline (eg,
--trace-turbo-graph, --trace-ic,
--trace-deopt...), and insert custom instruction (eg, using PrintF) as
needed if the existing flags are too coarse.
- Detect the Detour: Identify exactly where the execution path diverged
from your expectation. For instance:
- Did the function deoptimize prematurely due to an unexpected type feedback
change?
- Did load elimination optimize away a read that was supposed to trigger a
side effect?
- Did a map transition occur too early?
- Analyze the Cause: Figure out why V8 chose that detour.
- What invariant, heuristic, or state in your JavaScript code caused the
compiler to take a different optimization path?
- Heal and Steering: Modify your JavaScript reproducer to block the detour
and steer the compiler back onto the target path (e.g., altering map setups,
introducing fake side effects to prevent load-elimination, or adjusting
argument warming patterns).
[!IMPORTANT] The Ultimate Conceptual Checkpoint: If you repeatedly fail to
construct a clean, working reproducer from scratch, this almost always means
your conceptual understanding of the bug is either incorrect or too shallow.
Do not waste days trying to brute-force random JavaScript tweaks. When this
checkpoint is hit, you must go back to the drawing board, challenge what you
think you know about the bug, and deepen your understanding.
Synctatic Principles of a Good Regression Test
1. Minimal & "Leaf" Compiler Flags
- Strict Flag Minimization: Every flag included in
// Flags: ... must be
thoroughly understood and strictly necessary to reproduce the issue. Remove
all useless or redundant flags.
- Prefer "Leaf" Flags over "Compound" Flags:
- Bad: Using
--jit-fuzzing or --fuzzing because it implies a massive,
ever-changing bundle of underlying behaviors.
- Good: Identifying and specifying the exact leaf flags responsible (e.g.,
--no-lazy-feedback-allocation, --homomorphic-ic,
--stress-concurrent-inlining-attach-code).
2. Human-Friendly Naming
Avoid default fuzzer variable and function names (__f_0, __v_10, v17,
a6, a7).
- Use descriptive names for semantic concepts (
large_arr, global_var,
testGenerator).
- Use clean, standard placeholders for simple generic logic (
foo, bar,
obj, f, x, y).
- Maintain cognitive load at an absolute minimum.
3. Absolute Minimization of Code & Boilerplate
- Remove every parameter, variable, block, or helper that does not directly
contribute to reproducing the bug.
- Never leave unused parameters in function signatures, unless absolutely
required to reproduce the issue.
- Do not include catch-all blocks (
catch (e) {}) or dummy wrapper functions
(__wrapTC) unless they are semantically required to trigger the crash path.
4. Deterministic Optimization Control
- Avoid spinning CPU-burning loops (e.g.,
for (let i = 0; i < 100000; i++)) to
trigger JIT tier-up, unless the bug specifically relates to loop-scaling or
profiling feedback.
- Use V8 intrinsic compiler controls to force deterministic optimization:
%PrepareFunctionForOptimization(foo);
%OptimizeFunctionOnNextCall(foo); or %OptimizeMaglevOnNextCall(foo);
- Make sure
--allow-natives-syntax is included in the Flags header when using
percent (%) intrinsics.
1---2name: v8-regression-testing3description: Use this skill when designing, writing, or minimizing regression tests (e.g., mjsunit) for V8 bug fixes4---56# Skill: V8 Regression Testing78Use this skill to design, write, minimize, and refine high-quality regression9tests in the V8 codebase (typically under `test/mjsunit/` or its10component-specific subfolders like `compiler/`, `turboshaft/`, `maglev/`, etc.).1112## Core Mandates1314- **Mandatory Reproducer Rule**: Unless explicitly specified otherwise by the15 user, **every single bug fix in V8 MUST be uploaded with a working reproducer16 (regression test)**. Uploading just the fix alone is NEVER acceptable.17- **Semantic Quality**: V8 regression tests must be **semantic18 (root-cause-driven)** rather than just **syntactic (fuzzer-driven)**. A19 high-quality test case is a clear, human-readable, and robust demonstration of20 the specific logical bug or compiler invariant under test, rather than a21 fragile, black-box crash script.22- **CRITICAL REPRODUCER CONSTRAINT**: **NEVER** provide a reproducer that does23 not actually trigger the issue to the user without **explicitly** telling them24 about it.2526______________________________________________________________________2728## The "From-Scratch" Reproducer Philosophy2930When constructing a reproducer for a bug, **always prefer writing it from31scratch based on a deep conceptual understanding of the issue, rather than32starting from a fuzzer-generated crash script and trying to minimize it.**3334### Why the Post-Hoc Fuzzer Minimization Trap Fails:35361. **The Black Box Dilemma**: Starting with a fuzzer-generated block of code37 means you are constantly guessing which strange construct is necessary. You38 are highly likely to retain useless nesting (`__wrapTC`), redundant39 try-catches, and magic numbers because you are afraid to break the fragile40 reproduction state.412. **Fragility**: Fuzzer scripts often rely on incidental timings, engine42 heuristics, or side effects to trigger a crash. If optimization heuristics43 change slightly in a future V8 release, the fuzzer reproducer will silently44 break and stop testing the intended bug path.4546### The "From-Scratch" Synthesis Approach:47481. **Identify the Root Cause**: Analyze the compiler phase graph, the crash49 backtrace, and the failing `DCHECK` or invariant to understand the exact50 abstract state machine failure (e.g., "an incorrect type representation51 selection, a specific Map transition mismatch, or a loop-carried52 side-effect").532. **Begin with a Blank File**: Open an empty editor. Do not copy-paste the54 fuzzer script.553. **Target the State Directly**: Write the absolute minimum JavaScript56 necessary to construct that logical state:57 - Use clean, structured object definitions and allocations to establish58 target internal structures (such as distinct Maps or Shapes).59 - Control the IC feedback vector cleanly by warming it up with specific60 types.61 - Explicitly trigger the compiler pass with intrinsic calls (e.g.,62 `%PrepareFunctionForOptimization`, `%OptimizeFunctionOnNextCall`).634. **Result**: You produce a clean, robust, perfectly commented, 15-line64 reproducer that explicitly tests the underlying logic. It is65 self-documenting, easy to maintain, and impervious to unrelated engine66 optimization tweaks.6768______________________________________________________________________6970## Debugging a Failing Reproducer (The Scientific Loop)7172When writing a reproducer from scratch, it is common for the first attempt to73**not** trigger the crash. When this happens, do not give up or fall back to74blindly copying the fuzzer script. Instead, treat the reproducer's failure as a75debugging task and enter a scientific feedback loop.7677### The Feedback Loop:78791. **Trace the Execution**: Use V8's extensive set of tracing flags to inspect80 the JIT/engine pipeline (eg, `--trace-turbo-graph`, `--trace-ic`,81 `--trace-deopt`...), and insert custom instruction (eg, using `PrintF`) as82 needed if the existing flags are too coarse.832. **Detect the Detour**: Identify exactly where the execution path diverged84 from your expectation. For instance:85 - *Did the function deoptimize prematurely due to an unexpected type feedback86 change?*87 - *Did load elimination optimize away a read that was supposed to trigger a88 side effect?*89 - *Did a map transition occur too early?*903. **Analyze the Cause**: Figure out *why* V8 chose that detour.91 - What invariant, heuristic, or state in your JavaScript code caused the92 compiler to take a different optimization path?934. **Heal and Steering**: Modify your JavaScript reproducer to block the detour94 and steer the compiler back onto the target path (e.g., altering map setups,95 introducing fake side effects to prevent load-elimination, or adjusting96 argument warming patterns).9798[!IMPORTANT] **The Ultimate Conceptual Checkpoint**: If you repeatedly fail to99construct a clean, working reproducer from scratch, **this almost always means100your conceptual understanding of the bug is either incorrect or too shallow.**101Do not waste days trying to brute-force random JavaScript tweaks. When this102checkpoint is hit, you must **go back to the drawing board**, challenge what you103think you know about the bug, and deepen your understanding.104105______________________________________________________________________106107## Synctatic Principles of a Good Regression Test108109### 1. Minimal & "Leaf" Compiler Flags110111- **Strict Flag Minimization**: Every flag included in `// Flags: ...` must be112 thoroughly understood and strictly necessary to reproduce the issue. Remove113 all useless or redundant flags.114- **Prefer "Leaf" Flags over "Compound" Flags**:115 - **Bad**: Using `--jit-fuzzing` or `--fuzzing` because it implies a massive,116 ever-changing bundle of underlying behaviors.117 - **Good**: Identifying and specifying the exact leaf flags responsible (e.g.,118 `--no-lazy-feedback-allocation`, `--homomorphic-ic`,119 `--stress-concurrent-inlining-attach-code`).120121### 2. Human-Friendly Naming122123Avoid default fuzzer variable and function names (`__f_0`, `__v_10`, `v17`,124`a6`, `a7`).125126- Use **descriptive names** for semantic concepts (`large_arr`, `global_var`,127 `testGenerator`).128- Use **clean, standard placeholders** for simple generic logic (`foo`, `bar`,129 `obj`, `f`, `x`, `y`).130- Maintain cognitive load at an absolute minimum.131132### 3. Absolute Minimization of Code & Boilerplate133134- Remove every parameter, variable, block, or helper that does not directly135 contribute to reproducing the bug.136- Never leave unused parameters in function signatures, unless absolutely137 required to reproduce the issue.138- Do not include catch-all blocks (`catch (e) {}`) or dummy wrapper functions139 (`__wrapTC`) unless they are semantically required to trigger the crash path.140141### 4. Deterministic Optimization Control142143- Avoid spinning CPU-burning loops (e.g., `for (let i = 0; i < 100000; i++)`) to144 trigger JIT tier-up, unless the bug specifically relates to loop-scaling or145 profiling feedback.146- Use V8 intrinsic compiler controls to force deterministic optimization:147 - `%PrepareFunctionForOptimization(foo);`148 - `%OptimizeFunctionOnNextCall(foo);` or `%OptimizeMaglevOnNextCall(foo);`149- Make sure `--allow-natives-syntax` is included in the Flags header when using150 percent (`%`) intrinsics.