|
8 | 8 |
|
9 | 9 | #include "ImportVerilogInternals.h" |
10 | 10 | #include "slang/ast/Compilation.h" |
| 11 | +#include "slang/ast/symbols/ClassSymbols.h" |
11 | 12 | #include "llvm/ADT/ScopeExit.h" |
12 | 13 |
|
13 | 14 | using namespace circt; |
@@ -54,6 +55,11 @@ struct BaseVisitor { |
54 | 55 | return success(); |
55 | 56 | } |
56 | 57 |
|
| 58 | + // Handle classes without parameters |
| 59 | + LogicalResult visit(const slang::ast::ClassType &classdecl) { |
| 60 | + return context.convertClassDeclaration(classdecl); |
| 61 | + } |
| 62 | + |
57 | 63 | // Skip typedefs. |
58 | 64 | LogicalResult visit(const slang::ast::TypeAliasType &) { return success(); } |
59 | 65 | LogicalResult visit(const slang::ast::ForwardingTypedefSymbol &) { |
@@ -1309,3 +1315,204 @@ Context::finalizeFunctionBodyCaptures(FunctionLowering &lowering) { |
1309 | 1315 |
|
1310 | 1316 | return success(); |
1311 | 1317 | } |
| 1318 | + |
| 1319 | +namespace { |
| 1320 | + |
| 1321 | +/// Construct a fully qualified class name containing the instance hierarchy |
| 1322 | +/// and the class name formatted as H1::H2::@C |
| 1323 | +mlir::StringAttr fullyQualifiedClassName(Context &ctx, |
| 1324 | + const slang::ast::Type &ty) { |
| 1325 | + SmallString<64> name; |
| 1326 | + SmallVector<llvm::StringRef, 8> parts; |
| 1327 | + |
| 1328 | + const slang::ast::Scope *scope = ty.getParentScope(); |
| 1329 | + while (scope) { |
| 1330 | + const auto &sym = scope->asSymbol(); |
| 1331 | + switch (sym.kind) { |
| 1332 | + case slang::ast::SymbolKind::Root: |
| 1333 | + scope = nullptr; // stop at $root |
| 1334 | + continue; |
| 1335 | + case slang::ast::SymbolKind::InstanceBody: |
| 1336 | + case slang::ast::SymbolKind::Instance: |
| 1337 | + case slang::ast::SymbolKind::Package: |
| 1338 | + case slang::ast::SymbolKind::ClassType: |
| 1339 | + if (!sym.name.empty()) |
| 1340 | + parts.push_back(sym.name); // keep packages + outer classes |
| 1341 | + break; |
| 1342 | + default: |
| 1343 | + break; |
| 1344 | + } |
| 1345 | + scope = sym.getParentScope(); |
| 1346 | + } |
| 1347 | + |
| 1348 | + for (auto p : llvm::reverse(parts)) { |
| 1349 | + name += p; |
| 1350 | + name += "::"; |
| 1351 | + } |
| 1352 | + name += ty.name; // class’s own name |
| 1353 | + return mlir::StringAttr::get(ctx.getContext(), name); |
| 1354 | +} |
| 1355 | + |
| 1356 | +/// Helper function to construct the classes fully qualified base class name |
| 1357 | +/// and the name of all implemented interface classes |
| 1358 | +std::pair<mlir::SymbolRefAttr, mlir::ArrayAttr> |
| 1359 | +buildBaseAndImplementsAttrs(Context &context, |
| 1360 | + const slang::ast::ClassType &cls) { |
| 1361 | + mlir::MLIRContext *ctx = context.getContext(); |
| 1362 | + |
| 1363 | + // Base class (if any) |
| 1364 | + mlir::SymbolRefAttr base; |
| 1365 | + if (const auto *b = cls.getBaseClass()) |
| 1366 | + base = mlir::SymbolRefAttr::get(fullyQualifiedClassName(context, *b)); |
| 1367 | + |
| 1368 | + // Implemented interfaces (if any) |
| 1369 | + SmallVector<mlir::Attribute> impls; |
| 1370 | + if (auto ifaces = cls.getDeclaredInterfaces(); !ifaces.empty()) { |
| 1371 | + impls.reserve(ifaces.size()); |
| 1372 | + for (const auto *iface : ifaces) |
| 1373 | + impls.push_back(mlir::FlatSymbolRefAttr::get( |
| 1374 | + fullyQualifiedClassName(context, *iface))); |
| 1375 | + } |
| 1376 | + |
| 1377 | + mlir::ArrayAttr implArr = |
| 1378 | + impls.empty() ? mlir::ArrayAttr() : mlir::ArrayAttr::get(ctx, impls); |
| 1379 | + |
| 1380 | + return {base, implArr}; |
| 1381 | +} |
| 1382 | + |
| 1383 | +/// Visit a slang::ast::ClassType and populate the body of an existing |
| 1384 | +/// moore::ClassDeclOp with field/method decls. |
| 1385 | +struct ClassDeclVisitor { |
| 1386 | + Context &context; |
| 1387 | + OpBuilder &builder; |
| 1388 | + ClassLowering &classLowering; |
| 1389 | + |
| 1390 | + ClassDeclVisitor(Context &ctx, ClassLowering &lowering) |
| 1391 | + : context(ctx), builder(ctx.builder), classLowering(lowering) {} |
| 1392 | + |
| 1393 | + LogicalResult run(const slang::ast::ClassType &classAST) { |
| 1394 | + OpBuilder::InsertionGuard ig(builder); |
| 1395 | + Block *body = classLowering.op.getBody().empty() |
| 1396 | + ? &classLowering.op.getBody().emplaceBlock() |
| 1397 | + : &classLowering.op.getBody().front(); |
| 1398 | + builder.setInsertionPointToEnd(body); |
| 1399 | + |
| 1400 | + for (const auto &mem : classAST.members()) |
| 1401 | + if (failed(mem.visit(*this))) |
| 1402 | + return failure(); |
| 1403 | + |
| 1404 | + return success(); |
| 1405 | + } |
| 1406 | + |
| 1407 | + // Properties: ClassPropertySymbol |
| 1408 | + LogicalResult visit(const slang::ast::ClassPropertySymbol &prop) { |
| 1409 | + auto loc = convertLocation(prop.location); |
| 1410 | + auto ty = context.convertType(prop.getType()); |
| 1411 | + if (!ty) |
| 1412 | + return failure(); |
| 1413 | + |
| 1414 | + moore::ClassPropertyDeclOp::create(builder, loc, prop.name, ty); |
| 1415 | + return success(); |
| 1416 | + } |
| 1417 | + |
| 1418 | + // Fully-fledged functions - SubroutineSymbol |
| 1419 | + LogicalResult visit(const slang::ast::SubroutineSymbol &fn) { |
| 1420 | + if (fn.flags & slang::ast::MethodFlags::BuiltIn) { |
| 1421 | + static bool remarkEmitted = false; |
| 1422 | + if (remarkEmitted) |
| 1423 | + return success(); |
| 1424 | + |
| 1425 | + mlir::emitRemark(classLowering.op.getLoc()) |
| 1426 | + << "Class builtin functions (needed for randomization, constraints, " |
| 1427 | + "and covergroups) are not yet supported and will be dropped " |
| 1428 | + "during lowering."; |
| 1429 | + remarkEmitted = true; |
| 1430 | + return success(); |
| 1431 | + } |
| 1432 | + return failure(); |
| 1433 | + } |
| 1434 | + |
| 1435 | + // Nested class definition, convert |
| 1436 | + LogicalResult visit(const slang::ast::ClassType &cls) { |
| 1437 | + return context.convertClassDeclaration(cls); |
| 1438 | + } |
| 1439 | + |
| 1440 | + // Transparent members: ignore (inherited names pulled in by slang) |
| 1441 | + LogicalResult visit(const slang::ast::TransparentMemberSymbol &) { |
| 1442 | + return success(); |
| 1443 | + } |
| 1444 | + |
| 1445 | + // Empty members: ignore |
| 1446 | + LogicalResult visit(const slang::ast::EmptyMemberSymbol &) { |
| 1447 | + return success(); |
| 1448 | + } |
| 1449 | + |
| 1450 | + // Emit an error for all other members. |
| 1451 | + template <typename T> |
| 1452 | + LogicalResult visit(T &&node) { |
| 1453 | + Location loc = UnknownLoc::get(context.getContext()); |
| 1454 | + if constexpr (requires { node.location; }) |
| 1455 | + loc = convertLocation(node.location); |
| 1456 | + mlir::emitError(loc) << "unsupported construct in ClassType members: " |
| 1457 | + << slang::ast::toString(node.kind); |
| 1458 | + return failure(); |
| 1459 | + } |
| 1460 | + |
| 1461 | +private: |
| 1462 | + Location convertLocation(const slang::SourceLocation &sloc) { |
| 1463 | + return context.convertLocation(sloc); |
| 1464 | + } |
| 1465 | +}; |
| 1466 | +} // namespace |
| 1467 | + |
| 1468 | +ClassLowering *Context::declareClass(const slang::ast::ClassType &cls) { |
| 1469 | + // Check if there already is a declaration for this class. |
| 1470 | + auto &lowering = classes[&cls]; |
| 1471 | + if (lowering) { |
| 1472 | + if (!lowering->op) |
| 1473 | + return {}; |
| 1474 | + return lowering.get(); |
| 1475 | + } |
| 1476 | + |
| 1477 | + lowering = std::make_unique<ClassLowering>(); |
| 1478 | + auto loc = convertLocation(cls.location); |
| 1479 | + |
| 1480 | + // Pick an insertion point for this function according to the source file |
| 1481 | + // location. |
| 1482 | + OpBuilder::InsertionGuard g(builder); |
| 1483 | + auto it = orderedRootOps.upper_bound(cls.location); |
| 1484 | + if (it == orderedRootOps.end()) |
| 1485 | + builder.setInsertionPointToEnd(intoModuleOp.getBody()); |
| 1486 | + else |
| 1487 | + builder.setInsertionPoint(it->second); |
| 1488 | + |
| 1489 | + auto symName = fullyQualifiedClassName(*this, cls); |
| 1490 | + auto [base, impls] = buildBaseAndImplementsAttrs(*this, cls); |
| 1491 | + |
| 1492 | + auto classDeclOp = |
| 1493 | + moore::ClassDeclOp::create(builder, loc, symName, base, impls); |
| 1494 | + |
| 1495 | + SymbolTable::setSymbolVisibility(classDeclOp, |
| 1496 | + SymbolTable::Visibility::Public); |
| 1497 | + orderedRootOps.insert(it, {cls.location, classDeclOp}); |
| 1498 | + lowering->op = classDeclOp; |
| 1499 | + |
| 1500 | + symbolTable.insert(classDeclOp); |
| 1501 | + return lowering.get(); |
| 1502 | +} |
| 1503 | + |
| 1504 | +LogicalResult |
| 1505 | +Context::convertClassDeclaration(const slang::ast::ClassType &classdecl) { |
| 1506 | + |
| 1507 | + // Keep track of local time scale. |
| 1508 | + auto prevTimeScale = timeScale; |
| 1509 | + timeScale = classdecl.getTimeScale().value_or(slang::TimeScale()); |
| 1510 | + auto timeScaleGuard = |
| 1511 | + llvm::make_scope_exit([&] { timeScale = prevTimeScale; }); |
| 1512 | + |
| 1513 | + auto *lowering = declareClass(classdecl); |
| 1514 | + if (failed(ClassDeclVisitor(*this, *lowering).run(classdecl))) |
| 1515 | + return failure(); |
| 1516 | + |
| 1517 | + return success(); |
| 1518 | +} |
0 commit comments