[ImportVerilog] Allow enum variants in expressions (#7339)

Add support for enum variants in expressions. Until now, ImportVerilog
would emit a "unknown name" diagnostic when an enum variant is used in
an expression. This is due to the lowering of named value expressions
simply checking whether an MLIR value is present in the `valueSymbols`
table for the referenced symbol name. This works for variables which
create dedicated declarations, but not for enum variants which
potentially live outside the current module's scope.

Instead, ImportVerilog will now check if the named value is a constant,
for example a parameter or an enum variant, and materialize that
constant directly as a `moore.constant` op. This will also be able to
deal with parameters declared inside of packages, for which the table of
value symbols of the current module does not contain any value. (Local
parameters inside the module generate a `moore.named_constant` op that
is present in the table.)
This commit is contained in:
Fabian Schuiki 2024-07-18 10:56:57 -07:00 committed by GitHub
parent f15c4a7a4b
commit 846139b8ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 35 additions and 15 deletions

View File

@ -61,13 +61,26 @@ struct RvalueExprVisitor {
// Handle named values, such as references to declared variables.
Value visit(const slang::ast::NamedValueExpression &expr) {
if (auto value = context.valueSymbols.lookup(&expr.symbol))
return isa<moore::NamedConstantOp>(value.getDefiningOp())
? value
: builder.create<moore::ReadOp>(
loc,
cast<moore::RefType>(value.getType()).getNestedType(),
value);
if (auto value = context.valueSymbols.lookup(&expr.symbol)) {
if (auto refType = dyn_cast<moore::RefType>(value.getType()))
value =
builder.create<moore::ReadOp>(loc, refType.getNestedType(), value);
return value;
}
// Try to materialize constant values directly.
slang::ast::EvalContext evalContext(context.compilation,
slang::ast::EvalFlags::CacheResults);
auto constant = expr.eval(evalContext);
if (constant.isInteger()) {
auto type = context.convertType(*expr.type);
if (!type)
return {};
return convertSVInt(constant.integer(), type);
}
// Otherwise some other part of ImportVerilog should have added an MLIR
// value for this expression's symbol to the `context.valueSymbols` table.
auto d = mlir::emitError(loc, "unknown name `") << expr.symbol.name << "`";
d.attachNote(context.convertLocation(expr.symbol.location))
<< "no value generated for " << slang::ast::toString(expr.symbol.kind);

View File

@ -262,8 +262,8 @@ LogicalResult ImportDriver::importVerilog(ModuleOp module) {
mlirContext
->loadDialect<moore::MooreDialect, hw::HWDialect, scf::SCFDialect>();
auto conversionTimer = ts.nest("Verilog to dialect mapping");
Context context(module, driver.sourceManager, bufferFilePaths);
if (failed(context.convertCompilation(*compilation)))
Context context(*compilation, module, driver.sourceManager, bufferFilePaths);
if (failed(context.convertCompilation()))
return failure();
conversionTimer.stop();

View File

@ -44,11 +44,11 @@ struct ModuleLowering {
/// operations. Keeps track of the destination MLIR module, builders, and
/// various worklists and utilities needed for conversion.
struct Context {
Context(mlir::ModuleOp intoModuleOp,
Context(slang::ast::Compilation &compilation, mlir::ModuleOp intoModuleOp,
const slang::SourceManager &sourceManager,
SmallDenseMap<slang::BufferID, StringRef> &bufferFilePaths)
: intoModuleOp(intoModuleOp), sourceManager(sourceManager),
bufferFilePaths(bufferFilePaths),
: compilation(compilation), intoModuleOp(intoModuleOp),
sourceManager(sourceManager), bufferFilePaths(bufferFilePaths),
builder(OpBuilder::atBlockEnd(intoModuleOp.getBody())),
symbolTable(intoModuleOp) {}
Context(const Context &) = delete;
@ -69,7 +69,7 @@ struct Context {
Type convertType(const slang::ast::DeclaredType &type);
/// Convert hierarchy and structure AST nodes to MLIR ops.
LogicalResult convertCompilation(slang::ast::Compilation &compilation);
LogicalResult convertCompilation();
ModuleLowering *
convertModuleHeader(const slang::ast::InstanceBodySymbol *module);
LogicalResult convertModuleBody(const slang::ast::InstanceBodySymbol *module);
@ -86,6 +86,7 @@ struct Context {
LogicalResult
convertTimingControl(const slang::ast::TimingControl &timingControl);
slang::ast::Compilation &compilation;
mlir::ModuleOp intoModuleOp;
const slang::SourceManager &sourceManager;
SmallDenseMap<slang::BufferID, StringRef> &bufferFilePaths;

View File

@ -518,8 +518,7 @@ struct ModuleVisitor : public BaseVisitor {
/// Convert an entire Slang compilation to MLIR ops. This is the main entry
/// point for the conversion.
LogicalResult
Context::convertCompilation(slang::ast::Compilation &compilation) {
LogicalResult Context::convertCompilation() {
const auto &root = compilation.getRoot();
// Visit all top-level declarations in all compilation units. This does not

View File

@ -198,6 +198,13 @@ module Basic;
PackageType pkgType0;
// CHECK: %pkgType1 = moore.variable : <l42>
Package::PackageType pkgType1;
// CHECK: [[VARIANT_A:%.+]] = moore.constant 0 :
// CHECK: %ev1 = moore.variable [[VARIANT_A]]
// CHECK: [[VARIANT_B:%.+]] = moore.constant 1 :
// CHECK: %ev2 = moore.variable [[VARIANT_B]]
MyEnum ev1 = VariantA;
MyEnum ev2 = VariantB;
endmodule
// CHECK-LABEL: moore.module @Statements