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

[RISCV] Make RequiredExtensions for intrinsics scalable to more than 32 extensions. NFC #132895

Merged
merged 3 commits into from
Mar 26, 2025

Conversation

topperc
Copy link
Collaborator

@topperc topperc commented Mar 25, 2025

We have more than 32 extensions in our downstream and had to change this type from uint32_t to uint64_t.

To simplify our downstream and make the code more flexible, I propose to make it an array of uint32_t that we can size based on the number of extensions. I really wanted to use std::bitset, but we have to print the bits to a .inc file which can't easily be done with std::bitset.

…32 extensions. NFC

We have more than 32 extensions in our downstream and had to change
this type from uint32_t to uint64_t.

To simplify our downstream and make the code more flexible, I propose
to make it an array of uint32_t that we can size based on the number
of extensions. I really wanted to use std::bitset, but we have to print
the bits to a .inc file which can't easily be done with std::bitset.
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:RISC-V clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 25, 2025

@llvm/pr-subscribers-clang

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

Author: Craig Topper (topperc)

Changes

We have more than 32 extensions in our downstream and had to change this type from uint32_t to uint64_t.

To simplify our downstream and make the code more flexible, I propose to make it an array of uint32_t that we can size based on the number of extensions. I really wanted to use std::bitset, but we have to print the bits to a .inc file which can't easily be done with std::bitset.


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

4 Files Affected:

  • (modified) clang/include/clang/Support/RISCVVIntrinsicUtils.h (+24-26)
  • (modified) clang/lib/Sema/SemaRISCV.cpp (+3-2)
  • (modified) clang/lib/Support/RISCVVIntrinsicUtils.cpp (+4-1)
  • (modified) clang/utils/TableGen/RISCVVEmitter.cpp (+7-8)
diff --git a/clang/include/clang/Support/RISCVVIntrinsicUtils.h b/clang/include/clang/Support/RISCVVIntrinsicUtils.h
index 4819fc144f4dc..d8d7d61fdc59c 100644
--- a/clang/include/clang/Support/RISCVVIntrinsicUtils.h
+++ b/clang/include/clang/Support/RISCVVIntrinsicUtils.h
@@ -484,29 +484,27 @@ class RVVIntrinsic {
 // RVVRequire should be sync'ed with target features, but only
 // required features used in riscv_vector.td.
 enum RVVRequire : uint32_t {
-  RVV_REQ_None = 0,
-  RVV_REQ_RV64 = 1 << 0,
-  RVV_REQ_Zvfhmin = 1 << 1,
-  RVV_REQ_Xsfvcp = 1 << 2,
-  RVV_REQ_Xsfvfnrclipxfqf = 1 << 3,
-  RVV_REQ_Xsfvfwmaccqqq = 1 << 4,
-  RVV_REQ_Xsfvqmaccdod = 1 << 5,
-  RVV_REQ_Xsfvqmaccqoq = 1 << 6,
-  RVV_REQ_Zvbb = 1 << 7,
-  RVV_REQ_Zvbc = 1 << 8,
-  RVV_REQ_Zvkb = 1 << 9,
-  RVV_REQ_Zvkg = 1 << 10,
-  RVV_REQ_Zvkned = 1 << 11,
-  RVV_REQ_Zvknha = 1 << 12,
-  RVV_REQ_Zvknhb = 1 << 13,
-  RVV_REQ_Zvksed = 1 << 14,
-  RVV_REQ_Zvksh = 1 << 15,
-  RVV_REQ_Zvfbfwma = 1 << 16,
-  RVV_REQ_Zvfbfmin = 1 << 17,
-  RVV_REQ_Zvfh = 1 << 18,
-  RVV_REQ_Experimental = 1 << 19,
-
-  LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_Experimental)
+  RVV_REQ_RV64,
+  RVV_REQ_Zvfhmin,
+  RVV_REQ_Xsfvcp,
+  RVV_REQ_Xsfvfnrclipxfqf,
+  RVV_REQ_Xsfvfwmaccqqq,
+  RVV_REQ_Xsfvqmaccdod,
+  RVV_REQ_Xsfvqmaccqoq,
+  RVV_REQ_Zvbb,
+  RVV_REQ_Zvbc,
+  RVV_REQ_Zvkb,
+  RVV_REQ_Zvkg,
+  RVV_REQ_Zvkned,
+  RVV_REQ_Zvknha,
+  RVV_REQ_Zvknhb,
+  RVV_REQ_Zvksed,
+  RVV_REQ_Zvksh,
+  RVV_REQ_Zvfbfwma,
+  RVV_REQ_Zvfbfmin,
+  RVV_REQ_Zvfh,
+  RVV_REQ_Experimental,
+  RVV_REQ_NUM,
 };
 
 // Raw RVV intrinsic info, used to expand later.
