CIRCT  19.0.0git
HWLowerInstanceChoices.cpp
Go to the documentation of this file.
1 //===- HWLowerInstanceChoices.cpp - IR Prepass for Emitter ----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the "prepare" pass that walks the IR before the emitter
10 // gets involved. This allows us to do some transformations that would be
11 // awkward to implement inline in the emitter.
12 //
13 // NOTE: This file covers the preparation phase of `ExportVerilog` which mainly
14 // legalizes the IR and makes adjustments necessary for emission. This is the
15 // place to mutate the IR if emission needs it. The IR cannot be modified during
16 // emission itself, which happens in parallel.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "../PassDetail.h"
21 #include "ExportVerilogInternals.h"
23 #include "circt/Dialect/HW/HWOps.h"
24 #include "circt/Dialect/SV/SVOps.h"
25 #include "circt/Support/LLVM.h"
27 #include "circt/Support/SymCache.h"
28 #include "mlir/IR/ImplicitLocOpBuilder.h"
29 #include "mlir/IR/SymbolTable.h"
30 
31 using namespace mlir;
32 using namespace circt;
33 using namespace hw;
34 using namespace sv;
35 using namespace ExportVerilog;
36 
37 namespace {
38 
39 struct HWLowerInstanceChoicesPass
40  : public HWLowerInstanceChoicesBase<HWLowerInstanceChoicesPass> {
41  void runOnOperation() override;
42 };
43 
44 } // end anonymous namespace
45 
46 LogicalResult ExportVerilog::lowerHWInstanceChoices(mlir::ModuleOp module) {
47  // Collect all instance choices & symbols.
48  SmallVector<InstanceChoiceOp> instances;
49  SymbolCache symCache;
50  for (Operation &op : *module.getBody()) {
51  if (auto sym = dyn_cast<SymbolOpInterface>(&op))
52  symCache.addSymbol(sym);
53 
54  if (auto module = dyn_cast<HWModuleOp>(&op))
55  module.walk([&](InstanceChoiceOp inst) { instances.push_back(inst); });
56  }
57 
58  // Build a namespace to generate unique macro names.
59  Namespace ns;
60  ns.add(symCache);
61 
62  auto declBuilder = OpBuilder::atBlockBegin(module.getBody());
63  for (InstanceChoiceOp inst : instances) {
64  auto parent = inst->getParentOfType<HWModuleOp>();
65 
66  auto defaultModuleOp = cast<HWModuleLike>(
67  symCache.getDefinition(inst.getDefaultModuleNameAttr()));
68 
69  // Generate a macro name to describe the target of this instance.
70  SmallString<128> name;
71  {
72  llvm::raw_svector_ostream os(name);
73  os << "__circt_choice_" << parent.getName() << "_"
74  << inst.getInstanceName();
75  }
76 
77  auto symName = ns.newName(name);
78  auto symNameAttr = declBuilder.getStringAttr(symName);
79  auto symRef = FlatSymbolRefAttr::get(symNameAttr);
80  declBuilder.create<MacroDeclOp>(inst.getLoc(), symNameAttr,
81  /*args=*/ArrayAttr{},
82  /*verilogName=*/StringAttr{});
83 
84  // This pass now generates the macros and attaches them to the instance
85  // choice as an attribute. As a better solution, this pass should be moved
86  // out of the umbrella of ExportVerilog and it should lower the `hw`
87  // instance choices to a better SV-level representation of the operation.
88  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
89  builder.create<sv::IfDefOp>(
90  symName, [&] {},
91  [&] {
92  builder.create<sv::MacroDefOp>(
93  symRef, builder.getStringAttr("{{0}}"),
94  builder.getArrayAttr(
95  {FlatSymbolRefAttr::get(defaultModuleOp.getNameAttr())}));
96  });
97  inst->setAttr("hw.choiceTarget", symRef);
98  }
99 
100  return success();
101 }
102 
103 void HWLowerInstanceChoicesPass::runOnOperation() {
104  ModuleOp module = getOperation();
105  if (failed(lowerHWInstanceChoices(module)))
106  signalPassFailure();
107 }
108 
109 std::unique_ptr<mlir::Pass> circt::createHWLowerInstanceChoicesPass() {
110  return std::make_unique<HWLowerInstanceChoicesPass>();
111 }
Builder builder
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:29
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
Definition: Namespace.h:47
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition: Namespace.h:63
void addSymbol(mlir::SymbolOpInterface op)
Adds the symbol-defining 'op' to the cache.
Definition: SymCache.h:33
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition: SymCache.h:85
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition: SymCache.h:94
Definition: sv.py:15
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoicesPass()
Definition: hw.py:1
Definition: sv.py:1