How to Fix: Cranelift: `internal error: entered unreachable code: no rule matched for term emit_fcmp at src/isa/x64/inst.isle line 3595; should it be partial?`
Cranelift x64 emit_fcmp Crash: Why no rule matched for term emit_fcmp Happens and How to Fix It
This crash points to a missing ISLE lowering rule in Cranelift’s x64 backend: the compiler reaches an emit_fcmp path for a floating-point comparison shape that the instruction selector does not know how to encode, then hits an unreachable internal assertion at src/isa/x64/inst.isle. In practice, that means your .clif test exposed a backend pattern gap rather than a user-level syntax error.
Table of Contents
Understanding the Root Cause
The message internal error: entered unreachable code: no rule matched for term emit_fcmp means Cranelift successfully parsed the IR, reached instruction selection for the x64 ISA, and then failed inside the ISLE rule engine. The failing term, emit_fcmp, is used when lowering a floating-point compare into target-specific machine instructions.
Technically, this happens when all of the following are true:
- The IR contains an fcmp-related operation or a control-flow pattern derived from one.
- The x64 backend tries to map that operation into a supported instruction sequence.
- No ISLE rule matches the exact combination of operand types, condition code, result form, or value legalization state.
- The ISLE definition treats the term as total, but runtime behavior proves it is actually partial.
The phrase should it be partial? is the key clue. In ISLE, a term should be marked partial if some legal-looking call sites may still fail to match. If emit_fcmp is assumed to always succeed but certain compare variants are not covered, Cranelift crashes instead of rejecting or rewriting the pattern safely.
Typical triggers include:
- Unsupported floating-point comparison conditions such as unusual ordered/unordered variants.
- A compare result used in a form the backend did not anticipate.
- Type combinations that were not fully legalized before x64 lowering.
- Backend regressions introduced when adding new compare encodings without updating every related rule.
So the real bug is not in your .clif file alone. Your test is acting as a reproducer for a missing or incorrectly declared lowering rule in Cranelift.
Step-by-Step Solution
The correct fix is to identify which floating-point compare pattern reaches emit_fcmp, then either add the missing x64 ISLE rule or make the term partial and handle fallback behavior explicitly.
1. Reproduce the crash with the smallest possible test
First, reduce the .clif input until only the failing floating-point compare remains. Even if the original issue says “not reduced,” maintainers will need a minimal reproducer.
cargo build -p cranelift-tools --features all-arch
target/debug/clif-util test failing_case.clif
If possible, isolate a function containing just:
- one or two floating-point values,
- an
fcmp, - and the branch or boolean use that forces lowering.
2. Confirm the failure is x64-specific
Run the same IR through a different backend if available, or disable x64-specific targeting. If the crash only appears on x64, that strongly confirms an x64 lowering gap.
target/debug/clif-util test --target x86_64 failing_case.clif
If your environment supports other targets, compare behavior there.
3. Inspect src/isa/x64/inst.isle near the failing rule
Open the file around the line reported in the panic. You are looking for the definition of emit_fcmp and all rules that dispatch into it.
grep -n "emit_fcmp" src/isa/x64/inst.isle
Check whether the rules cover:
f32andf64,- all relevant FloatCC values,
- register-register and register-memory forms,
- comparison results used by branches versus integer materialization.
4. Find the missing condition or type combination
Usually one case is not represented. For example, Cranelift may support common conditions like eq, lt, and gt, but fail on an unordered variant or a transformed condition generated during legalization.
Add temporary debug output or inspect the IR right before lowering to determine the exact compare condition. If needed, use test instrumentation in the lowering path.
// Pseudocode idea while debugging Rust lowering paths
println!("fcmp cc={:?} ty={:?}", cc, ty);
5. Fix the ISLE definition
You generally have two valid approaches.
Preferred fix: add the missing lowering rule so every reachable emit_fcmp invocation has a matching implementation.
;; Pseudocode-style example only
(rule (emit_fcmp (FloatCC.UnorderedOrEqual) x y ty)
(... x64 compare sequence ...))
Defensive fix: if some inputs are not always legal, mark the term as partial and make callers handle failure.
;; Conceptual example
(decl emit_fcmp (FloatCC Value Value Type) InstOutput partial)
This prevents an unconditional unreachable panic when the matcher cannot prove total coverage.
In many Cranelift bugs of this class, the ideal patch is:
- make the term partial if it is semantically partial,
- add the missing lowering rules for legal cases,
- and ensure unsupported cases fail earlier or are legalized into supported ones.
6. Regenerate generated code if required
If the Cranelift build requires regenerating ISLE outputs or derived files, run the project’s normal build pipeline.
cargo test -p cranelift-codegen
cargo test -p cranelift-filetests
7. Add a regression test
This part is essential. Put the reduced .clif case into the appropriate filetest suite so future backend changes do not reintroduce the crash.
; add a reduced reproducer under filetests
; ensure it exercises the exact fcmp pattern that used to panic
A good regression test should verify that:
- Cranelift no longer panics,
- the function compiles successfully,
- and, if possible, the emitted control flow matches expected semantics.
8. If you cannot patch Cranelift immediately, use a temporary workaround
If you are blocked in downstream usage, rewrite the IR or source pattern so it avoids the unsupported compare form. For example:
- replace a rare unordered condition with an equivalent normalized sequence,
- split complex boolean logic into simpler compares,
- force legalization into a path already known to work.
This is only a temporary mitigation. The real fix belongs in the backend.
Common Edge Cases
Even after patching emit_fcmp, similar failures can still happen if adjacent lowering paths are incomplete.
- NaN-sensitive comparisons: floating-point ordered and unordered semantics are easy to mishandle. A rule that works for
eqmay still be wrong for NaN cases. - Result materialization differences: branching on an
fcmpand converting anfcmpinto an integer flag may use different backend paths. f32vsf64coverage: adding one rule does not guarantee both widths are supported.- Legalization assumptions: if legalization changes the compare into a transformed condition, the original direct rule may never fire.
- Register class constraints: x64 floating-point compares may require specific register forms, and memory operands can expose separate rule gaps.
- Debug vs release behavior: debug builds often expose the unreachable panic clearly, while release testing may only show a failed compile path or harder-to-diagnose backend issue.
When validating the fix, test multiple compare conditions and both operand widths, especially with NaN-relevant cases.
FAQ
Is this a bug in my .clif program?
Usually no. The panic indicates a Cranelift backend bug, specifically an instruction selection path that assumed total rule coverage for emit_fcmp when coverage was incomplete.
What does should it be partial? mean in this error?
It means the ISLE term was treated as always matchable, but runtime reached a case with no valid rule. Marking it partial tells the matcher that failure is possible and must be handled safely rather than triggering unreachable code.
What is the best long-term fix: add a rule or mark the term partial?
Usually both ideas matter. If the case is valid for x64, add the missing rule. If some invocations are not universally valid, also declare the term partial so future gaps do not crash the compiler.
The most robust resolution for this GitHub issue is to treat the failing .clif as a regression reproducer, identify the exact floating-point compare variant that reaches emit_fcmp, then patch src/isa/x64/inst.isle so the term’s declaration and rule coverage accurately reflect reality.