CIRCT  20.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 // Utilities
17 //===----------------------------------------------------------------------===//
18 
19 static void guessNamespacePrefix(const slang::ast::Symbol &symbol,
20  SmallString<64> &prefix) {
21  if (symbol.kind != slang::ast::SymbolKind::Package)
22  return;
23  guessNamespacePrefix(symbol.getParentScope()->asSymbol(), prefix);
24  if (!symbol.name.empty()) {
25  prefix += symbol.name;
26  prefix += "::";
27  }
28 }
29 
30 //===----------------------------------------------------------------------===//
31 // Base Visitor
32 //===----------------------------------------------------------------------===//
33 
34 namespace {
35 /// Base visitor which ignores AST nodes that are handled by Slang's name
36 /// resolution and type checking.
37 struct BaseVisitor {
38  Context &context;
39  Location loc;
40  OpBuilder &builder;
41 
42  BaseVisitor(Context &context, Location loc)
43  : context(context), loc(loc), builder(context.builder) {}
44 
45  // Skip semicolons.
46  LogicalResult visit(const slang::ast::EmptyMemberSymbol &) {
47  return success();
48  }
49 
50  // Skip members that are implicitly imported from some other scope for the
51  // sake of name resolution, such as enum variant names.
52  LogicalResult visit(const slang::ast::TransparentMemberSymbol &) {
53  return success();
54  }
55 
56  // Skip typedefs.
57  LogicalResult visit(const slang::ast::TypeAliasType &) { return success(); }
58 
59  // Skip imports. The AST already has its names resolved.
60  LogicalResult visit(const slang::ast::ExplicitImportSymbol &) {
61  return success();
62  }
63  LogicalResult visit(const slang::ast::WildcardImportSymbol &) {
64  return success();
65  }
66 
67  // Skip type parameters. The Slang AST is already monomorphized.
68  LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
69  return success();
70  }
71 
72  // Skip elaboration system tasks. These are reported directly by Slang.
73  LogicalResult visit(const slang::ast::ElabSystemTaskSymbol &) {
74  return success();
75  }
76 
77  // Handle parameters.
78  LogicalResult visit(const slang::ast::ParameterSymbol &param) {
79  visitParameter(param);
80  return success();
81  }
82 
83  LogicalResult visit(const slang::ast::SpecparamSymbol &param) {
84  visitParameter(param);
85  return success();
86  }
87 
88  template <class Node>
89  void visitParameter(const Node &param) {
90  // If debug info is enabled, try to materialize the parameter's constant
91  // value on a best-effort basis and create a `dbg.variable` to track the
92  // value.
93  if (!context.options.debugInfo)
94  return;
95  auto value =
96  context.materializeConstant(param.getValue(), param.getType(), loc);
97  if (!value)
98  return;
99  if (builder.getInsertionBlock()->getParentOp() == context.intoModuleOp)
100  context.orderedRootOps.insert({param.location, value.getDefiningOp()});
101 
102  // Prefix the parameter name with the surrounding namespace to create
103  // somewhat sane names in the IR.
104  SmallString<64> paramName;
105  guessNamespacePrefix(param.getParentScope()->asSymbol(), paramName);
106  paramName += param.name;
107 
108  builder.create<debug::VariableOp>(loc, builder.getStringAttr(paramName),
109  value, Value{});
110  }
111 };
112 } // namespace
113 
114 //===----------------------------------------------------------------------===//
115 // Top-Level Item Conversion
116 //===----------------------------------------------------------------------===//
117 
118 namespace {
119 struct RootVisitor : public BaseVisitor {
120  using BaseVisitor::BaseVisitor;
121  using BaseVisitor::visit;
122 
123  // Handle packages.
124  LogicalResult visit(const slang::ast::PackageSymbol &package) {
125  return context.convertPackage(package);
126  }
127 
128  // Handle functions and tasks.
129  LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
130  return context.convertFunction(subroutine);
131  }
132 
133  // Emit an error for all other members.
134  template <typename T>
135  LogicalResult visit(T &&node) {
136  mlir::emitError(loc, "unsupported construct: ")
137  << slang::ast::toString(node.kind);
138  return failure();
139  }
140 };
141 } // namespace
142 
143 //===----------------------------------------------------------------------===//
144 // Package Conversion
145 //===----------------------------------------------------------------------===//
146 
147 namespace {
148 struct PackageVisitor : public BaseVisitor {
149  using BaseVisitor::BaseVisitor;
150  using BaseVisitor::visit;
151 
152  // Handle functions and tasks.
153  LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
154  return context.convertFunction(subroutine);
155  }
156 
157  /// Emit an error for all other members.
158  template <typename T>
159  LogicalResult visit(T &&node) {
160  mlir::emitError(loc, "unsupported package member: ")
161  << slang::ast::toString(node.kind);
162  return failure();
163  }
164 };
165 } // namespace
166 
167 //===----------------------------------------------------------------------===//
168 // Module Conversion
169 //===----------------------------------------------------------------------===//
170 
171 static moore::ProcedureKind
172 convertProcedureKind(slang::ast::ProceduralBlockKind kind) {
173  switch (kind) {
174  case slang::ast::ProceduralBlockKind::Always:
175  return moore::ProcedureKind::Always;
176  case slang::ast::ProceduralBlockKind::AlwaysComb:
177  return moore::ProcedureKind::AlwaysComb;
178  case slang::ast::ProceduralBlockKind::AlwaysLatch:
179  return moore::ProcedureKind::AlwaysLatch;
180  case slang::ast::ProceduralBlockKind::AlwaysFF:
181  return moore::ProcedureKind::AlwaysFF;
182  case slang::ast::ProceduralBlockKind::Initial:
183  return moore::ProcedureKind::Initial;
184  case slang::ast::ProceduralBlockKind::Final:
185  return moore::ProcedureKind::Final;
186  }
187  llvm_unreachable("all procedure kinds handled");
188 }
189 
190 static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind) {
191  switch (kind) {
192  case slang::ast::NetType::Supply0:
193  return moore::NetKind::Supply0;
194  case slang::ast::NetType::Supply1:
195  return moore::NetKind::Supply1;
196  case slang::ast::NetType::Tri:
197  return moore::NetKind::Tri;
198  case slang::ast::NetType::TriAnd:
199  return moore::NetKind::TriAnd;
200  case slang::ast::NetType::TriOr:
201  return moore::NetKind::TriOr;
202  case slang::ast::NetType::TriReg:
203  return moore::NetKind::TriReg;
204  case slang::ast::NetType::Tri0:
205  return moore::NetKind::Tri0;
206  case slang::ast::NetType::Tri1:
207  return moore::NetKind::Tri1;
208  case slang::ast::NetType::UWire:
209  return moore::NetKind::UWire;
210  case slang::ast::NetType::Wire:
211  return moore::NetKind::Wire;
212  case slang::ast::NetType::WAnd:
213  return moore::NetKind::WAnd;
214  case slang::ast::NetType::WOr:
215  return moore::NetKind::WOr;
216  case slang::ast::NetType::Interconnect:
217  return moore::NetKind::Interconnect;
218  case slang::ast::NetType::UserDefined:
219  return moore::NetKind::UserDefined;
220  case slang::ast::NetType::Unknown:
221  return moore::NetKind::Unknown;
222  }
223  llvm_unreachable("all net kinds handled");
224 }
225 
226 namespace {
227 struct ModuleVisitor : public BaseVisitor {
228  using BaseVisitor::BaseVisitor;
229  using BaseVisitor::visit;
230 
231  // Skip ports which are already handled by the module itself.
232  LogicalResult visit(const slang::ast::PortSymbol &) { return success(); }
233  LogicalResult visit(const slang::ast::MultiPortSymbol &) { return success(); }
234 
235  // Skip genvars.
236  LogicalResult visit(const slang::ast::GenvarSymbol &genvarNode) {
237  return success();
238  }
239 
240  // Skip defparams which have been handled by slang.
241  LogicalResult visit(const slang::ast::DefParamSymbol &) { return success(); }
242 
243  // Ignore type parameters. These have already been handled by Slang's type
244  // checking.
245  LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
246  return success();
247  }
248 
249  // Handle instances.
250  LogicalResult visit(const slang::ast::InstanceSymbol &instNode) {
251  using slang::ast::ArgumentDirection;
252  using slang::ast::AssignmentExpression;
253  using slang::ast::MultiPortSymbol;
254  using slang::ast::PortSymbol;
255 
256  auto *moduleLowering = context.convertModuleHeader(&instNode.body);
257  if (!moduleLowering)
258  return failure();
259  auto module = moduleLowering->op;
260  auto moduleType = module.getModuleType();
261 
262  // Set visibility attribute for instantiated module.
263  SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
264 
265  // Prepare the values that are involved in port connections. This creates
266  // rvalues for input ports and appropriate lvalues for output, inout, and
267  // ref ports. We also separate multi-ports into the individual underlying
268  // ports with their corresponding connection.
270  portValues.reserve(moduleType.getNumPorts());
271 
272  for (const auto *con : instNode.getPortConnections()) {
273  const auto *expr = con->getExpression();
274 
275  // Handle unconnected behavior. The expression is null if it have no
276  // connection for the port.
277  if (!expr) {
278  auto *port = con->port.as_if<PortSymbol>();
279  if (auto *existingPort =
280  moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
281  port = existingPort;
282 
283  switch (port->direction) {
284  case ArgumentDirection::In: {
285  auto refType = moore::RefType::get(
286  cast<moore::UnpackedType>(context.convertType(port->getType())));
287 
288  if (const auto *net =
289  port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
290  auto netOp = builder.create<moore::NetOp>(
291  loc, refType, StringAttr::get(builder.getContext(), net->name),
292  convertNetKind(net->netType.netKind), nullptr);
293  auto readOp = builder.create<moore::ReadOp>(loc, netOp);
294  portValues.insert({port, readOp});
295  } else if (const auto *var =
296  port->internalSymbol
297  ->as_if<slang::ast::VariableSymbol>()) {
298  auto varOp = builder.create<moore::VariableOp>(
299  loc, refType, StringAttr::get(builder.getContext(), var->name),
300  nullptr);
301  auto readOp = builder.create<moore::ReadOp>(loc, varOp);
302  portValues.insert({port, readOp});
303  } else {
304  return mlir::emitError(loc)
305  << "unsupported internal symbol for unconnected port `"
306  << port->name << "`";
307  }
308  continue;
309  }
310 
311  // No need to express unconnected behavior for output port, skip to the
312  // next iteration of the loop.
313  case ArgumentDirection::Out:
314  continue;
315 
316  // TODO: Mark Inout port as unsupported and it will be supported later.
317  default:
318  return mlir::emitError(loc)
319  << "unsupported port `" << port->name << "` ("
320  << slang::ast::toString(port->kind) << ")";
321  }
322  }
323 
324  // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for
325  // output and inout ports.
326  if (const auto *assign = expr->as_if<AssignmentExpression>())
327  expr = &assign->left();
328 
329  // Regular ports lower the connected expression to an lvalue or rvalue and
330  // either attach it to the instance as an operand (for input, inout, and
331  // ref ports), or assign an instance output to it (for output ports).
332  if (auto *port = con->port.as_if<PortSymbol>()) {
333  // Convert as rvalue for inputs, lvalue for all others.
334  auto value = (port->direction == ArgumentDirection::In)
335  ? context.convertRvalueExpression(*expr)
336  : context.convertLvalueExpression(*expr);
337  if (!value)
338  return failure();
339  if (auto *existingPort =
340  moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
341  port = existingPort;
342  portValues.insert({port, value});
343  continue;
344  }
345 
346  // Multi-ports lower the connected expression to an lvalue and then slice
347  // it up into multiple sub-values, one for each of the ports in the
348  // multi-port.
349  if (const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
350  // Convert as lvalue.
351  auto value = context.convertLvalueExpression(*expr);
352  if (!value)
353  return failure();
354  unsigned offset = 0;
355  for (const auto *port : llvm::reverse(multiPort->ports)) {
356  if (auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
357  con->port.getSyntax()))
358  port = existingPort;
359  unsigned width = port->getType().getBitWidth();
360  auto sliceType = context.convertType(port->getType());
361  if (!sliceType)
362  return failure();
363  Value slice = builder.create<moore::ExtractRefOp>(
364  loc, moore::RefType::get(cast<moore::UnpackedType>(sliceType)),
365  value, offset);
366  // Create the "ReadOp" for input ports.
367  if (port->direction == ArgumentDirection::In)
368  slice = builder.create<moore::ReadOp>(loc, slice);
369  portValues.insert({port, slice});
370  offset += width;
371  }
372  continue;
373  }
374 
375  mlir::emitError(loc) << "unsupported instance port `" << con->port.name
376  << "` (" << slang::ast::toString(con->port.kind)
377  << ")";
378  return failure();
379  }
380 
381  // Match the module's ports up with the port values determined above.
382  SmallVector<Value> inputValues;
383  SmallVector<Value> outputValues;
384  inputValues.reserve(moduleType.getNumInputs());
385  outputValues.reserve(moduleType.getNumOutputs());
386 
387  for (auto &port : moduleLowering->ports) {
388  auto value = portValues.lookup(&port.ast);
389  if (port.ast.direction == ArgumentDirection::Out)
390  outputValues.push_back(value);
391  else
392  inputValues.push_back(value);
393  }
394 
395  // Insert conversions for input ports.
396  for (auto [value, type] :
397  llvm::zip(inputValues, moduleType.getInputTypes()))
398  if (value.getType() != type)
399  value =
400  builder.create<moore::ConversionOp>(value.getLoc(), type, value);
401 
402  // Here we use the hierarchical value recorded in `Context::valueSymbols`.
403  // Then we pass it as the input port with the ref<T> type of the instance.
404  for (const auto &hierPath : context.hierPaths[&instNode.body])
405  if (auto hierValue = context.valueSymbols.lookup(hierPath.valueSym);
406  hierPath.hierName && hierPath.direction == ArgumentDirection::In)
407  inputValues.push_back(hierValue);
408 
409  // Create the instance op itself.
410  auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
411  auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
412  auto inst = builder.create<moore::InstanceOp>(
413  loc, moduleType.getOutputTypes(), builder.getStringAttr(instNode.name),
414  FlatSymbolRefAttr::get(module.getSymNameAttr()), inputValues,
415  inputNames, outputNames);
416 
417  // Record instance's results generated by hierarchical names.
418  for (const auto &hierPath : context.hierPaths[&instNode.body])
419  if (hierPath.idx && hierPath.direction == ArgumentDirection::Out)
420  context.valueSymbols.insert(hierPath.valueSym,
421  inst->getResult(*hierPath.idx));
422 
423  // Assign output values from the instance to the connected expression.
424  for (auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs())) {
425  if (!lvalue)
426  continue;
427  Value rvalue = output;
428  auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
429  if (dstType != rvalue.getType())
430  rvalue = builder.create<moore::ConversionOp>(loc, dstType, rvalue);
431  builder.create<moore::ContinuousAssignOp>(loc, lvalue, rvalue);
432  }
433 
434  return success();
435  }
436 
437  // Handle variables.
438  LogicalResult visit(const slang::ast::VariableSymbol &varNode) {
439  auto loweredType = context.convertType(*varNode.getDeclaredType());
440  if (!loweredType)
441  return failure();
442 
443  Value initial;
444  if (const auto *init = varNode.getInitializer()) {
445  initial = context.convertRvalueExpression(*init, loweredType);
446  if (!initial)
447  return failure();
448  }
449 
450  auto varOp = builder.create<moore::VariableOp>(
451  loc, moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
452  builder.getStringAttr(varNode.name), initial);
453  context.valueSymbols.insert(&varNode, varOp);
454  return success();
455  }
456 
457  // Handle nets.
458  LogicalResult visit(const slang::ast::NetSymbol &netNode) {
459  auto loweredType = context.convertType(*netNode.getDeclaredType());
460  if (!loweredType)
461  return failure();
462 
463  Value assignment;
464  if (const auto *init = netNode.getInitializer()) {
465  assignment = context.convertRvalueExpression(*init, loweredType);
466  if (!assignment)
467  return failure();
468  }
469 
470  auto netkind = convertNetKind(netNode.netType.netKind);
471  if (netkind == moore::NetKind::Interconnect ||
472  netkind == moore::NetKind::UserDefined ||
473  netkind == moore::NetKind::Unknown)
474  return mlir::emitError(loc, "unsupported net kind `")
475  << netNode.netType.name << "`";
476 
477  auto netOp = builder.create<moore::NetOp>(
478  loc, moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
479  builder.getStringAttr(netNode.name), netkind, assignment);
480  context.valueSymbols.insert(&netNode, netOp);
481  return success();
482  }
483 
484  // Handle continuous assignments.
485  LogicalResult visit(const slang::ast::ContinuousAssignSymbol &assignNode) {
486  if (const auto *delay = assignNode.getDelay()) {
487  auto loc = context.convertLocation(delay->sourceRange);
488  return mlir::emitError(loc,
489  "delayed continuous assignments not supported");
490  }
491 
492  const auto &expr =
493  assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
494  auto lhs = context.convertLvalueExpression(expr.left());
495  if (!lhs)
496  return failure();
497 
498  auto rhs = context.convertRvalueExpression(
499  expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
500  if (!rhs)
501  return failure();
502 
503  builder.create<moore::ContinuousAssignOp>(loc, lhs, rhs);
504  return success();
505  }
506 
507  // Handle procedures.
508  LogicalResult visit(const slang::ast::ProceduralBlockSymbol &procNode) {
509  auto procOp = builder.create<moore::ProcedureOp>(
510  loc, convertProcedureKind(procNode.procedureKind));
511  OpBuilder::InsertionGuard guard(builder);
512  builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
514  if (failed(context.convertStatement(procNode.getBody())))
515  return failure();
516  if (builder.getBlock())
517  builder.create<moore::ReturnOp>(loc);
518  return success();
519  }
520 
521  // Handle generate block.
522  LogicalResult visit(const slang::ast::GenerateBlockSymbol &genNode) {
523  if (!genNode.isUninstantiated) {
524  for (auto &member : genNode.members()) {
525  if (failed(member.visit(ModuleVisitor(context, loc))))
526  return failure();
527  }
528  }
529  return success();
530  }
531 
532  // Handle generate block array.
533  LogicalResult visit(const slang::ast::GenerateBlockArraySymbol &genArrNode) {
534  for (const auto *member : genArrNode.entries) {
535  if (failed(member->asSymbol().visit(ModuleVisitor(context, loc))))
536  return failure();
537  }
538  return success();
539  }
540 
541  // Ignore statement block symbols. These get generated by Slang for blocks
542  // with variables and other declarations. For example, having an initial
543  // procedure with a variable declaration, such as `initial begin int x;
544  // end`, will create the procedure with a block and variable declaration as
545  // expected, but will also create a `StatementBlockSymbol` with just the
546  // variable layout _next to_ the initial procedure.
547  LogicalResult visit(const slang::ast::StatementBlockSymbol &) {
548  return success();
549  }
550 
551  // Handle functions and tasks.
552  LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
553  return context.convertFunction(subroutine);
554  }
555 
556  /// Emit an error for all other members.
557  template <typename T>
558  LogicalResult visit(T &&node) {
559  mlir::emitError(loc, "unsupported module member: ")
560  << slang::ast::toString(node.kind);
561  return failure();
562  }
563 };
564 } // namespace
565 
566 //===----------------------------------------------------------------------===//
567 // Structure and Hierarchy Conversion
568 //===----------------------------------------------------------------------===//
569 
570 /// Convert an entire Slang compilation to MLIR ops. This is the main entry
571 /// point for the conversion.
573  const auto &root = compilation.getRoot();
574 
575  // First only to visit the whole AST to collect the hierarchical names without
576  // any operation creating.
577  for (auto *inst : root.topInstances)
578  if (failed(traverseInstanceBody(inst->body)))
579  return failure();
580 
581  // Visit all top-level declarations in all compilation units. This does not
582  // include instantiable constructs like modules, interfaces, and programs,
583  // which are listed separately as top instances.
584  for (auto *unit : root.compilationUnits) {
585  for (const auto &member : unit->members()) {
586  auto loc = convertLocation(member.location);
587  if (failed(member.visit(RootVisitor(*this, loc))))
588  return failure();
589  }
590  }
591 
592  // Prime the root definition worklist by adding all the top-level modules.
593  SmallVector<const slang::ast::InstanceSymbol *> topInstances;
594  for (auto *inst : root.topInstances)
595  if (!convertModuleHeader(&inst->body))
596  return failure();
597 
598  // Convert all the root module definitions.
599  while (!moduleWorklist.empty()) {
600  auto *module = moduleWorklist.front();
601  moduleWorklist.pop();
602  if (failed(convertModuleBody(module)))
603  return failure();
604  }
605 
606  return success();
607 }
608 
609 /// Convert a module and its ports to an empty module op in the IR. Also adds
610 /// the op to the worklist of module bodies to be lowered. This acts like a
611 /// module "declaration", allowing instances to already refer to a module even
612 /// before its body has been lowered.
614 Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
615  using slang::ast::ArgumentDirection;
616  using slang::ast::MultiPortSymbol;
617  using slang::ast::ParameterSymbol;
618  using slang::ast::PortSymbol;
619  using slang::ast::TypeParameterSymbol;
620 
621  auto parameters = module->parameters;
622  bool hasModuleSame = false;
623  // If there is already exist a module that has the same name with this
624  // module ,has the same parent scope and has the same parameters we can
625  // define this module is a duplicate module
626  for (auto const &existingModule : modules) {
627  if (module->getDeclaringDefinition() ==
628  existingModule.getFirst()->getDeclaringDefinition()) {
629  auto moduleParameters = existingModule.getFirst()->parameters;
630  hasModuleSame = true;
631  for (auto it1 = parameters.begin(), it2 = moduleParameters.begin();
632  it1 != parameters.end() && it2 != moduleParameters.end();
633  it1++, it2++) {
634  // Parameters size different
635  if (it1 == parameters.end() || it2 == moduleParameters.end()) {
636  hasModuleSame = false;
637  break;
638  }
639  const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
640  const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
641  // Parameters kind different
642  if ((para1 == nullptr) ^ (para2 == nullptr)) {
643  hasModuleSame = false;
644  break;
645  }
646  // Compare ParameterSymbol
647  if (para1 != nullptr) {
648  hasModuleSame = para1->getValue() == para2->getValue();
649  }
650  // Compare TypeParameterSymbol
651  if (para1 == nullptr) {
652  auto para1Type = convertType(
653  (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
654  auto para2Type = convertType(
655  (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
656  hasModuleSame = para1Type == para2Type;
657  }
658  if (!hasModuleSame)
659  break;
660  }
661  if (hasModuleSame) {
662  module = existingModule.first;
663  break;
664  }
665  }
666  }
667 
668  auto &slot = modules[module];
669  if (slot)
670  return slot.get();
671  slot = std::make_unique<ModuleLowering>();
672  auto &lowering = *slot;
673 
674  auto loc = convertLocation(module->location);
675  OpBuilder::InsertionGuard g(builder);
676 
677  // We only support modules for now. Extension to interfaces and programs
678  // should be trivial though, since they are essentially the same thing with
679  // only minor differences in semantics.
680  if (module->getDefinition().definitionKind !=
681  slang::ast::DefinitionKind::Module) {
682  mlir::emitError(loc) << "unsupported definition: "
683  << module->getDefinition().getKindString();
684  return {};
685  }
686 
687  // Handle the port list.
688  auto block = std::make_unique<Block>();
689  SmallVector<hw::ModulePort> modulePorts;
690 
691  // It's used to tag where a hierarchical name is on the port list.
692  unsigned int outputIdx = 0, inputIdx = 0;
693  for (auto *symbol : module->getPortList()) {
694  auto handlePort = [&](const PortSymbol &port) {
695  auto portLoc = convertLocation(port.location);
696  auto type = convertType(port.getType());
697  if (!type)
698  return failure();
699  auto portName = builder.getStringAttr(port.name);
700  BlockArgument arg;
701  if (port.direction == ArgumentDirection::Out) {
702  modulePorts.push_back({portName, type, hw::ModulePort::Output});
703  outputIdx++;
704  } else {
705  // Only the ref type wrapper exists for the time being, the net type
706  // wrapper for inout may be introduced later if necessary.
707  if (port.direction != ArgumentDirection::In)
708  type = moore::RefType::get(cast<moore::UnpackedType>(type));
709  modulePorts.push_back({portName, type, hw::ModulePort::Input});
710  arg = block->addArgument(type, portLoc);
711  inputIdx++;
712  }
713  lowering.ports.push_back({port, portLoc, arg});
714  return success();
715  };
716 
717  if (const auto *port = symbol->as_if<PortSymbol>()) {
718  if (failed(handlePort(*port)))
719  return {};
720  } else if (const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
721  for (auto *port : multiPort->ports)
722  if (failed(handlePort(*port)))
723  return {};
724  } else {
725  mlir::emitError(convertLocation(symbol->location))
726  << "unsupported module port `" << symbol->name << "` ("
727  << slang::ast::toString(symbol->kind) << ")";
728  return {};
729  }
730  }
731 
732  // Mapping hierarchical names into the module's ports.
733  for (auto &hierPath : hierPaths[module]) {
734  auto hierType = convertType(hierPath.valueSym->getType());
735  if (!hierType)
736  return {};
737 
738  if (auto hierName = hierPath.hierName) {
739  // The type of all hierarchical names are marked as the "RefType".
740  hierType = moore::RefType::get(cast<moore::UnpackedType>(hierType));
741  if (hierPath.direction == ArgumentDirection::Out) {
742  hierPath.idx = outputIdx++;
743  modulePorts.push_back({hierName, hierType, hw::ModulePort::Output});
744  } else {
745  hierPath.idx = inputIdx++;
746  modulePorts.push_back({hierName, hierType, hw::ModulePort::Input});
747  auto hierLoc = convertLocation(hierPath.valueSym->location);
748  block->addArgument(hierType, hierLoc);
749  }
750  }
751  }
752  auto moduleType = hw::ModuleType::get(getContext(), modulePorts);
753 
754  // Pick an insertion point for this module according to the source file
755  // location.
756  auto it = orderedRootOps.upper_bound(module->location);
757  if (it == orderedRootOps.end())
758  builder.setInsertionPointToEnd(intoModuleOp.getBody());
759  else
760  builder.setInsertionPoint(it->second);
761 
762  // Create an empty module that corresponds to this module.
763  auto moduleOp =
764  builder.create<moore::SVModuleOp>(loc, module->name, moduleType);
765  orderedRootOps.insert(it, {module->location, moduleOp});
766  moduleOp.getBodyRegion().push_back(block.release());
767  lowering.op = moduleOp;
768 
769  // Add the module to the symbol table of the MLIR module, which uniquifies its
770  // name as we'd expect.
771  symbolTable.insert(moduleOp);
772 
773  // Schedule the body to be lowered.
774  moduleWorklist.push(module);
775 
776  // Map duplicate port by Syntax
777  for (const auto &port : lowering.ports)
778  lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
779 
780  return &lowering;
781 }
782 
783 /// Convert a module's body to the corresponding IR ops. The module op must have
784 /// already been created earlier through a `convertModuleHeader` call.
785 LogicalResult
786 Context::convertModuleBody(const slang::ast::InstanceBodySymbol *module) {
787  auto &lowering = *modules[module];
788  OpBuilder::InsertionGuard g(builder);
789  builder.setInsertionPointToEnd(lowering.op.getBody());
790 
792 
793  // Collect downward hierarchical names. Such as,
794  // module SubA; int x = Top.y; endmodule. The "Top" module is the parent of
795  // the "SubA", so "Top.y" is the downward hierarchical name.
796  for (auto &hierPath : hierPaths[module])
797  if (hierPath.direction == slang::ast::ArgumentDirection::In && hierPath.idx)
798  valueSymbols.insert(hierPath.valueSym,
799  lowering.op.getBody()->getArgument(*hierPath.idx));
800 
801  // Convert the body of the module.
802  for (auto &member : module->members()) {
803  auto loc = convertLocation(member.location);
804  if (failed(member.visit(ModuleVisitor(*this, loc))))
805  return failure();
806  }
807 
808  // Create additional ops to drive input port values onto the corresponding
809  // internal variables and nets, and to collect output port values for the
810  // terminator.
811  SmallVector<Value> outputs;
812  for (auto &port : lowering.ports) {
813  Value value;
814  if (auto *expr = port.ast.getInternalExpr()) {
815  value = convertLvalueExpression(*expr);
816  } else if (port.ast.internalSymbol) {
817  if (const auto *sym =
818  port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
819  value = valueSymbols.lookup(sym);
820  }
821  if (!value)
822  return mlir::emitError(port.loc, "unsupported port: `")
823  << port.ast.name
824  << "` does not map to an internal symbol or expression";
825 
826  // Collect output port values to be returned in the terminator.
827  if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
828  if (isa<moore::RefType>(value.getType()))
829  value = builder.create<moore::ReadOp>(value.getLoc(), value);
830  outputs.push_back(value);
831  continue;
832  }
833 
834  // Assign the value coming in through the port to the internal net or symbol
835  // of that port.
836  Value portArg = port.arg;
837  if (port.ast.direction != slang::ast::ArgumentDirection::In)
838  portArg = builder.create<moore::ReadOp>(port.loc, port.arg);
839  builder.create<moore::ContinuousAssignOp>(port.loc, value, portArg);
840  }
841 
842  // Ensure the number of operands of this module's terminator and the number of
843  // its(the current module) output ports remain consistent.
844  for (auto &hierPath : hierPaths[module])
845  if (auto hierValue = valueSymbols.lookup(hierPath.valueSym))
846  if (hierPath.direction == slang::ast::ArgumentDirection::Out)
847  outputs.push_back(hierValue);
848 
849  builder.create<moore::OutputOp>(lowering.op.getLoc(), outputs);
850  return success();
851 }
852 
853 /// Convert a package and its contents.
854 LogicalResult
855 Context::convertPackage(const slang::ast::PackageSymbol &package) {
856  OpBuilder::InsertionGuard g(builder);
857  builder.setInsertionPointToEnd(intoModuleOp.getBody());
859  for (auto &member : package.members()) {
860  auto loc = convertLocation(member.location);
861  if (failed(member.visit(PackageVisitor(*this, loc))))
862  return failure();
863  }
864  return success();
865 }
866 
867 /// Convert a function and its arguments to a function declaration in the IR.
868 /// This does not convert the function body.
870 Context::declareFunction(const slang::ast::SubroutineSymbol &subroutine) {
871  using slang::ast::ArgumentDirection;
872 
873  // Check if there already is a declaration for this function.
874  auto &lowering = functions[&subroutine];
875  if (lowering) {
876  if (!lowering->op)
877  return {};
878  return lowering.get();
879  }
880  lowering = std::make_unique<FunctionLowering>();
881  auto loc = convertLocation(subroutine.location);
882 
883  // Pick an insertion point for this function according to the source file
884  // location.
885  OpBuilder::InsertionGuard g(builder);
886  auto it = orderedRootOps.upper_bound(subroutine.location);
887  if (it == orderedRootOps.end())
888  builder.setInsertionPointToEnd(intoModuleOp.getBody());
889  else
890  builder.setInsertionPoint(it->second);
891 
892  // Class methods are currently not supported.
893  if (subroutine.thisVar) {
894  mlir::emitError(loc) << "unsupported class method";
895  return {};
896  }
897 
898  // Determine the function type.
899  SmallVector<Type> inputTypes;
900  SmallVector<Type, 1> outputTypes;
901 
902  for (const auto *arg : subroutine.getArguments()) {
903  auto type = cast<moore::UnpackedType>(convertType(arg->getType()));
904  if (!type)
905  return {};
906  if (arg->direction == ArgumentDirection::In) {
907  inputTypes.push_back(type);
908  } else {
909  inputTypes.push_back(moore::RefType::get(type));
910  }
911  }
912 
913  if (!subroutine.getReturnType().isVoid()) {
914  auto type = convertType(subroutine.getReturnType());
915  if (!type)
916  return {};
917  outputTypes.push_back(type);
918  }
919 
920  auto funcType = FunctionType::get(getContext(), inputTypes, outputTypes);
921 
922  // Prefix the function name with the surrounding namespace to create somewhat
923  // sane names in the IR.
924  SmallString<64> funcName;
925  guessNamespacePrefix(subroutine.getParentScope()->asSymbol(), funcName);
926  funcName += subroutine.name;
927 
928  // Create a function declaration.
929  auto funcOp = builder.create<mlir::func::FuncOp>(loc, funcName, funcType);
930  SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
931  orderedRootOps.insert(it, {subroutine.location, funcOp});
932  lowering->op = funcOp;
933 
934  // Add the function to the symbol table of the MLIR module, which uniquifies
935  // its name.
936  symbolTable.insert(funcOp);
937 
938  return lowering.get();
939 }
940 
941 /// Convert a function.
942 LogicalResult
943 Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {
944  // First get or create the function declaration.
945  auto *lowering = declareFunction(subroutine);
946  if (!lowering)
947  return failure();
949 
950  // Create a function body block and populate it with block arguments.
951  SmallVector<moore::VariableOp> argVariables;
952  auto &block = lowering->op.getBody().emplaceBlock();
953  for (auto [astArg, type] :
954  llvm::zip(subroutine.getArguments(),
955  lowering->op.getFunctionType().getInputs())) {
956  auto loc = convertLocation(astArg->location);
957  auto blockArg = block.addArgument(type, loc);
958 
959  if (isa<moore::RefType>(type)) {
960  valueSymbols.insert(astArg, blockArg);
961  } else {
962  // Convert the body of the function.
963  OpBuilder::InsertionGuard g(builder);
964  builder.setInsertionPointToEnd(&block);
965 
966  auto shadowArg = builder.create<moore::VariableOp>(
967  loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
968  StringAttr{}, blockArg);
969  valueSymbols.insert(astArg, shadowArg);
970  argVariables.push_back(shadowArg);
971  }
972  }
973 
974  // Convert the body of the function.
975  OpBuilder::InsertionGuard g(builder);
976  builder.setInsertionPointToEnd(&block);
977 
978  Value returnVar;
979  if (subroutine.returnValVar) {
980  auto type = convertType(*subroutine.returnValVar->getDeclaredType());
981  if (!type)
982  return failure();
983  returnVar = builder.create<moore::VariableOp>(
984  lowering->op.getLoc(),
985  moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
986  Value{});
987  valueSymbols.insert(subroutine.returnValVar, returnVar);
988  }
989 
990  if (failed(convertStatement(subroutine.getBody())))
991  return failure();
992 
993  // If there was no explicit return statement provided by the user, insert a
994  // default one.
995  if (builder.getBlock()) {
996  if (returnVar && !subroutine.getReturnType().isVoid()) {
997  Value read = builder.create<moore::ReadOp>(returnVar.getLoc(), returnVar);
998  builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), read);
999  } else {
1000  builder.create<mlir::func::ReturnOp>(lowering->op.getLoc(), ValueRange{});
1001  }
1002  }
1003  if (returnVar && returnVar.use_empty())
1004  returnVar.getDefiningOp()->erase();
1005 
1006  for (auto var : argVariables) {
1007  if (llvm::all_of(var->getUsers(),
1008  [](auto *user) { return isa<moore::ReadOp>(user); })) {
1009  for (auto *user : llvm::make_early_inc_range(var->getUsers())) {
1010  user->getResult(0).replaceAllUsesWith(var.getInitial());
1011  user->erase();
1012  }
1013  var->erase();
1014  }
1015  }
1016  return success();
1017 }
const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:197
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
Definition: Structure.cpp:172
static void guessNamespacePrefix(const slang::ast::Symbol &symbol, SmallString< 64 > &prefix)
Definition: Structure.cpp:19
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
Definition: Structure.cpp:190
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
bool debugInfo
Generate debug information in the form of debug dialect ops in the IR.
Definition: ImportVerilog.h:50
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
LogicalResult convertFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function.
Definition: Structure.cpp:943
ModuleLowering * convertModuleHeader(const slang::ast::InstanceBodySymbol *module)
Convert a module and its ports to an empty module op in the IR.
Definition: Structure.cpp:614
Value convertLvalueExpression(const slang::ast::Expression &expr)
Value materializeConstant(const slang::ConstantValue &constant, const slang::ast::Type &type, Location loc)
Helper function to materialize a ConstantValue as an SSA value.
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module)
Convert a module's body to the corresponding IR ops.
Definition: Structure.cpp:786
slang::ast::Compilation & compilation
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.
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:167
DenseMap< const slang::ast::SubroutineSymbol *, std::unique_ptr< FunctionLowering > > functions
Functions that have already been converted.
ValueSymbols::ScopeTy ValueSymbolScope
const ImportVerilogOptions & options
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
LogicalResult traverseInstanceBody(const slang::ast::Symbol &symbol)
SymbolTable symbolTable
A symbol table of the MLIR module we are emitting into.
DenseMap< const slang::ast::InstanceBodySymbol *, SmallVector< HierPathInfo > > hierPaths
Collect all hierarchical names used for the per module/instance.
FunctionLowering * declareFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function and its arguments to a function declaration in the IR.
Definition: Structure.cpp:870
LogicalResult convertPackage(const slang::ast::PackageSymbol &package)
Convert a package and its contents.
Definition: Structure.cpp:855
LogicalResult convertCompilation()
Convert hierarchy and structure AST nodes to MLIR ops.
Definition: Structure.cpp:572
MLIRContext * getContext()
Return the MLIR context.
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Definition: Statements.cpp:695
DenseMap< const slang::ast::InstanceBodySymbol *, std::unique_ptr< ModuleLowering > > modules
How we have lowered modules to MLIR.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.