This is an archive of the discontinued LLVM Phabricator instance.

[clang][Parse] properly parse asm-qualifiers, asm inline
ClosedPublic

Authored by nickdesaulniers on Mar 3 2020, 1:28 PM.

Details

Summary

The parsing of GNU C extended asm statements was a little brittle and
had a few issues:

  • It was using Parse::ParseTypeQualifierListOpt to parse the volatile qualifier. That parser is really meant for TypeQualifiers; an asm statement doesn't really have a type qualifier. This is still maybe nice to have, but not necessary. We now can check for the volatile token by properly expanding the grammer, rather than abusing Parse::ParseTypeQualifierListOpt.
  • The parsing of goto was position dependent, so asm goto volatile wouldn't parse. The qualifiers should be position independent to one another. Now they are.
  • We would warn on duplicate volatile, but the parse error for duplicate goto was a generic parse error and wasn't clear.
  • We need to add support for the recent GNU C extension asm inline. Adding support to the parser with the above issues highlighted the need for this refactoring.

Link: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

Diff Detail

Event Timeline

nickdesaulniers created this revision.Mar 3 2020, 1:28 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 3 2020, 1:28 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

I still plan to add CodeGen support for asm inline before the next release, which should be as simple as emitting an additional inlinehint, but I'd like to save that for a follow up patch on top of this.

  • remove impossible case in ParseGNUAsmQualifierListOpt
  • simplify SetGNUAsmQual
  • fix typo in comment
nickdesaulniers updated this revision to Diff 248020.EditedMar 3 2020, 1:49 PM
  • reupload patch
  • make sure to initialize GNUAsmQualifiers
nathanchance added inline comments.
clang/test/Parser/asm-qualifiers.c
21

I'm probably being dense but what is intended to be tested differently between combinations and permutations? I assume the order of the qualifiers? Wouldn't it just be better to merge combinations into permutations or was there some deeper reasoning for the compartmentalization?

nickdesaulniers added inline comments.Mar 3 2020, 2:38 PM
clang/test/Parser/asm-qualifiers.c
21

combinations tests a combination of different asm-qualifiers together. permutations are just permutations of the combinations that have not been tested above. I may not even have my nomenclature correct. Shall I combine them?

nathanchance added inline comments.Mar 3 2020, 4:09 PM
clang/test/Parser/asm-qualifiers.c
21

I assume that you want permutations since you want to make sure that the ordering does not matter, right? If you just care about combinations then

asm inline goto volatile("" ::::foo);
asm inline volatile goto("" ::::foo);

asm goto inline volatile("" ::::foo);
asm goto volatile inline("" ::::foo);

asm volatile goto inline("" ::::foo); // note, this one should probably be added in permutations
asm volatile inline goto("" ::::foo);

could just be distilled down to one of those since they are the same combination of qualifiers (combinations do not care about order). I would say that moving combinations into permutations would be wise since permutations tests the same thing that combinations does and more.

  • combine combinations into permutations test
nickdesaulniers marked 4 inline comments as done.Mar 4 2020, 9:48 AM
nickdesaulniers added inline comments.Mar 4 2020, 9:48 AM
clang/test/Parser/asm-qualifiers.c
21

Great suggestion, done. Thanks!

nickdesaulniers marked an inline comment as done.
  • git-clang-format HEAD~
nickdesaulniers marked an inline comment as done.Mar 4 2020, 9:55 AM
nickdesaulniers added inline comments.
clang/lib/Parse/ParseStmtAsm.cpp
725–726

I think in a follow up commit, I'll just delete this (the call to ParseTypeQualifierListOpt and subsequent warn_asm_qualifier_ignored emission); making it a parse error to put type qualifiers after the keyword asm, which would match the behavior of GCC, and not waste time looking for and parsing type qualifiers. It maybe made sense when ParseTypeQualifierListOpt was being abused to parse volatile, but this patch fixes that.

aaron.ballman added inline comments.Mar 5 2020, 10:20 AM
clang/include/clang/Basic/DiagnosticParseKinds.td
31

duplicate asm qualifier '%0' (with the quotes around the %0).

