Skip to content

Conversation

@wky17
Copy link

@wky17 wky17 commented Nov 3, 2025

[WIP][FIRRTL] Add formally verified alternative InferWidths pass

🔍 Related Issue

Fixes #9140 (InferWidths failures on cyclic constraints)

📖 Background & Motivation

The current InferWidths implementation has critical limitations:
Correctness gaps: Fails on cyclic constraint cases like issue9140_0.mlir and issue9140_1.mlir

This PR introduces InferWidths_new - a drop-in alternative pass that:

⚙️ Technical Approach

The new pass implements a constraint-based width inference engine with:

  • Graph propagation core: Using Boost's graph algorithms for: SCC decomposition (Tarjan)
  • Adapted Floyd-Warshall algorithm and **Adapted Branch-and-Bound algorithm **: Solving inside SCC
  • Formal verification: Core algorithm mechanically proven in Rocq
  • Incremental adoption: Preserves existing pass pipeline

Implementation note:
Boost is used for rapid prototyping of graph algorithms. We have prepped replacement stubs for:

  • Tarjan's algorithm (SCC decomposition)
  • Floyd-Warshall (computing the least path)
  • DFS (finding any existing path between two nodes)
    These can be swapped in if necessary

🧪 Testing Strategy

Comprehensive verification across 2 test dimensions:

Test Category Coverage Verified Method
Legacy compliance 100% existing tests
(test/Dialect/FIRRTL/infer-widths.mlir)
✅ Passes all 60 existing tests
Issue regression test_new_inferwidths/
• issue9140_0.mlir (example 1)
• issue9140_1.mlir (example 2)
🐛 Fixes #9140 failures

🚀 Integration Plan

Following maintainer's phased adoption strategy:

  1. [Current PR] Add alternative pass with firrtl-infer-widths-new opt-in
  2. [Next] Enable in downstream CI with shadow testing
  3. [Phase 3] Performance benchmarking (memory/runtime)
  4. [Future] Switch default after bake period

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please collect all the tests to a single file and put them in the appropriate tests folder with the normal LIT headers and directives and checks.


// Width inference creates canonicalization opportunities.
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createInferWidths());
//pm.nest<firrtl::CircuitOp>().addPass(firrtl::createInferWidths());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either add a cmdline control over this or remove the old line. Don't leave commented out code in the file.

auto cs_duration = std::chrono::duration_cast<std::chrono::microseconds>(cs_time - start);
auto solve_duration = std::chrono::duration_cast<std::chrono::microseconds>(update_time - solve_time);
auto update_duration = std::chrono::duration_cast<std::chrono::microseconds>(end - update_time);
// std::cout << "generate constraint time : " << cs_duration.count() / 1000.0 << " ms\n";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't commit commented out code. I might suggest adding some performance counters from MLIR to track and report these.

if (mapping.areAllModulesSkipped())
return markAllAnalysesPreserved();

auto solve_time = std::chrono::high_resolution_clock::now();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See if MLIR's timing mechanisms are sufficient.


// 输出结果
if (solution.has_value()) {
// LLVM_DEBUG(llvm::dbgs() << "可行解找到:\n";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't commit commented out code. It looks like the LLVM_DEBUG guard is sufficient to have the code in.

// bab
//===----------------------------------------------------------------------===//

using Bounds = std::unordered_map<FieldRef, std::pair<int, int>, FieldRef::Hash>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't checked, but unordered_map is a red flag for deterministic behavior. The code might be fine, but it needs to be carefully checked.

return success();

bool mappingFailed = false;
TypeSwitch<Operation *>(op)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using an OpVisitor.

#include "llvm/ADT/SetVector.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include <boost/graph/adjacency_list.hpp>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM has a dim view of adding external dependencies. These would have to be very carefully considered.

#include "llvm/Support/ErrorHandling.h"
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/floyd_warshall_shortest.hpp>
#include <boost/graph/strong_components.hpp>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a SCC implementation in MLIR which should likely be used.

#include <boost/graph/floyd_warshall_shortest.hpp>
#include <boost/graph/strong_components.hpp>
#include <iostream>
#include <iomanip>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use MLIR/LLVM streams rather than the standard library.

//===----------------------------------------------------------------------===//

// 节点类型
using Node = std::variant<Zero, FieldRef>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider using a sentinel FieldRef. I think one already exists (value null).

        This commit introduces `InferWidths_new` as an alternative implementation to the existing `InferWidths` pass. The new pass utilizes a constraint-based approach [based on graph propagation and is formally verified in Rocq] which aims to:
        - Correctly handle cases like test_new_inferwidths/issue9140_0.mlir and test_new_inferwidths/issue9140_1.mlir where the current MFC implementation fails (issue llvm#9140).
        - Provide an option for the new inferwidths pass to benefit from the potential of formal verification.

        Key changes:
        1. Added `InferWidths_new.cpp` implementing the core algorithm.
        2. Added comprehensive tests in `test_new_inferwidths/…` covering:
           - All existing InferWidths test cases (from test/Dialect/FIRRTL/infer-widths.mlir).
           - Specific cases like example (1) and example (2) in llvm#9140.
        3. Added a new command-line option `-infer-widths-new` to `circt-opt` to enable the new pass.
        4. Updated CMake build files to include the new source.

        This is the first step (adding the alternative pass) as suggested by maintainers. It's worth noting that the current graph propagation algorithm utilizes the Boost library for rapid implementation. If needed in the future, the Boost dependency can be optimized away by implementing our own Tarjan's algorithm and Floyd-Warshall algorithm.

        Related to llvm#9140.
@wky17 wky17 force-pushed the issue-9140-new-inferwidths branch from c9aaa58 to 2f3745a Compare November 24, 2025 03:04
@wky17 wky17 requested a review from darthscsi November 24, 2025 05:46
@wky17
Copy link
Author

wky17 commented Nov 24, 2025

PR Update Summary

Hi maintainers, I've comprehensively updated this PR to address all previous feedback:

  1. Formatting & Structure

    • Fixed all clang-format issues
    • Rebased onto latest upstream
    • Added proper timing via MLIR's timing mechanisms
  2. Implementation Improvements

    • Added new -use-new-infer-widths option in firtool (instead of commenting-out)
    • Replaced unordered_map with DenseMap for deterministic behavior and better performance
    • Removed Boost dependency by using GraphTraits from LLVM
    • Utilized MLIR/LLVM streams instead of standard library iostreams
  3. Semantic Refinements

    • Used LLVM_DEBUG for diagnostic output
    • Used sentinel FieldRef instead of custom Zero variable
    • Added width constraint: w_c <= 1 for condition handling

@llvm/circt-maintainers @seldridge @darthscsi Could you please re-run CI and review when convenient?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

InferWidths Pass Fails on Solvable Width Constraints with Circular Dependencies.

2 participants