CIRCT  19.0.0git
LegalizeAnonEnums.cpp
Go to the documentation of this file.
1 //===- LegalizeAnonEnums.cpp - Legalizes anonymous enumerations -----------===//
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 pass replaces all anonymous enumeration with typedecls in the output
10 // Verilog.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "ExportVerilogInternals.h"
16 #include "mlir/IR/ImplicitLocOpBuilder.h"
17 #include "mlir/Pass/Pass.h"
18 #include "llvm/ADT/DenseSet.h"
19 
20 namespace circt {
21 #define GEN_PASS_DEF_LEGALIZEANONENUMS
22 #include "circt/Conversion/Passes.h.inc"
23 } // namespace circt
24 
25 using namespace circt;
26 using namespace hw;
27 using namespace sv;
28 
29 namespace {
30 struct LegalizeAnonEnums
31  : public circt::impl::LegalizeAnonEnumsBase<LegalizeAnonEnums> {
32  /// Creates a TypeScope on demand for anonymous enumerations.
33  TypeScopeOp getTypeScope() {
34  auto topLevel = getOperation();
35  if (!typeScope) {
36  auto builder = OpBuilder::atBlockBegin(&topLevel.getRegion().front());
37  typeScope = builder.create<TypeScopeOp>(topLevel.getLoc(), "Enums");
38  typeScope.getBodyRegion().push_back(new Block());
39  mlir::SymbolTable symbolTable(topLevel);
40  symbolTable.insert(typeScope);
41  }
42  return typeScope;
43  }
44 
45  /// Helper to create TypeDecls and TypeAliases for EnumTypes;
46  Type getEnumTypeDecl(EnumType type) {
47  auto &typeAlias = enumTypeAliases[type];
48  if (typeAlias)
49  return typeAlias;
50  auto *context = &getContext();
51  auto loc = UnknownLoc::get(context);
52  auto typeScope = getTypeScope();
53  auto builder = OpBuilder::atBlockEnd(&typeScope.getRegion().front());
54  auto declName = StringAttr::get(context, "enum" + Twine(enumCount++));
55  builder.create<TypedeclOp>(loc, declName, TypeAttr::get(type), nullptr);
56  auto symRef = SymbolRefAttr::get(typeScope.getSymNameAttr(),
57  FlatSymbolRefAttr::get(declName));
58  typeAlias = TypeAliasType::get(symRef, type);
59  return typeAlias;
60  }
61 
62  /// Process a type, replacing any anonymous enumerations contained within.
63  Type processType(Type type) {
64  auto *context = &getContext();
65  if (auto structType = dyn_cast<StructType>(type)) {
66  bool changed = false;
67  SmallVector<StructType::FieldInfo> fields;
68  for (auto &element : structType.getElements()) {
69  if (auto newFieldType = processType(element.type)) {
70  changed = true;
71  fields.push_back({element.name, newFieldType});
72  } else {
73  fields.push_back(element);
74  }
75  }
76  if (changed)
77  return StructType::get(context, fields);
78  return {};
79  }
80 
81  if (auto arrayType = dyn_cast<ArrayType>(type)) {
82  if (auto newElementType = processType(arrayType.getElementType()))
83  return ArrayType::get(newElementType, arrayType.getNumElements());
84  return {};
85  }
86 
87  if (auto unionType = dyn_cast<UnionType>(type)) {
88  bool changed = false;
89  SmallVector<UnionType::FieldInfo> fields;
90  for (const auto &element : unionType.getElements()) {
91  if (auto newFieldType = processType(element.type)) {
92  fields.push_back({element.name, newFieldType, element.offset});
93  changed = true;
94  } else {
95  fields.push_back(element);
96  }
97  }
98  if (changed)
99  return UnionType::get(context, fields);
100  return {};
101  }
102 
103  if (auto typeAlias = dyn_cast<TypeAliasType>(type)) {
104  // Enum type aliases have already been handled.
105  if (isa<EnumType>(typeAlias.getInnerType()))
106  return {};
107  // Otherwise recursively update the type alias.
108  return processType(typeAlias.getInnerType());
109  }
110 
111  if (auto inoutType = dyn_cast<InOutType>(type)) {
112  if (auto newType = processType(inoutType.getElementType()))
113  return InOutType::get(newType);
114  return {};
115  }
116 
117  // EnumTypes must be changed into TypeAlias.
118  if (auto enumType = dyn_cast<EnumType>(type))
119  return getEnumTypeDecl(enumType);
120 
121  if (auto funcType = dyn_cast<FunctionType>(type)) {
122  bool changed = false;
123  SmallVector<Type> inputs;
124  for (auto &type : funcType.getInputs()) {
125  if (auto newType = processType(type)) {
126  inputs.push_back(newType);
127  changed = true;
128  } else {
129  inputs.push_back(type);
130  }
131  }
132  SmallVector<Type> results;
133  for (auto &type : funcType.getResults()) {
134  if (auto newType = processType(type)) {
135  results.push_back(newType);
136  changed = true;
137  } else {
138  results.push_back(type);
139  }
140  }
141  if (changed)
142  return FunctionType::get(context, inputs, results);
143  return {};
144  }
145  if (auto modType = dyn_cast<ModuleType>(type)) {
146  bool changed = false;
147  SmallVector<ModulePort> ports;
148  for (auto &p : modType.getPorts()) {
149  ports.push_back(p);
150  if (auto newType = processType(p.type)) {
151  ports.back().type = newType;
152  changed = true;
153  }
154  }
155  if (changed)
156  return ModuleType::get(context, ports);
157  return {};
158  }
159 
160  // Default case is that it is not an aggregate type.
161  return {};
162  };
163 
164  void runOnOperation() override {
165  enumCount = 0;
166  typeScope = {};
167 
168  // Perform the actual walk looking for anonymous enumeration types.
169  getOperation().walk([&](Operation *op) {
170  // If this is a constant operation, make sure to update the constant
171  // to reference the typedef, otherwise we will emit the wrong constant.
172  // Theoretically we should be searching all attributes on every operation
173  // for EnumFieldAttrs.
174  if (auto enumConst = dyn_cast<EnumConstantOp>(op)) {
175  auto fieldAttr = enumConst.getField();
176  if (auto newType = processType(fieldAttr.getType().getValue()))
177  enumConst.setFieldAttr(
178  EnumFieldAttr::get(op->getLoc(), fieldAttr.getField(), newType));
179  }
180 
181  // Update the operation signature if it is function-like.
182  if (auto modLike = dyn_cast<HWModuleLike>(op))
183  if (auto newType = processType(modLike.getHWModuleType()))
184  modLike.setHWModuleType(cast<ModuleType>(newType));
185 
186  // Update all operations results.
187  for (auto result : op->getResults())
188  if (auto newType = processType(result.getType()))
189  result.setType(newType);
190 
191  // Update all block arguments.
192  for (auto &region : op->getRegions())
193  for (auto &block : region.getBlocks())
194  for (auto arg : block.getArguments())
195  if (auto newType = processType(arg.getType()))
196  arg.setType(newType);
197  });
198 
199  enumTypeAliases.clear();
200  }
201 
202  TypeScopeOp typeScope;
203  unsigned enumCount;
204  DenseMap<Type, Type> enumTypeAliases;
205 };
206 
207 } // end anonymous namespace
208 
209 std::unique_ptr<mlir::Pass> circt::createLegalizeAnonEnumsPass() {
210  return std::make_unique<LegalizeAnonEnums>();
211 }
static Type processType(Type type)
Definition: FreezePaths.cpp:91
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 > createLegalizeAnonEnumsPass()
Definition: hw.py:1
Definition: sv.py:1