Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HW] Disallow duplicate field names in HW aggregate types #6225

Merged
merged 4 commits into from
Sep 29, 2023

Conversation

fzi-hielscher
Copy link
Contributor

hw.struct_extract, hw.struct_inject and hw.union_extract refer to their respective field by name. However, field names of structs and union types are currently not enforced to be unique. This can cause ambiguities, e.g.:

hw.module @extract(in %a: i8, in %b: i8, out o: i8) {
    %0 = hw.struct_create (%a, %b) : !hw.struct<foo: i8, foo: i8>
    %1 = hw.struct_extract %0["foo"] :  !hw.struct<foo: i8, foo: i8>
    hw.output %1 : i8
}

> circt-opt ... --canonicalize

hw.module @extract(in %a : i8, in %b : i8, out o : i8) {
    hw.output %a : i8
}

This PR prevents the creation of HW aggregate types with duplicate field names.

As @seldridge pointed out to me, the FIRRTL dialect refers to subfields by index rather than by name. I've prepared a follow-up commit adopting this for StructExtract and StructInject ops. Allowing ambiguous names in structs is probably not a good idea either way. But it should be marginally more efficient, as it avoids iterating over the field names to find the matching one.

Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach looks good! Some thoughts in the review.

lib/Dialect/HW/HWTypes.cpp Outdated Show resolved Hide resolved
lib/Dialect/HW/HWTypes.cpp Outdated Show resolved Hide resolved
Comment on lines 245 to 255
llvm::StringSet<> nameSet;
return p.parseCommaSeparatedList(
mlir::AsmParser::Delimiter::LessGreater, [&]() -> ParseResult {
std::string name;
Type type;
if (p.parseKeywordOrString(&name) || p.parseColon() ||
p.parseType(type))
return failure();
if (!nameSet.insert(name).second)
return p.emitError(p.getCurrentLocation(),
"duplicate field name \'" + name + "\'");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking this during parsing and verification results means the check has to be duplicated. Is the error just as good if you ignore the parsing check and wait for the verifier to blow up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally just did the verify method. It does the trick, but a failure there will dump a full stack trace, which I found too verbose and somewhat intimidating. 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. One thing you can do to further improve this is to use a better location for the error. Currently the error looks like:

# circt-opt Foo.mlir 
Foo.mlir:2:65: error: duplicate field name 'foo'
    %0 = hw.struct_create (%a, %b) : !hw.struct<foo: i8, foo: i8, foo: i8>
                                                                ^
Foo.mlir:2:74: error: duplicate field name 'foo'
    %0 = hw.struct_create (%a, %b) : !hw.struct<foo: i8, foo: i8, foo: i8>
                                                                         ^

If you use:

diff --git a/lib/Dialect/HW/HWTypes.cpp b/lib/Dialect/HW/HWTypes.cpp
index e89d9b3ab..0c22baef2 100644
--- a/lib/Dialect/HW/HWTypes.cpp
+++ b/lib/Dialect/HW/HWTypes.cpp
@@ -248,13 +248,14 @@ static ParseResult parseFields(AsmParser &p,
       mlir::AsmParser::Delimiter::LessGreater, [&]() -> ParseResult {
         std::string name;
         Type type;
+
+        auto fieldLoc = p.getCurrentLocation();
         if (p.parseKeywordOrString(&name) || p.parseColon() ||
             p.parseType(type))
           return failure();
 
         if (!nameSet.insert(name).second) {
-          p.emitError(p.getCurrentLocation(),
-                      "duplicate field name \'" + name + "\'");
+          p.emitError(fieldLoc, "duplicate field name \'" + name + "\'");
           // Continue parsing to print all duplicates, but make sure to error
           // eventually
           hasDuplicateName = true;

You get:

# circt-opt Foo.mlir 
Foo.mlir:2:58: error: duplicate field name 'foo'
    %0 = hw.struct_create (%a, %b) : !hw.struct<foo: i8, foo: i8, foo: i8>
                                                         ^
Foo.mlir:2:67: error: duplicate field name 'foo'
    %0 = hw.struct_create (%a, %b) : !hw.struct<foo: i8, foo: i8, foo: i8>
                                                                  ^

test/Dialect/HW/errors.mlir Outdated Show resolved Hide resolved
Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Nice improvement, thanks!

@fzi-hielscher
Copy link
Contributor Author

Thanks a lot for the review. I'll get the commit changing the StructExtract/Inject operations PR ready next week, assuming there is no specific reason to keep the reference by-name.

@fzi-hielscher fzi-hielscher merged commit fac2105 into llvm:main Sep 29, 2023
5 checks passed
mikeurbach pushed a commit that referenced this pull request Oct 2, 2023
Prevent the creation of HW struct/union types containing multiple fields of the same name to avoid ambiguities.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants