llvm / llvm/circt

[FIRRTL] CheckCombLoops crashes on a forceable register reference cast to a layer-colored RWProbe

Open
#11,085 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
2.2k
Forks
524
Avg merge
3d 2h
Merged PRs (30d)
46

Description

Summary

firrtl-check-comb-loops crashes on a forceable register whose writable SSA reference is cast to a layer-colored reference and defined as an output probe.

The input passes parsing and verification. No force/release statement is needed, and the design has no combinational feedback.

Environment

  • CIRCT revision: eade0de61bc5a0d2ba1b9da951b69efcab19f8ce(HEAD)
  • firtool version: 1.158.0
  • Platform: Linux x86-64

Reproducer

module {
  firrtl.circuit "Repro" {
    firrtl.layer @Test bind {}
    firrtl.module @Repro(
      in %clock: !firrtl.clock,
      in %data: !firrtl.uint<8>,
      out %out: !firrtl.uint<8>,
      out %probe: !firrtl.rwprobe<uint<8>, @Test>
    ) {
      %target, %target_ref = firrtl.reg %clock forceable
        : !firrtl.clock, !firrtl.uint<8>, !firrtl.rwprobe<uint<8>>
      firrtl.connect %target, %data : !firrtl.uint<8>
      firrtl.connect %out, %target : !firrtl.uint<8>
      firrtl.layerblock @Test {
        %ref = firrtl.ref.cast %target_ref
          : (!firrtl.rwprobe<uint<8>>) -> !firrtl.rwprobe<uint<8>, @Test>
        firrtl.ref.define %probe, %ref : !firrtl.rwprobe<uint<8>, @Test>
      }
    }
  }
}
# Succeeds.
circt-opt repro.mlir -o /dev/null

# Aborts with exit code 134.
circt-opt --firrtl-check-comb-loops repro.mlir -o /dev/null

Expected Behavior

The pass succeeds without reporting a combinational cycle.

Actual Behavior

llvm/ADT/EquivalenceClasses.h:192:
Assertion `MI != member_end() && "Value is not in the set!"' failed.

Relevant stack frames:

DiscoverLoops::addToPortPathsIfRWProbe
DiscoverLoops::dfsTraverse
CheckCombLoopsPass::runOnOperation

Suspected Cause

In CheckCombLoops.cpp, the connectivity graph builder matches hw::CombDataFlow before Forceable. RegOp implements both interfaces, so the register takes the former branch and skips recordProbe().

Additionally, ref.cast does not establish writable-reference equivalence between its input and output. ref.define registers the cast result and output probe, but not the original register reference.

Later, addToPortPathsIfRWProbe() calls getLeaderValue() for the original reference, which is absent from the equivalence set.

As a control, defining an uncolored output probe directly from %target_ref, without the cast/layer block, passes the analysis.
`

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided repro with and without --firrtl-check-comb-loops, then inspect CheckCombLoops.cpp around DiscoverLoops::addToPortPathsIfRWProbe and the CombDataFlow/Forceable handling. Trace how the ref.cast and ref.define values enter the writable-reference equivalence set; done means the reproducer completes without an assertion and reports no combinational cycle.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.