CIRCT  19.0.0git
Structure.cpp
Go to the documentation of this file.
1 //===- Structure.cpp - Slang hierarchy conversion -------------------------===//
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 
10 #include "slang/ast/Compilation.h"
11 
12 using namespace circt;
13 using namespace ImportVerilog;
14 
15 //===----------------------------------------------------------------------===//
16 // Module Member Conversion
17 //===----------------------------------------------------------------------===//
18 
19 static moore::ProcedureKind
20 convertProcedureKind(slang::ast::ProceduralBlockKind kind) {
21  switch (kind) {
22  case slang::ast::ProceduralBlockKind::Always:
23  return moore::ProcedureKind::Always;
24  case slang::ast::ProceduralBlockKind::AlwaysComb:
25  return moore::ProcedureKind::AlwaysComb;
26  case slang::ast::ProceduralBlockKind::AlwaysLatch:
27  return moore::ProcedureKind::AlwaysLatch;
28  case slang::ast::ProceduralBlockKind::AlwaysFF:
29  return moore::ProcedureKind::AlwaysFF;
30  case slang::ast::ProceduralBlockKind::Initial:
31  return moore::ProcedureKind::Initial;
32  case slang::ast::ProceduralBlockKind::Final:
33  return moore::ProcedureKind::Final;
34  }
35  llvm_unreachable("all procedure kinds handled");
36 }
37 
38 static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind) {
39  switch (kind) {
40  case slang::ast::NetType::Supply0:
41  return moore::NetKind::Supply0;
42  case slang::ast::NetType::Supply1:
43  return moore::NetKind::Supply1;
44  case slang::ast::NetType::Tri:
45  return moore::NetKind::Tri;
46  case slang::ast::NetType::TriAnd:
47  return moore::NetKind::TriAnd;
48  case slang::ast::NetType::TriOr:
49  return moore::NetKind::TriOr;
50  case slang::ast::NetType::TriReg:
51  return moore::NetKind::TriReg;
52  case slang::ast::NetType::Tri0:
53  return moore::NetKind::Tri0;
54  case slang::ast::NetType::Tri1:
55  return moore::NetKind::Tri1;
56  case slang::ast::NetType::UWire:
57  return moore::NetKind::UWire;
58  case slang::ast::NetType::Wire:
59  return moore::NetKind::Wire;
60  case slang::ast::NetType::WAnd:
61  return moore::NetKind::WAnd;
62  case slang::ast::NetType::WOr:
63  return moore::NetKind::WOr;
64  case slang::ast::NetType::Interconnect:
65  return moore::NetKind::Interconnect;
66  case slang::ast::NetType::UserDefined:
67  return moore::NetKind::UserDefined;
68  case slang::ast::NetType::Unknown:
69  return moore::NetKind::Unknown;
70  }
71  llvm_unreachable("all net kinds handled");
72 }
73 
74 namespace {
75 struct MemberVisitor {
76  Context &context;
77  Location loc;
78  OpBuilder &builder;
79 
80  MemberVisitor(Context &context, Location loc)
81  : context(context), loc(loc), builder(context.builder) {}
82 
83  // Skip empty members (stray semicolons).
84  LogicalResult visit(const slang::ast::EmptyMemberSymbol &) {
85  return success();
86  }
87 
88  // Skip members that are implicitly imported from some other scope for the
89  // sake of name resolution, such as enum variant names.
90  LogicalResult visit(const slang::ast::TransparentMemberSymbol &) {
91  return success();
92  }
93 
94  // Skip typedefs.
95  LogicalResult visit(const slang::ast::TypeAliasType &) { return success(); }
96 
97  // Handle instances.
98  LogicalResult visit(const slang::ast::InstanceSymbol &instNode) {
99  auto targetModule = context.convertModuleHeader(&instNode.body);
100  if (!targetModule)
101  return failure();
102 
103  builder.create<moore::InstanceOp>(
104  loc, builder.getStringAttr(instNode.name),
105  FlatSymbolRefAttr::get(targetModule.getSymNameAttr()));
106 
107  return success();
108  }
109 
110  // Handle variables.
111  LogicalResult visit(const slang::ast::VariableSymbol &varNode) {
112  auto loweredType = context.convertType(*varNode.getDeclaredType());
113  if (!loweredType)
114  return failure();
115 
116  Value initial;
117  if (const auto *init = varNode.getInitializer()) {
118  initial = context.convertExpression(*init);
119  if (!initial)
120  return failure();
121 
122  if (initial.getType() != loweredType)
123  initial =
124  builder.create<moore::ConversionOp>(loc, loweredType, initial);
125  }
126 
127  auto varOp = builder.create<moore::VariableOp>(
128  loc, loweredType, builder.getStringAttr(varNode.name), initial);
129  context.valueSymbols.insert(&varNode, varOp);
130  return success();
131  }
132 
133  // Handle nets.
134  LogicalResult visit(const slang::ast::NetSymbol &netNode) {
135  auto loweredType = context.convertType(*netNode.getDeclaredType());
136  if (!loweredType)
137  return failure();
138 
139  Value assignment;
140  if (netNode.getInitializer()) {
141  assignment = context.convertExpression(*netNode.getInitializer());
142  if (!assignment)
143  return failure();
144  }
145 
146  auto netkind = convertNetKind(netNode.netType.netKind);
147  if (netkind == moore::NetKind::Interconnect ||
148  netkind == moore::NetKind::UserDefined ||
149  netkind == moore::NetKind::Unknown)
150  return mlir::emitError(loc, "unsupported net kind `")
151  << netNode.netType.name << "`";
152 
153  auto netOp = builder.create<moore::NetOp>(
154  loc, loweredType, builder.getStringAttr(netNode.name), netkind,
155  assignment);
156  context.valueSymbols.insert(&netNode, netOp);
157  return success();
158  }
159 
160  // Handle continuous assignments.
161  LogicalResult visit(const slang::ast::ContinuousAssignSymbol &assignNode) {
162  if (const auto *delay = assignNode.getDelay()) {
163  auto loc = context.convertLocation(delay->sourceRange);
164  return mlir::emitError(loc,
165  "delayed continuous assignments not supported");
166  }
167 
168  const auto &expr =
169  assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
170 
171  auto lhs = context.convertExpression(expr.left());
172  auto rhs = context.convertExpression(expr.right());
173  if (!lhs || !rhs)
174  return failure();
175 
176  if (lhs.getType() != rhs.getType())
177  rhs = builder.create<moore::ConversionOp>(loc, lhs.getType(), rhs);
178 
179  builder.create<moore::ContinuousAssignOp>(loc, lhs, rhs);
180  return success();
181  }
182 
183  // Handle procedures.
184  LogicalResult visit(const slang::ast::ProceduralBlockSymbol &procNode) {
185  auto procOp = builder.create<moore::ProcedureOp>(
186  loc, convertProcedureKind(procNode.procedureKind));
187  procOp.getBodyRegion().emplaceBlock();
188  OpBuilder::InsertionGuard guard(builder);
189  builder.setInsertionPointToEnd(procOp.getBody());
191  return context.convertStatement(procNode.getBody());
192  }
193 
194  // Ignore statement block symbols. These get generated by Slang for blocks
195  // with variables and other declarations. For example, having an initial
196  // procedure with a variable declaration, such as `initial begin int x;
197  // end`, will create the procedure with a block and variable declaration as
198  // expected, but will also create a `StatementBlockSymbol` with just the
199  // variable layout _next to_ the initial procedure.
200  LogicalResult visit(const slang::ast::StatementBlockSymbol &) {
201  return success();
202  }
203 
204  /// Emit an error for all other members.
205  template <typename T>
206  LogicalResult visit(T &&node) {
207  mlir::emitError(loc, "unsupported construct: ")
208  << slang::ast::toString(node.kind);
209  return failure();
210  }
211 };
212 } // namespace
213 
214 //===----------------------------------------------------------------------===//
215 // Structure and Hierarchy Conversion
216 //===----------------------------------------------------------------------===//
217 
218 /// Convert an entire Slang compilation to MLIR ops. This is the main entry
219 /// point for the conversion.
220 LogicalResult
221 Context::convertCompilation(slang::ast::Compilation &compilation) {
222  const auto &root = compilation.getRoot();
223 
224  // Visit all top-level declarations in all compilation units. This does not
225  // include instantiable constructs like modules, interfaces, and programs,
226  // which are listed separately as top instances.
227  for (auto *unit : root.compilationUnits) {
228  for (const auto &member : unit->members()) {
229  // Error out on all top-level declarations.
230  auto loc = convertLocation(member.location);
231  return mlir::emitError(loc, "unsupported construct: ")
232  << slang::ast::toString(member.kind);
233  }
234  }
235 
236  // Prime the root definition worklist by adding all the top-level modules.
237  SmallVector<const slang::ast::InstanceSymbol *> topInstances;
238  for (auto *inst : root.topInstances)
239  convertModuleHeader(&inst->body);
240 
241  // Convert all the root module definitions.
242  while (!moduleWorklist.empty()) {
243  auto *module = moduleWorklist.front();
244  moduleWorklist.pop();
245  if (failed(convertModuleBody(module)))
246  return failure();
247  }
248 
249  return success();
250 }
251 
252 /// Convert a module and its ports to an empty module op in the IR. Also adds
253 /// the op to the worklist of module bodies to be lowered. This acts like a
254 /// module "declaration", allowing instances to already refer to a module even
255 /// before its body has been lowered.
256 moore::SVModuleOp
257 Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
258  if (auto op = moduleOps.lookup(module))
259  return op;
260  auto loc = convertLocation(module->location);
261  OpBuilder::InsertionGuard g(builder);
262 
263  // We only support modules for now. Extension to interfaces and programs
264  // should be trivial though, since they are essentially the same thing with
265  // only minor differences in semantics.
266  if (module->getDefinition().definitionKind !=
267  slang::ast::DefinitionKind::Module) {
268  mlir::emitError(loc, "unsupported construct: ")
269  << module->getDefinition().getKindString();
270  return {};
271  }
272 
273  // Handle the port list.
274  for (auto *symbol : module->getPortList()) {
275  auto portLoc = convertLocation(symbol->location);
276  mlir::emitError(portLoc, "unsupported module port: ")
277  << slang::ast::toString(symbol->kind);
278  return {};
279  }
280 
281  // Pick an insertion point for this module according to the source file
282  // location.
283  auto it = orderedRootOps.lower_bound(module->location);
284  if (it == orderedRootOps.end())
285  builder.setInsertionPointToEnd(intoModuleOp.getBody());
286  else
287  builder.setInsertionPoint(it->second);
288 
289  // Create an empty module that corresponds to this module.
290  auto moduleOp = builder.create<moore::SVModuleOp>(loc, module->name);
291  orderedRootOps.insert(it, {module->location, moduleOp});
292  moduleOp.getBodyRegion().emplaceBlock();
293 
294  // Add the module to the symbol table of the MLIR module, which uniquifies its
295  // name as we'd expect.
296  symbolTable.insert(moduleOp);
297 
298  // Schedule the body to be lowered.
299  moduleWorklist.push(module);
300  moduleOps.insert({module, moduleOp});
301  return moduleOp;
302 }
303 
304 /// Convert a module's body to the corresponding IR ops. The module op must have
305 /// already been created earlier through a `convertModuleHeader` call.
306 LogicalResult
307 Context::convertModuleBody(const slang::ast::InstanceBodySymbol *module) {
308  auto moduleOp = moduleOps.lookup(module);
309  assert(moduleOp);
310  OpBuilder::InsertionGuard g(builder);
311  builder.setInsertionPointToEnd(moduleOp.getBody());
312 
314  for (auto &member : module->members()) {
315  auto loc = convertLocation(member.location);
316  if (failed(member.visit(MemberVisitor(*this, loc))))
317  return failure();
318  }
319 
320  return success();
321 }
assert(baseType &&"element must be base type")
constexpr const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:169
Builder builder
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
Definition: Structure.cpp:20
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
Definition: Structure.cpp:38
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
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module)
Convert a module's body to the corresponding IR ops.
Definition: Structure.cpp:307
OpBuilder builder
The builder used to create IR operations.
std::queue< const slang::ast::InstanceBodySymbol * > moduleWorklist
A list of modules for which the header has been created, but the body has not been converted yet.
LogicalResult convertCompilation(slang::ast::Compilation &compilation)
Convert hierarchy and structure AST nodes to MLIR ops.
Definition: Structure.cpp:221
Value convertExpression(const slang::ast::Expression &expr)
std::map< slang::SourceLocation, Operation * > orderedRootOps
The top-level operations ordered by their Slang source location.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Definition: Types.cpp:186
DenseMap< const slang::ast::InstanceBodySymbol *, moore::SVModuleOp > moduleOps
How we have lowered modules to MLIR.
moore::SVModuleOp convertModuleHeader(const slang::ast::InstanceBodySymbol *module)
Convert a module and its ports to an empty module op in the IR.
Definition: Structure.cpp:257
ValueSymbols::ScopeTy ValueSymbolScope
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Definition: Statements.cpp:325
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.