arXiv:2606.26977v1 [cs.SE] 25 Jun 2026
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler SHANGTONG CAO, Beijing University of Posts and Telecommunications, China TIANLEI SONG, Harbin Institute of Technology, Shenzhen, China QIUPING YI, Beijing University of Posts and Telecommunications, China TIANYU CHEN, Peking University, China GUOAI XU, Harbin Institute of Technology, Shenzhen, China NINGYU HE, The Hong Kong Polytechnic University, China HAOYU WANG*, Huazhong University of Science and Technology, China Modern compilers are complex software systems that must correctly translate high-level programming languages into machine code across multiple architectures. Cranelift, a fast and modern compiler backend originally developed for WebAssembly and recently adopted as an experimental backend for Rust, has gained increasing importance due to its superior compilation speed compared to LLVM and comprehensive multiarchitecture support, including x86-64, AArch64, s390x, and RISCV64. However, despite decades of development in compiler testing, testing Cranelift still presents unique challenges, including (1) constructing valid IR under the strict enforcement of SSA form, (2) generating sequences with sufficient computational density to stress backend components, and (3) balancing broad backend coverage with efficient root cause analysis across heterogeneous architectures. To address these challenges, we propose CLIR, a differential testing framework that integrates a syntaxpreserving hierarchical generation strategy to guarantee SSA validity, a liveness-guided instruction refinement mechanism to maximize computational density, and a diagnosis-guided cross-architecture adaptation scheme to facilitate efficient root cause analysis across heterogeneous backends. Our comprehensive evaluation demonstrates that CLIR significantly outperforms existing state-of-the-art baselines, detecting 8×, 24×, and 8× more unique bugs than cranelift-fuzzgen, wasm-smith, and WASMaker, respectively, while RustSmith uncovered no bugs. Consequently, within 72 hours of testing, CLIR discovered 24 bugs spanning all target architectures, with 21 confirmed and 9 fixed.
1
Introduction
As sophisticated translation systems, modern compilers bridge the gap between high-level programming languages and diverse machine architectures. Despite decades of development and testing, even well-established compilers continue to harbor numerous bugs. For instance, LLVM [24], one of the most widely used compiler infrastructures, has accumulated over 26,000 reported issues throughout its development history, with more than 3,000 new bugs discovered annually [25]. GCC has accumulated tens of thousands of reported bugs over its lifetime [21, 35], demonstrating that compiler correctness remains a persistent challenge for mature compiler systems. The goal of Cranelift is to be a fast, safe, and modern compiler, originally developed as the JIT and AOT backend for the Wasmtime WebAssembly runtime [16], while currently it has been adopted as an experimental backend for the Rust compiler due to its significantly faster compilation speed compared to LLVM [39]. With Rust becoming a popular choice for developing low-level systems [9, 11, 30, 32], the correctness of Cranelift becomes the role under the spotlight. Consequently, the growing adoption of Cranelift in production environments, coupled with its relative novelty compared to GCC and LLVM, makes comprehensive testing for it an inevitable topic. Authors’ Contact Information: Shangtong Cao, Beijing University of Posts and Telecommunications, China; Tianlei Song, Harbin Institute of Technology, Shenzhen, China; Qiuping Yi, Beijing University of Posts and Telecommunications, China; Tianyu Chen, Peking University, China; Guoai Xu, Harbin Institute of Technology, Shenzhen, China; Ningyu He, The Hong Kong Polytechnic University, China; Haoyu Wang*, Huazhong University of Science and Technology, China. , Vol. 1, No. 1, Article . Publication date: June 2026.
2
Trovato et al.
One straightforward approach to testing Cranelift is to leverage existing ecosystem tools, such as RustSmith [34] or wasm-smith [8], to generate high-level source code and compile it down to IR. However, this top-down strategy is insufficient due to the translation gap, i.e., the frontend compilation process inherently normalizes and sanitizes inputs, filtering out many valid but edgecase IR patterns [31]. Consequently, a wide range of legal IR instruction sequences, which do not correspond to standard high-level source constructs, remain unreachable and untested. Furthermore, as a general-purpose backend intended to support diverse frontends, Cranelift must be robust against arbitrary valid IR inputs, not merely those produced by specific languages. The necessity of direct IR-level testing is explicitly validated by the Cranelift community through the maintenance of cranelift-fuzzgen [15], an official tool dedicated to raw IR generation. However, performing testing against Cranelift is still challenging, where three major challenges must be addressed. First, constructing syntactically valid IR is non-trivial due to Cranelift’s strict SSA constraints. The generator must ensure that every variable use is dominated by its definition across complex control flows to avoid immediate rejection before the compilation commences. Second, generated IR must exhibit high computational complexity to be effective. Naively generated code often lacks deep data dependencies and is susceptible to trivial simplification. To this end, the challenge lies in systematically synthesizing instructions with sufficient computational density and interconnectivity to stress critical backend components. Third, adapting to heterogeneous backends introduces a dilemma between compatibility and coverage. The framework must support diverse hardware architectural constraints while providing automated mechanisms to efficiently pinpoint the root causes of bugs amid massive failure reports. This work. In this work, we propose CLIR, a comprehensive Cranelift compiler testing framework designed to bridge the gap between syntactic validity and testing utility. To achieve this, we design a Syntax-Preserving Hierarchical SSA-form IR Generation strategy that constructs test cases in a topdown manner. This approach extracts basic blocks from real-world Rust and WebAssembly programs and assembles them via a dominator-driven algorithm to strictly enforce SSA compliance. Based upon this, CLIR further applies a Liveness-Guided Instruction Refinement mechanism to maximize testing utility. By systematically anchoring data dependency chains to observable behaviors and coupling block parameters, this strategy prevents trivial simplification and ensures the generated code exhibits high computational complexity. To efficiently investigate inconsistent behaviors across heterogeneous backends, CLIR incorporates a Diagnosis-Guided Cross-Architecture Adaptation strategy. This component utilizes dual-mode profiling to tailor test cases for specific architectures and employs hierarchical static instrumentation to pinpoint root causes at the instruction level. We conduct extensive experiments to show the superiority of CLIR over state-of-the-art techniques in terms of both effectiveness and coverage. Specifically, by applying CLIR to four target architectures, i.e., x86-64, AArch64, RISCV64, and s390x, over a 72-hour period, we identified 24 bugs, which is 8×, 24×, and 8× more than those detected by cranelift-fuzzgen, wasm-smith, and WASMaker, respectively, while the high-level fuzzer RustSmith failed to detect any bugs. Moreover, CLIR achieves 1.2× higher code coverage across all supported architectures. With our timely disclosure, 21 bugs have been confirmed by Cranelift developers, and 9 of them have been fixed by the time of this writing. Our contribution. We summarize the major contributions of this work as follows: • Cranelift IR Generator. We propose a robust Cranelift IR generator that combines syntaxpreserving hierarchical generation with liveness-guided instruction refinement. This approach efficiently produces syntactically valid and computationally complex Cranelift IRs by strictly enforcing SSA constraints while ensuring global data dependencies to maximize testing utility. , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
3
• Impactful Framework. We implement CLIR, a Cranelift compiler testing framework featuring diagnosis-guided adaptation. By integrating signature-guided failure clustering and feedbackdriven profile adaptation, it efficiently handles heterogeneous backends, automating root cause localization and reducing manual triage efforts. • Comprehensive Evaluation. We conduct a comprehensive evaluation demonstrating that CLIR significantly outperforms state-of-the-art baselines in both bug-finding capability and generated IR complexity. Within 72 hours, it uncovered 24 unique bugs, with 21 confirmed and 9 fixed, proving its superior effectiveness in exposing deep backend flaws. • Open Source. We open source CLIR and our dataset at link to facilitate future research in Cranelift compiler testing. 2
Background
In this section, we illustrate necessary background about Cranelift and the corresponding Cranelift intermediate representation (IR). 2.1
Cranelift Compiler
Cranelift is a code generator designed to be a fast, secure, and relatively simple compiler backend, implemented entirely in Rust. Unlike traditional backends, like LLVM, which prioritize peak execution performance often at the cost of compilation time, Cranelift focuses on achieving high compilation speed and low memory footprint, making it particularly suitable for Just-In-Time (JIT) compilation scenarios [2]. It accepts a target-independent Intermediate Representation (Cranelift IR) as input and translates it into executable machine code for various architectures, including x86-64, AArch64, s390x [5], and RISCV64 [4]. While originally developed as the JIT and AOT engine for the Wasmtime virtual machine [38] to execute WebAssembly, its general-purpose design has led to its adoption as an experimental backend for the Rust compiler [33], offering significantly faster debug build times. The core transformation from platform-independent IR to target-specific machine instructions, i.e., lowering, is driven by a domain-specific language called ISLE (Instruction Selection Lowering Expressions) [17]. Instead of hard-coding instruction selection logic, Cranelift defines these rules declaratively within .isle files for each architecture. These files consist of term-rewriting rules that explicitly map patterns in the Cranelift IR to their corresponding machine instructions. Consequently, the .isle files serve as the central repository for backend-specific lowering logic, making them a critical component in ensuring the correctness of code generation. 2.2
Cranelift IR Instance
Cranelift uses a strongly typed intermediate representation in Static Single-assignment (SSA) form [6], where each value is assigned exactly once and is immutable thereafter. Fig. 1 illustrates a concrete example of a C function compiled to Cranelift. Syntactically, a function definition begins with a signature (L11 ), specifying the function name (%sum), input parameter types, and return types. The body consists of a sequence of Extended Basic Blocks (EBBs), starting with the entry block (block0). Several structural distinctions exist between Cranelift IR and other ones like LLVM: Function Preamble. Before the first basic block, Cranelift employs a function preamble to declare various entities required by the function body, like stack slots (L2). Unlike LLVM, which typically uses inline instructions like alloca for allocation, Cranelift requires these entities to be defined upfront. In Fig. 1, the preamble explicitly defines a stack slot, which is subsequently accessed via 1 L1 refers to the first line, we adopt such notations in the following.
, Vol. 1, No. 1, Article . Publication date: June 2026.
4
Trovato et al.
int sum(int *array, int count){ int result = 0; for (int i = 0; i < count; i++) result += array[i]; if (result > 100){ return result; } else { return 0; } }
1 function %sum(i32, i32) -> i32 { ss0 = explicit_slot 4 2 3 block0(v0: i32, v1: i32): v2 = iconst.i32 0 4 stack_store v2, ss0 5 v3 = iconst.i32 0 6 jump block1(v3) 7 8 block1(v4: i32): v5 = imul_imm v4, 4 9 v6 = iadd v0, v5 10 v7 = load.i32 v6 11 v8 = stack_load.i32 ss0 12
(a) C source code
13 v9 = iadd v7, v8 stack_store v9, ss0 14 15 v10 = iadd_imm v4, 1 16 v11 = icmp slt v10, v1 brif v11, block1(v10), block2 17 18 block2: 19 v12 = stack_load.i32 ss0 20 v13 = icmp_imm sgt v12, 100 21 brif v13, block3(v12), block3(v2) 22 block3(v14: i32): 23 return v14 24 }
(b) Compiled Cranelift IR
Fig. 1. A C code snippet and the corresponding Cranelift IR program.
specific instructions such as stack_store (L5) and stack_load (L12), ensuring precise control over resource usage. Block Parameters. The most significant feature of Cranelift IR is its handling of SSA construction. Instead of using Phi nodes [3] to merge values from different control flow predecessors, Cranelift employs block parameters. Each basic block can explicitly declare typed parameters, acting similarly to function arguments. For instance, the loop header block1 declares a parameter v4: i32 (L8). The value of v4 is determined by the arguments passed during the jump to this block: • From the entry block (L7), the instruction jump block1(v3) passes the initial value v3 to v4. • From the loop back-edge (L17), the instruction brif passes the incremented value v10 to v4. This design unifies control flow transfer with data flow, simplifying the IR structure and making it easier to analyze and generate programmatically. Instruction Syntax. Instructions in Cranelift follow a three-address code format, typically consisting of an opcode, type suffixes, and operands (e.g., v5 = imul_imm v4, 4 in L9). Type suffixes (like .i32 in iconst.i32) are mandatory for polymorphic instructions to ensure strict type safety. Terminator instructions, such as brif (conditional branch) and jump (unconditional branch), are strictly required at the end of every block to explicitly define the control flow graph. 3
Challenges
Compiler testing is challenging. Despite being a well-studied topic, modern compilers like GCC and LLVM continue to exhibit numerous bugs, with historical reports showing they have accumulated tens of thousands of issues throughout their development [21, 25]. While traditional compilers have been extensively tested, newer compiler infrastructures, such as Cranelift, remain less explored. Generally, comprehensive compiler testing relies on two vital components: the seed (test case) and the oracle. Taking advantage of differential testing allows us to bypass the oracle problem, shifting the burden entirely to the quality of the generated seeds. However, testing Cranelift presents unique hurdles due to its specific IR design and multi-backend nature. We summarize the three major challenges as follows: Challenge #1: Constructing syntactically valid IR under strict SSA constraints. The primary prerequisite for backend testing is ensuring syntactic validity, which, however, is inherently difficult because the generator must satisfy rigorous dominance and type constraints while maintaining the structural diversity required for effective fuzzing. Cranelift IR enforces a strongly typed SSA form (§2.2), introducing rigorous dependencies where every variable usage must be dominated by its definition. In the presence of complex control flow containing loops and branches, maintaining these dominance invariants is non-trivial. While directly compiling high-level languages (e.g., Rust or Wasm) to obtain Cranelift IR ensures validity, such approaches suffer from frontend shielding (§1), which canonicalizes input and obscures backend-specific bugs. Consequently, direct IR generation is required, yet existing methods falter, i.e., naive fuzzing [7] lacks structural awareness, leading to , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
5
compile
Skeleton Generator ⁃ Inter-Procedural ⁃ Intra-Procedural
Corpus
IR Initiator
skeleton
⁃ Dominator-Driven SSA Instantiation ⁃ Liveness-Guided Instruction Refinement
Profile
Differential Testing Cranelift IR instrument
x86-64, AArch64, RISCV64, s390x
report
update
Fig. 2. The workflow of CLIR.
frequent dominance violations, while grammar-based generation [40] relies on computationally expensive constraint solving to track global variable scoping. Challenge #2: Generating Computationally Complex IR. Merely generating syntactically valid IR is insufficient, as effective test cases must also exhibit high computational complexity to stress critical compiler backend components like register allocation and instruction scheduling. Prior works like IRFuzzer [31] and Alive-mutate [20] rely on stochastic generation or local mutation. However, these approaches lack guidance to construct deep data dependencies. Also, their generation is hampered by uniform randomness, while mutation is bounded by the original seed complexity. Therefore, the challenge lies in systematically constructing IR that possesses sufficient computational density and interconnectivity to avoid trivial simplification, without compromising the strict correctness guarantees established in Challenge #1. Challenge #3: Adapting to heterogeneous backends and efficient bug diagnosis. Cranelift is designed as a cross-platform code generator supporting diverse architectures (e.g., x86-64, AArch64, RISCV64, s390x). This introduces a dilemma between compatibility and coverage. On one hand, different backends have distinct constraints, like different instruction sets (ISAs), specific operand types, and unique calling conventions. A generator must be architecture-aware to avoid producing IR that is valid generally but unsupported on a specific target, while simply generating compatible intersection sets limits the ability to test backend-specific features. On the other hand, in the context of differential testing, a single underlying compiler bug often manifests as thousands of failing test cases across different seeds. Manually triaging these failures is impractical. Thus, the final challenge is to design a framework that can flexibly adapt to heterogeneous backend constraints while providing automated mechanisms to pinpoint bug root causes, ensuring testing efficiency. To tackle Challenge #1, we propose a syntax-preserving hierarchical SSA-form IR generation approach that strictly adheres to SSA constraints and type consistency (§4.3). To address Challenge #2, we introduce a liveness-guided instruction refinement strategy that maximizes computational complexity, thereby ensuring the generated IR possesses high testing utility for stressing backend components (§4.4). As for Challenge #3, we design a diagnosis-guided cross-architecture adaptation mechanism to guide computing resources on identifying bugs with different root causes (§4.5). 4 Methodology 4.1 Overview As shown in Fig 2, the workflow of CLIR can be divided into three modules: skeleton generator, IR Initiator, and Differential Testing. First, the skeleton generator establishes the structural foundation of the test case by constructing an inter-procedural call graph and recursively nesting atomic control structures to form complex intra-procedural CFGs, resulting in a function skeleton. Next, the IR initiator populates this skeleton to produce valid Cranelift IR. It draws from a Corpus of real-world Cranelift instruction snippets (see §4.2) and utilizes a dominator-driven algorithm to ensure SSA compliance and type safety (see §4.3), while also applying instruction refinement to , Vol. 1, No. 1, Article . Publication date: June 2026.
BB1
BB1
BB2
While
If-else
If-else
BB4
BB5
Switch BB1
BB2
BB3
BB3
6
BB1
While
...
BB2
BB6
BB3
BBn BB3
BB3
If-else
Trovato et al.
Switch-case Entry
Entry
Entry
BB1
Switch
If-else BB1 Entry
BB1
...
BB2
Exit
BB3
BBn Exit
Exit
Sequential
If-Else
BB1
While
...
BB2
BB1
BB2
While
If-else BB4
BB5
Merge Exit
While-Loop
BB1
BBn
Switch-Case
(a) Atomic structures
BB3
BB3
BB6 BB3
(b) Construction example
If-e
Fig. 3. Overview of Intra-Procedural CFG Construction. (a) The four atomic structures used as building blocks; (b) An example of recursively constructing a CFG using these structures.
enhance testing utility (see §4.4). Finally, the differential testing module executes the generated IR across multiple architectures, i.e., x86-64, AArch64, RISCV64, and s390x. This phase incorporates an instrumentation loop for root cause localization and a feedback-driven adaptation that refines the architecture profile to guide future generation (see §4.5). 4.2
Cranelift Corpus Preparation
We leverage existing representative Rust and WebAssembly programs from crates.io [18] and WasmBench benchmark suite [37] to prepare the corpus. First, we compile collected Rust and WebAssembly programs into Cranelift IR. Then, we extract all basic blocks and store them as reusable units in the corpus. Each basic block is treated as an individual code snippet that captures realistic instruction patterns and operand usages commonly found in real-world programs. We underline that this corpus serves as the cornerstone for the later stages. Its rich and diverse semantics significantly increase the likelihood of triggering compiler bugs. Such corpus-based synthesizing methods are widely adopted in related testing work [12, 22, 23, 42]. 4.3
Syntax-Preserving Hierarchical SSA-form IR Generation
CLIR adopts a hierarchical, top-down strategy to construct IR test cases, ensuring structural validity from the global scope down to individual instructions. The generation process operates across three distinct layers as follows. First, it establishes the inter-procedural skeleton by defining function signatures and their invocation relationships (§4.3.1). Second, within each function, it constructs a complex Control Flow Graph (CFG) by recursively nesting atomic control structures (§4.3.2). Finally, it initiates these basic blocks with concrete instructions using a dominator-driven algorithm to satisfy strict SSA Def-before-Use constraints (§4.3.3). 4.3.1 Inter-Procedural Skeleton Generation. This stage aims to construct a diverse function call graph. Starting with a designated entry function, the generator recursively declares and invokes a set of sub-functions. To simulate the complexity of real-world software, we enforce diversity in function signatures, i.e., randomizing parameters and return values, which can increase the likelihood of exposing backend bugs related to stack frame management and parameter passing. Furthermore, we incorporate a wide range of calling mechanisms. Cranelift supports multiple invocation styles including direct calls, indirect calls (function pointers), and tail calls (e.g., Call, CallIndirect, ReturnCall). By integrating these variations into the generation process, CLIR ensures comprehensive coverage of the compiler’s function transition logic. 4.3.2 Intra-Procedural CFG Construction. Within each function, constructing a non-trivial CFG is critical as core compiler components, such as dominator tree construction, register allocation, and loop analysis, rely heavily on the topology of the CFG. Advanced optimizations, including , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
7
Algorithm 1 Dominator-Driven SSA Instantiation. Input: 𝑠 - the block-level skeleton; 𝑐𝑝𝑠 - a corpus of Cranelift IR instruction snippets (see §4.2) 1: 𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝 ← ∅ 2: 𝑑𝑜𝑚𝑀𝑎𝑝 ← getDominance(𝑠) 3: 𝑒𝑛𝑡𝑟𝑦𝐵𝑙𝑜𝑐𝑘 ← getEntry(𝑠) 4: initBlockParam(𝑒𝑛𝑡𝑟𝑦𝐵𝑙𝑜𝑐𝑘) 5: setDefUse(𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝, 𝑒𝑛𝑡𝑟𝑦𝐵𝑙𝑜𝑐𝑘) 6: for 𝑏𝑙𝑜𝑐𝑘 in dfs(𝑠) do 7: 𝑑𝑜𝑚𝑖𝑛𝑎𝑡𝑜𝑟𝑠 ← 𝑑𝑜𝑚𝑀𝑎𝑝 [𝑏𝑙𝑜𝑐𝑘] 8: 𝑖𝑛𝑠𝑡𝑟𝑠 ← sample(𝑐𝑝𝑠) 9: for 𝑖𝑛𝑠𝑡𝑟 in 𝑖𝑛𝑠𝑡𝑟𝑠 do 10: if random() > 𝜎 then 11: 𝑖𝑛𝑠𝑡𝑟 ← mutateOp(𝑖𝑛𝑠𝑡𝑟 ) 12: fillRef (𝑑𝑜𝑚𝑖𝑛𝑎𝑡𝑜𝑟𝑠, 𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝, 𝑖𝑛𝑠𝑡𝑟 ) ⊲ §4.4.2 13: setDefUse(𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝, 𝑏𝑙𝑜𝑐𝑘)
loop-invariant code motion and branch prediction, are only triggered by specific control flow patterns [29]. Obviously, linear or overly simple flows fail to exercise these passes, leaving potential logic errors undetected. To this end, we propose a recursive CFG-substructure substitution method to build complex yet valid CFGs within each function. Specifically, we define four atomic control flow structures: Sequential, If-Else, While-Loop, and Switch-Case, as illustrated in Fig. 3a. A key property of these atomic structures is that they maintain a Single-Entry-Single-Exit structure, i.e., the indegree of the entry block and the outdegree of the exit block are always one. This property allows us to treat any atomic structure as functionally equivalent to a single basic block, allowing us to iteratively replace a basic block within an existing structure with a more complex atomic structure. For instance, as shown in Fig. 3b, a block within a Sequential structure can be replaced by a While-Loop structure, which can subsequently be replaced by an If-Else structure. The substitution process preserves graph integrity by 1) redirecting incoming edges from the original block’s predecessors to the new structure’s entry, and 2) connecting the new structure’s exit to the original block’s successors. This recursive nesting allows CLIR to generate arbitrarily complex control flows within functions, effectively increasing the structural intricacy while ensuring logical correctness. 4.3.3 Dominator-Driven SSA Instantiation. Following the constructed control flow skeleton, the final phase involves populating the basic blocks with concrete IR instructions. To address Challenge #1, we propose a dominator-based instruction generation method (see Algorithm 1), which utilizes the dominance analysis to maintain a valid pool of live variables for operand selection, ensuring syntactic correctness by design. Specifically, as outlined in Algorithm 1, the process accepts the block-level function skeleton generated by §4.3.2 and a corpus of Cranelift IR instruction snippets (detailed in §4.2) as inputs. First, we initialize 𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝 to maintain the def-use relationships of variables, domination relationships among blocks 𝑑𝑜𝑚𝑀𝑎𝑝 extracted by the skeleton, and the entry block (L1–L3). At L4, for the entry block, we explicitly add block parameters (like L3 and L8 in Fig. 1) to create root nodes for all operand types (e.g., i32 and i8x16) in case the following instructions need a reference to such data types. L5 updates 𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝 to record that these variables are defined in the entry block. We then traverse the skeleton in a DFS manner (L6). For each block, we first extract its control-flow dominators (L7), and sample an instruction snippet from the corpus to try to initiate this block (L8). , Vol. 1, No. 1, Article . Publication date: June 2026.
8
Trovato et al.
Instead of directly copy-pasting an instruction, to improve the instruction diversity, we introduce a mutation operation to replace the opcode with a randomly selected alternative from the instruction set (L10 and L11). As operands may be required by an instruction, beyond considering the operand data type, we also consider if a reference really points to an existing variable. Therefore, fillRef at L12 only takes the variables in 𝑑𝑜𝑚𝑖𝑛𝑎𝑡𝑜𝑟𝑠 into consideration to avoid breaking the def-use constraints of SSA form (see more details in §4.4.2). Finally, the newly defined variable and the referenced one will be recorded in 𝑑𝑒 𝑓 𝑈 𝑠𝑒𝑀𝑎𝑝 (L13). Such an iteration continues until all basic blocks are initiated. 4.4
Liveness-Guided Instruction Refinement
While §4.3 ensures syntactic validity, syntactic validity alone does not equate to testing utility. A syntactically correct but simple program often fails to trigger deep-seated bugs in the compiling pipeline. To address this, our goal is to transform the valid skeleton into a test case with high testing utility characterized by deep dependency chains and tight inter-block coupling. We achieve this by integrating three liveness-guided strategies into the refinement process: (1) enforcing global data flow complexity via block parameter coupling (§4.4.1); (2) constructing deep Def-Use chains via priority-based operand selection (§4.4.2); and (3) anchoring dependency chains to observable behaviors via sink synthesis (§4.4.3). 4.4.1 Liveness-Aware Control Flow Merge. In Cranelift IR, control flow merge points are handled using block parameters rather than Phi nodes, presenting a unique opportunity to enforce liveness across basic block boundaries. If a merge block (i.e., block3 in Fig. 4) defines no parameters, the variables computed in its predecessors (block1, block2) are likely to be identified as unused at the end of their respective blocks, making them prime targets for optimization. To prevent this, we employ a type-matching strategy to construct block parameters, as illustrated in Fig. 4. When configuring a merge block, we analyze the live-out variable sets of all its predecessors. For instance, both block1 and block2 possess available i32 and i64 variables (v17 and v16 in block1 and v28 and v30 in block3). Based on this availability, we define block3 to accept parameters of these types (v41: i32, v42: i64). Consequently, we populate the terminal jump instructions of the predecessors with specific arguments: block1 passes (v17, v16) and block2 passes (v28, v30). This explicitly binds the computations in the predecessor blocks to the execution of the successor, forcing the compiler to retain the instruction chains that produced these values. 4.4.2 Priority-Based Operand Selection. Within individual basic blocks, preventing generated instructions from being optimized away requires ensuring that their defined variables are consumed by subsequent operations. To this end, when choosing operands for new instructions (L12 in Algorithm 1), we consider the following three variable categories. • Function Call Results. Instructions such as function calls are computationally expensive and critical to test. Thus, to prevent “dead call sites”, we intentionally keep a pool for function call results to prevent a function from being called but its results from being ignored. • Block Parameters. In blocks that accept parameters (like block3 in Fig. 4), we maintain a pool for such parameters, which ensures that they are not merely syntactic placeholders but functional components of the block’s logic. • Defined Variables. Except for the above two categories, we also take ordinarily defined variables into consideration, like v6 in v6 = iadd v0, v5. This mimics realistic programming patterns and reduces the window for a variable to be considered “dead” between its definition and usage. When an instruction requires one or more operands, the generator randomly selects one of the above pools and then chooses the most recently defined variable whose type matches the required , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
block1: ... v16 = load.i64 v10 v17 = uextend.i32 v8 jump block3(v17, v16)
9
block2: ... v28 = urem.i32 v2, v20 v30 = udiv.i64 v24, v21 jump block3(v28, v30)
block3(v41: i32, v42: i64): v43 = iconst.i32 0 stack_store v1, ss0 ...
Fig. 4. Constructing block parameters for control flow merge points.
Profile Profile
Instruction Set Profile Instruction Set Call Convention Instruction Call Convention Operand Type Set Call Convention …Operand Type …Operand Type …
§ 4.5.1
compatible
Mode Selection
Cranelift IR
x86-64 AArch64 RISCV64 s390x
IR Generation § 4.5.2 Func/Block/Instr-level instrumentation
target-specific
Cranelift Compiler
§ 4.5.3 feedback & adaptation
§ 4.5.4
Minimal Reproducible Example Construction
Fig. 5. The workflow of the diagnosis-guided cross-architecture adaptation strategy.
operand type. Specifically, among all type-compatible candidates in the selected pool, we select the one with the largest ID, as it is typically the one that has just been defined. This strategy keeps the def-use distance short and forms a tighter def-use chain within the basic block, thereby reducing the chance that the generated instruction is regarded as dead code and eliminated by compiler optimizations. This operand selection process is repeated until all variable operands required by the current instruction have been filled. 4.4.3 Sink-Anchoring for Transitive Liveness. Improving connectivity among variables in blocks should also be guaranteed by not discarding the final results. Therefore, in the final pass, we identify all leaf nodes of the dependency graph, i.e., variables that are defined but never used, or used only in non-escaping computations. At the function’s exit blocks, we deliberately synthesize strictly typed return instructions or memory store operations that consume these leaf variables. This explicitly anchors the refined dependency chains to observable program behaviors, forcing the compiler to preserve the entire upstream computation logic. 4.5
Diagnosis-Guided Cross-Architecture Adaptation
As highlighted in Challenge #3, differential testing of compilers faces significant hurdles due to architectural diversity. Variations in supported instruction sets and calling conventions make it difficult to utilize a single IR test case comprehensively across multiple backends. Furthermore, in large-scale testing campaigns, efficiency is often hampered by redundant inconsistencies triggered by the same underlying root cause. To address these challenges, we propose a diagnosis-guided cross-architecture adaptation strategy that dynamically tailors generated IR test cases according to target environments while actively filtering out known issues. , Vol. 1, No. 1, Article . Publication date: June 2026.
10
Trovato et al.
4.5.1 Dual-Mode Testing Configuration. To balance cross-platform compatibility with architecturespecific depth, we design different profiles for different architectures. Each profile explicitly defines the supported instruction set, operand type constraints, and calling conventions for a specific backend. As shown in Fig. 5, this strategy operates in two distinct modes. In compatible mode, the generator computes the intersection of features supported by all active target profiles. This ensures that the generated IR test cases are portable and syntactically valid across all backends, enabling broad differential testing. In contrast, target-specific mode enables the generator to fully exploit the capabilities of a single backend by generating test cases tailored to its complete feature set (e.g., using some specific SIMD instructions only available on x86-64). This configuration improves both the breadth of cross-architecture testing and the depth of intra-architecture feature coverage. 4.5.2 Instrumentation-Based Root Cause Diagnosis. A single bug often manifests across thousands of generated test cases, resulting in a deluge of failure reports. Manually analyzing these reports is impractical. To address this, we propose a two-phase diagnosis method designed to assist in root cause localization. Phase I: Signature-guided Failure Clustering. To systematically manage the volume of inconsistencies, we employ a clustering strategy based on failure symptoms. We define the failure behavioral signature S as ⟨R, K⟩, where R represents the process exit status (e.g., specific crash signals) and K denotes a canonical set of diagnostic patterns extracted from the error stream (e.g., panic strings or assertion failures). Cases sharing an identical S are grouped into a single cluster, under the assumption that they stem from the same root cause. Within each cluster, instead of random selection, we prioritize the test case with the minimum IR instruction count and the simplest control flow density. We designate this most concise instance as the cluster’s representative, which significantly reduces the analysis effort required for subsequent root cause diagnosis. Phase II: Hierarchical Fault Localization. For the identified representative, we devise a topdown fault localization technique based on static instrumentation, i.e., narrowing the search scope from the coarse-grained function level down to the fine-grained instruction level. Specifically, we perform the localization in the following two distinct stages: • Coarse-grained Localization (Function & Block). To efficiently narrow down the fault scope, we employ a top-down pruning strategy utilizing execution barriers (e.g., early-return instructions). The core mechanism involves generating program variants by inserting barriers at specific execution points: immediately following function call sites (for function-level localization) and at the tail of basic blocks (for block-level localization). This allows us to truncate execution and isolate the fault via differential observation. Specifically, if the inconsistency remains observable despite the early return inserted after a call or block, the fault must have occurred within the executed path (i.e., inside the called function or the current block sequence). In contrast, if the inconsistency disappears, the fault lies in the code subsequent to the barrier. We apply this logic hierarchically: first identifying the specific function containing the bug, and then recursively pinpointing the exact basic block within that function’s CFG. • Instruction-level Localization. Finally, to identify the responsible instruction within the suspect block, we propose an SSA-preserving instruction localization strategy. Direct deletion is infeasible in Cranelift IR as it breaks Def-Use chains mandated by the SSA form. Instead, we employ a semantic substitution mechanism: we replace a suspect arithmetic or logical instruction (e.g., v2 = iadd v0, v1) with a simplified constant assignment (e.g., v2 = iconst.i32 1). If the inconsistency disappears after this substitution, the original instruction is identified as the root cause; otherwise, it is deemed irrelevant and pruned from the search space. , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
11
4.5.3 Feedback-driven Profile Adaptation. Feedback-driven profile adaptation helps expose bugs hidden behind previously diagnosed failures and reduces redundant generation cycles. During test case clustering, a single cluster grouped by its primary failure signature may contain multiple test cases with different instruction patterns, each triggering distinct underlying bugs. For instance, a non-crashing cluster whose outputs diverge across architectures might group multiple test cases that independently trigger different backend bugs but manifest the same cross-architecture divergence symptom. Without intervention, these distinct bugs can remain hidden beneath the common failure signature. Additionally, in large-scale testing campaigns, repeatedly rediscovering known bugs wastes valuable generation cycles. The mechanism operates through an iterative feedback loop. After Phase II hierarchical localization identifies a root cause, the framework extracts a failure signature Sfail (typically represented as {Opcode, Type}). To avoid accidentally modifying the wrong instruction pattern, we manually mask this signature in the generation profile and then re-run the entire testing pipeline with the updated constraints. If the same failure cluster persists, it suggests that another underlying bug may still exist in that cluster, so the diagnosis process repeats. Although this iterative process requires minor manual effort on updating profiles and rerunning the testing pipeline, it progressively uncovers hidden bugs and reduces human labor compared to exhaustively triaging all individual test cases. Concretely, when diagnosis identifies that a specific instruction-type combination, such as vhigh_bits on type f32x4, triggers a crash on a specific backend, the framework disables this combination from its generation profile for subsequent runs. 4.5.4 Minimal Reproducible Example Construction. Minimal Reproducible Example (MRE) construction translates diagnostic results into actionable bug reports through targeted manual extraction. While Phase II isolates the suspected buggy code region, human intervention remains necessary to synthesize the final test case. Since automated diagnosis already narrows the search scope, this manual reduction becomes a focused extraction rather than a time-consuming debugging process. We start from the original uninstrumented IR, extract the identified buggy snippet, encapsulate it within a minimal yet syntactically valid function context, and iteratively remove surrounding instructions that are irrelevant to the bug manifestation. This process keeps the reported issue concise and readily analyzable by compiler developers. 5
Evaluation
In this section, we quantitatively evaluate CLIR from various perspectives. Baselines. As Cranelift IR can be compiled from Rust and WebAssembly, we select four state-of-theart baselines. Specifically, cranelift-fuzzgen [15] is Cranelift’s official IR test case generator, based on AFL, a coverage-guided fuzzing framework that explores diverse execution paths by mutating inputs. RustSmith [34] is a fuzzer originally designed for the Rust compiler. It employs a structure-aware generation strategy to produce valid Rust programs. As for wasm-smith [8] and WASMaker [12], they are end-to-end WebAssembly test case generators. wasm-smith, developed by the Bytecode Alliance, as part of the official WebAssembly suite, generates random but valid WebAssembly binaries. WASMaker synthesizes WebAssembly programs by combining code snippets extracted from real-world applications. Research Questions. Our evaluation is structured around the following research questions (RQs): RQ1: How is the performance of CLIR compared with baselines? RQ2: Are generated test cases complex enough compared with the baselines? RQ3: What about the contribution of components of CLIR? RQ4: What are the characteristics, distribution, and impact of the bugs detected by CLIR? , Vol. 1, No. 1, Article . Publication date: June 2026.
12
Trovato et al.
For RQ1, we conduct a comprehensive evaluation of the tools’ effectiveness in terms of (1) the bug-finding capability and (2) the code coverage achieved on different Cranelift backends. For RQ2, to evaluate the complexity of generated test cases, we perform a quantitative evaluation on four metrics, i.e., cyclomatic complexity and dominator tree depth for structural complexity, and def-use chain depth and instruction diversity for data complexity, on generated test cases by CLIR and baselines. For RQ3, we conduct ablation studies with two variants of CLIR to evaluate the contribution of each component: (1) CLIRw/o-CFG (see §4.3), which disables the function and control flow construction processes, restricting each test case to a single function containing a single basic block; and (2) CLIRw/o-Live (see §4.4), which excludes the liveness-guided instruction refinement strategies. For RQ4, we focused on a set of representative and impactful bugs to demonstrate the effectiveness of our framework. These bugs were selected based on their severity and impact on Cranelift’s functionality. Implementation. We implemented CLIR from the ground up in more than 7,500 lines of Rust code. To populate the corpus described in §4.2, we selected the top 100 Rust libraries from crates.io [18] by download count and used WasmBench [37], a dataset of real-world WebAssembly binaries covering a wide range of application domains. We compiled these programs into Cranelift IR, extracted their basic blocks, and serialized the blocks in a structured JSON-based format for efficient retrieval. Based on pilot experiments comparing several mutation rates on Cranelift IR, we set the mutation rate in the test generation configuration to 50%, which empirically balances semantic exploration with the preservation of structurally valid IR. Experimental Setup. All experiments were conducted on a dedicated server running Ubuntu 22.04, equipped with a 64-core AMD EPYC 7713 processor and 256 GB of RAM. We evaluated CLIR under five testing scenarios based on the Dual-Mode Testing Configuration (§4.5.1): one compatiblemode scenario that uses intersection-based IR for differential testing across all backends, and four target-specific scenarios tailored to x86-64, AArch64, s390x, and RISCV64, respectively. This setup allows us to exercise both cross-backend behaviors and backend-specific constraints. To expose compiler behaviors under different optimization paths, we applied three optimization levels (none, speed, and speed_and_size) to every generated test case. For coverage measurement, we focused on the IR-to-machine-code lowering components, i.e., code in isle_x64.rs, isle_aarch64.rs, isle_s390x.rs, and isle_riscv64.rs. We measured coverage hourly using cargo-llvm-cov [36] and restricted this measurement to the first 24 hours, since coverage tends to saturate within this period. The main fuzzing campaign was executed continuously for 72 hours across all configurations. To ensure the statistical validity of the code coverage results, we repeated the 24-hour coverage experiments 10 times independently and used these runs to calculate the standard deviation. For the bug oracle, we treat cross-architectural execution divergences as true positives by design, meaning any architecture-specific inconsistencies are actively flagged as potential bugs. 5.1
RQ1: Effectiveness
To evaluate the effectiveness of CLIR, we focus on how many unique compiler bugs are identified; and how thoroughly the generated inputs exercise the Cranelift backends. 5.1.1 Bug-finding Capability. Table 1 presents the number of unique bugs detected by each tool on different Cranelift backends. As we can see, CLIR covers the testing gaps left by existing SOTA baselines. CLIR identified a total of 24 unique bugs. This is 8× more than cranelift-fuzzgen and WASMaker (both found three bugs), and 24× more than wasm-smith (only one bug). More specifically, the official test suite, i.e., cranelift-fuzzgen, does not detect any bugs on the x86-64 and s390x backends. This absence is notable for x86-64, the most mature target, which suggests that the , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
13
Table 1. Unique bugs detected and average code coverage achieved by CLIR and baselines. CLIR
cranelift-fuzzgen
RustSmith
wasm-smith
WASMaker
5 5 8 2 4
0 2 1 0 0
0 0 0 0 0
1 0 0 0 0
0 0 3 0 0
x86-64 AArch64 RISCV64 Unique Bug s390x Other
0
1
3
53.4% ± 0.3%
55.0% ± 1.2%
53.9% ± 2.9%
60
43.7
40 4
8
12 16 20 24
Time (h)
(a) x86-64
79.1 73.6
70
61.6 60.7
60 50 0
52.8 4
8
12 16 20 24
Time (h)
(b) AArch64
Coverage (%)
68.4 64.7 61.6 57.6
Coverage (%)
Coverage (%)
3 65.1% ± 1.8%
80
80
20 0
24 75.1% ± 1.7%
80
76.6
60
60.7 58.7
40 20 0
31.5 31.0 4
8
12 16 20 24
Time (h)
(c) RISCV64
80
Coverage (%)
Total Avg. Coverage
76.1 68.5 63.2 61.2 58.5
60
40 0
4
8
12 16 20 24
Time (h)
(d) s390x
Fig. 6. The code coverage of CLIR and baselines on four arches. The colors correspond to cranelift-fuzzgen, RustSmith, wasm-smith, WASMaker, and CLIR.
official test suite may be saturated on well-tested paths. In comparison, CLIR identifies five unique bugs on x86-64 and two on s390x. It is also worth noting that RustSmith detects zero bugs in all architectures, indicating the difficulty of finding backend bugs through generating valid high-level source code. Similarly, the WebAssembly-based baselines are limited to specific architectures, where wasm-smith only finds issues on x86-64, and WASMaker only on RISCV64. CLIR is the only tool that exposed unique bugs across all four supported architectures. Moreover, CLIR, as the only one out of five tools, uncovers four bugs under the “other” category, i.e., bugs found in platform-independent components, such as the IR optimizer or interpreter. This demonstrates that CLIR is effective for the entire compiler pipeline, not just specific code generation backends. Regarding bug overlaps, CLIR demonstrated comprehensive bug-finding capabilities by fully covering the effective search space of all baselines. Specifically, CLIR successfully detected all three unique bugs identified by the official test suite, cranelift-fuzzgen. Furthermore, it also captured all four bugs exposed by the WebAssembly-based baselines (i.e., wasm-smith and WASMaker), whereas RustSmith failed to detect any. The fact that CLIR not only encompasses all bugs found by state-of-the-art baselines but also uncovers a significant number of additional unique issues demonstrates its practical advantage in exercising Cranelift-specific backend behaviors. Collectively, these results underscore the critical necessity of a dedicated, Cranelift-native testing framework that can explore the deep backend states unreachable by existing tools. 5.1.2 Code Coverage. Code coverage reflects how much of the compiler’s code is exercised during testing. Typically, the higher the coverage, the more effective the testing. Since Cranelift is a backend compiler, we focus on the codebase responsible for lowering Cranelift IR to machine instructions, as mentioned in §5. As shown in Fig. 6, WASMaker, wasm-smith, and RustSmith quickly reach their saturation points on most Cranelift backends. This is because they are less sensitive to feedback-driven coverage expansion and can generate a large volume of test cases in a short period. However, wasm-smith and WASMaker achieve only around 30% coverage on RISCV64, far lower than on other architectures. , Vol. 1, No. 1, Article . Publication date: June 2026.
14
Trovato et al.
After investigation, we found that when their WebAssembly binaries are compiled into Cranelift by Wasmtime, Wasmtime cannot fully utilize the architecture-specific lowering rules (as noted in §1). This leaves many RISCV64-specific lowering rules uncovered. This pattern also appears in other high-level language-based methods, such as RustSmith’s weak performance on x86-64 and AArch64. In contrast, cranelift-fuzzgen shows a slower but steady increase in coverage over time, because its AFL-based strategy allows each test case to gradually contribute new coverage. CLIR consistently starts with higher initial coverage than the baselines, demonstrating that our design explores the compiler pipeline more effectively in a shorter amount of time. Ultimately, CLIR achieves coverage rates of 68.4%, 79.1%, 76.6%, and 76.1% for x86-64, AArch64, RISCV64, and s390x, respectively, consistently outperforming all four baselines. Furthermore, to evaluate overall stability, we calculated the coverage standard deviation across 10 independent runs, as shown in Table 1. CLIR demonstrates excellent stability with a remarkably low variance of ± 1.7%, guaranteeing reproducible exploration of deep backend paths. While RustSmith exhibits a lower variance (± 0.3%) due to its limited exploration space, CLIR maintains superior stability compared to dynamically generating baselines like WASMaker (± 2.9%) and cranelift-fuzzgen (± 1.8%). RQ1 Answer: Compared to current representative test schemes that can generate Cranelift IRs, CLIR demonstrates a significant advantage in effectiveness with discovering at least 8x more unique bugs and improving coverage by at least 15% on average. 5.2
RQ2: Structural and Computational Complexity of Test Cases
To evaluate the complexity of generated test cases, we consider the structural complexity (i.e., cyclomatic complexity [1] and dominator tree depth) and data complexity (i.e., def-use chain depth and instruction diversity.) 5.2.1 Structural Complexity. Cyclomatic complexity characterizes the horizontal complexity and branching density, calculated as 𝑀 = 𝐸 − 𝑁 + 𝑃 (where 𝐸 is edges, 𝑁 is basic blocks, and 𝑃 is connected components), while dominator tree depth measures the vertical depth reflecting the hierarchy of control dependencies. We illustrate the distribution of cyclomatic complexity and dominator tree depth for all baselines and CLIR with different 𝐷 (specifying the maximum recursion depth of the CFG substitution process (detailed in §4.3.2)) in Fig. 7(a) and (b), respectively. As we can observe, there is a clear positive correlation between the cyclomatic complexity and 𝐷. When 𝐷 increases from 2 to 10, the generated code exhibits progressively higher logical intricacy, with the median cyclomatic complexity rising from 15.2 to 49.8. Consequently, CLIR (particularly at 𝐷 = 10) demonstrates a significantly higher cyclomatic complexity compared to all baselines, especially the WebAssembly-based WASMaker and wasm-smith. Regarding the dominator tree depth, as shown in Fig. 7(b), CLIR exceeds three of the four baselines. We observe two abnormal distributions in Fig. 7(b), i.e., the one of cranelift-fuzzgen and RustSmith. Regarding cranelift-fuzzgen, the distribution reflects its bias towards generating unit-test-like, single-block regression tests. Its generator favors straight-line code for instruction verification, leading to lower average dominator tree depth. As for RustSmith, this anomaly is identified as a structural artifact when combined with its relatively low cyclomatic complexity (10.3). This combination reveals that RustSmith tends to generate a massive main function consisting of long, linear sequences of basic blocks rather than complex loop or branching logic. In such linear chains, every block strictly dominates its successors, artificially inflating the tree depth. 5.2.2 Data Complexity. We take advantage of def-use chain depth and instruction diversity to quantitatively evaluate the data complexity. On one hand, as illustrated in Fig. 7(c), both Wasm-based , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler (b) Dominator Tree Depth
30
1000 100
r h n h =2) =5) 10) zge ake mit mit Fuz WASM asm-s RustS CLIR (D CLIR (D LIR (D= w C
(c) Def-Use Chain Depth
25
Chain Depth
80 70 60 50 40 30 20 10 0
Max. Tree Depth (Log Scale)
Complexity
(a) Cyclomatic Complexity
15
10
20 15 10 5
1
r h n h =2) =5) 10) zge ake mit mit Fuz WASM asm-s RustS CLIR (D CLIR (D LIR (D= w C
0
n er ith ith zge Mak sm-sm tSm Fuz Rus WAS wa
CLIR
Fig. 7. The distribution of the complexity of generated test cases in terms of (a) cyclomatic complexity, (b) dominator tree depth, (c) def-use chain depth, and (d) instruction diversity, where Fuzzgen denotes cranelift-fuzzgen. Table 2. Ablation study results, where Rate refers to #Inconsistencies / #Test Cases.
CLIR CLIR𝑤/𝑜 −𝐶𝐹𝐺 CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒
#Test Cases
#Inconsistencies
Rate
#Bug
Avg. Coverage
1,327,581 8,351,332 1,792,921
301,849 203,732 256,183
22.74% 2.44% 14.29%
24 13 9
75.08% 60.45% 71.74%
tools show notably long dependency chains. We find that this is due to a side effect of translating WebAssembly’s stack-based instructions into Cranelift IR, where frequent push-pop operations are converted into extended, albeit often repetitive, data-flow chains. As for RustSmith and craneliftfuzzgen, CLIR consistently outperforms them, achieving a median depth of 2.6 compared to ≈1.0 for both of them. Finally, regarding instruction diversity, CLIR attains a superior coverage of 89.58%. This edges out the closest competitor, cranelift-fuzzgen (84.90%), while far exceeding the other baselines (25.00%–37.50%). RQ2 Answer: The quantitative assessment concludes that CLIR can achieve a balance between breadth and depth in terms of structural complexity, and outperforms the remaining baselines in terms of data complexity after excluding two WebAssembly-related biased ones. 5.3
RQ3: Ablation Study
To evaluate the contribution of individual components, we conducted an ablation study comparing the full CLIR against two variants: CLIR𝑤/𝑜 −𝐶𝐹𝐺 and CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 , as shown in Table 2. The results demonstrate that the full framework outperforms both variants in bug detection, despite lower throughput. Specifically, CLIR𝑤/𝑜 −𝐶𝐹𝐺 generated the highest volume of test cases (8.3M, 6.3× the full version) by restricting generation to single-block functions. However, this structural simplicity severely compromised effectiveness, yielding only 13 bugs (vs. 24). This highlights that mere quantity cannot compensate for the lack of structural depth required to trigger deep backend paths. Conversely, CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 achieved relatively high coverage (71.74%) yet detected the fewest bugs (9). This indicates that while the hierarchical generation provides sufficient structural complexity to explore diverse compiler paths, the generated programs lack data-flow depth. Without livenessguided refinement, generated instructions fail to form resilient data dependencies and are actively pruned by early simplification passes. To further analyze the 15 bugs that were detected by the full CLIR but not by CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 under the same time budget, we performed a post-hoc inspection on these cases. Our goal is not to claim that CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 can never generate such triggers, but to explain why it is less likely to do so within the fixed ablation time. Concretely, we inspected the bug-triggering representatives generated by , Vol. 1, No. 1, Article . Publication date: June 2026.
16
Trovato et al.
Table 3. Liveness bottlenecks in the 15 bugs missed by CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 . Liveness bottleneck
How CLIR mitigates it
Bug IDs*
Lost sink anchoring
Sink synthesis anchors bug-relevant values to an observable sink (return/store), so the computations that produce these values are more likely to remain live and be exercised. Priority-based operand selection encourages long transitive usedef chains, keeping more generated code live and increasing the probability that bug-triggering instructions are executed. Cross-region dataflow coupling preserves dependencies across merge points and function calls through block parameters and call argument/return-value uses, improving the reproducibility of interprocedural triggering conditions.
#1, #2, #5, #6, #16, #22, #23
Shallow def-use chains
Lost cross-region live state
#13, #17, #18, #20 #3, #8, #11, #21
* Bug IDs are defined in Table 4.
CLIR and summarized three recurring liveness bottlenecks that make bug-triggering computations harder to preserve and observe. First, lost sink anchoring occurs when a sensitive computation does not flow into an observable sink (return/store), making it dead or behaviorally irrelevant. Second, shallow def-use chains make generated instructions easy to simplify away, reducing the executed code around the trigger. Third, lost cross-region live state occurs when dependencies are not preserved across merge points or calls, making inter-procedural triggers harder to reproduce. Table 3 summarizes how CLIR’s liveness-guided strategies address these bottlenecks, and we use Bugs #2, #13, and #3 as representative examples. • Bug #2 (Lost sink anchoring). This miscompilation involves scalar_to_vector. In our representative test case, the value defined by scalar_to_vector is directly returned from the function, making the bug observable as an output divergence. To reliably expose such a backend bug, the value produced by the sensitive instruction must affect an observable sink (e.g., it is returned or stored). Otherwise, the computation is behaviorally irrelevant and may not survive to backend-specific handling. In CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 , the generator does not prioritize any variable for sink connection, making it difficult to route a bug-triggering value to an observable sink within the fixed testing budget. In contrast, CLIR’s sink synthesis (§4.4.3) explicitly connects leaf values to typed return/store operations, turning the vector construction into an observable behavior and making the bug reproducible. • Bug #13 (Shallow def-use chains). This crash is triggered by uadd_overflow_trap in the interpreter. In our test case, uadd_overflow_trap appears within a def-use chain of length 6, allowing its surrounding computations to survive until the crash manifests. While the bug can often be reproduced with a small minimized snippet, discovering it efficiently at scale benefits from longer def-use chains, which increase the chance that the bug-triggering instruction and its context survive simplification. Without liveness-guided operand selection, CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 tends to generate shorter and more fragile chains, so many instructions become effectively irrelevant, leaving only a small executable core and reducing the probability of hitting the crash. CLIR’s priority-based operand selection (§4.4.2) strengthens intra-block dataflow by preferentially consuming recently-defined, type-matching variables (including call results and block parameters), producing longer dependency chains and improving execution density. • Bug #3 (Lost cross-region live state). This miscompilation manifests as output divergence affected by an unrelated call, i.e., the call is not expected to change the values that determine the function output. This indicates that backend state across a call boundary is mishandled. In our representative test case, the call site is kept semantically necessary because its return value is consumed by subsequent computations, preventing the call from being trivially removed. To , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
Table 4. Detailed information of all identified bugs. #, G #, and and "confirmed and fixed", respectively. ID
Component
Type
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
s390x x86-64 RISCV64 RISCV64 RISCV64 AArch64 RISCV64 RISCV64 AArch64 x86-64/AArch64 RISCV64 Other Other Other Other x86-64 x86-64 x86-64 AArch64 s390x RISCV64 AArch64 RISCV64
Miscompilation Miscompilation Miscompilation Miscompilation Miscompilation Miscompilation Miscompilation Miscompilation Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash Compiler Crash
17
denote "reported", "confirmed but unfixed",
Status Description
# # # G # G # # G # G # G # G # G # G # G # G # G
Little/big-endian flags has no effect on load instruction Inconsistent results from scalar_to_vector Unrelated calls cause inconsistent results Non-deterministic NaN patterns cause inconsistent outputs Unaligned addresses caused i16 polyfill errors Optimizations on band instruction mask regalloc issue IR interpretation divergence across targets Calling convention mismatch on RISCV64 AArch64 crashes on bitwise ops over floats Crash optimizing icmp with vectors Call instruction causes a crash on RISCV64 fmin causes crash in interpret uadd_overflow_trap causes crash in interpret Interpreter miscomputes bitcast result Optimizer panicked when processing vector inputs Missing lowering rule for vany_true with i8x16 Missing lowering rules for sadd_sat/usub_sat on vectors Missing lowering rule for uunarrow with i64x2 Assertion failure during emission of band_not with f64 Missing lowering rule for bxor with floats br_table crashes due to a missing gen_bitcast rule Missing lowering rule for vhigh_bits with f32x4 Missing lowering rule for select_spectre_guard with i8x16
expose such issues, values computed before the call must remain live across the call and be consumed afterward (e.g., returned or used in subsequent computations), forcing the backend to preserve/restore the relevant state. In CLIR𝑤/𝑜 −𝐿𝑖𝑣𝑒 , cross-region dependencies are less likely to be enforced, so pre-call values may not remain live in the post-call region, making this class of bugs harder to reproduce. In contrast, CLIR ensures that state-carrying values are carried across regions and become observable. RQ3 Answer: All proposed methods in CLIR are important and indispensable. Disabling any of them may increase the efficiency of test case generation, but the number of discovered bugs and code coverage will be hindered. 5.4
RQ4: Bug Characterization
Table 4 summarizes all 24 distinct bugs identified in RQ12 , along with the responsible component, bug type, status, and detailed descriptions. As we can see, these bugs fall into two categories: 8 miscompilations, where the compiler generates incorrect machine code leading to execution divergence (e.g., ABI mismatches in #8 or incorrect instruction lowering in #2), and 16 compiler crashes, which cause the compiler to panic. The latter are mainly attributed to missing lowering rules for specific types, like SIMD vectors and floating-point instructions (e.g., #16, #17, and #18), and assertion failures within the optimizer (e.g., #15). These bugs span all supported backend architectures, as well as platform-independent optimization and verification phases, further demonstrating the comprehensive testing coverage of CLIR. In the following, we present three representative cases as detailed case studies. 2 #10 corresponds to x86-64 and AArch64.
, Vol. 1, No. 1, Article . Publication date: June 2026.
18
Trovato et al.
1 function % main () -> i64 fast { 2 ss0 = explicit_slot 32 3 block0 : 4 v1 = iconst . i64 0 x0011_0022_0033_0044 5 stack_store v1 , ss0 6 v2 = stack_addr . i64 ss0 7 v3 = sload32 big v2 8 return v3 9 }
Listing 1. Big-endian loads/stores mishandled.
Case 1: Big-endian loads/stores mishandled. Listing 1 presents a reduced IR test case that highlights an endianness issue. L2 declares a 32-byte stack slot ss0. L4–L5 store a constant into ss0, and L6–L8 load the value using a signed 32-bit load with the big flag, indicating big-endian semantics. On the s390x architecture, which uses big-endian memory, the result is correct. However, on little-endian architectures, the output reflects a little-endian read. This inconsistency arises because the backend fails to honor the big flag and instead defaults to the native byte order, leading to incorrect results on non-s390x targets. 1 function % main () -> i16x8 , f64x2 fast { 2 sig0 = ( i64 ) -> i64 fast 3 fn0 = u1 :1 sig0 4 const0 = 0 x00110022003300440055006600770088 5 block0 : 6 v1 = iconst . i64 0 x1f96_3ea8_4eb6_5f81 7 v2 = vconst . i16x8 const0 8 v3 = vconst . f32x4 const0 9 v4 = fvpromote_low v3 10 v5 = call fn0 ( v1 ) 11 v6 = iadd v1 , v5 12 return v2 , v4 13 }
Listing 2. Missing vector state sync after call.
Case 2: Missing vector state sync after call. Listing 2 shows an IR test case involving a function call that leads to inconsistent outputs on the RISCV64 architecture. The call at L10 invokes fn0 with input 𝑣1 and stores the return value in 𝑣5. Although 𝑣5 is consumed by the subsequent instruction 𝑣6 = iadd 𝑣1, 𝑣5, the resulting value is not returned. The function main instead returns 𝑣2 and 𝑣4, both computed before the call. In principle, the call should not affect the final result because the returned values are computed before the call. However, in practice, removing the call instruction leads to different outputs. This inconsistency is caused by Cranelift’s mishandling of vector register state: it fails to preserve or restore registers across the call, allowing the callee to clobber values like 𝑣2 and 𝑣4, even though these values are unrelated to the call’s return. 1 function % main () -> i64 , f32 fast { 2 block0 : 3 v0 = iconst . i64 -3524126683585344751 4 v1 = f32const 0 x1 .66 e07ap -1 5 v2 = band . f32 v1 , v1 6 return v0 , v2 7 }
Listing 3. Optimizations mask regalloc issue.
Case 3: Optimizations mask regalloc issue. Listing 3 presents an IR that causes optimizationrelated inconsistency on AArch64. Specifically, it triggers a register allocation panic when compiled without optimizations. The issue occurs on L5, which is a bitwise operation. Although this instruction is theoretically legal and runs correctly on other architectures, it causes a crash on AArch64 due to the backend’s failure to properly handle the register pressure introduced by the instruction. As a result, regalloc2 reports an error and aborts compilation. However, when optimizations are enabled, the issue no longer occurs. Since band.f32 v1,v1 is semantically equivalent to 𝑣1, the , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
19
optimizer eliminates the instruction and treats 𝑣2 as an alias of 𝑣1. This avoids triggering the faulty register allocation path and produces the expected output. RQ4 Answer: CLIR effectively and efficiently uncovered 24 unique bugs of Cranelift compiler, leading to miscompilations and runtime crashes. The fact that the majority of them were confirmed in a timely manner and that their root causes are widely distributed demonstrates the influence of CLIR in the real world. 6
Threat to Validity & Discussion
External Validity. Regarding the bug fix rate, it is important to note that Cranelift is a relatively new compiler backend with limited development resources compared to established frameworks like LLVM. Although the development team has acknowledged the validity of our reported bugs, immediate fixes were not feasible for all cases due to resource constraints. Instead, the developers explicitly suggested aggregating these confirmed bugs into a centralized tracking issue. This list is intended to serve as a roadmap for future open-source contributors to address. Consequently, the presence of unfixed bugs in our results reflects the project’s current development stage and community collaboration strategy, rather than a lack of severity or validity of the discovered issues. Internal Validity. Although CLIR supports hierarchical bug localization down to instruction level, we acknowledge its boundary as a lightweight diagnostic aid rather than a universally precise reducer. In particular, not all inconsistencies are attributable to a single instruction: some failures emerge from interactions across multiple instructions, blocks, or loop iterations (e.g., optimization cascades and scheduling/regalloc coupling). In these cases, forcing instruction-level precision may be unstable or misleading. Therefore, our workflow is explicitly fallback-aware: if instructionlevel isolation is not reproducible, we roll back to basic-block level; if block-level isolation is still ambiguous, we report function-level culprit regions. This multi-tiered fallback still provides actionable guidance to developers because it substantially shrinks the triage space while preserving valid, reproducible IR artifacts. Discussion on Extensibility. Extending CLIR to another compiler infrastructure, such as LLVM, requires target-specific engineering, but this effort is narrower than reimplementing the full system. The main reusable parts are the Syntax-Preserving Hierarchical SSA Generation (§4.3) and the Liveness-Guided Refinement (§4.4). These components operate on SSA structure and liveness properties rather than Cranelift-specific backend rules, so they can guide test generation for other SSA-based compilers once the target IR interface is provided. The required engineering mainly falls into two parts. (1) Code Generator Substitution and IR Mapping. Our prototype emits instructions using Cranelift’s internal code generation API. Porting requires replacing this interface with the target compiler’s IR builder library, together with opcode and type mapping and compatibility checks. (2) Architecture-Specific Profiling. As detailed in §4.5.1, testing a new backend requires a configuration profile that defines its supported instruction set, operand type constraints, and calling conventions. For LLVM, we estimate that porting the core generation pipeline would require approximately six working days and around 1,800 lines of code, covering common functions, control flow, and arithmetic instructions. Discussion on Failure Localization and Reduction. Failure localization and reduction are related but distinct tasks in our setting. Reduction aims to shrink a failing input into a smaller reproducible case, while localization aims to identify the code region or instruction that is most relevant to the failure. Reduction methods such as Hierarchical Delta Debugging (HDD) [27] share a high-level similarity with our localization strategy because both follow a hierarchical, top-down process from coarse-grained structures to finer-grained ones. However, HDD primarily targets , Vol. 1, No. 1, Article . Publication date: June 2026.
20
Trovato et al.
tree-structured inputs such as Abstract Syntax Trees (ASTs). Through recursive decomposition, it repeatedly parses and prunes the AST to find a minimal reproducible test case. In this sense, HDD is a reduction technique: its output is a smaller failing input rather than a root-cause explanation. Our “Phase II: Hierarchical Fault Localization” (§4.5) has a different goal and operates under different constraints. Instead of deleting program fragments, our method narrows the search scope from function to block to instruction using semantic-aware IR instrumentation. Direct structural deletion is risky for SSA-form IR because it can break Def-Use chains and data dependencies required for a valid test case. Moreover, HDD is fundamentally designed for AST-like tree structures, while compiler IR often contains graph structures with cycles, such as control-flow graphs and call graphs. Our method therefore preserves SSA form and uses execution truncation and semantic substitution to identify a suspect fault region without invalidating the test case. 7
Related Work
Differential testing. Differential testing is an effective methodology that detects bugs by comparing outputs of multiple implementations given the same inputs. It has been successfully applied to diverse targets [10, 14, 19, 28, 43]. Classming [14] conducts differential testing on runtime-optimized JVMs by dynamically generating valid bytecode through real-time mutations of seed bytecode files, while Tensorscope [19] employs joint constraint analysis to generate test cases for differential testing of deep learning framework APIs, aiming to detect inconsistencies and security vulnerabilities in model conversions. Robin Morisset et al. [28] perform differential testing on compiler optimizations by comparing traces of key values before and after optimizing a given test program. Test case generation. There has been work on compiler test case generation [13, 23, 26, 31, 40, 41]. CSmith [40], one of the most prevalent compiler testing tools, generates C programs through grammar-guided synthesis, producing diverse program structures that can expose bugs in compiler implementations. Zhang et al. [41] proposed a test case generation method called Skeletal Program Enumeration, which systematically triggers optimization bugs by exhaustively enumerating all variable usage combinations within given syntactic skeletons to generate small yet diverse test programs. Creal [23] constructs test cases by semantically fusing real-world code fragments, extracting functions from existing projects and combining them through dynamic analysis to achieve richer feature coverage than synthetic generation. In addition to source-level approaches, some efforts generate IR-level test cases. IRFuzzer [31] targets the LLVM backend by generating various LLVM IR programs with structured control flow and vector types. It employs constrained mutations to maintain input validity and uses instrumentation-based feedback, including matcher table coverage, to guide the fuzzing process. HirGen [26] targets bugs in the high-level IR optimization stage of deep learning compilers. It generates diverse and valid IRs by applying coverage-driven computational graph generation and leveraging high-level IR language features. In contrast to previous work that focuses on source-level synthesis or targets specific IR stages, our CLIR generates test cases directly in the Cranelift IR with syntactic and semantic awareness. 8
Conclusion
This paper presents CLIR, a comprehensive differential testing framework designed for the Cranelift compiler. CLIR employs a structure-aware hierarchical generation strategy that extracts basic blocks from real-world corpora and assembles them using a dominator-driven algorithm to ensure SSA compliance. To further enhance testing utility, it integrates liveness-guided instruction refinement to maximize computational complexity and employs a diagnosis-guided cross-architecture adaptation mechanism for efficient root cause localization. Experimental evaluation demonstrates that CLIR significantly outperforms existing techniques. It detects 8×, 24×, and 8× more unique bugs than state-of-the-art baselines (cranelift-fuzzgen, wasm-smith, and WASMaker), respectively, whereas , Vol. 1, No. 1, Article . Publication date: June 2026.
CLIR: Liveness-Driven and Structure-Aware Fuzzing for the Cranelift Compiler
21
RustSmith failed to detect any. Moreover, CLIR achieves 75% average code coverage across all supported architectures, surpassing baselines by 1.2×. Within 72 hours of testing, CLIR discovered 24 unique bugs. Notably, 21 of these have been confirmed by Cranelift developers and 9 have already been fixed, demonstrating its practical impact in improving compiler reliability. Data Availability The artifact of CLIR is released at link. References [1] 2025. Cyclomatic complexity. https://en.wikipedia.org/wiki/Cyclomatic_complexity [2] 2025. Just-in-time compilation. https://en.wikipedia.org/wiki/Just-in-time_compilation [3] 2025. PHI instruction of LLVM. https://llvm.org/docs/LangRef.html#phi-instruction [4] 2025. RISCV64 architecture. https://en.wikipedia.org/wiki/RISC-V [5] 2025. S390X architecture. https://en.wikipedia.org/wiki/Linux_on_IBM_Z#Hardware [6] 2025. Static single-assignment form. https://en.wikipedia.org/wiki/Static_single-assignment_form [7] AFL. 2025. American Fuzzy Lop. https://lcamtuf.coredump.cx/afl/ [8] Bytecode Alliance. 2025. Github wasm-tools repository. https://github.com/bytecodealliance/wasm-tools/tree/main/ crates/wasm-smith [9] Android. 2025. Rust in Android platform. https://source.android.com/docs/setup/build/rust/building-rust-modules/ overview [10] Gergö Barany. 2018. Finding missed compiler optimizations by differential testing. In Proceedings of the 27th international conference on compiler construction. 82–92. [11] Kevin Boos, Namitha Liyanage, Ramla Ijaz, and Lin Zhong. 2020. Theseus: an experiment in operating system structure and state management. In 14th USENIX Symposium on Operating Systems Design and Implementation (OSDI 20). 1–19. [12] Shangtong Cao, Ningyu He, Xinyu She, Yixuan Zhang, Mu Zhang, and Haoyu Wang. 2024. Wasmaker: Differential testing of webassembly runtimes via semantic-aware binary generation. In Proceedings of the 33rd ACM SIGSOFT International Symposium on Software Testing and Analysis. 1262–1273. [13] Junjie Chen, Guancheng Wang, Dan Hao, Yingfei Xiong, Hongyu Zhang, and Lu Zhang. 2019. History-guided configuration diversification for compiler test-program generation. In 2019 34th IEEE/ACM International Conference on Automated Software Engineering (ASE). IEEE, 305–316. [14] Yuting Chen, Ting Su, and Zhendong Su. 2019. Deep differential testing of JVM implementations. In 2019 IEEE/ACM 41st International Conference on Software Engineering (ICSE). IEEE, 1257–1268. [15] Cranelift. 2025. Github cranelift-fuzzgen repository. https://github.com/bytecodealliance/wasmtime/blob/main/fuzz/ fuzz_targets/cranelift-fuzzgen.rs [16] Cranelift. 2025. Github Cranelift repository. https://github.com/bytecodealliance/wasmtime/tree/main/cranelift [17] cranelift. 2025. Github isle docs webpage. https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle [18] crates.io. 2025. The Rust community’s crate registry. https://crates.io/ [19] Zizhuang Deng, Guozhu Meng, Kai Chen, Tong Liu, Lu Xiang, and Chunyang Chen. 2023. Differential Testing of Cross Deep Learning Framework {APIs}: Revealing Inconsistencies and Vulnerabilities. In 32nd USENIX Security Symposium (USENIX Security 23). 7393–7410. [20] Yuyou Fan and John Regehr. 2024. High-Throughput, Formal-Methods-Assisted Fuzzing for LLVM. In 2024 IEEE/ACM International Symposium on Code Generation and Optimization (CGO). IEEE, 349–358. [21] GCC. 2025. GCC Bugzilla. https://gcc.gnu.org/bugzilla/ [22] Shuyao Jiang, Ruiying Zeng, Yangfan Zhou, and Michael R Lyu. 2025. Distinguishability-guided Test Program Generation for WebAssembly Runtime Performance Testing. In 2025 IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER). IEEE, 768–779. [23] Shaohua Li, Theodoros Theodoridis, and Zhendong Su. 2024. Boosting compiler testing by injecting real-world code. Proceedings of the ACM on Programming Languages 8, PLDI (2024), 223–245. [24] LLVM. 2025. Github LLVM repository. https://github.com/llvm/llvm-project/issues [25] LLVM GitHub Issue Tracker. 2025. The official GitHub issue tracking page for the LLVM project. https://github.com/ llvm/llvm-project/issues [26] Haoyang Ma, Qingchao Shen, Yongqiang Tian, Junjie Chen, and Shing-Chi Cheung. 2023. Fuzzing deep learning compilers with hirgen. In Proceedings of the 32nd ACM SIGSOFT International Symposium on Software Testing and Analysis. 248–260.
, Vol. 1, No. 1, Article . Publication date: June 2026.
22
Trovato et al.
[27] Ghassan Misherghi and Zhendong Su. 2006. HDD: hierarchical delta debugging. In Proceedings of the 28th international conference on Software engineering. 142–151. [28] Robin Morisset, Pankaj Pawan, and Francesco Zappa Nardelli. 2013. Compiler testing via a theory of sound optimisations in the C11/C++ 11 memory model. ACM SIGPLAN Notices 48, 6 (2013), 187–196. [29] Steven Muchnick. 1997. Advanced compiler design implementation. Morgan kaufmann. [30] Vikram Narayanan, Tianjiao Huang, David Detweiler, Dan Appel, Zhaofeng Li, Gerd Zellweger, and Anton Burtsev. 2020. {RedLeaf}: isolation and communication in a safe operating system. In 14th USENIX Symposium on Operating Systems Design and Implementation (OSDI 20). 21–39. [31] Yuyang Rong, Zhanghan Yu, Zhenkai Weng, Stephen Neuendorffer, and Hao Chen. 2024. IRFuzzer: Specialized fuzzing for LLVM backend code generation. arXiv preprint arXiv:2402.05256 (2024). [32] Rust for Linux. 2025. The Rust for Linux Project. https://rust-for-linux.com/ [33] rustc_codegen_cranelift. 2025. Github rustc_codegen_cranelift repository. https://github.com/rust-lang/rustc_ codegen_cranelift [34] Mayank Sharma, Pingshi Yu, and Alastair F Donaldson. 2023. Rustsmith: Random differential compiler testing for rust. In Proceedings of the 32nd ACM SIGSOFT International Symposium on Software Testing and Analysis. 1483–1486. [35] Chengnian Sun, Vu Le, Qirun Zhang, and Zhendong Su. 2016. Toward understanding compiler bugs in GCC and LLVM. In Proceedings of the 25th international symposium on software testing and analysis. 294–305. [36] taiki-e. 2025. Github cargo-llvm-cov repository. https://github.com/taiki-e/cargo-llvm-cov [37] WasmBench. 2025. Github WasmBench repository. https://github.com/sola-st/WasmBench [38] wasmtime. 2025. Github wasmtime repository. https://github.com/bytecodealliance/wasmtime [39] Haoran Xu and Fredrik Kjolstad. 2021. Copy-and-patch compilation: a fast compilation algorithm for high-level languages and bytecode. Proceedings of the ACM on Programming Languages 5, OOPSLA (2021), 1–30. [40] Xuejun Yang, Yang Chen, Eric Eide, and John Regehr. 2011. Finding and understanding bugs in C compilers. In Proceedings of the 32nd ACM SIGPLAN conference on Programming language design and implementation. 283–294. [41] Qirun Zhang, Chengnian Sun, and Zhendong Su. 2017. Skeletal program enumeration for rigorous compiler testing. In Proceedings of the 38th ACM SIGPLAN conference on programming language design and implementation. 347–361. [42] Hao Zhong. 2022. Enriching compiler testing with real program from bug report. In Proceedings of the 37th IEEE/ACM International conference on automated software engineering. 1–12. [43] Shiyao Zhou, Muhui Jiang, Weimin Chen, Hao Zhou, Haoyu Wang, and Xiapu Luo. 2023. WADIFF: A Differential Testing Framework for WebAssembly Runtimes. In 2023 38th IEEE/ACM International Conference on Automated Software Engineering (ASE). IEEE Computer Society, 939–950.
, Vol. 1, No. 1, Article . Publication date: June 2026.