20#include "mlir/IR/Builders.h"
21#include "mlir/IR/IRMapping.h"
22#include "mlir/IR/PatternMatch.h"
23#include "mlir/Pass/Pass.h"
24#include "mlir/Transforms/DialectConversion.h"
25#include "llvm/ADT/TypeSwitch.h"
29#define GEN_PASS_DEF_HWSPECIALIZE
30#include "circt/Dialect/HW/Passes.h.inc"
43 ArrayAttr parameters) {
44 assert(parameters.size() != 0);
45 std::string name = moduleOp.getName().str();
46 for (
auto param : parameters) {
47 auto paramAttr = cast<ParamDeclAttr>(param);
48 int64_t paramValue = cast<IntegerAttr>(paramAttr.getValue()).getInt();
49 name +=
"_" + paramAttr.getName().str() +
"_" + std::to_string(paramValue);
57static bool isParametricOp(Operation *op) {
64static FailureOr<Value> narrowValueToArrayWidth(OpBuilder &builder, Value array,
66 OpBuilder::InsertionGuard g(builder);
67 builder.setInsertionPointAfterValue(value);
68 auto arrayType = cast<hw::ArrayType>(array.getType());
69 unsigned hiBit = llvm::Log2_64_Ceil(arrayType.getNumElements());
73 APInt(arrayType.getNumElements(), 0))
82 auto *targetOp = sc.
getDefinition(instanceOp.getModuleNameAttr());
83 auto targetHWModule = dyn_cast<hw::HWModuleOp>(targetOp);
87 if (targetHWModule.getParameters().size() == 0)
90 return targetHWModule;
94struct ParameterSpecializationRegistry {
95 llvm::MapVector<hw::HWModuleOp, llvm::SetVector<ArrayAttr>>
96 uniqueModuleParameters;
98 bool isRegistered(hw::HWModuleOp moduleOp, ArrayAttr parameters)
const {
99 const auto *it = uniqueModuleParameters.find(moduleOp);
100 return it != uniqueModuleParameters.end() &&
101 it->second.contains(parameters);
104 void registerModuleOp(hw::HWModuleOp moduleOp, ArrayAttr parameters) {
105 uniqueModuleParameters[moduleOp].insert(parameters);
109struct EliminateParamValueOpPattern :
public OpRewritePattern<ParamValueOp> {
110 EliminateParamValueOpPattern(MLIRContext *
context, ArrayAttr parameters)
111 : OpRewritePattern<ParamValueOp>(
context), parameters(parameters) {}
113 LogicalResult matchAndRewrite(ParamValueOp op,
114 PatternRewriter &rewriter)
const override {
116 FailureOr<Attribute> evaluated =
118 if (failed(evaluated))
120 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
122 cast<IntegerAttr>(*evaluated).getValue().getSExtValue());
126 ArrayAttr parameters;
134 using OpConversionPattern<ArrayGetOp>::OpConversionPattern;
137 matchAndRewrite(ArrayGetOp op, OpAdaptor adaptor,
138 ConversionPatternRewriter &rewriter)
const override {
140 Type targetIndexType = IntegerType::get(
141 getContext(), inputType.getNumElements() == 1
143 : llvm::Log2_64_Ceil(inputType.getNumElements()));
145 if (op.getIndex().getType().getIntOrFloatBitWidth() ==
146 targetIndexType.getIntOrFloatBitWidth())
150 FailureOr<Value> narrowedIndex =
151 narrowValueToArrayWidth(rewriter, op.getInput(), op.getIndex());
152 if (failed(narrowedIndex))
154 rewriter.replaceOpWithNewOp<ArrayGetOp>(op, op.getInput(), *narrowedIndex);
161 ParametricTypeConversionPattern(MLIRContext *ctx,
162 TypeConverter &typeConverter,
163 ArrayAttr parameters)
164 : ConversionPattern(typeConverter, MatchAnyOpTypeTag(), 1,
166 parameters(parameters) {}
169 matchAndRewrite(Operation *op, ArrayRef<Value> operands,
170 ConversionPatternRewriter &rewriter)
const override {
171 llvm::SmallVector<Value, 4> convertedOperands;
174 rewriter.modifyOpInPlace(op, [&]() {
176 for (
auto it : llvm::enumerate(op->getResultTypes())) {
177 FailureOr<Type> res =
179 ok &= succeeded(res);
182 op->getResult(it.index()).setType(*res);
188 op->setOperands(operands);
193 ArrayAttr parameters;
196struct HWSpecializePass
197 :
public circt::hw::impl::HWSpecializeBase<HWSpecializePass> {
198 void runOnOperation()
override;
202 ArrayAttr parameters) {
204 typeConverter.addConversion([=](hw::IntType type) {
207 typeConverter.addConversion([=](hw::ArrayType type) {
212 typeConverter.addConversion([](mlir::IntegerType type) {
return type; });
217static LogicalResult registerNestedParametricInstanceOps(
219 const ParameterSpecializationRegistry ¤tRegistry,
220 ParameterSpecializationRegistry &nextRegistry,
222 llvm::DenseMap<ArrayAttr, llvm::SmallVector<hw::InstanceOp>>>
225 auto walkResult = target->walk([&](InstanceOp instanceOp) -> WalkResult {
226 auto instanceParameters = instanceOp.getParameters();
228 if (instanceParameters.empty())
229 return WalkResult::advance();
232 llvm::SmallVector<Attribute> evaluatedInstanceParameters;
233 evaluatedInstanceParameters.reserve(instanceParameters.size());
234 for (
auto instanceParameter : instanceParameters) {
235 auto instanceParameterDecl = cast<hw::ParamDeclAttr>(instanceParameter);
236 auto instanceParameterValue = instanceParameterDecl.getValue();
238 instanceParameterValue);
239 if (failed(evaluated))
240 return WalkResult::interrupt();
241 evaluatedInstanceParameters.push_back(
242 hw::ParamDeclAttr::get(instanceParameterDecl.getName(), *evaluated));
245 auto evaluatedInstanceParametersAttr =
246 ArrayAttr::get(target.getContext(), evaluatedInstanceParameters);
248 if (
auto targetHWModule = targetModuleOp(instanceOp, sc)) {
249 if (!currentRegistry.isRegistered(targetHWModule,
250 evaluatedInstanceParametersAttr))
251 nextRegistry.registerModuleOp(targetHWModule,
252 evaluatedInstanceParametersAttr);
253 parametersUsers[targetHWModule][evaluatedInstanceParametersAttr]
254 .push_back(instanceOp);
257 return WalkResult::advance();
260 return failure(walkResult.wasInterrupted());
271static LogicalResult specializeModule(
274 const ParameterSpecializationRegistry ¤tRegistry,
275 ParameterSpecializationRegistry &nextRegistry,
277 llvm::DenseMap<ArrayAttr, llvm::SmallVector<hw::InstanceOp>>>
279 auto *ctx = builder.getContext();
283 for (
auto in : llvm::enumerate(source.getInputTypes())) {
284 FailureOr<Type> resType =
288 ports.atInput(in.index()).type = *resType;
290 for (
auto out : llvm::enumerate(source.getOutputTypes())) {
291 FailureOr<Type> resolvedType =
293 if (failed(resolvedType))
295 ports.atOutput(out.index()).type = *resolvedType;
299 target = HWModuleOp::create(
300 builder, source.getLoc(),
301 StringAttr::get(ctx, generateModuleName(ns, source, parameters)), ports);
305 (*target.getOps<hw::OutputOp>().begin()).erase();
311 for (
auto &&[src, dst] : llvm::zip(source.getBodyBlock()->getArguments(),
312 target.getBodyBlock()->getArguments()))
313 mapper.set(src, dst);
314 builder.setInsertionPointToStart(target.getBodyBlock());
316 for (
auto &op : source.getOps()) {
318 for (
auto operand : op.getOperands())
319 bvMapper.map(operand, mapper.get(operand));
320 auto *newOp = builder.clone(op, bvMapper);
321 for (
auto &&[oldRes, newRes] :
322 llvm::zip(op.getResults(), newOp->getResults()))
323 mapper.set(oldRes, newRes);
327 auto nestedRegistrationResult = registerNestedParametricInstanceOps(
328 target, parameters, sc, currentRegistry, nextRegistry, parametersUsers);
329 if (failed(nestedRegistrationResult))
338 patterns.add<EliminateParamValueOpPattern>(ctx, parameters);
339 patterns.add<NarrowArrayGetIndexPattern>(ctx);
340 patterns.add<ParametricTypeConversionPattern>(ctx, t, parameters);
343 convTarget.addIllegalOp<hw::ParamValueOp>();
346 convTarget.markUnknownOpDynamicallyLegal(
347 [](Operation *op) {
return !isParametricOp(op); });
349 return applyPartialConversion(target, convTarget, std::move(
patterns));
352void HWSpecializePass::runOnOperation() {
353 ModuleOp module = getOperation();
356 llvm::DenseMap<hw::HWModuleOp,
357 llvm::DenseMap<ArrayAttr, llvm::SmallVector<hw::InstanceOp>>>
359 ParameterSpecializationRegistry registry;
367 for (
auto hwModule : module.getOps<hw::HWModuleOp>()) {
370 if (!hwModule.getParameters().empty())
372 for (
auto instanceOp : hwModule.getOps<hw::InstanceOp>()) {
373 if (
auto targetHWModule = targetModuleOp(instanceOp, sc)) {
374 auto parameters = instanceOp.getParameters();
375 registry.registerModuleOp(targetHWModule, parameters);
377 parametersUsers[targetHWModule][parameters].push_back(instanceOp);
383 OpBuilder builder = OpBuilder(&getContext());
384 builder.setInsertionPointToStart(module.getBody());
385 llvm::DenseMap<hw::HWModuleOp, llvm::DenseMap<ArrayAttr, hw::HWModuleOp>>
391 while (!registry.uniqueModuleParameters.empty()) {
393 ParameterSpecializationRegistry nextRegistry;
394 for (
auto it : registry.uniqueModuleParameters) {
395 for (
auto parameters : it.second) {
396 HWModuleOp specializedModule;
397 if (failed(specializeModule(builder, parameters, sc, ns, it.first,
398 specializedModule, registry, nextRegistry,
405 sc.
addDefinition(specializedModule.getNameAttr(), specializedModule);
408 specializations[it.first][parameters] = specializedModule;
413 registry.uniqueModuleParameters =
414 std::move(nextRegistry.uniqueModuleParameters);
418 for (
auto it : specializations) {
419 auto unspecialized = it.getFirst();
420 auto &users = parametersUsers[unspecialized];
421 for (
auto specialization : it.getSecond()) {
422 auto parameters = specialization.getFirst();
423 auto specializedModule = specialization.getSecond();
424 for (
auto instanceOp : users[parameters]) {
425 instanceOp->setAttr(
"moduleName",
426 FlatSymbolRefAttr::get(specializedModule));
427 instanceOp->setAttr(
"parameters", ArrayAttr::get(&getContext(), {}));
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static void populateTypeConversion(TypeConverter &converter)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
Instantiate one of these and use it to build typed backedges.
A namespace that is used to store existing names and generate new names in some scope within the IR.
void add(mlir::ModuleOp module)
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
void addDefinition(mlir::Attribute key, mlir::Operation *op) override
In the building phase, add symbols.
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
The ValueMapper class facilitates the definition and connection of SSA def-use chains between two loc...
mlir::FailureOr< mlir::TypedAttr > evaluateParametricAttr(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Attribute paramAttr, bool emitErrors=true)
Evaluates a parametric attribute (param.decl.ref/param.expr) based on a set of provided parameter val...
BaseTy type_cast(Type type)
mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type, bool emitErrors=true)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
bool isParametricType(mlir::Type t)
Returns true if any part of t is parametric.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds a decoded list of input/inout and output ports for a module or instance.