CIRCT 23.0.0git
Loading...
Searching...
No Matches
Types.cpp
Go to the documentation of this file.
1//===- Types.cpp - Slang type 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/types/AllTypes.h"
11#include "slang/syntax/AllSyntax.h"
12
13using namespace circt;
14using namespace ImportVerilog;
15using moore::Domain;
16
17namespace {
18struct TypeVisitor {
19 Context &context;
20 Location loc;
21 TypeVisitor(Context &context, Location loc) : context(context), loc(loc) {}
22
23 // Handle simple bit vector types such as `bit`, `int`, or `bit [41:0]`.
24 Type getSimpleBitVectorType(const slang::ast::IntegralType &type) {
25 return moore::IntType::get(context.getContext(), type.bitWidth,
26 type.isFourState ? Domain::FourValued
27 : Domain::TwoValued);
28 }
29
30 // NOLINTBEGIN(misc-no-recursion)
31 Type visit(const slang::ast::VoidType &type) {
32 return moore::VoidType::get(context.getContext());
33 }
34
35 Type visit(const slang::ast::ScalarType &type) {
36 return getSimpleBitVectorType(type);
37 }
38
39 Type visit(const slang::ast::FloatingType &type) {
40 if (type.floatKind == slang::ast::FloatingType::Kind::RealTime)
41 return moore::TimeType::get(context.getContext());
42 if (type.floatKind == slang::ast::FloatingType::Kind::Real)
43 return moore::RealType::get(context.getContext(), moore::RealWidth::f64);
44 return moore::RealType::get(context.getContext(), moore::RealWidth::f32);
45 }
46
47 Type visit(const slang::ast::PredefinedIntegerType &type) {
48 if (type.integerKind == slang::ast::PredefinedIntegerType::Kind::Time)
49 return moore::TimeType::get(context.getContext());
50 return getSimpleBitVectorType(type);
51 }
52
53 Type visit(const slang::ast::PackedArrayType &type) {
54 // Handle simple bit vector types of the form `bit [41:0]`.
55 if (type.elementType.as_if<slang::ast::ScalarType>())
56 return getSimpleBitVectorType(type);
57
58 // Handle all other packed arrays.
59 auto innerType = type.elementType.visit(*this);
60 if (!innerType)
61 return {};
62 // The Slang frontend guarantees the inner type to be packed.
63 return moore::ArrayType::get(type.range.width(),
64 cast<moore::PackedType>(innerType));
65 }
66
67 Type visit(const slang::ast::QueueType &type) {
68 auto innerType = type.elementType.visit(*this);
69 if (!innerType)
70 return {};
71 return moore::QueueType::get(cast<moore::UnpackedType>(innerType),
72 type.maxBound);
73 }
74
75 Type visit(const slang::ast::AssociativeArrayType &type) {
76 auto innerType = type.elementType.visit(*this);
77 if (!innerType)
78 return {};
79 if (!type.indexType) {
80 mlir::emitError(
81 loc, "unsupported type: associative arrays with wildcard index");
82 return {};
83 }
84 auto indexType = type.indexType->visit(*this);
85 if (!indexType)
86 return {};
87 return moore::AssocArrayType::get(cast<moore::UnpackedType>(innerType),
88 cast<moore::UnpackedType>(indexType));
89 }
90
91 Type visit(const slang::ast::FixedSizeUnpackedArrayType &type) {
92 auto innerType = type.elementType.visit(*this);
93 if (!innerType)
94 return {};
95 return moore::UnpackedArrayType::get(type.range.width(),
96 cast<moore::UnpackedType>(innerType));
97 }
98
99 Type visit(const slang::ast::DynamicArrayType &type) {
100 auto innerType = type.elementType.visit(*this);
101 if (!innerType)
102 return {};
103 return moore::OpenUnpackedArrayType::get(
104 cast<moore::UnpackedType>(innerType));
105 }
106
107 Type visit(const slang::ast::DPIOpenArrayType &type) {
108 auto innerType = type.elementType.visit(*this);
109 if (!innerType)
110 return {};
111 if (type.isPacked)
112 return moore::OpenArrayType::get(cast<moore::PackedType>(innerType));
113 return moore::OpenUnpackedArrayType::get(
114 cast<moore::UnpackedType>(innerType));
115 }
116
117 // Handle type defs.
118 Type visit(const slang::ast::TypeAliasType &type) {
119 // Simply return the underlying type.
120 return type.targetType.getType().visit(*this);
121 }
122
123 // Handle enums.
124 Type visit(const slang::ast::EnumType &type) {
125 // Simply return the underlying type.
126 return type.baseType.visit(*this);
127 }
128
129 // Collect the members in a struct or union.
130 LogicalResult
131 collectMembers(const slang::ast::Scope &structType,
132 SmallVectorImpl<moore::StructLikeMember> &members) {
133 for (auto &field : structType.membersOfType<slang::ast::FieldSymbol>()) {
134 auto name = StringAttr::get(context.getContext(), field.name);
135 auto innerType = context.convertType(*field.getDeclaredType());
136 if (!innerType)
137 return failure();
138 members.push_back({name, cast<moore::UnpackedType>(innerType)});
139 }
140 return success();
141 }
142
143 // Handle packed and unpacked structs.
144 Type visit(const slang::ast::PackedStructType &type) {
145 SmallVector<moore::StructLikeMember> members;
146 if (failed(collectMembers(type, members)))
147 return {};
148 return moore::StructType::get(context.getContext(), members);
149 }
150
151 Type visit(const slang::ast::UnpackedStructType &type) {
152 SmallVector<moore::StructLikeMember> members;
153 if (failed(collectMembers(type, members)))
154 return {};
155 return moore::UnpackedStructType::get(context.getContext(), members);
156 }
157
158 Type visit(const slang::ast::PackedUnionType &type) {
159 SmallVector<moore::StructLikeMember> members;
160 if (failed(collectMembers(type, members)))
161 return {};
162 return moore::UnionType::get(context.getContext(), members);
163 }
164
165 Type visit(const slang::ast::UnpackedUnionType &type) {
166 SmallVector<moore::StructLikeMember> members;
167 if (failed(collectMembers(type, members)))
168 return {};
169 return moore::UnpackedUnionType::get(context.getContext(), members);
170 }
171
172 Type visit(const slang::ast::StringType &type) {
173 return moore::StringType::get(context.getContext());
174 }
175
176 Type visit(const slang::ast::CHandleType &type) {
177 return moore::ChandleType::get(context.getContext());
178 }
179
180 Type visit(const slang::ast::ClassType &type) {
181 if (failed(context.buildClassProperties(type)))
182 return {};
183 auto *lowering = context.declareClass(type);
184 if (!lowering) {
185 mlir::emitError(loc) << "no lowering generated for class type `"
186 << type.toString() << "`";
187 return {};
188 }
189 mlir::StringAttr symName = lowering->op.getSymNameAttr();
190 mlir::FlatSymbolRefAttr symRef = mlir::FlatSymbolRefAttr::get(symName);
191 return moore::ClassHandleType::get(context.getContext(), symRef);
192 }
193
194 Type visit(const slang::ast::NullType &type) {
195 return moore::NullType::get(context.getContext());
196 }
197
198 Type visit(const slang::ast::VirtualInterfaceType &type) {
199 auto lowered = context.convertVirtualInterfaceType(type, loc);
200 if (failed(lowered))
201 return {};
202 return *lowered;
203 }
204
205 Type visit(const slang::ast::EventType &type) {
206 // Treat `event` types as simple `i1` values where an event is signaled by
207 // toggling the value.
208 return moore::IntType::getInt(context.getContext(), 1);
209 }
210
211 /// Emit an error for all other types.
212 template <typename T>
213 Type visit(T &&node) {
214 auto d = mlir::emitError(loc, "unsupported type: ")
215 << slang::ast::toString(node.kind);
216 d.attachNote() << node.template as<slang::ast::Type>().toString();
217 return {};
218 }
219 // NOLINTEND(misc-no-recursion)
220};
221} // namespace
222
223// NOLINTBEGIN(misc-no-recursion)
224Type Context::convertType(const slang::ast::Type &type, LocationAttr loc) {
225 if (!loc)
226 loc = convertLocation(type.location);
227 return type.visit(TypeVisitor(*this, loc));
228}
229
230Type Context::convertType(const slang::ast::DeclaredType &type) {
231 LocationAttr loc;
232 if (auto *ts = type.getTypeSyntax())
233 loc = convertLocation(ts->sourceRange().start());
234 return convertType(type.getType(), loc);
235}
236// NOLINTEND(misc-no-recursion)
237
238FailureOr<moore::UnpackedStructType> Context::convertVirtualInterfaceType(
239 const slang::ast::VirtualInterfaceType &type, Location loc) {
240 const slang::ast::InstanceBodySymbol &ifaceBody = type.iface.body;
241 const slang::ast::ModportSymbol *modport = type.modport;
242
243 auto &cache = modport ? virtualIfaceModportLowerings[modport]
244 : virtualIfaceLowerings[&ifaceBody];
245 if (cache.type)
246 return cache.type;
247
248 SmallVector<moore::StructLikeMember> members;
249 SmallVector<StringAttr, 8> fieldNames;
250 DenseMap<StringAttr, Type> fieldTypes;
251
252 auto addField = [&](StringRef name, const slang::ast::Type &fieldAstType,
253 Location fieldLoc) -> LogicalResult {
254 auto nameAttr = builder.getStringAttr(name);
255
256 Type loweredType = convertType(fieldAstType, fieldLoc);
257 if (!loweredType)
258 return failure();
259
260 auto unpacked = dyn_cast<moore::UnpackedType>(loweredType);
261 if (!unpacked) {
262 mlir::emitError(fieldLoc)
263 << "unsupported virtual interface member type: " << loweredType;
264 return failure();
265 }
266
267 auto refTy = moore::RefType::get(unpacked);
268
269 if (auto it = fieldTypes.find(nameAttr); it != fieldTypes.end()) {
270 if (it->second != refTy) {
271 mlir::emitError(fieldLoc) << "virtual interface member `" << name
272 << "` has conflicting types (" << it->second
273 << " vs " << refTy << ")";
274 return failure();
275 }
276 return success();
277 }
278
279 fieldTypes.try_emplace(nameAttr, refTy);
280 members.push_back({nameAttr, refTy});
281 fieldNames.push_back(nameAttr);
282 return success();
283 };
284
285 if (modport) {
286 for (auto &member : modport->members()) {
287 const auto *mpp = member.as_if<slang::ast::ModportPortSymbol>();
288 if (!mpp) {
289 auto d = mlir::emitError(convertLocation(member.location))
290 << "unsupported modport member: "
291 << slang::ast::toString(member.kind);
292 if (!member.name.empty())
293 d << " `" << member.name << "`";
294 return failure();
295 }
296 if (failed(addField(mpp->name, mpp->getType(),
297 convertLocation(mpp->location))))
298 return failure();
299 }
300 } else {
301 for (auto *symbol : ifaceBody.getPortList()) {
302 if (!symbol)
303 continue;
304 const auto *port = symbol->as_if<slang::ast::PortSymbol>();
305 if (!port) {
306 auto d = mlir::emitError(convertLocation(symbol->location))
307 << "unsupported interface port symbol: "
308 << slang::ast::toString(symbol->kind);
309 if (!symbol->name.empty())
310 d << " `" << symbol->name << "`";
311 return failure();
312 }
313 if (failed(addField(port->name, port->getType(),
314 convertLocation(port->location))))
315 return failure();
316 }
317
318 for (auto &member : ifaceBody.members()) {
319 if (const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
320 if (failed(addField(var->name, var->getType(),
321 convertLocation(var->location))))
322 return failure();
323 continue;
324 }
325 if (const auto *net = member.as_if<slang::ast::NetSymbol>()) {
326 if (failed(addField(net->name, net->getType(),
327 convertLocation(net->location))))
328 return failure();
329 continue;
330 }
331 // Skip non-data interface members that do not contribute to the virtual
332 // interface handle representation.
333 if (member.as_if<slang::ast::ModportSymbol>() ||
334 member.as_if<slang::ast::ParameterSymbol>() ||
335 member.as_if<slang::ast::TypeParameterSymbol>())
336 continue;
337
338 // Bail out loudly on unhandled value symbols to avoid silently dropping
339 // interface members that the user may expect to access through a virtual
340 // interface.
341 if (const auto *value = member.as_if<slang::ast::ValueSymbol>()) {
342 auto d = mlir::emitError(convertLocation(value->location))
343 << "unsupported interface member: "
344 << slang::ast::toString(value->kind);
345 if (!value->name.empty())
346 d << " `" << value->name << "`";
347 return failure();
348 }
349 }
350 }
351
352 cache.type = moore::UnpackedStructType::get(getContext(), members);
353 cache.fieldNames = fieldNames;
354 return cache.type;
355}
356
358 const slang::ast::VirtualInterfaceType &type, Location loc) {
359 if (!type.isRealIface) {
360 mlir::emitError(loc)
361 << "cannot materialize value for non-real virtual interface";
362 return failure();
363 }
364
365 auto loweredType = convertVirtualInterfaceType(type, loc);
366 if (failed(loweredType))
367 return failure();
368
369 const slang::ast::InstanceBodySymbol &ifaceBody = type.iface.body;
370 const slang::ast::ModportSymbol *modport = type.modport;
371 const auto &cache = modport ? virtualIfaceModportLowerings.lookup(modport)
372 : virtualIfaceLowerings.lookup(&ifaceBody);
373 if (!cache.type)
374 return failure();
375
376 auto *ifaceLowering = interfaceInstances.lookup(&type.iface);
377 if (!ifaceLowering) {
378 mlir::emitError(loc) << "interface instance `" << type.iface.name
379 << "` was not expanded";
380 return failure();
381 }
382
383 SmallVector<Value> fields;
384 fields.reserve(cache.fieldNames.size());
385
386 auto resolveInterfaceMember = [&](StringAttr nameAttr) -> FailureOr<Value> {
387 if (!nameAttr)
388 return failure();
389
390 if (Value val = ifaceLowering->expandedMembersByName.lookup(nameAttr))
391 return val;
392
393 mlir::emitError(loc) << "unresolved interface member `"
394 << nameAttr.getValue() << "`";
395 return failure();
396 };
397
398 if (modport) {
399 DenseMap<StringAttr, const slang::ast::ModportPortSymbol *> portsByName;
400 for (auto &sym : modport->members()) {
401 const auto *port = sym.as_if<slang::ast::ModportPortSymbol>();
402 if (!port) {
403 auto d = mlir::emitError(convertLocation(sym.location))
404 << "unsupported modport member: "
405 << slang::ast::toString(sym.kind);
406 if (!sym.name.empty())
407 d << " `" << sym.name << "`";
408 return failure();
409 }
410 auto nameAttr = builder.getStringAttr(port->name);
411 portsByName.try_emplace(nameAttr, port);
412 }
413
414 for (auto nameAttr : cache.fieldNames) {
415 const auto *port = portsByName.lookup(nameAttr);
416 if (!port) {
417 mlir::emitError(loc)
418 << "unresolved modport member `" << nameAttr.getValue() << "`";
419 return failure();
420 }
421
422 if (port->internalSymbol) {
423 if (Value val =
424 ifaceLowering->expandedMembers.lookup(port->internalSymbol)) {
425 fields.push_back(val);
426 continue;
427 }
428 // Fallback to a name-based lookup if the interface expansion recorded
429 // the member under a different symbol pointer.
430 auto resolved = resolveInterfaceMember(
431 builder.getStringAttr(port->internalSymbol->name));
432 if (failed(resolved))
433 return failure();
434 fields.push_back(*resolved);
435 continue;
436 }
437
438 const auto *connExpr = port->getConnectionExpr();
439 if (!connExpr) {
440 mlir::emitError(loc) << "modport member `" << nameAttr.getValue()
441 << "` has no connection";
442 return failure();
443 }
444
445 // Evaluate explicit modport connections in an environment where
446 // interface members are in scope as lvalues.
448 for (const auto &[sym, value] : ifaceLowering->expandedMembers) {
449 const auto *valueSym = sym->as_if<slang::ast::ValueSymbol>();
450 if (!valueSym)
451 continue;
452 valueSymbols.insertIntoScope(valueSymbols.getCurScope(), valueSym,
453 value);
454 }
455
456 Value val = convertLvalueExpression(*connExpr);
457 if (!val)
458 return failure();
459 fields.push_back(val);
460 }
461 } else {
462 for (auto nameAttr : cache.fieldNames) {
463 auto val = resolveInterfaceMember(nameAttr);
464 if (failed(val))
465 return failure();
466 fields.push_back(*val);
467 }
468 }
469
470 return moore::StructCreateOp::create(builder, loc, cache.type, fields)
471 .getResult();
472}
473
475 const slang::ast::ValueSymbol &base,
476 const slang::ast::VirtualInterfaceType &type, Location loc) {
477 auto *scope = virtualIfaceMembers.getCurScope();
478 if (!scope) {
479 mlir::emitError(loc) << "internal error: no virtual interface member scope";
480 return failure();
481 }
482
483 auto registerMember = [&](const slang::ast::ValueSymbol &member,
484 StringRef fieldName) {
486 entry.base = &base;
487 entry.fieldName = builder.getStringAttr(fieldName);
488
489 if (auto existing = virtualIfaceMembers.lookup(&member);
490 existing.base == &base && existing.fieldName == entry.fieldName)
491 return;
492
493 virtualIfaceMembers.insertIntoScope(scope, &member, entry);
494 };
495
496 if (const auto *modport = type.modport) {
497 for (auto &sym : modport->members()) {
498 const auto *port = sym.as_if<slang::ast::ModportPortSymbol>();
499 if (!port) {
500 auto d = mlir::emitError(convertLocation(sym.location))
501 << "unsupported modport member: "
502 << slang::ast::toString(sym.kind);
503 if (!sym.name.empty())
504 d << " `" << sym.name << "`";
505 return failure();
506 }
507 registerMember(*port, port->name);
508 if (port->internalSymbol)
509 if (const auto *internal =
510 port->internalSymbol->as_if<slang::ast::ValueSymbol>())
511 registerMember(*internal, port->name);
512 }
513 return success();
514 }
515
516 const slang::ast::InstanceBodySymbol &ifaceBody = type.iface.body;
517
518 // Register interface ports by mapping their internal symbols (where
519 // applicable) to the corresponding virtual interface field.
520 for (const auto *symbol : ifaceBody.getPortList()) {
521 if (!symbol)
522 continue;
523 const auto *port = symbol->as_if<slang::ast::PortSymbol>();
524 if (!port) {
525 auto d = mlir::emitError(convertLocation(symbol->location))
526 << "unsupported interface port symbol: "
527 << slang::ast::toString(symbol->kind);
528 if (!symbol->name.empty())
529 d << " `" << symbol->name << "`";
530 return failure();
531 }
532 if (!port->internalSymbol)
533 continue;
534 if (const auto *internal =
535 port->internalSymbol->as_if<slang::ast::ValueSymbol>())
536 registerMember(*internal, port->name);
537 }
538
539 // Register variables and nets declared in the interface body.
540 for (auto &member : ifaceBody.members()) {
541 if (const auto *var = member.as_if<slang::ast::VariableSymbol>()) {
542 registerMember(*var, var->name);
543 continue;
544 }
545 if (const auto *net = member.as_if<slang::ast::NetSymbol>()) {
546 registerMember(*net, net->name);
547 continue;
548 }
549 }
550
551 return success();
552}
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:422
Domain
The number of values each bit of a type can assume.
Definition MooreTypes.h:50
@ TwoValued
Two-valued types such as bit or int.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
Value convertLvalueExpression(const slang::ast::Expression &expr)
LogicalResult registerVirtualInterfaceMembers(const slang::ast::ValueSymbol &base, const slang::ast::VirtualInterfaceType &type, Location loc)
Register the interface members of a virtual interface base symbol for use in later expression convers...
Definition Types.cpp:474
DenseMap< const slang::ast::InstanceBodySymbol *, VirtualInterfaceLowering > virtualIfaceLowerings
Cached virtual interface layouts (type + field order).
OpBuilder builder
The builder used to create IR operations.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Definition Types.cpp:224
ClassLowering * declareClass(const slang::ast::ClassType &cls)
VirtualInterfaceMembers virtualIfaceMembers
DenseMap< const slang::ast::ModportSymbol *, VirtualInterfaceLowering > virtualIfaceModportLowerings
FailureOr< Value > materializeVirtualInterfaceValue(const slang::ast::VirtualInterfaceType &type, Location loc)
Materialize a Moore value representing a concrete interface instance as a virtual interface handle.
Definition Types.cpp:357
LogicalResult buildClassProperties(const slang::ast::ClassType &classdecl)
MLIRContext * getContext()
Return the MLIR context.
FailureOr< moore::UnpackedStructType > convertVirtualInterfaceType(const slang::ast::VirtualInterfaceType &type, Location loc)
Convert a Slang virtual interface type into the Moore type used to represent virtual interface handle...
Definition Types.cpp:238
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.
A mapping entry for resolving Slang virtual interface member accesses.
StringAttr fieldName
The name of the field in the lowered virtual interface handle struct that should be accessed for this...