Skip to content

Conversation

@Mr-Anyone
Copy link
Contributor

Fixed TableGen duplicate issues that causes the wrong interrupt attribute from being selected.

resolves #140701

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:RISC-V labels May 21, 2025
@llvmbot
Copy link
Member

llvmbot commented May 21, 2025

@llvm/pr-subscribers-backend-risc-v

Author: Vincent (Mr-Anyone)

Changes

Fixed TableGen duplicate issues that causes the wrong interrupt attribute from being selected.

resolves #140701


Full diff: https://github.com/llvm/llvm-project/pull/140828.diff

2 Files Affected:

  • (added) clang/test/AST/ast-dump-riscv-attributes.cpp (+12)
  • (modified) clang/utils/TableGen/ClangAttrEmitter.cpp (+21-5)
diff --git a/clang/test/AST/ast-dump-riscv-attributes.cpp b/clang/test/AST/ast-dump-riscv-attributes.cpp
new file mode 100644
index 0000000000000..7efe626072311
--- /dev/null
+++ b/clang/test/AST/ast-dump-riscv-attributes.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple riscv64 -ast-dump -ast-dump-filter c23 -std=c23 -x c %s | FileCheck --strict-whitespace %s
+
+// CHECK:       FunctionDecl{{.*}}pre_c23
+// CHECK-NEXT:    |-CompoundStmt
+// CHECK-NEXT:    `-RISCVInterruptAttr{{.*}}supervisor
+__attribute__((interrupt("supervisor"))) void pre_c23(){}
+
+// CHECK:       FunctionDecl{{.*}}in_c23
+// CHECK-NEXT:    |-CompoundStmt
+// CHECK-NEXT:    `-RISCVInterruptAttr{{.*}}supervisor
+// CHECK-NOT:     `-RISCVInterruptAttr{{.*}}machine
+[[gnu::interrupt("supervisor")]] void in_c23(){}
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 9684ec9520e5a..effef13c8d276 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -20,6 +20,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -3667,6 +3668,9 @@ static bool GenerateTargetSpecificAttrChecks(const Record *R,
 static void GenerateHasAttrSpellingStringSwitch(
     ArrayRef<std::pair<const Record *, FlattenedSpelling>> Attrs,
     raw_ostream &OS, StringRef Variety, StringRef Scope = "") {
+
+  llvm::StringMap<std::string> TargetSpecificMap;
+
   for (const auto &[Attr, Spelling] : Attrs) {
     // C++11-style attributes have specific version information associated with
     // them. If the attribute has no scope, the version information must not
@@ -3727,12 +3731,24 @@ static void GenerateHasAttrSpellingStringSwitch(
       }
     }
 
-    std::string TestStr = !Test.empty()
-                              ? Test + " ? " + itostr(Version) + " : 0"
-                              : itostr(Version);
-    if (Scope.empty() || Scope == Spelling.nameSpace())
-      OS << "    .Case(\"" << Spelling.name() << "\", " << TestStr << ")\n";
+    std::string TestStr =
+        !Test.empty() ? '(' + Test + " ? " + itostr(Version) + " : 0" + ')'
+                      : '(' + itostr(Version) + ')';
+
+    if (Scope.empty() || Scope == Spelling.nameSpace()) {
+      if (TargetSpecificMap.contains(Spelling.name())) {
+        TargetSpecificMap[Spelling.name()] += " || " + TestStr;
+      } else {
+        TargetSpecificMap[Spelling.name()] = TestStr;
+      }
+    }
+  }
+
+  for (auto &entry : TargetSpecificMap) {
+    OS << "    .Case(\"" << entry.getKey() << "\", " << entry.getValue()
+       << ")\n";
   }
+
   OS << "    .Default(0);\n";
 }
 

@llvmbot
Copy link
Member

llvmbot commented May 21, 2025

@llvm/pr-subscribers-clang

Author: Vincent (Mr-Anyone)

Changes

Fixed TableGen duplicate issues that causes the wrong interrupt attribute from being selected.

resolves #140701


Full diff: https://github.com/llvm/llvm-project/pull/140828.diff

2 Files Affected:

  • (added) clang/test/AST/ast-dump-riscv-attributes.cpp (+12)
  • (modified) clang/utils/TableGen/ClangAttrEmitter.cpp (+21-5)
