Skip to content

Conversation

@prithayan
Copy link
Contributor

@prithayan prithayan commented Nov 7, 2025

This PR adds the HWConstProp pass that performs inter-module constant propagation across hardware module boundaries in the HW dialect.

The pass uses the Sparse Conditional Constant Propagation (SCCP) with lattice-based analysis
Key Steps:

  1. Initialization
    • Mark public module inputs as overdefined (can't be constant)
    • Discover constants from hw.constant operations
    • Mark external/generated module outputs as overdefined
    • Enqueue operations for propagation
  2. Propagation
    • Process operations from two queues (overdefined first, then constants)
    • Propagate constants through:
      • Module outputs → instance results
      • Instance inputs → module arguments
      • Combinational operations (fold when possible)
    • Use lattice meet: unknown ⊔ constant = constant, constant₁ ⊔ constant₂ = overdefined
  3. Cycle Handling
    • Mark remaining unknown values as overdefined
    • Re-propagate to fold newly-resolved constants
  4. Folding
    • Replace SSA values with discovered constants
    • Remove dead operations

@prithayan prithayan requested a review from darthscsi as a code owner November 7, 2025 17:42
Copy link
Member

@uenoku uenoku left a comment

Choose a reason for hiding this comment

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

LGTM, do you think it makes sense to add to Firtool pipeline (with opt out flag) for better coverage? Also does this block constprop across ports with inner symbol?

// CHECK-NEXT: %wire = hw.wire %c42_i8 : i8
// CHECK-NEXT: hw.output %wire : i8
%c42_i8 = hw.constant 42 : i8
%wire = hw.wire %c42_i8 : i8
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this restriction? I think wire without inner symbols can be optimized?

}

def HWConstProp : Pass<
"hw-constprop", "mlir::ModuleOp"
Copy link
Member

Choose a reason for hiding this comment

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

nit: can we name this as imconstprop for consistency with FIRRTL?

Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

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

Partial review. I'd like to give this another look!

#include "mlir/Pass/Pass.h"
#include "llvm/Support/Debug.h"

#define DEBUG_TYPE "hw-constprop"
Copy link
Member

Choose a reason for hiding this comment

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

When changing to imconstprop, make that change here.

Also, I figured out a way to not have to hard-code this, but it requires reorganizing the file a little bit. See how this works here: https://github.com/llvm/circt/blob/main/lib/Dialect/FIRRTL/Transforms/LowerDomains.cpp#L69

void markUnknownValuesOverdefined(hw::HWModuleOp module);
std::pair<unsigned, unsigned> fold();

public:
Copy link
Member

Choose a reason for hiding this comment

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

This public is unnecessary as this is already in a public scope.

Comment on lines +47 to +51
/**
* Returns the lattice value associated with an SSA value.
*
* `std::nullopt` is unknown, `IntegerAttr{}` is overdefined.
*/
Copy link
Member

Choose a reason for hiding this comment

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

Nit: I think this needs doxygen-style comments not Javadoc style, i.e., ///.

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

namespace {
class ConstantPropagation {
Copy link
Member

Choose a reason for hiding this comment

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

Please include comments for this and for all member functions/members.

I'm still grokking why there are three queues here and this would help with that!

Comment on lines +83 to +84
auto module = dyn_cast_or_null<HWModuleOp>(referencedOp);
return !module;
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be:

Suggested change
auto module = dyn_cast_or_null<HWModuleOp>(referencedOp);
return !module;
return !isa_and_nonnull<HWModuleOp>(referencedOp);

Comment on lines +93 to +94
// Mark wires as overdefined since they can be targeted by force.
markOverdefined(wire.getResult());
Copy link
Member

Choose a reason for hiding this comment

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

Is there a less-strict check that can work here? Like, just if this has an inner symbol?

Comment on lines +123 to +127
if (attr) {
valueQueue.push_back(user);
} else {
overdefQueue.push_back(user);
}
Copy link
Member

Choose a reason for hiding this comment

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

Nit:

Suggested change
if (attr) {
valueQueue.push_back(user);
} else {
overdefQueue.push_back(user);
}
if (attr)
valueQueue.push_back(user);
else
overdefQueue.push_back(user);

private:
hw::InstanceGraph &graph;
DenseMap<Value, IntegerAttr> values;
DenseSet<Operation *> inQueue;
Copy link
Member

Choose a reason for hiding this comment

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

Calling this a "queue" is a bit disingenuous.

Comment on lines +132 to +141
std::optional<IntegerAttr> ConstantPropagation::map(Value value) {
if (auto constant = value.getDefiningOp<hw::ConstantOp>())
return constant.getValueAttr();

auto it = values.find(value);
if (it == values.end())
return std::nullopt;

return it->second;
}
Copy link
Member

Choose a reason for hiding this comment

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

Calling this map seems a bit odd given that it's "getMaybeConstant". (That is a terrible name!) Something describing what this is doing would be better.

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.

4 participants