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