clang/include/clang/Sema/DeclSpec.h
324 ↗(On Diff #248226)

Asm Qualifiers -> asm qualifiers

378 ↗(On Diff #248226)

I think we only need three bits to store this. Also, the comment should be clear that this is a bitwise OR of AQ, similar to the comment for TypeQualifiers.

582 ↗(On Diff #248226)

The comment and the code don't match. I prefer the spelling in the comment.

736 ↗(On Diff #248226)

Given that we're already inconsistent in this area regarding naming, I'd prefer to follow the LLVM naming convention and use setGNUAsmQualifier()

clang/lib/Parse/ParseStmtAsm.cpp
670

The comment doesn't match the code -- I think the comment should be:

/// asm-qualifier:
///   volatile
///   inline
///   goto
///
/// asm-qualifier-list:
///   asm-qualifier
///   asm-qualifier-list asm-qualifier
686

I would expect a diagnostic if the unknown token is anything other than a left paren.

clang/test/Parser/asm-qualifiers.c
4

There should also be tests where the qualifiers are incorrect. e.g.,

asm noodle("");
asm goto noodle("");
asm volatile noodle inline("");
nickdesaulniers marked 9 inline comments as done.
  • address review comments, add tests for global asm statements.
clang/lib/Parse/ParseStmtAsm.cpp
686

That currently is is handled by the caller, see if (Tok.isNot(tok::l_paren)) { in Parser::ParseAsmStatement (line 762 of clang/lib/Parse/ParseStmtAsm.cpp).

nickdesaulniers marked 2 inline comments as done and an inline comment as not done.Mar 5 2020, 2:16 PM
nickdesaulniers added inline comments.
clang/lib/Parse/ParseStmtAsm.cpp
703

I guess this should be asm-qualifier-list[opt]?

clang/test/Parser/asm-qualifiers.c
55

I'll probably also drop parsing for this here, too, in a follow up; GCC treats this case as a parse error.

aaron.ballman added inline comments.Mar 6 2020, 5:42 AM
clang/lib/Parse/ParseStmtAsm.cpp
686

My suggestion is to move it out of the caller and into the helper. This function is expected to parse the asm qualifiers, so it stands to reason if it gets something other than an asm qualifier, it should diagnose. (We probably won't need to re-use this parsing logic in other contexts, but that's why it's better to have the diagnostic here rather than assume the caller will do it.)

703

Yup, good catch!

clang/test/Parser/asm-qualifiers.c
13–15

I think these diagnostics should be improved as none of them suggest the underlying problem. As they read now, it sounds like you can write asm(volatile noodle inline("")) to appease the diagnostic.

nickdesaulniers marked an inline comment as done.
  • completely drop use of Parse::ParseTypeQualifierListOpt
  • move paren parsing into helper
  • fix up test cases for global asm statements
  • delete duplicate test cases covered by the added test file
nickdesaulniers marked 4 inline comments as done.Mar 6 2020, 1:44 PM
nickdesaulniers added inline comments.
clang/lib/Parse/ParseStmtAsm.cpp
725–726

Chatting with @aaron.ballman , I think it makes sense to tackle this in this CL, which will simplify the requested changes to parsing (above).

nickdesaulniers marked an inline comment as done.Mar 6 2020, 1:46 PM
nickdesaulniers added inline comments.
clang/include/clang/Basic/DiagnosticParseKinds.td
15–18

oh, I meant to rename this; and I think flipping the order of this and following def is a little clearer for code review.

  • use better error name
  • reorder new errors
  • git-clang-format HEAD~
nickdesaulniers marked an inline comment as done.Mar 6 2020, 1:51 PM
  • add release note about change in behavior of -Wduplicate-decl-specifier
aaron.ballman accepted this revision.Mar 7 2020, 12:28 PM

Thank you for working on this, this LGTM! If you wanted a follow-up patch beyond adding semantic support for the inline keyword, I think it might make sense to investigate divorcing the qualifier parsing from the DeclSpec interface. These are ASM statements, not declarations, so the fact that we're using a DeclSpec to smuggle the qualifiers around is a bit unexpected. However, I don't think that work needs to hold up this patch.

This revision is now accepted and ready to land.Mar 7 2020, 12:28 PM
nickdesaulniers planned changes to this revision.Mar 9 2020, 4:40 PM

Thank you for working on this, this LGTM! If you wanted a follow-up patch beyond adding semantic support for the inline keyword, I think it might make sense to investigate divorcing the qualifier parsing from the DeclSpec interface. These are ASM statements, not declarations, so the fact that we're using a DeclSpec to smuggle the qualifiers around is a bit unexpected. However, I don't think that work needs to hold up this patch.

I agree and I think that recommendation is in good taste. I'd rather do things the right way now and hopefully never need to revisit. Also, there's no rush on this. I'll declare a new class for the qualifiers, since we just need to know which qualifiers were used (what is essentially 3 booleans) and strings for each.

  • rebase, divorce from DeclSpec
This revision is now accepted and ready to land.Mar 9 2020, 4:40 PM
nickdesaulniers planned changes to this revision.Mar 9 2020, 4:44 PM
nickdesaulniers marked an inline comment as done.
nickdesaulniers added inline comments.
clang/lib/Parse/Parser.cpp
1544

This pattern is repeated twice, let me see if I can DRY that up.

  • DRY up getting the Qualifier, and checking Tok.is
This revision is now accepted and ready to land.Mar 9 2020, 5:25 PM
nickdesaulniers planned changes to this revision.Mar 9 2020, 6:34 PM
nickdesaulniers marked 3 inline comments as done.

Sorry, got a little trigger happy with the refactoring. Having a weekend to think about this more definitely helps. It's nice to avoid instantiating a whole instance of SemaDecl and instead use a single unsigned bitfield! I have a few more refactorings I'd like to do, but I'm working remote and my workstation is suddenly unavailable over SSH. Hopefully I can reboot it tomorrow.

clang/include/clang/Parse/Parser.h
3214

Might be nice to have an implicit constructor that takes a GNUAsmQualifier operand that defaults to AQ_unspecified.

3215

Now that we're not using DeclSpec, maybe we could drop this in preference of operator<<.

clang/lib/Parse/ParseStmtAsm.cpp
358–359

Can be implemented in terms of Parser::GNUAsmQualifiers::getQualifierForTokenKind.

  • small refactorings
This revision is now accepted and ready to land.Mar 10 2020, 9:18 AM
aaron.ballman added inline comments.Mar 10 2020, 1:47 PM
clang/include/clang/Basic/DiagnosticParseKinds.td
17

If we know of any compilers that support asm statements with qualifiers other than what Clang supports, it may make sense for these diagnostics to be warnings that are treated as an error by default so that users can disable the warning when compiling with Clang. I don't know of any such compilers off the top of my head, but perhaps you know of some.

clang/include/clang/Parse/Parser.h
3206

There's really no benefit to using a bit-field here, so I'd just store an unsigned.

3214

I think you should use an in-class initializer for Qualifiers and then can drop the constructor entirely.

3215

I prefer the named function to a streaming operator given how little this will be used -- it's easier to call the named function from within a debugger than to use a streaming operator.

Please don't use a type name as a parameter identifier. Also, drop the top-level const.

3216

Please don't use a type name as a parameter identifier. Also, drop the top-level const.

3224

This should be taking a reference rather than a pointer. It should also be named parseGNUAsmQualifierListOpt() with a lowercase p.

clang/lib/Parse/ParseStmtAsm.cpp
358

This is too clever for my tastes -- I'd prefer an explicit test against AQ_unspecified.

932

type vs param name and top-level const.

937

This looks wrong to me -- it flows through to an unreachable despite being reachable. I think this should return "unspecified";.

951

type vs param name and top-level const.

nickdesaulniers marked 8 inline comments as done.
  • address latest review comments, more refactoring
clang/lib/Parse/ParseStmtAsm.cpp
937

This method is only called for printing; it seems weird to return "unspecified" when it's kind of an invariant that that should never happen. Maybe an assert here would be better?

I've updated the implementation slightly, but leaving this comment thread open for more feedback.

  • remove isUnspecified(), was partially committed from refactoring I changed my mind on
  • drop auto as type is no longer implicit
Harbormaster failed remote builds in B48881: Diff 249754!
Harbormaster failed remote builds in B48883: Diff 249757!
aaron.ballman added inline comments.Mar 12 2020, 6:17 AM
clang/lib/Parse/ParseStmtAsm.cpp
684

I think this will point to the right paren because of the preceding SkipUntil when it should point at the original token location.

937

An assert seems wrong to me -- the enumeration value is valid and the user has asked to print it. There's not much to assert on there. It's the caller of the method that should know whether it's violating an invariant to have that value in the first place. Also, people print from debuggers with relative frequency, so I'd personally avoid an assert in this case.

nickdesaulniers marked 4 inline comments as done.
  • restore order between Diag and SkipUntil
This revision was automatically updated to reflect the committed changes.