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).

@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?

@wky17 wky17 force-pushed the issue-9140-new-inferwidths branch 4 times, most recently from 3c15856 to 221639e Compare December 8, 2025 02:04
@wky17 wky17 force-pushed the issue-9140-new-inferwidths branch 2 times, most recently from 1fe9115 to b837c49 Compare December 11, 2025 03:03
@wky17
Copy link
Author

wky17 commented Dec 11, 2025

@schulyer Hi! I have resolved the conflict in Firtool.cpp, please re-run again!

@wky17
Copy link
Author

wky17 commented Dec 11, 2025

@seldridge @darthscsi Hi! I have resolved the conflict in Firtool.cpp, please re-run again!

@wky17 wky17 force-pushed the issue-9140-new-inferwidths branch from b837c49 to 0f16622 Compare December 11, 2025 09:55
@wky17
Copy link
Author

wky17 commented Dec 11, 2025

@seldridge Hi! I have resolved the build error. I think the reason for the error is that my local environment is different from the Docker test environment. Please re-run again!

@wky17 wky17 force-pushed the issue-9140-new-inferwidths branch from 0f16622 to d1856cc Compare December 12, 2025 01:59
@wky17
Copy link
Author

wky17 commented Dec 12, 2025

@seldridge @darthscsi Dear reviewers,
All CI checks have passed after the recent fixes. Could you please review this PR when you have time?
Thank you for your time and consideration!

}
};

#include <list>
Copy link
Member

Choose a reason for hiding this comment

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

nit: Please include header at the top of file

Comment on lines +417 to +419
std::deque<Node> nodes;
DenseMap<FieldRef, Node *> nodeMap;

Copy link
Member

Choose a reason for hiding this comment

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

Isn't nodes reallocated when deque size is modified? If so I think nodeMap could refer to invalided node addresses. Certainly using bump allocator or unique_ptr is better here.

Comment on lines 752 to 761
LLVM_DEBUG(llvm::dbgs() << "initial matrix:\n");
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j] == INF)
LLVM_DEBUG(llvm::dbgs() << " INF ");
else
LLVM_DEBUG(llvm::dbgs() << " " << graph[i][j] << " ");
}
LLVM_DEBUG(llvm::dbgs() << "\n");
}
Copy link
Member

Choose a reason for hiding this comment

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

nit: you can put entire for loops under LLVM_DEBUG

Suggested change
LLVM_DEBUG(llvm::dbgs() << "initial matrix:\n");
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j] == INF)
LLVM_DEBUG(llvm::dbgs() << " INF ");
else
LLVM_DEBUG(llvm::dbgs() << " " << graph[i][j] << " ");
}
LLVM_DEBUG(llvm::dbgs() << "\n");
}
LLVM_DEBUG({
lvm::dbgs() << "initial matrix:\n";
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j] == INF)
llvm::dbgs() << " INF ";
else
llvm::dbgs() << " " << graph[i][j] << " ";
}
llvm::dbgs() << "\n";
}
});

Comment on lines 1 to 2
//===- InferWidths_new.cpp - Infer width of types -------------------*- C++
//-*-===//
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
//===- InferWidths_new.cpp - Infer width of types -------------------*- C++
//-*-===//
//===----------------------------------------------------------------------===//

@uenoku
Copy link
Member

uenoku commented Dec 15, 2025

Could you add

circt-opt --pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-widths-new))' --verify-diagnostics %s | FileCheck %s 

to

// RUN: circt-opt --pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-widths))' --verify-diagnostics %s | FileCheck %s
and see if it passes?

}
};

class Constraint1 {
Copy link
Member

Choose a reason for hiding this comment

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

Do you mind if you could add comments for Constraint1/2/Min? At the moment I need to guess from output "&operator<<(" ...

@wky17 wky17 force-pushed the issue-9140-new-inferwidths branch from 478d652 to cc26c73 Compare December 15, 2025 11:24
wky17 added 16 commits December 16, 2025 13:09
        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 cc26c73 to a48e50b Compare December 16, 2025 05:10
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.

3 participants