diff --git a/clang/test/AST/ast-dump-riscv-attributes.cpp b/clang/test/AST/ast-dump-riscv-attributes.cpp
new file mode 100644
index 0000000000000..7efe626072311
--- /dev/null
+++ b/clang/test/AST/ast-dump-riscv-attributes.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple riscv64 -ast-dump -ast-dump-filter c23 -std=c23 -x c %s | FileCheck --strict-whitespace %s
+
+// CHECK:       FunctionDecl{{.*}}pre_c23
+// CHECK-NEXT:    |-CompoundStmt
+// CHECK-NEXT:    `-RISCVInterruptAttr{{.*}}supervisor
+__attribute__((interrupt("supervisor"))) void pre_c23(){}
+
+// CHECK:       FunctionDecl{{.*}}in_c23
+// CHECK-NEXT:    |-CompoundStmt
+// CHECK-NEXT:    `-RISCVInterruptAttr{{.*}}supervisor
+// CHECK-NOT:     `-RISCVInterruptAttr{{.*}}machine
+[[gnu::interrupt("supervisor")]] void in_c23(){}
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 9684ec9520e5a..effef13c8d276 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -20,6 +20,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -3667,6 +3668,9 @@ static bool GenerateTargetSpecificAttrChecks(const Record *R,
 static void GenerateHasAttrSpellingStringSwitch(
     ArrayRef<std::pair<const Record *, FlattenedSpelling>> Attrs,
     raw_ostream &OS, StringRef Variety, StringRef Scope = "") {
+
+  llvm::StringMap<std::string> TargetSpecificMap;
+
   for (const auto &[Attr, Spelling] : Attrs) {
     // C++11-style attributes have specific version information associated with
     // them. If the attribute has no scope, the version information must not
@@ -3727,12 +3731,24 @@ static void GenerateHasAttrSpellingStringSwitch(
       }
     }
 
-    std::string TestStr = !Test.empty()
-                              ? Test + " ? " + itostr(Version) + " : 0"
-                              : itostr(Version);
-    if (Scope.empty() || Scope == Spelling.nameSpace())
-      OS << "    .Case(\"" << Spelling.name() << "\", " << TestStr << ")\n";
+    std::string TestStr =
+        !Test.empty() ? '(' + Test + " ? " + itostr(Version) + " : 0" + ')'
+                      : '(' + itostr(Version) + ')';
+
+    if (Scope.empty() || Scope == Spelling.nameSpace()) {
+      if (TargetSpecificMap.contains(Spelling.name())) {
+        TargetSpecificMap[Spelling.name()] += " || " + TestStr;
+      } else {
+        TargetSpecificMap[Spelling.name()] = TestStr;
+      }
+    }
+  }
+
+  for (auto &entry : TargetSpecificMap) {
+    OS << "    .Case(\"" << entry.getKey() << "\", " << entry.getValue()
+       << ")\n";
   }
+
   OS << "    .Default(0);\n";
 }
 

Fixed TableGen duplicate issues that causes the wrong interrupt
attribute from being selected.

resolves llvm#140701
@github-actions
Copy link

github-actions bot commented May 21, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@topperc topperc requested a review from AaronBallman May 21, 2025 03:38
@AaronBallman AaronBallman requested a review from erichkeane May 21, 2025 11:56
@AaronBallman AaronBallman added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label May 21, 2025
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

Thank you for working on this! In general, this looks good to me, just nits about our coding style.

}

