CIRCT 22.0.0git
Loading...
Searching...
No Matches
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#include "slang/ast/symbols/ClassSymbols.h"
12#include "llvm/ADT/ScopeExit.h"
13
14using namespace circt;
15using namespace ImportVerilog;
16
17//===----------------------------------------------------------------------===//
18// Utilities
19//===----------------------------------------------------------------------===//
20
21static void guessNamespacePrefix(const slang::ast::Symbol &symbol,
22 SmallString<64> &prefix) {
23 if (symbol.kind != slang::ast::SymbolKind::Package)
24 return;
25 guessNamespacePrefix(symbol.getParentScope()->asSymbol(), prefix);
26 if (!symbol.name.empty()) {
27 prefix += symbol.name;
28 prefix += "::";
29 }
30}
31
32//===----------------------------------------------------------------------===//
33// Base Visitor
34//===----------------------------------------------------------------------===//
35
36namespace {
37/// Base visitor which ignores AST nodes that are handled by Slang's name
38/// resolution and type checking.
39struct BaseVisitor {
40 Context &context;
41 Location loc;
42 OpBuilder &builder;
43
44 BaseVisitor(Context &context, Location loc)
45 : context(context), loc(loc), builder(context.builder) {}
46
47 // Skip semicolons.
48 LogicalResult visit(const slang::ast::EmptyMemberSymbol &) {
49 return success();
50 }
51
52 // Skip members that are implicitly imported from some other scope for the
53 // sake of name resolution, such as enum variant names.
54 LogicalResult visit(const slang::ast::TransparentMemberSymbol &) {
55 return success();
56 }
57
58 // Handle classes without parameters or specialized generic classes
59 LogicalResult visit(const slang::ast::ClassType &classdecl) {
60 return context.convertClassDeclaration(classdecl);
61 }
62
63 // GenericClassDefSymbol represents parameterized (template) classes, which
64 // per IEEE 1800-2023 ยง8.25 are abstract and not instantiable. Slang models
65 // concrete specializations as ClassType, so we skip GenericClassDefSymbol
66 // entirely.
67 LogicalResult visit(const slang::ast::GenericClassDefSymbol &) {
68 return success();
69 }
70
71 // Skip typedefs.
72 LogicalResult visit(const slang::ast::TypeAliasType &) { return success(); }
73 LogicalResult visit(const slang::ast::ForwardingTypedefSymbol &) {
74 return success();
75 }
76
77 // Skip imports. The AST already has its names resolved.
78 LogicalResult visit(const slang::ast::ExplicitImportSymbol &) {
79 return success();
80 }
81 LogicalResult visit(const slang::ast::WildcardImportSymbol &) {
82 return success();
83 }
84
85 // Skip type parameters. The Slang AST is already monomorphized.
86 LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
87 return success();
88 }
89
90 // Skip elaboration system tasks. These are reported directly by Slang.
91 LogicalResult visit(const slang::ast::ElabSystemTaskSymbol &) {
92 return success();
93 }
94
95 // Handle parameters.
96 LogicalResult visit(const slang::ast::ParameterSymbol &param) {
97 visitParameter(param);
98 return success();
99 }
100
101 LogicalResult visit(const slang::ast::SpecparamSymbol &param) {
102 visitParameter(param);
103 return success();
104 }
105
106 template <class Node>
107 void visitParameter(const Node &param) {
108 // If debug info is enabled, try to materialize the parameter's constant
109 // value on a best-effort basis and create a `dbg.variable` to track the
110 // value.
111 if (!context.options.debugInfo)
112 return;
113 auto value =
114 context.materializeConstant(param.getValue(), param.getType(), loc);
115 if (!value)
116 return;
117 if (builder.getInsertionBlock()->getParentOp() == context.intoModuleOp)
118 context.orderedRootOps.insert({param.location, value.getDefiningOp()});
119
120 // Prefix the parameter name with the surrounding namespace to create
121 // somewhat sane names in the IR.
122 SmallString<64> paramName;
123 guessNamespacePrefix(param.getParentScope()->asSymbol(), paramName);
124 paramName += param.name;
125
126 debug::VariableOp::create(builder, loc, builder.getStringAttr(paramName),
127 value, Value{});
128 }
129};
130} // namespace
131
132//===----------------------------------------------------------------------===//
133// Top-Level Item Conversion
134//===----------------------------------------------------------------------===//
135
136namespace {
137struct RootVisitor : public BaseVisitor {
138 using BaseVisitor::BaseVisitor;
139 using BaseVisitor::visit;
140
141 // Handle packages.
142 LogicalResult visit(const slang::ast::PackageSymbol &package) {
143 return context.convertPackage(package);
144 }
145
146 // Handle functions and tasks.
147 LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
148 return context.convertFunction(subroutine);
149 }
150
151 // Handle global variables.
152 LogicalResult visit(const slang::ast::VariableSymbol &var) {
153 return context.convertGlobalVariable(var);
154 }
155
156 // Emit an error for all other members.
157 template <typename T>
158 LogicalResult visit(T &&node) {
159 mlir::emitError(loc, "unsupported construct: ")
160 << slang::ast::toString(node.kind);
161 return failure();
162 }
163};
164} // namespace
165
166//===----------------------------------------------------------------------===//
167// Package Conversion
168//===----------------------------------------------------------------------===//
169
170namespace {
171struct PackageVisitor : public BaseVisitor {
172 using BaseVisitor::BaseVisitor;
173 using BaseVisitor::visit;
174
175 // Handle functions and tasks.
176 LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
177 return context.convertFunction(subroutine);
178 }
179
180 // Handle global variables.
181 LogicalResult visit(const slang::ast::VariableSymbol &var) {
182 return context.convertGlobalVariable(var);
183 }
184
185 /// Emit an error for all other members.
186 template <typename T>
187 LogicalResult visit(T &&node) {
188 mlir::emitError(loc, "unsupported package member: ")
189 << slang::ast::toString(node.kind);
190 return failure();
191 }
192};
193} // namespace
194
195//===----------------------------------------------------------------------===//
196// Module Conversion
197//===----------------------------------------------------------------------===//
198
199static moore::ProcedureKind
200convertProcedureKind(slang::ast::ProceduralBlockKind kind) {
201 switch (kind) {
202 case slang::ast::ProceduralBlockKind::Always:
203 return moore::ProcedureKind::Always;
204 case slang::ast::ProceduralBlockKind::AlwaysComb:
205 return moore::ProcedureKind::AlwaysComb;
206 case slang::ast::ProceduralBlockKind::AlwaysLatch:
207 return moore::ProcedureKind::AlwaysLatch;
208 case slang::ast::ProceduralBlockKind::AlwaysFF:
209 return moore::ProcedureKind::AlwaysFF;
210 case slang::ast::ProceduralBlockKind::Initial:
211 return moore::ProcedureKind::Initial;
212 case slang::ast::ProceduralBlockKind::Final:
213 return moore::ProcedureKind::Final;
214 }
215 llvm_unreachable("all procedure kinds handled");
216}
217
218static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind) {
219 switch (kind) {
220 case slang::ast::NetType::Supply0:
221 return moore::NetKind::Supply0;
222 case slang::ast::NetType::Supply1:
223 return moore::NetKind::Supply1;
224 case slang::ast::NetType::Tri:
225 return moore::NetKind::Tri;
226 case slang::ast::NetType::TriAnd:
227 return moore::NetKind::TriAnd;
228 case slang::ast::NetType::TriOr:
229 return moore::NetKind::TriOr;
230 case slang::ast::NetType::TriReg:
231 return moore::NetKind::TriReg;
232 case slang::ast::NetType::Tri0:
233 return moore::NetKind::Tri0;
234 case slang::ast::NetType::Tri1:
235 return moore::NetKind::Tri1;
236 case slang::ast::NetType::UWire:
237 return moore::NetKind::UWire;
238 case slang::ast::NetType::Wire:
239 return moore::NetKind::Wire;
240 case slang::ast::NetType::WAnd:
241 return moore::NetKind::WAnd;
242 case slang::ast::NetType::WOr:
243 return moore::NetKind::WOr;
244 case slang::ast::NetType::Interconnect:
245 return moore::NetKind::Interconnect;
246 case slang::ast::NetType::UserDefined:
247 return moore::NetKind::UserDefined;
248 case slang::ast::NetType::Unknown:
249 return moore::NetKind::Unknown;
250 }
251 llvm_unreachable("all net kinds handled");
252}
253
254namespace {
255struct ModuleVisitor : public BaseVisitor {
256 using BaseVisitor::visit;
257
258 // A prefix of block names such as `foo.bar.` to put in front of variable and
259 // instance names.
260 StringRef blockNamePrefix;
261
262 ModuleVisitor(Context &context, Location loc, StringRef blockNamePrefix = "")
263 : BaseVisitor(context, loc), blockNamePrefix(blockNamePrefix) {}
264
265 // Skip ports which are already handled by the module itself.
266 LogicalResult visit(const slang::ast::PortSymbol &) { return success(); }
267 LogicalResult visit(const slang::ast::MultiPortSymbol &) { return success(); }
268
269 // Skip genvars.
270 LogicalResult visit(const slang::ast::GenvarSymbol &genvarNode) {
271 return success();
272 }
273
274 // Skip defparams which have been handled by slang.
275 LogicalResult visit(const slang::ast::DefParamSymbol &) { return success(); }
276
277 // Ignore type parameters. These have already been handled by Slang's type
278 // checking.
279 LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
280 return success();
281 }
282
283 // Handle instances.
284 LogicalResult visit(const slang::ast::InstanceSymbol &instNode) {
285 using slang::ast::ArgumentDirection;
286 using slang::ast::AssignmentExpression;
287 using slang::ast::MultiPortSymbol;
288 using slang::ast::PortSymbol;
289
290 auto *moduleLowering = context.convertModuleHeader(&instNode.body);
291 if (!moduleLowering)
292 return failure();
293 auto module = moduleLowering->op;
294 auto moduleType = module.getModuleType();
295
296 // Set visibility attribute for instantiated module.
297 SymbolTable::setSymbolVisibility(module, SymbolTable::Visibility::Private);
298
299 // Prepare the values that are involved in port connections. This creates
300 // rvalues for input ports and appropriate lvalues for output, inout, and
301 // ref ports. We also separate multi-ports into the individual underlying
302 // ports with their corresponding connection.
304 portValues.reserve(moduleType.getNumPorts());
305
306 for (const auto *con : instNode.getPortConnections()) {
307 const auto *expr = con->getExpression();
308
309 // Handle unconnected behavior. The expression is null if it have no
310 // connection for the port.
311 if (!expr) {
312 auto *port = con->port.as_if<PortSymbol>();
313 if (auto *existingPort =
314 moduleLowering->portsBySyntaxNode.lookup(port->getSyntax()))
315 port = existingPort;
316
317 switch (port->direction) {
318 case ArgumentDirection::In: {
319 auto refType = moore::RefType::get(
320 cast<moore::UnpackedType>(context.convertType(port->getType())));
321
322 if (const auto *net =
323 port->internalSymbol->as_if<slang::ast::NetSymbol>()) {
324 auto netOp = moore::NetOp::create(
325 builder, loc, refType,
326 StringAttr::get(builder.getContext(), net->name),
327 convertNetKind(net->netType.netKind), nullptr);
328 auto readOp = moore::ReadOp::create(builder, loc, netOp);
329 portValues.insert({port, readOp});
330 } else if (const auto *var =
331 port->internalSymbol
332 ->as_if<slang::ast::VariableSymbol>()) {
333 auto varOp = moore::VariableOp::create(
334 builder, loc, refType,
335 StringAttr::get(builder.getContext(), var->name), nullptr);
336 auto readOp = moore::ReadOp::create(builder, loc, varOp);
337 portValues.insert({port, readOp});
338 } else {
339 return mlir::emitError(loc)
340 << "unsupported internal symbol for unconnected port `"
341 << port->name << "`";
342 }
343 continue;
344 }
345
346 // No need to express unconnected behavior for output port, skip to the
347 // next iteration of the loop.
348 case ArgumentDirection::Out:
349 continue;
350
351 // TODO: Mark Inout port as unsupported and it will be supported later.
352 default:
353 return mlir::emitError(loc)
354 << "unsupported port `" << port->name << "` ("
355 << slang::ast::toString(port->kind) << ")";
356 }
357 }
358
359 // Unpack the `<expr> = EmptyArgument` pattern emitted by Slang for
360 // output and inout ports.
361 if (const auto *assign = expr->as_if<AssignmentExpression>())
362 expr = &assign->left();
363
364 // Regular ports lower the connected expression to an lvalue or rvalue and
365 // either attach it to the instance as an operand (for input, inout, and
366 // ref ports), or assign an instance output to it (for output ports).
367 if (auto *port = con->port.as_if<PortSymbol>()) {
368 // Convert as rvalue for inputs, lvalue for all others.
369 auto value = (port->direction == ArgumentDirection::In)
370 ? context.convertRvalueExpression(*expr)
371 : context.convertLvalueExpression(*expr);
372 if (!value)
373 return failure();
374 if (auto *existingPort =
375 moduleLowering->portsBySyntaxNode.lookup(con->port.getSyntax()))
376 port = existingPort;
377 portValues.insert({port, value});
378 continue;
379 }
380
381 // Multi-ports lower the connected expression to an lvalue and then slice
382 // it up into multiple sub-values, one for each of the ports in the
383 // multi-port.
384 if (const auto *multiPort = con->port.as_if<MultiPortSymbol>()) {
385 // Convert as lvalue.
386 auto value = context.convertLvalueExpression(*expr);
387 if (!value)
388 return failure();
389 unsigned offset = 0;
390 for (const auto *port : llvm::reverse(multiPort->ports)) {
391 if (auto *existingPort = moduleLowering->portsBySyntaxNode.lookup(
392 con->port.getSyntax()))
393 port = existingPort;
394 unsigned width = port->getType().getBitWidth();
395 auto sliceType = context.convertType(port->getType());
396 if (!sliceType)
397 return failure();
398 Value slice = moore::ExtractRefOp::create(
399 builder, loc,
400 moore::RefType::get(cast<moore::UnpackedType>(sliceType)), value,
401 offset);
402 // Create the "ReadOp" for input ports.
403 if (port->direction == ArgumentDirection::In)
404 slice = moore::ReadOp::create(builder, loc, slice);
405 portValues.insert({port, slice});
406 offset += width;
407 }
408 continue;
409 }
410
411 mlir::emitError(loc) << "unsupported instance port `" << con->port.name
412 << "` (" << slang::ast::toString(con->port.kind)
413 << ")";
414 return failure();
415 }
416
417 // Match the module's ports up with the port values determined above.
418 SmallVector<Value> inputValues;
419 SmallVector<Value> outputValues;
420 inputValues.reserve(moduleType.getNumInputs());
421 outputValues.reserve(moduleType.getNumOutputs());
422
423 for (auto &port : moduleLowering->ports) {
424 auto value = portValues.lookup(&port.ast);
425 if (port.ast.direction == ArgumentDirection::Out)
426 outputValues.push_back(value);
427 else
428 inputValues.push_back(value);
429 }
430
431 // Insert conversions for input ports.
432 for (auto [value, type] :
433 llvm::zip(inputValues, moduleType.getInputTypes()))
434 // TODO: This should honor signedness in the conversion.
435 value = context.materializeConversion(type, value, false, value.getLoc());
436
437 // Here we use the hierarchical value recorded in `Context::valueSymbols`.
438 // Then we pass it as the input port with the ref<T> type of the instance.
439 for (const auto &hierPath : context.hierPaths[&instNode.body])
440 if (auto hierValue = context.valueSymbols.lookup(hierPath.valueSym);
441 hierPath.hierName && hierPath.direction == ArgumentDirection::In)
442 inputValues.push_back(hierValue);
443
444 // Create the instance op itself.
445 auto inputNames = builder.getArrayAttr(moduleType.getInputNames());
446 auto outputNames = builder.getArrayAttr(moduleType.getOutputNames());
447 auto inst = moore::InstanceOp::create(
448 builder, loc, moduleType.getOutputTypes(),
449 builder.getStringAttr(Twine(blockNamePrefix) + instNode.name),
450 FlatSymbolRefAttr::get(module.getSymNameAttr()), inputValues,
451 inputNames, outputNames);
452
453 // Record instance's results generated by hierarchical names.
454 for (const auto &hierPath : context.hierPaths[&instNode.body])
455 if (hierPath.idx && hierPath.direction == ArgumentDirection::Out)
456 context.valueSymbols.insert(hierPath.valueSym,
457 inst->getResult(*hierPath.idx));
458
459 // Assign output values from the instance to the connected expression.
460 for (auto [lvalue, output] : llvm::zip(outputValues, inst.getOutputs())) {
461 if (!lvalue)
462 continue;
463 Value rvalue = output;
464 auto dstType = cast<moore::RefType>(lvalue.getType()).getNestedType();
465 // TODO: This should honor signedness in the conversion.
466 rvalue = context.materializeConversion(dstType, rvalue, false, loc);
467 moore::ContinuousAssignOp::create(builder, loc, lvalue, rvalue);
468 }
469
470 return success();
471 }
472
473 // Handle variables.
474 LogicalResult visit(const slang::ast::VariableSymbol &varNode) {
475 auto loweredType = context.convertType(*varNode.getDeclaredType());
476 if (!loweredType)
477 return failure();
478
479 Value initial;
480 if (const auto *init = varNode.getInitializer()) {
481 initial = context.convertRvalueExpression(*init, loweredType);
482 if (!initial)
483 return failure();
484 }
485
486 auto varOp = moore::VariableOp::create(
487 builder, loc,
488 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
489 builder.getStringAttr(Twine(blockNamePrefix) + varNode.name), initial);
490 context.valueSymbols.insert(&varNode, varOp);
491 return success();
492 }
493
494 // Handle nets.
495 LogicalResult visit(const slang::ast::NetSymbol &netNode) {
496 auto loweredType = context.convertType(*netNode.getDeclaredType());
497 if (!loweredType)
498 return failure();
499
500 Value assignment;
501 if (const auto *init = netNode.getInitializer()) {
502 assignment = context.convertRvalueExpression(*init, loweredType);
503 if (!assignment)
504 return failure();
505 }
506
507 auto netkind = convertNetKind(netNode.netType.netKind);
508 if (netkind == moore::NetKind::Interconnect ||
509 netkind == moore::NetKind::UserDefined ||
510 netkind == moore::NetKind::Unknown)
511 return mlir::emitError(loc, "unsupported net kind `")
512 << netNode.netType.name << "`";
513
514 auto netOp = moore::NetOp::create(
515 builder, loc,
516 moore::RefType::get(cast<moore::UnpackedType>(loweredType)),
517 builder.getStringAttr(Twine(blockNamePrefix) + netNode.name), netkind,
518 assignment);
519 context.valueSymbols.insert(&netNode, netOp);
520 return success();
521 }
522
523 // Handle continuous assignments.
524 LogicalResult visit(const slang::ast::ContinuousAssignSymbol &assignNode) {
525 const auto &expr =
526 assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
527 auto lhs = context.convertLvalueExpression(expr.left());
528 if (!lhs)
529 return failure();
530
531 auto rhs = context.convertRvalueExpression(
532 expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
533 if (!rhs)
534 return failure();
535
536 // Handle delayed assignments.
537 if (auto *timingCtrl = assignNode.getDelay()) {
538 auto *ctrl = timingCtrl->as_if<slang::ast::DelayControl>();
539 assert(ctrl && "slang guarantees this to be a simple delay");
540 auto delay = context.convertRvalueExpression(
541 ctrl->expr, moore::TimeType::get(builder.getContext()));
542 if (!delay)
543 return failure();
544 moore::DelayedContinuousAssignOp::create(builder, loc, lhs, rhs, delay);
545 return success();
546 }
547
548 // Otherwise this is a regular assignment.
549 moore::ContinuousAssignOp::create(builder, loc, lhs, rhs);
550 return success();
551 }
552
553 // Handle procedures.
554 LogicalResult convertProcedure(moore::ProcedureKind kind,
555 const slang::ast::Statement &body) {
556 if (body.as_if<slang::ast::ConcurrentAssertionStatement>())
557 return context.convertStatement(body);
558 auto procOp = moore::ProcedureOp::create(builder, loc, kind);
559 OpBuilder::InsertionGuard guard(builder);
560 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
561 Context::ValueSymbolScope scope(context.valueSymbols);
562 if (failed(context.convertStatement(body)))
563 return failure();
564 if (builder.getBlock())
565 moore::ReturnOp::create(builder, loc);
566 return success();
567 }
568
569 LogicalResult visit(const slang::ast::ProceduralBlockSymbol &procNode) {
570 // Detect `always @(*) <stmt>` and convert to `always_comb <stmt>` if
571 // requested by the user.
572 if (context.options.lowerAlwaysAtStarAsComb) {
573 auto *stmt = procNode.getBody().as_if<slang::ast::TimedStatement>();
574 if (procNode.procedureKind == slang::ast::ProceduralBlockKind::Always &&
575 stmt &&
576 stmt->timing.kind == slang::ast::TimingControlKind::ImplicitEvent)
577 return convertProcedure(moore::ProcedureKind::AlwaysComb, stmt->stmt);
578 }
579
580 return convertProcedure(convertProcedureKind(procNode.procedureKind),
581 procNode.getBody());
582 }
583
584 // Handle generate block.
585 LogicalResult visit(const slang::ast::GenerateBlockSymbol &genNode) {
586 // Ignore uninstantiated blocks.
587 if (genNode.isUninstantiated)
588 return success();
589
590 // If the block has a name, add it to the list of block name prefices.
591 SmallString<64> prefix = blockNamePrefix;
592 if (!genNode.name.empty() ||
593 genNode.getParentScope()->asSymbol().kind !=
594 slang::ast::SymbolKind::GenerateBlockArray) {
595 prefix += genNode.getExternalName();
596 prefix += '.';
597 }
598
599 // Visit each member of the generate block.
600 for (auto &member : genNode.members())
601 if (failed(member.visit(ModuleVisitor(context, loc, prefix))))
602 return failure();
603 return success();
604 }
605
606 // Handle generate block array.
607 LogicalResult visit(const slang::ast::GenerateBlockArraySymbol &genArrNode) {
608 // If the block has a name, add it to the list of block name prefices and
609 // prepare to append the array index and a `.` in each iteration.
610 SmallString<64> prefix = blockNamePrefix;
611 prefix += genArrNode.getExternalName();
612 prefix += '_';
613 auto prefixBaseLen = prefix.size();
614
615 // Visit each iteration entry of the generate block.
616 for (const auto *entry : genArrNode.entries) {
617 // Append the index to the prefix.
618 prefix.resize(prefixBaseLen);
619 if (entry->arrayIndex)
620 prefix += entry->arrayIndex->toString();
621 else
622 Twine(entry->constructIndex).toVector(prefix);
623 prefix += '.';
624
625 // Visit this iteration entry.
626 if (failed(entry->asSymbol().visit(ModuleVisitor(context, loc, prefix))))
627 return failure();
628 }
629 return success();
630 }
631
632 // Ignore statement block symbols. These get generated by Slang for blocks
633 // with variables and other declarations. For example, having an initial
634 // procedure with a variable declaration, such as `initial begin int x;
635 // end`, will create the procedure with a block and variable declaration as
636 // expected, but will also create a `StatementBlockSymbol` with just the
637 // variable layout _next to_ the initial procedure.
638 LogicalResult visit(const slang::ast::StatementBlockSymbol &) {
639 return success();
640 }
641
642 // Ignore sequence declarations. The declarations are already evaluated by
643 // Slang and are part of an AssertionInstance.
644 LogicalResult visit(const slang::ast::SequenceSymbol &seqNode) {
645 return success();
646 }
647
648 // Ignore property declarations. The declarations are already evaluated by
649 // Slang and are part of an AssertionInstance.
650 LogicalResult visit(const slang::ast::PropertySymbol &propNode) {
651 return success();
652 }
653
654 // Handle functions and tasks.
655 LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
656 return context.convertFunction(subroutine);
657 }
658
659 /// Emit an error for all other members.
660 template <typename T>
661 LogicalResult visit(T &&node) {
662 mlir::emitError(loc, "unsupported module member: ")
663 << slang::ast::toString(node.kind);
664 return failure();
665 }
666};
667} // namespace
668
669//===----------------------------------------------------------------------===//
670// Structure and Hierarchy Conversion
671//===----------------------------------------------------------------------===//
672
673/// Convert an entire Slang compilation to MLIR ops. This is the main entry
674/// point for the conversion.
675LogicalResult Context::convertCompilation() {
676 const auto &root = compilation.getRoot();
677
678 // Keep track of the local time scale. `getTimeScale` automatically looks
679 // through parent scopes to find the time scale effective locally.
680 auto prevTimeScale = timeScale;
681 timeScale = root.getTimeScale().value_or(slang::TimeScale());
682 llvm::scope_exit timeScaleGuard([&] { timeScale = prevTimeScale; });
683
684 // First only to visit the whole AST to collect the hierarchical names without
685 // any operation creating.
686 for (auto *inst : root.topInstances)
687 if (failed(traverseInstanceBody(inst->body)))
688 return failure();
689
690 // Visit all top-level declarations in all compilation units. This does not
691 // include instantiable constructs like modules, interfaces, and programs,
692 // which are listed separately as top instances.
693 for (auto *unit : root.compilationUnits) {
694 for (const auto &member : unit->members()) {
695 auto loc = convertLocation(member.location);
696 if (failed(member.visit(RootVisitor(*this, loc))))
697 return failure();
698 }
699 }
700
701 // Prime the root definition worklist by adding all the top-level modules.
702 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
703 for (auto *inst : root.topInstances)
704 if (!convertModuleHeader(&inst->body))
705 return failure();
706
707 // Convert all the root module definitions.
708 while (!moduleWorklist.empty()) {
709 auto *module = moduleWorklist.front();
710 moduleWorklist.pop();
711 if (failed(convertModuleBody(module)))
712 return failure();
713 }
714
715 // Convert the initializers of global variables.
716 for (auto *var : globalVariableWorklist) {
717 auto varOp = globalVariables.at(var);
718 auto &block = varOp.getInitRegion().emplaceBlock();
719 OpBuilder::InsertionGuard guard(builder);
720 builder.setInsertionPointToEnd(&block);
721 auto value =
722 convertRvalueExpression(*var->getInitializer(), varOp.getType());
723 if (!value)
724 return failure();
725 moore::YieldOp::create(builder, varOp.getLoc(), value);
726 }
728
729 return success();
730}
731
732/// Convert a module and its ports to an empty module op in the IR. Also adds
733/// the op to the worklist of module bodies to be lowered. This acts like a
734/// module "declaration", allowing instances to already refer to a module even
735/// before its body has been lowered.
737Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
738 using slang::ast::ArgumentDirection;
739 using slang::ast::MultiPortSymbol;
740 using slang::ast::ParameterSymbol;
741 using slang::ast::PortSymbol;
742 using slang::ast::TypeParameterSymbol;
743
744 // Keep track of the local time scale. `getTimeScale` automatically looks
745 // through parent scopes to find the time scale effective locally.
746 auto prevTimeScale = timeScale;
747 timeScale = module->getTimeScale().value_or(slang::TimeScale());
748 llvm::scope_exit timeScaleGuard([&] { timeScale = prevTimeScale; });
749
750 auto parameters = module->getParameters();
751 bool hasModuleSame = false;
752 // If there is already exist a module that has the same name with this
753 // module ,has the same parent scope and has the same parameters we can
754 // define this module is a duplicate module
755 for (auto const &existingModule : modules) {
756 if (module->getDeclaringDefinition() ==
757 existingModule.getFirst()->getDeclaringDefinition()) {
758 auto moduleParameters = existingModule.getFirst()->getParameters();
759 hasModuleSame = true;
760 for (auto it1 = parameters.begin(), it2 = moduleParameters.begin();
761 it1 != parameters.end() && it2 != moduleParameters.end();
762 it1++, it2++) {
763 // Parameters size different
764 if (it1 == parameters.end() || it2 == moduleParameters.end()) {
765 hasModuleSame = false;
766 break;
767 }
768 const auto *para1 = (*it1)->symbol.as_if<ParameterSymbol>();
769 const auto *para2 = (*it2)->symbol.as_if<ParameterSymbol>();
770 // Parameters kind different
771 if ((para1 == nullptr) ^ (para2 == nullptr)) {
772 hasModuleSame = false;
773 break;
774 }
775 // Compare ParameterSymbol
776 if (para1 != nullptr) {
777 hasModuleSame = para1->getValue() == para2->getValue();
778 }
779 // Compare TypeParameterSymbol
780 if (para1 == nullptr) {
781 auto para1Type = convertType(
782 (*it1)->symbol.as<TypeParameterSymbol>().getTypeAlias());
783 auto para2Type = convertType(
784 (*it2)->symbol.as<TypeParameterSymbol>().getTypeAlias());
785 hasModuleSame = para1Type == para2Type;
786 }
787 if (!hasModuleSame)
788 break;
789 }
790 if (hasModuleSame) {
791 module = existingModule.first;
792 break;
793 }
794 }
795 }
796
797 auto &slot = modules[module];
798 if (slot)
799 return slot.get();
800 slot = std::make_unique<ModuleLowering>();
801 auto &lowering = *slot;
802
803 auto loc = convertLocation(module->location);
804 OpBuilder::InsertionGuard g(builder);
805
806 // We only support modules and programs for now. Extension to interfaces
807 // should be trivial though, since they are essentially the same thing with
808 // only minor differences in semantics.
809 auto kind = module->getDefinition().definitionKind;
810 if (kind != slang::ast::DefinitionKind::Module &&
811 kind != slang::ast::DefinitionKind::Program) {
812 mlir::emitError(loc) << "unsupported definition: "
813 << module->getDefinition().getKindString();
814 return {};
815 }
816
817 // Handle the port list.
818 auto block = std::make_unique<Block>();
819 SmallVector<hw::ModulePort> modulePorts;
820
821 // It's used to tag where a hierarchical name is on the port list.
822 unsigned int outputIdx = 0, inputIdx = 0;
823 for (auto *symbol : module->getPortList()) {
824 auto handlePort = [&](const PortSymbol &port) {
825 auto portLoc = convertLocation(port.location);
826 auto type = convertType(port.getType());
827 if (!type)
828 return failure();
829 auto portName = builder.getStringAttr(port.name);
830 BlockArgument arg;
831 if (port.direction == ArgumentDirection::Out) {
832 modulePorts.push_back({portName, type, hw::ModulePort::Output});
833 outputIdx++;
834 } else {
835 // Only the ref type wrapper exists for the time being, the net type
836 // wrapper for inout may be introduced later if necessary.
837 if (port.direction != ArgumentDirection::In)
838 type = moore::RefType::get(cast<moore::UnpackedType>(type));
839 modulePorts.push_back({portName, type, hw::ModulePort::Input});
840 arg = block->addArgument(type, portLoc);
841 inputIdx++;
842 }
843 lowering.ports.push_back({port, portLoc, arg});
844 return success();
845 };
846
847 if (const auto *port = symbol->as_if<PortSymbol>()) {
848 if (failed(handlePort(*port)))
849 return {};
850 } else if (const auto *multiPort = symbol->as_if<MultiPortSymbol>()) {
851 for (auto *port : multiPort->ports)
852 if (failed(handlePort(*port)))
853 return {};
854 } else {
855 mlir::emitError(convertLocation(symbol->location))
856 << "unsupported module port `" << symbol->name << "` ("
857 << slang::ast::toString(symbol->kind) << ")";
858 return {};
859 }
860 }
861
862 // Mapping hierarchical names into the module's ports.
863 for (auto &hierPath : hierPaths[module]) {
864 auto hierType = convertType(hierPath.valueSym->getType());
865 if (!hierType)
866 return {};
867
868 if (auto hierName = hierPath.hierName) {
869 // The type of all hierarchical names are marked as the "RefType".
870 hierType = moore::RefType::get(cast<moore::UnpackedType>(hierType));
871 if (hierPath.direction == ArgumentDirection::Out) {
872 hierPath.idx = outputIdx++;
873 modulePorts.push_back({hierName, hierType, hw::ModulePort::Output});
874 } else {
875 hierPath.idx = inputIdx++;
876 modulePorts.push_back({hierName, hierType, hw::ModulePort::Input});
877 auto hierLoc = convertLocation(hierPath.valueSym->location);
878 block->addArgument(hierType, hierLoc);
879 }
880 }
881 }
882 auto moduleType = hw::ModuleType::get(getContext(), modulePorts);
883
884 // Pick an insertion point for this module according to the source file
885 // location.
886 auto it = orderedRootOps.upper_bound(module->location);
887 if (it == orderedRootOps.end())
888 builder.setInsertionPointToEnd(intoModuleOp.getBody());
889 else
890 builder.setInsertionPoint(it->second);
891
892 // Create an empty module that corresponds to this module.
893 auto moduleOp =
894 moore::SVModuleOp::create(builder, loc, module->name, moduleType);
895 orderedRootOps.insert(it, {module->location, moduleOp});
896 moduleOp.getBodyRegion().push_back(block.release());
897 lowering.op = moduleOp;
898
899 // Add the module to the symbol table of the MLIR module, which uniquifies its
900 // name as we'd expect.
901 symbolTable.insert(moduleOp);
902
903 // Schedule the body to be lowered.
904 moduleWorklist.push(module);
905
906 // Map duplicate port by Syntax
907 for (const auto &port : lowering.ports)
908 lowering.portsBySyntaxNode.insert({port.ast.getSyntax(), &port.ast});
909
910 return &lowering;
911}
912
913/// Convert a module's body to the corresponding IR ops. The module op must have
914/// already been created earlier through a `convertModuleHeader` call.
915LogicalResult
916Context::convertModuleBody(const slang::ast::InstanceBodySymbol *module) {
917 auto &lowering = *modules[module];
918 OpBuilder::InsertionGuard g(builder);
919 builder.setInsertionPointToEnd(lowering.op.getBody());
920
922
923 // Keep track of the local time scale. `getTimeScale` automatically looks
924 // through parent scopes to find the time scale effective locally.
925 auto prevTimeScale = timeScale;
926 timeScale = module->getTimeScale().value_or(slang::TimeScale());
927 llvm::scope_exit timeScaleGuard([&] { timeScale = prevTimeScale; });
928
929 // Collect downward hierarchical names. Such as,
930 // module SubA; int x = Top.y; endmodule. The "Top" module is the parent of
931 // the "SubA", so "Top.y" is the downward hierarchical name.
932 for (auto &hierPath : hierPaths[module])
933 if (hierPath.direction == slang::ast::ArgumentDirection::In && hierPath.idx)
934 valueSymbols.insert(hierPath.valueSym,
935 lowering.op.getBody()->getArgument(*hierPath.idx));
936
937 // Convert the body of the module.
938 for (auto &member : module->members()) {
939 auto loc = convertLocation(member.location);
940 if (failed(member.visit(ModuleVisitor(*this, loc))))
941 return failure();
942 }
943
944 // Create additional ops to drive input port values onto the corresponding
945 // internal variables and nets, and to collect output port values for the
946 // terminator.
947 SmallVector<Value> outputs;
948 for (auto &port : lowering.ports) {
949 Value value;
950 if (auto *expr = port.ast.getInternalExpr()) {
951 value = convertLvalueExpression(*expr);
952 } else if (port.ast.internalSymbol) {
953 if (const auto *sym =
954 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
955 value = valueSymbols.lookup(sym);
956 }
957 if (!value)
958 return mlir::emitError(port.loc, "unsupported port: `")
959 << port.ast.name
960 << "` does not map to an internal symbol or expression";
961
962 // Collect output port values to be returned in the terminator.
963 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
964 if (isa<moore::RefType>(value.getType()))
965 value = moore::ReadOp::create(builder, value.getLoc(), value);
966 outputs.push_back(value);
967 continue;
968 }
969
970 // Assign the value coming in through the port to the internal net or symbol
971 // of that port.
972 Value portArg = port.arg;
973 if (port.ast.direction != slang::ast::ArgumentDirection::In)
974 portArg = moore::ReadOp::create(builder, port.loc, port.arg);
975 moore::ContinuousAssignOp::create(builder, port.loc, value, portArg);
976 }
977
978 // Ensure the number of operands of this module's terminator and the number of
979 // its(the current module) output ports remain consistent.
980 for (auto &hierPath : hierPaths[module])
981 if (auto hierValue = valueSymbols.lookup(hierPath.valueSym))
982 if (hierPath.direction == slang::ast::ArgumentDirection::Out)
983 outputs.push_back(hierValue);
984
985 moore::OutputOp::create(builder, lowering.op.getLoc(), outputs);
986 return success();
987}
988
989/// Convert a package and its contents.
990LogicalResult
991Context::convertPackage(const slang::ast::PackageSymbol &package) {
992 // Keep track of the local time scale. `getTimeScale` automatically looks
993 // through parent scopes to find the time scale effective locally.
994 auto prevTimeScale = timeScale;
995 timeScale = package.getTimeScale().value_or(slang::TimeScale());
996 llvm::scope_exit timeScaleGuard([&] { timeScale = prevTimeScale; });
997
998 OpBuilder::InsertionGuard g(builder);
999 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1001 for (auto &member : package.members()) {
1002 auto loc = convertLocation(member.location);
1003 if (failed(member.visit(PackageVisitor(*this, loc))))
1004 return failure();
1005 }
1006 return success();
1007}
1008
1009/// Convert a function and its arguments to a function declaration in the IR.
1010/// This does not convert the function body.
1012Context::declareFunction(const slang::ast::SubroutineSymbol &subroutine) {
1013 // Check if there already is a declaration for this function.
1014 auto &lowering = functions[&subroutine];
1015 if (lowering) {
1016 if (!lowering->op)
1017 return {};
1018 return lowering.get();
1019 }
1020
1021 if (!subroutine.thisVar) {
1022
1023 SmallString<64> name;
1024 guessNamespacePrefix(subroutine.getParentScope()->asSymbol(), name);
1025 name += subroutine.name;
1026
1027 SmallVector<Type, 1> noThis = {};
1028 return declareCallableImpl(subroutine, name, noThis);
1029 }
1030
1031 auto loc = convertLocation(subroutine.location);
1032
1033 // Extract 'this' type and ensure it's a class.
1034 const slang::ast::Type &thisTy = subroutine.thisVar->getType();
1035 moore::ClassDeclOp ownerDecl;
1036
1037 if (auto *classTy = thisTy.as_if<slang::ast::ClassType>()) {
1038 auto &ownerLowering = classes[classTy];
1039 ownerDecl = ownerLowering->op;
1040 } else {
1041 mlir::emitError(loc) << "expected 'this' to be a class type, got "
1042 << thisTy.toString();
1043 return {};
1044 }
1045
1046 // Build qualified name: @"Pkg::Class"::subroutine
1047 SmallString<64> qualName;
1048 qualName += ownerDecl.getSymName(); // already qualified
1049 qualName += "::";
1050 qualName += subroutine.name;
1051
1052 // %this : class<@C>
1053 SmallVector<Type, 1> extraParams;
1054 {
1055 auto classSym = mlir::FlatSymbolRefAttr::get(ownerDecl.getSymNameAttr());
1056 auto handleTy = moore::ClassHandleType::get(getContext(), classSym);
1057 extraParams.push_back(handleTy);
1058 }
1059
1060 auto *fLowering = declareCallableImpl(subroutine, qualName, extraParams);
1061 return fLowering;
1062}
1063
1064/// Helper function to generate the function signature from a SubroutineSymbol
1065/// and optional extra arguments (used for %this argument)
1066static FunctionType
1068 const slang::ast::SubroutineSymbol &subroutine,
1069 llvm::SmallVectorImpl<Type> &extraParams) {
1070 using slang::ast::ArgumentDirection;
1071
1072 SmallVector<Type> inputTypes;
1073 inputTypes.append(extraParams.begin(), extraParams.end());
1074 SmallVector<Type, 1> outputTypes;
1075
1076 for (const auto *arg : subroutine.getArguments()) {
1077 auto type = context.convertType(arg->getType());
1078 if (!type)
1079 return {};
1080 if (arg->direction == ArgumentDirection::In) {
1081 inputTypes.push_back(type);
1082 } else {
1083 inputTypes.push_back(
1084 moore::RefType::get(cast<moore::UnpackedType>(type)));
1085 }
1086 }
1087
1088 const auto &returnType = subroutine.getReturnType();
1089 if (!returnType.isVoid()) {
1090 auto type = context.convertType(returnType);
1091 if (!type)
1092 return {};
1093 outputTypes.push_back(type);
1094 }
1095
1096 auto funcType =
1097 FunctionType::get(context.getContext(), inputTypes, outputTypes);
1098
1099 // Create a function declaration.
1100 return funcType;
1101}
1102
1103/// Convert a function and its arguments to a function declaration in the IR.
1104/// This does not convert the function body.
1106Context::declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine,
1107 mlir::StringRef qualifiedName,
1108 llvm::SmallVectorImpl<Type> &extraParams) {
1109 auto loc = convertLocation(subroutine.location);
1110 std::unique_ptr<FunctionLowering> lowering =
1111 std::make_unique<FunctionLowering>();
1112
1113 // Pick an insertion point for this function according to the source file
1114 // location.
1115 OpBuilder::InsertionGuard g(builder);
1116 auto it = orderedRootOps.upper_bound(subroutine.location);
1117 if (it == orderedRootOps.end())
1118 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1119 else
1120 builder.setInsertionPoint(it->second);
1121
1122 auto funcTy = getFunctionSignature(*this, subroutine, extraParams);
1123 if (!funcTy)
1124 return nullptr;
1125 auto funcOp = mlir::func::FuncOp::create(builder, loc, qualifiedName, funcTy);
1126
1127 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
1128 orderedRootOps.insert(it, {subroutine.location, funcOp});
1129 lowering->op = funcOp;
1130
1131 // Add the function to the symbol table of the MLIR module, which uniquifies
1132 // its name.
1133 symbolTable.insert(funcOp);
1134 functions[&subroutine] = std::move(lowering);
1135
1136 return functions[&subroutine].get();
1137}
1138
1139/// Special case handling for recursive functions with captures;
1140/// this function fixes the in-body call of the recursive function with
1141/// the captured arguments.
1142static LogicalResult rewriteCallSitesToPassCaptures(mlir::func::FuncOp callee,
1143 ArrayRef<Value> captures) {
1144 if (captures.empty())
1145 return success();
1146
1147 mlir::ModuleOp module = callee->getParentOfType<mlir::ModuleOp>();
1148 if (!module)
1149 return callee.emitError("expected callee to be nested under ModuleOp");
1150
1151 auto usesOpt = mlir::SymbolTable::getSymbolUses(callee, module);
1152 if (!usesOpt)
1153 return callee.emitError("failed to compute symbol uses");
1154
1155 // Snapshot the relevant users before we mutate IR.
1156 SmallVector<mlir::func::CallOp, 8> callSites;
1157 callSites.reserve(std::distance(usesOpt->begin(), usesOpt->end()));
1158 for (const mlir::SymbolTable::SymbolUse &use : *usesOpt) {
1159 if (auto call = llvm::dyn_cast<mlir::func::CallOp>(use.getUser()))
1160 callSites.push_back(call);
1161 }
1162 if (callSites.empty())
1163 return success();
1164
1165 Block &entry = callee.getBody().front();
1166 const unsigned numCaps = captures.size();
1167 const unsigned numEntryArgs = entry.getNumArguments();
1168 if (numEntryArgs < numCaps)
1169 return callee.emitError("entry block has fewer args than captures");
1170 const unsigned capArgStart = numEntryArgs - numCaps;
1171
1172 // Current (finalized) function type.
1173 auto fTy = callee.getFunctionType();
1174
1175 for (auto call : callSites) {
1176 SmallVector<Value> newOperands(call.getArgOperands().begin(),
1177 call.getArgOperands().end());
1178
1179 const bool inSameFunc = callee->isProperAncestor(call);
1180 if (inSameFunc) {
1181 // Append the functionโ€™s *capture block arguments* in order.
1182 for (unsigned i = 0; i < numCaps; ++i)
1183 newOperands.push_back(entry.getArgument(capArgStart + i));
1184 } else {
1185 // External call site: pass the captured SSA values.
1186 newOperands.append(captures.begin(), captures.end());
1187 }
1188
1189 OpBuilder b(call);
1190 auto flatRef = mlir::FlatSymbolRefAttr::get(callee);
1191 auto newCall = mlir::func::CallOp::create(
1192 b, call.getLoc(), fTy.getResults(), flatRef, newOperands);
1193 call->replaceAllUsesWith(newCall.getOperation());
1194 call->erase();
1195 }
1196
1197 return success();
1198}
1199
1200/// Convert a function.
1201LogicalResult
1202Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {
1203 // Keep track of the local time scale. `getTimeScale` automatically looks
1204 // through parent scopes to find the time scale effective locally.
1205 auto prevTimeScale = timeScale;
1206 timeScale = subroutine.getTimeScale().value_or(slang::TimeScale());
1207 llvm::scope_exit timeScaleGuard([&] { timeScale = prevTimeScale; });
1208
1209 // First get or create the function declaration.
1210 auto *lowering = declareFunction(subroutine);
1211 if (!lowering)
1212 return failure();
1213
1214 // If function already has been finalized, or is already being converted
1215 // (recursive/re-entrant calls) stop here.
1216 if (lowering->capturesFinalized || lowering->isConverting)
1217 return success();
1218
1219 const bool isMethod = (subroutine.thisVar != nullptr);
1220
1222
1223 // Create a function body block and populate it with block arguments.
1224 SmallVector<moore::VariableOp> argVariables;
1225 auto &block = lowering->op.getBody().emplaceBlock();
1226
1227 // If this is a class method, the first input is %this :
1228 // !moore.class<@C>
1229 if (isMethod) {
1230 auto thisLoc = convertLocation(subroutine.location);
1231 auto thisType = lowering->op.getFunctionType().getInput(0);
1232 auto thisArg = block.addArgument(thisType, thisLoc);
1233
1234 // Bind `this` so NamedValue/MemberAccess can find it.
1235 valueSymbols.insert(subroutine.thisVar, thisArg);
1236 }
1237
1238 // Add user-defined block arguments
1239 auto inputs = lowering->op.getFunctionType().getInputs();
1240 auto astArgs = subroutine.getArguments();
1241 auto valInputs = llvm::ArrayRef<Type>(inputs).drop_front(isMethod ? 1 : 0);
1242
1243 for (auto [astArg, type] : llvm::zip(astArgs, valInputs)) {
1244 auto loc = convertLocation(astArg->location);
1245 auto blockArg = block.addArgument(type, loc);
1246
1247 if (isa<moore::RefType>(type)) {
1248 valueSymbols.insert(astArg, blockArg);
1249 } else {
1250 OpBuilder::InsertionGuard g(builder);
1251 builder.setInsertionPointToEnd(&block);
1252
1253 auto shadowArg = moore::VariableOp::create(
1254 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
1255 StringAttr{}, blockArg);
1256 valueSymbols.insert(astArg, shadowArg);
1257 argVariables.push_back(shadowArg);
1258 }
1259 }
1260
1261 // Convert the body of the function.
1262 OpBuilder::InsertionGuard g(builder);
1263 builder.setInsertionPointToEnd(&block);
1264
1265 Value returnVar;
1266 if (subroutine.returnValVar) {
1267 auto type = convertType(*subroutine.returnValVar->getDeclaredType());
1268 if (!type)
1269 return failure();
1270 returnVar = moore::VariableOp::create(
1271 builder, lowering->op.getLoc(),
1272 moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
1273 Value{});
1274 valueSymbols.insert(subroutine.returnValVar, returnVar);
1275 }
1276
1277 // Save previous callbacks
1278 auto prevRCb = rvalueReadCallback;
1279 auto prevWCb = variableAssignCallback;
1280 llvm::scope_exit prevRCbGuard([&] {
1281 rvalueReadCallback = prevRCb;
1282 variableAssignCallback = prevWCb;
1283 });
1284
1285 // Capture this function's captured context directly
1286 rvalueReadCallback = [lowering, prevRCb](moore::ReadOp rop) {
1287 mlir::Value ref = rop.getInput();
1288
1289 // Don't capture anything that's not a reference
1290 mlir::Type ty = ref.getType();
1291 if (!ty || !(isa<moore::RefType>(ty)))
1292 return;
1293
1294 // Don't capture anything that's a local reference
1295 mlir::Region *defReg = ref.getParentRegion();
1296 if (defReg && lowering->op.getBody().isAncestor(defReg))
1297 return;
1298
1299 // If we've already recorded this capture, skip.
1300 if (lowering->captureIndex.count(ref))
1301 return;
1302
1303 // Only capture refs defined outside this functionโ€™s region
1304 auto [it, inserted] =
1305 lowering->captureIndex.try_emplace(ref, lowering->captures.size());
1306 if (inserted) {
1307 lowering->captures.push_back(ref);
1308 // Propagate over outer scope
1309 if (prevRCb)
1310 prevRCb(rop); // chain previous callback
1311 }
1312 };
1313 // Capture this function's captured context directly
1314 variableAssignCallback = [lowering, prevWCb](mlir::Operation *op) {
1315 mlir::Value dstRef =
1316 llvm::TypeSwitch<mlir::Operation *, mlir::Value>(op)
1317 .Case<moore::BlockingAssignOp, moore::NonBlockingAssignOp,
1318 moore::DelayedNonBlockingAssignOp>(
1319 [](auto op) { return op.getDst(); })
1320 .Default([](auto) -> mlir::Value { return {}; });
1321
1322 // Don't capture anything that's not a reference
1323 mlir::Type ty = dstRef.getType();
1324 if (!ty || !(isa<moore::RefType>(ty)))
1325 return;
1326
1327 // Don't capture anything that's a local reference
1328 mlir::Region *defReg = dstRef.getParentRegion();
1329 if (defReg && lowering->op.getBody().isAncestor(defReg))
1330 return;
1331
1332 // If we've already recorded this capture, skip.
1333 if (lowering->captureIndex.count(dstRef))
1334 return;
1335
1336 // Only capture refs defined outside this functionโ€™s region
1337 auto [it, inserted] =
1338 lowering->captureIndex.try_emplace(dstRef, lowering->captures.size());
1339 if (inserted) {
1340 lowering->captures.push_back(dstRef);
1341 // Propagate over outer scope
1342 if (prevWCb)
1343 prevWCb(op); // chain previous callback
1344 }
1345 };
1346
1347 auto savedThis = currentThisRef;
1348 currentThisRef = valueSymbols.lookup(subroutine.thisVar);
1349 llvm::scope_exit restoreThis([&] { currentThisRef = savedThis; });
1350
1351 lowering->isConverting = true;
1352 llvm::scope_exit convertingGuard([&] { lowering->isConverting = false; });
1353
1354 if (failed(convertStatement(subroutine.getBody())))
1355 return failure();
1356
1357 // Plumb captures into the function as extra block arguments
1358 if (failed(finalizeFunctionBodyCaptures(*lowering)))
1359 return failure();
1360
1361 // For the special case of recursive functions, fix the call sites within the
1362 // body
1363 if (failed(rewriteCallSitesToPassCaptures(lowering->op, lowering->captures)))
1364 return failure();
1365
1366 // If there was no explicit return statement provided by the user, insert a
1367 // default one.
1368 if (builder.getBlock()) {
1369 if (returnVar && !subroutine.getReturnType().isVoid()) {
1370 Value read =
1371 moore::ReadOp::create(builder, returnVar.getLoc(), returnVar);
1372 mlir::func::ReturnOp::create(builder, lowering->op.getLoc(), read);
1373 } else {
1374 mlir::func::ReturnOp::create(builder, lowering->op.getLoc(),
1375 ValueRange{});
1376 }
1377 }
1378 if (returnVar && returnVar.use_empty())
1379 returnVar.getDefiningOp()->erase();
1380
1381 for (auto var : argVariables) {
1382 if (llvm::all_of(var->getUsers(),
1383 [](auto *user) { return isa<moore::ReadOp>(user); })) {
1384 for (auto *user : llvm::make_early_inc_range(var->getUsers())) {
1385 user->getResult(0).replaceAllUsesWith(var.getInitial());
1386 user->erase();
1387 }
1388 var->erase();
1389 }
1390 }
1391
1392 lowering->capturesFinalized = true;
1393 return success();
1394}
1395
1396LogicalResult
1398 if (lowering.captures.empty())
1399 return success();
1400
1401 MLIRContext *ctx = getContext();
1402
1403 // Build new input type list: existing inputs + capture ref types.
1404 SmallVector<Type> newInputs(lowering.op.getFunctionType().getInputs().begin(),
1405 lowering.op.getFunctionType().getInputs().end());
1406
1407 for (Value cap : lowering.captures) {
1408 // Expect captures to be refs.
1409 Type capTy = cap.getType();
1410 if (!isa<moore::RefType>(capTy)) {
1411 return lowering.op.emitError(
1412 "expected captured value to be a ref-like type");
1413 }
1414 newInputs.push_back(capTy);
1415 }
1416
1417 // Results unchanged.
1418 auto newFuncTy = FunctionType::get(
1419 ctx, newInputs, lowering.op.getFunctionType().getResults());
1420 lowering.op.setFunctionType(newFuncTy);
1421
1422 // Add the new block arguments to the entry block.
1423 Block &entry = lowering.op.getBody().front();
1424 SmallVector<Value> capArgs;
1425 capArgs.reserve(lowering.captures.size());
1426 for (Type t :
1427 llvm::ArrayRef<Type>(newInputs).take_back(lowering.captures.size())) {
1428 capArgs.push_back(entry.addArgument(t, lowering.op.getLoc()));
1429 }
1430
1431 // Replace uses of each captured Value *inside the function body* with the new
1432 // arg. Keep uses outside untouched (e.g., in callers).
1433 for (auto [cap, idx] : lowering.captureIndex) {
1434 Value arg = capArgs[idx];
1435 cap.replaceUsesWithIf(arg, [&](OpOperand &use) {
1436 return lowering.op->isProperAncestor(use.getOwner());
1437 });
1438 }
1439
1440 return success();
1441}
1442
1443namespace {
1444
1445/// Construct a fully qualified class name containing the instance hierarchy
1446/// and the class name formatted as H1::H2::@C
1447mlir::StringAttr fullyQualifiedClassName(Context &ctx,
1448 const slang::ast::Type &ty) {
1449 SmallString<64> name;
1450 SmallVector<llvm::StringRef, 8> parts;
1451
1452 const slang::ast::Scope *scope = ty.getParentScope();
1453 while (scope) {
1454 const auto &sym = scope->asSymbol();
1455 switch (sym.kind) {
1456 case slang::ast::SymbolKind::Root:
1457 scope = nullptr; // stop at $root
1458 continue;
1459 case slang::ast::SymbolKind::InstanceBody:
1460 case slang::ast::SymbolKind::Instance:
1461 case slang::ast::SymbolKind::Package:
1462 case slang::ast::SymbolKind::ClassType:
1463 if (!sym.name.empty())
1464 parts.push_back(sym.name); // keep packages + outer classes
1465 break;
1466 default:
1467 break;
1468 }
1469 scope = sym.getParentScope();
1470 }
1471
1472 for (auto p : llvm::reverse(parts)) {
1473 name += p;
1474 name += "::";
1475 }
1476 name += ty.name; // classโ€™s own name
1477 return mlir::StringAttr::get(ctx.getContext(), name);
1478}
1479
1480/// Helper function to construct the classes fully qualified base class name
1481/// and the name of all implemented interface classes
1482std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr>
1483buildBaseAndImplementsAttrs(Context &context,
1484 const slang::ast::ClassType &cls) {
1485 mlir::MLIRContext *ctx = context.getContext();
1486
1487 // Base class (if any)
1488 mlir::SymbolRefAttr base;
1489 if (const auto *b = cls.getBaseClass())
1490 base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(context, *b));
1491
1492 // Implemented interfaces (if any)
1493 SmallVector<mlir::Attribute> impls;
1494 if (auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) {
1495 impls.reserve(ifaces.size());
1496 for (const auto *iface : ifaces)
1497 impls.push_back(mlir::FlatSymbolRefAttr::get(
1498 fullyQualifiedClassName(context, *iface)));
1499 }
1500
1501 mlir::ArrayAttr implArr =
1502 impls.empty() ? mlir::ArrayAttr() : mlir::ArrayAttr::get(ctx, impls);
1503
1504 return {base, implArr};
1505}
1506
1507/// Visit a slang::ast::ClassType and populate the body of an existing
1508/// moore::ClassDeclOp with field/method decls.
1509struct ClassDeclVisitor {
1511 OpBuilder &builder;
1512 ClassLowering &classLowering;
1513
1514 ClassDeclVisitor(Context &ctx, ClassLowering &lowering)
1515 : context(ctx), builder(ctx.builder), classLowering(lowering) {}
1516
1517 LogicalResult run(const slang::ast::ClassType &classAST) {
1518 if (!classLowering.op.getBody().empty())
1519 return success();
1520
1521 OpBuilder::InsertionGuard ig(builder);
1522
1523 Block *body = &classLowering.op.getBody().emplaceBlock();
1524 builder.setInsertionPointToEnd(body);
1525
1526 llvm::SmallVector<const slang::ast::Symbol *, 8> delayedSymbols;
1527
1528 // Pass 1: visit only ClassPropertySymbols
1529 for (const auto &mem : classAST.members()) {
1530 if (const auto *proto = mem.as_if<slang::ast::ClassPropertySymbol>()) {
1531 if (failed(proto->visit(*this)))
1532 return failure();
1533
1534 } else {
1535 delayedSymbols.push_back(&mem);
1536 }
1537 }
1538
1539 // Pass 2: now resolve/convert everything else after member variables are
1540 // declared.
1541 for (const auto *sym : delayedSymbols) {
1542 if (failed(sym->visit(*this)))
1543 return failure();
1544 }
1545
1546 return success();
1547 }
1548
1549 // Properties: ClassPropertySymbol
1550 LogicalResult visit(const slang::ast::ClassPropertySymbol &prop) {
1551 auto loc = convertLocation(prop.location);
1552 auto ty = context.convertType(prop.getType());
1553 if (!ty)
1554 return failure();
1555
1556 if (prop.lifetime == slang::ast::VariableLifetime::Automatic) {
1557 moore::ClassPropertyDeclOp::create(builder, loc, prop.name, ty);
1558 return success();
1559 }
1560
1561 // Static variables should be accessed like globals, and not emit any
1562 // property declaration. Static variables might get hoisted elsewhere
1563 // so check first whether they have been declared already.
1564
1565 if (!context.globalVariables.lookup(&prop))
1566 return context.convertGlobalVariable(prop);
1567 return success();
1568 }
1569
1570 // Parameters in specialized classes hold no further information; slang
1571 // already elaborates them in all relevant places.
1572 LogicalResult visit(const slang::ast::ParameterSymbol &) { return success(); }
1573
1574 // Parameters in specialized classes hold no further information; slang
1575 // already elaborates them in all relevant places.
1576 LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
1577 return success();
1578 }
1579
1580 // Type aliases in specialized classes hold no further information; slang
1581 // already elaborates them in all relevant places.
1582 LogicalResult visit(const slang::ast::TypeAliasType &) { return success(); }
1583
1584 // Fully-fledged functions - SubroutineSymbol
1585 LogicalResult visit(const slang::ast::SubroutineSymbol &fn) {
1586 if (fn.flags & slang::ast::MethodFlags::BuiltIn) {
1587 static bool remarkEmitted = false;
1588 if (remarkEmitted)
1589 return success();
1590
1591 mlir::emitRemark(classLowering.op.getLoc())
1592 << "Class builtin functions (needed for randomization, constraints, "
1593 "and covergroups) are not yet supported and will be dropped "
1594 "during lowering.";
1595 remarkEmitted = true;
1596 return success();
1597 }
1598
1599 const mlir::UnitAttr isVirtual =
1600 (fn.flags & slang::ast::MethodFlags::Virtual)
1601 ? UnitAttr::get(context.getContext())
1602 : nullptr;
1603
1604 auto loc = convertLocation(fn.location);
1605 // Pure virtual functions regulate inheritance rules during parsing.
1606 // They don't emit any code, so we don't need to convert them, we only need
1607 // to register them for the purpose of stable VTable construction.
1608 if (fn.flags & slang::ast::MethodFlags::Pure) {
1609 // Add an extra %this argument.
1610 SmallVector<Type, 1> extraParams;
1611 auto classSym =
1612 mlir::FlatSymbolRefAttr::get(classLowering.op.getSymNameAttr());
1613 auto handleTy =
1614 moore::ClassHandleType::get(context.getContext(), classSym);
1615 extraParams.push_back(handleTy);
1616
1617 auto funcTy = getFunctionSignature(context, fn, extraParams);
1618 if (!funcTy) {
1619 mlir::emitError(loc) << "Invalid function signature for " << fn.name;
1620 return failure();
1621 }
1622
1623 moore::ClassMethodDeclOp::create(builder, loc, fn.name, funcTy, nullptr);
1624 return success();
1625 }
1626
1627 auto *lowering = context.declareFunction(fn);
1628 if (!lowering)
1629 return failure();
1630
1631 if (failed(context.convertFunction(fn)))
1632 return failure();
1633
1634 if (!lowering->capturesFinalized)
1635 return failure();
1636
1637 // We only emit methoddecls for virtual methods.
1638 if (!isVirtual)
1639 return success();
1640
1641 // Grab the finalized function type from the lowered func.op.
1642 FunctionType fnTy = lowering->op.getFunctionType();
1643 // Emit the method decl into the class body, preserving source order.
1644 moore::ClassMethodDeclOp::create(builder, loc, fn.name, fnTy,
1645 SymbolRefAttr::get(lowering->op));
1646
1647 return success();
1648 }
1649
1650 // A method prototype corresponds to the forward declaration of a concrete
1651 // method, the forward declaration of a virtual method, or the defintion of an
1652 // interface method meant to be implemented by classes implementing the
1653 // interface class.
1654 // In the first two cases, the best thing to do is to look up the actual
1655 // implementation and translate it when reading the method prototype, so we
1656 // can insert the MethodDeclOp in the correct order in the ClassDeclOp.
1657 // The latter case requires support for virtual interface methods, which is
1658 // currently not implemented. Since forward declarations of non-interface
1659 // methods must be followed by an implementation within the same compilation
1660 // unit, we can simply return a failure if we can't find a unique
1661 // implementation until we implement support for interface methods.
1662 LogicalResult visit(const slang::ast::MethodPrototypeSymbol &fn) {
1663 const auto *externImpl = fn.getSubroutine();
1664 // We needn't convert a forward declaration without a unique implementation.
1665 if (!externImpl) {
1666 mlir::emitError(convertLocation(fn.location))
1667 << "Didn't find an implementation matching the forward declaration "
1668 "of "
1669 << fn.name;
1670 return failure();
1671 }
1672 return visit(*externImpl);
1673 }
1674
1675 // Nested class definition, skip
1676 LogicalResult visit(const slang::ast::GenericClassDefSymbol &) {
1677 return success();
1678 }
1679
1680 // Nested class definition, convert
1681 LogicalResult visit(const slang::ast::ClassType &cls) {
1682 return context.convertClassDeclaration(cls);
1683 }
1684
1685 // Transparent members: ignore (inherited names pulled in by slang)
1686 LogicalResult visit(const slang::ast::TransparentMemberSymbol &) {
1687 return success();
1688 }
1689
1690 // Empty members: ignore
1691 LogicalResult visit(const slang::ast::EmptyMemberSymbol &) {
1692 return success();
1693 }
1694
1695 // Emit an error for all other members.
1696 template <typename T>
1697 LogicalResult visit(T &&node) {
1698 Location loc = UnknownLoc::get(context.getContext());
1699 if constexpr (requires { node.location; })
1700 loc = convertLocation(node.location);
1701 mlir::emitError(loc) << "unsupported construct in ClassType members: "
1702 << slang::ast::toString(node.kind);
1703 return failure();
1704 }
1705
1706private:
1707 Location convertLocation(const slang::SourceLocation &sloc) {
1708 return context.convertLocation(sloc);
1709 }
1710};
1711} // namespace
1712
1713ClassLowering *Context::declareClass(const slang::ast::ClassType &cls) {
1714 // Check if there already is a declaration for this class.
1715 auto &lowering = classes[&cls];
1716 if (lowering)
1717 return lowering.get();
1718 lowering = std::make_unique<ClassLowering>();
1719 auto loc = convertLocation(cls.location);
1720
1721 // Pick an insertion point for this function according to the source file
1722 // location.
1723 OpBuilder::InsertionGuard g(builder);
1724 auto it = orderedRootOps.upper_bound(cls.location);
1725 if (it == orderedRootOps.end())
1726 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1727 else
1728 builder.setInsertionPoint(it->second);
1729
1730 auto symName = fullyQualifiedClassName(*this, cls);
1731
1732 // Force build of base here.
1733 if (const auto *maybeBaseClass = cls.getBaseClass())
1734 if (const auto *baseClass = maybeBaseClass->as_if<slang::ast::ClassType>())
1735 if (!classes.contains(baseClass) &&
1736 failed(convertClassDeclaration(*baseClass))) {
1737 mlir::emitError(loc) << "Failed to convert base class "
1738 << baseClass->name << " of class " << cls.name;
1739 return {};
1740 }
1741
1742 auto [base, impls] = buildBaseAndImplementsAttrs(*this, cls);
1743 auto classDeclOp =
1744 moore::ClassDeclOp::create(builder, loc, symName, base, impls);
1745
1746 SymbolTable::setSymbolVisibility(classDeclOp,
1747 SymbolTable::Visibility::Public);
1748 orderedRootOps.insert(it, {cls.location, classDeclOp});
1749 lowering->op = classDeclOp;
1750
1751 symbolTable.insert(classDeclOp);
1752 return lowering.get();
1753}
1754
1755LogicalResult
1756Context::convertClassDeclaration(const slang::ast::ClassType &classdecl) {
1757
1758 // Keep track of local time scale.
1759 auto prevTimeScale = timeScale;
1760 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
1761 llvm::scope_exit timeScaleGuard([&] { timeScale = prevTimeScale; });
1762
1763 // Check if there already is a declaration for this class.
1764 if (classes.contains(&classdecl))
1765 return success();
1766
1767 if (classdecl.getBaseClass()) {
1768 if (const auto *baseClassDecl =
1769 classdecl.getBaseClass()->as_if<slang::ast::ClassType>()) {
1770 if (failed(convertClassDeclaration(*baseClassDecl)))
1771 return failure();
1772 }
1773 }
1774
1775 auto *lowering = declareClass(classdecl);
1776 if (failed(ClassDeclVisitor(*this, *lowering).run(classdecl)))
1777 return failure();
1778
1779 return success();
1780}
1781
1782/// Convert a variable to a `moore.global_variable` operation.
1783LogicalResult
1784Context::convertGlobalVariable(const slang::ast::VariableSymbol &var) {
1785 auto loc = convertLocation(var.location);
1786
1787 // Pick an insertion point for this variable according to the source file
1788 // location.
1789 OpBuilder::InsertionGuard g(builder);
1790 auto it = orderedRootOps.upper_bound(var.location);
1791 if (it == orderedRootOps.end())
1792 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1793 else
1794 builder.setInsertionPoint(it->second);
1795
1796 // Prefix the variable name with the surrounding namespace to create somewhat
1797 // sane names in the IR.
1798 SmallString<64> symName;
1799
1800 // If the variable is a class property, the symbol name needs to be fully
1801 // qualified with the hierarchical class name
1802 if (const auto *classVar = var.as_if<slang::ast::ClassPropertySymbol>()) {
1803 if (const auto *parentScope = classVar->getParentScope()) {
1804 if (const auto *parentClass =
1805 parentScope->asSymbol().as_if<slang::ast::ClassType>())
1806 symName = fullyQualifiedClassName(*this, *parentClass);
1807 else {
1808 mlir::emitError(loc)
1809 << "Could not access parent class of class property "
1810 << classVar->name;
1811 return failure();
1812 }
1813 } else {
1814 mlir::emitError(loc) << "Could not get parent scope of class property "
1815 << classVar->name;
1816 return failure();
1817 }
1818 symName += "::";
1819 symName += var.name;
1820 } else {
1821 guessNamespacePrefix(var.getParentScope()->asSymbol(), symName);
1822 symName += var.name;
1823 }
1824
1825 // Determine the type of the variable.
1826 auto type = convertType(var.getType());
1827 if (!type)
1828 return failure();
1829
1830 // Create the variable op itself.
1831 auto varOp = moore::GlobalVariableOp::create(builder, loc, symName,
1832 cast<moore::UnpackedType>(type));
1833 orderedRootOps.insert({var.location, varOp});
1834 globalVariables.insert({&var, varOp});
1835
1836 // If the variable has an initializer expression, remember it for later such
1837 // that we can convert the initializers once we have seen all global
1838 // variables.
1839 if (var.getInitializer())
1840 globalVariableWorklist.push_back(&var);
1841
1842 return success();
1843}
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition DropConst.cpp:32
static Location convertLocation(MLIRContext *context, const slang::SourceManager &sourceManager, slang::SourceLocation loc)
Convert a slang SourceLocation to an MLIR Location.
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:216
static moore::ProcedureKind convertProcedureKind(slang::ast::ProceduralBlockKind kind)
static FunctionType getFunctionSignature(Context &context, const slang::ast::SubroutineSymbol &subroutine, llvm::SmallVectorImpl< Type > &extraParams)
Helper function to generate the function signature from a SubroutineSymbol and optional extra argumen...
static void guessNamespacePrefix(const slang::ast::Symbol &symbol, SmallString< 64 > &prefix)
Definition Structure.cpp:21
static LogicalResult rewriteCallSitesToPassCaptures(mlir::func::FuncOp callee, ArrayRef< Value > captures)
Special case handling for recursive functions with captures; this function fixes the in-body call of ...
static moore::NetKind convertNetKind(slang::ast::NetType::NetKind kind)
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.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:121
bool debugInfo
Generate debug information in the form of debug dialect ops in the IR.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
LogicalResult convertFunction(const slang::ast::SubroutineSymbol &subroutine)
Convert a function.
FunctionLowering * declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine, mlir::StringRef qualifiedName, llvm::SmallVectorImpl< Type > &extraParams)
Helper function to extract the commonalities in lowering of functions and methods.
ModuleLowering * convertModuleHeader(const slang::ast::InstanceBodySymbol *module)
Convert a module and its ports to an empty module op in the IR.
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.
LogicalResult convertClassDeclaration(const slang::ast::ClassType &classdecl)
DenseMap< const slang::ast::ValueSymbol *, moore::GlobalVariableOp > globalVariables
A table of defined global variables that may be referred to by name in expressions.
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.
LogicalResult convertGlobalVariable(const slang::ast::VariableSymbol &var)
Convert a variable to a moore.global_variable operation.
DenseMap< const slang::ast::ClassType *, std::unique_ptr< ClassLowering > > classes
Classes that have already been converted.
std::function< void(moore::ReadOp)> rvalueReadCallback
A listener called for every variable or net being read.
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:206
DenseMap< const slang::ast::SubroutineSymbol *, std::unique_ptr< FunctionLowering > > functions
Functions that have already been converted.
slang::TimeScale timeScale
The time scale currently in effect.
LogicalResult finalizeFunctionBodyCaptures(FunctionLowering &lowering)
ClassLowering * declareClass(const slang::ast::ClassType &cls)
std::function< void(mlir::Operation *)> variableAssignCallback
A listener called for every variable or net being assigned.
const ImportVerilogOptions & options
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
LogicalResult traverseInstanceBody(const slang::ast::Symbol &symbol)
Value currentThisRef
Variable to track the value of the current function's implicit this reference.
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.
LogicalResult convertPackage(const slang::ast::PackageSymbol &package)
Convert a package and its contents.
MLIRContext * getContext()
Return the MLIR context.
LogicalResult convertStatement(const slang::ast::Statement &stmt)
SmallVector< const slang::ast::ValueSymbol * > globalVariableWorklist
A list of global variables that still need their initializers to be converted.
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.
llvm::DenseMap< Value, unsigned > captureIndex