@@ -519,6 +517,9 @@ struct RVVIntrinsicRecord {
   // e.g. vadd
   const char *OverloadedName;
 
+  // Required target features for this intrinsic.
+  uint32_t RequiredExtensions[(RVV_REQ_NUM + 31) / 32];
+
   // Prototype for this intrinsic, index of RVVSignatureTable.
   uint16_t PrototypeIndex;
 
@@ -537,9 +538,6 @@ struct RVVIntrinsicRecord {
   // Length of overloaded intrinsic suffix.
   uint8_t OverloadedSuffixSize;
 
-  // Required target features for this intrinsic.
-  uint32_t RequiredExtensions;
-
   // Supported type, mask of BasicType.
   uint8_t TypeRangeMask;
 
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index f23827d566610..746609604d1ba 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -206,7 +206,7 @@ class RISCVIntrinsicManagerImpl : public sema::RISCVIntrinsicManager {
 void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
     ArrayRef<RVVIntrinsicRecord> Recs, IntrinsicKind K) {
   const TargetInfo &TI = Context.getTargetInfo();
-  static const std::pair<const char *, RVVRequire> FeatureCheckList[] = {
+  static const std::pair<const char *, unsigned> FeatureCheckList[] = {
       {"64bit", RVV_REQ_RV64},
       {"xsfvcp", RVV_REQ_Xsfvcp},
       {"xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf},
@@ -232,7 +232,8 @@ void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
   for (auto &Record : Recs) {
     // Check requirements.
     if (llvm::any_of(FeatureCheckList, [&](const auto &Item) {
-          return (Record.RequiredExtensions & Item.second) == Item.second &&
+          return ((Record.RequiredExtensions[Item.second / 32] &
+                   (1U << (Item.second % 32))) != 0) &&
                  !TI.hasFeature(Item.first);
         }))
       continue;
diff --git a/clang/lib/Support/RISCVVIntrinsicUtils.cpp b/clang/lib/Support/RISCVVIntrinsicUtils.cpp
index 091e35f47fe8e..e44fbb0181830 100644
--- a/clang/lib/Support/RISCVVIntrinsicUtils.cpp
+++ b/clang/lib/Support/RISCVVIntrinsicUtils.cpp
@@ -1204,13 +1204,16 @@ raw_ostream &operator<<(raw_ostream &OS, const RVVIntrinsicRecord &Record) {
     OS << "nullptr,";
   else
     OS << "\"" << Record.OverloadedName << "\",";
+  OS << "{";
+  for (uint32_t Exts : Record.RequiredExtensions)
+    OS << Exts << ',';
+  OS << "},";
   OS << Record.PrototypeIndex << ",";
   OS << Record.SuffixIndex << ",";
   OS << Record.OverloadedSuffixIndex << ",";
   OS << (int)Record.PrototypeLength << ",";
   OS << (int)Record.SuffixLength << ",";
   OS << (int)Record.OverloadedSuffixSize << ",";
-  OS << Record.RequiredExtensions << ",";
   OS << (int)Record.TypeRangeMask << ",";
   OS << (int)Record.Log2LMULMask << ",";
   OS << (int)Record.NF << ",";
diff --git a/clang/utils/TableGen/RISCVVEmitter.cpp b/clang/utils/TableGen/RISCVVEmitter.cpp
index 0cdde20060b63..42cb0508ecb60 100644
--- a/clang/utils/TableGen/RISCVVEmitter.cpp
+++ b/clang/utils/TableGen/RISCVVEmitter.cpp
@@ -45,7 +45,7 @@ struct SemaRecord {
   unsigned Log2LMULMask;
 
   // Required extensions for this intrinsic.
-  uint32_t RequiredExtensions;
+  uint32_t RequiredExtensions[(RVV_REQ_Experimental + 31) / 32];
 
   // Prototype for this intrinsic.
   SmallVector<PrototypeDescriptor> Prototype;
@@ -769,9 +769,9 @@ void RVVEmitter::createRVVIntrinsics(
 
     SR.Log2LMULMask = Log2LMULMask;
 
-    SR.RequiredExtensions = 0;
+    memset(SR.RequiredExtensions, 0, sizeof(SR.RequiredExtensions));
     for (auto RequiredFeature : RequiredFeatures) {
-      RVVRequire RequireExt =
+      unsigned RequireExt =
           StringSwitch<RVVRequire>(RequiredFeature)
               .Case("RV64", RVV_REQ_RV64)
               .Case("Zvfhmin", RVV_REQ_Zvfhmin)
@@ -792,10 +792,8 @@ void RVVEmitter::createRVVIntrinsics(
               .Case("Zvfbfwma", RVV_REQ_Zvfbfwma)
               .Case("Zvfbfmin", RVV_REQ_Zvfbfmin)
               .Case("Zvfh", RVV_REQ_Zvfh)
-              .Case("Experimental", RVV_REQ_Experimental)
-              .Default(RVV_REQ_None);
-      assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?");
-      SR.RequiredExtensions |= RequireExt;
+              .Case("Experimental", RVV_REQ_Experimental);
+      SR.RequiredExtensions[RequireExt / 32] |= 1U << (RequireExt % 32);
     }
 
     SR.NF = NF;
@@ -839,7 +837,8 @@ void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
     R.PrototypeLength = SR.Prototype.size();
     R.SuffixLength = SR.Suffix.size();
     R.OverloadedSuffixSize = SR.OverloadedSuffix.size();
-    R.RequiredExtensions = SR.RequiredExtensions;
+    memcpy(R.RequiredExtensions, SR.RequiredExtensions,
+           sizeof(R.RequiredExtensions));
     R.TypeRangeMask = SR.TypeRangeMask;
     R.Log2LMULMask = SR.Log2LMULMask;
     R.NF = SR.NF;

@@ -519,6 +517,9 @@ struct RVVIntrinsicRecord {
// e.g. vadd
const char *OverloadedName;

// Required target features for this intrinsic.
uint32_t RequiredExtensions[(RVV_REQ_NUM + 31) / 32];
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can use FeatureBitset here and reuse extension enums so that we don't need to define RVVRequire.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FeatureBits is very large and will waste a lot of space.

We also don't have access to the RISCV feature enum if RISCV isn't passed to cmake's LLVM_TARGETS_TO_BUILD.

@lenary
Copy link
Member

lenary commented Mar 25, 2025

Do you hit the same problem with llvm::Bitset as you do with std::bitset?

@topperc
Copy link
Collaborator Author

topperc commented Mar 25, 2025

Do you hit the same problem with llvm::Bitset as you do with std::bitset?

Yeah. There's no way to access the underlying array. The constructor for llvm::Bitset takes a list of bits. Maybe we could print out the bit positions instead of the array?

@wangpc-pp
Copy link
Contributor

Do you hit the same problem with llvm::Bitset as you do with std::bitset?

Yeah. There's no way to access the underlying array. The constructor for llvm::Bitset takes a list of bits. Maybe we could print out the bit positions instead of the array?

Yeah I prefer to use llvm::BitSet and print all the bit positions. The generated code will also be more readable.

@topperc
Copy link
Collaborator Author

topperc commented Mar 26, 2025

Do you hit the same problem with llvm::Bitset as you do with std::bitset?

Yeah. There's no way to access the underlying array. The constructor for llvm::Bitset takes a list of bits. Maybe we could print out the bit positions instead of the array?

Yeah I prefer to use llvm::BitSet and print all the bit positions. The generated code will also be more readable.

The generated code is completely unreadable right now for other reasons.

An excerpt.

{"vadd_vv",nullptr,{0,},996,47,0,3,1,0,15,127,1,1,1,1,1,1,0,0,1,2,},             
{"vfcvt_rtz_xu_f_v","vfcvt_rtz_xu",{0,},542,45,0,2,1,0,192,127,1,1,1,1,1,1,0,0,1,2,},
{"vwaddu_vv","vwaddu_vv",{0,},1052,482,0,3,1,0,7,63,1,1,1,1,1,1,0,0,1,2,},       
{"vwaddu_wv","vwaddu_wv",{0,},1051,482,0,3,1,0,7,63,1,1,1,1,1,1,0,0,1,2,},       
{"vle8_v",nullptr,{0,},605,47,0,2,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},               
{"vle8_v",nullptr,{0,},545,45,0,2,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},               
{"vle8ff_v",nullptr,{0,},608,47,0,3,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},             
{"vle8ff_v",nullptr,{0,},548,45,0,3,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},             
{"vlse8_v",nullptr,{0,},605,47,0,3,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},              
{"vlse8_v",nullptr,{0,},545,45,0,3,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},              
{"vluxei8_v",nullptr,{0,},620,47,0,3,1,0,1,127,1,1,1,1,1,1,0,0,1,2,},

@wangpc-pp
Copy link
Contributor

Do you hit the same problem with llvm::Bitset as you do with std::bitset?

Yeah. There's no way to access the underlying array. The constructor for llvm::Bitset takes a list of bits. Maybe we could print out the bit positions instead of the array?

Yeah I prefer to use llvm::BitSet and print all the bit positions. The generated code will also be more readable.

The generated code is completely unreadable right now for other reasons.

I just found that, I think we should make it readable. I'll do that.

Copy link
Contributor

@wangpc-pp wangpc-pp left a comment

Choose a reason for hiding this comment

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

LGTM.

@topperc topperc merged commit 1752d52 into llvm:main Mar 26, 2025
6 of 11 checks passed
@topperc topperc deleted the pr/required branch March 26, 2025 03:46
@llvm-ci
Copy link
Collaborator

llvm-ci commented Mar 26, 2025

LLVM Buildbot has detected a new failure on builder clang-armv8-quick running on linaro-clang-armv8-quick while building clang at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/154/builds/13969

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'lit :: googletest-timeout.py' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 9
not env -u FILECHECK_OPTS "/usr/bin/python3.10" /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout    --param gtest_filter=InfiniteLoopSubTest --timeout=1 > /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/Output/googletest-timeout.py.tmp.cmd.out
# executed command: not env -u FILECHECK_OPTS /usr/bin/python3.10 /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout --param gtest_filter=InfiniteLoopSubTest --timeout=1
# .---command stderr------------
# | lit.py: /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/llvm/utils/lit/lit/main.py:72: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 1 seconds was requested on the command line. Forcing timeout to be 1 seconds.
# `-----------------------------
# RUN: at line 11
FileCheck --check-prefix=CHECK-INF < /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/Output/googletest-timeout.py.tmp.cmd.out /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/googletest-timeout.py
# executed command: FileCheck --check-prefix=CHECK-INF /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/googletest-timeout.py
# RUN: at line 16
not env -u FILECHECK_OPTS "/usr/bin/python3.10" /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout   --param gtest_filter=InfiniteLoopSubTest  --param set_timeout=1   > /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/Output/googletest-timeout.py.tmp.cfgset.out
# executed command: not env -u FILECHECK_OPTS /usr/bin/python3.10 /home/tcwg-buildbot/worker/clang-armv8-quick/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout --param gtest_filter=InfiniteLoopSubTest --param set_timeout=1
# RUN: at line 19
FileCheck --check-prefix=CHECK-INF < /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/Output/googletest-timeout.py.tmp.cfgset.out /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/googletest-timeout.py
# executed command: FileCheck --check-prefix=CHECK-INF /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/googletest-timeout.py
# .---command stderr------------
# | /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/googletest-timeout.py:34:14: error: CHECK-INF: expected string not found in input
# | # CHECK-INF: Timed Out: 1
# |              ^
# | <stdin>:13:29: note: scanning from here
# | Reached timeout of 1 seconds
# |                             ^
# | <stdin>:37:2: note: possible intended match here
# |  Timed Out: 2 (100.00%)
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/tcwg-buildbot/worker/clang-armv8-quick/stage1/utils/lit/tests/googletest-timeout.py
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |             8:  
# |             9:  
# |            10: -- 
# |            11: exit: -9 
# |            12: -- 
# |            13: Reached timeout of 1 seconds 
# | check:34'0                                 X error: no match found
# |            14: ******************** 
# | check:34'0     ~~~~~~~~~~~~~~~~~~~~~
...

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.

5 participants