// Create the actual string switch statement after all the attributes have
// been parsed
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// been parsed
// been parsed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Comment on lines 3752 to 3754
for (auto &entry : TestStringMap) {
OS << " .Case(\"" << entry.getKey() << "\", " << entry.getValue()
<< ")\n";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
for (auto &entry : TestStringMap) {
OS << " .Case(\"" << entry.getKey() << "\", " << entry.getValue()
<< ")\n";
for (auto &Entry : TestStringMap) {
OS << " .Case(\"" << Entry.getKey() << "\", " << Entry.getValue()
<< ")\n";

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

Comment on lines 3742 to 3746
if (TestStringMap.contains(Spelling.name())) {
TestStringMap[Spelling.name()] += " || " + TestStr;
} else {
TestStringMap[Spelling.name()] = TestStr;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (TestStringMap.contains(Spelling.name())) {
TestStringMap[Spelling.name()] += " || " + TestStr;
} else {
TestStringMap[Spelling.name()] = TestStr;
}
if (TestStringMap.contains(Spelling.name()))
TestStringMap[Spelling.name()] += " || " + TestStr;
else
TestStringMap[Spelling.name()] = TestStr;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, thanks.

// CHECK: FunctionDecl{{.*}}in_c23
// CHECK-NEXT: |-CompoundStmt
// CHECK-NEXT: `-RISCVInterruptAttr{{.*}}supervisor
// CHECK-NOT: `-RISCVInterruptAttr{{.*}}machine
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't get this test. Why does this choose the RISCV interrupt instead of the GNU one? And where would 'machine' come from?

Copy link
Collaborator

Choose a reason for hiding this comment

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

"-triple riscv64" is why you get the RISCVInterruptAttr, which is expected.

"machine" is on a CHECK-NOT line, which is what the bug was about.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess the question is 'how'? Is the "supervisor" not coming from the string?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Before this patch, our "has attribute" check was looking through a StringSwitch that had multiple cases with the same string argument but different resulting values (that would check for target triples and arches). So we'd hit the first case, say "this matches", and the test for the attribute would only pass if the target triple matched. Because the first triple was never the RISCV attribute, we'd always say "we don't have this attribute". In turn, this would mean parsing would say "well, we don't know about this attribute, so eat all the arguments". But because Sema could figure out which handleInterruptAttr function to call, we'd actually go ahead and make the correct attribute, just with the incorrect argument. The default if no argument is specified for RISCV is machine. So we'd ignore supervisor and just substitute in machine quietly for the user.

: itostr(Version);
if (Scope.empty() || Scope == Spelling.nameSpace())
OS << " .Case(\"" << Spelling.name() << "\", " << TestStr << ")\n";
std::string TestStr =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you share a before/after generated file so that I can take a look? I'm having a hard time visualizing what this does.

Copy link
Contributor Author

@Mr-Anyone Mr-Anyone May 21, 2025

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Take the interrupt attribute as an example:

This is the original string: true && (T.getArch() == llvm::Triple::riscv32 || T.getArch() == llvm::Triple::riscv64) ? 1 : 0.

The problem is that there are multiple of them for a single attribute, such as true && (T.getArch() == llvm::Triple::arm || T.getArch() == llvm::Triple::thumb || T.getArch() == llvm::Triple::armeb || T.getArch() == llvm::Triple::thumbeb) ? 1 : 0 for arm.

Meaning that, we used to get:

.Case("interrupt",true && (T.getArch() == llvm::Triple::riscv32 || T.getArch() == llvm::Triple::riscv64) ? 1 : 0 )
.Case("interrupt",true && (T.getArch() == llvm::Triple::arm || T.getArch() == llvm::Triple::thumb || T.getArch() == llvm::Triple::armeb || T.getArch() == llvm::Triple::thumbeb) ? 1 : 0
...

This is the new one TestStr: (true && (T.getArch() == llvm::Triple::avr) ? 1 : 0), with the only difference being the added parentheses. We can join test strings together with ||, meaning we generate something like this:

(true && (T.getArch() == llvm::Triple::riscv32 || T.getArch() == llvm::Triple::riscv64) ? 1 : 0) ||
                                (true && (T.getArch() == llvm::Triple::arm || T.getArch() == llvm::Triple::thumb || T.getArch() == llvm::Triple::armeb || T.getArch() == llvm::Triple::thumbeb) ? 1 : 0) ... 

So now we have a single case statement instead,

.Case("interrupt",(true && (T.getArch() == llvm::Triple::riscv32 || T.getArch() == llvm::Triple::riscv64) ? 1 : 0) ||
                                (true && (T.getArch() == llvm::Triple::arm || T.getArch() == llvm::Triple::thumb || T.getArch() == llvm::Triple::armeb || T.getArch() == llvm::Triple::thumbeb) ? 1 : 0) ... )
...

Copy link
Collaborator

Choose a reason for hiding this comment

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

Got it, thank you for that!

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

LGTM!

@AaronBallman AaronBallman merged commit 385752c into llvm:main May 21, 2025
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:RISC-V clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[clang] Clang ignores parameter for C23 gnu::interrupt attribute on RISC-V64

4 participants