CIRCT 23.0.0git
Loading...
Searching...
No Matches
PopulateInstanceChoiceSymbols.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 defines the PopulateInstanceChoiceSymbols pass, which populates
10// globally unique instance macros for all instance choice operations.
11//
12//===----------------------------------------------------------------------===//
13
20#include "mlir/Pass/Pass.h"
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/Support/Debug.h"
23
24#define DEBUG_TYPE "firrtl-populate-instance-choice-symbols"
25
26namespace circt {
27namespace firrtl {
28#define GEN_PASS_DEF_POPULATEINSTANCECHOICESYMBOLS
29#include "circt/Dialect/FIRRTL/Passes.h.inc"
30} // namespace firrtl
31} // namespace circt
32
33using namespace circt;
34using namespace firrtl;
35
36namespace {
37
38void getOptionCaseMacroName(StringAttr optionName, StringAttr caseName,
39 SmallVectorImpl<char> &macroName) {
40 llvm::raw_svector_ostream os(macroName);
41 os << "targets$" << optionName.getValue() << "$" << caseName.getValue();
42}
43
44class PopulateInstanceChoiceSymbolsPass
45 : public impl::PopulateInstanceChoiceSymbolsBase<
46 PopulateInstanceChoiceSymbolsPass> {
47public:
48 void runOnOperation() override;
49
50private:
51 /// Assign a unique instance macro symbol to the given instance choice
52 /// operation. Returns the assigned symbol, or nullptr if the operation
53 /// already has a symbol.
54 FlatSymbolRefAttr assignSymbol(InstanceChoiceOp op);
55
56 /// The namespace associated with the circuit. This is lazily constructed
57 /// using `getNamespace`.
58 std::optional<CircuitNamespace> circuitNamespace;
59 CircuitNamespace &getNamespace() {
60 if (!circuitNamespace)
61 circuitNamespace = CircuitNamespace(getOperation());
62 return *circuitNamespace;
63 }
64};
65} // namespace
66
67FlatSymbolRefAttr
68PopulateInstanceChoiceSymbolsPass::assignSymbol(InstanceChoiceOp op) {
69 // Skip if already has an instance macro.
70 if (op.getInstanceMacroAttr())
71 return nullptr;
72
73 // Get the parent module name.
74 auto parentModule = op->getParentOfType<FModuleLike>();
75
76 // Get the option name.
77 auto optionName = op.getOptionNameAttr();
78
79 // Generate the instance macro name.
80 // This is not public API and can be generated in any way as long as it's
81 // unique.
82 SmallString<128> instanceMacroName;
83 {
84 llvm::raw_svector_ostream os(instanceMacroName);
85 os << "targets$" << optionName.getValue() << "$"
86 << parentModule.getModuleName() << "$" << op.getInstanceName();
87 }
88
89 // Ensure global uniqueness using CircuitNamespace.
90 auto uniqueName = StringAttr::get(op.getContext(),
91 getNamespace().newName(instanceMacroName));
92 auto instanceMacro = FlatSymbolRefAttr::get(uniqueName);
93 op.setInstanceMacroAttr(instanceMacro);
94
95 LLVM_DEBUG(llvm::dbgs() << "Assigned instance macro '" << uniqueName
96 << "' to instance choice '" << op.getInstanceName()
97 << "' in module '" << parentModule.getModuleName()
98 << "'\n");
99
100 return instanceMacro;
101}
102
103void PopulateInstanceChoiceSymbolsPass::runOnOperation() {
104 auto circuit = getOperation();
105 auto &instanceGraph = getAnalysis<InstanceGraph>();
106 auto &symbolTable = getAnalysis<SymbolTable>();
107
108 OpBuilder builder(circuit.getContext());
109 builder.setInsertionPointToStart(circuit.getBodyBlock());
110
111 llvm::DenseSet<StringAttr> createdInstanceMacros;
112 bool changed = false;
113
114 // First, walk all OptionOps and assign case macros to OptionCaseOps.
115 for (auto optionOp : circuit.getOps<OptionOp>()) {
116 auto optionName = optionOp.getSymNameAttr();
117
118 for (auto caseOp : optionOp.getOps<OptionCaseOp>()) {
119 // Skip if already has a case macro.
120 if (caseOp.getCaseMacroAttr())
121 continue;
122
123 auto caseName = caseOp.getSymNameAttr();
124 SmallString<128> caseMacroName;
125 getOptionCaseMacroName(optionName, caseName, caseMacroName);
126 auto caseMacro =
127 FlatSymbolRefAttr::get(circuit.getContext(), caseMacroName);
128
129 // Check if this ABI-defined macro name conflicts with existing symbols.
130 if (auto *existingSymbol = symbolTable.lookup(caseMacroName)) {
131 // If the existing symbol is a macro, we can reuse it.
132 if (auto existingMacro = dyn_cast<sv::MacroDeclOp>(existingSymbol)) {
133 caseOp.setCaseMacroAttr(caseMacro);
134 changed = true;
135 continue;
136 }
137 // Otherwise, it's a conflict.
138 caseOp.emitError() << "case macro name conflicts with existing symbol '"
139 << caseMacroName << "' (existing symbol is '"
140 << existingSymbol->getName() << "')";
141 return signalPassFailure();
142 }
143
144 // Set the case_macro attribute on the OptionCaseOp.
145 caseOp.setCaseMacroAttr(caseMacro);
146 changed = true;
147
148 // Create macro declaration.
149 auto macroDecl = sv::MacroDeclOp::create(builder, circuit.getLoc(),
150 caseMacro.getValue());
151 auto symbolName = symbolTable.insert(macroDecl);
152 (void)symbolName;
153 assert(symbolName.getValue() == caseMacroName &&
154 "Symbol must have been inserted with the expected name");
155
156 LLVM_DEBUG(llvm::dbgs() << "Assigned case macro '" << caseMacro.getValue()
157 << "' to option case '" << caseName
158 << "' in option '" << optionName << "'\n");
159 }
160 }
161
162 // Second, iterate through all instance choices and assign instance macros.
163 instanceGraph.walkPostOrder([&](igraph::InstanceGraphNode &node) {
164 auto module = dyn_cast<FModuleLike>(node.getModule().getOperation());
165 if (!module)
166 return;
167
168 for (auto *record : node) {
169 auto op = record->getInstance<InstanceChoiceOp>();
170 if (!op)
171 continue;
172
173 auto instanceMacro = assignSymbol(op);
174 if (!instanceMacro)
175 continue;
176 changed = true;
177
178 // Create instance macro declaration only if we haven't created it yet.
179 if (createdInstanceMacros.insert(instanceMacro.getAttr()).second) {
180 auto decl = sv::MacroDeclOp::create(builder, circuit.getLoc(),
181 instanceMacro.getAttr());
182 symbolTable.insert(decl);
183 }
184 }
185 });
186
187 circuitNamespace.reset();
188 if (!changed)
189 return markAllAnalysesPreserved();
190
191 markAnalysesPreserved<InstanceGraph, InstanceInfo, SymbolTable>();
192}
assert(baseType &&"element must be base type")
This is a Node in the InstanceGraph.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The namespace of a CircuitOp, generally inhabited by modules.
Definition Namespace.h:24