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 auto procOp = moore::ProcedureOp::create(builder, loc, kind);
557 OpBuilder::InsertionGuard guard(builder);
558 builder.setInsertionPointToEnd(&procOp.getBody().emplaceBlock());
559 Context::ValueSymbolScope scope(context.valueSymbols);
560 if (failed(context.convertStatement(body)))
561 return failure();
562 if (builder.getBlock())
563 moore::ReturnOp::create(builder, loc);
564 return success();
565 }
566
567 LogicalResult visit(const slang::ast::ProceduralBlockSymbol &procNode) {
568 // Detect `always @(*) <stmt>` and convert to `always_comb <stmt>` if
569 // requested by the user.
570 if (context.options.lowerAlwaysAtStarAsComb) {
571 auto *stmt = procNode.getBody().as_if<slang::ast::TimedStatement>();
572 if (procNode.procedureKind == slang::ast::ProceduralBlockKind::Always &&
573 stmt &&
574 stmt->timing.kind == slang::ast::TimingControlKind::ImplicitEvent)
575 return convertProcedure(moore::ProcedureKind::AlwaysComb, stmt->stmt);
576 }
577
578 return convertProcedure(convertProcedureKind(procNode.procedureKind),
579 procNode.getBody());
580 }
581
582 // Handle generate block.
583 LogicalResult visit(const slang::ast::GenerateBlockSymbol &genNode) {
584 // Ignore uninstantiated blocks.
585 if (genNode.isUninstantiated)
586 return success();
587
588 // If the block has a name, add it to the list of block name prefices.
589 SmallString<64> prefix = blockNamePrefix;
590 if (!genNode.name.empty() ||
591 genNode.getParentScope()->asSymbol().kind !=
592 slang::ast::SymbolKind::GenerateBlockArray) {
593 prefix += genNode.getExternalName();
594 prefix += '.';
595 }
596
597 // Visit each member of the generate block.
598 for (auto &member : genNode.members())
599 if (failed(member.visit(ModuleVisitor(context, loc, prefix))))
600 return failure();
601 return success();
602 }
603
604 // Handle generate block array.
605 LogicalResult visit(const slang::ast::GenerateBlockArraySymbol &genArrNode) {
606 // If the block has a name, add it to the list of block name prefices and
607 // prepare to append the array index and a `.` in each iteration.
608 SmallString<64> prefix = blockNamePrefix;
609 prefix += genArrNode.getExternalName();
610 prefix += '_';
611 auto prefixBaseLen = prefix.size();
612
613 // Visit each iteration entry of the generate block.
614 for (const auto *entry : genArrNode.entries) {
615 // Append the index to the prefix.
616 prefix.resize(prefixBaseLen);
617 if (entry->arrayIndex)
618 prefix += entry->arrayIndex->toString();
619 else
620 Twine(entry->constructIndex).toVector(prefix);
621 prefix += '.';
622
623 // Visit this iteration entry.
624 if (failed(entry->asSymbol().visit(ModuleVisitor(context, loc, prefix))))
625 return failure();
626 }
627 return success();
628 }
629
630 // Ignore statement block symbols. These get generated by Slang for blocks
631 // with variables and other declarations. For example, having an initial
632 // procedure with a variable declaration, such as `initial begin int x;
633 // end`, will create the procedure with a block and variable declaration as
634 // expected, but will also create a `StatementBlockSymbol` with just the
635 // variable layout _next to_ the initial procedure.
636 LogicalResult visit(const slang::ast::StatementBlockSymbol &) {
637 return success();
638 }
639
640 // Ignore sequence declarations. The declarations are already evaluated by
641 // Slang and are part of an AssertionInstance.
642 LogicalResult visit(const slang::ast::SequenceSymbol &seqNode) {
643 return success();
644 }
645
646 // Ignore property declarations. The declarations are already evaluated by
647 // Slang and are part of an AssertionInstance.
648 LogicalResult visit(const slang::ast::PropertySymbol &propNode) {
649 return success();
650 }
651
652 // Handle functions and tasks.
653 LogicalResult visit(const slang::ast::SubroutineSymbol &subroutine) {
654 return context.convertFunction(subroutine);
655 }
656
657 /// Emit an error for all other members.
658 template <typename T>
659 LogicalResult visit(T &&node) {
660 mlir::emitError(loc, "unsupported module member: ")
661 << slang::ast::toString(node.kind);
662 return failure();
663 }
664};
665} // namespace
666
667//===----------------------------------------------------------------------===//
668// Structure and Hierarchy Conversion
669//===----------------------------------------------------------------------===//
670
671/// Convert an entire Slang compilation to MLIR ops. This is the main entry
672/// point for the conversion.
673LogicalResult Context::convertCompilation() {
674 const auto &root = compilation.getRoot();
675
676 // Keep track of the local time scale. `getTimeScale` automatically looks
677 // through parent scopes to find the time scale effective locally.
678 auto prevTimeScale = timeScale;
679 timeScale = root.getTimeScale().value_or(slang::TimeScale());
680 auto timeScaleGuard =
681 llvm::make_scope_exit([&] { timeScale = prevTimeScale; });
682
683 // First only to visit the whole AST to collect the hierarchical names without
684 // any operation creating.
685 for (auto *inst : root.topInstances)
686 if (failed(traverseInstanceBody(inst->body)))
687 return failure();
688
689 // Visit all top-level declarations in all compilation units. This does not
690 // include instantiable constructs like modules, interfaces, and programs,
691 // which are listed separately as top instances.
692 for (auto *unit : root.compilationUnits) {
693 for (const auto &member : unit->members()) {
694 auto loc = convertLocation(member.location);
695 if (failed(member.visit(RootVisitor(*this, loc))))
696 return failure();
697 }
698 }
699
700 // Prime the root definition worklist by adding all the top-level modules.
701 SmallVector<const slang::ast::InstanceSymbol *> topInstances;
702 for (auto *inst : root.topInstances)
703 if (!convertModuleHeader(&inst->body))
704 return failure();
705
706 // Convert all the root module definitions.
707 while (!moduleWorklist.empty()) {
708 auto *module = moduleWorklist.front();
709 moduleWorklist.pop();
710 if (failed(convertModuleBody(module)))
711 return failure();
712 }
713
714 // Convert the initializers of global variables.
715 for (auto *var : globalVariableWorklist) {
716 auto varOp = globalVariables.at(var);
717 auto &block = varOp.getInitRegion().emplaceBlock();
718 OpBuilder::InsertionGuard guard(builder);
719 builder.setInsertionPointToEnd(&block);
720 auto value =
721 convertRvalueExpression(*var->getInitializer(), varOp.getType());
722 if (!value)
723 return failure();
724 moore::YieldOp::create(builder, varOp.getLoc(), value);
725 }
727
728 return success();
729}
730
731/// Convert a module and its ports to an empty module op in the IR. Also adds
732/// the op to the worklist of module bodies to be lowered. This acts like a
733/// module "declaration", allowing instances to already refer to a module even
734/// before its body has been lowered.
736Context::convertModuleHeader(const slang::ast::InstanceBodySymbol *module) {
737 using slang::ast::ArgumentDirection;
738 using slang::ast::MultiPortSymbol;
739 using slang::ast::ParameterSymbol;
740 using slang::ast::PortSymbol;
741 using slang::ast::TypeParameterSymbol;
742
743 // Keep track of the local time scale. `getTimeScale` automatically looks
744 // through parent scopes to find the time scale effective locally.
745 auto prevTimeScale = timeScale;
746 timeScale = module->getTimeScale().value_or(slang::TimeScale());
747 auto timeScaleGuard =
748 llvm::make_scope_exit([&] { 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 auto timeScaleGuard =
928 llvm::make_scope_exit([&] { timeScale = prevTimeScale; });
929
930 // Collect downward hierarchical names. Such as,
931 // module SubA; int x = Top.y; endmodule. The "Top" module is the parent of
932 // the "SubA", so "Top.y" is the downward hierarchical name.
933 for (auto &hierPath : hierPaths[module])
934 if (hierPath.direction == slang::ast::ArgumentDirection::In && hierPath.idx)
935 valueSymbols.insert(hierPath.valueSym,
936 lowering.op.getBody()->getArgument(*hierPath.idx));
937
938 // Convert the body of the module.
939 for (auto &member : module->members()) {
940 auto loc = convertLocation(member.location);
941 if (failed(member.visit(ModuleVisitor(*this, loc))))
942 return failure();
943 }
944
945 // Create additional ops to drive input port values onto the corresponding
946 // internal variables and nets, and to collect output port values for the
947 // terminator.
948 SmallVector<Value> outputs;
949 for (auto &port : lowering.ports) {
950 Value value;
951 if (auto *expr = port.ast.getInternalExpr()) {
952 value = convertLvalueExpression(*expr);
953 } else if (port.ast.internalSymbol) {
954 if (const auto *sym =
955 port.ast.internalSymbol->as_if<slang::ast::ValueSymbol>())
956 value = valueSymbols.lookup(sym);
957 }
958 if (!value)
959 return mlir::emitError(port.loc, "unsupported port: `")
960 << port.ast.name
961 << "` does not map to an internal symbol or expression";
962
963 // Collect output port values to be returned in the terminator.
964 if (port.ast.direction == slang::ast::ArgumentDirection::Out) {
965 if (isa<moore::RefType>(value.getType()))
966 value = moore::ReadOp::create(builder, value.getLoc(), value);
967 outputs.push_back(value);
968 continue;
969 }
970
971 // Assign the value coming in through the port to the internal net or symbol
972 // of that port.
973 Value portArg = port.arg;
974 if (port.ast.direction != slang::ast::ArgumentDirection::In)
975 portArg = moore::ReadOp::create(builder, port.loc, port.arg);
976 moore::ContinuousAssignOp::create(builder, port.loc, value, portArg);
977 }
978
979 // Ensure the number of operands of this module's terminator and the number of
980 // its(the current module) output ports remain consistent.
981 for (auto &hierPath : hierPaths[module])
982 if (auto hierValue = valueSymbols.lookup(hierPath.valueSym))
983 if (hierPath.direction == slang::ast::ArgumentDirection::Out)
984 outputs.push_back(hierValue);
985
986 moore::OutputOp::create(builder, lowering.op.getLoc(), outputs);
987 return success();
988}
989
990/// Convert a package and its contents.
991LogicalResult
992Context::convertPackage(const slang::ast::PackageSymbol &package) {
993 // Keep track of the local time scale. `getTimeScale` automatically looks
994 // through parent scopes to find the time scale effective locally.
995 auto prevTimeScale = timeScale;
996 timeScale = package.getTimeScale().value_or(slang::TimeScale());
997 auto timeScaleGuard =
998 llvm::make_scope_exit([&] { timeScale = prevTimeScale; });
999
1000 OpBuilder::InsertionGuard g(builder);
1001 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1003 for (auto &member : package.members()) {
1004 auto loc = convertLocation(member.location);
1005 if (failed(member.visit(PackageVisitor(*this, loc))))
1006 return failure();
1007 }
1008 return success();
1009}
1010
1011/// Convert a function and its arguments to a function declaration in the IR.
1012/// This does not convert the function body.
1014Context::declareFunction(const slang::ast::SubroutineSymbol &subroutine) {
1015 // Check if there already is a declaration for this function.
1016 auto &lowering = functions[&subroutine];
1017 if (lowering) {
1018 if (!lowering->op)
1019 return {};
1020 return lowering.get();
1021 }
1022
1023 if (!subroutine.thisVar) {
1024
1025 SmallString<64> name;
1026 guessNamespacePrefix(subroutine.getParentScope()->asSymbol(), name);
1027 name += subroutine.name;
1028
1029 SmallVector<Type, 1> noThis = {};
1030 return declareCallableImpl(subroutine, name, noThis);
1031 }
1032
1033 auto loc = convertLocation(subroutine.location);
1034
1035 // Extract 'this' type and ensure it's a class.
1036 const slang::ast::Type &thisTy = subroutine.thisVar->getType();
1037 moore::ClassDeclOp ownerDecl;
1038
1039 if (auto *classTy = thisTy.as_if<slang::ast::ClassType>()) {
1040 auto &ownerLowering = classes[classTy];
1041 ownerDecl = ownerLowering->op;
1042 } else {
1043 mlir::emitError(loc) << "expected 'this' to be a class type, got "
1044 << thisTy.toString();
1045 return {};
1046 }
1047
1048 // Build qualified name: @"Pkg::Class"::subroutine
1049 SmallString<64> qualName;
1050 qualName += ownerDecl.getSymName(); // already qualified
1051 qualName += "::";
1052 qualName += subroutine.name;
1053
1054 // %this : class<@C>
1055 SmallVector<Type, 1> extraParams;
1056 {
1057 auto classSym = mlir::FlatSymbolRefAttr::get(ownerDecl.getSymNameAttr());
1058 auto handleTy = moore::ClassHandleType::get(getContext(), classSym);
1059 extraParams.push_back(handleTy);
1060 }
1061
1062 auto *fLowering = declareCallableImpl(subroutine, qualName, extraParams);
1063 return fLowering;
1064}
1065
1066/// Helper function to generate the function signature from a SubroutineSymbol
1067/// and optional extra arguments (used for %this argument)
1068static FunctionType
1070 const slang::ast::SubroutineSymbol &subroutine,
1071 llvm::SmallVectorImpl<Type> &extraParams) {
1072 using slang::ast::ArgumentDirection;
1073
1074 SmallVector<Type> inputTypes;
1075 inputTypes.append(extraParams.begin(), extraParams.end());
1076 SmallVector<Type, 1> outputTypes;
1077
1078 for (const auto *arg : subroutine.getArguments()) {
1079 auto type = context.convertType(arg->getType());
1080 if (!type)
1081 return {};
1082 if (arg->direction == ArgumentDirection::In) {
1083 inputTypes.push_back(type);
1084 } else {
1085 inputTypes.push_back(
1086 moore::RefType::get(cast<moore::UnpackedType>(type)));
1087 }
1088 }
1089
1090 const auto &returnType = subroutine.getReturnType();
1091 if (!returnType.isVoid()) {
1092 auto type = context.convertType(returnType);
1093 if (!type)
1094 return {};
1095 outputTypes.push_back(type);
1096 }
1097
1098 auto funcType =
1099 FunctionType::get(context.getContext(), inputTypes, outputTypes);
1100
1101 // Create a function declaration.
1102 return funcType;
1103}
1104
1105/// Convert a function and its arguments to a function declaration in the IR.
1106/// This does not convert the function body.
1108Context::declareCallableImpl(const slang::ast::SubroutineSymbol &subroutine,
1109 mlir::StringRef qualifiedName,
1110 llvm::SmallVectorImpl<Type> &extraParams) {
1111 auto loc = convertLocation(subroutine.location);
1112 std::unique_ptr<FunctionLowering> lowering =
1113 std::make_unique<FunctionLowering>();
1114
1115 // Pick an insertion point for this function according to the source file
1116 // location.
1117 OpBuilder::InsertionGuard g(builder);
1118 auto it = orderedRootOps.upper_bound(subroutine.location);
1119 if (it == orderedRootOps.end())
1120 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1121 else
1122 builder.setInsertionPoint(it->second);
1123
1124 auto funcTy = getFunctionSignature(*this, subroutine, extraParams);
1125 if (!funcTy)
1126 return nullptr;
1127 auto funcOp = mlir::func::FuncOp::create(builder, loc, qualifiedName, funcTy);
1128
1129 SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
1130 orderedRootOps.insert(it, {subroutine.location, funcOp});
1131 lowering->op = funcOp;
1132
1133 // Add the function to the symbol table of the MLIR module, which uniquifies
1134 // its name.
1135 symbolTable.insert(funcOp);
1136 functions[&subroutine] = std::move(lowering);
1137
1138 return functions[&subroutine].get();
1139}
1140
1141/// Special case handling for recursive functions with captures;
1142/// this function fixes the in-body call of the recursive function with
1143/// the captured arguments.
1144static LogicalResult rewriteCallSitesToPassCaptures(mlir::func::FuncOp callee,
1145 ArrayRef<Value> captures) {
1146 if (captures.empty())
1147 return success();
1148
1149 mlir::ModuleOp module = callee->getParentOfType<mlir::ModuleOp>();
1150 if (!module)
1151 return callee.emitError("expected callee to be nested under ModuleOp");
1152
1153 auto usesOpt = mlir::SymbolTable::getSymbolUses(callee, module);
1154 if (!usesOpt)
1155 return callee.emitError("failed to compute symbol uses");
1156
1157 // Snapshot the relevant users before we mutate IR.
1158 SmallVector<mlir::func::CallOp, 8> callSites;
1159 callSites.reserve(std::distance(usesOpt->begin(), usesOpt->end()));
1160 for (const mlir::SymbolTable::SymbolUse &use : *usesOpt) {
1161 if (auto call = llvm::dyn_cast<mlir::func::CallOp>(use.getUser()))
1162 callSites.push_back(call);
1163 }
1164 if (callSites.empty())
1165 return success();
1166
1167 Block &entry = callee.getBody().front();
1168 const unsigned numCaps = captures.size();
1169 const unsigned numEntryArgs = entry.getNumArguments();
1170 if (numEntryArgs < numCaps)
1171 return callee.emitError("entry block has fewer args than captures");
1172 const unsigned capArgStart = numEntryArgs - numCaps;
1173
1174 // Current (finalized) function type.
1175 auto fTy = callee.getFunctionType();
1176
1177 for (auto call : callSites) {
1178 SmallVector<Value> newOperands(call.getArgOperands().begin(),
1179 call.getArgOperands().end());
1180
1181 const bool inSameFunc = callee->isProperAncestor(call);
1182 if (inSameFunc) {
1183 // Append the functionโ€™s *capture block arguments* in order.
1184 for (unsigned i = 0; i < numCaps; ++i)
1185 newOperands.push_back(entry.getArgument(capArgStart + i));
1186 } else {
1187 // External call site: pass the captured SSA values.
1188 newOperands.append(captures.begin(), captures.end());
1189 }
1190
1191 OpBuilder b(call);
1192 auto flatRef = mlir::FlatSymbolRefAttr::get(callee);
1193 auto newCall = mlir::func::CallOp::create(
1194 b, call.getLoc(), fTy.getResults(), flatRef, newOperands);
1195 call->replaceAllUsesWith(newCall.getOperation());
1196 call->erase();
1197 }
1198
1199 return success();
1200}
1201
1202/// Convert a function.
1203LogicalResult
1204Context::convertFunction(const slang::ast::SubroutineSymbol &subroutine) {
1205 // Keep track of the local time scale. `getTimeScale` automatically looks
1206 // through parent scopes to find the time scale effective locally.
1207 auto prevTimeScale = timeScale;
1208 timeScale = subroutine.getTimeScale().value_or(slang::TimeScale());
1209 auto timeScaleGuard =
1210 llvm::make_scope_exit([&] { timeScale = prevTimeScale; });
1211
1212 // First get or create the function declaration.
1213 auto *lowering = declareFunction(subroutine);
1214 if (!lowering)
1215 return failure();
1216
1217 // If function already has been finalized, or is already being converted
1218 // (recursive/re-entrant calls) stop here.
1219 if (lowering->capturesFinalized || lowering->isConverting)
1220 return success();
1221
1222 const bool isMethod = (subroutine.thisVar != nullptr);
1223
1225
1226 // Create a function body block and populate it with block arguments.
1227 SmallVector<moore::VariableOp> argVariables;
1228 auto &block = lowering->op.getBody().emplaceBlock();
1229
1230 // If this is a class method, the first input is %this :
1231 // !moore.class<@C>
1232 if (isMethod) {
1233 auto thisLoc = convertLocation(subroutine.location);
1234 auto thisType = lowering->op.getFunctionType().getInput(0);
1235 auto thisArg = block.addArgument(thisType, thisLoc);
1236
1237 // Bind `this` so NamedValue/MemberAccess can find it.
1238 valueSymbols.insert(subroutine.thisVar, thisArg);
1239 }
1240
1241 // Add user-defined block arguments
1242 auto inputs = lowering->op.getFunctionType().getInputs();
1243 auto astArgs = subroutine.getArguments();
1244 auto valInputs = llvm::ArrayRef<Type>(inputs).drop_front(isMethod ? 1 : 0);
1245
1246 for (auto [astArg, type] : llvm::zip(astArgs, valInputs)) {
1247 auto loc = convertLocation(astArg->location);
1248 auto blockArg = block.addArgument(type, loc);
1249
1250 if (isa<moore::RefType>(type)) {
1251 valueSymbols.insert(astArg, blockArg);
1252 } else {
1253 OpBuilder::InsertionGuard g(builder);
1254 builder.setInsertionPointToEnd(&block);
1255
1256 auto shadowArg = moore::VariableOp::create(
1257 builder, loc, moore::RefType::get(cast<moore::UnpackedType>(type)),
1258 StringAttr{}, blockArg);
1259 valueSymbols.insert(astArg, shadowArg);
1260 argVariables.push_back(shadowArg);
1261 }
1262 }
1263
1264 // Convert the body of the function.
1265 OpBuilder::InsertionGuard g(builder);
1266 builder.setInsertionPointToEnd(&block);
1267
1268 Value returnVar;
1269 if (subroutine.returnValVar) {
1270 auto type = convertType(*subroutine.returnValVar->getDeclaredType());
1271 if (!type)
1272 return failure();
1273 returnVar = moore::VariableOp::create(
1274 builder, lowering->op.getLoc(),
1275 moore::RefType::get(cast<moore::UnpackedType>(type)), StringAttr{},
1276 Value{});
1277 valueSymbols.insert(subroutine.returnValVar, returnVar);
1278 }
1279
1280 // Save previous callbacks
1281 auto prevRCb = rvalueReadCallback;
1282 auto prevWCb = variableAssignCallback;
1283 auto prevRCbGuard = llvm::make_scope_exit([&] {
1284 rvalueReadCallback = prevRCb;
1285 variableAssignCallback = prevWCb;
1286 });
1287
1288 // Capture this function's captured context directly
1289 rvalueReadCallback = [lowering, prevRCb](moore::ReadOp rop) {
1290 mlir::Value ref = rop.getInput();
1291
1292 // Don't capture anything that's not a reference
1293 mlir::Type ty = ref.getType();
1294 if (!ty || !(isa<moore::RefType>(ty)))
1295 return;
1296
1297 // Don't capture anything that's a local reference
1298 mlir::Region *defReg = ref.getParentRegion();
1299 if (defReg && lowering->op.getBody().isAncestor(defReg))
1300 return;
1301
1302 // If we've already recorded this capture, skip.
1303 if (lowering->captureIndex.count(ref))
1304 return;
1305
1306 // Only capture refs defined outside this functionโ€™s region
1307 auto [it, inserted] =
1308 lowering->captureIndex.try_emplace(ref, lowering->captures.size());
1309 if (inserted) {
1310 lowering->captures.push_back(ref);
1311 // Propagate over outer scope
1312 if (prevRCb)
1313 prevRCb(rop); // chain previous callback
1314 }
1315 };
1316 // Capture this function's captured context directly
1317 variableAssignCallback = [lowering, prevWCb](mlir::Operation *op) {
1318 mlir::Value dstRef =
1319 llvm::TypeSwitch<mlir::Operation *, mlir::Value>(op)
1320 .Case<moore::BlockingAssignOp, moore::NonBlockingAssignOp,
1321 moore::DelayedNonBlockingAssignOp>(
1322 [](auto op) { return op.getDst(); })
1323 .Default([](auto) -> mlir::Value { return {}; });
1324
1325 // Don't capture anything that's not a reference
1326 mlir::Type ty = dstRef.getType();
1327 if (!ty || !(isa<moore::RefType>(ty)))
1328 return;
1329
1330 // Don't capture anything that's a local reference
1331 mlir::Region *defReg = dstRef.getParentRegion();
1332 if (defReg && lowering->op.getBody().isAncestor(defReg))
1333 return;
1334
1335 // If we've already recorded this capture, skip.
1336 if (lowering->captureIndex.count(dstRef))
1337 return;
1338
1339 // Only capture refs defined outside this functionโ€™s region
1340 auto [it, inserted] =
1341 lowering->captureIndex.try_emplace(dstRef, lowering->captures.size());
1342 if (inserted) {
1343 lowering->captures.push_back(dstRef);
1344 // Propagate over outer scope
1345 if (prevWCb)
1346 prevWCb(op); // chain previous callback
1347 }
1348 };
1349
1350 auto savedThis = currentThisRef;
1351 currentThisRef = valueSymbols.lookup(subroutine.thisVar);
1352 auto restoreThis = llvm::make_scope_exit([&] { currentThisRef = savedThis; });
1353
1354 lowering->isConverting = true;
1355 auto convertingGuard =
1356 llvm::make_scope_exit([&] { lowering->isConverting = false; });
1357
1358 if (failed(convertStatement(subroutine.getBody())))
1359 return failure();
1360
1361 // Plumb captures into the function as extra block arguments
1362 if (failed(finalizeFunctionBodyCaptures(*lowering)))
1363 return failure();
1364
1365 // For the special case of recursive functions, fix the call sites within the
1366 // body
1367 if (failed(rewriteCallSitesToPassCaptures(lowering->op, lowering->captures)))
1368 return failure();
1369
1370 // If there was no explicit return statement provided by the user, insert a
1371 // default one.
1372 if (builder.getBlock()) {
1373 if (returnVar && !subroutine.getReturnType().isVoid()) {
1374 Value read =
1375 moore::ReadOp::create(builder, returnVar.getLoc(), returnVar);
1376 mlir::func::ReturnOp::create(builder, lowering->op.getLoc(), read);
1377 } else {
1378 mlir::func::ReturnOp::create(builder, lowering->op.getLoc(),
1379 ValueRange{});
1380 }
1381 }
1382 if (returnVar && returnVar.use_empty())
1383 returnVar.getDefiningOp()->erase();
1384
1385 for (auto var : argVariables) {
1386 if (llvm::all_of(var->getUsers(),
1387 [](auto *user) { return isa<moore::ReadOp>(user); })) {
1388 for (auto *user : llvm::make_early_inc_range(var->getUsers())) {
1389 user->getResult(0).replaceAllUsesWith(var.getInitial());
1390 user->erase();
1391 }
1392 var->erase();
1393 }
1394 }
1395
1396 lowering->capturesFinalized = true;
1397 return success();
1398}
1399
1400LogicalResult
1402 if (lowering.captures.empty())
1403 return success();
1404
1405 MLIRContext *ctx = getContext();
1406
1407 // Build new input type list: existing inputs + capture ref types.
1408 SmallVector<Type> newInputs(lowering.op.getFunctionType().getInputs().begin(),
1409 lowering.op.getFunctionType().getInputs().end());
1410
1411 for (Value cap : lowering.captures) {
1412 // Expect captures to be refs.
1413 Type capTy = cap.getType();
1414 if (!isa<moore::RefType>(capTy)) {
1415 return lowering.op.emitError(
1416 "expected captured value to be a ref-like type");
1417 }
1418 newInputs.push_back(capTy);
1419 }
1420
1421 // Results unchanged.
1422 auto newFuncTy = FunctionType::get(
1423 ctx, newInputs, lowering.op.getFunctionType().getResults());
1424 lowering.op.setFunctionType(newFuncTy);
1425
1426 // Add the new block arguments to the entry block.
1427 Block &entry = lowering.op.getBody().front();
1428 SmallVector<Value> capArgs;
1429 capArgs.reserve(lowering.captures.size());
1430 for (Type t :
1431 llvm::ArrayRef<Type>(newInputs).take_back(lowering.captures.size())) {
1432 capArgs.push_back(entry.addArgument(t, lowering.op.getLoc()));
1433 }
1434
1435 // Replace uses of each captured Value *inside the function body* with the new
1436 // arg. Keep uses outside untouched (e.g., in callers).
1437 for (auto [cap, idx] : lowering.captureIndex) {
1438 Value arg = capArgs[idx];
1439 cap.replaceUsesWithIf(arg, [&](OpOperand &use) {
1440 return lowering.op->isProperAncestor(use.getOwner());
1441 });
1442 }
1443
1444 return success();
1445}
1446
1447namespace {
1448
1449/// Construct a fully qualified class name containing the instance hierarchy
1450/// and the class name formatted as H1::H2::@C
1451mlir::StringAttr fullyQualifiedClassName(Context &ctx,
1452 const slang::ast::Type &ty) {
1453 SmallString<64> name;
1454 SmallVector<llvm::StringRef, 8> parts;
1455
1456 const slang::ast::Scope *scope = ty.getParentScope();
1457 while (scope) {
1458 const auto &sym = scope->asSymbol();
1459 switch (sym.kind) {
1460 case slang::ast::SymbolKind::Root:
1461 scope = nullptr; // stop at $root
1462 continue;
1463 case slang::ast::SymbolKind::InstanceBody:
1464 case slang::ast::SymbolKind::Instance:
1465 case slang::ast::SymbolKind::Package:
1466 case slang::ast::SymbolKind::ClassType:
1467 if (!sym.name.empty())
1468 parts.push_back(sym.name); // keep packages + outer classes
1469 break;
1470 default:
1471 break;
1472 }
1473 scope = sym.getParentScope();
1474 }
1475
1476 for (auto p : llvm::reverse(parts)) {
1477 name += p;
1478 name += "::";
1479 }
1480 name += ty.name; // classโ€™s own name
1481 return mlir::StringAttr::get(ctx.getContext(), name);
1482}
1483
1484/// Helper function to construct the classes fully qualified base class name
1485/// and the name of all implemented interface classes
1486std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr>
1487buildBaseAndImplementsAttrs(Context &context,
1488 const slang::ast::ClassType &cls) {
1489 mlir::MLIRContext *ctx = context.getContext();
1490
1491 // Base class (if any)
1492 mlir::SymbolRefAttr base;
1493 if (const auto *b = cls.getBaseClass())
1494 base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(context, *b));
1495
1496 // Implemented interfaces (if any)
1497 SmallVector<mlir::Attribute> impls;
1498 if (auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) {
1499 impls.reserve(ifaces.size());
1500 for (const auto *iface : ifaces)
1501 impls.push_back(mlir::FlatSymbolRefAttr::get(
1502 fullyQualifiedClassName(context, *iface)));
1503 }
1504
1505 mlir::ArrayAttr implArr =
1506 impls.empty() ? mlir::ArrayAttr() : mlir::ArrayAttr::get(ctx, impls);
1507
1508 return {base, implArr};
1509}
1510
1511/// Visit a slang::ast::ClassType and populate the body of an existing
1512/// moore::ClassDeclOp with field/method decls.
1513struct ClassDeclVisitor {
1515 OpBuilder &builder;
1516 ClassLowering &classLowering;
1517
1518 ClassDeclVisitor(Context &ctx, ClassLowering &lowering)
1519 : context(ctx), builder(ctx.builder), classLowering(lowering) {}
1520
1521 LogicalResult run(const slang::ast::ClassType &classAST) {
1522 if (!classLowering.op.getBody().empty())
1523 return success();
1524
1525 OpBuilder::InsertionGuard ig(builder);
1526
1527 Block *body = &classLowering.op.getBody().emplaceBlock();
1528 builder.setInsertionPointToEnd(body);
1529
1530 for (const auto &mem : classAST.members())
1531 if (failed(mem.visit(*this)))
1532 return failure();
1533
1534 return success();
1535 }
1536
1537 // Properties: ClassPropertySymbol
1538 LogicalResult visit(const slang::ast::ClassPropertySymbol &prop) {
1539 auto loc = convertLocation(prop.location);
1540 auto ty = context.convertType(prop.getType());
1541 if (!ty)
1542 return failure();
1543
1544 moore::ClassPropertyDeclOp::create(builder, loc, prop.name, ty);
1545 return success();
1546 }
1547
1548 // Parameters in specialized classes hold no further information; slang
1549 // already elaborates them in all relevant places.
1550 LogicalResult visit(const slang::ast::ParameterSymbol &) { return success(); }
1551
1552 // Parameters in specialized classes hold no further information; slang
1553 // already elaborates them in all relevant places.
1554 LogicalResult visit(const slang::ast::TypeParameterSymbol &) {
1555 return success();
1556 }
1557
1558 // Type aliases in specialized classes hold no further information; slang
1559 // already elaborates them in all relevant places.
1560 LogicalResult visit(const slang::ast::TypeAliasType &) { return success(); }
1561
1562 // Fully-fledged functions - SubroutineSymbol
1563 LogicalResult visit(const slang::ast::SubroutineSymbol &fn) {
1564 if (fn.flags & slang::ast::MethodFlags::BuiltIn) {
1565 static bool remarkEmitted = false;
1566 if (remarkEmitted)
1567 return success();
1568
1569 mlir::emitRemark(classLowering.op.getLoc())
1570 << "Class builtin functions (needed for randomization, constraints, "
1571 "and covergroups) are not yet supported and will be dropped "
1572 "during lowering.";
1573 remarkEmitted = true;
1574 return success();
1575 }
1576
1577 const mlir::UnitAttr isVirtual =
1578 (fn.flags & slang::ast::MethodFlags::Virtual)
1579 ? UnitAttr::get(context.getContext())
1580 : nullptr;
1581
1582 auto loc = convertLocation(fn.location);
1583 // Pure virtual functions regulate inheritance rules during parsing.
1584 // They don't emit any code, so we don't need to convert them, we only need
1585 // to register them for the purpose of stable VTable construction.
1586 if (fn.flags & slang::ast::MethodFlags::Pure) {
1587 // Add an extra %this argument.
1588 SmallVector<Type, 1> extraParams;
1589 auto classSym =
1590 mlir::FlatSymbolRefAttr::get(classLowering.op.getSymNameAttr());
1591 auto handleTy =
1592 moore::ClassHandleType::get(context.getContext(), classSym);
1593 extraParams.push_back(handleTy);
1594
1595 auto funcTy = getFunctionSignature(context, fn, extraParams);
1596 moore::ClassMethodDeclOp::create(builder, loc, fn.name, funcTy, nullptr);
1597 return success();
1598 }
1599
1600 auto *lowering = context.declareFunction(fn);
1601 if (!lowering)
1602 return failure();
1603
1604 if (failed(context.convertFunction(fn)))
1605 return failure();
1606
1607 if (!lowering->capturesFinalized)
1608 return failure();
1609
1610 // We only emit methoddecls for virtual methods.
1611 if (!isVirtual)
1612 return success();
1613
1614 // Grab the finalized function type from the lowered func.op.
1615 FunctionType fnTy = lowering->op.getFunctionType();
1616 // Emit the method decl into the class body, preserving source order.
1617 moore::ClassMethodDeclOp::create(builder, loc, fn.name, fnTy,
1618 SymbolRefAttr::get(lowering->op));
1619
1620 return success();
1621 }
1622
1623 // A method prototype corresponds to the forward declaration of a concrete
1624 // method, the forward declaration of a virtual method, or the defintion of an
1625 // interface method meant to be implemented by classes implementing the
1626 // interface class.
1627 // In the first two cases, the best thing to do is to look up the actual
1628 // implementation and translate it when reading the method prototype, so we
1629 // can insert the MethodDeclOp in the correct order in the ClassDeclOp.
1630 // The latter case requires support for virtual interface methods, which is
1631 // currently not implemented. Since forward declarations of non-interface
1632 // methods must be followed by an implementation within the same compilation
1633 // unit, we can simply return a failure if we can't find a unique
1634 // implementation until we implement support for interface methods.
1635 LogicalResult visit(const slang::ast::MethodPrototypeSymbol &fn) {
1636 const auto *externImpl = fn.getSubroutine();
1637 // We needn't convert a forward declaration without a unique implementation.
1638 if (!externImpl) {
1639 mlir::emitError(convertLocation(fn.location))
1640 << "Didn't find an implementation matching the forward declaration "
1641 "of "
1642 << fn.name;
1643 return failure();
1644 }
1645 return visit(*externImpl);
1646 }
1647
1648 // Nested class definition, skip
1649 LogicalResult visit(const slang::ast::GenericClassDefSymbol &) {
1650 return success();
1651 }
1652
1653 // Nested class definition, convert
1654 LogicalResult visit(const slang::ast::ClassType &cls) {
1655 return context.convertClassDeclaration(cls);
1656 }
1657
1658 // Transparent members: ignore (inherited names pulled in by slang)
1659 LogicalResult visit(const slang::ast::TransparentMemberSymbol &) {
1660 return success();
1661 }
1662
1663 // Empty members: ignore
1664 LogicalResult visit(const slang::ast::EmptyMemberSymbol &) {
1665 return success();
1666 }
1667
1668 // Emit an error for all other members.
1669 template <typename T>
1670 LogicalResult visit(T &&node) {
1671 Location loc = UnknownLoc::get(context.getContext());
1672 if constexpr (requires { node.location; })
1673 loc = convertLocation(node.location);
1674 mlir::emitError(loc) << "unsupported construct in ClassType members: "
1675 << slang::ast::toString(node.kind);
1676 return failure();
1677 }
1678
1679private:
1680 Location convertLocation(const slang::SourceLocation &sloc) {
1681 return context.convertLocation(sloc);
1682 }
1683};
1684} // namespace
1685
1686ClassLowering *Context::declareClass(const slang::ast::ClassType &cls) {
1687 // Check if there already is a declaration for this class.
1688 auto &lowering = classes[&cls];
1689 if (lowering)
1690 return lowering.get();
1691 lowering = std::make_unique<ClassLowering>();
1692 auto loc = convertLocation(cls.location);
1693
1694 // Pick an insertion point for this function according to the source file
1695 // location.
1696 OpBuilder::InsertionGuard g(builder);
1697 auto it = orderedRootOps.upper_bound(cls.location);
1698 if (it == orderedRootOps.end())
1699 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1700 else
1701 builder.setInsertionPoint(it->second);
1702
1703 auto symName = fullyQualifiedClassName(*this, cls);
1704
1705 // Force build of base here.
1706 if (const auto *maybeBaseClass = cls.getBaseClass())
1707 if (const auto *baseClass = maybeBaseClass->as_if<slang::ast::ClassType>())
1708 if (!classes.contains(baseClass) &&
1709 failed(convertClassDeclaration(*baseClass))) {
1710 mlir::emitError(loc) << "Failed to convert base class "
1711 << baseClass->name << " of class " << cls.name;
1712 return {};
1713 }
1714
1715 auto [base, impls] = buildBaseAndImplementsAttrs(*this, cls);
1716 auto classDeclOp =
1717 moore::ClassDeclOp::create(builder, loc, symName, base, impls);
1718
1719 SymbolTable::setSymbolVisibility(classDeclOp,
1720 SymbolTable::Visibility::Public);
1721 orderedRootOps.insert(it, {cls.location, classDeclOp});
1722 lowering->op = classDeclOp;
1723
1724 symbolTable.insert(classDeclOp);
1725 return lowering.get();
1726}
1727
1728LogicalResult
1729Context::convertClassDeclaration(const slang::ast::ClassType &classdecl) {
1730
1731 // Keep track of local time scale.
1732 auto prevTimeScale = timeScale;
1733 timeScale = classdecl.getTimeScale().value_or(slang::TimeScale());
1734 auto timeScaleGuard =
1735 llvm::make_scope_exit([&] { timeScale = prevTimeScale; });
1736
1737 // Check if there already is a declaration for this class.
1738 if (classes.contains(&classdecl))
1739 return success();
1740
1741 auto *lowering = declareClass(classdecl);
1742 if (failed(ClassDeclVisitor(*this, *lowering).run(classdecl)))
1743 return failure();
1744
1745 return success();
1746}
1747
1748/// Convert a variable to a `moore.global_variable` operation.
1749LogicalResult
1750Context::convertGlobalVariable(const slang::ast::VariableSymbol &var) {
1751 auto loc = convertLocation(var.location);
1752
1753 // Pick an insertion point for this variable according to the source file
1754 // location.
1755 OpBuilder::InsertionGuard g(builder);
1756 auto it = orderedRootOps.upper_bound(var.location);
1757 if (it == orderedRootOps.end())
1758 builder.setInsertionPointToEnd(intoModuleOp.getBody());
1759 else
1760 builder.setInsertionPoint(it->second);
1761
1762 // Prefix the variable name with the surrounding namespace to create somewhat
1763 // sane names in the IR.
1764 SmallString<64> symName;
1765 guessNamespacePrefix(var.getParentScope()->asSymbol(), symName);
1766 symName += var.name;
1767
1768 // Determine the type of the variable.
1769 auto type = convertType(var.getType());
1770 if (!type)
1771 return failure();
1772
1773 // Create the variable op itself.
1774 auto varOp = moore::GlobalVariableOp::create(builder, loc, symName,
1775 cast<moore::UnpackedType>(type));
1776 orderedRootOps.insert({var.location, varOp});
1777 globalVariables.insert({&var, varOp});
1778
1779 // If the variable has an initializer expression, remember it for later such
1780 // that we can convert the initializers once we have seen all global
1781 // variables.
1782 if (var.getInitializer())
1783 globalVariableWorklist.push_back(&var);
1784
1785 return success();
1786}
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:202
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